1 2function run_test() { 3 setup({explicit_done: true}); 4 5 var subtle = self.crypto.subtle; // Change to test prefixed implementations 6 7 // When are all these tests really done? When all the promises they use have resolved. 8 var all_promises = []; 9 10 // Source file hmac_vectors.js provides the getTestVectors method 11 // for the algorithm that drives these tests. 12 var testVectors = getTestVectors(); 13 14 // Test verification first, because signing tests rely on that working 15 testVectors.forEach(function(vector) { 16 var promise = importVectorKeys(vector, ["verify", "sign"]) 17 .then(function(vector) { 18 promise_test(function(test) { 19 var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, vector.plaintext) 20 .then(function(is_verified) { 21 assert_true(is_verified, "Signature verified"); 22 }, function(err) { 23 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 24 }); 25 26 return operation; 27 }, vector.name + " verification"); 28 29 }, function(err) { 30 // We need a failed test if the importVectorKey operation fails, so 31 // we know we never tested verification. 32 promise_test(function(test) { 33 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 34 }, "importVectorKeys step: " + vector.name + " verification"); 35 }); 36 37 all_promises.push(promise); 38 }); 39 40 // Test verification with an altered buffer after call 41 testVectors.forEach(function(vector) { 42 var promise = importVectorKeys(vector, ["verify", "sign"]) 43 .then(function(vector) { 44 promise_test(function(test) { 45 var signature = copyBuffer(vector.signature); 46 var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext) 47 .then(function(is_verified) { 48 assert_true(is_verified, "Signature is not verified"); 49 }, function(err) { 50 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 51 }); 52 53 signature[0] = 255 - signature[0]; 54 return operation; 55 }, vector.name + " verification with altered signature after call"); 56 }, function(err) { 57 promise_test(function(test) { 58 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 59 }, "importVectorKeys step: " + vector.name + " verification with altered signature after call"); 60 }); 61 62 all_promises.push(promise); 63 }); 64 65 // Check for successful verification even if plaintext is altered after call. 66 testVectors.forEach(function(vector) { 67 var promise = importVectorKeys(vector, ["verify", "sign"]) 68 .then(function(vector) { 69 promise_test(function(test) { 70 var plaintext = copyBuffer(vector.plaintext); 71 var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, plaintext) 72 .then(function(is_verified) { 73 assert_true(is_verified, "Signature verified"); 74 }, function(err) { 75 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 76 }); 77 78 plaintext[0] = 255 - plaintext[0]; 79 return operation; 80 }, vector.name + " with altered plaintext after call"); 81 }, function(err) { 82 promise_test(function(test) { 83 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 84 }, "importVectorKeys step: " + vector.name + " with altered plaintext"); 85 }); 86 87 all_promises.push(promise); 88 }); 89 90 // Check for failures due to no "verify" usage. 91 testVectors.forEach(function(originalVector) { 92 var vector = Object.assign({}, originalVector); 93 94 var promise = importVectorKeys(vector, ["sign"]) 95 .then(function(vector) { 96 promise_test(function(test) { 97 return subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, vector.plaintext) 98 .then(function(plaintext) { 99 assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'"); 100 }, function(err) { 101 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); 102 }); 103 }, vector.name + " no verify usage"); 104 }, function(err) { 105 promise_test(function(test) { 106 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 107 }, "importVectorKeys step: " + vector.name + " no verify usage"); 108 }); 109 110 all_promises.push(promise); 111 }); 112 113 // Check for successful signing and verification. 114 testVectors.forEach(function(vector) { 115 var promise = importVectorKeys(vector, ["verify", "sign"]) 116 .then(function(vectors) { 117 promise_test(function(test) { 118 return subtle.sign({name: "HMAC", hash: vector.hash}, vector.key, vector.plaintext) 119 .then(function(signature) { 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} 351