mldsa.js (24231B)
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 [algorithm_name]_vectors.js provides the getTestVectors method 10 // for the algorithm that drives these tests. 11 var testVectors = getTestVectors(); 12 var invalidTestVectors = getInvalidTestVectors(); 13 14 // Test verification first, because signing tests rely on that working 15 testVectors.forEach(function (vector) { 16 var promise = importVectorKeys(vector, ['verify'], ['sign']).then( 17 function (vectors) { 18 var algorithm = vector.algorithmName; 19 promise_test(function (test) { 20 var operation = subtle 21 .verify(algorithm, vector.publicKey, vector.signature, vector.data) 22 .then( 23 function (is_verified) { 24 assert_true(is_verified, 'Signature verified'); 25 }, 26 function (err) { 27 assert_unreached( 28 'Verification should not throw error ' + 29 vector.name + 30 ': ' + 31 err.message + 32 "'" 33 ); 34 } 35 ); 36 37 return operation; 38 }, vector.name + ' verification'); 39 }, 40 function (err) { 41 // We need a failed test if the importVectorKey operation fails, so 42 // we know we never tested verification. 43 promise_test(function (test) { 44 assert_unreached( 45 'importVectorKeys failed for ' + 46 vector.name + 47 ". Message: ''" + 48 err.message + 49 "''" 50 ); 51 }, 'importVectorKeys step: ' + vector.name + ' verification'); 52 } 53 ); 54 55 all_promises.push(promise); 56 }); 57 58 // Test verification with an altered buffer after call 59 testVectors.forEach(function (vector) { 60 var promise = importVectorKeys(vector, ['verify'], ['sign']).then( 61 function (vectors) { 62 var algorithm = vector.algorithmName; 63 promise_test(function (test) { 64 var signature = copyBuffer(vector.signature); 65 var operation = subtle 66 .verify(algorithm, vector.publicKey, signature, vector.data) 67 .then( 68 function (is_verified) { 69 assert_true(is_verified, 'Signature verified'); 70 }, 71 function (err) { 72 assert_unreached( 73 'Verification should not throw error ' + 74 vector.name + 75 ': ' + 76 err.message + 77 "'" 78 ); 79 } 80 ); 81 82 signature[0] = 255 - signature[0]; 83 return operation; 84 }, vector.name + ' verification with altered signature after call'); 85 }, 86 function (err) { 87 promise_test(function (test) { 88 assert_unreached( 89 'importVectorKeys failed for ' + 90 vector.name + 91 ". Message: ''" + 92 err.message + 93 "''" 94 ); 95 }, 'importVectorKeys step: ' + 96 vector.name + 97 ' verification with altered signature after call'); 98 } 99 ); 100 101 all_promises.push(promise); 102 }); 103 104 // Check for successful verification even if plaintext is altered after call. 105 testVectors.forEach(function (vector) { 106 var promise = importVectorKeys(vector, ['verify'], ['sign']).then( 107 function (vectors) { 108 var algorithm = vector.algorithmName; 109 promise_test(function (test) { 110 var plaintext = copyBuffer(vector.data); 111 var operation = subtle 112 .verify(algorithm, vector.publicKey, vector.signature, plaintext) 113 .then( 114 function (is_verified) { 115 assert_true(is_verified, 'Signature verified'); 116 }, 117 function (err) { 118 assert_unreached( 119 'Verification should not throw error ' + 120 vector.name + 121 ': ' + 122 err.message + 123 "'" 124 ); 125 } 126 ); 127 128 plaintext[0] = 255 - plaintext[0]; 129 return operation; 130 }, vector.name + ' with altered plaintext after call'); 131 }, 132 function (err) { 133 promise_test(function (test) { 134 assert_unreached( 135 'importVectorKeys failed for ' + 136 vector.name + 137 ". Message: ''" + 138 err.message + 139 "''" 140 ); 141 }, 'importVectorKeys step: ' + 142 vector.name + 143 ' with altered plaintext after call'); 144 } 145 ); 146 147 all_promises.push(promise); 148 }); 149 150 // Check for failures due to using privateKey to verify. 151 testVectors.forEach(function (vector) { 152 var promise = importVectorKeys(vector, ['verify'], ['sign']).then( 153 function (vectors) { 154 var algorithm = vector.algorithmName; 155 promise_test(function (test) { 156 return subtle 157 .verify(algorithm, vector.privateKey, vector.signature, vector.data) 158 .then( 159 function (plaintext) { 160 assert_unreached( 161 'Should have thrown error for using privateKey to verify in ' + 162 vector.name + 163 ': ' + 164 err.message + 165 "'" 166 ); 167 }, 168 function (err) { 169 assert_equals( 170 err.name, 171 'InvalidAccessError', 172 "Should throw InvalidAccessError instead of '" + 173 err.message + 174 "'" 175 ); 176 } 177 ); 178 }, vector.name + ' using privateKey to verify'); 179 }, 180 function (err) { 181 promise_test(function (test) { 182 assert_unreached( 183 'importVectorKeys failed for ' + 184 vector.name + 185 ". Message: ''" + 186 err.message + 187 "''" 188 ); 189 }, 'importVectorKeys step: ' + 190 vector.name + 191 ' using privateKey to verify'); 192 } 193 ); 194 195 all_promises.push(promise); 196 }); 197 198 // Check for failures due to using publicKey to sign. 199 testVectors.forEach(function (vector) { 200 var promise = importVectorKeys(vector, ['verify'], ['sign']).then( 201 function (vectors) { 202 var algorithm = vector.algorithmName; 203 promise_test(function (test) { 204 return subtle.sign(algorithm, vector.publicKey, vector.data).then( 205 function (signature) { 206 assert_unreached( 207 'Should have thrown error for using publicKey to sign in ' + 208 vector.name + 209 ': ' + 210 err.message + 211 "'" 212 ); 213 }, 214 function (err) { 215 assert_equals( 216 err.name, 217 'InvalidAccessError', 218 "Should throw InvalidAccessError instead of '" + 219 err.message + 220 "'" 221 ); 222 } 223 ); 224 }, vector.name + ' using publicKey to sign'); 225 }, 226 function (err) { 227 promise_test(function (test) { 228 assert_unreached( 229 'importVectorKeys failed for ' + 230 vector.name + 231 ". Message: ''" + 232 err.message + 233 "''" 234 ); 235 }, 'importVectorKeys step: ' + 236 vector.name + 237 ' using publicKey to sign'); 238 } 239 ); 240 241 all_promises.push(promise); 242 }); 243 244 // Check for failures due to no "verify" usage. 245 testVectors.forEach(function (originalVector) { 246 var vector = Object.assign({}, originalVector); 247 248 var promise = importVectorKeys(vector, [], ['sign']).then( 249 function (vectors) { 250 var algorithm = vector.algorithmName; 251 promise_test(function (test) { 252 return subtle 253 .verify(algorithm, vector.publicKey, vector.signature, vector.data) 254 .then( 255 function (plaintext) { 256 assert_unreached( 257 'Should have thrown error for no verify usage in ' + 258 vector.name + 259 ': ' + 260 err.message + 261 "'" 262 ); 263 }, 264 function (err) { 265 assert_equals( 266 err.name, 267 'InvalidAccessError', 268 "Should throw InvalidAccessError instead of '" + 269 err.message + 270 "'" 271 ); 272 } 273 ); 274 }, vector.name + ' no verify usage'); 275 }, 276 function (err) { 277 promise_test(function (test) { 278 assert_unreached( 279 'importVectorKeys failed for ' + 280 vector.name + 281 ". Message: ''" + 282 err.message + 283 "''" 284 ); 285 }, 'importVectorKeys step: ' + vector.name + ' no verify usage'); 286 } 287 ); 288 289 all_promises.push(promise); 290 }); 291 292 // Check for successful signing and verification. 293 testVectors.forEach(function (vector) { 294 var promise = importVectorKeys(vector, ['verify'], ['sign']).then( 295 function (vectors) { 296 var algorithm = vector.algorithmName; 297 promise_test(function (test) { 298 return subtle.sign(algorithm, vector.privateKey, vector.data).then( 299 function (signature) { 300 // Can we verify the signature? 301 return subtle 302 .verify(algorithm, vector.publicKey, signature, vector.data) 303 .then( 304 function (is_verified) { 305 assert_true(is_verified, 'Round trip verification works'); 306 return signature; 307 }, 308 function (err) { 309 assert_unreached( 310 'verify error for test ' + 311 vector.name + 312 ': ' + 313 err.message + 314 "'" 315 ); 316 } 317 ); 318 }, 319 function (err) { 320 assert_unreached( 321 'sign error for test ' + vector.name + ": '" + err.message + "'" 322 ); 323 } 324 ); 325 }, vector.name + ' round trip'); 326 }, 327 function (err) { 328 // We need a failed test if the importVectorKey operation fails, so 329 // we know we never tested signing or verifying 330 promise_test(function (test) { 331 assert_unreached( 332 'importVectorKeys failed for ' + 333 vector.name + 334 ". Message: ''" + 335 err.message + 336 "''" 337 ); 338 }, 'importVectorKeys step: ' + vector.name + ' round trip'); 339 } 340 ); 341 342 all_promises.push(promise); 343 }); 344 345 // Test signing with the wrong algorithm 346 testVectors.forEach(function (vector) { 347 // Want to get the key for the wrong algorithm 348 var promise = subtle 349 .generateKey({ name: 'HMAC', hash: 'SHA-1' }, false, ['sign', 'verify']) 350 .then( 351 function (wrongKey) { 352 var algorithm = vector.algorithmName; 353 return importVectorKeys(vector, ['verify'], ['sign']).then( 354 function (vectors) { 355 promise_test(function (test) { 356 var operation = subtle 357 .sign(algorithm, wrongKey, vector.data) 358 .then( 359 function (signature) { 360 assert_unreached( 361 'Signing should not have succeeded for ' + vector.name 362 ); 363 }, 364 function (err) { 365 assert_equals( 366 err.name, 367 'InvalidAccessError', 368 "Should have thrown InvalidAccessError instead of '" + 369 err.message + 370 "'" 371 ); 372 } 373 ); 374 375 return operation; 376 }, vector.name + ' signing with wrong algorithm name'); 377 }, 378 function (err) { 379 // We need a failed test if the importVectorKey operation fails, so 380 // we know we never tested verification. 381 promise_test(function (test) { 382 assert_unreached( 383 'importVectorKeys failed for ' + 384 vector.name + 385 ". Message: ''" + 386 err.message + 387 "''" 388 ); 389 }, 'importVectorKeys step: ' + 390 vector.name + 391 ' signing with wrong algorithm name'); 392 } 393 ); 394 }, 395 function (err) { 396 promise_test(function (test) { 397 assert_unreached( 398 'Generate wrong key for test ' + 399 vector.name + 400 " failed: '" + 401 err.message + 402 "'" 403 ); 404 }, 'generate wrong key step: ' + 405 vector.name + 406 ' signing with wrong algorithm name'); 407 } 408 ); 409 410 all_promises.push(promise); 411 }); 412 413 // Test verification with the wrong algorithm 414 testVectors.forEach(function (vector) { 415 // Want to get the key for the wrong algorithm 416 var promise = subtle 417 .generateKey({ name: 'HMAC', hash: 'SHA-1' }, false, ['sign', 'verify']) 418 .then( 419 function (wrongKey) { 420 return importVectorKeys(vector, ['verify'], ['sign']).then( 421 function (vectors) { 422 var algorithm = vector.algorithmName; 423 promise_test(function (test) { 424 var operation = subtle 425 .verify(algorithm, wrongKey, vector.signature, vector.data) 426 .then( 427 function (signature) { 428 assert_unreached( 429 'Verifying should not have succeeded for ' + vector.name 430 ); 431 }, 432 function (err) { 433 assert_equals( 434 err.name, 435 'InvalidAccessError', 436 "Should have thrown InvalidAccessError instead of '" + 437 err.message + 438 "'" 439 ); 440 } 441 ); 442 443 return operation; 444 }, vector.name + ' verifying with wrong algorithm name'); 445 }, 446 function (err) { 447 // We need a failed test if the importVectorKey operation fails, so 448 // we know we never tested verification. 449 promise_test(function (test) { 450 assert_unreached( 451 'importVectorKeys failed for ' + 452 vector.name + 453 ". Message: ''" + 454 err.message + 455 "''" 456 ); 457 }, 'importVectorKeys step: ' + 458 vector.name + 459 ' verifying with wrong algorithm name'); 460 } 461 ); 462 }, 463 function (err) { 464 promise_test(function (test) { 465 assert_unreached( 466 'Generate wrong key for test ' + 467 vector.name + 468 " failed: '" + 469 err.message + 470 "'" 471 ); 472 }, 'generate wrong key step: ' + 473 vector.name + 474 ' verifying with wrong algorithm name'); 475 } 476 ); 477 478 all_promises.push(promise); 479 }); 480 481 // Test verification fails with wrong signature 482 testVectors.forEach(function (vector) { 483 var promise = importVectorKeys(vector, ['verify'], ['sign']).then( 484 function (vectors) { 485 var algorithm = vector.algorithmName; 486 var signature = copyBuffer(vector.signature); 487 signature[0] = 255 - signature[0]; 488 promise_test(function (test) { 489 var operation = subtle 490 .verify(algorithm, vector.publicKey, signature, vector.data) 491 .then( 492 function (is_verified) { 493 assert_false(is_verified, 'Signature NOT verified'); 494 }, 495 function (err) { 496 assert_unreached( 497 'Verification should not throw error ' + 498 vector.name + 499 ': ' + 500 err.message + 501 "'" 502 ); 503 } 504 ); 505 506 return operation; 507 }, vector.name + ' verification failure due to altered signature'); 508 }, 509 function (err) { 510 // We need a failed test if the importVectorKey operation fails, so 511 // we know we never tested verification. 512 promise_test(function (test) { 513 assert_unreached( 514 'importVectorKeys failed for ' + 515 vector.name + 516 ". Message: ''" + 517 err.message + 518 "''" 519 ); 520 }, 'importVectorKeys step: ' + 521 vector.name + 522 ' verification failure due to altered signature'); 523 } 524 ); 525 526 all_promises.push(promise); 527 }); 528 529 // Test verification fails with short (odd length) signature 530 testVectors.forEach(function (vector) { 531 var promise = importVectorKeys(vector, ['verify'], ['sign']).then( 532 function (vectors) { 533 var algorithm = vector.algorithmName; 534 var signature = vector.signature.slice(1); // Skip the first byte 535 promise_test(function (test) { 536 var operation = subtle 537 .verify(algorithm, vector.publicKey, signature, vector.data) 538 .then( 539 function (is_verified) { 540 assert_false(is_verified, 'Signature NOT verified'); 541 }, 542 function (err) { 543 assert_unreached( 544 'Verification should not throw error ' + 545 vector.name + 546 ': ' + 547 err.message + 548 "'" 549 ); 550 } 551 ); 552 553 return operation; 554 }, vector.name + ' verification failure due to shortened signature'); 555 }, 556 function (err) { 557 // We need a failed test if the importVectorKey operation fails, so 558 // we know we never tested verification. 559 promise_test(function (test) { 560 assert_unreached( 561 'importVectorKeys failed for ' + 562 vector.name + 563 ". Message: ''" + 564 err.message + 565 "''" 566 ); 567 }, 'importVectorKeys step: ' + 568 vector.name + 569 ' verification failure due to shortened signature'); 570 } 571 ); 572 573 all_promises.push(promise); 574 }); 575 576 // Test verification fails with wrong plaintext 577 testVectors.forEach(function (vector) { 578 var promise = importVectorKeys(vector, ['verify'], ['sign']).then( 579 function (vectors) { 580 var algorithm = vector.algorithmName; 581 var plaintext = copyBuffer(vector.data); 582 plaintext[0] = 255 - plaintext[0]; 583 promise_test(function (test) { 584 var operation = subtle 585 .verify(algorithm, vector.publicKey, vector.signature, plaintext) 586 .then( 587 function (is_verified) { 588 assert_false(is_verified, 'Signature NOT verified'); 589 }, 590 function (err) { 591 assert_unreached( 592 'Verification should not throw error ' + 593 vector.name + 594 ': ' + 595 err.message + 596 "'" 597 ); 598 } 599 ); 600 601 return operation; 602 }, vector.name + ' verification failure due to altered plaintext'); 603 }, 604 function (err) { 605 // We need a failed test if the importVectorKey operation fails, so 606 // we know we never tested verification. 607 promise_test(function (test) { 608 assert_unreached( 609 'importVectorKeys failed for ' + 610 vector.name + 611 ". Message: ''" + 612 err.message + 613 "''" 614 ); 615 }, 'importVectorKeys step: ' + 616 vector.name + 617 ' verification failure due to altered plaintext'); 618 } 619 ); 620 621 all_promises.push(promise); 622 }); 623 624 // Test invalid signatures 625 invalidTestVectors.forEach(function (vector) { 626 var promise = importVectorKeys(vector, ['verify'], ['sign']).then( 627 function (vectors) { 628 var algorithm = vector.algorithmName; 629 promise_test(function (test) { 630 var operation = subtle 631 .verify(algorithm, vector.publicKey, vector.signature, vector.data) 632 .then( 633 function (is_verified) { 634 assert_false(is_verified, 'Signature unexpectedly verified'); 635 }, 636 function (err) { 637 assert_unreached( 638 'Verification should not throw error ' + 639 vector.name + 640 ': ' + 641 err.message + 642 "'" 643 ); 644 } 645 ); 646 647 return operation; 648 }, vector.name + ' verification'); 649 }, 650 function (err) { 651 // We need a failed test if the importVectorKey operation fails, so 652 // we know we never tested verification. 653 promise_test(function (test) { 654 assert_unreached( 655 'importVectorKeys failed for ' + 656 vector.name + 657 ". Message: ''" + 658 err.message + 659 "''" 660 ); 661 }, 'importVectorKeys step: ' + vector.name + ' verification'); 662 } 663 ); 664 665 all_promises.push(promise); 666 }); 667 668 promise_test(function () { 669 return Promise.all(all_promises) 670 .then(function () { 671 done(); 672 }) 673 .catch(function () { 674 done(); 675 }); 676 }, 'setup'); 677 678 // A test vector has all needed fields for signing and verifying, EXCEPT that the 679 // key field may be null. This function replaces that null with the Correct 680 // CryptoKey object. 681 // 682 // Returns a Promise that yields an updated vector on success. 683 function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) { 684 var publicPromise, privatePromise; 685 686 if (vector.publicKey !== null) { 687 publicPromise = new Promise(function (resolve, reject) { 688 resolve(vector); 689 }); 690 } else { 691 publicPromise = subtle 692 .importKey( 693 vector.publicKeyFormat, 694 vector.publicKeyBuffer, 695 { name: vector.algorithmName }, 696 false, 697 publicKeyUsages 698 ) 699 .then(function (key) { 700 vector.publicKey = key; 701 return vector; 702 }); // Returns a copy of the sourceBuffer it is sent. 703 } 704 705 if (vector.privateKey !== null) { 706 privatePromise = new Promise(function (resolve, reject) { 707 resolve(vector); 708 }); 709 } else { 710 privatePromise = subtle 711 .importKey( 712 vector.privateKeyFormat, 713 vector.privateKeyBuffer, 714 { name: vector.algorithmName }, 715 false, 716 privateKeyUsages 717 ) 718 .then(function (key) { 719 vector.privateKey = key; 720 return vector; 721 }); 722 } 723 724 return Promise.all([publicPromise, privatePromise]); 725 } 726 727 // Returns a copy of the sourceBuffer it is sent. 728 function copyBuffer(sourceBuffer) { 729 var source = new Uint8Array(sourceBuffer); 730 var copy = new Uint8Array(sourceBuffer.byteLength); 731 732 for (var i = 0; i < source.byteLength; i++) { 733 copy[i] = source[i]; 734 } 735 736 return copy; 737 } 738 739 function equalBuffers(a, b) { 740 if (a.byteLength !== b.byteLength) { 741 return false; 742 } 743 744 var aBytes = new Uint8Array(a); 745 var bBytes = new Uint8Array(b); 746 747 for (var i = 0; i < a.byteLength; i++) { 748 if (aBytes[i] !== bBytes[i]) { 749 return false; 750 } 751 } 752 753 return true; 754 } 755 756 return; 757 }