Bug 1795172 [wpt PR 36447] - Disallow culled inlines in repeated content., a=testonly
[gecko.git] / dom / crypto / WebCryptoTask.cpp
blob08439f0cbf199be1d7f83f3a0ded9b516c2dcbc0
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 GetKeyLengthForAlgorithm(JSContext* aCx,
190 const ObjectOrString& aAlgorithm,
191 size_t& aLength) {
192 aLength = 0;
194 // Extract algorithm name
195 nsString algName;
196 if (NS_FAILED(GetAlgorithmName(aCx, aAlgorithm, algName))) {
197 return NS_ERROR_DOM_SYNTAX_ERR;
200 // Read AES key length from given algorithm object.
201 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
202 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
203 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
204 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
205 RootedDictionary<AesDerivedKeyParams> params(aCx);
206 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
207 return NS_ERROR_DOM_SYNTAX_ERR;
210 if (params.mLength != 128 && params.mLength != 192 &&
211 params.mLength != 256) {
212 return NS_ERROR_DOM_OPERATION_ERR;
215 aLength = params.mLength;
216 return NS_OK;
219 // Read HMAC key length from given algorithm object or
220 // determine key length as the block size of the given hash.
221 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
222 RootedDictionary<HmacDerivedKeyParams> params(aCx);
223 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
224 return NS_ERROR_DOM_SYNTAX_ERR;
227 // Return the passed length, if any.
228 if (params.mLength.WasPassed()) {
229 aLength = params.mLength.Value();
230 return NS_OK;
233 nsString hashName;
234 if (NS_FAILED(GetAlgorithmName(aCx, params.mHash, hashName))) {
235 return NS_ERROR_DOM_SYNTAX_ERR;
238 // Return the given hash algorithm's block size as the key length.
239 size_t length = MapHashAlgorithmNameToBlockSize(hashName);
240 if (length == 0) {
241 return NS_ERROR_DOM_SYNTAX_ERR;
244 aLength = length;
245 return NS_OK;
248 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
251 inline bool MapOIDTagToNamedCurve(SECOidTag aOIDTag, nsString& aResult) {
252 switch (aOIDTag) {
253 case SEC_OID_SECG_EC_SECP256R1:
254 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
255 break;
256 case SEC_OID_SECG_EC_SECP384R1:
257 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
258 break;
259 case SEC_OID_SECG_EC_SECP521R1:
260 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
261 break;
262 default:
263 return false;
266 return true;
269 inline SECOidTag MapHashAlgorithmNameToOID(const nsString& aName) {
270 SECOidTag hashOID(SEC_OID_UNKNOWN);
272 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
273 hashOID = SEC_OID_SHA1;
274 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
275 hashOID = SEC_OID_SHA256;
276 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
277 hashOID = SEC_OID_SHA384;
278 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
279 hashOID = SEC_OID_SHA512;
282 return hashOID;
285 inline CK_MECHANISM_TYPE MapHashAlgorithmNameToMgfMechanism(
286 const nsString& aName) {
287 CK_MECHANISM_TYPE mech(UNKNOWN_CK_MECHANISM);
289 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
290 mech = CKG_MGF1_SHA1;
291 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
292 mech = CKG_MGF1_SHA256;
293 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
294 mech = CKG_MGF1_SHA384;
295 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
296 mech = CKG_MGF1_SHA512;
299 return mech;
302 // Implementation of WebCryptoTask methods
304 void WebCryptoTask::DispatchWithPromise(Promise* aResultPromise) {
305 mResultPromise = aResultPromise;
307 // Fail if an error was set during the constructor
308 MAYBE_EARLY_FAIL(mEarlyRv)
310 // Perform pre-NSS operations, and fail if they fail
311 mEarlyRv = BeforeCrypto();
312 MAYBE_EARLY_FAIL(mEarlyRv)
314 // Skip dispatch if we're already done. Otherwise launch a CryptoTask
315 if (mEarlyComplete) {
316 CallCallback(mEarlyRv);
317 return;
320 // Store calling thread
321 mOriginalEventTarget = GetCurrentSerialEventTarget();
323 // If we are running on a worker thread we must hold the worker
324 // alive while we work on the thread pool. Otherwise the worker
325 // private may get torn down before we dispatch back to complete
326 // the transaction.
327 if (!NS_IsMainThread()) {
328 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
329 MOZ_ASSERT(workerPrivate);
331 RefPtr<StrongWorkerRef> workerRef =
332 StrongWorkerRef::Create(workerPrivate, "WebCryptoTask");
333 if (NS_WARN_IF(!workerRef)) {
334 mEarlyRv = NS_BINDING_ABORTED;
335 } else {
336 mWorkerRef = new ThreadSafeWorkerRef(workerRef);
339 MAYBE_EARLY_FAIL(mEarlyRv);
341 // dispatch to thread pool
343 if (!EnsureNSSInitializedChromeOrContent()) {
344 mEarlyRv = NS_ERROR_FAILURE;
346 MAYBE_EARLY_FAIL(mEarlyRv);
348 mEarlyRv = NS_DispatchBackgroundTask(this);
349 MAYBE_EARLY_FAIL(mEarlyRv)
352 NS_IMETHODIMP
353 WebCryptoTask::Run() {
354 // Run heavy crypto operations on the thread pool, off the original thread.
355 if (!IsOnOriginalThread()) {
356 mRv = CalculateResult();
358 // Back to the original thread, i.e. continue below.
359 mOriginalEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
360 return NS_OK;
363 // We're now back on the calling thread.
364 CallCallback(mRv);
366 // Stop holding the worker thread alive now that the async work has
367 // been completed.
368 mWorkerRef = nullptr;
370 return NS_OK;
373 nsresult WebCryptoTask::Cancel() {
374 MOZ_ASSERT(IsOnOriginalThread());
375 FailWithError(NS_BINDING_ABORTED);
376 return NS_OK;
379 void WebCryptoTask::FailWithError(nsresult aRv) {
380 MOZ_ASSERT(IsOnOriginalThread());
381 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
383 // Blindly convert nsresult to DOMException
384 // Individual tasks must ensure they pass the right values
385 mResultPromise->MaybeReject(aRv);
386 // Manually release mResultPromise while we're on the main thread
387 mResultPromise = nullptr;
388 mWorkerRef = nullptr;
389 Cleanup();
392 nsresult WebCryptoTask::CalculateResult() {
393 MOZ_ASSERT(!IsOnOriginalThread());
395 return DoCrypto();
398 void WebCryptoTask::CallCallback(nsresult rv) {
399 MOZ_ASSERT(IsOnOriginalThread());
400 if (NS_FAILED(rv)) {
401 FailWithError(rv);
402 return;
405 nsresult rv2 = AfterCrypto();
406 if (NS_FAILED(rv2)) {
407 FailWithError(rv2);
408 return;
411 Resolve();
412 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, true);
414 // Manually release mResultPromise while we're on the main thread
415 mResultPromise = nullptr;
416 Cleanup();
419 // Some generic utility classes
421 class FailureTask : public WebCryptoTask {
422 public:
423 explicit FailureTask(nsresult aRv) { mEarlyRv = aRv; }
426 class ReturnArrayBufferViewTask : public WebCryptoTask {
427 protected:
428 CryptoBuffer mResult;
430 private:
431 // Returns mResult as an ArrayBufferView, or an error
432 virtual void Resolve() override {
433 TypedArrayCreator<ArrayBuffer> ret(mResult);
434 mResultPromise->MaybeResolve(ret);
438 class DeferredData {
439 public:
440 template <class T>
441 void SetData(const T& aData) {
442 mDataIsSet = mData.Assign(aData);
445 protected:
446 DeferredData() : mDataIsSet(false) {}
448 CryptoBuffer mData;
449 bool mDataIsSet;
452 class AesTask : public ReturnArrayBufferViewTask, public DeferredData {
453 public:
454 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
455 bool aEncrypt)
456 : mMechanism(CKM_INVALID_MECHANISM),
457 mTagLength(0),
458 mCounterLength(0),
459 mEncrypt(aEncrypt) {
460 Init(aCx, aAlgorithm, aKey, aEncrypt);
463 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
464 const CryptoOperationData& aData, bool aEncrypt)
465 : mMechanism(CKM_INVALID_MECHANISM),
466 mTagLength(0),
467 mCounterLength(0),
468 mEncrypt(aEncrypt) {
469 Init(aCx, aAlgorithm, aKey, aEncrypt);
470 SetData(aData);
473 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
474 bool aEncrypt) {
475 nsString algName;
476 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
477 if (NS_FAILED(mEarlyRv)) {
478 return;
481 if (!mSymKey.Assign(aKey.GetSymKey())) {
482 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
483 return;
486 // Check that we got a reasonable key
487 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
488 (mSymKey.Length() != 32)) {
489 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
490 return;
493 // Cache parameters depending on the specific algorithm
494 TelemetryAlgorithm telemetryAlg;
495 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
496 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CBC);
498 mMechanism = CKM_AES_CBC_PAD;
499 telemetryAlg = TA_AES_CBC;
500 RootedDictionary<AesCbcParams> params(aCx);
501 nsresult rv = Coerce(aCx, params, aAlgorithm);
502 if (NS_FAILED(rv)) {
503 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
504 return;
507 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
508 if (mIv.Length() != 16) {
509 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
510 return;
512 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
513 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CTR);
515 mMechanism = CKM_AES_CTR;
516 telemetryAlg = TA_AES_CTR;
517 RootedDictionary<AesCtrParams> params(aCx);
518 nsresult rv = Coerce(aCx, params, aAlgorithm);
519 if (NS_FAILED(rv)) {
520 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
521 return;
524 ATTEMPT_BUFFER_INIT(mIv, params.mCounter)
525 if (mIv.Length() != 16) {
526 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
527 return;
530 mCounterLength = params.mLength;
531 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
532 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM);
534 mMechanism = CKM_AES_GCM;
535 telemetryAlg = TA_AES_GCM;
536 RootedDictionary<AesGcmParams> params(aCx);
537 nsresult rv = Coerce(aCx, params, aAlgorithm);
538 if (NS_FAILED(rv)) {
539 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
540 return;
543 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
545 if (params.mAdditionalData.WasPassed()) {
546 ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value())
549 // 32, 64, 96, 104, 112, 120 or 128
550 mTagLength = 128;
551 if (params.mTagLength.WasPassed()) {
552 mTagLength = params.mTagLength.Value();
553 if ((mTagLength > 128) ||
554 !(mTagLength == 32 || mTagLength == 64 ||
555 (mTagLength >= 96 && mTagLength % 8 == 0))) {
556 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
557 return;
560 } else {
561 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
562 return;
564 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
567 private:
568 CK_MECHANISM_TYPE mMechanism;
569 CryptoBuffer mSymKey;
570 CryptoBuffer mIv; // Initialization vector
571 CryptoBuffer mAad; // Additional Authenticated Data
572 uint8_t mTagLength;
573 uint8_t mCounterLength;
574 bool mEncrypt;
576 virtual nsresult DoCrypto() override {
577 nsresult rv;
579 if (!mDataIsSet) {
580 return NS_ERROR_DOM_OPERATION_ERR;
583 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
584 if (!arena) {
585 return NS_ERROR_DOM_OPERATION_ERR;
588 // Construct the parameters object depending on algorithm
589 SECItem param = {siBuffer, nullptr, 0};
590 CK_AES_CTR_PARAMS ctrParams;
591 CK_GCM_PARAMS gcmParams;
592 switch (mMechanism) {
593 case CKM_AES_CBC_PAD:
594 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &param, mIv);
595 break;
596 case CKM_AES_CTR:
597 ctrParams.ulCounterBits = mCounterLength;
598 MOZ_ASSERT(mIv.Length() == 16);
599 memcpy(&ctrParams.cb, mIv.Elements(), 16);
600 param.type = siBuffer;
601 param.data = (unsigned char*)&ctrParams;
602 param.len = sizeof(ctrParams);
603 break;
604 case CKM_AES_GCM:
605 gcmParams.pIv = mIv.Elements();
606 gcmParams.ulIvLen = mIv.Length();
607 gcmParams.ulIvBits = gcmParams.ulIvLen * 8;
608 gcmParams.pAAD = mAad.Elements();
609 gcmParams.ulAADLen = mAad.Length();
610 gcmParams.ulTagBits = mTagLength;
611 param.type = siBuffer;
612 param.data = (unsigned char*)&gcmParams;
613 param.len = sizeof(gcmParams);
614 break;
615 default:
616 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
619 // Import the key
620 SECItem keyItem = {siBuffer, nullptr, 0};
621 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
622 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
623 MOZ_ASSERT(slot.get());
624 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
625 PK11_OriginUnwrap, CKA_ENCRYPT,
626 &keyItem, nullptr));
627 if (!symKey) {
628 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
631 // Check whether the integer addition would overflow.
632 if (std::numeric_limits<CryptoBuffer::size_type>::max() - 16 <
633 mData.Length()) {
634 return NS_ERROR_DOM_DATA_ERR;
637 // Initialize the output buffer (enough space for padding / a full tag)
638 if (!mResult.SetLength(mData.Length() + 16, fallible)) {
639 return NS_ERROR_DOM_UNKNOWN_ERR;
642 uint32_t outLen = 0;
644 // Perform the encryption/decryption
645 if (mEncrypt) {
646 rv = MapSECStatus(PK11_Encrypt(
647 symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
648 mResult.Length(), mData.Elements(), mData.Length()));
649 } else {
650 rv = MapSECStatus(PK11_Decrypt(
651 symKey.get(), mMechanism, &param, mResult.Elements(), &outLen,
652 mResult.Length(), mData.Elements(), mData.Length()));
654 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
656 mResult.TruncateLength(outLen);
657 return rv;
661 // This class looks like an encrypt/decrypt task, like AesTask,
662 // but it is only exposed to wrapKey/unwrapKey, not encrypt/decrypt
663 class AesKwTask : public ReturnArrayBufferViewTask, public DeferredData {
664 public:
665 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
666 bool aEncrypt)
667 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
668 Init(aCx, aAlgorithm, aKey, aEncrypt);
671 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
672 const CryptoOperationData& aData, bool aEncrypt)
673 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
674 Init(aCx, aAlgorithm, aKey, aEncrypt);
675 SetData(aData);
678 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
679 bool aEncrypt) {
680 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_KW);
682 nsString algName;
683 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
684 if (NS_FAILED(mEarlyRv)) {
685 return;
688 if (!mSymKey.Assign(aKey.GetSymKey())) {
689 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
690 return;
693 // Check that we got a reasonable key
694 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
695 (mSymKey.Length() != 32)) {
696 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
697 return;
700 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_AES_KW);
703 private:
704 CK_MECHANISM_TYPE mMechanism;
705 CryptoBuffer mSymKey;
706 bool mEncrypt;
708 virtual nsresult DoCrypto() override {
709 nsresult rv;
711 if (!mDataIsSet) {
712 return NS_ERROR_DOM_OPERATION_ERR;
715 // Check that the input is a multiple of 64 bits long
716 if (mData.Length() == 0 || mData.Length() % 8 != 0) {
717 return NS_ERROR_DOM_DATA_ERR;
720 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
721 if (!arena) {
722 return NS_ERROR_DOM_OPERATION_ERR;
725 // Import the key
726 SECItem keyItem = {siBuffer, nullptr, 0};
727 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
728 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
729 MOZ_ASSERT(slot.get());
730 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
731 PK11_OriginUnwrap, CKA_WRAP,
732 &keyItem, nullptr));
733 if (!symKey) {
734 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
737 // Import the data to a SECItem
738 SECItem dataItem = {siBuffer, nullptr, 0};
739 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &dataItem, mData);
741 // Parameters for the fake keys
742 CK_MECHANISM_TYPE fakeMechanism = CKM_SHA_1_HMAC;
743 CK_ATTRIBUTE_TYPE fakeOperation = CKA_SIGN;
745 if (mEncrypt) {
746 // Import the data into a fake PK11SymKey structure
747 UniquePK11SymKey keyToWrap(
748 PK11_ImportSymKey(slot.get(), fakeMechanism, PK11_OriginUnwrap,
749 fakeOperation, &dataItem, nullptr));
750 if (!keyToWrap) {
751 return NS_ERROR_DOM_OPERATION_ERR;
754 // Encrypt and return the wrapped key
755 // AES-KW encryption results in a wrapped key 64 bits longer
756 if (!mResult.SetLength(mData.Length() + 8, fallible)) {
757 return NS_ERROR_DOM_OPERATION_ERR;
759 SECItem resultItem = {siBuffer, mResult.Elements(),
760 (unsigned int)mResult.Length()};
761 rv = MapSECStatus(PK11_WrapSymKey(mMechanism, nullptr, symKey.get(),
762 keyToWrap.get(), &resultItem));
763 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
764 } else {
765 // Decrypt the ciphertext into a temporary PK11SymKey
766 // Unwrapped key should be 64 bits shorter
767 int keySize = mData.Length() - 8;
768 UniquePK11SymKey unwrappedKey(
769 PK11_UnwrapSymKey(symKey.get(), mMechanism, nullptr, &dataItem,
770 fakeMechanism, fakeOperation, keySize));
771 if (!unwrappedKey) {
772 return NS_ERROR_DOM_OPERATION_ERR;
775 // Export the key to get the cleartext
776 rv = MapSECStatus(PK11_ExtractKeyValue(unwrappedKey.get()));
777 if (NS_FAILED(rv)) {
778 return NS_ERROR_DOM_UNKNOWN_ERR;
780 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(unwrappedKey.get()));
783 return rv;
787 class RsaOaepTask : public ReturnArrayBufferViewTask, public DeferredData {
788 public:
789 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
790 bool aEncrypt)
791 : mPrivKey(aKey.GetPrivateKey()),
792 mPubKey(aKey.GetPublicKey()),
793 mEncrypt(aEncrypt) {
794 Init(aCx, aAlgorithm, aKey, aEncrypt);
797 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
798 const CryptoOperationData& aData, bool aEncrypt)
799 : mPrivKey(aKey.GetPrivateKey()),
800 mPubKey(aKey.GetPublicKey()),
801 mEncrypt(aEncrypt) {
802 Init(aCx, aAlgorithm, aKey, aEncrypt);
803 SetData(aData);
806 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
807 bool aEncrypt) {
808 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_OAEP);
810 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_OAEP);
812 if (mEncrypt) {
813 if (!mPubKey) {
814 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
815 return;
817 mStrength = SECKEY_PublicKeyStrength(mPubKey.get());
818 } else {
819 if (!mPrivKey) {
820 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
821 return;
823 mStrength = PK11_GetPrivateModulusLen(mPrivKey.get());
826 // The algorithm could just be given as a string
827 // in which case there would be no label specified.
828 if (!aAlgorithm.IsString()) {
829 RootedDictionary<RsaOaepParams> params(aCx);
830 mEarlyRv = Coerce(aCx, params, aAlgorithm);
831 if (NS_FAILED(mEarlyRv)) {
832 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
833 return;
836 if (params.mLabel.WasPassed()) {
837 ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value());
840 // Otherwise mLabel remains the empty octet string, as intended
842 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
843 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
844 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlg.mName);
846 // Check we found appropriate mechanisms.
847 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
848 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
849 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
850 return;
854 private:
855 CK_MECHANISM_TYPE mHashMechanism;
856 CK_MECHANISM_TYPE mMgfMechanism;
857 UniqueSECKEYPrivateKey mPrivKey;
858 UniqueSECKEYPublicKey mPubKey;
859 CryptoBuffer mLabel;
860 uint32_t mStrength;
861 bool mEncrypt;
863 virtual nsresult DoCrypto() override {
864 nsresult rv;
866 if (!mDataIsSet) {
867 return NS_ERROR_DOM_OPERATION_ERR;
870 // Ciphertext is an integer mod the modulus, so it will be
871 // no longer than mStrength octets
872 if (!mResult.SetLength(mStrength, fallible)) {
873 return NS_ERROR_DOM_UNKNOWN_ERR;
876 CK_RSA_PKCS_OAEP_PARAMS oaepParams;
877 oaepParams.source = CKZ_DATA_SPECIFIED;
879 oaepParams.pSourceData = mLabel.Length() ? mLabel.Elements() : nullptr;
880 oaepParams.ulSourceDataLen = mLabel.Length();
882 oaepParams.mgf = mMgfMechanism;
883 oaepParams.hashAlg = mHashMechanism;
885 SECItem param;
886 param.type = siBuffer;
887 param.data = (unsigned char*)&oaepParams;
888 param.len = sizeof(oaepParams);
890 uint32_t outLen = 0;
891 if (mEncrypt) {
892 // PK11_PubEncrypt() checks the plaintext's length and fails if it is too
893 // long to encrypt, i.e. if it is longer than (k - 2hLen - 2) with 'k'
894 // being the length in octets of the RSA modulus n and 'hLen' being the
895 // output length in octets of the chosen hash function.
896 // <https://tools.ietf.org/html/rfc3447#section-7.1>
897 rv = MapSECStatus(PK11_PubEncrypt(
898 mPubKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(), &outLen,
899 mResult.Length(), mData.Elements(), mData.Length(), nullptr));
900 } else {
901 rv = MapSECStatus(PK11_PrivDecrypt(
902 mPrivKey.get(), CKM_RSA_PKCS_OAEP, &param, mResult.Elements(),
903 &outLen, mResult.Length(), mData.Elements(), mData.Length()));
905 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
907 mResult.TruncateLength(outLen);
908 return NS_OK;
912 class HmacTask : public WebCryptoTask {
913 public:
914 HmacTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
915 const CryptoOperationData& aSignature,
916 const CryptoOperationData& aData, bool aSign)
917 : mMechanism(aKey.Algorithm().Mechanism()), mSign(aSign) {
918 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HMAC);
920 ATTEMPT_BUFFER_INIT(mData, aData);
921 if (!aSign) {
922 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
925 if (!mSymKey.Assign(aKey.GetSymKey())) {
926 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
927 return;
930 // Check that we got a symmetric key
931 if (mSymKey.Length() == 0) {
932 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
933 return;
936 TelemetryAlgorithm telemetryAlg;
937 switch (mMechanism) {
938 case CKM_SHA_1_HMAC:
939 telemetryAlg = TA_HMAC_SHA_1;
940 break;
941 case CKM_SHA224_HMAC:
942 telemetryAlg = TA_HMAC_SHA_224;
943 break;
944 case CKM_SHA256_HMAC:
945 telemetryAlg = TA_HMAC_SHA_256;
946 break;
947 case CKM_SHA384_HMAC:
948 telemetryAlg = TA_HMAC_SHA_384;
949 break;
950 case CKM_SHA512_HMAC:
951 telemetryAlg = TA_HMAC_SHA_512;
952 break;
953 default:
954 telemetryAlg = TA_UNKNOWN;
956 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
959 private:
960 CK_MECHANISM_TYPE mMechanism;
961 CryptoBuffer mSymKey;
962 CryptoBuffer mData;
963 CryptoBuffer mSignature;
964 CryptoBuffer mResult;
965 bool mSign;
967 virtual nsresult DoCrypto() override {
968 // Initialize the output buffer
969 if (!mResult.SetLength(HASH_LENGTH_MAX, fallible)) {
970 return NS_ERROR_DOM_UNKNOWN_ERR;
973 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
974 if (!arena) {
975 return NS_ERROR_DOM_OPERATION_ERR;
978 // Import the key
979 uint32_t outLen;
980 SECItem keyItem = {siBuffer, nullptr, 0};
981 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
982 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
983 MOZ_ASSERT(slot.get());
984 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
985 PK11_OriginUnwrap, CKA_SIGN,
986 &keyItem, nullptr));
987 if (!symKey) {
988 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
991 // Compute the MAC
992 SECItem param = {siBuffer, nullptr, 0};
993 UniquePK11Context ctx(
994 PK11_CreateContextBySymKey(mMechanism, CKA_SIGN, symKey.get(), &param));
995 if (!ctx.get()) {
996 return NS_ERROR_DOM_OPERATION_ERR;
998 nsresult rv = MapSECStatus(PK11_DigestBegin(ctx.get()));
999 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1000 rv = MapSECStatus(
1001 PK11_DigestOp(ctx.get(), mData.Elements(), mData.Length()));
1002 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1003 rv = MapSECStatus(PK11_DigestFinal(ctx.get(), mResult.Elements(), &outLen,
1004 mResult.Length()));
1005 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1007 mResult.TruncateLength(outLen);
1008 return rv;
1011 // Returns mResult as an ArrayBufferView, or an error
1012 virtual void Resolve() override {
1013 if (mSign) {
1014 // Return the computed MAC
1015 TypedArrayCreator<ArrayBuffer> ret(mResult);
1016 mResultPromise->MaybeResolve(ret);
1017 } else {
1018 // Compare the MAC to the provided signature
1019 // No truncation allowed
1020 bool equal = (mResult.Length() == mSignature.Length());
1021 if (equal) {
1022 int cmp = NSS_SecureMemcmp(mSignature.Elements(), mResult.Elements(),
1023 mSignature.Length());
1024 equal = (cmp == 0);
1026 mResultPromise->MaybeResolve(equal);
1031 class AsymmetricSignVerifyTask : public WebCryptoTask {
1032 public:
1033 AsymmetricSignVerifyTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1034 CryptoKey& aKey,
1035 const CryptoOperationData& aSignature,
1036 const CryptoOperationData& aData, bool aSign)
1037 : mOidTag(SEC_OID_UNKNOWN),
1038 mHashMechanism(UNKNOWN_CK_MECHANISM),
1039 mMgfMechanism(UNKNOWN_CK_MECHANISM),
1040 mPrivKey(aKey.GetPrivateKey()),
1041 mPubKey(aKey.GetPublicKey()),
1042 mSaltLength(0),
1043 mSign(aSign),
1044 mVerified(false),
1045 mAlgorithm(Algorithm::UNKNOWN) {
1046 ATTEMPT_BUFFER_INIT(mData, aData);
1047 if (!aSign) {
1048 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
1051 nsString algName;
1052 nsString hashAlgName;
1053 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1054 if (NS_FAILED(mEarlyRv)) {
1055 return;
1058 if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
1059 mAlgorithm = Algorithm::RSA_PKCS1;
1060 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
1061 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
1062 hashAlgName = aKey.Algorithm().mRsa.mHash.mName;
1063 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1064 mAlgorithm = Algorithm::RSA_PSS;
1065 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_PSS);
1066 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_PSS);
1068 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
1069 hashAlgName = hashAlg.mName;
1070 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
1071 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlgName);
1073 // Check we found appropriate mechanisms.
1074 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
1075 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
1076 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1077 return;
1080 RootedDictionary<RsaPssParams> params(aCx);
1081 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1082 if (NS_FAILED(mEarlyRv)) {
1083 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1084 return;
1087 mSaltLength = params.mSaltLength;
1088 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1089 mAlgorithm = Algorithm::ECDSA;
1090 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDSA);
1091 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
1093 // For ECDSA, the hash name comes from the algorithm parameter
1094 RootedDictionary<EcdsaParams> params(aCx);
1095 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1096 if (NS_FAILED(mEarlyRv)) {
1097 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1098 return;
1101 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashAlgName);
1102 if (NS_FAILED(mEarlyRv)) {
1103 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1104 return;
1106 } else {
1107 // This shouldn't happen; CreateSignVerifyTask shouldn't create
1108 // one of these unless it's for the above algorithms.
1109 MOZ_ASSERT(false);
1112 // Must have a valid algorithm by now.
1113 MOZ_ASSERT(mAlgorithm != Algorithm::UNKNOWN);
1115 // Determine hash algorithm to use.
1116 mOidTag = MapHashAlgorithmNameToOID(hashAlgName);
1117 if (mOidTag == SEC_OID_UNKNOWN) {
1118 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1119 return;
1122 // Check that we have the appropriate key
1123 if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
1124 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
1125 return;
1129 private:
1130 SECOidTag mOidTag;
1131 CK_MECHANISM_TYPE mHashMechanism;
1132 CK_MECHANISM_TYPE mMgfMechanism;
1133 UniqueSECKEYPrivateKey mPrivKey;
1134 UniqueSECKEYPublicKey mPubKey;
1135 CryptoBuffer mSignature;
1136 CryptoBuffer mData;
1137 uint32_t mSaltLength;
1138 bool mSign;
1139 bool mVerified;
1141 // The signature algorithm to use.
1142 enum class Algorithm : uint8_t { ECDSA, RSA_PKCS1, RSA_PSS, UNKNOWN };
1143 Algorithm mAlgorithm;
1145 virtual nsresult DoCrypto() override {
1146 SECStatus rv;
1147 UniqueSECItem hash(
1148 ::SECITEM_AllocItem(nullptr, nullptr, HASH_ResultLenByOidTag(mOidTag)));
1149 if (!hash) {
1150 return NS_ERROR_DOM_OPERATION_ERR;
1153 // Compute digest over given data.
1154 rv = PK11_HashBuf(mOidTag, hash->data, mData.Elements(), mData.Length());
1155 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1157 // Wrap hash in a digest info template (RSA-PKCS1 only).
1158 if (mAlgorithm == Algorithm::RSA_PKCS1) {
1159 UniqueSGNDigestInfo di(
1160 SGN_CreateDigestInfo(mOidTag, hash->data, hash->len));
1161 if (!di) {
1162 return NS_ERROR_DOM_OPERATION_ERR;
1165 // Reuse |hash|.
1166 SECITEM_FreeItem(hash.get(), false);
1167 if (!SEC_ASN1EncodeItem(nullptr, hash.get(), di.get(),
1168 SGN_DigestInfoTemplate)) {
1169 return NS_ERROR_DOM_OPERATION_ERR;
1173 SECItem* params = nullptr;
1174 CK_MECHANISM_TYPE mech =
1175 PK11_MapSignKeyType((mSign ? mPrivKey->keyType : mPubKey->keyType));
1177 CK_RSA_PKCS_PSS_PARAMS rsaPssParams;
1178 SECItem rsaPssParamsItem = {
1179 siBuffer,
1182 // Set up parameters for RSA-PSS.
1183 if (mAlgorithm == Algorithm::RSA_PSS) {
1184 rsaPssParams.hashAlg = mHashMechanism;
1185 rsaPssParams.mgf = mMgfMechanism;
1186 rsaPssParams.sLen = mSaltLength;
1188 rsaPssParamsItem.data = (unsigned char*)&rsaPssParams;
1189 rsaPssParamsItem.len = sizeof(rsaPssParams);
1190 params = &rsaPssParamsItem;
1192 mech = CKM_RSA_PKCS_PSS;
1195 // Allocate SECItem to hold the signature.
1196 uint32_t len = mSign ? PK11_SignatureLen(mPrivKey.get()) : 0;
1197 UniqueSECItem sig(::SECITEM_AllocItem(nullptr, nullptr, len));
1198 if (!sig) {
1199 return NS_ERROR_DOM_OPERATION_ERR;
1202 if (mSign) {
1203 // Sign the hash.
1204 rv = PK11_SignWithMechanism(mPrivKey.get(), mech, params, sig.get(),
1205 hash.get());
1206 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1207 ATTEMPT_BUFFER_ASSIGN(mSignature, sig.get());
1208 } else {
1209 // Copy the given signature to the SECItem.
1210 if (!mSignature.ToSECItem(nullptr, sig.get())) {
1211 return NS_ERROR_DOM_OPERATION_ERR;
1214 // Verify the signature.
1215 rv = PK11_VerifyWithMechanism(mPubKey.get(), mech, params, sig.get(),
1216 hash.get(), nullptr);
1217 mVerified = NS_SUCCEEDED(MapSECStatus(rv));
1220 return NS_OK;
1223 virtual void Resolve() override {
1224 if (mSign) {
1225 TypedArrayCreator<ArrayBuffer> ret(mSignature);
1226 mResultPromise->MaybeResolve(ret);
1227 } else {
1228 mResultPromise->MaybeResolve(mVerified);
1233 class DigestTask : public ReturnArrayBufferViewTask {
1234 public:
1235 DigestTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1236 const CryptoOperationData& aData) {
1237 ATTEMPT_BUFFER_INIT(mData, aData);
1239 nsString algName;
1240 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1241 if (NS_FAILED(mEarlyRv)) {
1242 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1243 return;
1246 TelemetryAlgorithm telemetryAlg;
1247 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
1248 telemetryAlg = TA_SHA_1;
1249 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
1250 telemetryAlg = TA_SHA_224;
1251 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
1252 telemetryAlg = TA_SHA_256;
1253 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
1254 telemetryAlg = TA_SHA_384;
1255 } else {
1256 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1257 return;
1259 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
1260 mOidTag = MapHashAlgorithmNameToOID(algName);
1263 private:
1264 SECOidTag mOidTag;
1265 CryptoBuffer mData;
1267 virtual nsresult DoCrypto() override {
1268 // Resize the result buffer
1269 uint32_t hashLen = HASH_ResultLenByOidTag(mOidTag);
1270 if (!mResult.SetLength(hashLen, fallible)) {
1271 return NS_ERROR_DOM_UNKNOWN_ERR;
1274 // Compute the hash
1275 nsresult rv = MapSECStatus(PK11_HashBuf(mOidTag, mResult.Elements(),
1276 mData.Elements(), mData.Length()));
1277 if (NS_FAILED(rv)) {
1278 return NS_ERROR_DOM_UNKNOWN_ERR;
1281 return rv;
1285 class ImportKeyTask : public WebCryptoTask {
1286 public:
1287 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1288 const ObjectOrString& aAlgorithm, bool aExtractable,
1289 const Sequence<nsString>& aKeyUsages) {
1290 mFormat = aFormat;
1291 mDataIsSet = false;
1292 mDataIsJwk = false;
1294 // This stuff pretty much always happens, so we'll do it here
1295 mKey = new CryptoKey(aGlobal);
1296 mKey->SetExtractable(aExtractable);
1297 mKey->ClearUsages();
1298 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
1299 mEarlyRv = mKey->AddUsage(aKeyUsages[i]);
1300 if (NS_FAILED(mEarlyRv)) {
1301 return;
1305 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
1306 if (NS_FAILED(mEarlyRv)) {
1307 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1308 return;
1312 static bool JwkCompatible(const JsonWebKey& aJwk, const CryptoKey* aKey) {
1313 // Check 'ext'
1314 if (aKey->Extractable() && aJwk.mExt.WasPassed() && !aJwk.mExt.Value()) {
1315 return false;
1318 // Check 'alg'
1319 if (aJwk.mAlg.WasPassed() &&
1320 aJwk.mAlg.Value() != aKey->Algorithm().JwkAlg()) {
1321 return false;
1324 // Check 'key_ops'
1325 if (aJwk.mKey_ops.WasPassed()) {
1326 nsTArray<nsString> usages;
1327 aKey->GetUsages(usages);
1328 for (size_t i = 0; i < usages.Length(); ++i) {
1329 if (!aJwk.mKey_ops.Value().Contains(usages[i])) {
1330 return false;
1335 // Individual algorithms may still have to check 'use'
1336 return true;
1339 void SetKeyData(JSContext* aCx, JS::Handle<JSObject*> aKeyData) {
1340 mDataIsJwk = false;
1342 // Try ArrayBuffer
1343 RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
1344 if (ab.Init(aKeyData)) {
1345 if (!mKeyData.Assign(ab)) {
1346 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1348 return;
1351 // Try ArrayBufferView
1352 RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
1353 if (abv.Init(aKeyData)) {
1354 if (!mKeyData.Assign(abv)) {
1355 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1357 return;
1360 // Try JWK
1361 ClearException ce(aCx);
1362 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aKeyData));
1363 if (!mJwk.Init(aCx, value)) {
1364 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1365 return;
1368 mDataIsJwk = true;
1371 void SetKeyDataMaybeParseJWK(const CryptoBuffer& aKeyData) {
1372 if (!mKeyData.Assign(aKeyData)) {
1373 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1374 return;
1377 mDataIsJwk = false;
1379 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1380 nsDependentCSubstring utf8(
1381 (const char*)mKeyData.Elements(),
1382 (const char*)(mKeyData.Elements() + mKeyData.Length()));
1383 if (!IsUtf8(utf8)) {
1384 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1385 return;
1388 nsString json = NS_ConvertUTF8toUTF16(utf8);
1389 if (!mJwk.Init(json)) {
1390 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1391 return;
1394 mDataIsJwk = true;
1398 void SetRawKeyData(const CryptoBuffer& aKeyData) {
1399 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1400 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1401 return;
1404 if (!mKeyData.Assign(aKeyData)) {
1405 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1406 return;
1409 mDataIsJwk = false;
1412 protected:
1413 nsString mFormat;
1414 RefPtr<CryptoKey> mKey;
1415 CryptoBuffer mKeyData;
1416 bool mDataIsSet;
1417 bool mDataIsJwk;
1418 JsonWebKey mJwk;
1419 nsString mAlgName;
1421 private:
1422 virtual void Resolve() override { mResultPromise->MaybeResolve(mKey); }
1424 virtual void Cleanup() override { mKey = nullptr; }
1427 class ImportSymmetricKeyTask : public ImportKeyTask {
1428 public:
1429 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1430 const nsAString& aFormat,
1431 const ObjectOrString& aAlgorithm, bool aExtractable,
1432 const Sequence<nsString>& aKeyUsages) {
1433 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1436 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1437 const nsAString& aFormat,
1438 const JS::Handle<JSObject*> aKeyData,
1439 const ObjectOrString& aAlgorithm, bool aExtractable,
1440 const Sequence<nsString>& aKeyUsages) {
1441 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1442 if (NS_FAILED(mEarlyRv)) {
1443 return;
1446 SetKeyData(aCx, aKeyData);
1447 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1448 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1449 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1450 return;
1454 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1455 const ObjectOrString& aAlgorithm, bool aExtractable,
1456 const Sequence<nsString>& aKeyUsages) {
1457 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1458 aKeyUsages);
1459 if (NS_FAILED(mEarlyRv)) {
1460 return;
1463 // This task only supports raw and JWK format.
1464 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1465 !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1466 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1467 return;
1470 // If this is an HMAC key, import the hash name
1471 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1472 RootedDictionary<HmacImportParams> params(aCx);
1473 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1474 if (NS_FAILED(mEarlyRv)) {
1475 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1476 return;
1478 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1479 if (NS_FAILED(mEarlyRv)) {
1480 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1481 return;
1486 virtual nsresult BeforeCrypto() override {
1487 nsresult rv;
1489 // If we're doing a JWK import, import the key data
1490 if (mDataIsJwk) {
1491 if (!mJwk.mK.WasPassed()) {
1492 return NS_ERROR_DOM_DATA_ERR;
1495 // Import the key material
1496 rv = mKeyData.FromJwkBase64(mJwk.mK.Value());
1497 if (NS_FAILED(rv)) {
1498 return NS_ERROR_DOM_DATA_ERR;
1501 // Check that we have valid key data.
1502 if (mKeyData.Length() == 0 &&
1503 !mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
1504 return NS_ERROR_DOM_DATA_ERR;
1507 // Construct an appropriate KeyAlorithm,
1508 // and verify that usages are appropriate
1509 if (mKeyData.Length() > UINT32_MAX / 8) {
1510 return NS_ERROR_DOM_DATA_ERR;
1512 uint32_t length = 8 * mKeyData.Length(); // bytes to bits
1513 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
1514 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
1515 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
1516 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
1517 if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
1518 CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1519 return NS_ERROR_DOM_DATA_ERR;
1522 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) &&
1523 mKey->HasUsageOtherThan(CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1524 return NS_ERROR_DOM_DATA_ERR;
1527 if ((length != 128) && (length != 192) && (length != 256)) {
1528 return NS_ERROR_DOM_DATA_ERR;
1530 mKey->Algorithm().MakeAes(mAlgName, length);
1532 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1533 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_ENC)) {
1534 return NS_ERROR_DOM_DATA_ERR;
1536 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
1537 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
1538 if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY |
1539 CryptoKey::DERIVEBITS)) {
1540 return NS_ERROR_DOM_DATA_ERR;
1542 mKey->Algorithm().MakeAes(mAlgName, length);
1544 if (mDataIsJwk && mJwk.mUse.WasPassed()) {
1545 // There is not a 'use' value consistent with PBKDF or HKDF
1546 return NS_ERROR_DOM_DATA_ERR;
1548 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1549 if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) {
1550 return NS_ERROR_DOM_DATA_ERR;
1553 mKey->Algorithm().MakeHmac(length, mHashName);
1555 if (mKey->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM) {
1556 return NS_ERROR_DOM_SYNTAX_ERR;
1559 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1560 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_SIG)) {
1561 return NS_ERROR_DOM_DATA_ERR;
1563 } else {
1564 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1567 if (NS_FAILED(mKey->SetSymKey(mKeyData))) {
1568 return NS_ERROR_DOM_OPERATION_ERR;
1571 mKey->SetType(CryptoKey::SECRET);
1573 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1574 return NS_ERROR_DOM_DATA_ERR;
1577 mEarlyComplete = true;
1578 return NS_OK;
1581 private:
1582 nsString mHashName;
1585 class ImportRsaKeyTask : public ImportKeyTask {
1586 public:
1587 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1588 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1589 bool aExtractable, const Sequence<nsString>& aKeyUsages)
1590 : mModulusLength(0) {
1591 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1594 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1595 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1596 const ObjectOrString& aAlgorithm, bool aExtractable,
1597 const Sequence<nsString>& aKeyUsages)
1598 : mModulusLength(0) {
1599 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1600 if (NS_FAILED(mEarlyRv)) {
1601 return;
1604 SetKeyData(aCx, aKeyData);
1605 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1606 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1607 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1608 return;
1612 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1613 const ObjectOrString& aAlgorithm, bool aExtractable,
1614 const Sequence<nsString>& aKeyUsages) {
1615 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1616 aKeyUsages);
1617 if (NS_FAILED(mEarlyRv)) {
1618 return;
1621 // If this is RSA with a hash, cache the hash name
1622 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1623 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
1624 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1625 RootedDictionary<RsaHashedImportParams> params(aCx);
1626 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1627 if (NS_FAILED(mEarlyRv)) {
1628 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1629 return;
1632 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1633 if (NS_FAILED(mEarlyRv)) {
1634 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1635 return;
1639 // Check support for the algorithm and hash names
1640 CK_MECHANISM_TYPE mech1 = MapAlgorithmNameToMechanism(mAlgName);
1641 CK_MECHANISM_TYPE mech2 = MapAlgorithmNameToMechanism(mHashName);
1642 if ((mech1 == UNKNOWN_CK_MECHANISM) || (mech2 == UNKNOWN_CK_MECHANISM)) {
1643 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1644 return;
1648 private:
1649 nsString mHashName;
1650 uint32_t mModulusLength;
1651 CryptoBuffer mPublicExponent;
1653 virtual nsresult DoCrypto() override {
1654 // Import the key data itself
1655 UniqueSECKEYPublicKey pubKey;
1656 UniqueSECKEYPrivateKey privKey;
1657 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1658 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1659 !mJwk.mD.WasPassed())) {
1660 // Public key import
1661 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1662 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1663 } else {
1664 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1667 if (!pubKey) {
1668 return NS_ERROR_DOM_DATA_ERR;
1671 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1672 return NS_ERROR_DOM_OPERATION_ERR;
1675 mKey->SetType(CryptoKey::PUBLIC);
1676 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) ||
1677 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1678 mJwk.mD.WasPassed())) {
1679 // Private key import
1680 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1681 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
1682 } else {
1683 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1686 if (!privKey) {
1687 return NS_ERROR_DOM_DATA_ERR;
1690 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1691 return NS_ERROR_DOM_OPERATION_ERR;
1694 mKey->SetType(CryptoKey::PRIVATE);
1695 pubKey = UniqueSECKEYPublicKey(SECKEY_ConvertToPublicKey(privKey.get()));
1696 if (!pubKey) {
1697 return NS_ERROR_DOM_UNKNOWN_ERR;
1699 } else {
1700 // Invalid key format
1701 return NS_ERROR_DOM_SYNTAX_ERR;
1704 // Extract relevant information from the public key
1705 mModulusLength = 8 * pubKey->u.rsa.modulus.len;
1706 if (!mPublicExponent.Assign(&pubKey->u.rsa.publicExponent)) {
1707 return NS_ERROR_DOM_OPERATION_ERR;
1710 return NS_OK;
1713 virtual nsresult AfterCrypto() override {
1714 // Check permissions for the requested operation
1715 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
1716 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1717 mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) ||
1718 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1719 mKey->HasUsageOtherThan(CryptoKey::DECRYPT |
1720 CryptoKey::UNWRAPKEY))) {
1721 return NS_ERROR_DOM_DATA_ERR;
1723 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1724 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1725 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1726 mKey->HasUsageOtherThan(CryptoKey::VERIFY)) ||
1727 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1728 mKey->HasUsageOtherThan(CryptoKey::SIGN))) {
1729 return NS_ERROR_DOM_DATA_ERR;
1733 // Set an appropriate KeyAlgorithm
1734 if (!mKey->Algorithm().MakeRsa(mAlgName, mModulusLength, mPublicExponent,
1735 mHashName)) {
1736 return NS_ERROR_DOM_OPERATION_ERR;
1739 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1740 return NS_ERROR_DOM_DATA_ERR;
1743 return NS_OK;
1747 class ImportEcKeyTask : public ImportKeyTask {
1748 public:
1749 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1750 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1751 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
1752 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1755 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1756 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1757 const ObjectOrString& aAlgorithm, bool aExtractable,
1758 const Sequence<nsString>& aKeyUsages) {
1759 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1760 if (NS_FAILED(mEarlyRv)) {
1761 return;
1764 SetKeyData(aCx, aKeyData);
1765 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1768 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1769 const ObjectOrString& aAlgorithm, bool aExtractable,
1770 const Sequence<nsString>& aKeyUsages) {
1771 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1772 aKeyUsages);
1773 if (NS_FAILED(mEarlyRv)) {
1774 return;
1777 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1778 RootedDictionary<EcKeyImportParams> params(aCx);
1779 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1780 if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
1781 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1782 return;
1785 if (!NormalizeToken(params.mNamedCurve.Value(), mNamedCurve)) {
1786 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1787 return;
1792 private:
1793 nsString mNamedCurve;
1795 virtual nsresult DoCrypto() override {
1796 // Import the key data itself
1797 UniqueSECKEYPublicKey pubKey;
1798 UniqueSECKEYPrivateKey privKey;
1800 if ((mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1801 mJwk.mD.WasPassed()) ||
1802 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1803 // Private key import
1804 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1805 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1806 if (!privKey) {
1807 return NS_ERROR_DOM_DATA_ERR;
1809 } else {
1810 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
1811 if (!privKey) {
1812 return NS_ERROR_DOM_DATA_ERR;
1815 ScopedAutoSECItem ecParams;
1816 if (PK11_ReadRawAttribute(PK11_TypePrivKey, privKey.get(),
1817 CKA_EC_PARAMS, &ecParams) != SECSuccess) {
1818 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1820 // Construct the OID tag.
1821 SECItem oid = {siBuffer, nullptr, 0};
1822 oid.len = ecParams.data[1];
1823 oid.data = ecParams.data + 2;
1824 // Find a matching and supported named curve.
1825 if (!MapOIDTagToNamedCurve(SECOID_FindOIDTag(&oid), mNamedCurve)) {
1826 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1830 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1831 return NS_ERROR_DOM_OPERATION_ERR;
1834 mKey->SetType(CryptoKey::PRIVATE);
1835 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
1836 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1837 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1838 !mJwk.mD.WasPassed())) {
1839 // Public key import
1840 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1841 pubKey = CryptoKey::PublicECKeyFromRaw(mKeyData, mNamedCurve);
1842 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1843 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1844 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1845 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1846 } else {
1847 MOZ_ASSERT(false);
1850 if (!pubKey) {
1851 return NS_ERROR_DOM_DATA_ERR;
1854 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1855 if (!CheckEncodedECParameters(&pubKey->u.ec.DEREncodedParams)) {
1856 return NS_ERROR_DOM_OPERATION_ERR;
1859 // Construct the OID tag.
1860 SECItem oid = {siBuffer, nullptr, 0};
1861 oid.len = pubKey->u.ec.DEREncodedParams.data[1];
1862 oid.data = pubKey->u.ec.DEREncodedParams.data + 2;
1864 // Find a matching and supported named curve.
1865 if (!MapOIDTagToNamedCurve(SECOID_FindOIDTag(&oid), mNamedCurve)) {
1866 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1870 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1871 return NS_ERROR_DOM_OPERATION_ERR;
1874 mKey->SetType(CryptoKey::PUBLIC);
1875 } else {
1876 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1879 // Extract 'crv' parameter from JWKs.
1880 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1881 if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) {
1882 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1885 return NS_OK;
1888 virtual nsresult AfterCrypto() override {
1889 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
1890 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
1891 privateAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
1892 publicAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
1893 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1894 privateAllowedUsages = CryptoKey::SIGN;
1895 publicAllowedUsages = CryptoKey::VERIFY;
1898 // Check permissions for the requested operation
1899 if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
1900 mKey->HasUsageOtherThan(privateAllowedUsages)) ||
1901 (mKey->GetKeyType() == CryptoKey::PUBLIC &&
1902 mKey->HasUsageOtherThan(publicAllowedUsages))) {
1903 return NS_ERROR_DOM_DATA_ERR;
1906 mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
1908 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1909 return NS_ERROR_DOM_DATA_ERR;
1912 return NS_OK;
1916 class ExportKeyTask : public WebCryptoTask {
1917 public:
1918 ExportKeyTask(const nsAString& aFormat, CryptoKey& aKey)
1919 : mFormat(aFormat),
1920 mPrivateKey(aKey.GetPrivateKey()),
1921 mPublicKey(aKey.GetPublicKey()),
1922 mKeyType(aKey.GetKeyType()),
1923 mExtractable(aKey.Extractable()),
1924 mAlg(aKey.Algorithm().JwkAlg()) {
1925 aKey.GetUsages(mKeyUsages);
1927 if (!mSymKey.Assign(aKey.GetSymKey())) {
1928 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
1929 return;
1933 protected:
1934 nsString mFormat;
1935 CryptoBuffer mSymKey;
1936 UniqueSECKEYPrivateKey mPrivateKey;
1937 UniqueSECKEYPublicKey mPublicKey;
1938 CryptoKey::KeyType mKeyType;
1939 bool mExtractable;
1940 nsString mAlg;
1941 nsTArray<nsString> mKeyUsages;
1942 CryptoBuffer mResult;
1943 JsonWebKey mJwk;
1945 private:
1946 virtual nsresult DoCrypto() override {
1947 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1948 if (mPublicKey && mPublicKey->keyType == dhKey) {
1949 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1952 if (mPublicKey && mPublicKey->keyType == ecKey) {
1953 nsresult rv = CryptoKey::PublicECKeyToRaw(mPublicKey.get(), mResult);
1954 if (NS_FAILED(rv)) {
1955 return NS_ERROR_DOM_OPERATION_ERR;
1957 return NS_OK;
1960 if (!mResult.Assign(mSymKey)) {
1961 return NS_ERROR_OUT_OF_MEMORY;
1963 if (mResult.Length() == 0) {
1964 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1967 return NS_OK;
1968 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1969 if (!mPrivateKey) {
1970 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1973 switch (mPrivateKey->keyType) {
1974 case rsaKey:
1975 case ecKey: {
1976 nsresult rv =
1977 CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), mResult);
1978 if (NS_FAILED(rv)) {
1979 return NS_ERROR_DOM_OPERATION_ERR;
1981 return NS_OK;
1983 default:
1984 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1986 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1987 if (!mPublicKey) {
1988 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1991 return CryptoKey::PublicKeyToSpki(mPublicKey.get(), mResult);
1992 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1993 if (mKeyType == CryptoKey::SECRET) {
1994 nsString k;
1995 nsresult rv = mSymKey.ToJwkBase64(k);
1996 if (NS_FAILED(rv)) {
1997 return NS_ERROR_DOM_OPERATION_ERR;
1999 mJwk.mK.Construct(k);
2000 mJwk.mKty = NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_SYMMETRIC);
2001 } else if (mKeyType == CryptoKey::PUBLIC) {
2002 if (!mPublicKey) {
2003 return NS_ERROR_DOM_UNKNOWN_ERR;
2006 nsresult rv = CryptoKey::PublicKeyToJwk(mPublicKey.get(), mJwk);
2007 if (NS_FAILED(rv)) {
2008 return NS_ERROR_DOM_OPERATION_ERR;
2010 } else if (mKeyType == CryptoKey::PRIVATE) {
2011 if (!mPrivateKey) {
2012 return NS_ERROR_DOM_UNKNOWN_ERR;
2015 nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey.get(), mJwk);
2016 if (NS_FAILED(rv)) {
2017 return NS_ERROR_DOM_OPERATION_ERR;
2021 if (!mAlg.IsEmpty()) {
2022 mJwk.mAlg.Construct(mAlg);
2025 mJwk.mExt.Construct(mExtractable);
2027 mJwk.mKey_ops.Construct();
2028 if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
2029 return NS_ERROR_OUT_OF_MEMORY;
2032 return NS_OK;
2035 return NS_ERROR_DOM_SYNTAX_ERR;
2038 // Returns mResult as an ArrayBufferView or JWK, as appropriate
2039 virtual void Resolve() override {
2040 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2041 mResultPromise->MaybeResolve(mJwk);
2042 return;
2045 TypedArrayCreator<ArrayBuffer> ret(mResult);
2046 mResultPromise->MaybeResolve(ret);
2050 class GenerateSymmetricKeyTask : public WebCryptoTask {
2051 public:
2052 GenerateSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2053 const ObjectOrString& aAlgorithm, bool aExtractable,
2054 const Sequence<nsString>& aKeyUsages) {
2055 // Create an empty key and set easy attributes
2056 mKey = new CryptoKey(aGlobal);
2057 mKey->SetExtractable(aExtractable);
2058 mKey->SetType(CryptoKey::SECRET);
2060 // Extract algorithm name
2061 nsString algName;
2062 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
2063 if (NS_FAILED(mEarlyRv)) {
2064 return;
2067 // Construct an appropriate KeyAlorithm
2068 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2069 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2070 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2071 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
2072 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aAlgorithm, mLength);
2073 if (NS_FAILED(mEarlyRv)) {
2074 return;
2076 mKey->Algorithm().MakeAes(algName, mLength);
2078 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2079 RootedDictionary<HmacKeyGenParams> params(aCx);
2080 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2081 if (NS_FAILED(mEarlyRv)) {
2082 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2083 return;
2086 nsString hashName;
2087 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2088 if (NS_FAILED(mEarlyRv)) {
2089 return;
2092 if (params.mLength.WasPassed()) {
2093 mLength = params.mLength.Value();
2094 } else {
2095 mLength = MapHashAlgorithmNameToBlockSize(hashName);
2098 if (mLength == 0) {
2099 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2100 return;
2103 mKey->Algorithm().MakeHmac(mLength, hashName);
2104 } else {
2105 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2106 return;
2109 // Add key usages
2110 mKey->ClearUsages();
2111 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2112 mEarlyRv = mKey->AddAllowedUsageIntersecting(aKeyUsages[i], algName);
2113 if (NS_FAILED(mEarlyRv)) {
2114 return;
2117 if (!mKey->HasAnyUsage()) {
2118 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2119 return;
2122 mLength = mLength >> 3; // bits to bytes
2123 mMechanism = mKey->Algorithm().Mechanism();
2124 // SetSymKey done in Resolve, after we've done the keygen
2127 private:
2128 RefPtr<CryptoKey> mKey;
2129 size_t mLength;
2130 CK_MECHANISM_TYPE mMechanism;
2131 CryptoBuffer mKeyData;
2133 virtual nsresult DoCrypto() override {
2134 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2135 MOZ_ASSERT(slot.get());
2137 UniquePK11SymKey symKey(
2138 PK11_KeyGen(slot.get(), mMechanism, nullptr, mLength, nullptr));
2139 if (!symKey) {
2140 return NS_ERROR_DOM_UNKNOWN_ERR;
2143 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2144 if (NS_FAILED(rv)) {
2145 return NS_ERROR_DOM_UNKNOWN_ERR;
2148 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2149 // just refers to a buffer managed by symKey. The assignment copies the
2150 // data, so mKeyData manages one copy, while symKey manages another.
2151 ATTEMPT_BUFFER_ASSIGN(mKeyData, PK11_GetKeyData(symKey.get()));
2152 return NS_OK;
2155 virtual void Resolve() override {
2156 if (NS_SUCCEEDED(mKey->SetSymKey(mKeyData))) {
2157 mResultPromise->MaybeResolve(mKey);
2158 } else {
2159 mResultPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
2163 virtual void Cleanup() override { mKey = nullptr; }
2166 GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask(
2167 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
2168 bool aExtractable, const Sequence<nsString>& aKeyUsages)
2169 : mKeyPair(new CryptoKeyPair()),
2170 mMechanism(CKM_INVALID_MECHANISM),
2171 mRsaParams(),
2172 mDhParams() {
2173 mArena = UniquePLArenaPool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2174 if (!mArena) {
2175 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
2176 return;
2179 // Create an empty key pair and set easy attributes
2180 mKeyPair->mPrivateKey = new CryptoKey(aGlobal);
2181 mKeyPair->mPublicKey = new CryptoKey(aGlobal);
2183 // Extract algorithm name
2184 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
2185 if (NS_FAILED(mEarlyRv)) {
2186 return;
2189 // Construct an appropriate KeyAlorithm
2190 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
2191 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2192 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
2193 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
2194 RootedDictionary<RsaHashedKeyGenParams> params(aCx);
2195 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2196 if (NS_FAILED(mEarlyRv)) {
2197 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2198 return;
2201 // Pull relevant info
2202 uint32_t modulusLength = params.mModulusLength;
2203 CryptoBuffer publicExponent;
2204 ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
2205 nsString hashName;
2206 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2207 if (NS_FAILED(mEarlyRv)) {
2208 return;
2211 // Create algorithm
2212 if (!mKeyPair->mPublicKey->Algorithm().MakeRsa(mAlgName, modulusLength,
2213 publicExponent, hashName)) {
2214 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2215 return;
2217 if (!mKeyPair->mPrivateKey->Algorithm().MakeRsa(mAlgName, modulusLength,
2218 publicExponent, hashName)) {
2219 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2220 return;
2222 mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
2224 // Set up params struct
2225 mRsaParams.keySizeInBits = modulusLength;
2226 bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
2227 if (!converted) {
2228 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2229 return;
2231 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
2232 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2233 RootedDictionary<EcKeyGenParams> params(aCx);
2234 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2235 if (NS_FAILED(mEarlyRv)) {
2236 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2237 return;
2240 if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
2241 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2242 return;
2245 // Create algorithm.
2246 mKeyPair->mPublicKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2247 mKeyPair->mPrivateKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
2248 mMechanism = CKM_EC_KEY_PAIR_GEN;
2249 } else {
2250 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2251 return;
2254 // Set key usages.
2255 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2256 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
2257 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2258 privateAllowedUsages = CryptoKey::SIGN;
2259 publicAllowedUsages = CryptoKey::VERIFY;
2260 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
2261 privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
2262 publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
2263 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
2264 privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
2265 publicAllowedUsages = 0;
2266 } else {
2267 MOZ_ASSERT(false); // This shouldn't happen.
2270 mKeyPair->mPrivateKey->SetExtractable(aExtractable);
2271 mKeyPair->mPrivateKey->SetType(CryptoKey::PRIVATE);
2273 mKeyPair->mPublicKey->SetExtractable(true);
2274 mKeyPair->mPublicKey->SetType(CryptoKey::PUBLIC);
2276 mKeyPair->mPrivateKey->ClearUsages();
2277 mKeyPair->mPublicKey->ClearUsages();
2278 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2279 mEarlyRv = mKeyPair->mPrivateKey->AddAllowedUsageIntersecting(
2280 aKeyUsages[i], mAlgName, privateAllowedUsages);
2281 if (NS_FAILED(mEarlyRv)) {
2282 return;
2285 mEarlyRv = mKeyPair->mPublicKey->AddAllowedUsageIntersecting(
2286 aKeyUsages[i], mAlgName, publicAllowedUsages);
2287 if (NS_FAILED(mEarlyRv)) {
2288 return;
2293 nsresult GenerateAsymmetricKeyTask::DoCrypto() {
2294 MOZ_ASSERT(mKeyPair);
2296 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2297 MOZ_ASSERT(slot.get());
2299 void* param;
2300 switch (mMechanism) {
2301 case CKM_RSA_PKCS_KEY_PAIR_GEN:
2302 param = &mRsaParams;
2303 break;
2304 case CKM_DH_PKCS_KEY_PAIR_GEN:
2305 param = &mDhParams;
2306 break;
2307 case CKM_EC_KEY_PAIR_GEN: {
2308 param = CreateECParamsForCurve(mNamedCurve, mArena.get());
2309 if (!param) {
2310 return NS_ERROR_DOM_UNKNOWN_ERR;
2312 break;
2314 default:
2315 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2318 SECKEYPublicKey* pubKey = nullptr;
2319 mPrivateKey = UniqueSECKEYPrivateKey(PK11_GenerateKeyPair(
2320 slot.get(), mMechanism, param, &pubKey, PR_FALSE, PR_FALSE, nullptr));
2321 mPublicKey = UniqueSECKEYPublicKey(pubKey);
2322 pubKey = nullptr;
2323 if (!mPrivateKey.get() || !mPublicKey.get()) {
2324 return NS_ERROR_DOM_OPERATION_ERR;
2327 // If no usages ended up being allowed, SyntaxError
2328 if (!mKeyPair->mPrivateKey->HasAnyUsage()) {
2329 return NS_ERROR_DOM_SYNTAX_ERR;
2332 nsresult rv = mKeyPair->mPrivateKey->SetPrivateKey(mPrivateKey.get());
2333 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2334 rv = mKeyPair->mPublicKey->SetPublicKey(mPublicKey.get());
2335 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2337 // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
2338 // private key, we need this later when exporting to PKCS8 and JWK though.
2339 if (mMechanism == CKM_EC_KEY_PAIR_GEN) {
2340 rv = mKeyPair->mPrivateKey->AddPublicKeyData(mPublicKey.get());
2341 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2344 return NS_OK;
2347 void GenerateAsymmetricKeyTask::Resolve() {
2348 mResultPromise->MaybeResolve(*mKeyPair);
2351 void GenerateAsymmetricKeyTask::Cleanup() { mKeyPair = nullptr; }
2353 class DeriveHkdfBitsTask : public ReturnArrayBufferViewTask {
2354 public:
2355 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2356 CryptoKey& aKey, uint32_t aLength)
2357 : mMechanism(CKM_INVALID_MECHANISM) {
2358 Init(aCx, aAlgorithm, aKey, aLength);
2361 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2362 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2363 : mLengthInBits(0), mLengthInBytes(0), mMechanism(CKM_INVALID_MECHANISM) {
2364 size_t length;
2365 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2367 if (NS_SUCCEEDED(mEarlyRv)) {
2368 Init(aCx, aAlgorithm, aKey, length);
2372 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2373 uint32_t aLength) {
2374 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_HKDF);
2375 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HKDF);
2377 if (!mSymKey.Assign(aKey.GetSymKey())) {
2378 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2379 return;
2382 // Check that we have a key.
2383 if (mSymKey.Length() == 0) {
2384 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2385 return;
2388 RootedDictionary<HkdfParams> params(aCx);
2389 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2390 if (NS_FAILED(mEarlyRv)) {
2391 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2392 return;
2395 // length must be greater than zero.
2396 if (aLength == 0) {
2397 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2398 return;
2401 // Extract the hash algorithm.
2402 nsString hashName;
2403 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2404 if (NS_FAILED(mEarlyRv)) {
2405 return;
2408 // Check the given hash algorithm.
2409 switch (MapAlgorithmNameToMechanism(hashName)) {
2410 case CKM_SHA_1:
2411 mMechanism = CKM_NSS_HKDF_SHA1;
2412 break;
2413 case CKM_SHA256:
2414 mMechanism = CKM_NSS_HKDF_SHA256;
2415 break;
2416 case CKM_SHA384:
2417 mMechanism = CKM_NSS_HKDF_SHA384;
2418 break;
2419 case CKM_SHA512:
2420 mMechanism = CKM_NSS_HKDF_SHA512;
2421 break;
2422 default:
2423 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2424 return;
2427 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2428 ATTEMPT_BUFFER_INIT(mInfo, params.mInfo)
2429 mLengthInBytes = ceil((double)aLength / 8);
2430 mLengthInBits = aLength;
2433 private:
2434 size_t mLengthInBits;
2435 size_t mLengthInBytes;
2436 CryptoBuffer mSalt;
2437 CryptoBuffer mInfo;
2438 CryptoBuffer mSymKey;
2439 CK_MECHANISM_TYPE mMechanism;
2441 virtual nsresult DoCrypto() override {
2442 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2443 if (!arena) {
2444 return NS_ERROR_DOM_OPERATION_ERR;
2447 // Import the key
2448 SECItem keyItem = {siBuffer, nullptr, 0};
2449 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2451 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2452 if (!slot.get()) {
2453 return NS_ERROR_DOM_OPERATION_ERR;
2456 UniquePK11SymKey baseKey(PK11_ImportSymKey(slot.get(), mMechanism,
2457 PK11_OriginUnwrap, CKA_WRAP,
2458 &keyItem, nullptr));
2459 if (!baseKey) {
2460 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2463 SECItem salt = {siBuffer, nullptr, 0};
2464 SECItem info = {siBuffer, nullptr, 0};
2465 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2466 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &info, mInfo);
2468 CK_NSS_HKDFParams hkdfParams = {true, salt.data, salt.len,
2469 true, info.data, info.len};
2470 SECItem params = {siBuffer, (unsigned char*)&hkdfParams,
2471 sizeof(hkdfParams)};
2473 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2474 // derived symmetric key and don't matter because we ignore them anyway.
2475 UniquePK11SymKey symKey(PK11_Derive(baseKey.get(), mMechanism, &params,
2476 CKM_SHA512_HMAC, CKA_SIGN,
2477 mLengthInBytes));
2479 if (!symKey.get()) {
2480 return NS_ERROR_DOM_OPERATION_ERR;
2483 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2484 if (NS_FAILED(rv)) {
2485 return NS_ERROR_DOM_OPERATION_ERR;
2488 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2489 // just refers to a buffer managed by symKey. The assignment copies the
2490 // data, so mResult manages one copy, while symKey manages another.
2491 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2493 if (mLengthInBytes > mResult.Length()) {
2494 return NS_ERROR_DOM_DATA_ERR;
2497 if (!mResult.SetLength(mLengthInBytes, fallible)) {
2498 return NS_ERROR_DOM_UNKNOWN_ERR;
2501 // If the number of bits to derive is not a multiple of 8 we need to
2502 // zero out the remaining bits that were derived but not requested.
2503 if (mLengthInBits % 8) {
2504 mResult[mResult.Length() - 1] &= 0xff << (mLengthInBits % 8);
2507 return NS_OK;
2511 class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask {
2512 public:
2513 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2514 CryptoKey& aKey, uint32_t aLength)
2515 : mHashOidTag(SEC_OID_UNKNOWN) {
2516 Init(aCx, aAlgorithm, aKey, aLength);
2519 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2520 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2521 : mLength(0), mIterations(0), mHashOidTag(SEC_OID_UNKNOWN) {
2522 size_t length;
2523 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2525 if (NS_SUCCEEDED(mEarlyRv)) {
2526 Init(aCx, aAlgorithm, aKey, length);
2530 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2531 uint32_t aLength) {
2532 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_PBKDF2);
2533 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_PBKDF2);
2535 if (!mSymKey.Assign(aKey.GetSymKey())) {
2536 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2537 return;
2540 RootedDictionary<Pbkdf2Params> params(aCx);
2541 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2542 if (NS_FAILED(mEarlyRv)) {
2543 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2544 return;
2547 // length must be a multiple of 8 bigger than zero.
2548 if (aLength == 0 || aLength % 8) {
2549 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2550 return;
2553 // Extract the hash algorithm.
2554 nsString hashName;
2555 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2556 if (NS_FAILED(mEarlyRv)) {
2557 return;
2560 // Check the given hash algorithm.
2561 switch (MapAlgorithmNameToMechanism(hashName)) {
2562 case CKM_SHA_1:
2563 mHashOidTag = SEC_OID_HMAC_SHA1;
2564 break;
2565 case CKM_SHA256:
2566 mHashOidTag = SEC_OID_HMAC_SHA256;
2567 break;
2568 case CKM_SHA384:
2569 mHashOidTag = SEC_OID_HMAC_SHA384;
2570 break;
2571 case CKM_SHA512:
2572 mHashOidTag = SEC_OID_HMAC_SHA512;
2573 break;
2574 default:
2575 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2576 return;
2579 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2580 mLength = aLength >> 3; // bits to bytes
2581 mIterations = params.mIterations;
2584 private:
2585 size_t mLength;
2586 size_t mIterations;
2587 CryptoBuffer mSalt;
2588 CryptoBuffer mSymKey;
2589 SECOidTag mHashOidTag;
2591 virtual nsresult DoCrypto() override {
2592 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2593 if (!arena) {
2594 return NS_ERROR_DOM_OPERATION_ERR;
2597 SECItem salt = {siBuffer, nullptr, 0};
2598 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2599 // PK11_CreatePBEV2AlgorithmID will "helpfully" create PBKDF2 parameters
2600 // with a random salt if given a SECItem* that is either null or has a null
2601 // data pointer. This obviously isn't what we want, so we have to fake it
2602 // out by passing in a SECItem* with a non-null data pointer but with zero
2603 // length.
2604 if (!salt.data) {
2605 MOZ_ASSERT(salt.len == 0);
2606 salt.data =
2607 reinterpret_cast<unsigned char*>(PORT_ArenaAlloc(arena.get(), 1));
2608 if (!salt.data) {
2609 return NS_ERROR_DOM_UNKNOWN_ERR;
2613 // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this
2614 // parameter is unused for key generation. It is currently only used
2615 // for PBKDF2 authentication or key (un)wrapping when specifying an
2616 // encryption algorithm (PBES2).
2617 UniqueSECAlgorithmID algID(
2618 PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1,
2619 mHashOidTag, mLength, mIterations, &salt));
2621 if (!algID) {
2622 return NS_ERROR_DOM_OPERATION_ERR;
2625 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2626 if (!slot.get()) {
2627 return NS_ERROR_DOM_OPERATION_ERR;
2630 SECItem keyItem = {siBuffer, nullptr, 0};
2631 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2633 UniquePK11SymKey symKey(
2634 PK11_PBEKeyGen(slot.get(), algID.get(), &keyItem, false, nullptr));
2635 if (!symKey.get()) {
2636 return NS_ERROR_DOM_OPERATION_ERR;
2639 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2640 if (NS_FAILED(rv)) {
2641 return NS_ERROR_DOM_OPERATION_ERR;
2644 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2645 // just refers to a buffer managed by symKey. The assignment copies the
2646 // data, so mResult manages one copy, while symKey manages another.
2647 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2648 return NS_OK;
2652 template <class DeriveBitsTask>
2653 class DeriveKeyTask : public DeriveBitsTask {
2654 public:
2655 DeriveKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2656 const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
2657 const ObjectOrString& aDerivedKeyType, bool aExtractable,
2658 const Sequence<nsString>& aKeyUsages)
2659 : DeriveBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType) {
2660 if (NS_FAILED(this->mEarlyRv)) {
2661 return;
2664 constexpr auto format =
2665 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_FORMAT_RAW);
2666 mTask = new ImportSymmetricKeyTask(aGlobal, aCx, format, aDerivedKeyType,
2667 aExtractable, aKeyUsages);
2670 protected:
2671 RefPtr<ImportSymmetricKeyTask> mTask;
2673 private:
2674 virtual void Resolve() override {
2675 mTask->SetRawKeyData(this->mResult);
2676 mTask->DispatchWithPromise(this->mResultPromise);
2679 virtual void Cleanup() override { mTask = nullptr; }
2681 class DeriveEcdhBitsTask : public ReturnArrayBufferViewTask {
2682 public:
2683 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2684 CryptoKey& aKey, uint32_t aLength)
2685 : mLength(aLength), mPrivKey(aKey.GetPrivateKey()) {
2686 Init(aCx, aAlgorithm, aKey);
2689 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2690 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2691 : mPrivKey(aKey.GetPrivateKey()) {
2692 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, mLength);
2693 if (NS_SUCCEEDED(mEarlyRv)) {
2694 Init(aCx, aAlgorithm, aKey);
2698 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey) {
2699 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDH);
2700 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDH);
2702 // Check that we have a private key.
2703 if (!mPrivKey) {
2704 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2705 return;
2708 // Length must be a multiple of 8 bigger than zero.
2709 if (mLength == 0 || mLength % 8) {
2710 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2711 return;
2714 mLength = mLength >> 3; // bits to bytes
2716 // Retrieve the peer's public key.
2717 RootedDictionary<EcdhKeyDeriveParams> params(aCx);
2718 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2719 if (NS_FAILED(mEarlyRv)) {
2720 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2721 return;
2724 CryptoKey* publicKey = params.mPublic;
2725 mPubKey = publicKey->GetPublicKey();
2726 if (!mPubKey) {
2727 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2728 return;
2731 CHECK_KEY_ALGORITHM(publicKey->Algorithm(), WEBCRYPTO_ALG_ECDH);
2733 // Both keys must use the same named curve.
2734 nsString curve1 = aKey.Algorithm().mEc.mNamedCurve;
2735 nsString curve2 = publicKey->Algorithm().mEc.mNamedCurve;
2737 if (!curve1.Equals(curve2)) {
2738 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2739 return;
2743 private:
2744 size_t mLength;
2745 UniqueSECKEYPrivateKey mPrivKey;
2746 UniqueSECKEYPublicKey mPubKey;
2748 virtual nsresult DoCrypto() override {
2749 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2750 // derived symmetric key and don't matter because we ignore them anyway.
2751 UniquePK11SymKey symKey(
2752 PK11_PubDeriveWithKDF(mPrivKey.get(), mPubKey.get(), PR_FALSE, nullptr,
2753 nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC,
2754 CKA_SIGN, 0, CKD_NULL, nullptr, nullptr));
2756 if (!symKey.get()) {
2757 return NS_ERROR_DOM_OPERATION_ERR;
2760 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2761 if (NS_FAILED(rv)) {
2762 return NS_ERROR_DOM_OPERATION_ERR;
2765 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2766 // just refers to a buffer managed by symKey. The assignment copies the
2767 // data, so mResult manages one copy, while symKey manages another.
2768 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2770 if (mLength > mResult.Length()) {
2771 return NS_ERROR_DOM_DATA_ERR;
2774 if (!mResult.SetLength(mLength, fallible)) {
2775 return NS_ERROR_DOM_UNKNOWN_ERR;
2778 return NS_OK;
2782 template <class KeyEncryptTask>
2783 class WrapKeyTask : public ExportKeyTask {
2784 public:
2785 WrapKeyTask(JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
2786 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm)
2787 : ExportKeyTask(aFormat, aKey) {
2788 if (NS_FAILED(mEarlyRv)) {
2789 return;
2792 mTask = new KeyEncryptTask(aCx, aWrapAlgorithm, aWrappingKey, true);
2795 private:
2796 RefPtr<KeyEncryptTask> mTask;
2798 virtual nsresult AfterCrypto() override {
2799 // If wrapping JWK, stringify the JSON
2800 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2801 nsAutoString json;
2802 if (!mJwk.ToJSON(json)) {
2803 return NS_ERROR_DOM_OPERATION_ERR;
2806 NS_ConvertUTF16toUTF8 utf8(json);
2807 if (!mResult.Assign((const uint8_t*)utf8.BeginReading(), utf8.Length())) {
2808 return NS_ERROR_DOM_OPERATION_ERR;
2812 return NS_OK;
2815 virtual void Resolve() override {
2816 mTask->SetData(mResult);
2817 mTask->DispatchWithPromise(mResultPromise);
2820 virtual void Cleanup() override { mTask = nullptr; }
2823 template <class KeyEncryptTask>
2824 class UnwrapKeyTask : public KeyEncryptTask {
2825 public:
2826 UnwrapKeyTask(JSContext* aCx, const ArrayBufferViewOrArrayBuffer& aWrappedKey,
2827 CryptoKey& aUnwrappingKey,
2828 const ObjectOrString& aUnwrapAlgorithm, ImportKeyTask* aTask)
2829 : KeyEncryptTask(aCx, aUnwrapAlgorithm, aUnwrappingKey, aWrappedKey,
2830 false),
2831 mTask(aTask) {}
2833 private:
2834 RefPtr<ImportKeyTask> mTask;
2836 virtual void Resolve() override {
2837 mTask->SetKeyDataMaybeParseJWK(KeyEncryptTask::mResult);
2838 mTask->DispatchWithPromise(KeyEncryptTask::mResultPromise);
2841 virtual void Cleanup() override { mTask = nullptr; }
2844 // Task creation methods for WebCryptoTask
2846 // Note: We do not perform algorithm normalization as a monolithic process,
2847 // as described in the spec. Instead:
2848 // * Each method handles its slice of the supportedAlgorithms structure
2849 // * Task constructors take care of:
2850 // * Coercing the algorithm to the proper concrete type
2851 // * Cloning subordinate data items
2852 // * Cloning input data as needed
2854 // Thus, support for different algorithms is determined by the if-statements
2855 // below, rather than a data structure.
2857 // This results in algorithm normalization coming after some other checks,
2858 // and thus slightly more steps being done synchronously than the spec calls
2859 // for. But none of these steps is especially time-consuming.
2861 WebCryptoTask* WebCryptoTask::CreateEncryptDecryptTask(
2862 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2863 const CryptoOperationData& aData, bool aEncrypt) {
2864 TelemetryMethod method = (aEncrypt) ? TM_ENCRYPT : TM_DECRYPT;
2865 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
2866 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_ENC,
2867 aKey.Extractable());
2869 // Ensure key is usable for this operation
2870 if ((aEncrypt && !aKey.HasUsage(CryptoKey::ENCRYPT)) ||
2871 (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) {
2872 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2875 nsString algName;
2876 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2877 if (NS_FAILED(rv)) {
2878 return new FailureTask(rv);
2881 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2882 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2883 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
2884 return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
2885 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
2886 return new RsaOaepTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
2889 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2892 WebCryptoTask* WebCryptoTask::CreateSignVerifyTask(
2893 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2894 const CryptoOperationData& aSignature, const CryptoOperationData& aData,
2895 bool aSign) {
2896 TelemetryMethod method = (aSign) ? TM_SIGN : TM_VERIFY;
2897 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
2898 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_SIG,
2899 aKey.Extractable());
2901 // Ensure key is usable for this operation
2902 if ((aSign && !aKey.HasUsage(CryptoKey::SIGN)) ||
2903 (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) {
2904 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2907 nsString algName;
2908 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2909 if (NS_FAILED(rv)) {
2910 return new FailureTask(rv);
2913 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2914 return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
2915 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2916 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
2917 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2918 return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
2919 aData, aSign);
2922 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2925 WebCryptoTask* WebCryptoTask::CreateDigestTask(
2926 JSContext* aCx, const ObjectOrString& aAlgorithm,
2927 const CryptoOperationData& aData) {
2928 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DIGEST);
2930 nsString algName;
2931 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2932 if (NS_FAILED(rv)) {
2933 return new FailureTask(rv);
2936 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
2937 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256) ||
2938 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
2939 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
2940 return new DigestTask(aCx, aAlgorithm, aData);
2943 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2946 WebCryptoTask* WebCryptoTask::CreateImportKeyTask(
2947 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
2948 JS::Handle<JSObject*> aKeyData, const ObjectOrString& aAlgorithm,
2949 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
2950 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY);
2951 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable);
2953 // Verify that the format is recognized
2954 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
2955 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
2956 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
2957 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2958 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
2961 // Verify that aKeyUsages does not contain an unrecognized value
2962 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
2963 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
2966 nsString algName;
2967 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2968 if (NS_FAILED(rv)) {
2969 return new FailureTask(rv);
2972 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
2973 // However, the spec should be updated to allow it.
2974 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2975 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2976 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2977 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
2978 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
2979 algName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
2980 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2981 return new ImportSymmetricKeyTask(aGlobal, aCx, aFormat, aKeyData,
2982 aAlgorithm, aExtractable, aKeyUsages);
2983 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2984 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
2985 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
2986 return new ImportRsaKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
2987 aExtractable, aKeyUsages);
2988 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
2989 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2990 return new ImportEcKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
2991 aExtractable, aKeyUsages);
2992 } else {
2993 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2997 WebCryptoTask* WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat,
2998 CryptoKey& aKey) {
2999 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_EXPORTKEY);
3001 // Verify that the format is recognized
3002 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3003 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3004 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3005 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3006 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3009 // Verify that the key is extractable
3010 if (!aKey.Extractable()) {
3011 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3014 // Verify that the algorithm supports export
3015 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
3016 // However, the spec should be updated to allow it.
3017 nsString algName = aKey.Algorithm().mName;
3018 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3019 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3020 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
3021 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
3022 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
3023 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC) ||
3024 algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3025 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
3026 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
3027 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
3028 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
3029 return new ExportKeyTask(aFormat, aKey);
3032 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3035 WebCryptoTask* WebCryptoTask::CreateGenerateKeyTask(
3036 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3037 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3038 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_GENERATEKEY);
3039 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_GENERATE,
3040 aExtractable);
3042 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3043 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3046 nsString algName;
3047 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3048 if (NS_FAILED(rv)) {
3049 return new FailureTask(rv);
3052 if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3053 algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3054 algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3055 algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
3056 algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3057 return new GenerateSymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3058 aKeyUsages);
3059 } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3060 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3061 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS) ||
3062 algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
3063 algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA)) {
3064 return new GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3065 aKeyUsages);
3066 } else {
3067 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3071 WebCryptoTask* WebCryptoTask::CreateDeriveKeyTask(
3072 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3073 CryptoKey& aBaseKey, const ObjectOrString& aDerivedKeyType,
3074 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3075 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY);
3077 // Ensure baseKey is usable for this operation
3078 if (!aBaseKey.HasUsage(CryptoKey::DERIVEKEY)) {
3079 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3082 // Verify that aKeyUsages does not contain an unrecognized value
3083 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3084 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3087 nsString algName;
3088 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3089 if (NS_FAILED(rv)) {
3090 return new FailureTask(rv);
3093 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3094 return new DeriveKeyTask<DeriveHkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3095 aBaseKey, aDerivedKeyType,
3096 aExtractable, aKeyUsages);
3099 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3100 return new DeriveKeyTask<DerivePbkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3101 aBaseKey, aDerivedKeyType,
3102 aExtractable, aKeyUsages);
3105 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3106 return new DeriveKeyTask<DeriveEcdhBitsTask>(aGlobal, aCx, aAlgorithm,
3107 aBaseKey, aDerivedKeyType,
3108 aExtractable, aKeyUsages);
3111 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3114 WebCryptoTask* WebCryptoTask::CreateDeriveBitsTask(
3115 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
3116 uint32_t aLength) {
3117 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS);
3119 // Ensure baseKey is usable for this operation
3120 if (!aKey.HasUsage(CryptoKey::DERIVEBITS)) {
3121 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3124 nsString algName;
3125 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3126 if (NS_FAILED(rv)) {
3127 return new FailureTask(rv);
3130 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3131 return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3134 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3135 return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
3138 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3139 return new DeriveHkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3142 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3145 WebCryptoTask* WebCryptoTask::CreateWrapKeyTask(
3146 JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
3147 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm) {
3148 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_WRAPKEY);
3150 // Verify that the format is recognized
3151 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3152 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3153 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3154 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3155 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3158 // Ensure wrappingKey is usable for this operation
3159 if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) {
3160 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3163 // Ensure key is extractable
3164 if (!aKey.Extractable()) {
3165 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3168 nsString wrapAlgName;
3169 nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName);
3170 if (NS_FAILED(rv)) {
3171 return new FailureTask(rv);
3174 if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3175 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3176 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3177 return new WrapKeyTask<AesTask>(aCx, aFormat, aKey, aWrappingKey,
3178 aWrapAlgorithm);
3179 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3180 return new WrapKeyTask<AesKwTask>(aCx, aFormat, aKey, aWrappingKey,
3181 aWrapAlgorithm);
3182 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3183 return new WrapKeyTask<RsaOaepTask>(aCx, aFormat, aKey, aWrappingKey,
3184 aWrapAlgorithm);
3187 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3190 WebCryptoTask* WebCryptoTask::CreateUnwrapKeyTask(
3191 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
3192 const ArrayBufferViewOrArrayBuffer& aWrappedKey, CryptoKey& aUnwrappingKey,
3193 const ObjectOrString& aUnwrapAlgorithm,
3194 const ObjectOrString& aUnwrappedKeyAlgorithm, bool aExtractable,
3195 const Sequence<nsString>& aKeyUsages) {
3196 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_UNWRAPKEY);
3198 // Ensure key is usable for this operation
3199 if (!aUnwrappingKey.HasUsage(CryptoKey::UNWRAPKEY)) {
3200 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3203 // Verify that aKeyUsages does not contain an unrecognized value
3204 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3205 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3208 nsString keyAlgName;
3209 nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName);
3210 if (NS_FAILED(rv)) {
3211 return new FailureTask(rv);
3214 CryptoOperationData dummy;
3215 RefPtr<ImportKeyTask> importTask;
3216 if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3217 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3218 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3219 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
3220 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HKDF) ||
3221 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3222 importTask = new ImportSymmetricKeyTask(aGlobal, aCx, aFormat,
3223 aUnwrappedKeyAlgorithm,
3224 aExtractable, aKeyUsages);
3225 } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3226 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3227 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS)) {
3228 importTask =
3229 new ImportRsaKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3230 aExtractable, aKeyUsages);
3231 } else if (keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
3232 keyAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
3233 importTask =
3234 new ImportEcKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3235 aExtractable, aKeyUsages);
3236 } else {
3237 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3240 nsString unwrapAlgName;
3241 rv = GetAlgorithmName(aCx, aUnwrapAlgorithm, unwrapAlgName);
3242 if (NS_FAILED(rv)) {
3243 return new FailureTask(rv);
3245 if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3246 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3247 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3248 return new UnwrapKeyTask<AesTask>(aCx, aWrappedKey, aUnwrappingKey,
3249 aUnwrapAlgorithm, importTask);
3250 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3251 return new UnwrapKeyTask<AesKwTask>(aCx, aWrappedKey, aUnwrappingKey,
3252 aUnwrapAlgorithm, importTask);
3253 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3254 return new UnwrapKeyTask<RsaOaepTask>(aCx, aWrappedKey, aUnwrappingKey,
3255 aUnwrapAlgorithm, importTask);
3258 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3261 WebCryptoTask::WebCryptoTask()
3262 : CancelableRunnable("WebCryptoTask"),
3263 mEarlyRv(NS_OK),
3264 mEarlyComplete(false),
3265 mOriginalEventTarget(nullptr),
3266 mRv(NS_ERROR_NOT_INITIALIZED) {}
3268 WebCryptoTask::~WebCryptoTask() = default;
3270 } // namespace mozilla::dom