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 [algorithm_name]_vectors.js provides the getTestVectors method 11 // for the algorithm that drives these tests. 12 var testVectors = getTestVectors(); 13 var invalidTestVectors = getInvalidTestVectors(); 14 15 // Test verification first, because signing tests rely on that working 16 testVectors.forEach(function(vector) { 17 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 18 .then(function(vectors) { 19 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 20 promise_test(function(test) { 21 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) 22 .then(function(is_verified) { 23 assert_true(is_verified, "Signature verified"); 24 }, function(err) { 25 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 26 }); 27 28 return operation; 29 }, vector.name + " verification"); 30 31 }, function(err) { 32 // We need a failed test if the importVectorKey operation fails, so 33 // we know we never tested verification. 34 promise_test(function(test) { 35 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 36 }, "importVectorKeys step: " + vector.name + " verification"); 37 }); 38 39 all_promises.push(promise); 40 }); 41 42 // Test verification with an altered buffer after call 43 testVectors.forEach(function(vector) { 44 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 45 .then(function(vectors) { 46 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 47 promise_test(function(test) { 48 var signature = copyBuffer(vector.signature); 49 var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) 50 .then(function(is_verified) { 51 assert_true(is_verified, "Signature verified"); 52 }, function(err) { 53 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 54 }); 55 56 signature[0] = 255 - signature[0]; 57 return operation; 58 }, vector.name + " verification with altered signature after call"); 59 }, function(err) { 60 promise_test(function(test) { 61 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 62 }, "importVectorKeys step: " + vector.name + " verification with altered signature after call"); 63 }); 64 65 all_promises.push(promise); 66 }); 67 68 // Check for successful verification even if plaintext is altered after call. 69 testVectors.forEach(function(vector) { 70 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 71 .then(function(vectors) { 72 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 73 promise_test(function(test) { 74 var plaintext = copyBuffer(vector.plaintext); 75 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext) 76 .then(function(is_verified) { 77 assert_true(is_verified, "Signature verified"); 78 }, function(err) { 79 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 80 }); 81 82 plaintext[0] = 255 - plaintext[0]; 83 return operation; 84 }, vector.name + " with altered plaintext after call"); 85 }, function(err) { 86 promise_test(function(test) { 87 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 88 }, "importVectorKeys step: " + vector.name + " with altered plaintext after call"); 89 }); 90 91 all_promises.push(promise); 92 }); 93 94 // Check for failures due to using privateKey to verify. 95 testVectors.forEach(function(vector) { 96 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 97 .then(function(vectors) { 98 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 99 promise_test(function(test) { 100 return subtle.verify(algorithm, vector.privateKey, vector.signature, vector.plaintext) 101 .then(function(plaintext) { 102 assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name + ": " + err.message + "'"); 103 }, function(err) { 104 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); 105 }); 106 }, vector.name + " using privateKey to verify"); 107 108 }, function(err) { 109 promise_test(function(test) { 110 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 111 }, "importVectorKeys step: " + vector.name + " using privateKey to verify"); 112 }); 113 114 all_promises.push(promise); 115 }); 116 117 // Check for failures due to using publicKey to sign. 118 testVectors.forEach(function(vector) { 119 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 120 .then(function(vectors) { 121 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 122 promise_test(function(test) { 123 return subtle.sign(algorithm, vector.publicKey, vector.plaintext) 124 .then(function(signature) { 125 assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name + ": " + err.message + "'"); 126 }, function(err) { 127 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); 128 }); 129 }, vector.name + " using publicKey to sign"); 130 }, function(err) { 131 promise_test(function(test) { 132 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 133 }, "importVectorKeys step: " + vector.name + " using publicKey to sign"); 134 }); 135 136 all_promises.push(promise); 137 }); 138 139 // Check for failures due to no "verify" usage. 140 testVectors.forEach(function(originalVector) { 141 var vector = Object.assign({}, originalVector); 142 143 var promise = importVectorKeys(vector, [], ["sign"]) 144 .then(function(vectors) { 145 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 146 promise_test(function(test) { 147 return subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) 148 .then(function(plaintext) { 149 assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'"); 150 }, function(err) { 151 assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); 152 }); 153 }, vector.name + " no verify usage"); 154 }, function(err) { 155 promise_test(function(test) { 156 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 157 }, "importVectorKeys step: " + vector.name + " no verify usage"); 158 }); 159 160 all_promises.push(promise); 161 }); 162 163 // Check for successful signing and verification. 164 testVectors.forEach(function(vector) { 165 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 166 .then(function(vectors) { 167 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 168 promise_test(function(test) { 169 return subtle.sign(algorithm, vector.privateKey, vector.plaintext) 170 .then(function(signature) { 171 // Can we verify the signature? 172 return subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) 173 .then(function(is_verified) { 174 assert_true(is_verified, "Round trip verification works"); 175 return signature; 176 }, function(err) { 177 assert_unreached("verify error for test " + vector.name + ": " + err.message + "'"); 178 }); 179 }, function(err) { 180 assert_unreached("sign error for test " + vector.name + ": '" + err.message + "'"); 181 }); 182 }, vector.name + " round trip"); 183 184 }, function(err) { 185 // We need a failed test if the importVectorKey operation fails, so 186 // we know we never tested signing or verifying 187 promise_test(function(test) { 188 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 189 }, "importVectorKeys step: " + vector.name + " round trip"); 190 }); 191 192 all_promises.push(promise); 193 }); 194 195 // Test signing with the wrong algorithm 196 testVectors.forEach(function(vector) { 197 // Want to get the key for the wrong algorithm 198 var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) 199 .then(function(wrongKey) { 200 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 201 return importVectorKeys(vector, ["verify"], ["sign"]) 202 .then(function(vectors) { 203 promise_test(function(test) { 204 var operation = subtle.sign(algorithm, wrongKey, vector.plaintext) 205 .then(function(signature) { 206 assert_unreached("Signing should not have succeeded for " + vector.name); 207 }, function(err) { 208 assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); 209 }); 210 211 return operation; 212 }, vector.name + " signing with wrong algorithm name"); 213 214 }, function(err) { 215 // We need a failed test if the importVectorKey operation fails, so 216 // we know we never tested verification. 217 promise_test(function(test) { 218 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 219 }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name"); 220 }); 221 }, function(err) { 222 promise_test(function(test) { 223 assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); 224 }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name"); 225 }); 226 227 all_promises.push(promise); 228 }); 229 230 // Test verification with the wrong algorithm 231 testVectors.forEach(function(vector) { 232 // Want to get the key for the wrong algorithm 233 var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) 234 .then(function(wrongKey) { 235 return importVectorKeys(vector, ["verify"], ["sign"]) 236 .then(function(vectors) { 237 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 238 promise_test(function(test) { 239 var operation = subtle.verify(algorithm, wrongKey, vector.signature, vector.plaintext) 240 .then(function(signature) { 241 assert_unreached("Verifying should not have succeeded for " + vector.name); 242 }, function(err) { 243 assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); 244 }); 245 246 return operation; 247 }, vector.name + " verifying with wrong algorithm name"); 248 249 }, function(err) { 250 // We need a failed test if the importVectorKey operation fails, so 251 // we know we never tested verification. 252 promise_test(function(test) { 253 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 254 }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name"); 255 }); 256 }, function(err) { 257 promise_test(function(test) { 258 assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); 259 }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name"); 260 }); 261 262 all_promises.push(promise); 263 }); 264 265 // Test verification fails with wrong signature 266 testVectors.forEach(function(vector) { 267 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 268 .then(function(vectors) { 269 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 270 var signature = copyBuffer(vector.signature); 271 signature[0] = 255 - signature[0]; 272 promise_test(function(test) { 273 var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) 274 .then(function(is_verified) { 275 assert_false(is_verified, "Signature NOT verified"); 276 }, function(err) { 277 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 278 }); 279 280 return operation; 281 }, vector.name + " verification failure due to altered signature"); 282 283 }, function(err) { 284 // We need a failed test if the importVectorKey operation fails, so 285 // we know we never tested verification. 286 promise_test(function(test) { 287 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 288 }, "importVectorKeys step: " + vector.name + " verification failure due to altered signature"); 289 }); 290 291 all_promises.push(promise); 292 }); 293 294 // Test verification fails with wrong hash 295 testVectors.forEach(function(vector) { 296 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 297 .then(function(vectors) { 298 var hashName = "SHA-1"; 299 if (vector.hashName === "SHA-1") { 300 hashName = "SHA-256" 301 } 302 var algorithm = {name: vector.algorithmName, hash: hashName}; 303 promise_test(function(test) { 304 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) 305 .then(function(is_verified) { 306 assert_false(is_verified, "Signature NOT verified"); 307 }, function(err) { 308 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 309 }); 310 311 return operation; 312 }, vector.name + " verification failure due to wrong hash"); 313 314 }, function(err) { 315 // We need a failed test if the importVectorKey operation fails, so 316 // we know we never tested verification. 317 promise_test(function(test) { 318 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 319 }, "importVectorKeys step: " + vector.name + " verification failure due to wrong hash"); 320 }); 321 322 all_promises.push(promise); 323 }); 324 325 // Test verification fails with bad hash name 326 testVectors.forEach(function(vector) { 327 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 328 .then(function(vectors) { 329 // use the wrong name for the hash 330 var hashName = vector.hashName.substring(0, 3) + vector.hashName.substring(4); 331 var algorithm = {name: vector.algorithmName, hash: hashName}; 332 promise_test(function(test) { 333 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) 334 .then(function(is_verified) { 335 assert_unreached("Verification should throw an error"); 336 }, function(err) { 337 assert_equals(err.name, "NotSupportedError", "Correctly throws NotSupportedError for illegal hash name") 338 }); 339 340 return operation; 341 }, vector.name + " verification failure due to bad hash name"); 342 343 }, function(err) { 344 // We need a failed test if the importVectorKey operation fails, so 345 // we know we never tested verification. 346 promise_test(function(test) { 347 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 348 }, "importVectorKeys step: " + vector.name + " verification failure due to bad hash name"); 349 }); 350 351 all_promises.push(promise); 352 }); 353 354 // Test verification fails with short (odd length) signature 355 testVectors.forEach(function(vector) { 356 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 357 .then(function(vectors) { 358 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 359 var signature = vector.signature.slice(1); // Skip the first byte 360 promise_test(function(test) { 361 var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) 362 .then(function(is_verified) { 363 assert_false(is_verified, "Signature NOT verified"); 364 }, function(err) { 365 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 366 }); 367 368 return operation; 369 }, vector.name + " verification failure due to shortened signature"); 370 371 }, function(err) { 372 // We need a failed test if the importVectorKey operation fails, so 373 // we know we never tested verification. 374 promise_test(function(test) { 375 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 376 }, "importVectorKeys step: " + vector.name + " verification failure due to shortened signature"); 377 }); 378 379 all_promises.push(promise); 380 }); 381 382 // Test verification fails with wrong plaintext 383 testVectors.forEach(function(vector) { 384 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 385 .then(function(vectors) { 386 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 387 var plaintext = copyBuffer(vector.plaintext); 388 plaintext[0] = 255 - plaintext[0]; 389 promise_test(function(test) { 390 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext) 391 .then(function(is_verified) { 392 assert_false(is_verified, "Signature NOT verified"); 393 }, function(err) { 394 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 395 }); 396 397 return operation; 398 }, vector.name + " verification failure due to altered plaintext"); 399 400 }, function(err) { 401 // We need a failed test if the importVectorKey operation fails, so 402 // we know we never tested verification. 403 promise_test(function(test) { 404 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 405 }, "importVectorKeys step: " + vector.name + " verification failure due to altered plaintext"); 406 }); 407 408 all_promises.push(promise); 409 }); 410 411 // Test invalid signatures 412 invalidTestVectors.forEach(function(vector) { 413 var promise = importVectorKeys(vector, ["verify"], ["sign"]) 414 .then(function(vectors) { 415 var algorithm = {name: vector.algorithmName, hash: vector.hashName}; 416 promise_test(function(test) { 417 var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext) 418 .then(function(is_verified) { 419 assert_false(is_verified, "Signature unexpectedly verified"); 420 }, function(err) { 421 assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); 422 }); 423 424 return operation; 425 }, vector.name + " verification"); 426 427 }, function(err) { 428 // We need a failed test if the importVectorKey operation fails, so 429 // we know we never tested verification. 430 promise_test(function(test) { 431 assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); 432 }, "importVectorKeys step: " + vector.name + " verification"); 433 }); 434 435 all_promises.push(promise); 436 }); 437 438 promise_test(function() { 439 return Promise.all(all_promises) 440 .then(function() {done();}) 441 .catch(function() {done();}) 442 }, "setup"); 443 444 // A test vector has all needed fields for signing and verifying, EXCEPT that the 445 // key field may be null. This function replaces that null with the Correct 446 // CryptoKey object. 447 // 448 // Returns a Promise that yields an updated vector on success. 449 function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) { 450 var publicPromise, privatePromise; 451 452 if (vector.publicKey !== null) { 453 publicPromise = new Promise(function(resolve, reject) { 454 resolve(vector); 455 }); 456 } else { 457 publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithmName, namedCurve: vector.namedCurve}, false, publicKeyUsages) 458 .then(function(key) { 459 vector.publicKey = key; 460 return vector; 461 }); // Returns a copy of the sourceBuffer it is sent. 462 } 463 464 if (vector.privateKey !== null) { 465 privatePromise = new Promise(function(resolve, reject) { 466 resolve(vector); 467 }); 468 } else { 469 privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithmName, namedCurve: vector.namedCurve}, false, privateKeyUsages) 470 .then(function(key) { 471 vector.privateKey = key; 472 return vector; 473 }); 474 } 475 476 return Promise.all([publicPromise, privatePromise]); 477 } 478 479 // Returns a copy of the sourceBuffer it is sent. 480 function copyBuffer(sourceBuffer) { 481 var source = new Uint8Array(sourceBuffer); 482 var copy = new Uint8Array(sourceBuffer.byteLength) 483 484 for (var i=0; i<source.byteLength; i++) { 485 copy[i] = source[i]; 486 } 487 488 return copy; 489 } 490 491 function equalBuffers(a, b) { 492 if (a.byteLength !== b.byteLength) { 493 return false; 494 } 495 496 var aBytes = new Uint8Array(a); 497 var bBytes = new Uint8Array(b); 498 499 for (var i=0; i<a.byteLength; i++) { 500 if (aBytes[i] !== bBytes[i]) { 501 return false; 502 } 503 } 504 505 return true; 506 } 507 508 return; 509} 510