| |
| function define_tests() { |
| // May want to test prefixed implementations. |
| var subtle = self.crypto.subtle; |
| |
| // Verify the derive functions perform checks against the all-zero value results, |
| // ensuring small-order points are rejected. |
| // https://www.rfc-editor.org/rfc/rfc7748#section-6.1 |
| // TODO: The spec states that the check must be done on use, but there is discussion about doing it on import. |
| // https://github.com/WICG/webcrypto-secure-curves/pull/13 |
| Object.keys(kSmallOrderPoint).forEach(function(algorithmName) { |
| kSmallOrderPoint[algorithmName].forEach(function(test) { |
| promise_test(async() => { |
| let derived; |
| let privateKey; |
| let publicKey; |
| try { |
| privateKey = await subtle.importKey("pkcs8", pkcs8[algorithmName], |
| {name: algorithmName}, |
| false, ["deriveBits", "deriveKey"]); |
| publicKey = await subtle.importKey("spki", test.vector, |
| {name: algorithmName}, |
| false, []) |
| derived = await subtle.deriveKey({name: algorithmName, public: publicKey}, privateKey, |
| {name: "HMAC", hash: "SHA-256", length: 256}, true, |
| ["sign", "verify"]); |
| } catch (err) { |
| assert_false(privateKey === undefined, "Private key should be valid."); |
| assert_false(publicKey === undefined, "Public key should be valid."); |
| assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message + "."); |
| } |
| assert_equals(derived, undefined, "Operation succeeded, but should not have."); |
| }, algorithmName + " deriveBits checks for all-zero value result with a key of order " + test.order); |
| }); |
| }); |
| |
| // Ensure the keys generated by each algorithm are valid for key derivation. |
| Object.keys(sizes).forEach(function(algorithmName) { |
| promise_test(async() => { |
| let derived; |
| try { |
| let key = await subtle.generateKey({name: algorithmName}, true, ["deriveKey", "deriveBits"]); |
| derived = await subtle.deriveKey({name: algorithmName, public: key.publicKey}, key.privateKey, {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]); |
| } catch (err) { |
| assert_unreached("Threw an unexpected error: " + err.toString() + " -"); |
| } |
| assert_false (derived === undefined, "Key derivation failed."); |
| }, "Key derivation using a " + algorithmName + " generated keys."); |
| }); |
| |
| 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; |
| } |
| |
| } |