Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / crypto / WebCryptoTask.cpp
blobdc768679809e656fd58047cb489e7f1b9ed04b39
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "pk11pub.h"
8 #include "cryptohi.h"
9 #include "secerr.h"
10 #include "nsNSSComponent.h"
11 #include "nsProxyRelease.h"
13 #include "jsapi.h"
14 #include "mozilla/Telemetry.h"
15 #include "mozilla/Utf8.h"
16 #include "mozilla/dom/CryptoBuffer.h"
17 #include "mozilla/dom/CryptoKey.h"
18 #include "mozilla/dom/KeyAlgorithmProxy.h"
19 #include "mozilla/dom/TypedArray.h"
20 #include "mozilla/dom/WebCryptoCommon.h"
21 #include "mozilla/dom/WebCryptoTask.h"
22 #include "mozilla/dom/WorkerRef.h"
23 #include "mozilla/dom/WorkerPrivate.h"
24 #include "mozilla/dom/RootedDictionary.h"
26 // Template taken from security/nss/lib/util/templates.c
27 // This (or SGN_EncodeDigestInfo) would ideally be exported
28 // by NSS and until that happens we have to keep our own copy.
29 const SEC_ASN1Template SGN_DigestInfoTemplate[] = {
30 {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SGNDigestInfo)},
31 {SEC_ASN1_INLINE, offsetof(SGNDigestInfo, digestAlgorithm),
32 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate)},
33 {SEC_ASN1_OCTET_STRING, offsetof(SGNDigestInfo, digest)},
36 }};
38 namespace mozilla::dom {
40 // Pre-defined identifiers for telemetry histograms
42 enum TelemetryMethod {
43 TM_ENCRYPT = 0,
44 TM_DECRYPT = 1,
45 TM_SIGN = 2,
46 TM_VERIFY = 3,
47 TM_DIGEST = 4,
48 TM_GENERATEKEY = 5,
49 TM_DERIVEKEY = 6,
50 TM_DERIVEBITS = 7,
51 TM_IMPORTKEY = 8,
52 TM_EXPORTKEY = 9,
53 TM_WRAPKEY = 10,
54 TM_UNWRAPKEY = 11
57 enum TelemetryAlgorithm {
58 // Please make additions at the end of the list,
59 // to preserve comparability of histograms over time
60 TA_UNKNOWN = 0,
61 // encrypt / decrypt
62 TA_AES_CBC = 1,
63 TA_AES_CFB = 2,
64 TA_AES_CTR = 3,
65 TA_AES_GCM = 4,
66 TA_RSAES_PKCS1 = 5, // NB: This algorithm has been removed
67 TA_RSA_OAEP = 6,
68 // sign/verify
69 TA_RSASSA_PKCS1 = 7,
70 TA_RSA_PSS = 8,
71 TA_HMAC_SHA_1 = 9,
72 TA_HMAC_SHA_224 = 10,
73 TA_HMAC_SHA_256 = 11,
74 TA_HMAC_SHA_384 = 12,
75 TA_HMAC_SHA_512 = 13,
76 // digest
77 TA_SHA_1 = 14,
78 TA_SHA_224 = 15,
79 TA_SHA_256 = 16,
80 TA_SHA_384 = 17,
81 TA_SHA_512 = 18,
82 // Later additions
83 TA_AES_KW = 19,
84 TA_ECDH = 20,
85 TA_PBKDF2 = 21,
86 TA_ECDSA = 22,
87 TA_HKDF = 23,
88 TA_DH = 24,
91 // Convenience functions for extracting / converting information
93 // OOM-safe CryptoBuffer initialization, suitable for constructors
94 #define ATTEMPT_BUFFER_INIT(dst, src) \
95 if (!dst.Assign(src)) { \
96 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; \
97 return; \
100 // OOM-safe CryptoBuffer-to-SECItem copy, suitable for DoCrypto
101 #define ATTEMPT_BUFFER_TO_SECITEM(arena, dst, src) \
102 if (!src.ToSECItem(arena, dst)) { \
103 return NS_ERROR_DOM_UNKNOWN_ERR; \
106 // OOM-safe CryptoBuffer copy, suitable for DoCrypto
107 #define ATTEMPT_BUFFER_ASSIGN(dst, src) \
108 if (!dst.Assign(src)) { \
109 return NS_ERROR_DOM_UNKNOWN_ERR; \
112 // Safety check for algorithms that use keys, suitable for constructors
113 #define CHECK_KEY_ALGORITHM(keyAlg, algName) \
115 if (!NORMALIZED_EQUALS(keyAlg.mName, algName)) { \
116 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; \
117 return; \
121 class ClearException {
122 public:
123 explicit ClearException(JSContext* aCx) : mCx(aCx) {}
125 ~ClearException() { JS_ClearPendingException(mCx); }
127 private:
128 JSContext* mCx;
131 template <class OOS>
132 static nsresult GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
133 nsString& aName) {
134 ClearException ce(aCx);
136 if (aAlgorithm.IsString()) {
137 // If string, then treat as algorithm name
138 aName.Assign(aAlgorithm.GetAsString());
139 } else {
140 // Coerce to algorithm and extract name
141 JS::Rooted<JS::Value> value(aCx,
142 JS::ObjectValue(*aAlgorithm.GetAsObject()));
143 Algorithm alg;
145 if (!alg.Init(aCx, value)) {
146 return NS_ERROR_DOM_SYNTAX_ERR;
149 aName = alg.mName;
152 if (!NormalizeToken(aName, aName)) {
153 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
156 return NS_OK;
159 template <class T, class OOS>
160 static nsresult Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm) {
161 ClearException ce(aCx);
163 if (!aAlgorithm.IsObject()) {
164 return NS_ERROR_DOM_SYNTAX_ERR;
167 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
168 if (!aTarget.Init(aCx, value)) {
169 return NS_ERROR_DOM_SYNTAX_ERR;
172 return NS_OK;
175 inline size_t MapHashAlgorithmNameToBlockSize(const nsString& aName) {
176 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
177 aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
178 return 512;
181 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
182 aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
183 return 1024;
186 return 0;
189 inline nsresult GetKeyLengthForAlgorithmIfSpecified(
190 JSContext* aCx, const ObjectOrString& aAlgorithm, Maybe<size_t>& aLength) {
191 // Extract algorithm name
192 nsString algName;
193 if (NS_FAILED(GetAlgorithmName(aCx, aAlgorithm, algName))) {
194 return NS_ERROR_DOM_SYNTAX_ERR;
197 // Read AES key length from given algorithm object.
198 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
199 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
200 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
201 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
202 RootedDictionary<AesDerivedKeyParams> params(aCx);
203 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
204 return NS_ERROR_DOM_SYNTAX_ERR;
207 if (params.mLength != 128 && params.mLength != 192 &&
208 params.mLength != 256) {
209 return NS_ERROR_DOM_OPERATION_ERR;
212 aLength.emplace(params.mLength);
213 return NS_OK;
216 // Read HMAC key length from given algorithm object or
217 // determine key length as the block size of the given hash.
218 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
219 RootedDictionary<HmacDerivedKeyParams> params(aCx);
220 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
221 return NS_ERROR_DOM_SYNTAX_ERR;
224 // Return the passed length, if any.
225 if (params.mLength.WasPassed()) {
226 aLength.emplace(params.mLength.Value());
227 return NS_OK;
230 nsString hashName;
231 if (NS_FAILED(GetAlgorithmName(aCx, params.mHash, hashName))) {
232 return NS_ERROR_DOM_SYNTAX_ERR;
235 // Return the given hash algorithm's block size as the key length.
236 size_t blockSize = MapHashAlgorithmNameToBlockSize(hashName);
237 if (blockSize == 0) {
238 return NS_ERROR_DOM_SYNTAX_ERR;
241 aLength.emplace(blockSize);
242 return NS_OK;
245 return NS_OK;
248 inline nsresult GetKeyLengthForAlgorithm(JSContext* aCx,
249 const ObjectOrString& aAlgorithm,
250 size_t& aLength) {
251 Maybe<size_t> length;
252 nsresult rv = GetKeyLengthForAlgorithmIfSpecified(aCx, aAlgorithm, length);
253 if (NS_FAILED(rv)) {
254 return rv;
256 if (length.isNothing()) {
257 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
259 aLength = *length;
260 return NS_OK;
263 inline bool MapOIDTagToNamedCurve(SECOidTag aOIDTag, nsString& aResult) {
264 switch (aOIDTag) {
265 case SEC_OID_SECG_EC_SECP256R1:
266 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
267 break;
268 case SEC_OID_SECG_EC_SECP384R1:
269 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
270 break;
271 case SEC_OID_SECG_EC_SECP521R1:
272 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
273 break;
274 default:
275 return false;
278 return true;
281 inline SECOidTag MapHashAlgorithmNameToOID(const nsString& aName) {
282 SECOidTag hashOID(SEC_OID_UNKNOWN);
284 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
285 hashOID = SEC_OID_SHA1;
286 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
287 hashOID = SEC_OID_SHA256;
288 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
289 hashOID = SEC_OID_SHA384;
290 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
291 hashOID = SEC_OID_SHA512;
294 return hashOID;
297 inline CK_MECHANISM_TYPE MapHashAlgorithmNameToMgfMechanism(
298 const nsString& aName) {
299 CK_MECHANISM_TYPE mech(UNKNOWN_CK_MECHANISM);
301 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
302 mech = CKG_MGF1_SHA1;
303 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
304 mech = CKG_MGF1_SHA256;
305 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
306 mech = CKG_MGF1_SHA384;
307 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
308 mech = CKG_MGF1_SHA512;
311 return mech;
314 // Implementation of WebCryptoTask methods
316 void WebCryptoTask::DispatchWithPromise(Promise* aResultPromise) {
317 mResultPromise = aResultPromise;
319 // Fail if an error was set during the constructor
320 MAYBE_EARLY_FAIL(mEarlyRv)
322 // Perform pre-NSS operations, and fail if they fail
323 mEarlyRv = BeforeCrypto();
324 MAYBE_EARLY_FAIL(mEarlyRv)
326 // Skip dispatch if we're already done. Otherwise launch a CryptoTask
327 if (mEarlyComplete) {
328 CallCallback(mEarlyRv);
329 return;
332 // Store calling thread
333 mOriginalEventTarget = GetCurrentSerialEventTarget();
335 // If we are running on a worker thread we must hold the worker
336 // alive while we work on the thread pool. Otherwise the worker
337 // private may get torn down before we dispatch back to complete
338 // the transaction.
339 if (!NS_IsMainThread()) {
340 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
341 MOZ_ASSERT(workerPrivate);
343 RefPtr<StrongWorkerRef> workerRef =
344 StrongWorkerRef::Create(workerPrivate, "WebCryptoTask");
345 if (NS_WARN_IF(!workerRef)) {
346 mEarlyRv = NS_BINDING_ABORTED;
347 } else {
348 mWorkerRef = new ThreadSafeWorkerRef(workerRef);
351 MAYBE_EARLY_FAIL(mEarlyRv);
353 // dispatch to thread pool
355 if (!EnsureNSSInitializedChromeOrContent()) {
356 mEarlyRv = NS_ERROR_FAILURE;
358 MAYBE_EARLY_FAIL(mEarlyRv);
360 mEarlyRv = NS_DispatchBackgroundTask(this);
361 MAYBE_EARLY_FAIL(mEarlyRv)
364 NS_IMETHODIMP
365 WebCryptoTask::Run() {
366 // Run heavy crypto operations on the thread pool, off the original thread.
367 if (!IsOnOriginalThread()) {
368 mRv = CalculateResult();
370 // Back to the original thread, i.e. continue below.
371 mOriginalEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
372 return NS_OK;
375 // We're now back on the calling thread.
376 CallCallback(mRv);
378 // Stop holding the worker thread alive now that the async work has
379 // been completed.
380 mWorkerRef = nullptr;
382 return NS_OK;
385 nsresult WebCryptoTask::Cancel() {
386 MOZ_ASSERT(IsOnOriginalThread());
387 FailWithError(NS_BINDING_ABORTED);
388 return NS_OK;
391 void WebCryptoTask::FailWithError(nsresult aRv) {
392 MOZ_ASSERT(IsOnOriginalThread());
393 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
395 // Blindly convert nsresult to DOMException
396 // Individual tasks must ensure they pass the right values
397 mResultPromise->MaybeReject(aRv);
398 // Manually release mResultPromise while we're on the main thread
399 mResultPromise = nullptr;
400 mWorkerRef = nullptr;
401 Cleanup();
404 nsresult WebCryptoTask::CalculateResult() {
405 MOZ_ASSERT(!IsOnOriginalThread());
407 return DoCrypto();
410 void WebCryptoTask::CallCallback(nsresult rv) {
411 MOZ_ASSERT(IsOnOriginalThread());
412 if (NS_FAILED(rv)) {
413 FailWithError(rv);
414 return;
417 nsresult rv2 = AfterCrypto();
418 if (NS_FAILED(rv2)) {
419 FailWithError(rv2);
420 return;
423 Resolve();
424 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, true);
426 // Manually release mResultPromise while we're on the main thread
427 mResultPromise = nullptr;
428 Cleanup();
431 // Some generic utility classes
433 class FailureTask : public WebCryptoTask {
434 public:
435 explicit FailureTask(nsresult aRv) { mEarlyRv = aRv; }
438 class ReturnArrayBufferViewTask : public WebCryptoTask {
439 protected:
440 CryptoBuffer mResult;
442 private:
443 // Returns mResult as an ArrayBufferView, or an error
444 virtual void Resolve() override {
445 TypedArrayCreator<ArrayBuffer> ret(mResult);
446 mResultPromise->MaybeResolve(ret);
450 class DeferredData {
451 public:
452 template <class T>
453 void SetData(const T& aData) {
454 mDataIsSet = mData.Assign(aData);
457 protected:
458 DeferredData() : mDataIsSet(false) {}
460 CryptoBuffer mData;
461 bool mDataIsSet;
464 class AesTask : public ReturnArrayBufferViewTask, public DeferredData {
465 public:
466 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
467 bool aEncrypt)
468 : mMechanism(CKM_INVALID_MECHANISM),
469 mTagLength(0),
470 mCounterLength(0),
471 mEncrypt(aEncrypt) {
472 Init(aCx, aAlgorithm, aKey, aEncrypt);
475 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
476 const CryptoOperationData& aData, bool aEncrypt)
477 : mMechanism(CKM_INVALID_MECHANISM),
478 mTagLength(0),
479 mCounterLength(0),
480 mEncrypt(aEncrypt) {
481 Init(aCx, aAlgorithm, aKey, aEncrypt);
482 SetData(aData);
485 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
486 bool aEncrypt) {
487 nsString algName;
488 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
489 if (NS_FAILED(mEarlyRv)) {
490 return;
493 if (!mSymKey.Assign(aKey.GetSymKey())) {
494 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
495 return;
498 // Check that we got a reasonable key
499 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
500 (mSymKey.Length() != 32)) {
501 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
502 return;
505 // Cache parameters depending on the specific algorithm
506 TelemetryAlgorithm telemetryAlg;
507 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
508 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CBC);
510 mMechanism = CKM_AES_CBC_PAD;
511 telemetryAlg = TA_AES_CBC;
512 RootedDictionary<AesCbcParams> params(aCx);
513 nsresult rv = Coerce(aCx, params, aAlgorithm);
514 if (NS_FAILED(rv)) {
515 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
516 return;
519 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
520 if (mIv.Length() != 16) {
521 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
522 return;
524 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
525 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CTR);
527 mMechanism = CKM_AES_CTR;
528 telemetryAlg = TA_AES_CTR;
529 RootedDictionary<AesCtrParams> params(aCx);
530 nsresult rv = Coerce(aCx, params, aAlgorithm);
531 if (NS_FAILED(rv)) {
532 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
533 return;
536 ATTEMPT_BUFFER_INIT(mIv, params.mCounter)
537 if (mIv.Length() != 16) {
538 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
539 return;
542 mCounterLength = params.mLength;
543 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
544 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM);
546 mMechanism = CKM_AES_GCM;
547 telemetryAlg = TA_AES_GCM;
548 RootedDictionary<AesGcmParams> params(aCx);
549 nsresult rv = Coerce(aCx, params, aAlgorithm);
550 if (NS_FAILED(rv)) {
551 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
552 return;
555 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
557 if (params.mAdditionalData.WasPassed()) {
558 ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value())
561 // 32, 64, 96, 104, 112, 120 or 128
562 mTagLength = 128;
563 if (params.mTagLength.WasPassed()) {
564 mTagLength = params.mTagLength.Value();
565 if ((mTagLength > 128) ||
566 !(mTagLength == 32 || mTagLength == 64 ||
567 (mTagLength >= 96 && mTagLength % 8 == 0))) {
568 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
569 return;
572 } else {
573 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
574 return;
576 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
579 private:
580 CK_MECHANISM_TYPE mMechanism;
581 CryptoBuffer mSymKey;
582 CryptoBuffer mIv; // Initialization vector
583 CryptoBuffer mAad; // Additional Authenticated Data
584 uint8_t mTagLength;
585 uint8_t mCounterLength;
586 bool mEncrypt;
588 virtual nsresult DoCrypto() override {
589 nsresult rv;
591 if (!mDataIsSet) {
592 return NS_ERROR_DOM_OPERATION_ERR;
595 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
596 if (!arena) {
597 return NS_ERROR_DOM_OPERATION_ERR;
600 // Construct the parameters object depending on algorithm
601 SECItem param = {siBuffer, nullptr, 0};
602 CK_AES_CTR_PARAMS ctrParams;
603 CK_GCM_PARAMS gcmParams;
604 switch (mMechanism) {
605 case CKM_AES_CBC_PAD:
606 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &param, mIv);
607 break;
608 case CKM_AES_CTR:
609 ctrParams.ulCounterBits = mCounterLength;
610 MOZ_ASSERT(mIv.Length() == 16);
611 memcpy(&ctrParams.cb, mIv.Elements(), 16);
612 param.type = siBuffer;
613 param.data = (unsigned char*)&ctrParams;
614 param.len = sizeof(ctrParams);
615 break;
616 case CKM_AES_GCM:
617 gcmParams.pIv = mIv.Elements();
618 gcmParams.ulIvLen = mIv.Length();
619 gcmParams.ulIvBits = gcmParams.ulIvLen * 8;
620 gcmParams.pAAD = mAad.Elements();
621 gcmParams.ulAADLen = mAad.Length();
622 gcmParams.ulTagBits = mTagLength;
623 param.type = siBuffer;
624 param.data = (unsigned char*)&gcmParams;
625 param.len = sizeof(gcmParams);
626 break;
627 default:
628 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
631 // Import the key
632 SECItem keyItem = {siBuffer, nullptr, 0};
633 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
634 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
635 MOZ_ASSERT(slot.get());
636 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
637 PK11_OriginUnwrap, CKA_ENCRYPT,
638 &keyItem, nullptr));
639 if (!symKey) {
640 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
643 // Check whether the integer addition would overflow.
644 if (std::numeric_limits<CryptoBuffer::size_type>::max() - 16 <
645 mData.Length()) {
646 return NS_ERROR_DOM_DATA_ERR;
649 // Initialize the output buffer (enough space for padding / a full tag)
650 if (!mResult.SetLength(mData.Length() + 16, fallible)) {
651 return NS_ERROR_DOM_UNKNOWN_ERR;
654 uint32_t outLen = 0;
656 // Perform the encryption/decryption
657 if (mEncrypt) {
658 rv = MapSECStatus(PK11_Encrypt(
659 symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
660 mResult.Length(), mData.Elements(), mData.Length()));
661 } else {
662 rv = MapSECStatus(PK11_Decrypt(
663 symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
664 mResult.Length(), mData.Elements(), mData.Length()));
666 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
668 mResult.TruncateLength(outLen);
669 return rv;
673 // This class looks like an encrypt/decrypt task, like AesTask,
674 // but it is only exposed to wrapKey/unwrapKey, not encrypt/decrypt
675 class AesKwTask : public ReturnArrayBufferViewTask, public DeferredData {
676 public:
677 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
678 bool aEncrypt)
679 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
680 Init(aCx, aAlgorithm, aKey, aEncrypt);
683 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
684 const CryptoOperationData& aData, bool aEncrypt)
685 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
686 Init(aCx, aAlgorithm, aKey, aEncrypt);
687 SetData(aData);
690 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
691 bool aEncrypt) {
692 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_KW);
694 nsString algName;
695 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
696 if (NS_FAILED(mEarlyRv)) {
697 return;
700 if (!mSymKey.Assign(aKey.GetSymKey())) {
701 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
702 return;
705 // Check that we got a reasonable key
706 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
707 (mSymKey.Length() != 32)) {
708 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
709 return;
712 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_AES_KW);
715 private:
716 CK_MECHANISM_TYPE mMechanism;
717 CryptoBuffer mSymKey;
718 bool mEncrypt;
720 virtual nsresult DoCrypto() override {
721 nsresult rv;
723 if (!mDataIsSet) {
724 return NS_ERROR_DOM_OPERATION_ERR;
727 // Check that the input is a multiple of 64 bits long
728 if (mData.Length() == 0 || mData.Length() % 8 != 0) {
729 return NS_ERROR_DOM_DATA_ERR;
732 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
733 if (!arena) {
734 return NS_ERROR_DOM_OPERATION_ERR;
737 // Import the key
738 SECItem keyItem = {siBuffer, nullptr, 0};
739 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
740 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
741 MOZ_ASSERT(slot.get());
742 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
743 PK11_OriginUnwrap, CKA_WRAP,
744 &keyItem, nullptr));
745 if (!symKey) {
746 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
749 // Import the data to a SECItem
750 SECItem dataItem = {siBuffer, nullptr, 0};
751 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &dataItem, mData);
753 // Parameters for the fake keys
754 CK_MECHANISM_TYPE fakeMechanism = CKM_SHA_1_HMAC;
755 CK_ATTRIBUTE_TYPE fakeOperation = CKA_SIGN;
757 if (mEncrypt) {
758 // Import the data into a fake PK11SymKey structure
759 UniquePK11SymKey keyToWrap(
760 PK11_ImportSymKey(slot.get(), fakeMechanism, PK11_OriginUnwrap,
761 fakeOperation, &dataItem, nullptr));
762 if (!keyToWrap) {
763 return NS_ERROR_DOM_OPERATION_ERR;
766 // Encrypt and return the wrapped key
767 // AES-KW encryption results in a wrapped key 64 bits longer
768 if (!mResult.SetLength(mData.Length() + 8, fallible)) {
769 return NS_ERROR_DOM_OPERATION_ERR;
771 SECItem resultItem = {siBuffer, mResult.Elements(),
772 (unsigned int)mResult.Length()};
773 rv = MapSECStatus(PK11_WrapSymKey(mMechanism, nullptr, symKey.get(),
774 keyToWrap.get(), &resultItem));
775 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
776 } else {
777 // Decrypt the ciphertext into a temporary PK11SymKey
778 // Unwrapped key should be 64 bits shorter
779 int keySize = mData.Length() - 8;
780 UniquePK11SymKey unwrappedKey(
781 PK11_UnwrapSymKey(symKey.get(), mMechanism, nullptr, &dataItem,
782 fakeMechanism, fakeOperation, keySize));
783 if (!unwrappedKey) {
784 return NS_ERROR_DOM_OPERATION_ERR;
787 // Export the key to get the cleartext
788 rv = MapSECStatus(PK11_ExtractKeyValue(unwrappedKey.get()));
789 if (NS_FAILED(rv)) {
790 return NS_ERROR_DOM_UNKNOWN_ERR;
792 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(unwrappedKey.get()));
795 return rv;
799 class RsaOaepTask : public ReturnArrayBufferViewTask, public DeferredData {
800 public:
801 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
802 bool aEncrypt)
803 : mPrivKey(aKey.GetPrivateKey()),
804 mPubKey(aKey.GetPublicKey()),
805 mEncrypt(aEncrypt) {
806 Init(aCx, aAlgorithm, aKey, aEncrypt);
809 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
810 const CryptoOperationData& aData, bool aEncrypt)
811 : mPrivKey(aKey.GetPrivateKey()),
812 mPubKey(aKey.GetPublicKey()),
813 mEncrypt(aEncrypt) {
814 Init(aCx, aAlgorithm, aKey, aEncrypt);
815 SetData(aData);
818 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
819 bool aEncrypt) {
820 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_OAEP);
822 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_OAEP);
824 if (mEncrypt) {
825 if (!mPubKey) {
826 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
827 return;
829 mStrength = SECKEY_PublicKeyStrength(mPubKey.get());
830 } else {
831 if (!mPrivKey) {
832 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
833 return;
835 mStrength = PK11_GetPrivateModulusLen(mPrivKey.get());
838 // The algorithm could just be given as a string
839 // in which case there would be no label specified.
840 if (!aAlgorithm.IsString()) {
841 RootedDictionary<RsaOaepParams> params(aCx);
842 mEarlyRv = Coerce(aCx, params, aAlgorithm);
843 if (NS_FAILED(mEarlyRv)) {
844 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
845 return;
848 if (params.mLabel.WasPassed()) {
849 ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value());
852 // Otherwise mLabel remains the empty octet string, as intended
854 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
855 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
856 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlg.mName);
858 // Check we found appropriate mechanisms.
859 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
860 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
861 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
862 return;
866 private:
867 CK_MECHANISM_TYPE mHashMechanism;
868 CK_MECHANISM_TYPE mMgfMechanism;
869 UniqueSECKEYPrivateKey mPrivKey;
870 UniqueSECKEYPublicKey mPubKey;
871 CryptoBuffer mLabel;
872 uint32_t mStrength;
873 bool mEncrypt;
875 virtual nsresult DoCrypto() override {
876 nsresult rv;
878 if (!mDataIsSet) {
879 return NS_ERROR_DOM_OPERATION_ERR;
882 // Ciphertext is an integer mod the modulus, so it will be
883 // no longer than mStrength octets
884 if (!mResult.SetLength(mStrength, fallible)) {
885 return NS_ERROR_DOM_UNKNOWN_ERR;
888 CK_RSA_PKCS_OAEP_PARAMS oaepParams;
889 oaepParams.source = CKZ_DATA_SPECIFIED;
891 oaepParams.pSourceData = mLabel.Length() ? mLabel.Elements() : nullptr;
892 oaepParams.ulSourceDataLen = mLabel.Length();
894 oaepParams.mgf = mMgfMechanism;
895 oaepParams.hashAlg = mHashMechanism;
897 SECItem param;
898 param.type = siBuffer;
899 param.data = (unsigned char*)&oaepParams;
900 param.len = sizeof(oaepParams);
902 uint32_t outLen = 0;
903 if (mEncrypt) {
904 // PK11_PubEncrypt() checks the plaintext's length and fails if it is too
905 // long to encrypt, i.e. if it is longer than (k - 2hLen - 2) with 'k'
906 // being the length in octets of the RSA modulus n and 'hLen' being the
907 // output length in octets of the chosen hash function.
908 // <https://tools.ietf.org/html/rfc3447#section-7.1>
909 rv = MapSECStatus(PK11_PubEncrypt(
910 mPubKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(), &outLen,
911 mResult.Length(), mData.Elements(), mData.Length(), nullptr));
912 } else {
913 rv = MapSECStatus(PK11_PrivDecrypt(
914 mPrivKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(),
915 &outLen, mResult.Length(), mData.Elements(), mData.Length()));
917 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
919 mResult.TruncateLength(outLen);
920 return NS_OK;
924 class HmacTask : public WebCryptoTask {
925 public:
926 HmacTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
927 const CryptoOperationData& aSignature,
928 const CryptoOperationData& aData, bool aSign)
929 : mMechanism(aKey.Algorithm().Mechanism()), mSign(aSign) {
930 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HMAC);
932 ATTEMPT_BUFFER_INIT(mData, aData);
933 if (!aSign) {
934 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
937 if (!mSymKey.Assign(aKey.GetSymKey())) {
938 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
939 return;
942 // Check that we got a symmetric key
943 if (mSymKey.Length() == 0) {
944 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
945 return;
948 TelemetryAlgorithm telemetryAlg;
949 switch (mMechanism) {
950 case CKM_SHA_1_HMAC:
951 telemetryAlg = TA_HMAC_SHA_1;
952 break;
953 case CKM_SHA224_HMAC:
954 telemetryAlg = TA_HMAC_SHA_224;
955 break;
956 case CKM_SHA256_HMAC:
957 telemetryAlg = TA_HMAC_SHA_256;
958 break;
959 case CKM_SHA384_HMAC:
960 telemetryAlg = TA_HMAC_SHA_384;
961 break;
962 case CKM_SHA512_HMAC:
963 telemetryAlg = TA_HMAC_SHA_512;
964 break;
965 default:
966 telemetryAlg = TA_UNKNOWN;
968 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
971 private:
972 CK_MECHANISM_TYPE mMechanism;
973 CryptoBuffer mSymKey;
974 CryptoBuffer mData;
975 CryptoBuffer mSignature;
976 CryptoBuffer mResult;
977 bool mSign;
979 virtual nsresult DoCrypto() override {
980 // Initialize the output buffer
981 if (!mResult.SetLength(HASH_LENGTH_MAX, fallible)) {
982 return NS_ERROR_DOM_UNKNOWN_ERR;
985 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
986 if (!arena) {
987 return NS_ERROR_DOM_OPERATION_ERR;
990 // Import the key
991 uint32_t outLen;
992 SECItem keyItem = {siBuffer, nullptr, 0};
993 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
994 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
995 MOZ_ASSERT(slot.get());
996 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
997 PK11_OriginUnwrap, CKA_SIGN,
998 &keyItem, nullptr));
999 if (!symKey) {
1000 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1003 // Compute the MAC
1004 SECItem param = {siBuffer, nullptr, 0};
1005 UniquePK11Context ctx(
1006 PK11_CreateContextBySymKey(mMechanism, CKA_SIGN, symKey.get(), &param));
1007 if (!ctx.get()) {
1008 return NS_ERROR_DOM_OPERATION_ERR;
1010 nsresult rv = MapSECStatus(PK11_DigestBegin(ctx.get()));
1011 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1012 rv = MapSECStatus(
1013 PK11_DigestOp(ctx.get(), mData.Elements(), mData.Length()));
1014 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1015 rv = MapSECStatus(PK11_DigestFinal(ctx.get(), mResult.Elements(), &outLen,
1016 mResult.Length()));
1017 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1019 mResult.TruncateLength(outLen);
1020 return rv;
1023 // Returns mResult as an ArrayBufferView, or an error
1024 virtual void Resolve() override {
1025 if (mSign) {
1026 // Return the computed MAC
1027 TypedArrayCreator<ArrayBuffer> ret(mResult);
1028 mResultPromise->MaybeResolve(ret);
1029 } else {
1030 // Compare the MAC to the provided signature
1031 // No truncation allowed
1032 bool equal = (mResult.Length() == mSignature.Length());
1033 if (equal) {
1034 int cmp = NSS_SecureMemcmp(mSignature.Elements(), mResult.Elements(),
1035 mSignature.Length());
1036 equal = (cmp == 0);
1038 mResultPromise->MaybeResolve(equal);
1043 class AsymmetricSignVerifyTask : public WebCryptoTask {
1044 public:
1045 AsymmetricSignVerifyTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1046 CryptoKey& aKey,
1047 const CryptoOperationData& aSignature,
1048 const CryptoOperationData& aData, bool aSign)
1049 : mOidTag(SEC_OID_UNKNOWN),
1050 mHashMechanism(UNKNOWN_CK_MECHANISM),
1051 mMgfMechanism(UNKNOWN_CK_MECHANISM),
1052 mPrivKey(aKey.GetPrivateKey()),
1053 mPubKey(aKey.GetPublicKey()),
1054 mSaltLength(0),
1055 mSign(aSign),
1056 mVerified(false),
1057 mAlgorithm(Algorithm::UNKNOWN) {
1058 ATTEMPT_BUFFER_INIT(mData, aData);
1059 if (!aSign) {
1060 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
1063 nsString algName;
1064 nsString hashAlgName;
1065 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1066 if (NS_FAILED(mEarlyRv)) {
1067 return;
1070 if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
1071 mAlgorithm = Algorithm::RSA_PKCS1;
1072 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
1073 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
1074 hashAlgName = aKey.Algorithm().mRsa.mHash.mName;
1075 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1076 mAlgorithm = Algorithm::RSA_PSS;
1077 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_PSS);
1078 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_PSS);
1080 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
1081 hashAlgName = hashAlg.mName;
1082 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
1083 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlgName);
1085 // Check we found appropriate mechanisms.
1086 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
1087 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
1088 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1089 return;
1092 RootedDictionary<RsaPssParams> params(aCx);
1093 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1094 if (NS_FAILED(mEarlyRv)) {
1095 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1096 return;
1099 mSaltLength = params.mSaltLength;
1100 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1101 mAlgorithm = Algorithm::ECDSA;
1102 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDSA);
1103 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
1105 // For ECDSA, the hash name comes from the algorithm parameter
1106 RootedDictionary<EcdsaParams> params(aCx);
1107 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1108 if (NS_FAILED(mEarlyRv)) {
1109 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1110 return;
1113 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashAlgName);
1114 if (NS_FAILED(mEarlyRv)) {
1115 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1116 return;
1118 } else {
1119 // This shouldn't happen; CreateSignVerifyTask shouldn't create
1120 // one of these unless it's for the above algorithms.
1121 MOZ_ASSERT(false);
1124 // Must have a valid algorithm by now.
1125 MOZ_ASSERT(mAlgorithm != Algorithm::UNKNOWN);
1127 // Determine hash algorithm to use.
1128 mOidTag = MapHashAlgorithmNameToOID(hashAlgName);
1129 if (mOidTag == SEC_OID_UNKNOWN) {
1130 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1131 return;
1134 // Check that we have the appropriate key
1135 if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
1136 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
1137 return;
1141 private:
1142 SECOidTag mOidTag;
1143 CK_MECHANISM_TYPE mHashMechanism;
1144 CK_MECHANISM_TYPE mMgfMechanism;
1145 UniqueSECKEYPrivateKey mPrivKey;
1146 UniqueSECKEYPublicKey mPubKey;
1147 CryptoBuffer mSignature;
1148 CryptoBuffer mData;
1149 uint32_t mSaltLength;
1150 bool mSign;
1151 bool mVerified;
1153 // The signature algorithm to use.
1154 enum class Algorithm : uint8_t { ECDSA, RSA_PKCS1, RSA_PSS, UNKNOWN };
1155 Algorithm mAlgorithm;
1157 virtual nsresult DoCrypto() override {
1158 SECStatus rv;
1159 UniqueSECItem hash(
1160 ::SECITEM_AllocItem(nullptr, nullptr, HASH_ResultLenByOidTag(mOidTag)));
1161 if (!hash) {
1162 return NS_ERROR_DOM_OPERATION_ERR;
1165 // Compute digest over given data.
1166 rv = PK11_HashBuf(mOidTag, hash->data, mData.Elements(), mData.Length());
1167 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1169 // Wrap hash in a digest info template (RSA-PKCS1 only).
1170 if (mAlgorithm == Algorithm::RSA_PKCS1) {
1171 UniqueSGNDigestInfo di(
1172 SGN_CreateDigestInfo(mOidTag, hash->data, hash->len));
1173 if (!di) {
1174 return NS_ERROR_DOM_OPERATION_ERR;
1177 // Reuse |hash|.
1178 SECITEM_FreeItem(hash.get(), false);
1179 if (!SEC_ASN1EncodeItem(nullptr, hash.get(), di.get(),
1180 SGN_DigestInfoTemplate)) {
1181 return NS_ERROR_DOM_OPERATION_ERR;
1185 SECItem* params = nullptr;
1186 CK_MECHANISM_TYPE mech =
1187 PK11_MapSignKeyType((mSign ? mPrivKey->keyType : mPubKey->keyType));
1189 CK_RSA_PKCS_PSS_PARAMS rsaPssParams;
1190 SECItem rsaPssParamsItem = {
1191 siBuffer,
1194 // Set up parameters for RSA-PSS.
1195 if (mAlgorithm == Algorithm::RSA_PSS) {
1196 rsaPssParams.hashAlg = mHashMechanism;
1197 rsaPssParams.mgf = mMgfMechanism;
1198 rsaPssParams.sLen = mSaltLength;
1200 rsaPssParamsItem.data = (unsigned char*)&rsaPssParams;
1201 rsaPssParamsItem.len = sizeof(rsaPssParams);
1202 params = &rsaPssParamsItem;
1204 mech = CKM_RSA_PKCS_PSS;
1207 // Allocate SECItem to hold the signature.
1208 uint32_t len = mSign ? PK11_SignatureLen(mPrivKey.get()) : 0;
1209 UniqueSECItem sig(::SECITEM_AllocItem(nullptr, nullptr, len));
1210 if (!sig) {
1211 return NS_ERROR_DOM_OPERATION_ERR;
1214 if (mSign) {
1215 // Sign the hash.
1216 rv = PK11_SignWithMechanism(mPrivKey.get(), mech, params, sig.get(),
1217 hash.get());
1218 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1219 ATTEMPT_BUFFER_ASSIGN(mSignature, sig.get());
1220 } else {
1221 // Copy the given signature to the SECItem.
1222 if (!mSignature.ToSECItem(nullptr, sig.get())) {
1223 return NS_ERROR_DOM_OPERATION_ERR;
1226 // Verify the signature.
1227 rv = PK11_VerifyWithMechanism(mPubKey.get(), mech, params, sig.get(),
1228 hash.get(), nullptr);
1229 mVerified = NS_SUCCEEDED(MapSECStatus(rv));
1232 return NS_OK;
1235 virtual void Resolve() override {
1236 if (mSign) {
1237 TypedArrayCreator<ArrayBuffer> ret(mSignature);
1238 mResultPromise->MaybeResolve(ret);
1239 } else {
1240 mResultPromise->MaybeResolve(mVerified);
1245 class DigestTask : public ReturnArrayBufferViewTask {
1246 public:
1247 DigestTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1248 const CryptoOperationData& aData) {
1249 ATTEMPT_BUFFER_INIT(mData, aData);
1251 nsString algName;
1252 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1253 if (NS_FAILED(mEarlyRv)) {
1254 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1255 return;
1258 TelemetryAlgorithm telemetryAlg;
1259 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
1260 telemetryAlg = TA_SHA_1;
1261 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
1262 telemetryAlg = TA_SHA_224;
1263 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
1264 telemetryAlg = TA_SHA_256;
1265 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
1266 telemetryAlg = TA_SHA_384;
1267 } else {
1268 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1269 return;
1271 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
1272 mOidTag = MapHashAlgorithmNameToOID(algName);
1275 private:
1276 SECOidTag mOidTag;
1277 CryptoBuffer mData;
1279 virtual nsresult DoCrypto() override {
1280 // Resize the result buffer
1281 uint32_t hashLen = HASH_ResultLenByOidTag(mOidTag);
1282 if (!mResult.SetLength(hashLen, fallible)) {
1283 return NS_ERROR_DOM_UNKNOWN_ERR;
1286 // Compute the hash
1287 nsresult rv = MapSECStatus(PK11_HashBuf(mOidTag, mResult.Elements(),
1288 mData.Elements(), mData.Length()));
1289 if (NS_FAILED(rv)) {
1290 return NS_ERROR_DOM_UNKNOWN_ERR;
1293 return rv;
1297 class ImportKeyTask : public WebCryptoTask {
1298 public:
1299 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1300 const ObjectOrString& aAlgorithm, bool aExtractable,
1301 const Sequence<nsString>& aKeyUsages) {
1302 mFormat = aFormat;
1303 mDataIsSet = false;
1304 mDataIsJwk = false;
1306 // This stuff pretty much always happens, so we'll do it here
1307 mKey = new CryptoKey(aGlobal);
1308 mKey->SetExtractable(aExtractable);
1309 mKey->ClearUsages();
1310 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
1311 mEarlyRv = mKey->AddUsage(aKeyUsages[i]);
1312 if (NS_FAILED(mEarlyRv)) {
1313 return;
1317 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
1318 if (NS_FAILED(mEarlyRv)) {
1319 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1320 return;
1324 static bool JwkCompatible(const JsonWebKey& aJwk, const CryptoKey* aKey) {
1325 // Check 'ext'
1326 if (aKey->Extractable() && aJwk.mExt.WasPassed() && !aJwk.mExt.Value()) {
1327 return false;
1330 // Check 'alg'
1331 if (aJwk.mAlg.WasPassed() &&
1332 aJwk.mAlg.Value() != aKey->Algorithm().JwkAlg()) {
1333 return false;
1336 // Check 'key_ops'
1337 if (aJwk.mKey_ops.WasPassed()) {
1338 nsTArray<nsString> usages;
1339 aKey->GetUsages(usages);
1340 for (size_t i = 0; i < usages.Length(); ++i) {
1341 if (!aJwk.mKey_ops.Value().Contains(usages[i])) {
1342 return false;
1347 // Individual algorithms may still have to check 'use'
1348 return true;
1351 void SetKeyData(JSContext* aCx, JS::Handle<JSObject*> aKeyData) {
1352 mDataIsJwk = false;
1354 // Try ArrayBuffer
1355 RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
1356 if (ab.Init(aKeyData)) {
1357 if (!mKeyData.Assign(ab)) {
1358 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1360 return;
1363 // Try ArrayBufferView
1364 RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
1365 if (abv.Init(aKeyData)) {
1366 if (!mKeyData.Assign(abv)) {
1367 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1369 return;
1372 // Try JWK
1373 ClearException ce(aCx);
1374 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aKeyData));
1375 if (!mJwk.Init(aCx, value)) {
1376 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1377 return;
1380 mDataIsJwk = true;
1383 void SetKeyDataMaybeParseJWK(const CryptoBuffer& aKeyData) {
1384 if (!mKeyData.Assign(aKeyData)) {
1385 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1386 return;
1389 mDataIsJwk = false;
1391 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1392 nsDependentCSubstring utf8(
1393 (const char*)mKeyData.Elements(),
1394 (const char*)(mKeyData.Elements() + mKeyData.Length()));
1395 if (!IsUtf8(utf8)) {
1396 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1397 return;
1400 nsString json = NS_ConvertUTF8toUTF16(utf8);
1401 if (!mJwk.Init(json)) {
1402 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1403 return;
1406 mDataIsJwk = true;
1410 void SetRawKeyData(const CryptoBuffer& aKeyData) {
1411 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1412 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1413 return;
1416 if (!mKeyData.Assign(aKeyData)) {
1417 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1418 return;
1421 mDataIsJwk = false;
1424 protected:
1425 nsString mFormat;
1426 RefPtr<CryptoKey> mKey;
1427 CryptoBuffer mKeyData;
1428 bool mDataIsSet;
1429 bool mDataIsJwk;
1430 JsonWebKey mJwk;
1431 nsString mAlgName;
1433 private:
1434 virtual void Resolve() override { mResultPromise->MaybeResolve(mKey); }
1436 virtual void Cleanup() override { mKey = nullptr; }
1439 class ImportSymmetricKeyTask : public ImportKeyTask {
1440 public:
1441 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1442 const nsAString& aFormat,
1443 const ObjectOrString& aAlgorithm, bool aExtractable,
1444 const Sequence<nsString>& aKeyUsages) {
1445 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1448 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1449 const nsAString& aFormat,
1450 const JS::Handle<JSObject*> aKeyData,
1451 const ObjectOrString& aAlgorithm, bool aExtractable,
1452 const Sequence<nsString>& aKeyUsages) {
1453 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1454 if (NS_FAILED(mEarlyRv)) {
1455 return;
1458 SetKeyData(aCx, aKeyData);
1459 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1460 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1461 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1462 return;
1466 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1467 const ObjectOrString& aAlgorithm, bool aExtractable,
1468 const Sequence<nsString>& aKeyUsages) {
1469 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1470 aKeyUsages);
1471 if (NS_FAILED(mEarlyRv)) {
1472 return;
1475 // This task only supports raw and JWK format.
1476 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1477 !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1478 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1479 return;
1482 // If this is an HMAC key, import the hash name
1483 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1484 RootedDictionary<HmacImportParams> params(aCx);
1485 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1486 if (NS_FAILED(mEarlyRv)) {
1487 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1488 return;
1490 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1491 if (NS_FAILED(mEarlyRv)) {
1492 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1493 return;
1498 virtual nsresult BeforeCrypto() override {
1499 nsresult rv;
1501 // If we're doing a JWK import, import the key data
1502 if (mDataIsJwk) {
1503 if (!mJwk.mK.WasPassed()) {
1504 return NS_ERROR_DOM_DATA_ERR;
1507 // Import the key material
1508 rv = mKeyData.FromJwkBase64(mJwk.mK.Value());
1509 if (NS_FAILED(rv)) {
1510 return NS_ERROR_DOM_DATA_ERR;
1513 // Check that we have valid key data.
1514 if (mKeyData.Length() == 0 &&
1515 !mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
1516 return NS_ERROR_DOM_DATA_ERR;
1519 // Construct an appropriate KeyAlorithm,
1520 // and verify that usages are appropriate
1521 if (mKeyData.Length() > UINT32_MAX / 8) {
1522 return NS_ERROR_DOM_DATA_ERR;
1524 uint32_t length = 8 * mKeyData.Length(); // bytes to bits
1525 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
1526 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
1527 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
1528 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
1529 if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
1530 CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1531 return NS_ERROR_DOM_DATA_ERR;
1534 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) &&
1535 mKey->HasUsageOtherThan(CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1536 return NS_ERROR_DOM_DATA_ERR;
1539 if ((length != 128) && (length != 192) && (length != 256)) {
1540 return NS_ERROR_DOM_DATA_ERR;
1542 mKey->Algorithm().MakeAes(mAlgName, length);
1544 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1545 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_ENC)) {
1546 return NS_ERROR_DOM_DATA_ERR;
1548 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
1549 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
1550 if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY |
1551 CryptoKey::DERIVEBITS)) {
1552 return NS_ERROR_DOM_DATA_ERR;
1554 mKey->Algorithm().MakeAes(mAlgName, length);
1556 if (mDataIsJwk && mJwk.mUse.WasPassed()) {
1557 // There is not a 'use' value consistent with PBKDF or HKDF
1558 return NS_ERROR_DOM_DATA_ERR;
1560 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1561 if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) {
1562 return NS_ERROR_DOM_DATA_ERR;
1565 mKey->Algorithm().MakeHmac(length, mHashName);
1567 if (mKey->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM) {
1568 return NS_ERROR_DOM_SYNTAX_ERR;
1571 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1572 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_SIG)) {
1573 return NS_ERROR_DOM_DATA_ERR;
1575 } else {
1576 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1579 if (!mKey->HasAnyUsage()) {
1580 return NS_ERROR_DOM_SYNTAX_ERR;
1583 if (NS_FAILED(mKey->SetSymKey(mKeyData))) {
1584 return NS_ERROR_DOM_OPERATION_ERR;
1587 mKey->SetType(CryptoKey::SECRET);
1589 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1590 return NS_ERROR_DOM_DATA_ERR;
1593 mEarlyComplete = true;
1594 return NS_OK;
1597 private:
1598 nsString mHashName;
1601 class ImportRsaKeyTask : public ImportKeyTask {
1602 public:
1603 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1604 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1605 bool aExtractable, const Sequence<nsString>& aKeyUsages)
1606 : mModulusLength(0) {
1607 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1610 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1611 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1612 const ObjectOrString& aAlgorithm, bool aExtractable,
1613 const Sequence<nsString>& aKeyUsages)
1614 : mModulusLength(0) {
1615 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1616 if (NS_FAILED(mEarlyRv)) {
1617 return;
1620 SetKeyData(aCx, aKeyData);
1621 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1622 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1623 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1624 return;
1628 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1629 const ObjectOrString& aAlgorithm, bool aExtractable,
1630 const Sequence<nsString>& aKeyUsages) {
1631 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1632 aKeyUsages);
1633 if (NS_FAILED(mEarlyRv)) {
1634 return;
1637 // If this is RSA with a hash, cache the hash name
1638 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1639 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
1640 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1641 RootedDictionary<RsaHashedImportParams> params(aCx);
1642 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1643 if (NS_FAILED(mEarlyRv)) {
1644 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1645 return;
1648 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1649 if (NS_FAILED(mEarlyRv)) {
1650 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1651 return;
1655 // Check support for the algorithm and hash names
1656 CK_MECHANISM_TYPE mech1 = MapAlgorithmNameToMechanism(mAlgName);
1657 CK_MECHANISM_TYPE mech2 = MapAlgorithmNameToMechanism(mHashName);
1658 if ((mech1 == UNKNOWN_CK_MECHANISM) || (mech2 == UNKNOWN_CK_MECHANISM)) {
1659 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1660 return;
1664 private:
1665 nsString mHashName;
1666 uint32_t mModulusLength;
1667 CryptoBuffer mPublicExponent;
1669 virtual nsresult DoCrypto() override {
1670 // Import the key data itself
1671 UniqueSECKEYPublicKey pubKey;
1672 UniqueSECKEYPrivateKey privKey;
1673 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1674 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1675 !mJwk.mD.WasPassed())) {
1676 // Public key import
1677 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1678 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1679 } else {
1680 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1683 if (!pubKey) {
1684 return NS_ERROR_DOM_DATA_ERR;
1687 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1688 return NS_ERROR_DOM_OPERATION_ERR;
1691 mKey->SetType(CryptoKey::PUBLIC);
1692 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) ||
1693 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1694 mJwk.mD.WasPassed())) {
1695 // Private key import
1696 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1697 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
1698 } else {
1699 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1702 if (!privKey) {
1703 return NS_ERROR_DOM_DATA_ERR;
1706 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1707 return NS_ERROR_DOM_OPERATION_ERR;
1710 mKey->SetType(CryptoKey::PRIVATE);
1711 pubKey = UniqueSECKEYPublicKey(SECKEY_ConvertToPublicKey(privKey.get()));
1712 if (!pubKey) {
1713 return NS_ERROR_DOM_UNKNOWN_ERR;
1715 } else {
1716 // Invalid key format
1717 return NS_ERROR_DOM_SYNTAX_ERR;
1720 if (pubKey->keyType != rsaKey) {
1721 return NS_ERROR_DOM_DATA_ERR;
1723 // Extract relevant information from the public key
1724 mModulusLength = 8 * pubKey->u.rsa.modulus.len;
1725 if (!mPublicExponent.Assign(&pubKey->u.rsa.publicExponent)) {
1726 return NS_ERROR_DOM_OPERATION_ERR;
1729 return NS_OK;
1732 virtual nsresult AfterCrypto() override {
1733 // Check permissions for the requested operation
1734 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
1735 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1736 mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) ||
1737 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1738 mKey->HasUsageOtherThan(CryptoKey::DECRYPT |
1739 CryptoKey::UNWRAPKEY))) {
1740 return NS_ERROR_DOM_DATA_ERR;
1742 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1743 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1744 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1745 mKey->HasUsageOtherThan(CryptoKey::VERIFY)) ||
1746 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1747 mKey->HasUsageOtherThan(CryptoKey::SIGN))) {
1748 return NS_ERROR_DOM_DATA_ERR;
1752 if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
1753 return NS_ERROR_DOM_SYNTAX_ERR;
1756 // Set an appropriate KeyAlgorithm
1757 if (!mKey->Algorithm().MakeRsa(mAlgName, mModulusLength, mPublicExponent,
1758 mHashName)) {
1759 return NS_ERROR_DOM_OPERATION_ERR;
1762 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1763 return NS_ERROR_DOM_DATA_ERR;
1766 return NS_OK;
1770 class ImportEcKeyTask : public ImportKeyTask {
1771 public:
1772 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1773 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1774 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
1775 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1778 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1779 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1780 const ObjectOrString& aAlgorithm, bool aExtractable,
1781 const Sequence<nsString>& aKeyUsages) {
1782 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1783 if (NS_FAILED(mEarlyRv)) {
1784 return;
1787 SetKeyData(aCx, aKeyData);
1788 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1791 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1792 const ObjectOrString& aAlgorithm, bool aExtractable,
1793 const Sequence<nsString>& aKeyUsages) {
1794 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1795 aKeyUsages);
1796 if (NS_FAILED(mEarlyRv)) {
1797 return;
1800 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1801 RootedDictionary<EcKeyImportParams> params(aCx);
1802 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1803 if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
1804 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1805 return;
1808 if (!NormalizeToken(params.mNamedCurve.Value(), mNamedCurve)) {
1809 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1810 return;
1815 private:
1816 nsString mNamedCurve;
1818 virtual nsresult DoCrypto() override {
1819 // Import the key data itself
1820 UniqueSECKEYPublicKey pubKey;
1821 UniqueSECKEYPrivateKey privKey;
1823 if ((mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1824 mJwk.mD.WasPassed()) ||
1825 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1826 // Private key import
1827 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1828 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1829 if (!privKey) {
1830 return NS_ERROR_DOM_DATA_ERR;
1832 } else {
1833 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
1834 if (!privKey) {
1835 return NS_ERROR_DOM_DATA_ERR;
1838 ScopedAutoSECItem ecParams;
1839 if (PK11_ReadRawAttribute(PK11_TypePrivKey, privKey.get(),
1840 CKA_EC_PARAMS, &ecParams) != SECSuccess) {
1841 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1843 // Construct the OID tag.
1844 SECItem oid = {siBuffer, nullptr, 0};
1845 oid.len = ecParams.data[1];
1846 oid.data = ecParams.data + 2;
1847 // Find a matching and supported named curve.
1848 if (!MapOIDTagToNamedCurve(SECOID_FindOIDTag(&oid), mNamedCurve)) {
1849 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1853 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1854 return NS_ERROR_DOM_OPERATION_ERR;
1857 mKey->SetType(CryptoKey::PRIVATE);
1858 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
1859 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1860 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1861 !mJwk.mD.WasPassed())) {
1862 // Public key import
1863 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1864 pubKey = CryptoKey::PublicECKeyFromRaw(mKeyData, mNamedCurve);
1865 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1866 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1867 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1868 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1869 } else {
1870 MOZ_ASSERT(false);
1873 if (!pubKey) {
1874 return NS_ERROR_DOM_DATA_ERR;
1877 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1878 if (pubKey->keyType != ecKey) {
1879 return NS_ERROR_DOM_DATA_ERR;
1881 if (!CheckEncodedECParameters(&pubKey->u.ec.DEREncodedParams)) {
1882 return NS_ERROR_DOM_OPERATION_ERR;
1885 // Construct the OID tag.
1886 SECItem oid = {siBuffer, nullptr, 0};
1887 oid.len = pubKey->u.ec.DEREncodedParams.data[1];
1888 oid.data = pubKey->u.ec.DEREncodedParams.data + 2;
1890 // Find a matching and supported named curve.
1891 if (!MapOIDTagToNamedCurve(SECOID_FindOIDTag(&oid), mNamedCurve)) {
1892 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1896 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1897 return NS_ERROR_DOM_OPERATION_ERR;
1900 mKey->SetType(CryptoKey::PUBLIC);
1901 } else {
1902 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1905 // Extract 'crv' parameter from JWKs.
1906 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1907 if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) {
1908 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1911 return NS_OK;
1914 virtual nsresult AfterCrypto() override {
1915 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
1916 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
1917 privateAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
1918 publicAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
1919 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1920 privateAllowedUsages = CryptoKey::SIGN;
1921 publicAllowedUsages = CryptoKey::VERIFY;
1924 // Check permissions for the requested operation
1925 if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
1926 mKey->HasUsageOtherThan(privateAllowedUsages)) ||
1927 (mKey->GetKeyType() == CryptoKey::PUBLIC &&
1928 mKey->HasUsageOtherThan(publicAllowedUsages))) {
1929 return NS_ERROR_DOM_DATA_ERR;
1932 if (mKey->GetKeyType() == CryptoKey::PRIVATE && !mKey->HasAnyUsage()) {
1933 return NS_ERROR_DOM_SYNTAX_ERR;
1936 mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
1938 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1939 return NS_ERROR_DOM_DATA_ERR;
1942 return NS_OK;
1946 class ExportKeyTask : public WebCryptoTask {
1947 public:
1948 ExportKeyTask(const nsAString& aFormat, CryptoKey& aKey)
1949 : mFormat(aFormat),
1950 mPrivateKey(aKey.GetPrivateKey()),
1951 mPublicKey(aKey.GetPublicKey()),
1952 mKeyType(aKey.GetKeyType()),
1953 mExtractable(aKey.Extractable()),
1954 mAlg(aKey.Algorithm().JwkAlg()) {
1955 aKey.GetUsages(mKeyUsages);
1957 if (!mSymKey.Assign(aKey.GetSymKey())) {
1958 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
1959 return;
1963 protected:
1964 nsString mFormat;
1965 CryptoBuffer mSymKey;
1966 UniqueSECKEYPrivateKey mPrivateKey;
1967 UniqueSECKEYPublicKey mPublicKey;
1968 CryptoKey::KeyType mKeyType;
1969 bool mExtractable;
1970 nsString mAlg;
1971 nsTArray<nsString> mKeyUsages;
1972 CryptoBuffer mResult;
1973 JsonWebKey mJwk;
1975 private:
1976 virtual nsresult DoCrypto() override {
1977 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1978 if (mPublicKey && mPublicKey->keyType == dhKey) {
1979 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1982 if (mPublicKey && mPublicKey->keyType == ecKey) {
1983 nsresult rv = CryptoKey::PublicECKeyToRaw(mPublicKey.get(), mResult);
1984 if (NS_FAILED(rv)) {
1985 return NS_ERROR_DOM_OPERATION_ERR;
1987 return NS_OK;
1990 if (!mResult.Assign(mSymKey)) {
1991 return NS_ERROR_OUT_OF_MEMORY;
1993 if (mResult.Length() == 0) {
1994 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1997 return NS_OK;
1998 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1999 if (!mPrivateKey) {
2000 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2003 switch (mPrivateKey->keyType) {
2004 case rsaKey:
2005 case ecKey: {
2006 nsresult rv =
2007 CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), mResult);
2008 if (NS_FAILED(rv)) {
2009 return NS_ERROR_DOM_OPERATION_ERR;
2011 return NS_OK;
2013 default:
2014 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2016 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
2017 if (!mPublicKey) {
2018 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2021 return CryptoKey::PublicKeyToSpki(mPublicKey.get(), mResult);
2022 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2023 if (mKeyType == CryptoKey::SECRET) {
2024 nsString k;
2025 nsresult rv = mSymKey.ToJwkBase64(k);
2026 if (NS_FAILED(rv)) {
2027 return NS_ERROR_DOM_OPERATION_ERR;
2029 mJwk.mK.Construct(k);
2030 mJwk.mKty = NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_SYMMETRIC);
2031 } else if (mKeyType == CryptoKey::PUBLIC) {
2032 if (!mPublicKey) {
2033 return NS_ERROR_DOM_UNKNOWN_ERR;
2036 nsresult rv = CryptoKey::PublicKeyToJwk(mPublicKey.get(), mJwk);
2037 if (NS_FAILED(rv)) {
2038 return NS_ERROR_DOM_OPERATION_ERR;
2040 } else if (mKeyType == CryptoKey::PRIVATE) {
2041 if (!mPrivateKey) {
2042 return NS_ERROR_DOM_UNKNOWN_ERR;
2045 nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey.get(), mJwk);
2046 if (NS_FAILED(rv)) {
2047 return NS_ERROR_DOM_OPERATION_ERR;
2051 if (!mAlg.IsEmpty()) {
2052 mJwk.mAlg.Construct(mAlg);
2055 mJwk.mExt.Construct(mExtractable);
2057 mJwk.mKey_ops.Construct();
2058 if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
2059 return NS_ERROR_OUT_OF_MEMORY;
2062 return NS_OK;
2065 return NS_ERROR_DOM_SYNTAX_ERR;
2068 // Returns mResult as an ArrayBufferView or JWK, as appropriate
2069 virtual void Resolve() override {
2070 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2071 mResultPromise->MaybeResolve(mJwk);
2072 return;
2075 TypedArrayCreator<ArrayBuffer> ret(mResult);
2076 mResultPromise->MaybeResolve(ret);
2080 class GenerateSymmetricKeyTask : public WebCryptoTask {
2081 public:
2082 GenerateSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2083 const ObjectOrString& aAlgorithm, bool aExtractable,
2084 const Sequence<nsString>& aKeyUsages) {
2085 // Create an empty key and set easy attributes
2086 mKey = new CryptoKey(aGlobal);
2087 mKey->SetExtractable(aExtractable);
2088 mKey->SetType(CryptoKey::SECRET);
2090 // Extract algorithm name
2091 nsString algName;
2092 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
2093 if (NS_FAILED(mEarlyRv)) {
2094 return;
2097 // Construct an appropriate KeyAlorithm
2098 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2099 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2100 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2101 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
2102 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aAlgorithm, mLength);
2103 if (NS_FAILED(mEarlyRv)) {
2104 return;
2106 mKey->Algorithm().MakeAes(algName, mLength);
2108 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2109 RootedDictionary<HmacKeyGenParams> params(aCx);
2110 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2111 if (NS_FAILED(mEarlyRv)) {
2112 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2113 return;
2116 nsString hashName;
2117 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2118 if (NS_FAILED(mEarlyRv)) {
2119 return;
2122 if (params.mLength.WasPassed()) {
2123 mLength = params.mLength.Value();
2124 } else {
2125 mLength = MapHashAlgorithmNameToBlockSize(hashName);
2128 if (mLength == 0) {
2129 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2130 return;
2133 mKey->Algorithm().MakeHmac(mLength, hashName);
2134 } else {
2135 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2136 return;
2139 // Add key usages
2140 mKey->ClearUsages();
2141 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2142 mEarlyRv = mKey->AddAllowedUsageIntersecting(aKeyUsages[i], algName);
2143 if (NS_FAILED(mEarlyRv)) {
2144 return;
2147 if (!mKey->HasAnyUsage()) {
2148 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2149 return;
2152 mLength = mLength >> 3; // bits to bytes
2153 mMechanism = mKey->Algorithm().Mechanism();
2154 // SetSymKey done in Resolve, after we've done the keygen
2157 private:
2158 RefPtr<CryptoKey> mKey;
2159 size_t mLength;
2160 CK_MECHANISM_TYPE mMechanism;
2161 CryptoBuffer mKeyData;
2163 virtual nsresult DoCrypto() override {
2164 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2165 MOZ_ASSERT(slot.get());
2167 UniquePK11SymKey symKey(
2168 PK11_KeyGen(slot.get(), mMechanism, nullptr, mLength, nullptr));
2169 if (!symKey) {
2170 return NS_ERROR_DOM_UNKNOWN_ERR;
2173 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2174 if (NS_FAILED(rv)) {
2175 return NS_ERROR_DOM_UNKNOWN_ERR;
2178 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2179 // just refers to a buffer managed by symKey. The assignment copies the
2180 // data, so mKeyData manages one copy, while symKey manages another.
2181 ATTEMPT_BUFFER_ASSIGN(mKeyData, PK11_GetKeyData(symKey.get()));
2182 return NS_OK;
2185 virtual void Resolve() override {
2186 if (NS_SUCCEEDED(mKey->SetSymKey(mKeyData))) {
2187 mResultPromise->MaybeResolve(mKey);
2188 } else {
2189 mResultPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
2193 virtual void Cleanup() override { mKey = nullptr; }
2196 GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask(
2197 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
2198 bool aExtractable, const Sequence<nsString>& aKeyUsages)
2199 : mKeyPair(new CryptoKeyPair()),
2200 mMechanism(CKM_INVALID_MECHANISM),
2201 mRsaParams(),
2202 mDhParams() {
2203 mArena = UniquePLArenaPool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2204 if (!mArena) {
2205 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
2206 return;
2209 // Create an empty key pair and set easy attributes
2210 mKeyPair->mPrivateKey = new CryptoKey(aGlobal);
2211 mKeyPair->mPublicKey = new CryptoKey(aGlobal);
2213 // Extract algorithm name
2214 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
2215 if (NS_FAILED(mEarlyRv)) {
2216 return;
2219 // Construct an appropriate KeyAlorithm
2220 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
2221 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2222 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
2223 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
2224 RootedDictionary<RsaHashedKeyGenParams> params(aCx);
2225 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2226 if (NS_FAILED(mEarlyRv)) {
2227 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2228 return;
2231 // Pull relevant info
2232 uint32_t modulusLength = params.mModulusLength;
2233 CryptoBuffer publicExponent;
2234 ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
2235 nsString hashName;
2236 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2237 if (NS_FAILED(mEarlyRv)) {
2238 return;
2241 // Create algorithm
2242 if (!mKeyPair->mPublicKey->Algorithm().MakeRsa(mAlgName, modulusLength,
2243 publicExponent, hashName)) {
2244 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2245 return;
2247 if (!mKeyPair->mPrivateKey->Algorithm().MakeRsa(mAlgName, modulusLength,
2248 publicExponent, hashName)) {
2249 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2250 return;
2252 mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
2254 // Set up params struct
2255 mRsaParams.keySizeInBits = modulusLength;
2256 bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
2257 if (!converted) {
2258 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2259 return;
2261 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
2262 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2263 RootedDictionary<EcKeyGenParams> params(aCx);
2264 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2265 if (NS_FAILED(mEarlyRv)) {
2266 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2267 return;
2270 if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
2271 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2272 return;
2275 // Create algorithm.
2276 mKeyPair->mPublicKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2277 mKeyPair->mPrivateKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2278 mMechanism = CKM_EC_KEY_PAIR_GEN;
2279 } else {
2280 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2281 return;
2284 // Set key usages.
2285 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2286 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
2287 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2288 privateAllowedUsages = CryptoKey::SIGN;
2289 publicAllowedUsages = CryptoKey::VERIFY;
2290 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
2291 privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
2292 publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
2293 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
2294 privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
2295 publicAllowedUsages = 0;
2296 } else {
2297 MOZ_ASSERT(false); // This shouldn't happen.
2300 mKeyPair->mPrivateKey->SetExtractable(aExtractable);
2301 mKeyPair->mPrivateKey->SetType(CryptoKey::PRIVATE);
2303 mKeyPair->mPublicKey->SetExtractable(true);
2304 mKeyPair->mPublicKey->SetType(CryptoKey::PUBLIC);
2306 mKeyPair->mPrivateKey->ClearUsages();
2307 mKeyPair->mPublicKey->ClearUsages();
2308 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2309 mEarlyRv = mKeyPair->mPrivateKey->AddAllowedUsageIntersecting(
2310 aKeyUsages[i], mAlgName, privateAllowedUsages);
2311 if (NS_FAILED(mEarlyRv)) {
2312 return;
2315 mEarlyRv = mKeyPair->mPublicKey->AddAllowedUsageIntersecting(
2316 aKeyUsages[i], mAlgName, publicAllowedUsages);
2317 if (NS_FAILED(mEarlyRv)) {
2318 return;
2323 nsresult GenerateAsymmetricKeyTask::DoCrypto() {
2324 MOZ_ASSERT(mKeyPair);
2326 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2327 MOZ_ASSERT(slot.get());
2329 void* param;
2330 switch (mMechanism) {
2331 case CKM_RSA_PKCS_KEY_PAIR_GEN:
2332 param = &mRsaParams;
2333 break;
2334 case CKM_DH_PKCS_KEY_PAIR_GEN:
2335 param = &mDhParams;
2336 break;
2337 case CKM_EC_KEY_PAIR_GEN: {
2338 param = CreateECParamsForCurve(mNamedCurve, mArena.get());
2339 if (!param) {
2340 return NS_ERROR_DOM_UNKNOWN_ERR;
2342 break;
2344 default:
2345 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2348 SECKEYPublicKey* pubKey = nullptr;
2349 mPrivateKey = UniqueSECKEYPrivateKey(PK11_GenerateKeyPair(
2350 slot.get(), mMechanism, param, &pubKey, PR_FALSE, PR_FALSE, nullptr));
2351 mPublicKey = UniqueSECKEYPublicKey(pubKey);
2352 pubKey = nullptr;
2353 if (!mPrivateKey.get() || !mPublicKey.get()) {
2354 return NS_ERROR_DOM_OPERATION_ERR;
2357 // If no usages ended up being allowed, SyntaxError
2358 if (!mKeyPair->mPrivateKey->HasAnyUsage()) {
2359 return NS_ERROR_DOM_SYNTAX_ERR;
2362 nsresult rv = mKeyPair->mPrivateKey->SetPrivateKey(mPrivateKey.get());
2363 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2364 rv = mKeyPair->mPublicKey->SetPublicKey(mPublicKey.get());
2365 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2367 // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
2368 // private key, we need this later when exporting to PKCS8 and JWK though.
2369 if (mMechanism == CKM_EC_KEY_PAIR_GEN) {
2370 rv = mKeyPair->mPrivateKey->AddPublicKeyData(mPublicKey.get());
2371 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2374 return NS_OK;
2377 void GenerateAsymmetricKeyTask::Resolve() {
2378 mResultPromise->MaybeResolve(*mKeyPair);
2381 void GenerateAsymmetricKeyTask::Cleanup() { mKeyPair = nullptr; }
2383 class DeriveHkdfBitsTask : public ReturnArrayBufferViewTask {
2384 public:
2385 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2386 CryptoKey& aKey, uint32_t aLength)
2387 : mMechanism(CKM_INVALID_MECHANISM) {
2388 Init(aCx, aAlgorithm, aKey, aLength);
2391 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2392 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2393 : mLengthInBits(0), mLengthInBytes(0), mMechanism(CKM_INVALID_MECHANISM) {
2394 size_t length;
2395 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2397 if (NS_SUCCEEDED(mEarlyRv)) {
2398 Init(aCx, aAlgorithm, aKey, length);
2402 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2403 uint32_t aLength) {
2404 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_HKDF);
2405 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HKDF);
2407 if (!mSymKey.Assign(aKey.GetSymKey())) {
2408 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2409 return;
2412 // Check that we have a key.
2413 if (mSymKey.Length() == 0) {
2414 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2415 return;
2418 RootedDictionary<HkdfParams> params(aCx);
2419 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2420 if (NS_FAILED(mEarlyRv)) {
2421 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2422 return;
2425 // length must be greater than zero.
2426 if (aLength == 0) {
2427 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2428 return;
2431 // Extract the hash algorithm.
2432 nsString hashName;
2433 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2434 if (NS_FAILED(mEarlyRv)) {
2435 return;
2438 // Check the given hash algorithm.
2439 switch (MapAlgorithmNameToMechanism(hashName)) {
2440 case CKM_SHA_1:
2441 mMechanism = CKM_NSS_HKDF_SHA1;
2442 break;
2443 case CKM_SHA256:
2444 mMechanism = CKM_NSS_HKDF_SHA256;
2445 break;
2446 case CKM_SHA384:
2447 mMechanism = CKM_NSS_HKDF_SHA384;
2448 break;
2449 case CKM_SHA512:
2450 mMechanism = CKM_NSS_HKDF_SHA512;
2451 break;
2452 default:
2453 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2454 return;
2457 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2458 ATTEMPT_BUFFER_INIT(mInfo, params.mInfo)
2459 mLengthInBytes = ceil((double)aLength / 8);
2460 mLengthInBits = aLength;
2463 private:
2464 size_t mLengthInBits;
2465 size_t mLengthInBytes;
2466 CryptoBuffer mSalt;
2467 CryptoBuffer mInfo;
2468 CryptoBuffer mSymKey;
2469 CK_MECHANISM_TYPE mMechanism;
2471 virtual nsresult DoCrypto() override {
2472 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2473 if (!arena) {
2474 return NS_ERROR_DOM_OPERATION_ERR;
2477 // Import the key
2478 SECItem keyItem = {siBuffer, nullptr, 0};
2479 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2481 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2482 if (!slot.get()) {
2483 return NS_ERROR_DOM_OPERATION_ERR;
2486 UniquePK11SymKey baseKey(PK11_ImportSymKey(slot.get(), mMechanism,
2487 PK11_OriginUnwrap, CKA_WRAP,
2488 &keyItem, nullptr));
2489 if (!baseKey) {
2490 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2493 SECItem salt = {siBuffer, nullptr, 0};
2494 SECItem info = {siBuffer, nullptr, 0};
2495 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2496 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &info, mInfo);
2498 CK_NSS_HKDFParams hkdfParams = {true, salt.data, salt.len,
2499 true, info.data, info.len};
2500 SECItem params = {siBuffer, (unsigned char*)&hkdfParams,
2501 sizeof(hkdfParams)};
2503 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2504 // derived symmetric key and don't matter because we ignore them anyway.
2505 UniquePK11SymKey symKey(PK11_Derive(baseKey.get(), mMechanism, &params,
2506 CKM_SHA512_HMAC, CKA_SIGN,
2507 mLengthInBytes));
2509 if (!symKey.get()) {
2510 return NS_ERROR_DOM_OPERATION_ERR;
2513 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2514 if (NS_FAILED(rv)) {
2515 return NS_ERROR_DOM_OPERATION_ERR;
2518 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2519 // just refers to a buffer managed by symKey. The assignment copies the
2520 // data, so mResult manages one copy, while symKey manages another.
2521 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2523 if (mLengthInBytes > mResult.Length()) {
2524 return NS_ERROR_DOM_DATA_ERR;
2527 if (!mResult.SetLength(mLengthInBytes, fallible)) {
2528 return NS_ERROR_DOM_UNKNOWN_ERR;
2531 // If the number of bits to derive is not a multiple of 8 we need to
2532 // zero out the remaining bits that were derived but not requested.
2533 if (mLengthInBits % 8) {
2534 mResult[mResult.Length() - 1] &= 0xff << (mLengthInBits % 8);
2537 return NS_OK;
2541 class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask {
2542 public:
2543 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2544 CryptoKey& aKey, uint32_t aLength)
2545 : mHashOidTag(SEC_OID_UNKNOWN) {
2546 Init(aCx, aAlgorithm, aKey, aLength);
2549 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2550 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2551 : mLength(0), mIterations(0), mHashOidTag(SEC_OID_UNKNOWN) {
2552 size_t length;
2553 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2555 if (NS_SUCCEEDED(mEarlyRv)) {
2556 Init(aCx, aAlgorithm, aKey, length);
2560 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2561 uint32_t aLength) {
2562 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_PBKDF2);
2563 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_PBKDF2);
2565 if (!mSymKey.Assign(aKey.GetSymKey())) {
2566 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2567 return;
2570 RootedDictionary<Pbkdf2Params> params(aCx);
2571 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2572 if (NS_FAILED(mEarlyRv)) {
2573 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2574 return;
2577 // length must be a multiple of 8 bigger than zero.
2578 if (aLength == 0 || aLength % 8) {
2579 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2580 return;
2583 // Extract the hash algorithm.
2584 nsString hashName;
2585 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2586 if (NS_FAILED(mEarlyRv)) {
2587 return;
2590 // Check the given hash algorithm.
2591 switch (MapAlgorithmNameToMechanism(hashName)) {
2592 case CKM_SHA_1:
2593 mHashOidTag = SEC_OID_HMAC_SHA1;
2594 break;
2595 case CKM_SHA256:
2596 mHashOidTag = SEC_OID_HMAC_SHA256;
2597 break;
2598 case CKM_SHA384:
2599 mHashOidTag = SEC_OID_HMAC_SHA384;
2600 break;
2601 case CKM_SHA512:
2602 mHashOidTag = SEC_OID_HMAC_SHA512;
2603 break;
2604 default:
2605 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2606 return;
2609 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2610 mLength = aLength >> 3; // bits to bytes
2611 mIterations = params.mIterations;
2614 private:
2615 size_t mLength;
2616 size_t mIterations;
2617 CryptoBuffer mSalt;
2618 CryptoBuffer mSymKey;
2619 SECOidTag mHashOidTag;
2621 virtual nsresult DoCrypto() override {
2622 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2623 if (!arena) {
2624 return NS_ERROR_DOM_OPERATION_ERR;
2627 SECItem salt = {siBuffer, nullptr, 0};
2628 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2629 // PK11_CreatePBEV2AlgorithmID will "helpfully" create PBKDF2 parameters
2630 // with a random salt if given a SECItem* that is either null or has a null
2631 // data pointer. This obviously isn't what we want, so we have to fake it
2632 // out by passing in a SECItem* with a non-null data pointer but with zero
2633 // length.
2634 if (!salt.data) {
2635 MOZ_ASSERT(salt.len == 0);
2636 salt.data =
2637 reinterpret_cast<unsigned char*>(PORT_ArenaAlloc(arena.get(), 1));
2638 if (!salt.data) {
2639 return NS_ERROR_DOM_UNKNOWN_ERR;
2643 // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this
2644 // parameter is unused for key generation. It is currently only used
2645 // for PBKDF2 authentication or key (un)wrapping when specifying an
2646 // encryption algorithm (PBES2).
2647 UniqueSECAlgorithmID algID(
2648 PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1,
2649 mHashOidTag, mLength, mIterations, &salt));
2651 if (!algID) {
2652 return NS_ERROR_DOM_OPERATION_ERR;
2655 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2656 if (!slot.get()) {
2657 return NS_ERROR_DOM_OPERATION_ERR;
2660 SECItem keyItem = {siBuffer, nullptr, 0};
2661 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2663 UniquePK11SymKey symKey(
2664 PK11_PBEKeyGen(slot.get(), algID.get(), &keyItem, false, nullptr));
2665 if (!symKey.get()) {
2666 return NS_ERROR_DOM_OPERATION_ERR;
2669 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2670 if (NS_FAILED(rv)) {
2671 return NS_ERROR_DOM_OPERATION_ERR;
2674 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2675 // just refers to a buffer managed by symKey. The assignment copies the
2676 // data, so mResult manages one copy, while symKey manages another.
2677 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2678 return NS_OK;
2682 template <class DeriveBitsTask>
2683 class DeriveKeyTask : public DeriveBitsTask {
2684 public:
2685 DeriveKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2686 const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
2687 const ObjectOrString& aDerivedKeyType, bool aExtractable,
2688 const Sequence<nsString>& aKeyUsages)
2689 : DeriveBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType) {
2690 if (NS_FAILED(this->mEarlyRv)) {
2691 return;
2694 constexpr auto format =
2695 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_FORMAT_RAW);
2696 mTask = new ImportSymmetricKeyTask(aGlobal, aCx, format, aDerivedKeyType,
2697 aExtractable, aKeyUsages);
2700 protected:
2701 RefPtr<ImportSymmetricKeyTask> mTask;
2703 private:
2704 virtual void Resolve() override {
2705 mTask->SetRawKeyData(this->mResult);
2706 mTask->DispatchWithPromise(this->mResultPromise);
2709 virtual void Cleanup() override { mTask = nullptr; }
2711 class DeriveEcdhBitsTask : public ReturnArrayBufferViewTask {
2712 public:
2713 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2714 CryptoKey& aKey, uint32_t aLength)
2715 : mLength(Some(aLength)), mPrivKey(aKey.GetPrivateKey()) {
2716 Init(aCx, aAlgorithm, aKey);
2719 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2720 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2721 : mPrivKey(aKey.GetPrivateKey()) {
2722 mEarlyRv =
2723 GetKeyLengthForAlgorithmIfSpecified(aCx, aTargetAlgorithm, mLength);
2724 if (NS_SUCCEEDED(mEarlyRv)) {
2725 Init(aCx, aAlgorithm, aKey);
2729 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey) {
2730 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDH);
2731 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDH);
2733 // Check that we have a private key.
2734 if (!mPrivKey) {
2735 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2736 return;
2739 // If specified, length must be a multiple of 8 bigger than zero
2740 // (otherwise, the full output of the key derivation is used).
2741 if (mLength) {
2742 if (*mLength == 0 || *mLength % 8) {
2743 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2744 return;
2746 *mLength = *mLength >> 3; // bits to bytes
2749 // Retrieve the peer's public key.
2750 RootedDictionary<EcdhKeyDeriveParams> params(aCx);
2751 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2752 if (NS_FAILED(mEarlyRv)) {
2753 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2754 return;
2757 CryptoKey* publicKey = params.mPublic;
2758 mPubKey = publicKey->GetPublicKey();
2759 if (!mPubKey) {
2760 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2761 return;
2764 CHECK_KEY_ALGORITHM(publicKey->Algorithm(), WEBCRYPTO_ALG_ECDH);
2766 // Both keys must use the same named curve.
2767 nsString curve1 = aKey.Algorithm().mEc.mNamedCurve;
2768 nsString curve2 = publicKey->Algorithm().mEc.mNamedCurve;
2770 if (!curve1.Equals(curve2)) {
2771 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2772 return;
2776 private:
2777 Maybe<size_t> mLength;
2778 UniqueSECKEYPrivateKey mPrivKey;
2779 UniqueSECKEYPublicKey mPubKey;
2781 virtual nsresult DoCrypto() override {
2782 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2783 // derived symmetric key and don't matter because we ignore them anyway.
2784 UniquePK11SymKey symKey(
2785 PK11_PubDeriveWithKDF(mPrivKey.get(), mPubKey.get(), PR_FALSE, nullptr,
2786 nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC,
2787 CKA_SIGN, 0, CKD_NULL, nullptr, nullptr));
2789 if (!symKey.get()) {
2790 return NS_ERROR_DOM_OPERATION_ERR;
2793 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2794 if (NS_FAILED(rv)) {
2795 return NS_ERROR_DOM_OPERATION_ERR;
2798 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2799 // just refers to a buffer managed by symKey. The assignment copies the
2800 // data, so mResult manages one copy, while symKey manages another.
2801 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2803 if (mLength) {
2804 if (*mLength > mResult.Length()) {
2805 return NS_ERROR_DOM_DATA_ERR;
2807 if (!mResult.SetLength(*mLength, fallible)) {
2808 return NS_ERROR_DOM_UNKNOWN_ERR;
2812 return NS_OK;
2816 template <class KeyEncryptTask>
2817 class WrapKeyTask : public ExportKeyTask {
2818 public:
2819 WrapKeyTask(JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
2820 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm)
2821 : ExportKeyTask(aFormat, aKey) {
2822 if (NS_FAILED(mEarlyRv)) {
2823 return;
2826 mTask = new KeyEncryptTask(aCx, aWrapAlgorithm, aWrappingKey, true);
2829 private:
2830 RefPtr<KeyEncryptTask> mTask;
2832 virtual nsresult AfterCrypto() override {
2833 // If wrapping JWK, stringify the JSON
2834 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2835 nsAutoString json;
2836 if (!mJwk.ToJSON(json)) {
2837 return NS_ERROR_DOM_OPERATION_ERR;
2840 NS_ConvertUTF16toUTF8 utf8(json);
2841 if (!mResult.Assign((const uint8_t*)utf8.BeginReading(), utf8.Length())) {
2842 return NS_ERROR_DOM_OPERATION_ERR;
2846 return NS_OK;
2849 virtual void Resolve() override {
2850 mTask->SetData(mResult);
2851 mTask->DispatchWithPromise(mResultPromise);
2854 virtual void Cleanup() override { mTask = nullptr; }
2857 template <class KeyEncryptTask>
2858 class UnwrapKeyTask : public KeyEncryptTask {
2859 public:
2860 UnwrapKeyTask(JSContext* aCx, const ArrayBufferViewOrArrayBuffer& aWrappedKey,
2861 CryptoKey& aUnwrappingKey,
2862 const ObjectOrString& aUnwrapAlgorithm, ImportKeyTask* aTask)
2863 : KeyEncryptTask(aCx, aUnwrapAlgorithm, aUnwrappingKey, aWrappedKey,
2864 false),
2865 mTask(aTask) {}
2867 private:
2868 RefPtr<ImportKeyTask> mTask;
2870 virtual void Resolve() override {
2871 mTask->SetKeyDataMaybeParseJWK(KeyEncryptTask::mResult);
2872 mTask->DispatchWithPromise(KeyEncryptTask::mResultPromise);
2875 virtual void Cleanup() override { mTask = nullptr; }
2878 // Task creation methods for WebCryptoTask
2880 // Note: We do not perform algorithm normalization as a monolithic process,
2881 // as described in the spec. Instead:
2882 // * Each method handles its slice of the supportedAlgorithms structure
2883 // * Task constructors take care of:
2884 // * Coercing the algorithm to the proper concrete type
2885 // * Cloning subordinate data items
2886 // * Cloning input data as needed
2888 // Thus, support for different algorithms is determined by the if-statements
2889 // below, rather than a data structure.
2891 // This results in algorithm normalization coming after some other checks,
2892 // and thus slightly more steps being done synchronously than the spec calls
2893 // for. But none of these steps is especially time-consuming.
2895 WebCryptoTask* WebCryptoTask::CreateEncryptDecryptTask(
2896 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2897 const CryptoOperationData& aData, bool aEncrypt) {
2898 TelemetryMethod method = (aEncrypt) ? TM_ENCRYPT : TM_DECRYPT;
2899 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
2900 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_ENC,
2901 aKey.Extractable());
2903 // Ensure key is usable for this operation
2904 if ((aEncrypt && !aKey.HasUsage(CryptoKey::ENCRYPT)) ||
2905 (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) {
2906 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2909 nsString algName;
2910 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2911 if (NS_FAILED(rv)) {
2912 return new FailureTask(rv);
2915 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2916 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2917 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
2918 return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
2919 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
2920 return new RsaOaepTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
2923 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2926 WebCryptoTask* WebCryptoTask::CreateSignVerifyTask(
2927 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2928 const CryptoOperationData& aSignature, const CryptoOperationData& aData,
2929 bool aSign) {
2930 TelemetryMethod method = (aSign) ? TM_SIGN : TM_VERIFY;
2931 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
2932 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_SIG,
2933 aKey.Extractable());
2935 // Ensure key is usable for this operation
2936 if ((aSign && !aKey.HasUsage(CryptoKey::SIGN)) ||
2937 (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) {
2938 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2941 nsString algName;
2942 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2943 if (NS_FAILED(rv)) {
2944 return new FailureTask(rv);
2947 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2948 return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
2949 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2950 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
2951 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2952 return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
2953 aData, aSign);
2956 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2959 WebCryptoTask* WebCryptoTask::CreateDigestTask(
2960 JSContext* aCx, const ObjectOrString& aAlgorithm,
2961 const CryptoOperationData& aData) {
2962 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DIGEST);
2964 nsString algName;
2965 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2966 if (NS_FAILED(rv)) {
2967 return new FailureTask(rv);
2970 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
2971 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256) ||
2972 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
2973 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
2974 return new DigestTask(aCx, aAlgorithm, aData);
2977 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2980 WebCryptoTask* WebCryptoTask::CreateImportKeyTask(
2981 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
2982 JS::Handle<JSObject*> aKeyData, const ObjectOrString& aAlgorithm,
2983 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
2984 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY);
2985 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable);
2987 // Verify that the format is recognized
2988 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
2989 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
2990 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
2991 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2992 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
2995 // Verify that aKeyUsages does not contain an unrecognized value
2996 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
2997 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3000 nsString algName;
3001 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3002 if (NS_FAILED(rv)) {
3003 return new FailureTask(rv);
3006 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
3007 // However, the spec should be updated to allow it.
3008 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3009 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3010 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
3011 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
3012 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
3013 algName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
3014 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
3015 return new ImportSymmetricKeyTask(aGlobal, aCx, aFormat, aKeyData,
3016 aAlgorithm, aExtractable, aKeyUsages);
3017 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3018 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
3019 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
3020 return new ImportRsaKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
3021 aExtractable, aKeyUsages);
3022 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
3023 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
3024 return new ImportEcKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
3025 aExtractable, aKeyUsages);
3026 } else {
3027 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3031 WebCryptoTask* WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat,
3032 CryptoKey& aKey) {
3033 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_EXPORTKEY);
3035 // Verify that the format is recognized
3036 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3037 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3038 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3039 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3040 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3043 // Verify that the key is extractable
3044 if (!aKey.Extractable()) {
3045 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3048 // Verify that the algorithm supports export
3049 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
3050 // However, the spec should be updated to allow it.
3051 nsString algName = aKey.Algorithm().mName;
3052 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3053 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3054 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
3055 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
3056 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
3057 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC) ||
3058 algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3059 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
3060 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
3061 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
3062 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
3063 return new ExportKeyTask(aFormat, aKey);
3066 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3069 WebCryptoTask* WebCryptoTask::CreateGenerateKeyTask(
3070 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3071 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3072 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_GENERATEKEY);
3073 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_GENERATE,
3074 aExtractable);
3076 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3077 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3080 nsString algName;
3081 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3082 if (NS_FAILED(rv)) {
3083 return new FailureTask(rv);
3086 if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3087 algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3088 algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3089 algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
3090 algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3091 return new GenerateSymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3092 aKeyUsages);
3093 } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3094 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3095 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS) ||
3096 algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
3097 algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA)) {
3098 return new GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3099 aKeyUsages);
3100 } else {
3101 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3105 WebCryptoTask* WebCryptoTask::CreateDeriveKeyTask(
3106 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3107 CryptoKey& aBaseKey, const ObjectOrString& aDerivedKeyType,
3108 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3109 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY);
3111 // Ensure baseKey is usable for this operation
3112 if (!aBaseKey.HasUsage(CryptoKey::DERIVEKEY)) {
3113 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3116 // Verify that aKeyUsages does not contain an unrecognized value
3117 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3118 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3121 nsString algName;
3122 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3123 if (NS_FAILED(rv)) {
3124 return new FailureTask(rv);
3127 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3128 return new DeriveKeyTask<DeriveHkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3129 aBaseKey, aDerivedKeyType,
3130 aExtractable, aKeyUsages);
3133 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3134 return new DeriveKeyTask<DerivePbkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3135 aBaseKey, aDerivedKeyType,
3136 aExtractable, aKeyUsages);
3139 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3140 return new DeriveKeyTask<DeriveEcdhBitsTask>(aGlobal, aCx, aAlgorithm,
3141 aBaseKey, aDerivedKeyType,
3142 aExtractable, aKeyUsages);
3145 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3148 WebCryptoTask* WebCryptoTask::CreateDeriveBitsTask(
3149 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
3150 uint32_t aLength) {
3151 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS);
3153 // Ensure baseKey is usable for this operation
3154 if (!aKey.HasUsage(CryptoKey::DERIVEBITS)) {
3155 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3158 nsString algName;
3159 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3160 if (NS_FAILED(rv)) {
3161 return new FailureTask(rv);
3164 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3165 return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3168 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3169 return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
3172 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3173 return new DeriveHkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3176 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3179 WebCryptoTask* WebCryptoTask::CreateWrapKeyTask(
3180 JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
3181 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm) {
3182 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_WRAPKEY);
3184 // Verify that the format is recognized
3185 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3186 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3187 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3188 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3189 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3192 // Ensure wrappingKey is usable for this operation
3193 if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) {
3194 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3197 // Ensure key is extractable
3198 if (!aKey.Extractable()) {
3199 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3202 nsString wrapAlgName;
3203 nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName);
3204 if (NS_FAILED(rv)) {
3205 return new FailureTask(rv);
3208 if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3209 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3210 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3211 return new WrapKeyTask<AesTask>(aCx, aFormat, aKey, aWrappingKey,
3212 aWrapAlgorithm);
3213 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3214 return new WrapKeyTask<AesKwTask>(aCx, aFormat, aKey, aWrappingKey,
3215 aWrapAlgorithm);
3216 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3217 return new WrapKeyTask<RsaOaepTask>(aCx, aFormat, aKey, aWrappingKey,
3218 aWrapAlgorithm);
3221 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3224 WebCryptoTask* WebCryptoTask::CreateUnwrapKeyTask(
3225 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
3226 const ArrayBufferViewOrArrayBuffer& aWrappedKey, CryptoKey& aUnwrappingKey,
3227 const ObjectOrString& aUnwrapAlgorithm,
3228 const ObjectOrString& aUnwrappedKeyAlgorithm, bool aExtractable,
3229 const Sequence<nsString>& aKeyUsages) {
3230 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_UNWRAPKEY);
3232 // Ensure key is usable for this operation
3233 if (!aUnwrappingKey.HasUsage(CryptoKey::UNWRAPKEY)) {
3234 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3237 // Verify that aKeyUsages does not contain an unrecognized value
3238 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3239 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3242 nsString keyAlgName;
3243 nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName);
3244 if (NS_FAILED(rv)) {
3245 return new FailureTask(rv);
3248 CryptoOperationData dummy;
3249 RefPtr<ImportKeyTask> importTask;
3250 if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3251 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3252 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3253 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
3254 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HKDF) ||
3255 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3256 importTask = new ImportSymmetricKeyTask(aGlobal, aCx, aFormat,
3257 aUnwrappedKeyAlgorithm,
3258 aExtractable, aKeyUsages);
3259 } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3260 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3261 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS)) {
3262 importTask =
3263 new ImportRsaKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3264 aExtractable, aKeyUsages);
3265 } else if (keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
3266 keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
3267 importTask =
3268 new ImportEcKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3269 aExtractable, aKeyUsages);
3270 } else {
3271 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3274 nsString unwrapAlgName;
3275 rv = GetAlgorithmName(aCx, aUnwrapAlgorithm, unwrapAlgName);
3276 if (NS_FAILED(rv)) {
3277 return new FailureTask(rv);
3279 if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3280 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3281 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3282 return new UnwrapKeyTask<AesTask>(aCx, aWrappedKey, aUnwrappingKey,
3283 aUnwrapAlgorithm, importTask);
3284 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3285 return new UnwrapKeyTask<AesKwTask>(aCx, aWrappedKey, aUnwrappingKey,
3286 aUnwrapAlgorithm, importTask);
3287 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3288 return new UnwrapKeyTask<RsaOaepTask>(aCx, aWrappedKey, aUnwrappingKey,
3289 aUnwrapAlgorithm, importTask);
3292 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3295 WebCryptoTask::WebCryptoTask()
3296 : CancelableRunnable("WebCryptoTask"),
3297 mEarlyRv(NS_OK),
3298 mEarlyComplete(false),
3299 mOriginalEventTarget(nullptr),
3300 mRv(NS_ERROR_NOT_INITIALIZED) {}
3302 WebCryptoTask::~WebCryptoTask() = default;
3304 } // namespace mozilla::dom