Backed out changeset a5ff95602119 (bug 1905021) as requested for causing accessibilit...
[gecko.git] / dom / crypto / WebCryptoTask.cpp
blobf851b4ba5bd18caecca6a4b0e8922cb7e884c347
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"
24 #include "mozilla/dom/RootedDictionary.h"
26 // Template taken from security/nss/lib/util/templates.c
27 // This (or SGN_EncodeDigestInfo) would ideally be exported
28 // by NSS and until that happens we have to keep our own copy.
29 const SEC_ASN1Template SGN_DigestInfoTemplate[] = {
30 {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SGNDigestInfo)},
31 {SEC_ASN1_INLINE, offsetof(SGNDigestInfo, digestAlgorithm),
32 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate)},
33 {SEC_ASN1_OCTET_STRING, offsetof(SGNDigestInfo, digest)},
36 }};
38 namespace mozilla::dom {
40 // Pre-defined identifiers for telemetry histograms
42 enum TelemetryMethod {
43 TM_ENCRYPT = 0,
44 TM_DECRYPT = 1,
45 TM_SIGN = 2,
46 TM_VERIFY = 3,
47 TM_DIGEST = 4,
48 TM_GENERATEKEY = 5,
49 TM_DERIVEKEY = 6,
50 TM_DERIVEBITS = 7,
51 TM_IMPORTKEY = 8,
52 TM_EXPORTKEY = 9,
53 TM_WRAPKEY = 10,
54 TM_UNWRAPKEY = 11
57 enum TelemetryAlgorithm {
58 // Please make additions at the end of the list,
59 // to preserve comparability of histograms over time
60 TA_UNKNOWN = 0,
61 // encrypt / decrypt
62 TA_AES_CBC = 1,
63 TA_AES_CFB = 2,
64 TA_AES_CTR = 3,
65 TA_AES_GCM = 4,
66 TA_RSAES_PKCS1 = 5, // NB: This algorithm has been removed
67 TA_RSA_OAEP = 6,
68 // sign/verify
69 TA_RSASSA_PKCS1 = 7,
70 TA_RSA_PSS = 8,
71 TA_HMAC_SHA_1 = 9,
72 TA_HMAC_SHA_224 = 10,
73 TA_HMAC_SHA_256 = 11,
74 TA_HMAC_SHA_384 = 12,
75 TA_HMAC_SHA_512 = 13,
76 // digest
77 TA_SHA_1 = 14,
78 TA_SHA_224 = 15,
79 TA_SHA_256 = 16,
80 TA_SHA_384 = 17,
81 TA_SHA_512 = 18,
82 // Later additions
83 TA_AES_KW = 19,
84 TA_ECDH = 20,
85 TA_PBKDF2 = 21,
86 TA_ECDSA = 22,
87 TA_HKDF = 23,
88 TA_DH = 24,
91 // Convenience functions for extracting / converting information
93 // OOM-safe CryptoBuffer initialization, suitable for constructors
94 #define ATTEMPT_BUFFER_INIT(dst, src) \
95 if (!dst.Assign(src)) { \
96 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; \
97 return; \
100 // OOM-safe CryptoBuffer-to-SECItem copy, suitable for DoCrypto
101 #define ATTEMPT_BUFFER_TO_SECITEM(arena, dst, src) \
102 if (!src.ToSECItem(arena, dst)) { \
103 return NS_ERROR_DOM_UNKNOWN_ERR; \
106 // OOM-safe CryptoBuffer copy, suitable for DoCrypto
107 #define ATTEMPT_BUFFER_ASSIGN(dst, src) \
108 if (!dst.Assign(src)) { \
109 return NS_ERROR_DOM_UNKNOWN_ERR; \
112 // Safety check for algorithms that use keys, suitable for constructors
113 #define CHECK_KEY_ALGORITHM(keyAlg, algName) \
115 if (!NORMALIZED_EQUALS(keyAlg.mName, algName)) { \
116 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; \
117 return; \
121 class ClearException {
122 public:
123 explicit ClearException(JSContext* aCx) : mCx(aCx) {}
125 ~ClearException() { JS_ClearPendingException(mCx); }
127 private:
128 JSContext* mCx;
131 template <class OOS>
132 static nsresult GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
133 nsString& aName) {
134 ClearException ce(aCx);
136 if (aAlgorithm.IsString()) {
137 // If string, then treat as algorithm name
138 aName.Assign(aAlgorithm.GetAsString());
139 } else {
140 // Coerce to algorithm and extract name
141 JS::Rooted<JS::Value> value(aCx,
142 JS::ObjectValue(*aAlgorithm.GetAsObject()));
143 Algorithm alg;
145 if (!alg.Init(aCx, value)) {
146 return NS_ERROR_DOM_SYNTAX_ERR;
149 aName = alg.mName;
152 if (!NormalizeToken(aName, aName)) {
153 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
156 return NS_OK;
159 template <class T, class OOS>
160 static nsresult Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm) {
161 ClearException ce(aCx);
163 if (!aAlgorithm.IsObject()) {
164 return NS_ERROR_DOM_SYNTAX_ERR;
167 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
168 if (!aTarget.Init(aCx, value)) {
169 return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
172 return NS_OK;
175 inline size_t MapHashAlgorithmNameToBlockSize(const nsString& aName) {
176 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
177 aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
178 return 512;
181 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
182 aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
183 return 1024;
186 return 0;
189 inline nsresult GetKeyLengthForAlgorithmIfSpecified(
190 JSContext* aCx, const ObjectOrString& aAlgorithm, Maybe<size_t>& aLength) {
191 // Extract algorithm name
192 nsString algName;
193 if (NS_FAILED(GetAlgorithmName(aCx, aAlgorithm, algName))) {
194 return NS_ERROR_DOM_SYNTAX_ERR;
197 // Read AES key length from given algorithm object.
198 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
199 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
200 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
201 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
202 RootedDictionary<AesDerivedKeyParams> params(aCx);
203 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
204 return NS_ERROR_DOM_SYNTAX_ERR;
207 if (params.mLength != 128 && params.mLength != 192 &&
208 params.mLength != 256) {
209 return NS_ERROR_DOM_OPERATION_ERR;
212 aLength.emplace(params.mLength);
213 return NS_OK;
216 // Read HMAC key length from given algorithm object or
217 // determine key length as the block size of the given hash.
218 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
219 RootedDictionary<HmacDerivedKeyParams> params(aCx);
220 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
221 return NS_ERROR_DOM_SYNTAX_ERR;
224 // Return the passed length, if any.
225 if (params.mLength.WasPassed()) {
226 aLength.emplace(params.mLength.Value());
227 return NS_OK;
230 nsString hashName;
231 if (NS_FAILED(GetAlgorithmName(aCx, params.mHash, hashName))) {
232 return NS_ERROR_DOM_SYNTAX_ERR;
235 // Return the given hash algorithm's block size as the key length.
236 size_t blockSize = MapHashAlgorithmNameToBlockSize(hashName);
237 if (blockSize == 0) {
238 return NS_ERROR_DOM_SYNTAX_ERR;
241 aLength.emplace(blockSize);
242 return NS_OK;
245 return NS_OK;
248 inline nsresult GetKeyLengthForAlgorithm(JSContext* aCx,
249 const ObjectOrString& aAlgorithm,
250 size_t& aLength) {
251 Maybe<size_t> length;
252 nsresult rv = GetKeyLengthForAlgorithmIfSpecified(aCx, aAlgorithm, length);
253 if (NS_FAILED(rv)) {
254 return rv;
256 if (length.isNothing()) {
257 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
259 aLength = *length;
260 return NS_OK;
263 inline bool MapOIDTagToNamedCurve(SECOidTag aOIDTag, nsString& aResult) {
264 switch (aOIDTag) {
265 case SEC_OID_SECG_EC_SECP256R1:
266 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
267 break;
268 case SEC_OID_SECG_EC_SECP384R1:
269 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
270 break;
271 case SEC_OID_SECG_EC_SECP521R1:
272 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
273 break;
274 case SEC_OID_ED25519_PUBLIC_KEY:
275 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_ED25519);
276 break;
277 default:
278 return false;
281 return true;
284 inline SECOidTag MapHashAlgorithmNameToOID(const nsString& aName) {
285 SECOidTag hashOID(SEC_OID_UNKNOWN);
287 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
288 hashOID = SEC_OID_SHA1;
289 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
290 hashOID = SEC_OID_SHA256;
291 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
292 hashOID = SEC_OID_SHA384;
293 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
294 hashOID = SEC_OID_SHA512;
297 return hashOID;
300 inline CK_MECHANISM_TYPE MapHashAlgorithmNameToMgfMechanism(
301 const nsString& aName) {
302 CK_MECHANISM_TYPE mech(UNKNOWN_CK_MECHANISM);
304 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
305 mech = CKG_MGF1_SHA1;
306 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
307 mech = CKG_MGF1_SHA256;
308 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
309 mech = CKG_MGF1_SHA384;
310 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
311 mech = CKG_MGF1_SHA512;
314 return mech;
317 // Implementation of WebCryptoTask methods
319 void WebCryptoTask::DispatchWithPromise(Promise* aResultPromise) {
320 mResultPromise = aResultPromise;
322 // Fail if an error was set during the constructor
323 MAYBE_EARLY_FAIL(mEarlyRv)
325 // Perform pre-NSS operations, and fail if they fail
326 mEarlyRv = BeforeCrypto();
327 MAYBE_EARLY_FAIL(mEarlyRv)
329 // Skip dispatch if we're already done. Otherwise launch a CryptoTask
330 if (mEarlyComplete) {
331 CallCallback(mEarlyRv);
332 return;
335 // Store calling thread
336 mOriginalEventTarget = GetCurrentSerialEventTarget();
338 // If we are running on a worker thread we must hold the worker
339 // alive while we work on the thread pool. Otherwise the worker
340 // private may get torn down before we dispatch back to complete
341 // the transaction.
342 if (!NS_IsMainThread()) {
343 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
344 MOZ_ASSERT(workerPrivate);
346 RefPtr<StrongWorkerRef> workerRef =
347 StrongWorkerRef::Create(workerPrivate, "WebCryptoTask");
348 if (NS_WARN_IF(!workerRef)) {
349 mEarlyRv = NS_BINDING_ABORTED;
350 } else {
351 mWorkerRef = new ThreadSafeWorkerRef(workerRef);
354 MAYBE_EARLY_FAIL(mEarlyRv);
356 // dispatch to thread pool
358 if (!EnsureNSSInitializedChromeOrContent()) {
359 mEarlyRv = NS_ERROR_FAILURE;
361 MAYBE_EARLY_FAIL(mEarlyRv);
363 mEarlyRv = NS_DispatchBackgroundTask(this);
364 MAYBE_EARLY_FAIL(mEarlyRv)
367 NS_IMETHODIMP
368 WebCryptoTask::Run() {
369 // Run heavy crypto operations on the thread pool, off the original thread.
370 if (!IsOnOriginalThread()) {
371 mRv = CalculateResult();
373 // Back to the original thread, i.e. continue below.
374 mOriginalEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
375 return NS_OK;
378 // We're now back on the calling thread.
379 CallCallback(mRv);
381 // Stop holding the worker thread alive now that the async work has
382 // been completed.
383 mWorkerRef = nullptr;
385 return NS_OK;
388 nsresult WebCryptoTask::Cancel() {
389 MOZ_ASSERT(IsOnOriginalThread());
390 FailWithError(NS_BINDING_ABORTED);
391 return NS_OK;
394 void WebCryptoTask::FailWithError(nsresult aRv) {
395 MOZ_ASSERT(IsOnOriginalThread());
396 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
398 if (aRv == NS_ERROR_DOM_TYPE_MISMATCH_ERR) {
399 mResultPromise->MaybeRejectWithTypeError(
400 "The operation could not be performed.");
401 } else {
402 // Blindly convert nsresult to DOMException
403 // Individual tasks must ensure they pass the right values
404 mResultPromise->MaybeReject(aRv);
406 // Manually release mResultPromise while we're on the main thread
407 mResultPromise = nullptr;
408 mWorkerRef = nullptr;
409 Cleanup();
412 nsresult WebCryptoTask::CalculateResult() {
413 MOZ_ASSERT(!IsOnOriginalThread());
415 return DoCrypto();
418 void WebCryptoTask::CallCallback(nsresult rv) {
419 MOZ_ASSERT(IsOnOriginalThread());
420 if (NS_FAILED(rv)) {
421 FailWithError(rv);
422 return;
425 nsresult rv2 = AfterCrypto();
426 if (NS_FAILED(rv2)) {
427 FailWithError(rv2);
428 return;
431 Resolve();
432 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, true);
434 // Manually release mResultPromise while we're on the main thread
435 mResultPromise = nullptr;
436 Cleanup();
439 // Some generic utility classes
441 class FailureTask : public WebCryptoTask {
442 public:
443 explicit FailureTask(nsresult aRv) { mEarlyRv = aRv; }
446 class ReturnArrayBufferViewTask : public WebCryptoTask {
447 protected:
448 CryptoBuffer mResult;
450 private:
451 // Returns mResult as an ArrayBufferView, or an error
452 virtual void Resolve() override {
453 TypedArrayCreator<ArrayBuffer> ret(mResult);
454 mResultPromise->MaybeResolve(ret);
458 class DeferredData {
459 public:
460 template <class T>
461 void SetData(const T& aData) {
462 mDataIsSet = mData.Assign(aData);
465 protected:
466 DeferredData() : mDataIsSet(false) {}
468 CryptoBuffer mData;
469 bool mDataIsSet;
472 class AesTask : public ReturnArrayBufferViewTask, public DeferredData {
473 public:
474 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
475 bool aEncrypt)
476 : mMechanism(CKM_INVALID_MECHANISM),
477 mTagLength(0),
478 mCounterLength(0),
479 mEncrypt(aEncrypt) {
480 Init(aCx, aAlgorithm, aKey, aEncrypt);
483 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
484 const CryptoOperationData& aData, bool aEncrypt)
485 : mMechanism(CKM_INVALID_MECHANISM),
486 mTagLength(0),
487 mCounterLength(0),
488 mEncrypt(aEncrypt) {
489 Init(aCx, aAlgorithm, aKey, aEncrypt);
490 SetData(aData);
493 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
494 bool aEncrypt) {
495 nsString algName;
496 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
497 if (NS_FAILED(mEarlyRv)) {
498 return;
501 if (!mSymKey.Assign(aKey.GetSymKey())) {
502 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
503 return;
506 // Check that we got a reasonable key
507 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
508 (mSymKey.Length() != 32)) {
509 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
510 return;
513 // Cache parameters depending on the specific algorithm
514 TelemetryAlgorithm telemetryAlg;
515 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
516 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CBC);
518 mMechanism = CKM_AES_CBC_PAD;
519 telemetryAlg = TA_AES_CBC;
520 RootedDictionary<AesCbcParams> params(aCx);
521 nsresult rv = Coerce(aCx, params, aAlgorithm);
522 if (NS_FAILED(rv)) {
523 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
524 return;
527 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
528 if (mIv.Length() != 16) {
529 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
530 return;
532 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
533 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CTR);
535 mMechanism = CKM_AES_CTR;
536 telemetryAlg = TA_AES_CTR;
537 RootedDictionary<AesCtrParams> params(aCx);
538 nsresult rv = Coerce(aCx, params, aAlgorithm);
539 if (NS_FAILED(rv)) {
540 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
541 return;
544 ATTEMPT_BUFFER_INIT(mIv, params.mCounter)
545 if (mIv.Length() != 16) {
546 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
547 return;
550 mCounterLength = params.mLength;
551 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
552 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM);
554 mMechanism = CKM_AES_GCM;
555 telemetryAlg = TA_AES_GCM;
556 RootedDictionary<AesGcmParams> params(aCx);
557 nsresult rv = Coerce(aCx, params, aAlgorithm);
558 if (NS_FAILED(rv)) {
559 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
560 return;
563 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
565 if (params.mAdditionalData.WasPassed()) {
566 ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value())
569 // 32, 64, 96, 104, 112, 120 or 128
570 mTagLength = 128;
571 if (params.mTagLength.WasPassed()) {
572 mTagLength = params.mTagLength.Value();
573 if ((mTagLength > 128) ||
574 !(mTagLength == 32 || mTagLength == 64 ||
575 (mTagLength >= 96 && mTagLength % 8 == 0))) {
576 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
577 return;
580 } else {
581 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
582 return;
584 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
587 private:
588 CK_MECHANISM_TYPE mMechanism;
589 CryptoBuffer mSymKey;
590 CryptoBuffer mIv; // Initialization vector
591 CryptoBuffer mAad; // Additional Authenticated Data
592 uint8_t mTagLength;
593 uint8_t mCounterLength;
594 bool mEncrypt;
596 virtual nsresult DoCrypto() override {
597 nsresult rv;
599 if (!mDataIsSet) {
600 return NS_ERROR_DOM_OPERATION_ERR;
603 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
604 if (!arena) {
605 return NS_ERROR_DOM_OPERATION_ERR;
608 // Construct the parameters object depending on algorithm
609 SECItem param = {siBuffer, nullptr, 0};
610 CK_AES_CTR_PARAMS ctrParams;
611 CK_GCM_PARAMS gcmParams;
612 switch (mMechanism) {
613 case CKM_AES_CBC_PAD:
614 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &param, mIv);
615 break;
616 case CKM_AES_CTR:
617 ctrParams.ulCounterBits = mCounterLength;
618 MOZ_ASSERT(mIv.Length() == 16);
619 memcpy(&ctrParams.cb, mIv.Elements(), 16);
620 param.type = siBuffer;
621 param.data = (unsigned char*)&ctrParams;
622 param.len = sizeof(ctrParams);
623 break;
624 case CKM_AES_GCM:
625 gcmParams.pIv = mIv.Elements();
626 gcmParams.ulIvLen = mIv.Length();
627 gcmParams.ulIvBits = gcmParams.ulIvLen * 8;
628 gcmParams.pAAD = mAad.Elements();
629 gcmParams.ulAADLen = mAad.Length();
630 gcmParams.ulTagBits = mTagLength;
631 param.type = siBuffer;
632 param.data = (unsigned char*)&gcmParams;
633 param.len = sizeof(gcmParams);
634 break;
635 default:
636 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
639 // Import the key
640 SECItem keyItem = {siBuffer, nullptr, 0};
641 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
642 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
643 MOZ_ASSERT(slot.get());
644 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
645 PK11_OriginUnwrap, CKA_ENCRYPT,
646 &keyItem, nullptr));
647 if (!symKey) {
648 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
651 // Check whether the integer addition would overflow.
652 if (std::numeric_limits<CryptoBuffer::size_type>::max() - 16 <
653 mData.Length()) {
654 return NS_ERROR_DOM_DATA_ERR;
657 // Initialize the output buffer (enough space for padding / a full tag)
658 if (!mResult.SetLength(mData.Length() + 16, fallible)) {
659 return NS_ERROR_DOM_UNKNOWN_ERR;
662 uint32_t outLen = 0;
664 // Perform the encryption/decryption
665 if (mEncrypt) {
666 rv = MapSECStatus(PK11_Encrypt(
667 symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
668 mResult.Length(), mData.Elements(), mData.Length()));
669 } else {
670 rv = MapSECStatus(PK11_Decrypt(
671 symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
672 mResult.Length(), mData.Elements(), mData.Length()));
674 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
676 mResult.TruncateLength(outLen);
677 return rv;
681 // This class looks like an encrypt/decrypt task, like AesTask,
682 // but it is only exposed to wrapKey/unwrapKey, not encrypt/decrypt
683 class AesKwTask : public ReturnArrayBufferViewTask, public DeferredData {
684 public:
685 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
686 bool aEncrypt)
687 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
688 Init(aCx, aAlgorithm, aKey, aEncrypt);
691 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
692 const CryptoOperationData& aData, bool aEncrypt)
693 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
694 Init(aCx, aAlgorithm, aKey, aEncrypt);
695 SetData(aData);
698 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
699 bool aEncrypt) {
700 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_KW);
702 nsString algName;
703 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
704 if (NS_FAILED(mEarlyRv)) {
705 return;
708 if (!mSymKey.Assign(aKey.GetSymKey())) {
709 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
710 return;
713 // Check that we got a reasonable key
714 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
715 (mSymKey.Length() != 32)) {
716 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
717 return;
720 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_AES_KW);
723 private:
724 CK_MECHANISM_TYPE mMechanism;
725 CryptoBuffer mSymKey;
726 bool mEncrypt;
728 virtual nsresult DoCrypto() override {
729 nsresult rv;
731 if (!mDataIsSet) {
732 return NS_ERROR_DOM_OPERATION_ERR;
735 // Check that the input is a multiple of 64 bits long
736 if (mData.Length() == 0 || mData.Length() % 8 != 0) {
737 return NS_ERROR_DOM_DATA_ERR;
740 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
741 if (!arena) {
742 return NS_ERROR_DOM_OPERATION_ERR;
745 // Import the key
746 SECItem keyItem = {siBuffer, nullptr, 0};
747 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
748 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
749 MOZ_ASSERT(slot.get());
750 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
751 PK11_OriginUnwrap, CKA_WRAP,
752 &keyItem, nullptr));
753 if (!symKey) {
754 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
757 // Import the data to a SECItem
758 SECItem dataItem = {siBuffer, nullptr, 0};
759 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &dataItem, mData);
761 // Parameters for the fake keys
762 CK_MECHANISM_TYPE fakeMechanism = CKM_SHA_1_HMAC;
763 CK_ATTRIBUTE_TYPE fakeOperation = CKA_SIGN;
765 if (mEncrypt) {
766 // Import the data into a fake PK11SymKey structure
767 UniquePK11SymKey keyToWrap(
768 PK11_ImportSymKey(slot.get(), fakeMechanism, PK11_OriginUnwrap,
769 fakeOperation, &dataItem, nullptr));
770 if (!keyToWrap) {
771 return NS_ERROR_DOM_OPERATION_ERR;
774 // Encrypt and return the wrapped key
775 // AES-KW encryption results in a wrapped key 64 bits longer
776 if (!mResult.SetLength(mData.Length() + 8, fallible)) {
777 return NS_ERROR_DOM_OPERATION_ERR;
779 SECItem resultItem = {siBuffer, mResult.Elements(),
780 (unsigned int)mResult.Length()};
781 rv = MapSECStatus(PK11_WrapSymKey(mMechanism, nullptr, symKey.get(),
782 keyToWrap.get(), &resultItem));
783 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
784 } else {
785 // Decrypt the ciphertext into a temporary PK11SymKey
786 // Unwrapped key should be 64 bits shorter
787 int keySize = mData.Length() - 8;
788 UniquePK11SymKey unwrappedKey(
789 PK11_UnwrapSymKey(symKey.get(), mMechanism, nullptr, &dataItem,
790 fakeMechanism, fakeOperation, keySize));
791 if (!unwrappedKey) {
792 return NS_ERROR_DOM_OPERATION_ERR;
795 // Export the key to get the cleartext
796 rv = MapSECStatus(PK11_ExtractKeyValue(unwrappedKey.get()));
797 if (NS_FAILED(rv)) {
798 return NS_ERROR_DOM_UNKNOWN_ERR;
800 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(unwrappedKey.get()));
803 return rv;
807 class RsaOaepTask : public ReturnArrayBufferViewTask, public DeferredData {
808 public:
809 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
810 bool aEncrypt)
811 : mPrivKey(aKey.GetPrivateKey()),
812 mPubKey(aKey.GetPublicKey()),
813 mEncrypt(aEncrypt) {
814 Init(aCx, aAlgorithm, aKey, aEncrypt);
817 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
818 const CryptoOperationData& aData, bool aEncrypt)
819 : mPrivKey(aKey.GetPrivateKey()),
820 mPubKey(aKey.GetPublicKey()),
821 mEncrypt(aEncrypt) {
822 Init(aCx, aAlgorithm, aKey, aEncrypt);
823 SetData(aData);
826 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
827 bool aEncrypt) {
828 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_OAEP);
830 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_OAEP);
832 if (mEncrypt) {
833 if (!mPubKey) {
834 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
835 return;
837 mStrength = SECKEY_PublicKeyStrength(mPubKey.get());
838 } else {
839 if (!mPrivKey) {
840 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
841 return;
843 mStrength = PK11_GetPrivateModulusLen(mPrivKey.get());
846 // The algorithm could just be given as a string
847 // in which case there would be no label specified.
848 if (!aAlgorithm.IsString()) {
849 RootedDictionary<RsaOaepParams> params(aCx);
850 mEarlyRv = Coerce(aCx, params, aAlgorithm);
851 if (NS_FAILED(mEarlyRv)) {
852 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
853 return;
856 if (params.mLabel.WasPassed()) {
857 ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value());
860 // Otherwise mLabel remains the empty octet string, as intended
862 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
863 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
864 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlg.mName);
866 // Check we found appropriate mechanisms.
867 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
868 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
869 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
870 return;
874 private:
875 CK_MECHANISM_TYPE mHashMechanism;
876 CK_MECHANISM_TYPE mMgfMechanism;
877 UniqueSECKEYPrivateKey mPrivKey;
878 UniqueSECKEYPublicKey mPubKey;
879 CryptoBuffer mLabel;
880 uint32_t mStrength;
881 bool mEncrypt;
883 virtual nsresult DoCrypto() override {
884 nsresult rv;
886 if (!mDataIsSet) {
887 return NS_ERROR_DOM_OPERATION_ERR;
890 // Ciphertext is an integer mod the modulus, so it will be
891 // no longer than mStrength octets
892 if (!mResult.SetLength(mStrength, fallible)) {
893 return NS_ERROR_DOM_UNKNOWN_ERR;
896 CK_RSA_PKCS_OAEP_PARAMS oaepParams;
897 oaepParams.source = CKZ_DATA_SPECIFIED;
899 oaepParams.pSourceData = mLabel.Length() ? mLabel.Elements() : nullptr;
900 oaepParams.ulSourceDataLen = mLabel.Length();
902 oaepParams.mgf = mMgfMechanism;
903 oaepParams.hashAlg = mHashMechanism;
905 SECItem param;
906 param.type = siBuffer;
907 param.data = (unsigned char*)&oaepParams;
908 param.len = sizeof(oaepParams);
910 uint32_t outLen = 0;
911 if (mEncrypt) {
912 // PK11_PubEncrypt() checks the plaintext's length and fails if it is too
913 // long to encrypt, i.e. if it is longer than (k - 2hLen - 2) with 'k'
914 // being the length in octets of the RSA modulus n and 'hLen' being the
915 // output length in octets of the chosen hash function.
916 // <https://tools.ietf.org/html/rfc3447#section-7.1>
917 rv = MapSECStatus(PK11_PubEncrypt(
918 mPubKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(), &outLen,
919 mResult.Length(), mData.Elements(), mData.Length(), nullptr));
920 } else {
921 rv = MapSECStatus(PK11_PrivDecrypt(
922 mPrivKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(),
923 &outLen, mResult.Length(), mData.Elements(), mData.Length()));
925 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
927 mResult.TruncateLength(outLen);
928 return NS_OK;
932 class HmacTask : public WebCryptoTask {
933 public:
934 HmacTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
935 const CryptoOperationData& aSignature,
936 const CryptoOperationData& aData, bool aSign)
937 : mMechanism(aKey.Algorithm().Mechanism()), mSign(aSign) {
938 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HMAC);
940 ATTEMPT_BUFFER_INIT(mData, aData);
941 if (!aSign) {
942 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
945 if (!mSymKey.Assign(aKey.GetSymKey())) {
946 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
947 return;
950 // Check that we got a symmetric key
951 if (mSymKey.Length() == 0) {
952 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
953 return;
956 TelemetryAlgorithm telemetryAlg;
957 switch (mMechanism) {
958 case CKM_SHA_1_HMAC:
959 telemetryAlg = TA_HMAC_SHA_1;
960 break;
961 case CKM_SHA224_HMAC:
962 telemetryAlg = TA_HMAC_SHA_224;
963 break;
964 case CKM_SHA256_HMAC:
965 telemetryAlg = TA_HMAC_SHA_256;
966 break;
967 case CKM_SHA384_HMAC:
968 telemetryAlg = TA_HMAC_SHA_384;
969 break;
970 case CKM_SHA512_HMAC:
971 telemetryAlg = TA_HMAC_SHA_512;
972 break;
973 default:
974 telemetryAlg = TA_UNKNOWN;
976 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
979 private:
980 CK_MECHANISM_TYPE mMechanism;
981 CryptoBuffer mSymKey;
982 CryptoBuffer mData;
983 CryptoBuffer mSignature;
984 CryptoBuffer mResult;
985 bool mSign;
987 virtual nsresult DoCrypto() override {
988 // Initialize the output buffer
989 if (!mResult.SetLength(HASH_LENGTH_MAX, fallible)) {
990 return NS_ERROR_DOM_UNKNOWN_ERR;
993 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
994 if (!arena) {
995 return NS_ERROR_DOM_OPERATION_ERR;
998 // Import the key
999 uint32_t outLen;
1000 SECItem keyItem = {siBuffer, nullptr, 0};
1001 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
1002 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
1003 MOZ_ASSERT(slot.get());
1004 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
1005 PK11_OriginUnwrap, CKA_SIGN,
1006 &keyItem, nullptr));
1007 if (!symKey) {
1008 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1011 // Compute the MAC
1012 SECItem param = {siBuffer, nullptr, 0};
1013 UniquePK11Context ctx(
1014 PK11_CreateContextBySymKey(mMechanism, CKA_SIGN, symKey.get(), &param));
1015 if (!ctx.get()) {
1016 return NS_ERROR_DOM_OPERATION_ERR;
1018 nsresult rv = MapSECStatus(PK11_DigestBegin(ctx.get()));
1019 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1020 rv = MapSECStatus(
1021 PK11_DigestOp(ctx.get(), mData.Elements(), mData.Length()));
1022 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1023 rv = MapSECStatus(PK11_DigestFinal(ctx.get(), mResult.Elements(), &outLen,
1024 mResult.Length()));
1025 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1027 mResult.TruncateLength(outLen);
1028 return rv;
1031 // Returns mResult as an ArrayBufferView, or an error
1032 virtual void Resolve() override {
1033 if (mSign) {
1034 // Return the computed MAC
1035 TypedArrayCreator<ArrayBuffer> ret(mResult);
1036 mResultPromise->MaybeResolve(ret);
1037 } else {
1038 // Compare the MAC to the provided signature
1039 // No truncation allowed
1040 bool equal = (mResult.Length() == mSignature.Length());
1041 if (equal) {
1042 int cmp = NSS_SecureMemcmp(mSignature.Elements(), mResult.Elements(),
1043 mSignature.Length());
1044 equal = (cmp == 0);
1046 mResultPromise->MaybeResolve(equal);
1051 class AsymmetricSignVerifyTask : public WebCryptoTask {
1052 public:
1053 AsymmetricSignVerifyTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1054 CryptoKey& aKey,
1055 const CryptoOperationData& aSignature,
1056 const CryptoOperationData& aData, bool aSign)
1057 : mOidTag(SEC_OID_UNKNOWN),
1058 mHashMechanism(UNKNOWN_CK_MECHANISM),
1059 mMgfMechanism(UNKNOWN_CK_MECHANISM),
1060 mPrivKey(aKey.GetPrivateKey()),
1061 mPubKey(aKey.GetPublicKey()),
1062 mSaltLength(0),
1063 mSign(aSign),
1064 mVerified(false),
1065 mAlgorithm(Algorithm::UNKNOWN) {
1066 ATTEMPT_BUFFER_INIT(mData, aData);
1067 if (!aSign) {
1068 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
1071 nsString algName;
1072 nsString hashAlgName;
1073 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1074 if (NS_FAILED(mEarlyRv)) {
1075 return;
1078 if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
1079 mAlgorithm = Algorithm::RSA_PKCS1;
1080 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
1081 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
1082 hashAlgName = aKey.Algorithm().mRsa.mHash.mName;
1083 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1084 mAlgorithm = Algorithm::RSA_PSS;
1085 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_PSS);
1086 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_PSS);
1088 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
1089 hashAlgName = hashAlg.mName;
1090 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
1091 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlgName);
1093 // Check we found appropriate mechanisms.
1094 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
1095 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
1096 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1097 return;
1100 RootedDictionary<RsaPssParams> params(aCx);
1101 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1102 if (NS_FAILED(mEarlyRv)) {
1103 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1104 return;
1107 mSaltLength = params.mSaltLength;
1108 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1109 mAlgorithm = Algorithm::ECDSA;
1110 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDSA);
1111 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
1113 // For ECDSA, the hash name comes from the algorithm parameter
1114 RootedDictionary<EcdsaParams> params(aCx);
1115 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1116 if (NS_FAILED(mEarlyRv)) {
1117 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1118 return;
1121 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashAlgName);
1122 if (NS_FAILED(mEarlyRv)) {
1123 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1124 return;
1126 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
1127 mAlgorithm = Algorithm::ED25519;
1128 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ED25519);
1129 } else {
1130 // This shouldn't happen; CreateSignVerifyTask shouldn't create
1131 // one of these unless it's for the above algorithms.
1132 MOZ_ASSERT(false);
1135 // Must have a valid algorithm by now.
1136 MOZ_ASSERT(mAlgorithm != Algorithm::UNKNOWN);
1138 // Determine hash algorithm to use.
1139 mOidTag = MapHashAlgorithmNameToOID(hashAlgName);
1141 if (mOidTag == SEC_OID_UNKNOWN && AlgorithmRequiresHashing(mAlgorithm)) {
1142 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1143 return;
1146 // Check that we have the appropriate key
1147 if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
1148 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
1149 return;
1153 private:
1154 SECOidTag mOidTag;
1155 CK_MECHANISM_TYPE mHashMechanism;
1156 CK_MECHANISM_TYPE mMgfMechanism;
1157 UniqueSECKEYPrivateKey mPrivKey;
1158 UniqueSECKEYPublicKey mPubKey;
1159 CryptoBuffer mSignature;
1160 CryptoBuffer mData;
1161 uint32_t mSaltLength;
1162 bool mSign;
1163 bool mVerified;
1165 // The signature algorithm to use.
1166 enum class Algorithm : uint8_t {
1167 ECDSA,
1168 RSA_PKCS1,
1169 RSA_PSS,
1170 ED25519,
1171 UNKNOWN
1173 Algorithm mAlgorithm;
1175 bool AlgorithmRequiresHashing(Algorithm aAlgorithm) {
1176 MOZ_ASSERT(aAlgorithm != Algorithm::UNKNOWN);
1177 /* Currently, only ED25519 does not require hashing.*/
1178 switch (aAlgorithm) {
1179 case Algorithm::ED25519:
1180 return false;
1181 case Algorithm::ECDSA:
1182 case Algorithm::RSA_PKCS1:
1183 case Algorithm::RSA_PSS:
1184 // Impossible
1185 case Algorithm::UNKNOWN:
1186 return true;
1188 /*Also impossible, as all the algorithm options should be managed in the
1189 * switch. */
1190 return true;
1193 virtual nsresult DoCrypto() override {
1194 SECStatus rv;
1195 UniqueSECItem hash;
1197 SECItem* params = nullptr;
1198 CK_MECHANISM_TYPE mech =
1199 PK11_MapSignKeyType(mSign ? mPrivKey->keyType : mPubKey->keyType);
1201 CK_RSA_PKCS_PSS_PARAMS rsaPssParams;
1202 SECItem rsaPssParamsItem = {
1203 siBuffer,
1206 // Set up parameters for RSA-PSS.
1207 if (mAlgorithm == Algorithm::RSA_PSS) {
1208 rsaPssParams.hashAlg = mHashMechanism;
1209 rsaPssParams.mgf = mMgfMechanism;
1210 rsaPssParams.sLen = mSaltLength;
1212 rsaPssParamsItem.data = (unsigned char*)&rsaPssParams;
1213 rsaPssParamsItem.len = sizeof(rsaPssParams);
1214 params = &rsaPssParamsItem;
1216 mech = CKM_RSA_PKCS_PSS;
1219 if (AlgorithmRequiresHashing(mAlgorithm)) {
1220 // Compute digest over given data.
1221 hash.reset(::SECITEM_AllocItem(nullptr, nullptr,
1222 HASH_ResultLenByOidTag(mOidTag)));
1224 if (!hash || !hash->data || hash->len > PR_INT32_MAX) {
1225 return NS_ERROR_DOM_OPERATION_ERR;
1228 rv = PK11_HashBuf(mOidTag, hash->data, mData.Elements(),
1229 static_cast<PRInt32>(mData.Length()));
1230 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1233 // Wrap hash in a digest info template (RSA-PKCS1 only).
1234 if (mAlgorithm == Algorithm::RSA_PKCS1) {
1235 if (!hash) {
1236 return NS_ERROR_DOM_OPERATION_ERR;
1239 UniqueSGNDigestInfo di(
1240 SGN_CreateDigestInfo(mOidTag, hash->data, hash->len));
1241 if (!di) {
1242 return NS_ERROR_DOM_OPERATION_ERR;
1245 // Reuse |hash|.
1246 SECITEM_FreeItem(hash.get(), false);
1247 if (!SEC_ASN1EncodeItem(nullptr, hash.get(), di.get(),
1248 SGN_DigestInfoTemplate)) {
1249 return NS_ERROR_DOM_OPERATION_ERR;
1253 // Allocate SECItem to hold the signature.
1254 uint32_t len = mSign ? PK11_SignatureLen(mPrivKey.get()) : 0;
1255 UniqueSECItem sig(::SECITEM_AllocItem(nullptr, nullptr, len));
1256 if (!sig) {
1257 return NS_ERROR_DOM_OPERATION_ERR;
1260 // Buffer for signature/verification input.
1261 SECItem dataToOperateOn;
1262 if (mSign) {
1263 if (AlgorithmRequiresHashing(mAlgorithm)) {
1264 dataToOperateOn = {siBuffer, hash->data, hash->len};
1265 } else {
1266 dataToOperateOn = {siBuffer, mData.Elements(),
1267 static_cast<unsigned int>(mData.Length())};
1270 // Sign the hash.
1271 rv = PK11_SignWithMechanism(mPrivKey.get(), mech, params, sig.get(),
1272 &dataToOperateOn);
1273 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1274 ATTEMPT_BUFFER_ASSIGN(mSignature, sig.get());
1275 } else {
1276 if (AlgorithmRequiresHashing(mAlgorithm)) {
1277 dataToOperateOn = {siBuffer, hash->data, hash->len};
1278 } else {
1279 dataToOperateOn = {siBuffer, mData.Elements(),
1280 static_cast<unsigned int>(mData.Length())};
1283 // Copy the given signature to the SECItem.
1284 if (!mSignature.ToSECItem(nullptr, sig.get())) {
1285 return NS_ERROR_DOM_OPERATION_ERR;
1288 // Verify the signature.
1289 rv = PK11_VerifyWithMechanism(mPubKey.get(), mech, params, sig.get(),
1290 &dataToOperateOn, nullptr);
1291 mVerified = NS_SUCCEEDED(MapSECStatus(rv));
1294 return NS_OK;
1297 virtual void Resolve() override {
1298 if (mSign) {
1299 TypedArrayCreator<ArrayBuffer> ret(mSignature);
1300 mResultPromise->MaybeResolve(ret);
1301 } else {
1302 mResultPromise->MaybeResolve(mVerified);
1307 class DigestTask : public ReturnArrayBufferViewTask {
1308 public:
1309 DigestTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1310 const CryptoOperationData& aData) {
1311 ATTEMPT_BUFFER_INIT(mData, aData);
1313 nsString algName;
1314 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1315 if (NS_FAILED(mEarlyRv)) {
1316 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1317 return;
1320 TelemetryAlgorithm telemetryAlg;
1321 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
1322 telemetryAlg = TA_SHA_1;
1323 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
1324 telemetryAlg = TA_SHA_224;
1325 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
1326 telemetryAlg = TA_SHA_256;
1327 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
1328 telemetryAlg = TA_SHA_384;
1329 } else {
1330 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1331 return;
1333 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
1334 mOidTag = MapHashAlgorithmNameToOID(algName);
1337 private:
1338 SECOidTag mOidTag;
1339 CryptoBuffer mData;
1341 virtual nsresult DoCrypto() override {
1342 // Resize the result buffer
1343 uint32_t hashLen = HASH_ResultLenByOidTag(mOidTag);
1344 if (!mResult.SetLength(hashLen, fallible)) {
1345 return NS_ERROR_DOM_UNKNOWN_ERR;
1348 // Compute the hash
1349 nsresult rv = MapSECStatus(PK11_HashBuf(mOidTag, mResult.Elements(),
1350 mData.Elements(), mData.Length()));
1351 if (NS_FAILED(rv)) {
1352 return NS_ERROR_DOM_UNKNOWN_ERR;
1355 return rv;
1359 class ImportKeyTask : public WebCryptoTask {
1360 public:
1361 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1362 const ObjectOrString& aAlgorithm, bool aExtractable,
1363 const Sequence<nsString>& aKeyUsages) {
1364 mFormat = aFormat;
1365 mDataIsSet = false;
1366 mDataIsJwk = false;
1368 // This stuff pretty much always happens, so we'll do it here
1369 mKey = new CryptoKey(aGlobal);
1370 mKey->SetExtractable(aExtractable);
1371 mKey->ClearUsages();
1372 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
1373 mEarlyRv = mKey->AddUsage(aKeyUsages[i]);
1374 if (NS_FAILED(mEarlyRv)) {
1375 return;
1379 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
1380 if (NS_FAILED(mEarlyRv)) {
1381 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1382 return;
1386 static bool JwkCompatible(const JsonWebKey& aJwk, const CryptoKey* aKey) {
1387 // Check 'alg'
1388 if (!aJwk.mKty.EqualsLiteral(JWK_TYPE_OKP) && aJwk.mAlg.WasPassed() &&
1389 aJwk.mAlg.Value() != aKey->Algorithm().JwkAlg()) {
1390 return false;
1393 // Check 'ext'
1394 if (aKey->Extractable() && aJwk.mExt.WasPassed() && !aJwk.mExt.Value()) {
1395 return false;
1398 // Check 'key_ops'
1399 if (aJwk.mKey_ops.WasPassed()) {
1400 nsTArray<nsString> usages;
1401 aKey->GetUsages(usages);
1402 for (size_t i = 0; i < usages.Length(); ++i) {
1403 if (!aJwk.mKey_ops.Value().Contains(usages[i])) {
1404 return false;
1409 // Individual algorithms may still have to check 'use'
1410 return true;
1413 void SetKeyData(JSContext* aCx, JS::Handle<JSObject*> aKeyData) {
1414 mDataIsJwk = false;
1416 // Try ArrayBuffer
1417 RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
1418 if (ab.Init(aKeyData)) {
1419 if (!mKeyData.Assign(ab)) {
1420 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1422 return;
1425 // Try ArrayBufferView
1426 RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
1427 if (abv.Init(aKeyData)) {
1428 if (!mKeyData.Assign(abv)) {
1429 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1431 return;
1434 // Try JWK
1435 ClearException ce(aCx);
1436 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aKeyData));
1437 if (!mJwk.Init(aCx, value)) {
1438 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1439 return;
1442 mDataIsJwk = true;
1445 void SetKeyDataMaybeParseJWK(const CryptoBuffer& aKeyData) {
1446 if (!mKeyData.Assign(aKeyData)) {
1447 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1448 return;
1451 mDataIsJwk = false;
1453 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1454 nsDependentCSubstring utf8(
1455 (const char*)mKeyData.Elements(),
1456 (const char*)(mKeyData.Elements() + mKeyData.Length()));
1457 if (!IsUtf8(utf8)) {
1458 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1459 return;
1462 nsString json = NS_ConvertUTF8toUTF16(utf8);
1463 if (!mJwk.Init(json)) {
1464 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1465 return;
1468 mDataIsJwk = true;
1472 void SetRawKeyData(const CryptoBuffer& aKeyData) {
1473 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1474 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1475 return;
1478 if (!mKeyData.Assign(aKeyData)) {
1479 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1480 return;
1483 mDataIsJwk = false;
1486 protected:
1487 nsString mFormat;
1488 RefPtr<CryptoKey> mKey;
1489 CryptoBuffer mKeyData;
1490 bool mDataIsSet;
1491 bool mDataIsJwk;
1492 JsonWebKey mJwk;
1493 nsString mAlgName;
1495 private:
1496 virtual void Resolve() override { mResultPromise->MaybeResolve(mKey); }
1498 virtual void Cleanup() override { mKey = nullptr; }
1501 class ImportSymmetricKeyTask : public ImportKeyTask {
1502 public:
1503 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1504 const nsAString& aFormat,
1505 const ObjectOrString& aAlgorithm, bool aExtractable,
1506 const Sequence<nsString>& aKeyUsages) {
1507 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1510 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1511 const nsAString& aFormat,
1512 const JS::Handle<JSObject*> aKeyData,
1513 const ObjectOrString& aAlgorithm, bool aExtractable,
1514 const Sequence<nsString>& aKeyUsages) {
1515 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1516 if (NS_FAILED(mEarlyRv)) {
1517 return;
1520 SetKeyData(aCx, aKeyData);
1521 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1522 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1523 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1524 return;
1528 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1529 const ObjectOrString& aAlgorithm, bool aExtractable,
1530 const Sequence<nsString>& aKeyUsages) {
1531 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1532 aKeyUsages);
1533 if (NS_FAILED(mEarlyRv)) {
1534 return;
1537 // This task only supports raw and JWK format.
1538 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1539 !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1540 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1541 return;
1544 // If this is an HMAC key, import the hash name
1545 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1546 RootedDictionary<HmacImportParams> params(aCx);
1547 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1548 if (NS_FAILED(mEarlyRv)) {
1549 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1550 return;
1552 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1553 if (NS_FAILED(mEarlyRv)) {
1554 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1555 return;
1560 virtual nsresult BeforeCrypto() override {
1561 nsresult rv;
1562 // If we're doing a JWK import, import the key data
1563 if (mDataIsJwk) {
1564 if (!mJwk.mK.WasPassed()) {
1565 return NS_ERROR_DOM_DATA_ERR;
1568 // Import the key material
1569 rv = mKeyData.FromJwkBase64(mJwk.mK.Value());
1570 if (NS_FAILED(rv)) {
1571 return NS_ERROR_DOM_DATA_ERR;
1574 // Check that we have valid key data.
1575 if (mKeyData.Length() == 0 &&
1576 (!mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) &&
1577 !mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HKDF))) {
1578 return NS_ERROR_DOM_DATA_ERR;
1581 // Construct an appropriate KeyAlgorithm,
1582 // and verify that usages are appropriate
1583 if (mKeyData.Length() > UINT32_MAX / 8) {
1584 return NS_ERROR_DOM_DATA_ERR;
1586 uint32_t length = 8 * mKeyData.Length(); // bytes to bits
1587 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
1588 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
1589 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
1590 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
1591 if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
1592 CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1593 return NS_ERROR_DOM_DATA_ERR;
1596 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) &&
1597 mKey->HasUsageOtherThan(CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1598 return NS_ERROR_DOM_DATA_ERR;
1601 if ((length != 128) && (length != 192) && (length != 256)) {
1602 return NS_ERROR_DOM_DATA_ERR;
1604 mKey->Algorithm().MakeAes(mAlgName, length);
1606 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1607 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_ENC)) {
1608 return NS_ERROR_DOM_DATA_ERR;
1610 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
1611 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
1612 if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY |
1613 CryptoKey::DERIVEBITS)) {
1614 return NS_ERROR_DOM_DATA_ERR;
1616 mKey->Algorithm().MakeKDF(mAlgName);
1618 if (mDataIsJwk && mJwk.mUse.WasPassed()) {
1619 // There is not a 'use' value consistent with PBKDF or HKDF
1620 return NS_ERROR_DOM_DATA_ERR;
1622 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1623 if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) {
1624 return NS_ERROR_DOM_DATA_ERR;
1627 mKey->Algorithm().MakeHmac(length, mHashName);
1629 if (mKey->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM) {
1630 return NS_ERROR_DOM_SYNTAX_ERR;
1633 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1634 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_SIG)) {
1635 return NS_ERROR_DOM_DATA_ERR;
1637 } else {
1638 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1641 if (!mKey->HasAnyUsage()) {
1642 return NS_ERROR_DOM_SYNTAX_ERR;
1645 if (NS_FAILED(mKey->SetSymKey(mKeyData))) {
1646 return NS_ERROR_DOM_OPERATION_ERR;
1649 mKey->SetType(CryptoKey::SECRET);
1651 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1652 return NS_ERROR_DOM_DATA_ERR;
1655 mEarlyComplete = true;
1656 return NS_OK;
1659 private:
1660 nsString mHashName;
1663 class ImportRsaKeyTask : public ImportKeyTask {
1664 public:
1665 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1666 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1667 bool aExtractable, const Sequence<nsString>& aKeyUsages)
1668 : mModulusLength(0) {
1669 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1672 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1673 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1674 const ObjectOrString& aAlgorithm, bool aExtractable,
1675 const Sequence<nsString>& aKeyUsages)
1676 : mModulusLength(0) {
1677 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1678 if (NS_FAILED(mEarlyRv)) {
1679 return;
1682 SetKeyData(aCx, aKeyData);
1683 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1684 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1685 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1686 return;
1690 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1691 const ObjectOrString& aAlgorithm, bool aExtractable,
1692 const Sequence<nsString>& aKeyUsages) {
1693 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1694 aKeyUsages);
1695 if (NS_FAILED(mEarlyRv)) {
1696 return;
1699 // If this is RSA with a hash, cache the hash name
1700 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1701 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
1702 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1703 RootedDictionary<RsaHashedImportParams> params(aCx);
1704 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1705 if (NS_FAILED(mEarlyRv)) {
1706 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1707 return;
1710 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1711 if (NS_FAILED(mEarlyRv)) {
1712 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1713 return;
1717 // Check support for the algorithm and hash names
1718 CK_MECHANISM_TYPE mech1 = MapAlgorithmNameToMechanism(mAlgName);
1719 CK_MECHANISM_TYPE mech2 = MapAlgorithmNameToMechanism(mHashName);
1720 if ((mech1 == UNKNOWN_CK_MECHANISM) || (mech2 == UNKNOWN_CK_MECHANISM)) {
1721 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1722 return;
1726 private:
1727 nsString mHashName;
1728 uint32_t mModulusLength;
1729 CryptoBuffer mPublicExponent;
1731 virtual nsresult DoCrypto() override {
1732 // Import the key data itself
1733 UniqueSECKEYPublicKey pubKey;
1734 UniqueSECKEYPrivateKey privKey;
1735 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1736 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1737 !mJwk.mD.WasPassed())) {
1738 // Public key import
1739 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1740 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1741 } else {
1742 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1745 if (!pubKey) {
1746 return NS_ERROR_DOM_DATA_ERR;
1749 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1750 return NS_ERROR_DOM_OPERATION_ERR;
1753 mKey->SetType(CryptoKey::PUBLIC);
1754 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) ||
1755 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1756 mJwk.mD.WasPassed())) {
1757 // Private key import
1758 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1759 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
1760 } else {
1761 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1764 if (!privKey) {
1765 return NS_ERROR_DOM_DATA_ERR;
1768 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1769 return NS_ERROR_DOM_OPERATION_ERR;
1772 mKey->SetType(CryptoKey::PRIVATE);
1773 pubKey = UniqueSECKEYPublicKey(SECKEY_ConvertToPublicKey(privKey.get()));
1774 if (!pubKey) {
1775 return NS_ERROR_DOM_UNKNOWN_ERR;
1777 } else {
1778 // Invalid key format
1779 return NS_ERROR_DOM_SYNTAX_ERR;
1782 if (pubKey->keyType != rsaKey) {
1783 return NS_ERROR_DOM_DATA_ERR;
1785 // Extract relevant information from the public key
1786 mModulusLength = 8 * pubKey->u.rsa.modulus.len;
1787 if (!mPublicExponent.Assign(&pubKey->u.rsa.publicExponent)) {
1788 return NS_ERROR_DOM_OPERATION_ERR;
1791 return NS_OK;
1794 virtual nsresult AfterCrypto() override {
1795 // Check permissions for the requested operation
1796 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
1797 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1798 mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) ||
1799 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1800 mKey->HasUsageOtherThan(CryptoKey::DECRYPT |
1801 CryptoKey::UNWRAPKEY))) {
1802 return NS_ERROR_DOM_DATA_ERR;
1804 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1805 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1806 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1807 mKey->HasUsageOtherThan(CryptoKey::VERIFY)) ||
1808 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1809 mKey->HasUsageOtherThan(CryptoKey::SIGN))) {
1810 return NS_ERROR_DOM_DATA_ERR;
1814 if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
1815 return NS_ERROR_DOM_SYNTAX_ERR;
1818 // Set an appropriate KeyAlgorithm
1819 if (!mKey->Algorithm().MakeRsa(mAlgName, mModulusLength, mPublicExponent,
1820 mHashName)) {
1821 return NS_ERROR_DOM_OPERATION_ERR;
1824 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1825 return NS_ERROR_DOM_DATA_ERR;
1828 return NS_OK;
1832 class ImportEcKeyTask : public ImportKeyTask {
1833 public:
1834 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1835 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1836 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
1837 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1840 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1841 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1842 const ObjectOrString& aAlgorithm, bool aExtractable,
1843 const Sequence<nsString>& aKeyUsages) {
1844 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1845 if (NS_FAILED(mEarlyRv)) {
1846 return;
1849 SetKeyData(aCx, aKeyData);
1850 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1853 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1854 const ObjectOrString& aAlgorithm, bool aExtractable,
1855 const Sequence<nsString>& aKeyUsages) {
1856 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1857 aKeyUsages);
1858 if (NS_FAILED(mEarlyRv)) {
1859 return;
1862 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1863 RootedDictionary<EcKeyImportParams> params(aCx);
1864 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1865 if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
1866 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1867 return;
1870 if (!NormalizeToken(params.mNamedCurve.Value(), mNamedCurve)) {
1871 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1872 return;
1877 private:
1878 nsString mNamedCurve;
1880 virtual nsresult DoCrypto() override {
1881 // Import the key data itself
1882 UniqueSECKEYPublicKey pubKey;
1883 UniqueSECKEYPrivateKey privKey;
1885 if ((mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1886 mJwk.mD.WasPassed()) ||
1887 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1888 // Private key import
1889 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1890 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1891 if (!privKey) {
1892 return NS_ERROR_DOM_DATA_ERR;
1894 } else {
1895 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
1896 if (!privKey) {
1897 return NS_ERROR_DOM_DATA_ERR;
1900 ScopedAutoSECItem ecParams;
1901 if (PK11_ReadRawAttribute(PK11_TypePrivKey, privKey.get(),
1902 CKA_EC_PARAMS, &ecParams) != SECSuccess) {
1903 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1906 SECOidTag tag;
1907 if (!FindOIDTagForEncodedParameters(&ecParams, &tag)) {
1908 return NS_ERROR_DOM_DATA_ERR;
1911 // Find a matching and supported named curve.
1912 if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
1913 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1917 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1918 return NS_ERROR_DOM_OPERATION_ERR;
1921 mKey->SetType(CryptoKey::PRIVATE);
1922 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
1923 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1924 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1925 !mJwk.mD.WasPassed())) {
1926 // Public key import
1927 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1928 pubKey = CryptoKey::PublicECKeyFromRaw(mKeyData, mNamedCurve);
1929 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1930 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1931 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1932 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1933 } else {
1934 MOZ_ASSERT(false);
1937 if (!pubKey) {
1938 return NS_ERROR_DOM_DATA_ERR;
1941 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1942 if (pubKey->keyType != ecKey) {
1943 return NS_ERROR_DOM_DATA_ERR;
1945 if (!CheckEncodedParameters(&pubKey->u.ec.DEREncodedParams)) {
1946 return NS_ERROR_DOM_OPERATION_ERR;
1949 SECOidTag tag;
1950 if (!FindOIDTagForEncodedParameters(&pubKey->u.ec.DEREncodedParams,
1951 &tag)) {
1952 return NS_ERROR_DOM_DATA_ERR;
1955 // Find a matching and supported named curve.
1956 if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
1957 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1961 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1962 return NS_ERROR_DOM_OPERATION_ERR;
1965 mKey->SetType(CryptoKey::PUBLIC);
1966 } else {
1967 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1970 // Extract 'crv' parameter from JWKs.
1971 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1972 if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) {
1973 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1976 return NS_OK;
1979 virtual nsresult AfterCrypto() override {
1980 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
1981 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
1982 privateAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
1983 publicAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
1984 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1985 privateAllowedUsages = CryptoKey::SIGN;
1986 publicAllowedUsages = CryptoKey::VERIFY;
1989 // Check permissions for the requested operation
1990 if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
1991 mKey->HasUsageOtherThan(privateAllowedUsages)) ||
1992 (mKey->GetKeyType() == CryptoKey::PUBLIC &&
1993 mKey->HasUsageOtherThan(publicAllowedUsages))) {
1994 return NS_ERROR_DOM_DATA_ERR;
1997 if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
1998 return NS_ERROR_DOM_SYNTAX_ERR;
2001 mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2003 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
2004 return NS_ERROR_DOM_DATA_ERR;
2007 return NS_OK;
2011 class ImportEdKeyTask : public ImportKeyTask {
2012 public:
2013 ImportEdKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2014 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
2015 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
2016 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
2019 ImportEdKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2020 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
2021 const ObjectOrString& aAlgorithm, bool aExtractable,
2022 const Sequence<nsString>& aKeyUsages) {
2023 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
2024 if (NS_FAILED(mEarlyRv)) {
2025 return;
2028 SetKeyData(aCx, aKeyData);
2029 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
2032 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
2033 const ObjectOrString& aAlgorithm, bool aExtractable,
2034 const Sequence<nsString>& aKeyUsages) {
2035 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
2036 aKeyUsages);
2037 if (NS_FAILED(mEarlyRv)) {
2038 return;
2041 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
2042 RootedDictionary<Algorithm> params(aCx);
2043 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2044 if (NS_FAILED(mEarlyRv)) {
2045 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2046 return;
2049 nsString algName;
2050 if (!NormalizeToken(params.mName, algName)) {
2051 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2052 return;
2055 // Construct an appropriate KeyAlgorithm
2056 if (algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
2057 mNamedCurve.AssignLiteral(WEBCRYPTO_NAMED_CURVE_ED25519);
2058 } else {
2059 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2060 return;
2065 private:
2066 nsString mNamedCurve;
2068 virtual nsresult DoCrypto() override {
2069 // Import the key data itself
2070 UniqueSECKEYPublicKey pubKey;
2071 UniqueSECKEYPrivateKey privKey;
2073 if ((mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
2074 mJwk.mD.WasPassed()) ||
2075 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
2076 // Private key import
2077 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2078 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
2079 if (!privKey) {
2080 return NS_ERROR_DOM_DATA_ERR;
2082 } else {
2083 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
2084 if (!privKey) {
2085 return NS_ERROR_DOM_DATA_ERR;
2088 ScopedAutoSECItem ecParams;
2089 if (PK11_ReadRawAttribute(PK11_TypePrivKey, privKey.get(),
2090 CKA_EC_PARAMS, &ecParams) != SECSuccess) {
2091 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2094 SECOidTag tag;
2095 if (!FindOIDTagForEncodedParameters(&ecParams, &tag)) {
2096 return NS_ERROR_DOM_DATA_ERR;
2099 // Find a matching and supported named curve.
2100 if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
2101 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2105 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
2106 return NS_ERROR_DOM_OPERATION_ERR;
2109 mKey->SetType(CryptoKey::PRIVATE);
2110 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
2111 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
2112 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
2113 !mJwk.mD.WasPassed())) {
2114 // Public key import
2115 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
2116 pubKey = CryptoKey::PublicEDKeyFromRaw(mKeyData, mNamedCurve);
2117 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
2118 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
2119 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2120 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
2121 } else {
2122 MOZ_ASSERT(false);
2125 if (!pubKey) {
2126 return NS_ERROR_DOM_DATA_ERR;
2129 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
2130 if (pubKey->keyType != edKey) {
2131 return NS_ERROR_DOM_DATA_ERR;
2133 if (!CheckEncodedParameters(&pubKey->u.ec.DEREncodedParams)) {
2134 return NS_ERROR_DOM_OPERATION_ERR;
2137 SECOidTag tag;
2138 if (!FindOIDTagForEncodedParameters(&pubKey->u.ec.DEREncodedParams,
2139 &tag)) {
2140 return NS_ERROR_DOM_OPERATION_ERR;
2143 // Find a matching and supported named curve.
2144 if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
2145 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2149 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
2150 return NS_ERROR_DOM_OPERATION_ERR;
2153 mKey->SetType(CryptoKey::PUBLIC);
2154 } else {
2155 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2158 // Extract 'crv' parameter from JWKs.
2159 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2160 if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) {
2161 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2165 return NS_OK;
2168 virtual nsresult AfterCrypto() override {
2169 // Only Ed25519 is supported.
2170 uint32_t privateAllowedUsages = CryptoKey::SIGN;
2171 uint32_t publicAllowedUsages = CryptoKey::VERIFY;
2173 // Check permissions for the requested operation
2174 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
2175 mKey->HasUsageOtherThan(publicAllowedUsages))) {
2176 return NS_ERROR_DOM_SYNTAX_ERR;
2179 if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
2180 mKey->HasUsageOtherThan(privateAllowedUsages))) {
2181 return NS_ERROR_DOM_SYNTAX_ERR;
2184 if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
2185 return NS_ERROR_DOM_SYNTAX_ERR;
2188 mKey->Algorithm().MakeEd(mAlgName);
2190 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
2191 return NS_ERROR_DOM_DATA_ERR;
2194 return NS_OK;
2198 class ExportKeyTask : public WebCryptoTask {
2199 public:
2200 ExportKeyTask(const nsAString& aFormat, CryptoKey& aKey)
2201 : mFormat(aFormat),
2202 mPrivateKey(aKey.GetPrivateKey()),
2203 mPublicKey(aKey.GetPublicKey()),
2204 mKeyType(aKey.GetKeyType()),
2205 mExtractable(aKey.Extractable()),
2206 mAlg(aKey.Algorithm().JwkAlg()) {
2207 aKey.GetUsages(mKeyUsages);
2209 if (!mSymKey.Assign(aKey.GetSymKey())) {
2210 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2211 return;
2215 protected:
2216 nsString mFormat;
2217 CryptoBuffer mSymKey;
2218 UniqueSECKEYPrivateKey mPrivateKey;
2219 UniqueSECKEYPublicKey mPublicKey;
2220 CryptoKey::KeyType mKeyType;
2221 bool mExtractable;
2222 nsString mAlg;
2223 nsTArray<nsString> mKeyUsages;
2224 CryptoBuffer mResult;
2225 JsonWebKey mJwk;
2227 private:
2228 virtual nsresult DoCrypto() override {
2229 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
2230 if (mPublicKey && mPublicKey->keyType == dhKey) {
2231 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2234 if (mPublicKey &&
2235 (mPublicKey->keyType == ecKey || mPublicKey->keyType == edKey)) {
2236 nsresult rv = CryptoKey::PublicECKeyToRaw(mPublicKey.get(), mResult);
2237 if (NS_FAILED(rv)) {
2238 return NS_ERROR_DOM_OPERATION_ERR;
2240 return NS_OK;
2243 if (!mResult.Assign(mSymKey)) {
2244 return NS_ERROR_OUT_OF_MEMORY;
2246 if (mResult.Length() == 0) {
2247 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2250 return NS_OK;
2251 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
2252 if (!mPrivateKey) {
2253 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2256 switch (mPrivateKey->keyType) {
2257 case rsaKey:
2258 case edKey:
2259 case ecKey: {
2260 nsresult rv =
2261 CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), mResult);
2262 if (NS_FAILED(rv)) {
2263 return NS_ERROR_DOM_OPERATION_ERR;
2265 return NS_OK;
2267 default:
2268 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2270 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
2271 if (!mPublicKey) {
2272 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2275 return CryptoKey::PublicKeyToSpki(mPublicKey.get(), mResult);
2276 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2277 if (mKeyType == CryptoKey::SECRET) {
2278 nsString k;
2279 nsresult rv = mSymKey.ToJwkBase64(k);
2280 if (NS_FAILED(rv)) {
2281 return NS_ERROR_DOM_OPERATION_ERR;
2283 mJwk.mK.Construct(k);
2284 mJwk.mKty = NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_SYMMETRIC);
2285 } else if (mKeyType == CryptoKey::PUBLIC) {
2286 if (!mPublicKey) {
2287 return NS_ERROR_DOM_UNKNOWN_ERR;
2290 nsresult rv = CryptoKey::PublicKeyToJwk(mPublicKey.get(), mJwk);
2291 if (NS_FAILED(rv)) {
2292 return NS_ERROR_DOM_OPERATION_ERR;
2294 } else if (mKeyType == CryptoKey::PRIVATE) {
2295 if (!mPrivateKey) {
2296 return NS_ERROR_DOM_UNKNOWN_ERR;
2299 nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey.get(), mJwk);
2300 if (NS_FAILED(rv)) {
2301 return NS_ERROR_DOM_OPERATION_ERR;
2305 if (!mAlg.IsEmpty()) {
2306 mJwk.mAlg.Construct(mAlg);
2309 mJwk.mExt.Construct(mExtractable);
2311 mJwk.mKey_ops.Construct();
2312 if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
2313 return NS_ERROR_OUT_OF_MEMORY;
2316 return NS_OK;
2319 return NS_ERROR_DOM_SYNTAX_ERR;
2322 // Returns mResult as an ArrayBufferView or JWK, as appropriate
2323 virtual void Resolve() override {
2324 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2325 mResultPromise->MaybeResolve(mJwk);
2326 return;
2329 TypedArrayCreator<ArrayBuffer> ret(mResult);
2330 mResultPromise->MaybeResolve(ret);
2334 class GenerateSymmetricKeyTask : public WebCryptoTask {
2335 public:
2336 GenerateSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2337 const ObjectOrString& aAlgorithm, bool aExtractable,
2338 const Sequence<nsString>& aKeyUsages) {
2339 // Create an empty key and set easy attributes
2340 mKey = new CryptoKey(aGlobal);
2341 mKey->SetExtractable(aExtractable);
2342 mKey->SetType(CryptoKey::SECRET);
2344 // Extract algorithm name
2345 nsString algName;
2346 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
2347 if (NS_FAILED(mEarlyRv)) {
2348 return;
2351 // Construct an appropriate KeyAlorithm
2352 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2353 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2354 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2355 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
2356 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aAlgorithm, mLength);
2357 if (NS_FAILED(mEarlyRv)) {
2358 return;
2360 mKey->Algorithm().MakeAes(algName, mLength);
2362 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2363 RootedDictionary<HmacKeyGenParams> params(aCx);
2364 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2365 if (NS_FAILED(mEarlyRv)) {
2366 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2367 return;
2370 nsString hashName;
2371 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2372 if (NS_FAILED(mEarlyRv)) {
2373 return;
2376 if (params.mLength.WasPassed()) {
2377 mLength = params.mLength.Value();
2378 } else {
2379 mLength = MapHashAlgorithmNameToBlockSize(hashName);
2382 if (mLength == 0) {
2383 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2384 return;
2387 mKey->Algorithm().MakeHmac(mLength, hashName);
2388 } else {
2389 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2390 return;
2393 // Add key usages
2394 mKey->ClearUsages();
2395 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2396 mEarlyRv = mKey->AddAllowedUsageIntersecting(aKeyUsages[i], algName);
2397 if (NS_FAILED(mEarlyRv)) {
2398 return;
2401 if (!mKey->HasAnyUsage()) {
2402 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2403 return;
2406 mLength = mLength >> 3; // bits to bytes
2407 mMechanism = mKey->Algorithm().Mechanism();
2408 // SetSymKey done in Resolve, after we've done the keygen
2411 private:
2412 RefPtr<CryptoKey> mKey;
2413 size_t mLength;
2414 CK_MECHANISM_TYPE mMechanism;
2415 CryptoBuffer mKeyData;
2417 virtual nsresult DoCrypto() override {
2418 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2419 MOZ_ASSERT(slot.get());
2421 UniquePK11SymKey symKey(
2422 PK11_KeyGen(slot.get(), mMechanism, nullptr, mLength, nullptr));
2423 if (!symKey) {
2424 return NS_ERROR_DOM_UNKNOWN_ERR;
2427 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2428 if (NS_FAILED(rv)) {
2429 return NS_ERROR_DOM_UNKNOWN_ERR;
2432 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2433 // just refers to a buffer managed by symKey. The assignment copies the
2434 // data, so mKeyData manages one copy, while symKey manages another.
2435 ATTEMPT_BUFFER_ASSIGN(mKeyData, PK11_GetKeyData(symKey.get()));
2436 return NS_OK;
2439 virtual void Resolve() override {
2440 if (NS_SUCCEEDED(mKey->SetSymKey(mKeyData))) {
2441 mResultPromise->MaybeResolve(mKey);
2442 } else {
2443 mResultPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
2447 virtual void Cleanup() override { mKey = nullptr; }
2450 GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask(
2451 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
2452 bool aExtractable, const Sequence<nsString>& aKeyUsages)
2453 : mKeyPair(new CryptoKeyPair()),
2454 mMechanism(CKM_INVALID_MECHANISM),
2455 mRsaParams(),
2456 mDhParams() {
2457 mArena = UniquePLArenaPool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2458 if (!mArena) {
2459 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
2460 return;
2463 // Create an empty key pair and set easy attributes
2464 mKeyPair->mPrivateKey = new CryptoKey(aGlobal);
2465 mKeyPair->mPublicKey = new CryptoKey(aGlobal);
2467 // Extract algorithm name
2468 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
2469 if (NS_FAILED(mEarlyRv)) {
2470 return;
2473 // Construct an appropriate KeyAlorithm
2474 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
2475 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2476 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
2477 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
2478 RootedDictionary<RsaHashedKeyGenParams> params(aCx);
2479 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2480 if (NS_FAILED(mEarlyRv)) {
2481 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2482 return;
2485 // Pull relevant info
2486 uint32_t modulusLength = params.mModulusLength;
2487 CryptoBuffer publicExponent;
2488 ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
2489 nsString hashName;
2490 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2491 if (NS_FAILED(mEarlyRv)) {
2492 return;
2495 // Create algorithm
2496 if (!mKeyPair->mPublicKey->Algorithm().MakeRsa(mAlgName, modulusLength,
2497 publicExponent, hashName)) {
2498 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2499 return;
2501 if (!mKeyPair->mPrivateKey->Algorithm().MakeRsa(mAlgName, modulusLength,
2502 publicExponent, hashName)) {
2503 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2504 return;
2506 mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
2508 // Set up params struct
2509 mRsaParams.keySizeInBits = modulusLength;
2510 bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
2511 if (!converted) {
2512 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2513 return;
2515 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
2516 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2517 RootedDictionary<EcKeyGenParams> params(aCx);
2518 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2519 if (NS_FAILED(mEarlyRv)) {
2520 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2521 return;
2524 if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
2525 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2526 return;
2529 // Create algorithm.
2530 mKeyPair->mPublicKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2531 mKeyPair->mPrivateKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2532 mMechanism = CKM_EC_KEY_PAIR_GEN;
2535 else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
2536 mKeyPair->mPublicKey->Algorithm().MakeEd(mAlgName);
2537 mKeyPair->mPrivateKey->Algorithm().MakeEd(mAlgName);
2538 mMechanism = CKM_EC_EDWARDS_KEY_PAIR_GEN;
2539 mNamedCurve.AssignLiteral(WEBCRYPTO_NAMED_CURVE_ED25519);
2542 else {
2543 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2544 return;
2547 // Set key usages.
2548 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2549 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
2550 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
2551 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)
2554 privateAllowedUsages = CryptoKey::SIGN;
2555 publicAllowedUsages = CryptoKey::VERIFY;
2556 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
2557 privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
2558 publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
2559 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
2560 privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
2561 publicAllowedUsages = 0;
2562 } else {
2563 MOZ_ASSERT(false); // This shouldn't happen.
2566 mKeyPair->mPrivateKey->SetExtractable(aExtractable);
2567 mKeyPair->mPrivateKey->SetType(CryptoKey::PRIVATE);
2569 mKeyPair->mPublicKey->SetExtractable(true);
2570 mKeyPair->mPublicKey->SetType(CryptoKey::PUBLIC);
2572 mKeyPair->mPrivateKey->ClearUsages();
2573 mKeyPair->mPublicKey->ClearUsages();
2574 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2575 mEarlyRv = mKeyPair->mPrivateKey->AddAllowedUsageIntersecting(
2576 aKeyUsages[i], mAlgName, privateAllowedUsages);
2577 if (NS_FAILED(mEarlyRv)) {
2578 return;
2581 mEarlyRv = mKeyPair->mPublicKey->AddAllowedUsageIntersecting(
2582 aKeyUsages[i], mAlgName, publicAllowedUsages);
2583 if (NS_FAILED(mEarlyRv)) {
2584 return;
2589 nsresult GenerateAsymmetricKeyTask::DoCrypto() {
2590 MOZ_ASSERT(mKeyPair);
2592 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2593 MOZ_ASSERT(slot.get());
2595 void* param;
2596 switch (mMechanism) {
2597 case CKM_RSA_PKCS_KEY_PAIR_GEN:
2598 param = &mRsaParams;
2599 break;
2600 case CKM_DH_PKCS_KEY_PAIR_GEN:
2601 param = &mDhParams;
2602 break;
2603 case CKM_EC_EDWARDS_KEY_PAIR_GEN:
2604 case CKM_EC_KEY_PAIR_GEN: {
2605 param = CreateECParamsForCurve(mNamedCurve, mArena.get());
2606 if (!param) {
2607 return NS_ERROR_DOM_UNKNOWN_ERR;
2609 break;
2611 default:
2612 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2615 mPrivateKey = UniqueSECKEYPrivateKey(PK11_GenerateKeyPair(
2616 slot.get(), mMechanism, param, TempPtrToSetter(&mPublicKey), PR_FALSE,
2617 PR_FALSE, nullptr));
2619 if (!mPrivateKey.get() || !mPublicKey.get()) {
2620 return NS_ERROR_DOM_OPERATION_ERR;
2623 // If no usages ended up being allowed, SyntaxError
2624 if (!mKeyPair->mPrivateKey->HasAnyUsage()) {
2625 return NS_ERROR_DOM_SYNTAX_ERR;
2628 nsresult rv = mKeyPair->mPrivateKey->SetPrivateKey(mPrivateKey.get());
2629 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2630 rv = mKeyPair->mPublicKey->SetPublicKey(mPublicKey.get());
2631 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2632 // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
2633 // private key, we need this later when exporting to PKCS8 and JWK though.
2634 if (mMechanism == CKM_EC_KEY_PAIR_GEN ||
2635 mMechanism == CKM_EC_EDWARDS_KEY_PAIR_GEN) {
2636 rv = mKeyPair->mPrivateKey->AddPublicKeyData(mPublicKey.get());
2637 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2640 return NS_OK;
2643 void GenerateAsymmetricKeyTask::Resolve() {
2644 mResultPromise->MaybeResolve(*mKeyPair);
2647 void GenerateAsymmetricKeyTask::Cleanup() { mKeyPair = nullptr; }
2649 class DeriveHkdfBitsTask : public ReturnArrayBufferViewTask {
2650 public:
2651 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2652 CryptoKey& aKey, uint32_t aLength)
2653 : mMechanism(CKM_INVALID_MECHANISM) {
2654 Init(aCx, aAlgorithm, aKey, aLength);
2657 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2658 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2659 : mLengthInBits(0), mLengthInBytes(0), mMechanism(CKM_INVALID_MECHANISM) {
2660 size_t length;
2661 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2663 if (NS_SUCCEEDED(mEarlyRv)) {
2664 Init(aCx, aAlgorithm, aKey, length);
2668 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2669 uint32_t aLength) {
2670 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_HKDF);
2671 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HKDF);
2673 if (!mSymKey.Assign(aKey.GetSymKey())) {
2674 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2675 return;
2678 RootedDictionary<HkdfParams> params(aCx);
2679 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2680 if (NS_FAILED(mEarlyRv)) {
2681 mEarlyRv = NS_ERROR_DOM_TYPE_MISMATCH_ERR;
2682 return;
2685 // length must be greater than zero.
2686 if (aLength == 0) {
2687 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2688 return;
2691 // Extract the hash algorithm.
2692 nsString hashName;
2693 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2694 if (NS_FAILED(mEarlyRv)) {
2695 return;
2698 // Check the given hash algorithm.
2699 switch (MapAlgorithmNameToMechanism(hashName)) {
2700 case CKM_SHA_1:
2701 mMechanism = CKM_NSS_HKDF_SHA1;
2702 break;
2703 case CKM_SHA256:
2704 mMechanism = CKM_NSS_HKDF_SHA256;
2705 break;
2706 case CKM_SHA384:
2707 mMechanism = CKM_NSS_HKDF_SHA384;
2708 break;
2709 case CKM_SHA512:
2710 mMechanism = CKM_NSS_HKDF_SHA512;
2711 break;
2712 default:
2713 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2714 return;
2717 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2718 ATTEMPT_BUFFER_INIT(mInfo, params.mInfo)
2719 mLengthInBytes = ceil((double)aLength / 8);
2720 mLengthInBits = aLength;
2723 private:
2724 size_t mLengthInBits;
2725 size_t mLengthInBytes;
2726 CryptoBuffer mSalt;
2727 CryptoBuffer mInfo;
2728 CryptoBuffer mSymKey;
2729 CK_MECHANISM_TYPE mMechanism;
2731 virtual nsresult DoCrypto() override {
2732 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2733 if (!arena) {
2734 return NS_ERROR_DOM_OPERATION_ERR;
2737 // Import the key
2738 SECItem keyItem = {siBuffer, nullptr, 0};
2739 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2741 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2742 if (!slot.get()) {
2743 return NS_ERROR_DOM_OPERATION_ERR;
2746 UniquePK11SymKey baseKey(PK11_ImportSymKey(slot.get(), mMechanism,
2747 PK11_OriginUnwrap, CKA_WRAP,
2748 &keyItem, nullptr));
2749 if (!baseKey) {
2750 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2753 SECItem salt = {siBuffer, nullptr, 0};
2754 SECItem info = {siBuffer, nullptr, 0};
2755 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2756 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &info, mInfo);
2758 CK_NSS_HKDFParams hkdfParams = {true, salt.data, salt.len,
2759 true, info.data, info.len};
2760 SECItem params = {siBuffer, (unsigned char*)&hkdfParams,
2761 sizeof(hkdfParams)};
2763 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2764 // derived symmetric key and don't matter because we ignore them anyway.
2765 UniquePK11SymKey symKey(PK11_Derive(baseKey.get(), mMechanism, &params,
2766 CKM_SHA512_HMAC, CKA_SIGN,
2767 mLengthInBytes));
2769 if (!symKey.get()) {
2770 return NS_ERROR_DOM_OPERATION_ERR;
2773 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2774 if (NS_FAILED(rv)) {
2775 return NS_ERROR_DOM_OPERATION_ERR;
2778 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2779 // just refers to a buffer managed by symKey. The assignment copies the
2780 // data, so mResult manages one copy, while symKey manages another.
2781 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2783 if (mLengthInBytes > mResult.Length()) {
2784 return NS_ERROR_DOM_DATA_ERR;
2787 if (!mResult.SetLength(mLengthInBytes, fallible)) {
2788 return NS_ERROR_DOM_UNKNOWN_ERR;
2791 // If the number of bits to derive is not a multiple of 8 we need to
2792 // zero out the remaining bits that were derived but not requested.
2793 if (mLengthInBits % 8) {
2794 mResult[mResult.Length() - 1] &= 0xff << (mLengthInBits % 8);
2797 return NS_OK;
2801 class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask {
2802 public:
2803 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2804 CryptoKey& aKey, uint32_t aLength)
2805 : mHashOidTag(SEC_OID_UNKNOWN) {
2806 Init(aCx, aAlgorithm, aKey, aLength);
2809 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2810 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2811 : mLength(0), mIterations(0), mHashOidTag(SEC_OID_UNKNOWN) {
2812 size_t length;
2813 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2815 if (NS_SUCCEEDED(mEarlyRv)) {
2816 Init(aCx, aAlgorithm, aKey, length);
2820 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2821 uint32_t aLength) {
2822 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_PBKDF2);
2823 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_PBKDF2);
2825 if (!mSymKey.Assign(aKey.GetSymKey())) {
2826 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2827 return;
2830 RootedDictionary<Pbkdf2Params> params(aCx);
2831 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2832 if (NS_FAILED(mEarlyRv)) {
2833 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2834 return;
2837 // length must be a multiple of 8 bigger than zero.
2838 if (aLength == 0 || aLength % 8) {
2839 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2840 return;
2843 // Extract the hash algorithm.
2844 nsString hashName;
2845 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2846 if (NS_FAILED(mEarlyRv)) {
2847 return;
2850 // Check the given hash algorithm.
2851 switch (MapAlgorithmNameToMechanism(hashName)) {
2852 case CKM_SHA_1:
2853 mHashOidTag = SEC_OID_HMAC_SHA1;
2854 break;
2855 case CKM_SHA256:
2856 mHashOidTag = SEC_OID_HMAC_SHA256;
2857 break;
2858 case CKM_SHA384:
2859 mHashOidTag = SEC_OID_HMAC_SHA384;
2860 break;
2861 case CKM_SHA512:
2862 mHashOidTag = SEC_OID_HMAC_SHA512;
2863 break;
2864 default:
2865 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2866 return;
2869 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2870 mLength = aLength >> 3; // bits to bytes
2871 mIterations = params.mIterations;
2874 private:
2875 size_t mLength;
2876 size_t mIterations;
2877 CryptoBuffer mSalt;
2878 CryptoBuffer mSymKey;
2879 SECOidTag mHashOidTag;
2881 virtual nsresult DoCrypto() override {
2882 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2883 if (!arena) {
2884 return NS_ERROR_DOM_OPERATION_ERR;
2887 SECItem salt = {siBuffer, nullptr, 0};
2888 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2889 // PK11_CreatePBEV2AlgorithmID will "helpfully" create PBKDF2 parameters
2890 // with a random salt if given a SECItem* that is either null or has a null
2891 // data pointer. This obviously isn't what we want, so we have to fake it
2892 // out by passing in a SECItem* with a non-null data pointer but with zero
2893 // length.
2894 if (!salt.data) {
2895 MOZ_ASSERT(salt.len == 0);
2896 salt.data =
2897 reinterpret_cast<unsigned char*>(PORT_ArenaAlloc(arena.get(), 1));
2898 if (!salt.data) {
2899 return NS_ERROR_DOM_UNKNOWN_ERR;
2903 // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this
2904 // parameter is unused for key generation. It is currently only used
2905 // for PBKDF2 authentication or key (un)wrapping when specifying an
2906 // encryption algorithm (PBES2).
2907 UniqueSECAlgorithmID algID(
2908 PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1,
2909 mHashOidTag, mLength, mIterations, &salt));
2911 if (!algID) {
2912 return NS_ERROR_DOM_OPERATION_ERR;
2915 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2916 if (!slot.get()) {
2917 return NS_ERROR_DOM_OPERATION_ERR;
2920 SECItem keyItem = {siBuffer, nullptr, 0};
2921 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2923 UniquePK11SymKey symKey(
2924 PK11_PBEKeyGen(slot.get(), algID.get(), &keyItem, false, nullptr));
2925 if (!symKey.get()) {
2926 return NS_ERROR_DOM_OPERATION_ERR;
2929 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2930 if (NS_FAILED(rv)) {
2931 return NS_ERROR_DOM_OPERATION_ERR;
2934 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2935 // just refers to a buffer managed by symKey. The assignment copies the
2936 // data, so mResult manages one copy, while symKey manages another.
2937 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2938 return NS_OK;
2942 template <class DeriveBitsTask>
2943 class DeriveKeyTask : public DeriveBitsTask {
2944 public:
2945 DeriveKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2946 const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
2947 const ObjectOrString& aDerivedKeyType, bool aExtractable,
2948 const Sequence<nsString>& aKeyUsages)
2949 : DeriveBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType) {
2950 if (NS_FAILED(this->mEarlyRv)) {
2951 return;
2954 constexpr auto format =
2955 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_FORMAT_RAW);
2956 mTask = new ImportSymmetricKeyTask(aGlobal, aCx, format, aDerivedKeyType,
2957 aExtractable, aKeyUsages);
2960 protected:
2961 RefPtr<ImportSymmetricKeyTask> mTask;
2963 private:
2964 virtual void Resolve() override {
2965 mTask->SetRawKeyData(this->mResult);
2966 mTask->DispatchWithPromise(this->mResultPromise);
2969 virtual void Cleanup() override { mTask = nullptr; }
2971 class DeriveEcdhBitsTask : public ReturnArrayBufferViewTask {
2972 public:
2973 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2974 CryptoKey& aKey, uint32_t aLength)
2975 : mLength(Some(aLength)), mPrivKey(aKey.GetPrivateKey()) {
2976 Init(aCx, aAlgorithm, aKey);
2979 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2980 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2981 : mPrivKey(aKey.GetPrivateKey()) {
2982 mEarlyRv =
2983 GetKeyLengthForAlgorithmIfSpecified(aCx, aTargetAlgorithm, mLength);
2984 if (NS_SUCCEEDED(mEarlyRv)) {
2985 Init(aCx, aAlgorithm, aKey);
2989 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey) {
2990 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDH);
2991 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDH);
2993 // Check that we have a private key.
2994 if (!mPrivKey) {
2995 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2996 return;
2999 // If specified, length must be a multiple of 8 bigger than zero
3000 // (otherwise, the full output of the key derivation is used).
3001 if (mLength) {
3002 if (*mLength == 0 || *mLength % 8) {
3003 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
3004 return;
3006 *mLength = *mLength >> 3; // bits to bytes
3009 // Retrieve the peer's public key.
3010 RootedDictionary<EcdhKeyDeriveParams> params(aCx);
3011 mEarlyRv = Coerce(aCx, params, aAlgorithm);
3012 if (NS_FAILED(mEarlyRv)) {
3013 /* The returned code is installed by Coerce function. */
3014 return;
3017 CryptoKey* publicKey = params.mPublic;
3018 mPubKey = publicKey->GetPublicKey();
3019 if (!mPubKey) {
3020 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
3021 return;
3024 CHECK_KEY_ALGORITHM(publicKey->Algorithm(), WEBCRYPTO_ALG_ECDH);
3026 // Both keys must use the same named curve.
3027 nsString curve1 = aKey.Algorithm().mEc.mNamedCurve;
3028 nsString curve2 = publicKey->Algorithm().mEc.mNamedCurve;
3030 if (!curve1.Equals(curve2)) {
3031 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
3032 return;
3036 private:
3037 Maybe<size_t> mLength;
3038 UniqueSECKEYPrivateKey mPrivKey;
3039 UniqueSECKEYPublicKey mPubKey;
3041 virtual nsresult DoCrypto() override {
3042 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
3043 // derived symmetric key and don't matter because we ignore them anyway.
3044 UniquePK11SymKey symKey(
3045 PK11_PubDeriveWithKDF(mPrivKey.get(), mPubKey.get(), PR_FALSE, nullptr,
3046 nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC,
3047 CKA_SIGN, 0, CKD_NULL, nullptr, nullptr));
3049 if (!symKey.get()) {
3050 return NS_ERROR_DOM_OPERATION_ERR;
3053 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
3054 if (NS_FAILED(rv)) {
3055 return NS_ERROR_DOM_OPERATION_ERR;
3058 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
3059 // just refers to a buffer managed by symKey. The assignment copies the
3060 // data, so mResult manages one copy, while symKey manages another.
3061 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
3063 if (mLength) {
3064 if (*mLength > mResult.Length()) {
3065 return NS_ERROR_DOM_OPERATION_ERR;
3067 if (!mResult.SetLength(*mLength, fallible)) {
3068 return NS_ERROR_DOM_UNKNOWN_ERR;
3072 return NS_OK;
3076 template <class KeyEncryptTask>
3077 class WrapKeyTask : public ExportKeyTask {
3078 public:
3079 WrapKeyTask(JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
3080 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm)
3081 : ExportKeyTask(aFormat, aKey) {
3082 if (NS_FAILED(mEarlyRv)) {
3083 return;
3086 mTask = new KeyEncryptTask(aCx, aWrapAlgorithm, aWrappingKey, true);
3089 private:
3090 RefPtr<KeyEncryptTask> mTask;
3092 virtual nsresult AfterCrypto() override {
3093 // If wrapping JWK, stringify the JSON
3094 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3095 nsAutoString json;
3096 if (!mJwk.ToJSON(json)) {
3097 return NS_ERROR_DOM_OPERATION_ERR;
3100 NS_ConvertUTF16toUTF8 utf8(json);
3101 if (!mResult.Assign((const uint8_t*)utf8.BeginReading(), utf8.Length())) {
3102 return NS_ERROR_DOM_OPERATION_ERR;
3106 return NS_OK;
3109 virtual void Resolve() override {
3110 mTask->SetData(mResult);
3111 mTask->DispatchWithPromise(mResultPromise);
3114 virtual void Cleanup() override { mTask = nullptr; }
3117 template <class KeyEncryptTask>
3118 class UnwrapKeyTask : public KeyEncryptTask {
3119 public:
3120 UnwrapKeyTask(JSContext* aCx, const ArrayBufferViewOrArrayBuffer& aWrappedKey,
3121 CryptoKey& aUnwrappingKey,
3122 const ObjectOrString& aUnwrapAlgorithm, ImportKeyTask* aTask)
3123 : KeyEncryptTask(aCx, aUnwrapAlgorithm, aUnwrappingKey, aWrappedKey,
3124 false),
3125 mTask(aTask) {}
3127 private:
3128 RefPtr<ImportKeyTask> mTask;
3130 virtual void Resolve() override {
3131 mTask->SetKeyDataMaybeParseJWK(KeyEncryptTask::mResult);
3132 mTask->DispatchWithPromise(KeyEncryptTask::mResultPromise);
3135 virtual void Cleanup() override { mTask = nullptr; }
3138 // Task creation methods for WebCryptoTask
3140 // Note: We do not perform algorithm normalization as a monolithic process,
3141 // as described in the spec. Instead:
3142 // * Each method handles its slice of the supportedAlgorithms structure
3143 // * Task constructors take care of:
3144 // * Coercing the algorithm to the proper concrete type
3145 // * Cloning subordinate data items
3146 // * Cloning input data as needed
3148 // Thus, support for different algorithms is determined by the if-statements
3149 // below, rather than a data structure.
3151 // This results in algorithm normalization coming after some other checks,
3152 // and thus slightly more steps being done synchronously than the spec calls
3153 // for. But none of these steps is especially time-consuming.
3155 WebCryptoTask* WebCryptoTask::CreateEncryptDecryptTask(
3156 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
3157 const CryptoOperationData& aData, bool aEncrypt) {
3158 TelemetryMethod method = (aEncrypt) ? TM_ENCRYPT : TM_DECRYPT;
3159 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
3160 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_ENC,
3161 aKey.Extractable());
3163 // Ensure key is usable for this operation
3164 if ((aEncrypt && !aKey.HasUsage(CryptoKey::ENCRYPT)) ||
3165 (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) {
3166 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3169 nsString algName;
3170 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3171 if (NS_FAILED(rv)) {
3172 return new FailureTask(rv);
3175 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3176 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3177 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3178 return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
3179 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3180 return new RsaOaepTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
3183 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3186 WebCryptoTask* WebCryptoTask::CreateSignVerifyTask(
3187 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
3188 const CryptoOperationData& aSignature, const CryptoOperationData& aData,
3189 bool aSign) {
3190 TelemetryMethod method = (aSign) ? TM_SIGN : TM_VERIFY;
3191 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
3192 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_SIG,
3193 aKey.Extractable());
3195 // Ensure key is usable for this operation
3196 if ((aSign && !aKey.HasUsage(CryptoKey::SIGN)) ||
3197 (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) {
3198 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3201 nsString algName;
3202 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3203 if (NS_FAILED(rv)) {
3204 return new FailureTask(rv);
3207 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
3208 return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
3209 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3210 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
3211 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
3212 algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
3213 return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
3214 aData, aSign);
3217 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3220 WebCryptoTask* WebCryptoTask::CreateDigestTask(
3221 JSContext* aCx, const ObjectOrString& aAlgorithm,
3222 const CryptoOperationData& aData) {
3223 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DIGEST);
3225 nsString algName;
3226 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3227 if (NS_FAILED(rv)) {
3228 return new FailureTask(rv);
3231 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
3232 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256) ||
3233 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
3234 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
3235 return new DigestTask(aCx, aAlgorithm, aData);
3238 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3241 WebCryptoTask* WebCryptoTask::CreateImportKeyTask(
3242 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
3243 JS::Handle<JSObject*> aKeyData, const ObjectOrString& aAlgorithm,
3244 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3245 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY);
3246 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable);
3248 // Verify that the format is recognized
3249 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3250 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3251 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3252 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3253 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3256 // Verify that aKeyUsages does not contain an unrecognized value
3257 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3258 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3261 nsString algName;
3262 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3263 if (NS_FAILED(rv)) {
3264 return new FailureTask(rv);
3267 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
3268 // However, the spec should be updated to allow it.
3269 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3270 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3271 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
3272 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
3273 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
3274 algName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
3275 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
3276 return new ImportSymmetricKeyTask(aGlobal, aCx, aFormat, aKeyData,
3277 aAlgorithm, aExtractable, aKeyUsages);
3278 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3279 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
3280 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
3281 return new ImportRsaKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
3282 aExtractable, aKeyUsages);
3283 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
3284 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
3285 return new ImportEcKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
3286 aExtractable, aKeyUsages);
3287 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
3288 return new ImportEdKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
3289 aExtractable, aKeyUsages);
3290 } else {
3291 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3295 WebCryptoTask* WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat,
3296 CryptoKey& aKey) {
3297 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_EXPORTKEY);
3299 // Verify that the format is recognized
3300 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3301 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3302 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3303 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3304 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3307 // Verify that the key is extractable
3308 if (!aKey.Extractable()) {
3309 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3312 // Verify that the algorithm supports export
3313 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
3314 // However, the spec should be updated to allow it.
3315 nsString algName = aKey.Algorithm().mName;
3316 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3317 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3318 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
3319 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
3320 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
3321 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC) ||
3322 algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3323 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
3324 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
3325 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
3326 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
3327 algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
3328 return new ExportKeyTask(aFormat, aKey);
3330 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3333 WebCryptoTask* WebCryptoTask::CreateGenerateKeyTask(
3334 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3335 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3336 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_GENERATEKEY);
3337 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_GENERATE,
3338 aExtractable);
3339 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3340 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3343 nsString algName;
3344 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3345 if (NS_FAILED(rv)) {
3346 return new FailureTask(rv);
3349 if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3350 algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3351 algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3352 algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
3353 algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3354 return new GenerateSymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3355 aKeyUsages);
3356 } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3357 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3358 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS) ||
3359 algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
3360 algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA) ||
3361 algName.EqualsASCII(WEBCRYPTO_ALG_ED25519)
3364 return new GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3365 aKeyUsages);
3366 } else {
3367 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3371 WebCryptoTask* WebCryptoTask::CreateDeriveKeyTask(
3372 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3373 CryptoKey& aBaseKey, const ObjectOrString& aDerivedKeyType,
3374 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3375 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY);
3377 // Ensure baseKey is usable for this operation
3378 if (!aBaseKey.HasUsage(CryptoKey::DERIVEKEY)) {
3379 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3382 // Verify that aKeyUsages does not contain an unrecognized value
3383 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3384 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3387 nsString algName;
3388 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3389 if (NS_FAILED(rv)) {
3390 return new FailureTask(rv);
3393 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3394 return new DeriveKeyTask<DeriveHkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3395 aBaseKey, aDerivedKeyType,
3396 aExtractable, aKeyUsages);
3399 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3400 return new DeriveKeyTask<DerivePbkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3401 aBaseKey, aDerivedKeyType,
3402 aExtractable, aKeyUsages);
3405 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3406 return new DeriveKeyTask<DeriveEcdhBitsTask>(aGlobal, aCx, aAlgorithm,
3407 aBaseKey, aDerivedKeyType,
3408 aExtractable, aKeyUsages);
3411 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3414 WebCryptoTask* WebCryptoTask::CreateDeriveBitsTask(
3415 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
3416 uint32_t aLength) {
3417 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS);
3419 // Ensure baseKey is usable for this operation
3420 if (!aKey.HasUsage(CryptoKey::DERIVEBITS)) {
3421 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3424 nsString algName;
3425 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3426 if (NS_FAILED(rv)) {
3427 return new FailureTask(rv);
3430 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3431 return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3434 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3435 return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
3438 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3439 return new DeriveHkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3442 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3445 WebCryptoTask* WebCryptoTask::CreateWrapKeyTask(
3446 JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
3447 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm) {
3448 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_WRAPKEY);
3450 // Verify that the format is recognized
3451 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3452 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3453 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3454 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3455 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3458 // Ensure wrappingKey is usable for this operation
3459 if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) {
3460 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3463 // Ensure key is extractable
3464 if (!aKey.Extractable()) {
3465 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3468 nsString wrapAlgName;
3469 nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName);
3470 if (NS_FAILED(rv)) {
3471 return new FailureTask(rv);
3474 if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3475 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3476 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3477 return new WrapKeyTask<AesTask>(aCx, aFormat, aKey, aWrappingKey,
3478 aWrapAlgorithm);
3479 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3480 return new WrapKeyTask<AesKwTask>(aCx, aFormat, aKey, aWrappingKey,
3481 aWrapAlgorithm);
3482 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3483 return new WrapKeyTask<RsaOaepTask>(aCx, aFormat, aKey, aWrappingKey,
3484 aWrapAlgorithm);
3487 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3490 WebCryptoTask* WebCryptoTask::CreateUnwrapKeyTask(
3491 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
3492 const ArrayBufferViewOrArrayBuffer& aWrappedKey, CryptoKey& aUnwrappingKey,
3493 const ObjectOrString& aUnwrapAlgorithm,
3494 const ObjectOrString& aUnwrappedKeyAlgorithm, bool aExtractable,
3495 const Sequence<nsString>& aKeyUsages) {
3496 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_UNWRAPKEY);
3498 // Ensure key is usable for this operation
3499 if (!aUnwrappingKey.HasUsage(CryptoKey::UNWRAPKEY)) {
3500 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3503 // Verify that aKeyUsages does not contain an unrecognized value
3504 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3505 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3508 nsString keyAlgName;
3509 nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName);
3510 if (NS_FAILED(rv)) {
3511 return new FailureTask(rv);
3514 CryptoOperationData dummy;
3515 RefPtr<ImportKeyTask> importTask;
3516 if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3517 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3518 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3519 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
3520 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HKDF) ||
3521 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3522 importTask = new ImportSymmetricKeyTask(aGlobal, aCx, aFormat,
3523 aUnwrappedKeyAlgorithm,
3524 aExtractable, aKeyUsages);
3525 } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3526 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3527 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS)) {
3528 importTask =
3529 new ImportRsaKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3530 aExtractable, aKeyUsages);
3531 } else if (keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
3532 keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
3533 importTask =
3534 new ImportEcKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3535 aExtractable, aKeyUsages);
3536 } else if (keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
3537 importTask =
3538 new ImportEdKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3539 aExtractable, aKeyUsages);
3540 } else {
3541 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3544 nsString unwrapAlgName;
3545 rv = GetAlgorithmName(aCx, aUnwrapAlgorithm, unwrapAlgName);
3546 if (NS_FAILED(rv)) {
3547 return new FailureTask(rv);
3549 if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3550 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3551 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3552 return new UnwrapKeyTask<AesTask>(aCx, aWrappedKey, aUnwrappingKey,
3553 aUnwrapAlgorithm, importTask);
3554 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3555 return new UnwrapKeyTask<AesKwTask>(aCx, aWrappedKey, aUnwrappingKey,
3556 aUnwrapAlgorithm, importTask);
3557 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3558 return new UnwrapKeyTask<RsaOaepTask>(aCx, aWrappedKey, aUnwrappingKey,
3559 aUnwrapAlgorithm, importTask);
3562 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3565 WebCryptoTask::WebCryptoTask()
3566 : CancelableRunnable("WebCryptoTask"),
3567 mEarlyRv(NS_OK),
3568 mEarlyComplete(false),
3569 mOriginalEventTarget(nullptr),
3570 mRv(NS_ERROR_NOT_INITIALIZED) {}
3572 WebCryptoTask::~WebCryptoTask() = default;
3574 } // namespace mozilla::dom