Bug 1698238 return default dictionary from GetUserMediaRequest#getConstraints() if...
[gecko.git] / dom / crypto / WebCryptoTask.cpp
blob93c41a7746d0d23aeea7e1b377b733ad251d674b
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/. */
7 #include "pk11pub.h"
8 #include "cryptohi.h"
9 #include "secerr.h"
10 #include "nsNSSComponent.h"
11 #include "nsProxyRelease.h"
13 #include "jsapi.h"
14 #include "mozilla/Telemetry.h"
15 #include "mozilla/Utf8.h"
16 #include "mozilla/dom/CryptoBuffer.h"
17 #include "mozilla/dom/CryptoKey.h"
18 #include "mozilla/dom/KeyAlgorithmProxy.h"
19 #include "mozilla/dom/TypedArray.h"
20 #include "mozilla/dom/WebCryptoCommon.h"
21 #include "mozilla/dom/WebCryptoTask.h"
22 #include "mozilla/dom/WorkerRef.h"
23 #include "mozilla/dom/WorkerPrivate.h"
25 // Template taken from security/nss/lib/util/templates.c
26 // This (or SGN_EncodeDigestInfo) would ideally be exported
27 // by NSS and until that happens we have to keep our own copy.
28 const SEC_ASN1Template SGN_DigestInfoTemplate[] = {
29 {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SGNDigestInfo)},
30 {SEC_ASN1_INLINE, offsetof(SGNDigestInfo, digestAlgorithm),
31 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate)},
32 {SEC_ASN1_OCTET_STRING, offsetof(SGNDigestInfo, digest)},
35 }};
37 namespace mozilla::dom {
39 // Pre-defined identifiers for telemetry histograms
41 enum TelemetryMethod {
42 TM_ENCRYPT = 0,
43 TM_DECRYPT = 1,
44 TM_SIGN = 2,
45 TM_VERIFY = 3,
46 TM_DIGEST = 4,
47 TM_GENERATEKEY = 5,
48 TM_DERIVEKEY = 6,
49 TM_DERIVEBITS = 7,
50 TM_IMPORTKEY = 8,
51 TM_EXPORTKEY = 9,
52 TM_WRAPKEY = 10,
53 TM_UNWRAPKEY = 11
56 enum TelemetryAlgorithm {
57 // Please make additions at the end of the list,
58 // to preserve comparability of histograms over time
59 TA_UNKNOWN = 0,
60 // encrypt / decrypt
61 TA_AES_CBC = 1,
62 TA_AES_CFB = 2,
63 TA_AES_CTR = 3,
64 TA_AES_GCM = 4,
65 TA_RSAES_PKCS1 = 5, // NB: This algorithm has been removed
66 TA_RSA_OAEP = 6,
67 // sign/verify
68 TA_RSASSA_PKCS1 = 7,
69 TA_RSA_PSS = 8,
70 TA_HMAC_SHA_1 = 9,
71 TA_HMAC_SHA_224 = 10,
72 TA_HMAC_SHA_256 = 11,
73 TA_HMAC_SHA_384 = 12,
74 TA_HMAC_SHA_512 = 13,
75 // digest
76 TA_SHA_1 = 14,
77 TA_SHA_224 = 15,
78 TA_SHA_256 = 16,
79 TA_SHA_384 = 17,
80 TA_SHA_512 = 18,
81 // Later additions
82 TA_AES_KW = 19,
83 TA_ECDH = 20,
84 TA_PBKDF2 = 21,
85 TA_ECDSA = 22,
86 TA_HKDF = 23,
87 TA_DH = 24,
90 // Convenience functions for extracting / converting information
92 // OOM-safe CryptoBuffer initialization, suitable for constructors
93 #define ATTEMPT_BUFFER_INIT(dst, src) \
94 if (!dst.Assign(src)) { \
95 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; \
96 return; \
99 // OOM-safe CryptoBuffer-to-SECItem copy, suitable for DoCrypto
100 #define ATTEMPT_BUFFER_TO_SECITEM(arena, dst, src) \
101 if (!src.ToSECItem(arena, dst)) { \
102 return NS_ERROR_DOM_UNKNOWN_ERR; \
105 // OOM-safe CryptoBuffer copy, suitable for DoCrypto
106 #define ATTEMPT_BUFFER_ASSIGN(dst, src) \
107 if (!dst.Assign(src)) { \
108 return NS_ERROR_DOM_UNKNOWN_ERR; \
111 // Safety check for algorithms that use keys, suitable for constructors
112 #define CHECK_KEY_ALGORITHM(keyAlg, algName) \
114 if (!NORMALIZED_EQUALS(keyAlg.mName, algName)) { \
115 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; \
116 return; \
120 class ClearException {
121 public:
122 explicit ClearException(JSContext* aCx) : mCx(aCx) {}
124 ~ClearException() { JS_ClearPendingException(mCx); }
126 private:
127 JSContext* mCx;
130 template <class OOS>
131 static nsresult GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
132 nsString& aName) {
133 ClearException ce(aCx);
135 if (aAlgorithm.IsString()) {
136 // If string, then treat as algorithm name
137 aName.Assign(aAlgorithm.GetAsString());
138 } else {
139 // Coerce to algorithm and extract name
140 JS::RootedValue value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
141 Algorithm alg;
143 if (!alg.Init(aCx, value)) {
144 return NS_ERROR_DOM_SYNTAX_ERR;
147 aName = alg.mName;
150 if (!NormalizeToken(aName, aName)) {
151 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
154 return NS_OK;
157 template <class T, class OOS>
158 static nsresult Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm) {
159 ClearException ce(aCx);
161 if (!aAlgorithm.IsObject()) {
162 return NS_ERROR_DOM_SYNTAX_ERR;
165 JS::RootedValue value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
166 if (!aTarget.Init(aCx, value)) {
167 return NS_ERROR_DOM_SYNTAX_ERR;
170 return NS_OK;
173 inline size_t MapHashAlgorithmNameToBlockSize(const nsString& aName) {
174 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
175 aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
176 return 512;
179 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
180 aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
181 return 1024;
184 return 0;
187 inline nsresult GetKeyLengthForAlgorithm(JSContext* aCx,
188 const ObjectOrString& aAlgorithm,
189 size_t& aLength) {
190 aLength = 0;
192 // Extract algorithm name
193 nsString algName;
194 if (NS_FAILED(GetAlgorithmName(aCx, aAlgorithm, algName))) {
195 return NS_ERROR_DOM_SYNTAX_ERR;
198 // Read AES key length from given algorithm object.
199 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
200 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
201 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
202 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
203 RootedDictionary<AesDerivedKeyParams> params(aCx);
204 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
205 return NS_ERROR_DOM_SYNTAX_ERR;
208 if (params.mLength != 128 && params.mLength != 192 &&
209 params.mLength != 256) {
210 return NS_ERROR_DOM_OPERATION_ERR;
213 aLength = params.mLength;
214 return NS_OK;
217 // Read HMAC key length from given algorithm object or
218 // determine key length as the block size of the given hash.
219 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
220 RootedDictionary<HmacDerivedKeyParams> params(aCx);
221 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
222 return NS_ERROR_DOM_SYNTAX_ERR;
225 // Return the passed length, if any.
226 if (params.mLength.WasPassed()) {
227 aLength = params.mLength.Value();
228 return NS_OK;
231 nsString hashName;
232 if (NS_FAILED(GetAlgorithmName(aCx, params.mHash, hashName))) {
233 return NS_ERROR_DOM_SYNTAX_ERR;
236 // Return the given hash algorithm's block size as the key length.
237 size_t length = MapHashAlgorithmNameToBlockSize(hashName);
238 if (length == 0) {
239 return NS_ERROR_DOM_SYNTAX_ERR;
242 aLength = length;
243 return NS_OK;
246 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
249 inline bool MapOIDTagToNamedCurve(SECOidTag aOIDTag, nsString& aResult) {
250 switch (aOIDTag) {
251 case SEC_OID_SECG_EC_SECP256R1:
252 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
253 break;
254 case SEC_OID_SECG_EC_SECP384R1:
255 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
256 break;
257 case SEC_OID_SECG_EC_SECP521R1:
258 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
259 break;
260 default:
261 return false;
264 return true;
267 inline SECOidTag MapHashAlgorithmNameToOID(const nsString& aName) {
268 SECOidTag hashOID(SEC_OID_UNKNOWN);
270 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
271 hashOID = SEC_OID_SHA1;
272 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
273 hashOID = SEC_OID_SHA256;
274 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
275 hashOID = SEC_OID_SHA384;
276 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
277 hashOID = SEC_OID_SHA512;
280 return hashOID;
283 inline CK_MECHANISM_TYPE MapHashAlgorithmNameToMgfMechanism(
284 const nsString& aName) {
285 CK_MECHANISM_TYPE mech(UNKNOWN_CK_MECHANISM);
287 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
288 mech = CKG_MGF1_SHA1;
289 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
290 mech = CKG_MGF1_SHA256;
291 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
292 mech = CKG_MGF1_SHA384;
293 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
294 mech = CKG_MGF1_SHA512;
297 return mech;
300 // Implementation of WebCryptoTask methods
302 void WebCryptoTask::DispatchWithPromise(Promise* aResultPromise) {
303 mResultPromise = aResultPromise;
305 // Fail if an error was set during the constructor
306 MAYBE_EARLY_FAIL(mEarlyRv)
308 // Perform pre-NSS operations, and fail if they fail
309 mEarlyRv = BeforeCrypto();
310 MAYBE_EARLY_FAIL(mEarlyRv)
312 // Skip dispatch if we're already done. Otherwise launch a CryptoTask
313 if (mEarlyComplete) {
314 CallCallback(mEarlyRv);
315 return;
318 // Store calling thread
319 mOriginalEventTarget = GetCurrentSerialEventTarget();
321 // If we are running on a worker thread we must hold the worker
322 // alive while we work on the thread pool. Otherwise the worker
323 // private may get torn down before we dispatch back to complete
324 // the transaction.
325 if (!NS_IsMainThread()) {
326 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
327 MOZ_ASSERT(workerPrivate);
329 RefPtr<StrongWorkerRef> workerRef =
330 StrongWorkerRef::Create(workerPrivate, "WebCryptoTask");
331 if (NS_WARN_IF(!workerRef)) {
332 mEarlyRv = NS_BINDING_ABORTED;
333 } else {
334 mWorkerRef = new ThreadSafeWorkerRef(workerRef);
337 MAYBE_EARLY_FAIL(mEarlyRv);
339 // dispatch to thread pool
341 if (!EnsureNSSInitializedChromeOrContent()) {
342 mEarlyRv = NS_ERROR_FAILURE;
344 MAYBE_EARLY_FAIL(mEarlyRv);
346 mEarlyRv = NS_DispatchBackgroundTask(this);
347 MAYBE_EARLY_FAIL(mEarlyRv)
350 NS_IMETHODIMP
351 WebCryptoTask::Run() {
352 // Run heavy crypto operations on the thread pool, off the original thread.
353 if (!IsOnOriginalThread()) {
354 mRv = CalculateResult();
356 // Back to the original thread, i.e. continue below.
357 mOriginalEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
358 return NS_OK;
361 // We're now back on the calling thread.
362 CallCallback(mRv);
364 // Stop holding the worker thread alive now that the async work has
365 // been completed.
366 mWorkerRef = nullptr;
368 return NS_OK;
371 nsresult WebCryptoTask::Cancel() {
372 MOZ_ASSERT(IsOnOriginalThread());
373 FailWithError(NS_BINDING_ABORTED);
374 return NS_OK;
377 void WebCryptoTask::FailWithError(nsresult aRv) {
378 MOZ_ASSERT(IsOnOriginalThread());
379 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
381 // Blindly convert nsresult to DOMException
382 // Individual tasks must ensure they pass the right values
383 mResultPromise->MaybeReject(aRv);
384 // Manually release mResultPromise while we're on the main thread
385 mResultPromise = nullptr;
386 mWorkerRef = nullptr;
387 Cleanup();
390 nsresult WebCryptoTask::CalculateResult() {
391 MOZ_ASSERT(!IsOnOriginalThread());
393 return DoCrypto();
396 void WebCryptoTask::CallCallback(nsresult rv) {
397 MOZ_ASSERT(IsOnOriginalThread());
398 if (NS_FAILED(rv)) {
399 FailWithError(rv);
400 return;
403 nsresult rv2 = AfterCrypto();
404 if (NS_FAILED(rv2)) {
405 FailWithError(rv2);
406 return;
409 Resolve();
410 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, true);
412 // Manually release mResultPromise while we're on the main thread
413 mResultPromise = nullptr;
414 Cleanup();
417 // Some generic utility classes
419 class FailureTask : public WebCryptoTask {
420 public:
421 explicit FailureTask(nsresult aRv) { mEarlyRv = aRv; }
424 class ReturnArrayBufferViewTask : public WebCryptoTask {
425 protected:
426 CryptoBuffer mResult;
428 private:
429 // Returns mResult as an ArrayBufferView, or an error
430 virtual void Resolve() override {
431 TypedArrayCreator<ArrayBuffer> ret(mResult);
432 mResultPromise->MaybeResolve(ret);
436 class DeferredData {
437 public:
438 template <class T>
439 void SetData(const T& aData) {
440 mDataIsSet = mData.Assign(aData);
443 protected:
444 DeferredData() : mDataIsSet(false) {}
446 CryptoBuffer mData;
447 bool mDataIsSet;
450 class AesTask : public ReturnArrayBufferViewTask, public DeferredData {
451 public:
452 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
453 bool aEncrypt)
454 : mMechanism(CKM_INVALID_MECHANISM),
455 mTagLength(0),
456 mCounterLength(0),
457 mEncrypt(aEncrypt) {
458 Init(aCx, aAlgorithm, aKey, aEncrypt);
461 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
462 const CryptoOperationData& aData, bool aEncrypt)
463 : mMechanism(CKM_INVALID_MECHANISM),
464 mTagLength(0),
465 mCounterLength(0),
466 mEncrypt(aEncrypt) {
467 Init(aCx, aAlgorithm, aKey, aEncrypt);
468 SetData(aData);
471 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
472 bool aEncrypt) {
473 nsString algName;
474 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
475 if (NS_FAILED(mEarlyRv)) {
476 return;
479 if (!mSymKey.Assign(aKey.GetSymKey())) {
480 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
481 return;
484 // Check that we got a reasonable key
485 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
486 (mSymKey.Length() != 32)) {
487 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
488 return;
491 // Cache parameters depending on the specific algorithm
492 TelemetryAlgorithm telemetryAlg;
493 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
494 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CBC);
496 mMechanism = CKM_AES_CBC_PAD;
497 telemetryAlg = TA_AES_CBC;
498 RootedDictionary<AesCbcParams> params(aCx);
499 nsresult rv = Coerce(aCx, params, aAlgorithm);
500 if (NS_FAILED(rv)) {
501 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
502 return;
505 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
506 if (mIv.Length() != 16) {
507 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
508 return;
510 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
511 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CTR);
513 mMechanism = CKM_AES_CTR;
514 telemetryAlg = TA_AES_CTR;
515 RootedDictionary<AesCtrParams> params(aCx);
516 nsresult rv = Coerce(aCx, params, aAlgorithm);
517 if (NS_FAILED(rv)) {
518 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
519 return;
522 ATTEMPT_BUFFER_INIT(mIv, params.mCounter)
523 if (mIv.Length() != 16) {
524 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
525 return;
528 mCounterLength = params.mLength;
529 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
530 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM);
532 mMechanism = CKM_AES_GCM;
533 telemetryAlg = TA_AES_GCM;
534 RootedDictionary<AesGcmParams> params(aCx);
535 nsresult rv = Coerce(aCx, params, aAlgorithm);
536 if (NS_FAILED(rv)) {
537 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
538 return;
541 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
543 if (params.mAdditionalData.WasPassed()) {
544 ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value())
547 // 32, 64, 96, 104, 112, 120 or 128
548 mTagLength = 128;
549 if (params.mTagLength.WasPassed()) {
550 mTagLength = params.mTagLength.Value();
551 if ((mTagLength > 128) ||
552 !(mTagLength == 32 || mTagLength == 64 ||
553 (mTagLength >= 96 && mTagLength % 8 == 0))) {
554 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
555 return;
558 } else {
559 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
560 return;
562 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
565 private:
566 CK_MECHANISM_TYPE mMechanism;
567 CryptoBuffer mSymKey;
568 CryptoBuffer mIv; // Initialization vector
569 CryptoBuffer mAad; // Additional Authenticated Data
570 uint8_t mTagLength;
571 uint8_t mCounterLength;
572 bool mEncrypt;
574 virtual nsresult DoCrypto() override {
575 nsresult rv;
577 if (!mDataIsSet) {
578 return NS_ERROR_DOM_OPERATION_ERR;
581 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
582 if (!arena) {
583 return NS_ERROR_DOM_OPERATION_ERR;
586 // Construct the parameters object depending on algorithm
587 SECItem param = {siBuffer, nullptr, 0};
588 CK_AES_CTR_PARAMS ctrParams;
589 CK_GCM_PARAMS gcmParams;
590 switch (mMechanism) {
591 case CKM_AES_CBC_PAD:
592 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &param, mIv);
593 break;
594 case CKM_AES_CTR:
595 ctrParams.ulCounterBits = mCounterLength;
596 MOZ_ASSERT(mIv.Length() == 16);
597 memcpy(&ctrParams.cb, mIv.Elements(), 16);
598 param.type = siBuffer;
599 param.data = (unsigned char*)&ctrParams;
600 param.len = sizeof(ctrParams);
601 break;
602 case CKM_AES_GCM:
603 gcmParams.pIv = mIv.Elements();
604 gcmParams.ulIvLen = mIv.Length();
605 gcmParams.ulIvBits = gcmParams.ulIvLen * 8;
606 gcmParams.pAAD = mAad.Elements();
607 gcmParams.ulAADLen = mAad.Length();
608 gcmParams.ulTagBits = mTagLength;
609 param.type = siBuffer;
610 param.data = (unsigned char*)&gcmParams;
611 param.len = sizeof(gcmParams);
612 break;
613 default:
614 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
617 // Import the key
618 SECItem keyItem = {siBuffer, nullptr, 0};
619 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
620 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
621 MOZ_ASSERT(slot.get());
622 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
623 PK11_OriginUnwrap, CKA_ENCRYPT,
624 &keyItem, nullptr));
625 if (!symKey) {
626 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
629 // Check whether the integer addition would overflow.
630 if (std::numeric_limits<CryptoBuffer::size_type>::max() - 16 <
631 mData.Length()) {
632 return NS_ERROR_DOM_DATA_ERR;
635 // Initialize the output buffer (enough space for padding / a full tag)
636 if (!mResult.SetLength(mData.Length() + 16, fallible)) {
637 return NS_ERROR_DOM_UNKNOWN_ERR;
640 uint32_t outLen = 0;
642 // Perform the encryption/decryption
643 if (mEncrypt) {
644 rv = MapSECStatus(PK11_Encrypt(
645 symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
646 mResult.Length(), mData.Elements(), mData.Length()));
647 } else {
648 rv = MapSECStatus(PK11_Decrypt(
649 symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
650 mResult.Length(), mData.Elements(), mData.Length()));
652 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
654 mResult.TruncateLength(outLen);
655 return rv;
659 // This class looks like an encrypt/decrypt task, like AesTask,
660 // but it is only exposed to wrapKey/unwrapKey, not encrypt/decrypt
661 class AesKwTask : public ReturnArrayBufferViewTask, public DeferredData {
662 public:
663 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
664 bool aEncrypt)
665 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
666 Init(aCx, aAlgorithm, aKey, aEncrypt);
669 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
670 const CryptoOperationData& aData, bool aEncrypt)
671 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
672 Init(aCx, aAlgorithm, aKey, aEncrypt);
673 SetData(aData);
676 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
677 bool aEncrypt) {
678 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_KW);
680 nsString algName;
681 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
682 if (NS_FAILED(mEarlyRv)) {
683 return;
686 if (!mSymKey.Assign(aKey.GetSymKey())) {
687 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
688 return;
691 // Check that we got a reasonable key
692 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
693 (mSymKey.Length() != 32)) {
694 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
695 return;
698 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_AES_KW);
701 private:
702 CK_MECHANISM_TYPE mMechanism;
703 CryptoBuffer mSymKey;
704 bool mEncrypt;
706 virtual nsresult DoCrypto() override {
707 nsresult rv;
709 if (!mDataIsSet) {
710 return NS_ERROR_DOM_OPERATION_ERR;
713 // Check that the input is a multiple of 64 bits long
714 if (mData.Length() == 0 || mData.Length() % 8 != 0) {
715 return NS_ERROR_DOM_DATA_ERR;
718 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
719 if (!arena) {
720 return NS_ERROR_DOM_OPERATION_ERR;
723 // Import the key
724 SECItem keyItem = {siBuffer, nullptr, 0};
725 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
726 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
727 MOZ_ASSERT(slot.get());
728 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
729 PK11_OriginUnwrap, CKA_WRAP,
730 &keyItem, nullptr));
731 if (!symKey) {
732 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
735 // Import the data to a SECItem
736 SECItem dataItem = {siBuffer, nullptr, 0};
737 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &dataItem, mData);
739 // Parameters for the fake keys
740 CK_MECHANISM_TYPE fakeMechanism = CKM_SHA_1_HMAC;
741 CK_ATTRIBUTE_TYPE fakeOperation = CKA_SIGN;
743 if (mEncrypt) {
744 // Import the data into a fake PK11SymKey structure
745 UniquePK11SymKey keyToWrap(
746 PK11_ImportSymKey(slot.get(), fakeMechanism, PK11_OriginUnwrap,
747 fakeOperation, &dataItem, nullptr));
748 if (!keyToWrap) {
749 return NS_ERROR_DOM_OPERATION_ERR;
752 // Encrypt and return the wrapped key
753 // AES-KW encryption results in a wrapped key 64 bits longer
754 if (!mResult.SetLength(mData.Length() + 8, fallible)) {
755 return NS_ERROR_DOM_OPERATION_ERR;
757 SECItem resultItem = {siBuffer, mResult.Elements(),
758 (unsigned int)mResult.Length()};
759 rv = MapSECStatus(PK11_WrapSymKey(mMechanism, nullptr, symKey.get(),
760 keyToWrap.get(), &resultItem));
761 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
762 } else {
763 // Decrypt the ciphertext into a temporary PK11SymKey
764 // Unwrapped key should be 64 bits shorter
765 int keySize = mData.Length() - 8;
766 UniquePK11SymKey unwrappedKey(
767 PK11_UnwrapSymKey(symKey.get(), mMechanism, nullptr, &dataItem,
768 fakeMechanism, fakeOperation, keySize));
769 if (!unwrappedKey) {
770 return NS_ERROR_DOM_OPERATION_ERR;
773 // Export the key to get the cleartext
774 rv = MapSECStatus(PK11_ExtractKeyValue(unwrappedKey.get()));
775 if (NS_FAILED(rv)) {
776 return NS_ERROR_DOM_UNKNOWN_ERR;
778 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(unwrappedKey.get()));
781 return rv;
785 class RsaOaepTask : public ReturnArrayBufferViewTask, public DeferredData {
786 public:
787 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
788 bool aEncrypt)
789 : mPrivKey(aKey.GetPrivateKey()),
790 mPubKey(aKey.GetPublicKey()),
791 mEncrypt(aEncrypt) {
792 Init(aCx, aAlgorithm, aKey, aEncrypt);
795 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
796 const CryptoOperationData& aData, bool aEncrypt)
797 : mPrivKey(aKey.GetPrivateKey()),
798 mPubKey(aKey.GetPublicKey()),
799 mEncrypt(aEncrypt) {
800 Init(aCx, aAlgorithm, aKey, aEncrypt);
801 SetData(aData);
804 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
805 bool aEncrypt) {
806 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_OAEP);
808 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_OAEP);
810 if (mEncrypt) {
811 if (!mPubKey) {
812 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
813 return;
815 mStrength = SECKEY_PublicKeyStrength(mPubKey.get());
816 } else {
817 if (!mPrivKey) {
818 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
819 return;
821 mStrength = PK11_GetPrivateModulusLen(mPrivKey.get());
824 // The algorithm could just be given as a string
825 // in which case there would be no label specified.
826 if (!aAlgorithm.IsString()) {
827 RootedDictionary<RsaOaepParams> params(aCx);
828 mEarlyRv = Coerce(aCx, params, aAlgorithm);
829 if (NS_FAILED(mEarlyRv)) {
830 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
831 return;
834 if (params.mLabel.WasPassed()) {
835 ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value());
838 // Otherwise mLabel remains the empty octet string, as intended
840 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
841 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
842 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlg.mName);
844 // Check we found appropriate mechanisms.
845 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
846 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
847 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
848 return;
852 private:
853 CK_MECHANISM_TYPE mHashMechanism;
854 CK_MECHANISM_TYPE mMgfMechanism;
855 UniqueSECKEYPrivateKey mPrivKey;
856 UniqueSECKEYPublicKey mPubKey;
857 CryptoBuffer mLabel;
858 uint32_t mStrength;
859 bool mEncrypt;
861 virtual nsresult DoCrypto() override {
862 nsresult rv;
864 if (!mDataIsSet) {
865 return NS_ERROR_DOM_OPERATION_ERR;
868 // Ciphertext is an integer mod the modulus, so it will be
869 // no longer than mStrength octets
870 if (!mResult.SetLength(mStrength, fallible)) {
871 return NS_ERROR_DOM_UNKNOWN_ERR;
874 CK_RSA_PKCS_OAEP_PARAMS oaepParams;
875 oaepParams.source = CKZ_DATA_SPECIFIED;
877 oaepParams.pSourceData = mLabel.Length() ? mLabel.Elements() : nullptr;
878 oaepParams.ulSourceDataLen = mLabel.Length();
880 oaepParams.mgf = mMgfMechanism;
881 oaepParams.hashAlg = mHashMechanism;
883 SECItem param;
884 param.type = siBuffer;
885 param.data = (unsigned char*)&oaepParams;
886 param.len = sizeof(oaepParams);
888 uint32_t outLen = 0;
889 if (mEncrypt) {
890 // PK11_PubEncrypt() checks the plaintext's length and fails if it is too
891 // long to encrypt, i.e. if it is longer than (k - 2hLen - 2) with 'k'
892 // being the length in octets of the RSA modulus n and 'hLen' being the
893 // output length in octets of the chosen hash function.
894 // <https://tools.ietf.org/html/rfc3447#section-7.1>
895 rv = MapSECStatus(PK11_PubEncrypt(
896 mPubKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(), &outLen,
897 mResult.Length(), mData.Elements(), mData.Length(), nullptr));
898 } else {
899 rv = MapSECStatus(PK11_PrivDecrypt(
900 mPrivKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(),
901 &outLen, mResult.Length(), mData.Elements(), mData.Length()));
903 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
905 mResult.TruncateLength(outLen);
906 return NS_OK;
910 class HmacTask : public WebCryptoTask {
911 public:
912 HmacTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
913 const CryptoOperationData& aSignature,
914 const CryptoOperationData& aData, bool aSign)
915 : mMechanism(aKey.Algorithm().Mechanism()), mSign(aSign) {
916 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HMAC);
918 ATTEMPT_BUFFER_INIT(mData, aData);
919 if (!aSign) {
920 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
923 if (!mSymKey.Assign(aKey.GetSymKey())) {
924 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
925 return;
928 // Check that we got a symmetric key
929 if (mSymKey.Length() == 0) {
930 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
931 return;
934 TelemetryAlgorithm telemetryAlg;
935 switch (mMechanism) {
936 case CKM_SHA_1_HMAC:
937 telemetryAlg = TA_HMAC_SHA_1;
938 break;
939 case CKM_SHA224_HMAC:
940 telemetryAlg = TA_HMAC_SHA_224;
941 break;
942 case CKM_SHA256_HMAC:
943 telemetryAlg = TA_HMAC_SHA_256;
944 break;
945 case CKM_SHA384_HMAC:
946 telemetryAlg = TA_HMAC_SHA_384;
947 break;
948 case CKM_SHA512_HMAC:
949 telemetryAlg = TA_HMAC_SHA_512;
950 break;
951 default:
952 telemetryAlg = TA_UNKNOWN;
954 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
957 private:
958 CK_MECHANISM_TYPE mMechanism;
959 CryptoBuffer mSymKey;
960 CryptoBuffer mData;
961 CryptoBuffer mSignature;
962 CryptoBuffer mResult;
963 bool mSign;
965 virtual nsresult DoCrypto() override {
966 // Initialize the output buffer
967 if (!mResult.SetLength(HASH_LENGTH_MAX, fallible)) {
968 return NS_ERROR_DOM_UNKNOWN_ERR;
971 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
972 if (!arena) {
973 return NS_ERROR_DOM_OPERATION_ERR;
976 // Import the key
977 uint32_t outLen;
978 SECItem keyItem = {siBuffer, nullptr, 0};
979 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
980 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
981 MOZ_ASSERT(slot.get());
982 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
983 PK11_OriginUnwrap, CKA_SIGN,
984 &keyItem, nullptr));
985 if (!symKey) {
986 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
989 // Compute the MAC
990 SECItem param = {siBuffer, nullptr, 0};
991 UniquePK11Context ctx(
992 PK11_CreateContextBySymKey(mMechanism, CKA_SIGN, symKey.get(), &param));
993 if (!ctx.get()) {
994 return NS_ERROR_DOM_OPERATION_ERR;
996 nsresult rv = MapSECStatus(PK11_DigestBegin(ctx.get()));
997 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
998 rv = MapSECStatus(
999 PK11_DigestOp(ctx.get(), mData.Elements(), mData.Length()));
1000 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1001 rv = MapSECStatus(PK11_DigestFinal(ctx.get(), mResult.Elements(), &outLen,
1002 mResult.Length()));
1003 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1005 mResult.TruncateLength(outLen);
1006 return rv;
1009 // Returns mResult as an ArrayBufferView, or an error
1010 virtual void Resolve() override {
1011 if (mSign) {
1012 // Return the computed MAC
1013 TypedArrayCreator<ArrayBuffer> ret(mResult);
1014 mResultPromise->MaybeResolve(ret);
1015 } else {
1016 // Compare the MAC to the provided signature
1017 // No truncation allowed
1018 bool equal = (mResult.Length() == mSignature.Length());
1019 if (equal) {
1020 int cmp = NSS_SecureMemcmp(mSignature.Elements(), mResult.Elements(),
1021 mSignature.Length());
1022 equal = (cmp == 0);
1024 mResultPromise->MaybeResolve(equal);
1029 class AsymmetricSignVerifyTask : public WebCryptoTask {
1030 public:
1031 AsymmetricSignVerifyTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1032 CryptoKey& aKey,
1033 const CryptoOperationData& aSignature,
1034 const CryptoOperationData& aData, bool aSign)
1035 : mOidTag(SEC_OID_UNKNOWN),
1036 mHashMechanism(UNKNOWN_CK_MECHANISM),
1037 mMgfMechanism(UNKNOWN_CK_MECHANISM),
1038 mPrivKey(aKey.GetPrivateKey()),
1039 mPubKey(aKey.GetPublicKey()),
1040 mSaltLength(0),
1041 mSign(aSign),
1042 mVerified(false),
1043 mAlgorithm(Algorithm::UNKNOWN) {
1044 ATTEMPT_BUFFER_INIT(mData, aData);
1045 if (!aSign) {
1046 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
1049 nsString algName;
1050 nsString hashAlgName;
1051 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1052 if (NS_FAILED(mEarlyRv)) {
1053 return;
1056 if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
1057 mAlgorithm = Algorithm::RSA_PKCS1;
1058 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
1059 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
1060 hashAlgName = aKey.Algorithm().mRsa.mHash.mName;
1061 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1062 mAlgorithm = Algorithm::RSA_PSS;
1063 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_PSS);
1064 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_PSS);
1066 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
1067 hashAlgName = hashAlg.mName;
1068 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
1069 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlgName);
1071 // Check we found appropriate mechanisms.
1072 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
1073 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
1074 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1075 return;
1078 RootedDictionary<RsaPssParams> params(aCx);
1079 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1080 if (NS_FAILED(mEarlyRv)) {
1081 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1082 return;
1085 mSaltLength = params.mSaltLength;
1086 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1087 mAlgorithm = Algorithm::ECDSA;
1088 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDSA);
1089 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
1091 // For ECDSA, the hash name comes from the algorithm parameter
1092 RootedDictionary<EcdsaParams> params(aCx);
1093 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1094 if (NS_FAILED(mEarlyRv)) {
1095 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1096 return;
1099 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashAlgName);
1100 if (NS_FAILED(mEarlyRv)) {
1101 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1102 return;
1104 } else {
1105 // This shouldn't happen; CreateSignVerifyTask shouldn't create
1106 // one of these unless it's for the above algorithms.
1107 MOZ_ASSERT(false);
1110 // Must have a valid algorithm by now.
1111 MOZ_ASSERT(mAlgorithm != Algorithm::UNKNOWN);
1113 // Determine hash algorithm to use.
1114 mOidTag = MapHashAlgorithmNameToOID(hashAlgName);
1115 if (mOidTag == SEC_OID_UNKNOWN) {
1116 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1117 return;
1120 // Check that we have the appropriate key
1121 if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
1122 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
1123 return;
1127 private:
1128 SECOidTag mOidTag;
1129 CK_MECHANISM_TYPE mHashMechanism;
1130 CK_MECHANISM_TYPE mMgfMechanism;
1131 UniqueSECKEYPrivateKey mPrivKey;
1132 UniqueSECKEYPublicKey mPubKey;
1133 CryptoBuffer mSignature;
1134 CryptoBuffer mData;
1135 uint32_t mSaltLength;
1136 bool mSign;
1137 bool mVerified;
1139 // The signature algorithm to use.
1140 enum class Algorithm : uint8_t { ECDSA, RSA_PKCS1, RSA_PSS, UNKNOWN };
1141 Algorithm mAlgorithm;
1143 virtual nsresult DoCrypto() override {
1144 SECStatus rv;
1145 UniqueSECItem hash(
1146 ::SECITEM_AllocItem(nullptr, nullptr, HASH_ResultLenByOidTag(mOidTag)));
1147 if (!hash) {
1148 return NS_ERROR_DOM_OPERATION_ERR;
1151 // Compute digest over given data.
1152 rv = PK11_HashBuf(mOidTag, hash->data, mData.Elements(), mData.Length());
1153 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1155 // Wrap hash in a digest info template (RSA-PKCS1 only).
1156 if (mAlgorithm == Algorithm::RSA_PKCS1) {
1157 UniqueSGNDigestInfo di(
1158 SGN_CreateDigestInfo(mOidTag, hash->data, hash->len));
1159 if (!di) {
1160 return NS_ERROR_DOM_OPERATION_ERR;
1163 // Reuse |hash|.
1164 SECITEM_FreeItem(hash.get(), false);
1165 if (!SEC_ASN1EncodeItem(nullptr, hash.get(), di.get(),
1166 SGN_DigestInfoTemplate)) {
1167 return NS_ERROR_DOM_OPERATION_ERR;
1171 SECItem* params = nullptr;
1172 CK_MECHANISM_TYPE mech =
1173 PK11_MapSignKeyType((mSign ? mPrivKey->keyType : mPubKey->keyType));
1175 CK_RSA_PKCS_PSS_PARAMS rsaPssParams;
1176 SECItem rsaPssParamsItem = {
1177 siBuffer,
1180 // Set up parameters for RSA-PSS.
1181 if (mAlgorithm == Algorithm::RSA_PSS) {
1182 rsaPssParams.hashAlg = mHashMechanism;
1183 rsaPssParams.mgf = mMgfMechanism;
1184 rsaPssParams.sLen = mSaltLength;
1186 rsaPssParamsItem.data = (unsigned char*)&rsaPssParams;
1187 rsaPssParamsItem.len = sizeof(rsaPssParams);
1188 params = &rsaPssParamsItem;
1190 mech = CKM_RSA_PKCS_PSS;
1193 // Allocate SECItem to hold the signature.
1194 uint32_t len = mSign ? PK11_SignatureLen(mPrivKey.get()) : 0;
1195 UniqueSECItem sig(::SECITEM_AllocItem(nullptr, nullptr, len));
1196 if (!sig) {
1197 return NS_ERROR_DOM_OPERATION_ERR;
1200 if (mSign) {
1201 // Sign the hash.
1202 rv = PK11_SignWithMechanism(mPrivKey.get(), mech, params, sig.get(),
1203 hash.get());
1204 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1205 ATTEMPT_BUFFER_ASSIGN(mSignature, sig.get());
1206 } else {
1207 // Copy the given signature to the SECItem.
1208 if (!mSignature.ToSECItem(nullptr, sig.get())) {
1209 return NS_ERROR_DOM_OPERATION_ERR;
1212 // Verify the signature.
1213 rv = PK11_VerifyWithMechanism(mPubKey.get(), mech, params, sig.get(),
1214 hash.get(), nullptr);
1215 mVerified = NS_SUCCEEDED(MapSECStatus(rv));
1218 return NS_OK;
1221 virtual void Resolve() override {
1222 if (mSign) {
1223 TypedArrayCreator<ArrayBuffer> ret(mSignature);
1224 mResultPromise->MaybeResolve(ret);
1225 } else {
1226 mResultPromise->MaybeResolve(mVerified);
1231 class DigestTask : public ReturnArrayBufferViewTask {
1232 public:
1233 DigestTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1234 const CryptoOperationData& aData) {
1235 ATTEMPT_BUFFER_INIT(mData, aData);
1237 nsString algName;
1238 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1239 if (NS_FAILED(mEarlyRv)) {
1240 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1241 return;
1244 TelemetryAlgorithm telemetryAlg;
1245 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
1246 telemetryAlg = TA_SHA_1;
1247 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
1248 telemetryAlg = TA_SHA_224;
1249 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
1250 telemetryAlg = TA_SHA_256;
1251 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
1252 telemetryAlg = TA_SHA_384;
1253 } else {
1254 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1255 return;
1257 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
1258 mOidTag = MapHashAlgorithmNameToOID(algName);
1261 private:
1262 SECOidTag mOidTag;
1263 CryptoBuffer mData;
1265 virtual nsresult DoCrypto() override {
1266 // Resize the result buffer
1267 uint32_t hashLen = HASH_ResultLenByOidTag(mOidTag);
1268 if (!mResult.SetLength(hashLen, fallible)) {
1269 return NS_ERROR_DOM_UNKNOWN_ERR;
1272 // Compute the hash
1273 nsresult rv = MapSECStatus(PK11_HashBuf(mOidTag, mResult.Elements(),
1274 mData.Elements(), mData.Length()));
1275 if (NS_FAILED(rv)) {
1276 return NS_ERROR_DOM_UNKNOWN_ERR;
1279 return rv;
1283 class ImportKeyTask : public WebCryptoTask {
1284 public:
1285 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1286 const ObjectOrString& aAlgorithm, bool aExtractable,
1287 const Sequence<nsString>& aKeyUsages) {
1288 mFormat = aFormat;
1289 mDataIsSet = false;
1290 mDataIsJwk = false;
1292 // This stuff pretty much always happens, so we'll do it here
1293 mKey = new CryptoKey(aGlobal);
1294 mKey->SetExtractable(aExtractable);
1295 mKey->ClearUsages();
1296 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
1297 mEarlyRv = mKey->AddUsage(aKeyUsages[i]);
1298 if (NS_FAILED(mEarlyRv)) {
1299 return;
1303 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
1304 if (NS_FAILED(mEarlyRv)) {
1305 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1306 return;
1310 static bool JwkCompatible(const JsonWebKey& aJwk, const CryptoKey* aKey) {
1311 // Check 'ext'
1312 if (aKey->Extractable() && aJwk.mExt.WasPassed() && !aJwk.mExt.Value()) {
1313 return false;
1316 // Check 'alg'
1317 if (aJwk.mAlg.WasPassed() &&
1318 aJwk.mAlg.Value() != aKey->Algorithm().JwkAlg()) {
1319 return false;
1322 // Check 'key_ops'
1323 if (aJwk.mKey_ops.WasPassed()) {
1324 nsTArray<nsString> usages;
1325 aKey->GetUsages(usages);
1326 for (size_t i = 0; i < usages.Length(); ++i) {
1327 if (!aJwk.mKey_ops.Value().Contains(usages[i])) {
1328 return false;
1333 // Individual algorithms may still have to check 'use'
1334 return true;
1337 void SetKeyData(JSContext* aCx, JS::Handle<JSObject*> aKeyData) {
1338 mDataIsJwk = false;
1340 // Try ArrayBuffer
1341 RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
1342 if (ab.Init(aKeyData)) {
1343 if (!mKeyData.Assign(ab)) {
1344 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1346 return;
1349 // Try ArrayBufferView
1350 RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
1351 if (abv.Init(aKeyData)) {
1352 if (!mKeyData.Assign(abv)) {
1353 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1355 return;
1358 // Try JWK
1359 ClearException ce(aCx);
1360 JS::RootedValue value(aCx, JS::ObjectValue(*aKeyData));
1361 if (!mJwk.Init(aCx, value)) {
1362 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1363 return;
1366 mDataIsJwk = true;
1369 void SetKeyDataMaybeParseJWK(const CryptoBuffer& aKeyData) {
1370 if (!mKeyData.Assign(aKeyData)) {
1371 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1372 return;
1375 mDataIsJwk = false;
1377 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1378 nsDependentCSubstring utf8(
1379 (const char*)mKeyData.Elements(),
1380 (const char*)(mKeyData.Elements() + mKeyData.Length()));
1381 if (!IsUtf8(utf8)) {
1382 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1383 return;
1386 nsString json = NS_ConvertUTF8toUTF16(utf8);
1387 if (!mJwk.Init(json)) {
1388 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1389 return;
1392 mDataIsJwk = true;
1396 void SetRawKeyData(const CryptoBuffer& aKeyData) {
1397 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1398 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1399 return;
1402 if (!mKeyData.Assign(aKeyData)) {
1403 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1404 return;
1407 mDataIsJwk = false;
1410 protected:
1411 nsString mFormat;
1412 RefPtr<CryptoKey> mKey;
1413 CryptoBuffer mKeyData;
1414 bool mDataIsSet;
1415 bool mDataIsJwk;
1416 JsonWebKey mJwk;
1417 nsString mAlgName;
1419 private:
1420 virtual void Resolve() override { mResultPromise->MaybeResolve(mKey); }
1422 virtual void Cleanup() override { mKey = nullptr; }
1425 class ImportSymmetricKeyTask : public ImportKeyTask {
1426 public:
1427 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1428 const nsAString& aFormat,
1429 const ObjectOrString& aAlgorithm, bool aExtractable,
1430 const Sequence<nsString>& aKeyUsages) {
1431 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1434 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1435 const nsAString& aFormat,
1436 const JS::Handle<JSObject*> aKeyData,
1437 const ObjectOrString& aAlgorithm, bool aExtractable,
1438 const Sequence<nsString>& aKeyUsages) {
1439 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1440 if (NS_FAILED(mEarlyRv)) {
1441 return;
1444 SetKeyData(aCx, aKeyData);
1445 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1446 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1447 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1448 return;
1452 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1453 const ObjectOrString& aAlgorithm, bool aExtractable,
1454 const Sequence<nsString>& aKeyUsages) {
1455 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1456 aKeyUsages);
1457 if (NS_FAILED(mEarlyRv)) {
1458 return;
1461 // This task only supports raw and JWK format.
1462 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1463 !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1464 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1465 return;
1468 // If this is an HMAC key, import the hash name
1469 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1470 RootedDictionary<HmacImportParams> params(aCx);
1471 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1472 if (NS_FAILED(mEarlyRv)) {
1473 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1474 return;
1476 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1477 if (NS_FAILED(mEarlyRv)) {
1478 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1479 return;
1484 virtual nsresult BeforeCrypto() override {
1485 nsresult rv;
1487 // If we're doing a JWK import, import the key data
1488 if (mDataIsJwk) {
1489 if (!mJwk.mK.WasPassed()) {
1490 return NS_ERROR_DOM_DATA_ERR;
1493 // Import the key material
1494 rv = mKeyData.FromJwkBase64(mJwk.mK.Value());
1495 if (NS_FAILED(rv)) {
1496 return NS_ERROR_DOM_DATA_ERR;
1499 // Check that we have valid key data.
1500 if (mKeyData.Length() == 0 &&
1501 !mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
1502 return NS_ERROR_DOM_DATA_ERR;
1505 // Construct an appropriate KeyAlorithm,
1506 // and verify that usages are appropriate
1507 uint32_t length = 8 * mKeyData.Length(); // bytes to bits
1508 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
1509 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
1510 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
1511 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
1512 if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
1513 CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1514 return NS_ERROR_DOM_DATA_ERR;
1517 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) &&
1518 mKey->HasUsageOtherThan(CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1519 return NS_ERROR_DOM_DATA_ERR;
1522 if ((length != 128) && (length != 192) && (length != 256)) {
1523 return NS_ERROR_DOM_DATA_ERR;
1525 mKey->Algorithm().MakeAes(mAlgName, length);
1527 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1528 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_ENC)) {
1529 return NS_ERROR_DOM_DATA_ERR;
1531 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
1532 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
1533 if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY |
1534 CryptoKey::DERIVEBITS)) {
1535 return NS_ERROR_DOM_DATA_ERR;
1537 mKey->Algorithm().MakeAes(mAlgName, length);
1539 if (mDataIsJwk && mJwk.mUse.WasPassed()) {
1540 // There is not a 'use' value consistent with PBKDF or HKDF
1541 return NS_ERROR_DOM_DATA_ERR;
1543 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1544 if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) {
1545 return NS_ERROR_DOM_DATA_ERR;
1548 mKey->Algorithm().MakeHmac(length, mHashName);
1550 if (mKey->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM) {
1551 return NS_ERROR_DOM_SYNTAX_ERR;
1554 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1555 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_SIG)) {
1556 return NS_ERROR_DOM_DATA_ERR;
1558 } else {
1559 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1562 if (NS_FAILED(mKey->SetSymKey(mKeyData))) {
1563 return NS_ERROR_DOM_OPERATION_ERR;
1566 mKey->SetType(CryptoKey::SECRET);
1568 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1569 return NS_ERROR_DOM_DATA_ERR;
1572 mEarlyComplete = true;
1573 return NS_OK;
1576 private:
1577 nsString mHashName;
1580 class ImportRsaKeyTask : public ImportKeyTask {
1581 public:
1582 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1583 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1584 bool aExtractable, const Sequence<nsString>& aKeyUsages)
1585 : mModulusLength(0) {
1586 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1589 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1590 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1591 const ObjectOrString& aAlgorithm, bool aExtractable,
1592 const Sequence<nsString>& aKeyUsages)
1593 : mModulusLength(0) {
1594 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1595 if (NS_FAILED(mEarlyRv)) {
1596 return;
1599 SetKeyData(aCx, aKeyData);
1600 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1601 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1602 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1603 return;
1607 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1608 const ObjectOrString& aAlgorithm, bool aExtractable,
1609 const Sequence<nsString>& aKeyUsages) {
1610 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1611 aKeyUsages);
1612 if (NS_FAILED(mEarlyRv)) {
1613 return;
1616 // If this is RSA with a hash, cache the hash name
1617 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1618 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
1619 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1620 RootedDictionary<RsaHashedImportParams> params(aCx);
1621 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1622 if (NS_FAILED(mEarlyRv)) {
1623 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1624 return;
1627 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1628 if (NS_FAILED(mEarlyRv)) {
1629 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1630 return;
1634 // Check support for the algorithm and hash names
1635 CK_MECHANISM_TYPE mech1 = MapAlgorithmNameToMechanism(mAlgName);
1636 CK_MECHANISM_TYPE mech2 = MapAlgorithmNameToMechanism(mHashName);
1637 if ((mech1 == UNKNOWN_CK_MECHANISM) || (mech2 == UNKNOWN_CK_MECHANISM)) {
1638 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1639 return;
1643 private:
1644 nsString mHashName;
1645 uint32_t mModulusLength;
1646 CryptoBuffer mPublicExponent;
1648 virtual nsresult DoCrypto() override {
1649 // Import the key data itself
1650 UniqueSECKEYPublicKey pubKey;
1651 UniqueSECKEYPrivateKey privKey;
1652 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1653 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1654 !mJwk.mD.WasPassed())) {
1655 // Public key import
1656 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1657 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1658 } else {
1659 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1662 if (!pubKey) {
1663 return NS_ERROR_DOM_DATA_ERR;
1666 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1667 return NS_ERROR_DOM_OPERATION_ERR;
1670 mKey->SetType(CryptoKey::PUBLIC);
1671 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) ||
1672 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1673 mJwk.mD.WasPassed())) {
1674 // Private key import
1675 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1676 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
1677 } else {
1678 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1681 if (!privKey) {
1682 return NS_ERROR_DOM_DATA_ERR;
1685 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1686 return NS_ERROR_DOM_OPERATION_ERR;
1689 mKey->SetType(CryptoKey::PRIVATE);
1690 pubKey = UniqueSECKEYPublicKey(SECKEY_ConvertToPublicKey(privKey.get()));
1691 if (!pubKey) {
1692 return NS_ERROR_DOM_UNKNOWN_ERR;
1694 } else {
1695 // Invalid key format
1696 return NS_ERROR_DOM_SYNTAX_ERR;
1699 // Extract relevant information from the public key
1700 mModulusLength = 8 * pubKey->u.rsa.modulus.len;
1701 if (!mPublicExponent.Assign(&pubKey->u.rsa.publicExponent)) {
1702 return NS_ERROR_DOM_OPERATION_ERR;
1705 return NS_OK;
1708 virtual nsresult AfterCrypto() override {
1709 // Check permissions for the requested operation
1710 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
1711 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1712 mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) ||
1713 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1714 mKey->HasUsageOtherThan(CryptoKey::DECRYPT |
1715 CryptoKey::UNWRAPKEY))) {
1716 return NS_ERROR_DOM_DATA_ERR;
1718 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1719 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1720 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1721 mKey->HasUsageOtherThan(CryptoKey::VERIFY)) ||
1722 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1723 mKey->HasUsageOtherThan(CryptoKey::SIGN))) {
1724 return NS_ERROR_DOM_DATA_ERR;
1728 // Set an appropriate KeyAlgorithm
1729 if (!mKey->Algorithm().MakeRsa(mAlgName, mModulusLength, mPublicExponent,
1730 mHashName)) {
1731 return NS_ERROR_DOM_OPERATION_ERR;
1734 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1735 return NS_ERROR_DOM_DATA_ERR;
1738 return NS_OK;
1742 class ImportEcKeyTask : public ImportKeyTask {
1743 public:
1744 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1745 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1746 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
1747 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1750 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1751 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1752 const ObjectOrString& aAlgorithm, bool aExtractable,
1753 const Sequence<nsString>& aKeyUsages) {
1754 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1755 if (NS_FAILED(mEarlyRv)) {
1756 return;
1759 SetKeyData(aCx, aKeyData);
1760 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1763 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1764 const ObjectOrString& aAlgorithm, bool aExtractable,
1765 const Sequence<nsString>& aKeyUsages) {
1766 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1767 aKeyUsages);
1768 if (NS_FAILED(mEarlyRv)) {
1769 return;
1772 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1773 RootedDictionary<EcKeyImportParams> params(aCx);
1774 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1775 if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
1776 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1777 return;
1780 if (!NormalizeToken(params.mNamedCurve.Value(), mNamedCurve)) {
1781 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1782 return;
1787 private:
1788 nsString mNamedCurve;
1790 virtual nsresult DoCrypto() override {
1791 // Import the key data itself
1792 UniqueSECKEYPublicKey pubKey;
1793 UniqueSECKEYPrivateKey privKey;
1795 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1796 mJwk.mD.WasPassed()) {
1797 // Private key import
1798 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1799 if (!privKey) {
1800 return NS_ERROR_DOM_DATA_ERR;
1803 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1804 return NS_ERROR_DOM_OPERATION_ERR;
1807 mKey->SetType(CryptoKey::PRIVATE);
1808 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
1809 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1810 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1811 !mJwk.mD.WasPassed())) {
1812 // Public key import
1813 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1814 pubKey = CryptoKey::PublicECKeyFromRaw(mKeyData, mNamedCurve);
1815 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1816 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1817 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1818 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1819 } else {
1820 MOZ_ASSERT(false);
1823 if (!pubKey) {
1824 return NS_ERROR_DOM_DATA_ERR;
1827 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1828 if (!CheckEncodedECParameters(&pubKey->u.ec.DEREncodedParams)) {
1829 return NS_ERROR_DOM_OPERATION_ERR;
1832 // Construct the OID tag.
1833 SECItem oid = {siBuffer, nullptr, 0};
1834 oid.len = pubKey->u.ec.DEREncodedParams.data[1];
1835 oid.data = pubKey->u.ec.DEREncodedParams.data + 2;
1837 // Find a matching and supported named curve.
1838 if (!MapOIDTagToNamedCurve(SECOID_FindOIDTag(&oid), mNamedCurve)) {
1839 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1843 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1844 return NS_ERROR_DOM_OPERATION_ERR;
1847 mKey->SetType(CryptoKey::PUBLIC);
1848 } else {
1849 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1852 // Extract 'crv' parameter from JWKs.
1853 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1854 if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) {
1855 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1859 return NS_OK;
1862 virtual nsresult AfterCrypto() override {
1863 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
1864 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
1865 privateAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
1866 publicAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
1867 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1868 privateAllowedUsages = CryptoKey::SIGN;
1869 publicAllowedUsages = CryptoKey::VERIFY;
1872 // Check permissions for the requested operation
1873 if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
1874 mKey->HasUsageOtherThan(privateAllowedUsages)) ||
1875 (mKey->GetKeyType() == CryptoKey::PUBLIC &&
1876 mKey->HasUsageOtherThan(publicAllowedUsages))) {
1877 return NS_ERROR_DOM_DATA_ERR;
1880 mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
1882 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1883 return NS_ERROR_DOM_DATA_ERR;
1886 return NS_OK;
1890 class ExportKeyTask : public WebCryptoTask {
1891 public:
1892 ExportKeyTask(const nsAString& aFormat, CryptoKey& aKey)
1893 : mFormat(aFormat),
1894 mPrivateKey(aKey.GetPrivateKey()),
1895 mPublicKey(aKey.GetPublicKey()),
1896 mKeyType(aKey.GetKeyType()),
1897 mExtractable(aKey.Extractable()),
1898 mAlg(aKey.Algorithm().JwkAlg()) {
1899 aKey.GetUsages(mKeyUsages);
1901 if (!mSymKey.Assign(aKey.GetSymKey())) {
1902 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
1903 return;
1907 protected:
1908 nsString mFormat;
1909 CryptoBuffer mSymKey;
1910 UniqueSECKEYPrivateKey mPrivateKey;
1911 UniqueSECKEYPublicKey mPublicKey;
1912 CryptoKey::KeyType mKeyType;
1913 bool mExtractable;
1914 nsString mAlg;
1915 nsTArray<nsString> mKeyUsages;
1916 CryptoBuffer mResult;
1917 JsonWebKey mJwk;
1919 private:
1920 virtual nsresult DoCrypto() override {
1921 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1922 if (mPublicKey && mPublicKey->keyType == dhKey) {
1923 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1926 if (mPublicKey && mPublicKey->keyType == ecKey) {
1927 nsresult rv = CryptoKey::PublicECKeyToRaw(mPublicKey.get(), mResult);
1928 if (NS_FAILED(rv)) {
1929 return NS_ERROR_DOM_OPERATION_ERR;
1931 return NS_OK;
1934 if (!mResult.Assign(mSymKey)) {
1935 return NS_ERROR_OUT_OF_MEMORY;
1937 if (mResult.Length() == 0) {
1938 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1941 return NS_OK;
1942 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1943 if (!mPrivateKey) {
1944 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1947 switch (mPrivateKey->keyType) {
1948 case rsaKey: {
1949 nsresult rv =
1950 CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), mResult);
1951 if (NS_FAILED(rv)) {
1952 return NS_ERROR_DOM_OPERATION_ERR;
1954 return NS_OK;
1956 default:
1957 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1959 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1960 if (!mPublicKey) {
1961 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1964 return CryptoKey::PublicKeyToSpki(mPublicKey.get(), mResult);
1965 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1966 if (mKeyType == CryptoKey::SECRET) {
1967 nsString k;
1968 nsresult rv = mSymKey.ToJwkBase64(k);
1969 if (NS_FAILED(rv)) {
1970 return NS_ERROR_DOM_OPERATION_ERR;
1972 mJwk.mK.Construct(k);
1973 mJwk.mKty = NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_SYMMETRIC);
1974 } else if (mKeyType == CryptoKey::PUBLIC) {
1975 if (!mPublicKey) {
1976 return NS_ERROR_DOM_UNKNOWN_ERR;
1979 nsresult rv = CryptoKey::PublicKeyToJwk(mPublicKey.get(), mJwk);
1980 if (NS_FAILED(rv)) {
1981 return NS_ERROR_DOM_OPERATION_ERR;
1983 } else if (mKeyType == CryptoKey::PRIVATE) {
1984 if (!mPrivateKey) {
1985 return NS_ERROR_DOM_UNKNOWN_ERR;
1988 nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey.get(), mJwk);
1989 if (NS_FAILED(rv)) {
1990 return NS_ERROR_DOM_OPERATION_ERR;
1994 if (!mAlg.IsEmpty()) {
1995 mJwk.mAlg.Construct(mAlg);
1998 mJwk.mExt.Construct(mExtractable);
2000 mJwk.mKey_ops.Construct();
2001 if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
2002 return NS_ERROR_OUT_OF_MEMORY;
2005 return NS_OK;
2008 return NS_ERROR_DOM_SYNTAX_ERR;
2011 // Returns mResult as an ArrayBufferView or JWK, as appropriate
2012 virtual void Resolve() override {
2013 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2014 mResultPromise->MaybeResolve(mJwk);
2015 return;
2018 TypedArrayCreator<ArrayBuffer> ret(mResult);
2019 mResultPromise->MaybeResolve(ret);
2023 class GenerateSymmetricKeyTask : public WebCryptoTask {
2024 public:
2025 GenerateSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2026 const ObjectOrString& aAlgorithm, bool aExtractable,
2027 const Sequence<nsString>& aKeyUsages) {
2028 // Create an empty key and set easy attributes
2029 mKey = new CryptoKey(aGlobal);
2030 mKey->SetExtractable(aExtractable);
2031 mKey->SetType(CryptoKey::SECRET);
2033 // Extract algorithm name
2034 nsString algName;
2035 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
2036 if (NS_FAILED(mEarlyRv)) {
2037 return;
2040 // Construct an appropriate KeyAlorithm
2041 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2042 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2043 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2044 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
2045 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aAlgorithm, mLength);
2046 if (NS_FAILED(mEarlyRv)) {
2047 return;
2049 mKey->Algorithm().MakeAes(algName, mLength);
2051 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2052 RootedDictionary<HmacKeyGenParams> params(aCx);
2053 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2054 if (NS_FAILED(mEarlyRv)) {
2055 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2056 return;
2059 nsString hashName;
2060 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2061 if (NS_FAILED(mEarlyRv)) {
2062 return;
2065 if (params.mLength.WasPassed()) {
2066 mLength = params.mLength.Value();
2067 } else {
2068 mLength = MapHashAlgorithmNameToBlockSize(hashName);
2071 if (mLength == 0) {
2072 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2073 return;
2076 mKey->Algorithm().MakeHmac(mLength, hashName);
2077 } else {
2078 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2079 return;
2082 // Add key usages
2083 mKey->ClearUsages();
2084 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2085 mEarlyRv = mKey->AddAllowedUsageIntersecting(aKeyUsages[i], algName);
2086 if (NS_FAILED(mEarlyRv)) {
2087 return;
2090 if (!mKey->HasAnyUsage()) {
2091 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2092 return;
2095 mLength = mLength >> 3; // bits to bytes
2096 mMechanism = mKey->Algorithm().Mechanism();
2097 // SetSymKey done in Resolve, after we've done the keygen
2100 private:
2101 RefPtr<CryptoKey> mKey;
2102 size_t mLength;
2103 CK_MECHANISM_TYPE mMechanism;
2104 CryptoBuffer mKeyData;
2106 virtual nsresult DoCrypto() override {
2107 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2108 MOZ_ASSERT(slot.get());
2110 UniquePK11SymKey symKey(
2111 PK11_KeyGen(slot.get(), mMechanism, nullptr, mLength, nullptr));
2112 if (!symKey) {
2113 return NS_ERROR_DOM_UNKNOWN_ERR;
2116 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2117 if (NS_FAILED(rv)) {
2118 return NS_ERROR_DOM_UNKNOWN_ERR;
2121 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2122 // just refers to a buffer managed by symKey. The assignment copies the
2123 // data, so mKeyData manages one copy, while symKey manages another.
2124 ATTEMPT_BUFFER_ASSIGN(mKeyData, PK11_GetKeyData(symKey.get()));
2125 return NS_OK;
2128 virtual void Resolve() override {
2129 if (NS_SUCCEEDED(mKey->SetSymKey(mKeyData))) {
2130 mResultPromise->MaybeResolve(mKey);
2131 } else {
2132 mResultPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
2136 virtual void Cleanup() override { mKey = nullptr; }
2139 GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask(
2140 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
2141 bool aExtractable, const Sequence<nsString>& aKeyUsages)
2142 : mKeyPair(new CryptoKeyPair()),
2143 mMechanism(CKM_INVALID_MECHANISM),
2144 mRsaParams(),
2145 mDhParams() {
2146 mArena = UniquePLArenaPool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2147 if (!mArena) {
2148 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
2149 return;
2152 // Create an empty key pair and set easy attributes
2153 mKeyPair->mPrivateKey = new CryptoKey(aGlobal);
2154 mKeyPair->mPublicKey = new CryptoKey(aGlobal);
2156 // Extract algorithm name
2157 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
2158 if (NS_FAILED(mEarlyRv)) {
2159 return;
2162 // Construct an appropriate KeyAlorithm
2163 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
2164 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2165 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
2166 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
2167 RootedDictionary<RsaHashedKeyGenParams> params(aCx);
2168 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2169 if (NS_FAILED(mEarlyRv)) {
2170 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2171 return;
2174 // Pull relevant info
2175 uint32_t modulusLength = params.mModulusLength;
2176 CryptoBuffer publicExponent;
2177 ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
2178 nsString hashName;
2179 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2180 if (NS_FAILED(mEarlyRv)) {
2181 return;
2184 // Create algorithm
2185 if (!mKeyPair->mPublicKey->Algorithm().MakeRsa(mAlgName, modulusLength,
2186 publicExponent, hashName)) {
2187 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2188 return;
2190 if (!mKeyPair->mPrivateKey->Algorithm().MakeRsa(mAlgName, modulusLength,
2191 publicExponent, hashName)) {
2192 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2193 return;
2195 mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
2197 // Set up params struct
2198 mRsaParams.keySizeInBits = modulusLength;
2199 bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
2200 if (!converted) {
2201 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2202 return;
2204 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
2205 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2206 RootedDictionary<EcKeyGenParams> params(aCx);
2207 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2208 if (NS_FAILED(mEarlyRv)) {
2209 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2210 return;
2213 if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
2214 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2215 return;
2218 // Create algorithm.
2219 mKeyPair->mPublicKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2220 mKeyPair->mPrivateKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2221 mMechanism = CKM_EC_KEY_PAIR_GEN;
2222 } else {
2223 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2224 return;
2227 // Set key usages.
2228 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2229 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
2230 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2231 privateAllowedUsages = CryptoKey::SIGN;
2232 publicAllowedUsages = CryptoKey::VERIFY;
2233 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
2234 privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
2235 publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
2236 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
2237 privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
2238 publicAllowedUsages = 0;
2239 } else {
2240 MOZ_ASSERT(false); // This shouldn't happen.
2243 mKeyPair->mPrivateKey->SetExtractable(aExtractable);
2244 mKeyPair->mPrivateKey->SetType(CryptoKey::PRIVATE);
2246 mKeyPair->mPublicKey->SetExtractable(true);
2247 mKeyPair->mPublicKey->SetType(CryptoKey::PUBLIC);
2249 mKeyPair->mPrivateKey->ClearUsages();
2250 mKeyPair->mPublicKey->ClearUsages();
2251 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2252 mEarlyRv = mKeyPair->mPrivateKey->AddAllowedUsageIntersecting(
2253 aKeyUsages[i], mAlgName, privateAllowedUsages);
2254 if (NS_FAILED(mEarlyRv)) {
2255 return;
2258 mEarlyRv = mKeyPair->mPublicKey->AddAllowedUsageIntersecting(
2259 aKeyUsages[i], mAlgName, publicAllowedUsages);
2260 if (NS_FAILED(mEarlyRv)) {
2261 return;
2266 nsresult GenerateAsymmetricKeyTask::DoCrypto() {
2267 MOZ_ASSERT(mKeyPair);
2269 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2270 MOZ_ASSERT(slot.get());
2272 void* param;
2273 switch (mMechanism) {
2274 case CKM_RSA_PKCS_KEY_PAIR_GEN:
2275 param = &mRsaParams;
2276 break;
2277 case CKM_DH_PKCS_KEY_PAIR_GEN:
2278 param = &mDhParams;
2279 break;
2280 case CKM_EC_KEY_PAIR_GEN: {
2281 param = CreateECParamsForCurve(mNamedCurve, mArena.get());
2282 if (!param) {
2283 return NS_ERROR_DOM_UNKNOWN_ERR;
2285 break;
2287 default:
2288 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2291 SECKEYPublicKey* pubKey = nullptr;
2292 mPrivateKey = UniqueSECKEYPrivateKey(PK11_GenerateKeyPair(
2293 slot.get(), mMechanism, param, &pubKey, PR_FALSE, PR_FALSE, nullptr));
2294 mPublicKey = UniqueSECKEYPublicKey(pubKey);
2295 pubKey = nullptr;
2296 if (!mPrivateKey.get() || !mPublicKey.get()) {
2297 return NS_ERROR_DOM_OPERATION_ERR;
2300 // If no usages ended up being allowed, SyntaxError
2301 if (!mKeyPair->mPrivateKey->HasAnyUsage()) {
2302 return NS_ERROR_DOM_SYNTAX_ERR;
2305 nsresult rv = mKeyPair->mPrivateKey->SetPrivateKey(mPrivateKey.get());
2306 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2307 rv = mKeyPair->mPublicKey->SetPublicKey(mPublicKey.get());
2308 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2310 // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
2311 // private key, we need this later when exporting to PKCS8 and JWK though.
2312 if (mMechanism == CKM_EC_KEY_PAIR_GEN) {
2313 rv = mKeyPair->mPrivateKey->AddPublicKeyData(mPublicKey.get());
2314 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2317 return NS_OK;
2320 void GenerateAsymmetricKeyTask::Resolve() {
2321 mResultPromise->MaybeResolve(*mKeyPair);
2324 void GenerateAsymmetricKeyTask::Cleanup() { mKeyPair = nullptr; }
2326 class DeriveHkdfBitsTask : public ReturnArrayBufferViewTask {
2327 public:
2328 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2329 CryptoKey& aKey, uint32_t aLength)
2330 : mMechanism(CKM_INVALID_MECHANISM) {
2331 Init(aCx, aAlgorithm, aKey, aLength);
2334 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2335 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2336 : mLengthInBits(0), mLengthInBytes(0), mMechanism(CKM_INVALID_MECHANISM) {
2337 size_t length;
2338 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2340 if (NS_SUCCEEDED(mEarlyRv)) {
2341 Init(aCx, aAlgorithm, aKey, length);
2345 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2346 uint32_t aLength) {
2347 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_HKDF);
2348 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HKDF);
2350 if (!mSymKey.Assign(aKey.GetSymKey())) {
2351 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2352 return;
2355 // Check that we have a key.
2356 if (mSymKey.Length() == 0) {
2357 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2358 return;
2361 RootedDictionary<HkdfParams> params(aCx);
2362 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2363 if (NS_FAILED(mEarlyRv)) {
2364 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2365 return;
2368 // length must be greater than zero.
2369 if (aLength == 0) {
2370 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2371 return;
2374 // Extract the hash algorithm.
2375 nsString hashName;
2376 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2377 if (NS_FAILED(mEarlyRv)) {
2378 return;
2381 // Check the given hash algorithm.
2382 switch (MapAlgorithmNameToMechanism(hashName)) {
2383 case CKM_SHA_1:
2384 mMechanism = CKM_NSS_HKDF_SHA1;
2385 break;
2386 case CKM_SHA256:
2387 mMechanism = CKM_NSS_HKDF_SHA256;
2388 break;
2389 case CKM_SHA384:
2390 mMechanism = CKM_NSS_HKDF_SHA384;
2391 break;
2392 case CKM_SHA512:
2393 mMechanism = CKM_NSS_HKDF_SHA512;
2394 break;
2395 default:
2396 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2397 return;
2400 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2401 ATTEMPT_BUFFER_INIT(mInfo, params.mInfo)
2402 mLengthInBytes = ceil((double)aLength / 8);
2403 mLengthInBits = aLength;
2406 private:
2407 size_t mLengthInBits;
2408 size_t mLengthInBytes;
2409 CryptoBuffer mSalt;
2410 CryptoBuffer mInfo;
2411 CryptoBuffer mSymKey;
2412 CK_MECHANISM_TYPE mMechanism;
2414 virtual nsresult DoCrypto() override {
2415 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2416 if (!arena) {
2417 return NS_ERROR_DOM_OPERATION_ERR;
2420 // Import the key
2421 SECItem keyItem = {siBuffer, nullptr, 0};
2422 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2424 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2425 if (!slot.get()) {
2426 return NS_ERROR_DOM_OPERATION_ERR;
2429 UniquePK11SymKey baseKey(PK11_ImportSymKey(slot.get(), mMechanism,
2430 PK11_OriginUnwrap, CKA_WRAP,
2431 &keyItem, nullptr));
2432 if (!baseKey) {
2433 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2436 SECItem salt = {siBuffer, nullptr, 0};
2437 SECItem info = {siBuffer, nullptr, 0};
2438 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2439 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &info, mInfo);
2441 CK_NSS_HKDFParams hkdfParams = {true, salt.data, salt.len,
2442 true, info.data, info.len};
2443 SECItem params = {siBuffer, (unsigned char*)&hkdfParams,
2444 sizeof(hkdfParams)};
2446 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2447 // derived symmetric key and don't matter because we ignore them anyway.
2448 UniquePK11SymKey symKey(PK11_Derive(baseKey.get(), mMechanism, &params,
2449 CKM_SHA512_HMAC, CKA_SIGN,
2450 mLengthInBytes));
2452 if (!symKey.get()) {
2453 return NS_ERROR_DOM_OPERATION_ERR;
2456 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2457 if (NS_FAILED(rv)) {
2458 return NS_ERROR_DOM_OPERATION_ERR;
2461 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2462 // just refers to a buffer managed by symKey. The assignment copies the
2463 // data, so mResult manages one copy, while symKey manages another.
2464 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2466 if (mLengthInBytes > mResult.Length()) {
2467 return NS_ERROR_DOM_DATA_ERR;
2470 if (!mResult.SetLength(mLengthInBytes, fallible)) {
2471 return NS_ERROR_DOM_UNKNOWN_ERR;
2474 // If the number of bits to derive is not a multiple of 8 we need to
2475 // zero out the remaining bits that were derived but not requested.
2476 if (mLengthInBits % 8) {
2477 mResult[mResult.Length() - 1] &= 0xff << (mLengthInBits % 8);
2480 return NS_OK;
2484 class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask {
2485 public:
2486 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2487 CryptoKey& aKey, uint32_t aLength)
2488 : mHashOidTag(SEC_OID_UNKNOWN) {
2489 Init(aCx, aAlgorithm, aKey, aLength);
2492 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2493 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2494 : mLength(0), mIterations(0), mHashOidTag(SEC_OID_UNKNOWN) {
2495 size_t length;
2496 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2498 if (NS_SUCCEEDED(mEarlyRv)) {
2499 Init(aCx, aAlgorithm, aKey, length);
2503 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2504 uint32_t aLength) {
2505 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_PBKDF2);
2506 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_PBKDF2);
2508 if (!mSymKey.Assign(aKey.GetSymKey())) {
2509 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2510 return;
2513 RootedDictionary<Pbkdf2Params> params(aCx);
2514 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2515 if (NS_FAILED(mEarlyRv)) {
2516 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2517 return;
2520 // length must be a multiple of 8 bigger than zero.
2521 if (aLength == 0 || aLength % 8) {
2522 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2523 return;
2526 // Extract the hash algorithm.
2527 nsString hashName;
2528 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2529 if (NS_FAILED(mEarlyRv)) {
2530 return;
2533 // Check the given hash algorithm.
2534 switch (MapAlgorithmNameToMechanism(hashName)) {
2535 case CKM_SHA_1:
2536 mHashOidTag = SEC_OID_HMAC_SHA1;
2537 break;
2538 case CKM_SHA256:
2539 mHashOidTag = SEC_OID_HMAC_SHA256;
2540 break;
2541 case CKM_SHA384:
2542 mHashOidTag = SEC_OID_HMAC_SHA384;
2543 break;
2544 case CKM_SHA512:
2545 mHashOidTag = SEC_OID_HMAC_SHA512;
2546 break;
2547 default:
2548 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2549 return;
2552 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2553 mLength = aLength >> 3; // bits to bytes
2554 mIterations = params.mIterations;
2557 private:
2558 size_t mLength;
2559 size_t mIterations;
2560 CryptoBuffer mSalt;
2561 CryptoBuffer mSymKey;
2562 SECOidTag mHashOidTag;
2564 virtual nsresult DoCrypto() override {
2565 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2566 if (!arena) {
2567 return NS_ERROR_DOM_OPERATION_ERR;
2570 SECItem salt = {siBuffer, nullptr, 0};
2571 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2572 // PK11_CreatePBEV2AlgorithmID will "helpfully" create PBKDF2 parameters
2573 // with a random salt if given a SECItem* that is either null or has a null
2574 // data pointer. This obviously isn't what we want, so we have to fake it
2575 // out by passing in a SECItem* with a non-null data pointer but with zero
2576 // length.
2577 if (!salt.data) {
2578 MOZ_ASSERT(salt.len == 0);
2579 salt.data =
2580 reinterpret_cast<unsigned char*>(PORT_ArenaAlloc(arena.get(), 1));
2581 if (!salt.data) {
2582 return NS_ERROR_DOM_UNKNOWN_ERR;
2586 // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this
2587 // parameter is unused for key generation. It is currently only used
2588 // for PBKDF2 authentication or key (un)wrapping when specifying an
2589 // encryption algorithm (PBES2).
2590 UniqueSECAlgorithmID algID(
2591 PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1,
2592 mHashOidTag, mLength, mIterations, &salt));
2594 if (!algID) {
2595 return NS_ERROR_DOM_OPERATION_ERR;
2598 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2599 if (!slot.get()) {
2600 return NS_ERROR_DOM_OPERATION_ERR;
2603 SECItem keyItem = {siBuffer, nullptr, 0};
2604 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2606 UniquePK11SymKey symKey(
2607 PK11_PBEKeyGen(slot.get(), algID.get(), &keyItem, false, nullptr));
2608 if (!symKey.get()) {
2609 return NS_ERROR_DOM_OPERATION_ERR;
2612 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2613 if (NS_FAILED(rv)) {
2614 return NS_ERROR_DOM_OPERATION_ERR;
2617 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2618 // just refers to a buffer managed by symKey. The assignment copies the
2619 // data, so mResult manages one copy, while symKey manages another.
2620 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2621 return NS_OK;
2625 template <class DeriveBitsTask>
2626 class DeriveKeyTask : public DeriveBitsTask {
2627 public:
2628 DeriveKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2629 const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
2630 const ObjectOrString& aDerivedKeyType, bool aExtractable,
2631 const Sequence<nsString>& aKeyUsages)
2632 : DeriveBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType) {
2633 if (NS_FAILED(this->mEarlyRv)) {
2634 return;
2637 constexpr auto format =
2638 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_FORMAT_RAW);
2639 mTask = new ImportSymmetricKeyTask(aGlobal, aCx, format, aDerivedKeyType,
2640 aExtractable, aKeyUsages);
2643 protected:
2644 RefPtr<ImportSymmetricKeyTask> mTask;
2646 private:
2647 virtual void Resolve() override {
2648 mTask->SetRawKeyData(this->mResult);
2649 mTask->DispatchWithPromise(this->mResultPromise);
2652 virtual void Cleanup() override { mTask = nullptr; }
2655 class DeriveEcdhBitsTask : public ReturnArrayBufferViewTask {
2656 public:
2657 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2658 CryptoKey& aKey, uint32_t aLength)
2659 : mLength(aLength), mPrivKey(aKey.GetPrivateKey()) {
2660 Init(aCx, aAlgorithm, aKey);
2663 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2664 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2665 : mPrivKey(aKey.GetPrivateKey()) {
2666 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, mLength);
2667 if (NS_SUCCEEDED(mEarlyRv)) {
2668 Init(aCx, aAlgorithm, aKey);
2672 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey) {
2673 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDH);
2674 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDH);
2676 // Check that we have a private key.
2677 if (!mPrivKey) {
2678 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2679 return;
2682 // Length must be a multiple of 8 bigger than zero.
2683 if (mLength == 0 || mLength % 8) {
2684 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2685 return;
2688 mLength = mLength >> 3; // bits to bytes
2690 // Retrieve the peer's public key.
2691 RootedDictionary<EcdhKeyDeriveParams> params(aCx);
2692 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2693 if (NS_FAILED(mEarlyRv)) {
2694 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2695 return;
2698 CryptoKey* publicKey = params.mPublic;
2699 mPubKey = publicKey->GetPublicKey();
2700 if (!mPubKey) {
2701 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2702 return;
2705 CHECK_KEY_ALGORITHM(publicKey->Algorithm(), WEBCRYPTO_ALG_ECDH);
2707 // Both keys must use the same named curve.
2708 nsString curve1 = aKey.Algorithm().mEc.mNamedCurve;
2709 nsString curve2 = publicKey->Algorithm().mEc.mNamedCurve;
2711 if (!curve1.Equals(curve2)) {
2712 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2713 return;
2717 private:
2718 size_t mLength;
2719 UniqueSECKEYPrivateKey mPrivKey;
2720 UniqueSECKEYPublicKey mPubKey;
2722 virtual nsresult DoCrypto() override {
2723 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2724 // derived symmetric key and don't matter because we ignore them anyway.
2725 UniquePK11SymKey symKey(
2726 PK11_PubDeriveWithKDF(mPrivKey.get(), mPubKey.get(), PR_FALSE, nullptr,
2727 nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC,
2728 CKA_SIGN, 0, CKD_NULL, nullptr, nullptr));
2730 if (!symKey.get()) {
2731 return NS_ERROR_DOM_OPERATION_ERR;
2734 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2735 if (NS_FAILED(rv)) {
2736 return NS_ERROR_DOM_OPERATION_ERR;
2739 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2740 // just refers to a buffer managed by symKey. The assignment copies the
2741 // data, so mResult manages one copy, while symKey manages another.
2742 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2744 if (mLength > mResult.Length()) {
2745 return NS_ERROR_DOM_DATA_ERR;
2748 if (!mResult.SetLength(mLength, fallible)) {
2749 return NS_ERROR_DOM_UNKNOWN_ERR;
2752 return NS_OK;
2756 template <class KeyEncryptTask>
2757 class WrapKeyTask : public ExportKeyTask {
2758 public:
2759 WrapKeyTask(JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
2760 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm)
2761 : ExportKeyTask(aFormat, aKey) {
2762 if (NS_FAILED(mEarlyRv)) {
2763 return;
2766 mTask = new KeyEncryptTask(aCx, aWrapAlgorithm, aWrappingKey, true);
2769 private:
2770 RefPtr<KeyEncryptTask> mTask;
2772 virtual nsresult AfterCrypto() override {
2773 // If wrapping JWK, stringify the JSON
2774 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2775 nsAutoString json;
2776 if (!mJwk.ToJSON(json)) {
2777 return NS_ERROR_DOM_OPERATION_ERR;
2780 NS_ConvertUTF16toUTF8 utf8(json);
2781 if (!mResult.Assign((const uint8_t*)utf8.BeginReading(), utf8.Length())) {
2782 return NS_ERROR_DOM_OPERATION_ERR;
2786 return NS_OK;
2789 virtual void Resolve() override {
2790 mTask->SetData(mResult);
2791 mTask->DispatchWithPromise(mResultPromise);
2794 virtual void Cleanup() override { mTask = nullptr; }
2797 template <class KeyEncryptTask>
2798 class UnwrapKeyTask : public KeyEncryptTask {
2799 public:
2800 UnwrapKeyTask(JSContext* aCx, const ArrayBufferViewOrArrayBuffer& aWrappedKey,
2801 CryptoKey& aUnwrappingKey,
2802 const ObjectOrString& aUnwrapAlgorithm, ImportKeyTask* aTask)
2803 : KeyEncryptTask(aCx, aUnwrapAlgorithm, aUnwrappingKey, aWrappedKey,
2804 false),
2805 mTask(aTask) {}
2807 private:
2808 RefPtr<ImportKeyTask> mTask;
2810 virtual void Resolve() override {
2811 mTask->SetKeyDataMaybeParseJWK(KeyEncryptTask::mResult);
2812 mTask->DispatchWithPromise(KeyEncryptTask::mResultPromise);
2815 virtual void Cleanup() override { mTask = nullptr; }
2818 // Task creation methods for WebCryptoTask
2820 // Note: We do not perform algorithm normalization as a monolithic process,
2821 // as described in the spec. Instead:
2822 // * Each method handles its slice of the supportedAlgorithms structure
2823 // * Task constructors take care of:
2824 // * Coercing the algorithm to the proper concrete type
2825 // * Cloning subordinate data items
2826 // * Cloning input data as needed
2828 // Thus, support for different algorithms is determined by the if-statements
2829 // below, rather than a data structure.
2831 // This results in algorithm normalization coming after some other checks,
2832 // and thus slightly more steps being done synchronously than the spec calls
2833 // for. But none of these steps is especially time-consuming.
2835 WebCryptoTask* WebCryptoTask::CreateEncryptDecryptTask(
2836 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2837 const CryptoOperationData& aData, bool aEncrypt) {
2838 TelemetryMethod method = (aEncrypt) ? TM_ENCRYPT : TM_DECRYPT;
2839 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
2840 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_ENC,
2841 aKey.Extractable());
2843 // Ensure key is usable for this operation
2844 if ((aEncrypt && !aKey.HasUsage(CryptoKey::ENCRYPT)) ||
2845 (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) {
2846 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2849 nsString algName;
2850 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2851 if (NS_FAILED(rv)) {
2852 return new FailureTask(rv);
2855 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2856 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2857 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
2858 return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
2859 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
2860 return new RsaOaepTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
2863 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2866 WebCryptoTask* WebCryptoTask::CreateSignVerifyTask(
2867 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2868 const CryptoOperationData& aSignature, const CryptoOperationData& aData,
2869 bool aSign) {
2870 TelemetryMethod method = (aSign) ? TM_SIGN : TM_VERIFY;
2871 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
2872 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_SIG,
2873 aKey.Extractable());
2875 // Ensure key is usable for this operation
2876 if ((aSign && !aKey.HasUsage(CryptoKey::SIGN)) ||
2877 (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) {
2878 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2881 nsString algName;
2882 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2883 if (NS_FAILED(rv)) {
2884 return new FailureTask(rv);
2887 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2888 return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
2889 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2890 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
2891 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2892 return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
2893 aData, aSign);
2896 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2899 WebCryptoTask* WebCryptoTask::CreateDigestTask(
2900 JSContext* aCx, const ObjectOrString& aAlgorithm,
2901 const CryptoOperationData& aData) {
2902 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DIGEST);
2904 nsString algName;
2905 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2906 if (NS_FAILED(rv)) {
2907 return new FailureTask(rv);
2910 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
2911 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256) ||
2912 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
2913 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
2914 return new DigestTask(aCx, aAlgorithm, aData);
2917 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2920 WebCryptoTask* WebCryptoTask::CreateImportKeyTask(
2921 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
2922 JS::Handle<JSObject*> aKeyData, const ObjectOrString& aAlgorithm,
2923 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
2924 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY);
2925 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable);
2927 // Verify that the format is recognized
2928 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
2929 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
2930 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
2931 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2932 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
2935 // Verify that aKeyUsages does not contain an unrecognized value
2936 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
2937 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
2940 nsString algName;
2941 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2942 if (NS_FAILED(rv)) {
2943 return new FailureTask(rv);
2946 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
2947 // However, the spec should be updated to allow it.
2948 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2949 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2950 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2951 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
2952 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
2953 algName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
2954 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2955 return new ImportSymmetricKeyTask(aGlobal, aCx, aFormat, aKeyData,
2956 aAlgorithm, aExtractable, aKeyUsages);
2957 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2958 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
2959 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
2960 return new ImportRsaKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
2961 aExtractable, aKeyUsages);
2962 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
2963 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2964 return new ImportEcKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
2965 aExtractable, aKeyUsages);
2966 } else {
2967 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2971 WebCryptoTask* WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat,
2972 CryptoKey& aKey) {
2973 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_EXPORTKEY);
2975 // Verify that the format is recognized
2976 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
2977 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
2978 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
2979 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2980 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
2983 // Verify that the key is extractable
2984 if (!aKey.Extractable()) {
2985 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2988 // Verify that the algorithm supports export
2989 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
2990 // However, the spec should be updated to allow it.
2991 nsString algName = aKey.Algorithm().mName;
2992 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2993 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2994 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2995 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
2996 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
2997 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC) ||
2998 algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2999 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
3000 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
3001 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
3002 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
3003 return new ExportKeyTask(aFormat, aKey);
3006 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3009 WebCryptoTask* WebCryptoTask::CreateGenerateKeyTask(
3010 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3011 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3012 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_GENERATEKEY);
3013 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_GENERATE,
3014 aExtractable);
3016 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3017 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3020 nsString algName;
3021 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3022 if (NS_FAILED(rv)) {
3023 return new FailureTask(rv);
3026 if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3027 algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3028 algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3029 algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
3030 algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3031 return new GenerateSymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3032 aKeyUsages);
3033 } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3034 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3035 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS) ||
3036 algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
3037 algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA)) {
3038 return new GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3039 aKeyUsages);
3040 } else {
3041 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3045 WebCryptoTask* WebCryptoTask::CreateDeriveKeyTask(
3046 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3047 CryptoKey& aBaseKey, const ObjectOrString& aDerivedKeyType,
3048 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3049 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY);
3051 // Ensure baseKey is usable for this operation
3052 if (!aBaseKey.HasUsage(CryptoKey::DERIVEKEY)) {
3053 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3056 // Verify that aKeyUsages does not contain an unrecognized value
3057 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3058 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3061 nsString algName;
3062 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3063 if (NS_FAILED(rv)) {
3064 return new FailureTask(rv);
3067 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3068 return new DeriveKeyTask<DeriveHkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3069 aBaseKey, aDerivedKeyType,
3070 aExtractable, aKeyUsages);
3073 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3074 return new DeriveKeyTask<DerivePbkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3075 aBaseKey, aDerivedKeyType,
3076 aExtractable, aKeyUsages);
3079 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3080 return new DeriveKeyTask<DeriveEcdhBitsTask>(aGlobal, aCx, aAlgorithm,
3081 aBaseKey, aDerivedKeyType,
3082 aExtractable, aKeyUsages);
3085 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3088 WebCryptoTask* WebCryptoTask::CreateDeriveBitsTask(
3089 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
3090 uint32_t aLength) {
3091 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS);
3093 // Ensure baseKey is usable for this operation
3094 if (!aKey.HasUsage(CryptoKey::DERIVEBITS)) {
3095 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3098 nsString algName;
3099 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3100 if (NS_FAILED(rv)) {
3101 return new FailureTask(rv);
3104 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3105 return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3108 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3109 return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
3112 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3113 return new DeriveHkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3116 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3119 WebCryptoTask* WebCryptoTask::CreateWrapKeyTask(
3120 JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
3121 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm) {
3122 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_WRAPKEY);
3124 // Verify that the format is recognized
3125 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3126 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3127 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3128 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3129 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3132 // Ensure wrappingKey is usable for this operation
3133 if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) {
3134 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3137 // Ensure key is extractable
3138 if (!aKey.Extractable()) {
3139 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3142 nsString wrapAlgName;
3143 nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName);
3144 if (NS_FAILED(rv)) {
3145 return new FailureTask(rv);
3148 if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3149 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3150 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3151 return new WrapKeyTask<AesTask>(aCx, aFormat, aKey, aWrappingKey,
3152 aWrapAlgorithm);
3153 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3154 return new WrapKeyTask<AesKwTask>(aCx, aFormat, aKey, aWrappingKey,
3155 aWrapAlgorithm);
3156 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3157 return new WrapKeyTask<RsaOaepTask>(aCx, aFormat, aKey, aWrappingKey,
3158 aWrapAlgorithm);
3161 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3164 WebCryptoTask* WebCryptoTask::CreateUnwrapKeyTask(
3165 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
3166 const ArrayBufferViewOrArrayBuffer& aWrappedKey, CryptoKey& aUnwrappingKey,
3167 const ObjectOrString& aUnwrapAlgorithm,
3168 const ObjectOrString& aUnwrappedKeyAlgorithm, bool aExtractable,
3169 const Sequence<nsString>& aKeyUsages) {
3170 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_UNWRAPKEY);
3172 // Ensure key is usable for this operation
3173 if (!aUnwrappingKey.HasUsage(CryptoKey::UNWRAPKEY)) {
3174 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3177 // Verify that aKeyUsages does not contain an unrecognized value
3178 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3179 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3182 nsString keyAlgName;
3183 nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName);
3184 if (NS_FAILED(rv)) {
3185 return new FailureTask(rv);
3188 CryptoOperationData dummy;
3189 RefPtr<ImportKeyTask> importTask;
3190 if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3191 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3192 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3193 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HKDF) ||
3194 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3195 importTask = new ImportSymmetricKeyTask(aGlobal, aCx, aFormat,
3196 aUnwrappedKeyAlgorithm,
3197 aExtractable, aKeyUsages);
3198 } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3199 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3200 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS)) {
3201 importTask =
3202 new ImportRsaKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3203 aExtractable, aKeyUsages);
3204 } else if (keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
3205 keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
3206 importTask =
3207 new ImportEcKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3208 aExtractable, aKeyUsages);
3209 } else {
3210 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3213 nsString unwrapAlgName;
3214 rv = GetAlgorithmName(aCx, aUnwrapAlgorithm, unwrapAlgName);
3215 if (NS_FAILED(rv)) {
3216 return new FailureTask(rv);
3218 if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3219 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3220 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3221 return new UnwrapKeyTask<AesTask>(aCx, aWrappedKey, aUnwrappingKey,
3222 aUnwrapAlgorithm, importTask);
3223 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3224 return new UnwrapKeyTask<AesKwTask>(aCx, aWrappedKey, aUnwrappingKey,
3225 aUnwrapAlgorithm, importTask);
3226 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3227 return new UnwrapKeyTask<RsaOaepTask>(aCx, aWrappedKey, aUnwrappingKey,
3228 aUnwrapAlgorithm, importTask);
3231 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3234 WebCryptoTask::WebCryptoTask()
3235 : CancelableRunnable("WebCryptoTask"),
3236 mEarlyRv(NS_OK),
3237 mEarlyComplete(false),
3238 mOriginalEventTarget(nullptr),
3239 mRv(NS_ERROR_NOT_INITIALIZED) {}
3241 WebCryptoTask::~WebCryptoTask() = default;
3243 } // namespace mozilla::dom