tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }