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, ¶m, 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, ¶m,
730 mResult.Elements(), &outLen,
731 mResult.Length(), mData.Elements(),
732 mData.Length()));
733 } else {
734 rv = MapSECStatus(PK11_Decrypt(symKey.get(), mMechanism, ¶m,
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, ¶m,
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, ¶m,
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(), ¶m));
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, ¶ms,
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