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