cfrg_curves_keys.js (13402B)
1 function define_tests_25519() { 2 return define_tests("X25519"); 3 } 4 5 function define_tests_448() { 6 return define_tests("X448"); 7 } 8 9 function define_tests(algorithmName) { 10 // May want to test prefixed implementations. 11 var subtle = self.crypto.subtle; 12 13 // Verify the derive functions perform checks against the all-zero value results, 14 // ensuring small-order points are rejected. 15 // https://www.rfc-editor.org/rfc/rfc7748#section-6.1 16 // TODO: The spec states that the check must be done on use, but there is discussion about doing it on import. 17 // https://github.com/WICG/webcrypto-secure-curves/pull/13 18 { 19 kSmallOrderPoint[algorithmName].forEach(function(test) { 20 promise_test(async() => { 21 let derived; 22 let privateKey; 23 let publicKey; 24 try { 25 privateKey = await subtle.importKey("pkcs8", pkcs8[algorithmName], 26 {name: algorithmName}, 27 false, ["deriveBits", "deriveKey"]); 28 publicKey = await subtle.importKey("spki", test.vector, 29 {name: algorithmName}, 30 false, []) 31 derived = await subtle.deriveKey({name: algorithmName, public: publicKey}, privateKey, 32 {name: "HMAC", hash: "SHA-256", length: 256}, true, 33 ["sign", "verify"]); 34 } catch (err) { 35 assert_false(privateKey === undefined, "Private key should be valid."); 36 assert_false(publicKey === undefined, "Public key should be valid."); 37 assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message + "."); 38 } 39 assert_equals(derived, undefined, "Operation succeeded, but should not have."); 40 }, algorithmName + " deriveBits checks for all-zero value result with a key of order " + test.order); 41 }); 42 } 43 44 // Ensure the keys generated by each algorithm are valid for key derivation. 45 { 46 promise_test(async() => { 47 let derived; 48 try { 49 let key = await subtle.generateKey({name: algorithmName}, true, ["deriveKey", "deriveBits"]); 50 derived = await subtle.deriveKey({name: algorithmName, public: key.publicKey}, key.privateKey, {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]); 51 } catch (err) { 52 assert_unreached("Threw an unexpected error: " + err.toString() + " -"); 53 } 54 assert_false (derived === undefined, "Key derivation failed."); 55 }, "Key derivation using a " + algorithmName + " generated keys."); 56 } 57 58 return importKeys(pkcs8, spki, sizes) 59 .then(function(results) { 60 publicKeys = results.publicKeys; 61 privateKeys = results.privateKeys; 62 noDeriveKeyKeys = results.noDeriveKeyKeys; 63 ecdhKeys = results.ecdhKeys; 64 65 { 66 // Basic success case 67 promise_test(function(test) { 68 return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) 69 .then(function(key) {return crypto.subtle.exportKey("raw", key);}) 70 .then(function(exportedKey) { 71 assert_true(equalBuffers(exportedKey, derivations[algorithmName], 8 * exportedKey.length), "Derived correct key"); 72 }, function(err) { 73 assert_unreached("deriveKey failed with error " + err.name + ": " + err.message); 74 }); 75 }, algorithmName + " good parameters"); 76 77 // Case insensitivity check 78 promise_test(function(test) { 79 return subtle.deriveKey({name: algorithmName.toLowerCase(), public: publicKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) 80 .then(function(key) {return crypto.subtle.exportKey("raw", key);}) 81 .then(function(exportedKey) { 82 assert_true(equalBuffers(exportedKey, derivations[algorithmName], 8 * exportedKey.length), "Derived correct key"); 83 }, function(err) { 84 assert_unreached("deriveKey failed with error " + err.name + ": " + err.message); 85 }); 86 }, algorithmName + " mixed case parameters"); 87 // Errors to test: 88 89 // - missing public property TypeError 90 promise_test(function(test) { 91 return subtle.deriveKey({name: algorithmName}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) 92 .then(function(key) {return crypto.subtle.exportKey("raw", key);}) 93 .then(function(exportedKey) { 94 assert_unreached("deriveKey succeeded but should have failed with TypeError"); 95 }, function(err) { 96 assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message); 97 }); 98 }, algorithmName + " missing public property"); 99 100 // - Non CryptoKey public property TypeError 101 promise_test(function(test) { 102 return subtle.deriveKey({name: algorithmName, public: {message: "Not a CryptoKey"}}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) 103 .then(function(key) {return crypto.subtle.exportKey("raw", key);}) 104 .then(function(exportedKey) { 105 assert_unreached("deriveKey succeeded but should have failed with TypeError"); 106 }, function(err) { 107 assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message); 108 }); 109 }, algorithmName + " public property of algorithm is not a CryptoKey"); 110 111 // - wrong algorithm 112 promise_test(function(test) { 113 return subtle.deriveKey({name: algorithmName, public: ecdhKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) 114 .then(function(key) {return crypto.subtle.exportKey("raw", key);}) 115 .then(function(exportedKey) { 116 assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); 117 }, function(err) { 118 assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); 119 }); 120 }, algorithmName + " mismatched algorithms"); 121 122 // - No deriveKey usage in baseKey InvalidAccessError 123 promise_test(function(test) { 124 return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, noDeriveKeyKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) 125 .then(function(key) {return crypto.subtle.exportKey("raw", key);}) 126 .then(function(exportedKey) { 127 assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); 128 }, function(err) { 129 assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); 130 }); 131 }, algorithmName + " no deriveKey usage for base key"); 132 133 // - Use public key for baseKey InvalidAccessError 134 promise_test(function(test) { 135 return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, publicKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) 136 .then(function(key) {return crypto.subtle.exportKey("raw", key);}) 137 .then(function(exportedKey) { 138 assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); 139 }, function(err) { 140 assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); 141 }); 142 }, algorithmName + " base key is not a private key"); 143 144 // - Use private key for public property InvalidAccessError 145 promise_test(function(test) { 146 return subtle.deriveKey({name: algorithmName, public: privateKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) 147 .then(function(key) {return crypto.subtle.exportKey("raw", key);}) 148 .then(function(exportedKey) { 149 assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); 150 }, function(err) { 151 assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); 152 }); 153 }, algorithmName + " public property value is a private key"); 154 155 // - Use secret key for public property InvalidAccessError 156 promise_test(function(test) { 157 return subtle.generateKey({name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]) 158 .then(function(secretKey) { 159 return subtle.deriveKey({name: algorithmName, public: secretKey}, privateKeys[algorithmName], {name: "AES-CBC", length: 256}, true, ["sign", "verify"]) 160 .then(function(key) {return crypto.subtle.exportKey("raw", key);}) 161 .then(function(exportedKey) { 162 assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError"); 163 }, function(err) { 164 assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message); 165 }); 166 }); 167 }, algorithmName + " public property value is a secret key"); 168 } 169 }); 170 171 function importKeys(pkcs8, spki, sizes) { 172 var privateKeys = {}; 173 var publicKeys = {}; 174 var noDeriveKeyKeys = {}; 175 var ecdhPublicKeys = {}; 176 177 var promises = []; 178 { 179 var operation = subtle.importKey("pkcs8", pkcs8[algorithmName], 180 {name: algorithmName}, 181 false, ["deriveBits", "deriveKey"]) 182 .then(function(key) { 183 privateKeys[algorithmName] = key; 184 }, function (err) { 185 privateKeys[algorithmName] = null; 186 }); 187 promises.push(operation); 188 } 189 { 190 var operation = subtle.importKey("pkcs8", pkcs8[algorithmName], 191 {name: algorithmName}, 192 false, ["deriveBits"]) 193 .then(function(key) { 194 noDeriveKeyKeys[algorithmName] = key; 195 }, function (err) { 196 noDeriveKeyKeys[algorithmName] = null; 197 }); 198 promises.push(operation); 199 } 200 { 201 var operation = subtle.importKey("spki", spki[algorithmName], 202 {name: algorithmName}, 203 false, []) 204 .then(function(key) { 205 publicKeys[algorithmName] = key; 206 }, function (err) { 207 publicKeys[algorithmName] = null; 208 }); 209 promises.push(operation); 210 } 211 { 212 var operation = subtle.importKey("spki", ecSPKI, 213 {name: "ECDH", namedCurve: "P-256"}, 214 false, []) 215 .then(function(key) { 216 ecdhPublicKeys[algorithmName] = key; 217 }); 218 } 219 220 return Promise.all(promises) 221 .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveKeyKeys: noDeriveKeyKeys, ecdhKeys: ecdhPublicKeys}}); 222 } 223 224 // Compares two ArrayBuffer or ArrayBufferView objects. If bitCount is 225 // omitted, the two values must be the same length and have the same contents 226 // in every byte. If bitCount is included, only that leading number of bits 227 // have to match. 228 function equalBuffers(a, b, bitCount) { 229 var remainder; 230 231 if (typeof bitCount === "undefined" && a.byteLength !== b.byteLength) { 232 return false; 233 } 234 235 var aBytes = new Uint8Array(a); 236 var bBytes = new Uint8Array(b); 237 238 var length = a.byteLength; 239 if (typeof bitCount !== "undefined") { 240 length = Math.floor(bitCount / 8); 241 } 242 243 for (var i=0; i<length; i++) { 244 if (aBytes[i] !== bBytes[i]) { 245 return false; 246 } 247 } 248 249 if (typeof bitCount !== "undefined") { 250 remainder = bitCount % 8; 251 return aBytes[length] >> (8 - remainder) === bBytes[length] >> (8 - remainder); 252 } 253 254 return true; 255 } 256 257 }