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/. */
10 #include "nsNSSComponent.h"
11 #include "nsProxyRelease.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
)},
38 namespace mozilla::dom
{
40 // Pre-defined identifiers for telemetry histograms
42 enum TelemetryMethod
{
57 enum TelemetryAlgorithm
{
58 // Please make additions at the end of the list,
59 // to preserve comparability of histograms over time
66 TA_RSAES_PKCS1
= 5, // NB: This algorithm has been removed
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; \
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; \
121 class ClearException
{
123 explicit ClearException(JSContext
* aCx
) : mCx(aCx
) {}
125 ~ClearException() { JS_ClearPendingException(mCx
); }
132 static nsresult
GetAlgorithmName(JSContext
* aCx
, const OOS
& aAlgorithm
,
134 ClearException
ce(aCx
);
136 if (aAlgorithm
.IsString()) {
137 // If string, then treat as algorithm name
138 aName
.Assign(aAlgorithm
.GetAsString());
140 // Coerce to algorithm and extract name
141 JS::Rooted
<JS::Value
> value(aCx
,
142 JS::ObjectValue(*aAlgorithm
.GetAsObject()));
145 if (!alg
.Init(aCx
, value
)) {
146 return NS_ERROR_DOM_SYNTAX_ERR
;
152 if (!NormalizeToken(aName
, aName
)) {
153 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
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
;
175 inline size_t MapHashAlgorithmNameToBlockSize(const nsString
& aName
) {
176 if (aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA1
) ||
177 aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA256
)) {
181 if (aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA384
) ||
182 aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA512
)) {
189 inline nsresult
GetKeyLengthForAlgorithm(JSContext
* aCx
,
190 const ObjectOrString
& aAlgorithm
,
194 // Extract algorithm name
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
;
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();
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
);
241 return NS_ERROR_DOM_SYNTAX_ERR
;
248 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
251 inline bool MapOIDTagToNamedCurve(SECOidTag aOIDTag
, nsString
& aResult
) {
253 case SEC_OID_SECG_EC_SECP256R1
:
254 aResult
.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256
);
256 case SEC_OID_SECG_EC_SECP384R1
:
257 aResult
.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384
);
259 case SEC_OID_SECG_EC_SECP521R1
:
260 aResult
.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521
);
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
;
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
;
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
);
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
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
;
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
)
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
);
363 // We're now back on the calling thread.
366 // Stop holding the worker thread alive now that the async work has
368 mWorkerRef
= nullptr;
373 nsresult
WebCryptoTask::Cancel() {
374 MOZ_ASSERT(IsOnOriginalThread());
375 FailWithError(NS_BINDING_ABORTED
);
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;
392 nsresult
WebCryptoTask::CalculateResult() {
393 MOZ_ASSERT(!IsOnOriginalThread());
398 void WebCryptoTask::CallCallback(nsresult rv
) {
399 MOZ_ASSERT(IsOnOriginalThread());
405 nsresult rv2
= AfterCrypto();
406 if (NS_FAILED(rv2
)) {
412 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED
, true);
414 // Manually release mResultPromise while we're on the main thread
415 mResultPromise
= nullptr;
419 // Some generic utility classes
421 class FailureTask
: public WebCryptoTask
{
423 explicit FailureTask(nsresult aRv
) { mEarlyRv
= aRv
; }
426 class ReturnArrayBufferViewTask
: public WebCryptoTask
{
428 CryptoBuffer mResult
;
431 // Returns mResult as an ArrayBufferView, or an error
432 virtual void Resolve() override
{
433 TypedArrayCreator
<ArrayBuffer
> ret(mResult
);
434 mResultPromise
->MaybeResolve(ret
);
441 void SetData(const T
& aData
) {
442 mDataIsSet
= mData
.Assign(aData
);
446 DeferredData() : mDataIsSet(false) {}
452 class AesTask
: public ReturnArrayBufferViewTask
, public DeferredData
{
454 AesTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
456 : mMechanism(CKM_INVALID_MECHANISM
),
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
),
469 Init(aCx
, aAlgorithm
, aKey
, aEncrypt
);
473 void Init(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
476 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
477 if (NS_FAILED(mEarlyRv
)) {
481 if (!mSymKey
.Assign(aKey
.GetSymKey())) {
482 mEarlyRv
= NS_ERROR_OUT_OF_MEMORY
;
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
;
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
);
503 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
507 ATTEMPT_BUFFER_INIT(mIv
, params
.mIv
)
508 if (mIv
.Length() != 16) {
509 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
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
);
520 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
524 ATTEMPT_BUFFER_INIT(mIv
, params
.mCounter
)
525 if (mIv
.Length() != 16) {
526 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
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
);
539 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
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
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
;
561 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
564 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, telemetryAlg
);
568 CK_MECHANISM_TYPE mMechanism
;
569 CryptoBuffer mSymKey
;
570 CryptoBuffer mIv
; // Initialization vector
571 CryptoBuffer mAad
; // Additional Authenticated Data
573 uint8_t mCounterLength
;
576 virtual nsresult
DoCrypto() override
{
580 return NS_ERROR_DOM_OPERATION_ERR
;
583 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
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(), ¶m
, mIv
);
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
);
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
);
616 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
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
,
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 <
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
;
644 // Perform the encryption/decryption
646 rv
= MapSECStatus(PK11_Encrypt(
647 symKey
.get(), mMechanism
, ¶m
, mResult
.Elements(), &outLen
,
648 mResult
.Length(), mData
.Elements(), mData
.Length()));
650 rv
= MapSECStatus(PK11_Decrypt(
651 symKey
.get(), mMechanism
, ¶m
, mResult
.Elements(), &outLen
,
652 mResult
.Length(), mData
.Elements(), mData
.Length()));
654 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
656 mResult
.TruncateLength(outLen
);
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
{
665 AesKwTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
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
);
678 void Init(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
680 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_AES_KW
);
683 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
684 if (NS_FAILED(mEarlyRv
)) {
688 if (!mSymKey
.Assign(aKey
.GetSymKey())) {
689 mEarlyRv
= NS_ERROR_OUT_OF_MEMORY
;
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
;
700 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, TA_AES_KW
);
704 CK_MECHANISM_TYPE mMechanism
;
705 CryptoBuffer mSymKey
;
708 virtual nsresult
DoCrypto() override
{
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
));
722 return NS_ERROR_DOM_OPERATION_ERR
;
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
,
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
;
746 // Import the data into a fake PK11SymKey structure
747 UniquePK11SymKey
keyToWrap(
748 PK11_ImportSymKey(slot
.get(), fakeMechanism
, PK11_OriginUnwrap
,
749 fakeOperation
, &dataItem
, nullptr));
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
);
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
));
772 return NS_ERROR_DOM_OPERATION_ERR
;
775 // Export the key to get the cleartext
776 rv
= MapSECStatus(PK11_ExtractKeyValue(unwrappedKey
.get()));
778 return NS_ERROR_DOM_UNKNOWN_ERR
;
780 ATTEMPT_BUFFER_ASSIGN(mResult
, PK11_GetKeyData(unwrappedKey
.get()));
787 class RsaOaepTask
: public ReturnArrayBufferViewTask
, public DeferredData
{
789 RsaOaepTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
791 : mPrivKey(aKey
.GetPrivateKey()),
792 mPubKey(aKey
.GetPublicKey()),
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()),
802 Init(aCx
, aAlgorithm
, aKey
, aEncrypt
);
806 void Init(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
808 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, TA_RSA_OAEP
);
810 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_RSA_OAEP
);
814 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
817 mStrength
= SECKEY_PublicKeyStrength(mPubKey
.get());
820 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
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
;
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
;
855 CK_MECHANISM_TYPE mHashMechanism
;
856 CK_MECHANISM_TYPE mMgfMechanism
;
857 UniqueSECKEYPrivateKey mPrivKey
;
858 UniqueSECKEYPublicKey mPubKey
;
863 virtual nsresult
DoCrypto() override
{
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
;
886 param
.type
= siBuffer
;
887 param
.data
= (unsigned char*)&oaepParams
;
888 param
.len
= sizeof(oaepParams
);
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
, ¶m
, mResult
.Elements(), &outLen
,
899 mResult
.Length(), mData
.Elements(), mData
.Length(), nullptr));
901 rv
= MapSECStatus(PK11_PrivDecrypt(
902 mPrivKey
.get(), CKM_RSA_PKCS_OAEP
, ¶m
, mResult
.Elements(),
903 &outLen
, mResult
.Length(), mData
.Elements(), mData
.Length()));
905 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
907 mResult
.TruncateLength(outLen
);
912 class HmacTask
: public WebCryptoTask
{
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
);
922 ATTEMPT_BUFFER_INIT(mSignature
, aSignature
);
925 if (!mSymKey
.Assign(aKey
.GetSymKey())) {
926 mEarlyRv
= NS_ERROR_OUT_OF_MEMORY
;
930 // Check that we got a symmetric key
931 if (mSymKey
.Length() == 0) {
932 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
936 TelemetryAlgorithm telemetryAlg
;
937 switch (mMechanism
) {
939 telemetryAlg
= TA_HMAC_SHA_1
;
941 case CKM_SHA224_HMAC
:
942 telemetryAlg
= TA_HMAC_SHA_224
;
944 case CKM_SHA256_HMAC
:
945 telemetryAlg
= TA_HMAC_SHA_256
;
947 case CKM_SHA384_HMAC
:
948 telemetryAlg
= TA_HMAC_SHA_384
;
950 case CKM_SHA512_HMAC
:
951 telemetryAlg
= TA_HMAC_SHA_512
;
954 telemetryAlg
= TA_UNKNOWN
;
956 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, telemetryAlg
);
960 CK_MECHANISM_TYPE mMechanism
;
961 CryptoBuffer mSymKey
;
963 CryptoBuffer mSignature
;
964 CryptoBuffer mResult
;
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
));
975 return NS_ERROR_DOM_OPERATION_ERR
;
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
,
988 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
992 SECItem param
= {siBuffer
, nullptr, 0};
993 UniquePK11Context
ctx(
994 PK11_CreateContextBySymKey(mMechanism
, CKA_SIGN
, symKey
.get(), ¶m
));
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
);
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
,
1005 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
1007 mResult
.TruncateLength(outLen
);
1011 // Returns mResult as an ArrayBufferView, or an error
1012 virtual void Resolve() override
{
1014 // Return the computed MAC
1015 TypedArrayCreator
<ArrayBuffer
> ret(mResult
);
1016 mResultPromise
->MaybeResolve(ret
);
1018 // Compare the MAC to the provided signature
1019 // No truncation allowed
1020 bool equal
= (mResult
.Length() == mSignature
.Length());
1022 int cmp
= NSS_SecureMemcmp(mSignature
.Elements(), mResult
.Elements(),
1023 mSignature
.Length());
1026 mResultPromise
->MaybeResolve(equal
);
1031 class AsymmetricSignVerifyTask
: public WebCryptoTask
{
1033 AsymmetricSignVerifyTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
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()),
1045 mAlgorithm(Algorithm::UNKNOWN
) {
1046 ATTEMPT_BUFFER_INIT(mData
, aData
);
1048 ATTEMPT_BUFFER_INIT(mSignature
, aSignature
);
1052 nsString hashAlgName
;
1053 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
1054 if (NS_FAILED(mEarlyRv
)) {
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
;
1080 RootedDictionary
<RsaPssParams
> params(aCx
);
1081 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
1082 if (NS_FAILED(mEarlyRv
)) {
1083 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
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
;
1101 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, hashAlgName
);
1102 if (NS_FAILED(mEarlyRv
)) {
1103 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1107 // This shouldn't happen; CreateSignVerifyTask shouldn't create
1108 // one of these unless it's for the above algorithms.
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
;
1122 // Check that we have the appropriate key
1123 if ((mSign
&& !mPrivKey
) || (!mSign
&& !mPubKey
)) {
1124 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1131 CK_MECHANISM_TYPE mHashMechanism
;
1132 CK_MECHANISM_TYPE mMgfMechanism
;
1133 UniqueSECKEYPrivateKey mPrivKey
;
1134 UniqueSECKEYPublicKey mPubKey
;
1135 CryptoBuffer mSignature
;
1137 uint32_t mSaltLength
;
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
{
1148 ::SECITEM_AllocItem(nullptr, nullptr, HASH_ResultLenByOidTag(mOidTag
)));
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
));
1162 return NS_ERROR_DOM_OPERATION_ERR
;
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
= {
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
));
1199 return NS_ERROR_DOM_OPERATION_ERR
;
1204 rv
= PK11_SignWithMechanism(mPrivKey
.get(), mech
, params
, sig
.get(),
1206 NS_ENSURE_SUCCESS(MapSECStatus(rv
), NS_ERROR_DOM_OPERATION_ERR
);
1207 ATTEMPT_BUFFER_ASSIGN(mSignature
, sig
.get());
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
));
1223 virtual void Resolve() override
{
1225 TypedArrayCreator
<ArrayBuffer
> ret(mSignature
);
1226 mResultPromise
->MaybeResolve(ret
);
1228 mResultPromise
->MaybeResolve(mVerified
);
1233 class DigestTask
: public ReturnArrayBufferViewTask
{
1235 DigestTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
1236 const CryptoOperationData
& aData
) {
1237 ATTEMPT_BUFFER_INIT(mData
, aData
);
1240 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
1241 if (NS_FAILED(mEarlyRv
)) {
1242 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
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
;
1256 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1259 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, telemetryAlg
);
1260 mOidTag
= MapHashAlgorithmNameToOID(algName
);
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
;
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
;
1285 class ImportKeyTask
: public WebCryptoTask
{
1287 void Init(nsIGlobalObject
* aGlobal
, JSContext
* aCx
, const nsAString
& aFormat
,
1288 const ObjectOrString
& aAlgorithm
, bool aExtractable
,
1289 const Sequence
<nsString
>& aKeyUsages
) {
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
)) {
1305 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, mAlgName
);
1306 if (NS_FAILED(mEarlyRv
)) {
1307 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
1312 static bool JwkCompatible(const JsonWebKey
& aJwk
, const CryptoKey
* aKey
) {
1314 if (aKey
->Extractable() && aJwk
.mExt
.WasPassed() && !aJwk
.mExt
.Value()) {
1319 if (aJwk
.mAlg
.WasPassed() &&
1320 aJwk
.mAlg
.Value() != aKey
->Algorithm().JwkAlg()) {
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
])) {
1335 // Individual algorithms may still have to check 'use'
1339 void SetKeyData(JSContext
* aCx
, JS::Handle
<JSObject
*> aKeyData
) {
1343 RootedSpiderMonkeyInterface
<ArrayBuffer
> ab(aCx
);
1344 if (ab
.Init(aKeyData
)) {
1345 if (!mKeyData
.Assign(ab
)) {
1346 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
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
;
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
;
1371 void SetKeyDataMaybeParseJWK(const CryptoBuffer
& aKeyData
) {
1372 if (!mKeyData
.Assign(aKeyData
)) {
1373 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
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
;
1388 nsString json
= NS_ConvertUTF8toUTF16(utf8
);
1389 if (!mJwk
.Init(json
)) {
1390 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
1398 void SetRawKeyData(const CryptoBuffer
& aKeyData
) {
1399 if (!mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW
)) {
1400 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
1404 if (!mKeyData
.Assign(aKeyData
)) {
1405 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
1414 RefPtr
<CryptoKey
> mKey
;
1415 CryptoBuffer mKeyData
;
1422 virtual void Resolve() override
{ mResultPromise
->MaybeResolve(mKey
); }
1424 virtual void Cleanup() override
{ mKey
= nullptr; }
1427 class ImportSymmetricKeyTask
: public ImportKeyTask
{
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
)) {
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
;
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
,
1459 if (NS_FAILED(mEarlyRv
)) {
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
;
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
;
1478 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, mHashName
);
1479 if (NS_FAILED(mEarlyRv
)) {
1480 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1486 virtual nsresult
BeforeCrypto() override
{
1489 // If we're doing a JWK import, import the key data
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
;
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;
1585 class ImportRsaKeyTask
: public ImportKeyTask
{
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
)) {
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
;
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
,
1617 if (NS_FAILED(mEarlyRv
)) {
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
;
1632 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, mHashName
);
1633 if (NS_FAILED(mEarlyRv
)) {
1634 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
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
;
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
);
1664 pubKey
= CryptoKey::PublicKeyFromJwk(mJwk
);
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
);
1683 privKey
= CryptoKey::PrivateKeyFromJwk(mJwk
);
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()));
1697 return NS_ERROR_DOM_UNKNOWN_ERR
;
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
;
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
,
1736 return NS_ERROR_DOM_OPERATION_ERR
;
1739 if (mDataIsJwk
&& !JwkCompatible(mJwk
, mKey
)) {
1740 return NS_ERROR_DOM_DATA_ERR
;
1747 class ImportEcKeyTask
: public ImportKeyTask
{
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
)) {
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
,
1773 if (NS_FAILED(mEarlyRv
)) {
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
;
1785 if (!NormalizeToken(params
.mNamedCurve
.Value(), mNamedCurve
)) {
1786 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
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
);
1807 return NS_ERROR_DOM_DATA_ERR
;
1810 privKey
= CryptoKey::PrivateKeyFromPkcs8(mKeyData
);
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
);
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
);
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
;
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
;
1916 class ExportKeyTask
: public WebCryptoTask
{
1918 ExportKeyTask(const nsAString
& aFormat
, CryptoKey
& aKey
)
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
;
1935 CryptoBuffer mSymKey
;
1936 UniqueSECKEYPrivateKey mPrivateKey
;
1937 UniqueSECKEYPublicKey mPublicKey
;
1938 CryptoKey::KeyType mKeyType
;
1941 nsTArray
<nsString
> mKeyUsages
;
1942 CryptoBuffer mResult
;
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
;
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
;
1968 } else if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8
)) {
1970 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1973 switch (mPrivateKey
->keyType
) {
1977 CryptoKey::PrivateKeyToPkcs8(mPrivateKey
.get(), mResult
);
1978 if (NS_FAILED(rv
)) {
1979 return NS_ERROR_DOM_OPERATION_ERR
;
1984 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1986 } else if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI
)) {
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
) {
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
) {
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
) {
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
;
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
);
2045 TypedArrayCreator
<ArrayBuffer
> ret(mResult
);
2046 mResultPromise
->MaybeResolve(ret
);
2050 class GenerateSymmetricKeyTask
: public WebCryptoTask
{
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
2062 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
2063 if (NS_FAILED(mEarlyRv
)) {
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
)) {
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
;
2087 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, hashName
);
2088 if (NS_FAILED(mEarlyRv
)) {
2092 if (params
.mLength
.WasPassed()) {
2093 mLength
= params
.mLength
.Value();
2095 mLength
= MapHashAlgorithmNameToBlockSize(hashName
);
2099 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
2103 mKey
->Algorithm().MakeHmac(mLength
, hashName
);
2105 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
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
)) {
2117 if (!mKey
->HasAnyUsage()) {
2118 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
2122 mLength
= mLength
>> 3; // bits to bytes
2123 mMechanism
= mKey
->Algorithm().Mechanism();
2124 // SetSymKey done in Resolve, after we've done the keygen
2128 RefPtr
<CryptoKey
> mKey
;
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));
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()));
2155 virtual void Resolve() override
{
2156 if (NS_SUCCEEDED(mKey
->SetSymKey(mKeyData
))) {
2157 mResultPromise
->MaybeResolve(mKey
);
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
),
2173 mArena
= UniquePLArenaPool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
2175 mEarlyRv
= NS_ERROR_DOM_UNKNOWN_ERR
;
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
)) {
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
;
2201 // Pull relevant info
2202 uint32_t modulusLength
= params
.mModulusLength
;
2203 CryptoBuffer publicExponent
;
2204 ATTEMPT_BUFFER_INIT(publicExponent
, params
.mPublicExponent
);
2206 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, hashName
);
2207 if (NS_FAILED(mEarlyRv
)) {
2212 if (!mKeyPair
->mPublicKey
->Algorithm().MakeRsa(mAlgName
, modulusLength
,
2213 publicExponent
, hashName
)) {
2214 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
2217 if (!mKeyPair
->mPrivateKey
->Algorithm().MakeRsa(mAlgName
, modulusLength
,
2218 publicExponent
, hashName
)) {
2219 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
2222 mMechanism
= CKM_RSA_PKCS_KEY_PAIR_GEN
;
2224 // Set up params struct
2225 mRsaParams
.keySizeInBits
= modulusLength
;
2226 bool converted
= publicExponent
.GetBigIntValue(mRsaParams
.pe
);
2228 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
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
;
2240 if (!NormalizeToken(params
.mNamedCurve
, mNamedCurve
)) {
2241 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2245 // Create algorithm.
2246 mKeyPair
->mPublicKey
->Algorithm().MakeEc(mAlgName
, mNamedCurve
);
2247 mKeyPair
->mPrivateKey
->Algorithm().MakeEc(mAlgName
, mNamedCurve
);
2248 mMechanism
= CKM_EC_KEY_PAIR_GEN
;
2250 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
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;
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
)) {
2285 mEarlyRv
= mKeyPair
->mPublicKey
->AddAllowedUsageIntersecting(
2286 aKeyUsages
[i
], mAlgName
, publicAllowedUsages
);
2287 if (NS_FAILED(mEarlyRv
)) {
2293 nsresult
GenerateAsymmetricKeyTask::DoCrypto() {
2294 MOZ_ASSERT(mKeyPair
);
2296 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
2297 MOZ_ASSERT(slot
.get());
2300 switch (mMechanism
) {
2301 case CKM_RSA_PKCS_KEY_PAIR_GEN
:
2302 param
= &mRsaParams
;
2304 case CKM_DH_PKCS_KEY_PAIR_GEN
:
2307 case CKM_EC_KEY_PAIR_GEN
: {
2308 param
= CreateECParamsForCurve(mNamedCurve
, mArena
.get());
2310 return NS_ERROR_DOM_UNKNOWN_ERR
;
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
);
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
);
2347 void GenerateAsymmetricKeyTask::Resolve() {
2348 mResultPromise
->MaybeResolve(*mKeyPair
);
2351 void GenerateAsymmetricKeyTask::Cleanup() { mKeyPair
= nullptr; }
2353 class DeriveHkdfBitsTask
: public ReturnArrayBufferViewTask
{
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
) {
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
,
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
;
2382 // Check that we have a key.
2383 if (mSymKey
.Length() == 0) {
2384 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
2388 RootedDictionary
<HkdfParams
> params(aCx
);
2389 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
2390 if (NS_FAILED(mEarlyRv
)) {
2391 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
2395 // length must be greater than zero.
2397 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
2401 // Extract the hash algorithm.
2403 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, hashName
);
2404 if (NS_FAILED(mEarlyRv
)) {
2408 // Check the given hash algorithm.
2409 switch (MapAlgorithmNameToMechanism(hashName
)) {
2411 mMechanism
= CKM_NSS_HKDF_SHA1
;
2414 mMechanism
= CKM_NSS_HKDF_SHA256
;
2417 mMechanism
= CKM_NSS_HKDF_SHA384
;
2420 mMechanism
= CKM_NSS_HKDF_SHA512
;
2423 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2427 ATTEMPT_BUFFER_INIT(mSalt
, params
.mSalt
)
2428 ATTEMPT_BUFFER_INIT(mInfo
, params
.mInfo
)
2429 mLengthInBytes
= ceil((double)aLength
/ 8);
2430 mLengthInBits
= aLength
;
2434 size_t mLengthInBits
;
2435 size_t mLengthInBytes
;
2438 CryptoBuffer mSymKey
;
2439 CK_MECHANISM_TYPE mMechanism
;
2441 virtual nsresult
DoCrypto() override
{
2442 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
2444 return NS_ERROR_DOM_OPERATION_ERR
;
2448 SECItem keyItem
= {siBuffer
, nullptr, 0};
2449 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), &keyItem
, mSymKey
);
2451 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
2453 return NS_ERROR_DOM_OPERATION_ERR
;
2456 UniquePK11SymKey
baseKey(PK11_ImportSymKey(slot
.get(), mMechanism
,
2457 PK11_OriginUnwrap
, CKA_WRAP
,
2458 &keyItem
, nullptr));
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
, ¶ms
,
2476 CKM_SHA512_HMAC
, CKA_SIGN
,
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);
2511 class DerivePbkdfBitsTask
: public ReturnArrayBufferViewTask
{
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
) {
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
,
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
;
2540 RootedDictionary
<Pbkdf2Params
> params(aCx
);
2541 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
2542 if (NS_FAILED(mEarlyRv
)) {
2543 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
2547 // length must be a multiple of 8 bigger than zero.
2548 if (aLength
== 0 || aLength
% 8) {
2549 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
2553 // Extract the hash algorithm.
2555 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, hashName
);
2556 if (NS_FAILED(mEarlyRv
)) {
2560 // Check the given hash algorithm.
2561 switch (MapAlgorithmNameToMechanism(hashName
)) {
2563 mHashOidTag
= SEC_OID_HMAC_SHA1
;
2566 mHashOidTag
= SEC_OID_HMAC_SHA256
;
2569 mHashOidTag
= SEC_OID_HMAC_SHA384
;
2572 mHashOidTag
= SEC_OID_HMAC_SHA512
;
2575 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2579 ATTEMPT_BUFFER_INIT(mSalt
, params
.mSalt
)
2580 mLength
= aLength
>> 3; // bits to bytes
2581 mIterations
= params
.mIterations
;
2588 CryptoBuffer mSymKey
;
2589 SECOidTag mHashOidTag
;
2591 virtual nsresult
DoCrypto() override
{
2592 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
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
2605 MOZ_ASSERT(salt
.len
== 0);
2607 reinterpret_cast<unsigned char*>(PORT_ArenaAlloc(arena
.get(), 1));
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
));
2622 return NS_ERROR_DOM_OPERATION_ERR
;
2625 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
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()));
2652 template <class DeriveBitsTask
>
2653 class DeriveKeyTask
: public DeriveBitsTask
{
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
)) {
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
);
2671 RefPtr
<ImportSymmetricKeyTask
> mTask
;
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
{
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.
2704 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
2708 // Length must be a multiple of 8 bigger than zero.
2709 if (mLength
== 0 || mLength
% 8) {
2710 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
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
;
2724 CryptoKey
* publicKey
= params
.mPublic
;
2725 mPubKey
= publicKey
->GetPublicKey();
2727 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
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
;
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
;
2782 template <class KeyEncryptTask
>
2783 class WrapKeyTask
: public ExportKeyTask
{
2785 WrapKeyTask(JSContext
* aCx
, const nsAString
& aFormat
, CryptoKey
& aKey
,
2786 CryptoKey
& aWrappingKey
, const ObjectOrString
& aWrapAlgorithm
)
2787 : ExportKeyTask(aFormat
, aKey
) {
2788 if (NS_FAILED(mEarlyRv
)) {
2792 mTask
= new KeyEncryptTask(aCx
, aWrapAlgorithm
, aWrappingKey
, true);
2796 RefPtr
<KeyEncryptTask
> mTask
;
2798 virtual nsresult
AfterCrypto() override
{
2799 // If wrapping JWK, stringify the JSON
2800 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
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
;
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
{
2826 UnwrapKeyTask(JSContext
* aCx
, const ArrayBufferViewOrArrayBuffer
& aWrappedKey
,
2827 CryptoKey
& aUnwrappingKey
,
2828 const ObjectOrString
& aUnwrapAlgorithm
, ImportKeyTask
* aTask
)
2829 : KeyEncryptTask(aCx
, aUnwrapAlgorithm
, aUnwrappingKey
, aWrappedKey
,
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
);
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
,
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
);
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
,
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
);
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
);
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
);
2993 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
2997 WebCryptoTask
* WebCryptoTask::CreateExportKeyTask(const nsAString
& aFormat
,
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
,
3042 if (!CryptoKey::AllUsagesRecognized(aKeyUsages
)) {
3043 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR
);
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
,
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
,
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
);
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
,
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
);
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
,
3179 } else if (wrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_KW
)) {
3180 return new WrapKeyTask
<AesKwTask
>(aCx
, aFormat
, aKey
, aWrappingKey
,
3182 } else if (wrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP
)) {
3183 return new WrapKeyTask
<RsaOaepTask
>(aCx
, aFormat
, aKey
, aWrappingKey
,
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
)) {
3229 new ImportRsaKeyTask(aGlobal
, aCx
, aFormat
, aUnwrappedKeyAlgorithm
,
3230 aExtractable
, aKeyUsages
);
3231 } else if (keyAlgName
.EqualsLiteral(WEBCRYPTO_ALG_ECDH
) ||
3232 keyAlgName
.EqualsLiteral(WEBCRYPTO_ALG_ECDSA
)) {
3234 new ImportEcKeyTask(aGlobal
, aCx
, aFormat
, aUnwrappedKeyAlgorithm
,
3235 aExtractable
, aKeyUsages
);
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"),
3264 mEarlyComplete(false),
3265 mOriginalEventTarget(nullptr),
3266 mRv(NS_ERROR_NOT_INITIALIZED
) {}
3268 WebCryptoTask::~WebCryptoTask() = default;
3270 } // namespace mozilla::dom