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
GetKeyLengthForAlgorithmIfSpecified(
190 JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, Maybe
<size_t>& aLength
) {
191 // Extract algorithm name
193 if (NS_FAILED(GetAlgorithmName(aCx
, aAlgorithm
, algName
))) {
194 return NS_ERROR_DOM_SYNTAX_ERR
;
197 // Read AES key length from given algorithm object.
198 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC
) ||
199 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR
) ||
200 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM
) ||
201 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_KW
)) {
202 RootedDictionary
<AesDerivedKeyParams
> params(aCx
);
203 if (NS_FAILED(Coerce(aCx
, params
, aAlgorithm
))) {
204 return NS_ERROR_DOM_SYNTAX_ERR
;
207 if (params
.mLength
!= 128 && params
.mLength
!= 192 &&
208 params
.mLength
!= 256) {
209 return NS_ERROR_DOM_OPERATION_ERR
;
212 aLength
.emplace(params
.mLength
);
216 // Read HMAC key length from given algorithm object or
217 // determine key length as the block size of the given hash.
218 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_HMAC
)) {
219 RootedDictionary
<HmacDerivedKeyParams
> params(aCx
);
220 if (NS_FAILED(Coerce(aCx
, params
, aAlgorithm
))) {
221 return NS_ERROR_DOM_SYNTAX_ERR
;
224 // Return the passed length, if any.
225 if (params
.mLength
.WasPassed()) {
226 aLength
.emplace(params
.mLength
.Value());
231 if (NS_FAILED(GetAlgorithmName(aCx
, params
.mHash
, hashName
))) {
232 return NS_ERROR_DOM_SYNTAX_ERR
;
235 // Return the given hash algorithm's block size as the key length.
236 size_t blockSize
= MapHashAlgorithmNameToBlockSize(hashName
);
237 if (blockSize
== 0) {
238 return NS_ERROR_DOM_SYNTAX_ERR
;
241 aLength
.emplace(blockSize
);
248 inline nsresult
GetKeyLengthForAlgorithm(JSContext
* aCx
,
249 const ObjectOrString
& aAlgorithm
,
251 Maybe
<size_t> length
;
252 nsresult rv
= GetKeyLengthForAlgorithmIfSpecified(aCx
, aAlgorithm
, length
);
256 if (length
.isNothing()) {
257 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
263 inline bool MapOIDTagToNamedCurve(SECOidTag aOIDTag
, nsString
& aResult
) {
265 case SEC_OID_SECG_EC_SECP256R1
:
266 aResult
.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256
);
268 case SEC_OID_SECG_EC_SECP384R1
:
269 aResult
.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384
);
271 case SEC_OID_SECG_EC_SECP521R1
:
272 aResult
.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521
);
281 inline SECOidTag
MapHashAlgorithmNameToOID(const nsString
& aName
) {
282 SECOidTag
hashOID(SEC_OID_UNKNOWN
);
284 if (aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA1
)) {
285 hashOID
= SEC_OID_SHA1
;
286 } else if (aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA256
)) {
287 hashOID
= SEC_OID_SHA256
;
288 } else if (aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA384
)) {
289 hashOID
= SEC_OID_SHA384
;
290 } else if (aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA512
)) {
291 hashOID
= SEC_OID_SHA512
;
297 inline CK_MECHANISM_TYPE
MapHashAlgorithmNameToMgfMechanism(
298 const nsString
& aName
) {
299 CK_MECHANISM_TYPE
mech(UNKNOWN_CK_MECHANISM
);
301 if (aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA1
)) {
302 mech
= CKG_MGF1_SHA1
;
303 } else if (aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA256
)) {
304 mech
= CKG_MGF1_SHA256
;
305 } else if (aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA384
)) {
306 mech
= CKG_MGF1_SHA384
;
307 } else if (aName
.EqualsLiteral(WEBCRYPTO_ALG_SHA512
)) {
308 mech
= CKG_MGF1_SHA512
;
314 // Implementation of WebCryptoTask methods
316 void WebCryptoTask::DispatchWithPromise(Promise
* aResultPromise
) {
317 mResultPromise
= aResultPromise
;
319 // Fail if an error was set during the constructor
320 MAYBE_EARLY_FAIL(mEarlyRv
)
322 // Perform pre-NSS operations, and fail if they fail
323 mEarlyRv
= BeforeCrypto();
324 MAYBE_EARLY_FAIL(mEarlyRv
)
326 // Skip dispatch if we're already done. Otherwise launch a CryptoTask
327 if (mEarlyComplete
) {
328 CallCallback(mEarlyRv
);
332 // Store calling thread
333 mOriginalEventTarget
= GetCurrentSerialEventTarget();
335 // If we are running on a worker thread we must hold the worker
336 // alive while we work on the thread pool. Otherwise the worker
337 // private may get torn down before we dispatch back to complete
339 if (!NS_IsMainThread()) {
340 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
341 MOZ_ASSERT(workerPrivate
);
343 RefPtr
<StrongWorkerRef
> workerRef
=
344 StrongWorkerRef::Create(workerPrivate
, "WebCryptoTask");
345 if (NS_WARN_IF(!workerRef
)) {
346 mEarlyRv
= NS_BINDING_ABORTED
;
348 mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
351 MAYBE_EARLY_FAIL(mEarlyRv
);
353 // dispatch to thread pool
355 if (!EnsureNSSInitializedChromeOrContent()) {
356 mEarlyRv
= NS_ERROR_FAILURE
;
358 MAYBE_EARLY_FAIL(mEarlyRv
);
360 mEarlyRv
= NS_DispatchBackgroundTask(this);
361 MAYBE_EARLY_FAIL(mEarlyRv
)
365 WebCryptoTask::Run() {
366 // Run heavy crypto operations on the thread pool, off the original thread.
367 if (!IsOnOriginalThread()) {
368 mRv
= CalculateResult();
370 // Back to the original thread, i.e. continue below.
371 mOriginalEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
);
375 // We're now back on the calling thread.
378 // Stop holding the worker thread alive now that the async work has
380 mWorkerRef
= nullptr;
385 nsresult
WebCryptoTask::Cancel() {
386 MOZ_ASSERT(IsOnOriginalThread());
387 FailWithError(NS_BINDING_ABORTED
);
391 void WebCryptoTask::FailWithError(nsresult aRv
) {
392 MOZ_ASSERT(IsOnOriginalThread());
393 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED
, false);
395 // Blindly convert nsresult to DOMException
396 // Individual tasks must ensure they pass the right values
397 mResultPromise
->MaybeReject(aRv
);
398 // Manually release mResultPromise while we're on the main thread
399 mResultPromise
= nullptr;
400 mWorkerRef
= nullptr;
404 nsresult
WebCryptoTask::CalculateResult() {
405 MOZ_ASSERT(!IsOnOriginalThread());
410 void WebCryptoTask::CallCallback(nsresult rv
) {
411 MOZ_ASSERT(IsOnOriginalThread());
417 nsresult rv2
= AfterCrypto();
418 if (NS_FAILED(rv2
)) {
424 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED
, true);
426 // Manually release mResultPromise while we're on the main thread
427 mResultPromise
= nullptr;
431 // Some generic utility classes
433 class FailureTask
: public WebCryptoTask
{
435 explicit FailureTask(nsresult aRv
) { mEarlyRv
= aRv
; }
438 class ReturnArrayBufferViewTask
: public WebCryptoTask
{
440 CryptoBuffer mResult
;
443 // Returns mResult as an ArrayBufferView, or an error
444 virtual void Resolve() override
{
445 TypedArrayCreator
<ArrayBuffer
> ret(mResult
);
446 mResultPromise
->MaybeResolve(ret
);
453 void SetData(const T
& aData
) {
454 mDataIsSet
= mData
.Assign(aData
);
458 DeferredData() : mDataIsSet(false) {}
464 class AesTask
: public ReturnArrayBufferViewTask
, public DeferredData
{
466 AesTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
468 : mMechanism(CKM_INVALID_MECHANISM
),
472 Init(aCx
, aAlgorithm
, aKey
, aEncrypt
);
475 AesTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
476 const CryptoOperationData
& aData
, bool aEncrypt
)
477 : mMechanism(CKM_INVALID_MECHANISM
),
481 Init(aCx
, aAlgorithm
, aKey
, aEncrypt
);
485 void Init(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
488 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
489 if (NS_FAILED(mEarlyRv
)) {
493 if (!mSymKey
.Assign(aKey
.GetSymKey())) {
494 mEarlyRv
= NS_ERROR_OUT_OF_MEMORY
;
498 // Check that we got a reasonable key
499 if ((mSymKey
.Length() != 16) && (mSymKey
.Length() != 24) &&
500 (mSymKey
.Length() != 32)) {
501 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
505 // Cache parameters depending on the specific algorithm
506 TelemetryAlgorithm telemetryAlg
;
507 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC
)) {
508 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_AES_CBC
);
510 mMechanism
= CKM_AES_CBC_PAD
;
511 telemetryAlg
= TA_AES_CBC
;
512 RootedDictionary
<AesCbcParams
> params(aCx
);
513 nsresult rv
= Coerce(aCx
, params
, aAlgorithm
);
515 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
519 ATTEMPT_BUFFER_INIT(mIv
, params
.mIv
)
520 if (mIv
.Length() != 16) {
521 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
524 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR
)) {
525 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_AES_CTR
);
527 mMechanism
= CKM_AES_CTR
;
528 telemetryAlg
= TA_AES_CTR
;
529 RootedDictionary
<AesCtrParams
> params(aCx
);
530 nsresult rv
= Coerce(aCx
, params
, aAlgorithm
);
532 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
536 ATTEMPT_BUFFER_INIT(mIv
, params
.mCounter
)
537 if (mIv
.Length() != 16) {
538 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
542 mCounterLength
= params
.mLength
;
543 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM
)) {
544 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_AES_GCM
);
546 mMechanism
= CKM_AES_GCM
;
547 telemetryAlg
= TA_AES_GCM
;
548 RootedDictionary
<AesGcmParams
> params(aCx
);
549 nsresult rv
= Coerce(aCx
, params
, aAlgorithm
);
551 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
555 ATTEMPT_BUFFER_INIT(mIv
, params
.mIv
)
557 if (params
.mAdditionalData
.WasPassed()) {
558 ATTEMPT_BUFFER_INIT(mAad
, params
.mAdditionalData
.Value())
561 // 32, 64, 96, 104, 112, 120 or 128
563 if (params
.mTagLength
.WasPassed()) {
564 mTagLength
= params
.mTagLength
.Value();
565 if ((mTagLength
> 128) ||
566 !(mTagLength
== 32 || mTagLength
== 64 ||
567 (mTagLength
>= 96 && mTagLength
% 8 == 0))) {
568 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
573 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
576 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, telemetryAlg
);
580 CK_MECHANISM_TYPE mMechanism
;
581 CryptoBuffer mSymKey
;
582 CryptoBuffer mIv
; // Initialization vector
583 CryptoBuffer mAad
; // Additional Authenticated Data
585 uint8_t mCounterLength
;
588 virtual nsresult
DoCrypto() override
{
592 return NS_ERROR_DOM_OPERATION_ERR
;
595 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
597 return NS_ERROR_DOM_OPERATION_ERR
;
600 // Construct the parameters object depending on algorithm
601 SECItem param
= {siBuffer
, nullptr, 0};
602 CK_AES_CTR_PARAMS ctrParams
;
603 CK_GCM_PARAMS gcmParams
;
604 switch (mMechanism
) {
605 case CKM_AES_CBC_PAD
:
606 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), ¶m
, mIv
);
609 ctrParams
.ulCounterBits
= mCounterLength
;
610 MOZ_ASSERT(mIv
.Length() == 16);
611 memcpy(&ctrParams
.cb
, mIv
.Elements(), 16);
612 param
.type
= siBuffer
;
613 param
.data
= (unsigned char*)&ctrParams
;
614 param
.len
= sizeof(ctrParams
);
617 gcmParams
.pIv
= mIv
.Elements();
618 gcmParams
.ulIvLen
= mIv
.Length();
619 gcmParams
.ulIvBits
= gcmParams
.ulIvLen
* 8;
620 gcmParams
.pAAD
= mAad
.Elements();
621 gcmParams
.ulAADLen
= mAad
.Length();
622 gcmParams
.ulTagBits
= mTagLength
;
623 param
.type
= siBuffer
;
624 param
.data
= (unsigned char*)&gcmParams
;
625 param
.len
= sizeof(gcmParams
);
628 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
632 SECItem keyItem
= {siBuffer
, nullptr, 0};
633 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), &keyItem
, mSymKey
);
634 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
635 MOZ_ASSERT(slot
.get());
636 UniquePK11SymKey
symKey(PK11_ImportSymKey(slot
.get(), mMechanism
,
637 PK11_OriginUnwrap
, CKA_ENCRYPT
,
640 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
643 // Check whether the integer addition would overflow.
644 if (std::numeric_limits
<CryptoBuffer::size_type
>::max() - 16 <
646 return NS_ERROR_DOM_DATA_ERR
;
649 // Initialize the output buffer (enough space for padding / a full tag)
650 if (!mResult
.SetLength(mData
.Length() + 16, fallible
)) {
651 return NS_ERROR_DOM_UNKNOWN_ERR
;
656 // Perform the encryption/decryption
658 rv
= MapSECStatus(PK11_Encrypt(
659 symKey
.get(), mMechanism
, ¶m
, mResult
.Elements(), &outLen
,
660 mResult
.Length(), mData
.Elements(), mData
.Length()));
662 rv
= MapSECStatus(PK11_Decrypt(
663 symKey
.get(), mMechanism
, ¶m
, mResult
.Elements(), &outLen
,
664 mResult
.Length(), mData
.Elements(), mData
.Length()));
666 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
668 mResult
.TruncateLength(outLen
);
673 // This class looks like an encrypt/decrypt task, like AesTask,
674 // but it is only exposed to wrapKey/unwrapKey, not encrypt/decrypt
675 class AesKwTask
: public ReturnArrayBufferViewTask
, public DeferredData
{
677 AesKwTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
679 : mMechanism(CKM_NSS_AES_KEY_WRAP
), mEncrypt(aEncrypt
) {
680 Init(aCx
, aAlgorithm
, aKey
, aEncrypt
);
683 AesKwTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
684 const CryptoOperationData
& aData
, bool aEncrypt
)
685 : mMechanism(CKM_NSS_AES_KEY_WRAP
), mEncrypt(aEncrypt
) {
686 Init(aCx
, aAlgorithm
, aKey
, aEncrypt
);
690 void Init(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
692 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_AES_KW
);
695 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
696 if (NS_FAILED(mEarlyRv
)) {
700 if (!mSymKey
.Assign(aKey
.GetSymKey())) {
701 mEarlyRv
= NS_ERROR_OUT_OF_MEMORY
;
705 // Check that we got a reasonable key
706 if ((mSymKey
.Length() != 16) && (mSymKey
.Length() != 24) &&
707 (mSymKey
.Length() != 32)) {
708 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
712 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, TA_AES_KW
);
716 CK_MECHANISM_TYPE mMechanism
;
717 CryptoBuffer mSymKey
;
720 virtual nsresult
DoCrypto() override
{
724 return NS_ERROR_DOM_OPERATION_ERR
;
727 // Check that the input is a multiple of 64 bits long
728 if (mData
.Length() == 0 || mData
.Length() % 8 != 0) {
729 return NS_ERROR_DOM_DATA_ERR
;
732 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
734 return NS_ERROR_DOM_OPERATION_ERR
;
738 SECItem keyItem
= {siBuffer
, nullptr, 0};
739 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), &keyItem
, mSymKey
);
740 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
741 MOZ_ASSERT(slot
.get());
742 UniquePK11SymKey
symKey(PK11_ImportSymKey(slot
.get(), mMechanism
,
743 PK11_OriginUnwrap
, CKA_WRAP
,
746 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
749 // Import the data to a SECItem
750 SECItem dataItem
= {siBuffer
, nullptr, 0};
751 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), &dataItem
, mData
);
753 // Parameters for the fake keys
754 CK_MECHANISM_TYPE fakeMechanism
= CKM_SHA_1_HMAC
;
755 CK_ATTRIBUTE_TYPE fakeOperation
= CKA_SIGN
;
758 // Import the data into a fake PK11SymKey structure
759 UniquePK11SymKey
keyToWrap(
760 PK11_ImportSymKey(slot
.get(), fakeMechanism
, PK11_OriginUnwrap
,
761 fakeOperation
, &dataItem
, nullptr));
763 return NS_ERROR_DOM_OPERATION_ERR
;
766 // Encrypt and return the wrapped key
767 // AES-KW encryption results in a wrapped key 64 bits longer
768 if (!mResult
.SetLength(mData
.Length() + 8, fallible
)) {
769 return NS_ERROR_DOM_OPERATION_ERR
;
771 SECItem resultItem
= {siBuffer
, mResult
.Elements(),
772 (unsigned int)mResult
.Length()};
773 rv
= MapSECStatus(PK11_WrapSymKey(mMechanism
, nullptr, symKey
.get(),
774 keyToWrap
.get(), &resultItem
));
775 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
777 // Decrypt the ciphertext into a temporary PK11SymKey
778 // Unwrapped key should be 64 bits shorter
779 int keySize
= mData
.Length() - 8;
780 UniquePK11SymKey
unwrappedKey(
781 PK11_UnwrapSymKey(symKey
.get(), mMechanism
, nullptr, &dataItem
,
782 fakeMechanism
, fakeOperation
, keySize
));
784 return NS_ERROR_DOM_OPERATION_ERR
;
787 // Export the key to get the cleartext
788 rv
= MapSECStatus(PK11_ExtractKeyValue(unwrappedKey
.get()));
790 return NS_ERROR_DOM_UNKNOWN_ERR
;
792 ATTEMPT_BUFFER_ASSIGN(mResult
, PK11_GetKeyData(unwrappedKey
.get()));
799 class RsaOaepTask
: public ReturnArrayBufferViewTask
, public DeferredData
{
801 RsaOaepTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
803 : mPrivKey(aKey
.GetPrivateKey()),
804 mPubKey(aKey
.GetPublicKey()),
806 Init(aCx
, aAlgorithm
, aKey
, aEncrypt
);
809 RsaOaepTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
810 const CryptoOperationData
& aData
, bool aEncrypt
)
811 : mPrivKey(aKey
.GetPrivateKey()),
812 mPubKey(aKey
.GetPublicKey()),
814 Init(aCx
, aAlgorithm
, aKey
, aEncrypt
);
818 void Init(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
820 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, TA_RSA_OAEP
);
822 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_RSA_OAEP
);
826 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
829 mStrength
= SECKEY_PublicKeyStrength(mPubKey
.get());
832 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
835 mStrength
= PK11_GetPrivateModulusLen(mPrivKey
.get());
838 // The algorithm could just be given as a string
839 // in which case there would be no label specified.
840 if (!aAlgorithm
.IsString()) {
841 RootedDictionary
<RsaOaepParams
> params(aCx
);
842 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
843 if (NS_FAILED(mEarlyRv
)) {
844 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
848 if (params
.mLabel
.WasPassed()) {
849 ATTEMPT_BUFFER_INIT(mLabel
, params
.mLabel
.Value());
852 // Otherwise mLabel remains the empty octet string, as intended
854 KeyAlgorithm
& hashAlg
= aKey
.Algorithm().mRsa
.mHash
;
855 mHashMechanism
= KeyAlgorithmProxy::GetMechanism(hashAlg
);
856 mMgfMechanism
= MapHashAlgorithmNameToMgfMechanism(hashAlg
.mName
);
858 // Check we found appropriate mechanisms.
859 if (mHashMechanism
== UNKNOWN_CK_MECHANISM
||
860 mMgfMechanism
== UNKNOWN_CK_MECHANISM
) {
861 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
867 CK_MECHANISM_TYPE mHashMechanism
;
868 CK_MECHANISM_TYPE mMgfMechanism
;
869 UniqueSECKEYPrivateKey mPrivKey
;
870 UniqueSECKEYPublicKey mPubKey
;
875 virtual nsresult
DoCrypto() override
{
879 return NS_ERROR_DOM_OPERATION_ERR
;
882 // Ciphertext is an integer mod the modulus, so it will be
883 // no longer than mStrength octets
884 if (!mResult
.SetLength(mStrength
, fallible
)) {
885 return NS_ERROR_DOM_UNKNOWN_ERR
;
888 CK_RSA_PKCS_OAEP_PARAMS oaepParams
;
889 oaepParams
.source
= CKZ_DATA_SPECIFIED
;
891 oaepParams
.pSourceData
= mLabel
.Length() ? mLabel
.Elements() : nullptr;
892 oaepParams
.ulSourceDataLen
= mLabel
.Length();
894 oaepParams
.mgf
= mMgfMechanism
;
895 oaepParams
.hashAlg
= mHashMechanism
;
898 param
.type
= siBuffer
;
899 param
.data
= (unsigned char*)&oaepParams
;
900 param
.len
= sizeof(oaepParams
);
904 // PK11_PubEncrypt() checks the plaintext's length and fails if it is too
905 // long to encrypt, i.e. if it is longer than (k - 2hLen - 2) with 'k'
906 // being the length in octets of the RSA modulus n and 'hLen' being the
907 // output length in octets of the chosen hash function.
908 // <https://tools.ietf.org/html/rfc3447#section-7.1>
909 rv
= MapSECStatus(PK11_PubEncrypt(
910 mPubKey
.get(), CKM_RSA_PKCS_OAEP
, ¶m
, mResult
.Elements(), &outLen
,
911 mResult
.Length(), mData
.Elements(), mData
.Length(), nullptr));
913 rv
= MapSECStatus(PK11_PrivDecrypt(
914 mPrivKey
.get(), CKM_RSA_PKCS_OAEP
, ¶m
, mResult
.Elements(),
915 &outLen
, mResult
.Length(), mData
.Elements(), mData
.Length()));
917 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
919 mResult
.TruncateLength(outLen
);
924 class HmacTask
: public WebCryptoTask
{
926 HmacTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
927 const CryptoOperationData
& aSignature
,
928 const CryptoOperationData
& aData
, bool aSign
)
929 : mMechanism(aKey
.Algorithm().Mechanism()), mSign(aSign
) {
930 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_HMAC
);
932 ATTEMPT_BUFFER_INIT(mData
, aData
);
934 ATTEMPT_BUFFER_INIT(mSignature
, aSignature
);
937 if (!mSymKey
.Assign(aKey
.GetSymKey())) {
938 mEarlyRv
= NS_ERROR_OUT_OF_MEMORY
;
942 // Check that we got a symmetric key
943 if (mSymKey
.Length() == 0) {
944 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
948 TelemetryAlgorithm telemetryAlg
;
949 switch (mMechanism
) {
951 telemetryAlg
= TA_HMAC_SHA_1
;
953 case CKM_SHA224_HMAC
:
954 telemetryAlg
= TA_HMAC_SHA_224
;
956 case CKM_SHA256_HMAC
:
957 telemetryAlg
= TA_HMAC_SHA_256
;
959 case CKM_SHA384_HMAC
:
960 telemetryAlg
= TA_HMAC_SHA_384
;
962 case CKM_SHA512_HMAC
:
963 telemetryAlg
= TA_HMAC_SHA_512
;
966 telemetryAlg
= TA_UNKNOWN
;
968 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, telemetryAlg
);
972 CK_MECHANISM_TYPE mMechanism
;
973 CryptoBuffer mSymKey
;
975 CryptoBuffer mSignature
;
976 CryptoBuffer mResult
;
979 virtual nsresult
DoCrypto() override
{
980 // Initialize the output buffer
981 if (!mResult
.SetLength(HASH_LENGTH_MAX
, fallible
)) {
982 return NS_ERROR_DOM_UNKNOWN_ERR
;
985 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
987 return NS_ERROR_DOM_OPERATION_ERR
;
992 SECItem keyItem
= {siBuffer
, nullptr, 0};
993 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), &keyItem
, mSymKey
);
994 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
995 MOZ_ASSERT(slot
.get());
996 UniquePK11SymKey
symKey(PK11_ImportSymKey(slot
.get(), mMechanism
,
997 PK11_OriginUnwrap
, CKA_SIGN
,
1000 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1004 SECItem param
= {siBuffer
, nullptr, 0};
1005 UniquePK11Context
ctx(
1006 PK11_CreateContextBySymKey(mMechanism
, CKA_SIGN
, symKey
.get(), ¶m
));
1008 return NS_ERROR_DOM_OPERATION_ERR
;
1010 nsresult rv
= MapSECStatus(PK11_DigestBegin(ctx
.get()));
1011 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
1013 PK11_DigestOp(ctx
.get(), mData
.Elements(), mData
.Length()));
1014 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
1015 rv
= MapSECStatus(PK11_DigestFinal(ctx
.get(), mResult
.Elements(), &outLen
,
1017 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
1019 mResult
.TruncateLength(outLen
);
1023 // Returns mResult as an ArrayBufferView, or an error
1024 virtual void Resolve() override
{
1026 // Return the computed MAC
1027 TypedArrayCreator
<ArrayBuffer
> ret(mResult
);
1028 mResultPromise
->MaybeResolve(ret
);
1030 // Compare the MAC to the provided signature
1031 // No truncation allowed
1032 bool equal
= (mResult
.Length() == mSignature
.Length());
1034 int cmp
= NSS_SecureMemcmp(mSignature
.Elements(), mResult
.Elements(),
1035 mSignature
.Length());
1038 mResultPromise
->MaybeResolve(equal
);
1043 class AsymmetricSignVerifyTask
: public WebCryptoTask
{
1045 AsymmetricSignVerifyTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
1047 const CryptoOperationData
& aSignature
,
1048 const CryptoOperationData
& aData
, bool aSign
)
1049 : mOidTag(SEC_OID_UNKNOWN
),
1050 mHashMechanism(UNKNOWN_CK_MECHANISM
),
1051 mMgfMechanism(UNKNOWN_CK_MECHANISM
),
1052 mPrivKey(aKey
.GetPrivateKey()),
1053 mPubKey(aKey
.GetPublicKey()),
1057 mAlgorithm(Algorithm::UNKNOWN
) {
1058 ATTEMPT_BUFFER_INIT(mData
, aData
);
1060 ATTEMPT_BUFFER_INIT(mSignature
, aSignature
);
1064 nsString hashAlgName
;
1065 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
1066 if (NS_FAILED(mEarlyRv
)) {
1070 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1
)) {
1071 mAlgorithm
= Algorithm::RSA_PKCS1
;
1072 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, TA_RSASSA_PKCS1
);
1073 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1
);
1074 hashAlgName
= aKey
.Algorithm().mRsa
.mHash
.mName
;
1075 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS
)) {
1076 mAlgorithm
= Algorithm::RSA_PSS
;
1077 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, TA_RSA_PSS
);
1078 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_RSA_PSS
);
1080 KeyAlgorithm
& hashAlg
= aKey
.Algorithm().mRsa
.mHash
;
1081 hashAlgName
= hashAlg
.mName
;
1082 mHashMechanism
= KeyAlgorithmProxy::GetMechanism(hashAlg
);
1083 mMgfMechanism
= MapHashAlgorithmNameToMgfMechanism(hashAlgName
);
1085 // Check we found appropriate mechanisms.
1086 if (mHashMechanism
== UNKNOWN_CK_MECHANISM
||
1087 mMgfMechanism
== UNKNOWN_CK_MECHANISM
) {
1088 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1092 RootedDictionary
<RsaPssParams
> params(aCx
);
1093 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
1094 if (NS_FAILED(mEarlyRv
)) {
1095 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1099 mSaltLength
= params
.mSaltLength
;
1100 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_ECDSA
)) {
1101 mAlgorithm
= Algorithm::ECDSA
;
1102 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, TA_ECDSA
);
1103 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_ECDSA
);
1105 // For ECDSA, the hash name comes from the algorithm parameter
1106 RootedDictionary
<EcdsaParams
> params(aCx
);
1107 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
1108 if (NS_FAILED(mEarlyRv
)) {
1109 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1113 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, hashAlgName
);
1114 if (NS_FAILED(mEarlyRv
)) {
1115 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1119 // This shouldn't happen; CreateSignVerifyTask shouldn't create
1120 // one of these unless it's for the above algorithms.
1124 // Must have a valid algorithm by now.
1125 MOZ_ASSERT(mAlgorithm
!= Algorithm::UNKNOWN
);
1127 // Determine hash algorithm to use.
1128 mOidTag
= MapHashAlgorithmNameToOID(hashAlgName
);
1129 if (mOidTag
== SEC_OID_UNKNOWN
) {
1130 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1134 // Check that we have the appropriate key
1135 if ((mSign
&& !mPrivKey
) || (!mSign
&& !mPubKey
)) {
1136 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1143 CK_MECHANISM_TYPE mHashMechanism
;
1144 CK_MECHANISM_TYPE mMgfMechanism
;
1145 UniqueSECKEYPrivateKey mPrivKey
;
1146 UniqueSECKEYPublicKey mPubKey
;
1147 CryptoBuffer mSignature
;
1149 uint32_t mSaltLength
;
1153 // The signature algorithm to use.
1154 enum class Algorithm
: uint8_t { ECDSA
, RSA_PKCS1
, RSA_PSS
, UNKNOWN
};
1155 Algorithm mAlgorithm
;
1157 virtual nsresult
DoCrypto() override
{
1160 ::SECITEM_AllocItem(nullptr, nullptr, HASH_ResultLenByOidTag(mOidTag
)));
1162 return NS_ERROR_DOM_OPERATION_ERR
;
1165 // Compute digest over given data.
1166 rv
= PK11_HashBuf(mOidTag
, hash
->data
, mData
.Elements(), mData
.Length());
1167 NS_ENSURE_SUCCESS(MapSECStatus(rv
), NS_ERROR_DOM_OPERATION_ERR
);
1169 // Wrap hash in a digest info template (RSA-PKCS1 only).
1170 if (mAlgorithm
== Algorithm::RSA_PKCS1
) {
1171 UniqueSGNDigestInfo
di(
1172 SGN_CreateDigestInfo(mOidTag
, hash
->data
, hash
->len
));
1174 return NS_ERROR_DOM_OPERATION_ERR
;
1178 SECITEM_FreeItem(hash
.get(), false);
1179 if (!SEC_ASN1EncodeItem(nullptr, hash
.get(), di
.get(),
1180 SGN_DigestInfoTemplate
)) {
1181 return NS_ERROR_DOM_OPERATION_ERR
;
1185 SECItem
* params
= nullptr;
1186 CK_MECHANISM_TYPE mech
=
1187 PK11_MapSignKeyType((mSign
? mPrivKey
->keyType
: mPubKey
->keyType
));
1189 CK_RSA_PKCS_PSS_PARAMS rsaPssParams
;
1190 SECItem rsaPssParamsItem
= {
1194 // Set up parameters for RSA-PSS.
1195 if (mAlgorithm
== Algorithm::RSA_PSS
) {
1196 rsaPssParams
.hashAlg
= mHashMechanism
;
1197 rsaPssParams
.mgf
= mMgfMechanism
;
1198 rsaPssParams
.sLen
= mSaltLength
;
1200 rsaPssParamsItem
.data
= (unsigned char*)&rsaPssParams
;
1201 rsaPssParamsItem
.len
= sizeof(rsaPssParams
);
1202 params
= &rsaPssParamsItem
;
1204 mech
= CKM_RSA_PKCS_PSS
;
1207 // Allocate SECItem to hold the signature.
1208 uint32_t len
= mSign
? PK11_SignatureLen(mPrivKey
.get()) : 0;
1209 UniqueSECItem
sig(::SECITEM_AllocItem(nullptr, nullptr, len
));
1211 return NS_ERROR_DOM_OPERATION_ERR
;
1216 rv
= PK11_SignWithMechanism(mPrivKey
.get(), mech
, params
, sig
.get(),
1218 NS_ENSURE_SUCCESS(MapSECStatus(rv
), NS_ERROR_DOM_OPERATION_ERR
);
1219 ATTEMPT_BUFFER_ASSIGN(mSignature
, sig
.get());
1221 // Copy the given signature to the SECItem.
1222 if (!mSignature
.ToSECItem(nullptr, sig
.get())) {
1223 return NS_ERROR_DOM_OPERATION_ERR
;
1226 // Verify the signature.
1227 rv
= PK11_VerifyWithMechanism(mPubKey
.get(), mech
, params
, sig
.get(),
1228 hash
.get(), nullptr);
1229 mVerified
= NS_SUCCEEDED(MapSECStatus(rv
));
1235 virtual void Resolve() override
{
1237 TypedArrayCreator
<ArrayBuffer
> ret(mSignature
);
1238 mResultPromise
->MaybeResolve(ret
);
1240 mResultPromise
->MaybeResolve(mVerified
);
1245 class DigestTask
: public ReturnArrayBufferViewTask
{
1247 DigestTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
1248 const CryptoOperationData
& aData
) {
1249 ATTEMPT_BUFFER_INIT(mData
, aData
);
1252 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
1253 if (NS_FAILED(mEarlyRv
)) {
1254 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1258 TelemetryAlgorithm telemetryAlg
;
1259 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_SHA1
)) {
1260 telemetryAlg
= TA_SHA_1
;
1261 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_SHA256
)) {
1262 telemetryAlg
= TA_SHA_224
;
1263 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_SHA384
)) {
1264 telemetryAlg
= TA_SHA_256
;
1265 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_SHA512
)) {
1266 telemetryAlg
= TA_SHA_384
;
1268 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1271 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, telemetryAlg
);
1272 mOidTag
= MapHashAlgorithmNameToOID(algName
);
1279 virtual nsresult
DoCrypto() override
{
1280 // Resize the result buffer
1281 uint32_t hashLen
= HASH_ResultLenByOidTag(mOidTag
);
1282 if (!mResult
.SetLength(hashLen
, fallible
)) {
1283 return NS_ERROR_DOM_UNKNOWN_ERR
;
1287 nsresult rv
= MapSECStatus(PK11_HashBuf(mOidTag
, mResult
.Elements(),
1288 mData
.Elements(), mData
.Length()));
1289 if (NS_FAILED(rv
)) {
1290 return NS_ERROR_DOM_UNKNOWN_ERR
;
1297 class ImportKeyTask
: public WebCryptoTask
{
1299 void Init(nsIGlobalObject
* aGlobal
, JSContext
* aCx
, const nsAString
& aFormat
,
1300 const ObjectOrString
& aAlgorithm
, bool aExtractable
,
1301 const Sequence
<nsString
>& aKeyUsages
) {
1306 // This stuff pretty much always happens, so we'll do it here
1307 mKey
= new CryptoKey(aGlobal
);
1308 mKey
->SetExtractable(aExtractable
);
1309 mKey
->ClearUsages();
1310 for (uint32_t i
= 0; i
< aKeyUsages
.Length(); ++i
) {
1311 mEarlyRv
= mKey
->AddUsage(aKeyUsages
[i
]);
1312 if (NS_FAILED(mEarlyRv
)) {
1317 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, mAlgName
);
1318 if (NS_FAILED(mEarlyRv
)) {
1319 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
1324 static bool JwkCompatible(const JsonWebKey
& aJwk
, const CryptoKey
* aKey
) {
1326 if (aKey
->Extractable() && aJwk
.mExt
.WasPassed() && !aJwk
.mExt
.Value()) {
1331 if (aJwk
.mAlg
.WasPassed() &&
1332 aJwk
.mAlg
.Value() != aKey
->Algorithm().JwkAlg()) {
1337 if (aJwk
.mKey_ops
.WasPassed()) {
1338 nsTArray
<nsString
> usages
;
1339 aKey
->GetUsages(usages
);
1340 for (size_t i
= 0; i
< usages
.Length(); ++i
) {
1341 if (!aJwk
.mKey_ops
.Value().Contains(usages
[i
])) {
1347 // Individual algorithms may still have to check 'use'
1351 void SetKeyData(JSContext
* aCx
, JS::Handle
<JSObject
*> aKeyData
) {
1355 RootedSpiderMonkeyInterface
<ArrayBuffer
> ab(aCx
);
1356 if (ab
.Init(aKeyData
)) {
1357 if (!mKeyData
.Assign(ab
)) {
1358 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
1363 // Try ArrayBufferView
1364 RootedSpiderMonkeyInterface
<ArrayBufferView
> abv(aCx
);
1365 if (abv
.Init(aKeyData
)) {
1366 if (!mKeyData
.Assign(abv
)) {
1367 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
1373 ClearException
ce(aCx
);
1374 JS::Rooted
<JS::Value
> value(aCx
, JS::ObjectValue(*aKeyData
));
1375 if (!mJwk
.Init(aCx
, value
)) {
1376 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
1383 void SetKeyDataMaybeParseJWK(const CryptoBuffer
& aKeyData
) {
1384 if (!mKeyData
.Assign(aKeyData
)) {
1385 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
1391 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
1392 nsDependentCSubstring
utf8(
1393 (const char*)mKeyData
.Elements(),
1394 (const char*)(mKeyData
.Elements() + mKeyData
.Length()));
1395 if (!IsUtf8(utf8
)) {
1396 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
1400 nsString json
= NS_ConvertUTF8toUTF16(utf8
);
1401 if (!mJwk
.Init(json
)) {
1402 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
1410 void SetRawKeyData(const CryptoBuffer
& aKeyData
) {
1411 if (!mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW
)) {
1412 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
1416 if (!mKeyData
.Assign(aKeyData
)) {
1417 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
1426 RefPtr
<CryptoKey
> mKey
;
1427 CryptoBuffer mKeyData
;
1434 virtual void Resolve() override
{ mResultPromise
->MaybeResolve(mKey
); }
1436 virtual void Cleanup() override
{ mKey
= nullptr; }
1439 class ImportSymmetricKeyTask
: public ImportKeyTask
{
1441 ImportSymmetricKeyTask(nsIGlobalObject
* aGlobal
, JSContext
* aCx
,
1442 const nsAString
& aFormat
,
1443 const ObjectOrString
& aAlgorithm
, bool aExtractable
,
1444 const Sequence
<nsString
>& aKeyUsages
) {
1445 Init(aGlobal
, aCx
, aFormat
, aAlgorithm
, aExtractable
, aKeyUsages
);
1448 ImportSymmetricKeyTask(nsIGlobalObject
* aGlobal
, JSContext
* aCx
,
1449 const nsAString
& aFormat
,
1450 const JS::Handle
<JSObject
*> aKeyData
,
1451 const ObjectOrString
& aAlgorithm
, bool aExtractable
,
1452 const Sequence
<nsString
>& aKeyUsages
) {
1453 Init(aGlobal
, aCx
, aFormat
, aAlgorithm
, aExtractable
, aKeyUsages
);
1454 if (NS_FAILED(mEarlyRv
)) {
1458 SetKeyData(aCx
, aKeyData
);
1459 NS_ENSURE_SUCCESS_VOID(mEarlyRv
);
1460 if (mDataIsJwk
&& !mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
1461 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1466 void Init(nsIGlobalObject
* aGlobal
, JSContext
* aCx
, const nsAString
& aFormat
,
1467 const ObjectOrString
& aAlgorithm
, bool aExtractable
,
1468 const Sequence
<nsString
>& aKeyUsages
) {
1469 ImportKeyTask::Init(aGlobal
, aCx
, aFormat
, aAlgorithm
, aExtractable
,
1471 if (NS_FAILED(mEarlyRv
)) {
1475 // This task only supports raw and JWK format.
1476 if (!mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
) &&
1477 !mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW
)) {
1478 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1482 // If this is an HMAC key, import the hash name
1483 if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_HMAC
)) {
1484 RootedDictionary
<HmacImportParams
> params(aCx
);
1485 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
1486 if (NS_FAILED(mEarlyRv
)) {
1487 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1490 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, mHashName
);
1491 if (NS_FAILED(mEarlyRv
)) {
1492 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1498 virtual nsresult
BeforeCrypto() override
{
1501 // If we're doing a JWK import, import the key data
1503 if (!mJwk
.mK
.WasPassed()) {
1504 return NS_ERROR_DOM_DATA_ERR
;
1507 // Import the key material
1508 rv
= mKeyData
.FromJwkBase64(mJwk
.mK
.Value());
1509 if (NS_FAILED(rv
)) {
1510 return NS_ERROR_DOM_DATA_ERR
;
1513 // Check that we have valid key data.
1514 if (mKeyData
.Length() == 0 &&
1515 !mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2
)) {
1516 return NS_ERROR_DOM_DATA_ERR
;
1519 // Construct an appropriate KeyAlorithm,
1520 // and verify that usages are appropriate
1521 if (mKeyData
.Length() > UINT32_MAX
/ 8) {
1522 return NS_ERROR_DOM_DATA_ERR
;
1524 uint32_t length
= 8 * mKeyData
.Length(); // bytes to bits
1525 if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC
) ||
1526 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR
) ||
1527 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM
) ||
1528 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_KW
)) {
1529 if (mKey
->HasUsageOtherThan(CryptoKey::ENCRYPT
| CryptoKey::DECRYPT
|
1530 CryptoKey::WRAPKEY
| CryptoKey::UNWRAPKEY
)) {
1531 return NS_ERROR_DOM_DATA_ERR
;
1534 if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_KW
) &&
1535 mKey
->HasUsageOtherThan(CryptoKey::WRAPKEY
| CryptoKey::UNWRAPKEY
)) {
1536 return NS_ERROR_DOM_DATA_ERR
;
1539 if ((length
!= 128) && (length
!= 192) && (length
!= 256)) {
1540 return NS_ERROR_DOM_DATA_ERR
;
1542 mKey
->Algorithm().MakeAes(mAlgName
, length
);
1544 if (mDataIsJwk
&& mJwk
.mUse
.WasPassed() &&
1545 !mJwk
.mUse
.Value().EqualsLiteral(JWK_USE_ENC
)) {
1546 return NS_ERROR_DOM_DATA_ERR
;
1548 } else if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_HKDF
) ||
1549 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2
)) {
1550 if (mKey
->HasUsageOtherThan(CryptoKey::DERIVEKEY
|
1551 CryptoKey::DERIVEBITS
)) {
1552 return NS_ERROR_DOM_DATA_ERR
;
1554 mKey
->Algorithm().MakeAes(mAlgName
, length
);
1556 if (mDataIsJwk
&& mJwk
.mUse
.WasPassed()) {
1557 // There is not a 'use' value consistent with PBKDF or HKDF
1558 return NS_ERROR_DOM_DATA_ERR
;
1560 } else if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_HMAC
)) {
1561 if (mKey
->HasUsageOtherThan(CryptoKey::SIGN
| CryptoKey::VERIFY
)) {
1562 return NS_ERROR_DOM_DATA_ERR
;
1565 mKey
->Algorithm().MakeHmac(length
, mHashName
);
1567 if (mKey
->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM
) {
1568 return NS_ERROR_DOM_SYNTAX_ERR
;
1571 if (mDataIsJwk
&& mJwk
.mUse
.WasPassed() &&
1572 !mJwk
.mUse
.Value().EqualsLiteral(JWK_USE_SIG
)) {
1573 return NS_ERROR_DOM_DATA_ERR
;
1576 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1579 if (!mKey
->HasAnyUsage()) {
1580 return NS_ERROR_DOM_SYNTAX_ERR
;
1583 if (NS_FAILED(mKey
->SetSymKey(mKeyData
))) {
1584 return NS_ERROR_DOM_OPERATION_ERR
;
1587 mKey
->SetType(CryptoKey::SECRET
);
1589 if (mDataIsJwk
&& !JwkCompatible(mJwk
, mKey
)) {
1590 return NS_ERROR_DOM_DATA_ERR
;
1593 mEarlyComplete
= true;
1601 class ImportRsaKeyTask
: public ImportKeyTask
{
1603 ImportRsaKeyTask(nsIGlobalObject
* aGlobal
, JSContext
* aCx
,
1604 const nsAString
& aFormat
, const ObjectOrString
& aAlgorithm
,
1605 bool aExtractable
, const Sequence
<nsString
>& aKeyUsages
)
1606 : mModulusLength(0) {
1607 Init(aGlobal
, aCx
, aFormat
, aAlgorithm
, aExtractable
, aKeyUsages
);
1610 ImportRsaKeyTask(nsIGlobalObject
* aGlobal
, JSContext
* aCx
,
1611 const nsAString
& aFormat
, JS::Handle
<JSObject
*> aKeyData
,
1612 const ObjectOrString
& aAlgorithm
, bool aExtractable
,
1613 const Sequence
<nsString
>& aKeyUsages
)
1614 : mModulusLength(0) {
1615 Init(aGlobal
, aCx
, aFormat
, aAlgorithm
, aExtractable
, aKeyUsages
);
1616 if (NS_FAILED(mEarlyRv
)) {
1620 SetKeyData(aCx
, aKeyData
);
1621 NS_ENSURE_SUCCESS_VOID(mEarlyRv
);
1622 if (mDataIsJwk
&& !mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
1623 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1628 void Init(nsIGlobalObject
* aGlobal
, JSContext
* aCx
, const nsAString
& aFormat
,
1629 const ObjectOrString
& aAlgorithm
, bool aExtractable
,
1630 const Sequence
<nsString
>& aKeyUsages
) {
1631 ImportKeyTask::Init(aGlobal
, aCx
, aFormat
, aAlgorithm
, aExtractable
,
1633 if (NS_FAILED(mEarlyRv
)) {
1637 // If this is RSA with a hash, cache the hash name
1638 if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
1639 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP
) ||
1640 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS
)) {
1641 RootedDictionary
<RsaHashedImportParams
> params(aCx
);
1642 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
1643 if (NS_FAILED(mEarlyRv
)) {
1644 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
1648 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, mHashName
);
1649 if (NS_FAILED(mEarlyRv
)) {
1650 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
1655 // Check support for the algorithm and hash names
1656 CK_MECHANISM_TYPE mech1
= MapAlgorithmNameToMechanism(mAlgName
);
1657 CK_MECHANISM_TYPE mech2
= MapAlgorithmNameToMechanism(mHashName
);
1658 if ((mech1
== UNKNOWN_CK_MECHANISM
) || (mech2
== UNKNOWN_CK_MECHANISM
)) {
1659 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1666 uint32_t mModulusLength
;
1667 CryptoBuffer mPublicExponent
;
1669 virtual nsresult
DoCrypto() override
{
1670 // Import the key data itself
1671 UniqueSECKEYPublicKey pubKey
;
1672 UniqueSECKEYPrivateKey privKey
;
1673 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI
) ||
1674 (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
) &&
1675 !mJwk
.mD
.WasPassed())) {
1676 // Public key import
1677 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI
)) {
1678 pubKey
= CryptoKey::PublicKeyFromSpki(mKeyData
);
1680 pubKey
= CryptoKey::PublicKeyFromJwk(mJwk
);
1684 return NS_ERROR_DOM_DATA_ERR
;
1687 if (NS_FAILED(mKey
->SetPublicKey(pubKey
.get()))) {
1688 return NS_ERROR_DOM_OPERATION_ERR
;
1691 mKey
->SetType(CryptoKey::PUBLIC
);
1692 } else if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8
) ||
1693 (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
) &&
1694 mJwk
.mD
.WasPassed())) {
1695 // Private key import
1696 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8
)) {
1697 privKey
= CryptoKey::PrivateKeyFromPkcs8(mKeyData
);
1699 privKey
= CryptoKey::PrivateKeyFromJwk(mJwk
);
1703 return NS_ERROR_DOM_DATA_ERR
;
1706 if (NS_FAILED(mKey
->SetPrivateKey(privKey
.get()))) {
1707 return NS_ERROR_DOM_OPERATION_ERR
;
1710 mKey
->SetType(CryptoKey::PRIVATE
);
1711 pubKey
= UniqueSECKEYPublicKey(SECKEY_ConvertToPublicKey(privKey
.get()));
1713 return NS_ERROR_DOM_UNKNOWN_ERR
;
1716 // Invalid key format
1717 return NS_ERROR_DOM_SYNTAX_ERR
;
1720 if (pubKey
->keyType
!= rsaKey
) {
1721 return NS_ERROR_DOM_DATA_ERR
;
1723 // Extract relevant information from the public key
1724 mModulusLength
= 8 * pubKey
->u
.rsa
.modulus
.len
;
1725 if (!mPublicExponent
.Assign(&pubKey
->u
.rsa
.publicExponent
)) {
1726 return NS_ERROR_DOM_OPERATION_ERR
;
1732 virtual nsresult
AfterCrypto() override
{
1733 // Check permissions for the requested operation
1734 if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP
)) {
1735 if ((mKey
->GetKeyType() == CryptoKey::PUBLIC
&&
1736 mKey
->HasUsageOtherThan(CryptoKey::ENCRYPT
| CryptoKey::WRAPKEY
)) ||
1737 (mKey
->GetKeyType() == CryptoKey::PRIVATE
&&
1738 mKey
->HasUsageOtherThan(CryptoKey::DECRYPT
|
1739 CryptoKey::UNWRAPKEY
))) {
1740 return NS_ERROR_DOM_DATA_ERR
;
1742 } else if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
1743 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS
)) {
1744 if ((mKey
->GetKeyType() == CryptoKey::PUBLIC
&&
1745 mKey
->HasUsageOtherThan(CryptoKey::VERIFY
)) ||
1746 (mKey
->GetKeyType() == CryptoKey::PRIVATE
&&
1747 mKey
->HasUsageOtherThan(CryptoKey::SIGN
))) {
1748 return NS_ERROR_DOM_DATA_ERR
;
1752 if (mKey
->GetKeyType() == CryptoKey::PRIVATE
&& !mKey
->HasAnyUsage()) {
1753 return NS_ERROR_DOM_SYNTAX_ERR
;
1756 // Set an appropriate KeyAlgorithm
1757 if (!mKey
->Algorithm().MakeRsa(mAlgName
, mModulusLength
, mPublicExponent
,
1759 return NS_ERROR_DOM_OPERATION_ERR
;
1762 if (mDataIsJwk
&& !JwkCompatible(mJwk
, mKey
)) {
1763 return NS_ERROR_DOM_DATA_ERR
;
1770 class ImportEcKeyTask
: public ImportKeyTask
{
1772 ImportEcKeyTask(nsIGlobalObject
* aGlobal
, JSContext
* aCx
,
1773 const nsAString
& aFormat
, const ObjectOrString
& aAlgorithm
,
1774 bool aExtractable
, const Sequence
<nsString
>& aKeyUsages
) {
1775 Init(aGlobal
, aCx
, aFormat
, aAlgorithm
, aExtractable
, aKeyUsages
);
1778 ImportEcKeyTask(nsIGlobalObject
* aGlobal
, JSContext
* aCx
,
1779 const nsAString
& aFormat
, JS::Handle
<JSObject
*> aKeyData
,
1780 const ObjectOrString
& aAlgorithm
, bool aExtractable
,
1781 const Sequence
<nsString
>& aKeyUsages
) {
1782 Init(aGlobal
, aCx
, aFormat
, aAlgorithm
, aExtractable
, aKeyUsages
);
1783 if (NS_FAILED(mEarlyRv
)) {
1787 SetKeyData(aCx
, aKeyData
);
1788 NS_ENSURE_SUCCESS_VOID(mEarlyRv
);
1791 void Init(nsIGlobalObject
* aGlobal
, JSContext
* aCx
, const nsAString
& aFormat
,
1792 const ObjectOrString
& aAlgorithm
, bool aExtractable
,
1793 const Sequence
<nsString
>& aKeyUsages
) {
1794 ImportKeyTask::Init(aGlobal
, aCx
, aFormat
, aAlgorithm
, aExtractable
,
1796 if (NS_FAILED(mEarlyRv
)) {
1800 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW
)) {
1801 RootedDictionary
<EcKeyImportParams
> params(aCx
);
1802 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
1803 if (NS_FAILED(mEarlyRv
) || !params
.mNamedCurve
.WasPassed()) {
1804 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
1808 if (!NormalizeToken(params
.mNamedCurve
.Value(), mNamedCurve
)) {
1809 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1816 nsString mNamedCurve
;
1818 virtual nsresult
DoCrypto() override
{
1819 // Import the key data itself
1820 UniqueSECKEYPublicKey pubKey
;
1821 UniqueSECKEYPrivateKey privKey
;
1823 if ((mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
) &&
1824 mJwk
.mD
.WasPassed()) ||
1825 mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8
)) {
1826 // Private key import
1827 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
1828 privKey
= CryptoKey::PrivateKeyFromJwk(mJwk
);
1830 return NS_ERROR_DOM_DATA_ERR
;
1833 privKey
= CryptoKey::PrivateKeyFromPkcs8(mKeyData
);
1835 return NS_ERROR_DOM_DATA_ERR
;
1838 ScopedAutoSECItem ecParams
;
1839 if (PK11_ReadRawAttribute(PK11_TypePrivKey
, privKey
.get(),
1840 CKA_EC_PARAMS
, &ecParams
) != SECSuccess
) {
1841 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1843 // Construct the OID tag.
1844 SECItem oid
= {siBuffer
, nullptr, 0};
1845 oid
.len
= ecParams
.data
[1];
1846 oid
.data
= ecParams
.data
+ 2;
1847 // Find a matching and supported named curve.
1848 if (!MapOIDTagToNamedCurve(SECOID_FindOIDTag(&oid
), mNamedCurve
)) {
1849 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1853 if (NS_FAILED(mKey
->SetPrivateKey(privKey
.get()))) {
1854 return NS_ERROR_DOM_OPERATION_ERR
;
1857 mKey
->SetType(CryptoKey::PRIVATE
);
1858 } else if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW
) ||
1859 mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI
) ||
1860 (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
) &&
1861 !mJwk
.mD
.WasPassed())) {
1862 // Public key import
1863 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW
)) {
1864 pubKey
= CryptoKey::PublicECKeyFromRaw(mKeyData
, mNamedCurve
);
1865 } else if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI
)) {
1866 pubKey
= CryptoKey::PublicKeyFromSpki(mKeyData
);
1867 } else if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
1868 pubKey
= CryptoKey::PublicKeyFromJwk(mJwk
);
1874 return NS_ERROR_DOM_DATA_ERR
;
1877 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI
)) {
1878 if (pubKey
->keyType
!= ecKey
) {
1879 return NS_ERROR_DOM_DATA_ERR
;
1881 if (!CheckEncodedECParameters(&pubKey
->u
.ec
.DEREncodedParams
)) {
1882 return NS_ERROR_DOM_OPERATION_ERR
;
1885 // Construct the OID tag.
1886 SECItem oid
= {siBuffer
, nullptr, 0};
1887 oid
.len
= pubKey
->u
.ec
.DEREncodedParams
.data
[1];
1888 oid
.data
= pubKey
->u
.ec
.DEREncodedParams
.data
+ 2;
1890 // Find a matching and supported named curve.
1891 if (!MapOIDTagToNamedCurve(SECOID_FindOIDTag(&oid
), mNamedCurve
)) {
1892 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1896 if (NS_FAILED(mKey
->SetPublicKey(pubKey
.get()))) {
1897 return NS_ERROR_DOM_OPERATION_ERR
;
1900 mKey
->SetType(CryptoKey::PUBLIC
);
1902 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1905 // Extract 'crv' parameter from JWKs.
1906 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
1907 if (!NormalizeToken(mJwk
.mCrv
.Value(), mNamedCurve
)) {
1908 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1914 virtual nsresult
AfterCrypto() override
{
1915 uint32_t privateAllowedUsages
= 0, publicAllowedUsages
= 0;
1916 if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_ECDH
)) {
1917 privateAllowedUsages
= CryptoKey::DERIVEBITS
| CryptoKey::DERIVEKEY
;
1918 publicAllowedUsages
= CryptoKey::DERIVEBITS
| CryptoKey::DERIVEKEY
;
1919 } else if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_ECDSA
)) {
1920 privateAllowedUsages
= CryptoKey::SIGN
;
1921 publicAllowedUsages
= CryptoKey::VERIFY
;
1924 // Check permissions for the requested operation
1925 if ((mKey
->GetKeyType() == CryptoKey::PRIVATE
&&
1926 mKey
->HasUsageOtherThan(privateAllowedUsages
)) ||
1927 (mKey
->GetKeyType() == CryptoKey::PUBLIC
&&
1928 mKey
->HasUsageOtherThan(publicAllowedUsages
))) {
1929 return NS_ERROR_DOM_DATA_ERR
;
1932 if (mKey
->GetKeyType() == CryptoKey::PRIVATE
&& !mKey
->HasAnyUsage()) {
1933 return NS_ERROR_DOM_SYNTAX_ERR
;
1936 mKey
->Algorithm().MakeEc(mAlgName
, mNamedCurve
);
1938 if (mDataIsJwk
&& !JwkCompatible(mJwk
, mKey
)) {
1939 return NS_ERROR_DOM_DATA_ERR
;
1946 class ExportKeyTask
: public WebCryptoTask
{
1948 ExportKeyTask(const nsAString
& aFormat
, CryptoKey
& aKey
)
1950 mPrivateKey(aKey
.GetPrivateKey()),
1951 mPublicKey(aKey
.GetPublicKey()),
1952 mKeyType(aKey
.GetKeyType()),
1953 mExtractable(aKey
.Extractable()),
1954 mAlg(aKey
.Algorithm().JwkAlg()) {
1955 aKey
.GetUsages(mKeyUsages
);
1957 if (!mSymKey
.Assign(aKey
.GetSymKey())) {
1958 mEarlyRv
= NS_ERROR_OUT_OF_MEMORY
;
1965 CryptoBuffer mSymKey
;
1966 UniqueSECKEYPrivateKey mPrivateKey
;
1967 UniqueSECKEYPublicKey mPublicKey
;
1968 CryptoKey::KeyType mKeyType
;
1971 nsTArray
<nsString
> mKeyUsages
;
1972 CryptoBuffer mResult
;
1976 virtual nsresult
DoCrypto() override
{
1977 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW
)) {
1978 if (mPublicKey
&& mPublicKey
->keyType
== dhKey
) {
1979 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1982 if (mPublicKey
&& mPublicKey
->keyType
== ecKey
) {
1983 nsresult rv
= CryptoKey::PublicECKeyToRaw(mPublicKey
.get(), mResult
);
1984 if (NS_FAILED(rv
)) {
1985 return NS_ERROR_DOM_OPERATION_ERR
;
1990 if (!mResult
.Assign(mSymKey
)) {
1991 return NS_ERROR_OUT_OF_MEMORY
;
1993 if (mResult
.Length() == 0) {
1994 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1998 } else if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8
)) {
2000 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2003 switch (mPrivateKey
->keyType
) {
2007 CryptoKey::PrivateKeyToPkcs8(mPrivateKey
.get(), mResult
);
2008 if (NS_FAILED(rv
)) {
2009 return NS_ERROR_DOM_OPERATION_ERR
;
2014 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2016 } else if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI
)) {
2018 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2021 return CryptoKey::PublicKeyToSpki(mPublicKey
.get(), mResult
);
2022 } else if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
2023 if (mKeyType
== CryptoKey::SECRET
) {
2025 nsresult rv
= mSymKey
.ToJwkBase64(k
);
2026 if (NS_FAILED(rv
)) {
2027 return NS_ERROR_DOM_OPERATION_ERR
;
2029 mJwk
.mK
.Construct(k
);
2030 mJwk
.mKty
= NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_SYMMETRIC
);
2031 } else if (mKeyType
== CryptoKey::PUBLIC
) {
2033 return NS_ERROR_DOM_UNKNOWN_ERR
;
2036 nsresult rv
= CryptoKey::PublicKeyToJwk(mPublicKey
.get(), mJwk
);
2037 if (NS_FAILED(rv
)) {
2038 return NS_ERROR_DOM_OPERATION_ERR
;
2040 } else if (mKeyType
== CryptoKey::PRIVATE
) {
2042 return NS_ERROR_DOM_UNKNOWN_ERR
;
2045 nsresult rv
= CryptoKey::PrivateKeyToJwk(mPrivateKey
.get(), mJwk
);
2046 if (NS_FAILED(rv
)) {
2047 return NS_ERROR_DOM_OPERATION_ERR
;
2051 if (!mAlg
.IsEmpty()) {
2052 mJwk
.mAlg
.Construct(mAlg
);
2055 mJwk
.mExt
.Construct(mExtractable
);
2057 mJwk
.mKey_ops
.Construct();
2058 if (!mJwk
.mKey_ops
.Value().AppendElements(mKeyUsages
, fallible
)) {
2059 return NS_ERROR_OUT_OF_MEMORY
;
2065 return NS_ERROR_DOM_SYNTAX_ERR
;
2068 // Returns mResult as an ArrayBufferView or JWK, as appropriate
2069 virtual void Resolve() override
{
2070 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
2071 mResultPromise
->MaybeResolve(mJwk
);
2075 TypedArrayCreator
<ArrayBuffer
> ret(mResult
);
2076 mResultPromise
->MaybeResolve(ret
);
2080 class GenerateSymmetricKeyTask
: public WebCryptoTask
{
2082 GenerateSymmetricKeyTask(nsIGlobalObject
* aGlobal
, JSContext
* aCx
,
2083 const ObjectOrString
& aAlgorithm
, bool aExtractable
,
2084 const Sequence
<nsString
>& aKeyUsages
) {
2085 // Create an empty key and set easy attributes
2086 mKey
= new CryptoKey(aGlobal
);
2087 mKey
->SetExtractable(aExtractable
);
2088 mKey
->SetType(CryptoKey::SECRET
);
2090 // Extract algorithm name
2092 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
2093 if (NS_FAILED(mEarlyRv
)) {
2097 // Construct an appropriate KeyAlorithm
2098 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC
) ||
2099 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR
) ||
2100 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM
) ||
2101 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_KW
)) {
2102 mEarlyRv
= GetKeyLengthForAlgorithm(aCx
, aAlgorithm
, mLength
);
2103 if (NS_FAILED(mEarlyRv
)) {
2106 mKey
->Algorithm().MakeAes(algName
, mLength
);
2108 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_HMAC
)) {
2109 RootedDictionary
<HmacKeyGenParams
> params(aCx
);
2110 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
2111 if (NS_FAILED(mEarlyRv
)) {
2112 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
2117 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, hashName
);
2118 if (NS_FAILED(mEarlyRv
)) {
2122 if (params
.mLength
.WasPassed()) {
2123 mLength
= params
.mLength
.Value();
2125 mLength
= MapHashAlgorithmNameToBlockSize(hashName
);
2129 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
2133 mKey
->Algorithm().MakeHmac(mLength
, hashName
);
2135 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2140 mKey
->ClearUsages();
2141 for (uint32_t i
= 0; i
< aKeyUsages
.Length(); ++i
) {
2142 mEarlyRv
= mKey
->AddAllowedUsageIntersecting(aKeyUsages
[i
], algName
);
2143 if (NS_FAILED(mEarlyRv
)) {
2147 if (!mKey
->HasAnyUsage()) {
2148 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
2152 mLength
= mLength
>> 3; // bits to bytes
2153 mMechanism
= mKey
->Algorithm().Mechanism();
2154 // SetSymKey done in Resolve, after we've done the keygen
2158 RefPtr
<CryptoKey
> mKey
;
2160 CK_MECHANISM_TYPE mMechanism
;
2161 CryptoBuffer mKeyData
;
2163 virtual nsresult
DoCrypto() override
{
2164 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
2165 MOZ_ASSERT(slot
.get());
2167 UniquePK11SymKey
symKey(
2168 PK11_KeyGen(slot
.get(), mMechanism
, nullptr, mLength
, nullptr));
2170 return NS_ERROR_DOM_UNKNOWN_ERR
;
2173 nsresult rv
= MapSECStatus(PK11_ExtractKeyValue(symKey
.get()));
2174 if (NS_FAILED(rv
)) {
2175 return NS_ERROR_DOM_UNKNOWN_ERR
;
2178 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2179 // just refers to a buffer managed by symKey. The assignment copies the
2180 // data, so mKeyData manages one copy, while symKey manages another.
2181 ATTEMPT_BUFFER_ASSIGN(mKeyData
, PK11_GetKeyData(symKey
.get()));
2185 virtual void Resolve() override
{
2186 if (NS_SUCCEEDED(mKey
->SetSymKey(mKeyData
))) {
2187 mResultPromise
->MaybeResolve(mKey
);
2189 mResultPromise
->MaybeReject(NS_ERROR_DOM_OPERATION_ERR
);
2193 virtual void Cleanup() override
{ mKey
= nullptr; }
2196 GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask(
2197 nsIGlobalObject
* aGlobal
, JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
2198 bool aExtractable
, const Sequence
<nsString
>& aKeyUsages
)
2199 : mKeyPair(new CryptoKeyPair()),
2200 mMechanism(CKM_INVALID_MECHANISM
),
2203 mArena
= UniquePLArenaPool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
2205 mEarlyRv
= NS_ERROR_DOM_UNKNOWN_ERR
;
2209 // Create an empty key pair and set easy attributes
2210 mKeyPair
->mPrivateKey
= new CryptoKey(aGlobal
);
2211 mKeyPair
->mPublicKey
= new CryptoKey(aGlobal
);
2213 // Extract algorithm name
2214 mEarlyRv
= GetAlgorithmName(aCx
, aAlgorithm
, mAlgName
);
2215 if (NS_FAILED(mEarlyRv
)) {
2219 // Construct an appropriate KeyAlorithm
2220 uint32_t privateAllowedUsages
= 0, publicAllowedUsages
= 0;
2221 if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
2222 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP
) ||
2223 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS
)) {
2224 RootedDictionary
<RsaHashedKeyGenParams
> params(aCx
);
2225 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
2226 if (NS_FAILED(mEarlyRv
)) {
2227 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
2231 // Pull relevant info
2232 uint32_t modulusLength
= params
.mModulusLength
;
2233 CryptoBuffer publicExponent
;
2234 ATTEMPT_BUFFER_INIT(publicExponent
, params
.mPublicExponent
);
2236 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, hashName
);
2237 if (NS_FAILED(mEarlyRv
)) {
2242 if (!mKeyPair
->mPublicKey
->Algorithm().MakeRsa(mAlgName
, modulusLength
,
2243 publicExponent
, hashName
)) {
2244 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
2247 if (!mKeyPair
->mPrivateKey
->Algorithm().MakeRsa(mAlgName
, modulusLength
,
2248 publicExponent
, hashName
)) {
2249 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
2252 mMechanism
= CKM_RSA_PKCS_KEY_PAIR_GEN
;
2254 // Set up params struct
2255 mRsaParams
.keySizeInBits
= modulusLength
;
2256 bool converted
= publicExponent
.GetBigIntValue(mRsaParams
.pe
);
2258 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
2261 } else if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_ECDH
) ||
2262 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_ECDSA
)) {
2263 RootedDictionary
<EcKeyGenParams
> params(aCx
);
2264 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
2265 if (NS_FAILED(mEarlyRv
)) {
2266 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
2270 if (!NormalizeToken(params
.mNamedCurve
, mNamedCurve
)) {
2271 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2275 // Create algorithm.
2276 mKeyPair
->mPublicKey
->Algorithm().MakeEc(mAlgName
, mNamedCurve
);
2277 mKeyPair
->mPrivateKey
->Algorithm().MakeEc(mAlgName
, mNamedCurve
);
2278 mMechanism
= CKM_EC_KEY_PAIR_GEN
;
2280 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2285 if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
2286 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS
) ||
2287 mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_ECDSA
)) {
2288 privateAllowedUsages
= CryptoKey::SIGN
;
2289 publicAllowedUsages
= CryptoKey::VERIFY
;
2290 } else if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP
)) {
2291 privateAllowedUsages
= CryptoKey::DECRYPT
| CryptoKey::UNWRAPKEY
;
2292 publicAllowedUsages
= CryptoKey::ENCRYPT
| CryptoKey::WRAPKEY
;
2293 } else if (mAlgName
.EqualsLiteral(WEBCRYPTO_ALG_ECDH
)) {
2294 privateAllowedUsages
= CryptoKey::DERIVEKEY
| CryptoKey::DERIVEBITS
;
2295 publicAllowedUsages
= 0;
2297 MOZ_ASSERT(false); // This shouldn't happen.
2300 mKeyPair
->mPrivateKey
->SetExtractable(aExtractable
);
2301 mKeyPair
->mPrivateKey
->SetType(CryptoKey::PRIVATE
);
2303 mKeyPair
->mPublicKey
->SetExtractable(true);
2304 mKeyPair
->mPublicKey
->SetType(CryptoKey::PUBLIC
);
2306 mKeyPair
->mPrivateKey
->ClearUsages();
2307 mKeyPair
->mPublicKey
->ClearUsages();
2308 for (uint32_t i
= 0; i
< aKeyUsages
.Length(); ++i
) {
2309 mEarlyRv
= mKeyPair
->mPrivateKey
->AddAllowedUsageIntersecting(
2310 aKeyUsages
[i
], mAlgName
, privateAllowedUsages
);
2311 if (NS_FAILED(mEarlyRv
)) {
2315 mEarlyRv
= mKeyPair
->mPublicKey
->AddAllowedUsageIntersecting(
2316 aKeyUsages
[i
], mAlgName
, publicAllowedUsages
);
2317 if (NS_FAILED(mEarlyRv
)) {
2323 nsresult
GenerateAsymmetricKeyTask::DoCrypto() {
2324 MOZ_ASSERT(mKeyPair
);
2326 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
2327 MOZ_ASSERT(slot
.get());
2330 switch (mMechanism
) {
2331 case CKM_RSA_PKCS_KEY_PAIR_GEN
:
2332 param
= &mRsaParams
;
2334 case CKM_DH_PKCS_KEY_PAIR_GEN
:
2337 case CKM_EC_KEY_PAIR_GEN
: {
2338 param
= CreateECParamsForCurve(mNamedCurve
, mArena
.get());
2340 return NS_ERROR_DOM_UNKNOWN_ERR
;
2345 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2348 SECKEYPublicKey
* pubKey
= nullptr;
2349 mPrivateKey
= UniqueSECKEYPrivateKey(PK11_GenerateKeyPair(
2350 slot
.get(), mMechanism
, param
, &pubKey
, PR_FALSE
, PR_FALSE
, nullptr));
2351 mPublicKey
= UniqueSECKEYPublicKey(pubKey
);
2353 if (!mPrivateKey
.get() || !mPublicKey
.get()) {
2354 return NS_ERROR_DOM_OPERATION_ERR
;
2357 // If no usages ended up being allowed, SyntaxError
2358 if (!mKeyPair
->mPrivateKey
->HasAnyUsage()) {
2359 return NS_ERROR_DOM_SYNTAX_ERR
;
2362 nsresult rv
= mKeyPair
->mPrivateKey
->SetPrivateKey(mPrivateKey
.get());
2363 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
2364 rv
= mKeyPair
->mPublicKey
->SetPublicKey(mPublicKey
.get());
2365 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
2367 // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
2368 // private key, we need this later when exporting to PKCS8 and JWK though.
2369 if (mMechanism
== CKM_EC_KEY_PAIR_GEN
) {
2370 rv
= mKeyPair
->mPrivateKey
->AddPublicKeyData(mPublicKey
.get());
2371 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_OPERATION_ERR
);
2377 void GenerateAsymmetricKeyTask::Resolve() {
2378 mResultPromise
->MaybeResolve(*mKeyPair
);
2381 void GenerateAsymmetricKeyTask::Cleanup() { mKeyPair
= nullptr; }
2383 class DeriveHkdfBitsTask
: public ReturnArrayBufferViewTask
{
2385 DeriveHkdfBitsTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
2386 CryptoKey
& aKey
, uint32_t aLength
)
2387 : mMechanism(CKM_INVALID_MECHANISM
) {
2388 Init(aCx
, aAlgorithm
, aKey
, aLength
);
2391 DeriveHkdfBitsTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
2392 CryptoKey
& aKey
, const ObjectOrString
& aTargetAlgorithm
)
2393 : mLengthInBits(0), mLengthInBytes(0), mMechanism(CKM_INVALID_MECHANISM
) {
2395 mEarlyRv
= GetKeyLengthForAlgorithm(aCx
, aTargetAlgorithm
, length
);
2397 if (NS_SUCCEEDED(mEarlyRv
)) {
2398 Init(aCx
, aAlgorithm
, aKey
, length
);
2402 void Init(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
2404 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, TA_HKDF
);
2405 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_HKDF
);
2407 if (!mSymKey
.Assign(aKey
.GetSymKey())) {
2408 mEarlyRv
= NS_ERROR_OUT_OF_MEMORY
;
2412 // Check that we have a key.
2413 if (mSymKey
.Length() == 0) {
2414 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
2418 RootedDictionary
<HkdfParams
> params(aCx
);
2419 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
2420 if (NS_FAILED(mEarlyRv
)) {
2421 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
2425 // length must be greater than zero.
2427 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
2431 // Extract the hash algorithm.
2433 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, hashName
);
2434 if (NS_FAILED(mEarlyRv
)) {
2438 // Check the given hash algorithm.
2439 switch (MapAlgorithmNameToMechanism(hashName
)) {
2441 mMechanism
= CKM_NSS_HKDF_SHA1
;
2444 mMechanism
= CKM_NSS_HKDF_SHA256
;
2447 mMechanism
= CKM_NSS_HKDF_SHA384
;
2450 mMechanism
= CKM_NSS_HKDF_SHA512
;
2453 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2457 ATTEMPT_BUFFER_INIT(mSalt
, params
.mSalt
)
2458 ATTEMPT_BUFFER_INIT(mInfo
, params
.mInfo
)
2459 mLengthInBytes
= ceil((double)aLength
/ 8);
2460 mLengthInBits
= aLength
;
2464 size_t mLengthInBits
;
2465 size_t mLengthInBytes
;
2468 CryptoBuffer mSymKey
;
2469 CK_MECHANISM_TYPE mMechanism
;
2471 virtual nsresult
DoCrypto() override
{
2472 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
2474 return NS_ERROR_DOM_OPERATION_ERR
;
2478 SECItem keyItem
= {siBuffer
, nullptr, 0};
2479 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), &keyItem
, mSymKey
);
2481 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
2483 return NS_ERROR_DOM_OPERATION_ERR
;
2486 UniquePK11SymKey
baseKey(PK11_ImportSymKey(slot
.get(), mMechanism
,
2487 PK11_OriginUnwrap
, CKA_WRAP
,
2488 &keyItem
, nullptr));
2490 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
2493 SECItem salt
= {siBuffer
, nullptr, 0};
2494 SECItem info
= {siBuffer
, nullptr, 0};
2495 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), &salt
, mSalt
);
2496 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), &info
, mInfo
);
2498 CK_NSS_HKDFParams hkdfParams
= {true, salt
.data
, salt
.len
,
2499 true, info
.data
, info
.len
};
2500 SECItem params
= {siBuffer
, (unsigned char*)&hkdfParams
,
2501 sizeof(hkdfParams
)};
2503 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2504 // derived symmetric key and don't matter because we ignore them anyway.
2505 UniquePK11SymKey
symKey(PK11_Derive(baseKey
.get(), mMechanism
, ¶ms
,
2506 CKM_SHA512_HMAC
, CKA_SIGN
,
2509 if (!symKey
.get()) {
2510 return NS_ERROR_DOM_OPERATION_ERR
;
2513 nsresult rv
= MapSECStatus(PK11_ExtractKeyValue(symKey
.get()));
2514 if (NS_FAILED(rv
)) {
2515 return NS_ERROR_DOM_OPERATION_ERR
;
2518 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2519 // just refers to a buffer managed by symKey. The assignment copies the
2520 // data, so mResult manages one copy, while symKey manages another.
2521 ATTEMPT_BUFFER_ASSIGN(mResult
, PK11_GetKeyData(symKey
.get()));
2523 if (mLengthInBytes
> mResult
.Length()) {
2524 return NS_ERROR_DOM_DATA_ERR
;
2527 if (!mResult
.SetLength(mLengthInBytes
, fallible
)) {
2528 return NS_ERROR_DOM_UNKNOWN_ERR
;
2531 // If the number of bits to derive is not a multiple of 8 we need to
2532 // zero out the remaining bits that were derived but not requested.
2533 if (mLengthInBits
% 8) {
2534 mResult
[mResult
.Length() - 1] &= 0xff << (mLengthInBits
% 8);
2541 class DerivePbkdfBitsTask
: public ReturnArrayBufferViewTask
{
2543 DerivePbkdfBitsTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
2544 CryptoKey
& aKey
, uint32_t aLength
)
2545 : mHashOidTag(SEC_OID_UNKNOWN
) {
2546 Init(aCx
, aAlgorithm
, aKey
, aLength
);
2549 DerivePbkdfBitsTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
2550 CryptoKey
& aKey
, const ObjectOrString
& aTargetAlgorithm
)
2551 : mLength(0), mIterations(0), mHashOidTag(SEC_OID_UNKNOWN
) {
2553 mEarlyRv
= GetKeyLengthForAlgorithm(aCx
, aTargetAlgorithm
, length
);
2555 if (NS_SUCCEEDED(mEarlyRv
)) {
2556 Init(aCx
, aAlgorithm
, aKey
, length
);
2560 void Init(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
2562 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, TA_PBKDF2
);
2563 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_PBKDF2
);
2565 if (!mSymKey
.Assign(aKey
.GetSymKey())) {
2566 mEarlyRv
= NS_ERROR_OUT_OF_MEMORY
;
2570 RootedDictionary
<Pbkdf2Params
> params(aCx
);
2571 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
2572 if (NS_FAILED(mEarlyRv
)) {
2573 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
2577 // length must be a multiple of 8 bigger than zero.
2578 if (aLength
== 0 || aLength
% 8) {
2579 mEarlyRv
= NS_ERROR_DOM_OPERATION_ERR
;
2583 // Extract the hash algorithm.
2585 mEarlyRv
= GetAlgorithmName(aCx
, params
.mHash
, hashName
);
2586 if (NS_FAILED(mEarlyRv
)) {
2590 // Check the given hash algorithm.
2591 switch (MapAlgorithmNameToMechanism(hashName
)) {
2593 mHashOidTag
= SEC_OID_HMAC_SHA1
;
2596 mHashOidTag
= SEC_OID_HMAC_SHA256
;
2599 mHashOidTag
= SEC_OID_HMAC_SHA384
;
2602 mHashOidTag
= SEC_OID_HMAC_SHA512
;
2605 mEarlyRv
= NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
2609 ATTEMPT_BUFFER_INIT(mSalt
, params
.mSalt
)
2610 mLength
= aLength
>> 3; // bits to bytes
2611 mIterations
= params
.mIterations
;
2618 CryptoBuffer mSymKey
;
2619 SECOidTag mHashOidTag
;
2621 virtual nsresult
DoCrypto() override
{
2622 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
2624 return NS_ERROR_DOM_OPERATION_ERR
;
2627 SECItem salt
= {siBuffer
, nullptr, 0};
2628 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), &salt
, mSalt
);
2629 // PK11_CreatePBEV2AlgorithmID will "helpfully" create PBKDF2 parameters
2630 // with a random salt if given a SECItem* that is either null or has a null
2631 // data pointer. This obviously isn't what we want, so we have to fake it
2632 // out by passing in a SECItem* with a non-null data pointer but with zero
2635 MOZ_ASSERT(salt
.len
== 0);
2637 reinterpret_cast<unsigned char*>(PORT_ArenaAlloc(arena
.get(), 1));
2639 return NS_ERROR_DOM_UNKNOWN_ERR
;
2643 // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this
2644 // parameter is unused for key generation. It is currently only used
2645 // for PBKDF2 authentication or key (un)wrapping when specifying an
2646 // encryption algorithm (PBES2).
2647 UniqueSECAlgorithmID
algID(
2648 PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2
, SEC_OID_HMAC_SHA1
,
2649 mHashOidTag
, mLength
, mIterations
, &salt
));
2652 return NS_ERROR_DOM_OPERATION_ERR
;
2655 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
2657 return NS_ERROR_DOM_OPERATION_ERR
;
2660 SECItem keyItem
= {siBuffer
, nullptr, 0};
2661 ATTEMPT_BUFFER_TO_SECITEM(arena
.get(), &keyItem
, mSymKey
);
2663 UniquePK11SymKey
symKey(
2664 PK11_PBEKeyGen(slot
.get(), algID
.get(), &keyItem
, false, nullptr));
2665 if (!symKey
.get()) {
2666 return NS_ERROR_DOM_OPERATION_ERR
;
2669 nsresult rv
= MapSECStatus(PK11_ExtractKeyValue(symKey
.get()));
2670 if (NS_FAILED(rv
)) {
2671 return NS_ERROR_DOM_OPERATION_ERR
;
2674 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2675 // just refers to a buffer managed by symKey. The assignment copies the
2676 // data, so mResult manages one copy, while symKey manages another.
2677 ATTEMPT_BUFFER_ASSIGN(mResult
, PK11_GetKeyData(symKey
.get()));
2682 template <class DeriveBitsTask
>
2683 class DeriveKeyTask
: public DeriveBitsTask
{
2685 DeriveKeyTask(nsIGlobalObject
* aGlobal
, JSContext
* aCx
,
2686 const ObjectOrString
& aAlgorithm
, CryptoKey
& aBaseKey
,
2687 const ObjectOrString
& aDerivedKeyType
, bool aExtractable
,
2688 const Sequence
<nsString
>& aKeyUsages
)
2689 : DeriveBitsTask(aCx
, aAlgorithm
, aBaseKey
, aDerivedKeyType
) {
2690 if (NS_FAILED(this->mEarlyRv
)) {
2694 constexpr auto format
=
2695 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_FORMAT_RAW
);
2696 mTask
= new ImportSymmetricKeyTask(aGlobal
, aCx
, format
, aDerivedKeyType
,
2697 aExtractable
, aKeyUsages
);
2701 RefPtr
<ImportSymmetricKeyTask
> mTask
;
2704 virtual void Resolve() override
{
2705 mTask
->SetRawKeyData(this->mResult
);
2706 mTask
->DispatchWithPromise(this->mResultPromise
);
2709 virtual void Cleanup() override
{ mTask
= nullptr; }
2711 class DeriveEcdhBitsTask
: public ReturnArrayBufferViewTask
{
2713 DeriveEcdhBitsTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
2714 CryptoKey
& aKey
, uint32_t aLength
)
2715 : mLength(Some(aLength
)), mPrivKey(aKey
.GetPrivateKey()) {
2716 Init(aCx
, aAlgorithm
, aKey
);
2719 DeriveEcdhBitsTask(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
2720 CryptoKey
& aKey
, const ObjectOrString
& aTargetAlgorithm
)
2721 : mPrivKey(aKey
.GetPrivateKey()) {
2723 GetKeyLengthForAlgorithmIfSpecified(aCx
, aTargetAlgorithm
, mLength
);
2724 if (NS_SUCCEEDED(mEarlyRv
)) {
2725 Init(aCx
, aAlgorithm
, aKey
);
2729 void Init(JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
) {
2730 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG
, TA_ECDH
);
2731 CHECK_KEY_ALGORITHM(aKey
.Algorithm(), WEBCRYPTO_ALG_ECDH
);
2733 // Check that we have a private key.
2735 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
2739 // If specified, length must be a multiple of 8 bigger than zero
2740 // (otherwise, the full output of the key derivation is used).
2742 if (*mLength
== 0 || *mLength
% 8) {
2743 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
2746 *mLength
= *mLength
>> 3; // bits to bytes
2749 // Retrieve the peer's public key.
2750 RootedDictionary
<EcdhKeyDeriveParams
> params(aCx
);
2751 mEarlyRv
= Coerce(aCx
, params
, aAlgorithm
);
2752 if (NS_FAILED(mEarlyRv
)) {
2753 mEarlyRv
= NS_ERROR_DOM_SYNTAX_ERR
;
2757 CryptoKey
* publicKey
= params
.mPublic
;
2758 mPubKey
= publicKey
->GetPublicKey();
2760 mEarlyRv
= NS_ERROR_DOM_INVALID_ACCESS_ERR
;
2764 CHECK_KEY_ALGORITHM(publicKey
->Algorithm(), WEBCRYPTO_ALG_ECDH
);
2766 // Both keys must use the same named curve.
2767 nsString curve1
= aKey
.Algorithm().mEc
.mNamedCurve
;
2768 nsString curve2
= publicKey
->Algorithm().mEc
.mNamedCurve
;
2770 if (!curve1
.Equals(curve2
)) {
2771 mEarlyRv
= NS_ERROR_DOM_DATA_ERR
;
2777 Maybe
<size_t> mLength
;
2778 UniqueSECKEYPrivateKey mPrivKey
;
2779 UniqueSECKEYPublicKey mPubKey
;
2781 virtual nsresult
DoCrypto() override
{
2782 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2783 // derived symmetric key and don't matter because we ignore them anyway.
2784 UniquePK11SymKey
symKey(
2785 PK11_PubDeriveWithKDF(mPrivKey
.get(), mPubKey
.get(), PR_FALSE
, nullptr,
2786 nullptr, CKM_ECDH1_DERIVE
, CKM_SHA512_HMAC
,
2787 CKA_SIGN
, 0, CKD_NULL
, nullptr, nullptr));
2789 if (!symKey
.get()) {
2790 return NS_ERROR_DOM_OPERATION_ERR
;
2793 nsresult rv
= MapSECStatus(PK11_ExtractKeyValue(symKey
.get()));
2794 if (NS_FAILED(rv
)) {
2795 return NS_ERROR_DOM_OPERATION_ERR
;
2798 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2799 // just refers to a buffer managed by symKey. The assignment copies the
2800 // data, so mResult manages one copy, while symKey manages another.
2801 ATTEMPT_BUFFER_ASSIGN(mResult
, PK11_GetKeyData(symKey
.get()));
2804 if (*mLength
> mResult
.Length()) {
2805 return NS_ERROR_DOM_DATA_ERR
;
2807 if (!mResult
.SetLength(*mLength
, fallible
)) {
2808 return NS_ERROR_DOM_UNKNOWN_ERR
;
2816 template <class KeyEncryptTask
>
2817 class WrapKeyTask
: public ExportKeyTask
{
2819 WrapKeyTask(JSContext
* aCx
, const nsAString
& aFormat
, CryptoKey
& aKey
,
2820 CryptoKey
& aWrappingKey
, const ObjectOrString
& aWrapAlgorithm
)
2821 : ExportKeyTask(aFormat
, aKey
) {
2822 if (NS_FAILED(mEarlyRv
)) {
2826 mTask
= new KeyEncryptTask(aCx
, aWrapAlgorithm
, aWrappingKey
, true);
2830 RefPtr
<KeyEncryptTask
> mTask
;
2832 virtual nsresult
AfterCrypto() override
{
2833 // If wrapping JWK, stringify the JSON
2834 if (mFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
2836 if (!mJwk
.ToJSON(json
)) {
2837 return NS_ERROR_DOM_OPERATION_ERR
;
2840 NS_ConvertUTF16toUTF8
utf8(json
);
2841 if (!mResult
.Assign((const uint8_t*)utf8
.BeginReading(), utf8
.Length())) {
2842 return NS_ERROR_DOM_OPERATION_ERR
;
2849 virtual void Resolve() override
{
2850 mTask
->SetData(mResult
);
2851 mTask
->DispatchWithPromise(mResultPromise
);
2854 virtual void Cleanup() override
{ mTask
= nullptr; }
2857 template <class KeyEncryptTask
>
2858 class UnwrapKeyTask
: public KeyEncryptTask
{
2860 UnwrapKeyTask(JSContext
* aCx
, const ArrayBufferViewOrArrayBuffer
& aWrappedKey
,
2861 CryptoKey
& aUnwrappingKey
,
2862 const ObjectOrString
& aUnwrapAlgorithm
, ImportKeyTask
* aTask
)
2863 : KeyEncryptTask(aCx
, aUnwrapAlgorithm
, aUnwrappingKey
, aWrappedKey
,
2868 RefPtr
<ImportKeyTask
> mTask
;
2870 virtual void Resolve() override
{
2871 mTask
->SetKeyDataMaybeParseJWK(KeyEncryptTask::mResult
);
2872 mTask
->DispatchWithPromise(KeyEncryptTask::mResultPromise
);
2875 virtual void Cleanup() override
{ mTask
= nullptr; }
2878 // Task creation methods for WebCryptoTask
2880 // Note: We do not perform algorithm normalization as a monolithic process,
2881 // as described in the spec. Instead:
2882 // * Each method handles its slice of the supportedAlgorithms structure
2883 // * Task constructors take care of:
2884 // * Coercing the algorithm to the proper concrete type
2885 // * Cloning subordinate data items
2886 // * Cloning input data as needed
2888 // Thus, support for different algorithms is determined by the if-statements
2889 // below, rather than a data structure.
2891 // This results in algorithm normalization coming after some other checks,
2892 // and thus slightly more steps being done synchronously than the spec calls
2893 // for. But none of these steps is especially time-consuming.
2895 WebCryptoTask
* WebCryptoTask::CreateEncryptDecryptTask(
2896 JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
2897 const CryptoOperationData
& aData
, bool aEncrypt
) {
2898 TelemetryMethod method
= (aEncrypt
) ? TM_ENCRYPT
: TM_DECRYPT
;
2899 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD
, method
);
2900 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_ENC
,
2901 aKey
.Extractable());
2903 // Ensure key is usable for this operation
2904 if ((aEncrypt
&& !aKey
.HasUsage(CryptoKey::ENCRYPT
)) ||
2905 (!aEncrypt
&& !aKey
.HasUsage(CryptoKey::DECRYPT
))) {
2906 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
2910 nsresult rv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
2911 if (NS_FAILED(rv
)) {
2912 return new FailureTask(rv
);
2915 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC
) ||
2916 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR
) ||
2917 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM
)) {
2918 return new AesTask(aCx
, aAlgorithm
, aKey
, aData
, aEncrypt
);
2919 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP
)) {
2920 return new RsaOaepTask(aCx
, aAlgorithm
, aKey
, aData
, aEncrypt
);
2923 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
2926 WebCryptoTask
* WebCryptoTask::CreateSignVerifyTask(
2927 JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
2928 const CryptoOperationData
& aSignature
, const CryptoOperationData
& aData
,
2930 TelemetryMethod method
= (aSign
) ? TM_SIGN
: TM_VERIFY
;
2931 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD
, method
);
2932 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_SIG
,
2933 aKey
.Extractable());
2935 // Ensure key is usable for this operation
2936 if ((aSign
&& !aKey
.HasUsage(CryptoKey::SIGN
)) ||
2937 (!aSign
&& !aKey
.HasUsage(CryptoKey::VERIFY
))) {
2938 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
2942 nsresult rv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
2943 if (NS_FAILED(rv
)) {
2944 return new FailureTask(rv
);
2947 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_HMAC
)) {
2948 return new HmacTask(aCx
, aAlgorithm
, aKey
, aSignature
, aData
, aSign
);
2949 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
2950 algName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS
) ||
2951 algName
.EqualsLiteral(WEBCRYPTO_ALG_ECDSA
)) {
2952 return new AsymmetricSignVerifyTask(aCx
, aAlgorithm
, aKey
, aSignature
,
2956 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
2959 WebCryptoTask
* WebCryptoTask::CreateDigestTask(
2960 JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
2961 const CryptoOperationData
& aData
) {
2962 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD
, TM_DIGEST
);
2965 nsresult rv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
2966 if (NS_FAILED(rv
)) {
2967 return new FailureTask(rv
);
2970 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_SHA1
) ||
2971 algName
.EqualsLiteral(WEBCRYPTO_ALG_SHA256
) ||
2972 algName
.EqualsLiteral(WEBCRYPTO_ALG_SHA384
) ||
2973 algName
.EqualsLiteral(WEBCRYPTO_ALG_SHA512
)) {
2974 return new DigestTask(aCx
, aAlgorithm
, aData
);
2977 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
2980 WebCryptoTask
* WebCryptoTask::CreateImportKeyTask(
2981 nsIGlobalObject
* aGlobal
, JSContext
* aCx
, const nsAString
& aFormat
,
2982 JS::Handle
<JSObject
*> aKeyData
, const ObjectOrString
& aAlgorithm
,
2983 bool aExtractable
, const Sequence
<nsString
>& aKeyUsages
) {
2984 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD
, TM_IMPORTKEY
);
2985 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT
, aExtractable
);
2987 // Verify that the format is recognized
2988 if (!aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW
) &&
2989 !aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI
) &&
2990 !aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8
) &&
2991 !aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
2992 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR
);
2995 // Verify that aKeyUsages does not contain an unrecognized value
2996 if (!CryptoKey::AllUsagesRecognized(aKeyUsages
)) {
2997 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR
);
3001 nsresult rv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
3002 if (NS_FAILED(rv
)) {
3003 return new FailureTask(rv
);
3006 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
3007 // However, the spec should be updated to allow it.
3008 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC
) ||
3009 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR
) ||
3010 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM
) ||
3011 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_KW
) ||
3012 algName
.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2
) ||
3013 algName
.EqualsLiteral(WEBCRYPTO_ALG_HKDF
) ||
3014 algName
.EqualsLiteral(WEBCRYPTO_ALG_HMAC
)) {
3015 return new ImportSymmetricKeyTask(aGlobal
, aCx
, aFormat
, aKeyData
,
3016 aAlgorithm
, aExtractable
, aKeyUsages
);
3017 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
3018 algName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP
) ||
3019 algName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS
)) {
3020 return new ImportRsaKeyTask(aGlobal
, aCx
, aFormat
, aKeyData
, aAlgorithm
,
3021 aExtractable
, aKeyUsages
);
3022 } else if (algName
.EqualsLiteral(WEBCRYPTO_ALG_ECDH
) ||
3023 algName
.EqualsLiteral(WEBCRYPTO_ALG_ECDSA
)) {
3024 return new ImportEcKeyTask(aGlobal
, aCx
, aFormat
, aKeyData
, aAlgorithm
,
3025 aExtractable
, aKeyUsages
);
3027 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
3031 WebCryptoTask
* WebCryptoTask::CreateExportKeyTask(const nsAString
& aFormat
,
3033 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD
, TM_EXPORTKEY
);
3035 // Verify that the format is recognized
3036 if (!aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW
) &&
3037 !aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI
) &&
3038 !aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8
) &&
3039 !aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
3040 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR
);
3043 // Verify that the key is extractable
3044 if (!aKey
.Extractable()) {
3045 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
3048 // Verify that the algorithm supports export
3049 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
3050 // However, the spec should be updated to allow it.
3051 nsString algName
= aKey
.Algorithm().mName
;
3052 if (algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC
) ||
3053 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR
) ||
3054 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM
) ||
3055 algName
.EqualsLiteral(WEBCRYPTO_ALG_AES_KW
) ||
3056 algName
.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2
) ||
3057 algName
.EqualsLiteral(WEBCRYPTO_ALG_HMAC
) ||
3058 algName
.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
3059 algName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP
) ||
3060 algName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS
) ||
3061 algName
.EqualsLiteral(WEBCRYPTO_ALG_ECDSA
) ||
3062 algName
.EqualsLiteral(WEBCRYPTO_ALG_ECDH
)) {
3063 return new ExportKeyTask(aFormat
, aKey
);
3066 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
3069 WebCryptoTask
* WebCryptoTask::CreateGenerateKeyTask(
3070 nsIGlobalObject
* aGlobal
, JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
3071 bool aExtractable
, const Sequence
<nsString
>& aKeyUsages
) {
3072 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD
, TM_GENERATEKEY
);
3073 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_GENERATE
,
3076 if (!CryptoKey::AllUsagesRecognized(aKeyUsages
)) {
3077 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR
);
3081 nsresult rv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
3082 if (NS_FAILED(rv
)) {
3083 return new FailureTask(rv
);
3086 if (algName
.EqualsASCII(WEBCRYPTO_ALG_AES_CBC
) ||
3087 algName
.EqualsASCII(WEBCRYPTO_ALG_AES_CTR
) ||
3088 algName
.EqualsASCII(WEBCRYPTO_ALG_AES_GCM
) ||
3089 algName
.EqualsASCII(WEBCRYPTO_ALG_AES_KW
) ||
3090 algName
.EqualsASCII(WEBCRYPTO_ALG_HMAC
)) {
3091 return new GenerateSymmetricKeyTask(aGlobal
, aCx
, aAlgorithm
, aExtractable
,
3093 } else if (algName
.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
3094 algName
.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP
) ||
3095 algName
.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS
) ||
3096 algName
.EqualsASCII(WEBCRYPTO_ALG_ECDH
) ||
3097 algName
.EqualsASCII(WEBCRYPTO_ALG_ECDSA
)) {
3098 return new GenerateAsymmetricKeyTask(aGlobal
, aCx
, aAlgorithm
, aExtractable
,
3101 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
3105 WebCryptoTask
* WebCryptoTask::CreateDeriveKeyTask(
3106 nsIGlobalObject
* aGlobal
, JSContext
* aCx
, const ObjectOrString
& aAlgorithm
,
3107 CryptoKey
& aBaseKey
, const ObjectOrString
& aDerivedKeyType
,
3108 bool aExtractable
, const Sequence
<nsString
>& aKeyUsages
) {
3109 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD
, TM_DERIVEKEY
);
3111 // Ensure baseKey is usable for this operation
3112 if (!aBaseKey
.HasUsage(CryptoKey::DERIVEKEY
)) {
3113 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
3116 // Verify that aKeyUsages does not contain an unrecognized value
3117 if (!CryptoKey::AllUsagesRecognized(aKeyUsages
)) {
3118 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR
);
3122 nsresult rv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
3123 if (NS_FAILED(rv
)) {
3124 return new FailureTask(rv
);
3127 if (algName
.EqualsASCII(WEBCRYPTO_ALG_HKDF
)) {
3128 return new DeriveKeyTask
<DeriveHkdfBitsTask
>(aGlobal
, aCx
, aAlgorithm
,
3129 aBaseKey
, aDerivedKeyType
,
3130 aExtractable
, aKeyUsages
);
3133 if (algName
.EqualsASCII(WEBCRYPTO_ALG_PBKDF2
)) {
3134 return new DeriveKeyTask
<DerivePbkdfBitsTask
>(aGlobal
, aCx
, aAlgorithm
,
3135 aBaseKey
, aDerivedKeyType
,
3136 aExtractable
, aKeyUsages
);
3139 if (algName
.EqualsASCII(WEBCRYPTO_ALG_ECDH
)) {
3140 return new DeriveKeyTask
<DeriveEcdhBitsTask
>(aGlobal
, aCx
, aAlgorithm
,
3141 aBaseKey
, aDerivedKeyType
,
3142 aExtractable
, aKeyUsages
);
3145 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
3148 WebCryptoTask
* WebCryptoTask::CreateDeriveBitsTask(
3149 JSContext
* aCx
, const ObjectOrString
& aAlgorithm
, CryptoKey
& aKey
,
3151 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD
, TM_DERIVEBITS
);
3153 // Ensure baseKey is usable for this operation
3154 if (!aKey
.HasUsage(CryptoKey::DERIVEBITS
)) {
3155 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
3159 nsresult rv
= GetAlgorithmName(aCx
, aAlgorithm
, algName
);
3160 if (NS_FAILED(rv
)) {
3161 return new FailureTask(rv
);
3164 if (algName
.EqualsASCII(WEBCRYPTO_ALG_PBKDF2
)) {
3165 return new DerivePbkdfBitsTask(aCx
, aAlgorithm
, aKey
, aLength
);
3168 if (algName
.EqualsASCII(WEBCRYPTO_ALG_ECDH
)) {
3169 return new DeriveEcdhBitsTask(aCx
, aAlgorithm
, aKey
, aLength
);
3172 if (algName
.EqualsASCII(WEBCRYPTO_ALG_HKDF
)) {
3173 return new DeriveHkdfBitsTask(aCx
, aAlgorithm
, aKey
, aLength
);
3176 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
3179 WebCryptoTask
* WebCryptoTask::CreateWrapKeyTask(
3180 JSContext
* aCx
, const nsAString
& aFormat
, CryptoKey
& aKey
,
3181 CryptoKey
& aWrappingKey
, const ObjectOrString
& aWrapAlgorithm
) {
3182 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD
, TM_WRAPKEY
);
3184 // Verify that the format is recognized
3185 if (!aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW
) &&
3186 !aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI
) &&
3187 !aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8
) &&
3188 !aFormat
.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK
)) {
3189 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR
);
3192 // Ensure wrappingKey is usable for this operation
3193 if (!aWrappingKey
.HasUsage(CryptoKey::WRAPKEY
)) {
3194 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
3197 // Ensure key is extractable
3198 if (!aKey
.Extractable()) {
3199 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
3202 nsString wrapAlgName
;
3203 nsresult rv
= GetAlgorithmName(aCx
, aWrapAlgorithm
, wrapAlgName
);
3204 if (NS_FAILED(rv
)) {
3205 return new FailureTask(rv
);
3208 if (wrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC
) ||
3209 wrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR
) ||
3210 wrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM
)) {
3211 return new WrapKeyTask
<AesTask
>(aCx
, aFormat
, aKey
, aWrappingKey
,
3213 } else if (wrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_KW
)) {
3214 return new WrapKeyTask
<AesKwTask
>(aCx
, aFormat
, aKey
, aWrappingKey
,
3216 } else if (wrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP
)) {
3217 return new WrapKeyTask
<RsaOaepTask
>(aCx
, aFormat
, aKey
, aWrappingKey
,
3221 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
3224 WebCryptoTask
* WebCryptoTask::CreateUnwrapKeyTask(
3225 nsIGlobalObject
* aGlobal
, JSContext
* aCx
, const nsAString
& aFormat
,
3226 const ArrayBufferViewOrArrayBuffer
& aWrappedKey
, CryptoKey
& aUnwrappingKey
,
3227 const ObjectOrString
& aUnwrapAlgorithm
,
3228 const ObjectOrString
& aUnwrappedKeyAlgorithm
, bool aExtractable
,
3229 const Sequence
<nsString
>& aKeyUsages
) {
3230 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD
, TM_UNWRAPKEY
);
3232 // Ensure key is usable for this operation
3233 if (!aUnwrappingKey
.HasUsage(CryptoKey::UNWRAPKEY
)) {
3234 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
3237 // Verify that aKeyUsages does not contain an unrecognized value
3238 if (!CryptoKey::AllUsagesRecognized(aKeyUsages
)) {
3239 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR
);
3242 nsString keyAlgName
;
3243 nsresult rv
= GetAlgorithmName(aCx
, aUnwrappedKeyAlgorithm
, keyAlgName
);
3244 if (NS_FAILED(rv
)) {
3245 return new FailureTask(rv
);
3248 CryptoOperationData dummy
;
3249 RefPtr
<ImportKeyTask
> importTask
;
3250 if (keyAlgName
.EqualsASCII(WEBCRYPTO_ALG_AES_CBC
) ||
3251 keyAlgName
.EqualsASCII(WEBCRYPTO_ALG_AES_CTR
) ||
3252 keyAlgName
.EqualsASCII(WEBCRYPTO_ALG_AES_GCM
) ||
3253 keyAlgName
.EqualsASCII(WEBCRYPTO_ALG_AES_KW
) ||
3254 keyAlgName
.EqualsASCII(WEBCRYPTO_ALG_HKDF
) ||
3255 keyAlgName
.EqualsASCII(WEBCRYPTO_ALG_HMAC
)) {
3256 importTask
= new ImportSymmetricKeyTask(aGlobal
, aCx
, aFormat
,
3257 aUnwrappedKeyAlgorithm
,
3258 aExtractable
, aKeyUsages
);
3259 } else if (keyAlgName
.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
3260 keyAlgName
.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP
) ||
3261 keyAlgName
.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS
)) {
3263 new ImportRsaKeyTask(aGlobal
, aCx
, aFormat
, aUnwrappedKeyAlgorithm
,
3264 aExtractable
, aKeyUsages
);
3265 } else if (keyAlgName
.EqualsLiteral(WEBCRYPTO_ALG_ECDH
) ||
3266 keyAlgName
.EqualsLiteral(WEBCRYPTO_ALG_ECDSA
)) {
3268 new ImportEcKeyTask(aGlobal
, aCx
, aFormat
, aUnwrappedKeyAlgorithm
,
3269 aExtractable
, aKeyUsages
);
3271 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
3274 nsString unwrapAlgName
;
3275 rv
= GetAlgorithmName(aCx
, aUnwrapAlgorithm
, unwrapAlgName
);
3276 if (NS_FAILED(rv
)) {
3277 return new FailureTask(rv
);
3279 if (unwrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC
) ||
3280 unwrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR
) ||
3281 unwrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM
)) {
3282 return new UnwrapKeyTask
<AesTask
>(aCx
, aWrappedKey
, aUnwrappingKey
,
3283 aUnwrapAlgorithm
, importTask
);
3284 } else if (unwrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_AES_KW
)) {
3285 return new UnwrapKeyTask
<AesKwTask
>(aCx
, aWrappedKey
, aUnwrappingKey
,
3286 aUnwrapAlgorithm
, importTask
);
3287 } else if (unwrapAlgName
.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP
)) {
3288 return new UnwrapKeyTask
<RsaOaepTask
>(aCx
, aWrappedKey
, aUnwrappingKey
,
3289 aUnwrapAlgorithm
, importTask
);
3292 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
3295 WebCryptoTask::WebCryptoTask()
3296 : CancelableRunnable("WebCryptoTask"),
3298 mEarlyComplete(false),
3299 mOriginalEventTarget(nullptr),
3300 mRv(NS_ERROR_NOT_INITIALIZED
) {}
3302 WebCryptoTask::~WebCryptoTask() = default;
3304 } // namespace mozilla::dom