Backed out changeset f594e6f00208 (bug 1940883) for causing crashes in bug 1941164.
[gecko.git] / dom / crypto / WebCryptoTask.cpp
blob76a27d2f8a7a2ce9940056b923cb5ef14ce6a375
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 MOZ_GLOBINIT 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,
89 TA_ED25519 = 25,
90 TA_X25519 = 26,
93 // Convenience functions for extracting / converting information
95 // OOM-safe CryptoBuffer initialization, suitable for constructors
96 #define ATTEMPT_BUFFER_INIT(dst, src) \
97 if (!dst.Assign(src)) { \
98 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; \
99 return; \
102 // OOM-safe CryptoBuffer-to-SECItem copy, suitable for DoCrypto
103 #define ATTEMPT_BUFFER_TO_SECITEM(arena, dst, src) \
104 if (!src.ToSECItem(arena, dst)) { \
105 return NS_ERROR_DOM_UNKNOWN_ERR; \
108 // OOM-safe CryptoBuffer copy, suitable for DoCrypto
109 #define ATTEMPT_BUFFER_ASSIGN(dst, src) \
110 if (!dst.Assign(src)) { \
111 return NS_ERROR_DOM_UNKNOWN_ERR; \
114 // Safety check for algorithms that use keys, suitable for constructors
115 #define CHECK_KEY_ALGORITHM(keyAlg, algName) \
117 if (!NORMALIZED_EQUALS(keyAlg.mName, algName)) { \
118 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; \
119 return; \
123 class ClearException {
124 public:
125 explicit ClearException(JSContext* aCx) : mCx(aCx) {}
127 ~ClearException() { JS_ClearPendingException(mCx); }
129 private:
130 JSContext* mCx;
133 template <class OOS>
134 static nsresult GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
135 nsString& aName) {
136 ClearException ce(aCx);
138 if (aAlgorithm.IsString()) {
139 // If string, then treat as algorithm name
140 aName.Assign(aAlgorithm.GetAsString());
141 } else {
142 // Coerce to algorithm and extract name
143 JS::Rooted<JS::Value> value(aCx,
144 JS::ObjectValue(*aAlgorithm.GetAsObject()));
145 Algorithm alg;
147 if (!alg.Init(aCx, value)) {
148 return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
151 aName = alg.mName;
154 if (!NormalizeToken(aName, aName)) {
155 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
158 return NS_OK;
161 template <class T, class OOS>
162 static nsresult Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm) {
163 ClearException ce(aCx);
165 if (!aAlgorithm.IsObject()) {
166 return NS_ERROR_DOM_SYNTAX_ERR;
169 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
170 if (!aTarget.Init(aCx, value)) {
171 return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
174 return NS_OK;
177 inline size_t MapHashAlgorithmNameToBlockSize(const nsString& aName) {
178 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
179 aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
180 return 512;
183 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
184 aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
185 return 1024;
188 return 0;
191 inline nsresult GetKeyLengthForAlgorithmIfSpecified(
192 JSContext* aCx, const ObjectOrString& aAlgorithm, Maybe<size_t>& aLength) {
193 // Extract algorithm name
194 nsString algName;
195 if (NS_FAILED(GetAlgorithmName(aCx, aAlgorithm, algName))) {
196 return NS_ERROR_DOM_SYNTAX_ERR;
199 // Read AES key length from given algorithm object.
200 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
201 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
202 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
203 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
204 RootedDictionary<AesDerivedKeyParams> params(aCx);
205 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
206 return NS_ERROR_DOM_SYNTAX_ERR;
209 if (params.mLength != 128 && params.mLength != 192 &&
210 params.mLength != 256) {
211 return NS_ERROR_DOM_OPERATION_ERR;
214 aLength.emplace(params.mLength);
215 return NS_OK;
218 // Read HMAC key length from given algorithm object or
219 // determine key length as the block size of the given hash.
220 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
221 RootedDictionary<HmacDerivedKeyParams> params(aCx);
222 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
223 return NS_ERROR_DOM_SYNTAX_ERR;
226 // Return the passed length, if any.
227 if (params.mLength.WasPassed()) {
228 aLength.emplace(params.mLength.Value());
229 return NS_OK;
232 nsString hashName;
233 if (NS_FAILED(GetAlgorithmName(aCx, params.mHash, hashName))) {
234 return NS_ERROR_DOM_SYNTAX_ERR;
237 // Return the given hash algorithm's block size as the key length.
238 size_t blockSize = MapHashAlgorithmNameToBlockSize(hashName);
239 if (blockSize == 0) {
240 return NS_ERROR_DOM_SYNTAX_ERR;
243 aLength.emplace(blockSize);
244 return NS_OK;
247 return NS_OK;
250 inline nsresult GetKeyLengthForAlgorithm(JSContext* aCx,
251 const ObjectOrString& aAlgorithm,
252 size_t& aLength) {
253 Maybe<size_t> length;
254 nsresult rv = GetKeyLengthForAlgorithmIfSpecified(aCx, aAlgorithm, length);
255 if (NS_FAILED(rv)) {
256 return rv;
258 if (length.isNothing()) {
259 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
261 aLength = *length;
262 return NS_OK;
265 inline bool MapOIDTagToNamedCurve(SECOidTag aOIDTag, nsString& aResult) {
266 switch (aOIDTag) {
267 case SEC_OID_SECG_EC_SECP256R1:
268 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
269 break;
270 case SEC_OID_SECG_EC_SECP384R1:
271 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
272 break;
273 case SEC_OID_SECG_EC_SECP521R1:
274 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
275 break;
276 case SEC_OID_ED25519_PUBLIC_KEY:
277 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_ED25519);
278 break;
279 case SEC_OID_X25519:
280 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519);
281 break;
282 default:
283 return false;
286 return true;
289 inline SECOidTag MapHashAlgorithmNameToOID(const nsString& aName) {
290 SECOidTag hashOID(SEC_OID_UNKNOWN);
292 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
293 hashOID = SEC_OID_SHA1;
294 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
295 hashOID = SEC_OID_SHA256;
296 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
297 hashOID = SEC_OID_SHA384;
298 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
299 hashOID = SEC_OID_SHA512;
302 return hashOID;
305 inline CK_MECHANISM_TYPE MapHashAlgorithmNameToMgfMechanism(
306 const nsString& aName) {
307 CK_MECHANISM_TYPE mech(UNKNOWN_CK_MECHANISM);
309 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
310 mech = CKG_MGF1_SHA1;
311 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
312 mech = CKG_MGF1_SHA256;
313 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
314 mech = CKG_MGF1_SHA384;
315 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
316 mech = CKG_MGF1_SHA512;
319 return mech;
322 // Implementation of WebCryptoTask methods
324 void WebCryptoTask::DispatchWithPromise(Promise* aResultPromise) {
325 mResultPromise = aResultPromise;
327 // Fail if an error was set during the constructor
328 MAYBE_EARLY_FAIL(mEarlyRv)
330 // Perform pre-NSS operations, and fail if they fail
331 mEarlyRv = BeforeCrypto();
332 MAYBE_EARLY_FAIL(mEarlyRv)
334 // Skip dispatch if we're already done. Otherwise launch a CryptoTask
335 if (mEarlyComplete) {
336 CallCallback(mEarlyRv);
337 return;
340 // Store calling thread
341 mOriginalEventTarget = GetCurrentSerialEventTarget();
343 // If we are running on a worker thread we must hold the worker
344 // alive while we work on the thread pool. Otherwise the worker
345 // private may get torn down before we dispatch back to complete
346 // the transaction.
347 if (!NS_IsMainThread()) {
348 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
349 MOZ_ASSERT(workerPrivate);
351 RefPtr<StrongWorkerRef> workerRef =
352 StrongWorkerRef::Create(workerPrivate, "WebCryptoTask");
353 if (NS_WARN_IF(!workerRef)) {
354 mEarlyRv = NS_BINDING_ABORTED;
355 } else {
356 mWorkerRef = new ThreadSafeWorkerRef(workerRef);
359 MAYBE_EARLY_FAIL(mEarlyRv);
361 // dispatch to thread pool
363 if (!EnsureNSSInitializedChromeOrContent()) {
364 mEarlyRv = NS_ERROR_FAILURE;
366 MAYBE_EARLY_FAIL(mEarlyRv);
368 mEarlyRv = NS_DispatchBackgroundTask(this);
369 MAYBE_EARLY_FAIL(mEarlyRv)
372 NS_IMETHODIMP
373 WebCryptoTask::Run() {
374 // Run heavy crypto operations on the thread pool, off the original thread.
375 if (!IsOnOriginalThread()) {
376 mRv = CalculateResult();
378 // Back to the original thread, i.e. continue below.
379 mOriginalEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
380 return NS_OK;
383 // We're now back on the calling thread.
384 CallCallback(mRv);
386 // Stop holding the worker thread alive now that the async work has
387 // been completed.
388 mWorkerRef = nullptr;
390 return NS_OK;
393 nsresult WebCryptoTask::Cancel() {
394 MOZ_ASSERT(IsOnOriginalThread());
395 FailWithError(NS_BINDING_ABORTED);
396 return NS_OK;
399 void WebCryptoTask::FailWithError(nsresult aRv) {
400 MOZ_ASSERT(IsOnOriginalThread());
401 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
403 if (aRv == NS_ERROR_DOM_TYPE_MISMATCH_ERR) {
404 mResultPromise->MaybeRejectWithTypeError(
405 "The operation could not be performed.");
406 } else {
407 // Blindly convert nsresult to DOMException
408 // Individual tasks must ensure they pass the right values
409 mResultPromise->MaybeReject(aRv);
411 // Manually release mResultPromise while we're on the main thread
412 mResultPromise = nullptr;
413 mWorkerRef = nullptr;
414 Cleanup();
417 nsresult WebCryptoTask::CalculateResult() {
418 MOZ_ASSERT(!IsOnOriginalThread());
420 return DoCrypto();
423 void WebCryptoTask::CallCallback(nsresult rv) {
424 MOZ_ASSERT(IsOnOriginalThread());
425 if (NS_FAILED(rv)) {
426 FailWithError(rv);
427 return;
430 nsresult rv2 = AfterCrypto();
431 if (NS_FAILED(rv2)) {
432 FailWithError(rv2);
433 return;
436 Resolve();
437 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, true);
439 // Manually release mResultPromise while we're on the main thread
440 mResultPromise = nullptr;
441 Cleanup();
444 // Some generic utility classes
446 class FailureTask : public WebCryptoTask {
447 public:
448 explicit FailureTask(nsresult aRv) { mEarlyRv = aRv; }
451 class ReturnArrayBufferViewTask : public WebCryptoTask {
452 protected:
453 CryptoBuffer mResult;
455 private:
456 // Returns mResult as an ArrayBufferView, or an error
457 virtual void Resolve() override {
458 TypedArrayCreator<ArrayBuffer> ret(mResult);
459 mResultPromise->MaybeResolve(ret);
463 class DeferredData {
464 public:
465 template <class T>
466 void SetData(const T& aData) {
467 mDataIsSet = mData.Assign(aData);
470 protected:
471 DeferredData() : mDataIsSet(false) {}
473 CryptoBuffer mData;
474 bool mDataIsSet;
477 class AesTask : public ReturnArrayBufferViewTask, public DeferredData {
478 public:
479 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
480 bool aEncrypt)
481 : mMechanism(CKM_INVALID_MECHANISM),
482 mTagLength(0),
483 mCounterLength(0),
484 mEncrypt(aEncrypt) {
485 Init(aCx, aAlgorithm, aKey, aEncrypt);
488 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
489 const CryptoOperationData& aData, bool aEncrypt)
490 : mMechanism(CKM_INVALID_MECHANISM),
491 mTagLength(0),
492 mCounterLength(0),
493 mEncrypt(aEncrypt) {
494 Init(aCx, aAlgorithm, aKey, aEncrypt);
495 SetData(aData);
498 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
499 bool aEncrypt) {
500 nsString algName;
501 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
502 if (NS_FAILED(mEarlyRv)) {
503 return;
506 if (!mSymKey.Assign(aKey.GetSymKey())) {
507 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
508 return;
511 // Check that we got a reasonable key
512 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
513 (mSymKey.Length() != 32)) {
514 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
515 return;
518 // Cache parameters depending on the specific algorithm
519 TelemetryAlgorithm telemetryAlg;
520 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
521 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CBC);
523 mMechanism = CKM_AES_CBC_PAD;
524 telemetryAlg = TA_AES_CBC;
525 RootedDictionary<AesCbcParams> params(aCx);
526 nsresult rv = Coerce(aCx, params, aAlgorithm);
527 if (NS_FAILED(rv)) {
528 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
529 return;
532 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
533 if (mIv.Length() != 16) {
534 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
535 return;
537 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
538 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CTR);
540 mMechanism = CKM_AES_CTR;
541 telemetryAlg = TA_AES_CTR;
542 RootedDictionary<AesCtrParams> params(aCx);
543 nsresult rv = Coerce(aCx, params, aAlgorithm);
544 if (NS_FAILED(rv)) {
545 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
546 return;
549 ATTEMPT_BUFFER_INIT(mIv, params.mCounter)
550 if (mIv.Length() != 16) {
551 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
552 return;
555 mCounterLength = params.mLength;
556 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
557 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM);
559 mMechanism = CKM_AES_GCM;
560 telemetryAlg = TA_AES_GCM;
561 RootedDictionary<AesGcmParams> params(aCx);
562 nsresult rv = Coerce(aCx, params, aAlgorithm);
563 if (NS_FAILED(rv)) {
564 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
565 return;
568 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
570 if (params.mAdditionalData.WasPassed()) {
571 ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value())
574 // 32, 64, 96, 104, 112, 120 or 128
575 mTagLength = 128;
576 if (params.mTagLength.WasPassed()) {
577 mTagLength = params.mTagLength.Value();
578 if ((mTagLength > 128) ||
579 !(mTagLength == 32 || mTagLength == 64 ||
580 (mTagLength >= 96 && mTagLength % 8 == 0))) {
581 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
582 return;
585 } else {
586 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
587 return;
589 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
592 private:
593 CK_MECHANISM_TYPE mMechanism;
594 CryptoBuffer mSymKey;
595 CryptoBuffer mIv; // Initialization vector
596 CryptoBuffer mAad; // Additional Authenticated Data
597 uint8_t mTagLength;
598 uint8_t mCounterLength;
599 bool mEncrypt;
601 virtual nsresult DoCrypto() override {
602 nsresult rv;
604 if (!mDataIsSet) {
605 return NS_ERROR_DOM_OPERATION_ERR;
608 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
609 if (!arena) {
610 return NS_ERROR_DOM_OPERATION_ERR;
613 // Construct the parameters object depending on algorithm
614 SECItem param = {siBuffer, nullptr, 0};
615 CK_AES_CTR_PARAMS ctrParams;
616 CK_GCM_PARAMS gcmParams;
617 switch (mMechanism) {
618 case CKM_AES_CBC_PAD:
619 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &param, mIv);
620 break;
621 case CKM_AES_CTR:
622 ctrParams.ulCounterBits = mCounterLength;
623 MOZ_ASSERT(mIv.Length() == 16);
624 memcpy(&ctrParams.cb, mIv.Elements(), 16);
625 param.type = siBuffer;
626 param.data = (unsigned char*)&ctrParams;
627 param.len = sizeof(ctrParams);
628 break;
629 case CKM_AES_GCM:
630 gcmParams.pIv = mIv.Elements();
631 gcmParams.ulIvLen = mIv.Length();
632 gcmParams.ulIvBits = gcmParams.ulIvLen * 8;
633 gcmParams.pAAD = mAad.Elements();
634 gcmParams.ulAADLen = mAad.Length();
635 gcmParams.ulTagBits = mTagLength;
636 param.type = siBuffer;
637 param.data = (unsigned char*)&gcmParams;
638 param.len = sizeof(gcmParams);
639 break;
640 default:
641 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
644 // Import the key
645 SECItem keyItem = {siBuffer, nullptr, 0};
646 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
647 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
648 MOZ_ASSERT(slot.get());
649 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
650 PK11_OriginUnwrap, CKA_ENCRYPT,
651 &keyItem, nullptr));
652 if (!symKey) {
653 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
656 // Check whether the integer addition would overflow.
657 if (std::numeric_limits<CryptoBuffer::size_type>::max() - 16 <
658 mData.Length()) {
659 return NS_ERROR_DOM_DATA_ERR;
662 // Initialize the output buffer (enough space for padding / a full tag)
663 if (!mResult.SetLength(mData.Length() + 16, fallible)) {
664 return NS_ERROR_DOM_UNKNOWN_ERR;
667 uint32_t outLen = 0;
669 // Perform the encryption/decryption
670 if (mEncrypt) {
671 rv = MapSECStatus(PK11_Encrypt(
672 symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
673 mResult.Length(), mData.Elements(), mData.Length()));
674 } else {
675 rv = MapSECStatus(PK11_Decrypt(
676 symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
677 mResult.Length(), mData.Elements(), mData.Length()));
679 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
681 mResult.TruncateLength(outLen);
682 return rv;
686 // This class looks like an encrypt/decrypt task, like AesTask,
687 // but it is only exposed to wrapKey/unwrapKey, not encrypt/decrypt
688 class AesKwTask : public ReturnArrayBufferViewTask, public DeferredData {
689 public:
690 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
691 bool aEncrypt)
692 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
693 Init(aCx, aAlgorithm, aKey, aEncrypt);
696 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
697 const CryptoOperationData& aData, bool aEncrypt)
698 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
699 Init(aCx, aAlgorithm, aKey, aEncrypt);
700 SetData(aData);
703 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
704 bool aEncrypt) {
705 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_KW);
707 nsString algName;
708 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
709 if (NS_FAILED(mEarlyRv)) {
710 return;
713 if (!mSymKey.Assign(aKey.GetSymKey())) {
714 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
715 return;
718 // Check that we got a reasonable key
719 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
720 (mSymKey.Length() != 32)) {
721 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
722 return;
725 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_AES_KW);
728 private:
729 CK_MECHANISM_TYPE mMechanism;
730 CryptoBuffer mSymKey;
731 bool mEncrypt;
733 virtual nsresult DoCrypto() override {
734 nsresult rv;
736 if (!mDataIsSet) {
737 return NS_ERROR_DOM_OPERATION_ERR;
740 // Check that the input is a multiple of 64 bits long
741 if (mData.Length() == 0 || mData.Length() % 8 != 0) {
742 return NS_ERROR_DOM_DATA_ERR;
745 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
746 if (!arena) {
747 return NS_ERROR_DOM_OPERATION_ERR;
750 // Import the key
751 SECItem keyItem = {siBuffer, nullptr, 0};
752 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
753 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
754 MOZ_ASSERT(slot.get());
755 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
756 PK11_OriginUnwrap, CKA_WRAP,
757 &keyItem, nullptr));
758 if (!symKey) {
759 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
762 // Import the data to a SECItem
763 SECItem dataItem = {siBuffer, nullptr, 0};
764 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &dataItem, mData);
766 // Parameters for the fake keys
767 CK_MECHANISM_TYPE fakeMechanism = CKM_SHA_1_HMAC;
768 CK_ATTRIBUTE_TYPE fakeOperation = CKA_SIGN;
770 if (mEncrypt) {
771 // Import the data into a fake PK11SymKey structure
772 UniquePK11SymKey keyToWrap(
773 PK11_ImportSymKey(slot.get(), fakeMechanism, PK11_OriginUnwrap,
774 fakeOperation, &dataItem, nullptr));
775 if (!keyToWrap) {
776 return NS_ERROR_DOM_OPERATION_ERR;
779 // Encrypt and return the wrapped key
780 // AES-KW encryption results in a wrapped key 64 bits longer
781 if (!mResult.SetLength(mData.Length() + 8, fallible)) {
782 return NS_ERROR_DOM_OPERATION_ERR;
784 SECItem resultItem = {siBuffer, mResult.Elements(),
785 (unsigned int)mResult.Length()};
786 rv = MapSECStatus(PK11_WrapSymKey(mMechanism, nullptr, symKey.get(),
787 keyToWrap.get(), &resultItem));
788 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
789 } else {
790 // Decrypt the ciphertext into a temporary PK11SymKey
791 // Unwrapped key should be 64 bits shorter
792 int keySize = mData.Length() - 8;
793 UniquePK11SymKey unwrappedKey(
794 PK11_UnwrapSymKey(symKey.get(), mMechanism, nullptr, &dataItem,
795 fakeMechanism, fakeOperation, keySize));
796 if (!unwrappedKey) {
797 return NS_ERROR_DOM_OPERATION_ERR;
800 // Export the key to get the cleartext
801 rv = MapSECStatus(PK11_ExtractKeyValue(unwrappedKey.get()));
802 if (NS_FAILED(rv)) {
803 return NS_ERROR_DOM_UNKNOWN_ERR;
805 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(unwrappedKey.get()));
808 return rv;
812 class RsaOaepTask : public ReturnArrayBufferViewTask, public DeferredData {
813 public:
814 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
815 bool aEncrypt)
816 : mPrivKey(aKey.GetPrivateKey()),
817 mPubKey(aKey.GetPublicKey()),
818 mEncrypt(aEncrypt) {
819 Init(aCx, aAlgorithm, aKey, aEncrypt);
822 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
823 const CryptoOperationData& aData, bool aEncrypt)
824 : mPrivKey(aKey.GetPrivateKey()),
825 mPubKey(aKey.GetPublicKey()),
826 mEncrypt(aEncrypt) {
827 Init(aCx, aAlgorithm, aKey, aEncrypt);
828 SetData(aData);
831 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
832 bool aEncrypt) {
833 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_OAEP);
835 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_OAEP);
837 if (mEncrypt) {
838 if (!mPubKey) {
839 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
840 return;
842 mStrength = SECKEY_PublicKeyStrength(mPubKey.get());
843 } else {
844 if (!mPrivKey) {
845 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
846 return;
848 mStrength = PK11_GetPrivateModulusLen(mPrivKey.get());
851 // The algorithm could just be given as a string
852 // in which case there would be no label specified.
853 if (!aAlgorithm.IsString()) {
854 RootedDictionary<RsaOaepParams> params(aCx);
855 mEarlyRv = Coerce(aCx, params, aAlgorithm);
856 if (NS_FAILED(mEarlyRv)) {
857 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
858 return;
861 if (params.mLabel.WasPassed()) {
862 ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value());
865 // Otherwise mLabel remains the empty octet string, as intended
867 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
868 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
869 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlg.mName);
871 // Check we found appropriate mechanisms.
872 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
873 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
874 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
875 return;
879 private:
880 CK_MECHANISM_TYPE mHashMechanism;
881 CK_MECHANISM_TYPE mMgfMechanism;
882 UniqueSECKEYPrivateKey mPrivKey;
883 UniqueSECKEYPublicKey mPubKey;
884 CryptoBuffer mLabel;
885 uint32_t mStrength;
886 bool mEncrypt;
888 virtual nsresult DoCrypto() override {
889 nsresult rv;
891 if (!mDataIsSet) {
892 return NS_ERROR_DOM_OPERATION_ERR;
895 // Ciphertext is an integer mod the modulus, so it will be
896 // no longer than mStrength octets
897 if (!mResult.SetLength(mStrength, fallible)) {
898 return NS_ERROR_DOM_UNKNOWN_ERR;
901 CK_RSA_PKCS_OAEP_PARAMS oaepParams;
902 oaepParams.source = CKZ_DATA_SPECIFIED;
904 oaepParams.pSourceData = mLabel.Length() ? mLabel.Elements() : nullptr;
905 oaepParams.ulSourceDataLen = mLabel.Length();
907 oaepParams.mgf = mMgfMechanism;
908 oaepParams.hashAlg = mHashMechanism;
910 SECItem param;
911 param.type = siBuffer;
912 param.data = (unsigned char*)&oaepParams;
913 param.len = sizeof(oaepParams);
915 uint32_t outLen = 0;
916 if (mEncrypt) {
917 // PK11_PubEncrypt() checks the plaintext's length and fails if it is too
918 // long to encrypt, i.e. if it is longer than (k - 2hLen - 2) with 'k'
919 // being the length in octets of the RSA modulus n and 'hLen' being the
920 // output length in octets of the chosen hash function.
921 // <https://tools.ietf.org/html/rfc3447#section-7.1>
922 rv = MapSECStatus(PK11_PubEncrypt(
923 mPubKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(), &outLen,
924 mResult.Length(), mData.Elements(), mData.Length(), nullptr));
925 } else {
926 rv = MapSECStatus(PK11_PrivDecrypt(
927 mPrivKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(),
928 &outLen, mResult.Length(), mData.Elements(), mData.Length()));
930 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
932 mResult.TruncateLength(outLen);
933 return NS_OK;
937 class HmacTask : public WebCryptoTask {
938 public:
939 HmacTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
940 const CryptoOperationData& aSignature,
941 const CryptoOperationData& aData, bool aSign)
942 : mMechanism(aKey.Algorithm().Mechanism()), mSign(aSign) {
943 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HMAC);
945 ATTEMPT_BUFFER_INIT(mData, aData);
946 if (!aSign) {
947 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
950 if (!mSymKey.Assign(aKey.GetSymKey())) {
951 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
952 return;
955 // Check that we got a symmetric key
956 if (mSymKey.Length() == 0) {
957 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
958 return;
961 TelemetryAlgorithm telemetryAlg;
962 switch (mMechanism) {
963 case CKM_SHA_1_HMAC:
964 telemetryAlg = TA_HMAC_SHA_1;
965 break;
966 case CKM_SHA224_HMAC:
967 telemetryAlg = TA_HMAC_SHA_224;
968 break;
969 case CKM_SHA256_HMAC:
970 telemetryAlg = TA_HMAC_SHA_256;
971 break;
972 case CKM_SHA384_HMAC:
973 telemetryAlg = TA_HMAC_SHA_384;
974 break;
975 case CKM_SHA512_HMAC:
976 telemetryAlg = TA_HMAC_SHA_512;
977 break;
978 default:
979 telemetryAlg = TA_UNKNOWN;
981 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
984 private:
985 CK_MECHANISM_TYPE mMechanism;
986 CryptoBuffer mSymKey;
987 CryptoBuffer mData;
988 CryptoBuffer mSignature;
989 CryptoBuffer mResult;
990 bool mSign;
992 virtual nsresult DoCrypto() override {
993 // Initialize the output buffer
994 if (!mResult.SetLength(HASH_LENGTH_MAX, fallible)) {
995 return NS_ERROR_DOM_UNKNOWN_ERR;
998 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
999 if (!arena) {
1000 return NS_ERROR_DOM_OPERATION_ERR;
1003 // Import the key
1004 uint32_t outLen;
1005 SECItem keyItem = {siBuffer, nullptr, 0};
1006 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
1007 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
1008 MOZ_ASSERT(slot.get());
1009 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
1010 PK11_OriginUnwrap, CKA_SIGN,
1011 &keyItem, nullptr));
1012 if (!symKey) {
1013 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1016 // Compute the MAC
1017 SECItem param = {siBuffer, nullptr, 0};
1018 UniquePK11Context ctx(
1019 PK11_CreateContextBySymKey(mMechanism, CKA_SIGN, symKey.get(), &param));
1020 if (!ctx.get()) {
1021 return NS_ERROR_DOM_OPERATION_ERR;
1023 nsresult rv = MapSECStatus(PK11_DigestBegin(ctx.get()));
1024 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1025 rv = MapSECStatus(
1026 PK11_DigestOp(ctx.get(), mData.Elements(), mData.Length()));
1027 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1028 rv = MapSECStatus(PK11_DigestFinal(ctx.get(), mResult.Elements(), &outLen,
1029 mResult.Length()));
1030 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1032 mResult.TruncateLength(outLen);
1033 return rv;
1036 // Returns mResult as an ArrayBufferView, or an error
1037 virtual void Resolve() override {
1038 if (mSign) {
1039 // Return the computed MAC
1040 TypedArrayCreator<ArrayBuffer> ret(mResult);
1041 mResultPromise->MaybeResolve(ret);
1042 } else {
1043 // Compare the MAC to the provided signature
1044 // No truncation allowed
1045 bool equal = (mResult.Length() == mSignature.Length());
1046 if (equal) {
1047 int cmp = NSS_SecureMemcmp(mSignature.Elements(), mResult.Elements(),
1048 mSignature.Length());
1049 equal = (cmp == 0);
1051 mResultPromise->MaybeResolve(equal);
1056 class AsymmetricSignVerifyTask : public WebCryptoTask {
1057 public:
1058 AsymmetricSignVerifyTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1059 CryptoKey& aKey,
1060 const CryptoOperationData& aSignature,
1061 const CryptoOperationData& aData, bool aSign)
1062 : mOidTag(SEC_OID_UNKNOWN),
1063 mHashMechanism(UNKNOWN_CK_MECHANISM),
1064 mMgfMechanism(UNKNOWN_CK_MECHANISM),
1065 mPrivKey(aKey.GetPrivateKey()),
1066 mPubKey(aKey.GetPublicKey()),
1067 mSaltLength(0),
1068 mSign(aSign),
1069 mVerified(false),
1070 mAlgorithm(Algorithm::UNKNOWN) {
1071 ATTEMPT_BUFFER_INIT(mData, aData);
1072 if (!aSign) {
1073 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
1076 nsString algName;
1077 nsString hashAlgName;
1078 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1079 if (NS_FAILED(mEarlyRv)) {
1080 return;
1083 if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
1084 mAlgorithm = Algorithm::RSA_PKCS1;
1085 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
1086 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
1087 hashAlgName = aKey.Algorithm().mRsa.mHash.mName;
1088 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1089 mAlgorithm = Algorithm::RSA_PSS;
1090 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_PSS);
1091 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_PSS);
1093 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
1094 hashAlgName = hashAlg.mName;
1095 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
1096 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlgName);
1098 // Check we found appropriate mechanisms.
1099 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
1100 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
1101 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1102 return;
1105 RootedDictionary<RsaPssParams> params(aCx);
1106 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1107 if (NS_FAILED(mEarlyRv)) {
1108 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1109 return;
1112 mSaltLength = params.mSaltLength;
1113 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1114 mAlgorithm = Algorithm::ECDSA;
1115 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDSA);
1116 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
1118 // For ECDSA, the hash name comes from the algorithm parameter
1119 RootedDictionary<EcdsaParams> params(aCx);
1120 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1121 if (NS_FAILED(mEarlyRv)) {
1122 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1123 return;
1126 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashAlgName);
1127 if (NS_FAILED(mEarlyRv)) {
1128 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1129 return;
1131 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
1132 mAlgorithm = Algorithm::ED25519;
1133 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ED25519);
1134 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ED25519);
1135 } else {
1136 // This shouldn't happen; CreateSignVerifyTask shouldn't create
1137 // one of these unless it's for the above algorithms.
1138 MOZ_ASSERT(false);
1141 // Must have a valid algorithm by now.
1142 MOZ_ASSERT(mAlgorithm != Algorithm::UNKNOWN);
1144 // Determine hash algorithm to use.
1145 mOidTag = MapHashAlgorithmNameToOID(hashAlgName);
1147 if (mOidTag == SEC_OID_UNKNOWN && AlgorithmRequiresHashing(mAlgorithm)) {
1148 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1149 return;
1152 // Check that we have the appropriate key
1153 if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
1154 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
1155 return;
1159 private:
1160 SECOidTag mOidTag;
1161 CK_MECHANISM_TYPE mHashMechanism;
1162 CK_MECHANISM_TYPE mMgfMechanism;
1163 UniqueSECKEYPrivateKey mPrivKey;
1164 UniqueSECKEYPublicKey mPubKey;
1165 CryptoBuffer mSignature;
1166 CryptoBuffer mData;
1167 uint32_t mSaltLength;
1168 bool mSign;
1169 bool mVerified;
1171 // The signature algorithm to use.
1172 enum class Algorithm : uint8_t {
1173 ECDSA,
1174 RSA_PKCS1,
1175 RSA_PSS,
1176 ED25519,
1177 UNKNOWN
1179 Algorithm mAlgorithm;
1181 bool AlgorithmRequiresHashing(Algorithm aAlgorithm) {
1182 MOZ_ASSERT(aAlgorithm != Algorithm::UNKNOWN);
1183 /* Currently, only ED25519 does not require hashing.*/
1184 switch (aAlgorithm) {
1185 case Algorithm::ED25519:
1186 return false;
1187 case Algorithm::ECDSA:
1188 case Algorithm::RSA_PKCS1:
1189 case Algorithm::RSA_PSS:
1190 // Impossible
1191 case Algorithm::UNKNOWN:
1192 return true;
1194 /*Also impossible, as all the algorithm options should be managed in the
1195 * switch. */
1196 return true;
1199 virtual nsresult DoCrypto() override {
1200 SECStatus rv;
1201 UniqueSECItem hash;
1203 SECItem* params = nullptr;
1204 CK_MECHANISM_TYPE mech =
1205 PK11_MapSignKeyType(mSign ? mPrivKey->keyType : mPubKey->keyType);
1207 CK_RSA_PKCS_PSS_PARAMS rsaPssParams;
1208 SECItem rsaPssParamsItem = {
1209 siBuffer,
1212 // Set up parameters for RSA-PSS.
1213 if (mAlgorithm == Algorithm::RSA_PSS) {
1214 rsaPssParams.hashAlg = mHashMechanism;
1215 rsaPssParams.mgf = mMgfMechanism;
1216 rsaPssParams.sLen = mSaltLength;
1218 rsaPssParamsItem.data = (unsigned char*)&rsaPssParams;
1219 rsaPssParamsItem.len = sizeof(rsaPssParams);
1220 params = &rsaPssParamsItem;
1222 mech = CKM_RSA_PKCS_PSS;
1225 if (AlgorithmRequiresHashing(mAlgorithm)) {
1226 // Compute digest over given data.
1227 hash.reset(::SECITEM_AllocItem(nullptr, nullptr,
1228 HASH_ResultLenByOidTag(mOidTag)));
1230 if (!hash || !hash->data || hash->len > PR_INT32_MAX) {
1231 return NS_ERROR_DOM_OPERATION_ERR;
1234 rv = PK11_HashBuf(mOidTag, hash->data, mData.Elements(),
1235 static_cast<PRInt32>(mData.Length()));
1236 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1239 // Wrap hash in a digest info template (RSA-PKCS1 only).
1240 if (mAlgorithm == Algorithm::RSA_PKCS1) {
1241 if (!hash) {
1242 return NS_ERROR_DOM_OPERATION_ERR;
1245 UniqueSGNDigestInfo di(
1246 SGN_CreateDigestInfo(mOidTag, hash->data, hash->len));
1247 if (!di) {
1248 return NS_ERROR_DOM_OPERATION_ERR;
1251 // Reuse |hash|.
1252 SECITEM_FreeItem(hash.get(), false);
1253 if (!SEC_ASN1EncodeItem(nullptr, hash.get(), di.get(),
1254 SGN_DigestInfoTemplate)) {
1255 return NS_ERROR_DOM_OPERATION_ERR;
1259 // Allocate SECItem to hold the signature.
1260 uint32_t len = mSign ? PK11_SignatureLen(mPrivKey.get()) : 0;
1261 UniqueSECItem sig(::SECITEM_AllocItem(nullptr, nullptr, len));
1262 if (!sig) {
1263 return NS_ERROR_DOM_OPERATION_ERR;
1266 // Buffer for signature/verification input.
1267 SECItem dataToOperateOn;
1268 if (mSign) {
1269 if (AlgorithmRequiresHashing(mAlgorithm)) {
1270 dataToOperateOn = {siBuffer, hash->data, hash->len};
1271 } else {
1272 dataToOperateOn = {siBuffer, mData.Elements(),
1273 static_cast<unsigned int>(mData.Length())};
1276 // Sign the hash.
1277 rv = PK11_SignWithMechanism(mPrivKey.get(), mech, params, sig.get(),
1278 &dataToOperateOn);
1279 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1280 ATTEMPT_BUFFER_ASSIGN(mSignature, sig.get());
1281 } else {
1282 if (AlgorithmRequiresHashing(mAlgorithm)) {
1283 dataToOperateOn = {siBuffer, hash->data, hash->len};
1284 } else {
1285 dataToOperateOn = {siBuffer, mData.Elements(),
1286 static_cast<unsigned int>(mData.Length())};
1289 // Copy the given signature to the SECItem.
1290 if (!mSignature.ToSECItem(nullptr, sig.get())) {
1291 return NS_ERROR_DOM_OPERATION_ERR;
1294 // Verify the signature.
1295 rv = PK11_VerifyWithMechanism(mPubKey.get(), mech, params, sig.get(),
1296 &dataToOperateOn, nullptr);
1297 mVerified = NS_SUCCEEDED(MapSECStatus(rv));
1300 return NS_OK;
1303 virtual void Resolve() override {
1304 if (mSign) {
1305 TypedArrayCreator<ArrayBuffer> ret(mSignature);
1306 mResultPromise->MaybeResolve(ret);
1307 } else {
1308 mResultPromise->MaybeResolve(mVerified);
1313 class DigestTask : public ReturnArrayBufferViewTask {
1314 public:
1315 DigestTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1316 const CryptoOperationData& aData) {
1317 ATTEMPT_BUFFER_INIT(mData, aData);
1319 nsString algName;
1320 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1321 if (NS_FAILED(mEarlyRv)) {
1322 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1323 return;
1326 TelemetryAlgorithm telemetryAlg;
1327 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
1328 telemetryAlg = TA_SHA_1;
1329 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
1330 telemetryAlg = TA_SHA_224;
1331 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
1332 telemetryAlg = TA_SHA_256;
1333 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
1334 telemetryAlg = TA_SHA_384;
1335 } else {
1336 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1337 return;
1339 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
1340 mOidTag = MapHashAlgorithmNameToOID(algName);
1343 private:
1344 SECOidTag mOidTag;
1345 CryptoBuffer mData;
1347 virtual nsresult DoCrypto() override {
1348 // Resize the result buffer
1349 uint32_t hashLen = HASH_ResultLenByOidTag(mOidTag);
1350 if (!mResult.SetLength(hashLen, fallible)) {
1351 return NS_ERROR_DOM_UNKNOWN_ERR;
1354 // Compute the hash
1355 nsresult rv = MapSECStatus(PK11_HashBuf(mOidTag, mResult.Elements(),
1356 mData.Elements(), mData.Length()));
1357 if (NS_FAILED(rv)) {
1358 return NS_ERROR_DOM_UNKNOWN_ERR;
1361 return rv;
1365 class ImportKeyTask : public WebCryptoTask {
1366 public:
1367 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1368 const ObjectOrString& aAlgorithm, bool aExtractable,
1369 const Sequence<nsString>& aKeyUsages) {
1370 mFormat = aFormat;
1371 mDataIsSet = false;
1372 mDataIsJwk = false;
1374 // This stuff pretty much always happens, so we'll do it here
1375 mKey = new CryptoKey(aGlobal);
1376 mKey->SetExtractable(aExtractable);
1377 mKey->ClearUsages();
1378 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
1379 mEarlyRv = mKey->AddUsage(aKeyUsages[i]);
1380 if (NS_FAILED(mEarlyRv)) {
1381 return;
1385 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
1386 if (NS_FAILED(mEarlyRv)) {
1387 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1388 return;
1392 static bool JwkCompatible(const JsonWebKey& aJwk, const CryptoKey* aKey) {
1393 // Check 'alg'
1394 if (!aJwk.mKty.EqualsLiteral(JWK_TYPE_OKP) &&
1395 !(aJwk.mKty.EqualsLiteral(JWK_TYPE_EC) &&
1396 aKey->Algorithm().Mechanism() == CKM_ECDH1_DERIVE) &&
1397 aJwk.mAlg.WasPassed() &&
1398 aJwk.mAlg.Value() != aKey->Algorithm().JwkAlg()) {
1399 return false;
1402 // Check 'ext'
1403 if (aKey->Extractable() && aJwk.mExt.WasPassed() && !aJwk.mExt.Value()) {
1404 return false;
1407 // Check 'key_ops'
1408 if (aJwk.mKey_ops.WasPassed()) {
1409 nsTArray<nsString> usages;
1410 aKey->GetUsages(usages);
1411 for (size_t i = 0; i < usages.Length(); ++i) {
1412 if (!aJwk.mKey_ops.Value().Contains(usages[i])) {
1413 return false;
1418 // Individual algorithms may still have to check 'use'
1419 return true;
1422 void SetKeyData(JSContext* aCx, JS::Handle<JSObject*> aKeyData) {
1423 mDataIsJwk = false;
1425 // Try ArrayBuffer
1426 RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
1427 if (ab.Init(aKeyData)) {
1428 if (!mKeyData.Assign(ab)) {
1429 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1431 return;
1434 // Try ArrayBufferView
1435 RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
1436 if (abv.Init(aKeyData)) {
1437 if (!mKeyData.Assign(abv)) {
1438 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1440 return;
1443 // Try JWK
1444 ClearException ce(aCx);
1445 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aKeyData));
1446 if (!mJwk.Init(aCx, value)) {
1447 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1448 return;
1451 mDataIsJwk = true;
1454 void SetKeyDataMaybeParseJWK(const CryptoBuffer& aKeyData) {
1455 if (!mKeyData.Assign(aKeyData)) {
1456 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1457 return;
1460 mDataIsJwk = false;
1462 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1463 nsDependentCSubstring utf8(
1464 (const char*)mKeyData.Elements(),
1465 (const char*)(mKeyData.Elements() + mKeyData.Length()));
1466 if (!IsUtf8(utf8)) {
1467 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1468 return;
1471 nsString json = NS_ConvertUTF8toUTF16(utf8);
1472 if (!mJwk.Init(json)) {
1473 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1474 return;
1477 mDataIsJwk = true;
1481 void SetRawKeyData(const CryptoBuffer& aKeyData) {
1482 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1483 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1484 return;
1487 if (!mKeyData.Assign(aKeyData)) {
1488 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1489 return;
1492 mDataIsJwk = false;
1495 protected:
1496 nsString mFormat;
1497 RefPtr<CryptoKey> mKey;
1498 CryptoBuffer mKeyData;
1499 bool mDataIsSet;
1500 bool mDataIsJwk;
1501 JsonWebKey mJwk;
1502 nsString mAlgName;
1504 private:
1505 virtual void Resolve() override { mResultPromise->MaybeResolve(mKey); }
1507 virtual void Cleanup() override { mKey = nullptr; }
1510 class ImportSymmetricKeyTask : public ImportKeyTask {
1511 public:
1512 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1513 const nsAString& aFormat,
1514 const ObjectOrString& aAlgorithm, bool aExtractable,
1515 const Sequence<nsString>& aKeyUsages) {
1516 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1519 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1520 const nsAString& aFormat,
1521 const JS::Handle<JSObject*> aKeyData,
1522 const ObjectOrString& aAlgorithm, bool aExtractable,
1523 const Sequence<nsString>& aKeyUsages) {
1524 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1525 if (NS_FAILED(mEarlyRv)) {
1526 return;
1529 SetKeyData(aCx, aKeyData);
1530 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1531 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1532 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1533 return;
1537 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1538 const ObjectOrString& aAlgorithm, bool aExtractable,
1539 const Sequence<nsString>& aKeyUsages) {
1540 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1541 aKeyUsages);
1542 if (NS_FAILED(mEarlyRv)) {
1543 return;
1546 // This task only supports raw and JWK format.
1547 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1548 !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1549 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1550 return;
1553 // If this is an HMAC key, import the hash name
1554 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1555 RootedDictionary<HmacImportParams> params(aCx);
1556 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1557 if (NS_FAILED(mEarlyRv)) {
1558 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1559 return;
1561 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1562 if (NS_FAILED(mEarlyRv)) {
1563 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1564 return;
1569 virtual nsresult BeforeCrypto() override {
1570 nsresult rv;
1571 // If we're doing a JWK import, import the key data
1572 if (mDataIsJwk) {
1573 if (!mJwk.mK.WasPassed()) {
1574 return NS_ERROR_DOM_DATA_ERR;
1577 // Import the key material
1578 rv = mKeyData.FromJwkBase64(mJwk.mK.Value());
1579 if (NS_FAILED(rv)) {
1580 return NS_ERROR_DOM_DATA_ERR;
1583 // Check that we have valid key data.
1584 if (mKeyData.Length() == 0 &&
1585 (!mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) &&
1586 !mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HKDF))) {
1587 return NS_ERROR_DOM_DATA_ERR;
1590 // Construct an appropriate KeyAlgorithm,
1591 // and verify that usages are appropriate
1592 if (mKeyData.Length() > UINT32_MAX / 8) {
1593 return NS_ERROR_DOM_DATA_ERR;
1595 uint32_t length = 8 * mKeyData.Length(); // bytes to bits
1596 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
1597 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
1598 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
1599 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
1600 if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
1601 CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1602 return NS_ERROR_DOM_SYNTAX_ERR;
1605 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) &&
1606 mKey->HasUsageOtherThan(CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1607 return NS_ERROR_DOM_SYNTAX_ERR;
1610 if ((length != 128) && (length != 192) && (length != 256)) {
1611 return NS_ERROR_DOM_DATA_ERR;
1613 mKey->Algorithm().MakeAes(mAlgName, length);
1615 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1616 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_ENC)) {
1617 return NS_ERROR_DOM_DATA_ERR;
1619 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
1620 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
1621 if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY |
1622 CryptoKey::DERIVEBITS)) {
1623 return NS_ERROR_DOM_SYNTAX_ERR;
1625 mKey->Algorithm().MakeKDF(mAlgName);
1627 if (mDataIsJwk && mJwk.mUse.WasPassed()) {
1628 // There is not a 'use' value consistent with PBKDF or HKDF
1629 return NS_ERROR_DOM_DATA_ERR;
1631 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1632 if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) {
1633 return NS_ERROR_DOM_SYNTAX_ERR;
1636 mKey->Algorithm().MakeHmac(length, mHashName);
1638 if (mKey->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM) {
1639 return NS_ERROR_DOM_SYNTAX_ERR;
1642 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1643 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_SIG)) {
1644 return NS_ERROR_DOM_DATA_ERR;
1646 } else {
1647 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1650 if (!mKey->HasAnyUsage()) {
1651 return NS_ERROR_DOM_SYNTAX_ERR;
1654 if (NS_FAILED(mKey->SetSymKey(mKeyData))) {
1655 return NS_ERROR_DOM_OPERATION_ERR;
1658 mKey->SetType(CryptoKey::SECRET);
1660 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1661 return NS_ERROR_DOM_DATA_ERR;
1664 mEarlyComplete = true;
1665 return NS_OK;
1668 private:
1669 nsString mHashName;
1672 class ImportRsaKeyTask : public ImportKeyTask {
1673 public:
1674 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1675 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1676 bool aExtractable, const Sequence<nsString>& aKeyUsages)
1677 : mModulusLength(0) {
1678 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1681 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1682 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1683 const ObjectOrString& aAlgorithm, bool aExtractable,
1684 const Sequence<nsString>& aKeyUsages)
1685 : mModulusLength(0) {
1686 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1687 if (NS_FAILED(mEarlyRv)) {
1688 return;
1691 SetKeyData(aCx, aKeyData);
1692 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1693 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1694 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1695 return;
1699 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1700 const ObjectOrString& aAlgorithm, bool aExtractable,
1701 const Sequence<nsString>& aKeyUsages) {
1702 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1703 aKeyUsages);
1704 if (NS_FAILED(mEarlyRv)) {
1705 return;
1708 // If this is RSA with a hash, cache the hash name
1709 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1710 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
1711 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1712 RootedDictionary<RsaHashedImportParams> params(aCx);
1713 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1714 if (NS_FAILED(mEarlyRv)) {
1715 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1716 return;
1719 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1720 if (NS_FAILED(mEarlyRv)) {
1721 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1722 return;
1726 // Check support for the algorithm and hash names
1727 CK_MECHANISM_TYPE mech1 = MapAlgorithmNameToMechanism(mAlgName);
1728 CK_MECHANISM_TYPE mech2 = MapAlgorithmNameToMechanism(mHashName);
1729 if ((mech1 == UNKNOWN_CK_MECHANISM) || (mech2 == UNKNOWN_CK_MECHANISM)) {
1730 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1731 return;
1735 private:
1736 nsString mHashName;
1737 uint32_t mModulusLength;
1738 CryptoBuffer mPublicExponent;
1740 virtual nsresult DoCrypto() override {
1741 // Import the key data itself
1742 UniqueSECKEYPublicKey pubKey;
1743 UniqueSECKEYPrivateKey privKey;
1744 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1745 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1746 !mJwk.mD.WasPassed())) {
1747 // Public key import
1748 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1749 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1750 } else {
1751 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1754 if (!pubKey) {
1755 return NS_ERROR_DOM_DATA_ERR;
1758 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1759 return NS_ERROR_DOM_OPERATION_ERR;
1762 mKey->SetType(CryptoKey::PUBLIC);
1763 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) ||
1764 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1765 mJwk.mD.WasPassed())) {
1766 // Private key import
1767 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1768 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
1769 } else {
1770 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1773 if (!privKey) {
1774 return NS_ERROR_DOM_DATA_ERR;
1777 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1778 return NS_ERROR_DOM_OPERATION_ERR;
1781 mKey->SetType(CryptoKey::PRIVATE);
1782 pubKey = UniqueSECKEYPublicKey(SECKEY_ConvertToPublicKey(privKey.get()));
1783 if (!pubKey) {
1784 return NS_ERROR_DOM_UNKNOWN_ERR;
1786 } else {
1787 // Invalid key format
1788 return NS_ERROR_DOM_SYNTAX_ERR;
1791 if (pubKey->keyType != rsaKey) {
1792 return NS_ERROR_DOM_DATA_ERR;
1794 // Extract relevant information from the public key
1795 mModulusLength = 8 * pubKey->u.rsa.modulus.len;
1796 if (!mPublicExponent.Assign(&pubKey->u.rsa.publicExponent)) {
1797 return NS_ERROR_DOM_OPERATION_ERR;
1800 return NS_OK;
1803 virtual nsresult AfterCrypto() override {
1804 // Check permissions for the requested operation
1805 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
1806 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1807 mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) ||
1808 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1809 mKey->HasUsageOtherThan(CryptoKey::DECRYPT |
1810 CryptoKey::UNWRAPKEY))) {
1811 return NS_ERROR_DOM_SYNTAX_ERR;
1813 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1814 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1815 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1816 mKey->HasUsageOtherThan(CryptoKey::VERIFY)) ||
1817 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1818 mKey->HasUsageOtherThan(CryptoKey::SIGN))) {
1819 return NS_ERROR_DOM_SYNTAX_ERR;
1823 if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
1824 return NS_ERROR_DOM_SYNTAX_ERR;
1827 // Set an appropriate KeyAlgorithm
1828 if (!mKey->Algorithm().MakeRsa(mAlgName, mModulusLength, mPublicExponent,
1829 mHashName)) {
1830 return NS_ERROR_DOM_OPERATION_ERR;
1833 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1834 return NS_ERROR_DOM_DATA_ERR;
1837 return NS_OK;
1841 class ImportEcKeyTask : public ImportKeyTask {
1842 public:
1843 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1844 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1845 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
1846 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1849 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1850 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1851 const ObjectOrString& aAlgorithm, bool aExtractable,
1852 const Sequence<nsString>& aKeyUsages) {
1853 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1854 if (NS_FAILED(mEarlyRv)) {
1855 return;
1858 SetKeyData(aCx, aKeyData);
1859 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1862 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1863 const ObjectOrString& aAlgorithm, bool aExtractable,
1864 const Sequence<nsString>& aKeyUsages) {
1865 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1866 aKeyUsages);
1867 if (NS_FAILED(mEarlyRv)) {
1868 return;
1871 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
1872 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1873 RootedDictionary<EcKeyImportParams> params(aCx);
1874 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1875 if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
1876 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1877 return;
1880 if (!NormalizeToken(params.mNamedCurve.Value(), mNamedCurve)) {
1881 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1882 return;
1887 private:
1888 nsString mNamedCurve;
1890 virtual nsresult DoCrypto() override {
1891 // Import the key data itself
1892 UniqueSECKEYPublicKey pubKey;
1893 UniqueSECKEYPrivateKey privKey;
1895 if ((mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1896 mJwk.mD.WasPassed()) ||
1897 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1898 // Private key import
1899 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1900 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1901 if (!privKey) {
1902 return NS_ERROR_DOM_DATA_ERR;
1904 } else {
1905 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
1906 if (!privKey) {
1907 return NS_ERROR_DOM_DATA_ERR;
1910 ScopedAutoSECItem ecParams;
1911 if (PK11_ReadRawAttribute(PK11_TypePrivKey, privKey.get(),
1912 CKA_EC_PARAMS, &ecParams) != SECSuccess) {
1913 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1916 SECOidTag tag;
1917 if (!FindOIDTagForEncodedParameters(&ecParams, &tag)) {
1918 return NS_ERROR_DOM_DATA_ERR;
1921 // Find a matching and supported named curve.
1922 if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
1923 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1927 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1928 return NS_ERROR_DOM_OPERATION_ERR;
1931 mKey->SetType(CryptoKey::PRIVATE);
1932 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
1933 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1934 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1935 !mJwk.mD.WasPassed())) {
1936 // Public key import
1937 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1938 pubKey = CryptoKey::PublicECKeyFromRaw(mKeyData, mNamedCurve);
1939 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1940 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1941 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1942 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1943 } else {
1944 MOZ_ASSERT(false);
1947 if (!pubKey) {
1948 return NS_ERROR_DOM_DATA_ERR;
1951 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1952 if (pubKey->keyType != ecKey) {
1953 return NS_ERROR_DOM_DATA_ERR;
1955 if (!CheckEncodedParameters(&pubKey->u.ec.DEREncodedParams)) {
1956 return NS_ERROR_DOM_OPERATION_ERR;
1959 SECOidTag tag;
1960 if (!FindOIDTagForEncodedParameters(&pubKey->u.ec.DEREncodedParams,
1961 &tag)) {
1962 return NS_ERROR_DOM_DATA_ERR;
1965 // Find a matching and supported named curve.
1966 if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
1967 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1971 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1972 return NS_ERROR_DOM_OPERATION_ERR;
1975 mKey->SetType(CryptoKey::PUBLIC);
1976 } else {
1977 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1980 // Checking the 'crv' consistency
1981 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1982 // the curve stated in 'crv field'
1983 nsString namedCurveFromCrv;
1984 if (!NormalizeToken(mJwk.mCrv.Value(), namedCurveFromCrv)) {
1985 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1988 // https://w3c.github.io/webcrypto/#ecdh-operations
1989 // https://w3c.github.io/webcrypto/#ecdsa-operations
1990 // If namedCurve is not equal to the namedCurve member of
1991 // normalizedAlgorithm (mNamedCurve in our case), throw a DataError.
1992 if (!mNamedCurve.Equals(namedCurveFromCrv)) {
1993 return NS_ERROR_DOM_DATA_ERR;
1996 return NS_OK;
1999 virtual nsresult AfterCrypto() override {
2000 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
2001 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
2002 privateAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
2003 publicAllowedUsages = 0;
2004 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2005 privateAllowedUsages = CryptoKey::SIGN;
2006 publicAllowedUsages = CryptoKey::VERIFY;
2009 // Check permissions for the requested operation
2010 if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
2011 mKey->HasUsageOtherThan(privateAllowedUsages)) ||
2012 (mKey->GetKeyType() == CryptoKey::PUBLIC &&
2013 mKey->HasUsageOtherThan(publicAllowedUsages))) {
2014 return NS_ERROR_DOM_SYNTAX_ERR;
2017 if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
2018 return NS_ERROR_DOM_SYNTAX_ERR;
2021 mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2023 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
2024 return NS_ERROR_DOM_DATA_ERR;
2027 return NS_OK;
2031 class ImportOKPKeyTask : public ImportKeyTask {
2032 public:
2033 ImportOKPKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2034 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
2035 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
2036 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
2039 ImportOKPKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2040 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
2041 const ObjectOrString& aAlgorithm, bool aExtractable,
2042 const Sequence<nsString>& aKeyUsages) {
2043 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
2044 if (NS_FAILED(mEarlyRv)) {
2045 return;
2048 SetKeyData(aCx, aKeyData);
2049 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
2052 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
2053 const ObjectOrString& aAlgorithm, bool aExtractable,
2054 const Sequence<nsString>& aKeyUsages) {
2055 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
2056 aKeyUsages);
2057 if (NS_FAILED(mEarlyRv)) {
2058 return;
2061 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
2062 nsString paramsAlgName;
2063 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, paramsAlgName);
2064 if (NS_FAILED(mEarlyRv)) {
2065 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2066 return;
2069 nsString algName;
2070 if (!NormalizeToken(paramsAlgName, algName)) {
2071 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2072 return;
2075 // Construct an appropriate KeyAlgorithm
2076 if (algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
2077 mNamedCurve.AssignLiteral(WEBCRYPTO_NAMED_CURVE_ED25519);
2078 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
2079 mNamedCurve.AssignLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519);
2080 } else {
2081 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2082 return;
2087 private:
2088 nsString mNamedCurve;
2090 virtual nsresult DoCrypto() override {
2091 // Import the key data itself
2092 UniqueSECKEYPublicKey pubKey;
2093 UniqueSECKEYPrivateKey privKey;
2095 if ((mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
2096 mJwk.mD.WasPassed()) ||
2097 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
2098 // Private key import
2099 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2100 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
2101 if (!privKey) {
2102 return NS_ERROR_DOM_DATA_ERR;
2104 } else {
2105 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
2106 if (!privKey) {
2107 return NS_ERROR_DOM_DATA_ERR;
2110 ScopedAutoSECItem ecParams;
2111 if (PK11_ReadRawAttribute(PK11_TypePrivKey, privKey.get(),
2112 CKA_EC_PARAMS, &ecParams) != SECSuccess) {
2113 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2116 SECOidTag tag;
2117 if (!FindOIDTagForEncodedParameters(&ecParams, &tag)) {
2118 return NS_ERROR_DOM_DATA_ERR;
2121 // Find a matching and supported named curve.
2122 if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
2123 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2127 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
2128 return NS_ERROR_DOM_OPERATION_ERR;
2131 mKey->SetType(CryptoKey::PRIVATE);
2132 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
2133 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
2134 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
2135 !mJwk.mD.WasPassed())) {
2136 // Public key import
2137 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
2138 pubKey = CryptoKey::PublicOKPKeyFromRaw(mKeyData, mNamedCurve);
2139 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
2140 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
2141 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2142 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
2143 } else {
2144 MOZ_ASSERT(false);
2147 if (!pubKey) {
2148 return NS_ERROR_DOM_DATA_ERR;
2151 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
2152 if (pubKey->keyType != edKey && pubKey->keyType != ecMontKey) {
2153 return NS_ERROR_DOM_DATA_ERR;
2155 if (!CheckEncodedParameters(&pubKey->u.ec.DEREncodedParams)) {
2156 return NS_ERROR_DOM_OPERATION_ERR;
2159 SECOidTag tag;
2160 if (!FindOIDTagForEncodedParameters(&pubKey->u.ec.DEREncodedParams,
2161 &tag)) {
2162 return NS_ERROR_DOM_OPERATION_ERR;
2165 // Find a matching and supported named curve.
2166 if (!MapOIDTagToNamedCurve(tag, mNamedCurve)) {
2167 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2171 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
2172 return NS_ERROR_DOM_OPERATION_ERR;
2175 mKey->SetType(CryptoKey::PUBLIC);
2176 } else {
2177 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2180 // Extract 'crv' parameter from JWKs.
2181 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2182 if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) {
2183 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2187 return NS_OK;
2190 virtual nsresult AfterCrypto() override {
2191 // Only Ed25519 is supported.
2192 uint32_t privateAllowedUsages = 0;
2193 uint32_t publicAllowedUsages = 0;
2195 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
2196 privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
2197 publicAllowedUsages = 0;
2198 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
2199 privateAllowedUsages = CryptoKey::SIGN;
2200 publicAllowedUsages = CryptoKey::VERIFY;
2203 // Check permissions for the requested operation
2204 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
2205 mKey->HasUsageOtherThan(publicAllowedUsages))) {
2206 return NS_ERROR_DOM_SYNTAX_ERR;
2209 if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
2210 mKey->HasUsageOtherThan(privateAllowedUsages))) {
2211 return NS_ERROR_DOM_SYNTAX_ERR;
2214 if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
2215 return NS_ERROR_DOM_SYNTAX_ERR;
2218 mKey->Algorithm().MakeOKP(mAlgName);
2220 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
2221 return NS_ERROR_DOM_DATA_ERR;
2224 return NS_OK;
2228 class ExportKeyTask : public WebCryptoTask {
2229 public:
2230 ExportKeyTask(const nsAString& aFormat, CryptoKey& aKey)
2231 : mFormat(aFormat),
2232 mPrivateKey(aKey.GetPrivateKey()),
2233 mPublicKey(aKey.GetPublicKey()),
2234 mKeyType(aKey.GetKeyType()),
2235 mExtractable(aKey.Extractable()),
2236 mAlg(aKey.Algorithm().JwkAlg()) {
2237 aKey.GetUsages(mKeyUsages);
2239 if (!mSymKey.Assign(aKey.GetSymKey())) {
2240 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2241 return;
2245 protected:
2246 nsString mFormat;
2247 CryptoBuffer mSymKey;
2248 UniqueSECKEYPrivateKey mPrivateKey;
2249 UniqueSECKEYPublicKey mPublicKey;
2250 CryptoKey::KeyType mKeyType;
2251 bool mExtractable;
2252 nsString mAlg;
2253 nsTArray<nsString> mKeyUsages;
2254 CryptoBuffer mResult;
2255 JsonWebKey mJwk;
2257 private:
2258 virtual nsresult DoCrypto() override {
2259 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
2260 if (mPublicKey && mPublicKey->keyType == dhKey) {
2261 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2264 if (mPublicKey &&
2265 (mPublicKey->keyType == ecKey || mPublicKey->keyType == edKey ||
2266 mPublicKey->keyType == ecMontKey)) {
2267 nsresult rv = CryptoKey::PublicECKeyToRaw(mPublicKey.get(), mResult);
2268 if (NS_FAILED(rv)) {
2269 return NS_ERROR_DOM_OPERATION_ERR;
2271 return NS_OK;
2274 if (!mResult.Assign(mSymKey)) {
2275 return NS_ERROR_OUT_OF_MEMORY;
2277 if (mResult.Length() == 0) {
2278 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2281 return NS_OK;
2282 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
2283 if (!mPrivateKey) {
2284 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2287 switch (mPrivateKey->keyType) {
2288 case rsaKey:
2289 case edKey:
2290 case ecKey:
2291 case ecMontKey: {
2292 nsresult rv =
2293 CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), mResult);
2294 if (NS_FAILED(rv)) {
2295 return NS_ERROR_DOM_OPERATION_ERR;
2297 return NS_OK;
2299 default:
2300 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2302 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
2303 if (!mPublicKey) {
2304 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2307 return CryptoKey::PublicKeyToSpki(mPublicKey.get(), mResult);
2308 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2309 if (mKeyType == CryptoKey::SECRET) {
2310 nsString k;
2311 nsresult rv = mSymKey.ToJwkBase64(k);
2312 if (NS_FAILED(rv)) {
2313 return NS_ERROR_DOM_OPERATION_ERR;
2315 mJwk.mK.Construct(k);
2316 mJwk.mKty = NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_SYMMETRIC);
2317 } else if (mKeyType == CryptoKey::PUBLIC) {
2318 if (!mPublicKey) {
2319 return NS_ERROR_DOM_UNKNOWN_ERR;
2322 nsresult rv = CryptoKey::PublicKeyToJwk(mPublicKey.get(), mJwk);
2323 if (NS_FAILED(rv)) {
2324 return NS_ERROR_DOM_OPERATION_ERR;
2326 } else if (mKeyType == CryptoKey::PRIVATE) {
2327 if (!mPrivateKey) {
2328 return NS_ERROR_DOM_UNKNOWN_ERR;
2331 nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey.get(), mJwk);
2332 if (NS_FAILED(rv)) {
2333 return NS_ERROR_DOM_OPERATION_ERR;
2337 if (!mAlg.IsEmpty()) {
2338 mJwk.mAlg.Construct(mAlg);
2341 mJwk.mExt.Construct(mExtractable);
2343 mJwk.mKey_ops.Construct();
2344 if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
2345 return NS_ERROR_OUT_OF_MEMORY;
2348 return NS_OK;
2351 return NS_ERROR_DOM_SYNTAX_ERR;
2354 // Returns mResult as an ArrayBufferView or JWK, as appropriate
2355 virtual void Resolve() override {
2356 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2357 mResultPromise->MaybeResolve(mJwk);
2358 return;
2361 TypedArrayCreator<ArrayBuffer> ret(mResult);
2362 mResultPromise->MaybeResolve(ret);
2366 class GenerateSymmetricKeyTask : public WebCryptoTask {
2367 public:
2368 GenerateSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2369 const ObjectOrString& aAlgorithm, bool aExtractable,
2370 const Sequence<nsString>& aKeyUsages) {
2371 // Create an empty key and set easy attributes
2372 mKey = new CryptoKey(aGlobal);
2373 mKey->SetExtractable(aExtractable);
2374 mKey->SetType(CryptoKey::SECRET);
2376 // Extract algorithm name
2377 nsString algName;
2378 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
2379 if (NS_FAILED(mEarlyRv)) {
2380 return;
2383 // Construct an appropriate KeyAlorithm
2384 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2385 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2386 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2387 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
2388 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aAlgorithm, mLength);
2389 if (NS_FAILED(mEarlyRv)) {
2390 return;
2392 mKey->Algorithm().MakeAes(algName, mLength);
2394 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2395 RootedDictionary<HmacKeyGenParams> params(aCx);
2396 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2397 if (NS_FAILED(mEarlyRv)) {
2398 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2399 return;
2402 nsString hashName;
2403 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2404 if (NS_FAILED(mEarlyRv)) {
2405 return;
2408 if (params.mLength.WasPassed()) {
2409 mLength = params.mLength.Value();
2410 } else {
2411 mLength = MapHashAlgorithmNameToBlockSize(hashName);
2414 if (mLength == 0) {
2415 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2416 return;
2419 mKey->Algorithm().MakeHmac(mLength, hashName);
2420 } else {
2421 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2422 return;
2425 // Add key usages
2426 mKey->ClearUsages();
2427 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2428 mEarlyRv = mKey->AddAllowedUsageIntersecting(aKeyUsages[i], algName);
2429 if (NS_FAILED(mEarlyRv)) {
2430 return;
2433 if (!mKey->HasAnyUsage()) {
2434 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2435 return;
2438 mLength = mLength >> 3; // bits to bytes
2439 mMechanism = mKey->Algorithm().Mechanism();
2440 // SetSymKey done in Resolve, after we've done the keygen
2443 private:
2444 RefPtr<CryptoKey> mKey;
2445 size_t mLength;
2446 CK_MECHANISM_TYPE mMechanism;
2447 CryptoBuffer mKeyData;
2449 virtual nsresult DoCrypto() override {
2450 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2451 MOZ_ASSERT(slot.get());
2453 UniquePK11SymKey symKey(
2454 PK11_KeyGen(slot.get(), mMechanism, nullptr, mLength, nullptr));
2455 if (!symKey) {
2456 return NS_ERROR_DOM_UNKNOWN_ERR;
2459 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2460 if (NS_FAILED(rv)) {
2461 return NS_ERROR_DOM_UNKNOWN_ERR;
2464 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2465 // just refers to a buffer managed by symKey. The assignment copies the
2466 // data, so mKeyData manages one copy, while symKey manages another.
2467 ATTEMPT_BUFFER_ASSIGN(mKeyData, PK11_GetKeyData(symKey.get()));
2468 return NS_OK;
2471 virtual void Resolve() override {
2472 if (NS_SUCCEEDED(mKey->SetSymKey(mKeyData))) {
2473 mResultPromise->MaybeResolve(mKey);
2474 } else {
2475 mResultPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
2479 virtual void Cleanup() override { mKey = nullptr; }
2482 class DeriveX25519BitsTask : public ReturnArrayBufferViewTask {
2483 public:
2484 DeriveX25519BitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2485 CryptoKey& aKey, const Nullable<uint32_t>& aLength)
2486 : mLength(aLength), mPrivKey(aKey.GetPrivateKey()) {
2487 Init(aCx, aAlgorithm, aKey);
2490 DeriveX25519BitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2491 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2492 : mPrivKey(aKey.GetPrivateKey()) {
2493 Init(aCx, aAlgorithm, aKey);
2496 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey) {
2497 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_X25519);
2498 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_X25519);
2500 // Check that we have a private key.
2501 if (!mPrivKey) {
2502 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2503 return;
2506 // If specified, length must be a multiple of 8.
2507 if (!mLength.IsNull()) {
2508 if (mLength.Value() % 8) {
2509 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2510 return;
2512 mLength.SetValue(mLength.Value() >> 3); // bits to bytes
2515 // Retrieve the peer's public key.
2516 RootedDictionary<EcdhKeyDeriveParams> params(aCx);
2517 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2519 if (NS_FAILED(mEarlyRv)) {
2520 /* The returned code is installed by Coerce function. */
2521 return;
2524 CHECK_KEY_ALGORITHM(params.mPublic->Algorithm(), WEBCRYPTO_ALG_X25519);
2526 CryptoKey* publicKey = params.mPublic;
2527 mPubKey = publicKey->GetPublicKey();
2528 if (!mPubKey) {
2529 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2530 return;
2534 private:
2535 Nullable<uint32_t> mLength;
2536 UniqueSECKEYPrivateKey mPrivKey;
2537 UniqueSECKEYPublicKey mPubKey;
2539 virtual nsresult DoCrypto() override {
2540 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2541 // derived symmetric key and don't matter because we ignore them anyway.
2543 // Derive Bits requires checking that the generated key is not all-zero
2544 // value. See:
2545 // https://wicg.github.io/webcrypto-secure-curves/#x25519-operations This
2546 // step is performed internally inside PK11_PubDeriveWithKDF function.
2547 UniquePK11SymKey symKey(
2548 PK11_PubDeriveWithKDF(mPrivKey.get(), mPubKey.get(), PR_FALSE, nullptr,
2549 nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC,
2550 CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr));
2552 if (!symKey.get()) {
2553 return NS_ERROR_DOM_OPERATION_ERR;
2556 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2557 if (NS_FAILED(rv)) {
2558 return NS_ERROR_DOM_OPERATION_ERR;
2561 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2562 // just refers to a buffer managed by symKey. The assignment copies the
2563 // data, so mResult manages one copy, while symKey manages another.
2564 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2566 if (!mLength.IsNull()) {
2567 if (mLength.Value() > mResult.Length()) {
2568 return NS_ERROR_DOM_OPERATION_ERR;
2570 if (!mResult.SetLength(mLength.Value(), fallible)) {
2571 return NS_ERROR_DOM_UNKNOWN_ERR;
2575 return NS_OK;
2579 GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask(
2580 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
2581 bool aExtractable, const Sequence<nsString>& aKeyUsages)
2582 : mKeyPair(new CryptoKeyPair()),
2583 mMechanism(CKM_INVALID_MECHANISM),
2584 mRsaParams(),
2585 mDhParams() {
2586 mArena = UniquePLArenaPool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2587 if (!mArena) {
2588 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
2589 return;
2592 // Create an empty key pair and set easy attributes
2593 mKeyPair->mPrivateKey = new CryptoKey(aGlobal);
2594 mKeyPair->mPublicKey = new CryptoKey(aGlobal);
2596 // Extract algorithm name
2597 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
2598 if (NS_FAILED(mEarlyRv)) {
2599 return;
2602 // Construct an appropriate KeyAlorithm
2603 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
2604 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2605 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
2606 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
2607 RootedDictionary<RsaHashedKeyGenParams> params(aCx);
2608 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2609 if (NS_FAILED(mEarlyRv)) {
2610 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2611 return;
2614 // Pull relevant info
2615 uint32_t modulusLength = params.mModulusLength;
2616 CryptoBuffer publicExponent;
2617 ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
2618 nsString hashName;
2619 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2620 if (NS_FAILED(mEarlyRv)) {
2621 return;
2624 // Create algorithm
2625 if (!mKeyPair->mPublicKey->Algorithm().MakeRsa(mAlgName, modulusLength,
2626 publicExponent, hashName)) {
2627 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2628 return;
2630 if (!mKeyPair->mPrivateKey->Algorithm().MakeRsa(mAlgName, modulusLength,
2631 publicExponent, hashName)) {
2632 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2633 return;
2635 mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
2637 // Set up params struct
2638 mRsaParams.keySizeInBits = modulusLength;
2639 bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
2640 if (!converted) {
2641 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2642 return;
2644 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
2645 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2646 RootedDictionary<EcKeyGenParams> params(aCx);
2647 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2648 if (NS_FAILED(mEarlyRv)) {
2649 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2650 return;
2653 if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
2654 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2655 return;
2658 // Create algorithm.
2659 mKeyPair->mPublicKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2660 mKeyPair->mPrivateKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2661 mMechanism = CKM_EC_KEY_PAIR_GEN;
2664 else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
2665 mKeyPair->mPublicKey->Algorithm().MakeOKP(mAlgName);
2666 mKeyPair->mPrivateKey->Algorithm().MakeOKP(mAlgName);
2667 mMechanism = CKM_EC_MONTGOMERY_KEY_PAIR_GEN;
2668 mNamedCurve.AssignLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519);
2671 else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
2672 mKeyPair->mPublicKey->Algorithm().MakeOKP(mAlgName);
2673 mKeyPair->mPrivateKey->Algorithm().MakeOKP(mAlgName);
2674 mMechanism = CKM_EC_EDWARDS_KEY_PAIR_GEN;
2675 mNamedCurve.AssignLiteral(WEBCRYPTO_NAMED_CURVE_ED25519);
2678 else {
2679 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2680 return;
2683 // Set key usages.
2684 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2685 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
2686 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
2687 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
2688 privateAllowedUsages = CryptoKey::SIGN;
2689 publicAllowedUsages = CryptoKey::VERIFY;
2690 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
2691 privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
2692 publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
2693 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
2694 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
2695 privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
2696 publicAllowedUsages = 0;
2697 } else {
2698 MOZ_ASSERT(false); // This shouldn't happen.
2701 mKeyPair->mPrivateKey->SetExtractable(aExtractable);
2702 mKeyPair->mPrivateKey->SetType(CryptoKey::PRIVATE);
2704 mKeyPair->mPublicKey->SetExtractable(true);
2705 mKeyPair->mPublicKey->SetType(CryptoKey::PUBLIC);
2707 mKeyPair->mPrivateKey->ClearUsages();
2708 mKeyPair->mPublicKey->ClearUsages();
2709 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2710 mEarlyRv = mKeyPair->mPrivateKey->AddAllowedUsageIntersecting(
2711 aKeyUsages[i], mAlgName, privateAllowedUsages);
2712 if (NS_FAILED(mEarlyRv)) {
2713 return;
2716 mEarlyRv = mKeyPair->mPublicKey->AddAllowedUsageIntersecting(
2717 aKeyUsages[i], mAlgName, publicAllowedUsages);
2718 if (NS_FAILED(mEarlyRv)) {
2719 return;
2724 nsresult GenerateAsymmetricKeyTask::DoCrypto() {
2725 MOZ_ASSERT(mKeyPair);
2727 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2728 MOZ_ASSERT(slot.get());
2730 void* param;
2731 switch (mMechanism) {
2732 case CKM_RSA_PKCS_KEY_PAIR_GEN:
2733 param = &mRsaParams;
2734 break;
2735 case CKM_DH_PKCS_KEY_PAIR_GEN:
2736 param = &mDhParams;
2737 break;
2738 case CKM_EC_MONTGOMERY_KEY_PAIR_GEN:
2739 case CKM_EC_EDWARDS_KEY_PAIR_GEN:
2740 case CKM_EC_KEY_PAIR_GEN: {
2741 param = CreateECParamsForCurve(mNamedCurve, mArena.get());
2742 if (!param) {
2743 return NS_ERROR_DOM_UNKNOWN_ERR;
2745 break;
2747 default:
2748 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2751 mPrivateKey = UniqueSECKEYPrivateKey(PK11_GenerateKeyPair(
2752 slot.get(), mMechanism, param, TempPtrToSetter(&mPublicKey), PR_FALSE,
2753 PR_FALSE, nullptr));
2755 if (!mPrivateKey.get() || !mPublicKey.get()) {
2756 return NS_ERROR_DOM_OPERATION_ERR;
2759 // If no usages ended up being allowed, SyntaxError
2760 if (!mKeyPair->mPrivateKey->HasAnyUsage()) {
2761 return NS_ERROR_DOM_SYNTAX_ERR;
2764 nsresult rv = mKeyPair->mPrivateKey->SetPrivateKey(mPrivateKey.get());
2765 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2766 rv = mKeyPair->mPublicKey->SetPublicKey(mPublicKey.get());
2767 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2768 // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
2769 // private key, we need this later when exporting to PKCS8 and JWK though.
2770 if (mMechanism == CKM_EC_KEY_PAIR_GEN ||
2771 mMechanism == CKM_EC_MONTGOMERY_KEY_PAIR_GEN ||
2772 mMechanism == CKM_EC_EDWARDS_KEY_PAIR_GEN) {
2773 rv = mKeyPair->mPrivateKey->AddPublicKeyData(mPublicKey.get());
2774 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2777 return NS_OK;
2780 void GenerateAsymmetricKeyTask::Resolve() {
2781 mResultPromise->MaybeResolve(*mKeyPair);
2784 void GenerateAsymmetricKeyTask::Cleanup() { mKeyPair = nullptr; }
2786 class DeriveHkdfBitsTask : public ReturnArrayBufferViewTask {
2787 public:
2788 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2789 CryptoKey& aKey, const Nullable<uint32_t>& aLength)
2790 : mMechanism(CKM_INVALID_MECHANISM) {
2791 Init(aCx, aAlgorithm, aKey, aLength);
2794 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2795 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2796 : mLengthInBits(0), mLengthInBytes(0), mMechanism(CKM_INVALID_MECHANISM) {
2797 size_t length;
2798 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2800 const Nullable<uint32_t> keyLength(length);
2801 if (NS_SUCCEEDED(mEarlyRv)) {
2802 Init(aCx, aAlgorithm, aKey, keyLength);
2806 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2807 const Nullable<uint32_t>& aLength) {
2808 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_HKDF);
2809 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HKDF);
2811 if (!mSymKey.Assign(aKey.GetSymKey())) {
2812 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2813 return;
2816 RootedDictionary<HkdfParams> params(aCx);
2817 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2818 if (NS_FAILED(mEarlyRv)) {
2819 mEarlyRv = NS_ERROR_DOM_TYPE_MISMATCH_ERR;
2820 return;
2823 // length must be non-null and multiple of eight.
2824 if (aLength.IsNull() || aLength.Value() % 8 != 0) {
2825 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2826 return;
2829 // Extract the hash algorithm.
2830 nsString hashName;
2831 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2832 if (NS_FAILED(mEarlyRv)) {
2833 return;
2836 // Check the given hash algorithm.
2837 switch (MapAlgorithmNameToMechanism(hashName)) {
2838 case CKM_SHA_1:
2839 mMechanism = CKM_NSS_HKDF_SHA1;
2840 break;
2841 case CKM_SHA256:
2842 mMechanism = CKM_NSS_HKDF_SHA256;
2843 break;
2844 case CKM_SHA384:
2845 mMechanism = CKM_NSS_HKDF_SHA384;
2846 break;
2847 case CKM_SHA512:
2848 mMechanism = CKM_NSS_HKDF_SHA512;
2849 break;
2850 default:
2851 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2852 return;
2855 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2856 ATTEMPT_BUFFER_INIT(mInfo, params.mInfo)
2857 mLengthInBytes = ceil((double)aLength.Value() / 8);
2858 mLengthInBits = aLength.Value();
2861 private:
2862 size_t mLengthInBits;
2863 size_t mLengthInBytes;
2864 CryptoBuffer mSalt;
2865 CryptoBuffer mInfo;
2866 CryptoBuffer mSymKey;
2867 CK_MECHANISM_TYPE mMechanism;
2869 virtual nsresult DoCrypto() override {
2870 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2871 if (!arena) {
2872 return NS_ERROR_DOM_OPERATION_ERR;
2875 // Import the key
2876 SECItem keyItem = {siBuffer, nullptr, 0};
2877 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2879 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2880 if (!slot.get()) {
2881 return NS_ERROR_DOM_OPERATION_ERR;
2884 UniquePK11SymKey baseKey(PK11_ImportSymKey(slot.get(), mMechanism,
2885 PK11_OriginUnwrap, CKA_WRAP,
2886 &keyItem, nullptr));
2887 if (!baseKey) {
2888 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2891 // We are going to return an empty string, so we can skip the rest.
2892 if (mLengthInBits == 0) {
2893 mResult.Clear();
2894 return NS_OK;
2897 SECItem salt = {siBuffer, nullptr, 0};
2898 SECItem info = {siBuffer, nullptr, 0};
2899 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2900 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &info, mInfo);
2902 CK_NSS_HKDFParams hkdfParams = {true, salt.data, salt.len,
2903 true, info.data, info.len};
2904 SECItem params = {siBuffer, (unsigned char*)&hkdfParams,
2905 sizeof(hkdfParams)};
2907 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2908 // derived symmetric key and don't matter because we ignore them anyway.
2909 UniquePK11SymKey symKey(PK11_Derive(baseKey.get(), mMechanism, &params,
2910 CKM_SHA512_HMAC, CKA_SIGN,
2911 mLengthInBytes));
2913 if (!symKey.get()) {
2914 return NS_ERROR_DOM_OPERATION_ERR;
2917 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2918 if (NS_FAILED(rv)) {
2919 return NS_ERROR_DOM_OPERATION_ERR;
2922 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2923 // just refers to a buffer managed by symKey. The assignment copies the
2924 // data, so mResult manages one copy, while symKey manages another.
2925 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2927 if (mLengthInBytes > mResult.Length()) {
2928 return NS_ERROR_DOM_DATA_ERR;
2931 if (!mResult.SetLength(mLengthInBytes, fallible)) {
2932 return NS_ERROR_DOM_UNKNOWN_ERR;
2935 return NS_OK;
2939 class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask {
2940 public:
2941 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2942 CryptoKey& aKey, const Nullable<uint32_t>& aLength)
2943 : mHashOidTag(SEC_OID_UNKNOWN) {
2944 Init(aCx, aAlgorithm, aKey, aLength);
2947 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2948 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2949 : mLength(0), mIterations(0), mHashOidTag(SEC_OID_UNKNOWN) {
2950 size_t length;
2951 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2953 const Nullable<uint32_t> keyLength(length);
2954 if (NS_SUCCEEDED(mEarlyRv)) {
2955 Init(aCx, aAlgorithm, aKey, keyLength);
2959 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2960 const Nullable<uint32_t>& aLength) {
2961 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_PBKDF2);
2962 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_PBKDF2);
2964 if (!mSymKey.Assign(aKey.GetSymKey())) {
2965 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2966 return;
2969 RootedDictionary<Pbkdf2Params> params(aCx);
2970 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2971 if (NS_FAILED(mEarlyRv)) {
2972 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2973 return;
2976 // length must be non-null and multiple of eight.
2977 if (aLength.IsNull() || aLength.Value() % 8) {
2978 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2979 return;
2982 // Extract the hash algorithm.
2983 nsString hashName;
2984 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2985 if (NS_FAILED(mEarlyRv)) {
2986 return;
2989 // Check the given hash algorithm.
2990 switch (MapAlgorithmNameToMechanism(hashName)) {
2991 case CKM_SHA_1:
2992 mHashOidTag = SEC_OID_HMAC_SHA1;
2993 break;
2994 case CKM_SHA256:
2995 mHashOidTag = SEC_OID_HMAC_SHA256;
2996 break;
2997 case CKM_SHA384:
2998 mHashOidTag = SEC_OID_HMAC_SHA384;
2999 break;
3000 case CKM_SHA512:
3001 mHashOidTag = SEC_OID_HMAC_SHA512;
3002 break;
3003 default:
3004 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
3005 return;
3008 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
3009 mLength = aLength.Value() >> 3; // bits to bytes
3010 mIterations = params.mIterations;
3013 private:
3014 size_t mLength;
3015 size_t mIterations;
3016 CryptoBuffer mSalt;
3017 CryptoBuffer mSymKey;
3018 SECOidTag mHashOidTag;
3020 virtual nsresult DoCrypto() override {
3021 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
3022 if (!arena) {
3023 return NS_ERROR_DOM_OPERATION_ERR;
3026 SECItem salt = {siBuffer, nullptr, 0};
3027 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
3028 // PK11_CreatePBEV2AlgorithmID will "helpfully" create PBKDF2 parameters
3029 // with a random salt if given a SECItem* that is either null or has a null
3030 // data pointer. This obviously isn't what we want, so we have to fake it
3031 // out by passing in a SECItem* with a non-null data pointer but with zero
3032 // length.
3033 if (!salt.data) {
3034 MOZ_ASSERT(salt.len == 0);
3035 salt.data =
3036 reinterpret_cast<unsigned char*>(PORT_ArenaAlloc(arena.get(), 1));
3037 if (!salt.data) {
3038 return NS_ERROR_DOM_UNKNOWN_ERR;
3042 // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this
3043 // parameter is unused for key generation. It is currently only used
3044 // for PBKDF2 authentication or key (un)wrapping when specifying an
3045 // encryption algorithm (PBES2).
3046 UniqueSECAlgorithmID algID(
3047 PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1,
3048 mHashOidTag, mLength, mIterations, &salt));
3050 if (!algID) {
3051 return NS_ERROR_DOM_OPERATION_ERR;
3054 // We are going to return an empty string, so we can skip the rest.
3055 if (mLength == 0) {
3056 mResult.Clear();
3057 return NS_OK;
3060 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
3061 if (!slot.get()) {
3062 return NS_ERROR_DOM_OPERATION_ERR;
3065 SECItem keyItem = {siBuffer, nullptr, 0};
3066 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
3068 UniquePK11SymKey symKey(
3069 PK11_PBEKeyGen(slot.get(), algID.get(), &keyItem, false, nullptr));
3070 if (!symKey.get()) {
3071 return NS_ERROR_DOM_OPERATION_ERR;
3074 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
3075 if (NS_FAILED(rv)) {
3076 return NS_ERROR_DOM_OPERATION_ERR;
3079 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
3080 // just refers to a buffer managed by symKey. The assignment copies the
3081 // data, so mResult manages one copy, while symKey manages another.
3082 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
3083 return NS_OK;
3087 template <class DeriveBitsTask>
3088 class DeriveKeyTask : public DeriveBitsTask {
3089 public:
3090 DeriveKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
3091 const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
3092 const ObjectOrString& aDerivedKeyType, bool aExtractable,
3093 const Sequence<nsString>& aKeyUsages)
3094 : DeriveBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType) {
3095 if (NS_FAILED(this->mEarlyRv)) {
3096 return;
3099 constexpr auto format =
3100 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_FORMAT_RAW);
3101 mTask = new ImportSymmetricKeyTask(aGlobal, aCx, format, aDerivedKeyType,
3102 aExtractable, aKeyUsages);
3105 protected:
3106 RefPtr<ImportSymmetricKeyTask> mTask;
3108 private:
3109 virtual void Resolve() override {
3110 mTask->SetRawKeyData(this->mResult);
3111 mTask->DispatchWithPromise(this->mResultPromise);
3114 virtual void Cleanup() override { mTask = nullptr; }
3116 class DeriveEcdhBitsTask : public ReturnArrayBufferViewTask {
3117 public:
3118 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
3119 CryptoKey& aKey, const Nullable<uint32_t>& aLength)
3120 : mLengthInBits(aLength), mPrivKey(aKey.GetPrivateKey()) {
3121 Init(aCx, aAlgorithm, aKey);
3124 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
3125 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
3126 : mPrivKey(aKey.GetPrivateKey()) {
3127 Maybe<size_t> lengthInBits;
3128 mEarlyRv = GetKeyLengthForAlgorithmIfSpecified(aCx, aTargetAlgorithm,
3129 lengthInBits);
3130 if (lengthInBits.isNothing()) {
3131 mLengthInBits.SetNull();
3132 } else {
3133 mLengthInBits.SetValue(*lengthInBits);
3135 if (NS_SUCCEEDED(mEarlyRv)) {
3136 Init(aCx, aAlgorithm, aKey);
3140 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey) {
3141 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDH);
3142 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDH);
3144 // Check that we have a private key.
3145 if (!mPrivKey) {
3146 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
3147 return;
3150 // Retrieve the peer's public key.
3151 RootedDictionary<EcdhKeyDeriveParams> params(aCx);
3152 mEarlyRv = Coerce(aCx, params, aAlgorithm);
3153 if (NS_FAILED(mEarlyRv)) {
3154 /* The returned code is installed by Coerce function. */
3155 return;
3158 CryptoKey* publicKey = params.mPublic;
3159 mPubKey = publicKey->GetPublicKey();
3160 if (!mPubKey) {
3161 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
3162 return;
3165 CHECK_KEY_ALGORITHM(publicKey->Algorithm(), WEBCRYPTO_ALG_ECDH);
3167 // Both keys must use the same named curve.
3168 nsString curve1 = aKey.Algorithm().mEc.mNamedCurve;
3169 nsString curve2 = publicKey->Algorithm().mEc.mNamedCurve;
3171 if (!curve1.Equals(curve2)) {
3172 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
3173 return;
3177 private:
3178 Nullable<uint32_t> mLengthInBits;
3179 UniqueSECKEYPrivateKey mPrivKey;
3180 UniqueSECKEYPublicKey mPubKey;
3182 virtual nsresult DoCrypto() override {
3183 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
3184 // derived symmetric key and don't matter because we ignore them anyway.
3185 UniquePK11SymKey symKey(
3186 PK11_PubDeriveWithKDF(mPrivKey.get(), mPubKey.get(), PR_FALSE, nullptr,
3187 nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC,
3188 CKA_SIGN, 0, CKD_NULL, nullptr, nullptr));
3190 if (!symKey.get()) {
3191 return NS_ERROR_DOM_OPERATION_ERR;
3194 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
3195 if (NS_FAILED(rv)) {
3196 return NS_ERROR_DOM_OPERATION_ERR;
3199 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
3200 // just refers to a buffer managed by symKey. The assignment copies the
3201 // data, so mResult manages one copy, while symKey manages another.
3202 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
3204 if (!mLengthInBits.IsNull()) {
3205 size_t length = mLengthInBits.Value();
3206 size_t lengthInBytes = ceil((double)length / 8); // bits to bytes
3207 if (lengthInBytes > mResult.Length()) {
3208 return NS_ERROR_DOM_OPERATION_ERR;
3211 if (!mResult.SetLength(lengthInBytes, fallible)) {
3212 return NS_ERROR_DOM_UNKNOWN_ERR;
3215 // If the number of bits to derive is not a multiple of 8 we need to
3216 // zero out the remaining bits that were derived but not requested.
3217 if (length % 8) {
3218 mResult[mResult.Length() - 1] &= 0xff << (8 - (length % 8));
3222 return NS_OK;
3226 template <class KeyEncryptTask>
3227 class WrapKeyTask : public ExportKeyTask {
3228 public:
3229 WrapKeyTask(JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
3230 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm)
3231 : ExportKeyTask(aFormat, aKey) {
3232 if (NS_FAILED(mEarlyRv)) {
3233 return;
3236 mTask = new KeyEncryptTask(aCx, aWrapAlgorithm, aWrappingKey, true);
3239 private:
3240 RefPtr<KeyEncryptTask> mTask;
3242 virtual nsresult AfterCrypto() override {
3243 // If wrapping JWK, stringify the JSON
3244 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3245 nsAutoString json;
3246 if (!mJwk.ToJSON(json)) {
3247 return NS_ERROR_DOM_OPERATION_ERR;
3250 NS_ConvertUTF16toUTF8 utf8(json);
3251 if (!mResult.Assign((const uint8_t*)utf8.BeginReading(), utf8.Length())) {
3252 return NS_ERROR_DOM_OPERATION_ERR;
3256 return NS_OK;
3259 virtual void Resolve() override {
3260 mTask->SetData(mResult);
3261 mTask->DispatchWithPromise(mResultPromise);
3264 virtual void Cleanup() override { mTask = nullptr; }
3267 template <class KeyEncryptTask>
3268 class UnwrapKeyTask : public KeyEncryptTask {
3269 public:
3270 UnwrapKeyTask(JSContext* aCx, const ArrayBufferViewOrArrayBuffer& aWrappedKey,
3271 CryptoKey& aUnwrappingKey,
3272 const ObjectOrString& aUnwrapAlgorithm, ImportKeyTask* aTask)
3273 : KeyEncryptTask(aCx, aUnwrapAlgorithm, aUnwrappingKey, aWrappedKey,
3274 false),
3275 mTask(aTask) {}
3277 private:
3278 RefPtr<ImportKeyTask> mTask;
3280 virtual void Resolve() override {
3281 mTask->SetKeyDataMaybeParseJWK(KeyEncryptTask::mResult);
3282 mTask->DispatchWithPromise(KeyEncryptTask::mResultPromise);
3285 virtual void Cleanup() override { mTask = nullptr; }
3288 // Task creation methods for WebCryptoTask
3290 // Note: We do not perform algorithm normalization as a monolithic process,
3291 // as described in the spec. Instead:
3292 // * Each method handles its slice of the supportedAlgorithms structure
3293 // * Task constructors take care of:
3294 // * Coercing the algorithm to the proper concrete type
3295 // * Cloning subordinate data items
3296 // * Cloning input data as needed
3298 // Thus, support for different algorithms is determined by the if-statements
3299 // below, rather than a data structure.
3301 // This results in algorithm normalization coming after some other checks,
3302 // and thus slightly more steps being done synchronously than the spec calls
3303 // for. But none of these steps is especially time-consuming.
3305 WebCryptoTask* WebCryptoTask::CreateEncryptDecryptTask(
3306 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
3307 const CryptoOperationData& aData, bool aEncrypt) {
3308 TelemetryMethod method = (aEncrypt) ? TM_ENCRYPT : TM_DECRYPT;
3309 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
3310 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_ENC,
3311 aKey.Extractable());
3313 // Ensure key is usable for this operation
3314 if ((aEncrypt && !aKey.HasUsage(CryptoKey::ENCRYPT)) ||
3315 (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) {
3316 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3319 nsString algName;
3320 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3321 if (NS_FAILED(rv)) {
3322 return new FailureTask(rv);
3325 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3326 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3327 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3328 return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
3329 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3330 return new RsaOaepTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
3333 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3336 WebCryptoTask* WebCryptoTask::CreateSignVerifyTask(
3337 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
3338 const CryptoOperationData& aSignature, const CryptoOperationData& aData,
3339 bool aSign) {
3340 TelemetryMethod method = (aSign) ? TM_SIGN : TM_VERIFY;
3341 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
3342 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_SIG,
3343 aKey.Extractable());
3345 // Ensure key is usable for this operation
3346 if ((aSign && !aKey.HasUsage(CryptoKey::SIGN)) ||
3347 (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) {
3348 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3351 nsString algName;
3352 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3353 if (NS_FAILED(rv)) {
3354 return new FailureTask(rv);
3357 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
3358 return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
3359 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3360 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
3361 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
3362 algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
3363 return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
3364 aData, aSign);
3367 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3370 WebCryptoTask* WebCryptoTask::CreateDigestTask(
3371 JSContext* aCx, const ObjectOrString& aAlgorithm,
3372 const CryptoOperationData& aData) {
3373 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DIGEST);
3375 nsString algName;
3376 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3377 if (NS_FAILED(rv)) {
3378 return new FailureTask(rv);
3381 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
3382 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256) ||
3383 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
3384 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
3385 return new DigestTask(aCx, aAlgorithm, aData);
3388 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3391 WebCryptoTask* WebCryptoTask::CreateImportKeyTask(
3392 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
3393 JS::Handle<JSObject*> aKeyData, const ObjectOrString& aAlgorithm,
3394 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3395 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY);
3396 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable);
3398 // Verify that the format is recognized
3399 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3400 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3401 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3402 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3403 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3406 // Verify that aKeyUsages does not contain an unrecognized value
3407 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3408 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3411 nsString algName;
3412 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3413 if (NS_FAILED(rv)) {
3414 return new FailureTask(rv);
3417 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
3418 // However, the spec should be updated to allow it.
3419 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3420 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3421 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
3422 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
3423 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
3424 algName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
3425 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
3426 return new ImportSymmetricKeyTask(aGlobal, aCx, aFormat, aKeyData,
3427 aAlgorithm, aExtractable, aKeyUsages);
3428 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3429 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
3430 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
3431 return new ImportRsaKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
3432 aExtractable, aKeyUsages);
3433 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
3434 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
3435 return new ImportEcKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
3436 aExtractable, aKeyUsages);
3437 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_X25519) ||
3438 algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519)) {
3439 return new ImportOKPKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
3440 aExtractable, aKeyUsages);
3441 } else {
3442 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3446 WebCryptoTask* WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat,
3447 CryptoKey& aKey) {
3448 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_EXPORTKEY);
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 // Verify that the key is extractable
3459 if (!aKey.Extractable()) {
3460 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3463 // Verify that the algorithm supports export
3464 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
3465 // However, the spec should be updated to allow it.
3466 nsString algName = aKey.Algorithm().mName;
3467 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3468 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3469 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
3470 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
3471 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
3472 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC) ||
3473 algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3474 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
3475 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
3476 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
3477 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
3478 algName.EqualsLiteral(WEBCRYPTO_ALG_ED25519) ||
3479 algName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
3480 return new ExportKeyTask(aFormat, aKey);
3482 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3485 WebCryptoTask* WebCryptoTask::CreateGenerateKeyTask(
3486 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3487 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3488 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_GENERATEKEY);
3489 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_GENERATE,
3490 aExtractable);
3491 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3492 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3495 nsString algName;
3496 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3497 if (NS_FAILED(rv)) {
3498 return new FailureTask(rv);
3501 if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3502 algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3503 algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3504 algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
3505 algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3506 return new GenerateSymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3507 aKeyUsages);
3508 } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3509 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3510 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS) ||
3511 algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
3512 algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA) ||
3513 algName.EqualsASCII(WEBCRYPTO_ALG_ED25519) ||
3514 algName.EqualsASCII(WEBCRYPTO_ALG_X25519)) {
3515 return new GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3516 aKeyUsages);
3517 } else {
3518 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3522 WebCryptoTask* WebCryptoTask::CreateDeriveKeyTask(
3523 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3524 CryptoKey& aBaseKey, const ObjectOrString& aDerivedKeyType,
3525 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3526 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY);
3528 // Ensure baseKey is usable for this operation
3529 if (!aBaseKey.HasUsage(CryptoKey::DERIVEKEY)) {
3530 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3533 // Verify that aKeyUsages does not contain an unrecognized value
3534 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3535 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3538 nsString algName;
3539 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3540 if (NS_FAILED(rv)) {
3541 return new FailureTask(rv);
3544 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3545 return new DeriveKeyTask<DeriveHkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3546 aBaseKey, aDerivedKeyType,
3547 aExtractable, aKeyUsages);
3550 if (algName.EqualsASCII(WEBCRYPTO_ALG_X25519)) {
3551 return new DeriveKeyTask<DeriveX25519BitsTask>(aGlobal, aCx, aAlgorithm,
3552 aBaseKey, aDerivedKeyType,
3553 aExtractable, aKeyUsages);
3556 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3557 return new DeriveKeyTask<DerivePbkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3558 aBaseKey, aDerivedKeyType,
3559 aExtractable, aKeyUsages);
3562 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3563 return new DeriveKeyTask<DeriveEcdhBitsTask>(aGlobal, aCx, aAlgorithm,
3564 aBaseKey, aDerivedKeyType,
3565 aExtractable, aKeyUsages);
3568 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3571 WebCryptoTask* WebCryptoTask::CreateDeriveBitsTask(
3572 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
3573 const Nullable<uint32_t>& aLength) {
3574 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS);
3576 // Ensure baseKey is usable for this operation
3577 if (!aKey.HasUsage(CryptoKey::DERIVEBITS)) {
3578 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3581 nsString algName;
3582 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3583 if (NS_FAILED(rv)) {
3584 return new FailureTask(rv);
3587 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3588 return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3591 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3592 return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
3595 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3596 return new DeriveHkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3599 if (algName.EqualsASCII(WEBCRYPTO_ALG_X25519)) {
3600 return new DeriveX25519BitsTask(aCx, aAlgorithm, aKey, aLength);
3603 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3606 WebCryptoTask* WebCryptoTask::CreateWrapKeyTask(
3607 JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
3608 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm) {
3609 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_WRAPKEY);
3611 // Verify that the format is recognized
3612 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3613 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3614 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3615 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3616 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3619 // Ensure wrappingKey is usable for this operation
3620 if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) {
3621 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3624 // Ensure key is extractable
3625 if (!aKey.Extractable()) {
3626 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3629 nsString wrapAlgName;
3630 nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName);
3631 if (NS_FAILED(rv)) {
3632 return new FailureTask(rv);
3635 if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3636 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3637 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3638 return new WrapKeyTask<AesTask>(aCx, aFormat, aKey, aWrappingKey,
3639 aWrapAlgorithm);
3640 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3641 return new WrapKeyTask<AesKwTask>(aCx, aFormat, aKey, aWrappingKey,
3642 aWrapAlgorithm);
3643 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3644 return new WrapKeyTask<RsaOaepTask>(aCx, aFormat, aKey, aWrappingKey,
3645 aWrapAlgorithm);
3648 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3651 WebCryptoTask* WebCryptoTask::CreateUnwrapKeyTask(
3652 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
3653 const ArrayBufferViewOrArrayBuffer& aWrappedKey, CryptoKey& aUnwrappingKey,
3654 const ObjectOrString& aUnwrapAlgorithm,
3655 const ObjectOrString& aUnwrappedKeyAlgorithm, bool aExtractable,
3656 const Sequence<nsString>& aKeyUsages) {
3657 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_UNWRAPKEY);
3659 // Ensure key is usable for this operation
3660 if (!aUnwrappingKey.HasUsage(CryptoKey::UNWRAPKEY)) {
3661 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3664 // Verify that aKeyUsages does not contain an unrecognized value
3665 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3666 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3669 nsString keyAlgName;
3670 nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName);
3671 if (NS_FAILED(rv)) {
3672 return new FailureTask(rv);
3675 CryptoOperationData dummy;
3676 RefPtr<ImportKeyTask> importTask;
3677 if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3678 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3679 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3680 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
3681 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HKDF) ||
3682 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3683 importTask = new ImportSymmetricKeyTask(aGlobal, aCx, aFormat,
3684 aUnwrappedKeyAlgorithm,
3685 aExtractable, aKeyUsages);
3686 } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3687 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3688 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS)) {
3689 importTask =
3690 new ImportRsaKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3691 aExtractable, aKeyUsages);
3692 } else if (keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
3693 keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
3694 importTask =
3695 new ImportEcKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3696 aExtractable, aKeyUsages);
3697 } else if (keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ED25519) ||
3698 keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_X25519)) {
3699 importTask =
3700 new ImportOKPKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3701 aExtractable, aKeyUsages);
3702 } else {
3703 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3706 nsString unwrapAlgName;
3707 rv = GetAlgorithmName(aCx, aUnwrapAlgorithm, unwrapAlgName);
3708 if (NS_FAILED(rv)) {
3709 return new FailureTask(rv);
3711 if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3712 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3713 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3714 return new UnwrapKeyTask<AesTask>(aCx, aWrappedKey, aUnwrappingKey,
3715 aUnwrapAlgorithm, importTask);
3716 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3717 return new UnwrapKeyTask<AesKwTask>(aCx, aWrappedKey, aUnwrappingKey,
3718 aUnwrapAlgorithm, importTask);
3719 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3720 return new UnwrapKeyTask<RsaOaepTask>(aCx, aWrappedKey, aUnwrappingKey,
3721 aUnwrapAlgorithm, importTask);
3724 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3727 WebCryptoTask::WebCryptoTask()
3728 : CancelableRunnable("WebCryptoTask"),
3729 mEarlyRv(NS_OK),
3730 mEarlyComplete(false),
3731 mOriginalEventTarget(nullptr),
3732 mRv(NS_ERROR_NOT_INITIALIZED) {}
3734 WebCryptoTask::~WebCryptoTask() = default;
3736 } // namespace mozilla::dom