tor-browser

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

cfrg_curves_bits.js (13119B)


      1 function define_tests_25519() {
      2    return define_tests("X25519");
      3 }
      4 
      5 function define_tests_448() {
      6    return define_tests("X448");
      7 }
      8 
      9 function define_tests(algorithmName) {
     10  // May want to test prefixed implementations.
     11  var subtle = self.crypto.subtle;
     12 
     13  // Verify the derive functions perform checks against the all-zero value results,
     14  // ensuring small-order points are rejected.
     15  // https://www.rfc-editor.org/rfc/rfc7748#section-6.1
     16  {
     17      kSmallOrderPoint[algorithmName].forEach(function(test) {
     18          promise_test(async() => {
     19              let derived;
     20              let privateKey;
     21              let publicKey;
     22              try {
     23                  privateKey = await subtle.importKey("pkcs8", pkcs8[algorithmName],
     24                                                  {name: algorithmName},
     25                                                  false, ["deriveBits", "deriveKey"]);
     26                  publicKey = await subtle.importKey("spki", test.vector,
     27                                                 {name: algorithmName},
     28                                                 false, [])
     29                  derived = await subtle.deriveBits({name: algorithmName, public: publicKey}, privateKey, 8 * sizes[algorithmName]);
     30              } catch (err) {
     31                  assert_true(privateKey !== undefined, "Private key should be valid.");
     32                  assert_true(publicKey !== undefined, "Public key should be valid.");
     33                  assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message + ".");
     34              }
     35              assert_equals(derived, undefined, "Operation succeeded, but should not have.");
     36          }, algorithmName + " key derivation checks for all-zero value result with a key of order " + test.order);
     37      });
     38  }
     39 
     40  return importKeys(pkcs8, spki, sizes)
     41  .then(function(results) {
     42      publicKeys = results.publicKeys;
     43      privateKeys = results.privateKeys;
     44      noDeriveBitsKeys = results.noDeriveBitsKeys;
     45      ecdhKeys = results.ecdhKeys;
     46 
     47      {
     48          // Basic success case
     49          promise_test(function(test) {
     50              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName])
     51              .then(function(derivation) {
     52                  assert_true(equalBuffers(derivation, derivations[algorithmName]), "Derived correct bits");
     53              }, function(err) {
     54                  assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
     55              });
     56          }, algorithmName + " good parameters");
     57 
     58          // Case insensitivity check
     59          promise_test(function(test) {
     60              return subtle.deriveBits({name: algorithmName.toLowerCase(), public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName])
     61              .then(function(derivation) {
     62                  assert_true(equalBuffers(derivation, derivations[algorithmName]), "Derived correct bits");
     63              }, function(err) {
     64                  assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
     65              });
     66          }, algorithmName + " mixed case parameters");
     67 
     68          // Shorter than entire derivation per algorithm
     69          promise_test(function(test) {
     70              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName] - 32)
     71              .then(function(derivation) {
     72                  assert_true(equalBuffers(derivation, derivations[algorithmName], 8 * sizes[algorithmName] - 32), "Derived correct bits");
     73              }, function(err) {
     74                  assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
     75              });
     76          }, algorithmName + " short result");
     77 
     78          // Non-multiple of 8
     79          promise_test(function(test) {
     80              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName] - 11)
     81              .then(function(derivation) {
     82                  assert_true(equalBuffers(derivation, derivations[algorithmName], 8 * sizes[algorithmName] - 11), "Derived correct bits");
     83              }, function(err) {
     84                  assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
     85              });
     86          }, algorithmName + " non-multiple of 8 bits");
     87 
     88          // Errors to test:
     89 
     90          // - missing public property TypeError
     91          promise_test(function(test) {
     92              return subtle.deriveBits({name: algorithmName}, privateKeys[algorithmName], 8 * sizes[algorithmName])
     93              .then(function(derivation) {
     94                  assert_unreached("deriveBits succeeded but should have failed with TypeError");
     95              }, function(err) {
     96                  assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
     97              });
     98          }, algorithmName + " missing public property");
     99 
    100          // - Non CryptoKey public property TypeError
    101          promise_test(function(test) {
    102              return subtle.deriveBits({name: algorithmName, public: {message: "Not a CryptoKey"}}, privateKeys[algorithmName], 8 * sizes[algorithmName])
    103              .then(function(derivation) {
    104                  assert_unreached("deriveBits succeeded but should have failed with TypeError");
    105              }, function(err) {
    106                  assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
    107              });
    108          }, algorithmName + " public property of algorithm is not a CryptoKey");
    109 
    110          // - wrong algorithm
    111          promise_test(function(test) {
    112              return subtle.deriveBits({name: algorithmName, public: ecdhKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName])
    113              .then(function(derivation) {
    114                  assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
    115              }, function(err) {
    116                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
    117              });
    118          }, algorithmName + " mismatched algorithms");
    119 
    120          // - No deriveBits usage in baseKey InvalidAccessError
    121          promise_test(function(test) {
    122              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, noDeriveBitsKeys[algorithmName], 8 * sizes[algorithmName])
    123              .then(function(derivation) {
    124                  assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
    125              }, function(err) {
    126                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
    127              });
    128          }, algorithmName + " no deriveBits usage for base key");
    129 
    130          // - Use public key for baseKey InvalidAccessError
    131          promise_test(function(test) {
    132              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, publicKeys[algorithmName], 8 * sizes[algorithmName])
    133              .then(function(derivation) {
    134                  assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
    135              }, function(err) {
    136                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
    137              });
    138          }, algorithmName + " base key is not a private key");
    139 
    140          // - Use private key for public property InvalidAccessError
    141          promise_test(function(test) {
    142              return subtle.deriveBits({name: algorithmName, public: privateKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName])
    143              .then(function(derivation) {
    144                  assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
    145              }, function(err) {
    146                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
    147              });
    148          }, algorithmName + " public property value is a private key");
    149 
    150          // - Use secret key for public property InvalidAccessError
    151          promise_test(function(test) {
    152              return subtle.generateKey({name: "AES-CBC", length: 128}, true, ["encrypt", "decrypt"])
    153              .then(function(secretKey) {
    154                  return subtle.deriveBits({name: algorithmName, public: secretKey}, privateKeys[algorithmName], 8 * sizes[algorithmName])
    155                  .then(function(derivation) {
    156                      assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
    157                  }, function(err) {
    158                      assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
    159                  });
    160              });
    161          }, algorithmName + " public property value is a secret key");
    162 
    163          // - Length greater than possible for particular curves OperationError
    164          promise_test(function(test) {
    165              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName] + 8)
    166              .then(function(derivation) {
    167                  assert_unreached("deriveBits succeeded but should have failed with OperationError");
    168              }, function(err) {
    169                  assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message);
    170              });
    171          }, algorithmName + " asking for too many bits");
    172      }
    173  });
    174 
    175  function importKeys(pkcs8, spki, sizes) {
    176      var privateKeys = {};
    177      var publicKeys = {};
    178      var noDeriveBitsKeys = {};
    179      var ecdhPublicKeys = {};
    180 
    181      var promises = [];
    182      {
    183          var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
    184                                          {name: algorithmName},
    185                                          false, ["deriveBits", "deriveKey"])
    186                          .then(function(key) {
    187                              privateKeys[algorithmName] = key;
    188                            }, function (err) {
    189                              privateKeys[algorithmName] = null;
    190                          });
    191          promises.push(operation);
    192      }
    193      {
    194          var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
    195                                          {name: algorithmName},
    196                                          false, ["deriveKey"])
    197                          .then(function(key) {
    198                              noDeriveBitsKeys[algorithmName] = key;
    199                            }, function (err) {
    200                              noDeriveBitsKeys[algorithmName] = null;
    201                          });
    202          promises.push(operation);
    203      }
    204      {
    205          var operation = subtle.importKey("spki", spki[algorithmName],
    206                                          {name: algorithmName},
    207                                          false, [])
    208                          .then(function(key) {
    209                              publicKeys[algorithmName] = key;
    210                            }, function (err) {
    211                              publicKeys[algorithmName] = null;
    212                          });
    213          promises.push(operation);
    214      }
    215      {
    216          var operation = subtle.importKey("spki", ecSPKI,
    217                                           {name: "ECDH", namedCurve: "P-256"},
    218                                           false, [])
    219                          .then(function(key) {
    220                              ecdhPublicKeys[algorithmName] = key;
    221                          });
    222      }
    223      return Promise.all(promises)
    224             .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveBitsKeys: noDeriveBitsKeys, ecdhKeys: ecdhPublicKeys}});
    225  }
    226 
    227  // Compares two ArrayBuffer or ArrayBufferView objects. If bitCount is
    228  // omitted, the two values must be the same length and have the same contents
    229  // in every byte. If bitCount is included, only that leading number of bits
    230  // have to match.
    231  function equalBuffers(a, b, bitCount) {
    232      var remainder;
    233 
    234      if (typeof bitCount === "undefined" && a.byteLength !== b.byteLength) {
    235          return false;
    236      }
    237 
    238      var aBytes = new Uint8Array(a);
    239      var bBytes = new Uint8Array(b);
    240 
    241      var length = a.byteLength;
    242      if (typeof bitCount !== "undefined") {
    243          length = Math.floor(bitCount / 8);
    244      }
    245 
    246      for (var i=0; i<length; i++) {
    247          if (aBytes[i] !== bBytes[i]) {
    248              return false;
    249          }
    250      }
    251 
    252      if (typeof bitCount !== "undefined") {
    253          remainder = bitCount % 8;
    254          return aBytes[length] >> (8 - remainder) === bBytes[length] >> (8 - remainder);
    255      }
    256 
    257      return true;
    258  }
    259 
    260 }