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