tor-browser

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

WebCryptoTask.cpp (122467B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/WebCryptoTask.h"
      8 
      9 #include "cryptohi.h"
     10 #include "jsapi.h"
     11 #include "mozilla/Utf8.h"
     12 #include "mozilla/dom/CryptoBuffer.h"
     13 #include "mozilla/dom/CryptoKey.h"
     14 #include "mozilla/dom/KeyAlgorithmProxy.h"
     15 #include "mozilla/dom/RootedDictionary.h"
     16 #include "mozilla/dom/TypedArray.h"
     17 #include "mozilla/dom/WebCryptoCommon.h"
     18 #include "mozilla/dom/WorkerPrivate.h"
     19 #include "mozilla/dom/WorkerRef.h"
     20 #include "mozilla/glean/DomCryptoMetrics.h"
     21 #include "nsNSSComponent.h"
     22 #include "nsProxyRelease.h"
     23 #include "pk11pub.h"
     24 #include "secerr.h"
     25 
     26 // Template taken from security/nss/lib/util/templates.c
     27 // This (or SGN_EncodeDigestInfo) would ideally be exported
     28 // by NSS and until that happens we have to keep our own copy.
     29 MOZ_GLOBINIT const SEC_ASN1Template SGN_DigestInfoTemplate[] = {
     30    {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SGNDigestInfo)},
     31    {SEC_ASN1_INLINE, offsetof(SGNDigestInfo, digestAlgorithm),
     32     SEC_ASN1_GET(SECOID_AlgorithmIDTemplate)},
     33    {SEC_ASN1_OCTET_STRING, offsetof(SGNDigestInfo, digest)},
     34    {
     35        0,
     36    }};
     37 
     38 namespace mozilla::dom {
     39 
     40 // Pre-defined identifiers for telemetry histograms
     41 
     42 enum TelemetryMethod {
     43  TM_ENCRYPT = 0,
     44  TM_DECRYPT = 1,
     45  TM_SIGN = 2,
     46  TM_VERIFY = 3,
     47  TM_DIGEST = 4,
     48  TM_GENERATEKEY = 5,
     49  TM_DERIVEKEY = 6,
     50  TM_DERIVEBITS = 7,
     51  TM_IMPORTKEY = 8,
     52  TM_EXPORTKEY = 9,
     53  TM_WRAPKEY = 10,
     54  TM_UNWRAPKEY = 11
     55 };
     56 
     57 enum TelemetryAlgorithm {
     58  // Please make additions at the end of the list,
     59  // to preserve comparability of histograms over time
     60  TA_UNKNOWN = 0,
     61  // encrypt / decrypt
     62  TA_AES_CBC = 1,
     63  TA_AES_CFB = 2,
     64  TA_AES_CTR = 3,
     65  TA_AES_GCM = 4,
     66  TA_RSAES_PKCS1 = 5,  // NB: This algorithm has been removed
     67  TA_RSA_OAEP = 6,
     68  // sign/verify
     69  TA_RSASSA_PKCS1 = 7,
     70  TA_RSA_PSS = 8,
     71  TA_HMAC_SHA_1 = 9,
     72  TA_HMAC_SHA_224 = 10,
     73  TA_HMAC_SHA_256 = 11,
     74  TA_HMAC_SHA_384 = 12,
     75  TA_HMAC_SHA_512 = 13,
     76  // digest
     77  TA_SHA_1 = 14,
     78  TA_SHA_224 = 15,
     79  TA_SHA_256 = 16,
     80  TA_SHA_384 = 17,
     81  TA_SHA_512 = 18,
     82  // Later additions
     83  TA_AES_KW = 19,
     84  TA_ECDH = 20,
     85  TA_PBKDF2 = 21,
     86  TA_ECDSA = 22,
     87  TA_HKDF = 23,
     88  TA_DH = 24,
     89  TA_ED25519 = 25,
     90  TA_X25519 = 26,
     91 };
     92 
     93 // Convenience functions for extracting / converting information
     94 
     95 // OOM-safe CryptoBuffer initialization, suitable for constructors
     96 #define ATTEMPT_BUFFER_INIT(dst, src)    \
     97  if (!dst.Assign(src)) {                \
     98    mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; \
     99    return;                              \
    100  }
    101 
    102 // OOM-safe CryptoBuffer-to-SECItem copy, suitable for DoCrypto
    103 #define ATTEMPT_BUFFER_TO_SECITEM(arena, dst, src) \
    104  if (!src.ToSECItem(arena, dst)) {                \
    105    return NS_ERROR_DOM_UNKNOWN_ERR;               \
    106  }
    107 
    108 // OOM-safe CryptoBuffer copy, suitable for DoCrypto
    109 #define ATTEMPT_BUFFER_ASSIGN(dst, src) \
    110  if (!dst.Assign(src)) {               \
    111    return NS_ERROR_DOM_UNKNOWN_ERR;    \
    112  }
    113 
    114 // Safety check for algorithms that use keys, suitable for constructors
    115 #define CHECK_KEY_ALGORITHM(keyAlg, algName)         \
    116  {                                                  \
    117    if (!NORMALIZED_EQUALS(keyAlg.mName, algName)) { \
    118      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;    \
    119      return;                                        \
    120    }                                                \
    121  }
    122 
    123 class ClearException {
    124 public:
    125  explicit ClearException(JSContext* aCx) : mCx(aCx) {}
    126 
    127  ~ClearException() { JS_ClearPendingException(mCx); }
    128 
    129 private:
    130  JSContext* mCx;
    131 };
    132 
    133 template <class OOS>
    134 static nsresult GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
    135                                 nsString& aName) {
    136  ClearException ce(aCx);
    137 
    138  if (aAlgorithm.IsString()) {
    139    // If string, then treat as algorithm name
    140    aName.Assign(aAlgorithm.GetAsString());
    141  } else {
    142    // Coerce to algorithm and extract name
    143    JS::Rooted<JS::Value> value(aCx,
    144                                JS::ObjectValue(*aAlgorithm.GetAsObject()));
    145    Algorithm alg;
    146 
    147    if (!alg.Init(aCx, value)) {
    148      return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
    149    }
    150 
    151    aName = alg.mName;
    152  }
    153 
    154  if (!NormalizeToken(aName, aName)) {
    155    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    156  }
    157 
    158  return NS_OK;
    159 }
    160 
    161 template <class T, class OOS>
    162 static nsresult Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm) {
    163  ClearException ce(aCx);
    164 
    165  if (!aAlgorithm.IsObject()) {
    166    return NS_ERROR_DOM_SYNTAX_ERR;
    167  }
    168 
    169  JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
    170  if (!aTarget.Init(aCx, value)) {
    171    return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
    172  }
    173 
    174  return NS_OK;
    175 }
    176 
    177 inline size_t MapHashAlgorithmNameToBlockSize(const nsString& aName) {
    178  if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
    179      aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
    180    return 512;
    181  }
    182 
    183  if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
    184      aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
    185    return 1024;
    186  }
    187 
    188  return 0;
    189 }
    190 
    191 inline nsresult GetKeyLengthForAlgorithmIfSpecified(
    192    JSContext* aCx, const ObjectOrString& aAlgorithm, Maybe<size_t>& aLength) {
    193  // Extract algorithm name
    194  nsString algName;
    195  if (NS_FAILED(GetAlgorithmName(aCx, aAlgorithm, algName))) {
    196    return NS_ERROR_DOM_SYNTAX_ERR;
    197  }
    198 
    199  // Read AES key length from given algorithm object.
    200  if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
    201      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
    202      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
    203      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
    204    RootedDictionary<AesDerivedKeyParams> params(aCx);
    205    if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
    206      return NS_ERROR_DOM_SYNTAX_ERR;
    207    }
    208 
    209    if (params.mLength != 128 && params.mLength != 192 &&
    210        params.mLength != 256) {
    211      return NS_ERROR_DOM_OPERATION_ERR;
    212    }
    213 
    214    aLength.emplace(params.mLength);
    215    return NS_OK;
    216  }
    217 
    218  // Read HMAC key length from given algorithm object or
    219  // determine key length as the block size of the given hash.
    220  if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
    221    RootedDictionary<HmacDerivedKeyParams> params(aCx);
    222    if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
    223      return NS_ERROR_DOM_SYNTAX_ERR;
    224    }
    225 
    226    // Return the passed length, if any.
    227    if (params.mLength.WasPassed()) {
    228      aLength.emplace(params.mLength.Value());
    229      return NS_OK;
    230    }
    231 
    232    nsString hashName;
    233    if (NS_FAILED(GetAlgorithmName(aCx, params.mHash, hashName))) {
    234      return NS_ERROR_DOM_SYNTAX_ERR;
    235    }
    236 
    237    // Return the given hash algorithm's block size as the key length.
    238    size_t blockSize = MapHashAlgorithmNameToBlockSize(hashName);
    239    if (blockSize == 0) {
    240      return NS_ERROR_DOM_SYNTAX_ERR;
    241    }
    242 
    243    aLength.emplace(blockSize);
    244    return NS_OK;
    245  }
    246 
    247  return NS_OK;
    248 }
    249 
    250 inline nsresult GetKeyLengthForAlgorithm(JSContext* aCx,
    251                                         const ObjectOrString& aAlgorithm,
    252                                         size_t& aLength) {
    253  Maybe<size_t> length;
    254  nsresult rv = GetKeyLengthForAlgorithmIfSpecified(aCx, aAlgorithm, length);
    255  if (NS_FAILED(rv)) {
    256    return rv;
    257  }
    258  if (length.isNothing()) {
    259    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    260  }
    261  aLength = *length;
    262  return NS_OK;
    263 }
    264 
    265 inline bool MapOIDTagToNamedCurve(SECOidTag aOIDTag, nsString& aResult) {
    266  switch (aOIDTag) {
    267    case SEC_OID_SECG_EC_SECP256R1:
    268      aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
    269      break;
    270    case SEC_OID_SECG_EC_SECP384R1:
    271      aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
    272      break;
    273    case SEC_OID_SECG_EC_SECP521R1:
    274      aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
    275      break;
    276    case SEC_OID_ED25519_PUBLIC_KEY:
    277      aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_ED25519);
    278      break;
    279    case SEC_OID_X25519:
    280      aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519);
    281      break;
    282    default:
    283      return false;
    284  }
    285 
    286  return true;
    287 }
    288 
    289 inline SECOidTag MapHashAlgorithmNameToOID(const nsString& aName) {
    290  SECOidTag hashOID(SEC_OID_UNKNOWN);
    291 
    292  if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
    293    hashOID = SEC_OID_SHA1;
    294  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
    295    hashOID = SEC_OID_SHA256;
    296  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
    297    hashOID = SEC_OID_SHA384;
    298  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
    299    hashOID = SEC_OID_SHA512;
    300  }
    301 
    302  return hashOID;
    303 }
    304 
    305 inline CK_MECHANISM_TYPE MapHashAlgorithmNameToMgfMechanism(
    306    const nsString& aName) {
    307  CK_MECHANISM_TYPE mech(UNKNOWN_CK_MECHANISM);
    308 
    309  if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
    310    mech = CKG_MGF1_SHA1;
    311  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
    312    mech = CKG_MGF1_SHA256;
    313  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
    314    mech = CKG_MGF1_SHA384;
    315  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
    316    mech = CKG_MGF1_SHA512;
    317  }
    318 
    319  return mech;
    320 }
    321 
    322 // Implementation of WebCryptoTask methods
    323 
    324 void WebCryptoTask::DispatchWithPromise(Promise* aResultPromise) {
    325  mResultPromise = aResultPromise;
    326 
    327  // Fail if an error was set during the constructor
    328  MAYBE_EARLY_FAIL(mEarlyRv)
    329 
    330  // Perform pre-NSS operations, and fail if they fail
    331  mEarlyRv = BeforeCrypto();
    332  MAYBE_EARLY_FAIL(mEarlyRv)
    333 
    334  // Skip dispatch if we're already done. Otherwise launch a CryptoTask
    335  if (mEarlyComplete) {
    336    CallCallback(mEarlyRv);
    337    return;
    338  }
    339 
    340  // Store calling thread
    341  mOriginalEventTarget = GetCurrentSerialEventTarget();
    342 
    343  // If we are running on a worker thread we must hold the worker
    344  // alive while we work on the thread pool.  Otherwise the worker
    345  // private may get torn down before we dispatch back to complete
    346  // the transaction.
    347  if (!NS_IsMainThread()) {
    348    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    349    MOZ_ASSERT(workerPrivate);
    350 
    351    RefPtr<StrongWorkerRef> workerRef =
    352        StrongWorkerRef::Create(workerPrivate, "WebCryptoTask");
    353    if (NS_WARN_IF(!workerRef)) {
    354      mEarlyRv = NS_BINDING_ABORTED;
    355    } else {
    356      mWorkerRef = new ThreadSafeWorkerRef(workerRef);
    357    }
    358  }
    359  MAYBE_EARLY_FAIL(mEarlyRv);
    360 
    361  // dispatch to thread pool
    362 
    363  if (!EnsureNSSInitializedChromeOrContent()) {
    364    mEarlyRv = NS_ERROR_FAILURE;
    365  }
    366  MAYBE_EARLY_FAIL(mEarlyRv);
    367 
    368  mEarlyRv = NS_DispatchBackgroundTask(this);
    369  MAYBE_EARLY_FAIL(mEarlyRv)
    370 }
    371 
    372 NS_IMETHODIMP
    373 WebCryptoTask::Run() {
    374  // Run heavy crypto operations on the thread pool, off the original thread.
    375  if (!IsOnOriginalThread()) {
    376    mRv = CalculateResult();
    377 
    378    // Back to the original thread, i.e. continue below.
    379    mOriginalEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
    380    return NS_OK;
    381  }
    382 
    383  // We're now back on the calling thread.
    384  CallCallback(mRv);
    385 
    386  // Stop holding the worker thread alive now that the async work has
    387  // been completed.
    388  mWorkerRef = nullptr;
    389 
    390  return NS_OK;
    391 }
    392 
    393 nsresult WebCryptoTask::Cancel() {
    394  MOZ_ASSERT(IsOnOriginalThread());
    395  FailWithError(NS_BINDING_ABORTED);
    396  return NS_OK;
    397 }
    398 
    399 void WebCryptoTask::FailWithError(nsresult aRv) {
    400  MOZ_ASSERT(IsOnOriginalThread());
    401  glean::webcrypto::resolved.EnumGet(glean::webcrypto::ResolvedLabel::eFalse)
    402      .Add();
    403 
    404  if (aRv == NS_ERROR_DOM_TYPE_MISMATCH_ERR) {
    405    mResultPromise->MaybeRejectWithTypeError(
    406        "The operation could not be performed.");
    407  } else {
    408    // Blindly convert nsresult to DOMException
    409    // Individual tasks must ensure they pass the right values
    410    mResultPromise->MaybeReject(aRv);
    411  }
    412  // Manually release mResultPromise while we're on the main thread
    413  mResultPromise = nullptr;
    414  mWorkerRef = nullptr;
    415  Cleanup();
    416 }
    417 
    418 nsresult WebCryptoTask::CalculateResult() {
    419  MOZ_ASSERT(!IsOnOriginalThread());
    420 
    421  return DoCrypto();
    422 }
    423 
    424 void WebCryptoTask::CallCallback(nsresult rv) {
    425  MOZ_ASSERT(IsOnOriginalThread());
    426  if (NS_FAILED(rv)) {
    427    FailWithError(rv);
    428    return;
    429  }
    430 
    431  nsresult rv2 = AfterCrypto();
    432  if (NS_FAILED(rv2)) {
    433    FailWithError(rv2);
    434    return;
    435  }
    436 
    437  Resolve();
    438  glean::webcrypto::resolved.EnumGet(glean::webcrypto::ResolvedLabel::eTrue)
    439      .Add();
    440 
    441  // Manually release mResultPromise while we're on the main thread
    442  mResultPromise = nullptr;
    443  Cleanup();
    444 }
    445 
    446 // Some generic utility classes
    447 
    448 class FailureTask : public WebCryptoTask {
    449 public:
    450  explicit FailureTask(nsresult aRv) { mEarlyRv = aRv; }
    451 };
    452 
    453 class ReturnArrayBufferViewTask : public WebCryptoTask {
    454 protected:
    455  CryptoBuffer mResult;
    456 
    457 private:
    458  // Returns mResult as an ArrayBufferView, or an error
    459  virtual void Resolve() override {
    460    TypedArrayCreator<ArrayBuffer> ret(mResult);
    461    mResultPromise->MaybeResolve(ret);
    462  }
    463 };
    464 
    465 class DeferredData {
    466 public:
    467  template <class T>
    468  void SetData(const T& aData) {
    469    mDataIsSet = mData.Assign(aData);
    470  }
    471 
    472 protected:
    473  DeferredData() : mDataIsSet(false) {}
    474 
    475  CryptoBuffer mData;
    476  bool mDataIsSet;
    477 };
    478 
    479 class AesTask : public ReturnArrayBufferViewTask, public DeferredData {
    480 public:
    481  AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
    482          bool aEncrypt)
    483      : mMechanism(CKM_INVALID_MECHANISM),
    484        mTagLength(0),
    485        mCounterLength(0),
    486        mEncrypt(aEncrypt) {
    487    Init(aCx, aAlgorithm, aKey, aEncrypt);
    488  }
    489 
    490  AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
    491          const CryptoOperationData& aData, bool aEncrypt)
    492      : mMechanism(CKM_INVALID_MECHANISM),
    493        mTagLength(0),
    494        mCounterLength(0),
    495        mEncrypt(aEncrypt) {
    496    Init(aCx, aAlgorithm, aKey, aEncrypt);
    497    SetData(aData);
    498  }
    499 
    500  void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
    501            bool aEncrypt) {
    502    nsString algName;
    503    mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
    504    if (NS_FAILED(mEarlyRv)) {
    505      return;
    506    }
    507 
    508    if (!mSymKey.Assign(aKey.GetSymKey())) {
    509      mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
    510      return;
    511    }
    512 
    513    // Check that we got a reasonable key
    514    if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
    515        (mSymKey.Length() != 32)) {
    516      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
    517      return;
    518    }
    519 
    520    // Cache parameters depending on the specific algorithm
    521    TelemetryAlgorithm telemetryAlg;
    522    if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
    523      CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CBC);
    524 
    525      mMechanism = CKM_AES_CBC_PAD;
    526      telemetryAlg = TA_AES_CBC;
    527      RootedDictionary<AesCbcParams> params(aCx);
    528      nsresult rv = Coerce(aCx, params, aAlgorithm);
    529      if (NS_FAILED(rv)) {
    530        mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
    531        return;
    532      }
    533 
    534      ATTEMPT_BUFFER_INIT(mIv, params.mIv)
    535      if (mIv.Length() != 16) {
    536        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
    537        return;
    538      }
    539    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
    540      CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CTR);
    541 
    542      mMechanism = CKM_AES_CTR;
    543      telemetryAlg = TA_AES_CTR;
    544      RootedDictionary<AesCtrParams> params(aCx);
    545      nsresult rv = Coerce(aCx, params, aAlgorithm);
    546      if (NS_FAILED(rv)) {
    547        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
    548        return;
    549      }
    550 
    551      ATTEMPT_BUFFER_INIT(mIv, params.mCounter)
    552      if (mIv.Length() != 16) {
    553        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
    554        return;
    555      }
    556 
    557      mCounterLength = params.mLength;
    558    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
    559      CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM);
    560 
    561      mMechanism = CKM_AES_GCM;
    562      telemetryAlg = TA_AES_GCM;
    563      RootedDictionary<AesGcmParams> params(aCx);
    564      nsresult rv = Coerce(aCx, params, aAlgorithm);
    565      if (NS_FAILED(rv)) {
    566        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
    567        return;
    568      }
    569 
    570      ATTEMPT_BUFFER_INIT(mIv, params.mIv)
    571 
    572      if (params.mAdditionalData.WasPassed()) {
    573        ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value())
    574      }
    575 
    576      // 32, 64, 96, 104, 112, 120 or 128
    577      mTagLength = 128;
    578      if (params.mTagLength.WasPassed()) {
    579        mTagLength = params.mTagLength.Value();
    580        if ((mTagLength > 128) ||
    581            !(mTagLength == 32 || mTagLength == 64 ||
    582              (mTagLength >= 96 && mTagLength % 8 == 0))) {
    583          mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
    584          return;
    585        }
    586      }
    587    } else {
    588      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    589      return;
    590    }
    591    glean::webcrypto::alg.AccumulateSingleSample(telemetryAlg);
    592  }
    593 
    594 private:
    595  CK_MECHANISM_TYPE mMechanism;
    596  CryptoBuffer mSymKey;
    597  CryptoBuffer mIv;   // Initialization vector
    598  CryptoBuffer mAad;  // Additional Authenticated Data
    599  uint8_t mTagLength;
    600  uint8_t mCounterLength;
    601  bool mEncrypt;
    602 
    603  virtual nsresult DoCrypto() override {
    604    nsresult rv;
    605 
    606    if (!mDataIsSet) {
    607      return NS_ERROR_DOM_OPERATION_ERR;
    608    }
    609 
    610    UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
    611    if (!arena) {
    612      return NS_ERROR_DOM_OPERATION_ERR;
    613    }
    614 
    615    // Construct the parameters object depending on algorithm
    616    SECItem param = {siBuffer, nullptr, 0};
    617    CK_AES_CTR_PARAMS ctrParams;
    618    CK_GCM_PARAMS gcmParams;
    619    switch (mMechanism) {
    620      case CKM_AES_CBC_PAD:
    621        ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &param, mIv);
    622        break;
    623      case CKM_AES_CTR:
    624        ctrParams.ulCounterBits = mCounterLength;
    625        MOZ_ASSERT(mIv.Length() == 16);
    626        memcpy(&ctrParams.cb, mIv.Elements(), 16);
    627        param.type = siBuffer;
    628        param.data = (unsigned char*)&ctrParams;
    629        param.len = sizeof(ctrParams);
    630        break;
    631      case CKM_AES_GCM:
    632        gcmParams.pIv = mIv.Elements();
    633        gcmParams.ulIvLen = mIv.Length();
    634        gcmParams.ulIvBits = gcmParams.ulIvLen * 8;
    635        gcmParams.pAAD = mAad.Elements();
    636        gcmParams.ulAADLen = mAad.Length();
    637        gcmParams.ulTagBits = mTagLength;
    638        param.type = siBuffer;
    639        param.data = (unsigned char*)&gcmParams;
    640        param.len = sizeof(gcmParams);
    641        break;
    642      default:
    643        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    644    }
    645 
    646    // Import the key
    647    SECItem keyItem = {siBuffer, nullptr, 0};
    648    ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
    649    UniquePK11SlotInfo slot(PK11_GetInternalSlot());
    650    MOZ_ASSERT(slot.get());
    651    UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
    652                                              PK11_OriginUnwrap, CKA_ENCRYPT,
    653                                              &keyItem, nullptr));
    654    if (!symKey) {
    655      return NS_ERROR_DOM_INVALID_ACCESS_ERR;
    656    }
    657 
    658    // Check whether the integer addition would overflow.
    659    if (std::numeric_limits<CryptoBuffer::size_type>::max() - 16 <
    660        mData.Length()) {
    661      return NS_ERROR_DOM_DATA_ERR;
    662    }
    663 
    664    // Initialize the output buffer (enough space for padding / a full tag)
    665    if (!mResult.SetLength(mData.Length() + 16, fallible)) {
    666      return NS_ERROR_DOM_UNKNOWN_ERR;
    667    }
    668 
    669    uint32_t outLen = 0;
    670 
    671    // Perform the encryption/decryption
    672    if (mEncrypt) {
    673      rv = MapSECStatus(PK11_Encrypt(
    674          symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
    675          mResult.Length(), mData.Elements(), mData.Length()));
    676    } else {
    677      rv = MapSECStatus(PK11_Decrypt(
    678          symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
    679          mResult.Length(), mData.Elements(), mData.Length()));
    680    }
    681    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
    682 
    683    mResult.TruncateLength(outLen);
    684    return rv;
    685  }
    686 };
    687 
    688 // This class looks like an encrypt/decrypt task, like AesTask,
    689 // but it is only exposed to wrapKey/unwrapKey, not encrypt/decrypt
    690 class AesKwTask : public ReturnArrayBufferViewTask, public DeferredData {
    691 public:
    692  AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
    693            bool aEncrypt)
    694      : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
    695    Init(aCx, aAlgorithm, aKey, aEncrypt);
    696  }
    697 
    698  AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
    699            const CryptoOperationData& aData, bool aEncrypt)
    700      : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
    701    Init(aCx, aAlgorithm, aKey, aEncrypt);
    702    SetData(aData);
    703  }
    704 
    705  void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
    706            bool aEncrypt) {
    707    CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_KW);
    708 
    709    nsString algName;
    710    mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
    711    if (NS_FAILED(mEarlyRv)) {
    712      return;
    713    }
    714 
    715    if (!mSymKey.Assign(aKey.GetSymKey())) {
    716      mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
    717      return;
    718    }
    719 
    720    // Check that we got a reasonable key
    721    if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
    722        (mSymKey.Length() != 32)) {
    723      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
    724      return;
    725    }
    726 
    727    glean::webcrypto::alg.AccumulateSingleSample(TA_AES_KW);
    728  }
    729 
    730 private:
    731  CK_MECHANISM_TYPE mMechanism;
    732  CryptoBuffer mSymKey;
    733  bool mEncrypt;
    734 
    735  virtual nsresult DoCrypto() override {
    736    nsresult rv;
    737 
    738    if (!mDataIsSet) {
    739      return NS_ERROR_DOM_OPERATION_ERR;
    740    }
    741 
    742    // Check that the input is a multiple of 64 bits long
    743    if (mData.Length() == 0 || mData.Length() % 8 != 0) {
    744      return NS_ERROR_DOM_DATA_ERR;
    745    }
    746 
    747    UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
    748    if (!arena) {
    749      return NS_ERROR_DOM_OPERATION_ERR;
    750    }
    751 
    752    // Import the key
    753    SECItem keyItem = {siBuffer, nullptr, 0};
    754    ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
    755    UniquePK11SlotInfo slot(PK11_GetInternalSlot());
    756    MOZ_ASSERT(slot.get());
    757    UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
    758                                              PK11_OriginUnwrap, CKA_WRAP,
    759                                              &keyItem, nullptr));
    760    if (!symKey) {
    761      return NS_ERROR_DOM_INVALID_ACCESS_ERR;
    762    }
    763 
    764    // Import the data to a SECItem
    765    SECItem dataItem = {siBuffer, nullptr, 0};
    766    ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &dataItem, mData);
    767 
    768    // Parameters for the fake keys
    769    CK_MECHANISM_TYPE fakeMechanism = CKM_SHA_1_HMAC;
    770    CK_ATTRIBUTE_TYPE fakeOperation = CKA_SIGN;
    771 
    772    if (mEncrypt) {
    773      // Import the data into a fake PK11SymKey structure
    774      UniquePK11SymKey keyToWrap(
    775          PK11_ImportSymKey(slot.get(), fakeMechanism, PK11_OriginUnwrap,
    776                            fakeOperation, &dataItem, nullptr));
    777      if (!keyToWrap) {
    778        return NS_ERROR_DOM_OPERATION_ERR;
    779      }
    780 
    781      // Encrypt and return the wrapped key
    782      // AES-KW encryption results in a wrapped key 64 bits longer
    783      if (!mResult.SetLength(mData.Length() + 8, fallible)) {
    784        return NS_ERROR_DOM_OPERATION_ERR;
    785      }
    786      SECItem resultItem = {siBuffer, mResult.Elements(),
    787                            (unsigned int)mResult.Length()};
    788      rv = MapSECStatus(PK11_WrapSymKey(mMechanism, nullptr, symKey.get(),
    789                                        keyToWrap.get(), &resultItem));
    790      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
    791    } else {
    792      // Decrypt the ciphertext into a temporary PK11SymKey
    793      // Unwrapped key should be 64 bits shorter
    794      int keySize = mData.Length() - 8;
    795      UniquePK11SymKey unwrappedKey(
    796          PK11_UnwrapSymKey(symKey.get(), mMechanism, nullptr, &dataItem,
    797                            fakeMechanism, fakeOperation, keySize));
    798      if (!unwrappedKey) {
    799        return NS_ERROR_DOM_OPERATION_ERR;
    800      }
    801 
    802      // Export the key to get the cleartext
    803      rv = MapSECStatus(PK11_ExtractKeyValue(unwrappedKey.get()));
    804      if (NS_FAILED(rv)) {
    805        return NS_ERROR_DOM_UNKNOWN_ERR;
    806      }
    807      ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(unwrappedKey.get()));
    808    }
    809 
    810    return rv;
    811  }
    812 };
    813 
    814 class RsaOaepTask : public ReturnArrayBufferViewTask, public DeferredData {
    815 public:
    816  RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
    817              bool aEncrypt)
    818      : mPrivKey(aKey.GetPrivateKey()),
    819        mPubKey(aKey.GetPublicKey()),
    820        mEncrypt(aEncrypt) {
    821    Init(aCx, aAlgorithm, aKey, aEncrypt);
    822  }
    823 
    824  RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
    825              const CryptoOperationData& aData, bool aEncrypt)
    826      : mPrivKey(aKey.GetPrivateKey()),
    827        mPubKey(aKey.GetPublicKey()),
    828        mEncrypt(aEncrypt) {
    829    Init(aCx, aAlgorithm, aKey, aEncrypt);
    830    SetData(aData);
    831  }
    832 
    833  void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
    834            bool aEncrypt) {
    835    glean::webcrypto::alg.AccumulateSingleSample(TA_RSA_OAEP);
    836 
    837    CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_OAEP);
    838 
    839    if (mEncrypt) {
    840      if (!mPubKey) {
    841        mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
    842        return;
    843      }
    844      mStrength = SECKEY_PublicKeyStrength(mPubKey.get());
    845    } else {
    846      if (!mPrivKey) {
    847        mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
    848        return;
    849      }
    850      mStrength = PK11_GetPrivateModulusLen(mPrivKey.get());
    851    }
    852 
    853    // The algorithm could just be given as a string
    854    // in which case there would be no label specified.
    855    if (!aAlgorithm.IsString()) {
    856      RootedDictionary<RsaOaepParams> params(aCx);
    857      mEarlyRv = Coerce(aCx, params, aAlgorithm);
    858      if (NS_FAILED(mEarlyRv)) {
    859        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
    860        return;
    861      }
    862 
    863      if (params.mLabel.WasPassed()) {
    864        ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value());
    865      }
    866    }
    867    // Otherwise mLabel remains the empty octet string, as intended
    868 
    869    KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
    870    mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
    871    mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlg.mName);
    872 
    873    // Check we found appropriate mechanisms.
    874    if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
    875        mMgfMechanism == UNKNOWN_CK_MECHANISM) {
    876      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    877      return;
    878    }
    879  }
    880 
    881 private:
    882  CK_MECHANISM_TYPE mHashMechanism;
    883  CK_MECHANISM_TYPE mMgfMechanism;
    884  UniqueSECKEYPrivateKey mPrivKey;
    885  UniqueSECKEYPublicKey mPubKey;
    886  CryptoBuffer mLabel;
    887  uint32_t mStrength;
    888  bool mEncrypt;
    889 
    890  virtual nsresult DoCrypto() override {
    891    nsresult rv;
    892 
    893    if (!mDataIsSet) {
    894      return NS_ERROR_DOM_OPERATION_ERR;
    895    }
    896 
    897    // Ciphertext is an integer mod the modulus, so it will be
    898    // no longer than mStrength octets
    899    if (!mResult.SetLength(mStrength, fallible)) {
    900      return NS_ERROR_DOM_UNKNOWN_ERR;
    901    }
    902 
    903    CK_RSA_PKCS_OAEP_PARAMS oaepParams;
    904    oaepParams.source = CKZ_DATA_SPECIFIED;
    905 
    906    oaepParams.pSourceData = mLabel.Length() ? mLabel.Elements() : nullptr;
    907    oaepParams.ulSourceDataLen = mLabel.Length();
    908 
    909    oaepParams.mgf = mMgfMechanism;
    910    oaepParams.hashAlg = mHashMechanism;
    911 
    912    SECItem param;
    913    param.type = siBuffer;
    914    param.data = (unsigned char*)&oaepParams;
    915    param.len = sizeof(oaepParams);
    916 
    917    uint32_t outLen = 0;
    918    if (mEncrypt) {
    919      // PK11_PubEncrypt() checks the plaintext's length and fails if it is too
    920      // long to encrypt, i.e. if it is longer than (k - 2hLen - 2) with 'k'
    921      // being the length in octets of the RSA modulus n and 'hLen' being the
    922      // output length in octets of the chosen hash function.
    923      // <https://tools.ietf.org/html/rfc3447#section-7.1>
    924      rv = MapSECStatus(PK11_PubEncrypt(
    925          mPubKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(), &outLen,
    926          mResult.Length(), mData.Elements(), mData.Length(), nullptr));
    927    } else {
    928      rv = MapSECStatus(PK11_PrivDecrypt(
    929          mPrivKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(),
    930          &outLen, mResult.Length(), mData.Elements(), mData.Length()));
    931    }
    932    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
    933 
    934    mResult.TruncateLength(outLen);
    935    return NS_OK;
    936  }
    937 };
    938 
    939 class HmacTask : public WebCryptoTask {
    940 public:
    941  HmacTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
    942           const CryptoOperationData& aSignature,
    943           const CryptoOperationData& aData, bool aSign)
    944      : mMechanism(aKey.Algorithm().Mechanism()), mSign(aSign) {
    945    CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HMAC);
    946 
    947    ATTEMPT_BUFFER_INIT(mData, aData);
    948    if (!aSign) {
    949      ATTEMPT_BUFFER_INIT(mSignature, aSignature);
    950    }
    951 
    952    if (!mSymKey.Assign(aKey.GetSymKey())) {
    953      mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
    954      return;
    955    }
    956 
    957    // Check that we got a symmetric key
    958    if (mSymKey.Length() == 0) {
    959      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
    960      return;
    961    }
    962 
    963    TelemetryAlgorithm telemetryAlg;
    964    switch (mMechanism) {
    965      case CKM_SHA_1_HMAC:
    966        telemetryAlg = TA_HMAC_SHA_1;
    967        break;
    968      case CKM_SHA224_HMAC:
    969        telemetryAlg = TA_HMAC_SHA_224;
    970        break;
    971      case CKM_SHA256_HMAC:
    972        telemetryAlg = TA_HMAC_SHA_256;
    973        break;
    974      case CKM_SHA384_HMAC:
    975        telemetryAlg = TA_HMAC_SHA_384;
    976        break;
    977      case CKM_SHA512_HMAC:
    978        telemetryAlg = TA_HMAC_SHA_512;
    979        break;
    980      default:
    981        telemetryAlg = TA_UNKNOWN;
    982    }
    983    glean::webcrypto::alg.AccumulateSingleSample(telemetryAlg);
    984  }
    985 
    986 private:
    987  CK_MECHANISM_TYPE mMechanism;
    988  CryptoBuffer mSymKey;
    989  CryptoBuffer mData;
    990  CryptoBuffer mSignature;
    991  CryptoBuffer mResult;
    992  bool mSign;
    993 
    994  virtual nsresult DoCrypto() override {
    995    // Initialize the output buffer
    996    if (!mResult.SetLength(HASH_LENGTH_MAX, fallible)) {
    997      return NS_ERROR_DOM_UNKNOWN_ERR;
    998    }
    999 
   1000    UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   1001    if (!arena) {
   1002      return NS_ERROR_DOM_OPERATION_ERR;
   1003    }
   1004 
   1005    // Import the key
   1006    uint32_t outLen;
   1007    SECItem keyItem = {siBuffer, nullptr, 0};
   1008    ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
   1009    UniquePK11SlotInfo slot(PK11_GetInternalSlot());
   1010    MOZ_ASSERT(slot.get());
   1011    UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
   1012                                              PK11_OriginUnwrap, CKA_SIGN,
   1013                                              &keyItem, nullptr));
   1014    if (!symKey) {
   1015      return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   1016    }
   1017 
   1018    // Compute the MAC
   1019    SECItem param = {siBuffer, nullptr, 0};
   1020    UniquePK11Context ctx(
   1021        PK11_CreateContextBySymKey(mMechanism, CKA_SIGN, symKey.get(), &param));
   1022    if (!ctx.get()) {
   1023      return NS_ERROR_DOM_OPERATION_ERR;
   1024    }
   1025    nsresult rv = MapSECStatus(PK11_DigestBegin(ctx.get()));
   1026    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
   1027    rv = MapSECStatus(
   1028        PK11_DigestOp(ctx.get(), mData.Elements(), mData.Length()));
   1029    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
   1030    rv = MapSECStatus(PK11_DigestFinal(ctx.get(), mResult.Elements(), &outLen,
   1031                                       mResult.Length()));
   1032    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
   1033 
   1034    mResult.TruncateLength(outLen);
   1035    return rv;
   1036  }
   1037 
   1038  // Returns mResult as an ArrayBufferView, or an error
   1039  virtual void Resolve() override {
   1040    if (mSign) {
   1041      // Return the computed MAC
   1042      TypedArrayCreator<ArrayBuffer> ret(mResult);
   1043      mResultPromise->MaybeResolve(ret);
   1044    } else {
   1045      // Compare the MAC to the provided signature
   1046      // No truncation allowed
   1047      bool equal = (mResult.Length() == mSignature.Length());
   1048      if (equal) {
   1049        int cmp = NSS_SecureMemcmp(mSignature.Elements(), mResult.Elements(),
   1050                                   mSignature.Length());
   1051        equal = (cmp == 0);
   1052      }
   1053      mResultPromise->MaybeResolve(equal);
   1054    }
   1055  }
   1056 };
   1057 
   1058 class AsymmetricSignVerifyTask : public WebCryptoTask {
   1059 public:
   1060  AsymmetricSignVerifyTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
   1061                           CryptoKey& aKey,
   1062                           const CryptoOperationData& aSignature,
   1063                           const CryptoOperationData& aData, bool aSign)
   1064      : mOidTag(SEC_OID_UNKNOWN),
   1065        mHashMechanism(UNKNOWN_CK_MECHANISM),
   1066        mMgfMechanism(UNKNOWN_CK_MECHANISM),
   1067        mPrivKey(aKey.GetPrivateKey()),
   1068        mPubKey(aKey.GetPublicKey()),
   1069        mSaltLength(0),
   1070        mSign(aSign),
   1071        mVerified(false),
   1072        mAlgorithm(Algorithm::UNKNOWN) {
   1073    ATTEMPT_BUFFER_INIT(mData, aData);
   1074    if (!aSign) {
   1075      ATTEMPT_BUFFER_INIT(mSignature, aSignature);
   1076    }
   1077 
   1078    nsString algName;
   1079    nsString hashAlgName;
   1080    mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
   1081    if (NS_FAILED(mEarlyRv)) {
   1082      return;
   1083    }
   1084 
   1085    if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
   1086      mAlgorithm = Algorithm::RSA_PKCS1;
   1087      glean::webcrypto::alg.AccumulateSingleSample(TA_RSASSA_PKCS1);
   1088      CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
   1089      hashAlgName = aKey.Algorithm().mRsa.mHash.mName;
   1090    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
   1091      mAlgorithm = Algorithm::RSA_PSS;
   1092      glean::webcrypto::alg.AccumulateSingleSample(TA_RSA_PSS);
   1093      CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_PSS);
   1094 
   1095      KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
   1096      hashAlgName = hashAlg.mName;
   1097      mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
   1098      mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlgName);
   1099 
   1100      // Check we found appropriate mechanisms.
   1101      if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
   1102          mMgfMechanism == UNKNOWN_CK_MECHANISM) {
   1103        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1104        return;
   1105      }
   1106 
   1107      RootedDictionary<RsaPssParams> params(aCx);
   1108      mEarlyRv = Coerce(aCx, params, aAlgorithm);
   1109      if (NS_FAILED(mEarlyRv)) {
   1110        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   1111        return;
   1112      }
   1113 
   1114      mSaltLength = params.mSaltLength;
   1115    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
   1116      mAlgorithm = Algorithm::ECDSA;
   1117      glean::webcrypto::alg.AccumulateSingleSample(TA_ECDSA);
   1118      CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
   1119 
   1120      // For ECDSA, the hash name comes from the algorithm parameter
   1121      RootedDictionary<EcdsaParams> params(aCx);
   1122      mEarlyRv = Coerce(aCx, params, aAlgorithm);
   1123      if (NS_FAILED(mEarlyRv)) {
   1124        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   1125        return;
   1126      }
   1127 
   1128      mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashAlgName);
   1129      if (NS_FAILED(mEarlyRv)) {
   1130        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1131        return;
   1132      }
   1133    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
   1134      mAlgorithm = Algorithm::ED25519;
   1135      glean::webcrypto::alg.AccumulateSingleSample(TA_ED25519);
   1136      CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ED25519);
   1137    } else {
   1138      // This shouldn't happen; CreateSignVerifyTask shouldn't create
   1139      // one of these unless it's for the above algorithms.
   1140      MOZ_ASSERT(false);
   1141    }
   1142 
   1143    // Must have a valid algorithm by now.
   1144    MOZ_ASSERT(mAlgorithm != Algorithm::UNKNOWN);
   1145 
   1146    // Determine hash algorithm to use.
   1147    mOidTag = MapHashAlgorithmNameToOID(hashAlgName);
   1148 
   1149    if (mOidTag == SEC_OID_UNKNOWN && AlgorithmRequiresHashing(mAlgorithm)) {
   1150      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1151      return;
   1152    }
   1153 
   1154    // Check that we have the appropriate key
   1155    if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
   1156      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
   1157      return;
   1158    }
   1159  }
   1160 
   1161 private:
   1162  SECOidTag mOidTag;
   1163  CK_MECHANISM_TYPE mHashMechanism;
   1164  CK_MECHANISM_TYPE mMgfMechanism;
   1165  UniqueSECKEYPrivateKey mPrivKey;
   1166  UniqueSECKEYPublicKey mPubKey;
   1167  CryptoBuffer mSignature;
   1168  CryptoBuffer mData;
   1169  uint32_t mSaltLength;
   1170  bool mSign;
   1171  bool mVerified;
   1172 
   1173  // The signature algorithm to use.
   1174  enum class Algorithm : uint8_t {
   1175    ECDSA,
   1176    RSA_PKCS1,
   1177    RSA_PSS,
   1178    ED25519,
   1179    UNKNOWN
   1180  };
   1181  Algorithm mAlgorithm;
   1182 
   1183  bool AlgorithmRequiresHashing(Algorithm aAlgorithm) {
   1184    MOZ_ASSERT(aAlgorithm != Algorithm::UNKNOWN);
   1185    /* Currently, only ED25519 does not require hashing.*/
   1186    switch (aAlgorithm) {
   1187      case Algorithm::ED25519:
   1188        return false;
   1189      case Algorithm::ECDSA:
   1190      case Algorithm::RSA_PKCS1:
   1191      case Algorithm::RSA_PSS:
   1192      // Impossible
   1193      case Algorithm::UNKNOWN:
   1194        return true;
   1195    }
   1196    /*Also impossible, as all the algorithm options should be managed in the
   1197     * switch. */
   1198    return true;
   1199  }
   1200 
   1201  virtual nsresult DoCrypto() override {
   1202    SECStatus rv;
   1203    UniqueSECItem hash;
   1204 
   1205    SECItem* params = nullptr;
   1206    CK_MECHANISM_TYPE mech =
   1207        PK11_MapSignKeyType(mSign ? mPrivKey->keyType : mPubKey->keyType);
   1208 
   1209    CK_RSA_PKCS_PSS_PARAMS rsaPssParams;
   1210    SECItem rsaPssParamsItem = {
   1211        siBuffer,
   1212    };
   1213 
   1214    // Set up parameters for RSA-PSS.
   1215    if (mAlgorithm == Algorithm::RSA_PSS) {
   1216      rsaPssParams.hashAlg = mHashMechanism;
   1217      rsaPssParams.mgf = mMgfMechanism;
   1218      rsaPssParams.sLen = mSaltLength;
   1219 
   1220      rsaPssParamsItem.data = (unsigned char*)&rsaPssParams;
   1221      rsaPssParamsItem.len = sizeof(rsaPssParams);
   1222      params = &rsaPssParamsItem;
   1223 
   1224      mech = CKM_RSA_PKCS_PSS;
   1225    }
   1226 
   1227    if (AlgorithmRequiresHashing(mAlgorithm)) {
   1228      // Compute digest over given data.
   1229      hash.reset(::SECITEM_AllocItem(nullptr, nullptr,
   1230                                     HASH_ResultLenByOidTag(mOidTag)));
   1231 
   1232      if (!hash || !hash->data || hash->len > PR_INT32_MAX) {
   1233        return NS_ERROR_DOM_OPERATION_ERR;
   1234      }
   1235 
   1236      rv = PK11_HashBuf(mOidTag, hash->data, mData.Elements(),
   1237                        static_cast<PRInt32>(mData.Length()));
   1238      NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
   1239    }
   1240 
   1241    // Wrap hash in a digest info template (RSA-PKCS1 only).
   1242    if (mAlgorithm == Algorithm::RSA_PKCS1) {
   1243      if (!hash) {
   1244        return NS_ERROR_DOM_OPERATION_ERR;
   1245      }
   1246 
   1247      UniqueSGNDigestInfo di(
   1248          SGN_CreateDigestInfo(mOidTag, hash->data, hash->len));
   1249      if (!di) {
   1250        return NS_ERROR_DOM_OPERATION_ERR;
   1251      }
   1252 
   1253      // Reuse |hash|.
   1254      SECITEM_FreeItem(hash.get(), false);
   1255      if (!SEC_ASN1EncodeItem(nullptr, hash.get(), di.get(),
   1256                              SGN_DigestInfoTemplate)) {
   1257        return NS_ERROR_DOM_OPERATION_ERR;
   1258      }
   1259    }
   1260 
   1261    // Allocate SECItem to hold the signature.
   1262    uint32_t len = mSign ? PK11_SignatureLen(mPrivKey.get()) : 0;
   1263    UniqueSECItem sig(::SECITEM_AllocItem(nullptr, nullptr, len));
   1264    if (!sig) {
   1265      return NS_ERROR_DOM_OPERATION_ERR;
   1266    }
   1267 
   1268    // Buffer for signature/verification input.
   1269    SECItem dataToOperateOn;
   1270    if (mSign) {
   1271      if (AlgorithmRequiresHashing(mAlgorithm)) {
   1272        dataToOperateOn = {siBuffer, hash->data, hash->len};
   1273      } else {
   1274        dataToOperateOn = {siBuffer, mData.Elements(),
   1275                           static_cast<unsigned int>(mData.Length())};
   1276      }
   1277 
   1278      // Sign the hash.
   1279      rv = PK11_SignWithMechanism(mPrivKey.get(), mech, params, sig.get(),
   1280                                  &dataToOperateOn);
   1281      NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
   1282      ATTEMPT_BUFFER_ASSIGN(mSignature, sig.get());
   1283    } else {
   1284      if (AlgorithmRequiresHashing(mAlgorithm)) {
   1285        dataToOperateOn = {siBuffer, hash->data, hash->len};
   1286      } else {
   1287        dataToOperateOn = {siBuffer, mData.Elements(),
   1288                           static_cast<unsigned int>(mData.Length())};
   1289      }
   1290 
   1291      // Copy the given signature to the SECItem.
   1292      if (!mSignature.ToSECItem(nullptr, sig.get())) {
   1293        return NS_ERROR_DOM_OPERATION_ERR;
   1294      }
   1295 
   1296      // Verify the signature.
   1297      rv = PK11_VerifyWithMechanism(mPubKey.get(), mech, params, sig.get(),
   1298                                    &dataToOperateOn, nullptr);
   1299      mVerified = NS_SUCCEEDED(MapSECStatus(rv));
   1300    }
   1301 
   1302    return NS_OK;
   1303  }
   1304 
   1305  virtual void Resolve() override {
   1306    if (mSign) {
   1307      TypedArrayCreator<ArrayBuffer> ret(mSignature);
   1308      mResultPromise->MaybeResolve(ret);
   1309    } else {
   1310      mResultPromise->MaybeResolve(mVerified);
   1311    }
   1312  }
   1313 };
   1314 
   1315 class DigestTask : public ReturnArrayBufferViewTask {
   1316 public:
   1317  DigestTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
   1318             const CryptoOperationData& aData) {
   1319    ATTEMPT_BUFFER_INIT(mData, aData);
   1320 
   1321    nsString algName;
   1322    mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
   1323    if (NS_FAILED(mEarlyRv)) {
   1324      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   1325      return;
   1326    }
   1327 
   1328    TelemetryAlgorithm telemetryAlg;
   1329    if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
   1330      telemetryAlg = TA_SHA_1;
   1331    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
   1332      telemetryAlg = TA_SHA_224;
   1333    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
   1334      telemetryAlg = TA_SHA_256;
   1335    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
   1336      telemetryAlg = TA_SHA_384;
   1337    } else {
   1338      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   1339      return;
   1340    }
   1341    glean::webcrypto::alg.AccumulateSingleSample(telemetryAlg);
   1342    mOidTag = MapHashAlgorithmNameToOID(algName);
   1343  }
   1344 
   1345 private:
   1346  SECOidTag mOidTag;
   1347  CryptoBuffer mData;
   1348 
   1349  virtual nsresult DoCrypto() override {
   1350    // Resize the result buffer
   1351    uint32_t hashLen = HASH_ResultLenByOidTag(mOidTag);
   1352    if (!mResult.SetLength(hashLen, fallible)) {
   1353      return NS_ERROR_DOM_UNKNOWN_ERR;
   1354    }
   1355 
   1356    // Compute the hash
   1357    nsresult rv = MapSECStatus(PK11_HashBuf(mOidTag, mResult.Elements(),
   1358                                            mData.Elements(), mData.Length()));
   1359    if (NS_FAILED(rv)) {
   1360      return NS_ERROR_DOM_UNKNOWN_ERR;
   1361    }
   1362 
   1363    return rv;
   1364  }
   1365 };
   1366 
   1367 class ImportKeyTask : public WebCryptoTask {
   1368 public:
   1369  void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
   1370            const ObjectOrString& aAlgorithm, bool aExtractable,
   1371            const Sequence<nsString>& aKeyUsages) {
   1372    mFormat = aFormat;
   1373    mDataIsSet = false;
   1374    mDataIsJwk = false;
   1375 
   1376    // This stuff pretty much always happens, so we'll do it here
   1377    mKey = new CryptoKey(aGlobal);
   1378    mKey->SetExtractable(aExtractable);
   1379    mKey->ClearUsages();
   1380    for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
   1381      mEarlyRv = mKey->AddUsage(aKeyUsages[i]);
   1382      if (NS_FAILED(mEarlyRv)) {
   1383        return;
   1384      }
   1385    }
   1386 
   1387    mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
   1388    if (NS_FAILED(mEarlyRv)) {
   1389      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
   1390      return;
   1391    }
   1392  }
   1393 
   1394  static bool JwkCompatible(const JsonWebKey& aJwk, const CryptoKey* aKey) {
   1395    // Check 'alg'
   1396    if (!aJwk.mKty.EqualsLiteral(JWK_TYPE_OKP) &&
   1397        !(aJwk.mKty.EqualsLiteral(JWK_TYPE_EC) &&
   1398          aKey->Algorithm().Mechanism() == CKM_ECDH1_DERIVE) &&
   1399        aJwk.mAlg.WasPassed() &&
   1400        aJwk.mAlg.Value() != aKey->Algorithm().JwkAlg()) {
   1401      return false;
   1402    }
   1403 
   1404    // Check 'ext'
   1405    if (aKey->Extractable() && aJwk.mExt.WasPassed() && !aJwk.mExt.Value()) {
   1406      return false;
   1407    }
   1408 
   1409    // Check 'key_ops'
   1410    if (aJwk.mKey_ops.WasPassed()) {
   1411      nsTArray<nsString> usages;
   1412      aKey->GetUsages(usages);
   1413      for (size_t i = 0; i < usages.Length(); ++i) {
   1414        if (!aJwk.mKey_ops.Value().Contains(usages[i])) {
   1415          return false;
   1416        }
   1417      }
   1418    }
   1419 
   1420    // Individual algorithms may still have to check 'use'
   1421    return true;
   1422  }
   1423 
   1424  void SetKeyData(JSContext* aCx, JS::Handle<JSObject*> aKeyData) {
   1425    mDataIsJwk = false;
   1426 
   1427    // Try ArrayBuffer
   1428    RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
   1429    if (ab.Init(aKeyData)) {
   1430      if (!mKeyData.Assign(ab)) {
   1431        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
   1432      }
   1433      return;
   1434    }
   1435 
   1436    // Try ArrayBufferView
   1437    RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
   1438    if (abv.Init(aKeyData)) {
   1439      if (!mKeyData.Assign(abv)) {
   1440        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
   1441      }
   1442      return;
   1443    }
   1444 
   1445    // Try JWK
   1446    ClearException ce(aCx);
   1447    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aKeyData));
   1448    if (!mJwk.Init(aCx, value)) {
   1449      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
   1450      return;
   1451    }
   1452 
   1453    mDataIsJwk = true;
   1454  }
   1455 
   1456  void SetKeyDataMaybeParseJWK(const CryptoBuffer& aKeyData) {
   1457    if (!mKeyData.Assign(aKeyData)) {
   1458      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
   1459      return;
   1460    }
   1461 
   1462    mDataIsJwk = false;
   1463 
   1464    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   1465      nsDependentCSubstring utf8(
   1466          (const char*)mKeyData.Elements(),
   1467          (const char*)(mKeyData.Elements() + mKeyData.Length()));
   1468      if (!IsUtf8(utf8)) {
   1469        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
   1470        return;
   1471      }
   1472 
   1473      nsString json = NS_ConvertUTF8toUTF16(utf8);
   1474      if (!mJwk.Init(json)) {
   1475        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
   1476        return;
   1477      }
   1478 
   1479      mDataIsJwk = true;
   1480    }
   1481  }
   1482 
   1483  void SetRawKeyData(const CryptoBuffer& aKeyData) {
   1484    if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
   1485      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
   1486      return;
   1487    }
   1488 
   1489    if (!mKeyData.Assign(aKeyData)) {
   1490      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
   1491      return;
   1492    }
   1493 
   1494    mDataIsJwk = false;
   1495  }
   1496 
   1497 protected:
   1498  nsString mFormat;
   1499  RefPtr<CryptoKey> mKey;
   1500  CryptoBuffer mKeyData;
   1501  bool mDataIsSet;
   1502  bool mDataIsJwk;
   1503  JsonWebKey mJwk;
   1504  nsString mAlgName;
   1505 
   1506 private:
   1507  virtual void Resolve() override { mResultPromise->MaybeResolve(mKey); }
   1508 
   1509  virtual void Cleanup() override { mKey = nullptr; }
   1510 };
   1511 
   1512 class ImportSymmetricKeyTask : public ImportKeyTask {
   1513 public:
   1514  ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
   1515                         const nsAString& aFormat,
   1516                         const ObjectOrString& aAlgorithm, bool aExtractable,
   1517                         const Sequence<nsString>& aKeyUsages) {
   1518    Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
   1519  }
   1520 
   1521  ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
   1522                         const nsAString& aFormat,
   1523                         const JS::Handle<JSObject*> aKeyData,
   1524                         const ObjectOrString& aAlgorithm, bool aExtractable,
   1525                         const Sequence<nsString>& aKeyUsages) {
   1526    Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
   1527    if (NS_FAILED(mEarlyRv)) {
   1528      return;
   1529    }
   1530 
   1531    SetKeyData(aCx, aKeyData);
   1532    NS_ENSURE_SUCCESS_VOID(mEarlyRv);
   1533    if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   1534      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   1535      return;
   1536    }
   1537  }
   1538 
   1539  void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
   1540            const ObjectOrString& aAlgorithm, bool aExtractable,
   1541            const Sequence<nsString>& aKeyUsages) {
   1542    ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
   1543                        aKeyUsages);
   1544    if (NS_FAILED(mEarlyRv)) {
   1545      return;
   1546    }
   1547 
   1548    // This task only supports raw and JWK format.
   1549    if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
   1550        !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
   1551      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1552      return;
   1553    }
   1554 
   1555    // If this is an HMAC key, import the hash name
   1556    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
   1557      RootedDictionary<HmacImportParams> params(aCx);
   1558      mEarlyRv = Coerce(aCx, params, aAlgorithm);
   1559      if (NS_FAILED(mEarlyRv)) {
   1560        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   1561        return;
   1562      }
   1563      mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
   1564      if (NS_FAILED(mEarlyRv)) {
   1565        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   1566        return;
   1567      }
   1568    }
   1569  }
   1570 
   1571  virtual nsresult BeforeCrypto() override {
   1572    nsresult rv;
   1573    // If we're doing a JWK import, import the key data
   1574    if (mDataIsJwk) {
   1575      if (!mJwk.mK.WasPassed()) {
   1576        return NS_ERROR_DOM_DATA_ERR;
   1577      }
   1578 
   1579      // Import the key material
   1580      rv = mKeyData.FromJwkBase64(mJwk.mK.Value());
   1581      if (NS_FAILED(rv)) {
   1582        return NS_ERROR_DOM_DATA_ERR;
   1583      }
   1584    }
   1585    // Check that we have valid key data.
   1586    if (mKeyData.Length() == 0 &&
   1587        (!mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) &&
   1588         !mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HKDF))) {
   1589      return NS_ERROR_DOM_DATA_ERR;
   1590    }
   1591 
   1592    // Construct an appropriate KeyAlgorithm,
   1593    // and verify that usages are appropriate
   1594    if (mKeyData.Length() > UINT32_MAX / 8) {
   1595      return NS_ERROR_DOM_DATA_ERR;
   1596    }
   1597    uint32_t length = 8 * mKeyData.Length();  // bytes to bits
   1598    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
   1599        mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
   1600        mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
   1601        mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
   1602      if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
   1603                                  CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
   1604        return NS_ERROR_DOM_SYNTAX_ERR;
   1605      }
   1606 
   1607      if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) &&
   1608          mKey->HasUsageOtherThan(CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
   1609        return NS_ERROR_DOM_SYNTAX_ERR;
   1610      }
   1611 
   1612      if ((length != 128) && (length != 192) && (length != 256)) {
   1613        return NS_ERROR_DOM_DATA_ERR;
   1614      }
   1615      mKey->Algorithm().MakeAes(mAlgName, length);
   1616 
   1617      if (mDataIsJwk && mJwk.mUse.WasPassed() &&
   1618          !mJwk.mUse.Value().EqualsLiteral(JWK_USE_ENC)) {
   1619        return NS_ERROR_DOM_DATA_ERR;
   1620      }
   1621    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
   1622               mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
   1623      if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY |
   1624                                  CryptoKey::DERIVEBITS)) {
   1625        return NS_ERROR_DOM_SYNTAX_ERR;
   1626      }
   1627      mKey->Algorithm().MakeKDF(mAlgName);
   1628 
   1629      if (mDataIsJwk && mJwk.mUse.WasPassed()) {
   1630        // There is not a 'use' value consistent with PBKDF or HKDF
   1631        return NS_ERROR_DOM_DATA_ERR;
   1632      };
   1633    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
   1634      if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) {
   1635        return NS_ERROR_DOM_SYNTAX_ERR;
   1636      }
   1637 
   1638      mKey->Algorithm().MakeHmac(length, mHashName);
   1639 
   1640      if (mKey->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM) {
   1641        return NS_ERROR_DOM_SYNTAX_ERR;
   1642      }
   1643 
   1644      if (mDataIsJwk && mJwk.mUse.WasPassed() &&
   1645          !mJwk.mUse.Value().EqualsLiteral(JWK_USE_SIG)) {
   1646        return NS_ERROR_DOM_DATA_ERR;
   1647      }
   1648    } else {
   1649      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1650    }
   1651 
   1652    if (!mKey->HasAnyUsage()) {
   1653      return NS_ERROR_DOM_SYNTAX_ERR;
   1654    }
   1655 
   1656    if (NS_FAILED(mKey->SetSymKey(mKeyData))) {
   1657      return NS_ERROR_DOM_OPERATION_ERR;
   1658    }
   1659 
   1660    mKey->SetType(CryptoKey::SECRET);
   1661 
   1662    if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
   1663      return NS_ERROR_DOM_DATA_ERR;
   1664    }
   1665 
   1666    mEarlyComplete = true;
   1667    return NS_OK;
   1668  }
   1669 
   1670 private:
   1671  nsString mHashName;
   1672 };
   1673 
   1674 class ImportRsaKeyTask : public ImportKeyTask {
   1675 public:
   1676  ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
   1677                   const nsAString& aFormat, const ObjectOrString& aAlgorithm,
   1678                   bool aExtractable, const Sequence<nsString>& aKeyUsages)
   1679      : mModulusLength(0) {
   1680    Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
   1681  }
   1682 
   1683  ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
   1684                   const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
   1685                   const ObjectOrString& aAlgorithm, bool aExtractable,
   1686                   const Sequence<nsString>& aKeyUsages)
   1687      : mModulusLength(0) {
   1688    Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
   1689    if (NS_FAILED(mEarlyRv)) {
   1690      return;
   1691    }
   1692 
   1693    SetKeyData(aCx, aKeyData);
   1694    NS_ENSURE_SUCCESS_VOID(mEarlyRv);
   1695    if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   1696      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   1697      return;
   1698    }
   1699  }
   1700 
   1701  void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
   1702            const ObjectOrString& aAlgorithm, bool aExtractable,
   1703            const Sequence<nsString>& aKeyUsages) {
   1704    ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
   1705                        aKeyUsages);
   1706    if (NS_FAILED(mEarlyRv)) {
   1707      return;
   1708    }
   1709 
   1710    // If this is RSA with a hash, cache the hash name
   1711    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
   1712        mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
   1713        mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
   1714      RootedDictionary<RsaHashedImportParams> params(aCx);
   1715      mEarlyRv = Coerce(aCx, params, aAlgorithm);
   1716      if (NS_FAILED(mEarlyRv)) {
   1717        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
   1718        return;
   1719      }
   1720 
   1721      mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
   1722      if (NS_FAILED(mEarlyRv)) {
   1723        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
   1724        return;
   1725      }
   1726    }
   1727 
   1728    // Check support for the algorithm and hash names
   1729    CK_MECHANISM_TYPE mech1 = MapAlgorithmNameToMechanism(mAlgName);
   1730    CK_MECHANISM_TYPE mech2 = MapAlgorithmNameToMechanism(mHashName);
   1731    if ((mech1 == UNKNOWN_CK_MECHANISM) || (mech2 == UNKNOWN_CK_MECHANISM)) {
   1732      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1733      return;
   1734    }
   1735  }
   1736 
   1737 private:
   1738  nsString mHashName;
   1739  uint32_t mModulusLength;
   1740  CryptoBuffer mPublicExponent;
   1741 
   1742  virtual nsresult DoCrypto() override {
   1743    // Import the key data itself
   1744    UniqueSECKEYPublicKey pubKey;
   1745    UniqueSECKEYPrivateKey privKey;
   1746    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
   1747        (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
   1748         !mJwk.mD.WasPassed())) {
   1749      // Public key import
   1750      if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
   1751        pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
   1752      } else {
   1753        pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
   1754      }
   1755 
   1756      if (!pubKey) {
   1757        return NS_ERROR_DOM_DATA_ERR;
   1758      }
   1759 
   1760      if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
   1761        return NS_ERROR_DOM_OPERATION_ERR;
   1762      }
   1763 
   1764      mKey->SetType(CryptoKey::PUBLIC);
   1765    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) ||
   1766               (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
   1767                mJwk.mD.WasPassed())) {
   1768      // Private key import
   1769      if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
   1770        privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
   1771      } else {
   1772        privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
   1773      }
   1774 
   1775      if (!privKey) {
   1776        return NS_ERROR_DOM_DATA_ERR;
   1777      }
   1778 
   1779      if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
   1780        return NS_ERROR_DOM_OPERATION_ERR;
   1781      }
   1782 
   1783      mKey->SetType(CryptoKey::PRIVATE);
   1784      pubKey = UniqueSECKEYPublicKey(SECKEY_ConvertToPublicKey(privKey.get()));
   1785      if (!pubKey) {
   1786        return NS_ERROR_DOM_UNKNOWN_ERR;
   1787      }
   1788    } else {
   1789      // Invalid key format
   1790      return NS_ERROR_DOM_SYNTAX_ERR;
   1791    }
   1792 
   1793    if (pubKey->keyType != rsaKey) {
   1794      return NS_ERROR_DOM_DATA_ERR;
   1795    }
   1796    // Extract relevant information from the public key
   1797    mModulusLength = 8 * pubKey->u.rsa.modulus.len;
   1798    if (!mPublicExponent.Assign(&pubKey->u.rsa.publicExponent)) {
   1799      return NS_ERROR_DOM_OPERATION_ERR;
   1800    }
   1801 
   1802    return NS_OK;
   1803  }
   1804 
   1805  virtual nsresult AfterCrypto() override {
   1806    // Check permissions for the requested operation
   1807    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
   1808      if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
   1809           mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) ||
   1810          (mKey->GetKeyType() == CryptoKey::PRIVATE &&
   1811           mKey->HasUsageOtherThan(CryptoKey::DECRYPT |
   1812                                   CryptoKey::UNWRAPKEY))) {
   1813        return NS_ERROR_DOM_SYNTAX_ERR;
   1814      }
   1815    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
   1816               mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
   1817      if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
   1818           mKey->HasUsageOtherThan(CryptoKey::VERIFY)) ||
   1819          (mKey->GetKeyType() == CryptoKey::PRIVATE &&
   1820           mKey->HasUsageOtherThan(CryptoKey::SIGN))) {
   1821        return NS_ERROR_DOM_SYNTAX_ERR;
   1822      }
   1823    }
   1824 
   1825    if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
   1826      return NS_ERROR_DOM_SYNTAX_ERR;
   1827    }
   1828 
   1829    // Set an appropriate KeyAlgorithm
   1830    if (!mKey->Algorithm().MakeRsa(mAlgName, mModulusLength, mPublicExponent,
   1831                                   mHashName)) {
   1832      return NS_ERROR_DOM_OPERATION_ERR;
   1833    }
   1834 
   1835    if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
   1836      return NS_ERROR_DOM_DATA_ERR;
   1837    }
   1838 
   1839    return NS_OK;
   1840  }
   1841 };
   1842 
   1843 class ImportEcKeyTask : public ImportKeyTask {
   1844 public:
   1845  ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
   1846                  const nsAString& aFormat, const ObjectOrString& aAlgorithm,
   1847                  bool aExtractable, const Sequence<nsString>& aKeyUsages) {
   1848    Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
   1849  }
   1850 
   1851  ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
   1852                  const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
   1853                  const ObjectOrString& aAlgorithm, bool aExtractable,
   1854                  const Sequence<nsString>& aKeyUsages) {
   1855    Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
   1856    if (NS_FAILED(mEarlyRv)) {
   1857      return;
   1858    }
   1859 
   1860    SetKeyData(aCx, aKeyData);
   1861    NS_ENSURE_SUCCESS_VOID(mEarlyRv);
   1862  }
   1863 
   1864  void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
   1865            const ObjectOrString& aAlgorithm, bool aExtractable,
   1866            const Sequence<nsString>& aKeyUsages) {
   1867    ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
   1868                        aKeyUsages);
   1869    if (NS_FAILED(mEarlyRv)) {
   1870      return;
   1871    }
   1872 
   1873    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
   1874        mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   1875      RootedDictionary<EcKeyImportParams> params(aCx);
   1876      mEarlyRv = Coerce(aCx, params, aAlgorithm);
   1877      if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
   1878        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   1879        return;
   1880      }
   1881 
   1882      if (!NormalizeToken(params.mNamedCurve.Value(), mNamedCurve)) {
   1883        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1884        return;
   1885      }
   1886    }
   1887  }
   1888 
   1889 private:
   1890  nsString mNamedCurve;
   1891 
   1892  virtual nsresult DoCrypto() override {
   1893    // Import the key data itself
   1894    UniqueSECKEYPublicKey pubKey;
   1895    UniqueSECKEYPrivateKey privKey;
   1896 
   1897    if ((mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
   1898         mJwk.mD.WasPassed()) ||
   1899        mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
   1900      // Private key import
   1901      if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   1902        privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
   1903        if (!privKey) {
   1904          return NS_ERROR_DOM_DATA_ERR;
   1905        }
   1906      } else {
   1907        privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
   1908        if (!privKey) {
   1909          return NS_ERROR_DOM_DATA_ERR;
   1910        }
   1911 
   1912        ScopedAutoSECItem ecParams;
   1913        if (PK11_ReadRawAttribute(PK11_TypePrivKey, privKey.get(),
   1914                                  CKA_EC_PARAMS, &ecParams) != SECSuccess) {
   1915          return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1916        }
   1917 
   1918        SECOidTag tag;
   1919        if (!FindOIDTagForEncodedParameters(&ecParams, &tag)) {
   1920          return NS_ERROR_DOM_DATA_ERR;
   1921        }
   1922 
   1923        // Find a matching and supported named curve.
   1924        if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
   1925          return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1926        }
   1927      }
   1928 
   1929      if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
   1930        return NS_ERROR_DOM_OPERATION_ERR;
   1931      }
   1932 
   1933      mKey->SetType(CryptoKey::PRIVATE);
   1934    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
   1935               mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
   1936               (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
   1937                !mJwk.mD.WasPassed())) {
   1938      // Public key import
   1939      if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
   1940        pubKey = CryptoKey::PublicECKeyFromRaw(mKeyData, mNamedCurve);
   1941      } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
   1942        pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
   1943      } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   1944        pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
   1945      } else {
   1946        MOZ_ASSERT(false);
   1947      }
   1948 
   1949      if (!pubKey) {
   1950        return NS_ERROR_DOM_DATA_ERR;
   1951      }
   1952 
   1953      if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
   1954        if (pubKey->keyType != ecKey) {
   1955          return NS_ERROR_DOM_DATA_ERR;
   1956        }
   1957        if (!CheckEncodedParameters(&pubKey->u.ec.DEREncodedParams)) {
   1958          return NS_ERROR_DOM_OPERATION_ERR;
   1959        }
   1960 
   1961        SECOidTag tag;
   1962        if (!FindOIDTagForEncodedParameters(&pubKey->u.ec.DEREncodedParams,
   1963                                            &tag)) {
   1964          return NS_ERROR_DOM_DATA_ERR;
   1965        }
   1966 
   1967        // Find a matching and supported named curve.
   1968        if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
   1969          return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1970        }
   1971      }
   1972 
   1973      if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
   1974        return NS_ERROR_DOM_OPERATION_ERR;
   1975      }
   1976 
   1977      mKey->SetType(CryptoKey::PUBLIC);
   1978    } else {
   1979      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1980    }
   1981 
   1982    // Checking the 'crv' consistency
   1983    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   1984      // the curve stated in 'crv field'
   1985      nsString namedCurveFromCrv;
   1986      if (!NormalizeToken(mJwk.mCrv.Value(), namedCurveFromCrv)) {
   1987        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   1988      }
   1989 
   1990      // https://w3c.github.io/webcrypto/#ecdh-operations
   1991      // https://w3c.github.io/webcrypto/#ecdsa-operations
   1992      // If namedCurve is not equal to the namedCurve member of
   1993      // normalizedAlgorithm (mNamedCurve in our case), throw a DataError.
   1994      if (!mNamedCurve.Equals(namedCurveFromCrv)) {
   1995        return NS_ERROR_DOM_DATA_ERR;
   1996      }
   1997    }
   1998    return NS_OK;
   1999  }
   2000 
   2001  virtual nsresult AfterCrypto() override {
   2002    uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
   2003    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
   2004      privateAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
   2005      publicAllowedUsages = 0;
   2006    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
   2007      privateAllowedUsages = CryptoKey::SIGN;
   2008      publicAllowedUsages = CryptoKey::VERIFY;
   2009    }
   2010 
   2011    // Check permissions for the requested operation
   2012    if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
   2013         mKey->HasUsageOtherThan(privateAllowedUsages)) ||
   2014        (mKey->GetKeyType() == CryptoKey::PUBLIC &&
   2015         mKey->HasUsageOtherThan(publicAllowedUsages))) {
   2016      return NS_ERROR_DOM_SYNTAX_ERR;
   2017    }
   2018 
   2019    if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
   2020      return NS_ERROR_DOM_SYNTAX_ERR;
   2021    }
   2022 
   2023    mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
   2024 
   2025    if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
   2026      return NS_ERROR_DOM_DATA_ERR;
   2027    }
   2028 
   2029    return NS_OK;
   2030  }
   2031 };
   2032 
   2033 class ImportOKPKeyTask : public ImportKeyTask {
   2034 public:
   2035  ImportOKPKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
   2036                   const nsAString& aFormat, const ObjectOrString& aAlgorithm,
   2037                   bool aExtractable, const Sequence<nsString>& aKeyUsages) {
   2038    Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
   2039  }
   2040 
   2041  ImportOKPKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
   2042                   const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
   2043                   const ObjectOrString& aAlgorithm, bool aExtractable,
   2044                   const Sequence<nsString>& aKeyUsages) {
   2045    Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
   2046    if (NS_FAILED(mEarlyRv)) {
   2047      return;
   2048    }
   2049 
   2050    SetKeyData(aCx, aKeyData);
   2051    NS_ENSURE_SUCCESS_VOID(mEarlyRv);
   2052  }
   2053 
   2054  void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
   2055            const ObjectOrString& aAlgorithm, bool aExtractable,
   2056            const Sequence<nsString>& aKeyUsages) {
   2057    ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
   2058                        aKeyUsages);
   2059    if (NS_FAILED(mEarlyRv)) {
   2060      return;
   2061    }
   2062 
   2063    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
   2064      nsString paramsAlgName;
   2065      mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, paramsAlgName);
   2066      if (NS_FAILED(mEarlyRv)) {
   2067        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   2068        return;
   2069      }
   2070 
   2071      nsString algName;
   2072      if (!NormalizeToken(paramsAlgName, algName)) {
   2073        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2074        return;
   2075      }
   2076 
   2077      // Construct an appropriate KeyAlgorithm
   2078      if (algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
   2079        mNamedCurve.AssignLiteral(WEBCRYPTO_NAMED_CURVE_ED25519);
   2080      } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
   2081        mNamedCurve.AssignLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519);
   2082      } else {
   2083        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2084        return;
   2085      }
   2086    }
   2087  }
   2088 
   2089 private:
   2090  nsString mNamedCurve;
   2091 
   2092  virtual nsresult DoCrypto() override {
   2093    // Import the key data itself
   2094    UniqueSECKEYPublicKey pubKey;
   2095    UniqueSECKEYPrivateKey privKey;
   2096 
   2097    if ((mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
   2098         mJwk.mD.WasPassed()) ||
   2099        mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
   2100      // Private key import
   2101      if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   2102        privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
   2103        if (!privKey) {
   2104          return NS_ERROR_DOM_DATA_ERR;
   2105        }
   2106      } else {
   2107        privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
   2108        if (!privKey) {
   2109          return NS_ERROR_DOM_DATA_ERR;
   2110        }
   2111 
   2112        ScopedAutoSECItem ecParams;
   2113        if (PK11_ReadRawAttribute(PK11_TypePrivKey, privKey.get(),
   2114                                  CKA_EC_PARAMS, &ecParams) != SECSuccess) {
   2115          return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2116        }
   2117 
   2118        SECOidTag tag;
   2119        if (!FindOIDTagForEncodedParameters(&ecParams, &tag)) {
   2120          return NS_ERROR_DOM_DATA_ERR;
   2121        }
   2122 
   2123        // Find a matching and supported named curve.
   2124        if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
   2125          return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2126        }
   2127      }
   2128 
   2129      if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
   2130        return NS_ERROR_DOM_OPERATION_ERR;
   2131      }
   2132 
   2133      mKey->SetType(CryptoKey::PRIVATE);
   2134    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
   2135               mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
   2136               (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
   2137                !mJwk.mD.WasPassed())) {
   2138      // Public key import
   2139      if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
   2140        pubKey = CryptoKey::PublicOKPKeyFromRaw(mKeyData, mNamedCurve);
   2141      } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
   2142        pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
   2143      } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   2144        pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
   2145      } else {
   2146        MOZ_ASSERT(false);
   2147      }
   2148 
   2149      if (!pubKey) {
   2150        return NS_ERROR_DOM_DATA_ERR;
   2151      }
   2152 
   2153      if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
   2154        if (pubKey->keyType != edKey && pubKey->keyType != ecMontKey) {
   2155          return NS_ERROR_DOM_DATA_ERR;
   2156        }
   2157        if (!CheckEncodedParameters(&pubKey->u.ec.DEREncodedParams)) {
   2158          return NS_ERROR_DOM_OPERATION_ERR;
   2159        }
   2160 
   2161        SECOidTag tag;
   2162        if (!FindOIDTagForEncodedParameters(&pubKey->u.ec.DEREncodedParams,
   2163                                            &tag)) {
   2164          return NS_ERROR_DOM_OPERATION_ERR;
   2165        }
   2166 
   2167        // Find a matching and supported named curve.
   2168        if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
   2169          return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2170        }
   2171      }
   2172 
   2173      if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
   2174        return NS_ERROR_DOM_OPERATION_ERR;
   2175      }
   2176 
   2177      mKey->SetType(CryptoKey::PUBLIC);
   2178    } else {
   2179      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2180    }
   2181 
   2182    // Extract 'crv' and "alg" parameter from JWKs.
   2183    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   2184      if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
   2185        if (mJwk.mAlg.WasPassed() &&
   2186            !mJwk.mAlg.Value().EqualsLiteral(JWK_ALG_EDDSA) &&
   2187            !mJwk.mAlg.Value().EqualsLiteral(JWK_ALG_ED25519)) {
   2188          return NS_ERROR_DOM_DATA_ERR;
   2189        }
   2190      }
   2191      if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) {
   2192        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2193      }
   2194    }
   2195 
   2196    return NS_OK;
   2197  }
   2198 
   2199  virtual nsresult AfterCrypto() override {
   2200    // Only Ed25519 is supported.
   2201    uint32_t privateAllowedUsages = 0;
   2202    uint32_t publicAllowedUsages = 0;
   2203 
   2204    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
   2205      privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
   2206      publicAllowedUsages = 0;
   2207    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
   2208      privateAllowedUsages = CryptoKey::SIGN;
   2209      publicAllowedUsages = CryptoKey::VERIFY;
   2210    }
   2211 
   2212    // Check permissions for the requested operation
   2213    if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
   2214         mKey->HasUsageOtherThan(publicAllowedUsages))) {
   2215      return NS_ERROR_DOM_SYNTAX_ERR;
   2216    }
   2217 
   2218    if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
   2219         mKey->HasUsageOtherThan(privateAllowedUsages))) {
   2220      return NS_ERROR_DOM_SYNTAX_ERR;
   2221    }
   2222 
   2223    if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
   2224      return NS_ERROR_DOM_SYNTAX_ERR;
   2225    }
   2226 
   2227    mKey->Algorithm().MakeOKP(mAlgName);
   2228 
   2229    if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
   2230      return NS_ERROR_DOM_DATA_ERR;
   2231    }
   2232 
   2233    return NS_OK;
   2234  }
   2235 };
   2236 
   2237 class ExportKeyTask : public WebCryptoTask {
   2238 public:
   2239  ExportKeyTask(const nsAString& aFormat, CryptoKey& aKey)
   2240      : mFormat(aFormat),
   2241        mPrivateKey(aKey.GetPrivateKey()),
   2242        mPublicKey(aKey.GetPublicKey()),
   2243        mKeyType(aKey.GetKeyType()),
   2244        mExtractable(aKey.Extractable()),
   2245        mAlg(aKey.Algorithm().JwkAlg()) {
   2246    aKey.GetUsages(mKeyUsages);
   2247 
   2248    if (!mSymKey.Assign(aKey.GetSymKey())) {
   2249      mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
   2250      return;
   2251    }
   2252  }
   2253 
   2254 protected:
   2255  nsString mFormat;
   2256  CryptoBuffer mSymKey;
   2257  UniqueSECKEYPrivateKey mPrivateKey;
   2258  UniqueSECKEYPublicKey mPublicKey;
   2259  CryptoKey::KeyType mKeyType;
   2260  bool mExtractable;
   2261  nsString mAlg;
   2262  nsTArray<nsString> mKeyUsages;
   2263  CryptoBuffer mResult;
   2264  JsonWebKey mJwk;
   2265 
   2266 private:
   2267  virtual nsresult DoCrypto() override {
   2268    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
   2269      if (mPublicKey && mPublicKey->keyType == dhKey) {
   2270        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2271      }
   2272 
   2273      if (mPublicKey &&
   2274          (mPublicKey->keyType == ecKey || mPublicKey->keyType == edKey ||
   2275           mPublicKey->keyType == ecMontKey)) {
   2276        nsresult rv = CryptoKey::PublicECKeyToRaw(mPublicKey.get(), mResult);
   2277        if (NS_FAILED(rv)) {
   2278          return NS_ERROR_DOM_OPERATION_ERR;
   2279        }
   2280        return NS_OK;
   2281      }
   2282 
   2283      if (!mResult.Assign(mSymKey)) {
   2284        return NS_ERROR_OUT_OF_MEMORY;
   2285      }
   2286      if (mResult.Length() == 0) {
   2287        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2288      }
   2289 
   2290      return NS_OK;
   2291    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
   2292      if (!mPrivateKey) {
   2293        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2294      }
   2295 
   2296      switch (mPrivateKey->keyType) {
   2297        case rsaKey:
   2298        case edKey:
   2299        case ecKey:
   2300        case ecMontKey: {
   2301          nsresult rv =
   2302              CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), mResult);
   2303          if (NS_FAILED(rv)) {
   2304            return NS_ERROR_DOM_OPERATION_ERR;
   2305          }
   2306          return NS_OK;
   2307        }
   2308        default:
   2309          return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2310      }
   2311    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
   2312      if (!mPublicKey) {
   2313        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2314      }
   2315 
   2316      return CryptoKey::PublicKeyToSpki(mPublicKey.get(), mResult);
   2317    } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   2318      if (mKeyType == CryptoKey::SECRET) {
   2319        nsString k;
   2320        nsresult rv = mSymKey.ToJwkBase64(k);
   2321        if (NS_FAILED(rv)) {
   2322          return NS_ERROR_DOM_OPERATION_ERR;
   2323        }
   2324        mJwk.mK.Construct(k);
   2325        mJwk.mKty = NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_SYMMETRIC);
   2326      } else if (mKeyType == CryptoKey::PUBLIC) {
   2327        if (!mPublicKey) {
   2328          return NS_ERROR_DOM_UNKNOWN_ERR;
   2329        }
   2330 
   2331        nsresult rv = CryptoKey::PublicKeyToJwk(mPublicKey.get(), mJwk);
   2332        if (NS_FAILED(rv)) {
   2333          return NS_ERROR_DOM_OPERATION_ERR;
   2334        }
   2335      } else if (mKeyType == CryptoKey::PRIVATE) {
   2336        if (!mPrivateKey) {
   2337          return NS_ERROR_DOM_UNKNOWN_ERR;
   2338        }
   2339 
   2340        nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey.get(), mJwk);
   2341        if (NS_FAILED(rv)) {
   2342          return NS_ERROR_DOM_OPERATION_ERR;
   2343        }
   2344      }
   2345 
   2346      if (!mAlg.IsEmpty()) {
   2347        mJwk.mAlg.Construct(mAlg);
   2348      }
   2349 
   2350      mJwk.mExt.Construct(mExtractable);
   2351 
   2352      mJwk.mKey_ops.Construct();
   2353      if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
   2354        return NS_ERROR_OUT_OF_MEMORY;
   2355      }
   2356 
   2357      return NS_OK;
   2358    }
   2359 
   2360    return NS_ERROR_DOM_SYNTAX_ERR;
   2361  }
   2362 
   2363  // Returns mResult as an ArrayBufferView or JWK, as appropriate
   2364  virtual void Resolve() override {
   2365    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   2366      mResultPromise->MaybeResolve(mJwk);
   2367      return;
   2368    }
   2369 
   2370    TypedArrayCreator<ArrayBuffer> ret(mResult);
   2371    mResultPromise->MaybeResolve(ret);
   2372  }
   2373 };
   2374 
   2375 class GenerateSymmetricKeyTask : public WebCryptoTask {
   2376 public:
   2377  GenerateSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
   2378                           const ObjectOrString& aAlgorithm, bool aExtractable,
   2379                           const Sequence<nsString>& aKeyUsages) {
   2380    // Create an empty key and set easy attributes
   2381    mKey = new CryptoKey(aGlobal);
   2382    mKey->SetExtractable(aExtractable);
   2383    mKey->SetType(CryptoKey::SECRET);
   2384 
   2385    // Extract algorithm name
   2386    nsString algName;
   2387    mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
   2388    if (NS_FAILED(mEarlyRv)) {
   2389      return;
   2390    }
   2391 
   2392    // Construct an appropriate KeyAlorithm
   2393    if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
   2394        algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
   2395        algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
   2396        algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
   2397      mEarlyRv = GetKeyLengthForAlgorithm(aCx, aAlgorithm, mLength);
   2398      if (NS_FAILED(mEarlyRv)) {
   2399        return;
   2400      }
   2401      mKey->Algorithm().MakeAes(algName, mLength);
   2402 
   2403    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
   2404      RootedDictionary<HmacKeyGenParams> params(aCx);
   2405      mEarlyRv = Coerce(aCx, params, aAlgorithm);
   2406      if (NS_FAILED(mEarlyRv)) {
   2407        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   2408        return;
   2409      }
   2410 
   2411      nsString hashName;
   2412      mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
   2413      if (NS_FAILED(mEarlyRv)) {
   2414        return;
   2415      }
   2416 
   2417      if (params.mLength.WasPassed()) {
   2418        mLength = params.mLength.Value();
   2419      } else {
   2420        mLength = MapHashAlgorithmNameToBlockSize(hashName);
   2421      }
   2422 
   2423      if (mLength == 0) {
   2424        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
   2425        return;
   2426      }
   2427 
   2428      mKey->Algorithm().MakeHmac(mLength, hashName);
   2429    } else {
   2430      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2431      return;
   2432    }
   2433 
   2434    // Add key usages
   2435    mKey->ClearUsages();
   2436    for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
   2437      mEarlyRv = mKey->AddAllowedUsageIntersecting(aKeyUsages[i], algName);
   2438      if (NS_FAILED(mEarlyRv)) {
   2439        return;
   2440      }
   2441    }
   2442    if (!mKey->HasAnyUsage()) {
   2443      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   2444      return;
   2445    }
   2446 
   2447    mLength = mLength >> 3;  // bits to bytes
   2448    mMechanism = mKey->Algorithm().Mechanism();
   2449    // SetSymKey done in Resolve, after we've done the keygen
   2450  }
   2451 
   2452 private:
   2453  RefPtr<CryptoKey> mKey;
   2454  size_t mLength;
   2455  CK_MECHANISM_TYPE mMechanism;
   2456  CryptoBuffer mKeyData;
   2457 
   2458  virtual nsresult DoCrypto() override {
   2459    UniquePK11SlotInfo slot(PK11_GetInternalSlot());
   2460    MOZ_ASSERT(slot.get());
   2461 
   2462    UniquePK11SymKey symKey(
   2463        PK11_KeyGen(slot.get(), mMechanism, nullptr, mLength, nullptr));
   2464    if (!symKey) {
   2465      return NS_ERROR_DOM_UNKNOWN_ERR;
   2466    }
   2467 
   2468    nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
   2469    if (NS_FAILED(rv)) {
   2470      return NS_ERROR_DOM_UNKNOWN_ERR;
   2471    }
   2472 
   2473    // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
   2474    // just refers to a buffer managed by symKey.  The assignment copies the
   2475    // data, so mKeyData manages one copy, while symKey manages another.
   2476    ATTEMPT_BUFFER_ASSIGN(mKeyData, PK11_GetKeyData(symKey.get()));
   2477    return NS_OK;
   2478  }
   2479 
   2480  virtual void Resolve() override {
   2481    if (NS_SUCCEEDED(mKey->SetSymKey(mKeyData))) {
   2482      mResultPromise->MaybeResolve(mKey);
   2483    } else {
   2484      mResultPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
   2485    }
   2486  }
   2487 
   2488  virtual void Cleanup() override { mKey = nullptr; }
   2489 };
   2490 
   2491 class DeriveX25519BitsTask : public ReturnArrayBufferViewTask {
   2492 public:
   2493  DeriveX25519BitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
   2494                       CryptoKey& aKey, const Nullable<uint32_t>& aLength)
   2495      : mLength(aLength), mPrivKey(aKey.GetPrivateKey()) {
   2496    Init(aCx, aAlgorithm, aKey);
   2497  }
   2498 
   2499  DeriveX25519BitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
   2500                       CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
   2501      : mPrivKey(aKey.GetPrivateKey()) {
   2502    Maybe<size_t> lengthInBits;
   2503    mEarlyRv = GetKeyLengthForAlgorithmIfSpecified(aCx, aTargetAlgorithm,
   2504                                                   lengthInBits);
   2505    if (lengthInBits.isNothing()) {
   2506      mLength.SetNull();
   2507    } else {
   2508      mLength.SetValue(*lengthInBits);
   2509    }
   2510    if (NS_SUCCEEDED(mEarlyRv)) {
   2511      Init(aCx, aAlgorithm, aKey);
   2512    }
   2513  }
   2514 
   2515  void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey) {
   2516    glean::webcrypto::alg.AccumulateSingleSample(TA_X25519);
   2517    CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_X25519);
   2518 
   2519    // Check that we have a private key.
   2520    if (!mPrivKey) {
   2521      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
   2522      return;
   2523    }
   2524 
   2525    // If specified, length must be a multiple of 8.
   2526    if (!mLength.IsNull()) {
   2527      if (mLength.Value() % 8) {
   2528        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
   2529        return;
   2530      }
   2531      mLength.SetValue(mLength.Value() >> 3);  // bits to bytes
   2532    }
   2533 
   2534    // Retrieve the peer's public key.
   2535    RootedDictionary<EcdhKeyDeriveParams> params(aCx);
   2536    mEarlyRv = Coerce(aCx, params, aAlgorithm);
   2537 
   2538    if (NS_FAILED(mEarlyRv)) {
   2539      /* The returned code is installed by Coerce function. */
   2540      return;
   2541    }
   2542 
   2543    CHECK_KEY_ALGORITHM(params.mPublic->Algorithm(), WEBCRYPTO_ALG_X25519);
   2544 
   2545    CryptoKey* publicKey = params.mPublic;
   2546    mPubKey = publicKey->GetPublicKey();
   2547    if (!mPubKey) {
   2548      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
   2549      return;
   2550    }
   2551  }
   2552 
   2553 private:
   2554  Nullable<uint32_t> mLength;
   2555  UniqueSECKEYPrivateKey mPrivKey;
   2556  UniqueSECKEYPublicKey mPubKey;
   2557 
   2558  virtual nsresult DoCrypto() override {
   2559    // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
   2560    // derived symmetric key and don't matter because we ignore them anyway.
   2561 
   2562    // Derive Bits requires checking that the generated key is not all-zero
   2563    // value. See:
   2564    // https://wicg.github.io/webcrypto-secure-curves/#x25519-operations This
   2565    // step is performed internally inside PK11_PubDeriveWithKDF function.
   2566    UniquePK11SymKey symKey(
   2567        PK11_PubDeriveWithKDF(mPrivKey.get(), mPubKey.get(), PR_FALSE, nullptr,
   2568                              nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC,
   2569                              CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr));
   2570 
   2571    if (!symKey.get()) {
   2572      return NS_ERROR_DOM_OPERATION_ERR;
   2573    }
   2574 
   2575    nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
   2576    if (NS_FAILED(rv)) {
   2577      return NS_ERROR_DOM_OPERATION_ERR;
   2578    }
   2579 
   2580    // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
   2581    // just refers to a buffer managed by symKey. The assignment copies the
   2582    // data, so mResult manages one copy, while symKey manages another.
   2583    ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
   2584 
   2585    if (!mLength.IsNull()) {
   2586      if (mLength.Value() > mResult.Length()) {
   2587        return NS_ERROR_DOM_OPERATION_ERR;
   2588      }
   2589      if (!mResult.SetLength(mLength.Value(), fallible)) {
   2590        return NS_ERROR_DOM_UNKNOWN_ERR;
   2591      }
   2592    }
   2593 
   2594    return NS_OK;
   2595  }
   2596 };
   2597 
   2598 GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask(
   2599    nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
   2600    bool aExtractable, const Sequence<nsString>& aKeyUsages)
   2601    : mKeyPair(new CryptoKeyPair()),
   2602      mMechanism(CKM_INVALID_MECHANISM),
   2603      mRsaParams(),
   2604      mDhParams() {
   2605  mArena = UniquePLArenaPool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   2606  if (!mArena) {
   2607    mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
   2608    return;
   2609  }
   2610 
   2611  // Create an empty key pair and set easy attributes
   2612  mKeyPair->mPrivateKey = new CryptoKey(aGlobal);
   2613  mKeyPair->mPublicKey = new CryptoKey(aGlobal);
   2614 
   2615  // Extract algorithm name
   2616  mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
   2617  if (NS_FAILED(mEarlyRv)) {
   2618    return;
   2619  }
   2620 
   2621  // Construct an appropriate KeyAlorithm
   2622  uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
   2623  if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
   2624      mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
   2625      mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
   2626    RootedDictionary<RsaHashedKeyGenParams> params(aCx);
   2627    mEarlyRv = Coerce(aCx, params, aAlgorithm);
   2628    if (NS_FAILED(mEarlyRv)) {
   2629      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   2630      return;
   2631    }
   2632 
   2633    // Pull relevant info
   2634    uint32_t modulusLength = params.mModulusLength;
   2635    CryptoBuffer publicExponent;
   2636    ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
   2637    nsString hashName;
   2638    mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
   2639    if (NS_FAILED(mEarlyRv)) {
   2640      return;
   2641    }
   2642 
   2643    // Create algorithm
   2644    if (!mKeyPair->mPublicKey->Algorithm().MakeRsa(mAlgName, modulusLength,
   2645                                                   publicExponent, hashName)) {
   2646      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
   2647      return;
   2648    }
   2649    if (!mKeyPair->mPrivateKey->Algorithm().MakeRsa(mAlgName, modulusLength,
   2650                                                    publicExponent, hashName)) {
   2651      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
   2652      return;
   2653    }
   2654    mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
   2655 
   2656    // Set up params struct
   2657    mRsaParams.keySizeInBits = modulusLength;
   2658    bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
   2659    if (!converted) {
   2660      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
   2661      return;
   2662    }
   2663  } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
   2664             mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
   2665    RootedDictionary<EcKeyGenParams> params(aCx);
   2666    mEarlyRv = Coerce(aCx, params, aAlgorithm);
   2667    if (NS_FAILED(mEarlyRv)) {
   2668      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   2669      return;
   2670    }
   2671 
   2672    if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
   2673      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2674      return;
   2675    }
   2676 
   2677    // Create algorithm.
   2678    mKeyPair->mPublicKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
   2679    mKeyPair->mPrivateKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
   2680    mMechanism = CKM_EC_KEY_PAIR_GEN;
   2681  }
   2682 
   2683  else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
   2684    mKeyPair->mPublicKey->Algorithm().MakeOKP(mAlgName);
   2685    mKeyPair->mPrivateKey->Algorithm().MakeOKP(mAlgName);
   2686    mMechanism = CKM_EC_MONTGOMERY_KEY_PAIR_GEN;
   2687    mNamedCurve.AssignLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519);
   2688  }
   2689 
   2690  else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
   2691    mKeyPair->mPublicKey->Algorithm().MakeOKP(mAlgName);
   2692    mKeyPair->mPrivateKey->Algorithm().MakeOKP(mAlgName);
   2693    mMechanism = CKM_EC_EDWARDS_KEY_PAIR_GEN;
   2694    mNamedCurve.AssignLiteral(WEBCRYPTO_NAMED_CURVE_ED25519);
   2695  }
   2696 
   2697  else {
   2698    mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2699    return;
   2700  }
   2701 
   2702  // Set key usages.
   2703  if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
   2704      mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
   2705      mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
   2706      mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
   2707    privateAllowedUsages = CryptoKey::SIGN;
   2708    publicAllowedUsages = CryptoKey::VERIFY;
   2709  } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
   2710    privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
   2711    publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
   2712  } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
   2713             mAlgName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
   2714    privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
   2715    publicAllowedUsages = 0;
   2716  } else {
   2717    MOZ_ASSERT(false);  // This shouldn't happen.
   2718  }
   2719 
   2720  mKeyPair->mPrivateKey->SetExtractable(aExtractable);
   2721  mKeyPair->mPrivateKey->SetType(CryptoKey::PRIVATE);
   2722 
   2723  mKeyPair->mPublicKey->SetExtractable(true);
   2724  mKeyPair->mPublicKey->SetType(CryptoKey::PUBLIC);
   2725 
   2726  mKeyPair->mPrivateKey->ClearUsages();
   2727  mKeyPair->mPublicKey->ClearUsages();
   2728  for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
   2729    mEarlyRv = mKeyPair->mPrivateKey->AddAllowedUsageIntersecting(
   2730        aKeyUsages[i], mAlgName, privateAllowedUsages);
   2731    if (NS_FAILED(mEarlyRv)) {
   2732      return;
   2733    }
   2734 
   2735    mEarlyRv = mKeyPair->mPublicKey->AddAllowedUsageIntersecting(
   2736        aKeyUsages[i], mAlgName, publicAllowedUsages);
   2737    if (NS_FAILED(mEarlyRv)) {
   2738      return;
   2739    }
   2740  }
   2741 }
   2742 
   2743 nsresult GenerateAsymmetricKeyTask::DoCrypto() {
   2744  MOZ_ASSERT(mKeyPair);
   2745 
   2746  UniquePK11SlotInfo slot(PK11_GetInternalSlot());
   2747  MOZ_ASSERT(slot.get());
   2748 
   2749  void* param;
   2750  switch (mMechanism) {
   2751    case CKM_RSA_PKCS_KEY_PAIR_GEN:
   2752      param = &mRsaParams;
   2753      break;
   2754    case CKM_DH_PKCS_KEY_PAIR_GEN:
   2755      param = &mDhParams;
   2756      break;
   2757    case CKM_EC_MONTGOMERY_KEY_PAIR_GEN:
   2758    case CKM_EC_EDWARDS_KEY_PAIR_GEN:
   2759    case CKM_EC_KEY_PAIR_GEN: {
   2760      param = CreateECParamsForCurve(mNamedCurve, mArena.get());
   2761      if (!param) {
   2762        return NS_ERROR_DOM_UNKNOWN_ERR;
   2763      }
   2764      break;
   2765    }
   2766    default:
   2767      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2768  }
   2769 
   2770  mPrivateKey = UniqueSECKEYPrivateKey(PK11_GenerateKeyPair(
   2771      slot.get(), mMechanism, param, TempPtrToSetter(&mPublicKey), PR_FALSE,
   2772      PR_FALSE, nullptr));
   2773 
   2774  if (!mPrivateKey.get() || !mPublicKey.get()) {
   2775    return NS_ERROR_DOM_OPERATION_ERR;
   2776  }
   2777 
   2778  // If no usages ended up being allowed, SyntaxError
   2779  if (!mKeyPair->mPrivateKey->HasAnyUsage()) {
   2780    return NS_ERROR_DOM_SYNTAX_ERR;
   2781  }
   2782 
   2783  nsresult rv = mKeyPair->mPrivateKey->SetPrivateKey(mPrivateKey.get());
   2784  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
   2785  rv = mKeyPair->mPublicKey->SetPublicKey(mPublicKey.get());
   2786  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
   2787  // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
   2788  // private key, we need this later when exporting to PKCS8 and JWK though.
   2789  if (mMechanism == CKM_EC_KEY_PAIR_GEN ||
   2790      mMechanism == CKM_EC_MONTGOMERY_KEY_PAIR_GEN ||
   2791      mMechanism == CKM_EC_EDWARDS_KEY_PAIR_GEN) {
   2792    rv = mKeyPair->mPrivateKey->AddPublicKeyData(mPublicKey.get());
   2793    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
   2794  }
   2795 
   2796  return NS_OK;
   2797 }
   2798 
   2799 void GenerateAsymmetricKeyTask::Resolve() {
   2800  mResultPromise->MaybeResolve(*mKeyPair);
   2801 }
   2802 
   2803 void GenerateAsymmetricKeyTask::Cleanup() { mKeyPair = nullptr; }
   2804 
   2805 class DeriveHkdfBitsTask : public ReturnArrayBufferViewTask {
   2806 public:
   2807  DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
   2808                     CryptoKey& aKey, const Nullable<uint32_t>& aLength)
   2809      : mMechanism(CKM_INVALID_MECHANISM) {
   2810    Init(aCx, aAlgorithm, aKey, aLength);
   2811  }
   2812 
   2813  DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
   2814                     CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
   2815      : mLengthInBits(0), mLengthInBytes(0), mMechanism(CKM_INVALID_MECHANISM) {
   2816    size_t length;
   2817    mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
   2818 
   2819    const Nullable<uint32_t> keyLength(length);
   2820    if (NS_SUCCEEDED(mEarlyRv)) {
   2821      Init(aCx, aAlgorithm, aKey, keyLength);
   2822    }
   2823  }
   2824 
   2825  void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
   2826            const Nullable<uint32_t>& aLength) {
   2827    glean::webcrypto::alg.AccumulateSingleSample(TA_HKDF);
   2828    CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HKDF);
   2829 
   2830    if (!mSymKey.Assign(aKey.GetSymKey())) {
   2831      mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
   2832      return;
   2833    }
   2834 
   2835    RootedDictionary<HkdfParams> params(aCx);
   2836    mEarlyRv = Coerce(aCx, params, aAlgorithm);
   2837    if (NS_FAILED(mEarlyRv)) {
   2838      mEarlyRv = NS_ERROR_DOM_TYPE_MISMATCH_ERR;
   2839      return;
   2840    }
   2841 
   2842    // length must be non-null and multiple of eight.
   2843    if (aLength.IsNull() || aLength.Value() % 8 != 0) {
   2844      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
   2845      return;
   2846    }
   2847 
   2848    // Extract the hash algorithm.
   2849    nsString hashName;
   2850    mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
   2851    if (NS_FAILED(mEarlyRv)) {
   2852      return;
   2853    }
   2854 
   2855    // Check the given hash algorithm.
   2856    switch (MapAlgorithmNameToMechanism(hashName)) {
   2857      case CKM_SHA_1:
   2858        mMechanism = CKM_NSS_HKDF_SHA1;
   2859        break;
   2860      case CKM_SHA256:
   2861        mMechanism = CKM_NSS_HKDF_SHA256;
   2862        break;
   2863      case CKM_SHA384:
   2864        mMechanism = CKM_NSS_HKDF_SHA384;
   2865        break;
   2866      case CKM_SHA512:
   2867        mMechanism = CKM_NSS_HKDF_SHA512;
   2868        break;
   2869      default:
   2870        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   2871        return;
   2872    }
   2873 
   2874    ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
   2875    ATTEMPT_BUFFER_INIT(mInfo, params.mInfo)
   2876    mLengthInBytes = ceil((double)aLength.Value() / 8);
   2877    mLengthInBits = aLength.Value();
   2878  }
   2879 
   2880 private:
   2881  size_t mLengthInBits;
   2882  size_t mLengthInBytes;
   2883  CryptoBuffer mSalt;
   2884  CryptoBuffer mInfo;
   2885  CryptoBuffer mSymKey;
   2886  CK_MECHANISM_TYPE mMechanism;
   2887 
   2888  virtual nsresult DoCrypto() override {
   2889    UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   2890    if (!arena) {
   2891      return NS_ERROR_DOM_OPERATION_ERR;
   2892    }
   2893 
   2894    // Import the key
   2895    SECItem keyItem = {siBuffer, nullptr, 0};
   2896    ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
   2897 
   2898    UniquePK11SlotInfo slot(PK11_GetInternalSlot());
   2899    if (!slot.get()) {
   2900      return NS_ERROR_DOM_OPERATION_ERR;
   2901    }
   2902 
   2903    UniquePK11SymKey baseKey(PK11_ImportSymKey(slot.get(), mMechanism,
   2904                                               PK11_OriginUnwrap, CKA_WRAP,
   2905                                               &keyItem, nullptr));
   2906    if (!baseKey) {
   2907      return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   2908    }
   2909 
   2910    // We are going to return an empty string, so we can skip the rest.
   2911    if (mLengthInBits == 0) {
   2912      mResult.Clear();
   2913      return NS_OK;
   2914    }
   2915 
   2916    SECItem salt = {siBuffer, nullptr, 0};
   2917    SECItem info = {siBuffer, nullptr, 0};
   2918    ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
   2919    ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &info, mInfo);
   2920 
   2921    CK_NSS_HKDFParams hkdfParams = {true, salt.data, salt.len,
   2922                                    true, info.data, info.len};
   2923    SECItem params = {siBuffer, (unsigned char*)&hkdfParams,
   2924                      sizeof(hkdfParams)};
   2925 
   2926    // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
   2927    // derived symmetric key and don't matter because we ignore them anyway.
   2928    UniquePK11SymKey symKey(PK11_Derive(baseKey.get(), mMechanism, &params,
   2929                                        CKM_SHA512_HMAC, CKA_SIGN,
   2930                                        mLengthInBytes));
   2931 
   2932    if (!symKey.get()) {
   2933      return NS_ERROR_DOM_OPERATION_ERR;
   2934    }
   2935 
   2936    nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
   2937    if (NS_FAILED(rv)) {
   2938      return NS_ERROR_DOM_OPERATION_ERR;
   2939    }
   2940 
   2941    // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
   2942    // just refers to a buffer managed by symKey. The assignment copies the
   2943    // data, so mResult manages one copy, while symKey manages another.
   2944    ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
   2945 
   2946    if (mLengthInBytes > mResult.Length()) {
   2947      return NS_ERROR_DOM_DATA_ERR;
   2948    }
   2949 
   2950    if (!mResult.SetLength(mLengthInBytes, fallible)) {
   2951      return NS_ERROR_DOM_UNKNOWN_ERR;
   2952    }
   2953 
   2954    return NS_OK;
   2955  }
   2956 };
   2957 
   2958 class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask {
   2959 public:
   2960  DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
   2961                      CryptoKey& aKey, const Nullable<uint32_t>& aLength)
   2962      : mHashOidTag(SEC_OID_UNKNOWN) {
   2963    Init(aCx, aAlgorithm, aKey, aLength);
   2964  }
   2965 
   2966  DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
   2967                      CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
   2968      : mLength(0), mIterations(0), mHashOidTag(SEC_OID_UNKNOWN) {
   2969    size_t length;
   2970    mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
   2971 
   2972    const Nullable<uint32_t> keyLength(length);
   2973    if (NS_SUCCEEDED(mEarlyRv)) {
   2974      Init(aCx, aAlgorithm, aKey, keyLength);
   2975    }
   2976  }
   2977 
   2978  void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
   2979            const Nullable<uint32_t>& aLength) {
   2980    glean::webcrypto::alg.AccumulateSingleSample(TA_PBKDF2);
   2981    CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_PBKDF2);
   2982 
   2983    if (!mSymKey.Assign(aKey.GetSymKey())) {
   2984      mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
   2985      return;
   2986    }
   2987 
   2988    RootedDictionary<Pbkdf2Params> params(aCx);
   2989    mEarlyRv = Coerce(aCx, params, aAlgorithm);
   2990    if (NS_FAILED(mEarlyRv)) {
   2991      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
   2992      return;
   2993    }
   2994 
   2995    // length must be non-null and multiple of eight.
   2996    if (aLength.IsNull() || aLength.Value() % 8) {
   2997      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
   2998      return;
   2999    }
   3000 
   3001    // Extract the hash algorithm.
   3002    nsString hashName;
   3003    mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
   3004    if (NS_FAILED(mEarlyRv)) {
   3005      return;
   3006    }
   3007 
   3008    // Check the given hash algorithm.
   3009    switch (MapAlgorithmNameToMechanism(hashName)) {
   3010      case CKM_SHA_1:
   3011        mHashOidTag = SEC_OID_HMAC_SHA1;
   3012        break;
   3013      case CKM_SHA256:
   3014        mHashOidTag = SEC_OID_HMAC_SHA256;
   3015        break;
   3016      case CKM_SHA384:
   3017        mHashOidTag = SEC_OID_HMAC_SHA384;
   3018        break;
   3019      case CKM_SHA512:
   3020        mHashOidTag = SEC_OID_HMAC_SHA512;
   3021        break;
   3022      default:
   3023        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   3024        return;
   3025    }
   3026 
   3027    ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
   3028    mLength = aLength.Value() >> 3;  // bits to bytes
   3029    mIterations = params.mIterations;
   3030  }
   3031 
   3032 private:
   3033  size_t mLength;
   3034  size_t mIterations;
   3035  CryptoBuffer mSalt;
   3036  CryptoBuffer mSymKey;
   3037  SECOidTag mHashOidTag;
   3038 
   3039  virtual nsresult DoCrypto() override {
   3040    UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   3041    if (!arena) {
   3042      return NS_ERROR_DOM_OPERATION_ERR;
   3043    }
   3044 
   3045    SECItem salt = {siBuffer, nullptr, 0};
   3046    ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
   3047    // PK11_CreatePBEV2AlgorithmID will "helpfully" create PBKDF2 parameters
   3048    // with a random salt if given a SECItem* that is either null or has a null
   3049    // data pointer. This obviously isn't what we want, so we have to fake it
   3050    // out by passing in a SECItem* with a non-null data pointer but with zero
   3051    // length.
   3052    if (!salt.data) {
   3053      MOZ_ASSERT(salt.len == 0);
   3054      salt.data =
   3055          reinterpret_cast<unsigned char*>(PORT_ArenaAlloc(arena.get(), 1));
   3056      if (!salt.data) {
   3057        return NS_ERROR_DOM_UNKNOWN_ERR;
   3058      }
   3059    }
   3060 
   3061    // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this
   3062    // parameter is unused for key generation. It is currently only used
   3063    // for PBKDF2 authentication or key (un)wrapping when specifying an
   3064    // encryption algorithm (PBES2).
   3065    UniqueSECAlgorithmID algID(
   3066        PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1,
   3067                                    mHashOidTag, mLength, mIterations, &salt));
   3068 
   3069    if (!algID) {
   3070      return NS_ERROR_DOM_OPERATION_ERR;
   3071    }
   3072 
   3073    // We are going to return an empty string, so we can skip the rest.
   3074    if (mLength == 0) {
   3075      mResult.Clear();
   3076      return NS_OK;
   3077    }
   3078 
   3079    UniquePK11SlotInfo slot(PK11_GetInternalSlot());
   3080    if (!slot.get()) {
   3081      return NS_ERROR_DOM_OPERATION_ERR;
   3082    }
   3083 
   3084    SECItem keyItem = {siBuffer, nullptr, 0};
   3085    ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
   3086 
   3087    UniquePK11SymKey symKey(
   3088        PK11_PBEKeyGen(slot.get(), algID.get(), &keyItem, false, nullptr));
   3089    if (!symKey.get()) {
   3090      return NS_ERROR_DOM_OPERATION_ERR;
   3091    }
   3092 
   3093    nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
   3094    if (NS_FAILED(rv)) {
   3095      return NS_ERROR_DOM_OPERATION_ERR;
   3096    }
   3097 
   3098    // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
   3099    // just refers to a buffer managed by symKey. The assignment copies the
   3100    // data, so mResult manages one copy, while symKey manages another.
   3101    ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
   3102    return NS_OK;
   3103  }
   3104 };
   3105 
   3106 template <class DeriveBitsTask>
   3107 class DeriveKeyTask : public DeriveBitsTask {
   3108 public:
   3109  DeriveKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
   3110                const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
   3111                const ObjectOrString& aDerivedKeyType, bool aExtractable,
   3112                const Sequence<nsString>& aKeyUsages)
   3113      : DeriveBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType) {
   3114    if (NS_FAILED(this->mEarlyRv)) {
   3115      return;
   3116    }
   3117 
   3118    constexpr auto format =
   3119        NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_FORMAT_RAW);
   3120    mTask = new ImportSymmetricKeyTask(aGlobal, aCx, format, aDerivedKeyType,
   3121                                       aExtractable, aKeyUsages);
   3122  }
   3123 
   3124 protected:
   3125  RefPtr<ImportSymmetricKeyTask> mTask;
   3126 
   3127 private:
   3128  virtual void Resolve() override {
   3129    mTask->SetRawKeyData(this->mResult);
   3130    mTask->DispatchWithPromise(this->mResultPromise);
   3131  }
   3132 
   3133  virtual void Cleanup() override { mTask = nullptr; }
   3134 };
   3135 class DeriveEcdhBitsTask : public ReturnArrayBufferViewTask {
   3136 public:
   3137  DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
   3138                     CryptoKey& aKey, const Nullable<uint32_t>& aLength)
   3139      : mLengthInBits(aLength), mPrivKey(aKey.GetPrivateKey()) {
   3140    Init(aCx, aAlgorithm, aKey);
   3141  }
   3142 
   3143  DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
   3144                     CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
   3145      : mPrivKey(aKey.GetPrivateKey()) {
   3146    Maybe<size_t> lengthInBits;
   3147    mEarlyRv = GetKeyLengthForAlgorithmIfSpecified(aCx, aTargetAlgorithm,
   3148                                                   lengthInBits);
   3149    if (lengthInBits.isNothing()) {
   3150      mLengthInBits.SetNull();
   3151    } else {
   3152      mLengthInBits.SetValue(*lengthInBits);
   3153    }
   3154    if (NS_SUCCEEDED(mEarlyRv)) {
   3155      Init(aCx, aAlgorithm, aKey);
   3156    }
   3157  }
   3158 
   3159  void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey) {
   3160    glean::webcrypto::alg.AccumulateSingleSample(TA_ECDH);
   3161    CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDH);
   3162 
   3163    // Check that we have a private key.
   3164    if (!mPrivKey) {
   3165      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
   3166      return;
   3167    }
   3168 
   3169    // Retrieve the peer's public key.
   3170    RootedDictionary<EcdhKeyDeriveParams> params(aCx);
   3171    mEarlyRv = Coerce(aCx, params, aAlgorithm);
   3172    if (NS_FAILED(mEarlyRv)) {
   3173      /* The returned code is installed by Coerce function. */
   3174      return;
   3175    }
   3176 
   3177    CryptoKey* publicKey = params.mPublic;
   3178    mPubKey = publicKey->GetPublicKey();
   3179    if (!mPubKey) {
   3180      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
   3181      return;
   3182    }
   3183 
   3184    CHECK_KEY_ALGORITHM(publicKey->Algorithm(), WEBCRYPTO_ALG_ECDH);
   3185 
   3186    // Both keys must use the same named curve.
   3187    nsString curve1 = aKey.Algorithm().mEc.mNamedCurve;
   3188    nsString curve2 = publicKey->Algorithm().mEc.mNamedCurve;
   3189 
   3190    if (!curve1.Equals(curve2)) {
   3191      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
   3192      return;
   3193    }
   3194  }
   3195 
   3196 private:
   3197  Nullable<uint32_t> mLengthInBits;
   3198  UniqueSECKEYPrivateKey mPrivKey;
   3199  UniqueSECKEYPublicKey mPubKey;
   3200 
   3201  virtual nsresult DoCrypto() override {
   3202    // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
   3203    // derived symmetric key and don't matter because we ignore them anyway.
   3204    UniquePK11SymKey symKey(
   3205        PK11_PubDeriveWithKDF(mPrivKey.get(), mPubKey.get(), PR_FALSE, nullptr,
   3206                              nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC,
   3207                              CKA_SIGN, 0, CKD_NULL, nullptr, nullptr));
   3208 
   3209    if (!symKey.get()) {
   3210      return NS_ERROR_DOM_OPERATION_ERR;
   3211    }
   3212 
   3213    nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
   3214    if (NS_FAILED(rv)) {
   3215      return NS_ERROR_DOM_OPERATION_ERR;
   3216    }
   3217 
   3218    // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
   3219    // just refers to a buffer managed by symKey. The assignment copies the
   3220    // data, so mResult manages one copy, while symKey manages another.
   3221    ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
   3222 
   3223    if (!mLengthInBits.IsNull()) {
   3224      size_t length = mLengthInBits.Value();
   3225      size_t lengthInBytes = ceil((double)length / 8);  // bits to bytes
   3226      if (lengthInBytes > mResult.Length()) {
   3227        return NS_ERROR_DOM_OPERATION_ERR;
   3228      }
   3229 
   3230      if (!mResult.SetLength(lengthInBytes, fallible)) {
   3231        return NS_ERROR_DOM_UNKNOWN_ERR;
   3232      }
   3233 
   3234      // If the number of bits to derive is not a multiple of 8 we need to
   3235      // zero out the remaining bits that were derived but not requested.
   3236      if (length % 8) {
   3237        mResult[mResult.Length() - 1] &= 0xff << (8 - (length % 8));
   3238      }
   3239    }
   3240 
   3241    return NS_OK;
   3242  }
   3243 };
   3244 
   3245 template <class KeyEncryptTask>
   3246 class WrapKeyTask : public ExportKeyTask {
   3247 public:
   3248  WrapKeyTask(JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
   3249              CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm)
   3250      : ExportKeyTask(aFormat, aKey) {
   3251    if (NS_FAILED(mEarlyRv)) {
   3252      return;
   3253    }
   3254 
   3255    mTask = new KeyEncryptTask(aCx, aWrapAlgorithm, aWrappingKey, true);
   3256  }
   3257 
   3258 private:
   3259  RefPtr<KeyEncryptTask> mTask;
   3260 
   3261  virtual nsresult AfterCrypto() override {
   3262    // If wrapping JWK, stringify the JSON
   3263    if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   3264      nsAutoString json;
   3265      if (!mJwk.ToJSON(json)) {
   3266        return NS_ERROR_DOM_OPERATION_ERR;
   3267      }
   3268 
   3269      NS_ConvertUTF16toUTF8 utf8(json);
   3270      if (!mResult.Assign((const uint8_t*)utf8.BeginReading(), utf8.Length())) {
   3271        return NS_ERROR_DOM_OPERATION_ERR;
   3272      }
   3273    }
   3274 
   3275    return NS_OK;
   3276  }
   3277 
   3278  virtual void Resolve() override {
   3279    mTask->SetData(mResult);
   3280    mTask->DispatchWithPromise(mResultPromise);
   3281  }
   3282 
   3283  virtual void Cleanup() override { mTask = nullptr; }
   3284 };
   3285 
   3286 template <class KeyEncryptTask>
   3287 class UnwrapKeyTask : public KeyEncryptTask {
   3288 public:
   3289  UnwrapKeyTask(JSContext* aCx, const ArrayBufferViewOrArrayBuffer& aWrappedKey,
   3290                CryptoKey& aUnwrappingKey,
   3291                const ObjectOrString& aUnwrapAlgorithm, ImportKeyTask* aTask)
   3292      : KeyEncryptTask(aCx, aUnwrapAlgorithm, aUnwrappingKey, aWrappedKey,
   3293                       false),
   3294        mTask(aTask) {}
   3295 
   3296 private:
   3297  RefPtr<ImportKeyTask> mTask;
   3298 
   3299  virtual void Resolve() override {
   3300    mTask->SetKeyDataMaybeParseJWK(KeyEncryptTask::mResult);
   3301    mTask->DispatchWithPromise(KeyEncryptTask::mResultPromise);
   3302  }
   3303 
   3304  virtual void Cleanup() override { mTask = nullptr; }
   3305 };
   3306 
   3307 // Task creation methods for WebCryptoTask
   3308 
   3309 // Note: We do not perform algorithm normalization as a monolithic process,
   3310 // as described in the spec.  Instead:
   3311 // * Each method handles its slice of the supportedAlgorithms structure
   3312 // * Task constructors take care of:
   3313 //    * Coercing the algorithm to the proper concrete type
   3314 //    * Cloning subordinate data items
   3315 //    * Cloning input data as needed
   3316 //
   3317 // Thus, support for different algorithms is determined by the if-statements
   3318 // below, rather than a data structure.
   3319 //
   3320 // This results in algorithm normalization coming after some other checks,
   3321 // and thus slightly more steps being done synchronously than the spec calls
   3322 // for.  But none of these steps is especially time-consuming.
   3323 
   3324 WebCryptoTask* WebCryptoTask::CreateEncryptDecryptTask(
   3325    JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
   3326    const CryptoOperationData& aData, bool aEncrypt) {
   3327  TelemetryMethod method = (aEncrypt) ? TM_ENCRYPT : TM_DECRYPT;
   3328  glean::webcrypto::method.AccumulateSingleSample(method);
   3329  glean::webcrypto::extractable_enc
   3330      .EnumGet(static_cast<glean::webcrypto::ExtractableEncLabel>(
   3331          aKey.Extractable()))
   3332      .Add();
   3333 
   3334  // Ensure key is usable for this operation
   3335  if ((aEncrypt && !aKey.HasUsage(CryptoKey::ENCRYPT)) ||
   3336      (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) {
   3337    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   3338  }
   3339 
   3340  nsString algName;
   3341  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   3342  if (NS_FAILED(rv)) {
   3343    return new FailureTask(rv);
   3344  }
   3345 
   3346  if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
   3347      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
   3348      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
   3349    return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
   3350  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
   3351    return new RsaOaepTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
   3352  }
   3353 
   3354  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3355 }
   3356 
   3357 WebCryptoTask* WebCryptoTask::CreateSignVerifyTask(
   3358    JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
   3359    const CryptoOperationData& aSignature, const CryptoOperationData& aData,
   3360    bool aSign) {
   3361  TelemetryMethod method = (aSign) ? TM_SIGN : TM_VERIFY;
   3362  glean::webcrypto::method.AccumulateSingleSample(method);
   3363  glean::webcrypto::extractable_sig
   3364      .EnumGet(static_cast<glean::webcrypto::ExtractableSigLabel>(
   3365          aKey.Extractable()))
   3366      .Add();
   3367 
   3368  // Ensure key is usable for this operation
   3369  if ((aSign && !aKey.HasUsage(CryptoKey::SIGN)) ||
   3370      (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) {
   3371    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   3372  }
   3373 
   3374  nsString algName;
   3375  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   3376  if (NS_FAILED(rv)) {
   3377    return new FailureTask(rv);
   3378  }
   3379 
   3380  if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
   3381    return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
   3382  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
   3383             algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
   3384             algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
   3385             algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
   3386    return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
   3387                                        aData, aSign);
   3388  }
   3389 
   3390  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3391 }
   3392 
   3393 WebCryptoTask* WebCryptoTask::CreateDigestTask(
   3394    JSContext* aCx, const ObjectOrString& aAlgorithm,
   3395    const CryptoOperationData& aData) {
   3396  glean::webcrypto::method.AccumulateSingleSample(TM_DIGEST);
   3397 
   3398  nsString algName;
   3399  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   3400  if (NS_FAILED(rv)) {
   3401    return new FailureTask(rv);
   3402  }
   3403 
   3404  if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
   3405      algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256) ||
   3406      algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
   3407      algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
   3408    return new DigestTask(aCx, aAlgorithm, aData);
   3409  }
   3410 
   3411  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3412 }
   3413 
   3414 WebCryptoTask* WebCryptoTask::CreateImportKeyTask(
   3415    nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
   3416    JS::Handle<JSObject*> aKeyData, const ObjectOrString& aAlgorithm,
   3417    bool aExtractable, const Sequence<nsString>& aKeyUsages) {
   3418  glean::webcrypto::method.AccumulateSingleSample(TM_IMPORTKEY);
   3419  glean::webcrypto::extractable_import
   3420      .EnumGet(
   3421          static_cast<glean::webcrypto::ExtractableImportLabel>(aExtractable))
   3422      .Add();
   3423 
   3424  // Verify that the format is recognized
   3425  if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
   3426      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
   3427      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
   3428      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   3429    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
   3430  }
   3431 
   3432  // Verify that aKeyUsages does not contain an unrecognized value
   3433  if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
   3434    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
   3435  }
   3436 
   3437  nsString algName;
   3438  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   3439  if (NS_FAILED(rv)) {
   3440    return new FailureTask(rv);
   3441  }
   3442 
   3443  // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
   3444  // However, the spec should be updated to allow it.
   3445  if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
   3446      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
   3447      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
   3448      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
   3449      algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
   3450      algName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
   3451      algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
   3452    return new ImportSymmetricKeyTask(aGlobal, aCx, aFormat, aKeyData,
   3453                                      aAlgorithm, aExtractable, aKeyUsages);
   3454  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
   3455             algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
   3456             algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
   3457    return new ImportRsaKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
   3458                                aExtractable, aKeyUsages);
   3459  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
   3460             algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
   3461    return new ImportEcKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
   3462                               aExtractable, aKeyUsages);
   3463  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_X25519) ||
   3464             algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
   3465    return new ImportOKPKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
   3466                                aExtractable, aKeyUsages);
   3467  } else {
   3468    return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3469  }
   3470 }
   3471 
   3472 WebCryptoTask* WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat,
   3473                                                  CryptoKey& aKey) {
   3474  glean::webcrypto::method.AccumulateSingleSample(TM_EXPORTKEY);
   3475 
   3476  // Verify that the format is recognized
   3477  if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
   3478      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
   3479      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
   3480      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   3481    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
   3482  }
   3483 
   3484  // Verify that the key is extractable
   3485  if (!aKey.Extractable()) {
   3486    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   3487  }
   3488 
   3489  // Verify that the algorithm supports export
   3490  // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
   3491  // However, the spec should be updated to allow it.
   3492  nsString algName = aKey.Algorithm().mName;
   3493  if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
   3494      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
   3495      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
   3496      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
   3497      algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
   3498      algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC) ||
   3499      algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
   3500      algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
   3501      algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
   3502      algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
   3503      algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
   3504      algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519) ||
   3505      algName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
   3506    return new ExportKeyTask(aFormat, aKey);
   3507  }
   3508  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3509 }
   3510 
   3511 WebCryptoTask* WebCryptoTask::CreateGenerateKeyTask(
   3512    nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
   3513    bool aExtractable, const Sequence<nsString>& aKeyUsages) {
   3514  glean::webcrypto::method.AccumulateSingleSample(TM_GENERATEKEY);
   3515  glean::webcrypto::extractable_generate
   3516      .EnumGet(
   3517          static_cast<glean::webcrypto::ExtractableGenerateLabel>(aExtractable))
   3518      .Add();
   3519  if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
   3520    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
   3521  }
   3522 
   3523  nsString algName;
   3524  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   3525  if (NS_FAILED(rv)) {
   3526    return new FailureTask(rv);
   3527  }
   3528 
   3529  if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
   3530      algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
   3531      algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
   3532      algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
   3533      algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
   3534    return new GenerateSymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
   3535                                        aKeyUsages);
   3536  } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
   3537             algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
   3538             algName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS) ||
   3539             algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
   3540             algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA) ||
   3541             algName.EqualsASCII(WEBCRYPTO_ALG_ED25519) ||
   3542             algName.EqualsASCII(WEBCRYPTO_ALG_X25519)) {
   3543    return new GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
   3544                                         aKeyUsages);
   3545  } else {
   3546    return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3547  }
   3548 }
   3549 
   3550 WebCryptoTask* WebCryptoTask::CreateDeriveKeyTask(
   3551    nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
   3552    CryptoKey& aBaseKey, const ObjectOrString& aDerivedKeyType,
   3553    bool aExtractable, const Sequence<nsString>& aKeyUsages) {
   3554  glean::webcrypto::method.AccumulateSingleSample(TM_DERIVEKEY);
   3555 
   3556  // Ensure baseKey is usable for this operation
   3557  if (!aBaseKey.HasUsage(CryptoKey::DERIVEKEY)) {
   3558    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   3559  }
   3560 
   3561  // Verify that aKeyUsages does not contain an unrecognized value
   3562  if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
   3563    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
   3564  }
   3565 
   3566  nsString algName;
   3567  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   3568  if (NS_FAILED(rv)) {
   3569    return new FailureTask(rv);
   3570  }
   3571 
   3572  if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
   3573    return new DeriveKeyTask<DeriveHkdfBitsTask>(aGlobal, aCx, aAlgorithm,
   3574                                                 aBaseKey, aDerivedKeyType,
   3575                                                 aExtractable, aKeyUsages);
   3576  }
   3577 
   3578  if (algName.EqualsASCII(WEBCRYPTO_ALG_X25519)) {
   3579    return new DeriveKeyTask<DeriveX25519BitsTask>(aGlobal, aCx, aAlgorithm,
   3580                                                   aBaseKey, aDerivedKeyType,
   3581                                                   aExtractable, aKeyUsages);
   3582  }
   3583 
   3584  if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
   3585    return new DeriveKeyTask<DerivePbkdfBitsTask>(aGlobal, aCx, aAlgorithm,
   3586                                                  aBaseKey, aDerivedKeyType,
   3587                                                  aExtractable, aKeyUsages);
   3588  }
   3589 
   3590  if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
   3591    return new DeriveKeyTask<DeriveEcdhBitsTask>(aGlobal, aCx, aAlgorithm,
   3592                                                 aBaseKey, aDerivedKeyType,
   3593                                                 aExtractable, aKeyUsages);
   3594  }
   3595 
   3596  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3597 }
   3598 
   3599 WebCryptoTask* WebCryptoTask::CreateDeriveBitsTask(
   3600    JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
   3601    const Nullable<uint32_t>& aLength) {
   3602  glean::webcrypto::method.AccumulateSingleSample(TM_DERIVEBITS);
   3603 
   3604  // Ensure baseKey is usable for this operation
   3605  if (!aKey.HasUsage(CryptoKey::DERIVEBITS)) {
   3606    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   3607  }
   3608 
   3609  nsString algName;
   3610  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   3611  if (NS_FAILED(rv)) {
   3612    return new FailureTask(rv);
   3613  }
   3614 
   3615  if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
   3616    return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
   3617  }
   3618 
   3619  if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
   3620    return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
   3621  }
   3622 
   3623  if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
   3624    return new DeriveHkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
   3625  }
   3626 
   3627  if (algName.EqualsASCII(WEBCRYPTO_ALG_X25519)) {
   3628    return new DeriveX25519BitsTask(aCx, aAlgorithm, aKey, aLength);
   3629  }
   3630 
   3631  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3632 }
   3633 
   3634 WebCryptoTask* WebCryptoTask::CreateWrapKeyTask(
   3635    JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
   3636    CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm) {
   3637  glean::webcrypto::method.AccumulateSingleSample(TM_WRAPKEY);
   3638 
   3639  // Verify that the format is recognized
   3640  if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
   3641      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
   3642      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
   3643      !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
   3644    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
   3645  }
   3646 
   3647  // Ensure wrappingKey is usable for this operation
   3648  if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) {
   3649    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   3650  }
   3651 
   3652  // Ensure key is extractable
   3653  if (!aKey.Extractable()) {
   3654    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   3655  }
   3656 
   3657  nsString wrapAlgName;
   3658  nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName);
   3659  if (NS_FAILED(rv)) {
   3660    return new FailureTask(rv);
   3661  }
   3662 
   3663  if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
   3664      wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
   3665      wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
   3666    return new WrapKeyTask<AesTask>(aCx, aFormat, aKey, aWrappingKey,
   3667                                    aWrapAlgorithm);
   3668  } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
   3669    return new WrapKeyTask<AesKwTask>(aCx, aFormat, aKey, aWrappingKey,
   3670                                      aWrapAlgorithm);
   3671  } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
   3672    return new WrapKeyTask<RsaOaepTask>(aCx, aFormat, aKey, aWrappingKey,
   3673                                        aWrapAlgorithm);
   3674  }
   3675 
   3676  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3677 }
   3678 
   3679 WebCryptoTask* WebCryptoTask::CreateUnwrapKeyTask(
   3680    nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
   3681    const ArrayBufferViewOrArrayBuffer& aWrappedKey, CryptoKey& aUnwrappingKey,
   3682    const ObjectOrString& aUnwrapAlgorithm,
   3683    const ObjectOrString& aUnwrappedKeyAlgorithm, bool aExtractable,
   3684    const Sequence<nsString>& aKeyUsages) {
   3685  glean::webcrypto::method.AccumulateSingleSample(TM_UNWRAPKEY);
   3686 
   3687  // Ensure key is usable for this operation
   3688  if (!aUnwrappingKey.HasUsage(CryptoKey::UNWRAPKEY)) {
   3689    return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   3690  }
   3691 
   3692  // Verify that aKeyUsages does not contain an unrecognized value
   3693  if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
   3694    return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
   3695  }
   3696 
   3697  nsString keyAlgName;
   3698  nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName);
   3699  if (NS_FAILED(rv)) {
   3700    return new FailureTask(rv);
   3701  }
   3702 
   3703  CryptoOperationData dummy;
   3704  RefPtr<ImportKeyTask> importTask;
   3705  if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
   3706      keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
   3707      keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
   3708      keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
   3709      keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HKDF) ||
   3710      keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
   3711    importTask = new ImportSymmetricKeyTask(aGlobal, aCx, aFormat,
   3712                                            aUnwrappedKeyAlgorithm,
   3713                                            aExtractable, aKeyUsages);
   3714  } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
   3715             keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
   3716             keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS)) {
   3717    importTask =
   3718        new ImportRsaKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
   3719                             aExtractable, aKeyUsages);
   3720  } else if (keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
   3721             keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
   3722    importTask =
   3723        new ImportEcKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
   3724                            aExtractable, aKeyUsages);
   3725  } else if (keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519) ||
   3726             keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
   3727    importTask =
   3728        new ImportOKPKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
   3729                             aExtractable, aKeyUsages);
   3730  } else {
   3731    return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3732  }
   3733 
   3734  nsString unwrapAlgName;
   3735  rv = GetAlgorithmName(aCx, aUnwrapAlgorithm, unwrapAlgName);
   3736  if (NS_FAILED(rv)) {
   3737    return new FailureTask(rv);
   3738  }
   3739  if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
   3740      unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
   3741      unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
   3742    return new UnwrapKeyTask<AesTask>(aCx, aWrappedKey, aUnwrappingKey,
   3743                                      aUnwrapAlgorithm, importTask);
   3744  } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
   3745    return new UnwrapKeyTask<AesKwTask>(aCx, aWrappedKey, aUnwrappingKey,
   3746                                        aUnwrapAlgorithm, importTask);
   3747  } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
   3748    return new UnwrapKeyTask<RsaOaepTask>(aCx, aWrappedKey, aUnwrappingKey,
   3749                                          aUnwrapAlgorithm, importTask);
   3750  }
   3751 
   3752  return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   3753 }
   3754 
   3755 WebCryptoTask::WebCryptoTask()
   3756    : CancelableRunnable("WebCryptoTask"),
   3757      mEarlyRv(NS_OK),
   3758      mEarlyComplete(false),
   3759      mOriginalEventTarget(nullptr),
   3760      mRv(NS_ERROR_NOT_INITIALIZED) {}
   3761 
   3762 WebCryptoTask::~WebCryptoTask() = default;
   3763 
   3764 }  // namespace mozilla::dom