diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/WebCryptoAPI | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/WebCryptoAPI')
90 files changed, 9482 insertions, 0 deletions
diff --git a/testing/web-platform/tests/WebCryptoAPI/META.yml b/testing/web-platform/tests/WebCryptoAPI/META.yml new file mode 100644 index 0000000000..27a11a1fe4 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/META.yml @@ -0,0 +1,3 @@ +spec: https://w3c.github.io/webcrypto/ +suggested_reviewers: + - twiss diff --git a/testing/web-platform/tests/WebCryptoAPI/README.md b/testing/web-platform/tests/WebCryptoAPI/README.md new file mode 100644 index 0000000000..5546cf2b6f --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/README.md @@ -0,0 +1 @@ +Directory for Crypto API tests diff --git a/testing/web-platform/tests/WebCryptoAPI/algorithm-discards-context.https.window.js b/testing/web-platform/tests/WebCryptoAPI/algorithm-discards-context.https.window.js new file mode 100644 index 0000000000..e648bc2f23 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/algorithm-discards-context.https.window.js @@ -0,0 +1,213 @@ +// META: title=WebCryptoAPI: Properties discard the context in algorithm normalization + +let nextTest = 0; +let tests = {}; +function closeChild(testId) { + if (tests[testId]) { + let {child, t} = tests[testId]; + delete tests[testId]; + document.body.removeChild(child); + t.done(); + } +} + +function runInChild(t, childScript) { + let testId = nextTest++; + const preamble = ` +let testId = ${testId}; +function closeChildOnAccess(obj, key) { + const oldValue = obj[key]; + Object.defineProperty(obj, key, {get: () => { + top.closeChild(testId); + return oldValue; + }}); +} +`; + childScript = preamble + childScript; + + let child = document.createElement("iframe"); + tests[testId] = {t, child}; + document.body.appendChild(child); + let script = document.createElement("script"); + script.textContent = childScript; + child.contentDocument.body.appendChild(script); +} + +async_test((t) => { + const childScript = ` +let algorithm = {name: "AES-GCM", length: 128}; +closeChildOnAccess(algorithm, "name"); +crypto.subtle.generateKey(algorithm, true, ["encrypt", "decrypt"]);`; + runInChild(t, childScript); +}, "Context is discarded in generateKey"); + +async_test((t) => { + const childScript = ` +let algorithm = {name: "AES-GCM"}; +closeChildOnAccess(algorithm, "name"); +crypto.subtle.importKey("raw", new Uint8Array(16), algorithm, true, + ["encrypt", "decrypt"]);`; + runInChild(t, childScript); +}, "Context is discarded in importKey"); + +async_test((t) => { + const childScript = ` +(async () => { + let key = await crypto.subtle.generateKey( + {name: "AES-GCM", length: 128}, true, ["encrypt", "decrypt"]); + let algorithm = {name: "AES-GCM", iv: new Uint8Array(12)}; + closeChildOnAccess(algorithm, "name"); + crypto.subtle.encrypt(algorithm, key, new Uint8Array()); +})();`; + runInChild(t, childScript); +}, "Context is discarded in encrypt"); + +async_test((t) => { + const childScript = ` +(async () => { + let key = await crypto.subtle.generateKey( + {name: "AES-GCM", length: 128}, true, ["encrypt", "decrypt"]); + let algorithm = {name: "AES-GCM", iv: new Uint8Array(12)}; + let encrypted = await crypto.subtle.encrypt(algorithm, key, new Uint8Array()); + closeChildOnAccess(algorithm, "name"); + crypto.subtle.decrypt(algorithm, key, encrypted); +})();`; + runInChild(t, childScript); +}, "Context is discarded in decrypt"); + +async_test((t) => { + const childScript = ` +let algorithm = {name: "SHA-256"}; +closeChildOnAccess(algorithm, "name"); +crypto.subtle.digest(algorithm, new Uint8Array());`; + runInChild(t, childScript); +}, "Context is discarded in digest"); + +async_test((t) => { + const childScript = ` +(async () => { + let key = await crypto.subtle.generateKey( + {name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]); + let algorithm = {name: "ECDSA", hash: "SHA-256"}; + closeChildOnAccess(algorithm, "name"); + crypto.subtle.sign(algorithm, key.privateKey, new Uint8Array()); +})();`; + runInChild(t, childScript); +}, "Context is discarded in sign"); + +async_test((t) => { + const childScript = ` +(async () => { + let key = await crypto.subtle.generateKey( + {name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]); + let algorithm = {name: "ECDSA", hash: "SHA-256"}; + let data = new Uint8Array(); + let signature = await crypto.subtle.sign(algorithm, key.privateKey, data); + closeChildOnAccess(algorithm, "name"); + crypto.subtle.verify(algorithm, key.publicKey, signature, data); +})();`; + runInChild(t, childScript); +}, "Context is discarded in verify"); + +async_test((t) => { + const childScript = ` +(async () => { + let key = await crypto.subtle.importKey( + "raw", new Uint8Array(16), "HKDF", false, ["deriveBits"]); + let algorithm = { + name: "HKDF", + hash: "SHA-256", + salt: new Uint8Array(), + info: new Uint8Array(), + }; + closeChildOnAccess(algorithm, "name"); + crypto.subtle.deriveBits(algorithm, key, 16); +})();`; + runInChild(t, childScript); +}, "Context is discarded in deriveBits"); + +async_test((t) => { + const childScript = ` +(async () => { + let key = await crypto.subtle.importKey( + "raw", new Uint8Array(16), "HKDF", false, ["deriveKey"]); + let algorithm = { + name: "HKDF", + hash: "SHA-256", + salt: new Uint8Array(), + info: new Uint8Array(), + }; + let derivedAlgorithm = {name: "AES-GCM", length: 128}; + closeChildOnAccess(algorithm, "name"); + crypto.subtle.deriveKey(algorithm, key, derivedAlgorithm, true, + ["encrypt", "decrypt"]); +})();`; + runInChild(t, childScript); +}, "Context is discarded in deriveKey"); + +async_test((t) => { + const childScript = ` +(async () => { + let key = await crypto.subtle.importKey( + "raw", new Uint8Array(16), "HKDF", false, ["deriveKey"]); + let algorithm = { + name: "HKDF", + hash: "SHA-256", + salt: new Uint8Array(), + info: new Uint8Array(), + }; + let derivedAlgorithm = {name: "AES-GCM", length: 128}; + closeChildOnAccess(derivedAlgorithm, "name"); + crypto.subtle.deriveKey(algorithm, key, derivedAlgorithm, true, + ["encrypt", "decrypt"]); +})();`; + runInChild(t, childScript); +}, "Context is discarded in deriveKey (2)"); + +async_test((t) => { + const childScript = ` +(async () => { + let wrapKey = await crypto.subtle.generateKey( + {name: "AES-GCM", length: 128}, true, ["wrapKey", "unwrapKey"]); + let key = await crypto.subtle.generateKey( + {name: "AES-GCM", length: 128}, true, ["encrypt", "decrypt"]); + let wrapAlgorithm = {name: "AES-GCM", iv: new Uint8Array(12)}; + closeChildOnAccess(wrapAlgorithm, "name"); + crypto.subtle.wrapKey("raw", key, wrapKey, wrapAlgorithm); +})();`; + runInChild(t, childScript); +}, "Context is discarded in wrapKey"); + +async_test((t) => { + const childScript = ` +(async () => { + let wrapKey = await crypto.subtle.generateKey( + {name: "AES-GCM", length: 128}, true, ["wrapKey", "unwrapKey"]); + let keyAlgorithm = {name: "AES-GCM", length: 128}; + let keyUsages = ["encrypt", "decrypt"]; + let key = await crypto.subtle.generateKey(keyAlgorithm, true, keyUsages); + let wrapAlgorithm = {name: "AES-GCM", iv: new Uint8Array(12)}; + let wrapped = await crypto.subtle.wrapKey("raw", key, wrapKey, wrapAlgorithm); + closeChildOnAccess(wrapAlgorithm, "name"); + crypto.subtle.unwrapKey( + "raw", wrapped, wrapKey, wrapAlgorithm, keyAlgorithm, true, keyUsages); +})();`; + runInChild(t, childScript); +}, "Context is discarded in unwrapKey"); + +async_test((t) => { + const childScript = ` +(async () => { + let wrapKey = await crypto.subtle.generateKey( + {name: "AES-GCM", length: 128}, true, ["wrapKey", "unwrapKey"]); + let keyAlgorithm = {name: "AES-GCM", length: 128}; + let keyUsages = ["encrypt", "decrypt"]; + let key = await crypto.subtle.generateKey(keyAlgorithm, true, keyUsages); + let wrapAlgorithm = {name: "AES-GCM", iv: new Uint8Array(12)}; + let wrapped = await crypto.subtle.wrapKey("raw", key, wrapKey, wrapAlgorithm); + closeChildOnAccess(keyAlgorithm, "name"); + crypto.subtle.unwrapKey( + "raw", wrapped, wrapKey, wrapAlgorithm, keyAlgorithm, true, keyUsages); +})();`; + runInChild(t, childScript); +}, "Context is discarded in unwrapKey (2)"); diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.https.any.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.https.any.js new file mode 100644 index 0000000000..afa7db845f --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.https.any.js @@ -0,0 +1,9 @@ +// META: title=WebCryptoAPI: deriveBits() Using ECDH with CFRG Elliptic Curves +// META: script=cfrg_curves_bits.js + +// Define subtests from a `promise_test` to ensure the harness does not +// complete before the subtests are available. `explicit_done` cannot be used +// for this purpose because the global `done` function is automatically invoked +// by the WPT infrastructure in dedicated worker tests defined using the +// "multi-global" pattern. +promise_test(define_tests, 'setup - define tests'); diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.js new file mode 100644 index 0000000000..9a824ce951 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.js @@ -0,0 +1,251 @@ + +function define_tests() { + // May want to test prefixed implementations. + var subtle = self.crypto.subtle; + + var pkcs8 = { + "X25519": new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255, 97]), + "X448": new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146, 158]) + }; + + var spki = { + "X25519": new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]), + "X448": new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]) + }; + + var sizes = { + "X25519": 32, + "X448": 56 + }; + + var derivations = { + "X25519": new Uint8Array([39, 104, 64, 157, 250, 185, 158, 194, 59, 140, 137, 185, 63, 245, 136, 2, 149, 247, 97, 118, 8, 143, 137, 228, 61, 254, 190, 126, 161, 149, 0, 8]), + "X448": new Uint8Array([240, 246, 197, 241, 127, 148, 244, 41, 30, 171, 113, 120, 134, 109, 55, 236, 137, 6, 221, 108, 81, 65, 67, 220, 133, 190, 124, 242, 141, 239, 243, 155, 114, 110, 15, 109, 207, 129, 14, 181, 148, 220, 169, 123, 72, 130, 189, 68, 196, 62, 167, 220, 103, 244, 154, 78]) + }; + + return importKeys(pkcs8, spki, sizes) + .then(function(results) { + publicKeys = results.publicKeys; + privateKeys = results.privateKeys; + noDeriveBitsKeys = results.noDeriveBitsKeys; + + Object.keys(sizes).forEach(function(algorithmName) { + // Basic success case + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName]) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[algorithmName]), "Derived correct bits"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, algorithmName + " good parameters"); + + // Case insensitivity check + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName.toLowerCase(), public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName]) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[algorithmName]), "Derived correct bits"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, algorithmName + " mixed case parameters"); + + // Null length + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], null) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[algorithmName]), "Derived correct bits"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, algorithmName + " with null length"); + + // Shorter than entire derivation per algorithm + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName] - 32) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[algorithmName], 8 * sizes[algorithmName] - 32), "Derived correct bits"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, algorithmName + " short result"); + + // Non-multiple of 8 + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName] - 11) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[algorithmName], 8 * sizes[algorithmName] - 11), "Derived correct bits"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, algorithmName + " non-multiple of 8 bits"); + + // Errors to test: + + // - missing public property TypeError + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName}, privateKeys[algorithmName], 8 * sizes[algorithmName]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with TypeError"); + }, function(err) { + assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " missing public property"); + + // - Non CryptoKey public property TypeError + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName, public: {message: "Not a CryptoKey"}}, privateKeys[algorithmName], 8 * sizes[algorithmName]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with TypeError"); + }, function(err) { + assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " public property of algorithm is not a CryptoKey"); + + // - wrong algorithm + promise_test(function(test) { + publicKey = publicKeys["X25519"]; + if (algorithmName === "X25519") { + publicKey = publicKeys["X448"]; + } + return subtle.deriveBits({name: algorithmName, public: publicKey}, privateKeys[algorithmName], 8 * sizes[algorithmName]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " mismatched algorithms"); + + // - No deriveBits usage in baseKey InvalidAccessError + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, noDeriveBitsKeys[algorithmName], 8 * sizes[algorithmName]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " no deriveBits usage for base key"); + + // - Use public key for baseKey InvalidAccessError + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, publicKeys[algorithmName], 8 * sizes[algorithmName]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " base key is not a private key"); + + // - Use private key for public property InvalidAccessError + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName, public: privateKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " public property value is a private key"); + + // - Use secret key for public property InvalidAccessError + promise_test(function(test) { + return subtle.generateKey({name: "AES-CBC", length: 128}, true, ["encrypt", "decrypt"]) + .then(function(secretKey) { + return subtle.deriveBits({name: algorithmName, public: secretKey}, privateKeys[algorithmName], 8 * sizes[algorithmName]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }); + }, algorithmName + " public property value is a secret key"); + + // - Length greater than possible for particular curves OperationError + promise_test(function(test) { + return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName] + 8) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with OperationError"); + }, function(err) { + assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " asking for too many bits"); + }); + }); + + function importKeys(pkcs8, spki, sizes) { + var privateKeys = {}; + var publicKeys = {}; + var noDeriveBitsKeys = {}; + + var promises = []; + Object.keys(pkcs8).forEach(function(algorithmName) { + var operation = subtle.importKey("pkcs8", pkcs8[algorithmName], + {name: algorithmName}, + false, ["deriveBits", "deriveKey"]) + .then(function(key) { + privateKeys[algorithmName] = key; + }, function (err) { + privateKeys[algorithmName] = null; + }); + promises.push(operation); + }); + Object.keys(pkcs8).forEach(function(algorithmName) { + var operation = subtle.importKey("pkcs8", pkcs8[algorithmName], + {name: algorithmName}, + false, ["deriveKey"]) + .then(function(key) { + noDeriveBitsKeys[algorithmName] = key; + }, function (err) { + noDeriveBitsKeys[algorithmName] = null; + }); + promises.push(operation); + }); + Object.keys(spki).forEach(function(algorithmName) { + var operation = subtle.importKey("spki", spki[algorithmName], + {name: algorithmName}, + false, []) + .then(function(key) { + publicKeys[algorithmName] = key; + }, function (err) { + publicKeys[algorithmName] = null; + }); + promises.push(operation); + }); + + return Promise.all(promises) + .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveBitsKeys: noDeriveBitsKeys}}); + } + + // Compares two ArrayBuffer or ArrayBufferView objects. If bitCount is + // omitted, the two values must be the same length and have the same contents + // in every byte. If bitCount is included, only that leading number of bits + // have to match. + function equalBuffers(a, b, bitCount) { + var remainder; + + if (typeof bitCount === "undefined" && a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + var length = a.byteLength; + if (typeof bitCount !== "undefined") { + length = Math.floor(bitCount / 8); + } + + for (var i=0; i<length; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + if (typeof bitCount !== "undefined") { + remainder = bitCount % 8; + return aBytes[length] >> (8 - remainder) === bBytes[length] >> (8 - remainder); + } + + return true; + } + +} diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.https.any.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.https.any.js new file mode 100644 index 0000000000..71fe87d9a8 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.https.any.js @@ -0,0 +1,9 @@ +// META: title=WebCryptoAPI: deriveKey() Using ECDH with CFRG Elliptic Curves +// META: script=cfrg_curves_keys.js + +// Define subtests from a `promise_test` to ensure the harness does not +// complete before the subtests are available. `explicit_done` cannot be used +// for this purpose because the global `done` function is automatically invoked +// by the WPT infrastructure in dedicated worker tests defined using the +// "multi-global" pattern. +promise_test(define_tests, 'setup - define tests'); diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.js new file mode 100644 index 0000000000..1e8e9d48d3 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.js @@ -0,0 +1,219 @@ + +function define_tests() { + // May want to test prefixed implementations. + var subtle = self.crypto.subtle; + + var pkcs8 = { + "X25519": new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255, 97]), + "X448": new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146, 158]) + }; + + var spki = { + "X25519": new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]), + "X448": new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]) + }; + + var sizes = { + "X25519": 32, + "X448": 56 + }; + + var derivations = { + "X25519": new Uint8Array([39, 104, 64, 157, 250, 185, 158, 194, 59, 140, 137, 185, 63, 245, 136, 2, 149, 247, 97, 118, 8, 143, 137, 228, 61, 254, 190, 126, 161, 149, 0, 8]), + "X448": new Uint8Array([240, 246, 197, 241, 127, 148, 244, 41, 30, 171, 113, 120, 134, 109, 55, 236, 137, 6, 221, 108, 81, 65, 67, 220, 133, 190, 124, 242, 141, 239, 243, 155, 114, 110, 15, 109, 207, 129, 14, 181, 148, 220, 169, 123, 72, 130, 189, 68, 196, 62, 167, 220, 103, 244, 154, 78]) + }; + + return importKeys(pkcs8, spki, sizes) + .then(function(results) { + publicKeys = results.publicKeys; + privateKeys = results.privateKeys; + noDeriveKeyKeys = results.noDeriveKeyKeys; + + Object.keys(sizes).forEach(function(algorithmName) { + // Basic success case + promise_test(function(test) { + return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_true(equalBuffers(exportedKey, derivations[algorithmName], 8 * exportedKey.length), "Derived correct key"); + }, function(err) { + assert_unreached("deriveKey failed with error " + err.name + ": " + err.message); + }); + }, algorithmName + " good parameters"); + + // Case insensitivity check + promise_test(function(test) { + return subtle.deriveKey({name: algorithmName.toLowerCase(), public: publicKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_true(equalBuffers(exportedKey, derivations[algorithmName], 8 * exportedKey.length), "Derived correct key"); + }, function(err) { + assert_unreached("deriveKey failed with error " + err.name + ": " + err.message); + }); + }, algorithmName + " mixed case parameters"); + // Errors to test: + + // - missing public property TypeError + promise_test(function(test) { + return subtle.deriveKey({name: algorithmName}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with TypeError"); + }, function(err) { + assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " missing public property"); + + // - Non CryptoKey public property TypeError + promise_test(function(test) { + return subtle.deriveKey({name: algorithmName, public: {message: "Not a CryptoKey"}}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with TypeError"); + }, function(err) { + assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " public property of algorithm is not a CryptoKey"); + + // - wrong algorithm + promise_test(function(test) { + publicKey = publicKeys["X25519"]; + if (algorithmName === "X25519") { + publicKey = publicKeys["X448"]; + } + return subtle.deriveKey({name: algorithmName, public: publicKey}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " mismatched algorithms"); + + // - No deriveKey usage in baseKey InvalidAccessError + promise_test(function(test) { + return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, noDeriveKeyKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " no deriveKey usage for base key"); + + // - Use public key for baseKey InvalidAccessError + promise_test(function(test) { + return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, publicKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " base key is not a private key"); + + // - Use private key for public property InvalidAccessError + promise_test(function(test) { + return subtle.deriveKey({name: algorithmName, public: privateKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, algorithmName + " public property value is a private key"); + + // - Use secret key for public property InvalidAccessError + promise_test(function(test) { + return subtle.generateKey({name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(secretKey) { + return subtle.deriveKey({name: algorithmName, public: secretKey}, privateKeys[algorithmName], {name: "AES-CBC", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }); + }, algorithmName + " public property value is a secret key"); + }); + }); + + function importKeys(pkcs8, spki, sizes) { + var privateKeys = {}; + var publicKeys = {}; + var noDeriveKeyKeys = {}; + + var promises = []; + Object.keys(pkcs8).forEach(function(algorithmName) { + var operation = subtle.importKey("pkcs8", pkcs8[algorithmName], + {name: algorithmName}, + false, ["deriveBits", "deriveKey"]) + .then(function(key) { + privateKeys[algorithmName] = key; + }, function (err) { + privateKeys[algorithmName] = null; + }); + promises.push(operation); + }); + Object.keys(pkcs8).forEach(function(algorithmName) { + var operation = subtle.importKey("pkcs8", pkcs8[algorithmName], + {name: algorithmName}, + false, ["deriveBits"]) + .then(function(key) { + noDeriveKeyKeys[algorithmName] = key; + }, function (err) { + noDeriveKeyKeys[algorithmName] = null; + }); + promises.push(operation); + }); + Object.keys(spki).forEach(function(algorithmName) { + var operation = subtle.importKey("spki", spki[algorithmName], + {name: algorithmName}, + false, []) + .then(function(key) { + publicKeys[algorithmName] = key; + }, function (err) { + publicKeys[algorithmName] = null; + }); + promises.push(operation); + }); + + return Promise.all(promises) + .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveKeyKeys: noDeriveKeyKeys}}); + } + + // Compares two ArrayBuffer or ArrayBufferView objects. If bitCount is + // omitted, the two values must be the same length and have the same contents + // in every byte. If bitCount is included, only that leading number of bits + // have to match. + function equalBuffers(a, b, bitCount) { + var remainder; + + if (typeof bitCount === "undefined" && a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + var length = a.byteLength; + if (typeof bitCount !== "undefined") { + length = Math.floor(bitCount / 8); + } + + for (var i=0; i<length; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + if (typeof bitCount !== "undefined") { + remainder = bitCount % 8; + return aBytes[length] >> (8 - remainder) === bBytes[length] >> (8 - remainder); + } + + return true; + } + +} diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_bits.https.any.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_bits.https.any.js new file mode 100644 index 0000000000..37e3eb4324 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_bits.https.any.js @@ -0,0 +1,9 @@ +// META: title=WebCryptoAPI: deriveBits() Using ECDH +// META: script=ecdh_bits.js + +// Define subtests from a `promise_test` to ensure the harness does not +// complete before the subtests are available. `explicit_done` cannot be used +// for this purpose because the global `done` function is automatically invoked +// by the WPT infrastructure in dedicated worker tests defined using the +// "multi-global" pattern. +promise_test(define_tests, 'setup - define tests'); diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_bits.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_bits.js new file mode 100644 index 0000000000..e52ffc6bfd --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_bits.js @@ -0,0 +1,276 @@ + +function define_tests() { + // May want to test prefixed implementations. + var subtle = self.crypto.subtle; + + var pkcs8 = { + "P-521": new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 1, 166, 126, 211, 33, 145, 90, 100, 170, 53, 155, 125, 100, 141, 220, 38, 24, 250, 142, 141, 24, 103, 232, 247, 24, 48, 177, 13, 37, 237, 40, 145, 250, 241, 47, 60, 126, 117, 66, 26, 46, 162, 100, 249, 169, 21, 50, 13, 39, 79, 225, 71, 7, 66, 185, 132, 233, 107, 152, 145, 32, 129, 250, 205, 71, 141, 161, 129, 137, 3, 129, 134, 0, 4, 0, 32, 157, 72, 63, 40, 102, 104, 129, 198, 100, 31, 58, 18, 111, 64, 15, 81, 228, 101, 17, 112, 254, 103, 140, 117, 232, 87, 18, 226, 134, 138, 220, 133, 8, 36, 153, 123, 235, 240, 188, 130, 180, 48, 40, 166, 210, 236, 23, 119, 202, 69, 39, 159, 114, 6, 163, 234, 139, 92, 210, 7, 63, 73, 62, 69, 0, 12, 181, 76, 58, 90, 202, 162, 104, 197, 103, 16, 66, 136, 120, 217, 139, 138, 251, 246, 138, 97, 33, 83, 99, 40, 70, 216, 7, 233, 38, 114, 105, 143, 27, 156, 97, 29, 231, 211, 142, 52, 205, 108, 115, 136, 144, 146, 197, 110, 82, 214, 128, 241, 223, 208, 146, 184, 122, 200, 239, 159, 243, 200, 251, 72]), + "P-256": new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 15, 247, 79, 232, 241, 202, 175, 97, 92, 206, 241, 29, 217, 53, 114, 87, 98, 217, 216, 65, 236, 186, 185, 94, 170, 38, 68, 123, 52, 100, 245, 113, 161, 68, 3, 66, 0, 4, 140, 96, 11, 44, 102, 25, 45, 97, 158, 39, 210, 37, 107, 59, 151, 118, 178, 141, 30, 5, 246, 13, 234, 189, 98, 174, 123, 154, 211, 157, 224, 217, 59, 4, 102, 109, 199, 119, 14, 126, 207, 13, 211, 203, 203, 211, 110, 221, 107, 94, 220, 153, 81, 7, 55, 161, 237, 104, 46, 205, 112, 244, 10, 47]), + "P-384": new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 248, 113, 165, 102, 101, 137, 193, 74, 87, 71, 38, 62, 248, 91, 49, 156, 192, 35, 219, 110, 53, 103, 108, 61, 120, 30, 239, 139, 5, 95, 207, 190, 134, 250, 13, 6, 208, 86, 181, 25, 95, 177, 50, 58, 248, 222, 37, 179, 161, 100, 3, 98, 0, 4, 241, 25, 101, 223, 125, 212, 89, 77, 4, 25, 197, 8, 100, 130, 163, 184, 38, 185, 121, 127, 155, 224, 189, 13, 16, 156, 158, 30, 153, 137, 193, 185, 169, 43, 143, 38, 159, 152, 225, 122, 209, 132, 186, 115, 193, 247, 151, 98, 175, 69, 175, 129, 65, 96, 38, 66, 218, 39, 26, 107, 176, 255, 235, 12, 180, 71, 143, 207, 112, 126, 102, 26, 166, 214, 205, 245, 21, 73, 200, 140, 63, 19, 11, 233, 232, 32, 31, 111, 106, 9, 244, 24, 90, 175, 149, 196]) + }; + + var spki = { + "P-521": new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 0, 238, 105, 249, 71, 21, 215, 1, 233, 226, 1, 19, 51, 212, 244, 249, 108, 186, 125, 145, 248, 139, 17, 43, 175, 117, 207, 9, 204, 31, 138, 202, 151, 97, 141, 169, 56, 152, 34, 210, 155, 111, 233, 153, 106, 97, 32, 62, 247, 82, 183, 113, 232, 149, 143, 196, 103, 123, 179, 119, 133, 101, 171, 96, 214, 237, 0, 222, 171, 103, 97, 137, 91, 147, 94, 58, 211, 37, 251, 133, 73, 229, 111, 19, 120, 106, 167, 63, 136, 162, 236, 254, 64, 147, 52, 115, 216, 174, 242, 64, 196, 223, 215, 213, 6, 242, 44, 221, 14, 85, 85, 143, 63, 191, 5, 235, 247, 239, 239, 122, 114, 215, 143, 70, 70, 155, 132, 72, 242, 110, 39, 18]), + "P-256": new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 154, 116, 32, 120, 126, 95, 77, 105, 211, 232, 34, 114, 115, 1, 109, 56, 224, 71, 129, 133, 223, 127, 238, 156, 142, 103, 60, 202, 211, 79, 126, 128, 254, 49, 141, 182, 221, 107, 119, 218, 99, 32, 165, 246, 151, 89, 9, 68, 23, 177, 52, 239, 138, 139, 116, 193, 101, 4, 57, 198, 115, 0, 90, 61]), + "P-384": new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 145, 130, 45, 194, 175, 89, 193, 143, 91, 103, 248, 13, 246, 26, 38, 3, 194, 168, 240, 179, 192, 175, 130, 45, 99, 194, 121, 112, 26, 130, 69, 96, 64, 68, 1, 221, 233, 165, 110, 229, 39, 87, 234, 139, 199, 72, 212, 200, 43, 83, 55, 180, 141, 123, 101, 88, 58, 61, 87, 36, 56, 136, 0, 54, 186, 198, 115, 15, 66, 202, 82, 120, 150, 107, 213, 242, 30, 134, 226, 29, 48, 197, 166, 208, 70, 62, 197, 19, 221, 80, 159, 252, 220, 175, 31, 245]) + }; + + var sizes = { + "P-521": 66, + "P-256": 32, + "P-384": 48 + }; + + var derivations = { + "P-521": new Uint8Array([0, 156, 43, 206, 87, 190, 128, 173, 171, 59, 7, 56, 91, 142, 89, 144, 235, 125, 111, 222, 189, 176, 27, 243, 83, 113, 164, 246, 7, 94, 157, 40, 138, 193, 42, 109, 254, 3, 170, 87, 67, 188, 129, 112, 157, 73, 168, 34, 148, 2, 25, 182, 75, 118, 138, 205, 82, 15, 161, 54, 142, 160, 175, 141, 71, 93]), + "P-256": new Uint8Array([14, 143, 60, 77, 177, 178, 162, 131, 115, 90, 0, 220, 87, 31, 26, 232, 151, 28, 227, 35, 250, 17, 131, 137, 203, 95, 65, 196, 59, 61, 181, 161]), + "P-384": new Uint8Array([224, 189, 107, 206, 10, 239, 140, 164, 136, 56, 166, 226, 252, 197, 126, 103, 185, 197, 232, 134, 12, 95, 11, 233, 218, 190, 197, 62, 69, 78, 24, 160, 161, 116, 196, 136, 136, 162, 100, 136, 17, 91, 45, 201, 241, 223, 165, 45]) + }; + + return importKeys(pkcs8, spki, sizes) + .then(function(results) { + publicKeys = results.publicKeys; + privateKeys = results.privateKeys; + ecdsaKeyPairs = results.ecdsaKeyPairs; + noDeriveBitsKeys = results.noDeriveBitsKeys; + + Object.keys(sizes).forEach(function(namedCurve) { + // Basic success case + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve]) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[namedCurve]), "Derived correct bits"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, namedCurve + " good parameters"); + + // Case insensitivity check + promise_test(function(test) { + return subtle.deriveBits({name: "EcDh", public: publicKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve]) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[namedCurve]), "Derived correct bits"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, namedCurve + " mixed case parameters"); + + // Null length + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], null) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[namedCurve]), "Derived correct bits"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, namedCurve + " with null length"); + + // Shorter than entire derivation per algorithm + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve] - 32) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[namedCurve], 8 * sizes[namedCurve] - 32), "Derived correct bits"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, namedCurve + " short result"); + + // Non-multiple of 8 + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve] - 11) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[namedCurve], 8 * sizes[namedCurve] - 11), "Derived correct bits"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, namedCurve + " non-multiple of 8 bits"); + + // Errors to test: + + // - missing public property TypeError + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH"}, privateKeys[namedCurve], 8 * sizes[namedCurve]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with TypeError"); + }, function(err) { + assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " missing public curve"); + + // - Non CryptoKey public property TypeError + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH", public: {message: "Not a CryptoKey"}}, privateKeys[namedCurve], 8 * sizes[namedCurve]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with TypeError"); + }, function(err) { + assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " public property of algorithm is not a CryptoKey"); + + // - wrong named curve + promise_test(function(test) { + publicKey = publicKeys["P-256"]; + if (namedCurve === "P-256") { + publicKey = publicKeys["P-384"]; + } + return subtle.deriveBits({name: "ECDH", public: publicKey}, privateKeys[namedCurve], 8 * sizes[namedCurve]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " mismatched curves"); + + // - not ECDH public property InvalidAccessError + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH", public: ecdsaKeyPairs[namedCurve].publicKey}, privateKeys[namedCurve], 8 * sizes[namedCurve]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " public property of algorithm is not an ECDSA public key"); + + // - No deriveBits usage in baseKey InvalidAccessError + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, noDeriveBitsKeys[namedCurve], 8 * sizes[namedCurve]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " no deriveBits usage for base key"); + + // - Use public key for baseKey InvalidAccessError + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, publicKeys[namedCurve], 8 * sizes[namedCurve]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " base key is not a private key"); + + // - Use private key for public property InvalidAccessError + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH", public: privateKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " public property value is a private key"); + + // - Use secret key for public property InvalidAccessError + promise_test(function(test) { + return subtle.generateKey({name: "AES-CBC", length: 128}, true, ["encrypt", "decrypt"]) + .then(function(secretKey) { + return subtle.deriveBits({name: "ECDH", public: secretKey}, privateKeys[namedCurve], 8 * sizes[namedCurve]) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }); + }, namedCurve + " public property value is a secret key"); + + // - Length greater than 256, 384, 521 for particular curves OperationError + promise_test(function(test) { + return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve] + 8) + .then(function(derivation) { + assert_unreached("deriveBits succeeded but should have failed with OperationError"); + }, function(err) { + assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " asking for too many bits"); + }); + }); + + function importKeys(pkcs8, spki, sizes) { + var privateKeys = {}; + var publicKeys = {}; + var ecdsaKeyPairs = {}; + var noDeriveBitsKeys = {}; + + var promises = []; + Object.keys(pkcs8).forEach(function(namedCurve) { + var operation = subtle.importKey("pkcs8", pkcs8[namedCurve], + {name: "ECDH", namedCurve: namedCurve}, + false, ["deriveBits", "deriveKey"]) + .then(function(key) { + privateKeys[namedCurve] = key; + }, function (err) { + privateKeys[namedCurve] = null; + }); + promises.push(operation); + }); + Object.keys(pkcs8).forEach(function(namedCurve) { + var operation = subtle.importKey("pkcs8", pkcs8[namedCurve], + {name: "ECDH", namedCurve: namedCurve}, + false, ["deriveKey"]) + .then(function(key) { + noDeriveBitsKeys[namedCurve] = key; + }, function (err) { + noDeriveBitsKeys[namedCurve] = null; + }); + promises.push(operation); + }); + Object.keys(spki).forEach(function(namedCurve) { + var operation = subtle.importKey("spki", spki[namedCurve], + {name: "ECDH", namedCurve: namedCurve}, + false, []) + .then(function(key) { + publicKeys[namedCurve] = key; + }, function (err) { + publicKeys[namedCurve] = null; + }); + promises.push(operation); + }); + Object.keys(sizes).forEach(function(namedCurve) { + var operation = subtle.generateKey({name: "ECDSA", namedCurve: namedCurve}, false, ["sign", "verify"]) + .then(function(keyPair) { + ecdsaKeyPairs[namedCurve] = keyPair; + }, function (err) { + ecdsaKeyPairs[namedCurve] = null; + }); + promises.push(operation); + }); + + return Promise.all(promises) + .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, ecdsaKeyPairs: ecdsaKeyPairs, noDeriveBitsKeys: noDeriveBitsKeys}}); + } + + // Compares two ArrayBuffer or ArrayBufferView objects. If bitCount is + // omitted, the two values must be the same length and have the same contents + // in every byte. If bitCount is included, only that leading number of bits + // have to match. + function equalBuffers(a, b, bitCount) { + var remainder; + + if (typeof bitCount === "undefined" && a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + var length = a.byteLength; + if (typeof bitCount !== "undefined") { + length = Math.floor(bitCount / 8); + } + + for (var i=0; i<length; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + if (typeof bitCount !== "undefined") { + remainder = bitCount % 8; + return aBytes[length] >> (8 - remainder) === bBytes[length] >> (8 - remainder); + } + + return true; + } + +} diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_keys.https.any.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_keys.https.any.js new file mode 100644 index 0000000000..d8235fce5a --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_keys.https.any.js @@ -0,0 +1,9 @@ +// META: title=WebCryptoAPI: deriveKey() Using ECDH +// META: script=ecdh_keys.js + +// Define subtests from a `promise_test` to ensure the harness does not +// complete before the subtests are available. `explicit_done` cannot be used +// for this purpose because the global `done` function is automatically invoked +// by the WPT infrastructure in dedicated worker tests defined using the +// "multi-global" pattern. +promise_test(define_tests, 'setup - define tests'); diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_keys.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_keys.js new file mode 100644 index 0000000000..fce76f1855 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/ecdh_keys.js @@ -0,0 +1,245 @@ + +function define_tests() { + // May want to test prefixed implementations. + var subtle = self.crypto.subtle; + + var pkcs8 = { + "P-521": new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 1, 166, 126, 211, 33, 145, 90, 100, 170, 53, 155, 125, 100, 141, 220, 38, 24, 250, 142, 141, 24, 103, 232, 247, 24, 48, 177, 13, 37, 237, 40, 145, 250, 241, 47, 60, 126, 117, 66, 26, 46, 162, 100, 249, 169, 21, 50, 13, 39, 79, 225, 71, 7, 66, 185, 132, 233, 107, 152, 145, 32, 129, 250, 205, 71, 141, 161, 129, 137, 3, 129, 134, 0, 4, 0, 32, 157, 72, 63, 40, 102, 104, 129, 198, 100, 31, 58, 18, 111, 64, 15, 81, 228, 101, 17, 112, 254, 103, 140, 117, 232, 87, 18, 226, 134, 138, 220, 133, 8, 36, 153, 123, 235, 240, 188, 130, 180, 48, 40, 166, 210, 236, 23, 119, 202, 69, 39, 159, 114, 6, 163, 234, 139, 92, 210, 7, 63, 73, 62, 69, 0, 12, 181, 76, 58, 90, 202, 162, 104, 197, 103, 16, 66, 136, 120, 217, 139, 138, 251, 246, 138, 97, 33, 83, 99, 40, 70, 216, 7, 233, 38, 114, 105, 143, 27, 156, 97, 29, 231, 211, 142, 52, 205, 108, 115, 136, 144, 146, 197, 110, 82, 214, 128, 241, 223, 208, 146, 184, 122, 200, 239, 159, 243, 200, 251, 72]), + "P-256": new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 15, 247, 79, 232, 241, 202, 175, 97, 92, 206, 241, 29, 217, 53, 114, 87, 98, 217, 216, 65, 236, 186, 185, 94, 170, 38, 68, 123, 52, 100, 245, 113, 161, 68, 3, 66, 0, 4, 140, 96, 11, 44, 102, 25, 45, 97, 158, 39, 210, 37, 107, 59, 151, 118, 178, 141, 30, 5, 246, 13, 234, 189, 98, 174, 123, 154, 211, 157, 224, 217, 59, 4, 102, 109, 199, 119, 14, 126, 207, 13, 211, 203, 203, 211, 110, 221, 107, 94, 220, 153, 81, 7, 55, 161, 237, 104, 46, 205, 112, 244, 10, 47]), + "P-384": new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 248, 113, 165, 102, 101, 137, 193, 74, 87, 71, 38, 62, 248, 91, 49, 156, 192, 35, 219, 110, 53, 103, 108, 61, 120, 30, 239, 139, 5, 95, 207, 190, 134, 250, 13, 6, 208, 86, 181, 25, 95, 177, 50, 58, 248, 222, 37, 179, 161, 100, 3, 98, 0, 4, 241, 25, 101, 223, 125, 212, 89, 77, 4, 25, 197, 8, 100, 130, 163, 184, 38, 185, 121, 127, 155, 224, 189, 13, 16, 156, 158, 30, 153, 137, 193, 185, 169, 43, 143, 38, 159, 152, 225, 122, 209, 132, 186, 115, 193, 247, 151, 98, 175, 69, 175, 129, 65, 96, 38, 66, 218, 39, 26, 107, 176, 255, 235, 12, 180, 71, 143, 207, 112, 126, 102, 26, 166, 214, 205, 245, 21, 73, 200, 140, 63, 19, 11, 233, 232, 32, 31, 111, 106, 9, 244, 24, 90, 175, 149, 196]) + }; + + var spki = { + "P-521": new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 0, 238, 105, 249, 71, 21, 215, 1, 233, 226, 1, 19, 51, 212, 244, 249, 108, 186, 125, 145, 248, 139, 17, 43, 175, 117, 207, 9, 204, 31, 138, 202, 151, 97, 141, 169, 56, 152, 34, 210, 155, 111, 233, 153, 106, 97, 32, 62, 247, 82, 183, 113, 232, 149, 143, 196, 103, 123, 179, 119, 133, 101, 171, 96, 214, 237, 0, 222, 171, 103, 97, 137, 91, 147, 94, 58, 211, 37, 251, 133, 73, 229, 111, 19, 120, 106, 167, 63, 136, 162, 236, 254, 64, 147, 52, 115, 216, 174, 242, 64, 196, 223, 215, 213, 6, 242, 44, 221, 14, 85, 85, 143, 63, 191, 5, 235, 247, 239, 239, 122, 114, 215, 143, 70, 70, 155, 132, 72, 242, 110, 39, 18]), + "P-256": new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 154, 116, 32, 120, 126, 95, 77, 105, 211, 232, 34, 114, 115, 1, 109, 56, 224, 71, 129, 133, 223, 127, 238, 156, 142, 103, 60, 202, 211, 79, 126, 128, 254, 49, 141, 182, 221, 107, 119, 218, 99, 32, 165, 246, 151, 89, 9, 68, 23, 177, 52, 239, 138, 139, 116, 193, 101, 4, 57, 198, 115, 0, 90, 61]), + "P-384": new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 145, 130, 45, 194, 175, 89, 193, 143, 91, 103, 248, 13, 246, 26, 38, 3, 194, 168, 240, 179, 192, 175, 130, 45, 99, 194, 121, 112, 26, 130, 69, 96, 64, 68, 1, 221, 233, 165, 110, 229, 39, 87, 234, 139, 199, 72, 212, 200, 43, 83, 55, 180, 141, 123, 101, 88, 58, 61, 87, 36, 56, 136, 0, 54, 186, 198, 115, 15, 66, 202, 82, 120, 150, 107, 213, 242, 30, 134, 226, 29, 48, 197, 166, 208, 70, 62, 197, 19, 221, 80, 159, 252, 220, 175, 31, 245]) + }; + + var sizes = { + "P-521": 66, + "P-256": 32, + "P-384": 48 + }; + + var derivations = { + "P-521": new Uint8Array([0, 156, 43, 206, 87, 190, 128, 173, 171, 59, 7, 56, 91, 142, 89, 144, 235, 125, 111, 222, 189, 176, 27, 243, 83, 113, 164, 246, 7, 94, 157, 40, 138, 193, 42, 109, 254, 3, 170, 87, 67, 188, 129, 112, 157, 73, 168, 34, 148, 2, 25, 182, 75, 118, 138, 205, 82, 15, 161, 54, 142, 160, 175, 141, 71, 93]), + "P-256": new Uint8Array([14, 143, 60, 77, 177, 178, 162, 131, 115, 90, 0, 220, 87, 31, 26, 232, 151, 28, 227, 35, 250, 17, 131, 137, 203, 95, 65, 196, 59, 61, 181, 161]), + "P-384": new Uint8Array([224, 189, 107, 206, 10, 239, 140, 164, 136, 56, 166, 226, 252, 197, 126, 103, 185, 197, 232, 134, 12, 95, 11, 233, 218, 190, 197, 62, 69, 78, 24, 160, 161, 116, 196, 136, 136, 162, 100, 136, 17, 91, 45, 201, 241, 223, 165, 45]) + }; + + return importKeys(pkcs8, spki, sizes) + .then(function(results) { + publicKeys = results.publicKeys; + privateKeys = results.privateKeys; + ecdsaKeyPairs = results.ecdsaKeyPairs; + noDeriveKeyKeys = results.noDeriveKeyKeys; + + Object.keys(sizes).forEach(function(namedCurve) { + // Basic success case + promise_test(function(test) { + return subtle.deriveKey({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_true(equalBuffers(exportedKey, derivations[namedCurve], 8 * exportedKey.length), "Derived correct key"); + }, function(err) { + assert_unreached("deriveKey failed with error " + err.name + ": " + err.message); + }); + }, namedCurve + " good parameters"); + + // Case insensitivity check + promise_test(function(test) { + return subtle.deriveKey({name: "EcDh", public: publicKeys[namedCurve]}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_true(equalBuffers(exportedKey, derivations[namedCurve], 8 * exportedKey.length), "Derived correct key"); + }, function(err) { + assert_unreached("deriveKey failed with error " + err.name + ": " + err.message); + }); + }, namedCurve + " mixed case parameters"); + // Errors to test: + + // - missing public property TypeError + promise_test(function(test) { + return subtle.deriveKey({name: "ECDH"}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with TypeError"); + }, function(err) { + assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " missing public curve"); + + // - Non CryptoKey public property TypeError + promise_test(function(test) { + return subtle.deriveKey({name: "ECDH", public: {message: "Not a CryptoKey"}}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with TypeError"); + }, function(err) { + assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " public property of algorithm is not a CryptoKey"); + + // - wrong named curve + promise_test(function(test) { + publicKey = publicKeys["P-256"]; + if (namedCurve === "P-256") { + publicKey = publicKeys["P-384"]; + } + return subtle.deriveKey({name: "ECDH", public: publicKey}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " mismatched curves"); + + // - not ECDH public property InvalidAccessError + promise_test(function(test) { + return subtle.deriveKey({name: "ECDH", public: ecdsaKeyPairs[namedCurve].publicKey}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " public property of algorithm is not an ECDSA public key"); + + // - No deriveKey usage in baseKey InvalidAccessError + promise_test(function(test) { + return subtle.deriveKey({name: "ECDH", public: publicKeys[namedCurve]}, noDeriveKeyKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " no deriveKey usage for base key"); + + // - Use public key for baseKey InvalidAccessError + promise_test(function(test) { + return subtle.deriveKey({name: "ECDH", public: publicKeys[namedCurve]}, publicKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " base key is not a private key"); + + // - Use private key for public property InvalidAccessError + promise_test(function(test) { + return subtle.deriveKey({name: "ECDH", public: privateKeys[namedCurve]}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }, namedCurve + " public property value is a private key"); + + // - Use secret key for public property InvalidAccessError + promise_test(function(test) { + return subtle.generateKey({name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) + .then(function(secretKey) { + return subtle.deriveKey({name: "ECDH", public: secretKey}, privateKeys[namedCurve], {name: "AES-CBC", length: 256}, true, ["sign", "verify"]) + .then(function(key) {return crypto.subtle.exportKey("raw", key);}) + .then(function(exportedKey) { + assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); + }); + }); + }, namedCurve + " public property value is a secret key"); + }); + }); + + function importKeys(pkcs8, spki, sizes) { + var privateKeys = {}; + var publicKeys = {}; + var ecdsaKeyPairs = {}; + var noDeriveKeyKeys = {}; + + var promises = []; + Object.keys(pkcs8).forEach(function(namedCurve) { + var operation = subtle.importKey("pkcs8", pkcs8[namedCurve], + {name: "ECDH", namedCurve: namedCurve}, + false, ["deriveBits", "deriveKey"]) + .then(function(key) { + privateKeys[namedCurve] = key; + }, function (err) { + privateKeys[namedCurve] = null; + }); + promises.push(operation); + }); + Object.keys(pkcs8).forEach(function(namedCurve) { + var operation = subtle.importKey("pkcs8", pkcs8[namedCurve], + {name: "ECDH", namedCurve: namedCurve}, + false, ["deriveBits"]) + .then(function(key) { + noDeriveKeyKeys[namedCurve] = key; + }, function (err) { + noDeriveKeyKeys[namedCurve] = null; + }); + promises.push(operation); + }); + Object.keys(spki).forEach(function(namedCurve) { + var operation = subtle.importKey("spki", spki[namedCurve], + {name: "ECDH", namedCurve: namedCurve}, + false, []) + .then(function(key) { + publicKeys[namedCurve] = key; + }, function (err) { + publicKeys[namedCurve] = null; + }); + promises.push(operation); + }); + Object.keys(sizes).forEach(function(namedCurve) { + var operation = subtle.generateKey({name: "ECDSA", namedCurve: namedCurve}, false, ["sign", "verify"]) + .then(function(keyPair) { + ecdsaKeyPairs[namedCurve] = keyPair; + }, function (err) { + ecdsaKeyPairs[namedCurve] = null; + }); + promises.push(operation); + }); + + return Promise.all(promises) + .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, ecdsaKeyPairs: ecdsaKeyPairs, noDeriveKeyKeys: noDeriveKeyKeys}}); + } + + // Compares two ArrayBuffer or ArrayBufferView objects. If bitCount is + // omitted, the two values must be the same length and have the same contents + // in every byte. If bitCount is included, only that leading number of bits + // have to match. + function equalBuffers(a, b, bitCount) { + var remainder; + + if (typeof bitCount === "undefined" && a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + var length = a.byteLength; + if (typeof bitCount !== "undefined") { + length = Math.floor(bitCount / 8); + } + + for (var i=0; i<length; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + if (typeof bitCount !== "undefined") { + remainder = bitCount % 8; + return aBytes[length] >> (8 - remainder) === bBytes[length] >> (8 - remainder); + } + + return true; + } + +} diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/hkdf.https.any.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/hkdf.https.any.js new file mode 100644 index 0000000000..02492c3741 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/hkdf.https.any.js @@ -0,0 +1,15 @@ +// META: title=WebCryptoAPI: deriveBits() and deriveKey() Using HKDF +// META: variant=?1-1000 +// META: variant=?1001-2000 +// META: variant=?2001-3000 +// META: variant=?3001-last +// META: script=/common/subset-tests.js +// META: script=hkdf_vectors.js +// META: script=hkdf.js + +// Define subtests from a `promise_test` to ensure the harness does not +// complete before the subtests are available. `explicit_done` cannot be used +// for this purpose because the global `done` function is automatically invoked +// by the WPT infrastructure in dedicated worker tests defined using the +// "multi-global" pattern. +promise_test(define_tests, 'setup - define tests'); diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/hkdf.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/hkdf.js new file mode 100644 index 0000000000..2bb5853347 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/hkdf.js @@ -0,0 +1,305 @@ + +function define_tests() { + // May want to test prefixed implementations. + var subtle = self.crypto.subtle; + + // hkdf2_vectors sets up test data with the correct derivations for each + // test case. + var testData = getTestData(); + var derivedKeys = testData.derivedKeys; + var salts = testData.salts; + var derivations = testData.derivations; + var infos = testData.infos; + + // What kinds of keys can be created with deriveKey? The following: + var derivedKeyTypes = testData.derivedKeyTypes; + + return setUpBaseKeys(derivedKeys) + .then(function(allKeys) { + // We get several kinds of base keys. Normal ones that can be used for + // derivation operations, ones that lack the deriveBits usage, ones + // that lack the deriveKeys usage, and one key that is for the wrong + // algorithm (not HKDF in this case). + var baseKeys = allKeys.baseKeys; + var noBits = allKeys.noBits; + var noKey = allKeys.noKey; + var wrongKey = allKeys.wrongKey; + + // Test each combination of derivedKey size, salt size, hash function, + // and number of iterations. The derivations object is structured in + // that way, so navigate it to run tests and compare with correct results. + Object.keys(derivations).forEach(function(derivedKeySize) { + Object.keys(derivations[derivedKeySize]).forEach(function(saltSize) { + Object.keys(derivations[derivedKeySize][saltSize]).forEach(function(hashName) { + Object.keys(derivations[derivedKeySize][saltSize][hashName]).forEach(function(infoSize) { + var testName = derivedKeySize + " derivedKey, " + saltSize + " salt, " + hashName + ", with " + infoSize + " info"; + var algorithm = {name: "HKDF", salt: salts[saltSize], info: infos[infoSize], hash: hashName}; + + // Check for correct deriveBits result + subsetTest(promise_test, function(test) { + return subtle.deriveBits(algorithm, baseKeys[derivedKeySize], 256) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[derivedKeySize][saltSize][hashName][infoSize]), "Derived correct key"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, testName); + + // 0 length (OperationError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits(algorithm, baseKeys[derivedKeySize], 0) + .then(function(derivation) { + assert_equals(derivation.byteLength, 0, "Derived correctly empty key"); + }, function(err) { + assert_equals(err.name, "OperationError", "deriveBits with 0 length correctly threw OperationError: " + err.message); + }); + }, testName + " with 0 length"); + + // Check for correct deriveKey results for every kind of + // key that can be created by the deriveKeys operation. + derivedKeyTypes.forEach(function(derivedKeyType) { + var testName = "Derived key of type "; + Object.keys(derivedKeyType.algorithm).forEach(function(prop) { + testName += prop + ": " + derivedKeyType.algorithm[prop] + " "; + }); + testName += " using " + derivedKeySize + " derivedKey, " + saltSize + " salt, " + hashName + ", with " + infoSize + " info"; + + // Test the particular key derivation. + subsetTest(promise_test, function(test) { + return subtle.deriveKey(algorithm, baseKeys[derivedKeySize], derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(key) { + // Need to export the key to see that the correct bits were set. + return subtle.exportKey("raw", key) + .then(function(buffer) { + assert_true(equalBuffers(buffer, derivations[derivedKeySize][saltSize][hashName][infoSize].slice(0, derivedKeyType.algorithm.length/8)), "Exported key matches correct value"); + }, function(err) { + assert_unreached("Exporting derived key failed with error " + err.name + ": " + err.message); + }); + }, function(err) { + assert_unreached("deriveKey failed with error " + err.name + ": " + err.message); + + }); + }, testName); + + // Test various error conditions for deriveKey: + + // - illegal name for hash algorithm (NotSupportedError) + var badHash = hashName.substring(0, 3) + hashName.substring(4); + subsetTest(promise_test, function(test) { + var badAlgorithm = {name: "HKDF", salt: salts[saltSize], hash: badHash, info: algorithm.info}; + return subtle.deriveKey(badAlgorithm, baseKeys[derivedKeySize], derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(key) { + assert_unreached("bad hash name should have thrown an NotSupportedError"); + }, function(err) { + assert_equals(err.name, "NotSupportedError", "deriveKey with bad hash name correctly threw NotSupportedError: " + err.message); + }); + }, testName + " with bad hash name " + badHash); + + // - baseKey usages missing "deriveKey" (InvalidAccessError) + subsetTest(promise_test, function(test) { + return subtle.deriveKey(algorithm, noKey[derivedKeySize], derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(key) { + assert_unreached("missing deriveKey usage should have thrown an InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "deriveKey with missing deriveKey usage correctly threw InvalidAccessError: " + err.message); + }); + }, testName + " with missing deriveKey usage"); + + // - baseKey algorithm does not match HKDF (InvalidAccessError) + subsetTest(promise_test, function(test) { + return subtle.deriveKey(algorithm, wrongKey, derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(key) { + assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "deriveKey with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message); + }); + }, testName + " with wrong (ECDH) key"); + + }); + + // Test various error conditions for deriveBits below: + + // missing salt (TypeError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "HKDF", info: infos[infoSize], hash: hashName}, baseKeys[derivedKeySize], 0) + .then(function(derivation) { + assert_equals(derivation.byteLength, 0, "Derived even with missing salt"); + }, function(err) { + assert_equals(err.name, "TypeError", "deriveBits missing salt correctly threw OperationError: " + err.message); + }); + }, testName + " with missing salt"); + + // missing info (TypeError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "HKDF", salt: salts[saltSize], hash: hashName}, baseKeys[derivedKeySize], 0) + .then(function(derivation) { + assert_equals(derivation.byteLength, 0, "Derived even with missing info"); + }, function(err) { + assert_equals(err.name, "TypeError", "deriveBits missing info correctly threw OperationError: " + err.message); + }); + }, testName + " with missing info"); + + // length null (OperationError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits(algorithm, baseKeys[derivedKeySize], null) + .then(function(derivation) { + assert_unreached("null length should have thrown an OperationError"); + }, function(err) { + assert_equals(err.name, "OperationError", "deriveBits with null length correctly threw OperationError: " + err.message); + }); + }, testName + " with null length"); + + // length not multiple of 8 (OperationError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits(algorithm, baseKeys[derivedKeySize], 44) + .then(function(derivation) { + assert_unreached("non-multiple of 8 length should have thrown an OperationError"); + }, function(err) { + assert_equals(err.name, "OperationError", "deriveBits with non-multiple of 8 length correctly threw OperationError: " + err.message); + }); + }, testName + " with non-multiple of 8 length"); + + // - illegal name for hash algorithm (NotSupportedError) + var badHash = hashName.substring(0, 3) + hashName.substring(4); + subsetTest(promise_test, function(test) { + var badAlgorithm = {name: "HKDF", salt: salts[saltSize], hash: badHash, info: algorithm.info}; + return subtle.deriveBits(badAlgorithm, baseKeys[derivedKeySize], 256) + .then(function(derivation) { + assert_unreached("bad hash name should have thrown an NotSupportedError"); + }, function(err) { + assert_equals(err.name, "NotSupportedError", "deriveBits with bad hash name correctly threw NotSupportedError: " + err.message); + }); + }, testName + " with bad hash name " + badHash); + + // - baseKey usages missing "deriveBits" (InvalidAccessError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits(algorithm, noBits[derivedKeySize], 256) + .then(function(derivation) { + assert_unreached("missing deriveBits usage should have thrown an InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "deriveBits with missing deriveBits usage correctly threw InvalidAccessError: " + err.message); + }); + }, testName + " with missing deriveBits usage"); + + // - baseKey algorithm does not match HKDF (InvalidAccessError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits(algorithm, wrongKey, 256) + .then(function(derivation) { + assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "deriveBits with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message); + }); + }, testName + " with wrong (ECDH) key"); + }); + }); + + // - legal algorithm name but not digest one (e.g., PBKDF2) (NotSupportedError) + var nonDigestHash = "PBKDF2"; + Object.keys(infos).forEach(function(infoSize) { + var testName = derivedKeySize + " derivedKey, " + saltSize + " salt, " + nonDigestHash + ", with " + infoSize + " info"; + var algorithm = {name: "HKDF", salt: salts[saltSize], hash: nonDigestHash}; + if (infoSize !== "missing") { + algorithm.info = infos[infoSize]; + } + + subsetTest(promise_test, function(test) { + return subtle.deriveBits(algorithm, baseKeys[derivedKeySize], 256) + .then(function(derivation) { + assert_unreached("non-digest algorithm should have thrown an NotSupportedError"); + }, function(err) { + assert_equals(err.name, "NotSupportedError", "deriveBits with non-digest algorithm correctly threw NotSupportedError: " + err.message); + }); + }, testName + " with non-digest algorithm " + nonDigestHash); + + derivedKeyTypes.forEach(function(derivedKeyType) { + var testName = "Derived key of type "; + Object.keys(derivedKeyType.algorithm).forEach(function(prop) { + testName += prop + ": " + derivedKeyType.algorithm[prop] + " "; + }); + testName += " using " + derivedKeySize + " derivedKey, " + saltSize + " salt, " + nonDigestHash + ", with " + infoSize + " info"; + + subsetTest(promise_test, function(test) { + return subtle.deriveKey(algorithm, baseKeys[derivedKeySize], derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(derivation) { + assert_unreached("non-digest algorithm should have thrown an NotSupportedError"); + }, function(err) { + assert_equals(err.name, "NotSupportedError", "derivekey with non-digest algorithm correctly threw NotSupportedError: " + err.message); + }); + }, testName); + }); + + }); + + }); + }); + }); + + // Deriving bits and keys requires starting with a base key, which is created + // by importing a derivedKey. setUpBaseKeys returns a promise that yields the + // necessary base keys. + function setUpBaseKeys(derivedKeys) { + var promises = []; + + var baseKeys = {}; + var noBits = {}; + var noKey = {}; + var wrongKey = null; + + Object.keys(derivedKeys).forEach(function(derivedKeySize) { + var promise = subtle.importKey("raw", derivedKeys[derivedKeySize], {name: "HKDF"}, false, ["deriveKey", "deriveBits"]) + .then(function(baseKey) { + baseKeys[derivedKeySize] = baseKey; + }, function(err) { + baseKeys[derivedKeySize] = null; + }); + promises.push(promise); + + promise = subtle.importKey("raw", derivedKeys[derivedKeySize], {name: "HKDF"}, false, ["deriveBits"]) + .then(function(baseKey) { + noKey[derivedKeySize] = baseKey; + }, function(err) { + noKey[derivedKeySize] = null; + }); + promises.push(promise); + + promise = subtle.importKey("raw", derivedKeys[derivedKeySize], {name: "HKDF"}, false, ["deriveKey"]) + .then(function(baseKey) { + noBits[derivedKeySize] = baseKey; + }, function(err) { + noBits[derivedKeySize] = null; + }); + promises.push(promise); + }); + + var promise = subtle.generateKey({name: "ECDH", namedCurve: "P-256"}, false, ["deriveKey", "deriveBits"]) + .then(function(baseKey) { + wrongKey = baseKey.privateKey; + }, function(err) { + wrongKey = null; + }); + promises.push(promise); + + + return Promise.all(promises).then(function() { + return {baseKeys: baseKeys, noBits: noBits, noKey: noKey, wrongKey: wrongKey}; + }); + } + + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + +} diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/hkdf_vectors.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/hkdf_vectors.js new file mode 100644 index 0000000000..ef448ec5f2 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/hkdf_vectors.js @@ -0,0 +1,184 @@ + +function getTestData() { + + // deriveBits and deriveKey take as input: + // - derivedKey (actually, a CryptoKey representing a derivedKey) + // - salt (BufferSource) + // - hash (which one to use) + // - iterations (how many times to use it) + + // deriveBits also takes a length. deriveKey uses the length of the output key + // - length is the number of bits, NOT octets, but it MUST be a multiple of 8 + // - note that result of length(n) is first n bits of length(m) if m>n + + // Variations to test: + // - empty, short, and fairly long derivedKey + // - empty, short, and fairly long salt + // - SHA-1, SHA-256, SHA-384, SHA-512 hash + // - 1, 1000, and 100000 million iterations + + // Test cases to generate: 3 * 3 * 4 * 3 = 108 + + // Error conditions to test: + // - length null (OperationError) + // - length not a multiple of 8 (OperationError) + // - illegal name for hash algorithm (NotSupportedError) + // - legal algorithm name but not digest one (e.g., AES-CBC) (NotSupportedError) + // - baseKey usages missing "deriveBits" (InvalidAccessError) + // - baseKey algorithm does not match HKDF (InvalidAccessError) + // - 0 iterations + + var derivedKeyTypes = [ + {algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CBC", length: 192}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CBC", length: 256}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CTR", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CTR", length: 192}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CTR", length: 256}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-GCM", length: 192}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-GCM", length: 256}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-KW", length: 128}, usages: ["wrapKey", "unwrapKey"]}, + {algorithm: {name: "AES-KW", length: 192}, usages: ["wrapKey", "unwrapKey"]}, + {algorithm: {name: "AES-KW", length: 256}, usages: ["wrapKey", "unwrapKey"]}, + {algorithm: {name: "HMAC", hash: "SHA-1", length: 256}, usages: ["sign", "verify"]}, + {algorithm: {name: "HMAC", hash: "SHA-256", length: 256}, usages: ["sign", "verify"]}, + {algorithm: {name: "HMAC", hash: "SHA-384", length: 256}, usages: ["sign", "verify"]}, + {algorithm: {name: "HMAC", hash: "SHA-512", length: 256}, usages: ["sign", "verify"]} + ]; + + var derivedKeys = { + "short": new Uint8Array([80, 64, 115, 115, 119, 48, 114, 100]), + "long": new Uint8Array([85, 115, 101, 114, 115, 32, 115, 104, 111, 117, 108, 100, 32, 112, 105, 99, 107, 32, 108, 111, 110, 103, 32, 112, 97, 115, 115, 112, 104, 114, 97, 115, 101, 115, 32, 40, 110, 111, 116, 32, 117, 115, 101, 32, 115, 104, 111, 114, 116, 32, 112, 97, 115, 115, 119, 111, 114, 100, 115, 41, 33]), + "empty": new Uint8Array([]) + }; + + var salts = { + "normal": new Uint8Array([83, 111, 100, 105, 117, 109, 32, 67, 104, 108, 111, 114, 105, 100, 101, 32, 99, 111, 109, 112, 111, 117, 110, 100]), + "empty": new Uint8Array([]) + }; + + var infos = { + "normal": new Uint8Array([72, 75, 68, 70, 32, 101, 120, 116, 114, 97, 32, 105, 110, 102, 111]), + "empty": new Uint8Array([]) + }; + + var derivations = { + "short": { + "normal": { + "SHA-384": { + "normal": new Uint8Array([25, 186, 116, 54, 142, 107, 153, 51, 144, 242, 127, 233, 167, 208, 43, 195, 56, 23, 63, 114, 190, 113, 161, 159, 199, 68, 252, 219, 63, 212, 184, 75]), + "empty": new Uint8Array([151, 96, 31, 78, 12, 83, 165, 211, 243, 162, 129, 0, 153, 188, 104, 32, 236, 80, 8, 52, 52, 118, 155, 89, 252, 36, 164, 23, 169, 84, 55, 52]) + }, + "SHA-512": { + "normal": new Uint8Array([75, 189, 109, 178, 67, 95, 182, 150, 21, 127, 96, 137, 201, 119, 195, 199, 63, 62, 172, 94, 243, 221, 107, 170, 230, 4, 203, 83, 191, 187, 21, 62]), + "empty": new Uint8Array([47, 49, 87, 231, 254, 12, 16, 176, 18, 152, 200, 240, 136, 106, 144, 237, 207, 128, 171, 222, 245, 219, 193, 223, 43, 20, 130, 83, 43, 82, 185, 52]) + }, + "SHA-1": { + "normal": new Uint8Array([5, 173, 34, 237, 33, 56, 201, 96, 14, 77, 158, 39, 37, 222, 211, 1, 245, 210, 135, 251, 251, 87, 2, 249, 153, 188, 101, 54, 211, 237, 239, 152]), + "empty": new Uint8Array([213, 27, 111, 183, 229, 153, 202, 48, 197, 238, 38, 69, 147, 228, 184, 95, 34, 32, 199, 195, 171, 0, 49, 87, 191, 248, 203, 79, 54, 156, 117, 96]) + }, + "SHA-256": { + "normal": new Uint8Array([42, 245, 144, 30, 40, 132, 156, 40, 68, 56, 87, 56, 106, 161, 172, 59, 177, 39, 233, 38, 49, 193, 192, 81, 72, 45, 102, 144, 148, 23, 114, 180]), + "empty": new Uint8Array([158, 75, 113, 144, 51, 116, 33, 1, 233, 15, 26, 214, 30, 47, 243, 180, 37, 104, 99, 102, 114, 150, 215, 67, 137, 241, 240, 42, 242, 196, 230, 166]) + } + }, + "empty": { + "SHA-384": { + "normal": new Uint8Array([251, 72, 47, 242, 44, 79, 141, 70, 108, 77, 254, 110, 41, 242, 204, 46, 205, 171, 245, 136, 67, 40, 251, 240, 138, 115, 143, 217, 69, 241, 102, 203]), + "empty": new Uint8Array([30, 2, 60, 23, 179, 64, 83, 60, 234, 239, 57, 35, 12, 184, 179, 187, 219, 246, 99, 161, 61, 96, 117, 208, 221, 50, 108, 4, 148, 120, 251, 165]) + }, + "SHA-512": { + "normal": new Uint8Array([241, 123, 91, 220, 216, 215, 211, 212, 96, 16, 54, 161, 148, 54, 49, 125, 22, 68, 249, 164, 224, 149, 110, 252, 14, 55, 43, 131, 172, 218, 207, 219]), + "empty": new Uint8Array([199, 180, 116, 148, 47, 49, 248, 63, 175, 93, 20, 115, 24, 2, 177, 189, 73, 71, 133, 73, 203, 58, 143, 61, 191, 237, 196, 211, 32, 156, 245, 182]) + }, + "SHA-1": { + "normal": new Uint8Array([193, 38, 241, 230, 242, 90, 157, 228, 44, 247, 212, 39, 5, 154, 82, 237, 150, 1, 242, 154, 88, 21, 203, 251, 198, 75, 199, 246, 104, 198, 163, 65]), + "empty": new Uint8Array([50, 21, 195, 240, 141, 231, 5, 73, 176, 81, 183, 3, 55, 69, 168, 24, 79, 140, 186, 166, 177, 115, 83, 48, 210, 188, 182, 177, 111, 70, 66, 239]) + }, + "SHA-256": { + "normal": new Uint8Array([115, 60, 139, 107, 207, 172, 135, 92, 127, 8, 152, 42, 110, 63, 251, 86, 10, 206, 166, 241, 101, 71, 110, 184, 52, 96, 185, 53, 62, 212, 29, 254]), + "empty": new Uint8Array([200, 225, 39, 116, 19, 83, 5, 201, 20, 127, 44, 196, 118, 110, 94, 173, 37, 216, 244, 87, 185, 161, 149, 61, 82, 103, 115, 97, 206, 213, 88, 251]) + } + } + }, + "long": { + "normal": { + "SHA-384": { + "normal": new Uint8Array([249, 21, 113, 181, 33, 247, 238, 241, 62, 87, 58, 164, 99, 120, 101, 158, 243, 183, 243, 111, 253, 209, 187, 5, 93, 178, 205, 119, 210, 96, 196, 103]), + "empty": new Uint8Array([104, 175, 28, 44, 246, 185, 55, 13, 32, 84, 52, 71, 152, 189, 187, 24, 71, 204, 244, 7, 183, 101, 43, 121, 61, 209, 54, 212, 100, 14, 3, 72]) + }, + "SHA-512": { + "normal": new Uint8Array([113, 10, 174, 47, 223, 136, 158, 69, 254, 15, 185, 149, 178, 194, 107, 51, 235, 152, 134, 80, 236, 15, 174, 241, 103, 2, 138, 122, 108, 203, 54, 56]), + "empty": new Uint8Array([229, 222, 86, 128, 129, 199, 30, 86, 39, 80, 130, 152, 113, 195, 66, 117, 129, 4, 118, 94, 214, 243, 6, 240, 97, 60, 157, 75, 179, 54, 242, 170]) + }, + "SHA-1": { + "normal": new Uint8Array([127, 149, 126, 220, 188, 227, 203, 11, 112, 86, 110, 30, 182, 14, 253, 30, 64, 90, 19, 48, 76, 102, 29, 54, 99, 119, 129, 9, 191, 6, 137, 156]), + "empty": new Uint8Array([48, 98, 243, 207, 26, 115, 11, 156, 239, 81, 240, 44, 29, 250, 200, 94, 217, 30, 75, 0, 101, 235, 80, 202, 159, 216, 176, 16, 126, 114, 135, 51]) + }, + "SHA-256": { + "normal": new Uint8Array([49, 183, 214, 133, 48, 168, 99, 231, 23, 192, 129, 202, 105, 23, 182, 134, 80, 179, 221, 154, 41, 243, 6, 6, 226, 202, 209, 153, 190, 193, 77, 19]), + "empty": new Uint8Array([229, 121, 209, 249, 231, 240, 142, 111, 153, 15, 252, 252, 206, 30, 210, 1, 197, 227, 126, 98, 205, 246, 6, 240, 186, 74, 202, 128, 66, 127, 188, 68]) + } + }, + "empty": { + "SHA-384": { + "normal": new Uint8Array([97, 158, 182, 249, 40, 115, 149, 187, 213, 237, 106, 103, 201, 104, 70, 90, 216, 43, 108, 85, 159, 60, 56, 182, 4, 187, 176, 143, 88, 50, 11, 3]), + "empty": new Uint8Array([255, 68, 123, 66, 61, 131, 254, 118, 131, 108, 50, 51, 114, 40, 181, 107, 91, 217, 191, 104, 213, 142, 125, 202, 75, 124, 202, 132, 42, 69, 225, 26]) + }, + "SHA-512": { + "normal": new Uint8Array([19, 62, 138, 127, 127, 244, 51, 105, 12, 200, 132, 50, 194, 163, 56, 194, 119, 229, 193, 55, 86, 255, 135, 143, 70, 117, 63, 230, 165, 100, 227, 229]), + "empty": new Uint8Array([222, 84, 247, 238, 200, 12, 156, 198, 109, 52, 159, 201, 135, 248, 13, 70, 29, 178, 239, 79, 244, 225, 133, 5, 210, 139, 216, 12, 180, 44, 125, 118]) + }, + "SHA-1": { + "normal": new Uint8Array([173, 185, 60, 219, 206, 121, 183, 213, 17, 89, 182, 192, 19, 26, 43, 98, 242, 56, 40, 210, 106, 205, 104, 94, 52, 192, 101, 53, 230, 247, 116, 150]), + "empty": new Uint8Array([71, 113, 13, 42, 117, 7, 224, 90, 29, 220, 200, 122, 124, 47, 144, 97, 119, 162, 102, 239, 185, 230, 34, 81, 12, 204, 179, 113, 60, 208, 141, 88]) + }, + "SHA-256": { + "normal": new Uint8Array([164, 1, 215, 201, 21, 138, 41, 229, 199, 25, 58, 185, 115, 15, 7, 72, 133, 28, 197, 186, 173, 180, 44, 173, 2, 75, 98, 144, 254, 33, 52, 54]), + "empty": new Uint8Array([180, 247, 231, 85, 118, 116, 213, 1, 203, 251, 192, 20, 138, 216, 0, 192, 117, 1, 137, 254, 41, 90, 42, 202, 94, 27, 244, 18, 44, 133, 237, 249]) + } + } + }, + "empty": { + "normal": { + "SHA-384": { + "normal": new Uint8Array([106, 134, 50, 228, 134, 137, 157, 194, 100, 241, 161, 249, 32, 89, 63, 40, 128, 128, 78, 14, 26, 218, 207, 148, 235, 78, 213, 229, 248, 61, 13, 18]), + "empty": new Uint8Array([234, 80, 18, 254, 181, 135, 81, 213, 188, 142, 182, 78, 13, 234, 205, 89, 126, 215, 16, 201, 243, 82, 88, 174, 107, 154, 8, 122, 237, 7, 37, 174]) + }, + "SHA-512": { + "normal": new Uint8Array([199, 151, 225, 209, 242, 202, 183, 242, 138, 95, 67, 69, 92, 16, 89, 127, 148, 51, 133, 237, 251, 66, 140, 254, 43, 152, 190, 212, 169, 85, 215, 161]), + "empty": new Uint8Array([224, 140, 220, 196, 197, 166, 170, 121, 157, 134, 188, 3, 169, 84, 117, 39, 110, 187, 128, 29, 154, 222, 1, 110, 20, 168, 250, 91, 100, 5, 22, 81]) + }, + "SHA-1": { + "normal": new Uint8Array([171, 103, 158, 103, 188, 180, 48, 95, 238, 66, 239, 148, 14, 80, 156, 221, 212, 6, 227, 73, 143, 133, 116, 24, 169, 121, 171, 57, 207, 49, 95, 81]), + "empty": new Uint8Array([254, 66, 33, 135, 24, 140, 134, 54, 211, 109, 170, 213, 142, 242, 132, 49, 164, 51, 191, 15, 239, 114, 209, 202, 231, 53, 160, 75, 219, 190, 185, 211]) + }, + "SHA-256": { + "normal": new Uint8Array([223, 146, 185, 169, 250, 156, 1, 184, 152, 206, 234, 161, 49, 52, 131, 46, 49, 203, 28, 8, 29, 22, 165, 35, 92, 105, 216, 86, 81, 227, 23, 172]), + "empty": new Uint8Array([230, 13, 67, 43, 6, 238, 136, 157, 250, 183, 41, 154, 32, 236, 35, 105, 117, 49, 209, 25, 252, 247, 102, 208, 152, 141, 10, 203, 12, 0, 199, 247]) + } + }, + "empty": { + "SHA-384": { + "normal": new Uint8Array([234, 203, 157, 102, 112, 255, 59, 25, 4, 119, 154, 65, 145, 1, 177, 255, 170, 189, 109, 101, 16, 189, 80, 133, 104, 1, 116, 106, 135, 31, 123, 49]), + "empty": new Uint8Array([71, 12, 198, 83, 135, 202, 74, 16, 199, 166, 138, 59, 81, 72, 200, 229, 19, 218, 166, 49, 1, 0, 7, 57, 196, 198, 101, 155, 134, 17, 136, 132]) + }, + "SHA-512": { + "normal": new Uint8Array([87, 3, 145, 116, 241, 111, 84, 24, 168, 104, 86, 218, 235, 119, 246, 157, 75, 77, 80, 0, 51, 75, 109, 209, 244, 244, 179, 231, 179, 220, 185, 211]), + "empty": new Uint8Array([157, 115, 201, 142, 121, 30, 128, 235, 229, 180, 203, 69, 105, 58, 163, 47, 221, 68, 181, 250, 62, 218, 179, 236, 130, 249, 208, 244, 214, 105, 5, 226]) + }, + "SHA-1": { + "normal": new Uint8Array([161, 189, 216, 195, 50, 198, 70, 74, 75, 182, 162, 242, 49, 174, 201, 164, 68, 35, 126, 171, 224, 77, 47, 85, 242, 171, 37, 212, 12, 84, 235, 238]), + "empty": new Uint8Array([136, 95, 192, 41, 179, 34, 75, 137, 110, 9, 224, 187, 229, 235, 52, 126, 197, 158, 104, 39, 200, 232, 87, 179, 148, 245, 79, 244, 155, 136, 168, 246]) + }, + "SHA-256": { + "normal": new Uint8Array([183, 184, 110, 66, 42, 209, 200, 165, 113, 253, 165, 40, 218, 22, 160, 102, 244, 36, 134, 221, 64, 86, 121, 47, 217, 51, 98, 8, 142, 93, 212, 194]), + "empty": new Uint8Array([235, 112, 240, 29, 237, 233, 175, 175, 164, 73, 238, 225, 177, 40, 101, 4, 225, 246, 35, 136, 179, 247, 221, 79, 149, 102, 151, 176, 232, 40, 254, 24]) + } + } + } + }; + + return {derivedKeys: derivedKeys, salts: salts, derivations: derivations, derivedKeyTypes: derivedKeyTypes, infos: infos}; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/pbkdf2.https.any.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/pbkdf2.https.any.js new file mode 100644 index 0000000000..2efbe523f8 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/pbkdf2.https.any.js @@ -0,0 +1,21 @@ +// META: title=WebCryptoAPI: deriveBits() and deriveKey() Using PBKDF2 +// META: timeout=long +// META: variant=?1-1000 +// META: variant=?1001-2000 +// META: variant=?2001-3000 +// META: variant=?3001-4000 +// META: variant=?4001-5000 +// META: variant=?5001-6000 +// META: variant=?6001-7000 +// META: variant=?7001-8000 +// META: variant=?8001-last +// META: script=/common/subset-tests.js +// META: script=pbkdf2_vectors.js +// META: script=pbkdf2.js + +// Define subtests from a `promise_test` to ensure the harness does not +// complete before the subtests are available. `explicit_done` cannot be used +// for this purpose because the global `done` function is automatically invoked +// by the WPT infrastructure in dedicated worker tests defined using the +// "multi-global" pattern. +promise_test(define_tests, 'setup - define tests'); diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/pbkdf2.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/pbkdf2.js new file mode 100644 index 0000000000..0403f382e1 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/pbkdf2.js @@ -0,0 +1,302 @@ +function define_tests() { + // May want to test prefixed implementations. + var subtle = self.crypto.subtle; + + // pbkdf2_vectors sets up test data with the correct derivations for each + // test case. + var testData = getTestData(); + var passwords = testData.passwords; + var salts = testData.salts; + var derivations = testData.derivations; + + // What kinds of keys can be created with deriveKey? The following: + var derivedKeyTypes = testData.derivedKeyTypes; + + return setUpBaseKeys(passwords) + .then(function(allKeys) { + // We get several kinds of base keys. Normal ones that can be used for + // derivation operations, ones that lack the deriveBits usage, ones + // that lack the deriveKeys usage, and one key that is for the wrong + // algorithm (not PBKDF2 in this case). + var baseKeys = allKeys.baseKeys; + var noBits = allKeys.noBits; + var noKey = allKeys.noKey; + var wrongKey = allKeys.wrongKey; + + // Test each combination of password size, salt size, hash function, + // and number of iterations. The derivations object is structured in + // that way, so navigate it to run tests and compare with correct results. + Object.keys(derivations).forEach(function(passwordSize) { + Object.keys(derivations[passwordSize]).forEach(function(saltSize) { + Object.keys(derivations[passwordSize][saltSize]).forEach(function(hashName) { + Object.keys(derivations[passwordSize][saltSize][hashName]).forEach(function(iterations) { + var testName = passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations"; + + // Check for correct deriveBits result + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256) + .then(function(derivation) { + assert_true(equalBuffers(derivation, derivations[passwordSize][saltSize][hashName][iterations]), "Derived correct key"); + }, function(err) { + assert_unreached("deriveBits failed with error " + err.name + ": " + err.message); + }); + }, testName); + + // Check for correct deriveKey results for every kind of + // key that can be created by the deriveKeys operation. + derivedKeyTypes.forEach(function(derivedKeyType) { + var testName = "Derived key of type "; + Object.keys(derivedKeyType.algorithm).forEach(function(prop) { + testName += prop + ": " + derivedKeyType.algorithm[prop] + " "; + }); + testName += " using " + passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations"; + + // Test the particular key derivation. + subsetTest(promise_test, function(test) { + return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(key) { + // Need to export the key to see that the correct bits were set. + return subtle.exportKey("raw", key) + .then(function(buffer) { + assert_true(equalBuffers(buffer, derivations[passwordSize][saltSize][hashName][iterations].slice(0, derivedKeyType.algorithm.length/8)), "Exported key matches correct value"); + }, function(err) { + assert_unreached("Exporting derived key failed with error " + err.name + ": " + err.message); + }); + }, function(err) { + assert_unreached("deriveKey failed with error " + err.name + ": " + err.message); + + }); + }, testName); + + // Test various error conditions for deriveKey: + + // - illegal name for hash algorithm (NotSupportedError) + var badHash = hashName.substring(0, 3) + hashName.substring(4); + subsetTest(promise_test, function(test) { + return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: badHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(key) { + assert_unreached("bad hash name should have thrown an NotSupportedError"); + }, function(err) { + assert_equals(err.name, "NotSupportedError", "deriveKey with bad hash name correctly threw NotSupportedError: " + err.message); + }); + }, testName + " with bad hash name " + badHash); + + // - baseKey usages missing "deriveKey" (InvalidAccessError) + subsetTest(promise_test, function(test) { + return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, noKey[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(key) { + assert_unreached("missing deriveKey usage should have thrown an InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "deriveKey with missing deriveKey usage correctly threw InvalidAccessError: " + err.message); + }); + }, testName + " with missing deriveKey usage"); + + // - baseKey algorithm does not match PBKDF2 (InvalidAccessError) + subsetTest(promise_test, function(test) { + return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, wrongKey, derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(key) { + assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "deriveKey with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message); + }); + }, testName + " with wrong (ECDH) key"); + + }); + + // Test various error conditions for deriveBits below: + // length null (OperationError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], null) + .then(function(derivation) { + assert_unreached("null length should have thrown an OperationError"); + }, function(err) { + assert_equals(err.name, "OperationError", "deriveBits with null length correctly threw OperationError: " + err.message); + }); + }, testName + " with null length"); + + // 0 length (OperationError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 0) + .then(function(derivation) { + assert_unreached("0 length should have thrown an OperationError"); + }, function(err) { + assert_equals(err.name, "OperationError", "deriveBits with 0 length correctly threw OperationError: " + err.message); + }); + }, testName + " with 0 length"); + + // length not multiple of 8 (OperationError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 44) + .then(function(derivation) { + assert_unreached("non-multiple of 8 length should have thrown an OperationError"); + }, function(err) { + assert_equals(err.name, "OperationError", "deriveBits with non-multiple of 8 length correctly threw OperationError: " + err.message); + }); + }, testName + " with non-multiple of 8 length"); + + // - illegal name for hash algorithm (NotSupportedError) + var badHash = hashName.substring(0, 3) + hashName.substring(4); + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: badHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256) + .then(function(derivation) { + assert_unreached("bad hash name should have thrown an NotSupportedError"); + }, function(err) { + assert_equals(err.name, "NotSupportedError", "deriveBits with bad hash name correctly threw NotSupportedError: " + err.message); + }); + }, testName + " with bad hash name " + badHash); + + // - baseKey usages missing "deriveBits" (InvalidAccessError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, noBits[passwordSize], 256) + .then(function(derivation) { + assert_unreached("missing deriveBits usage should have thrown an InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "deriveBits with missing deriveBits usage correctly threw InvalidAccessError: " + err.message); + }); + }, testName + " with missing deriveBits usage"); + + // - baseKey algorithm does not match PBKDF2 (InvalidAccessError) + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, wrongKey, 256) + .then(function(derivation) { + assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "deriveBits with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message); + }); + }, testName + " with wrong (ECDH) key"); + }); + + // Check that 0 iterations throws proper error + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0}, baseKeys[passwordSize], 256) + .then(function(derivation) { + assert_unreached("0 iterations should have thrown an error"); + }, function(err) { + assert_equals(err.name, "OperationError", "deriveBits with 0 iterations correctly threw OperationError: " + err.message); + }); + }, passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations"); + + derivedKeyTypes.forEach(function(derivedKeyType) { + var testName = "Derived key of type "; + Object.keys(derivedKeyType.algorithm).forEach(function(prop) { + testName += prop + ": " + derivedKeyType.algorithm[prop] + " "; + }); + testName += " using " + passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations"; + + subsetTest(promise_test, function(test) { + return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(derivation) { + assert_unreached("0 iterations should have thrown an error"); + }, function(err) { + assert_equals(err.name, "OperationError", "derivekey with 0 iterations correctly threw OperationError: " + err.message); + }); + }, testName); + }); + }); + + // - legal algorithm name but not digest one (e.g., PBKDF2) (NotSupportedError) + var nonDigestHash = "PBKDF2"; + [1, 1000, 100000].forEach(function(iterations) { + var testName = passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations"; + + subsetTest(promise_test, function(test) { + return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256) + .then(function(derivation) { + assert_unreached("non-digest algorithm should have thrown an NotSupportedError"); + }, function(err) { + assert_equals(err.name, "NotSupportedError", "deriveBits with non-digest algorithm correctly threw NotSupportedError: " + err.message); + }); + }, testName + " with non-digest algorithm " + nonDigestHash); + + derivedKeyTypes.forEach(function(derivedKeyType) { + var testName = "Derived key of type "; + Object.keys(derivedKeyType.algorithm).forEach(function(prop) { + testName += prop + ": " + derivedKeyType.algorithm[prop] + " "; + }); + testName += " using " + passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations"; + + subsetTest(promise_test, function(test) { + return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages) + .then(function(derivation) { + assert_unreached("non-digest algorithm should have thrown an NotSupportedError"); + }, function(err) { + assert_equals(err.name, "NotSupportedError", "derivekey with non-digest algorithm correctly threw NotSupportedError: " + err.message); + }); + }, testName); + }); + + }); + + }); + }); + }); + + // Deriving bits and keys requires starting with a base key, which is created + // by importing a password. setUpBaseKeys returns a promise that yields the + // necessary base keys. + function setUpBaseKeys(passwords) { + var promises = []; + + var baseKeys = {}; + var noBits = {}; + var noKey = {}; + var wrongKey = null; + + Object.keys(passwords).forEach(function(passwordSize) { + var promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveKey", "deriveBits"]) + .then(function(baseKey) { + baseKeys[passwordSize] = baseKey; + }, function(err) { + baseKeys[passwordSize] = null; + }); + promises.push(promise); + + promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveBits"]) + .then(function(baseKey) { + noKey[passwordSize] = baseKey; + }, function(err) { + noKey[passwordSize] = null; + }); + promises.push(promise); + + promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveKey"]) + .then(function(baseKey) { + noBits[passwordSize] = baseKey; + }, function(err) { + noBits[passwordSize] = null; + }); + promises.push(promise); + }); + + var promise = subtle.generateKey({name: "ECDH", namedCurve: "P-256"}, false, ["deriveKey", "deriveBits"]) + .then(function(baseKey) { + wrongKey = baseKey.privateKey; + }, function(err) { + wrongKey = null; + }); + promises.push(promise); + + + return Promise.all(promises).then(function() { + return {baseKeys: baseKeys, noBits: noBits, noKey: noKey, wrongKey: wrongKey}; + }); + } + + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + +} diff --git a/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/pbkdf2_vectors.js b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/pbkdf2_vectors.js new file mode 100644 index 0000000000..b074f874ad --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/derive_bits_keys/pbkdf2_vectors.js @@ -0,0 +1,269 @@ +function getTestData() { + + // deriveBits and deriveKey take as input: + // - password (actually, a CryptoKey representing a password) + // - salt (BufferSource) + // - hash (which one to use) + // - iterations (how many times to use it) + + // deriveBits also takes a length. deriveKey uses the length of the output key + // - length is the number of bits, NOT octets, but it MUST be a multiple of 8 + // - note that result of length(n) is first n bits of length(m) if m>n + + // Variations to test: + // - empty, short, and fairly long password + // - empty, short, and fairly long salt + // - SHA-1, SHA-256, SHA-384, SHA-512 hash + // - 1, 1000, and 100000 million iterations + + // Test cases to generate: 3 * 3 * 4 * 3 = 108 + + // Error conditions to test: + // - length null (OperationError) + // - length not a multiple of 8 (OperationError) + // - illegal name for hash algorithm (NotSupportedError) + // - legal algorithm name but not digest one (e.g., AES-CBC) (NotSupportedError) + // - baseKey usages missing "deriveBits" (InvalidAccessError) + // - baseKey algorithm does not match PBKDF2 (InvalidAccessError) + // - 0 iterations + + var derivedKeyTypes = [ + {algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CBC", length: 192}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CBC", length: 256}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CTR", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CTR", length: 192}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CTR", length: 256}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-GCM", length: 192}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-GCM", length: 256}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-KW", length: 128}, usages: ["wrapKey", "unwrapKey"]}, + {algorithm: {name: "AES-KW", length: 192}, usages: ["wrapKey", "unwrapKey"]}, + {algorithm: {name: "AES-KW", length: 256}, usages: ["wrapKey", "unwrapKey"]}, + {algorithm: {name: "HMAC", hash: "SHA-1", length: 256}, usages: ["sign", "verify"]}, + {algorithm: {name: "HMAC", hash: "SHA-256", length: 256}, usages: ["sign", "verify"]}, + {algorithm: {name: "HMAC", hash: "SHA-384", length: 256}, usages: ["sign", "verify"]}, + {algorithm: {name: "HMAC", hash: "SHA-512", length: 256}, usages: ["sign", "verify"]} + ]; + + var passwords = { + "short": new Uint8Array([80, 64, 115, 115, 119, 48, 114, 100]), + "long": new Uint8Array([85, 115, 101, 114, 115, 32, 115, 104, 111, 117, 108, 100, 32, 112, 105, 99, 107, 32, 108, 111, 110, 103, 32, 112, 97, 115, 115, 112, 104, 114, 97, 115, 101, 115, 32, 40, 110, 111, 116, 32, 117, 115, 101, 32, 115, 104, 111, 114, 116, 32, 112, 97, 115, 115, 119, 111, 114, 100, 115, 41, 33]), + "empty": new Uint8Array([]) + }; + + var salts = { + "short": new Uint8Array([78, 97, 67, 108]), + "long": new Uint8Array([83, 111, 100, 105, 117, 109, 32, 67, 104, 108, 111, 114, 105, 100, 101, 32, 99, 111, 109, 112, 111, 117, 110, 100]), + "empty": new Uint8Array([]) + }; + + var derivations = { + "short": { + "short": { + "SHA-384": { + "1000": new Uint8Array([170, 236, 90, 151, 109, 77, 53, 203, 32, 36, 72, 111, 201, 249, 187, 154, 163, 234, 231, 206, 242, 188, 230, 38, 100, 181, 179, 117, 28, 245, 15, 241]), + "1": new Uint8Array([128, 205, 15, 21, 54, 67, 102, 167, 37, 81, 195, 121, 117, 247, 182, 55, 186, 137, 194, 155, 70, 57, 236, 114, 15, 105, 167, 13, 187, 237, 81, 92]), + "100000": new Uint8Array([111, 94, 163, 198, 198, 245, 228, 131, 52, 103, 180, 124, 58, 103, 30, 101, 113, 78, 135, 7, 27, 209, 227, 109, 113, 111, 132, 107, 92, 210, 137, 128]) + }, + "SHA-512": { + "1000": new Uint8Array([134, 92, 89, 69, 225, 31, 91, 243, 221, 240, 2, 231, 203, 23, 72, 246, 34, 77, 38, 113, 232, 6, 218, 212, 170, 240, 144, 160, 67, 103, 218, 41]), + "1": new Uint8Array([105, 244, 213, 206, 245, 199, 216, 186, 147, 142, 136, 3, 136, 200, 246, 59, 107, 36, 72, 178, 98, 109, 19, 67, 252, 92, 182, 139, 189, 127, 39, 178]), + "100000": new Uint8Array([72, 59, 167, 242, 226, 254, 56, 44, 246, 29, 32, 178, 152, 18, 226, 212, 150, 16, 166, 0, 65, 174, 64, 236, 249, 252, 126, 241, 56, 233, 56, 118]) + }, + "SHA-1": { + "1000": new Uint8Array([83, 136, 234, 94, 98, 225, 181, 87, 152, 26, 190, 92, 228, 19, 33, 39, 88, 170, 106, 157, 44, 91, 240, 140, 1, 157, 69, 157, 186, 102, 107, 144]), + "1": new Uint8Array([70, 36, 219, 210, 19, 115, 238, 86, 89, 193, 37, 177, 132, 238, 218, 162, 106, 51, 183, 124, 161, 19, 20, 185, 240, 201, 218, 225, 228, 78, 155, 4]), + "100000": new Uint8Array([245, 143, 67, 95, 188, 92, 5, 134, 92, 145, 79, 217, 114, 16, 138, 9, 69, 125, 95, 154, 72, 241, 78, 117, 228, 204, 2, 217, 137, 131, 3, 138]) + }, + "SHA-256": { + "1000": new Uint8Array([78, 108, 165, 121, 87, 67, 155, 227, 167, 83, 112, 66, 66, 37, 226, 33, 29, 85, 240, 90, 240, 5, 97, 223, 63, 62, 254, 233, 17, 107, 195, 76]), + "1": new Uint8Array([198, 188, 85, 164, 4, 173, 206, 163, 106, 26, 181, 103, 152, 8, 94, 10, 175, 105, 127, 107, 178, 193, 106, 80, 114, 248, 56, 241, 125, 254, 108, 182]), + "100000": new Uint8Array([171, 37, 121, 101, 152, 231, 75, 41, 195, 36, 245, 186, 77, 144, 234, 125, 200, 159, 198, 137, 16, 65, 180, 213, 108, 148, 21, 101, 5, 247, 34, 192]) + } + }, + "long": { + "SHA-384": { + "1000": new Uint8Array([163, 16, 239, 60, 107, 58, 149, 230, 216, 202, 102, 68, 227, 220, 253, 136, 34, 42, 89, 254, 142, 0, 197, 45, 106, 18, 99, 29, 130, 193, 210, 75]), + "1": new Uint8Array([104, 7, 52, 108, 197, 62, 222, 209, 203, 150, 74, 114, 98, 133, 137, 166, 189, 72, 53, 89, 144, 191, 223, 231, 70, 81, 9, 113, 2, 7, 5, 157]), + "100000": new Uint8Array([44, 140, 102, 116, 200, 121, 207, 24, 80, 188, 155, 127, 189, 204, 110, 167, 171, 176, 161, 82, 33, 150, 168, 102, 135, 83, 5, 222, 165, 116, 134, 243]) + }, + "SHA-512": { + "1000": new Uint8Array([156, 23, 254, 150, 137, 94, 173, 191, 209, 204, 9, 95, 193, 187, 131, 79, 40, 229, 204, 201, 236, 150, 202, 129, 76, 255, 148, 26, 75, 244, 7, 39]), + "1": new Uint8Array([87, 119, 2, 122, 255, 64, 81, 251, 155, 67, 193, 241, 239, 4, 99, 189, 103, 117, 17, 117, 212, 40, 161, 61, 163, 218, 132, 90, 89, 19, 50, 205]), + "100000": new Uint8Array([180, 121, 201, 113, 92, 66, 22, 56, 220, 224, 167, 5, 252, 11, 123, 167, 213, 111, 163, 6, 49, 136, 6, 53, 128, 224, 112, 223, 241, 219, 73, 124]) + }, + "SHA-1": { + "1000": new Uint8Array([137, 211, 178, 123, 95, 110, 138, 240, 21, 242, 248, 124, 243, 104, 161, 67, 138, 32, 108, 78, 207, 95, 230, 129, 252, 59, 249, 76, 86, 33, 62, 246]), + "1": new Uint8Array([87, 111, 124, 22, 88, 37, 190, 249, 239, 20, 180, 188, 44, 130, 70, 157, 30, 64, 143, 248, 231, 186, 48, 102, 148, 121, 127, 158, 69, 183, 102, 237]), + "100000": new Uint8Array([30, 57, 232, 191, 102, 118, 252, 211, 21, 102, 85, 69, 122, 250, 20, 190, 231, 113, 219, 203, 252, 208, 114, 65, 199, 206, 226, 9, 167, 203, 31, 233]) + }, + "SHA-256": { + "1000": new Uint8Array([177, 167, 183, 220, 32, 223, 23, 74, 74, 14, 65, 13, 191, 175, 3, 180, 195, 117, 196, 80, 168, 157, 122, 158, 211, 73, 180, 229, 46, 100, 223, 216]), + "1": new Uint8Array([18, 185, 15, 89, 79, 9, 8, 207, 145, 45, 101, 92, 148, 143, 156, 42, 30, 171, 133, 87, 101, 188, 18, 120, 94, 241, 138, 160, 43, 142, 126, 220]), + "100000": new Uint8Array([212, 89, 77, 138, 27, 89, 82, 10, 72, 135, 137, 34, 166, 93, 102, 61, 40, 246, 165, 250, 73, 233, 49, 211, 0, 216, 249, 186, 249, 61, 10, 235]) + } + }, + "empty": { + "SHA-384": { + "1000": new Uint8Array([174, 181, 249, 125, 102, 39, 238, 188, 222, 107, 19, 154, 0, 137, 85, 0, 48, 247, 64, 28, 103, 224, 28, 5, 122, 51, 56, 23, 94, 63, 58, 23]), + "1": new Uint8Array([79, 16, 137, 192, 30, 67, 139, 222, 100, 154, 55, 159, 164, 24, 251, 195, 184, 86, 37, 135, 114, 223, 233, 17, 128, 111, 155, 208, 128, 159, 188, 126]), + "100000": new Uint8Array([215, 104, 125, 246, 199, 129, 220, 136, 214, 78, 249, 203, 175, 149, 211, 213, 209, 21, 95, 102, 178, 48, 35, 158, 110, 129, 193, 85, 12, 136, 64, 207]) + }, + "SHA-512": { + "1000": new Uint8Array([181, 172, 114, 11, 122, 190, 8, 50, 252, 81, 163, 27, 30, 197, 103, 59, 235, 30, 65, 132, 10, 223, 211, 214, 6, 232, 99, 143, 64, 6, 235, 72]), + "1": new Uint8Array([143, 123, 125, 69, 156, 117, 47, 100, 191, 18, 190, 98, 91, 101, 212, 150, 172, 36, 234, 54, 81, 107, 22, 142, 22, 251, 2, 104, 69, 180, 232, 46]), + "100000": new Uint8Array([186, 26, 15, 54, 186, 215, 113, 82, 101, 100, 5, 30, 185, 202, 32, 125, 161, 155, 98, 229, 55, 98, 52, 153, 118, 169, 163, 209, 176, 239, 126, 32]) + }, + "SHA-1": { + "1000": new Uint8Array([115, 111, 60, 61, 110, 188, 194, 167, 185, 112, 64, 62, 38, 150, 192, 235, 76, 209, 119, 15, 85, 241, 150, 252, 112, 137, 230, 102, 193, 31, 119, 218]), + "1": new Uint8Array([192, 207, 251, 12, 229, 219, 53, 31, 170, 36, 218, 213, 144, 37, 131, 207, 195, 10, 159, 84, 217, 170, 105, 145, 254, 130, 29, 3, 18, 33, 39, 233]), + "100000": new Uint8Array([28, 80, 149, 172, 154, 123, 212, 16, 239, 15, 114, 201, 147, 236, 169, 27, 176, 229, 113, 233, 178, 251, 171, 112, 79, 140, 19, 17, 145, 250, 209, 108]) + }, + "SHA-256": { + "1000": new Uint8Array([185, 210, 242, 33, 123, 78, 229, 168, 191, 3, 69, 243, 107, 44, 152, 135, 51, 245, 3, 169, 117, 223, 234, 199, 183, 19, 95, 84, 165, 242, 153, 113]), + "1": new Uint8Array([1, 158, 84, 171, 66, 240, 4, 133, 211, 170, 27, 38, 252, 222, 33, 174, 95, 82, 203, 15, 9, 96, 255, 201, 118, 127, 37, 198, 94, 45, 178, 249]), + "100000": new Uint8Array([167, 162, 134, 152, 41, 121, 120, 7, 179, 229, 118, 193, 120, 120, 180, 102, 68, 158, 137, 230, 4, 71, 213, 65, 119, 90, 150, 235, 124, 26, 93, 237]) + } + } + }, + "long": { + "short": { + "SHA-384": { + "1000": new Uint8Array([250, 164, 66, 251, 171, 244, 5, 140, 198, 83, 104, 181, 61, 126, 197, 17, 60, 9, 234, 126, 94, 55, 67, 49, 47, 75, 235, 237, 217, 128, 186, 55]), + "1": new Uint8Array([94, 222, 136, 54, 253, 171, 238, 197, 211, 115, 59, 67, 74, 186, 196, 67, 212, 21, 25, 59, 89, 158, 9, 38, 25, 59, 0, 15, 64, 106, 90, 125]), + "100000": new Uint8Array([246, 42, 230, 199, 135, 27, 24, 26, 167, 18, 50, 245, 235, 136, 55, 36, 152, 239, 50, 172, 10, 125, 113, 81, 25, 232, 240, 82, 235, 16, 45, 41]) + }, + "SHA-512": { + "1000": new Uint8Array([240, 146, 143, 80, 161, 85, 242, 106, 140, 156, 27, 199, 243, 181, 203, 83, 28, 83, 168, 245, 16, 64, 201, 206, 95, 199, 157, 67, 15, 240, 192, 244]), + "1": new Uint8Array([62, 156, 18, 179, 246, 223, 182, 68, 21, 148, 236, 112, 99, 252, 169, 98, 255, 218, 16, 182, 207, 48, 184, 152, 163, 30, 249, 241, 48, 107, 17, 25]), + "100000": new Uint8Array([151, 74, 207, 187, 15, 15, 32, 200, 30, 201, 40, 41, 243, 140, 61, 175, 8, 106, 125, 245, 139, 145, 43, 133, 109, 31, 94, 204, 147, 85, 239, 27]) + }, + "SHA-1": { + "1000": new Uint8Array([83, 180, 33, 97, 19, 78, 21, 200, 113, 171, 215, 26, 186, 19, 144, 208, 31, 76, 106, 148, 12, 170, 245, 193, 121, 37, 141, 143, 27, 29, 104, 11]), + "1": new Uint8Array([138, 231, 47, 148, 230, 252, 213, 79, 203, 252, 166, 98, 0, 162, 17, 165, 27, 47, 132, 103, 135, 210, 11, 104, 8, 190, 223, 21, 108, 228, 108, 160]), + "100000": new Uint8Array([167, 253, 164, 199, 157, 211, 186, 26, 135, 95, 101, 233, 36, 139, 33, 8, 153, 202, 8, 20, 174, 56, 153, 93, 140, 229, 165, 53, 96, 203, 172, 49]) + }, + "SHA-256": { + "1000": new Uint8Array([238, 235, 119, 20, 66, 10, 0, 177, 138, 206, 194, 181, 151, 157, 29, 166, 19, 115, 32, 43, 127, 139, 167, 27, 8, 98, 147, 170, 184, 89, 224, 160]), + "1": new Uint8Array([255, 161, 233, 167, 39, 169, 44, 39, 174, 111, 116, 177, 199, 151, 143, 158, 26, 248, 96, 225, 6, 55, 99, 64, 172, 67, 217, 105, 209, 54, 64, 91]), + "100000": new Uint8Array([222, 172, 112, 203, 227, 241, 114, 14, 53, 59, 78, 128, 22, 221, 181, 148, 117, 239, 183, 11, 106, 35, 133, 231, 53, 210, 214, 234, 109, 98, 74, 77]) + } + }, + "long": { + "SHA-384": { + "1000": new Uint8Array([53, 101, 133, 81, 240, 236, 19, 57, 138, 123, 69, 224, 38, 28, 253, 101, 76, 30, 82, 65, 30, 110, 69, 125, 238, 104, 244, 174, 171, 233, 37, 167]), + "1": new Uint8Array([207, 85, 66, 44, 239, 110, 27, 196, 158, 109, 8, 43, 34, 115, 212, 128, 232, 242, 232, 130, 45, 173, 209, 70, 156, 42, 50, 217, 101, 125, 18, 241]), + "100000": new Uint8Array([26, 186, 181, 241, 228, 97, 223, 55, 139, 136, 192, 162, 43, 231, 110, 242, 241, 98, 125, 247, 74, 199, 203, 251, 132, 189, 204, 179, 84, 188, 136, 137]) + }, + "SHA-512": { + "1000": new Uint8Array([67, 225, 32, 36, 196, 211, 84, 114, 127, 126, 88, 132, 44, 203, 96, 51, 161, 97, 214, 13, 197, 174, 81, 111, 7, 110, 74, 88, 161, 136, 13, 56]), + "1": new Uint8Array([222, 74, 251, 192, 173, 211, 228, 211, 47, 75, 198, 225, 34, 168, 138, 228, 74, 43, 60, 207, 1, 72, 231, 118, 43, 172, 5, 196, 62, 148, 239, 127]), + "100000": new Uint8Array([249, 169, 35, 132, 164, 234, 223, 195, 86, 6, 73, 179, 127, 182, 118, 232, 60, 69, 60, 187, 217, 159, 128, 187, 166, 240, 161, 14, 189, 21, 11, 82]) + }, + "SHA-1": { + "1000": new Uint8Array([110, 144, 200, 110, 224, 123, 135, 62, 150, 80, 113, 2, 86, 115, 255, 5, 66, 159, 103, 140, 48, 249, 27, 55, 225, 226, 218, 81, 32, 54, 211, 32]), + "1": new Uint8Array([29, 16, 78, 165, 210, 53, 0, 106, 18, 168, 15, 113, 184, 14, 229, 40, 4, 139, 100, 204, 26, 122, 15, 48, 247, 223, 75, 162, 107, 131, 32, 199]), + "100000": new Uint8Array([20, 16, 48, 118, 59, 249, 131, 200, 86, 77, 93, 76, 147, 95, 227, 202, 53, 73, 96, 129, 89, 172, 25, 52, 193, 89, 144, 64, 102, 140, 35, 99]) + }, + "SHA-256": { + "1000": new Uint8Array([63, 213, 135, 201, 75, 169, 70, 184, 185, 220, 205, 221, 42, 91, 116, 246, 119, 141, 79, 97, 230, 145, 248, 58, 196, 122, 47, 169, 88, 11, 253, 248]), + "1": new Uint8Array([253, 92, 174, 184, 179, 171, 229, 137, 188, 21, 156, 78, 81, 248, 0, 87, 14, 116, 246, 67, 151, 166, 197, 238, 19, 29, 254, 217, 63, 5, 17, 170]), + "100000": new Uint8Array([17, 153, 45, 139, 129, 51, 17, 36, 76, 84, 75, 98, 41, 41, 69, 226, 8, 212, 3, 206, 189, 107, 149, 82, 161, 165, 98, 6, 93, 153, 88, 234]) + } + }, + "empty": { + "SHA-384": { + "1000": new Uint8Array([249, 202, 20, 139, 12, 4, 24, 144, 191, 248, 131, 29, 182, 23, 71, 25, 126, 148, 206, 104, 241, 144, 237, 242, 105, 105, 75, 77, 100, 72, 97, 202]), + "1": new Uint8Array([73, 171, 63, 159, 136, 47, 219, 158, 82, 139, 77, 159, 27, 62, 140, 113, 210, 99, 154, 191, 23, 1, 213, 110, 185, 155, 213, 18, 1, 228, 32, 255]), + "100000": new Uint8Array([23, 73, 223, 205, 119, 229, 37, 133, 25, 234, 34, 49, 186, 44, 214, 84, 59, 7, 51, 57, 172, 155, 21, 69, 187, 100, 49, 83, 250, 246, 209, 123]) + }, + "SHA-512": { + "1000": new Uint8Array([69, 122, 121, 85, 235, 236, 236, 113, 165, 30, 251, 98, 55, 229, 177, 214, 47, 77, 234, 181, 201, 61, 123, 61, 17, 209, 231, 15, 175, 250, 65, 126]), + "1": new Uint8Array([209, 191, 161, 166, 184, 169, 119, 131, 159, 140, 63, 157, 82, 221, 2, 16, 78, 32, 41, 192, 235, 42, 98, 8, 204, 64, 136, 22, 231, 118, 138, 140]), + "100000": new Uint8Array([232, 5, 172, 156, 193, 216, 65, 44, 66, 68, 109, 35, 125, 27, 80, 79, 149, 64, 179, 98, 189, 27, 117, 228, 81, 83, 30, 133, 62, 36, 117, 61]) + }, + "SHA-1": { + "1000": new Uint8Array([231, 55, 93, 229, 3, 103, 102, 196, 12, 184, 95, 67, 181, 63, 206, 79, 250, 64, 42, 182, 190, 53, 113, 0, 126, 245, 213, 84, 83, 253, 127, 10]), + "1": new Uint8Array([164, 106, 98, 152, 109, 156, 57, 9, 244, 16, 20, 221, 114, 207, 227, 74, 38, 18, 71, 133, 77, 115, 18, 207, 79, 190, 173, 96, 185, 182, 158, 221]), + "100000": new Uint8Array([122, 64, 61, 154, 19, 174, 216, 22, 78, 156, 7, 44, 84, 84, 98, 37, 31, 217, 66, 241, 115, 106, 107, 240, 60, 225, 200, 131, 48, 4, 142, 4]) + }, + "SHA-256": { + "1000": new Uint8Array([126, 102, 200, 75, 234, 136, 143, 146, 195, 72, 217, 20, 85, 133, 24, 108, 174, 71, 43, 18, 251, 167, 240, 173, 40, 23, 149, 117, 193, 170, 129, 90]), + "1": new Uint8Array([79, 81, 12, 81, 129, 172, 92, 44, 95, 212, 189, 20, 31, 151, 18, 73, 91, 236, 162, 121, 98, 71, 66, 180, 214, 211, 13, 8, 185, 108, 10, 105]), + "100000": new Uint8Array([95, 26, 106, 196, 165, 109, 151, 150, 167, 48, 154, 120, 218, 170, 249, 24, 186, 218, 245, 237, 30, 236, 195, 240, 184, 163, 164, 76, 61, 56, 214, 84]) + } + } + }, + "empty": { + "short": { + "SHA-384": { + "1000": new Uint8Array([127, 247, 149, 74, 237, 223, 65, 121, 95, 200, 48, 6, 102, 120, 109, 73, 116, 38, 154, 169, 28, 199, 233, 56, 17, 201, 83, 51, 29, 86, 214, 9]), + "1": new Uint8Array([233, 240, 218, 30, 151, 223, 164, 85, 248, 88, 206, 107, 154, 241, 236, 192, 41, 159, 18, 95, 241, 168, 71, 235, 93, 73, 85, 134, 111, 67, 230, 4]), + "100000": new Uint8Array([28, 115, 19, 43, 106, 85, 233, 217, 222, 44, 219, 254, 31, 85, 191, 10, 181, 159, 217, 31, 120, 241, 9, 197, 0, 150, 3, 139, 133, 87, 177, 71]) + }, + "SHA-512": { + "1000": new Uint8Array([213, 97, 196, 200, 78, 156, 96, 186, 71, 82, 162, 211, 131, 191, 85, 239, 246, 67, 252, 158, 69, 34, 82, 214, 130, 30, 57, 68, 147, 80, 207, 114]), + "1": new Uint8Array([231, 226, 180, 31, 72, 135, 66, 27, 203, 118, 78, 180, 165, 111, 99, 210, 80, 46, 51, 199, 100, 251, 223, 96, 98, 106, 212, 46, 217, 103, 35, 66]), + "100000": new Uint8Array([239, 208, 7, 82, 188, 159, 250, 251, 90, 57, 157, 209, 213, 131, 78, 141, 44, 43, 103, 110, 205, 75, 32, 99, 251, 31, 229, 129, 208, 241, 56, 11]) + }, + "SHA-1": { + "1000": new Uint8Array([114, 201, 43, 189, 61, 218, 180, 120, 158, 136, 228, 42, 209, 205, 168, 60, 192, 114, 158, 108, 181, 16, 106, 87, 126, 80, 213, 207, 97, 120, 36, 129]), + "1": new Uint8Array([166, 103, 218, 71, 184, 248, 87, 183, 198, 95, 112, 166, 200, 231, 160, 108, 224, 210, 82, 17, 162, 182, 235, 175, 88, 220, 170, 242, 104, 180, 107, 29]), + "100000": new Uint8Array([6, 225, 158, 27, 131, 230, 72, 11, 21, 84, 223, 43, 49, 162, 201, 45, 27, 252, 249, 188, 27, 219, 200, 117, 31, 248, 104, 91, 222, 239, 125, 201]) + }, + "SHA-256": { + "1000": new Uint8Array([40, 53, 243, 237, 83, 86, 84, 32, 201, 9, 81, 80, 155, 12, 17, 115, 182, 69, 23, 79, 21, 70, 171, 58, 195, 230, 200, 92, 180, 113, 181, 59]), + "1": new Uint8Array([45, 219, 73, 36, 62, 179, 181, 145, 44, 178, 96, 205, 216, 127, 176, 78, 240, 209, 17, 191, 164, 77, 64, 164, 94, 2, 168, 165, 195, 193, 81, 141]), + "100000": new Uint8Array([128, 174, 217, 5, 202, 50, 174, 11, 178, 169, 216, 245, 50, 240, 72, 160, 230, 114, 70, 62, 239, 159, 131, 223, 167, 216, 139, 202, 114, 101, 83, 234]) + } + }, + "long": { + "SHA-384": { + "1000": new Uint8Array([139, 184, 156, 247, 25, 114, 254, 90, 204, 22, 253, 197, 248, 207, 253, 44, 46, 113, 120, 192, 134, 179, 187, 230, 28, 193, 49, 70, 25, 19, 89, 88]), + "1": new Uint8Array([123, 11, 204, 168, 29, 214, 55, 163, 179, 57, 134, 102, 97, 151, 22, 197, 242, 177, 244, 165, 194, 78, 133, 193, 138, 153, 85, 85, 158, 77, 118, 146]), + "100000": new Uint8Array([38, 198, 168, 174, 75, 209, 251, 231, 21, 174, 71, 142, 255, 243, 236, 174, 131, 175, 166, 23, 237, 53, 189, 74, 63, 99, 195, 218, 118, 164, 45, 34]) + }, + "SHA-512": { + "1000": new Uint8Array([92, 172, 193, 108, 223, 190, 5, 44, 253, 115, 169, 137, 27, 140, 14, 120, 177, 155, 46, 7, 234, 226, 66, 61, 72, 254, 213, 224, 138, 168, 73, 75]), + "1": new Uint8Array([187, 115, 248, 22, 138, 143, 57, 29, 61, 84, 202, 137, 47, 183, 43, 142, 96, 53, 227, 127, 137, 30, 90, 112, 73, 27, 148, 220, 5, 81, 11, 196]), + "100000": new Uint8Array([135, 253, 252, 41, 51, 146, 203, 243, 62, 204, 155, 81, 65, 162, 254, 250, 116, 209, 80, 73, 151, 86, 134, 60, 72, 76, 10, 120, 182, 39, 77, 127]) + }, + "SHA-1": { + "1000": new Uint8Array([204, 87, 72, 236, 196, 18, 136, 160, 225, 51, 104, 84, 58, 170, 46, 246, 44, 151, 186, 117, 24, 250, 136, 246, 225, 28, 53, 118, 63, 201, 48, 180]), + "1": new Uint8Array([31, 70, 180, 12, 242, 251, 61, 196, 26, 61, 156, 237, 136, 151, 184, 97, 5, 3, 104, 16, 226, 191, 172, 112, 64, 129, 75, 214, 93, 66, 141, 103]), + "100000": new Uint8Array([51, 226, 153, 59, 244, 114, 157, 201, 147, 255, 246, 110, 105, 204, 85, 119, 113, 53, 235, 250, 188, 229, 51, 87, 91, 206, 74, 150, 100, 90, 116, 44]) + }, + "SHA-256": { + "1000": new Uint8Array([19, 83, 247, 69, 130, 55, 171, 51, 46, 224, 82, 226, 159, 130, 154, 42, 185, 14, 114, 99, 14, 161, 4, 147, 180, 238, 207, 251, 159, 248, 158, 29]), + "1": new Uint8Array([97, 201, 53, 196, 98, 195, 50, 28, 137, 102, 53, 69, 209, 58, 79, 107, 82, 181, 25, 28, 251, 116, 121, 229, 141, 207, 230, 68, 77, 67, 16, 108]), + "100000": new Uint8Array([121, 186, 248, 14, 197, 130, 146, 5, 56, 128, 30, 157, 146, 156, 224, 112, 132, 39, 121, 135, 72, 141, 115, 58, 2, 104, 82, 196, 82, 240, 111, 180]) + } + }, + "empty": { + "SHA-384": { + "1000": new Uint8Array([156, 191, 231, 45, 25, 77, 163, 78, 23, 200, 33, 221, 21, 105, 239, 80, 168, 110, 180, 216, 147, 89, 23, 118, 173, 198, 165, 194, 30, 0, 49, 207]), + "1": new Uint8Array([75, 176, 66, 165, 194, 140, 238, 111, 102, 249, 145, 199, 23, 253, 119, 2, 103, 120, 126, 43, 179, 3, 30, 174, 39, 13, 135, 214, 58, 217, 149, 52]), + "100000": new Uint8Array([237, 107, 215, 40, 37, 103, 171, 228, 141, 84, 45, 6, 125, 9, 244, 4, 189, 4, 74, 226, 206, 254, 17, 218, 204, 83, 28, 71, 100, 205, 53, 205]) + }, + "SHA-512": { + "1000": new Uint8Array([203, 147, 9, 108, 58, 2, 190, 235, 28, 95, 172, 54, 118, 92, 144, 17, 254, 153, 248, 216, 234, 98, 54, 96, 72, 252, 152, 203, 152, 223, 234, 143]), + "1": new Uint8Array([109, 46, 203, 187, 251, 46, 109, 205, 112, 86, 250, 249, 175, 106, 160, 110, 174, 89, 67, 145, 219, 152, 50, 121, 166, 191, 39, 224, 235, 34, 134, 20]), + "100000": new Uint8Array([137, 225, 98, 84, 235, 173, 92, 186, 114, 224, 174, 190, 22, 20, 199, 249, 183, 149, 167, 80, 95, 38, 55, 32, 108, 225, 10, 52, 73, 162, 184, 187]) + }, + "SHA-1": { + "1000": new Uint8Array([110, 64, 145, 10, 192, 46, 200, 156, 235, 185, 216, 152, 177, 58, 9, 209, 205, 122, 223, 111, 140, 192, 140, 196, 115, 48, 44, 137, 115, 170, 46, 25]), + "1": new Uint8Array([30, 67, 122, 28, 121, 215, 91, 230, 30, 145, 20, 29, 174, 32, 175, 252, 72, 146, 204, 153, 171, 204, 63, 231, 83, 136, 123, 204, 200, 146, 1, 118]), + "100000": new Uint8Array([169, 225, 190, 187, 54, 188, 38, 215, 201, 151, 213, 72, 60, 188, 141, 228, 164, 25, 209, 231, 6, 87, 19, 66, 99, 37, 134, 236, 51, 10, 114, 144]) + }, + "SHA-256": { + "1000": new Uint8Array([79, 197, 138, 33, 193, 0, 206, 24, 53, 184, 249, 153, 29, 115, 139, 86, 150, 93, 20, 178, 78, 23, 97, 251, 223, 252, 105, 172, 94, 11, 102, 122]), + "1": new Uint8Array([247, 206, 11, 101, 61, 45, 114, 164, 16, 140, 245, 171, 233, 18, 255, 221, 119, 118, 22, 219, 187, 39, 167, 14, 130, 4, 243, 174, 45, 15, 111, 173]), + "100000": new Uint8Array([100, 168, 104, 212, 178, 58, 246, 150, 211, 115, 77, 11, 129, 77, 4, 205, 209, 172, 40, 1, 40, 233, 118, 83, 160, 95, 50, 180, 156, 19, 162, 154]) + } + } + } + }; + + return {passwords: passwords, salts: salts, derivations: derivations, derivedKeyTypes: derivedKeyTypes}; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/digest/digest.https.any.js b/testing/web-platform/tests/WebCryptoAPI/digest/digest.https.any.js new file mode 100644 index 0000000000..379d9311f3 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/digest/digest.https.any.js @@ -0,0 +1,152 @@ +// META: title=WebCryptoAPI: digest() +// META: timeout=long + + var subtle = crypto.subtle; // Change to test prefixed implementations + + var sourceData = { + empty: new Uint8Array(0), + short: new Uint8Array([21, 110, 234, 124, 193, 76, 86, 203, 148, 219, 3, 10, 74, 157, 149, 255]), + medium: new Uint8Array([182, 200, 249, 223, 100, 140, 208, 136, 183, 15, 56, 231, 65, 151, 177, 140, 184, 30, 30, 67, 80, 213, 11, 204, 184, 251, 90, 115, 121, 200, 123, 178, 227, 214, 237, 84, 97, 237, 30, 159, 54, 243, 64, 163, 150, 42, 68, 107, 129, 91, 121, 75, 75, 212, 58, 68, 3, 80, 32, 119, 178, 37, 108, 200, 7, 131, 127, 58, 172, 209, 24, 235, 75, 156, 43, 174, 184, 151, 6, 134, 37, 171, 172, 161, 147]) + }; + + sourceData.long = new Uint8Array(1024 * sourceData.medium.byteLength); + for (var i=0; i<1024; i++) { + sourceData.long.set(sourceData.medium, i * sourceData.medium.byteLength); + } + + var digestedData = { + "sha-1": { + empty: new Uint8Array([218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9]), + short: new Uint8Array([201, 19, 24, 205, 242, 57, 106, 1, 94, 63, 78, 106, 134, 160, 186, 101, 184, 99, 89, 68]), + medium: new Uint8Array([229, 65, 6, 8, 112, 235, 22, 191, 51, 182, 142, 81, 245, 19, 82, 104, 147, 152, 103, 41]), + long: new Uint8Array([48, 152, 181, 0, 55, 236, 208, 46, 189, 101, 118, 83, 178, 191, 160, 30, 238, 39, 162, 234]) + }, + "sha-256": { + empty: new Uint8Array([227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174, 65, 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85]), + short: new Uint8Array([162, 131, 17, 134, 152, 71, 146, 199, 211, 45, 89, 200, 151, 64, 104, 127, 25, 173, 220, 27, 149, 158, 113, 161, 204, 83, 138, 59, 126, 216, 67, 242]), + medium: new Uint8Array([83, 83, 103, 135, 126, 240, 20, 215, 252, 113, 126, 92, 183, 132, 62, 89, 182, 26, 238, 98, 199, 2, 156, 236, 126, 198, 193, 47, 217, 36, 224, 228]), + long: new Uint8Array([20, 205, 234, 157, 199, 95, 90, 98, 116, 217, 252, 30, 100, 0, 153, 18, 241, 220, 211, 6, 180, 143, 232, 233, 207, 18, 45, 230, 113, 87, 23, 129]) + }, + "sha-384": { + empty: new Uint8Array([56, 176, 96, 167, 81, 172, 150, 56, 76, 217, 50, 126, 177, 177, 227, 106, 33, 253, 183, 17, 20, 190, 7, 67, 76, 12, 199, 191, 99, 246, 225, 218, 39, 78, 222, 191, 231, 111, 101, 251, 213, 26, 210, 241, 72, 152, 185, 91]), + short: new Uint8Array([107, 245, 234, 101, 36, 209, 205, 220, 67, 247, 207, 59, 86, 238, 5, 146, 39, 64, 74, 47, 83, 143, 2, 42, 61, 183, 68, 122, 120, 44, 6, 193, 237, 5, 232, 171, 79, 94, 220, 23, 243, 113, 20, 64, 223, 233, 119, 49]), + medium: new Uint8Array([203, 194, 197, 136, 254, 91, 37, 249, 22, 218, 40, 180, 228, 122, 72, 74, 230, 252, 31, 228, 144, 45, 213, 201, 147, 154, 107, 253, 3, 74, 179, 180, 139, 57, 8, 116, 54, 1, 31, 106, 153, 135, 157, 39, 149, 64, 233, 119]), + long: new Uint8Array([73, 244, 253, 179, 152, 25, 104, 249, 125, 87, 55, 15, 133, 52, 80, 103, 205, 82, 150, 169, 125, 209, 161, 142, 6, 145, 30, 117, 110, 150, 8, 73, 37, 41, 135, 14, 26, 209, 48, 153, 141, 87, 203, 251, 183, 193, 208, 158]) + }, + "sha-512": { + empty: new Uint8Array([207, 131, 225, 53, 126, 239, 184, 189, 241, 84, 40, 80, 214, 109, 128, 7, 214, 32, 228, 5, 11, 87, 21, 220, 131, 244, 169, 33, 211, 108, 233, 206, 71, 208, 209, 60, 93, 133, 242, 176, 255, 131, 24, 210, 135, 126, 236, 47, 99, 185, 49, 189, 71, 65, 122, 129, 165, 56, 50, 122, 249, 39, 218, 62]), + short: new Uint8Array([55, 82, 72, 190, 95, 243, 75, 231, 76, 171, 79, 241, 195, 188, 141, 198, 139, 213, 248, 223, 244, 2, 62, 152, 248, 123, 134, 92, 255, 44, 114, 66, 146, 223, 24, 148, 67, 166, 79, 244, 19, 74, 101, 205, 70, 53, 185, 212, 245, 220, 13, 63, 182, 117, 40, 0, 42, 99, 172, 242, 108, 157, 165, 117]), + medium: new Uint8Array([185, 16, 159, 131, 158, 142, 164, 60, 137, 15, 41, 60, 225, 29, 198, 226, 121, 141, 30, 36, 49, 241, 228, 185, 25, 227, 178, 12, 79, 54, 48, 59, 163, 156, 145, 109, 179, 6, 196, 90, 59, 101, 118, 31, 245, 190, 133, 50, 142, 234, 244, 44, 56, 48, 241, 217, 94, 122, 65, 22, 91, 125, 45, 54]), + long: new Uint8Array([75, 2, 202, 246, 80, 39, 96, 48, 234, 86, 23, 229, 151, 197, 213, 63, 217, 218, 166, 139, 120, 191, 230, 11, 34, 170, 184, 211, 106, 76, 42, 58, 255, 219, 113, 35, 79, 73, 39, 103, 55, 197, 117, 221, 247, 77, 20, 5, 76, 189, 111, 219, 152, 253, 13, 220, 188, 180, 111, 145, 173, 118, 182, 238]) + }, + } + + // Try every combination of hash with source data size. Variations tested are + // hash name in upper, lower, or mixed case, and upper-case version with the + // source buffer altered after call. + Object.keys(sourceData).forEach(function(size) { + Object.keys(digestedData).forEach(function(alg) { + var upCase = alg.toUpperCase(); + var downCase = alg.toLowerCase(); + var mixedCase = upCase.substr(0, 1) + downCase.substr(1); + + promise_test(function(test) { + var promise = subtle.digest({name: upCase}, sourceData[size]) + .then(function(result) { + assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size); + }, function(err) { + assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message); + }); + + return promise; + }, upCase + " with " + size + " source data"); + + promise_test(function(test) { + var promise = subtle.digest({name: downCase}, sourceData[size]) + .then(function(result) { + assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size); + }, function(err) { + assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message); + }); + + return promise; + }, downCase + " with " + size + " source data"); + + promise_test(function(test) { + var promise = subtle.digest({name: mixedCase}, sourceData[size]) + .then(function(result) { + assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size); + }, function(err) { + assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message); + }); + + return promise; + }, mixedCase + " with " + size + " source data"); + + promise_test(function(test) { + var copiedBuffer = copyBuffer(sourceData[size]); + var promise = subtle.digest({name: upCase}, copiedBuffer) + .then(function(result) { + assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size); + }, function(err) { + assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message); + }); + + copiedBuffer[0] = 255 - copiedBuffer; + return promise; + }, upCase + " with " + size + " source data and altered buffer after call"); + + }); + }); + + // Call digest() with bad algorithm names to get an error + var badNames = ["AES-GCM", "RSA-OAEP", "PBKDF2", "AES-KW"]; + Object.keys(sourceData).forEach(function(size) { + badNames.forEach(function(badName) { + + promise_test(function(test) { + var promise = subtle.digest({name: badName}, sourceData[size]) + .then(function(result) { + assert_unreached("digest() should not have worked for " + badName + ":" + size); + }, function(err) { + assert_equals(err.name, "NotSupportedError", "Bad algorithm name should cause NotSupportedError") + }); + + return promise; + }, badName + " with " + size); + + }); + }); + + + done(); + + + // Returns a copy of the sourceBuffer it is sent. + function copyBuffer(sourceBuffer) { + var source = new Uint8Array(sourceBuffer); + var copy = new Uint8Array(sourceBuffer.byteLength) + + for (var i=0; i<source.byteLength; i++) { + copy[i] = source[i]; + } + + return copy; + } + + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } diff --git a/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes.js b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes.js new file mode 100644 index 0000000000..fdeb7963f7 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes.js @@ -0,0 +1,325 @@ + +function run_test() { + var subtle = self.crypto.subtle; // Change to test prefixed implementations + + // When are all these tests really done? When all the promises they use have resolved. + var all_promises = []; + + // Source file aes_XXX_vectors.js provides the getTestVectors method + // for the AES-XXX algorithm that drives these tests. + var vectors = getTestVectors(); + var passingVectors = vectors.passing; + var failingVectors = vectors.failing; + var decryptionFailingVectors = vectors.decryptionFailing; + + // Check for successful encryption. + passingVectors.forEach(function(vector) { + var promise = importVectorKey(vector, ["encrypt", "decrypt"]) + .then(function(vector) { + promise_test(function(test) { + return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext) + .then(function(result) { + assert_true(equalBuffers(result, vector.result), "Should return expected result"); + }, function(err) { + assert_unreached("encrypt error for test " + vector.name + ": " + err.message); + }); + }, vector.name); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step: " + vector.name); + }); + + all_promises.push(promise); + }); + + // Check for successful encryption even if the buffer is changed after calling encrypt. + passingVectors.forEach(function(vector) { + var plaintext = copyBuffer(vector.plaintext); + var promise = importVectorKey(vector, ["encrypt", "decrypt"]) + .then(function(vector) { + promise_test(function(test) { + var operation = subtle.encrypt(vector.algorithm, vector.key, plaintext) + .then(function(result) { + assert_true(equalBuffers(result, vector.result), "Should return expected result"); + }, function(err) { + assert_unreached("encrypt error for test " + vector.name + ": " + err.message); + }); + plaintext[0] = 255 - plaintext[0]; + return operation; + }, vector.name + " with altered plaintext"); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step: " + vector.name + " with altered plaintext"); + }); + + all_promises.push(promise); + }); + + // Check for successful decryption. + passingVectors.forEach(function(vector) { + var promise = importVectorKey(vector, ["encrypt", "decrypt"]) + .then(function(vector) { + promise_test(function(test) { + return subtle.decrypt(vector.algorithm, vector.key, vector.result) + .then(function(result) { + assert_true(equalBuffers(result, vector.plaintext), "Should return expected result"); + }, function(err) { + assert_unreached("decrypt error for test " + vector.name + ": " + err.message); + }); + }, vector.name + " decryption"); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step for decryption: " + vector.name); + }); + + all_promises.push(promise); + }); + + // Check for successful decryption even if ciphertext is altered. + passingVectors.forEach(function(vector) { + var ciphertext = copyBuffer(vector.result); + var promise = importVectorKey(vector, ["encrypt", "decrypt"]) + .then(function(vector) { + promise_test(function(test) { + var operation = subtle.decrypt(vector.algorithm, vector.key, ciphertext) + .then(function(result) { + assert_true(equalBuffers(result, vector.plaintext), "Should return expected result"); + }, function(err) { + assert_unreached("decrypt error for test " + vector.name + ": " + err.message); + }); + ciphertext[0] = 255 - ciphertext[0]; + return operation; + }, vector.name + " decryption with altered ciphertext"); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step for decryption: " + vector.name + " with altered ciphertext"); + }); + + all_promises.push(promise); + }); + + // Everything that succeeded should fail if no "encrypt" usage. + passingVectors.forEach(function(vector) { + // Don't want to overwrite key being used for success tests! + var badVector = Object.assign({}, vector); + badVector.key = null; + + var promise = importVectorKey(badVector, ["decrypt"]) + .then(function(vector) { + promise_test(function(test) { + return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext) + .then(function(result) { + assert_unreached("should have thrown exception for test " + vector.name); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw an InvalidAccessError instead of " + err.message) + }); + }, vector.name + " without encrypt usage"); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step: " + vector.name + " without encrypt usage"); + }); + + all_promises.push(promise); + }); + + // Encryption should fail if algorithm of key doesn't match algorithm of function call. + passingVectors.forEach(function(vector) { + var algorithm = Object.assign({}, vector.algorithm); + if (algorithm.name === "AES-CBC") { + algorithm.name = "AES-CTR"; + algorithm.counter = new Uint8Array(16); + algorithm.length = 64; + } else { + algorithm.name = "AES-CBC"; + algorithm.iv = new Uint8Array(16); // Need syntactically valid parameter to get to error being checked. + } + + var promise = importVectorKey(vector, ["encrypt", "decrypt"]) + .then(function(vector) { + promise_test(function(test) { + return subtle.encrypt(algorithm, vector.key, vector.plaintext) + .then(function(result) { + assert_unreached("encrypt succeeded despite mismatch " + vector.name + ": " + err.message); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Mismatch should cause InvalidAccessError instead of " + err.message); + }); + }, vector.name + " with mismatched key and algorithm"); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step: " + vector.name + " with mismatched key and algorithm"); + }); + + all_promises.push(promise); + }); + + // Everything that succeeded decrypting should fail if no "decrypt" usage. + passingVectors.forEach(function(vector) { + // Don't want to overwrite key being used for success tests! + var badVector = Object.assign({}, vector); + badVector.key = null; + + var promise = importVectorKey(badVector, ["encrypt"]) + .then(function(vector) { + promise_test(function(test) { + return subtle.decrypt(vector.algorithm, vector.key, vector.result) + .then(function(result) { + assert_unreached("should have thrown exception for test " + vector.name); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw an InvalidAccessError instead of " + err.message) + }); + }, vector.name + " without decrypt usage"); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step: " + vector.name + " without decrypt usage"); + }); + + all_promises.push(promise); + }); + + // Check for OperationError due to data lengths. + failingVectors.forEach(function(vector) { + var promise = importVectorKey(vector, ["encrypt", "decrypt"]) + .then(function(vector) { + promise_test(function(test) { + return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext) + .then(function(result) { + assert_unreached("should have thrown exception for test " + vector.name); + }, function(err) { + assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message) + }); + }, vector.name); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step: " + vector.name); + }); + + all_promises.push(promise); + }); + + // Check for OperationError due to data lengths for decryption, too. + failingVectors.forEach(function(vector) { + var promise = importVectorKey(vector, ["encrypt", "decrypt"]) + .then(function(vector) { + promise_test(function(test) { + return subtle.decrypt(vector.algorithm, vector.key, vector.result) + .then(function(result) { + assert_unreached("should have thrown exception for test " + vector.name); + }, function(err) { + assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message) + }); + }, vector.name + " decryption"); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step: decryption " + vector.name); + }); + + all_promises.push(promise); + }); + + // Check for decryption failing for algorithm-specific reasons (such as bad + // padding for AES-CBC). + decryptionFailingVectors.forEach(function(vector) { + var promise = importVectorKey(vector, ["encrypt", "decrypt"]) + .then(function(vector) { + promise_test(function(test) { + return subtle.decrypt(vector.algorithm, vector.key, vector.result) + .then(function(result) { + assert_unreached("should have thrown exception for test " + vector.name); + }, function(err) { + assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message) + }); + }, vector.name); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step: decryption " + vector.name); + }); + + all_promises.push(promise); + }); + + promise_test(function() { + return Promise.all(all_promises) + .then(function() {done();}) + .catch(function() {done();}) + }, "setup"); + + // A test vector has all needed fields for encryption, EXCEPT that the + // key field may be null. This function replaces that null with the Correct + // CryptoKey object. + // + // Returns a Promise that yields an updated vector on success. + function importVectorKey(vector, usages) { + if (vector.key !== null) { + return new Promise(function(resolve, reject) { + resolve(vector); + }); + } else { + return subtle.importKey("raw", vector.keyBuffer, {name: vector.algorithm.name}, false, usages) + .then(function(key) { + vector.key = key; + return vector; + }); + } + } + + // Returns a copy of the sourceBuffer it is sent. + function copyBuffer(sourceBuffer) { + var source = new Uint8Array(sourceBuffer); + var copy = new Uint8Array(sourceBuffer.byteLength) + + for (var i=0; i<source.byteLength; i++) { + copy[i] = source[i]; + } + + return copy; + } + + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + return; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_cbc.https.any.js b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_cbc.https.any.js new file mode 100644 index 0000000000..35637a46af --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_cbc.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: encrypt() Using AES-CBC +// META: script=aes_cbc_vectors.js +// META: script=aes.js +// META: timeout=long + +run_test(); diff --git a/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_cbc_vectors.js b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_cbc_vectors.js new file mode 100644 index 0000000000..96445a9632 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_cbc_vectors.js @@ -0,0 +1,261 @@ + +// aes_cbc_vectors.js + +// The following function returns an array of test vectors +// for the subtleCrypto encrypt method. +// +// Each test vector has the following fields: +// name - a unique name for this vector +// keyBuffer - an arrayBuffer with the key data in raw form +// key - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// algorithm - the value of the AlgorithmIdentifier parameter to provide to encrypt +// plaintext - the text to encrypt +// result - the expected result (usually just ciphertext, sometimes with added authentication) +function getTestVectors() { + // Before we can really start, we need to fill a bunch of buffers with data + var plaintext = new Uint8Array([84, 104, 105, 115, 32, 115, + 112, 101, 99, 105, 102, 105, 99, 97, 116, 105, 111, 110, + 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32, + 74, 97, 118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80, + 73, 32, 102, 111, 114, 32, 112, 101, 114, 102, 111, 114, + 109, 105, 110, 103, 32, 98, 97, 115, 105, 99, 32, 99, 114, + 121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32, + 111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105, + 110, 32, 119, 101, 98, 32, 97, 112, 112, 108, 105, 99, 97, + 116, 105, 111, 110, 115, 44, 32, 115, 117, 99, 104, 32, 97, + 115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32, 115, + 105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110, + 101, 114, 97, 116, 105, 111, 110, 32, 97, 110, 100, 32, + 118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110, + 44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121, 112, + 116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99, + 114, 121, 112, 116, 105, 111, 110, 46, 32, 65, 100, 100, + 105, 116, 105, 111, 110, 97, 108, 108, 121, 44, 32, 105, + 116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, + 110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112, + 108, 105, 99, 97, 116, 105, 111, 110, 115, 32, 116, 111, + 32, 103, 101, 110, 101, 114, 97, 116, 101, 32, 97, 110, + 100, 47, 111, 114, 32, 109, 97, 110, 97, 103, 101, 32, 116, + 104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97, + 116, 101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115, + 115, 97, 114, 121, 32, 116, 111, 32, 112, 101, 114, 102, + 111, 114, 109, 32, 116, 104, 101, 115, 101, 32, 111, 112, + 101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115, + 101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32, + 65, 80, 73, 32, 114, 97, 110, 103, 101, 32, 102, 114, 111, + 109, 32, 117, 115, 101, 114, 32, 111, 114, 32, 115, 101, + 114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101, 110, + 116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111, + 99, 117, 109, 101, 110, 116, 32, 111, 114, 32, 99, 111, + 100, 101, 32, 115, 105, 103, 110, 105, 110, 103, 44, 32, + 97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110, 102, + 105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32, + 97, 110, 100, 32, 105, 110, 116, 101, 103, 114, 105, 116, + 121, 32, 111, 102, 32, 99, 111, 109, 109, 117, 110, 105, + 99, 97, 116, 105, 111, 110, 115, 46]); + + // We want some random key bytes of various sizes. + // These were randomly generated from a script. + var keyBytes = { + 128: new Uint8Array([222, 192, 212, 252, 191, 60, 71, + 65, 200, 146, 218, 189, 28, 212, 192, 78]), + 192: new Uint8Array([208, 238, 131, 65, 63, 68, 196, 63, 186, 208, + 61, 207, 166, 18, 99, 152, 29, 109, 221, 95, 240, 30, 28, 246]), + 256: new Uint8Array([103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236, + 233, 204, 58, 249, 16, 229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116, + 216, 0, 11, 191, 48]) + } + + // AES-CBC needs a 16 byte (128 bit) IV. + var iv = new Uint8Array([85, 170, 248, 155, 168, 148, 19, 213, 78, 167, 39, + 167, 108, 39, 162, 132]); + + + // Results. These were created using the Python cryptography module. + + // AES-CBC produces ciphertext + var ciphertext = { + 128: new Uint8Array([35, 127, 3, 254, 231, 8, 114, 231, 143, 174, 193, + 72, 221, 189, 1, 189, 119, 203, 150, 227, 56, 30, 244, 236, 226, 175, + 234, 23, 167, 175, 211, 124, 203, 228, 97, 223, 156, 77, 88, 174, + 166, 187, 186, 225, 176, 92, 250, 177, 225, 41, 135, 124, 215, 86, + 198, 134, 124, 49, 154, 60, 224, 93, 165, 12, 190, 245, 241, 164, + 247, 220, 227, 69, 242, 105, 208, 108, 222, 193, 223, 0, 226, 217, + 39, 160, 78, 147, 191, 38, 153, 232, 206, 221, 254, 25, 185, 249, 7, + 181, 215, 104, 98, 163, 194, 161, 103, 161, 237, 167, 10, 242, 37, + 80, 2, 255, 173, 96, 20, 106, 170, 110, 80, 38, 136, 127, 16, 85, + 244, 78, 172, 56, 106, 3, 115, 130, 58, 186, 129, 236, 255, 251, + 178, 112, 24, 159, 82, 252, 1, 178, 132, 92, 40, 125, 18, 135, 116, + 64, 178, 31, 174, 87, 114, 114, 218, 78, 111, 0, 239, 252, 79, 63, + 119, 58, 118, 78, 55, 249, 36, 130, 225, 205, 13, 76, 97, 214, 250, + 174, 232, 67, 103, 211, 178, 206, 32, 129, 188, 243, 100, 71, 63, + 154, 159, 200, 125, 34, 138, 39, 73, 130, 75, 97, 203, 204, 111, + 244, 75, 186, 181, 43, 207, 175, 146, 98, 207, 27, 23, 90, 144, 161, + 19, 235, 199, 93, 98, 238, 72, 134, 157, 220, 207, 66, 167, 236, 94, + 57, 0, 3, 202, 250, 55, 26, 163, 20, 133, 191, 67, 20, 63, 150, 203, + 87, 216, 44, 57, 188, 236, 64, 80, 111, 68, 26, 12, 10, 163, 82, 3, + 191, 19, 71, 186, 196, 177, 84, 244, 7, 78, 41, 172, 203, 27, 225, + 231, 108, 206, 141, 221, 253, 204, 220, 134, 20, 130, 54, 113, 81, + 127, 197, 27, 101, 121, 159, 223, 193, 115, 190, 12, 153, 174, 231, + 196, 92, 142, 156, 61, 189, 3, 18, 153, 206, 190, 58, 255, 154, 115, + 66, 23, 107, 94, 220, 156, 220, 228, 241, 66, 6, 184, 44, 238, 249, + 51, 240, 109, 142, 208, 189, 11, 117, 70, 170, 217, 170, 216, 66, + 231, 18, 175, 121, 221, 16, 29, 139, 55, 103, 91, 239, 111, 29, 108, + 94, 179, 138, 134, 73, 130, 29, 69, 182, 192, 249, 150, 165, 79, 47, + 91, 203, 226, 63, 87, 52, 60, 172, 191, 190, 179, 171, 155, 205, 88, + 172, 111, 59, 40, 198, 250, 209, 148, 177, 115, 200, 40, 43, 165, + 167, 67, 116, 64, 159, 240, 81, 253, 235, 137, 132, 49, 223, 214, + 172, 53, 7, 47, 184, 223, 120, 59, 51, 33, 124, 147, 221, 27, 60, + 16, 254, 24, 115, 115, 214, 75, 73, 97, 136, 214, 209, 177, 106, 71, + 254, 211, 94, 57, 104, 170, 168, 35, 37, 93, 203, 199, 38, 28, 84]), + + 192: new Uint8Array([131, 160, 2, 14, 214, 229, 41, 230, 47, 99, 83, + 193, 62, 133, 172, 195, 127, 61, 247, 80, 71, 167, 37, 184, 230, + 207, 168, 163, 139, 145, 18, 225, 205, 134, 87, 138, 80, 247, 166, + 176, 177, 18, 71, 88, 193, 56, 45, 96, 36, 78, 134, 212, 9, 250, 217, + 24, 207, 215, 111, 72, 114, 203, 27, 188, 122, 34, 212, 191, 88, 72, + 22, 194, 224, 217, 236, 201, 191, 236, 214, 231, 90, 244, 100, 153, + 211, 35, 182, 205, 128, 84, 79, 161, 53, 166, 236, 196, 181, 163, + 140, 255, 80, 59, 49, 71, 170, 118, 14, 100, 40, 105, 184, 187, 41, + 198, 180, 135, 69, 211, 69, 74, 132, 243, 76, 144, 102, 90, 155, + 243, 125, 140, 190, 20, 9, 232, 188, 198, 221, 148, 13, 53, 155, 91, + 34, 235, 24, 121, 109, 48, 242, 142, 8, 160, 223, 242, 163, 98, 198, + 131, 164, 160, 79, 27, 210, 216, 192, 228, 27, 4, 254, 222, 195, 14, + 77, 72, 225, 151, 114, 38, 130, 143, 6, 17, 138, 229, 193, 114, 169, + 2, 108, 225, 35, 37, 232, 200, 167, 147, 251, 210, 138, 243, 44, 48, + 12, 84, 192, 169, 108, 0, 113, 77, 160, 218, 96, 4, 138, 171, 207, + 20, 189, 146, 255, 206, 68, 160, 87, 127, 3, 83, 182, 203, 116, 59, + 24, 186, 79, 68, 220, 161, 85, 227, 29, 118, 134, 128, 187, 29, 128, + 121, 120, 64, 211, 30, 255, 52, 187, 185, 216, 151, 30, 10, 165, + 203, 148, 39, 224, 14, 173, 199, 57, 0, 194, 79, 115, 206, 159, 43, + 13, 36, 169, 97, 144, 32, 0, 207, 230, 16, 162, 156, 166, 34, 150, + 12, 93, 141, 164, 181, 194, 10, 47, 139, 82, 75, 42, 23, 224, 3, 92, + 151, 154, 249, 170, 57, 141, 113, 32, 52, 158, 218, 49, 242, 134, + 65, 69, 203, 71, 19, 133, 125, 117, 1, 207, 210, 224, 130, 45, 37, + 42, 181, 139, 34, 85, 8, 67, 165, 249, 180, 89, 3, 60, 152, 1, 231, + 49, 1, 124, 243, 81, 44, 72, 232, 239, 129, 75, 108, 4, 169, 132, + 73, 183, 21, 29, 46, 94, 138, 83, 190, 131, 146, 65, 104, 107, 251, + 218, 95, 227, 94, 145, 70, 0, 2, 252, 59, 188, 58, 150, 203, 148, + 100, 219, 36, 182, 81, 237, 138, 160, 83, 151, 119, 11, 216, 122, + 134, 189, 246, 251, 192, 41, 158, 125, 247, 190, 32, 173, 104, 9, + 58, 223, 97, 212, 48, 62, 3, 112, 21, 74, 206, 87, 182, 110, 197, + 67, 68, 155, 189, 223, 136, 2, 239, 137, 151, 138, 252, 162, 141, + 255, 209, 25, 4, 146, 24, 221, 43, 148, 120, 26, 228, 208, 200, 198, + 192, 4, 96, 70, 227, 237, 104, 17, 67, 9, 211]), + + 256: new Uint8Array([41, 213, 121, 140, 181, 227, 200, 97, 100, 133, 58, + 227, 106, 115, 25, 63, 77, 51, 26, 57, 238, 140, 99, 63, 71, 211, + 128, 84, 115, 26, 236, 52, 103, 81, 145, 14, 101, 161, 181, 58, 135, + 193, 56, 167, 214, 220, 5, 52, 85, 222, 183, 27, 101, 134, 86, 155, + 64, 148, 124, 212, 219, 251, 65, 42, 32, 44, 128, 2, 50, 128, 221, + 22, 238, 56, 189, 83, 28, 122, 121, 157, 215, 135, 151, 128, 233, + 193, 65, 190, 86, 148, 191, 140, 196, 120, 8, 172, 100, 166, 254, + 41, 245, 75, 56, 6, 166, 244, 178, 111, 234, 23, 4, 107, 6, 22, 132, + 187, 230, 17, 71, 172, 113, 238, 73, 4, 180, 90, 103, 77, 37, 51, + 118, 112, 129, 238, 199, 7, 222, 122, 173, 30, 232, 178, 233, 234, + 144, 98, 14, 234, 112, 77, 68, 62, 62, 159, 230, 101, 98, 43, 2, + 204, 69, 156, 86, 104, 128, 34, 128, 7, 173, 90, 120, 33, 104, 59, + 45, 251, 93, 51, 240, 232, 60, 94, 189, 134, 90, 20, 184, 122, 29, + 225, 85, 213, 38, 116, 159, 80, 69, 106, 168, 236, 201, 69, 140, 98, + 240, 45, 160, 133, 225, 106, 45, 245, 212, 160, 176, 128, 27, 114, + 153, 182, 144, 145, 214, 72, 196, 138, 183, 87, 61, 245, 150, 56, + 82, 158, 224, 50, 114, 125, 122, 172, 161, 129, 234, 70, 63, 245, + 136, 30, 136, 9, 128, 220, 229, 157, 222, 195, 149, 189, 70, 8, 71, + 40, 195, 93, 27, 7, 234, 164, 175, 102, 201, 149, 115, 248, 179, + 125, 66, 122, 194, 26, 61, 218, 198, 181, 152, 140, 199, 48, 148, + 31, 14, 241, 197, 3, 70, 128, 239, 32, 86, 15, 215, 86, 245, 190, + 95, 141, 41, 111, 0, 232, 28, 152, 67, 87, 197, 255, 118, 13, 251, + 71, 84, 22, 231, 134, 188, 175, 115, 138, 37, 199, 5, 238, 199, 2, + 99, 203, 75, 62, 231, 21, 150, 239, 94, 201, 185, 219, 58, 210, 228, + 151, 131, 76, 148, 104, 60, 74, 82, 6, 168, 49, 251, 182, 3, 232, + 173, 210, 201, 19, 101, 166, 7, 94, 11, 194, 211, 146, 229, 75, 241, + 15, 50, 187, 36, 175, 78, 227, 98, 224, 3, 95, 209, 93, 126, 112, + 178, 29, 18, 108, 241, 232, 79, 210, 41, 2, 238, 208, 190, 171, 134, + 147, 188, 191, 229, 122, 32, 209, 166, 118, 129, 223, 130, 214, 195, + 89, 67, 94, 218, 155, 185, 0, 144, 255, 132, 213, 25, 59, 83, 242, + 57, 69, 148, 109, 133, 61, 163, 30, 214, 254, 54, 169, 3, 217, 77, + 66, 123, 193, 204, 199, 109, 123, 49, 186, 223, 229, 8, 230, 164, + 171, 196, 145, 225, 10, 111, 248, 111, 164, 216, 54, 225, 253]) + }; + + // Replace the last block of each ciphertext with bad padding below for decryption errors + var badPadding = { + 128: { + "zeroPadChar": new Uint8Array([238, 27, 248, 169, 218, 138, 164, 86, 207, 102, 36, 223, 6, 166, 77, 14]), + "bigPadChar": new Uint8Array([91, 67, 119, 104, 252, 238, 175, 144, 17, 75, 12, 163, 212, 52, 46, 51]), + "inconsistentPadChars": new Uint8Array([135, 101, 112, 208, 3, 106, 226, 20, 25, 219, 79, 94, 58, 212, 242, 192]) + }, + 192: { + "zeroPadChar": new Uint8Array([22, 158, 50, 15, 168, 47, 19, 194, 182, 133, 184, 65, 36, 43, 177, 254]), + "bigPadChar": new Uint8Array([207, 110, 28, 160, 165, 213, 48, 213, 163, 242, 15, 78, 96, 117, 106, 87]), + "inconsistentPadChars": new Uint8Array([143, 227, 12, 112, 216, 207, 136, 167, 78, 137, 93, 30, 50, 75, 102, 101]) + }, + 256: { + "zeroPadChar": new Uint8Array([1, 253, 141, 214, 30, 193, 254, 68, 140, 200, 157, 110, 200, 89, 177, 129]), + "bigPadChar": new Uint8Array([88, 7, 110, 221, 74, 34, 97, 109, 99, 25, 189, 222, 94, 90, 27, 60]), + "inconsistentPadChars": new Uint8Array([152, 54, 60, 148, 59, 136, 193, 21, 77, 140, 170, 67, 120, 74, 106, 62]) + } + }; + + var keyLengths = [128, 192, 256]; + + // All the scenarios that should succeed, if the key has "encrypt" usage + var passing = []; + keyLengths.forEach(function(keyLength) { + passing.push({ + name: "AES-CBC " + keyLength.toString() + "-bit key", + keyBuffer: keyBytes[keyLength], + key: null, + algorithm: {name: "AES-CBC", iv: iv}, + plaintext: plaintext, + result: ciphertext[keyLength] + }); + }); + + // Scenarios that should fail because of a bad iv length, causing an OperationError + var failing = []; + keyLengths.forEach(function(keyLength) { + var shortIv = iv.slice(0, 8); + failing.push({ + name: "AES-CBC " + keyLength.toString() + "-bit key, 64-bit IV", + keyBuffer: keyBytes[keyLength], + key: null, + algorithm: {name: "AES-CBC", iv: shortIv}, + plaintext: plaintext, + result: ciphertext[keyLength] + }); + + var longIv = new Uint8Array(24); + longIv.set(iv, 0); + longIv.set(iv.slice(0, 8), 16); + failing.push({ + name: "AES-CBC " + keyLength.toString() + "-bit key, 192-bit IV", + keyBuffer: keyBytes[keyLength], + key: null, + algorithm: {name: "AES-CBC", iv: longIv}, + plaintext: plaintext, + result: ciphertext[keyLength] + }); + }); + + // Scenarios that should fail decryption because of bad padding + var decryptionFailing = []; + keyLengths.forEach(function(keyLength) { + ["zeroPadChar", "bigPadChar", "inconsistentPadChars"].forEach(function(paddingProblem) { + var badCiphertext = new Uint8Array(ciphertext[keyLength].byteLength); + badCiphertext.set(ciphertext[keyLength].slice(0, ciphertext[keyLength].byteLength - 16)); + badCiphertext.set(badPadding[keyLength][paddingProblem]); + + decryptionFailing.push({ + name: "AES-CBC " + keyLength.toString() + "-bit key, " + paddingProblem, + keyBuffer: keyBytes[keyLength], + key: null, + algorithm: {name: "AES-CBC", iv: iv}, + plaintext: plaintext, + result: badCiphertext + }); + }); + }); + + return {passing: passing, failing: failing, decryptionFailing: decryptionFailing}; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_ctr.https.any.js b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_ctr.https.any.js new file mode 100644 index 0000000000..9f0c66900e --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_ctr.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: encrypt() Using AES-CTR +// META: script=aes_ctr_vectors.js +// META: script=aes.js +// META: timeout=long + +run_test(); diff --git a/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_ctr_vectors.js b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_ctr_vectors.js new file mode 100644 index 0000000000..201dff83ce --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_ctr_vectors.js @@ -0,0 +1,123 @@ + +// aes_ctr_vectors.js + +// The following function returns an array of test vectors +// for the subtleCrypto encrypt method. +// +// Each test vector has the following fields: +// name - a unique name for this vector +// keyBuffer - an arrayBuffer with the key data in raw form +// key - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// algorithm - the value of the AlgorithmIdentifier parameter to provide to encrypt +// plaintext - the text to encrypt +// result - the expected result (usually just ciphertext, sometimes with added authentication) +function getTestVectors() { + // Before we can really start, we need to fill a bunch of buffers with data + var plaintext = new Uint8Array([84, 104, 105, 115, 32, 115, + 112, 101, 99, 105, 102, 105, 99, 97, 116, 105, 111, 110, + 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32, + 74, 97, 118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80, + 73, 32, 102, 111, 114, 32, 112, 101, 114, 102, 111, 114, + 109, 105, 110, 103, 32, 98, 97, 115, 105, 99, 32, 99, 114, + 121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32, + 111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105, + 110, 32, 119, 101, 98, 32, 97, 112, 112, 108, 105, 99, 97, + 116, 105, 111, 110, 115, 44, 32, 115, 117, 99, 104, 32, 97, + 115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32, 115, + 105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110, + 101, 114, 97, 116, 105, 111, 110, 32, 97, 110, 100, 32, + 118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110, + 44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121, 112, + 116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99, + 114, 121, 112, 116, 105, 111, 110, 46, 32, 65, 100, 100, + 105, 116, 105, 111, 110, 97, 108, 108, 121, 44, 32, 105, + 116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, + 110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112, + 108, 105, 99, 97, 116, 105, 111, 110, 115, 32, 116, 111, + 32, 103, 101, 110, 101, 114, 97, 116, 101, 32, 97, 110, + 100, 47, 111, 114, 32, 109, 97, 110, 97, 103, 101, 32, 116, + 104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97, + 116, 101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115, + 115, 97, 114, 121, 32, 116, 111, 32, 112, 101, 114, 102, + 111, 114, 109, 32, 116, 104, 101, 115, 101, 32, 111, 112, + 101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115, + 101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32, + 65, 80, 73, 32, 114, 97, 110, 103, 101, 32, 102, 114, 111, + 109, 32, 117, 115, 101, 114, 32, 111, 114, 32, 115, 101, + 114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101, 110, + 116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111, + 99, 117, 109, 101, 110, 116, 32, 111, 114, 32, 99, 111, + 100, 101, 32, 115, 105, 103, 110, 105, 110, 103, 44, 32, + 97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110, 102, + 105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32, + 97, 110, 100, 32, 105, 110, 116, 101, 103, 114, 105, 116, + 121, 32, 111, 102, 32, 99, 111, 109, 109, 117, 110, 105, + 99, 97, 116, 105, 111, 110, 115, 46]); + + // We want some random key bytes of various sizes. + // These were randomly generated from a script. + var keyBytes = { + 128: new Uint8Array([222, 192, 212, 252, 191, 60, 71, + 65, 200, 146, 218, 189, 28, 212, 192, 78]), + 192: new Uint8Array([208, 238, 131, 65, 63, 68, 196, 63, 186, 208, + 61, 207, 166, 18, 99, 152, 29, 109, 221, 95, 240, 30, 28, 246]), + 256: new Uint8Array([103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236, + 233, 204, 58, 249, 16, 229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116, + 216, 0, 11, 191, 48]) + } + + // AES-CTR needs a 16 byte (128 bit) counter. + var counter = new Uint8Array([85, 170, 248, 155, 168, 148, 19, 213, 78, 167, 39, + 167, 108, 39, 162, 132]); + + + // Results. These were created using the Python cryptography module. + + // AES-CTR produces ciphertext + var ciphertext = { + 128: new Uint8Array([233, 17, 117, 253, 164, 245, 234, 87, 197, 43, 13, 0, 11, 190, 152, 175, 104, 192, 165, 144, 88, 174, 237, 138, 181, 183, 6, 53, 3, 161, 206, 71, 13, 121, 218, 209, 116, 249, 10, 170, 250, 165, 68, 157, 132, 141, 200, 178, 197, 87, 209, 231, 250, 75, 154, 65, 162, 251, 30, 159, 234, 20, 20, 181, 147, 218, 180, 12, 4, 241, 75, 79, 129, 64, 15, 228, 60, 147, 153, 1, 129, 176, 150, 161, 85, 97, 22, 154, 234, 23, 127, 16, 4, 22, 226, 11, 104, 16, 176, 14, 225, 176, 79, 239, 103, 243, 190, 222, 40, 186, 244, 212, 29, 57, 125, 175, 21, 17, 233, 2, 13, 119, 102, 233, 230, 4, 16, 222, 56, 225, 67, 45, 191, 250, 15, 153, 45, 193, 240, 212, 117, 101, 68, 232, 199, 101, 175, 125, 247, 6, 249, 14, 0, 157, 185, 56, 76, 51, 228, 77, 234, 84, 60, 42, 119, 187, 213, 32, 34, 222, 65, 231, 215, 26, 73, 141, 231, 254, 185, 118, 14, 180, 126, 80, 51, 102, 200, 141, 204, 45, 26, 56, 119, 136, 222, 45, 143, 120, 231, 44, 43, 221, 136, 21, 188, 138, 84, 232, 208, 238, 226, 117, 104, 60, 165, 4, 18, 144, 240, 49, 173, 90, 68, 84, 239, 161, 124, 196, 144, 119, 24, 243, 239, 75, 117, 254, 219, 209, 53, 131, 37, 79, 68, 26, 21, 168, 163, 50, 59, 18, 244, 11, 143, 190, 188, 129, 108, 249, 180, 104, 216, 215, 165, 160, 251, 84, 132, 152, 195, 154, 110, 216, 70, 21, 248, 148, 146, 152, 56, 174, 248, 227, 1, 102, 15, 118, 182, 50, 73, 63, 35, 112, 159, 237, 253, 94, 16, 127, 120, 38, 127, 51, 27, 96, 163, 140, 20, 111, 151, 16, 72, 74, 74, 205, 239, 241, 16, 179, 183, 116, 95, 248, 58, 168, 203, 93, 233, 225, 91, 17, 226, 10, 120, 85, 114, 4, 31, 40, 82, 161, 152, 17, 86, 237, 207, 7, 228, 110, 182, 65, 68, 68, 156, 206, 116, 185, 204, 148, 22, 58, 111, 218, 138, 225, 146, 25, 114, 29, 96, 183, 87, 181, 181, 236, 113, 141, 171, 213, 9, 84, 182, 230, 163, 147, 246, 86, 246, 52, 111, 64, 34, 157, 12, 80, 224, 28, 21, 112, 31, 42, 79, 229, 210, 90, 23, 78, 223, 155, 144, 238, 12, 14, 191, 158, 6, 181, 254, 0, 85, 134, 56, 161, 234, 55, 129, 64, 59, 12, 146, 6, 217, 232, 20, 214, 167, 159, 183, 165, 96, 96, 225, 199, 23, 106, 243, 108, 106, 26, 214, 53, 152, 26, 155, 253, 128, 7, 216, 207, 109, 159, 147, 240, 232, 226, 43, 147, 169, 162, 204, 215, 9, 10, 177, 223, 99, 206, 163, 240, 64]), + + 192: new Uint8Array([98, 123, 235, 65, 14, 86, 80, 133, 88, 104, 244, 125, 165, 185, 163, 4, 3, 230, 62, 58, 113, 222, 46, 210, 17, 155, 95, 19, 125, 125, 70, 234, 105, 54, 23, 246, 114, 9, 237, 191, 9, 194, 34, 254, 156, 11, 50, 216, 80, 178, 185, 221, 132, 154, 27, 85, 82, 49, 241, 123, 23, 106, 119, 134, 203, 0, 151, 66, 149, 218, 124, 247, 227, 233, 236, 184, 88, 234, 174, 250, 83, 168, 33, 15, 122, 26, 96, 213, 210, 4, 52, 92, 20, 12, 64, 12, 209, 197, 69, 100, 15, 56, 60, 63, 241, 52, 18, 189, 93, 146, 47, 60, 33, 200, 218, 243, 43, 169, 17, 108, 19, 199, 174, 33, 107, 186, 57, 95, 167, 138, 180, 187, 53, 113, 208, 148, 190, 48, 167, 53, 209, 52, 153, 184, 231, 63, 168, 54, 179, 238, 93, 130, 125, 3, 149, 119, 60, 25, 142, 150, 183, 193, 29, 18, 3, 219, 235, 219, 26, 116, 217, 196, 108, 6, 96, 103, 212, 48, 227, 91, 124, 77, 181, 169, 18, 111, 123, 83, 26, 169, 230, 88, 103, 185, 153, 93, 143, 152, 142, 231, 41, 226, 226, 156, 179, 206, 212, 67, 18, 193, 187, 53, 252, 214, 15, 228, 246, 131, 170, 101, 134, 212, 100, 170, 146, 47, 57, 125, 50, 230, 51, 246, 74, 175, 129, 196, 178, 206, 176, 52, 153, 39, 77, 24, 186, 99, 137, 83, 105, 111, 168, 35, 176, 24, 29, 170, 223, 74, 160, 138, 247, 12, 102, 233, 136, 59, 172, 228, 242, 84, 13, 34, 155, 80, 80, 87, 180, 143, 129, 61, 213, 54, 41, 8, 183, 102, 126, 179, 127, 77, 55, 176, 152, 41, 131, 85, 86, 225, 87, 216, 139, 226, 196, 195, 210, 34, 33, 161, 249, 153, 205, 197, 128, 41, 28, 121, 6, 159, 25, 211, 168, 137, 26, 217, 249, 113, 81, 141, 18, 1, 250, 228, 68, 238, 74, 54, 99, 167, 236, 176, 199, 148, 161, 143, 156, 51, 189, 204, 59, 240, 151, 170, 85, 63, 23, 38, 152, 199, 12, 81, 217, 244, 178, 231, 249, 159, 224, 107, 214, 58, 127, 116, 143, 219, 155, 80, 55, 213, 171, 80, 127, 235, 20, 247, 12, 104, 228, 147, 202, 124, 143, 110, 223, 76, 221, 154, 175, 143, 185, 237, 222, 189, 104, 218, 72, 244, 55, 253, 138, 183, 92, 231, 68, 176, 239, 171, 100, 10, 63, 61, 194, 228, 15, 133, 216, 45, 60, 135, 203, 142, 127, 153, 172, 223, 213, 230, 220, 189, 223, 234, 156, 134, 238, 220, 251, 104, 209, 117, 175, 47, 46, 148, 6, 61, 216, 215, 39, 30, 116, 212, 45, 112, 202, 227, 198, 98, 253, 97, 177, 120, 74, 238, 68, 99, 240, 96, 43, 88, 166]), + + 256: new Uint8Array([55, 82, 154, 67, 47, 80, 186, 78, 83, 56, 95, 130, 102, 236, 61, 236, 204, 236, 234, 222, 122, 226, 147, 149, 233, 41, 16, 118, 201, 91, 185, 162, 79, 71, 146, 252, 221, 110, 165, 137, 75, 129, 94, 219, 93, 94, 64, 34, 250, 190, 5, 90, 6, 177, 167, 224, 25, 121, 85, 91, 87, 152, 56, 100, 191, 35, 1, 156, 177, 179, 127, 253, 173, 176, 87, 247, 40, 207, 178, 175, 10, 51, 209, 70, 52, 76, 251, 160, 172, 203, 77, 191, 97, 58, 123, 238, 82, 60, 166, 214, 134, 14, 71, 74, 156, 15, 77, 6, 141, 76, 10, 205, 148, 204, 85, 203, 242, 30, 66, 133, 202, 21, 17, 108, 151, 2, 15, 44, 51, 180, 88, 80, 8, 248, 254, 151, 201, 226, 156, 6, 39, 197, 212, 124, 72, 217, 75, 232, 139, 155, 22, 199, 242, 223, 116, 10, 141, 42, 7, 85, 99, 5, 184, 43, 145, 159, 122, 135, 202, 46, 209, 157, 178, 114, 98, 194, 119, 194, 19, 242, 167, 236, 162, 94, 90, 106, 219, 234, 67, 11, 162, 225, 6, 17, 152, 23, 16, 84, 40, 90, 255, 158, 8, 105, 198, 56, 220, 213, 36, 203, 241, 242, 85, 218, 103, 90, 202, 214, 215, 134, 121, 169, 149, 139, 122, 143, 155, 178, 29, 217, 197, 128, 173, 25, 111, 154, 14, 76, 106, 101, 0, 215, 187, 33, 223, 116, 205, 89, 52, 206, 60, 77, 141, 31, 57, 211, 74, 42, 219, 88, 210, 36, 196, 128, 151, 136, 124, 222, 157, 59, 225, 70, 163, 234, 59, 173, 228, 198, 134, 76, 249, 228, 69, 181, 196, 194, 179, 239, 78, 43, 143, 94, 234, 10, 177, 192, 185, 171, 231, 164, 254, 91, 44, 11, 29, 148, 223, 107, 18, 149, 61, 50, 115, 38, 14, 128, 189, 9, 77, 236, 151, 163, 23, 122, 156, 236, 11, 80, 66, 190, 24, 4, 4, 12, 148, 57, 64, 59, 143, 114, 247, 66, 111, 167, 86, 173, 98, 102, 207, 44, 134, 89, 231, 64, 50, 157, 208, 210, 79, 159, 133, 73, 118, 98, 202, 215, 57, 247, 29, 97, 116, 1, 28, 119, 248, 243, 31, 180, 66, 38, 40, 141, 251, 134, 129, 126, 241, 113, 22, 50, 28, 113, 187, 158, 217, 125, 182, 233, 144, 246, 32, 88, 88, 15, 0, 102, 131, 67, 31, 34, 150, 98, 241, 213, 227, 205, 175, 254, 3, 53, 70, 124, 167, 38, 53, 104, 140, 147, 158, 200, 179, 45, 100, 101, 246, 81, 166, 53, 247, 60, 10, 78, 127, 10, 173, 176, 232, 31, 91, 203, 250, 236, 38, 113, 172, 151, 253, 194, 253, 50, 242, 76, 148, 23, 117, 195, 122, 104, 16, 212, 177, 113, 188, 138, 186, 144, 168, 102, 3]) + }; + + var keyLengths = [128, 192, 256]; + + // All the scenarios that should succeed, if the key has "encrypt" usage + var passing = []; + keyLengths.forEach(function(keyLength) { + passing.push({ + name: "AES-CTR " + keyLength.toString() + "-bit key", + keyBuffer: keyBytes[keyLength], + key: null, + algorithm: {name: "AES-CTR", counter: counter, length: 64}, + plaintext: plaintext, + result: ciphertext[keyLength] + }); + }); + + // Scenarios that should fail because of a bad length parameter, causing an OperationError + var failing = []; + keyLengths.forEach(function(keyLength) { + failing.push({ + name: "AES-CTR " + keyLength.toString() + "-bit key, 0-bit counter", + keyBuffer: keyBytes[keyLength], + key: null, + algorithm: {name: "AES-CTR", counter: counter, length: 0}, + plaintext: plaintext, + result: ciphertext[keyLength] + }); + + failing.push({ + name: "AES-CTR " + keyLength.toString() + "-bit key, 129-bit counter", + keyBuffer: keyBytes[keyLength], + key: null, + algorithm: {name: "AES-CTR", counter: counter, length: 129}, + plaintext: plaintext, + result: ciphertext[keyLength] + }); + }); + + return {passing: passing, failing: failing, decryptionFailing: []}; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_gcm.https.any.js b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_gcm.https.any.js new file mode 100644 index 0000000000..8f1103d8f4 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_gcm.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: encrypt() Using AES-GCM +// META: script=aes_gcm_vectors.js +// META: script=aes.js +// META: timeout=long + +run_test(); diff --git a/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_gcm_vectors.js b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_gcm_vectors.js new file mode 100644 index 0000000000..72566ded62 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/aes_gcm_vectors.js @@ -0,0 +1,262 @@ + +// aes_gcm_vectors.js + +// The following function returns an array of test vectors +// for the subtleCrypto encrypt method. +// +// Each test vector has the following fields: +// name - a unique name for this vector +// keyBuffer - an arrayBuffer with the key data in raw form +// key - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// algorithm - the value of the AlgorithmIdentifier parameter to provide to encrypt +// plaintext - the text to encrypt +// result - the expected result (usually just ciphertext, sometimes with added authentication) +function getTestVectors() { + // Before we can really start, we need to fill a bunch of buffers with data + var plaintext = new Uint8Array([84, 104, 105, 115, 32, 115, + 112, 101, 99, 105, 102, 105, 99, 97, 116, 105, 111, 110, + 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32, + 74, 97, 118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80, + 73, 32, 102, 111, 114, 32, 112, 101, 114, 102, 111, 114, + 109, 105, 110, 103, 32, 98, 97, 115, 105, 99, 32, 99, 114, + 121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32, + 111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105, + 110, 32, 119, 101, 98, 32, 97, 112, 112, 108, 105, 99, 97, + 116, 105, 111, 110, 115, 44, 32, 115, 117, 99, 104, 32, 97, + 115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32, 115, + 105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110, + 101, 114, 97, 116, 105, 111, 110, 32, 97, 110, 100, 32, + 118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110, + 44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121, 112, + 116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99, + 114, 121, 112, 116, 105, 111, 110, 46, 32, 65, 100, 100, + 105, 116, 105, 111, 110, 97, 108, 108, 121, 44, 32, 105, + 116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, + 110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112, + 108, 105, 99, 97, 116, 105, 111, 110, 115, 32, 116, 111, + 32, 103, 101, 110, 101, 114, 97, 116, 101, 32, 97, 110, + 100, 47, 111, 114, 32, 109, 97, 110, 97, 103, 101, 32, 116, + 104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97, + 116, 101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115, + 115, 97, 114, 121, 32, 116, 111, 32, 112, 101, 114, 102, + 111, 114, 109, 32, 116, 104, 101, 115, 101, 32, 111, 112, + 101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115, + 101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32, + 65, 80, 73, 32, 114, 97, 110, 103, 101, 32, 102, 114, 111, + 109, 32, 117, 115, 101, 114, 32, 111, 114, 32, 115, 101, + 114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101, 110, + 116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111, + 99, 117, 109, 101, 110, 116, 32, 111, 114, 32, 99, 111, + 100, 101, 32, 115, 105, 103, 110, 105, 110, 103, 44, 32, + 97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110, 102, + 105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32, + 97, 110, 100, 32, 105, 110, 116, 101, 103, 114, 105, 116, + 121, 32, 111, 102, 32, 99, 111, 109, 109, 117, 110, 105, + 99, 97, 116, 105, 111, 110, 115, 46]); + + // We want some random key bytes of various sizes. + // These were randomly generated from a script. + var keyBytes = { + 128: new Uint8Array([222, 192, 212, 252, 191, 60, 71, + 65, 200, 146, 218, 189, 28, 212, 192, 78]), + 192: new Uint8Array([208, 238, 131, 65, 63, 68, 196, 63, 186, 208, + 61, 207, 166, 18, 99, 152, 29, 109, 221, 95, 240, 30, 28, 246]), + 256: new Uint8Array([103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236, + 233, 204, 58, 249, 16, 229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116, + 216, 0, 11, 191, 48]) + } + + // AES-GCM needs an IV of no more than 2^64 - 1 bytes. Well, 32 bytes is okay then. + var iv = new Uint8Array([58, 146, 115, 42, 166, 234, 57, + 191, 57, 134, 224, 199, 63, 169, 32, 0, 32, 33, 117, 56, + 94, 248, 173, 234, 194, 200, 115, 53, 235, 146, 141, 212]); + + // Authenticated encryption via AES-GCM requires additional data that + // will be checked. We use the ASCII encoded Editorial Note + // following the Abstract of the Web Cryptography API recommendation. + var additionalData = new Uint8Array([84, 104, 101, 114, 101, + 32, 97, 114, 101, 32, 55, 32, 102, 117, 114, 116, 104, 101, + 114, 32, 101, 100, 105, 116, 111, 114, 105, 97, 108, 32, + 110, 111, 116, 101, 115, 32, 105, 110, 32, 116, 104, 101, + 32, 100, 111, 99, 117, 109, 101, 110, 116, 46]); + + // The length of the tag defaults to 16 bytes (128 bit). + var tag = { + 128: new Uint8Array([194, 226, 198, 253, 239, 28, + 197, 240, 123, 216, 176, 151, 239, 200, 184, 183]), + 192: new Uint8Array([183, 57, 32, 144, 164, 76, 121, 77, 58, + 86, 62, 132, 53, 130, 96, 225]), + 256: new Uint8Array([188, 239, 241, 48, 159, 21, 213, 0, 241, + 42, 85, 76, 194, 28, 49, 60]) + }; + + var tag_with_empty_ad = { + 128: new Uint8Array([222, 51, 11, 23, 36, 222, 250, 248, 27, 98, 30, 81, 150, 35, 220, 198]), + 192: new Uint8Array([243, 11, 130, 112, 169, 239, 114, 238, 185, 219, 93, 1, 95, 108, 184, 183]), + 256: new Uint8Array([244, 186, 86, 203, 154, 37, 191, 248, 246, 57, 139, 130, 224, 47, 217, 238]) + }; + + + // Results. These were created using the Python cryptography module. + + // AES-GCM produces ciphertext and a tag. + var ciphertext = { + 128: new Uint8Array([180, 241, 40, 183, 105, + 52, 147, 238, 224, 175, 175, 236, 168, 244, 241, 121, 9, + 202, 225, 237, 56, 216, 253, 254, 186, 102, 111, 207, 228, + 190, 130, 177, 159, 246, 6, 53, 249, 113, 228, 254, 81, + 126, 253, 191, 100, 43, 251, 147, 107, 91, 166, 231, 201, + 241, 180, 214, 112, 47, 123, 164, 186, 134, 54, 65, 22, + 181, 201, 82, 236, 59, 52, 139, 172, 39, 41, 89, 123, 62, + 102, 167, 82, 150, 250, 93, 96, 169, 135, 89, 245, 255, + 164, 192, 169, 159, 25, 16, 139, 145, 76, 4, 144, 131, + 148, 197, 204, 46, 23, 110, 193, 228, 127, 120, 242, 24, + 54, 240, 181, 162, 98, 244, 249, 68, 134, 122, 126, 151, + 38, 108, 116, 68, 150, 109, 38, 194, 21, 159, 140, 205, + 183, 35, 97, 151, 186, 120, 145, 22, 235, 22, 210, 223, + 187, 143, 162, 183, 93, 196, 104, 51, 96, 53, 234, 250, + 184, 76, 237, 157, 37, 203, 226, 87, 222, 75, 240, 95, 218, + 222, 64, 81, 165, 75, 201, 216, 190, 13, 116, 217, 69, 66, + 47, 161, 68, 247, 74, 253, 157, 181, 162, 121, 53, 32, 91, + 124, 230, 105, 224, 17, 187, 50, 61, 77, 103, 79, 71, 57, + 163, 116, 234, 149, 27, 105, 24, 31, 159, 3, 128, 130, 42, + 94, 125, 200, 142, 251, 148, 201, 17, 149, 232, 84, 50, 17, + 18, 203, 186, 226, 164, 227, 202, 76, 65, 16, 163, 224, + 132, 52, 31, 101, 129, 72, 171, 159, 42, 177, 253, 98, 86, + 201, 95, 117, 62, 12, 205, 78, 36, 126, 196, 121, 89, 185, + 37, 161, 66, 181, 117, 186, 71, 124, 132, 110, 120, 27, + 246, 163, 18, 13, 90, 200, 127, 82, 209, 241, 170, 73, 247, + 137, 96, 244, 254, 251, 119, 71, 156, 27, 107, 53, 33, 45, + 22, 0, 144, 48, 32, 11, 116, 21, 125, 246, 217, 171, 158, + 224, 142, 234, 141, 242, 168, 89, 154, 66, 227, 161, 182, + 96, 1, 88, 78, 12, 7, 239, 30, 206, 31, 89, 111, 107, 42, + 37, 241, 148, 232, 1, 8, 251, 117, 146, 183, 9, 48, 39, 94, + 59, 70, 230, 26, 165, 97, 156, 140, 141, 31, 62, 10, 206, + 55, 48, 207, 0, 197, 202, 197, 108, 133, 175, 80, 4, 16, + 154, 223, 255, 4, 196, 188, 178, 240, 29, 13, 120, 5, 225, + 202, 3, 35, 225, 158, 92, 152, 73, 205, 107, 157, 224, 245, + 99, 194, 171, 156, 245, 247, 183, 165, 40, 62, 200, 110, + 29, 151, 206, 100, 175, 88, 36, 242, 90, 4, 82, 73, 250, + 140, 245, 217, 9, 153, 35, 242, 206, 78, 197, 121, 115, 15, + 80, 128, 101, 191, 240, 91, 151, 249, 62, 62, 244, 18, 3, + 17, 135, 222, 210, 93, 149, 123]), + + 192: new Uint8Array([126, 160, 166, 112, 227, 212, 106, + 186, 175, 70, 24, 28, 86, 149, 31, 154, 156, 190, 244, 132, 44, 61, 149, + 242, 105, 67, 17, 136, 7, 146, 153, 170, 200, 214, 142, 205, 170, 225, + 85, 44, 241, 159, 255, 234, 10, 13, 37, 48, 255, 21, 141, 176, 60, 117, + 73, 130, 247, 204, 144, 102, 167, 89, 203, 235, 229, 129, 122, 253, 124, + 179, 115, 118, 163, 157, 67, 141, 122, 146, 209, 11, 112, 5, 230, 117, + 123, 184, 243, 99, 83, 10, 31, 166, 96, 1, 121, 44, 10, 241, 24, 43, + 184, 187, 25, 239, 246, 176, 108, 230, 127, 25, 42, 67, 202, 140, 179, + 104, 159, 75, 103, 43, 248, 98, 166, 179, 67, 0, 163, 227, 84, 40, 129, + 227, 198, 205, 7, 156, 16, 185, 24, 166, 59, 218, 197, 114, 74, 34, 126, + 22, 226, 226, 85, 212, 69, 83, 163, 185, 68, 109, 182, 54, 209, 237, 96, + 184, 32, 53, 127, 175, 13, 146, 141, 115, 164, 184, 98, 245, 174, 223, + 46, 32, 167, 39, 103, 19, 210, 80, 131, 254, 103, 249, 247, 29, 120, 31, + 105, 241, 103, 169, 249, 93, 153, 74, 56, 53, 239, 157, 132, 236, 169, + 246, 242, 24, 113, 97, 128, 238, 152, 148, 31, 84, 8, 52, 105, 198, 116, + 103, 132, 48, 199, 23, 90, 24, 29, 63, 41, 117, 191, 57, 31, 209, 128, + 60, 119, 175, 84, 141, 177, 165, 169, 195, 35, 163, 105, 146, 157, 209, + 93, 149, 105, 160, 93, 231, 78, 201, 92, 235, 200, 89, 37, 50, 181, 30, + 213, 242, 59, 156, 219, 19, 158, 17, 224, 81, 108, 52, 87, 248, 101, 23, + 39, 107, 67, 151, 103, 230, 126, 202, 184, 118, 226, 18, 29, 93, 37, 208, + 40, 82, 113, 35, 157, 145, 152, 50, 253, 140, 47, 141, 192, 1, 148, 114, + 40, 10, 112, 79, 227, 16, 105, 247, 31, 49, 102, 195, 75, 183, 172, 254, + 188, 42, 89, 77, 38, 104, 1, 180, 106, 61, 71, 70, 35, 160, 103, 101, + 244, 26, 226, 37, 159, 155, 4, 107, 222, 219, 136, 37, 24, 246, 44, 23, + 44, 248, 132, 108, 59, 179, 99, 145, 132, 82, 53, 203, 111, 150, 55, + 123, 51, 214, 165, 108, 124, 179, 131, 174, 139, 224, 114, 96, 218, 181, + 243, 128, 198, 98, 115, 92, 95, 165, 23, 229, 108, 146, 14, 244, 162, + 37, 85, 201, 33, 44, 92, 106, 112, 185, 16, 189, 42, 114, 109, 59, 124, + 131, 16, 211, 31, 97, 29, 135, 61, 150, 75, 250, 207, 129, 38, 205, 187, + 186, 55, 207, 232, 24, 48, 232, 49, 226, 16, 12, 27, 70, 31, 124, 128, + 218, 100, 91, 200, 184, 78, 252, 100, 235, 62, 43, 69, 214, 163, 65, 14, + 44, 180]), + + 256: new Uint8Array([8, 97, 235, 113, 70, 32, 135, 131, + 210, 209, 124, 160, 255, 182, 9, 29, 125, 193, 27, 240, 129, 46, 2, 137, + 169, 142, 61, 7, 145, 54, 170, 207, 159, 111, 39, 95, 87, 63, 162, 27, + 6, 18, 219, 215, 116, 34, 90, 57, 114, 244, 102, 145, 67, 6, 51, 152, + 247, 165, 242, 116, 100, 219, 177, 72, 177, 17, 110, 67, 93, 219, 100, + 217, 20, 207, 89, 154, 45, 37, 105, 83, 67, 162, 140, 235, 129, 40, 177, + 202, 174, 54, 148, 55, 156, 193, 232, 249, 134, 163, 195, 51, 114, 116, + 65, 38, 73, 99, 96, 249, 224, 69, 17, 119, 186, 188, 181, 43, 78, 156, + 76, 138, 226, 63, 5, 248, 9, 94, 26, 1, 2, 235, 39, 174, 74, 47, 183, + 22, 40, 47, 47, 13, 100, 119, 12, 67, 178, 184, 56, 167, 238, 143, 13, + 44, 208, 185, 151, 108, 6, 17, 52, 122, 182, 210, 207, 42, 219, 37, 74, + 94, 126, 36, 249, 37, 32, 4, 218, 44, 238, 69, 56, 219, 31, 77, 173, 46, + 187, 103, 36, 112, 213, 252, 40, 87, 164, 240, 163, 159, 32, 129, 125, + 178, 108, 47, 28, 31, 36, 42, 115, 36, 14, 145, 195, 156, 191, 46, 163, + 249, 181, 31, 90, 73, 30, 72, 57, 223, 63, 60, 79, 140, 14, 117, 31, + 145, 222, 156, 121, 237, 32, 145, 143, 96, 12, 254, 35, 21, 21, 59, 168, + 171, 154, 217, 0, 59, 202, 175, 103, 214, 192, 175, 26, 18, 43, 54, 176, + 222, 75, 22, 7, 122, 253, 224, 145, 61, 42, 208, 73, 237, 84, 141, 209, + 213, 228, 46, 244, 59, 9, 68, 6, 35, 88, 189, 10, 62, 9, 85, 28, 44, 82, + 19, 153, 160, 178, 240, 56, 160, 244, 201, 173, 77, 61, 20, 227, 30, + 180, 167, 16, 105, 185, 193, 95, 207, 41, 23, 134, 78, 198, 182, 93, 24, + 89, 247, 231, 75, 233, 194, 137, 242, 114, 194, 190, 130, 138, 238, 94, + 137, 193, 194, 115, 137, 190, 207, 169, 83, 155, 14, 210, 160, 129, 195, + 161, 234, 221, 255, 114, 67, 98, 12, 93, 41, 65, 183, 244, 103, 247, + 101, 82, 246, 125, 87, 125, 78, 21, 186, 102, 205, 20, 40, 32, 201, 174, + 15, 52, 240, 217, 180, 162, 108, 6, 211, 41, 18, 135, 232, 184, 18, 188, + 169, 157, 190, 76, 166, 75, 176, 127, 39, 251, 22, 203, 153, 80, 49, + 241, 124, 137, 151, 123, 204, 43, 159, 190, 177, 196, 18, 117, 169, 46, + 152, 251, 45, 25, 164, 27, 145, 214, 228, 55, 15, 2, 131, 216, 80, 255, + 204, 175, 100, 59, 145, 15, 103, 40, 33, 45, 255, 200, 254, 172, 138, + 20, 58, 87, 182, 192, 148, 219, 41, 88, 230, 229, 70, 249]) + }; + + var keyLengths = [128, 192, 256]; + var tagLengths = [32, 64, 96, 104, 112, 120, 128]; + + // All the scenarios that should succeed, if the key has "encrypt" usage + var passing = []; + keyLengths.forEach(function(keyLength) { + tagLengths.forEach(function(tagLength) { + var byteCount = tagLength / 8; + + var result = new Uint8Array(ciphertext[keyLength].byteLength + byteCount); + result.set(ciphertext[keyLength], 0); + result.set(tag[keyLength].slice(0, byteCount), ciphertext[keyLength].byteLength); + passing.push({ + name: "AES-GCM " + keyLength.toString() + "-bit key, " + tagLength.toString() + "-bit tag", + keyBuffer: keyBytes[keyLength], + key: null, + algorithm: {name: "AES-GCM", iv: iv, additionalData: additionalData, tagLength: tagLength}, + plaintext: plaintext, + result: result + }); + + var noadresult = new Uint8Array(ciphertext[keyLength].byteLength + byteCount); + noadresult.set(ciphertext[keyLength], 0); + noadresult.set(tag_with_empty_ad[keyLength].slice(0, byteCount), ciphertext[keyLength].byteLength); + passing.push({ + name: "AES-GCM " + keyLength.toString() + "-bit key, no additional data, " + tagLength.toString() + "-bit tag", + keyBuffer: keyBytes[keyLength], + key: null, + algorithm: {name: "AES-GCM", iv: iv, tagLength: tagLength}, + plaintext: plaintext, + result: noadresult + }); + }); + }); + + // Scenarios that should fail because of a bad tag length, causing an OperationError + var failing = []; + keyLengths.forEach(function(keyLength) { + // First, make some tests for bad tag lengths + [24, 48, 72, 95, 129, 256].forEach(function(badTagLength) { + failing.push({ + name: "AES-GCM " + keyLength.toString() + "-bit key, illegal tag length " + badTagLength.toString() + "-bits", + keyBuffer: keyBytes[keyLength], + key: null, + algorithm: {name: "AES-GCM", iv: iv, additionalData: additionalData, tagLength: badTagLength}, + plaintext: plaintext, + result: ciphertext[keyLength] + }); + }); + }); + + return {passing: passing, failing: failing, decryptionFailing: []}; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/rsa.js b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/rsa.js new file mode 100644 index 0000000000..5eae06e474 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/rsa.js @@ -0,0 +1,378 @@ + +function run_test() { + var subtle = self.crypto.subtle; // Change to test prefixed implementations + + // When are all these tests really done? When all the promises they use have resolved. + var all_promises = []; + + // Source file rsa_vectors.js provides the getTestVectors method + // for the RSA-OAEP algorithm that drives these tests. + var vectors = getTestVectors(); + var passingVectors = vectors.passing; + var failingVectors = vectors.failing; + + // Test decryption, first, because encryption tests rely on that working + passingVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) + .then(function(vectors) { + // Get a one byte longer plaintext to encrypt + if (!("ciphertext" in vector)) { + return; + } + + promise_test(function(test) { + return subtle.decrypt(vector.algorithm, vector.privateKey, vector.ciphertext) + .then(function(plaintext) { + assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works")); + }, function(err) { + assert_unreached("Decryption should not throw error " + vector.name + ": " + err.message + "'"); + }); + }, vector.name + " decryption"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " decryption"); + }); + + all_promises.push(promise); + }); + + // Test decryption with an altered buffer + passingVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) + .then(function(vectors) { + // Get a one byte longer plaintext to encrypt + if (!("ciphertext" in vector)) { + return; + } + + promise_test(function(test) { + var ciphertext = copyBuffer(vector.ciphertext); + var operation = subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext) + .then(function(plaintext) { + assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works")); + }, function(err) { + assert_unreached("Decryption should not throw error " + vector.name + ": " + err.message + "'"); + }); + ciphertext[0] = 255 - ciphertext[0]; + return operation; + }, vector.name + " decryption with altered ciphertext"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " decryption with altered ciphertext"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to using publicKey to decrypt. + passingVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) + .then(function(vectors) { + promise_test(function(test) { + return subtle.decrypt(vector.algorithm, vector.publicKey, vector.ciphertext) + .then(function(plaintext) { + assert_unreached("Should have thrown error for using publicKey to decrypt in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message); + }); + }, vector.name + " using publicKey to decrypt"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " using publicKey to decrypt"); + }); + + all_promises.push(promise); + }); + + + // Check for failures due to no "decrypt" usage. + passingVectors.forEach(function(originalVector) { + var vector = Object.assign({}, originalVector); + + var promise = importVectorKeys(vector, ["encrypt"], ["unwrapKey"]) + .then(function(vectors) { + // Get a one byte longer plaintext to encrypt + promise_test(function(test) { + return subtle.decrypt(vector.algorithm, vector.publicKey, vector.ciphertext) + .then(function(plaintext) { + assert_unreached("Should have thrown error for no decrypt usage in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message); + }); + }, vector.name + " no decrypt usage"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " no decrypt usage"); + }); + + all_promises.push(promise); + }); + + + // Check for successful encryption even if plaintext is altered after call. + passingVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) + .then(function(vectors) { + promise_test(function(test) { + var plaintext = copyBuffer(vector.plaintext); + var operation = subtle.encrypt(vector.algorithm, vector.publicKey, plaintext) + .then(function(ciphertext) { + assert_equals(ciphertext.byteLength * 8, vector.privateKey.algorithm.modulusLength, "Ciphertext length matches modulus length"); + // Can we get the original plaintext back via decrypt? + return subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext) + .then(function(result) { + assert_true(equalBuffers(result, vector.plaintext), "Round trip returns original plaintext"); + return ciphertext; + }, function(err) { + assert_unreached("decrypt error for test " + vector.name + ": " + err.message + "'"); + }); + }) + .then(function(priorCiphertext) { + // Will a second encrypt give us different ciphertext, as it should? + return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext) + .then(function(ciphertext) { + assert_false(equalBuffers(priorCiphertext, ciphertext), "Two encrypts give different results") + }, function(err) { + assert_unreached("second time encrypt error for test " + vector.name + ": '" + err.message + "'"); + }); + }, function(err) { + assert_unreached("decrypt error for test " + vector.name + ": '" + err.message + "'"); + }); + + plaintext[0] = 255 - plaintext[0]; + return operation; + }, vector.name + " with altered plaintext"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext"); + }); + + all_promises.push(promise); + }); + + // Check for successful encryption. + passingVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) + .then(function(vectors) { + promise_test(function(test) { + return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext) + .then(function(ciphertext) { + assert_equals(ciphertext.byteLength * 8, vector.privateKey.algorithm.modulusLength, "Ciphertext length matches modulus length"); + + // Can we get the original plaintext back via decrypt? + return subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext) + .then(function(result) { + assert_true(equalBuffers(result, vector.plaintext), "Round trip returns original plaintext"); + return ciphertext; + }, function(err) { + assert_unreached("decrypt error for test " + vector.name + ": " + err.message + "'"); + }); + }) + .then(function(priorCiphertext) { + // Will a second encrypt give us different ciphertext, as it should? + return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext) + .then(function(ciphertext) { + assert_false(equalBuffers(priorCiphertext, ciphertext), "Two encrypts give different results") + }, function(err) { + assert_unreached("second time encrypt error for test " + vector.name + ": '" + err.message + "'"); + }); + }, function(err) { + assert_unreached("decrypt error for test " + vector.name + ": '" + err.message + "'"); + }); + }, vector.name); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name); + }); + + all_promises.push(promise); + }); + + // Check for failures due to too long plaintext. + passingVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) + .then(function(vectors) { + // Get a one byte longer plaintext to encrypt + var plaintext = new Uint8Array(vector.plaintext.byteLength + 1); + plaintext.set(plaintext, 0); + plaintext.set(new Uint8Array([32]), vector.plaintext.byteLength); + promise_test(function(test) { + return subtle.encrypt(vector.algorithm, vector.publicKey, plaintext) + .then(function(ciphertext) { + assert_unreached("Should have thrown error for too long plaintext in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "OperationError", "Should throw OperationError instead of " + err.message); + }); + }, vector.name + " too long plaintext"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " too long plaintext"); + }); + + all_promises.push(promise); + }); + + + // Check for failures due to using privateKey to encrypt. + passingVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) + .then(function(vectors) { + promise_test(function(test) { + return subtle.encrypt(vector.algorithm, vector.privateKey, vector.plaintext) + .then(function(ciphertext) { + assert_unreached("Should have thrown error for using privateKey to encrypt in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message); + }); + }, vector.name + " using privateKey to encrypt"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " using privateKey to encrypt"); + }); + + all_promises.push(promise); + }); + + + // Check for failures due to no "encrypt usage". + passingVectors.forEach(function(originalVector) { + var vector = Object.assign({}, originalVector); + + var promise = importVectorKeys(vector, [], ["decrypt"]) + .then(function(vectors) { + // Get a one byte longer plaintext to encrypt + promise_test(function(test) { + return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext) + .then(function(ciphertext) { + assert_unreached("Should have thrown error for no encrypt usage in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of " + err.message); + }); + }, vector.name + " no encrypt usage"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " no encrypt usage"); + }); + + all_promises.push(promise); + }); + + promise_test(function() { + return Promise.all(all_promises) + .then(function() {done();}) + .catch(function() {done();}) + }, "setup"); + + // A test vector has all needed fields for encryption, EXCEPT that the + // key field may be null. This function replaces that null with the Correct + // CryptoKey object. + // + // Returns a Promise that yields an updated vector on success. + function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) { + var publicPromise, privatePromise; + + if (vector.publicKey !== null) { + publicPromise = new Promise(function(resolve, reject) { + resolve(vector); + }); + } else { + publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, publicKeyUsages) + .then(function(key) { + vector.publicKey = key; + return vector; + }); // Returns a copy of the sourceBuffer it is sent. + function copyBuffer(sourceBuffer) { + var source = new Uint8Array(sourceBuffer); + var copy = new Uint8Array(sourceBuffer.byteLength) + + for (var i=0; i<source.byteLength; i++) { + copy[i] = source[i]; + } + + return copy; + } + + } + + if (vector.privateKey !== null) { + privatePromise = new Promise(function(resolve, reject) { + resolve(vector); + }); + } else { + privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, privateKeyUsages) + .then(function(key) { + vector.privateKey = key; + return vector; + }); + } + + return Promise.all([publicPromise, privatePromise]); + } + + // Returns a copy of the sourceBuffer it is sent. + function copyBuffer(sourceBuffer) { + var source = new Uint8Array(sourceBuffer); + var copy = new Uint8Array(sourceBuffer.byteLength) + + for (var i=0; i<source.byteLength; i++) { + copy[i] = source[i]; + } + + return copy; + } + + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + return; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/rsa_oaep.https.any.js b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/rsa_oaep.https.any.js new file mode 100644 index 0000000000..20fd17c571 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/rsa_oaep.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: encrypt() Using RSA-OAEP +// META: script=rsa_vectors.js +// META: script=rsa.js +// META: timeout=long + +run_test(); diff --git a/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/rsa_vectors.js b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/rsa_vectors.js new file mode 100644 index 0000000000..fcc732eddc --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/encrypt_decrypt/rsa_vectors.js @@ -0,0 +1,204 @@ + +// rsa_vectors.js + +// Data for testing RSA-OAEP with a 2048-bit modulus and 65537 public exponent. + +// The following function returns an array of test vectors +// for the subtleCrypto encrypt method. +// +// Each test vector has the following fields: +// name - a unique name for this vector +// publicKeyBuffer - an arrayBuffer with the key data +// publicKeyFormat - "spki" "jwk" +// publicKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// privateKeyBuffer - an arrayBuffer with the key data +// privateKeyFormat - "pkcs8" or "jwk" +// privateKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// algorithm - the value of the AlgorithmIdentifier parameter to provide to encrypt +// plaintext - the text to encrypt +// result - the expected result (usually just ciphertext, sometimes with added authentication) +function getTestVectors() { + var pkcs8 = new Uint8Array([48, 130, 4, 191, 2, 1, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 4, 130, 4, 169, 48, 130, 4, 165, 2, 1, 0, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1, 2, 130, 1, 1, 0, 139, 55, 92, 203, 135, 200, 37, 197, 255, 61, 83, 208, 9, 145, 110, 150, 65, 5, 126, 24, 82, 114, 39, 160, 122, 178, 38, 190, 16, 136, 129, 58, 59, 56, 187, 123, 72, 243, 119, 5, 81, 101, 250, 42, 147, 57, 210, 77, 198, 103, 213, 197, 186, 52, 39, 230, 164, 129, 23, 110, 172, 21, 255, 212, 144, 104, 49, 30, 28, 40, 59, 159, 58, 142, 12, 184, 9, 180, 99, 12, 80, 170, 143, 62, 69, 166, 11, 53, 158, 25, 191, 140, 187, 94, 202, 214, 78, 118, 31, 16, 149, 116, 63, 243, 106, 175, 92, 240, 236, 185, 127, 237, 173, 221, 166, 11, 91, 243, 93, 129, 26, 117, 184, 34, 35, 12, 250, 160, 25, 47, 173, 64, 84, 126, 39, 84, 72, 170, 51, 22, 191, 142, 43, 76, 224, 133, 79, 199, 112, 139, 83, 123, 162, 45, 19, 33, 11, 9, 174, 195, 122, 39, 89, 239, 192, 130, 161, 83, 27, 35, 169, 23, 48, 3, 125, 222, 78, 242, 107, 95, 150, 239, 220, 195, 159, 211, 76, 52, 90, 213, 28, 187, 228, 79, 229, 139, 138, 59, 78, 201, 151, 134, 108, 8, 109, 255, 27, 136, 49, 239, 10, 31, 234, 38, 60, 247, 218, 205, 3, 192, 76, 188, 194, 178, 121, 229, 127, 165, 185, 83, 153, 107, 251, 29, 214, 136, 23, 175, 127, 180, 44, 222, 247, 165, 41, 74, 87, 250, 194, 184, 173, 115, 159, 27, 2, 153, 2, 129, 129, 0, 251, 248, 51, 194, 198, 49, 201, 112, 36, 12, 142, 116, 133, 240, 106, 62, 162, 168, 72, 34, 81, 26, 134, 39, 221, 70, 78, 248, 175, 175, 113, 72, 209, 164, 37, 182, 184, 101, 125, 221, 82, 70, 131, 43, 142, 83, 48, 32, 197, 187, 181, 104, 133, 90, 106, 236, 62, 66, 33, 215, 147, 241, 220, 91, 47, 37, 132, 226, 65, 94, 72, 233, 162, 189, 41, 43, 19, 64, 49, 249, 156, 142, 180, 47, 192, 188, 208, 68, 155, 242, 44, 230, 222, 201, 112, 20, 239, 229, 172, 147, 235, 232, 53, 135, 118, 86, 37, 44, 187, 177, 108, 65, 91, 103, 177, 132, 210, 40, 69, 104, 162, 119, 213, 147, 53, 88, 92, 253, 2, 129, 129, 0, 214, 184, 206, 39, 199, 41, 93, 93, 22, 252, 53, 112, 237, 100, 200, 218, 147, 3, 250, 210, 148, 136, 193, 166, 94, 154, 215, 17, 249, 3, 112, 24, 125, 187, 253, 129, 49, 109, 105, 100, 139, 200, 140, 197, 200, 53, 81, 175, 255, 69, 222, 186, 207, 182, 17, 5, 247, 9, 228, 195, 8, 9, 185, 0, 49, 235, 214, 134, 36, 68, 150, 198, 246, 158, 105, 46, 189, 200, 20, 246, 66, 57, 244, 173, 21, 117, 110, 203, 120, 197, 165, 176, 153, 49, 219, 24, 48, 119, 197, 70, 163, 140, 76, 116, 56, 137, 173, 61, 62, 208, 121, 181, 98, 46, 208, 18, 15, 160, 225, 249, 59, 89, 61, 183, 216, 82, 224, 95, 2, 129, 128, 56, 135, 75, 157, 131, 247, 129, 120, 206, 45, 158, 252, 23, 92, 131, 137, 127, 214, 127, 48, 107, 191, 166, 159, 100, 238, 52, 35, 104, 206, 212, 124, 128, 195, 241, 206, 23, 122, 117, 141, 100, 186, 251, 12, 151, 134, 164, 66, 133, 250, 1, 205, 236, 53, 7, 205, 238, 125, 201, 183, 226, 178, 29, 60, 187, 204, 16, 14, 238, 153, 103, 132, 59, 5, 115, 41, 253, 204, 166, 41, 152, 237, 15, 17, 179, 140, 232, 176, 171, 199, 222, 57, 1, 124, 113, 207, 208, 174, 87, 84, 108, 85, 145, 68, 205, 208, 175, 208, 100, 95, 126, 168, 255, 7, 185, 116, 209, 237, 68, 253, 31, 142, 0, 245, 96, 191, 109, 69, 2, 129, 129, 0, 133, 41, 239, 144, 115, 207, 143, 123, 95, 249, 226, 26, 186, 223, 58, 65, 115, 211, 144, 6, 112, 223, 175, 89, 66, 106, 188, 223, 4, 147, 193, 61, 47, 29, 27, 70, 184, 36, 166, 172, 24, 148, 179, 217, 37, 37, 12, 24, 30, 52, 114, 193, 96, 120, 5, 110, 177, 154, 141, 40, 247, 31, 48, 128, 146, 117, 52, 129, 212, 148, 68, 253, 247, 140, 158, 166, 194, 68, 7, 220, 1, 142, 119, 211, 175, 239, 56, 91, 47, 247, 67, 158, 150, 35, 121, 65, 51, 45, 212, 70, 206, 190, 255, 219, 68, 4, 254, 79, 113, 89, 81, 97, 208, 22, 64, 44, 51, 77, 15, 87, 198, 26, 190, 79, 249, 244, 203, 249, 2, 129, 129, 0, 135, 216, 119, 8, 212, 103, 99, 228, 204, 190, 178, 209, 233, 113, 46, 91, 240, 33, 109, 112, 222, 148, 32, 165, 178, 6, 155, 116, 89, 185, 159, 93, 159, 127, 47, 173, 124, 215, 154, 174, 230, 122, 127, 154, 52, 67, 126, 60, 121, 168, 74, 240, 205, 141, 233, 223, 242, 104, 235, 12, 71, 147, 245, 1, 249, 136, 213, 64, 246, 211, 71, 92, 32, 121, 184, 34, 122, 35, 217, 104, 222, 196, 227, 198, 101, 3, 24, 113, 147, 69, 150, 48, 71, 43, 253, 182, 186, 29, 231, 134, 199, 151, 250, 111, 78, 166, 90, 42, 132, 25, 38, 47, 41, 103, 136, 86, 203, 115, 201, 189, 75, 200, 155, 94, 4, 27, 34, 119]); + var spki = new Uint8Array([48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1]); + + // Can optionally provide a label for encryption. We use the ASCII-encoded + // abstract from the candidate recommendation. + var label = new Uint8Array([84, 104, 101, 114, 101, 32, 97, 114, 101, 32, 55, 32, 102, 117, 114, 116, 104, 101, 114, 32, 101, 100, 105, 116, 111, 114, 105, 97, 108, 32, 110, 111, 116, 101, 115, 32, 105, 110, 32, 116, 104, 101, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46]); + + // overlong plaintext for RSA-OAEP + var plaintext = new Uint8Array([95, 77, 186, 79, 50, 12, 12, 232, 118, 114, 90, 252, 229, 251, 210, 91, 248, 62, 90, 113, 37, 160, 140, 175, 231, 60, 62, 186, 196, 33, 119, 157, 249, 213, 93, 24, 12, 58, 233, 148, 38, 69, 225, 216, 47, 238, 140, 157, 41, 75, 60, 177, 160, 138, 153, 49, 32, 27, 60, 14, 129, 252, 71, 202, 207, 131, 21, 162, 175, 102, 50, 65, 19, 195, 182, 98, 48, 195, 70, 8, 196, 244, 89, 54, 52, 206, 2, 178, 103, 54, 34, 119, 240, 168, 64, 202, 116, 188, 61, 26, 98, 54, 149, 44, 94, 215, 170, 248, 168, 254, 203, 221, 250, 117, 132, 230, 151, 140, 234, 93, 42, 91, 159, 183, 241, 180, 140, 139, 11, 229, 138, 48, 82, 2, 117, 77, 131, 118, 16, 115, 116, 121, 60, 240, 38, 170, 238, 83, 0, 114, 125, 131, 108, 215, 30, 113, 179, 69, 221, 178, 228, 68, 70, 255, 197, 185, 1, 99, 84, 19, 137, 13, 145, 14, 163, 128, 152, 74, 144, 25, 16, 49, 50, 63, 22, 219, 204, 157, 107, 225, 104, 184, 72, 133, 56, 76, 160, 62, 18, 96, 10, 193, 194, 72, 2, 138, 243, 114, 108, 201, 52, 99, 136, 46, 168, 192, 42, 171]); + + var ciphertext = { + "sha-1, no label": new Uint8Array([144, 30, 240, 156, 187, 254, 154, 73, 57, 185, 169, 196, 60, 207, 34, 51, 158, 21, 117, 247, 199, 67, 36, 73, 64, 117, 193, 146, 196, 11, 104, 153, 110, 176, 224, 77, 123, 21, 23, 116, 255, 247, 192, 11, 102, 23, 74, 36, 159, 195, 209, 166, 18, 60, 112, 166, 111, 214, 27, 103, 203, 84, 246, 131, 254, 1, 4, 158, 74, 68, 165, 147, 126, 53, 202, 225, 183, 61, 206, 18, 174, 160, 156, 208, 28, 76, 72, 144, 15, 175, 221, 117, 58, 196, 1, 86, 96, 118, 185, 184, 99, 128, 124, 241, 65, 166, 48, 68, 134, 94, 12, 188, 66, 185, 149, 251, 99, 159, 233, 153, 78, 148, 192, 227, 129, 64, 74, 225, 181, 84, 205, 178, 117, 21, 240, 151, 152, 220, 90, 7, 166, 11, 22, 43, 241, 47, 140, 239, 124, 225, 102, 49, 77, 148, 45, 128, 254, 67, 208, 223, 31, 176, 215, 233, 81, 78, 247, 41, 220, 108, 132, 85, 131, 247, 74, 178, 195, 109, 150, 132, 212, 59, 113, 150, 42, 24, 255, 14, 43, 19, 206, 116, 245, 55, 251, 58, 11, 0, 237, 227, 41, 231, 124, 17, 144, 10, 7, 14, 32, 248, 109, 192, 124, 172, 181, 111, 120, 33, 208, 36, 146, 52, 16, 108, 110, 11, 77, 218, 130, 224, 254, 189, 178, 2, 239, 12, 123, 16, 213, 96, 240, 186, 253, 199, 143, 6, 36, 24, 87, 131, 181, 34, 131, 202, 20, 161]), + "sha-256, no label": new Uint8Array([5, 49, 234, 235, 75, 140, 239, 142, 183, 125, 215, 54, 208, 150, 182, 220, 232, 64, 209, 100, 233, 117, 31, 134, 168, 213, 206, 208, 153, 153, 80, 111, 250, 240, 0, 235, 97, 83, 247, 69, 111, 49, 26, 233, 158, 71, 241, 32, 113, 80, 235, 151, 242, 152, 45, 183, 115, 190, 158, 153, 14, 138, 18, 152, 90, 95, 17, 90, 249, 6, 239, 12, 135, 14, 239, 60, 159, 202, 28, 91, 77, 94, 153, 4, 153, 182, 125, 0, 148, 173, 175, 141, 235, 223, 154, 93, 114, 51, 91, 84, 179, 209, 202, 38, 234, 9, 87, 214, 62, 6, 76, 233, 105, 213, 46, 83, 189, 96, 92, 143, 169, 50, 13, 158, 171, 226, 57, 238, 244, 112, 153, 85, 92, 25, 77, 27, 184, 223, 239, 254, 173, 107, 79, 215, 248, 242, 137, 144, 53, 92, 200, 238, 34, 163, 108, 72, 103, 240, 172, 234, 215, 244, 165, 2, 95, 21, 23, 247, 82, 167, 232, 192, 147, 83, 61, 12, 214, 89, 173, 96, 167, 220, 5, 4, 66, 32, 1, 152, 135, 1, 100, 55, 220, 201, 76, 111, 158, 130, 2, 176, 59, 201, 85, 235, 44, 121, 13, 63, 183, 199, 231, 126, 38, 18, 255, 165, 33, 218, 244, 103, 246, 64, 167, 73, 233, 225, 25, 21, 116, 190, 118, 226, 213, 92, 60, 254, 122, 147, 85, 26, 124, 40, 221, 178, 186, 107, 38, 195, 58, 48, 194, 55, 28, 216, 151, 77]), + "sha-384, no label": new Uint8Array([12, 35, 146, 227, 15, 146, 241, 244, 228, 172, 209, 180, 166, 253, 153, 249, 131, 198, 29, 202, 243, 155, 221, 222, 71, 178, 158, 173, 58, 221, 16, 74, 122, 134, 223, 31, 112, 153, 243, 104, 60, 101, 175, 254, 50, 157, 43, 202, 185, 80, 53, 236, 150, 193, 61, 171, 154, 12, 180, 185, 89, 96, 254, 224, 138, 165, 131, 85, 102, 161, 181, 125, 223, 84, 90, 201, 80, 80, 145, 52, 206, 208, 2, 115, 158, 166, 255, 62, 61, 228, 132, 31, 159, 169, 6, 22, 96, 236, 241, 5, 51, 233, 245, 12, 165, 22, 79, 50, 73, 68, 204, 123, 142, 58, 236, 105, 152, 163, 102, 249, 12, 250, 238, 121, 119, 101, 26, 13, 30, 141, 25, 75, 205, 28, 42, 0, 135, 41, 170, 1, 26, 157, 13, 140, 162, 113, 249, 142, 1, 91, 244, 102, 188, 217, 156, 217, 118, 134, 181, 146, 246, 111, 177, 54, 159, 84, 163, 88, 147, 122, 188, 249, 23, 223, 111, 57, 186, 220, 111, 95, 246, 48, 199, 172, 115, 185, 47, 173, 186, 221, 12, 41, 252, 224, 76, 167, 214, 42, 171, 82, 178, 100, 229, 162, 130, 188, 191, 2, 114, 28, 17, 158, 40, 233, 66, 108, 217, 150, 179, 121, 25, 115, 216, 162, 172, 244, 58, 44, 47, 103, 255, 136, 76, 26, 119, 184, 63, 195, 38, 140, 100, 12, 171, 65, 67, 54, 195, 31, 122, 105, 119, 228, 149, 16, 49, 212]), + "sha-512, no label": new Uint8Array([6, 38, 211, 70, 213, 37, 49, 19, 221, 192, 248, 206, 209, 145, 141, 136, 235, 240, 8, 105, 248, 128, 175, 137, 197, 230, 123, 179, 121, 75, 181, 138, 155, 246, 25, 229, 165, 89, 9, 65, 143, 108, 126, 20, 168, 88, 160, 197, 48, 66, 117, 2, 219, 122, 254, 96, 36, 147, 170, 111, 123, 168, 57, 151, 25, 139, 10, 233, 125, 219, 141, 58, 125, 174, 89, 38, 220, 25, 15, 5, 135, 69, 33, 5, 10, 49, 28, 253, 148, 251, 213, 53, 223, 8, 184, 64, 185, 94, 249, 211, 64, 53, 131, 136, 32, 2, 163, 61, 76, 9, 162, 189, 80, 71, 109, 237, 147, 9, 11, 147, 61, 254, 1, 185, 208, 228, 44, 219, 17, 163, 239, 184, 212, 197, 229, 210, 35, 236, 4, 117, 37, 187, 169, 26, 248, 95, 160, 165, 252, 21, 102, 253, 73, 115, 145, 124, 88, 143, 121, 128, 236, 70, 144, 101, 181, 54, 179, 64, 57, 232, 153, 72, 158, 96, 241, 79, 255, 90, 67, 16, 110, 43, 234, 153, 20, 57, 75, 83, 23, 184, 216, 25, 215, 52, 9, 241, 114, 21, 223, 184, 177, 247, 66, 98, 13, 15, 175, 204, 82, 81, 205, 22, 15, 92, 28, 99, 186, 234, 241, 33, 37, 210, 15, 8, 197, 30, 208, 97, 7, 42, 51, 173, 213, 171, 59, 154, 71, 53, 78, 88, 244, 50, 157, 33, 111, 143, 185, 61, 91, 118, 237, 245, 93, 91, 156, 36]), + "sha-1, with label": new Uint8Array([69, 12, 147, 43, 219, 95, 34, 61, 29, 64, 218, 190, 133, 69, 125, 35, 8, 73, 73, 154, 87, 194, 91, 201, 130, 111, 243, 86, 90, 124, 254, 130, 187, 133, 158, 32, 159, 234, 150, 98, 91, 246, 120, 166, 57, 233, 98, 7, 160, 58, 125, 113, 53, 79, 2, 202, 208, 104, 123, 195, 27, 84, 250, 98, 8, 169, 83, 207, 94, 31, 97, 31, 81, 0, 247, 153, 208, 99, 64, 185, 244, 213, 194, 62, 200, 171, 78, 245, 10, 62, 144, 176, 206, 94, 104, 172, 45, 57, 114, 196, 243, 166, 34, 67, 137, 41, 77, 6, 32, 225, 9, 234, 183, 32, 38, 40, 26, 93, 230, 191, 27, 192, 183, 67, 224, 156, 64, 189, 36, 27, 216, 57, 58, 164, 48, 164, 74, 218, 167, 196, 208, 221, 79, 102, 118, 23, 123, 26, 227, 53, 185, 196, 14, 233, 154, 6, 140, 233, 204, 153, 109, 163, 164, 226, 170, 207, 79, 123, 26, 188, 129, 124, 98, 82, 255, 95, 142, 71, 26, 5, 215, 198, 129, 179, 110, 130, 251, 222, 140, 210, 226, 37, 200, 117, 100, 172, 26, 138, 97, 11, 87, 161, 104, 210, 68, 117, 46, 87, 74, 250, 152, 86, 194, 42, 117, 122, 254, 235, 202, 150, 249, 63, 110, 109, 23, 197, 32, 81, 89, 39, 217, 156, 163, 78, 237, 253, 25, 188, 227, 31, 35, 174, 190, 21, 157, 160, 37, 60, 39, 193, 11, 92, 15, 251, 125, 151]), + "sha-256, with label": new Uint8Array([180, 212, 109, 8, 118, 130, 24, 5, 96, 121, 113, 102, 114, 157, 149, 31, 234, 5, 95, 138, 184, 10, 16, 249, 49, 78, 85, 222, 225, 47, 172, 108, 33, 36, 206, 47, 211, 159, 202, 241, 52, 172, 221, 28, 99, 1, 176, 51, 82, 146, 190, 137, 163, 202, 212, 74, 150, 204, 58, 26, 43, 54, 135, 91, 246, 201, 59, 106, 181, 9, 10, 118, 191, 106, 122, 102, 175, 114, 2, 182, 146, 169, 55, 124, 84, 187, 254, 237, 225, 252, 32, 197, 79, 97, 219, 250, 54, 81, 52, 121, 146, 194, 13, 196, 88, 212, 7, 146, 237, 232, 31, 113, 215, 200, 42, 158, 239, 180, 51, 152, 214, 145, 106, 11, 115, 160, 21, 71, 171, 190, 45, 25, 229, 19, 130, 210, 52, 58, 14, 55, 82, 250, 215, 193, 110, 178, 143, 101, 163, 63, 149, 216, 176, 163, 145, 66, 239, 62, 204, 195, 102, 14, 157, 2, 155, 114, 230, 225, 55, 121, 167, 172, 182, 187, 75, 149, 49, 213, 104, 144, 202, 230, 111, 125, 119, 236, 213, 155, 131, 122, 43, 55, 245, 183, 60, 140, 103, 88, 45, 84, 157, 247, 67, 243, 169, 102, 176, 225, 192, 181, 154, 252, 90, 95, 17, 161, 112, 96, 196, 105, 104, 55, 6, 92, 212, 18, 127, 85, 190, 12, 105, 123, 219, 110, 130, 111, 179, 32, 183, 81, 246, 240, 135, 59, 5, 210, 173, 15, 102, 215, 87, 95, 136, 136, 238, 12]), + "sha-384, with label": new Uint8Array([204, 60, 188, 131, 15, 114, 86, 246, 190, 155, 206, 62, 68, 249, 98, 63, 178, 144, 1, 244, 42, 248, 47, 9, 253, 8, 139, 186, 215, 180, 191, 92, 247, 19, 146, 242, 65, 195, 105, 211, 136, 84, 225, 236, 75, 202, 243, 148, 197, 68, 115, 51, 135, 65, 180, 62, 123, 91, 27, 67, 133, 14, 183, 65, 49, 1, 6, 94, 114, 249, 66, 36, 250, 189, 73, 218, 155, 213, 204, 240, 103, 185, 206, 189, 80, 60, 249, 1, 127, 190, 28, 196, 167, 67, 123, 222, 203, 124, 205, 153, 251, 242, 77, 151, 213, 215, 162, 189, 31, 28, 244, 1, 167, 58, 3, 169, 93, 143, 27, 40, 167, 86, 225, 85, 59, 93, 176, 82, 209, 224, 144, 21, 35, 252, 182, 97, 115, 200, 70, 117, 109, 240, 175, 102, 208, 100, 124, 230, 180, 232, 159, 77, 176, 75, 139, 58, 57, 254, 13, 183, 25, 27, 246, 182, 51, 171, 197, 226, 26, 7, 105, 225, 238, 147, 61, 68, 102, 97, 247, 149, 39, 8, 68, 70, 215, 220, 205, 74, 195, 183, 112, 152, 91, 70, 122, 163, 30, 66, 51, 52, 190, 204, 209, 223, 111, 67, 44, 18, 14, 108, 156, 62, 165, 221, 6, 246, 148, 233, 148, 50, 217, 248, 44, 99, 233, 118, 235, 248, 78, 45, 202, 61, 211, 220, 193, 74, 6, 229, 203, 212, 114, 116, 242, 214, 85, 165, 199, 114, 125, 53, 5, 87, 238, 208, 145, 184]), + "sha-512, with label": new Uint8Array([134, 151, 181, 94, 239, 91, 13, 83, 17, 57, 138, 21, 242, 206, 24, 86, 184, 239, 238, 57, 231, 116, 113, 139, 12, 128, 104, 100, 147, 57, 228, 183, 167, 225, 41, 180, 247, 133, 141, 0, 121, 193, 235, 168, 184, 248, 107, 34, 34, 233, 97, 215, 247, 240, 20, 245, 14, 0, 167, 17, 235, 204, 81, 97, 52, 81, 9, 136, 91, 59, 122, 200, 249, 74, 63, 68, 10, 18, 162, 243, 10, 190, 118, 60, 24, 74, 231, 92, 98, 179, 221, 150, 5, 66, 78, 93, 200, 212, 29, 76, 50, 246, 190, 84, 7, 245, 176, 148, 97, 5, 16, 22, 222, 173, 165, 200, 169, 93, 58, 8, 123, 229, 124, 220, 66, 123, 34, 69, 49, 33, 25, 107, 32, 250, 98, 61, 41, 222, 108, 112, 37, 42, 178, 163, 81, 157, 28, 160, 3, 121, 88, 10, 249, 25, 25, 22, 66, 0, 36, 187, 176, 199, 154, 122, 138, 43, 72, 217, 90, 43, 119, 50, 210, 166, 202, 2, 121, 172, 24, 172, 51, 74, 161, 45, 107, 150, 187, 89, 9, 89, 183, 233, 169, 149, 78, 145, 228, 156, 138, 215, 232, 219, 33, 33, 224, 178, 174, 16, 6, 72, 185, 214, 34, 204, 159, 161, 154, 11, 151, 142, 39, 244, 74, 75, 243, 191, 123, 231, 32, 54, 118, 235, 12, 19, 200, 165, 252, 161, 87, 46, 99, 51, 248, 146, 180, 122, 44, 210, 103, 237, 169, 50, 28, 210, 121, 136]) + }; + + var passing = [ + { + name: "RSA-OAEP with SHA-1 and no label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP"}, + hash: "SHA-1", + plaintext: plaintext.slice(0, 214), + ciphertext: ciphertext["sha-1, no label"] + }, + { + name: "RSA-OAEP with SHA-256 and no label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP"}, + hash: "SHA-256", + plaintext: plaintext.slice(0, 190), + ciphertext: ciphertext["sha-256, no label"] + }, + { + name: "RSA-OAEP with SHA-384 and no label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP"}, + hash: "SHA-384", + plaintext: plaintext.slice(0, 158), + ciphertext: ciphertext["sha-384, no label"] + }, + { + name: "RSA-OAEP with SHA-512 and no label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP"}, + hash: "SHA-512", + plaintext: plaintext.slice(0, 126), + ciphertext: ciphertext["sha-512, no label"] + }, + { + name: "RSA-OAEP with SHA-1 and empty label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP", label: new Uint8Array([])}, + hash: "SHA-1", + plaintext: plaintext.slice(0, 214), + ciphertext: ciphertext["sha-1, no label"] + }, + { + name: "RSA-OAEP with SHA-256 and empty label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP", label: new Uint8Array([])}, + hash: "SHA-256", + plaintext: plaintext.slice(0, 190), + ciphertext: ciphertext["sha-256, no label"] + }, + { + name: "RSA-OAEP with SHA-384 and empty label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP", label: new Uint8Array([])}, + hash: "SHA-384", + plaintext: plaintext.slice(0, 158), + ciphertext: ciphertext["sha-384, no label"] + }, + { + name: "RSA-OAEP with SHA-512 and empty label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP", label: new Uint8Array([])}, + hash: "SHA-512", + plaintext: plaintext.slice(0, 126), + ciphertext: ciphertext["sha-512, no label"] + }, + { + name: "RSA-OAEP with SHA-1 and a label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP", label: label}, + hash: "SHA-1", + plaintext: plaintext.slice(0, 214), + ciphertext: ciphertext["sha-1, with label"] + }, + { + name: "RSA-OAEP with SHA-256 and a label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP", label: label}, + hash: "SHA-256", + plaintext: plaintext.slice(0, 190), + ciphertext: ciphertext["sha-256, with label"] + }, + { + name: "RSA-OAEP with SHA-384 and a label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP", label: label}, + hash: "SHA-384", + plaintext: plaintext.slice(0, 158), + ciphertext: ciphertext["sha-384, with label"] + }, + { + name: "RSA-OAEP with SHA-512 and a label", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-OAEP", label: label}, + hash: "SHA-512", + plaintext: plaintext.slice(0, 126), + ciphertext: ciphertext["sha-512, with label"] + } + ]; + + var failing = []; + + return {passing: passing, failing: failing}; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures.js new file mode 100644 index 0000000000..c39e4d211c --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures.js @@ -0,0 +1,220 @@ +function run_test(algorithmNames) { + var subtle = crypto.subtle; // Change to test prefixed implementations + + setup({explicit_timeout: true}); + +// These tests check that generateKey throws an error, and that +// the error is of the right type, for a wide set of incorrect parameters. +// +// Error testing occurs by setting the parameter that should trigger the +// error to an invalid value, then combining that with all valid +// parameters that should be checked earlier by generateKey, and all +// valid and invalid parameters that should be checked later by +// generateKey. +// +// There are a lot of combinations of possible parameters for both +// success and failure modes, resulting in a very large number of tests +// performed. + + +// Setup: define the correct behaviors that should be sought, and create +// helper functions that generate all possible test parameters for +// different situations. + + var allTestVectors = [ // Parameters that should work for generateKey + {name: "AES-CTR", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, + {name: "AES-CBC", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, + {name: "AES-GCM", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, + {name: "AES-KW", resultType: CryptoKey, usages: ["wrapKey", "unwrapKey"], mandatoryUsages: []}, + {name: "HMAC", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []}, + {name: "RSASSA-PKCS1-v1_5", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, + {name: "RSA-PSS", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, + {name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]}, + {name: "ECDSA", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, + {name: "ECDH", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, + {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, + {name: "Ed448", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, + {name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, + {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, + ]; + + var testVectors = []; + if (algorithmNames && !Array.isArray(algorithmNames)) { + algorithmNames = [algorithmNames]; + }; + allTestVectors.forEach(function(vector) { + if (!algorithmNames || algorithmNames.includes(vector.name)) { + testVectors.push(vector); + } + }); + + + function parameterString(algorithm, extractable, usages) { + if (typeof algorithm !== "object" && typeof algorithm !== "string") { + alert(algorithm); + } + + var result = "(" + + objectToString(algorithm) + ", " + + objectToString(extractable) + ", " + + objectToString(usages) + + ")"; + + return result; + } + + // Test that a given combination of parameters results in an error, + // AND that it is the correct kind of error. + // + // Expected error is either a number, tested against the error code, + // or a string, tested against the error name. + function testError(algorithm, extractable, usages, expectedError, testTag) { + promise_test(function(test) { + return crypto.subtle.generateKey(algorithm, extractable, usages) + .then(function(result) { + assert_unreached("Operation succeeded, but should not have"); + }, function(err) { + if (typeof expectedError === "number") { + assert_equals(err.code, expectedError, testTag + " not supported"); + } else { + assert_equals(err.name, expectedError, testTag + " not supported"); + } + }); + }, testTag + ": generateKey" + parameterString(algorithm, extractable, usages)); + } + + + // Given an algorithm name, create several invalid parameters. + function badAlgorithmPropertySpecifiersFor(algorithmName) { + var results = []; + + if (algorithmName.toUpperCase().substring(0, 3) === "AES") { + // Specifier properties are name and length + [64, 127, 129, 255, 257, 512].forEach(function(length) { + results.push({name: algorithmName, length: length}); + }); + } else if (algorithmName.toUpperCase().substring(0, 3) === "RSA") { + [new Uint8Array([1]), new Uint8Array([1,0,0])].forEach(function(publicExponent) { + results.push({name: algorithmName, hash: "SHA-256", modulusLength: 1024, publicExponent: publicExponent}); + }); + } else if (algorithmName.toUpperCase().substring(0, 2) === "EC") { + ["P-512", "Curve25519"].forEach(function(curveName) { + results.push({name: algorithmName, namedCurve: curveName}); + }); + } + + return results; + } + + + // Don't create an exhaustive list of all invalid usages, + // because there would usually be nearly 2**8 of them, + // way too many to test. Instead, create every singleton + // of an illegal usage, and "poison" every valid usage + // with an illegal one. + function invalidUsages(validUsages, mandatoryUsages) { + var results = []; + + var illegalUsages = []; + ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey", "deriveKey", "deriveBits"].forEach(function(usage) { + if (!validUsages.includes(usage)) { + illegalUsages.push(usage); + } + }); + + var goodUsageCombinations = allValidUsages(validUsages, false, mandatoryUsages); + + illegalUsages.forEach(function(illegalUsage) { + results.push([illegalUsage]); + goodUsageCombinations.forEach(function(usageCombination) { + results.push(usageCombination.concat([illegalUsage])); + }); + }); + + return results; + } + + +// Now test for properly handling errors +// - Unsupported algorithm +// - Bad usages for algorithm +// - Bad key lengths + + // Algorithm normalization should fail with "Not supported" + var badAlgorithmNames = [ + "AES", + {name: "AES"}, + {name: "AES", length: 128}, + {name: "AES-CMAC", length: 128}, // Removed after CR + {name: "AES-CFB", length: 128}, // Removed after CR + {name: "HMAC", hash: "MD5"}, + {name: "RSA", hash: "SHA-256", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])}, + {name: "RSA-PSS", hash: "SHA", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])}, + {name: "EC", namedCurve: "P521"} + ]; + + + // Algorithm normalization failures should be found first + // - all other parameters can be good or bad, should fail + // due to NotSupportedError. + badAlgorithmNames.forEach(function(algorithm) { + allValidUsages(["decrypt", "sign", "deriveBits"], true, []) // Small search space, shouldn't matter because should fail before used + .forEach(function(usages) { + [false, true, "RED", 7].forEach(function(extractable){ + testError(algorithm, extractable, usages, "NotSupportedError", "Bad algorithm"); + }); + }); + }); + + + // Algorithms normalize okay, but usages bad (though not empty). + // It shouldn't matter what other extractable is. Should fail + // due to SyntaxError + testVectors.forEach(function(vector) { + var name = vector.name; + + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + invalidUsages(vector.usages, vector.mandatoryUsages).forEach(function(usages) { + [true].forEach(function(extractable) { + testError(algorithm, extractable, usages, "SyntaxError", "Bad usages"); + }); + }); + }); + }); + + + // Other algorithm properties should be checked next, so try good + // algorithm names and usages, but bad algorithm properties next. + // - Special case: normally bad usage [] isn't checked until after properties, + // so it's included in this test case. It should NOT cause an error. + testVectors.forEach(function(vector) { + var name = vector.name; + badAlgorithmPropertySpecifiersFor(name).forEach(function(algorithm) { + allValidUsages(vector.usages, true, vector.mandatoryUsages) + .forEach(function(usages) { + [false, true].forEach(function(extractable) { + if (name.substring(0,2) === "EC") { + testError(algorithm, extractable, usages, "NotSupportedError", "Bad algorithm property"); + } else { + testError(algorithm, extractable, usages, "OperationError", "Bad algorithm property"); + } + }); + }); + }); + }); + + + // The last thing that should be checked is an empty usages (for secret keys). + testVectors.forEach(function(vector) { + var name = vector.name; + + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + var usages = []; + [false, true].forEach(function(extractable) { + testError(algorithm, extractable, usages, "SyntaxError", "Empty usages"); + }); + }); + }); + + +} diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-CBC.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-CBC.https.any.js new file mode 100644 index 0000000000..38bed1cc70 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-CBC.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["AES-CBC"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-CTR.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-CTR.https.any.js new file mode 100644 index 0000000000..0e7940775f --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-CTR.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["AES-CTR"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-GCM.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-GCM.https.any.js new file mode 100644 index 0000000000..a394c8b629 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-GCM.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["AES-GCM"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-KW.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-KW.https.any.js new file mode 100644 index 0000000000..40c199b29a --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_AES-KW.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["AES-KW"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_ECDH.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_ECDH.https.any.js new file mode 100644 index 0000000000..e522254d74 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_ECDH.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["ECDH"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_ECDSA.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_ECDSA.https.any.js new file mode 100644 index 0000000000..e19974ff48 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_ECDSA.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["ECDSA"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_Ed25519.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_Ed25519.https.any.js new file mode 100644 index 0000000000..8f18fb1efe --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_Ed25519.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["Ed25519"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_Ed448.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_Ed448.https.any.js new file mode 100644 index 0000000000..b25dcd1490 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_Ed448.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["Ed448"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_HMAC.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_HMAC.https.any.js new file mode 100644 index 0000000000..43ce1c026f --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_HMAC.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["HMAC"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_RSA-OAEP.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_RSA-OAEP.https.any.js new file mode 100644 index 0000000000..1d2bca96b1 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_RSA-OAEP.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["RSA-OAEP"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_RSA-PSS.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_RSA-PSS.https.any.js new file mode 100644 index 0000000000..562f66697c --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_RSA-PSS.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["RSA-PSS"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_RSASSA-PKCS1-v1_5.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_RSASSA-PKCS1-v1_5.https.any.js new file mode 100644 index 0000000000..fb19308de6 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_RSASSA-PKCS1-v1_5.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["RSASSA-PKCS1-v1_5"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_X25519.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_X25519.https.any.js new file mode 100644 index 0000000000..2662d8697a --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_X25519.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["X25519"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_X448.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_X448.https.any.js new file mode 100644 index 0000000000..455e260d1f --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/failures_X448.https.any.js @@ -0,0 +1,5 @@ +// META: title=WebCryptoAPI: generateKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=failures.js +run_test(["X448"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes.js new file mode 100644 index 0000000000..4a047aa060 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes.js @@ -0,0 +1,91 @@ + +function run_test(algorithmNames, slowTest) { + var subtle = crypto.subtle; // Change to test prefixed implementations + + setup({explicit_timeout: true}); + +// These tests check that generateKey successfully creates keys +// when provided any of a wide set of correct parameters. +// +// There are a lot of combinations of possible parameters, +// resulting in a very large number of tests +// performed. + + +// Setup: define the correct behaviors that should be sought, and create +// helper functions that generate all possible test parameters for +// different situations. + + var allTestVectors = [ // Parameters that should work for generateKey + {name: "AES-CTR", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, + {name: "AES-CBC", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, + {name: "AES-GCM", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, + {name: "AES-KW", resultType: CryptoKey, usages: ["wrapKey", "unwrapKey"], mandatoryUsages: []}, + {name: "HMAC", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []}, + {name: "RSASSA-PKCS1-v1_5", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, + {name: "RSA-PSS", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, + {name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]}, + {name: "ECDSA", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, + {name: "ECDH", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, + {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, + {name: "Ed448", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, + {name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, + {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, + ]; + + var testVectors = []; + if (algorithmNames && !Array.isArray(algorithmNames)) { + algorithmNames = [algorithmNames]; + }; + allTestVectors.forEach(function(vector) { + if (!algorithmNames || algorithmNames.includes(vector.name)) { + testVectors.push(vector); + } + }); + + function parameterString(algorithm, extractable, usages) { + var result = "(" + + objectToString(algorithm) + ", " + + objectToString(extractable) + ", " + + objectToString(usages) + + ")"; + + return result; + } + + // Test that a given combination of parameters is successful + function testSuccess(algorithm, extractable, usages, resultType, testTag) { + // algorithm, extractable, and usages are the generateKey parameters + // resultType is the expected result, either the CryptoKey object or "CryptoKeyPair" + // testTag is a string to prepend to the test name. + + promise_test(function(test) { + return subtle.generateKey(algorithm, extractable, usages) + .then(function(result) { + if (resultType === "CryptoKeyPair") { + assert_goodCryptoKey(result.privateKey, algorithm, extractable, usages, "private"); + assert_goodCryptoKey(result.publicKey, algorithm, extractable, usages, "public"); + } else { + assert_goodCryptoKey(result, algorithm, extractable, usages, "secret"); + } + }, function(err) { + assert_unreached("Threw an unexpected error: " + err.toString()); + }); + }, testTag + ": generateKey" + parameterString(algorithm, extractable, usages)); + } + + // Test all valid sets of parameters for successful + // key generation. + testVectors.forEach(function(vector) { + allNameVariants(vector.name, slowTest).forEach(function(name) { + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + allValidUsages(vector.usages, false, vector.mandatoryUsages).forEach(function(usages) { + [false, true].forEach(function(extractable) { + subsetTest(testSuccess, algorithm, extractable, usages, vector.resultType, "Success"); + }); + }); + }); + }); + }); + +} diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-CBC.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-CBC.https.any.js new file mode 100644 index 0000000000..80f92c2cb7 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-CBC.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["AES-CBC"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-CTR.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-CTR.https.any.js new file mode 100644 index 0000000000..243a104b60 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-CTR.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["AES-CTR"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-GCM.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-GCM.https.any.js new file mode 100644 index 0000000000..f0f947c816 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-GCM.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["AES-GCM"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-KW.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-KW.https.any.js new file mode 100644 index 0000000000..dbc040fdc5 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_AES-KW.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["AES-KW"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_ECDH.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_ECDH.https.any.js new file mode 100644 index 0000000000..e9dee52614 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_ECDH.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["ECDH"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_ECDSA.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_ECDSA.https.any.js new file mode 100644 index 0000000000..a022f31fe9 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_ECDSA.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["ECDSA"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_Ed25519.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_Ed25519.https.any.js new file mode 100644 index 0000000000..6b3bc460f6 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_Ed25519.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["Ed25519"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_Ed448.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_Ed448.https.any.js new file mode 100644 index 0000000000..8e37f57b24 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_Ed448.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["Ed448"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_HMAC.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_HMAC.https.any.js new file mode 100644 index 0000000000..18e0b27122 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_HMAC.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["HMAC"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_RSA-OAEP.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_RSA-OAEP.https.any.js new file mode 100644 index 0000000000..d933fd981d --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_RSA-OAEP.https.any.js @@ -0,0 +1,22 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: variant=?1-10 +// META: variant=?11-20 +// META: variant=?21-30 +// META: variant=?31-40 +// META: variant=?41-50 +// META: variant=?51-60 +// META: variant=?61-70 +// META: variant=?71-80 +// META: variant=?81-90 +// META: variant=?91-100 +// META: variant=?101-110 +// META: variant=?111-120 +// META: variant=?121-130 +// META: variant=?131-140 +// META: variant=?141-150 +// META: variant=?151-last +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["RSA-OAEP"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_RSA-PSS.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_RSA-PSS.https.any.js new file mode 100644 index 0000000000..cb43e3de3d --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_RSA-PSS.https.any.js @@ -0,0 +1,10 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: variant=?1-10 +// META: variant=?11-20 +// META: variant=?21-30 +// META: variant=?31-last +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["RSA-PSS"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_RSASSA-PKCS1-v1_5.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_RSASSA-PKCS1-v1_5.https.any.js new file mode 100644 index 0000000000..b8db597228 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_RSASSA-PKCS1-v1_5.https.any.js @@ -0,0 +1,10 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: variant=?1-10 +// META: variant=?11-20 +// META: variant=?21-30 +// META: variant=?31-last +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["RSASSA-PKCS1-v1_5"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_X25519.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_X25519.https.any.js new file mode 100644 index 0000000000..0e87cf5010 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_X25519.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["X25519"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_X448.https.any.js b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_X448.https.any.js new file mode 100644 index 0000000000..e7dbe32696 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/generateKey/successes_X448.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: generateKey() Successful Calls +// META: timeout=long +// META: script=../util/helpers.js +// META: script=/common/subset-tests.js +// META: script=successes.js +run_test(["X448"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/getRandomValues.any.js b/testing/web-platform/tests/WebCryptoAPI/getRandomValues.any.js new file mode 100644 index 0000000000..1a3370ea13 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/getRandomValues.any.js @@ -0,0 +1,60 @@ +// Step 1. +test(function() { + assert_throws_dom("TypeMismatchError", function() { + self.crypto.getRandomValues(new Float32Array(6)) + }, "Float32Array") + assert_throws_dom("TypeMismatchError", function() { + self.crypto.getRandomValues(new Float64Array(6)) + }, "Float64Array") + + assert_throws_dom("TypeMismatchError", function() { + const len = 65536 / Float32Array.BYTES_PER_ELEMENT + 1; + self.crypto.getRandomValues(new Float32Array(len)); + }, "Float32Array (too long)") + assert_throws_dom("TypeMismatchError", function() { + const len = 65536 / Float64Array.BYTES_PER_ELEMENT + 1; + self.crypto.getRandomValues(new Float64Array(len)) + }, "Float64Array (too long)") +}, "Float arrays"); + +test(function() { + assert_throws_dom("TypeMismatchError", function() { + self.crypto.getRandomValues(new DataView(new ArrayBuffer(6))) + }, "DataView") + + assert_throws_dom("TypeMismatchError", function() { + self.crypto.getRandomValues(new DataView(new ArrayBuffer(65536 + 1))) + }, "DataView (too long)") +}, "DataView"); + +const arrays = [ + 'Int8Array', + 'Int16Array', + 'Int32Array', + 'BigInt64Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Uint16Array', + 'Uint32Array', + 'BigUint64Array', +]; + +for (const array of arrays) { + const ctor = globalThis[array]; + + test(function() { + assert_equals(self.crypto.getRandomValues(new ctor(8)).constructor, + ctor, "crypto.getRandomValues(new " + array + "(8))") + }, "Integer array: " + array); + + test(function() { + const maxlength = 65536 / ctor.BYTES_PER_ELEMENT; + assert_throws_dom("QuotaExceededError", function() { + self.crypto.getRandomValues(new ctor(maxlength + 1)) + }, "crypto.getRandomValues length over 65536") + }, "Large length: " + array); + + test(function() { + assert_true(self.crypto.getRandomValues(new ctor(0)).length == 0) + }, "Null arrays: " + array); +} diff --git a/testing/web-platform/tests/WebCryptoAPI/historical.any.js b/testing/web-platform/tests/WebCryptoAPI/historical.any.js new file mode 100644 index 0000000000..67ca9d9767 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/historical.any.js @@ -0,0 +1,18 @@ +// META: global=window,dedicatedworker,sharedworker +// +// Do not run this in a service worker as that's always in a secure context + +test(() => { + assert_equals(self.crypto.subtle, undefined); + assert_false("subtle" in self.crypto); +}, "Non-secure context window does not have access to crypto.subtle"); + +test(() => { + assert_equals(self.SubtleCrypto, undefined); + assert_false("SubtleCrypto" in self); +}, "Non-secure context window does not have access to SubtleCrypto") + +test(() => { + assert_equals(self.CryptoKey, undefined); + assert_false("CryptoKey" in self); +}, "Non-secure context window does not have access to CryptoKey") diff --git a/testing/web-platform/tests/WebCryptoAPI/idlharness.https.any.js b/testing/web-platform/tests/WebCryptoAPI/idlharness.https.any.js new file mode 100644 index 0000000000..ae65eb49f2 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/idlharness.https.any.js @@ -0,0 +1,16 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js +// META: timeout=long + +// https://w3c.github.io/webcrypto/Overview.html + +idl_test( + ['WebCryptoAPI'], + ['html', 'dom'], + idl_array => { + idl_array.add_objects({ + Crypto: ['crypto'], + SubtleCrypto: ['crypto.subtle'] + }); + } +); diff --git a/testing/web-platform/tests/WebCryptoAPI/import_export/ec_importKey.https.any.js b/testing/web-platform/tests/WebCryptoAPI/import_export/ec_importKey.https.any.js new file mode 100644 index 0000000000..66be9ff65d --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/import_export/ec_importKey.https.any.js @@ -0,0 +1,297 @@ +// META: title=WebCryptoAPI: importKey() for EC keys +// META: timeout=long + +// Test importKey and exportKey for EC algorithms. Only "happy paths" are +// currently tested - those where the operation should succeed. + + var subtle = crypto.subtle; + + var curves = ['P-256', 'P-384', 'P-521']; + + var keyData = { + "P-521": { + spki: new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]), + spki_compressed: new Uint8Array([48, 88, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 68, 0, 3, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63]), + raw: new Uint8Array([4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]), + raw_compressed: new Uint8Array([3, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63]), + pkcs8: new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 0, 244, 8, 117, 131, 104, 186, 147, 15, 48, 247, 106, 224, 84, 254, 92, 210, 206, 127, 218, 44, 159, 118, 166, 212, 54, 207, 117, 214, 108, 68, 11, 254, 99, 49, 199, 193, 114, 161, 36, 120, 25, 60, 130, 81, 72, 123, 201, 18, 99, 250, 80, 33, 127, 133, 255, 99, 111, 89, 205, 84, 110, 58, 180, 131, 180, 161, 129, 137, 3, 129, 134, 0, 4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]), + jwk: { + kty: "EC", + crv: "P-521", + x: "AVb0efjfHiCn_8BM5CDD4VSuJRmWvuQvA0uE1Bt0PzTkXzEbgTqc3sjNpZu7vTHUYLMpJSHnwbci5WZ8A9svrnU_", + y: "AVAXNs_iRzlDINjkr8L9ObWpMxBhuB4iQSgrnheJGCK1t54FL0WXtZZD_Tk3nFG9USXE9IvD8CXOPNNpUyhsyzj7", + d: "APQIdYNoupMPMPdq4FT-XNLOf9osn3am1DbPddZsRAv-YzHHwXKhJHgZPIJRSHvJEmP6UCF_hf9jb1nNVG46tIO0" + } + }, + + "P-256": { + spki: new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]), + spki_compressed: new Uint8Array([48, 57, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 34, 0, 2, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209]), + raw: new Uint8Array([4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]), + raw_compressed: new Uint8Array([2, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209]), + pkcs8: new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 19, 211, 58, 45, 90, 191, 156, 249, 235, 178, 31, 248, 96, 212, 174, 254, 110, 86, 231, 119, 144, 244, 222, 233, 180, 8, 132, 235, 211, 53, 68, 234, 161, 68, 3, 66, 0, 4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]), + jwk: { + kty: "EC", + crv: "P-256", + x: "0hCwpvnZ8BKGgFi0P6T0cQGFQ7ugDJJQ35JXwqyuXdE", + y: "zgN1UtSBRQzjm00QlXAbF1v6s0uObAmeGPHBmDWDYeg", + d: "E9M6LVq_nPnrsh_4YNSu_m5W53eQ9N7ptAiE69M1ROo" + } + }, + + "P-384": { + spki: new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]), + spki_compressed: new Uint8Array([48, 70, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 50, 0, 2, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53]), + raw: new Uint8Array([4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]), + raw_compressed: new Uint8Array([2, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53]), + pkcs8: new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 69, 55, 181, 153, 7, 132, 211, 194, 210, 46, 150, 168, 249, 47, 161, 170, 73, 46, 232, 115, 229, 118, 164, 21, 130, 225, 68, 24, 60, 152, 136, 209, 14, 107, 158, 180, 206, 212, 178, 204, 64, 18, 228, 172, 94, 168, 64, 115, 161, 100, 3, 98, 0, 4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]), + jwk: { + kty: "EC", + crv: "P-384", + x: "IZwU1mYXs27G2IVrOFtzp000T9iude8EZDXdpU47RL1fvevR0I3Wni19wdwhjLQ1", + y: "vSgTjMd4M3qEL2vWGyQOdCSfJGZ8KlgQp2v8KOAzX4imUB3sAZdtqFr7AIactqzo", + d: "RTe1mQeE08LSLpao-S-hqkku6HPldqQVguFEGDyYiNEOa560ztSyzEAS5KxeqEBz" + } + }, + + }; + + // combinations to test + var testVectors = [ + {name: "ECDSA", privateUsages: ["sign"], publicUsages: ["verify"]}, + {name: "ECDH", privateUsages: ["deriveKey", "deriveBits"], publicUsages: []} + ]; + + // TESTS ARE HERE: + // Test every test vector, along with all available key data + testVectors.forEach(function(vector) { + curves.forEach(function(curve) { + + [true, false].forEach(function(extractable) { + + // Test public keys first + [[]].forEach(function(usages) { // Only valid usages argument is empty array + ['spki', 'spki_compressed', 'jwk', 'raw', 'raw_compressed'].forEach(function(format) { + var algorithm = {name: vector.name, namedCurve: curve}; + var data = keyData[curve]; + if (format === "jwk") { // Not all fields used for public keys + data = {jwk: {kty: keyData[curve].jwk.kty, crv: keyData[curve].jwk.crv, x: keyData[curve].jwk.x, y: keyData[curve].jwk.y}}; + } + + testFormat(format, algorithm, data, curve, usages, extractable); + }); + + }); + + // Next, test private keys + allValidUsages(vector.privateUsages, []).forEach(function(usages) { + ['pkcs8', 'jwk'].forEach(function(format) { + var algorithm = {name: vector.name, namedCurve: curve}; + var data = keyData[curve]; + + testFormat(format, algorithm, data, curve, usages, extractable); + }); + }); + }); + + }); + }); + + + // Test importKey with a given key format and other parameters. If + // extrable is true, export the key and verify that it matches the input. + function testFormat(format, algorithm, data, keySize, usages, extractable) { + const keyData = data[format]; + const compressed = format.endsWith("_compressed"); + if (compressed) { + [format] = format.split("_compressed"); + } + promise_test(function(test) { + return subtle.importKey(format, keyData, algorithm, extractable, usages). + then(function(key) { + assert_equals(key.constructor, CryptoKey, "Imported a CryptoKey object"); + if (!extractable) { + return; + } + + return subtle.exportKey(format, key). + then(function(result) { + if (format !== "jwk") { + assert_true(equalBuffers(data[format], result), "Round trip works"); + } else { + assert_true(equalJwk(data[format], result), "Round trip works"); + } + }, function(err) { + assert_unreached("Threw an unexpected error: " + err.toString()); + }); + }, function(err) { + if (compressed && err.name === "DataError") { + assert_implements_optional(false, "Compressed point format not supported: " + err.toString()); + } else { + assert_unreached("Threw an unexpected error: " + err.toString()); + } + }); + }, "Good parameters: " + keySize.toString() + " bits " + parameterString(format, compressed, keyData, algorithm, extractable, usages)); + } + + + + // Helper methods follow: + + // Are two array buffers the same? + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + // Are two Jwk objects "the same"? That is, does the object returned include + // matching values for each property that was expected? It's okay if the + // returned object has extra methods; they aren't checked. + function equalJwk(expected, got) { + var fields = Object.keys(expected); + var fieldName; + + for(var i=0; i<fields.length; i++) { + fieldName = fields[i]; + if (!(fieldName in got)) { + return false; + } + if (expected[fieldName] !== got[fieldName]) { + return false; + } + } + + return true; + } + + // Build minimal Jwk objects from raw key data and algorithm specifications + function jwkData(keyData, algorithm) { + var result = { + kty: "oct", + k: byteArrayToUnpaddedBase64(keyData) + }; + + if (algorithm.name.substring(0, 3) === "AES") { + result.alg = "A" + (8 * keyData.byteLength).toString() + algorithm.name.substring(4); + } else if (algorithm.name === "HMAC") { + result.alg = "HS" + algorithm.hash.substring(4); + } + return result; + } + + // Jwk format wants Base 64 without the typical padding at the end. + function byteArrayToUnpaddedBase64(byteArray){ + var binaryString = ""; + for (var i=0; i<byteArray.byteLength; i++){ + binaryString += String.fromCharCode(byteArray[i]); + } + var base64String = btoa(binaryString); + + return base64String.replace(/=/g, ""); + } + + // Want to test every valid combination of usages. Start by creating a list + // of all non-empty subsets to possible usages. + function allNonemptySubsetsOf(arr) { + var results = []; + var firstElement; + var remainingElements; + + for(var i=0; i<arr.length; i++) { + firstElement = arr[i]; + remainingElements = arr.slice(i+1); + results.push([firstElement]); + + if (remainingElements.length > 0) { + allNonemptySubsetsOf(remainingElements).forEach(function(combination) { + combination.push(firstElement); + results.push(combination); + }); + } + } + + return results; + } + + // Return a list of all valid usage combinations, given the possible ones + // and the ones that are required for a particular operation. + function allValidUsages(possibleUsages, requiredUsages) { + var allUsages = []; + + allNonemptySubsetsOf(possibleUsages).forEach(function(usage) { + for (var i=0; i<requiredUsages.length; i++) { + if (!usage.includes(requiredUsages[i])) { + return; + } + } + allUsages.push(usage); + }); + + return allUsages; + } + + // Convert method parameters to a string to uniquely name each test + function parameterString(format, compressed, data, algorithm, extractable, usages) { + if ("byteLength" in data) { + data = "buffer(" + data.byteLength.toString() + (compressed ? ", compressed" : "") + ")"; + } else { + data = "object(" + Object.keys(data).join(", ") + ")"; + } + var result = "(" + + objectToString(format) + ", " + + objectToString(data) + ", " + + objectToString(algorithm) + ", " + + objectToString(extractable) + ", " + + objectToString(usages) + + ")"; + + return result; + } + + // Character representation of any object we may use as a parameter. + function objectToString(obj) { + var keyValuePairs = []; + + if (Array.isArray(obj)) { + return "[" + obj.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else if (typeof obj === "object") { + Object.keys(obj).sort().forEach(function(keyName) { + keyValuePairs.push(keyName + ": " + objectToString(obj[keyName])); + }); + return "{" + keyValuePairs.join(", ") + "}"; + } else if (typeof obj === "undefined") { + return "undefined"; + } else { + return obj.toString(); + } + + var keyValuePairs = []; + + Object.keys(obj).sort().forEach(function(keyName) { + var value = obj[keyName]; + if (typeof value === "object") { + value = objectToString(value); + } else if (typeof value === "array") { + value = "[" + value.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else { + value = value.toString(); + } + + keyValuePairs.push(keyName + ": " + value); + }); + + return "{" + keyValuePairs.join(", ") + "}"; + } + diff --git a/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey.https.any.js b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey.https.any.js new file mode 100644 index 0000000000..0bd460b62f --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey.https.any.js @@ -0,0 +1,286 @@ +// META: title=WebCryptoAPI: importKey() for OKP keys +// META: timeout=long + +// Test importKey and exportKey for OKP algorithms. Only "happy paths" are +// currently tested - those where the operation should succeed. + + var subtle = crypto.subtle; + + var keyData = { + "Ed25519": { + spki: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]), + raw: new Uint8Array([216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]), + pkcs8: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168, 31]), + jwk: { + crv: "Ed25519", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8", + x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw", + kty: "OKP" + } + }, + + "Ed448": { + spki: new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]), + raw: new Uint8Array([171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]), + pkcs8: new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29, 46]), + jwk: { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + kty: "OKP" + } + }, + + "X25519": { + spki: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]), + raw: new Uint8Array([28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]), + pkcs8: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255, 97]), + jwk: { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + kty: "OKP" + } + }, + + "X448": { + spki: new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]), + raw: new Uint8Array([182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]), + pkcs8: new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146, 158]), + jwk: { + crv: "X448", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + kty: "OKP" + } + }, + + }; + + // combinations to test + var testVectors = [ + {name: "Ed25519", privateUsages: ["sign"], publicUsages: ["verify"]}, + {name: "Ed448", privateUsages: ["sign"], publicUsages: ["verify"]}, + {name: "X25519", privateUsages: ["deriveKey", "deriveBits"], publicUsages: []}, + {name: "X448", privateUsages: ["deriveKey", "deriveBits"], publicUsages: []}, + ]; + + // TESTS ARE HERE: + // Test every test vector, along with all available key data + testVectors.forEach(function(vector) { + [true, false].forEach(function(extractable) { + + // Test public keys first + [[]].forEach(function(usages) { // Only valid usages argument is empty array + ['spki', 'jwk', 'raw'].forEach(function(format) { + var algorithm = {name: vector.name}; + var data = keyData[vector.name]; + if (format === "jwk") { // Not all fields used for public keys + data = {jwk: {kty: keyData[vector.name].jwk.kty, crv: keyData[vector.name].jwk.crv, x: keyData[vector.name].jwk.x}}; + } + + testFormat(format, algorithm, data, vector.name, usages, extractable); + }); + + }); + + // Next, test private keys + allValidUsages(vector.privateUsages, []).forEach(function(usages) { + ['pkcs8', 'jwk'].forEach(function(format) { + var algorithm = {name: vector.name}; + var data = keyData[vector.name]; + + testFormat(format, algorithm, data, vector.name, usages, extractable); + }); + }); + }); + }); + + + // Test importKey with a given key format and other parameters. If + // extrable is true, export the key and verify that it matches the input. + function testFormat(format, algorithm, keyData, keySize, usages, extractable) { + promise_test(function(test) { + return subtle.importKey(format, keyData[format], algorithm, extractable, usages). + then(function(key) { + assert_equals(key.constructor, CryptoKey, "Imported a CryptoKey object"); + if (!extractable) { + return; + } + + return subtle.exportKey(format, key). + then(function(result) { + if (format !== "jwk") { + assert_true(equalBuffers(keyData[format], result), "Round trip works"); + } else { + assert_true(equalJwk(keyData[format], result), "Round trip works"); + } + }, function(err) { + assert_unreached("Threw an unexpected error: " + err.toString()); + }); + }, function(err) { + assert_unreached("Threw an unexpected error: " + err.toString()); + }); + }, "Good parameters: " + keySize.toString() + " bits " + parameterString(format, keyData[format], algorithm, extractable, usages)); + } + + + + // Helper methods follow: + + // Are two array buffers the same? + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + // Are two Jwk objects "the same"? That is, does the object returned include + // matching values for each property that was expected? It's okay if the + // returned object has extra methods; they aren't checked. + function equalJwk(expected, got) { + var fields = Object.keys(expected); + var fieldName; + + for(var i=0; i<fields.length; i++) { + fieldName = fields[i]; + if (!(fieldName in got)) { + return false; + } + if (expected[fieldName] !== got[fieldName]) { + return false; + } + } + + return true; + } + + // Build minimal Jwk objects from raw key data and algorithm specifications + function jwkData(keyData, algorithm) { + var result = { + kty: "oct", + k: byteArrayToUnpaddedBase64(keyData) + }; + + if (algorithm.name.substring(0, 3) === "AES") { + result.alg = "A" + (8 * keyData.byteLength).toString() + algorithm.name.substring(4); + } else if (algorithm.name === "HMAC") { + result.alg = "HS" + algorithm.hash.substring(4); + } + return result; + } + + // Jwk format wants Base 64 without the typical padding at the end. + function byteArrayToUnpaddedBase64(byteArray){ + var binaryString = ""; + for (var i=0; i<byteArray.byteLength; i++){ + binaryString += String.fromCharCode(byteArray[i]); + } + var base64String = btoa(binaryString); + + return base64String.replace(/=/g, ""); + } + + // Want to test every valid combination of usages. Start by creating a list + // of all non-empty subsets to possible usages. + function allNonemptySubsetsOf(arr) { + var results = []; + var firstElement; + var remainingElements; + + for(var i=0; i<arr.length; i++) { + firstElement = arr[i]; + remainingElements = arr.slice(i+1); + results.push([firstElement]); + + if (remainingElements.length > 0) { + allNonemptySubsetsOf(remainingElements).forEach(function(combination) { + combination.push(firstElement); + results.push(combination); + }); + } + } + + return results; + } + + // Return a list of all valid usage combinations, given the possible ones + // and the ones that are required for a particular operation. + function allValidUsages(possibleUsages, requiredUsages) { + var allUsages = []; + + allNonemptySubsetsOf(possibleUsages).forEach(function(usage) { + for (var i=0; i<requiredUsages.length; i++) { + if (!usage.includes(requiredUsages[i])) { + return; + } + } + allUsages.push(usage); + }); + + return allUsages; + } + + // Convert method parameters to a string to uniquely name each test + function parameterString(format, data, algorithm, extractable, usages) { + if ("byteLength" in data) { + data = "buffer(" + data.byteLength.toString() + ")"; + } else { + data = "object(" + Object.keys(data).join(", ") + ")"; + } + var result = "(" + + objectToString(format) + ", " + + objectToString(data) + ", " + + objectToString(algorithm) + ", " + + objectToString(extractable) + ", " + + objectToString(usages) + + ")"; + + return result; + } + + // Character representation of any object we may use as a parameter. + function objectToString(obj) { + var keyValuePairs = []; + + if (Array.isArray(obj)) { + return "[" + obj.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else if (typeof obj === "object") { + Object.keys(obj).sort().forEach(function(keyName) { + keyValuePairs.push(keyName + ": " + objectToString(obj[keyName])); + }); + return "{" + keyValuePairs.join(", ") + "}"; + } else if (typeof obj === "undefined") { + return "undefined"; + } else { + return obj.toString(); + } + + var keyValuePairs = []; + + Object.keys(obj).sort().forEach(function(keyName) { + var value = obj[keyName]; + if (typeof value === "object") { + value = objectToString(value); + } else if (typeof value === "array") { + value = "[" + value.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else { + value = value.toString(); + } + + keyValuePairs.push(keyName + ": " + value); + }); + + return "{" + keyValuePairs.join(", ") + "}"; + } diff --git a/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures.js b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures.js new file mode 100644 index 0000000000..4e2d717595 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures.js @@ -0,0 +1,176 @@ +function run_test(algorithmNames) { + var subtle = crypto.subtle; // Change to test prefixed implementations + + setup({explicit_timeout: true}); + +// These tests check that importKey and exportKey throw an error, and that +// the error is of the right type, for a wide set of incorrect parameters. + +// Error testing occurs by setting the parameter that should trigger the +// error to an invalid value, then combining that with all valid +// parameters that should be checked earlier by importKey, and all +// valid and invalid parameters that should be checked later by +// importKey. +// +// There are a lot of combinations of possible parameters for both +// success and failure modes, resulting in a very large number of tests +// performed. + + + var allTestVectors = [ // Parameters that should work for importKey / exportKey + {name: "Ed25519", privateUsages: ["sign"], publicUsages: ["verify"]}, + {name: "Ed448", privateUsages: ["sign"], publicUsages: ["verify"]}, + {name: "X25519", privateUsages: ["deriveKey", "deriveBits"], publicUsages: []}, + {name: "X448", privateUsages: ["deriveKey", "deriveBits"], publicUsages: []}, + ]; + + var testVectors = []; + if (algorithmNames && !Array.isArray(algorithmNames)) { + algorithmNames = [algorithmNames]; + }; + allTestVectors.forEach(function(vector) { + if (!algorithmNames || algorithmNames.includes(vector.name)) { + testVectors.push(vector); + } + }); + + function parameterString(format, algorithm, extractable, usages, data) { + if (typeof algorithm !== "object" && typeof algorithm !== "string") { + alert(algorithm); + } + + var jwk_label = ""; + if (format === "jwk") + jwk_label = data.d === undefined ? " (public) " : "(private)"; + + var result = "(" + + objectToString(format) + jwk_label + ", " + + objectToString(algorithm) + ", " + + objectToString(extractable) + ", " + + objectToString(usages) + + ")"; + + return result; + } + + // Test that a given combination of parameters results in an error, + // AND that it is the correct kind of error. + // + // Expected error is either a number, tested against the error code, + // or a string, tested against the error name. + function testError(format, algorithm, keyData, keySize, usages, extractable, expectedError, testTag) { + promise_test(async() => { + let key; + try { + key = await subtle.importKey(format, keyData, algorithm, extractable, usages); + } catch(err) { + let actualError = typeof expectedError === "number" ? err.code : err.name; + assert_equals(actualError, expectedError, testTag + " not supported."); + } + assert_equals(key, undefined, "Operation succeeded, but should not have."); + }, testTag + ": importKey" + parameterString(format, algorithm, extractable, usages, keyData)); + } + + // Don't create an exhaustive list of all invalid usages, + // because there would usually be nearly 2**8 of them, + // way too many to test. Instead, create every singleton + // of an illegal usage, and "poison" every valid usage + // with an illegal one. + function invalidUsages(validUsages, mandatoryUsages) { + var results = []; + + var illegalUsages = []; + ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey", "deriveKey", "deriveBits"].forEach(function(usage) { + if (!validUsages.includes(usage)) { + illegalUsages.push(usage); + } + }); + + var goodUsageCombinations = validUsages.length === 0 ? [] : allValidUsages(validUsages, false, mandatoryUsages); + + illegalUsages.forEach(function(illegalUsage) { + results.push([illegalUsage]); + goodUsageCombinations.forEach(function(usageCombination) { + results.push(usageCombination.concat([illegalUsage])); + }); + }); + + return results; + } + + function validUsages(usages, format, data) { + if (format === 'spki') return usages.publicUsages + if (format === 'pkcs8') return usages.privateUsages + if (format === 'jwk') { + if (data === undefined) + return []; + return data.d === undefined ? usages.publicUsages : usages.privateUsages; + } + return []; + } + +// Now test for properly handling errors +// - Unsupported algorithm +// - Bad usages for algorithm +// - Bad key lengths +// - Lack of a mandatory format field +// - Incompatible keys pair + + // Algorithms normalize okay, but usages bad (though not empty). + // It shouldn't matter what other extractable is. Should fail + // due to SyntaxError + testVectors.forEach(function(vector) { + var name = vector.name; + validKeyData.forEach(function(test) { + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + invalidUsages(validUsages(vector, test.format, test.data)).forEach(function(usages) { + [true, false].forEach(function(extractable) { + testError(test.format, algorithm, test.data, name, usages, extractable, "SyntaxError", "Bad usages"); + }); + }); + }); + }); + }); + + // Algorithms normalize okay, usages ok. The length of the key must thouw a DataError exception. + testVectors.forEach(function(vector) { + var name = vector.name; + badKeyLengthData.forEach(function(test) { + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + allValidUsages(validUsages(vector, test.format, test.data)).forEach(function(usages) { + [true, false].forEach(function(extractable) { + testError(test.format, algorithm, test.data, name, usages, extractable, "DataError", "Bad key length"); + }); + }); + }); + }); + }); + + // Algorithms normalize okay, usages ok and valid key. The lack of the mandatory JWK parameter must throw a syntax error. + testVectors.forEach(function(vector) { + var name = vector.name; + missingJWKFieldKeyData.forEach(function(test) { + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + allValidUsages(validUsages(vector, 'jwk', test.data)).forEach(function(usages) { + [true, false].forEach(function(extractable) { + testError('jwk', algorithm, test.data, name, usages, extractable, "DataError", "Missing JWK '" + test.param + "' parameter"); + }); + }); + }); + }); + }); + + // Algorithms normalize okay, usages ok and valid key. The public key is not compatible with the private key. + testVectors.forEach(function(vector) { + var name = vector.name; + invalidJWKKeyData.forEach(function(data) { + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + allValidUsages(vector.privateUsages).forEach(function(usages) { + [true].forEach(function(extractable) { + testError('jwk', algorithm, data, name, usages, extractable, "DataError", "Invalid key pair"); + }); + }); + }); + }); + }); +} diff --git a/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js new file mode 100644 index 0000000000..7d6ec6171c --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js @@ -0,0 +1,102 @@ +// META: title=WebCryptoAPI: importKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=okp_importKey_failures.js + +// Setup: define the correct behaviors that should be sought, and create +// helper functions that generate all possible test parameters for +// different situations. +var validKeyData = [ + { + format: "spki", + data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]) + }, + { + format: "pkcs8", + data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168, 31]) + }, + { + format: "jwk", + data: { + crv: "Ed25519", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8", + x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "Ed25519", + x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw", + kty: "OKP" + }, + }, +]; + +// Removed just the last byte. +var badKeyLengthData = [ + { + format: "spki", + data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61]) + }, + { + format: "pkcs8", + data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168]) + }, + { + format: "jwk", + data: { + crv: "Ed25519", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB", + x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw", + kty: "OKP" + } + }, + { + format: "jwk", + data: { + crv: "Ed25519", + x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPc", + kty: "OKP" + } + }, +]; + +var missingJWKFieldKeyData = [ + { + param: "x", + data: { + crv: "Ed25519", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8", + kty: "OKP" + }, + }, + { + param: "kty", + data: { + crv: "Ed25519", + x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8", + }, + }, + { + param: "crv", + data: { + x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + kty: "OKP" + }, + } +]; + +// The public key doesn't match the private key. +var invalidJWKKeyData = [ + { + crv: "Ed25519", + d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8", + x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + kty: "OKP" + }, +]; + +run_test(["Ed25519"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js new file mode 100644 index 0000000000..1035800faf --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.https.any.js @@ -0,0 +1,103 @@ +// META: title=WebCryptoAPI: importKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=okp_importKey_failures.js + +// Setup: define the correct behaviors that should be sought, and create +// helper functions that generate all possible test parameters for +// different situations. +var validKeyData = [ + { + format: "spki", + data: new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29, 46]), + }, + { + format: "jwk", + data: { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "Ed448", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + kty: "OKP" + }, + }, +]; + +// Removed just the last byte. +var badKeyLengthData = [ + { + format: "spki", + data: new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29]), + }, + { + format: "jwk", + data: { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "Ed448", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalq", + kty: "OKP" + }, + }, +]; + +var missingJWKFieldKeyData = [ + { + param: "x", + data: { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + kty: "OKP" + } + }, + { + param: "kty", + data: { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + } + }, + { + param: "crv", + data: { + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA", + kty: "OKP" + } + } +]; + +// The public key doesn't match the private key. +var invalidJWKKeyData = [ + { + crv: "Ed448", + d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u", + x: "X9dEm1m0Yf0s54fsYWrUah2hNCSFpw4fig6nXYDpZ3jt8SR2m0bHBhvWeD3x5Q9s0foavq_oJWGA", + kty: "OKP" + }, +]; + +run_test(["Ed448"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js new file mode 100644 index 0000000000..fe5fd54da5 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js @@ -0,0 +1,102 @@ +// META: title=WebCryptoAPI: importKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=okp_importKey_failures.js + +// Setup: define the correct behaviors that should be sought, and create +// helper functions that generate all possible test parameters for +// different situations. +var validKeyData = [ + { + format: "spki", + data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255, 97]), + }, + { + format: "jwk", + data: { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "X25519", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + kty: "OKP" + }, + }, +]; + +// Removed just the last byte. +var badKeyLengthData = [ + { + format: "spki", + data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255]), + }, + { + format: "jwk", + data: { + crv: "X25519", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lw", + kty: "OKP" + } + }, + { + format: "jwk", + data: { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + kty: "OKP" + }, + }, +]; + +var missingJWKFieldKeyData = [ + { + param: "x", + data: { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E", + kty: "OKP" + }, + }, + { + param: "kty", + data: { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E", + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + }, + }, + { + param: "crv", + data: { + x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY", + kty: "OKP" + }, + } +]; + +// The public key doesn't match the private key. +var invalidJWKKeyData = [ + { + crv: "X25519", + d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E", + x: "hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo", + kty: "OKP" + }, +]; + +run_test(["X25519"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js new file mode 100644 index 0000000000..9e3b05c48a --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/import_export/okp_importKey_failures_X448.https.any.js @@ -0,0 +1,103 @@ +// META: title=WebCryptoAPI: importKey() for Failures +// META: timeout=long +// META: script=../util/helpers.js +// META: script=okp_importKey_failures.js + +// Setup: define the correct behaviors that should be sought, and create +// helper functions that generate all possible test parameters for +// different situations. +var validKeyData = [ + { + format: "spki", + data: new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146, 158]), + }, + { + format: "jwk", + data: { + crv: "X448", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "X448", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + kty: "OKP" + }, + }, +]; + +// Removed just the last byte. +var badKeyLengthData = [ + { + format: "spki", + data: new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206]), + }, + { + format: "pkcs8", + data: new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146]), + }, + { + format: "jwk", + data: { + crv: "X448", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + kty: "OKP" + }, + }, + { + format: "jwk", + data: { + crv: "X448", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm", + kty: "OKP" + }, + }, +]; + +var missingJWKFieldKeyData = [ + { + param: "x", + data: { + crv: "X448", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4", + kty: "OKP" + } + }, + { + param: "kty", + data: { + crv: "X448", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4", + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + } + }, + { + param: "crv", + data: { + x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8", + kty: "OKP" + } + } +]; + +// The public key doesn't match the private key. +var invalidJWKKeyData = [ + { + + crv: "X448", + kty: "OKP", + d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4", + x: "mwj3zDG34+Z9ItWuoSEHSic70rg94Jxj+qc9LCLF2bvINmRyQdlT1AxbEtqIEg1TF3+A5TLEH6A", + }, +]; + +run_test(["X448"]); diff --git a/testing/web-platform/tests/WebCryptoAPI/import_export/rsa_importKey.https.any.js b/testing/web-platform/tests/WebCryptoAPI/import_export/rsa_importKey.https.any.js new file mode 100644 index 0000000000..9f9d38a955 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/import_export/rsa_importKey.https.any.js @@ -0,0 +1,295 @@ +// META: title=WebCryptoAPI: importKey() for RSA keys +// META: timeout=long + +// Test importKey and exportKey for RSA algorithms. Only "happy paths" are +// currently tested - those where the operation should succeed. + + var subtle = crypto.subtle; + + var sizes = [1024, 2048, 4096]; + + var hashes = ["SHA-1", "SHA-256", "SHA-384", "SHA-512"]; + + var keyData = { + 1024: { + spki: new Uint8Array([48, 129, 159, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 129, 141, 0, 48, 129, 137, 2, 129, 129, 0, 205, 153, 248, 177, 17, 159, 141, 10, 44, 231, 172, 139, 253, 12, 181, 71, 211, 72, 249, 49, 204, 156, 92, 167, 159, 222, 32, 229, 28, 64, 235, 1, 171, 38, 30, 1, 37, 61, 241, 232, 143, 113, 208, 134, 233, 75, 122, 190, 119, 131, 145, 3, 164, 118, 190, 224, 204, 135, 199, 67, 21, 26, 253, 68, 49, 250, 93, 143, 160, 81, 39, 28, 245, 78, 73, 207, 117, 0, 216, 169, 149, 126, 192, 155, 157, 67, 239, 112, 9, 140, 87, 241, 13, 3, 191, 211, 23, 72, 175, 86, 59, 136, 22, 135, 114, 13, 60, 123, 16, 161, 205, 85, 58, 199, 29, 41, 107, 110, 222, 236, 165, 185, 156, 138, 251, 54, 221, 151, 2, 3, 1, 0, 1]), + pkcs8: new Uint8Array([48, 130, 2, 120, 2, 1, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 4, 130, 2, 98, 48, 130, 2, 94, 2, 1, 0, 2, 129, 129, 0, 205, 153, 248, 177, 17, 159, 141, 10, 44, 231, 172, 139, 253, 12, 181, 71, 211, 72, 249, 49, 204, 156, 92, 167, 159, 222, 32, 229, 28, 64, 235, 1, 171, 38, 30, 1, 37, 61, 241, 232, 143, 113, 208, 134, 233, 75, 122, 190, 119, 131, 145, 3, 164, 118, 190, 224, 204, 135, 199, 67, 21, 26, 253, 68, 49, 250, 93, 143, 160, 81, 39, 28, 245, 78, 73, 207, 117, 0, 216, 169, 149, 126, 192, 155, 157, 67, 239, 112, 9, 140, 87, 241, 13, 3, 191, 211, 23, 72, 175, 86, 59, 136, 22, 135, 114, 13, 60, 123, 16, 161, 205, 85, 58, 199, 29, 41, 107, 110, 222, 236, 165, 185, 156, 138, 251, 54, 221, 151, 2, 3, 1, 0, 1, 2, 129, 128, 98, 162, 10, 252, 103, 71, 243, 145, 126, 25, 102, 93, 129, 248, 38, 191, 94, 77, 19, 191, 32, 57, 162, 249, 135, 104, 56, 191, 176, 222, 51, 223, 137, 11, 176, 57, 60, 116, 139, 40, 214, 39, 243, 177, 197, 25, 192, 184, 190, 253, 15, 4, 128, 81, 183, 32, 128, 254, 98, 73, 124, 70, 134, 88, 228, 85, 8, 229, 210, 6, 149, 141, 122, 147, 24, 166, 42, 57, 218, 125, 240, 230, 232, 249, 81, 145, 44, 6, 118, 237, 101, 205, 4, 181, 104, 85, 23, 96, 46, 169, 174, 213, 110, 34, 171, 89, 196, 20, 18, 1, 8, 241, 93, 32, 19, 144, 248, 183, 32, 96, 240, 101, 239, 247, 222, 249, 117, 1, 2, 65, 0, 244, 26, 192, 131, 146, 245, 205, 250, 134, 62, 229, 137, 14, 224, 194, 5, 127, 147, 154, 214, 93, 172, 226, 55, 98, 206, 25, 104, 223, 178, 48, 249, 83, 143, 5, 146, 16, 243, 180, 170, 119, 227, 17, 151, 48, 217, 88, 23, 30, 2, 73, 153, 181, 92, 163, 164, 241, 114, 66, 66, 152, 70, 42, 121, 2, 65, 0, 215, 158, 227, 12, 157, 88, 107, 153, 230, 66, 244, 207, 110, 18, 128, 60, 7, 140, 90, 136, 49, 11, 38, 144, 78, 64, 107, 167, 125, 41, 16, 167, 122, 152, 100, 129, 223, 206, 97, 170, 190, 1, 34, 79, 44, 221, 254, 204, 117, 122, 76, 249, 68, 169, 105, 152, 20, 161, 62, 40, 255, 101, 68, 143, 2, 65, 0, 169, 215, 127, 65, 76, 220, 104, 31, 186, 142, 66, 168, 213, 72, 62, 215, 18, 136, 2, 0, 203, 22, 194, 35, 37, 69, 31, 90, 223, 226, 28, 191, 45, 139, 98, 165, 217, 211, 167, 77, 192, 178, 166, 7, 155, 62, 110, 83, 79, 86, 234, 28, 223, 154, 128, 102, 0, 116, 174, 115, 165, 125, 148, 137, 2, 65, 0, 132, 212, 95, 192, 228, 169, 148, 215, 225, 46, 252, 75, 80, 222, 218, 218, 160, 55, 201, 137, 190, 212, 196, 179, 255, 80, 214, 64, 254, 236, 174, 82, 206, 70, 85, 28, 96, 248, 109, 216, 86, 102, 178, 113, 30, 13, 192, 42, 202, 112, 70, 61, 5, 28, 108, 109, 128, 191, 248, 96, 31, 61, 142, 103, 2, 65, 0, 205, 186, 73, 64, 8, 98, 158, 188, 82, 109, 82, 177, 5, 13, 132, 100, 97, 84, 15, 103, 183, 88, 37, 219, 0, 148, 88, 166, 79, 7, 85, 14, 64, 3, 157, 142, 132, 164, 226, 112, 236, 158, 218, 17, 7, 158, 184, 41, 20, 172, 194, 242, 44, 231, 78, 192, 134, 220, 83, 36, 191, 7, 35, 225]), + jwk: { + kty: "RSA", + n: "zZn4sRGfjQos56yL_Qy1R9NI-THMnFynn94g5RxA6wGrJh4BJT3x6I9x0IbpS3q-d4ORA6R2vuDMh8dDFRr9RDH6XY-gUScc9U5Jz3UA2KmVfsCbnUPvcAmMV_ENA7_TF0ivVjuIFodyDTx7EKHNVTrHHSlrbt7spbmcivs23Zc", + e: "AQAB", + d: "YqIK_GdH85F-GWZdgfgmv15NE78gOaL5h2g4v7DeM9-JC7A5PHSLKNYn87HFGcC4vv0PBIBRtyCA_mJJfEaGWORVCOXSBpWNepMYpio52n3w5uj5UZEsBnbtZc0EtWhVF2Auqa7VbiKrWcQUEgEI8V0gE5D4tyBg8GXv9975dQE", + p: "9BrAg5L1zfqGPuWJDuDCBX-TmtZdrOI3Ys4ZaN-yMPlTjwWSEPO0qnfjEZcw2VgXHgJJmbVco6TxckJCmEYqeQ", + q: "157jDJ1Ya5nmQvTPbhKAPAeMWogxCyaQTkBrp30pEKd6mGSB385hqr4BIk8s3f7MdXpM-USpaZgUoT4o_2VEjw", + dp: "qdd_QUzcaB-6jkKo1Ug-1xKIAgDLFsIjJUUfWt_iHL8ti2Kl2dOnTcCypgebPm5TT1bqHN-agGYAdK5zpX2UiQ", + dq: "hNRfwOSplNfhLvxLUN7a2qA3yYm-1MSz_1DWQP7srlLORlUcYPht2FZmsnEeDcAqynBGPQUcbG2Av_hgHz2OZw", + qi: "zbpJQAhinrxSbVKxBQ2EZGFUD2e3WCXbAJRYpk8HVQ5AA52OhKTicOye2hEHnrgpFKzC8iznTsCG3FMkvwcj4Q" + } + }, + + 2048: { + spki: new Uint8Array([48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 217, 133, 128, 235, 45, 23, 114, 244, 164, 118, 188, 84, 4, 190, 230, 13, 154, 60, 42, 203, 188, 242, 74, 116, 117, 77, 159, 90, 104, 18, 56, 143, 158, 63, 38, 10, 216, 22, 135, 221, 179, 102, 248, 218, 85, 148, 98, 179, 151, 241, 192, 151, 137, 109, 13, 246, 230, 222, 49, 192, 79, 141, 71, 205, 21, 96, 13, 17, 190, 78, 196, 230, 48, 158, 32, 4, 22, 37, 127, 171, 186, 139, 190, 211, 58, 176, 193, 101, 218, 60, 155, 31, 206, 194, 196, 233, 229, 42, 202, 99, 89, 167, 207, 84, 213, 39, 91, 68, 134, 191, 1, 162, 180, 95, 4, 250, 226, 11, 113, 125, 1, 167, 148, 87, 7, 40, 129, 82, 151, 178, 183, 242, 43, 224, 14, 243, 2, 56, 19, 202, 135, 183, 224, 190, 131, 67, 51, 92, 250, 240, 118, 158, 54, 108, 249, 37, 108, 244, 66, 57, 69, 139, 180, 126, 189, 107, 50, 240, 22, 137, 128, 103, 0, 146, 115, 247, 157, 69, 184, 91, 159, 51, 245, 115, 24, 223, 197, 175, 152, 26, 162, 150, 72, 52, 231, 245, 179, 48, 18, 211, 105, 100, 106, 103, 56, 178, 43, 202, 85, 229, 144, 102, 241, 230, 159, 106, 105, 241, 238, 222, 204, 232, 129, 183, 66, 63, 212, 77, 252, 122, 124, 152, 156, 66, 103, 65, 216, 129, 60, 63, 205, 192, 36, 181, 61, 132, 41, 10, 59, 237, 163, 200, 56, 114, 202, 253, 2, 3, 1, 0, 1]), + pkcs8: new Uint8Array([48, 130, 4, 190, 2, 1, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 4, 130, 4, 168, 48, 130, 4, 164, 2, 1, 0, 2, 130, 1, 1, 0, 217, 133, 128, 235, 45, 23, 114, 244, 164, 118, 188, 84, 4, 190, 230, 13, 154, 60, 42, 203, 188, 242, 74, 116, 117, 77, 159, 90, 104, 18, 56, 143, 158, 63, 38, 10, 216, 22, 135, 221, 179, 102, 248, 218, 85, 148, 98, 179, 151, 241, 192, 151, 137, 109, 13, 246, 230, 222, 49, 192, 79, 141, 71, 205, 21, 96, 13, 17, 190, 78, 196, 230, 48, 158, 32, 4, 22, 37, 127, 171, 186, 139, 190, 211, 58, 176, 193, 101, 218, 60, 155, 31, 206, 194, 196, 233, 229, 42, 202, 99, 89, 167, 207, 84, 213, 39, 91, 68, 134, 191, 1, 162, 180, 95, 4, 250, 226, 11, 113, 125, 1, 167, 148, 87, 7, 40, 129, 82, 151, 178, 183, 242, 43, 224, 14, 243, 2, 56, 19, 202, 135, 183, 224, 190, 131, 67, 51, 92, 250, 240, 118, 158, 54, 108, 249, 37, 108, 244, 66, 57, 69, 139, 180, 126, 189, 107, 50, 240, 22, 137, 128, 103, 0, 146, 115, 247, 157, 69, 184, 91, 159, 51, 245, 115, 24, 223, 197, 175, 152, 26, 162, 150, 72, 52, 231, 245, 179, 48, 18, 211, 105, 100, 106, 103, 56, 178, 43, 202, 85, 229, 144, 102, 241, 230, 159, 106, 105, 241, 238, 222, 204, 232, 129, 183, 66, 63, 212, 77, 252, 122, 124, 152, 156, 66, 103, 65, 216, 129, 60, 63, 205, 192, 36, 181, 61, 132, 41, 10, 59, 237, 163, 200, 56, 114, 202, 253, 2, 3, 1, 0, 1, 2, 130, 1, 0, 90, 210, 167, 117, 138, 170, 83, 209, 90, 42, 73, 144, 59, 59, 10, 11, 123, 238, 203, 95, 174, 80, 236, 77, 155, 253, 1, 32, 90, 123, 225, 41, 246, 69, 31, 185, 63, 104, 136, 234, 68, 210, 37, 237, 227, 245, 197, 16, 127, 204, 237, 65, 88, 156, 52, 76, 119, 49, 39, 76, 200, 234, 144, 164, 76, 220, 130, 24, 122, 129, 161, 45, 11, 247, 186, 30, 122, 176, 197, 146, 10, 157, 246, 219, 115, 146, 1, 238, 105, 37, 13, 16, 70, 224, 132, 31, 181, 20, 28, 213, 70, 198, 14, 135, 185, 72, 105, 143, 63, 67, 217, 134, 250, 17, 2, 159, 78, 106, 192, 196, 21, 64, 199, 107, 95, 13, 198, 144, 212, 69, 255, 226, 191, 121, 46, 30, 103, 153, 111, 171, 166, 137, 88, 229, 86, 142, 66, 238, 136, 24, 72, 248, 27, 43, 116, 101, 215, 99, 39, 246, 212, 111, 241, 132, 169, 7, 252, 19, 104, 172, 233, 8, 40, 227, 172, 42, 47, 36, 134, 34, 214, 97, 228, 179, 215, 193, 4, 222, 129, 165, 1, 59, 216, 171, 50, 17, 100, 68, 199, 226, 114, 175, 49, 6, 95, 129, 122, 189, 198, 152, 17, 113, 70, 121, 104, 51, 75, 18, 210, 27, 237, 93, 87, 104, 49, 64, 112, 122, 198, 34, 61, 209, 7, 6, 121, 22, 191, 95, 151, 248, 124, 7, 87, 143, 45, 123, 22, 128, 153, 197, 130, 196, 244, 164, 225, 241, 2, 129, 129, 0, 252, 223, 109, 18, 211, 223, 124, 146, 67, 138, 211, 142, 156, 153, 102, 192, 192, 236, 129, 21, 14, 158, 28, 228, 12, 184, 69, 239, 165, 195, 209, 9, 236, 240, 88, 59, 143, 104, 199, 197, 124, 83, 168, 201, 166, 249, 158, 156, 67, 158, 15, 116, 155, 224, 83, 172, 112, 187, 1, 225, 127, 254, 175, 175, 214, 214, 36, 111, 218, 85, 109, 33, 228, 157, 192, 61, 195, 207, 25, 136, 154, 244, 134, 69, 18, 103, 225, 172, 131, 16, 168, 70, 3, 30, 5, 98, 162, 47, 88, 191, 99, 241, 127, 93, 36, 4, 72, 97, 227, 7, 70, 60, 141, 25, 150, 77, 170, 201, 86, 129, 29, 96, 60, 41, 231, 190, 200, 107, 2, 129, 129, 0, 220, 54, 40, 140, 204, 79, 7, 149, 241, 40, 229, 237, 13, 3, 118, 172, 76, 61, 137, 8, 253, 72, 223, 119, 189, 19, 87, 199, 3, 61, 197, 45, 111, 18, 58, 224, 121, 190, 144, 46, 143, 225, 7, 129, 10, 154, 24, 140, 96, 246, 212, 224, 232, 144, 67, 98, 6, 188, 167, 17, 224, 215, 160, 182, 249, 132, 174, 249, 21, 78, 138, 59, 186, 184, 239, 10, 71, 146, 46, 189, 206, 165, 57, 50, 38, 241, 230, 57, 169, 77, 76, 229, 53, 45, 184, 87, 22, 194, 94, 48, 68, 246, 171, 255, 73, 197, 25, 64, 13, 132, 56, 120, 241, 100, 197, 243, 171, 84, 246, 32, 86, 55, 55, 216, 121, 64, 52, 55, 2, 129, 128, 109, 221, 189, 12, 35, 21, 196, 143, 223, 220, 159, 82, 36, 227, 217, 107, 1, 231, 63, 166, 32, 117, 189, 227, 175, 75, 24, 199, 168, 99, 205, 156, 220, 95, 8, 86, 200, 86, 36, 5, 191, 160, 177, 130, 251, 147, 20, 192, 155, 248, 62, 138, 209, 118, 195, 163, 246, 78, 169, 224, 137, 181, 228, 43, 39, 210, 94, 126, 98, 132, 31, 40, 76, 165, 229, 114, 112, 114, 184, 139, 75, 151, 214, 6, 136, 154, 173, 200, 64, 33, 170, 154, 208, 155, 232, 135, 20, 36, 50, 16, 229, 161, 117, 78, 200, 105, 59, 241, 155, 171, 251, 110, 47, 119, 224, 127, 218, 38, 35, 249, 113, 3, 240, 223, 220, 26, 94, 5, 2, 129, 129, 0, 149, 113, 187, 187, 49, 188, 64, 109, 165, 168, 23, 193, 244, 30, 241, 158, 164, 110, 238, 92, 199, 103, 121, 32, 141, 148, 94, 241, 148, 101, 139, 54, 246, 53, 236, 247, 2, 40, 45, 57, 44, 51, 143, 32, 39, 205, 195, 243, 32, 170, 226, 117, 111, 222, 215, 155, 226, 238, 140, 131, 57, 143, 156, 102, 16, 151, 215, 22, 251, 58, 189, 221, 35, 46, 246, 42, 135, 191, 209, 48, 198, 216, 162, 36, 67, 1, 207, 56, 58, 137, 87, 50, 6, 16, 237, 21, 77, 64, 195, 35, 6, 234, 80, 119, 131, 220, 218, 241, 249, 58, 78, 8, 229, 233, 121, 221, 143, 220, 172, 219, 237, 38, 180, 35, 152, 197, 213, 169, 2, 129, 129, 0, 157, 34, 27, 203, 101, 161, 91, 231, 149, 223, 255, 186, 178, 175, 168, 93, 194, 163, 171, 101, 186, 95, 110, 38, 250, 23, 38, 18, 213, 87, 33, 41, 187, 18, 0, 21, 202, 68, 70, 236, 63, 219, 158, 201, 128, 166, 97, 210, 170, 210, 56, 80, 81, 24, 152, 240, 124, 20, 135, 22, 9, 92, 209, 189, 96, 214, 49, 70, 74, 200, 155, 82, 70, 96, 189, 70, 89, 82, 210, 229, 125, 135, 64, 183, 195, 243, 219, 121, 73, 43, 22, 184, 122, 92, 209, 118, 126, 19, 82, 110, 246, 109, 121, 198, 145, 226, 199, 242, 82, 139, 105, 101, 44, 41, 186, 33, 10, 94, 103, 157, 35, 178, 26, 104, 12, 191, 13, 7]), + jwk: { + kty: "RSA", + n: "2YWA6y0XcvSkdrxUBL7mDZo8Ksu88kp0dU2fWmgSOI-ePyYK2BaH3bNm-NpVlGKzl_HAl4ltDfbm3jHAT41HzRVgDRG-TsTmMJ4gBBYlf6u6i77TOrDBZdo8mx_OwsTp5SrKY1mnz1TVJ1tEhr8BorRfBPriC3F9AaeUVwcogVKXsrfyK-AO8wI4E8qHt-C-g0MzXPrwdp42bPklbPRCOUWLtH69azLwFomAZwCSc_edRbhbnzP1cxjfxa-YGqKWSDTn9bMwEtNpZGpnOLIrylXlkGbx5p9qafHu3szogbdCP9RN_Hp8mJxCZ0HYgTw_zcAktT2EKQo77aPIOHLK_Q", + e: "AQAB", + d: "WtKndYqqU9FaKkmQOzsKC3vuy1-uUOxNm_0BIFp74Sn2RR-5P2iI6kTSJe3j9cUQf8ztQVicNEx3MSdMyOqQpEzcghh6gaEtC_e6HnqwxZIKnfbbc5IB7mklDRBG4IQftRQc1UbGDoe5SGmPP0PZhvoRAp9OasDEFUDHa18NxpDURf_iv3kuHmeZb6umiVjlVo5C7ogYSPgbK3Rl12Mn9tRv8YSpB_wTaKzpCCjjrCovJIYi1mHks9fBBN6BpQE72KsyEWREx-JyrzEGX4F6vcaYEXFGeWgzSxLSG-1dV2gxQHB6xiI90QcGeRa_X5f4fAdXjy17FoCZxYLE9KTh8Q", + p: "_N9tEtPffJJDitOOnJlmwMDsgRUOnhzkDLhF76XD0Qns8Fg7j2jHxXxTqMmm-Z6cQ54PdJvgU6xwuwHhf_6vr9bWJG_aVW0h5J3APcPPGYia9IZFEmfhrIMQqEYDHgVioi9Yv2Pxf10kBEhh4wdGPI0Zlk2qyVaBHWA8Kee-yGs", + q: "3DYojMxPB5XxKOXtDQN2rEw9iQj9SN93vRNXxwM9xS1vEjrgeb6QLo_hB4EKmhiMYPbU4OiQQ2IGvKcR4NegtvmErvkVToo7urjvCkeSLr3OpTkyJvHmOalNTOU1LbhXFsJeMET2q_9JxRlADYQ4ePFkxfOrVPYgVjc32HlANDc", + dp: "bd29DCMVxI_f3J9SJOPZawHnP6Ygdb3jr0sYx6hjzZzcXwhWyFYkBb-gsYL7kxTAm_g-itF2w6P2TqngibXkKyfSXn5ihB8oTKXlcnByuItLl9YGiJqtyEAhqprQm-iHFCQyEOWhdU7IaTvxm6v7bi934H_aJiP5cQPw39waXgU", + dq: "lXG7uzG8QG2lqBfB9B7xnqRu7lzHZ3kgjZRe8ZRlizb2Nez3AigtOSwzjyAnzcPzIKridW_e15vi7oyDOY-cZhCX1xb7Or3dIy72Koe_0TDG2KIkQwHPODqJVzIGEO0VTUDDIwbqUHeD3Nrx-TpOCOXped2P3Kzb7Sa0I5jF1ak", + qi: "nSIby2WhW-eV3_-6sq-oXcKjq2W6X24m-hcmEtVXISm7EgAVykRG7D_bnsmApmHSqtI4UFEYmPB8FIcWCVzRvWDWMUZKyJtSRmC9RllS0uV9h0C3w_PbeUkrFrh6XNF2fhNSbvZtecaR4sfyUotpZSwpuiEKXmedI7IaaAy_DQc" + } + }, + + 4096: { + spki: new Uint8Array([48, 130, 2, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 2, 15, 0, 48, 130, 2, 10, 2, 130, 2, 1, 0, 218, 170, 246, 76, 189, 156, 216, 153, 155, 176, 221, 14, 44, 132, 103, 104, 0, 127, 100, 166, 245, 248, 104, 125, 31, 74, 155, 226, 90, 193, 184, 54, 170, 145, 111, 222, 20, 252, 19, 248, 146, 44, 190, 115, 73, 188, 52, 251, 4, 178, 121, 238, 212, 204, 34, 62, 122, 100, 203, 111, 233, 231, 210, 73, 53, 146, 147, 211, 14, 161, 109, 137, 212, 175, 226, 18, 183, 173, 103, 103, 30, 128, 31, 218, 69, 126, 234, 65, 88, 231, 160, 91, 51, 245, 77, 54, 4, 167, 192, 33, 68, 244, 163, 242, 187, 111, 209, 180, 241, 221, 107, 172, 5, 40, 134, 47, 210, 85, 8, 112, 57, 186, 29, 131, 176, 93, 116, 198, 202, 82, 108, 251, 209, 3, 72, 75, 143, 59, 44, 222, 56, 89, 69, 103, 159, 211, 160, 19, 214, 173, 77, 133, 0, 68, 219, 164, 79, 64, 238, 65, 189, 201, 248, 173, 180, 146, 196, 238, 86, 232, 215, 109, 39, 165, 162, 16, 230, 46, 134, 234, 148, 106, 34, 230, 198, 63, 231, 143, 16, 179, 208, 109, 22, 100, 54, 156, 107, 132, 28, 208, 118, 205, 217, 89, 228, 75, 196, 169, 181, 5, 85, 157, 144, 110, 129, 186, 141, 119, 104, 162, 206, 170, 115, 7, 96, 82, 240, 33, 143, 81, 243, 215, 67, 96, 137, 207, 209, 22, 162, 251, 108, 208, 232, 32, 236, 205, 167, 174, 161, 116, 13, 249, 187, 22, 240, 185, 172, 160, 103, 94, 162, 147, 26, 15, 143, 183, 147, 98, 231, 117, 134, 185, 50, 64, 40, 30, 27, 13, 152, 132, 40, 138, 32, 78, 158, 162, 207, 212, 229, 210, 251, 88, 116, 67, 229, 164, 164, 147, 59, 32, 94, 217, 197, 242, 149, 102, 74, 219, 46, 127, 68, 28, 116, 10, 2, 249, 231, 130, 123, 29, 45, 73, 56, 17, 195, 208, 45, 25, 60, 252, 98, 189, 109, 25, 0, 253, 151, 254, 124, 211, 48, 23, 156, 78, 163, 154, 188, 17, 69, 14, 188, 16, 64, 59, 190, 136, 70, 162, 253, 237, 156, 111, 41, 27, 40, 63, 205, 204, 94, 0, 50, 237, 62, 87, 211, 115, 91, 68, 194, 104, 119, 72, 106, 226, 160, 48, 165, 138, 134, 2, 138, 153, 181, 38, 249, 48, 120, 72, 15, 245, 227, 15, 164, 64, 188, 74, 4, 84, 213, 83, 67, 73, 87, 181, 72, 94, 46, 54, 193, 252, 188, 14, 207, 28, 82, 159, 131, 168, 238, 168, 145, 28, 230, 27, 126, 151, 93, 5, 96, 68, 126, 66, 174, 155, 101, 123, 20, 218, 131, 92, 124, 78, 82, 44, 55, 139, 77, 105, 177, 136, 121, 177, 43, 77, 12, 240, 0, 76, 20, 133, 121, 129, 73, 15, 160, 200, 150, 114, 95, 59, 59, 165, 240, 204, 13, 156, 134, 194, 4, 70, 158, 213, 111, 229, 103, 216, 239, 132, 16, 184, 151, 206, 254, 229, 62, 23, 58, 125, 49, 144, 208, 215, 2, 3, 1, 0, 1]), + pkcs8: new Uint8Array([48, 130, 9, 68, 2, 1, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 4, 130, 9, 46, 48, 130, 9, 42, 2, 1, 0, 2, 130, 2, 1, 0, 218, 170, 246, 76, 189, 156, 216, 153, 155, 176, 221, 14, 44, 132, 103, 104, 0, 127, 100, 166, 245, 248, 104, 125, 31, 74, 155, 226, 90, 193, 184, 54, 170, 145, 111, 222, 20, 252, 19, 248, 146, 44, 190, 115, 73, 188, 52, 251, 4, 178, 121, 238, 212, 204, 34, 62, 122, 100, 203, 111, 233, 231, 210, 73, 53, 146, 147, 211, 14, 161, 109, 137, 212, 175, 226, 18, 183, 173, 103, 103, 30, 128, 31, 218, 69, 126, 234, 65, 88, 231, 160, 91, 51, 245, 77, 54, 4, 167, 192, 33, 68, 244, 163, 242, 187, 111, 209, 180, 241, 221, 107, 172, 5, 40, 134, 47, 210, 85, 8, 112, 57, 186, 29, 131, 176, 93, 116, 198, 202, 82, 108, 251, 209, 3, 72, 75, 143, 59, 44, 222, 56, 89, 69, 103, 159, 211, 160, 19, 214, 173, 77, 133, 0, 68, 219, 164, 79, 64, 238, 65, 189, 201, 248, 173, 180, 146, 196, 238, 86, 232, 215, 109, 39, 165, 162, 16, 230, 46, 134, 234, 148, 106, 34, 230, 198, 63, 231, 143, 16, 179, 208, 109, 22, 100, 54, 156, 107, 132, 28, 208, 118, 205, 217, 89, 228, 75, 196, 169, 181, 5, 85, 157, 144, 110, 129, 186, 141, 119, 104, 162, 206, 170, 115, 7, 96, 82, 240, 33, 143, 81, 243, 215, 67, 96, 137, 207, 209, 22, 162, 251, 108, 208, 232, 32, 236, 205, 167, 174, 161, 116, 13, 249, 187, 22, 240, 185, 172, 160, 103, 94, 162, 147, 26, 15, 143, 183, 147, 98, 231, 117, 134, 185, 50, 64, 40, 30, 27, 13, 152, 132, 40, 138, 32, 78, 158, 162, 207, 212, 229, 210, 251, 88, 116, 67, 229, 164, 164, 147, 59, 32, 94, 217, 197, 242, 149, 102, 74, 219, 46, 127, 68, 28, 116, 10, 2, 249, 231, 130, 123, 29, 45, 73, 56, 17, 195, 208, 45, 25, 60, 252, 98, 189, 109, 25, 0, 253, 151, 254, 124, 211, 48, 23, 156, 78, 163, 154, 188, 17, 69, 14, 188, 16, 64, 59, 190, 136, 70, 162, 253, 237, 156, 111, 41, 27, 40, 63, 205, 204, 94, 0, 50, 237, 62, 87, 211, 115, 91, 68, 194, 104, 119, 72, 106, 226, 160, 48, 165, 138, 134, 2, 138, 153, 181, 38, 249, 48, 120, 72, 15, 245, 227, 15, 164, 64, 188, 74, 4, 84, 213, 83, 67, 73, 87, 181, 72, 94, 46, 54, 193, 252, 188, 14, 207, 28, 82, 159, 131, 168, 238, 168, 145, 28, 230, 27, 126, 151, 93, 5, 96, 68, 126, 66, 174, 155, 101, 123, 20, 218, 131, 92, 124, 78, 82, 44, 55, 139, 77, 105, 177, 136, 121, 177, 43, 77, 12, 240, 0, 76, 20, 133, 121, 129, 73, 15, 160, 200, 150, 114, 95, 59, 59, 165, 240, 204, 13, 156, 134, 194, 4, 70, 158, 213, 111, 229, 103, 216, 239, 132, 16, 184, 151, 206, 254, 229, 62, 23, 58, 125, 49, 144, 208, 215, 2, 3, 1, 0, 1, 2, 130, 2, 1, 0, 185, 115, 209, 92, 24, 92, 19, 159, 131, 89, 166, 193, 68, 164, 46, 135, 24, 20, 243, 42, 94, 230, 4, 200, 73, 103, 159, 121, 131, 251, 83, 222, 153, 30, 171, 191, 176, 16, 114, 103, 152, 161, 118, 12, 148, 246, 152, 0, 100, 101, 113, 224, 74, 125, 174, 117, 74, 156, 125, 165, 54, 189, 179, 172, 255, 80, 135, 42, 178, 247, 217, 204, 209, 163, 49, 155, 42, 72, 88, 176, 46, 63, 255, 195, 192, 184, 248, 183, 223, 76, 226, 197, 54, 245, 206, 60, 8, 10, 181, 122, 1, 223, 113, 196, 133, 143, 58, 77, 185, 235, 78, 76, 32, 59, 212, 66, 110, 162, 75, 123, 210, 153, 180, 58, 97, 179, 129, 60, 175, 142, 228, 123, 85, 50, 241, 119, 147, 204, 94, 43, 65, 163, 4, 167, 243, 247, 41, 134, 105, 197, 165, 63, 45, 145, 56, 174, 203, 192, 135, 209, 29, 195, 83, 179, 14, 184, 131, 104, 152, 48, 245, 179, 207, 178, 60, 23, 21, 1, 84, 207, 82, 124, 9, 137, 171, 141, 187, 55, 172, 180, 180, 10, 48, 185, 97, 79, 156, 39, 249, 192, 27, 98, 77, 250, 93, 18, 157, 130, 72, 210, 115, 96, 36, 132, 116, 101, 225, 96, 234, 79, 89, 243, 89, 135, 97, 252, 53, 72, 97, 34, 226, 41, 41, 45, 144, 243, 189, 162, 243, 43, 69, 136, 143, 182, 140, 223, 134, 93, 38, 245, 36, 125, 46, 93, 48, 94, 215, 39, 156, 57, 86, 93, 207, 204, 72, 106, 112, 215, 203, 230, 80, 20, 137, 224, 242, 33, 146, 33, 108, 188, 185, 254, 117, 189, 240, 82, 64, 60, 186, 247, 190, 138, 170, 159, 147, 75, 49, 148, 101, 174, 130, 21, 177, 211, 121, 6, 153, 144, 230, 166, 181, 155, 94, 232, 2, 4, 119, 236, 35, 133, 253, 223, 14, 30, 199, 57, 215, 31, 251, 90, 167, 19, 231, 154, 54, 225, 85, 68, 17, 234, 158, 53, 50, 243, 182, 149, 193, 214, 60, 188, 6, 38, 2, 200, 161, 232, 193, 30, 153, 231, 221, 57, 140, 55, 69, 35, 21, 153, 34, 238, 175, 65, 253, 210, 119, 125, 120, 116, 153, 127, 67, 204, 9, 66, 210, 200, 165, 212, 216, 2, 62, 19, 15, 171, 77, 183, 247, 127, 224, 138, 41, 208, 170, 227, 36, 158, 176, 111, 128, 172, 70, 73, 241, 148, 172, 50, 174, 126, 80, 177, 235, 93, 89, 102, 84, 76, 221, 30, 216, 49, 125, 142, 35, 45, 96, 224, 60, 161, 63, 48, 85, 143, 20, 76, 182, 111, 15, 156, 139, 55, 155, 113, 226, 248, 239, 130, 252, 241, 197, 247, 124, 61, 39, 197, 170, 119, 76, 136, 195, 180, 169, 106, 240, 234, 101, 114, 207, 11, 160, 170, 139, 194, 187, 48, 22, 114, 84, 64, 151, 30, 212, 99, 213, 176, 106, 79, 232, 127, 197, 153, 133, 8, 56, 210, 83, 67, 106, 124, 231, 96, 2, 145, 2, 130, 1, 1, 0, 244, 218, 215, 194, 174, 36, 99, 217, 1, 4, 236, 11, 160, 86, 85, 65, 206, 36, 36, 143, 205, 108, 166, 191, 91, 209, 75, 117, 7, 81, 33, 179, 44, 101, 145, 215, 39, 117, 195, 81, 31, 111, 36, 7, 26, 105, 30, 249, 91, 2, 2, 237, 126, 141, 231, 153, 213, 181, 100, 234, 219, 192, 114, 179, 215, 229, 39, 212, 107, 9, 55, 220, 136, 233, 237, 28, 74, 97, 6, 22, 26, 47, 150, 83, 82, 95, 186, 146, 22, 38, 176, 231, 255, 166, 199, 223, 217, 86, 142, 56, 43, 199, 25, 247, 249, 122, 59, 142, 152, 20, 49, 147, 13, 132, 249, 203, 251, 146, 116, 96, 88, 81, 232, 45, 106, 100, 187, 99, 73, 32, 203, 134, 30, 223, 100, 179, 179, 128, 81, 242, 25, 85, 137, 125, 96, 153, 240, 224, 86, 20, 206, 24, 26, 197, 233, 164, 158, 50, 222, 103, 197, 211, 144, 101, 182, 205, 201, 51, 23, 231, 125, 229, 130, 61, 139, 204, 195, 243, 69, 38, 185, 187, 48, 249, 140, 107, 137, 39, 234, 21, 13, 43, 24, 112, 108, 109, 15, 25, 57, 55, 127, 40, 152, 238, 227, 96, 86, 157, 114, 35, 52, 54, 38, 140, 85, 42, 119, 53, 99, 35, 133, 208, 240, 65, 171, 8, 71, 255, 243, 248, 176, 166, 17, 178, 92, 62, 203, 56, 158, 31, 169, 223, 123, 7, 118, 216, 166, 132, 83, 62, 112, 160, 99, 244, 132, 29, 2, 130, 1, 1, 0, 228, 158, 249, 243, 243, 94, 42, 189, 87, 61, 152, 139, 197, 122, 33, 97, 4, 39, 135, 66, 219, 225, 11, 70, 103, 92, 115, 10, 8, 225, 5, 2, 220, 32, 23, 147, 56, 111, 237, 98, 48, 174, 122, 207, 109, 152, 187, 125, 220, 186, 73, 127, 42, 82, 39, 228, 163, 12, 188, 36, 71, 107, 52, 235, 223, 200, 7, 38, 6, 167, 28, 158, 26, 213, 126, 186, 90, 152, 133, 44, 53, 156, 61, 130, 92, 163, 3, 27, 35, 185, 141, 112, 236, 246, 210, 107, 75, 245, 33, 126, 134, 215, 41, 1, 244, 220, 36, 93, 22, 232, 50, 62, 68, 141, 153, 118, 62, 1, 167, 197, 202, 113, 187, 196, 186, 251, 161, 128, 66, 211, 145, 103, 133, 69, 207, 155, 117, 65, 76, 251, 125, 43, 224, 105, 171, 6, 29, 254, 31, 111, 144, 5, 158, 166, 180, 143, 163, 205, 212, 151, 7, 11, 50, 234, 82, 37, 143, 75, 104, 124, 97, 69, 220, 246, 202, 45, 25, 40, 220, 23, 92, 116, 112, 114, 204, 198, 140, 48, 111, 191, 53, 28, 9, 134, 234, 90, 168, 243, 108, 75, 197, 99, 162, 173, 31, 194, 97, 224, 184, 76, 227, 170, 199, 106, 129, 14, 77, 234, 231, 38, 192, 197, 233, 174, 150, 240, 55, 252, 241, 27, 97, 169, 49, 49, 115, 9, 218, 65, 253, 14, 253, 217, 91, 141, 44, 68, 32, 247, 219, 199, 31, 45, 212, 68, 46, 131, 2, 130, 1, 1, 0, 225, 142, 199, 187, 155, 88, 2, 114, 225, 49, 123, 144, 170, 63, 93, 130, 165, 55, 62, 71, 10, 97, 208, 169, 239, 23, 58, 127, 176, 33, 216, 253, 137, 36, 119, 216, 207, 140, 248, 68, 62, 196, 207, 87, 139, 200, 210, 179, 186, 86, 124, 3, 243, 213, 29, 72, 229, 73, 152, 145, 145, 166, 19, 4, 1, 26, 36, 58, 213, 239, 67, 250, 112, 85, 174, 11, 165, 169, 3, 70, 81, 17, 13, 85, 236, 72, 43, 66, 112, 13, 108, 98, 11, 107, 196, 44, 61, 182, 50, 133, 36, 46, 225, 137, 65, 212, 140, 16, 171, 159, 206, 155, 60, 149, 6, 216, 22, 3, 176, 25, 32, 195, 51, 50, 195, 19, 208, 91, 129, 254, 39, 254, 129, 106, 33, 6, 57, 145, 55, 235, 225, 210, 158, 57, 85, 71, 250, 81, 110, 122, 243, 239, 216, 154, 0, 197, 152, 198, 27, 131, 85, 5, 179, 187, 63, 79, 10, 205, 122, 115, 209, 210, 30, 204, 59, 128, 129, 242, 19, 253, 188, 146, 232, 102, 186, 40, 69, 204, 243, 34, 57, 99, 61, 188, 50, 229, 180, 70, 244, 34, 95, 141, 50, 116, 190, 24, 253, 49, 68, 247, 145, 29, 97, 29, 93, 71, 37, 81, 148, 230, 32, 91, 125, 55, 193, 42, 123, 201, 25, 34, 58, 248, 128, 204, 225, 149, 38, 248, 29, 17, 230, 22, 236, 234, 207, 92, 124, 232, 225, 22, 96, 2, 32, 146, 27, 49, 2, 130, 1, 1, 0, 129, 62, 34, 61, 183, 242, 31, 37, 68, 193, 108, 144, 111, 133, 248, 130, 184, 239, 131, 182, 215, 72, 164, 176, 27, 84, 151, 48, 48, 14, 205, 95, 109, 131, 178, 240, 38, 50, 152, 55, 47, 32, 36, 11, 73, 128, 211, 85, 118, 199, 213, 46, 207, 132, 252, 74, 115, 166, 138, 97, 212, 2, 22, 59, 214, 25, 101, 121, 40, 191, 166, 28, 247, 60, 132, 84, 227, 76, 95, 212, 187, 69, 229, 59, 226, 20, 193, 119, 193, 61, 111, 105, 76, 124, 200, 61, 162, 6, 36, 246, 59, 82, 61, 59, 126, 234, 72, 160, 91, 135, 206, 135, 135, 7, 169, 158, 191, 180, 253, 220, 129, 242, 195, 220, 150, 124, 20, 51, 199, 19, 133, 154, 201, 43, 203, 14, 174, 61, 201, 64, 78, 229, 212, 10, 200, 133, 63, 197, 94, 142, 26, 20, 35, 57, 72, 207, 255, 33, 40, 50, 108, 231, 246, 211, 162, 182, 219, 8, 29, 60, 91, 93, 60, 106, 67, 167, 53, 22, 245, 61, 59, 166, 19, 191, 194, 101, 231, 240, 165, 235, 169, 33, 125, 125, 72, 213, 17, 183, 243, 27, 238, 173, 193, 212, 47, 37, 27, 98, 7, 174, 103, 242, 46, 163, 213, 235, 121, 62, 247, 135, 223, 232, 194, 143, 81, 130, 225, 147, 219, 213, 199, 226, 247, 13, 102, 100, 70, 127, 145, 136, 189, 22, 248, 123, 153, 111, 182, 87, 136, 102, 76, 9, 3, 123, 187, 243, 2, 130, 1, 0, 36, 121, 149, 41, 189, 115, 193, 110, 98, 69, 30, 145, 9, 231, 177, 98, 120, 118, 126, 102, 62, 220, 58, 207, 73, 211, 60, 15, 24, 107, 208, 95, 29, 107, 40, 190, 182, 84, 106, 17, 217, 198, 210, 27, 233, 227, 153, 252, 128, 181, 44, 145, 101, 156, 7, 209, 23, 149, 66, 78, 109, 145, 138, 13, 241, 174, 198, 3, 26, 222, 15, 241, 120, 176, 54, 190, 97, 80, 215, 99, 49, 62, 204, 135, 226, 32, 141, 102, 251, 32, 152, 108, 113, 237, 59, 142, 30, 185, 195, 135, 145, 1, 86, 115, 56, 253, 215, 186, 221, 202, 196, 36, 227, 118, 177, 130, 60, 59, 56, 190, 198, 157, 142, 18, 96, 43, 218, 199, 150, 42, 174, 44, 198, 65, 103, 139, 167, 177, 46, 26, 155, 248, 209, 56, 155, 209, 204, 42, 89, 224, 212, 75, 80, 135, 106, 203, 4, 81, 181, 85, 128, 247, 73, 134, 41, 48, 183, 57, 127, 28, 234, 26, 244, 177, 159, 113, 90, 249, 120, 32, 248, 134, 79, 99, 123, 155, 173, 201, 185, 216, 166, 32, 152, 181, 6, 154, 118, 18, 181, 245, 106, 25, 37, 146, 118, 16, 215, 30, 83, 96, 35, 154, 93, 0, 13, 5, 206, 156, 129, 147, 118, 87, 248, 155, 49, 135, 7, 39, 157, 226, 171, 96, 16, 112, 122, 173, 58, 145, 19, 6, 90, 11, 221, 109, 208, 16, 251, 188, 18, 120, 106, 170, 143, 149, 79, 192]), + jwk: { + kty: "RSA", + n: "2qr2TL2c2JmbsN0OLIRnaAB_ZKb1-Gh9H0qb4lrBuDaqkW_eFPwT-JIsvnNJvDT7BLJ57tTMIj56ZMtv6efSSTWSk9MOoW2J1K_iEretZ2cegB_aRX7qQVjnoFsz9U02BKfAIUT0o_K7b9G08d1rrAUohi_SVQhwObodg7BddMbKUmz70QNIS487LN44WUVnn9OgE9atTYUARNukT0DuQb3J-K20ksTuVujXbSelohDmLobqlGoi5sY_548Qs9BtFmQ2nGuEHNB2zdlZ5EvEqbUFVZ2QboG6jXdoos6qcwdgUvAhj1Hz10Ngic_RFqL7bNDoIOzNp66hdA35uxbwuaygZ16ikxoPj7eTYud1hrkyQCgeGw2YhCiKIE6eos_U5dL7WHRD5aSkkzsgXtnF8pVmStsuf0QcdAoC-eeCex0tSTgRw9AtGTz8Yr1tGQD9l_580zAXnE6jmrwRRQ68EEA7vohGov3tnG8pGyg_zcxeADLtPlfTc1tEwmh3SGrioDClioYCipm1JvkweEgP9eMPpEC8SgRU1VNDSVe1SF4uNsH8vA7PHFKfg6juqJEc5ht-l10FYER-Qq6bZXsU2oNcfE5SLDeLTWmxiHmxK00M8ABMFIV5gUkPoMiWcl87O6XwzA2chsIERp7Vb-Vn2O-EELiXzv7lPhc6fTGQ0Nc", + e: "AQAB", + d: "uXPRXBhcE5-DWabBRKQuhxgU8ype5gTISWefeYP7U96ZHqu_sBByZ5ihdgyU9pgAZGVx4Ep9rnVKnH2lNr2zrP9Qhyqy99nM0aMxmypIWLAuP__DwLj4t99M4sU29c48CAq1egHfccSFjzpNuetOTCA71EJuokt70pm0OmGzgTyvjuR7VTLxd5PMXitBowSn8_cphmnFpT8tkTiuy8CH0R3DU7MOuINomDD1s8-yPBcVAVTPUnwJiauNuzestLQKMLlhT5wn-cAbYk36XRKdgkjSc2AkhHRl4WDqT1nzWYdh_DVIYSLiKSktkPO9ovMrRYiPtozfhl0m9SR9Ll0wXtcnnDlWXc_MSGpw18vmUBSJ4PIhkiFsvLn-db3wUkA8uve-iqqfk0sxlGWughWx03kGmZDmprWbXugCBHfsI4X93w4exznXH_tapxPnmjbhVUQR6p41MvO2lcHWPLwGJgLIoejBHpnn3TmMN0UjFZki7q9B_dJ3fXh0mX9DzAlC0sil1NgCPhMPq02393_giinQquMknrBvgKxGSfGUrDKuflCx611ZZlRM3R7YMX2OIy1g4DyhPzBVjxRMtm8PnIs3m3Hi-O-C_PHF93w9J8Wqd0yIw7SpavDqZXLPC6Cqi8K7MBZyVECXHtRj1bBqT-h_xZmFCDjSU0NqfOdgApE", + p: "9NrXwq4kY9kBBOwLoFZVQc4kJI_NbKa_W9FLdQdRIbMsZZHXJ3XDUR9vJAcaaR75WwIC7X6N55nVtWTq28Bys9flJ9RrCTfciOntHEphBhYaL5ZTUl-6khYmsOf_psff2VaOOCvHGff5ejuOmBQxkw2E-cv7knRgWFHoLWpku2NJIMuGHt9ks7OAUfIZVYl9YJnw4FYUzhgaxemknjLeZ8XTkGW2zckzF-d95YI9i8zD80Umubsw-YxriSfqFQ0rGHBsbQ8ZOTd_KJju42BWnXIjNDYmjFUqdzVjI4XQ8EGrCEf_8_iwphGyXD7LOJ4fqd97B3bYpoRTPnCgY_SEHQ", + q: "5J758_NeKr1XPZiLxXohYQQnh0Lb4QtGZ1xzCgjhBQLcIBeTOG_tYjCues9tmLt93LpJfypSJ-SjDLwkR2s069_IByYGpxyeGtV-ulqYhSw1nD2CXKMDGyO5jXDs9tJrS_UhfobXKQH03CRdFugyPkSNmXY-AafFynG7xLr7oYBC05FnhUXPm3VBTPt9K-BpqwYd_h9vkAWeprSPo83UlwcLMupSJY9LaHxhRdz2yi0ZKNwXXHRwcszGjDBvvzUcCYbqWqjzbEvFY6KtH8Jh4LhM46rHaoEOTernJsDF6a6W8Df88RthqTExcwnaQf0O_dlbjSxEIPfbxx8t1EQugw", + dp: "4Y7Hu5tYAnLhMXuQqj9dgqU3PkcKYdCp7xc6f7Ah2P2JJHfYz4z4RD7Ez1eLyNKzulZ8A_PVHUjlSZiRkaYTBAEaJDrV70P6cFWuC6WpA0ZREQ1V7EgrQnANbGILa8QsPbYyhSQu4YlB1IwQq5_OmzyVBtgWA7AZIMMzMsMT0FuB_if-gWohBjmRN-vh0p45VUf6UW568-_YmgDFmMYbg1UFs7s_TwrNenPR0h7MO4CB8hP9vJLoZrooRczzIjljPbwy5bRG9CJfjTJ0vhj9MUT3kR1hHV1HJVGU5iBbfTfBKnvJGSI6-IDM4ZUm-B0R5hbs6s9cfOjhFmACIJIbMQ", + dq: "gT4iPbfyHyVEwWyQb4X4grjvg7bXSKSwG1SXMDAOzV9tg7LwJjKYNy8gJAtJgNNVdsfVLs-E_Epzpoph1AIWO9YZZXkov6Yc9zyEVONMX9S7ReU74hTBd8E9b2lMfMg9ogYk9jtSPTt-6kigW4fOh4cHqZ6_tP3cgfLD3JZ8FDPHE4WaySvLDq49yUBO5dQKyIU_xV6OGhQjOUjP_yEoMmzn9tOittsIHTxbXTxqQ6c1FvU9O6YTv8Jl5_Cl66khfX1I1RG38xvurcHULyUbYgeuZ_Iuo9XreT73h9_owo9RguGT29XH4vcNZmRGf5GIvRb4e5lvtleIZkwJA3u78w", + qi: "JHmVKb1zwW5iRR6RCeexYnh2fmY-3DrPSdM8Dxhr0F8dayi-tlRqEdnG0hvp45n8gLUskWWcB9EXlUJObZGKDfGuxgMa3g_xeLA2vmFQ12MxPsyH4iCNZvsgmGxx7TuOHrnDh5EBVnM4_de63crEJON2sYI8Ozi-xp2OEmAr2seWKq4sxkFni6exLhqb-NE4m9HMKlng1EtQh2rLBFG1VYD3SYYpMLc5fxzqGvSxn3Fa-Xgg-IZPY3ubrcm52KYgmLUGmnYStfVqGSWSdhDXHlNgI5pdAA0FzpyBk3ZX-JsxhwcnneKrYBBweq06kRMGWgvdbdAQ-7wSeGqqj5VPwA" + } + }, + + }; + + // combinations to test + var testVectors = [ + {name: "RSA-OAEP", privateUsages: ["decrypt", "unwrapKey"], publicUsages: ["encrypt", "wrapKey"]}, + {name: "RSA-PSS", privateUsages: ["sign"], publicUsages: ["verify"]}, + {name: "RSASSA-PKCS1-v1_5", privateUsages: ["sign"], publicUsages: ["verify"]} + ]; + + // TESTS ARE HERE: + // Test every test vector, along with all available key data + testVectors.forEach(function(vector) { + sizes.forEach(function(size) { + + hashes.forEach(function(hash) { + [true, false].forEach(function(extractable) { + + // Test public keys first + allValidUsages(vector.publicUsages, []).forEach(function(usages) { + ['spki', 'jwk'].forEach(function(format) { + var algorithm = {name: vector.name, hash: hash}; + var data = keyData[size]; + if (format === "jwk") { // Not all fields used for public keys + data = {jwk: {kty: keyData[size].jwk.kty, n: keyData[size].jwk.n, e: keyData[size].jwk.e}}; + } + + testFormat(format, algorithm, data, size, usages, extractable); + }); + + }); + + // Next, test private keys + allValidUsages(vector.privateUsages, []).forEach(function(usages) { + ['pkcs8', 'jwk'].forEach(function(format) { + var algorithm = {name: vector.name, hash: hash}; + var data = keyData[size]; + + testFormat(format, algorithm, data, size, usages, extractable); + }); + }); + }); + }); + + }); + }); + + + // Test importKey with a given key format and other parameters. If + // extrable is true, export the key and verify that it matches the input. + function testFormat(format, algorithm, keyData, keySize, usages, extractable) { + promise_test(function(test) { + return subtle.importKey(format, keyData[format], algorithm, extractable, usages). + then(function(key) { + assert_equals(key.constructor, CryptoKey, "Imported a CryptoKey object"); + if (!extractable) { + return; + } + + return subtle.exportKey(format, key). + then(function(result) { + if (format !== "jwk") { + assert_true(equalBuffers(keyData[format], result), "Round trip works"); + } else { + assert_true(equalJwk(keyData[format], result), "Round trip works"); + } + }, function(err) { + assert_unreached("Threw an unexpected error: " + err.toString()); + }); + }, function(err) { + assert_unreached("Threw an unexpected error: " + err.toString()); + }); + }, "Good parameters: " + keySize.toString() + " bits " + parameterString(format, keyData[format], algorithm, extractable, usages)); + } + + + + // Helper methods follow: + + // Are two array buffers the same? + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + // Are two Jwk objects "the same"? That is, does the object returned include + // matching values for each property that was expected? It's okay if the + // returned object has extra methods; they aren't checked. + function equalJwk(expected, got) { + var fields = Object.keys(expected); + var fieldName; + + for(var i=0; i<fields.length; i++) { + fieldName = fields[i]; + if (!(fieldName in got)) { + return false; + } + if (expected[fieldName] !== got[fieldName]) { + return false; + } + } + + return true; + } + + // Build minimal Jwk objects from raw key data and algorithm specifications + function jwkData(keyData, algorithm) { + var result = { + kty: "oct", + k: byteArrayToUnpaddedBase64(keyData) + }; + + if (algorithm.name.substring(0, 3) === "AES") { + result.alg = "A" + (8 * keyData.byteLength).toString() + algorithm.name.substring(4); + } else if (algorithm.name === "HMAC") { + result.alg = "HS" + algorithm.hash.substring(4); + } + return result; + } + + // Jwk format wants Base 64 without the typical padding at the end. + function byteArrayToUnpaddedBase64(byteArray){ + var binaryString = ""; + for (var i=0; i<byteArray.byteLength; i++){ + binaryString += String.fromCharCode(byteArray[i]); + } + var base64String = btoa(binaryString); + + return base64String.replace(/=/g, ""); + } + + // Want to test every valid combination of usages. Start by creating a list + // of all non-empty subsets to possible usages. + function allNonemptySubsetsOf(arr) { + var results = []; + var firstElement; + var remainingElements; + + for(var i=0; i<arr.length; i++) { + firstElement = arr[i]; + remainingElements = arr.slice(i+1); + results.push([firstElement]); + + if (remainingElements.length > 0) { + allNonemptySubsetsOf(remainingElements).forEach(function(combination) { + combination.push(firstElement); + results.push(combination); + }); + } + } + + return results; + } + + // Return a list of all valid usage combinations, given the possible ones + // and the ones that are required for a particular operation. + function allValidUsages(possibleUsages, requiredUsages) { + var allUsages = []; + + allNonemptySubsetsOf(possibleUsages).forEach(function(usage) { + for (var i=0; i<requiredUsages.length; i++) { + if (!usage.includes(requiredUsages[i])) { + return; + } + } + allUsages.push(usage); + }); + + return allUsages; + } + + // Convert method parameters to a string to uniquely name each test + function parameterString(format, data, algorithm, extractable, usages) { + if ("byteLength" in data) { + data = "buffer(" + data.byteLength.toString() + ")"; + } else { + data = "object(" + Object.keys(data).join(", ") + ")"; + } + var result = "(" + + objectToString(format) + ", " + + objectToString(data) + ", " + + objectToString(algorithm) + ", " + + objectToString(extractable) + ", " + + objectToString(usages) + + ")"; + + return result; + } + + // Character representation of any object we may use as a parameter. + function objectToString(obj) { + var keyValuePairs = []; + + if (Array.isArray(obj)) { + return "[" + obj.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else if (typeof obj === "object") { + Object.keys(obj).sort().forEach(function(keyName) { + keyValuePairs.push(keyName + ": " + objectToString(obj[keyName])); + }); + return "{" + keyValuePairs.join(", ") + "}"; + } else if (typeof obj === "undefined") { + return "undefined"; + } else { + return obj.toString(); + } + + var keyValuePairs = []; + + Object.keys(obj).sort().forEach(function(keyName) { + var value = obj[keyName]; + if (typeof value === "object") { + value = objectToString(value); + } else if (typeof value === "array") { + value = "[" + value.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else { + value = value.toString(); + } + + keyValuePairs.push(keyName + ": " + value); + }); + + return "{" + keyValuePairs.join(", ") + "}"; + } diff --git a/testing/web-platform/tests/WebCryptoAPI/import_export/symmetric_importKey.https.any.js b/testing/web-platform/tests/WebCryptoAPI/import_export/symmetric_importKey.https.any.js new file mode 100644 index 0000000000..3d76daac1e --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/import_export/symmetric_importKey.https.any.js @@ -0,0 +1,241 @@ +// META: title=WebCryptoAPI: importKey() for symmetric keys +// META: timeout=long + +// Test importKey and exportKey for non-PKC algorithms. Only "happy paths" are +// currently tested - those where the operation should succeed. + + var subtle = crypto.subtle; + + // keying material for algorithms that can use any bit string. + var rawKeyData = [ + new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]), + new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24]), + new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]) + ]; + + // combinations of algorithms, usages, parameters, and formats to test + var testVectors = [ + {name: "AES-CTR", legalUsages: ["encrypt", "decrypt"], extractable: [true, false], formats: ["raw", "jwk"]}, + {name: "AES-CBC", legalUsages: ["encrypt", "decrypt"], extractable: [true, false], formats: ["raw", "jwk"]}, + {name: "AES-GCM", legalUsages: ["encrypt", "decrypt"], extractable: [true, false], formats: ["raw", "jwk"]}, + {name: "AES-KW", legalUsages: ["wrapKey", "unwrapKey"], extractable: [true, false], formats: ["raw", "jwk"]}, + {name: "HMAC", hash: "SHA-1", legalUsages: ["sign", "verify"], extractable: [false], formats: ["raw", "jwk"]}, + {name: "HMAC", hash: "SHA-256", legalUsages: ["sign", "verify"], extractable: [false], formats: ["raw", "jwk"]}, + {name: "HMAC", hash: "SHA-384", legalUsages: ["sign", "verify"], extractable: [false], formats: ["raw", "jwk"]}, + {name: "HMAC", hash: "SHA-512", legalUsages: ["sign", "verify"], extractable: [false], formats: ["raw", "jwk"]}, + {name: "HKDF", legalUsages: ["deriveBits", "deriveKey"], extractable: [false], formats: ["raw"]}, + {name: "PBKDF2", legalUsages: ["deriveBits", "deriveKey"], extractable: [false], formats: ["raw"]} + ]; + + + + // TESTS ARE HERE: + // Test every test vector, along with all available key data + testVectors.forEach(function(vector) { + var algorithm = {name: vector.name}; + if ("hash" in vector) { + algorithm.hash = vector.hash; + } + + rawKeyData.forEach(function(keyData) { + // Generate all combinations of valid usages for testing + allValidUsages(vector.legalUsages, []).forEach(function(usages) { + // Try each legal value of the extractable parameter + vector.extractable.forEach(function(extractable) { + vector.formats.forEach(function(format) { + var data = keyData; + if (format === "jwk") { + data = jwkData(keyData, algorithm); + } + testFormat(format, algorithm, data, keyData.length * 8, usages, extractable); + }); + }); + }); + + }); + }); + + // Test importKey with a given key format and other parameters. If + // extrable is true, export the key and verify that it matches the input. + function testFormat(format, algorithm, keyData, keySize, usages, extractable) { + promise_test(function(test) { + return subtle.importKey(format, keyData, algorithm, extractable, usages). + then(function(key) { + assert_equals(key.constructor, CryptoKey, "Imported a CryptoKey object"); + if (!extractable) { + return; + } + + return subtle.exportKey(format, key). + then(function(result) { + if (format !== "jwk") { + assert_true(equalBuffers(keyData, result), "Round trip works"); + } else { + assert_true(equalJwk(keyData, result), "Round trip works"); + } + }, function(err) { + assert_unreached("Threw an unexpected error: " + err.toString()); + }); + }, function(err) { + assert_unreached("Threw an unexpected error: " + err.toString()); + }); + }, "Good parameters: " + keySize.toString() + " bits " + parameterString(format, keyData, algorithm, extractable, usages)); + } + + + + // Helper methods follow: + + // Are two array buffers the same? + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + // Are two Jwk objects "the same"? That is, does the object returned include + // matching values for each property that was expected? It's okay if the + // returned object has extra methods; they aren't checked. + function equalJwk(expected, got) { + var fields = Object.keys(expected); + var fieldName; + + for(var i=0; i<fields.length; i++) { + fieldName = fields[i]; + if (!(fieldName in got)) { + return false; + } + if (expected[fieldName] !== got[fieldName]) { + return false; + } + } + + return true; + } + + // Build minimal Jwk objects from raw key data and algorithm specifications + function jwkData(keyData, algorithm) { + var result = { + kty: "oct", + k: byteArrayToUnpaddedBase64(keyData) + }; + + if (algorithm.name.substring(0, 3) === "AES") { + result.alg = "A" + (8 * keyData.byteLength).toString() + algorithm.name.substring(4); + } else if (algorithm.name === "HMAC") { + result.alg = "HS" + algorithm.hash.substring(4); + } + return result; + } + + // Jwk format wants Base 64 without the typical padding at the end. + function byteArrayToUnpaddedBase64(byteArray){ + var binaryString = ""; + for (var i=0; i<byteArray.byteLength; i++){ + binaryString += String.fromCharCode(byteArray[i]); + } + var base64String = btoa(binaryString); + + return base64String.replace(/=/g, ""); + } + + // Want to test every valid combination of usages. Start by creating a list + // of all non-empty subsets to possible usages. + function allNonemptySubsetsOf(arr) { + var results = []; + var firstElement; + var remainingElements; + + for(var i=0; i<arr.length; i++) { + firstElement = arr[i]; + remainingElements = arr.slice(i+1); + results.push([firstElement]); + + if (remainingElements.length > 0) { + allNonemptySubsetsOf(remainingElements).forEach(function(combination) { + combination.push(firstElement); + results.push(combination); + }); + } + } + + return results; + } + + // Return a list of all valid usage combinations, given the possible ones + // and the ones that are required for a particular operation. + function allValidUsages(possibleUsages, requiredUsages) { + var allUsages = []; + + allNonemptySubsetsOf(possibleUsages).forEach(function(usage) { + for (var i=0; i<requiredUsages.length; i++) { + if (!usage.includes(requiredUsages[i])) { + return; + } + } + allUsages.push(usage); + }); + + return allUsages; + } + + // Convert method parameters to a string to uniquely name each test + function parameterString(format, data, algorithm, extractable, usages) { + var result = "(" + + objectToString(format) + ", " + + objectToString(data) + ", " + + objectToString(algorithm) + ", " + + objectToString(extractable) + ", " + + objectToString(usages) + + ")"; + + return result; + } + + // Character representation of any object we may use as a parameter. + function objectToString(obj) { + var keyValuePairs = []; + + if (Array.isArray(obj)) { + return "[" + obj.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else if (typeof obj === "object") { + Object.keys(obj).sort().forEach(function(keyName) { + keyValuePairs.push(keyName + ": " + objectToString(obj[keyName])); + }); + return "{" + keyValuePairs.join(", ") + "}"; + } else if (typeof obj === "undefined") { + return "undefined"; + } else { + return obj.toString(); + } + + var keyValuePairs = []; + + Object.keys(obj).sort().forEach(function(keyName) { + var value = obj[keyName]; + if (typeof value === "object") { + value = objectToString(value); + } else if (typeof value === "array") { + value = "[" + value.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else { + value = value.toString(); + } + + keyValuePairs.push(keyName + ": " + value); + }); + + return "{" + keyValuePairs.join(", ") + "}"; + } diff --git a/testing/web-platform/tests/WebCryptoAPI/randomUUID.https.any.js b/testing/web-platform/tests/WebCryptoAPI/randomUUID.https.any.js new file mode 100644 index 0000000000..600a9728f0 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/randomUUID.https.any.js @@ -0,0 +1,42 @@ +// Run for enough iterations that we're likely to catch edge-cases, like +// failing to set a reserved bit: +const iterations = 256; +// Track all the UUIDs generated during test run, bail if we ever collide: +const uuids = new Set() +function randomUUID() { + const uuid = self.crypto.randomUUID(); + if (uuids.has(uuid)) { + throw new Error(`uuid collision ${uuid}`) + } + uuids.add(uuid); + return uuid; +} + +// UUID is in namespace format (16 bytes separated by dashes): +test(function() { + const UUIDRegex = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/ + for (let i = 0; i < iterations; i++) { + assert_true(UUIDRegex.test(randomUUID())); + } +}, "namespace format"); + +// Set the 4 most significant bits of array[6], which represent the UUID +// version, to 0b0100: +test(function() { + for (let i = 0; i < iterations; i++) { + let value = parseInt(randomUUID().split('-')[2].slice(0, 2), 16); + value &= 0b11110000; + assert_true(value === 0b01000000); + } +}, "version set"); + +// Set the 2 most significant bits of array[8], which represent the UUID +// variant, to 0b10: +test(function() { + for (let i = 0; i < iterations; i++) { + // Grab the byte representing array[8]: + let value = parseInt(randomUUID().split('-')[3].slice(0, 2), 16); + value &= 0b11000000 + assert_true(value === 0b10000000); + } +}, "variant set"); diff --git a/testing/web-platform/tests/WebCryptoAPI/secure_context/crypto-subtle-secure-context-available.https.sub.html b/testing/web-platform/tests/WebCryptoAPI/secure_context/crypto-subtle-secure-context-available.https.sub.html new file mode 100644 index 0000000000..cc3617c37a --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/secure_context/crypto-subtle-secure-context-available.https.sub.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> +<head> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> + <script> + async_test(function(t) { + assert_not_equals(typeof crypto.subtle, 'undefined'); + t.done(); + }, "Secure context window has access to crypto.subtle"); + async_test(function(t) { + var w = new Worker('../util/worker-report-crypto-subtle-presence.js'); + + w.onmessage = t.step_func(function (e) { + if (e.data.msg_type == 'subtle_crypto_found') { + assert_equals(e.data.msg_value, true); + t.done(); + } + }); + + }, "Secure context worker has access to crypto.subtle"); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/ecdsa.https.any.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/ecdsa.https.any.js new file mode 100644 index 0000000000..9764cc3354 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/ecdsa.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: sign() and verify() Using ECDSA +// META: script=ecdsa_vectors.js +// META: script=ecdsa.js +// META: timeout=long + +run_test(); diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/ecdsa.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/ecdsa.js new file mode 100644 index 0000000000..6bf662adcc --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/ecdsa.js @@ -0,0 +1,509 @@ + +function run_test() { + setup({explicit_done: true}); + + var subtle = self.crypto.subtle; // Change to test prefixed implementations + + // When are all these tests really done? When all the promises they use have resolved. + var all_promises = []; + + // Source file [algorithm_name]_vectors.js provides the getTestVectors method + // for the algorithm that drives these tests. + var testVectors = getTestVectors(); + var invalidTestVectors = getInvalidTestVectors(); + + // Test verification first, because signing tests rely on that working + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification"); + }); + + all_promises.push(promise); + }); + + // Test verification with an altered buffer after call + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + promise_test(function(test) { + var signature = copyBuffer(vector.signature); + var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + signature[0] = 255 - signature[0]; + return operation; + }, vector.name + " verification with altered signature after call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification with altered signature after call"); + }); + + all_promises.push(promise); + }); + + // Check for successful verification even if plaintext is altered after call. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + promise_test(function(test) { + var plaintext = copyBuffer(vector.plaintext); + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + plaintext[0] = 255 - plaintext[0]; + return operation; + }, vector.name + " with altered plaintext after call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext after call"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to using privateKey to verify. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + promise_test(function(test) { + return subtle.verify(algorithm, vector.privateKey, vector.signature, vector.plaintext) + .then(function(plaintext) { + assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }); + }, vector.name + " using privateKey to verify"); + + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " using privateKey to verify"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to using publicKey to sign. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + promise_test(function(test) { + return subtle.sign(algorithm, vector.publicKey, vector.plaintext) + .then(function(signature) { + assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }); + }, vector.name + " using publicKey to sign"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " using publicKey to sign"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to no "verify" usage. + testVectors.forEach(function(originalVector) { + var vector = Object.assign({}, originalVector); + + var promise = importVectorKeys(vector, [], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + promise_test(function(test) { + return subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) + .then(function(plaintext) { + assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }); + }, vector.name + " no verify usage"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " no verify usage"); + }); + + all_promises.push(promise); + }); + + // Check for successful signing and verification. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + promise_test(function(test) { + return subtle.sign(algorithm, vector.privateKey, vector.plaintext) + .then(function(signature) { + // Can we verify the signature? + return subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Round trip verification works"); + return signature; + }, function(err) { + assert_unreached("verify error for test " + vector.name + ": " + err.message + "'"); + }); + }, function(err) { + assert_unreached("sign error for test " + vector.name + ": '" + err.message + "'"); + }); + }, vector.name + " round trip"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested signing or verifying + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " round trip"); + }); + + all_promises.push(promise); + }); + + // Test signing with the wrong algorithm + testVectors.forEach(function(vector) { + // Want to get the key for the wrong algorithm + var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) + .then(function(wrongKey) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + return importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var operation = subtle.sign(algorithm, wrongKey, vector.plaintext) + .then(function(signature) { + assert_unreached("Signing should not have succeeded for " + vector.name); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }); + + return operation; + }, vector.name + " signing with wrong algorithm name"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name"); + }); + }, function(err) { + promise_test(function(test) { + assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); + }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name"); + }); + + all_promises.push(promise); + }); + + // Test verification with the wrong algorithm + testVectors.forEach(function(vector) { + // Want to get the key for the wrong algorithm + var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) + .then(function(wrongKey) { + return importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + promise_test(function(test) { + var operation = subtle.verify(algorithm, wrongKey, vector.signature, vector.plaintext) + .then(function(signature) { + assert_unreached("Verifying should not have succeeded for " + vector.name); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }); + + return operation; + }, vector.name + " verifying with wrong algorithm name"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name"); + }); + }, function(err) { + promise_test(function(test) { + assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); + }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name"); + }); + + all_promises.push(promise); + }); + + // Test verification fails with wrong signature + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure due to altered signature"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to altered signature"); + }); + + all_promises.push(promise); + }); + + // Test verification fails with wrong hash + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var hashName = "SHA-1"; + if (vector.hashName === "SHA-1") { + hashName = "SHA-256" + } + var algorithm = {name: vector.algorithmName, hash: hashName}; + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure due to wrong hash"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to wrong hash"); + }); + + all_promises.push(promise); + }); + + // Test verification fails with bad hash name + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + // use the wrong name for the hash + var hashName = vector.hashName.substring(0, 3) + vector.hashName.substring(4); + var algorithm = {name: vector.algorithmName, hash: hashName}; + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) + .then(function(is_verified) { + assert_unreached("Verification should throw an error"); + }, function(err) { + assert_equals(err.name, "NotSupportedError", "Correctly throws NotSupportedError for illegal hash name") + }); + + return operation; + }, vector.name + " verification failure due to bad hash name"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to bad hash name"); + }); + + all_promises.push(promise); + }); + + // Test verification fails with short (odd length) signature + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + var signature = vector.signature.slice(1); // Skip the first byte + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure due to shortened signature"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to shortened signature"); + }); + + all_promises.push(promise); + }); + + // Test verification fails with wrong plaintext + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + var plaintext = copyBuffer(vector.plaintext); + plaintext[0] = 255 - plaintext[0]; + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure due to altered plaintext"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to altered plaintext"); + }); + + all_promises.push(promise); + }); + + // Test invalid signatures + invalidTestVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName, hash: vector.hashName}; + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature unexpectedly verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification"); + }); + + all_promises.push(promise); + }); + + promise_test(function() { + return Promise.all(all_promises) + .then(function() {done();}) + .catch(function() {done();}) + }, "setup"); + + // A test vector has all needed fields for signing and verifying, EXCEPT that the + // key field may be null. This function replaces that null with the Correct + // CryptoKey object. + // + // Returns a Promise that yields an updated vector on success. + function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) { + var publicPromise, privatePromise; + + if (vector.publicKey !== null) { + publicPromise = new Promise(function(resolve, reject) { + resolve(vector); + }); + } else { + publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithmName, namedCurve: vector.namedCurve}, false, publicKeyUsages) + .then(function(key) { + vector.publicKey = key; + return vector; + }); // Returns a copy of the sourceBuffer it is sent. + } + + if (vector.privateKey !== null) { + privatePromise = new Promise(function(resolve, reject) { + resolve(vector); + }); + } else { + privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithmName, namedCurve: vector.namedCurve}, false, privateKeyUsages) + .then(function(key) { + vector.privateKey = key; + return vector; + }); + } + + return Promise.all([publicPromise, privatePromise]); + } + + // Returns a copy of the sourceBuffer it is sent. + function copyBuffer(sourceBuffer) { + var source = new Uint8Array(sourceBuffer); + var copy = new Uint8Array(sourceBuffer.byteLength) + + for (var i=0; i<source.byteLength; i++) { + copy[i] = source[i]; + } + + return copy; + } + + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + return; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/ecdsa_vectors.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/ecdsa_vectors.js new file mode 100644 index 0000000000..2d1fb6d5c9 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/ecdsa_vectors.js @@ -0,0 +1,137 @@ + +// ecdsa_vectors.js + +// Data for testing ECDSA with every curve currently in the WebCryptoAPI recommendation. + +// The following function returns an array of test vectors +// for the subtleCrypto encrypt method. +// +// Each test vector has the following fields: +// name - a unique name for this vector +// publicKeyBuffer - an arrayBuffer with the key data +// publicKeyFormat - "spki" "jwk" +// publicKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// privateKeyBuffer - an arrayBuffer with the key data +// privateKeyFormat - "pkcs8" or "jwk" +// privateKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// algorithmName - the name of the AlgorithmIdentifier parameter to provide to encrypt +// namedCurve - the curve used +// hashName - the hash function to sign with +// plaintext - the text to encrypt +// signature - the expected signature +function getTestVectors() { + var pkcs8 = { + "P-256": new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 230, 238, 207, 158, 98, 108, 202, 142, 24, 7, 155, 146, 197, 238, 38, 158, 84, 202, 18, 142, 175, 212, 137, 71, 255, 81, 171, 160, 10, 192, 229, 214, 161, 68, 3, 66, 0, 4, 10, 5, 30, 56, 111, 103, 196, 166, 225, 229, 203, 238, 125, 55, 116, 91, 88, 142, 190, 114, 15, 117, 89, 22, 40, 111, 150, 41, 105, 122, 57, 23, 17, 216, 106, 234, 201, 103, 8, 210, 58, 38, 35, 216, 198, 237, 187, 84, 217, 164, 63, 100, 6, 105, 49, 128, 15, 53, 29, 158, 117, 235, 238, 30]), + "P-384": new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 2, 169, 160, 216, 153, 239, 168, 126, 117, 100, 17, 9, 7, 233, 216, 44, 33, 189, 98, 101, 163, 122, 189, 154, 111, 219, 15, 128, 236, 132, 77, 211, 161, 66, 83, 32, 214, 125, 220, 48, 245, 219, 116, 239, 185, 162, 230, 97, 161, 100, 3, 98, 0, 4, 29, 49, 157, 105, 45, 202, 95, 87, 84, 186, 123, 50, 193, 22, 66, 198, 216, 210, 180, 251, 130, 73, 195, 242, 20, 215, 30, 144, 181, 37, 41, 102, 217, 127, 123, 235, 31, 170, 177, 228, 243, 226, 96, 85, 73, 194, 238, 219, 82, 3, 41, 179, 190, 166, 181, 229, 86, 36, 161, 81, 80, 161, 105, 102, 99, 95, 25, 22, 239, 4, 221, 117, 142, 105, 64, 157, 6, 51, 203, 75, 37, 153, 65, 121, 178, 42, 118, 156, 116, 52, 54, 145, 14, 121, 153, 81]), + "P-521": new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 1, 83, 62, 97, 143, 152, 234, 209, 181, 19, 236, 136, 120, 200, 130, 13, 55, 122, 54, 216, 240, 63, 43, 160, 70, 201, 49, 130, 90, 61, 53, 135, 48, 192, 178, 96, 51, 219, 183, 247, 228, 163, 212, 67, 74, 3, 94, 36, 183, 7, 249, 18, 71, 102, 23, 110, 26, 240, 184, 93, 242, 46, 170, 186, 156, 37, 161, 129, 137, 3, 129, 134, 0, 4, 0, 166, 222, 236, 251, 72, 145, 23, 241, 228, 28, 196, 160, 100, 7, 61, 134, 115, 8, 110, 81, 219, 37, 8, 110, 141, 183, 100, 212, 239, 246, 10, 173, 99, 88, 253, 207, 150, 122, 198, 132, 89, 39, 94, 42, 128, 79, 142, 238, 183, 228, 196, 40, 75, 20, 81, 192, 165, 234, 118, 254, 112, 7, 172, 5, 71, 1, 197, 237, 218, 249, 168, 158, 124, 79, 220, 201, 36, 199, 55, 216, 245, 133, 218, 151, 3, 169, 84, 194, 59, 231, 193, 74, 175, 166, 102, 84, 178, 86, 119, 10, 147, 142, 127, 38, 231, 0, 198, 3, 147, 28, 123, 208, 189, 181, 208, 99, 44, 125, 30, 171, 70, 111, 9, 217, 118, 194, 74, 50, 62, 27, 124]) + }; + + var spki = { + "P-256": new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 10, 5, 30, 56, 111, 103, 196, 166, 225, 229, 203, 238, 125, 55, 116, 91, 88, 142, 190, 114, 15, 117, 89, 22, 40, 111, 150, 41, 105, 122, 57, 23, 17, 216, 106, 234, 201, 103, 8, 210, 58, 38, 35, 216, 198, 237, 187, 84, 217, 164, 63, 100, 6, 105, 49, 128, 15, 53, 29, 158, 117, 235, 238, 30]), + "P-384": new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 29, 49, 157, 105, 45, 202, 95, 87, 84, 186, 123, 50, 193, 22, 66, 198, 216, 210, 180, 251, 130, 73, 195, 242, 20, 215, 30, 144, 181, 37, 41, 102, 217, 127, 123, 235, 31, 170, 177, 228, 243, 226, 96, 85, 73, 194, 238, 219, 82, 3, 41, 179, 190, 166, 181, 229, 86, 36, 161, 81, 80, 161, 105, 102, 99, 95, 25, 22, 239, 4, 221, 117, 142, 105, 64, 157, 6, 51, 203, 75, 37, 153, 65, 121, 178, 42, 118, 156, 116, 52, 54, 145, 14, 121, 153, 81]), + "P-521": new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 0, 166, 222, 236, 251, 72, 145, 23, 241, 228, 28, 196, 160, 100, 7, 61, 134, 115, 8, 110, 81, 219, 37, 8, 110, 141, 183, 100, 212, 239, 246, 10, 173, 99, 88, 253, 207, 150, 122, 198, 132, 89, 39, 94, 42, 128, 79, 142, 238, 183, 228, 196, 40, 75, 20, 81, 192, 165, 234, 118, 254, 112, 7, 172, 5, 71, 1, 197, 237, 218, 249, 168, 158, 124, 79, 220, 201, 36, 199, 55, 216, 245, 133, 218, 151, 3, 169, 84, 194, 59, 231, 193, 74, 175, 166, 102, 84, 178, 86, 119, 10, 147, 142, 127, 38, 231, 0, 198, 3, 147, 28, 123, 208, 189, 181, 208, 99, 44, 125, 30, 171, 70, 111, 9, 217, 118, 194, 74, 50, 62, 27, 124]) + }; + + // plaintext + var plaintext = new Uint8Array([95, 77, 186, 79, 50, 12, 12, 232, 118, 114, 90, 252, 229, 251, 210, 91, 248, 62, 90, 113, 37, 160, 140, 175, 231, 60, 62, 186, 196, 33, 119, 157, 249, 213, 93, 24, 12, 58, 233, 148, 38, 69, 225, 216, 47, 238, 140, 157, 41, 75, 60, 177, 160, 138, 153, 49, 32, 27, 60, 14, 129, 252, 71, 202, 207, 131, 21, 162, 175, 102, 50, 65, 19, 195, 182, 98, 48, 195, 70, 8, 196, 244, 89, 54, 52, 206, 2, 178, 103, 54, 34, 119, 240, 168, 64, 202, 116, 188, 61, 26, 98, 54, 149, 44, 94, 215, 170, 248, 168, 254, 203, 221, 250, 117, 132, 230, 151, 140, 234, 93, 42, 91, 159, 183, 241, 180, 140, 139, 11, 229, 138, 48, 82, 2, 117, 77, 131, 118, 16, 115, 116, 121, 60, 240, 38, 170, 238, 83, 0, 114, 125, 131, 108, 215, 30, 113, 179, 69, 221, 178, 228, 68, 70, 255, 197, 185, 1, 99, 84, 19, 137, 13, 145, 14, 163, 128, 152, 74, 144, 25, 16, 49, 50, 63, 22, 219, 204, 157, 107, 225, 104, 184, 72, 133, 56, 76, 160, 62, 18, 96, 10, 193, 194, 72, 2, 138, 243, 114, 108, 201, 52, 99, 136, 46, 168, 192, 42, 171]); + + // For verification tests. + var signatures = { + "P-256": { + "SHA-1": new Uint8Array([172, 224, 125, 170, 52, 83, 158, 179, 85, 149, 130, 217, 59, 201, 0, 251, 237, 196, 51, 243, 218, 231, 211, 136, 157, 249, 219, 16, 140, 178, 145, 16, 177, 104, 68, 179, 88, 49, 219, 184, 212, 202, 109, 248, 110, 64, 202, 129, 7, 173, 226, 88, 194, 69, 164, 158, 120, 120, 128, 3, 115, 14, 181, 197]), + "SHA-256": new Uint8Array([83, 223, 63, 226, 42, 29, 106, 105, 225, 145, 197, 180, 118, 154, 109, 110, 66, 67, 47, 251, 53, 190, 203, 65, 207, 36, 19, 57, 49, 122, 124, 118, 59, 74, 222, 134, 42, 235, 180, 229, 134, 24, 205, 81, 171, 156, 100, 218, 127, 242, 126, 53, 27, 77, 249, 101, 157, 132, 244, 30, 67, 30, 64, 12]), + "SHA-384": new Uint8Array([235, 111, 173, 249, 151, 252, 218, 129, 123, 117, 136, 26, 162, 115, 247, 110, 169, 145, 95, 189, 228, 98, 32, 82, 34, 94, 154, 197, 47, 83, 124, 137, 215, 71, 222, 247, 135, 22, 221, 238, 77, 247, 223, 194, 42, 158, 175, 224, 76, 182, 56, 138, 97, 196, 238, 109, 42, 102, 13, 71, 1, 43, 56, 92]), + "SHA-512": new Uint8Array([74, 201, 175, 173, 69, 107, 160, 142, 203, 41, 225, 5, 73, 146, 6, 40, 93, 130, 129, 35, 156, 171, 190, 161, 12, 10, 234, 123, 7, 5, 112, 97, 57, 183, 15, 52, 94, 215, 79, 255, 175, 222, 66, 234, 253, 180, 62, 161, 7, 11, 249, 37, 118, 185, 13, 102, 67, 84, 101, 189, 73, 132, 110, 206]) + }, + "P-384": { + "SHA-1": new Uint8Array([101, 254, 7, 14, 195, 234, 195, 82, 80, 208, 11, 158, 230, 219, 77, 45, 173, 213, 243, 187, 185, 196, 149, 200, 103, 29, 42, 13, 43, 153, 20, 159, 178, 79, 136, 175, 7, 78, 11, 144, 50, 104, 179, 208, 237, 95, 14, 20, 104, 87, 150, 178, 143, 227, 75, 45, 142, 220, 223, 16, 132, 91, 36, 207, 121, 179, 54, 39, 216, 189, 44, 129, 98, 28, 181, 30, 3, 12, 33, 164, 58, 187, 10, 135, 64, 250, 194, 111, 133, 34, 230, 131, 195, 103, 172, 150]), + "SHA-256": new Uint8Array([75, 194, 223, 234, 59, 205, 164, 251, 180, 253, 146, 123, 3, 15, 155, 128, 177, 245, 210, 173, 155, 183, 170, 6, 41, 56, 105, 87, 113, 32, 178, 177, 208, 239, 17, 204, 217, 254, 208, 113, 74, 171, 54, 190, 246, 57, 40, 247, 132, 245, 60, 126, 9, 223, 147, 233, 179, 229, 176, 200, 131, 207, 114, 9, 81, 180, 254, 35, 130, 199, 132, 46, 220, 252, 212, 93, 149, 106, 114, 210, 154, 64, 48, 160, 56, 169, 0, 230, 247, 221, 133, 122, 86, 80, 211, 232]), + "SHA-384": new Uint8Array([13, 217, 194, 199, 240, 182, 244, 217, 50, 130, 84, 169, 2, 232, 115, 116, 179, 192, 146, 25, 94, 107, 226, 26, 161, 166, 220, 216, 235, 166, 15, 123, 11, 56, 196, 0, 109, 250, 33, 70, 212, 233, 253, 35, 220, 51, 97, 121, 151, 64, 23, 73, 58, 31, 79, 116, 238, 207, 228, 85, 190, 61, 169, 237, 153, 100, 29, 129, 97, 13, 254, 180, 104, 182, 7, 218, 148, 29, 87, 20, 231, 181, 26, 238, 44, 69, 170, 14, 156, 77, 160, 33, 178, 55, 0, 144]), + "SHA-512": new Uint8Array([114, 251, 219, 54, 159, 211, 76, 28, 84, 38, 77, 7, 244, 250, 205, 105, 176, 46, 66, 6, 248, 168, 187, 37, 155, 136, 42, 48, 92, 86, 253, 226, 211, 81, 7, 228, 147, 197, 60, 214, 180, 175, 11, 49, 48, 111, 77, 3, 253, 67, 207, 199, 98, 161, 3, 14, 23, 163, 215, 117, 69, 58, 18, 18, 177, 66, 159, 123, 61, 147, 6, 106, 95, 66, 161, 11, 19, 140, 209, 119, 220, 9, 97, 110, 130, 125, 89, 136, 34, 215, 141, 70, 39, 183, 84, 230]) + }, + "P-521": { + "SHA-1": new Uint8Array([1, 120, 26, 23, 166, 14, 67, 18, 105, 96, 253, 57, 110, 18, 16, 145, 108, 33, 21, 202, 68, 40, 217, 104, 56, 156, 75, 70, 193, 85, 54, 116, 206, 147, 123, 142, 33, 112, 12, 230, 9, 50, 174, 15, 87, 92, 161, 135, 221, 89, 119, 32, 219, 131, 158, 177, 242, 12, 126, 51, 148, 120, 117, 89, 220, 213, 0, 32, 126, 87, 13, 245, 199, 228, 173, 159, 192, 165, 247, 32, 101, 233, 206, 28, 158, 61, 18, 202, 94, 109, 217, 244, 79, 225, 40, 86, 27, 117, 244, 34, 108, 79, 173, 242, 61, 131, 83, 108, 198, 105, 234, 64, 152, 227, 115, 182, 203, 145, 156, 139, 92, 252, 5, 5, 166, 125, 150, 178, 118, 164, 106, 61]), + "SHA-256": new Uint8Array([1, 116, 219, 167, 123, 20, 215, 63, 102, 245, 113, 103, 134, 163, 229, 168, 215, 201, 49, 68, 94, 109, 50, 10, 146, 41, 217, 97, 216, 161, 179, 239, 209, 26, 94, 163, 60, 121, 73, 90, 197, 153, 187, 182, 138, 100, 26, 132, 157, 88, 216, 62, 248, 84, 204, 38, 95, 166, 201, 23, 223, 246, 238, 67, 90, 103, 1, 179, 213, 82, 125, 172, 32, 251, 10, 112, 51, 195, 254, 121, 116, 78, 172, 239, 123, 63, 252, 39, 182, 77, 200, 99, 248, 111, 66, 152, 44, 178, 34, 146, 69, 254, 157, 228, 138, 165, 158, 182, 83, 212, 73, 112, 134, 217, 17, 165, 189, 39, 14, 149, 197, 30, 126, 152, 247, 165, 134, 63, 199, 251, 6, 92]), + "SHA-384": new Uint8Array([1, 247, 125, 177, 229, 19, 120, 225, 23, 197, 184, 190, 200, 160, 63, 150, 87, 210, 68, 197, 78, 131, 121, 8, 191, 113, 1, 37, 95, 65, 81, 82, 93, 158, 137, 207, 127, 84, 99, 27, 51, 104, 145, 157, 56, 36, 255, 159, 127, 120, 254, 129, 35, 154, 26, 159, 222, 43, 122, 131, 233, 92, 166, 160, 202, 17, 1, 185, 139, 29, 164, 237, 0, 236, 118, 147, 103, 233, 149, 139, 128, 71, 212, 127, 146, 171, 139, 255, 150, 241, 51, 11, 249, 72, 201, 34, 9, 1, 27, 140, 219, 180, 150, 212, 100, 219, 185, 22, 114, 14, 183, 2, 189, 173, 146, 140, 153, 185, 128, 183, 101, 4, 224, 173, 28, 18, 180, 168, 87, 49, 199, 12]), + "SHA-512": new Uint8Array([0, 178, 202, 175, 103, 152, 81, 154, 157, 54, 219, 250, 254, 120, 107, 47, 186, 28, 194, 172, 185, 149, 147, 193, 119, 179, 110, 58, 28, 238, 183, 2, 39, 90, 226, 60, 252, 202, 10, 173, 120, 246, 182, 222, 230, 180, 113, 139, 149, 208, 209, 167, 21, 170, 51, 120, 71, 14, 80, 181, 22, 193, 142, 15, 51, 5, 1, 240, 7, 30, 106, 50, 134, 127, 167, 15, 105, 92, 211, 156, 78, 135, 225, 66, 185, 228, 19, 77, 56, 116, 11, 214, 254, 227, 84, 165, 117, 22, 126, 19, 82, 78, 148, 131, 38, 55, 145, 15, 225, 30, 83, 168, 95, 178, 27, 145, 173, 184, 27, 177, 119, 156, 78, 43, 139, 200, 124, 113, 125, 195, 80, 132]) + } + } + + var vectors = []; + ["P-256", "P-384", "P-521"].forEach(function(curveName) { + ["SHA-1", "SHA-256", "SHA-384", "SHA-512"].forEach(function(hashName) { + var vector = { + name: "ECDSA " + curveName + " with " + hashName, + publicKeyBuffer: spki[curveName], + publicKeyFormat: "spki", + publicKey: null, + privateKeyBuffer: pkcs8[curveName], + privateKeyFormat: "pkcs8", + privateKey: null, + algorithmName: "ECDSA", + namedCurve: curveName, + hashName: hashName, + plaintext: plaintext, + signature: signatures[curveName][hashName] + }; + + vectors.push(vector); + }) + }); + + return vectors; +} + +// Additional test vectors, using the same format as getTestVectors, but the +// signatures are invalid. +function getInvalidTestVectors() { + var vectors = [ + { + name: "The signature was truncated by 1 byte", + publicKeyBuffer: new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 156, 176, 207, 105, 48, 61, 175, 199, 97, 212, 228, 104, 123, 78, 207, 3, 158, 109, 52, 171, 150, 74, 248, 8, 16, 216, 213, 88, 164, 168, 214, 247, 45, 81, 35, 58, 23, 136, 146, 10, 134, 238, 8, 161, 150, 44, 121, 239, 163, 23, 251, 120, 121, 226, 151, 218, 210, 20, 109, 185, 149, 250, 28, 120]), + publicKeyFormat: "spki", + publicKey: null, + algorithmName: "ECDSA", + namedCurve: "P-256", + hashName: "SHA-512", + plaintext: new Uint8Array([110, 41, 50, 21, 51, 1, 164, 238, 246, 128, 230, 66, 137, 41, 173, 174, 152, 140, 16, 141, 102, 138, 49, 255, 85, 208, 72, 153, 71, 215, 95, 248, 26, 70, 191, 137, 232, 77, 100, 1, 240, 35, 190, 110, 135, 104, 143, 188, 215, 132, 215, 133, 202, 132, 103, 53, 82, 74, 203, 82, 208, 4, 82, 200, 64, 64, 164, 121, 231, 204, 51, 9, 54, 68, 29, 147, 187, 231, 34, 169, 67, 42, 110, 29, 177, 18, 181, 201, 64, 59, 16, 39, 44, 177, 52, 127, 214, 25, 212, 99, 247, 169, 210, 35, 173, 118, 253, 224, 109, 138, 104, 131, 80, 15, 184, 67, 35, 90, 191, 249, 142, 36, 27, 223, 181, 83, 140, 62]), + signature: new Uint8Array([75, 159, 145, 228, 40, 82, 135, 38, 26, 29, 28, 146, 60, 246, 25, 205, 82, 193, 117, 207, 231, 241, 190, 96, 165, 37, 140, 97, 3, 72, 186, 61, 40, 196, 95, 144, 29, 113, 196, 27, 41, 134, 56, 236, 13, 106, 133, 215, 252, 176, 195, 59, 191, 236, 90, 156, 129, 8, 70, 182, 57, 40, 154]), + }, + { + name: "The signature was made using SHA-512, however verification is being done using SHA-1.", + publicKeyBuffer: new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 156, 176, 207, 105, 48, 61, 175, 199, 97, 212, 228, 104, 123, 78, 207, 3, 158, 109, 52, 171, 150, 74, 248, 8, 16, 216, 213, 88, 164, 168, 214, 247, 45, 81, 35, 58, 23, 136, 146, 10, 134, 238, 8, 161, 150, 44, 121, 239, 163, 23, 251, 120, 121, 226, 151, 218, 210, 20, 109, 185, 149, 250, 28, 120]), + publicKeyFormat: "spki", + publicKey: null, + algorithmName: "ECDSA", + namedCurve: "P-256", + hashName: "SHA-1", + plaintext: new Uint8Array([110, 41, 50, 21, 51, 1, 164, 238, 246, 128, 230, 66, 137, 41, 173, 174, 152, 140, 16, 141, 102, 138, 49, 255, 85, 208, 72, 153, 71, 215, 95, 248, 26, 70, 191, 137, 232, 77, 100, 1, 240, 35, 190, 110, 135, 104, 143, 188, 215, 132, 215, 133, 202, 132, 103, 53, 82, 74, 203, 82, 208, 4, 82, 200, 64, 64, 164, 121, 231, 204, 51, 9, 54, 68, 29, 147, 187, 231, 34, 169, 67, 42, 110, 29, 177, 18, 181, 201, 64, 59, 16, 39, 44, 177, 52, 127, 214, 25, 212, 99, 247, 169, 210, 35, 173, 118, 253, 224, 109, 138, 104, 131, 80, 15, 184, 67, 35, 90, 191, 249, 142, 36, 27, 223, 181, 83, 140, 62]), + signature: new Uint8Array([75, 159, 145, 228, 40, 82, 135, 38, 26, 29, 28, 146, 60, 246, 25, 205, 82, 193, 117, 207, 231, 241, 190, 96, 165, 37, 140, 97, 3, 72, 186, 61, 40, 196, 95, 144, 29, 113, 196, 27, 41, 134, 56, 236, 13, 106, 133, 215, 252, 176, 195, 59, 191, 236, 90, 156, 129, 8, 70, 182, 57, 40, 154, 132]), + }, + { + name: "Excess padding", + publicKeyBuffer: new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 8, 116, 162, 224, 184, 255, 68, 143, 14, 84, 50, 30, 39, 244, 241, 230, 77, 6, 76, 222, 183, 210, 111, 69, 140, 50, 233, 48, 18, 15, 78, 87, 220, 133, 194, 105, 63, 151, 126, 237, 74, 142, 204, 141, 185, 129, 180, 217, 31, 105, 68, 109, 244, 244, 198, 245, 222, 25, 0, 63, 69, 248, 145, 208, 235, 205, 47, 255, 219, 92, 129, 192, 64, 232, 214, 153, 76, 67, 199, 254, 237, 185, 138, 74, 49, 237, 251, 53, 232, 154, 48, 1, 60, 59, 146, 103]), + publicKeyFormat: "spki", + publicKey: null, + algorithmName: "ECDSA", + namedCurve: "P-384", + hashName: "SHA-1", + plaintext: new Uint8Array([63, 7, 131, 165, 142, 102, 243, 210, 192, 204, 251, 95, 172, 63, 9, 219, 111, 134, 9, 208, 89, 43, 199, 127, 223, 254, 217, 207, 14, 19, 125, 38, 168, 103, 5, 118, 101, 243, 173, 129, 190, 235, 187, 219, 114, 61, 90, 71, 197, 128, 130, 143, 16, 247, 52, 122, 184, 169, 194, 77, 25, 95, 115, 109, 250, 230, 234, 227, 125, 136, 254, 59, 71, 53, 231, 198, 105, 168, 10, 193, 145, 62, 92, 36, 200, 193, 213, 205, 177, 95, 153, 79, 62, 194, 241, 199, 116, 117, 46, 20, 245, 150, 179, 140, 47, 191, 3, 118, 22, 214, 8, 36, 77, 61, 167, 212, 186, 223, 53, 19, 48, 249, 71, 224, 76, 195, 80, 231]), + // Each of r and s in this input is padded up to one extra byte. + signature: new Uint8Array([0, 141, 157, 62, 61, 11, 43, 40, 113, 234, 47, 3, 242, 123, 168, 105, 159, 33, 75, 232, 216, 117, 192, 215, 112, 176, 255, 241, 196, 206, 52, 31, 12, 131, 74, 193, 31, 158, 193, 43, 253, 184, 50, 11, 23, 36, 200, 194, 32, 0, 98, 21, 13, 251, 168, 230, 92, 12, 123, 231, 239, 129, 200, 114, 65, 210, 195, 122, 131, 194, 126, 179, 28, 204, 43, 60, 57, 87, 103, 10, 116, 76, 129, 190, 109, 116, 19, 64, 181, 24, 156, 192, 197, 71, 223, 129, 176, 210]), + }, + { + name: "Empty signature", + publicKeyBuffer: new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 8, 116, 162, 224, 184, 255, 68, 143, 14, 84, 50, 30, 39, 244, 241, 230, 77, 6, 76, 222, 183, 210, 111, 69, 140, 50, 233, 48, 18, 15, 78, 87, 220, 133, 194, 105, 63, 151, 126, 237, 74, 142, 204, 141, 185, 129, 180, 217, 31, 105, 68, 109, 244, 244, 198, 245, 222, 25, 0, 63, 69, 248, 145, 208, 235, 205, 47, 255, 219, 92, 129, 192, 64, 232, 214, 153, 76, 67, 199, 254, 237, 185, 138, 74, 49, 237, 251, 53, 232, 154, 48, 1, 60, 59, 146, 103]), + publicKeyFormat: "spki", + publicKey: null, + algorithmName: "ECDSA", + namedCurve: "P-384", + hashName: "SHA-1", + plaintext: new Uint8Array([63, 7, 131, 165, 142, 102, 243, 210, 192, 204, 251, 95, 172, 63, 9, 219, 111, 134, 9, 208, 89, 43, 199, 127, 223, 254, 217, 207, 14, 19, 125, 38, 168, 103, 5, 118, 101, 243, 173, 129, 190, 235, 187, 219, 114, 61, 90, 71, 197, 128, 130, 143, 16, 247, 52, 122, 184, 169, 194, 77, 25, 95, 115, 109, 250, 230, 234, 227, 125, 136, 254, 59, 71, 53, 231, 198, 105, 168, 10, 193, 145, 62, 92, 36, 200, 193, 213, 205, 177, 95, 153, 79, 62, 194, 241, 199, 116, 117, 46, 20, 245, 150, 179, 140, 47, 191, 3, 118, 22, 214, 8, 36, 77, 61, 167, 212, 186, 223, 53, 19, 48, 249, 71, 224, 76, 195, 80, 231]), + signature: new Uint8Array([]), + }, + ]; + + return vectors; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa.https.any.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa.https.any.js new file mode 100644 index 0000000000..85993a48b9 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: sign() and verify() Using EdDSA +// META: script=eddsa_vectors.js +// META: script=eddsa.js +// META: timeout=long + +run_test(); diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa.js new file mode 100644 index 0000000000..d425fec2dc --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa.js @@ -0,0 +1,434 @@ + +function run_test() { + setup({explicit_done: true}); + + var subtle = self.crypto.subtle; // Change to test prefixed implementations + + // When are all these tests really done? When all the promises they use have resolved. + var all_promises = []; + + // Source file [algorithm_name]_vectors.js provides the getTestVectors method + // for the algorithm that drives these tests. + var testVectors = getTestVectors(); + + // Test verification first, because signing tests rely on that working + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.data) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification"); + }); + + all_promises.push(promise); + }); + + // Test verification with an altered buffer after call + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + promise_test(function(test) { + var signature = copyBuffer(vector.signature); + var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + signature[0] = 255 - signature[0]; + return operation; + }, vector.name + " verification with altered signature after call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification with altered signature after call"); + }); + + all_promises.push(promise); + }); + + // Check for successful verification even if data is altered after call. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + promise_test(function(test) { + var data = copyBuffer(vector.data); + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, data) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + data[0] = 255 - data[0]; + return operation; + }, vector.name + " with altered data after call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " with altered data after call"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to using privateKey to verify. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + promise_test(function(test) { + return subtle.verify(algorithm, vector.privateKey, vector.signature, vector.data) + .then(function(data) { + assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }); + }, vector.name + " using privateKey to verify"); + + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " using privateKey to verify"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to using publicKey to sign. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + promise_test(function(test) { + return subtle.sign(algorithm, vector.publicKey, vector.data) + .then(function(signature) { + assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }); + }, vector.name + " using publicKey to sign"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " using publicKey to sign"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to no "verify" usage. + testVectors.forEach(function(originalVector) { + var vector = Object.assign({}, originalVector); + + var promise = importVectorKeys(vector, [], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + promise_test(function(test) { + return subtle.verify(algorithm, vector.publicKey, vector.signature, vector.data) + .then(function(data) { + assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }); + }, vector.name + " no verify usage"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " no verify usage"); + }); + + all_promises.push(promise); + }); + + // Check for successful signing and verification. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + promise_test(function(test) { + return subtle.sign(algorithm, vector.privateKey, vector.data) + .then(function(signature) { + // Can we verify the signature? + return subtle.verify(algorithm, vector.publicKey, signature, vector.data) + .then(function(is_verified) { + assert_true(is_verified, "Round trip verification works"); + return signature; + }, function(err) { + assert_unreached("verify error for test " + vector.name + ": " + err.message + "'"); + }); + }, function(err) { + assert_unreached("sign error for test " + vector.name + ": '" + err.message + "'"); + }); + }, vector.name + " round trip"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested signing or verifying + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " round trip"); + }); + + all_promises.push(promise); + }); + + // Test signing with the wrong algorithm + testVectors.forEach(function(vector) { + // Want to get the key for the wrong algorithm + var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) + .then(function(wrongKey) { + var algorithm = {name: vector.algorithmName}; + return importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var operation = subtle.sign(algorithm, wrongKey, vector.data) + .then(function(signature) { + assert_unreached("Signing should not have succeeded for " + vector.name); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }); + + return operation; + }, vector.name + " signing with wrong algorithm name"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name"); + }); + }, function(err) { + promise_test(function(test) { + assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); + }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name"); + }); + + all_promises.push(promise); + }); + + // Test verification with the wrong algorithm + testVectors.forEach(function(vector) { + // Want to get the key for the wrong algorithm + var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) + .then(function(wrongKey) { + return importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + promise_test(function(test) { + var operation = subtle.verify(algorithm, wrongKey, vector.signature, vector.data) + .then(function(signature) { + assert_unreached("Verifying should not have succeeded for " + vector.name); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }); + + return operation; + }, vector.name + " verifying with wrong algorithm name"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name"); + }); + }, function(err) { + promise_test(function(test) { + assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); + }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name"); + }); + + all_promises.push(promise); + }); + + // Test verification fails with wrong signature + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data) + .then(function(is_verified) { + assert_false(is_verified, "Signature NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure due to altered signature"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to altered signature"); + }); + + all_promises.push(promise); + }); + + // Test verification fails with short (odd length) signature + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + var signature = vector.signature.slice(1); // Skip the first byte + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data) + .then(function(is_verified) { + assert_false(is_verified, "Signature NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure due to shortened signature"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to shortened signature"); + }); + + all_promises.push(promise); + }); + + // Test verification fails with wrong data + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + var algorithm = {name: vector.algorithmName}; + var data = copyBuffer(vector.data); + data[0] = 255 - data[0]; + promise_test(function(test) { + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, data) + .then(function(is_verified) { + assert_false(is_verified, "Signature NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure due to altered data"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to altered data"); + }); + + all_promises.push(promise); + }); + + + promise_test(function() { + return Promise.all(all_promises) + .then(function() {done();}) + .catch(function() {done();}) + }, "setup"); + + // Test that generated keys are valid for signing and verifying. + testVectors.forEach(function(vector) { + var algorithm = {name: vector.algorithmName}; + promise_test(async() => { + let key = await subtle.generateKey(algorithm, false, ["sign", "verify"]); + let signature = await subtle.sign(algorithm, key.privateKey, vector.data); + let isVerified = await subtle.verify(algorithm, key.publicKey, signature, vector.data); + assert_true(isVerified, "Verificaton failed."); + }, "Sign and verify using generated " + vector.algorithmName + " keys."); + }); + + + // A test vector has all needed fields for signing and verifying, EXCEPT that the + // key field may be null. This function replaces that null with the Correct + // CryptoKey object. + // + // Returns a Promise that yields an updated vector on success. + function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) { + var publicPromise, privatePromise; + + if (vector.publicKey !== null) { + publicPromise = new Promise(function(resolve, reject) { + resolve(vector); + }); + } else { + publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithmName}, false, publicKeyUsages) + .then(function(key) { + vector.publicKey = key; + return vector; + }); // Returns a copy of the sourceBuffer it is sent. + } + + if (vector.privateKey !== null) { + privatePromise = new Promise(function(resolve, reject) { + resolve(vector); + }); + } else { + privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithmName}, false, privateKeyUsages) + .then(function(key) { + vector.privateKey = key; + return vector; + }); + } + + return Promise.all([publicPromise, privatePromise]); + } + + // Returns a copy of the sourceBuffer it is sent. + function copyBuffer(sourceBuffer) { + var source = new Uint8Array(sourceBuffer); + var copy = new Uint8Array(sourceBuffer.byteLength) + + for (var i=0; i<source.byteLength; i++) { + copy[i] = source[i]; + } + + return copy; + } + + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + return; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa_vectors.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa_vectors.js new file mode 100644 index 0000000000..96ec2b01af --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa_vectors.js @@ -0,0 +1,58 @@ +// eddsa_vectors.js + +// Data for testing Ed25519 and Ed448. + +// The following function returns an array of test vectors +// for the subtleCrypto sign method. +// +// Each test vector has the following fields: +// name - a unique name for this vector +// publicKeyBuffer - an arrayBuffer with the key data +// publicKeyFormat - "spki" "jwk" +// publicKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// privateKeyBuffer - an arrayBuffer with the key data +// privateKeyFormat - "pkcs8" or "jwk" +// privateKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// algorithmName - the name of the AlgorithmIdentifier parameter to provide to sign +// data - the text to sign +// signature - the expected signature +function getTestVectors() { + var pkcs8 = { + "Ed25519": new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168, 31]), + "Ed448": new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29, 46]), + }; + + var spki = { + "Ed25519": new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]), + "Ed448": new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]), + }; + + // data + var data = new Uint8Array([43, 126, 208, 188, 119, 149, 105, 74, 180, 172, 211, 89, 3, 254, 140, 215, 216, 15, 106, 28, 134, 136, 166, 195, 65, 68, 9, 69, 117, 20, 161, 69, 120, 85, 187, 178, 25, 227, 10, 27, 238, 168, 254, 134, 144, 130, 217, 159, 200, 40, 47, 144, 80, 208, 36, 229, 158, 175, 7, 48, 186, 157, 183, 10]); + + // For verification tests. + var signatures = { + "Ed25519": new Uint8Array([61, 144, 222, 94, 87, 67, 223, 194, 130, 37, 191, 173, 179, 65, 177, 22, 203, 248, 163, 241, 206, 237, 191, 74, 220, 53, 14, 245, 211, 71, 24, 67, 164, 24, 97, 77, 203, 110, 97, 72, 98, 97, 76, 247, 175, 20, 150, 249, 52, 11, 60, 132, 78, 164, 220, 234, 177, 211, 209, 85, 235, 126, 204, 0]), + "Ed448": new Uint8Array([118, 137, 126, 140, 80, 172, 107, 17, 50, 115, 92, 9, 197, 95, 80, 108, 1, 73, 210, 103, 124, 117, 102, 79, 139, 193, 11, 130, 111, 189, 157, 240, 160, 60, 217, 134, 188, 232, 51, 158, 100, 199, 209, 114, 14, 169, 54, 23, 132, 220, 115, 131, 119, 101, 172, 41, 128, 192, 218, 192, 129, 74, 139, 193, 135, 209, 201, 201, 7, 197, 220, 192, 121, 86, 248, 91, 112, 147, 15, 228, 45, 231, 100, 23, 114, 23, 203, 45, 82, 186, 183, 193, 222, 190, 12, 168, 156, 206, 203, 205, 99, 247, 2, 90, 42, 90, 87, 43, 157, 35, 176, 100, 47, 0]), + } + + var vectors = []; + ["Ed25519", "Ed448"].forEach(function(algorithmName) { + var vector = { + name: "EdDSA " + algorithmName, + publicKeyBuffer: spki[algorithmName], + publicKeyFormat: "spki", + publicKey: null, + privateKeyBuffer: pkcs8[algorithmName], + privateKeyFormat: "pkcs8", + privateKey: null, + algorithmName: algorithmName, + data: data, + signature: signatures[algorithmName] + }; + + vectors.push(vector); + }); + + return vectors; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/hmac.https.any.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/hmac.https.any.js new file mode 100644 index 0000000000..419bab0506 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/hmac.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: sign() and verify() Using HMAC +// META: script=hmac_vectors.js +// META: script=hmac.js +// META: timeout=long + +run_test(); diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/hmac.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/hmac.js new file mode 100644 index 0000000000..8df4b042f5 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/hmac.js @@ -0,0 +1,350 @@ + +function run_test() { + setup({explicit_done: true}); + + var subtle = self.crypto.subtle; // Change to test prefixed implementations + + // When are all these tests really done? When all the promises they use have resolved. + var all_promises = []; + + // Source file hmac_vectors.js provides the getTestVectors method + // for the algorithm that drives these tests. + var testVectors = getTestVectors(); + + // Test verification first, because signing tests rely on that working + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + promise_test(function(test) { + var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification"); + }); + + all_promises.push(promise); + }); + + // Test verification with an altered buffer after call + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + promise_test(function(test) { + var signature = copyBuffer(vector.signature); + var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature is not verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + signature[0] = 255 - signature[0]; + return operation; + }, vector.name + " verification with altered signature after call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification with altered signature after call"); + }); + + all_promises.push(promise); + }); + + // Check for successful verification even if plaintext is altered after call. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + promise_test(function(test) { + var plaintext = copyBuffer(vector.plaintext); + var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + plaintext[0] = 255 - plaintext[0]; + return operation; + }, vector.name + " with altered plaintext after call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to no "verify" usage. + testVectors.forEach(function(originalVector) { + var vector = Object.assign({}, originalVector); + + var promise = importVectorKeys(vector, ["sign"]) + .then(function(vector) { + promise_test(function(test) { + return subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, vector.plaintext) + .then(function(plaintext) { + assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }); + }, vector.name + " no verify usage"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " no verify usage"); + }); + + all_promises.push(promise); + }); + + // Check for successful signing and verification. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vectors) { + promise_test(function(test) { + return subtle.sign({name: "HMAC", hash: vector.hash}, vector.key, vector.plaintext) + .then(function(signature) { + // Can we get the verify the new signature? + return subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Round trip verifies"); + return signature; + }, function(err) { + assert_unreached("verify error for test " + vector.name + ": " + err.message + "'"); + }); + }); + }, vector.name + " round trip"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested signing or verifying + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " round trip"); + }); + + all_promises.push(promise); + }); + + // Test signing with the wrong algorithm + testVectors.forEach(function(vector) { + // Want to get the key for the wrong algorithm + var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"]) + .then(function(wrongKey) { + return importVectorKeys(vector, ["verify", "sign"]) + .then(function(vectors) { + promise_test(function(test) { + var operation = subtle.sign({name: "HMAC", hash: vector.hash}, wrongKey.privateKey, vector.plaintext) + .then(function(signature) { + assert_unreached("Signing should not have succeeded for " + vector.name); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }); + + return operation; + }, vector.name + " signing with wrong algorithm name"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name"); + }); + }, function(err) { + promise_test(function(test) { + assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); + }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name"); + }); + + all_promises.push(promise); + }); + + // Test verification with the wrong algorithm + testVectors.forEach(function(vector) { + // Want to get the key for the wrong algorithm + var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"]) + .then(function(wrongKey) { + return importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + promise_test(function(test) { + var operation = subtle.verify({name: "HMAC", hash: vector.hash}, wrongKey.publicKey, vector.signature, vector.plaintext) + .then(function(signature) { + assert_unreached("Verifying should not have succeeded for " + vector.name); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }); + + return operation; + }, vector.name + " verifying with wrong algorithm name"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name"); + }); + }, function(err) { + promise_test(function(test) { + assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); + }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name"); + }); + + all_promises.push(promise); + }); + + // Verification should fail if the plaintext is changed + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + var plaintext = copyBuffer(vector.plaintext); + plaintext[0] = 255 - plaintext[0]; + promise_test(function(test) { + var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature is NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure due to wrong plaintext"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to wrong plaintext"); + }); + + all_promises.push(promise); + }); + + // Verification should fail if the signature is changed + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + promise_test(function(test) { + var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature is NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure due to wrong signature"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to wrong signature"); + }); + + all_promises.push(promise); + }); + + // Verification should fail if the signature is wrong length + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + var signature = vector.signature.slice(1); // Drop first byte + promise_test(function(test) { + var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature is NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure due to short signature"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure due to short signature"); + }); + + all_promises.push(promise); + }); + + + + promise_test(function() { + return Promise.all(all_promises) + .then(function() {done();}) + .catch(function() {done();}) + }, "setup"); + + // A test vector has all needed fields for signing and verifying, EXCEPT that the + // key field may be null. This function replaces that null with the Correct + // CryptoKey object. + // + // Returns a Promise that yields an updated vector on success. + function importVectorKeys(vector, keyUsages) { + if (vector.key !== null) { + return new Promise(function(resolve, reject) { + resolve(vector); + }); + } else { + return subtle.importKey("raw", vector.keyBuffer, {name: "HMAC", hash: vector.hash}, false, keyUsages) + .then(function(key) { + vector.key = key; + return vector; + }); + } + } + + // Returns a copy of the sourceBuffer it is sent. + function copyBuffer(sourceBuffer) { + var source = new Uint8Array(sourceBuffer); + var copy = new Uint8Array(sourceBuffer.byteLength) + + for (var i=0; i<source.byteLength; i++) { + copy[i] = source[i]; + } + + return copy; + } + + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + return; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/hmac_vectors.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/hmac_vectors.js new file mode 100644 index 0000000000..de9642b181 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/hmac_vectors.js @@ -0,0 +1,39 @@ + +function getTestVectors() { + var plaintext = new Uint8Array([95, 77, 186, 79, 50, 12, 12, 232, 118, 114, 90, 252, 229, 251, 210, 91, 248, 62, 90, 113, 37, 160, 140, 175, 231, 60, 62, 186, 196, 33, 119, 157, 249, 213, 93, 24, 12, 58, 233, 148, 38, 69, 225, 216, 47, 238, 140, 157, 41, 75, 60, 177, 160, 138, 153, 49, 32, 27, 60, 14, 129, 252, 71, 202, 207, 131, 21, 162, 175, 102, 50, 65, 19, 195, 182, 98, 48, 195, 70, 8, 196, 244, 89, 54, 52, 206, 2, 178, 103, 54, 34, 119, 240, 168, 64, 202, 116, 188, 61, 26, 98, 54, 149, 44, 94, 215, 170, 248, 168, 254, 203, 221, 250, 117, 132, 230, 151, 140, 234, 93, 42, 91, 159, 183, 241, 180, 140, 139, 11, 229, 138, 48, 82, 2, 117, 77, 131, 118, 16, 115, 116, 121, 60, 240, 38, 170, 238, 83, 0, 114, 125, 131, 108, 215, 30, 113, 179, 69, 221, 178, 228, 68, 70, 255, 197, 185, 1, 99, 84, 19, 137, 13, 145, 14, 163, 128, 152, 74, 144, 25, 16, 49, 50, 63, 22, 219, 204, 157, 107, 225, 104, 184, 72, 133, 56, 76, 160, 62, 18, 96, 10, 193, 194, 72, 2, 138, 243, 114, 108, 201, 52, 99, 136, 46, 168, 192, 42, 171]); + + var raw = { + "SHA-1": new Uint8Array([71, 162, 7, 70, 209, 113, 121, 219, 101, 224, 167, 157, 237, 255, 199, 253, 241, 129, 8, 27]), + "SHA-256": new Uint8Array([229, 136, 236, 8, 17, 70, 61, 118, 114, 65, 223, 16, 116, 180, 122, 228, 7, 27, 81, 242, 206, 54, 83, 123, 166, 156, 205, 195, 253, 194, 183, 168]), + "SHA-384": new Uint8Array([107, 29, 162, 142, 171, 31, 88, 42, 217, 113, 142, 255, 224, 94, 35, 213, 253, 44, 152, 119, 162, 217, 68, 63, 144, 190, 192, 147, 190, 206, 46, 167, 210, 53, 76, 208, 189, 197, 225, 71, 210, 233, 0, 147, 115, 73, 68, 136]), + "SHA-512": new Uint8Array([93, 204, 53, 148, 67, 170, 246, 82, 250, 19, 117, 214, 179, 230, 31, 220, 242, 155, 180, 162, 139, 213, 211, 220, 250, 64, 248, 47, 144, 107, 178, 128, 4, 85, 219, 3, 181, 211, 31, 185, 114, 161, 90, 109, 1, 3, 162, 78, 86, 209, 86, 161, 25, 192, 229, 161, 233, 42, 68, 195, 197, 101, 124, 249]) + }; + + var signatures = { + "SHA-1": new Uint8Array([5, 51, 144, 42, 153, 248, 82, 78, 229, 10, 240, 29, 56, 222, 220, 225, 51, 217, 140, 160]), + "SHA-256": new Uint8Array([133, 164, 12, 234, 46, 7, 140, 40, 39, 163, 149, 63, 251, 102, 194, 123, 41, 26, 71, 43, 13, 112, 160, 0, 11, 69, 216, 35, 128, 62, 235, 84]), + "SHA-384": new Uint8Array([33, 124, 61, 80, 240, 186, 154, 109, 110, 174, 30, 253, 215, 165, 24, 254, 46, 56, 128, 181, 130, 164, 13, 6, 30, 144, 153, 193, 224, 38, 239, 88, 130, 84, 139, 93, 92, 236, 221, 85, 152, 217, 155, 107, 111, 48, 87, 255]), + "SHA-512": new Uint8Array([97, 251, 39, 140, 63, 251, 12, 206, 43, 241, 207, 114, 61, 223, 216, 239, 31, 147, 28, 12, 97, 140, 37, 144, 115, 36, 96, 89, 57, 227, 249, 162, 198, 244, 175, 105, 11, 218, 52, 7, 220, 47, 87, 112, 246, 160, 164, 75, 149, 77, 100, 163, 50, 227, 238, 8, 33, 171, 248, 43, 127, 62, 153, 193]) + }; + + // Each test vector has the following fields: + // name - a unique name for this vector + // keyBuffer - an arrayBuffer with the key data + // key - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! + // hashName - the hash function to sign with + // plaintext - the text to encrypt + // signature - the expected signature + var vectors = []; + Object.keys(raw).forEach(function(hashName) { + vectors.push({ + name: "HMAC with " + hashName, + hash: hashName, + keyBuffer: raw[hashName], + key: null, + plaintext: plaintext, + signature: signatures[hashName] + }); + }); + + return vectors; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa.js new file mode 100644 index 0000000000..3eb79fb013 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa.js @@ -0,0 +1,409 @@ + +function run_test() { + setup({explicit_done: true}); + + var subtle = self.crypto.subtle; // Change to test prefixed implementations + + // When are all these tests really done? When all the promises they use have resolved. + var all_promises = []; + + // Source file [algorithm_name]_vectors.js provides the getTestVectors method + // for the algorithm that drives these tests. + var testVectors = getTestVectors(); + + // Test verification first, because signing tests rely on that working + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var operation = subtle.verify(vector.algorithm, vector.publicKey, vector.signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification"); + }); + + all_promises.push(promise); + }); + + // Test verification with an altered buffer after call + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var signature = copyBuffer(vector.signature); + var operation = subtle.verify(vector.algorithm, vector.publicKey, signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + signature[0] = 255 - signature[0]; + return operation; + }, vector.name + " verification with altered signature after call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification with altered signature after call"); + }); + + all_promises.push(promise); + }); + + // Check for successful verification even if plaintext is altered after call. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var plaintext = copyBuffer(vector.plaintext); + var operation = subtle.verify(vector.algorithm, vector.publicKey, vector.signature, plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + plaintext[0] = 255 - plaintext[0]; + return operation; + }, vector.name + " with altered plaintext after call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext after call"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to using privateKey to verify. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + return subtle.verify(vector.algorithm, vector.privateKey, vector.signature, vector.plaintext) + .then(function(plaintext) { + assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }); + }, vector.name + " using privateKey to verify"); + + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " using privateKey to verify"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to using publicKey to sign. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + return subtle.sign(vector.algorithm, vector.publicKey, vector.plaintext) + .then(function(signature) { + assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }); + }, vector.name + " using publicKey to sign"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " using publicKey to sign"); + }); + + all_promises.push(promise); + }); + + // Check for failures due to no "verify" usage. + testVectors.forEach(function(originalVector) { + var vector = Object.assign({}, originalVector); + + var promise = importVectorKeys(vector, [], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + return subtle.verify(vector.algorithm, vector.publicKey, vector.signature, vector.plaintext) + .then(function(plaintext) { + assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'"); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }); + }, vector.name + " no verify usage"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " no verify usage"); + }); + + all_promises.push(promise); + }); + + // Check for successful signing and verification. + testVectors.forEach(function(vector) { + // RSA signing is deterministic with PKCS#1 v1.5, or PSS with zero-length salts. + const isDeterministic = !("saltLength" in vector.algorithm) || vector.algorithm.saltLength == 0; + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + return subtle.sign(vector.algorithm, vector.privateKey, vector.plaintext) + .then(function(signature) { + if (isDeterministic) { + // If deterministic, we can check the output matches. Otherwise, we can only check it verifies. + assert_true(equalBuffers(signature, vector.signature), "Signing did not give the expected output"); + } + // Can we verify the new signature? + return subtle.verify(vector.algorithm, vector.publicKey, signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Round trip verifies"); + return signature; + }, function(err) { + assert_unreached("verify error for test " + vector.name + ": " + err.message + "'"); + }); + }) + .then(function(priorSignature) { + // Will a second signing give us different signature? It should for PSS with non-empty salt + return subtle.sign(vector.algorithm, vector.privateKey, vector.plaintext) + .then(function(signature) { + if (isDeterministic) { + assert_true(equalBuffers(priorSignature, signature), "Two signings with empty salt give same signature") + } else { + assert_false(equalBuffers(priorSignature, signature), "Two signings with a salt give different signatures") + } + }, function(err) { + assert_unreached("second time verify error for test " + vector.name + ": '" + err.message + "'"); + }); + }, function(err) { + assert_unreached("sign error for test " + vector.name + ": '" + err.message + "'"); + }); + }, vector.name + " round trip"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested signing or verifying + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " round trip"); + }); + + all_promises.push(promise); + }); + + + // Test signing with the wrong algorithm + testVectors.forEach(function(vector) { + // Want to get the key for the wrong algorithm + var alteredVector = Object.assign({}, vector); + alteredVector.algorithm = Object.assign({}, vector.algorithm); + if (vector.algorithm.name === "RSA-PSS") { + alteredVector.algorithm.name = "RSASSA-PKCS1-v1_5"; + } else { + alteredVector.algorithm.name = "RSA-PSS"; + } + + var promise = importVectorKeys(alteredVector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var operation = subtle.sign(vector.algorithm, alteredVector.privateKey, vector.plaintext) + .then(function(signature) { + assert_unreached("Signing should not have succeeded for " + vector.name); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }); + + return operation; + }, vector.name + " signing with wrong algorithm name"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name"); + }); + + all_promises.push(promise); + }); + + // Test verification with the wrong algorithm + testVectors.forEach(function(vector) { + // Want to get the key for the wrong algorithm + var alteredVector = Object.assign({}, vector); + alteredVector.algorithm = Object.assign({}, vector.algorithm); + if (vector.algorithm.name === "RSA-PSS") { + alteredVector.algorithm.name = "RSASSA-PKCS1-v1_5"; + } else { + alteredVector.algorithm.name = "RSA-PSS"; + } + + var promise = importVectorKeys(alteredVector, ["verify"], ["sign"]) + .then(function(vectors) { + // Some tests are sign only + if (!("signature" in vector)) { + return; + } + promise_test(function(test) { + var operation = subtle.verify(vector.algorithm, alteredVector.publicKey, vector.signature, vector.plaintext) + .then(function(is_verified) { + assert_unreached("Verification should not have succeeded for " + vector.name); + }, function(err) { + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }); + + return operation; + }, vector.name + " verification with wrong algorithm name"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification with wrong algorithm name"); + }); + + all_promises.push(promise); + }); + + // Verification should fail with wrong signature + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + var operation = subtle.verify(vector.algorithm, vector.publicKey, signature, vector.plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure with altered signature"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure with altered signature"); + }); + + all_promises.push(promise); + }); + + // Verification should fail with wrong plaintext + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var plaintext = copyBuffer(vector.plaintext); + plaintext[0] = 255 - plaintext[0]; + var operation = subtle.verify(vector.algorithm, vector.publicKey, vector.signature, plaintext) + .then(function(is_verified) { + assert_false(is_verified, "Signature NOT verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification failure with altered plaintext"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested verification. + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification failure with altered plaintext"); + }); + + all_promises.push(promise); + }); + + + promise_test(function() { + return Promise.all(all_promises) + .then(function() {done();}) + .catch(function() {done();}) + }, "setup"); + + // A test vector has all needed fields for signing and verifying, EXCEPT that the + // key field may be null. This function replaces that null with the Correct + // CryptoKey object. + // + // Returns a Promise that yields an updated vector on success. + function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) { + var publicPromise, privatePromise; + + if (vector.publicKey !== null) { + publicPromise = new Promise(function(resolve, reject) { + resolve(vector); + }); + } else { + publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, publicKeyUsages) + .then(function(key) { + vector.publicKey = key; + return vector; + }); // Returns a copy of the sourceBuffer it is sent. + } + + if (vector.privateKey !== null) { + privatePromise = new Promise(function(resolve, reject) { + resolve(vector); + }); + } else { + privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, privateKeyUsages) + .then(function(key) { + vector.privateKey = key; + return vector; + }); + } + + return Promise.all([publicPromise, privatePromise]); + } + + // Returns a copy of the sourceBuffer it is sent. + function copyBuffer(sourceBuffer) { + var source = new Uint8Array(sourceBuffer); + var copy = new Uint8Array(sourceBuffer.byteLength) + + for (var i=0; i<source.byteLength; i++) { + copy[i] = source[i]; + } + + return copy; + } + + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + return; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pkcs.https.any.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pkcs.https.any.js new file mode 100644 index 0000000000..d930a715ac --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pkcs.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: sign() and verify() Using RSASSA-PKCS1-v1_5 +// META: script=rsa_pkcs_vectors.js +// META: script=rsa.js +// META: timeout=long + +run_test(); diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pkcs_vectors.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pkcs_vectors.js new file mode 100644 index 0000000000..71e5d8571b --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pkcs_vectors.js @@ -0,0 +1,92 @@ + +// rsa_pkcs_vectors.js + +// Data for testing RSASSA-PKCS1-v1_5 with a 2048-bit modulus and 65537 public exponent. + +// The following function returns an array of test vectors +// for the subtleCrypto encrypt method. +// +// Each test vector has the following fields: +// name - a unique name for this vector +// publicKeyBuffer - an arrayBuffer with the key data +// publicKeyFormat - "spki" "jwk" +// publicKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// privateKeyBuffer - an arrayBuffer with the key data +// privateKeyFormat - "pkcs8" or "jwk" +// privateKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// algorithm - the value of the AlgorithmIdentifier parameter to provide to encrypt +// plaintext - the text to encrypt +// signature - the expected signature +function getTestVectors() { + var pkcs8 = new Uint8Array([48, 130, 4, 191, 2, 1, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 4, 130, 4, 169, 48, 130, 4, 165, 2, 1, 0, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1, 2, 130, 1, 1, 0, 139, 55, 92, 203, 135, 200, 37, 197, 255, 61, 83, 208, 9, 145, 110, 150, 65, 5, 126, 24, 82, 114, 39, 160, 122, 178, 38, 190, 16, 136, 129, 58, 59, 56, 187, 123, 72, 243, 119, 5, 81, 101, 250, 42, 147, 57, 210, 77, 198, 103, 213, 197, 186, 52, 39, 230, 164, 129, 23, 110, 172, 21, 255, 212, 144, 104, 49, 30, 28, 40, 59, 159, 58, 142, 12, 184, 9, 180, 99, 12, 80, 170, 143, 62, 69, 166, 11, 53, 158, 25, 191, 140, 187, 94, 202, 214, 78, 118, 31, 16, 149, 116, 63, 243, 106, 175, 92, 240, 236, 185, 127, 237, 173, 221, 166, 11, 91, 243, 93, 129, 26, 117, 184, 34, 35, 12, 250, 160, 25, 47, 173, 64, 84, 126, 39, 84, 72, 170, 51, 22, 191, 142, 43, 76, 224, 133, 79, 199, 112, 139, 83, 123, 162, 45, 19, 33, 11, 9, 174, 195, 122, 39, 89, 239, 192, 130, 161, 83, 27, 35, 169, 23, 48, 3, 125, 222, 78, 242, 107, 95, 150, 239, 220, 195, 159, 211, 76, 52, 90, 213, 28, 187, 228, 79, 229, 139, 138, 59, 78, 201, 151, 134, 108, 8, 109, 255, 27, 136, 49, 239, 10, 31, 234, 38, 60, 247, 218, 205, 3, 192, 76, 188, 194, 178, 121, 229, 127, 165, 185, 83, 153, 107, 251, 29, 214, 136, 23, 175, 127, 180, 44, 222, 247, 165, 41, 74, 87, 250, 194, 184, 173, 115, 159, 27, 2, 153, 2, 129, 129, 0, 251, 248, 51, 194, 198, 49, 201, 112, 36, 12, 142, 116, 133, 240, 106, 62, 162, 168, 72, 34, 81, 26, 134, 39, 221, 70, 78, 248, 175, 175, 113, 72, 209, 164, 37, 182, 184, 101, 125, 221, 82, 70, 131, 43, 142, 83, 48, 32, 197, 187, 181, 104, 133, 90, 106, 236, 62, 66, 33, 215, 147, 241, 220, 91, 47, 37, 132, 226, 65, 94, 72, 233, 162, 189, 41, 43, 19, 64, 49, 249, 156, 142, 180, 47, 192, 188, 208, 68, 155, 242, 44, 230, 222, 201, 112, 20, 239, 229, 172, 147, 235, 232, 53, 135, 118, 86, 37, 44, 187, 177, 108, 65, 91, 103, 177, 132, 210, 40, 69, 104, 162, 119, 213, 147, 53, 88, 92, 253, 2, 129, 129, 0, 214, 184, 206, 39, 199, 41, 93, 93, 22, 252, 53, 112, 237, 100, 200, 218, 147, 3, 250, 210, 148, 136, 193, 166, 94, 154, 215, 17, 249, 3, 112, 24, 125, 187, 253, 129, 49, 109, 105, 100, 139, 200, 140, 197, 200, 53, 81, 175, 255, 69, 222, 186, 207, 182, 17, 5, 247, 9, 228, 195, 8, 9, 185, 0, 49, 235, 214, 134, 36, 68, 150, 198, 246, 158, 105, 46, 189, 200, 20, 246, 66, 57, 244, 173, 21, 117, 110, 203, 120, 197, 165, 176, 153, 49, 219, 24, 48, 119, 197, 70, 163, 140, 76, 116, 56, 137, 173, 61, 62, 208, 121, 181, 98, 46, 208, 18, 15, 160, 225, 249, 59, 89, 61, 183, 216, 82, 224, 95, 2, 129, 128, 56, 135, 75, 157, 131, 247, 129, 120, 206, 45, 158, 252, 23, 92, 131, 137, 127, 214, 127, 48, 107, 191, 166, 159, 100, 238, 52, 35, 104, 206, 212, 124, 128, 195, 241, 206, 23, 122, 117, 141, 100, 186, 251, 12, 151, 134, 164, 66, 133, 250, 1, 205, 236, 53, 7, 205, 238, 125, 201, 183, 226, 178, 29, 60, 187, 204, 16, 14, 238, 153, 103, 132, 59, 5, 115, 41, 253, 204, 166, 41, 152, 237, 15, 17, 179, 140, 232, 176, 171, 199, 222, 57, 1, 124, 113, 207, 208, 174, 87, 84, 108, 85, 145, 68, 205, 208, 175, 208, 100, 95, 126, 168, 255, 7, 185, 116, 209, 237, 68, 253, 31, 142, 0, 245, 96, 191, 109, 69, 2, 129, 129, 0, 133, 41, 239, 144, 115, 207, 143, 123, 95, 249, 226, 26, 186, 223, 58, 65, 115, 211, 144, 6, 112, 223, 175, 89, 66, 106, 188, 223, 4, 147, 193, 61, 47, 29, 27, 70, 184, 36, 166, 172, 24, 148, 179, 217, 37, 37, 12, 24, 30, 52, 114, 193, 96, 120, 5, 110, 177, 154, 141, 40, 247, 31, 48, 128, 146, 117, 52, 129, 212, 148, 68, 253, 247, 140, 158, 166, 194, 68, 7, 220, 1, 142, 119, 211, 175, 239, 56, 91, 47, 247, 67, 158, 150, 35, 121, 65, 51, 45, 212, 70, 206, 190, 255, 219, 68, 4, 254, 79, 113, 89, 81, 97, 208, 22, 64, 44, 51, 77, 15, 87, 198, 26, 190, 79, 249, 244, 203, 249, 2, 129, 129, 0, 135, 216, 119, 8, 212, 103, 99, 228, 204, 190, 178, 209, 233, 113, 46, 91, 240, 33, 109, 112, 222, 148, 32, 165, 178, 6, 155, 116, 89, 185, 159, 93, 159, 127, 47, 173, 124, 215, 154, 174, 230, 122, 127, 154, 52, 67, 126, 60, 121, 168, 74, 240, 205, 141, 233, 223, 242, 104, 235, 12, 71, 147, 245, 1, 249, 136, 213, 64, 246, 211, 71, 92, 32, 121, 184, 34, 122, 35, 217, 104, 222, 196, 227, 198, 101, 3, 24, 113, 147, 69, 150, 48, 71, 43, 253, 182, 186, 29, 231, 134, 199, 151, 250, 111, 78, 166, 90, 42, 132, 25, 38, 47, 41, 103, 136, 86, 203, 115, 201, 189, 75, 200, 155, 94, 4, 27, 34, 119]); + var spki = new Uint8Array([48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1]); + + // plaintext + var plaintext = new Uint8Array([95, 77, 186, 79, 50, 12, 12, 232, 118, 114, 90, 252, 229, 251, 210, 91, 248, 62, 90, 113, 37, 160, 140, 175, 231, 60, 62, 186, 196, 33, 119, 157, 249, 213, 93, 24, 12, 58, 233, 148, 38, 69, 225, 216, 47, 238, 140, 157, 41, 75, 60, 177, 160, 138, 153, 49, 32, 27, 60, 14, 129, 252, 71, 202, 207, 131, 21, 162, 175, 102, 50, 65, 19, 195, 182, 98, 48, 195, 70, 8, 196, 244, 89, 54, 52, 206, 2, 178, 103, 54, 34, 119, 240, 168, 64, 202, 116, 188, 61, 26, 98, 54, 149, 44, 94, 215, 170, 248, 168, 254, 203, 221, 250, 117, 132, 230, 151, 140, 234, 93, 42, 91, 159, 183, 241, 180, 140, 139, 11, 229, 138, 48, 82, 2, 117, 77, 131, 118, 16, 115, 116, 121, 60, 240, 38, 170, 238, 83, 0, 114, 125, 131, 108, 215, 30, 113, 179, 69, 221, 178, 228, 68, 70, 255, 197, 185, 1, 99, 84, 19, 137, 13, 145, 14, 163, 128, 152, 74, 144, 25, 16, 49, 50, 63, 22, 219, 204, 157, 107, 225, 104, 184, 72, 133, 56, 76, 160, 62, 18, 96, 10, 193, 194, 72, 2, 138, 243, 114, 108, 201, 52, 99, 136, 46, 168, 192, 42, 171]); + + // For verification tests. + var signatures = { + "sha-1": new Uint8Array([83, 46, 47, 27, 105, 204, 46, 232, 71, 46, 242, 143, 127, 54, 168, 26, 36, 205, 228, 238, 131, 133, 138, 125, 23, 5, 74, 195, 96, 44, 152, 221, 67, 46, 59, 54, 144, 68, 9, 53, 7, 43, 183, 192, 49, 230, 128, 112, 29, 25, 185, 124, 181, 81, 13, 134, 201, 190, 219, 231, 209, 192, 104, 57, 206, 237, 138, 59, 106, 201, 86, 65, 49, 196, 81, 43, 187, 171, 222, 35, 123, 77, 170, 41, 250, 13, 60, 151, 72, 123, 72, 168, 254, 233, 214, 59, 80, 86, 157, 198, 183, 209, 8, 80, 200, 50, 5, 89, 52, 59, 133, 55, 182, 18, 20, 167, 228, 84, 58, 113, 77, 101, 226, 28, 78, 71, 130, 148, 235, 66, 70, 206, 166, 104, 227, 81, 252, 224, 180, 225, 24, 199, 88, 190, 79, 220, 70, 199, 179, 34, 107, 191, 64, 181, 179, 149, 13, 98, 184, 189, 170, 79, 107, 183, 106, 48, 34, 43, 163, 39, 52, 237, 93, 244, 172, 141, 79, 255, 167, 85, 113, 5, 8, 122, 106, 207, 186, 91, 72, 81, 97, 99, 187, 145, 104, 100, 232, 44, 184, 97, 235, 145, 13, 207, 111, 26, 219, 173, 83, 153, 175, 212, 151, 251, 122, 251, 127, 117, 218, 131, 200, 5, 146, 234, 26, 222, 62, 56, 3, 180, 187, 104, 49, 185, 51, 41, 124, 15, 204, 195, 105, 55, 228, 96, 24, 121, 127, 202, 133, 148, 125, 41, 198, 162, 122, 129]), + "sha-256": new Uint8Array([19, 48, 106, 186, 37, 29, 238, 82, 100, 89, 194, 131, 82, 164, 41, 205, 216, 85, 84, 199, 228, 166, 121, 10, 44, 110, 68, 180, 94, 187, 196, 160, 10, 10, 85, 36, 77, 98, 166, 13, 223, 14, 215, 212, 45, 119, 20, 241, 47, 120, 58, 198, 112, 157, 14, 39, 2, 219, 38, 146, 174, 170, 204, 90, 92, 219, 190, 193, 115, 25, 140, 170, 176, 209, 79, 232, 133, 208, 168, 218, 31, 22, 106, 150, 92, 158, 37, 212, 132, 112, 180, 136, 77, 92, 146, 164, 216, 68, 23, 68, 5, 86, 143, 74, 192, 52, 13, 246, 196, 16, 252, 68, 207, 126, 230, 213, 155, 166, 52, 249, 198, 36, 12, 150, 181, 154, 6, 252, 238, 255, 77, 210, 150, 34, 231, 249, 131, 174, 191, 0, 236, 242, 65, 241, 201, 18, 207, 213, 220, 110, 238, 185, 79, 157, 145, 97, 19, 232, 67, 169, 133, 85, 194, 66, 87, 248, 195, 237, 171, 31, 39, 131, 159, 140, 201, 169, 99, 232, 184, 84, 101, 165, 110, 193, 216, 118, 34, 90, 224, 1, 251, 212, 36, 71, 1, 226, 228, 125, 129, 181, 87, 132, 126, 56, 39, 227, 59, 54, 243, 245, 232, 254, 223, 164, 154, 13, 52, 208, 29, 189, 175, 132, 250, 92, 117, 47, 2, 2, 168, 202, 178, 196, 204, 44, 181, 7, 111, 101, 55, 217, 194, 45, 53, 55, 56, 233, 179, 156, 151, 2, 107, 5, 156, 233, 93, 137]), + "sha-384": new Uint8Array([53, 79, 205, 28, 98, 226, 54, 45, 78, 139, 206, 223, 81, 80, 247, 178, 123, 236, 51, 171, 50, 162, 121, 117, 52, 90, 76, 140, 254, 178, 52, 102, 155, 196, 171, 175, 129, 231, 25, 221, 244, 193, 175, 174, 69, 67, 44, 183, 174, 185, 19, 60, 189, 135, 141, 231, 102, 232, 114, 98, 129, 120, 163, 58, 194, 10, 2, 138, 125, 140, 43, 100, 26, 92, 82, 59, 22, 187, 230, 94, 178, 11, 171, 51, 28, 152, 58, 150, 27, 174, 109, 230, 78, 107, 144, 119, 170, 137, 200, 70, 184, 214, 157, 207, 113, 1, 71, 141, 16, 117, 26, 59, 135, 178, 165, 222, 44, 166, 206, 113, 160, 191, 237, 127, 88, 122, 33, 110, 5, 58, 30, 83, 196, 162, 172, 233, 60, 38, 27, 68, 15, 236, 212, 51, 13, 215, 203, 215, 146, 184, 57, 133, 2, 178, 162, 8, 69, 164, 194, 145, 141, 135, 42, 172, 245, 11, 48, 39, 19, 87, 1, 154, 88, 174, 24, 129, 158, 117, 196, 142, 158, 248, 8, 16, 134, 15, 160, 73, 100, 119, 108, 160, 75, 32, 3, 41, 103, 77, 202, 83, 32, 180, 0, 245, 23, 134, 78, 113, 224, 135, 182, 139, 129, 223, 97, 62, 226, 75, 172, 52, 220, 242, 230, 69, 149, 225, 44, 121, 144, 112, 243, 247, 25, 217, 61, 120, 67, 182, 149, 146, 52, 108, 252, 32, 198, 187, 249, 49, 7, 242, 121, 214, 32, 126, 198, 87]), + "sha-512": new Uint8Array([98, 41, 183, 8, 151, 248, 98, 11, 99, 84, 135, 205, 74, 169, 150, 38, 152, 49, 255, 41, 49, 210, 135, 20, 240, 30, 88, 177, 101, 241, 8, 44, 49, 152, 184, 244, 81, 120, 140, 253, 62, 213, 154, 120, 248, 52, 209, 28, 226, 133, 209, 5, 28, 66, 165, 206, 160, 34, 127, 222, 254, 41, 52, 68, 194, 81, 142, 190, 92, 176, 5, 91, 234, 75, 88, 6, 240, 235, 161, 182, 101, 2, 42, 99, 190, 68, 192, 136, 254, 154, 210, 99, 37, 215, 159, 124, 65, 237, 151, 249, 9, 205, 76, 162, 131, 40, 228, 196, 169, 222, 141, 166, 124, 53, 220, 28, 133, 183, 30, 214, 255, 170, 249, 157, 116, 178, 184, 142, 159, 95, 5, 167, 50, 246, 104, 140, 153, 59, 88, 160, 237, 53, 232, 240, 161, 6, 212, 232, 177, 179, 96, 227, 52, 65, 92, 116, 46, 148, 103, 88, 35, 219, 15, 210, 94, 34, 207, 247, 166, 51, 92, 112, 225, 147, 35, 93, 205, 164, 138, 221, 104, 88, 98, 107, 217, 99, 17, 230, 15, 126, 94, 164, 73, 27, 108, 30, 98, 72, 175, 225, 43, 187, 213, 79, 136, 105, 176, 67, 165, 176, 68, 69, 98, 129, 63, 10, 152, 179, 0, 53, 111, 48, 110, 107, 120, 58, 41, 243, 190, 201, 124, 164, 14, 162, 0, 98, 202, 184, 146, 110, 197, 217, 106, 163, 135, 204, 132, 130, 26, 109, 114, 184, 234, 18, 110, 125]) + }; + + var vectors = [ + { + name: "RSASSA-PKCS1-v1_5 with SHA-1", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSASSA-PKCS1-v1_5"}, + hash: "SHA-1", + plaintext: plaintext, + signature: signatures["sha-1"] + }, + { + name: "RSASSA-PKCS1-v1_5 with SHA-256", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSASSA-PKCS1-v1_5"}, + hash: "SHA-256", + plaintext: plaintext, + signature: signatures["sha-256"] + }, + { + name: "RSASSA-PKCS1-v1_5 with SHA-384", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSASSA-PKCS1-v1_5"}, + hash: "SHA-384", + plaintext: plaintext, + signature: signatures["sha-384"] + }, + { + name: "RSASSA-PKCS1-v1_5 with SHA-512", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSASSA-PKCS1-v1_5"}, + hash: "SHA-512", + plaintext: plaintext, + signature: signatures["sha-512"] + } + ]; + + + return vectors; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pss.https.any.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pss.https.any.js new file mode 100644 index 0000000000..f02ba2096b --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pss.https.any.js @@ -0,0 +1,6 @@ +// META: title=WebCryptoAPI: sign() and verify() Using RSA-PSS +// META: script=rsa_pss_vectors.js +// META: script=rsa.js +// META: timeout=long + +run_test(); diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pss_vectors.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pss_vectors.js new file mode 100644 index 0000000000..c3ce779606 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/rsa_pss_vectors.js @@ -0,0 +1,147 @@ + +// rsa_pss_vectors.js + +// Data for testing RSA-PSS with a 2048-bit modulus and 65537 public exponent. + +// The following function returns an array of test vectors +// for the subtleCrypto encrypt method. +// +// Each test vector has the following fields: +// name - a unique name for this vector +// publicKeyBuffer - an arrayBuffer with the key data +// publicKeyFormat - "spki" "jwk" +// publicKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// privateKeyBuffer - an arrayBuffer with the key data +// privateKeyFormat - "pkcs8" or "jwk" +// privateKey - a CryptoKey object for the keyBuffer. INITIALLY null! You must fill this in first to use it! +// algorithm - the value of the AlgorithmIdentifier parameter to provide to encrypt +// plaintext - the text to encrypt +// signature - the expected signature +function getTestVectors() { + var pkcs8 = new Uint8Array([48, 130, 4, 191, 2, 1, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 4, 130, 4, 169, 48, 130, 4, 165, 2, 1, 0, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1, 2, 130, 1, 1, 0, 139, 55, 92, 203, 135, 200, 37, 197, 255, 61, 83, 208, 9, 145, 110, 150, 65, 5, 126, 24, 82, 114, 39, 160, 122, 178, 38, 190, 16, 136, 129, 58, 59, 56, 187, 123, 72, 243, 119, 5, 81, 101, 250, 42, 147, 57, 210, 77, 198, 103, 213, 197, 186, 52, 39, 230, 164, 129, 23, 110, 172, 21, 255, 212, 144, 104, 49, 30, 28, 40, 59, 159, 58, 142, 12, 184, 9, 180, 99, 12, 80, 170, 143, 62, 69, 166, 11, 53, 158, 25, 191, 140, 187, 94, 202, 214, 78, 118, 31, 16, 149, 116, 63, 243, 106, 175, 92, 240, 236, 185, 127, 237, 173, 221, 166, 11, 91, 243, 93, 129, 26, 117, 184, 34, 35, 12, 250, 160, 25, 47, 173, 64, 84, 126, 39, 84, 72, 170, 51, 22, 191, 142, 43, 76, 224, 133, 79, 199, 112, 139, 83, 123, 162, 45, 19, 33, 11, 9, 174, 195, 122, 39, 89, 239, 192, 130, 161, 83, 27, 35, 169, 23, 48, 3, 125, 222, 78, 242, 107, 95, 150, 239, 220, 195, 159, 211, 76, 52, 90, 213, 28, 187, 228, 79, 229, 139, 138, 59, 78, 201, 151, 134, 108, 8, 109, 255, 27, 136, 49, 239, 10, 31, 234, 38, 60, 247, 218, 205, 3, 192, 76, 188, 194, 178, 121, 229, 127, 165, 185, 83, 153, 107, 251, 29, 214, 136, 23, 175, 127, 180, 44, 222, 247, 165, 41, 74, 87, 250, 194, 184, 173, 115, 159, 27, 2, 153, 2, 129, 129, 0, 251, 248, 51, 194, 198, 49, 201, 112, 36, 12, 142, 116, 133, 240, 106, 62, 162, 168, 72, 34, 81, 26, 134, 39, 221, 70, 78, 248, 175, 175, 113, 72, 209, 164, 37, 182, 184, 101, 125, 221, 82, 70, 131, 43, 142, 83, 48, 32, 197, 187, 181, 104, 133, 90, 106, 236, 62, 66, 33, 215, 147, 241, 220, 91, 47, 37, 132, 226, 65, 94, 72, 233, 162, 189, 41, 43, 19, 64, 49, 249, 156, 142, 180, 47, 192, 188, 208, 68, 155, 242, 44, 230, 222, 201, 112, 20, 239, 229, 172, 147, 235, 232, 53, 135, 118, 86, 37, 44, 187, 177, 108, 65, 91, 103, 177, 132, 210, 40, 69, 104, 162, 119, 213, 147, 53, 88, 92, 253, 2, 129, 129, 0, 214, 184, 206, 39, 199, 41, 93, 93, 22, 252, 53, 112, 237, 100, 200, 218, 147, 3, 250, 210, 148, 136, 193, 166, 94, 154, 215, 17, 249, 3, 112, 24, 125, 187, 253, 129, 49, 109, 105, 100, 139, 200, 140, 197, 200, 53, 81, 175, 255, 69, 222, 186, 207, 182, 17, 5, 247, 9, 228, 195, 8, 9, 185, 0, 49, 235, 214, 134, 36, 68, 150, 198, 246, 158, 105, 46, 189, 200, 20, 246, 66, 57, 244, 173, 21, 117, 110, 203, 120, 197, 165, 176, 153, 49, 219, 24, 48, 119, 197, 70, 163, 140, 76, 116, 56, 137, 173, 61, 62, 208, 121, 181, 98, 46, 208, 18, 15, 160, 225, 249, 59, 89, 61, 183, 216, 82, 224, 95, 2, 129, 128, 56, 135, 75, 157, 131, 247, 129, 120, 206, 45, 158, 252, 23, 92, 131, 137, 127, 214, 127, 48, 107, 191, 166, 159, 100, 238, 52, 35, 104, 206, 212, 124, 128, 195, 241, 206, 23, 122, 117, 141, 100, 186, 251, 12, 151, 134, 164, 66, 133, 250, 1, 205, 236, 53, 7, 205, 238, 125, 201, 183, 226, 178, 29, 60, 187, 204, 16, 14, 238, 153, 103, 132, 59, 5, 115, 41, 253, 204, 166, 41, 152, 237, 15, 17, 179, 140, 232, 176, 171, 199, 222, 57, 1, 124, 113, 207, 208, 174, 87, 84, 108, 85, 145, 68, 205, 208, 175, 208, 100, 95, 126, 168, 255, 7, 185, 116, 209, 237, 68, 253, 31, 142, 0, 245, 96, 191, 109, 69, 2, 129, 129, 0, 133, 41, 239, 144, 115, 207, 143, 123, 95, 249, 226, 26, 186, 223, 58, 65, 115, 211, 144, 6, 112, 223, 175, 89, 66, 106, 188, 223, 4, 147, 193, 61, 47, 29, 27, 70, 184, 36, 166, 172, 24, 148, 179, 217, 37, 37, 12, 24, 30, 52, 114, 193, 96, 120, 5, 110, 177, 154, 141, 40, 247, 31, 48, 128, 146, 117, 52, 129, 212, 148, 68, 253, 247, 140, 158, 166, 194, 68, 7, 220, 1, 142, 119, 211, 175, 239, 56, 91, 47, 247, 67, 158, 150, 35, 121, 65, 51, 45, 212, 70, 206, 190, 255, 219, 68, 4, 254, 79, 113, 89, 81, 97, 208, 22, 64, 44, 51, 77, 15, 87, 198, 26, 190, 79, 249, 244, 203, 249, 2, 129, 129, 0, 135, 216, 119, 8, 212, 103, 99, 228, 204, 190, 178, 209, 233, 113, 46, 91, 240, 33, 109, 112, 222, 148, 32, 165, 178, 6, 155, 116, 89, 185, 159, 93, 159, 127, 47, 173, 124, 215, 154, 174, 230, 122, 127, 154, 52, 67, 126, 60, 121, 168, 74, 240, 205, 141, 233, 223, 242, 104, 235, 12, 71, 147, 245, 1, 249, 136, 213, 64, 246, 211, 71, 92, 32, 121, 184, 34, 122, 35, 217, 104, 222, 196, 227, 198, 101, 3, 24, 113, 147, 69, 150, 48, 71, 43, 253, 182, 186, 29, 231, 134, 199, 151, 250, 111, 78, 166, 90, 42, 132, 25, 38, 47, 41, 103, 136, 86, 203, 115, 201, 189, 75, 200, 155, 94, 4, 27, 34, 119]); + var spki = new Uint8Array([48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1]); + + // plaintext for RSA-PSS + var plaintext = new Uint8Array([95, 77, 186, 79, 50, 12, 12, 232, 118, 114, 90, 252, 229, 251, 210, 91, 248, 62, 90, 113, 37, 160, 140, 175, 231, 60, 62, 186, 196, 33, 119, 157, 249, 213, 93, 24, 12, 58, 233, 148, 38, 69, 225, 216, 47, 238, 140, 157, 41, 75, 60, 177, 160, 138, 153, 49, 32, 27, 60, 14, 129, 252, 71, 202, 207, 131, 21, 162, 175, 102, 50, 65, 19, 195, 182, 98, 48, 195, 70, 8, 196, 244, 89, 54, 52, 206, 2, 178, 103, 54, 34, 119, 240, 168, 64, 202, 116, 188, 61, 26, 98, 54, 149, 44, 94, 215, 170, 248, 168, 254, 203, 221, 250, 117, 132, 230, 151, 140, 234, 93, 42, 91, 159, 183, 241, 180, 140, 139, 11, 229, 138, 48, 82, 2, 117, 77, 131, 118, 16, 115, 116, 121, 60, 240, 38, 170, 238, 83, 0, 114, 125, 131, 108, 215, 30, 113, 179, 69, 221, 178, 228, 68, 70, 255, 197, 185, 1, 99, 84, 19, 137, 13, 145, 14, 163, 128, 152, 74, 144, 25, 16, 49, 50, 63, 22, 219, 204, 157, 107, 225, 104, 184, 72, 133, 56, 76, 160, 62, 18, 96, 10, 193, 194, 72, 2, 138, 243, 114, 108, 201, 52, 99, 136, 46, 168, 192, 42, 171]); + + // For verification tests. Note that "salted" signatures use a salt length equal to the hash size + var signatures = { + "sha-1, no salt": new Uint8Array([31, 28, 216, 30, 203, 59, 179, 29, 242, 229, 240, 246, 76, 92, 10, 49, 12, 124, 248, 141, 25, 235, 81, 42, 80, 120, 225, 86, 216, 35, 114, 122, 248, 134, 138, 18, 229, 223, 191, 169, 118, 56, 103, 132, 186, 152, 44, 57, 146, 135, 137, 177, 52, 149, 42, 74, 40, 198, 36, 17, 119, 188, 242, 36, 143, 42, 219, 96, 7, 127, 84, 93, 209, 126, 79, 128, 155, 59, 133, 159, 212, 48, 209, 104, 30, 128, 71, 18, 109, 119, 54, 149, 25, 238, 213, 182, 24, 243, 41, 122, 87, 80, 133, 240, 201, 49, 237, 36, 140, 246, 11, 189, 126, 255, 250, 10, 140, 43, 135, 75, 167, 248, 30, 205, 107, 243, 145, 208, 31, 30, 136, 29, 130, 122, 123, 149, 223, 135, 77, 154, 218, 187, 123, 7, 241, 49, 171, 51, 20, 42, 139, 11, 109, 92, 169, 104, 86, 113, 212, 155, 152, 43, 103, 101, 25, 9, 234, 161, 123, 150, 179, 147, 224, 79, 179, 109, 151, 47, 155, 37, 143, 27, 121, 18, 61, 242, 18, 211, 153, 36, 164, 222, 174, 197, 6, 207, 100, 15, 29, 237, 208, 45, 40, 132, 95, 53, 72, 216, 72, 134, 82, 120, 142, 46, 33, 70, 243, 206, 138, 134, 165, 86, 216, 75, 69, 120, 241, 13, 162, 154, 189, 177, 118, 166, 135, 24, 204, 27, 34, 112, 176, 115, 92, 46, 92, 166, 198, 187, 10, 250, 194, 58, 91, 250, 129, 122]), + "sha-256, no salt": new Uint8Array([97, 87, 214, 104, 237, 101, 93, 151, 139, 76, 21, 140, 132, 25, 235, 128, 113, 141, 253, 252, 125, 75, 52, 53, 127, 153, 23, 233, 225, 22, 182, 243, 182, 80, 64, 201, 209, 97, 85, 192, 129, 214, 136, 122, 188, 179, 186, 79, 250, 1, 145, 228, 128, 126, 226, 6, 104, 26, 161, 212, 128, 158, 162, 13, 229, 24, 107, 119, 227, 202, 206, 208, 127, 201, 179, 215, 27, 157, 240, 172, 129, 181, 195, 39, 63, 243, 247, 79, 50, 167, 173, 52, 198, 80, 98, 163, 21, 64, 206, 211, 5, 39, 239, 164, 183, 170, 45, 39, 255, 127, 128, 83, 95, 62, 101, 206, 53, 46, 185, 225, 139, 80, 84, 65, 109, 233, 89, 53, 74, 77, 204, 203, 37, 66, 227, 58, 131, 88, 237, 166, 32, 168, 101, 61, 214, 69, 143, 86, 171, 148, 254, 225, 220, 1, 239, 66, 251, 137, 88, 170, 19, 72, 16, 228, 216, 254, 29, 212, 254, 238, 106, 240, 71, 66, 248, 13, 165, 121, 56, 117, 167, 138, 42, 76, 192, 141, 78, 10, 104, 171, 3, 241, 192, 34, 160, 232, 167, 211, 9, 96, 137, 146, 210, 78, 205, 215, 232, 241, 137, 94, 62, 92, 211, 110, 73, 144, 107, 83, 25, 50, 217, 255, 149, 134, 24, 177, 165, 15, 152, 69, 95, 81, 94, 12, 99, 16, 61, 46, 78, 22, 81, 175, 197, 102, 235, 156, 173, 30, 126, 250, 225, 169, 117, 12, 56, 128]), + "sha-384, no salt": new Uint8Array([123, 149, 170, 182, 179, 76, 9, 98, 210, 40, 64, 158, 48, 223, 155, 4, 60, 27, 11, 170, 218, 8, 231, 61, 136, 116, 34, 85, 43, 143, 21, 34, 226, 228, 43, 242, 185, 255, 44, 108, 154, 163, 235, 12, 210, 55, 6, 24, 232, 241, 163, 104, 115, 89, 94, 0, 189, 231, 90, 156, 224, 98, 236, 50, 181, 246, 57, 79, 34, 103, 163, 245, 193, 24, 64, 255, 146, 230, 225, 91, 243, 28, 197, 62, 145, 124, 168, 239, 192, 137, 95, 177, 18, 194, 239, 143, 104, 28, 187, 106, 65, 1, 82, 246, 233, 48, 202, 255, 31, 38, 14, 49, 249, 131, 84, 46, 104, 205, 21, 222, 161, 126, 211, 19, 156, 172, 115, 81, 6, 251, 5, 252, 22, 59, 46, 208, 90, 13, 237, 147, 144, 89, 161, 12, 92, 215, 97, 158, 33, 178, 210, 6, 144, 121, 148, 39, 75, 52, 164, 218, 239, 161, 206, 89, 182, 179, 25, 247, 57, 85, 160, 145, 138, 94, 35, 126, 27, 191, 218, 219, 69, 201, 7, 165, 0, 131, 87, 126, 113, 146, 129, 136, 69, 153, 91, 74, 109, 63, 241, 151, 142, 15, 154, 66, 105, 88, 83, 40, 46, 53, 195, 183, 129, 51, 179, 224, 198, 36, 18, 90, 255, 20, 161, 135, 61, 25, 143, 99, 4, 255, 236, 127, 193, 207, 42, 222, 204, 108, 209, 75, 31, 137, 177, 166, 55, 247, 46, 209, 255, 93, 231, 198, 180, 217, 101, 153]), + "sha-512, no salt": new Uint8Array([175, 27, 192, 127, 167, 10, 221, 25, 243, 206, 31, 27, 239, 141, 252, 110, 36, 175, 67, 103, 28, 251, 151, 230, 184, 105, 232, 107, 126, 240, 53, 80, 166, 88, 19, 24, 255, 246, 68, 154, 250, 139, 103, 231, 62, 42, 106, 20, 226, 6, 119, 216, 176, 103, 20, 90, 132, 66, 37, 116, 174, 12, 253, 42, 93, 255, 112, 198, 215, 233, 127, 106, 14, 22, 101, 5, 7, 158, 180, 38, 74, 67, 196, 147, 242, 235, 63, 176, 111, 172, 192, 27, 230, 7, 116, 194, 119, 100, 106, 40, 8, 18, 71, 103, 150, 34, 178, 32, 34, 126, 146, 73, 117, 72, 103, 170, 143, 225, 128, 64, 21, 196, 249, 135, 0, 152, 46, 218, 64, 232, 77, 11, 160, 51, 108, 244, 79, 88, 47, 184, 120, 19, 116, 128, 78, 143, 180, 62, 185, 213, 119, 172, 244, 114, 53, 135, 163, 154, 43, 74, 158, 22, 139, 118, 118, 50, 183, 165, 84, 247, 123, 197, 39, 40, 33, 201, 56, 192, 153, 75, 22, 47, 116, 130, 99, 111, 127, 250, 197, 100, 161, 155, 215, 51, 244, 135, 120, 1, 220, 50, 77, 196, 113, 150, 239, 18, 202, 154, 143, 73, 33, 165, 73, 108, 214, 115, 121, 53, 202, 85, 91, 115, 70, 109, 221, 129, 126, 175, 240, 63, 237, 160, 235, 45, 97, 46, 60, 219, 89, 177, 152, 158, 239, 253, 193, 129, 1, 212, 110, 86, 185, 255, 92, 145, 249, 93]), + "sha-1, salted": new Uint8Array([31, 96, 138, 113, 209, 136, 76, 254, 33, 131, 180, 144, 55, 170, 133, 85, 176, 19, 154, 138, 18, 103, 165, 197, 185, 204, 226, 7, 1, 242, 173, 75, 189, 91, 50, 151, 64, 191, 243, 26, 204, 195, 75, 249, 175, 209, 67, 154, 5, 54, 187, 50, 182, 212, 39, 210, 105, 104, 219, 201, 233, 200, 13, 33, 17, 217, 72, 196, 129, 203, 23, 49, 119, 138, 205, 49, 16, 70, 50, 65, 196, 242, 59, 62, 19, 184, 85, 209, 98, 203, 21, 56, 81, 41, 15, 217, 95, 120, 21, 25, 226, 206, 249, 55, 69, 164, 19, 207, 238, 200, 233, 79, 186, 120, 34, 183, 37, 212, 116, 67, 24, 69, 140, 246, 180, 169, 23, 182, 91, 21, 238, 111, 84, 185, 195, 145, 246, 6, 74, 158, 3, 31, 112, 9, 245, 146, 68, 156, 11, 70, 213, 69, 122, 39, 153, 203, 14, 189, 120, 161, 2, 160, 85, 238, 4, 112, 178, 96, 194, 179, 216, 255, 189, 238, 15, 212, 118, 68, 130, 32, 144, 236, 85, 174, 98, 51, 190, 16, 98, 244, 65, 196, 50, 237, 60, 39, 94, 116, 214, 32, 19, 38, 129, 236, 46, 128, 30, 155, 91, 106, 204, 26, 215, 31, 137, 53, 56, 143, 126, 44, 3, 55, 13, 18, 233, 68, 227, 65, 140, 42, 182, 59, 180, 42, 190, 27, 185, 230, 149, 48, 240, 36, 88, 186, 40, 64, 11, 54, 128, 111, 247, 141, 165, 121, 26, 206]), + "sha-256, salted": new Uint8Array([140, 61, 3, 189, 232, 196, 45, 148, 83, 99, 27, 11, 170, 200, 158, 98, 150, 218, 32, 84, 55, 19, 192, 4, 223, 53, 188, 26, 111, 174, 32, 90, 178, 191, 88, 83, 105, 104, 144, 115, 205, 238, 52, 90, 214, 226, 120, 59, 45, 218, 24, 123, 73, 121, 234, 4, 87, 70, 55, 88, 21, 110, 16, 62, 237, 208, 239, 24, 52, 211, 91, 214, 173, 84, 13, 155, 139, 34, 95, 209, 119, 14, 81, 78, 160, 175, 53, 247, 7, 242, 231, 160, 56, 43, 230, 245, 237, 157, 107, 89, 29, 83, 108, 225, 33, 91, 23, 239, 62, 235, 69, 11, 180, 138, 0, 23, 73, 124, 103, 190, 2, 64, 71, 10, 221, 210, 137, 26, 129, 168, 241, 207, 110, 128, 227, 248, 55, 254, 66, 55, 98, 146, 223, 85, 91, 139, 5, 147, 27, 105, 83, 5, 151, 250, 227, 109, 205, 1, 177, 200, 23, 103, 212, 236, 212, 202, 240, 107, 239, 192, 53, 34, 75, 221, 42, 94, 107, 137, 213, 21, 57, 35, 90, 201, 85, 112, 231, 87, 219, 215, 15, 220, 21, 4, 0, 1, 176, 123, 147, 123, 240, 20, 140, 204, 0, 95, 76, 39, 42, 207, 95, 143, 192, 150, 163, 125, 38, 32, 142, 150, 172, 52, 28, 45, 29, 33, 44, 68, 214, 213, 21, 108, 147, 79, 102, 239, 66, 253, 186, 199, 122, 32, 134, 129, 85, 11, 4, 139, 70, 110, 50, 199, 108, 122, 123, 7]), + "sha-384, salted": new Uint8Array([121, 247, 40, 75, 180, 33, 109, 230, 132, 41, 133, 78, 219, 66, 24, 239, 120, 173, 23, 64, 132, 133, 103, 55, 115, 21, 219, 136, 103, 161, 87, 51, 199, 4, 46, 139, 249, 7, 98, 230, 115, 201, 12, 14, 44, 88, 198, 197, 206, 244, 151, 86, 139, 217, 42, 109, 33, 150, 18, 196, 117, 108, 85, 250, 196, 85, 7, 248, 22, 8, 188, 39, 32, 218, 78, 237, 213, 178, 62, 31, 60, 135, 64, 198, 180, 205, 126, 76, 240, 224, 67, 66, 177, 132, 193, 17, 1, 153, 230, 80, 128, 215, 59, 152, 94, 97, 29, 102, 248, 233, 121, 144, 129, 110, 73, 23, 186, 219, 176, 66, 93, 217, 67, 131, 137, 46, 42, 169, 109, 228, 219, 13, 224, 147, 106, 238, 132, 213, 72, 42, 61, 163, 27, 39, 49, 159, 67, 131, 15, 196, 135, 3, 204, 125, 78, 174, 219, 32, 253, 48, 50, 61, 191, 63, 34, 96, 141, 181, 22, 55, 211, 179, 5, 179, 25, 121, 98, 101, 141, 128, 147, 92, 38, 109, 51, 204, 251, 41, 117, 144, 98, 31, 74, 150, 124, 114, 69, 233, 43, 1, 88, 192, 220, 234, 148, 62, 42, 206, 113, 158, 189, 177, 150, 169, 186, 231, 223, 62, 217, 204, 98, 118, 94, 39, 182, 53, 113, 116, 62, 40, 160, 83, 141, 176, 130, 92, 173, 37, 57, 235, 93, 229, 230, 163, 32, 168, 139, 87, 62, 193, 151, 44, 38, 64, 21, 48]), + "sha-512, salted": new Uint8Array([183, 79, 48, 153, 216, 7, 135, 17, 139, 31, 157, 231, 159, 194, 7, 137, 62, 13, 45, 117, 196, 17, 15, 75, 21, 155, 133, 186, 7, 214, 58, 2, 86, 252, 60, 208, 246, 108, 232, 217, 162, 227, 207, 122, 61, 90, 123, 156, 11, 239, 172, 102, 56, 137, 74, 62, 54, 206, 117, 230, 73, 238, 6, 157, 216, 221, 152, 170, 139, 96, 36, 116, 201, 139, 20, 187, 3, 73, 45, 229, 81, 169, 232, 231, 121, 52, 239, 155, 104, 69, 131, 147, 79, 33, 141, 149, 118, 190, 36, 11, 92, 79, 54, 46, 175, 94, 1, 64, 200, 234, 146, 99, 144, 133, 166, 38, 150, 83, 80, 93, 207, 160, 4, 34, 109, 185, 246, 50, 119, 101, 58, 100, 161, 130, 110, 75, 171, 177, 122, 181, 77, 216, 84, 61, 207, 28, 232, 9, 112, 109, 104, 22, 230, 167, 95, 248, 70, 163, 212, 193, 141, 17, 189, 235, 31, 49, 177, 13, 85, 163, 121, 91, 100, 150, 49, 158, 110, 117, 21, 4, 216, 106, 78, 123, 182, 83, 91, 159, 4, 21, 232, 21, 216, 199, 137, 197, 177, 227, 135, 242, 168, 192, 15, 239, 110, 50, 116, 98, 203, 126, 82, 91, 143, 148, 91, 229, 177, 114, 72, 224, 224, 164, 216, 85, 211, 151, 226, 45, 6, 124, 228, 83, 147, 115, 223, 186, 70, 209, 121, 146, 80, 175, 199, 15, 83, 80, 6, 202, 205, 39, 102, 245, 221, 207, 143, 145]) + }; + + var vectors = [ + { + name: "RSA-PSS with SHA-1 and no salt", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-PSS", saltLength: 0}, + hash: "SHA-1", + plaintext: plaintext, + signature: signatures["sha-1, no salt"] + }, + { + name: "RSA-PSS with SHA-256 and no salt", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-PSS", saltLength: 0}, + hash: "SHA-256", + plaintext: plaintext, + signature: signatures["sha-256, no salt"] + }, + { + name: "RSA-PSS with SHA-384 and no salt", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-PSS", saltLength: 0}, + hash: "SHA-384", + plaintext: plaintext, + signature: signatures["sha-384, no salt"] + }, + { + name: "RSA-PSS with SHA-512 and no salt", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-PSS", saltLength: 0}, + hash: "SHA-512", + plaintext: plaintext, + signature: signatures["sha-512, no salt"] + }, + { + name: "RSA-PSS with SHA-1, salted", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-PSS", saltLength: 20}, + hash: "SHA-1", + plaintext: plaintext, + signature: signatures["sha-1, salted"] + }, + { + name: "RSA-PSS with SHA-256, salted", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-PSS", saltLength: 32}, + hash: "SHA-256", + plaintext: plaintext, + signature: signatures["sha-256, salted"] + }, + { + name: "RSA-PSS with SHA-384, salted", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-PSS", saltLength: 48}, + hash: "SHA-384", + plaintext: plaintext, + signature: signatures["sha-384, salted"] + }, + { + name: "RSA-PSS with SHA-512, salted", + publicKeyBuffer: spki, + publicKeyFormat: "spki", + privateKey: null, + privateKeyBuffer: pkcs8, + privateKeyFormat: "pkcs8", + publicKey: null, + algorithm: {name: "RSA-PSS", saltLength: 64}, + hash: "SHA-512", + plaintext: plaintext, + signature: signatures["sha-512, salted"] + } + ]; + + return vectors; +} diff --git a/testing/web-platform/tests/WebCryptoAPI/tools/generate.py b/testing/web-platform/tests/WebCryptoAPI/tools/generate.py new file mode 100644 index 0000000000..35ba0f0031 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/tools/generate.py @@ -0,0 +1,77 @@ +# script to generate the generateKey tests + +import os + +here = os.path.dirname(__file__) + +successes_html = """<!DOCTYPE html> +<meta charset=utf-8> +<meta name="timeout" content="long"> +<title>WebCryptoAPI: generateKey() Successful Calls</title> +<link rel="author" title="Charles Engelke" href="mailto:w3c@engelke.com"> +<link rel="help" href="https://www.w3.org/TR/WebCryptoAPI/#dfn-SubtleCrypto-method-generateKey"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<script src="/WebCryptoAPI/util/helpers.js"></script> +<script src="successes.js"></script> + +<h1>generateKey Tests for Good Parameters</h1> +<p> + <strong>Warning!</strong> RSA key generation is intrinsically + very slow, so the related tests can take up to + several minutes to complete, depending on browser! +</p> + +<div id="log"></div> +<script> +run_test([%s]); +</script>""" + +failures_html = """<!DOCTYPE html> +<meta charset=utf-8> +<meta name="timeout" content="long"> +<title>WebCryptoAPI: generateKey() for Failures</title> +<link rel="author" title="Charles Engelke" href="mailto:w3c@engelke.com"> +<link rel="help" href="https://www.w3.org/TR/WebCryptoAPI/#dfn-SubtleCrypto-method-generateKey"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<script src="/WebCryptoAPI/util/helpers.js"></script> +<script src="failures.js"></script> + +<h1>generateKey Tests for Bad Parameters</h1> + +<div id="log"></div> +<script> +run_test([%s]); +</script> +""" + +successes_worker = """// META: timeout=long +importScripts("/resources/testharness.js"); +importScripts("../util/helpers.js"); +importScripts("successes.js"); + +run_test([%s]); +done();""" + +failures_worker = """// META: timeout=long +importScripts("/resources/testharness.js"); +importScripts("../util/helpers.js"); +importScripts("failures.js"); +run_test([%s]); +done();""" + +names = ["AES-CTR", "AES-CBC", "AES-GCM", "AES-KW", "HMAC", "RSASSA-PKCS1-v1_5", + "RSA-PSS", "RSA-OAEP", "ECDSA", "ECDH", "Ed25519", "Ed448", "X25519", + "X448"] + +for filename_pattern, template in [("test_successes_%s.https.html", successes_html), + ("test_failures_%s.https.html", failures_html), + ("successes_%s.https.worker.js", successes_worker), + ("failures_%s.https.worker.js", failures_worker)]: + for name in names: + path = os.path.join(here, os.pardir, "generateKey", filename_pattern % name) + with open(path, "w") as f: + f.write(template % '"%s"' % name) diff --git a/testing/web-platform/tests/WebCryptoAPI/util/helpers.js b/testing/web-platform/tests/WebCryptoAPI/util/helpers.js new file mode 100644 index 0000000000..8874f731f8 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/util/helpers.js @@ -0,0 +1,259 @@ +// +// helpers.js +// +// Helper functions used by several WebCryptoAPI tests +// + +var registeredAlgorithmNames = [ + "RSASSA-PKCS1-v1_5", + "RSA-PSS", + "RSA-OAEP", + "ECDSA", + "ECDH", + "AES-CTR", + "AES-CBC", + "AES-GCM", + "AES-KW", + "HMAC", + "SHA-1", + "SHA-256", + "SHA-384", + "SHA-512", + "HKDF-CTR", + "PBKDF2", + "Ed25519", + "Ed448", + "X25519", + "X448" +]; + + +// Treats an array as a set, and generates an array of all non-empty +// subsets (which are themselves arrays). +// +// The order of members of the "subsets" is not guaranteed. +function allNonemptySubsetsOf(arr) { + var results = []; + var firstElement; + var remainingElements; + + for(var i=0; i<arr.length; i++) { + firstElement = arr[i]; + remainingElements = arr.slice(i+1); + results.push([firstElement]); + + if (remainingElements.length > 0) { + allNonemptySubsetsOf(remainingElements).forEach(function(combination) { + combination.push(firstElement); + results.push(combination); + }); + } + } + + return results; +} + + +// Create a string representation of keyGeneration parameters for +// test names and labels. +function objectToString(obj) { + var keyValuePairs = []; + + if (Array.isArray(obj)) { + return "[" + obj.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else if (typeof obj === "object") { + Object.keys(obj).sort().forEach(function(keyName) { + keyValuePairs.push(keyName + ": " + objectToString(obj[keyName])); + }); + return "{" + keyValuePairs.join(", ") + "}"; + } else if (typeof obj === "undefined") { + return "undefined"; + } else { + return obj.toString(); + } + + var keyValuePairs = []; + + Object.keys(obj).sort().forEach(function(keyName) { + var value = obj[keyName]; + if (typeof value === "object") { + value = objectToString(value); + } else if (typeof value === "array") { + value = "[" + value.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else { + value = value.toString(); + } + + keyValuePairs.push(keyName + ": " + value); + }); + + return "{" + keyValuePairs.join(", ") + "}"; +} + +// Is key a CryptoKey object with correct algorithm, extractable, and usages? +// Is it a secret, private, or public kind of key? +function assert_goodCryptoKey(key, algorithm, extractable, usages, kind) { + var correctUsages = []; + + var registeredAlgorithmName; + registeredAlgorithmNames.forEach(function(name) { + if (name.toUpperCase() === algorithm.name.toUpperCase()) { + registeredAlgorithmName = name; + } + }); + + assert_equals(key.constructor, CryptoKey, "Is a CryptoKey"); + assert_equals(key.type, kind, "Is a " + kind + " key"); + if (key.type === "public") { + extractable = true; // public keys are always extractable + } + assert_equals(key.extractable, extractable, "Extractability is correct"); + + assert_equals(key.algorithm.name, registeredAlgorithmName, "Correct algorithm name"); + if (key.algorithm.name.toUpperCase() === "HMAC" && algorithm.length === undefined) { + switch (key.algorithm.hash.name.toUpperCase()) { + case 'SHA-1': + case 'SHA-256': + assert_equals(key.algorithm.length, 512, "Correct length"); + break; + case 'SHA-384': + case 'SHA-512': + assert_equals(key.algorithm.length, 1024, "Correct length"); + break; + default: + assert_unreached("Unrecognized hash"); + } + } else { + assert_equals(key.algorithm.length, algorithm.length, "Correct length"); + } + if (["HMAC", "RSASSA-PKCS1-v1_5", "RSA-PSS"].includes(registeredAlgorithmName)) { + assert_equals(key.algorithm.hash.name.toUpperCase(), algorithm.hash.toUpperCase(), "Correct hash function"); + } + + // usages is expected to be provided for a key pair, but we are checking + // only a single key. The publicKey and privateKey portions of a key pair + // recognize only some of the usages appropriate for a key pair. + if (key.type === "public") { + ["encrypt", "verify", "wrapKey"].forEach(function(usage) { + if (usages.includes(usage)) { + correctUsages.push(usage); + } + }); + } else if (key.type === "private") { + ["decrypt", "sign", "unwrapKey", "deriveKey", "deriveBits"].forEach(function(usage) { + if (usages.includes(usage)) { + correctUsages.push(usage); + } + }); + } else { + correctUsages = usages; + } + + assert_equals((typeof key.usages), "object", key.type + " key.usages is an object"); + assert_not_equals(key.usages, null, key.type + " key.usages isn't null"); + + // The usages parameter could have repeats, but the usages + // property of the result should not. + var usageCount = 0; + key.usages.forEach(function(usage) { + usageCount += 1; + assert_in_array(usage, correctUsages, "Has " + usage + " usage"); + }); + assert_equals(key.usages.length, usageCount, "usages property is correct"); +} + + +// The algorithm parameter is an object with a name and other +// properties. Given the name, generate all valid parameters. +function allAlgorithmSpecifiersFor(algorithmName) { + var results = []; + + // RSA key generation is slow. Test a minimal set of parameters + var hashes = ["SHA-1", "SHA-256"]; + + // EC key generation is a lot faster. Check all curves in the spec + var curves = ["P-256", "P-384", "P-521"]; + + if (algorithmName.toUpperCase().substring(0, 3) === "AES") { + // Specifier properties are name and length + [128, 192, 256].forEach(function(length) { + results.push({name: algorithmName, length: length}); + }); + } else if (algorithmName.toUpperCase() === "HMAC") { + [ + {hash: "SHA-1", length: 160}, + {hash: "SHA-256", length: 256}, + {hash: "SHA-384", length: 384}, + {hash: "SHA-512", length: 512}, + {hash: "SHA-1"}, + {hash: "SHA-256"}, + {hash: "SHA-384"}, + {hash: "SHA-512"}, + ].forEach(function(hashAlgorithm) { + results.push({name: algorithmName, ...hashAlgorithm}); + }); + } else if (algorithmName.toUpperCase().substring(0, 3) === "RSA") { + hashes.forEach(function(hashName) { + results.push({name: algorithmName, hash: hashName, modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])}); + }); + } else if (algorithmName.toUpperCase().substring(0, 2) === "EC") { + curves.forEach(function(curveName) { + results.push({name: algorithmName, namedCurve: curveName}); + }); + } else if (algorithmName.toUpperCase().substring(0, 1) === "X" || algorithmName.toUpperCase().substring(0, 2) === "ED") { + results.push({ name: algorithmName }); + } + + return results; +} + + +// Create every possible valid usages parameter, given legal +// usages. Note that an empty usages parameter is not always valid. +// +// There is an optional parameter - mandatoryUsages. If provided, +// it should be an array containing those usages of which one must be +// included. +function allValidUsages(validUsages, emptyIsValid, mandatoryUsages) { + if (typeof mandatoryUsages === "undefined") { + mandatoryUsages = []; + } + + var okaySubsets = []; + allNonemptySubsetsOf(validUsages).forEach(function(subset) { + if (mandatoryUsages.length === 0) { + okaySubsets.push(subset); + } else { + for (var i=0; i<mandatoryUsages.length; i++) { + if (subset.includes(mandatoryUsages[i])) { + okaySubsets.push(subset); + return; + } + } + } + }); + + if (emptyIsValid) { + okaySubsets.push([]); + } + + okaySubsets.push(validUsages.concat(mandatoryUsages).concat(validUsages)); // Repeated values are allowed + return okaySubsets; +} + +function unique(names) { + return [...new Set(names)]; +} + +// Algorithm name specifiers are case-insensitive. Generate several +// case variations of a given name. +function allNameVariants(name, slowTest) { + var upCaseName = name.toUpperCase(); + var lowCaseName = name.toLowerCase(); + var mixedCaseName = upCaseName.substring(0, 1) + lowCaseName.substring(1); + + // for slow tests effectively cut the amount of work in third by only + // returning one variation + if (slowTest) return [mixedCaseName]; + return unique([upCaseName, lowCaseName, mixedCaseName]); +} diff --git a/testing/web-platform/tests/WebCryptoAPI/util/worker-report-crypto-subtle-presence.js b/testing/web-platform/tests/WebCryptoAPI/util/worker-report-crypto-subtle-presence.js new file mode 100644 index 0000000000..22cca7b623 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/util/worker-report-crypto-subtle-presence.js @@ -0,0 +1,3 @@ +subtle_crypto_found = true; +if (typeof crypto.subtle === 'undefined') subtle_crypto_found = false; +postMessage({ msg_type: 'subtle_crypto_found', msg_value: subtle_crypto_found }); diff --git a/testing/web-platform/tests/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js b/testing/web-platform/tests/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js new file mode 100644 index 0000000000..65e640a258 --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js @@ -0,0 +1,513 @@ +// META: title=WebCryptoAPI: wrapKey() and unwrapKey() +// META: timeout=long + +// Tests for wrapKey and unwrapKey round tripping + + var subtle = self.crypto.subtle; + + var wrappers = []; // Things we wrap (and upwrap) keys with + var keys = []; // Things to wrap and unwrap + var ecdhPeerKey; // ECDH peer public key needed for non-extractable ECDH key comparison + + // Generate all the keys needed, then iterate over all combinations + // to test wrapping and unwrapping. + promise_test(function() { + return Promise.all([generateWrappingKeys(), generateKeysToWrap(), generateEcdhPeerKey()]) + .then(function(results) { + var promises = []; + wrappers.forEach(function(wrapper) { + keys.forEach(function(key) { + promises.push(testWrapping(wrapper, key)); + }) + }); + return Promise.all(promises); + }); + }, "setup"); + + function generateWrappingKeys() { + // There are five algorithms that can be used for wrapKey/unwrapKey. + // Generate one key with typical parameters for each kind. + // + // Note: we don't need cryptographically strong parameters for things + // like IV - just any legal value will do. + var parameters = [ + { + name: "RSA-OAEP", + generateParameters: {name: "RSA-OAEP", modulusLength: 4096, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, + wrapParameters: {name: "RSA-OAEP", label: new Uint8Array(8)} + }, + { + name: "AES-CTR", + generateParameters: {name: "AES-CTR", length: 128}, + wrapParameters: {name: "AES-CTR", counter: new Uint8Array(16), length: 64} + }, + { + name: "AES-CBC", + generateParameters: {name: "AES-CBC", length: 128}, + wrapParameters: {name: "AES-CBC", iv: new Uint8Array(16)} + }, + { + name: "AES-GCM", + generateParameters: {name: "AES-GCM", length: 128}, + wrapParameters: {name: "AES-GCM", iv: new Uint8Array(16), additionalData: new Uint8Array(16), tagLength: 64} + }, + { + name: "AES-KW", + generateParameters: {name: "AES-KW", length: 128}, + wrapParameters: {name: "AES-KW"} + } + ]; + + // Using allSettled to skip unsupported test cases. + return Promise.allSettled(parameters.map(function(params) { + return subtle.generateKey(params.generateParameters, true, ["wrapKey", "unwrapKey"]) + .then(function(key) { + var wrapper; + if (params.name === "RSA-OAEP") { // we have a key pair, not just a key + wrapper = {wrappingKey: key.publicKey, unwrappingKey: key.privateKey, parameters: params}; + } else { + wrapper = {wrappingKey: key, unwrappingKey: key, parameters: params}; + } + wrappers.push(wrapper); + return true; + }) + })); + } + + + function generateKeysToWrap() { + var parameters = [ + {algorithm: {name: "RSASSA-PKCS1-v1_5", modulusLength: 1024, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["sign"], publicUsages: ["verify"]}, + {algorithm: {name: "RSA-PSS", modulusLength: 1024, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["sign"], publicUsages: ["verify"]}, + {algorithm: {name: "RSA-OAEP", modulusLength: 1024, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["decrypt"], publicUsages: ["encrypt"]}, + {algorithm: {name: "ECDSA", namedCurve: "P-256"}, privateUsages: ["sign"], publicUsages: ["verify"]}, + {algorithm: {name: "ECDH", namedCurve: "P-256"}, privateUsages: ["deriveBits"], publicUsages: []}, + {algorithm: {name: "Ed25519" }, privateUsages: ["sign"], publicUsages: ["verify"]}, + {algorithm: {name: "AES-CTR", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "decrypt"]}, + {algorithm: {name: "AES-KW", length: 128}, usages: ["wrapKey", "unwrapKey"]}, + {algorithm: {name: "HMAC", length: 128, hash: "SHA-256"}, usages: ["sign", "verify"]} + ]; + + // Using allSettled to skip unsupported test cases. + return Promise.allSettled(parameters.map(function(params) { + var usages; + if ("usages" in params) { + usages = params.usages; + } else { + usages = params.publicUsages.concat(params.privateUsages); + } + + return subtle.generateKey(params.algorithm, true, usages) + .then(function(result) { + if (result.constructor === CryptoKey) { + keys.push({name: params.algorithm.name, algorithm: params.algorithm, usages: params.usages, key: result}); + } else { + keys.push({name: params.algorithm.name + " public key", algorithm: params.algorithm, usages: params.publicUsages, key: result.publicKey}); + keys.push({name: params.algorithm.name + " private key", algorithm: params.algorithm, usages: params.privateUsages, key: result.privateKey}); + } + return true; + }); + })); + } + + function generateEcdhPeerKey() { + return subtle.generateKey({name: "ECDH", namedCurve: "P-256"},true,["deriveBits"]) + .then(function(result){ + ecdhPeerKey = result.publicKey; + }); + } + + // Can we successfully "round-trip" (wrap, then unwrap, a key)? + function testWrapping(wrapper, toWrap) { + var formats; + + if (toWrap.name.includes("private")) { + formats = ["pkcs8", "jwk"]; + } else if (toWrap.name.includes("public")) { + formats = ["spki", "jwk"] + } else { + formats = ["raw", "jwk"] + } + + return Promise.all(formats.map(function(fmt) { + var originalExport; + return subtle.exportKey(fmt, toWrap.key).then(function(exportedKey) { + originalExport = exportedKey; + if (wrappingIsPossible(originalExport, wrapper.parameters.name)) { + promise_test(function(test) { + return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters) + .then(function(wrappedResult) { + return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages); + }).then(function(unwrappedResult) { + assert_true(unwrappedResult.extractable, "Unwrapped result is extractable"); + return subtle.exportKey(fmt, unwrappedResult) + }).then(function(roundTripExport) { + assert_true(equalExport(originalExport, roundTripExport), "Post-wrap export matches original export"); + }, function(err) { + assert_unreached("Round trip for extractable key threw an error - " + err.name + ': "' + err.message + '"'); + }); + }, "Can wrap and unwrap " + toWrap.name + " keys using " + fmt + " and " + wrapper.parameters.name); + + if (canCompareNonExtractableKeys(toWrap.key)) { + promise_test(function(test){ + return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters) + .then(function(wrappedResult) { + return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages); + }).then(function(unwrappedResult){ + assert_false(unwrappedResult.extractable, "Unwrapped result is non-extractable"); + return equalKeys(toWrap.key, unwrappedResult); + }).then(function(result){ + assert_true(result, "Unwrapped key matches original"); + }).catch(function(err){ + assert_unreached("Round trip for key unwrapped non-extractable threw an error - " + err.name + ': "' + err.message + '"'); + }); + }, "Can wrap and unwrap " + toWrap.name + " keys as non-extractable using " + fmt + " and " + wrapper.parameters.name); + + if (fmt === "jwk") { + promise_test(function(test){ + var wrappedKey; + return wrapAsNonExtractableJwk(toWrap.key,wrapper).then(function(wrappedResult){ + wrappedKey = wrappedResult; + return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages); + }).then(function(unwrappedResult){ + assert_false(unwrappedResult.extractable, "Unwrapped key is non-extractable"); + return equalKeys(toWrap.key,unwrappedResult); + }).then(function(result){ + assert_true(result, "Unwrapped key matches original"); + }).catch(function(err){ + assert_unreached("Round trip for non-extractable key threw an error - " + err.name + ': "' + err.message + '"'); + }).then(function(){ + return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages); + }).then(function(unwrappedResult){ + assert_unreached("Unwrapping a non-extractable JWK as extractable should fail"); + }).catch(function(err){ + assert_equals(err.name, "DataError", "Unwrapping a non-extractable JWK as extractable fails with DataError"); + }); + }, "Can unwrap " + toWrap.name + " non-extractable keys using jwk and " + wrapper.parameters.name); + } + } + } + }); + })); + } + + // Implement key wrapping by hand to wrap a key as non-extractable JWK + function wrapAsNonExtractableJwk(key, wrapper){ + var wrappingKey = wrapper.wrappingKey, + encryptKey; + + return subtle.exportKey("jwk",wrappingKey) + .then(function(jwkWrappingKey){ + // Update the key generation parameters to work as key import parameters + var params = Object.create(wrapper.parameters.generateParameters); + if(params.name === "AES-KW") { + params.name = "AES-CBC"; + jwkWrappingKey.alg = "A"+params.length+"CBC"; + } else if (params.name === "RSA-OAEP") { + params.modulusLength = undefined; + params.publicExponent = undefined; + } + jwkWrappingKey.key_ops = ["encrypt"]; + return subtle.importKey("jwk", jwkWrappingKey, params, true, ["encrypt"]); + }).then(function(importedWrappingKey){ + encryptKey = importedWrappingKey; + return subtle.exportKey("jwk",key); + }).then(function(exportedKey){ + exportedKey.ext = false; + var jwk = JSON.stringify(exportedKey) + if (wrappingKey.algorithm.name === "AES-KW") { + return aeskw(encryptKey, str2ab(jwk.slice(0,-1) + " ".repeat(jwk.length%8 ? 8-jwk.length%8 : 0) + "}")); + } else { + return subtle.encrypt(wrapper.parameters.wrapParameters,encryptKey,str2ab(jwk)); + } + }); + } + + + // RSA-OAEP can only wrap relatively small payloads. AES-KW can only + // wrap payloads a multiple of 8 bytes long. + function wrappingIsPossible(exportedKey, algorithmName) { + if ("byteLength" in exportedKey && algorithmName === "AES-KW") { + return exportedKey.byteLength % 8 === 0; + } + + if ("byteLength" in exportedKey && algorithmName === "RSA-OAEP") { + // RSA-OAEP can only encrypt payloads with lengths shorter + // than modulusLength - 2*hashLength - 1 bytes long. For + // a 4096 bit modulus and SHA-256, that comes to + // 4096/8 - 2*(256/8) - 1 = 512 - 2*32 - 1 = 447 bytes. + return exportedKey.byteLength <= 446; + } + + if ("kty" in exportedKey && algorithmName === "AES-KW") { + return JSON.stringify(exportedKey).length % 8 == 0; + } + + if ("kty" in exportedKey && algorithmName === "RSA-OAEP") { + return JSON.stringify(exportedKey).length <= 478; + } + + return true; + } + + + // Helper methods follow: + + // Are two exported keys equal + function equalExport(originalExport, roundTripExport) { + if ("byteLength" in originalExport) { + return equalBuffers(originalExport, roundTripExport); + } else { + return equalJwk(originalExport, roundTripExport); + } + } + + // Are two array buffers the same? + function equalBuffers(a, b) { + if (a.byteLength !== b.byteLength) { + return false; + } + + var aBytes = new Uint8Array(a); + var bBytes = new Uint8Array(b); + + for (var i=0; i<a.byteLength; i++) { + if (aBytes[i] !== bBytes[i]) { + return false; + } + } + + return true; + } + + // Are two Jwk objects "the same"? That is, does the object returned include + // matching values for each property that was expected? It's okay if the + // returned object has extra methods; they aren't checked. + function equalJwk(expected, got) { + var fields = Object.keys(expected); + var fieldName; + + for(var i=0; i<fields.length; i++) { + fieldName = fields[i]; + if (!(fieldName in got)) { + return false; + } + if (objectToString(expected[fieldName]) !== objectToString(got[fieldName])) { + return false; + } + } + + return true; + } + + // Character representation of any object we may use as a parameter. + function objectToString(obj) { + var keyValuePairs = []; + + if (Array.isArray(obj)) { + return "[" + obj.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else if (typeof obj === "object") { + Object.keys(obj).sort().forEach(function(keyName) { + keyValuePairs.push(keyName + ": " + objectToString(obj[keyName])); + }); + return "{" + keyValuePairs.join(", ") + "}"; + } else if (typeof obj === "undefined") { + return "undefined"; + } else { + return obj.toString(); + } + + var keyValuePairs = []; + + Object.keys(obj).sort().forEach(function(keyName) { + var value = obj[keyName]; + if (typeof value === "object") { + value = objectToString(value); + } else if (typeof value === "array") { + value = "[" + value.map(function(elem){return objectToString(elem);}).join(", ") + "]"; + } else { + value = value.toString(); + } + + keyValuePairs.push(keyName + ": " + value); + }); + + return "{" + keyValuePairs.join(", ") + "}"; + } + + // Can we compare key values by using them + function canCompareNonExtractableKeys(key){ + if (key.usages.indexOf("decrypt") !== -1) { + return true; + } + if (key.usages.indexOf("sign") !== -1) { + return true; + } + if (key.usages.indexOf("wrapKey") !== -1) { + return true; + } + if (key.usages.indexOf("deriveBits") !== -1) { + return true; + } + return false; + } + + // Compare two keys by using them (works for non-extractable keys) + function equalKeys(expected, got){ + if ( expected.algorithm.name !== got.algorithm.name ) { + return Promise.resolve(false); + } + + var cryptParams, signParams, wrapParams, deriveParams; + switch(expected.algorithm.name){ + case "AES-CTR" : + cryptParams = {name: "AES-CTR", counter: new Uint8Array(16), length: 64}; + break; + case "AES-CBC" : + cryptParams = {name: "AES-CBC", iv: new Uint8Array(16) }; + break; + case "AES-GCM" : + cryptParams = {name: "AES-GCM", iv: new Uint8Array(16) }; + break; + case "RSA-OAEP" : + cryptParams = {name: "RSA-OAEP", label: new Uint8Array(8) }; + break; + case "RSASSA-PKCS1-v1_5" : + signParams = {name: "RSASSA-PKCS1-v1_5"}; + break; + case "RSA-PSS" : + signParams = {name: "RSA-PSS", saltLength: 32 }; + break; + case "ECDSA" : + signParams = {name: "ECDSA", hash: "SHA-256"}; + break; + case "Ed25519" : + signParams = {name: "Ed25519"}; + break; + case "HMAC" : + signParams = {name: "HMAC"}; + break; + case "AES-KW" : + wrapParams = {name: "AES-KW"}; + break; + case "ECDH" : + deriveParams = {name: "ECDH", public: ecdhPeerKey}; + break; + default: + throw new Error("Unsupported algorithm for key comparison"); + } + + if (cryptParams) { + return subtle.exportKey("jwk",expected) + .then(function(jwkExpectedKey){ + if (expected.algorithm.name === "RSA-OAEP") { + ["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; }); + } + jwkExpectedKey.key_ops = ["encrypt"]; + return subtle.importKey("jwk", jwkExpectedKey, expected.algorithm, true, ["encrypt"]); + }).then(function(expectedEncryptKey){ + return subtle.encrypt(cryptParams, expectedEncryptKey, new Uint8Array(32)); + }).then(function(encryptedData){ + return subtle.decrypt(cryptParams, got, encryptedData); + }).then(function(decryptedData){ + var result = new Uint8Array(decryptedData); + return !result.some(x => x); + }); + } else if (signParams) { + var verifyKey; + return subtle.exportKey("jwk",expected) + .then(function(jwkExpectedKey){ + if (expected.algorithm.name === "RSA-PSS" || expected.algorithm.name === "RSASSA-PKCS1-v1_5") { + ["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; }); + } + if (expected.algorithm.name === "ECDSA" || expected.algorithm.name === "Ed25519") { + delete jwkExpectedKey["d"]; + } + jwkExpectedKey.key_ops = ["verify"]; + return subtle.importKey("jwk", jwkExpectedKey, expected.algorithm, true, ["verify"]); + }).then(function(expectedVerifyKey){ + verifyKey = expectedVerifyKey; + return subtle.sign(signParams, got, new Uint8Array(32)); + }).then(function(signature){ + return subtle.verify(signParams, verifyKey, signature, new Uint8Array(32)); + }); + } else if (wrapParams) { + var aKeyToWrap, wrappedWithExpected; + return subtle.importKey("raw", new Uint8Array(16), "AES-CBC", true, ["encrypt"]) + .then(function(key){ + aKeyToWrap = key; + return subtle.wrapKey("raw", aKeyToWrap, expected, wrapParams); + }).then(function(wrapResult){ + wrappedWithExpected = Array.from((new Uint8Array(wrapResult)).values()); + return subtle.wrapKey("raw", aKeyToWrap, got, wrapParams); + }).then(function(wrapResult){ + var wrappedWithGot = Array.from((new Uint8Array(wrapResult)).values()); + return wrappedWithGot.every((x,i) => x === wrappedWithExpected[i]); + }); + } else { + var expectedDerivedBits; + return subtle.deriveBits(deriveParams, expected, 128) + .then(function(result){ + expectedDerivedBits = Array.from((new Uint8Array(result)).values()); + return subtle.deriveBits(deriveParams, got, 128); + }).then(function(result){ + var gotDerivedBits = Array.from((new Uint8Array(result)).values()); + return gotDerivedBits.every((x,i) => x === expectedDerivedBits[i]); + }); + } + } + + // Raw AES encryption + function aes( k, p ) { + return subtle.encrypt({name: "AES-CBC", iv: new Uint8Array(16) }, k, p).then(function(ciphertext){return ciphertext.slice(0,16);}); + } + + // AES Key Wrap + function aeskw(key, data) { + if (data.byteLength % 8 !== 0) { + throw new Error("AES Key Wrap data must be a multiple of 8 bytes in length"); + } + + var A = Uint8Array.from([0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0, 0, 0, 0, 0, 0, 0, 0]), + Av = new DataView(A.buffer), + R = [], + n = data.byteLength / 8; + + for(var i = 0; i<data.byteLength; i+=8) { + R.push(new Uint8Array(data.slice(i,i+8))); + } + + function aeskw_step(j, i, final, B) { + A.set(new Uint8Array(B.slice(0,8))); + Av.setUint32(4,Av.getUint32(4) ^ (n*j+i+1)); + R[i] = new Uint8Array(B.slice(8,16)); + if (final) { + R.unshift(A.slice(0,8)); + var result = new Uint8Array(R.length * 8); + R.forEach(function(Ri,i){ result.set(Ri, i*8); }); + return result; + } else { + A.set(R[(i+1)%n],8); + return aes(key,A); + } + } + + var p = new Promise(function(resolve){ + A.set(R[0],8); + resolve(aes(key,A)); + }); + + for(var j=0;j<6;++j) { + for(var i=0;i<n;++i) { + p = p.then(aeskw_step.bind(undefined, j, i,j===5 && i===(n-1))); + } + } + + return p; + } + + function str2ab(str) { return Uint8Array.from( str.split(''), function(s){return s.charCodeAt(0)} ); } + function ab2str(ab) { return String.fromCharCode.apply(null, new Uint8Array(ab)); } + |