tor-browser

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

cfrg_curves_keys.js (13402B)


      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  // TODO: The spec states that the check must be done on use, but there is discussion about doing it on import.
     17  // https://github.com/WICG/webcrypto-secure-curves/pull/13
     18  {
     19      kSmallOrderPoint[algorithmName].forEach(function(test) {
     20          promise_test(async() => {
     21              let derived;
     22              let privateKey;
     23              let publicKey;
     24              try {
     25                  privateKey = await subtle.importKey("pkcs8", pkcs8[algorithmName],
     26                                                  {name: algorithmName},
     27                                                  false, ["deriveBits", "deriveKey"]);
     28                  publicKey = await subtle.importKey("spki", test.vector,
     29                                                 {name: algorithmName},
     30                                                 false, [])
     31                  derived = await subtle.deriveKey({name: algorithmName, public: publicKey}, privateKey,
     32                                                   {name: "HMAC", hash: "SHA-256", length: 256}, true,
     33                                                   ["sign", "verify"]);
     34              } catch (err) {
     35                  assert_false(privateKey === undefined, "Private key should be valid.");
     36                  assert_false(publicKey === undefined, "Public key should be valid.");
     37                  assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message + ".");
     38              }
     39              assert_equals(derived, undefined, "Operation succeeded, but should not have.");
     40          }, algorithmName + " deriveBits checks for all-zero value result with a key of order " + test.order);
     41      });
     42  }
     43 
     44  // Ensure the keys generated by each algorithm are valid for key derivation.
     45  {
     46      promise_test(async() => {
     47          let derived;
     48          try {
     49              let key = await subtle.generateKey({name: algorithmName}, true, ["deriveKey", "deriveBits"]);
     50              derived = await subtle.deriveKey({name: algorithmName, public: key.publicKey}, key.privateKey, {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]);
     51          } catch (err) {
     52              assert_unreached("Threw an unexpected error: " + err.toString() + " -");
     53          }
     54          assert_false (derived === undefined, "Key derivation failed.");
     55      }, "Key derivation using a " + algorithmName + " generated keys.");
     56  }
     57 
     58  return importKeys(pkcs8, spki, sizes)
     59  .then(function(results) {
     60      publicKeys = results.publicKeys;
     61      privateKeys = results.privateKeys;
     62      noDeriveKeyKeys = results.noDeriveKeyKeys;
     63      ecdhKeys = results.ecdhKeys;
     64 
     65      {
     66          // Basic success case
     67          promise_test(function(test) {
     68              return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
     69              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
     70              .then(function(exportedKey) {
     71                  assert_true(equalBuffers(exportedKey, derivations[algorithmName], 8 * exportedKey.length), "Derived correct key");
     72              }, function(err) {
     73                  assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
     74              });
     75          }, algorithmName + " good parameters");
     76 
     77          // Case insensitivity check
     78          promise_test(function(test) {
     79              return subtle.deriveKey({name: algorithmName.toLowerCase(), public: publicKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
     80              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
     81              .then(function(exportedKey) {
     82                  assert_true(equalBuffers(exportedKey, derivations[algorithmName], 8 * exportedKey.length), "Derived correct key");
     83              }, function(err) {
     84                  assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
     85              });
     86          }, algorithmName + " mixed case parameters");
     87          // Errors to test:
     88 
     89          // - missing public property TypeError
     90          promise_test(function(test) {
     91              return subtle.deriveKey({name: algorithmName}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
     92              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
     93              .then(function(exportedKey) {
     94                  assert_unreached("deriveKey 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.deriveKey({name: algorithmName, public: {message: "Not a CryptoKey"}}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
    103              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
    104              .then(function(exportedKey) {
    105                  assert_unreached("deriveKey succeeded but should have failed with TypeError");
    106              }, function(err) {
    107                  assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
    108              });
    109          }, algorithmName + " public property of algorithm is not a CryptoKey");
    110 
    111          // - wrong algorithm
    112          promise_test(function(test) {
    113              return subtle.deriveKey({name: algorithmName, public: ecdhKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
    114              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
    115              .then(function(exportedKey) {
    116                  assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
    117              }, function(err) {
    118                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
    119              });
    120          }, algorithmName + " mismatched algorithms");
    121 
    122          // - No deriveKey usage in baseKey InvalidAccessError
    123          promise_test(function(test) {
    124              return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, noDeriveKeyKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
    125              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
    126              .then(function(exportedKey) {
    127                  assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
    128              }, function(err) {
    129                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
    130              });
    131          }, algorithmName + " no deriveKey usage for base key");
    132 
    133          // - Use public key for baseKey InvalidAccessError
    134          promise_test(function(test) {
    135              return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, publicKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
    136              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
    137              .then(function(exportedKey) {
    138                  assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
    139              }, function(err) {
    140                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
    141              });
    142          }, algorithmName + " base key is not a private key");
    143 
    144          // - Use private key for public property InvalidAccessError
    145          promise_test(function(test) {
    146              return subtle.deriveKey({name: algorithmName, public: privateKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
    147              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
    148              .then(function(exportedKey) {
    149                  assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
    150              }, function(err) {
    151                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
    152              });
    153          }, algorithmName + " public property value is a private key");
    154 
    155          // - Use secret key for public property InvalidAccessError
    156          promise_test(function(test) {
    157              return subtle.generateKey({name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
    158              .then(function(secretKey) {
    159                  return subtle.deriveKey({name: algorithmName, public: secretKey}, privateKeys[algorithmName], {name: "AES-CBC", length: 256}, true, ["sign", "verify"])
    160                  .then(function(key) {return crypto.subtle.exportKey("raw", key);})
    161                  .then(function(exportedKey) {
    162                      assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
    163                  }, function(err) {
    164                      assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
    165                  });
    166              });
    167          }, algorithmName + " public property value is a secret key");
    168      }
    169  });
    170 
    171  function importKeys(pkcs8, spki, sizes) {
    172      var privateKeys = {};
    173      var publicKeys = {};
    174      var noDeriveKeyKeys = {};
    175      var ecdhPublicKeys = {};
    176 
    177      var promises = [];
    178      {
    179          var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
    180                                          {name: algorithmName},
    181                                          false, ["deriveBits", "deriveKey"])
    182                          .then(function(key) {
    183                              privateKeys[algorithmName] = key;
    184                          }, function (err) {
    185                            privateKeys[algorithmName] = null;
    186                          });
    187          promises.push(operation);
    188      }
    189      {
    190          var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
    191                                          {name: algorithmName},
    192                                          false, ["deriveBits"])
    193                          .then(function(key) {
    194                              noDeriveKeyKeys[algorithmName] = key;
    195                          }, function (err) {
    196                            noDeriveKeyKeys[algorithmName] = null;
    197                          });
    198          promises.push(operation);
    199      }
    200      {
    201          var operation = subtle.importKey("spki", spki[algorithmName],
    202                                          {name: algorithmName},
    203                                          false, [])
    204                          .then(function(key) {
    205                              publicKeys[algorithmName] = key;
    206                          }, function (err) {
    207                            publicKeys[algorithmName] = null;
    208                          });
    209          promises.push(operation);
    210      }
    211      {
    212          var operation = subtle.importKey("spki", ecSPKI,
    213                                           {name: "ECDH", namedCurve: "P-256"},
    214                                           false, [])
    215                          .then(function(key) {
    216                              ecdhPublicKeys[algorithmName] = key;
    217                          });
    218      }
    219 
    220      return Promise.all(promises)
    221             .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveKeyKeys: noDeriveKeyKeys, ecdhKeys: ecdhPublicKeys}});
    222  }
    223 
    224  // Compares two ArrayBuffer or ArrayBufferView objects. If bitCount is
    225  // omitted, the two values must be the same length and have the same contents
    226  // in every byte. If bitCount is included, only that leading number of bits
    227  // have to match.
    228  function equalBuffers(a, b, bitCount) {
    229      var remainder;
    230 
    231      if (typeof bitCount === "undefined" && a.byteLength !== b.byteLength) {
    232          return false;
    233      }
    234 
    235      var aBytes = new Uint8Array(a);
    236      var bBytes = new Uint8Array(b);
    237 
    238      var length = a.byteLength;
    239      if (typeof bitCount !== "undefined") {
    240          length = Math.floor(bitCount / 8);
    241      }
    242 
    243      for (var i=0; i<length; i++) {
    244          if (aBytes[i] !== bBytes[i]) {
    245              return false;
    246          }
    247      }
    248 
    249      if (typeof bitCount !== "undefined") {
    250          remainder = bitCount % 8;
    251          return aBytes[length] >> (8 - remainder) === bBytes[length] >> (8 - remainder);
    252      }
    253 
    254      return true;
    255  }
    256 
    257 }