hmac.js (16177B)
1 function run_test() { 2 setup({explicit_done: true}); 3 4 var subtle = self.crypto.subtle; // Change to test prefixed implementations 5 6 // When are all these tests really done? When all the promises they use have resolved. 7 var all_promises = []; 8 9 // Source file hmac_vectors.js provides the getTestVectors method 10 // for the algorithm that drives these tests. 11 var testVectors = getTestVectors(); 12 13 // Test verification first, because signing tests rely on that working 14 testVectors.forEach(function(vector) { 15 var promise = importVectorKeys(vector, ["verify", "sign"]) 16 .then(function(vector) { 17 promise_test(function(test) { 18 var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, vector.plaintext) 19 .then(function(is_verified) { 20 assert_true(is_verified, "Signature verified"); 21 }, function(err) { 22 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 23 }); 24 25 return operation; 26 }, vector.name + " verification"); 27 28 }, function(err) { 29 // We need a failed test if the importVectorKey operation fails, so 30 // we know we never tested verification. 31 promise_test(function(test) { 32 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 33 }, "importVectorKeys step: " + vector.name + " verification"); 34 }); 35 36 all_promises.push(promise); 37 }); 38 39 // Test verification with an altered buffer after call 40 testVectors.forEach(function(vector) { 41 var promise = importVectorKeys(vector, ["verify", "sign"]) 42 .then(function(vector) { 43 promise_test(function(test) { 44 var signature = copyBuffer(vector.signature); 45 var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext) 46 .then(function(is_verified) { 47 assert_true(is_verified, "Signature is not verified"); 48 }, function(err) { 49 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 50 }); 51 52 signature[0] = 255 - signature[0]; 53 return operation; 54 }, vector.name + " verification with altered signature after call"); 55 }, function(err) { 56 promise_test(function(test) { 57 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 58 }, "importVectorKeys step: " + vector.name + " verification with altered signature after call"); 59 }); 60 61 all_promises.push(promise); 62 }); 63 64 // Check for successful verification even if plaintext is altered after call. 65 testVectors.forEach(function(vector) { 66 var promise = importVectorKeys(vector, ["verify", "sign"]) 67 .then(function(vector) { 68 promise_test(function(test) { 69 var plaintext = copyBuffer(vector.plaintext); 70 var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, plaintext) 71 .then(function(is_verified) { 72 assert_true(is_verified, "Signature verified"); 73 }, function(err) { 74 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 75 }); 76 77 plaintext[0] = 255 - plaintext[0]; 78 return operation; 79 }, vector.name + " with altered plaintext after call"); 80 }, function(err) { 81 promise_test(function(test) { 82 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 83 }, "importVectorKeys step: " + vector.name + " with altered plaintext"); 84 }); 85 86 all_promises.push(promise); 87 }); 88 89 // Check for failures due to no "verify" usage. 90 testVectors.forEach(function(originalVector) { 91 var vector = Object.assign({}, originalVector); 92 93 var promise = importVectorKeys(vector, ["sign"]) 94 .then(function(vector) { 95 promise_test(function(test) { 96 return subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, vector.plaintext) 97 .then(function(plaintext) { 98 assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'"); 99 }, function(err) { 100 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); 101 }); 102 }, vector.name + " no verify usage"); 103 }, function(err) { 104 promise_test(function(test) { 105 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 106 }, "importVectorKeys step: " + vector.name + " no verify usage"); 107 }); 108 109 all_promises.push(promise); 110 }); 111 112 // Check for successful signing and verification. 113 testVectors.forEach(function(vector) { 114 var promise = importVectorKeys(vector, ["verify", "sign"]) 115 .then(function(vectors) { 116 promise_test(function(test) { 117 return subtle.sign({name: "HMAC", hash: vector.hash}, vector.key, vector.plaintext) 118 .then(function(signature) { 119 assert_true(equalBuffers(signature, vector.signature), "Signing did not give the expected output"); 120 // Can we get the verify the new signature? 121 return subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext) 122 .then(function(is_verified) { 123 assert_true(is_verified, "Round trip verifies"); 124 return signature; 125 }, function(err) { 126 assert_unreached("verify error for test " + vector.name + ": " + err.message + "'"); 127 }); 128 }); 129 }, vector.name + " round trip"); 130 131 }, function(err) { 132 // We need a failed test if the importVectorKey operation fails, so 133 // we know we never tested signing or verifying 134 promise_test(function(test) { 135 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 136 }, "importVectorKeys step: " + vector.name + " round trip"); 137 }); 138 139 all_promises.push(promise); 140 }); 141 142 // Test signing with the wrong algorithm 143 testVectors.forEach(function(vector) { 144 // Want to get the key for the wrong algorithm 145 var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"]) 146 .then(function(wrongKey) { 147 return importVectorKeys(vector, ["verify", "sign"]) 148 .then(function(vectors) { 149 promise_test(function(test) { 150 var operation = subtle.sign({name: "HMAC", hash: vector.hash}, wrongKey.privateKey, vector.plaintext) 151 .then(function(signature) { 152 assert_unreached("Signing should not have succeeded for " + vector.name); 153 }, function(err) { 154 assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); 155 }); 156 157 return operation; 158 }, vector.name + " signing with wrong algorithm name"); 159 160 }, function(err) { 161 // We need a failed test if the importVectorKey operation fails, so 162 // we know we never tested verification. 163 promise_test(function(test) { 164 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 165 }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name"); 166 }); 167 }, function(err) { 168 promise_test(function(test) { 169 assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); 170 }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name"); 171 }); 172 173 all_promises.push(promise); 174 }); 175 176 // Test verification with the wrong algorithm 177 testVectors.forEach(function(vector) { 178 // Want to get the key for the wrong algorithm 179 var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"]) 180 .then(function(wrongKey) { 181 return importVectorKeys(vector, ["verify", "sign"]) 182 .then(function(vector) { 183 promise_test(function(test) { 184 var operation = subtle.verify({name: "HMAC", hash: vector.hash}, wrongKey.publicKey, vector.signature, vector.plaintext) 185 .then(function(signature) { 186 assert_unreached("Verifying should not have succeeded for " + vector.name); 187 }, function(err) { 188 assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); 189 }); 190 191 return operation; 192 }, vector.name + " verifying with wrong algorithm name"); 193 194 }, function(err) { 195 // We need a failed test if the importVectorKey operation fails, so 196 // we know we never tested verification. 197 promise_test(function(test) { 198 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 199 }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name"); 200 }); 201 }, function(err) { 202 promise_test(function(test) { 203 assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); 204 }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name"); 205 }); 206 207 all_promises.push(promise); 208 }); 209 210 // Verification should fail if the plaintext is changed 211 testVectors.forEach(function(vector) { 212 var promise = importVectorKeys(vector, ["verify", "sign"]) 213 .then(function(vector) { 214 var plaintext = copyBuffer(vector.plaintext); 215 plaintext[0] = 255 - plaintext[0]; 216 promise_test(function(test) { 217 var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, plaintext) 218 .then(function(is_verified) { 219 assert_false(is_verified, "Signature is NOT verified"); 220 }, function(err) { 221 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 222 }); 223 224 return operation; 225 }, vector.name + " verification failure due to wrong plaintext"); 226 227 }, function(err) { 228 // We need a failed test if the importVectorKey operation fails, so 229 // we know we never tested verification. 230 promise_test(function(test) { 231 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 232 }, "importVectorKeys step: " + vector.name + " verification failure due to wrong plaintext"); 233 }); 234 235 all_promises.push(promise); 236 }); 237 238 // Verification should fail if the signature is changed 239 testVectors.forEach(function(vector) { 240 var promise = importVectorKeys(vector, ["verify", "sign"]) 241 .then(function(vector) { 242 var signature = copyBuffer(vector.signature); 243 signature[0] = 255 - signature[0]; 244 promise_test(function(test) { 245 var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext) 246 .then(function(is_verified) { 247 assert_false(is_verified, "Signature is NOT verified"); 248 }, function(err) { 249 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 250 }); 251 252 return operation; 253 }, vector.name + " verification failure due to wrong signature"); 254 255 }, function(err) { 256 // We need a failed test if the importVectorKey operation fails, so 257 // we know we never tested verification. 258 promise_test(function(test) { 259 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 260 }, "importVectorKeys step: " + vector.name + " verification failure due to wrong signature"); 261 }); 262 263 all_promises.push(promise); 264 }); 265 266 // Verification should fail if the signature is wrong length 267 testVectors.forEach(function(vector) { 268 var promise = importVectorKeys(vector, ["verify", "sign"]) 269 .then(function(vector) { 270 var signature = vector.signature.slice(1); // Drop first byte 271 promise_test(function(test) { 272 var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext) 273 .then(function(is_verified) { 274 assert_false(is_verified, "Signature is NOT verified"); 275 }, function(err) { 276 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 277 }); 278 279 return operation; 280 }, vector.name + " verification failure due to short signature"); 281 282 }, function(err) { 283 // We need a failed test if the importVectorKey operation fails, so 284 // we know we never tested verification. 285 promise_test(function(test) { 286 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 287 }, "importVectorKeys step: " + vector.name + " verification failure due to short signature"); 288 }); 289 290 all_promises.push(promise); 291 }); 292 293 294 295 promise_test(function() { 296 return Promise.all(all_promises) 297 .then(function() {done();}) 298 .catch(function() {done();}) 299 }, "setup"); 300 301 // A test vector has all needed fields for signing and verifying, EXCEPT that the 302 // key field may be null. This function replaces that null with the Correct 303 // CryptoKey object. 304 // 305 // Returns a Promise that yields an updated vector on success. 306 function importVectorKeys(vector, keyUsages) { 307 if (vector.key !== null) { 308 return new Promise(function(resolve, reject) { 309 resolve(vector); 310 }); 311 } else { 312 return subtle.importKey("raw", vector.keyBuffer, {name: "HMAC", hash: vector.hash}, false, keyUsages) 313 .then(function(key) { 314 vector.key = key; 315 return vector; 316 }); 317 } 318 } 319 320 // Returns a copy of the sourceBuffer it is sent. 321 function copyBuffer(sourceBuffer) { 322 var source = new Uint8Array(sourceBuffer); 323 var copy = new Uint8Array(sourceBuffer.byteLength) 324 325 for (var i=0; i<source.byteLength; i++) { 326 copy[i] = source[i]; 327 } 328 329 return copy; 330 } 331 332 function equalBuffers(a, b) { 333 if (a.byteLength !== b.byteLength) { 334 return false; 335 } 336 337 var aBytes = new Uint8Array(a); 338 var bBytes = new Uint8Array(b); 339 340 for (var i=0; i<a.byteLength; i++) { 341 if (aBytes[i] !== bBytes[i]) { 342 return false; 343 } 344 } 345 346 return true; 347 } 348 349 return; 350 }