1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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/. */
9 #include "ScopedNSSTypes.h"
10 #include "mozilla/dom/CryptoKey.h"
11 #include "mozilla/dom/WebCryptoCommon.h"
12 #include "mozilla/dom/SubtleCryptoBinding.h"
13 #include "mozilla/dom/ToJSValue.h"
18 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey
, mGlobal
)
19 NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKey
)
20 NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKey
)
21 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKey
)
22 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
23 NS_INTERFACE_MAP_ENTRY(nsISupports
)
27 StringToUsage(const nsString
& aUsage
, CryptoKey::KeyUsage
& aUsageOut
)
29 if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_ENCRYPT
)) {
30 aUsageOut
= CryptoKey::ENCRYPT
;
31 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DECRYPT
)) {
32 aUsageOut
= CryptoKey::DECRYPT
;
33 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_SIGN
)) {
34 aUsageOut
= CryptoKey::SIGN
;
35 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_VERIFY
)) {
36 aUsageOut
= CryptoKey::VERIFY
;
37 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEKEY
)) {
38 aUsageOut
= CryptoKey::DERIVEKEY
;
39 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEBITS
)) {
40 aUsageOut
= CryptoKey::DERIVEBITS
;
41 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_WRAPKEY
)) {
42 aUsageOut
= CryptoKey::WRAPKEY
;
43 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_UNWRAPKEY
)) {
44 aUsageOut
= CryptoKey::UNWRAPKEY
;
46 return NS_ERROR_DOM_SYNTAX_ERR
;
51 CryptoKey::CryptoKey(nsIGlobalObject
* aGlobal
)
55 , mPrivateKey(nullptr)
61 CryptoKey::~CryptoKey()
63 nsNSSShutDownPreventionLock locker
;
64 if (isAlreadyShutDown()) {
67 destructorSafeDestroyNSSReference();
68 shutdown(calledFromObject
);
72 CryptoKey::WrapObject(JSContext
* aCx
)
74 return CryptoKeyBinding::Wrap(aCx
, this);
78 CryptoKey::GetType(nsString
& aRetVal
) const
80 uint32_t type
= mAttributes
& TYPE_MASK
;
82 case PUBLIC
: aRetVal
.AssignLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC
); break;
83 case PRIVATE
: aRetVal
.AssignLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE
); break;
84 case SECRET
: aRetVal
.AssignLiteral(WEBCRYPTO_KEY_TYPE_SECRET
); break;
89 CryptoKey::Extractable() const
91 return (mAttributes
& EXTRACTABLE
);
95 CryptoKey::GetAlgorithm(JSContext
* cx
, JS::MutableHandle
<JSObject
*> aRetVal
,
96 ErrorResult
& aRv
) const
98 bool converted
= false;
99 JS::RootedValue
val(cx
);
100 switch (mAlgorithm
.mType
) {
101 case KeyAlgorithmProxy::AES
:
102 converted
= ToJSValue(cx
, mAlgorithm
.mAes
, &val
);
104 case KeyAlgorithmProxy::HMAC
:
105 converted
= ToJSValue(cx
, mAlgorithm
.mHmac
, &val
);
107 case KeyAlgorithmProxy::RSA
: {
108 RootedDictionary
<RsaHashedKeyAlgorithm
> rsa(cx
);
109 mAlgorithm
.mRsa
.ToKeyAlgorithm(cx
, rsa
);
110 converted
= ToJSValue(cx
, rsa
, &val
);
113 case KeyAlgorithmProxy::EC
:
114 converted
= ToJSValue(cx
, mAlgorithm
.mEc
, &val
);
118 aRv
.Throw(NS_ERROR_DOM_OPERATION_ERR
);
122 aRetVal
.set(&val
.toObject());
126 CryptoKey::GetUsages(nsTArray
<nsString
>& aRetVal
) const
128 if (mAttributes
& ENCRYPT
) {
129 aRetVal
.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_ENCRYPT
));
131 if (mAttributes
& DECRYPT
) {
132 aRetVal
.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DECRYPT
));
134 if (mAttributes
& SIGN
) {
135 aRetVal
.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_SIGN
));
137 if (mAttributes
& VERIFY
) {
138 aRetVal
.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_VERIFY
));
140 if (mAttributes
& DERIVEKEY
) {
141 aRetVal
.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEKEY
));
143 if (mAttributes
& DERIVEBITS
) {
144 aRetVal
.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEBITS
));
146 if (mAttributes
& WRAPKEY
) {
147 aRetVal
.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_WRAPKEY
));
149 if (mAttributes
& UNWRAPKEY
) {
150 aRetVal
.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_UNWRAPKEY
));
155 CryptoKey::Algorithm()
160 const KeyAlgorithmProxy
&
161 CryptoKey::Algorithm() const
167 CryptoKey::GetKeyType() const
169 return static_cast<CryptoKey::KeyType
>(mAttributes
& TYPE_MASK
);
173 CryptoKey::SetType(const nsString
& aType
)
175 mAttributes
&= CLEAR_TYPE
;
176 if (aType
.EqualsLiteral(WEBCRYPTO_KEY_TYPE_SECRET
)) {
177 mAttributes
|= SECRET
;
178 } else if (aType
.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC
)) {
179 mAttributes
|= PUBLIC
;
180 } else if (aType
.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE
)) {
181 mAttributes
|= PRIVATE
;
183 mAttributes
|= UNKNOWN
;
184 return NS_ERROR_DOM_SYNTAX_ERR
;
191 CryptoKey::SetType(CryptoKey::KeyType aType
)
193 mAttributes
&= CLEAR_TYPE
;
194 mAttributes
|= aType
;
198 CryptoKey::SetExtractable(bool aExtractable
)
200 mAttributes
&= CLEAR_EXTRACTABLE
;
202 mAttributes
|= EXTRACTABLE
;
207 CryptoKey::ClearUsages()
209 mAttributes
&= CLEAR_USAGES
;
213 CryptoKey::AddUsage(const nsString
& aUsage
)
215 return AddUsageIntersecting(aUsage
, USAGES_MASK
);
219 CryptoKey::AddUsageIntersecting(const nsString
& aUsage
, uint32_t aUsageMask
)
222 if (NS_FAILED(StringToUsage(aUsage
, usage
))) {
223 return NS_ERROR_DOM_SYNTAX_ERR
;
226 if (usage
& aUsageMask
) {
235 CryptoKey::AddUsage(CryptoKey::KeyUsage aUsage
)
237 mAttributes
|= aUsage
;
241 CryptoKey::HasAnyUsage()
243 return !!(mAttributes
& USAGES_MASK
);
247 CryptoKey::HasUsage(CryptoKey::KeyUsage aUsage
)
249 return !!(mAttributes
& aUsage
);
253 CryptoKey::HasUsageOtherThan(uint32_t aUsages
)
255 return !!(mAttributes
& USAGES_MASK
& ~aUsages
);
259 CryptoKey::IsRecognizedUsage(const nsString
& aUsage
)
262 nsresult rv
= StringToUsage(aUsage
, dummy
);
263 return NS_SUCCEEDED(rv
);
267 CryptoKey::AllUsagesRecognized(const Sequence
<nsString
>& aUsages
)
269 for (uint32_t i
= 0; i
< aUsages
.Length(); ++i
) {
270 if (!IsRecognizedUsage(aUsages
[i
])) {
277 void CryptoKey::SetSymKey(const CryptoBuffer
& aSymKey
)
283 CryptoKey::SetPrivateKey(SECKEYPrivateKey
* aPrivateKey
)
285 nsNSSShutDownPreventionLock locker
;
286 if (!aPrivateKey
|| isAlreadyShutDown()) {
287 mPrivateKey
= nullptr;
290 mPrivateKey
= SECKEY_CopyPrivateKey(aPrivateKey
);
294 CryptoKey::SetPublicKey(SECKEYPublicKey
* aPublicKey
)
296 nsNSSShutDownPreventionLock locker
;
297 if (!aPublicKey
|| isAlreadyShutDown()) {
298 mPublicKey
= nullptr;
301 mPublicKey
= SECKEY_CopyPublicKey(aPublicKey
);
305 CryptoKey::GetSymKey() const
311 CryptoKey::GetPrivateKey() const
313 nsNSSShutDownPreventionLock locker
;
314 if (!mPrivateKey
|| isAlreadyShutDown()) {
317 return SECKEY_CopyPrivateKey(mPrivateKey
.get());
321 CryptoKey::GetPublicKey() const
323 nsNSSShutDownPreventionLock locker
;
324 if (!mPublicKey
|| isAlreadyShutDown()) {
327 return SECKEY_CopyPublicKey(mPublicKey
.get());
330 void CryptoKey::virtualDestroyNSSReference()
332 destructorSafeDestroyNSSReference();
335 void CryptoKey::destructorSafeDestroyNSSReference()
337 mPrivateKey
.dispose();
338 mPublicKey
.dispose();
342 // Serialization and deserialization convenience methods
345 CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer
& aKeyData
,
346 const nsNSSShutDownPreventionLock
& /*proofOfLock*/)
348 SECKEYPrivateKey
* privKey
;
349 ScopedPK11SlotInfo
slot(PK11_GetInternalSlot());
350 ScopedSECItem
pkcs8Item(aKeyData
.ToSECItem());
355 // Allow everything, we enforce usage ourselves
356 unsigned int usage
= KU_ALL
;
358 SECStatus rv
= PK11_ImportDERPrivateKeyInfoAndReturnKey(
359 slot
.get(), pkcs8Item
.get(), nullptr, nullptr, false, false,
360 usage
, &privKey
, nullptr);
362 if (rv
== SECFailure
) {
369 CryptoKey::PublicKeyFromSpki(CryptoBuffer
& aKeyData
,
370 const nsNSSShutDownPreventionLock
& /*proofOfLock*/)
372 ScopedSECItem
spkiItem(aKeyData
.ToSECItem());
377 ScopedCERTSubjectPublicKeyInfo
spki(SECKEY_DecodeDERSubjectPublicKeyInfo(spkiItem
.get()));
382 // Check for id-ecDH. Per the WebCrypto spec we must support it but NSS
383 // does unfortunately not know about it. Let's change the algorithm to
384 // id-ecPublicKey to make NSS happy.
385 if (SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH
, &spki
->algorithm
.algorithm
)) {
386 // Retrieve OID data for id-ecPublicKey (1.2.840.10045.2.1).
387 SECOidData
* oidData
= SECOID_FindOIDByTag(SEC_OID_ANSIX962_EC_PUBLIC_KEY
);
392 SECStatus rv
= SECITEM_CopyItem(spki
->arena
, &spki
->algorithm
.algorithm
,
394 if (rv
!= SECSuccess
) {
399 return SECKEY_ExtractPublicKey(spki
.get());
403 CryptoKey::PrivateKeyToPkcs8(SECKEYPrivateKey
* aPrivKey
,
404 CryptoBuffer
& aRetVal
,
405 const nsNSSShutDownPreventionLock
& /*proofOfLock*/)
407 ScopedSECItem
pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey
, nullptr));
408 if (!pkcs8Item
.get()) {
409 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
411 aRetVal
.Assign(pkcs8Item
.get());
416 CryptoKey::PublicKeyToSpki(SECKEYPublicKey
* aPubKey
,
417 CryptoBuffer
& aRetVal
,
418 const nsNSSShutDownPreventionLock
& /*proofOfLock*/)
420 ScopedCERTSubjectPublicKeyInfo
spki(SECKEY_CreateSubjectPublicKeyInfo(aPubKey
));
422 return NS_ERROR_DOM_OPERATION_ERR
;
425 // Per WebCrypto spec we must export ECDH SPKIs with the algorithm OID
426 // id-ecDH (1.3.132.112). NSS doesn't know about that OID and there is
427 // no way to specify the algorithm to use when exporting a public key.
428 if (aPubKey
->keyType
== ecKey
) {
429 SECStatus rv
= SECITEM_CopyItem(spki
->arena
, &spki
->algorithm
.algorithm
,
430 &SEC_OID_DATA_EC_DH
);
431 if (rv
!= SECSuccess
) {
432 return NS_ERROR_DOM_OPERATION_ERR
;
436 const SEC_ASN1Template
* tpl
= SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate
);
437 ScopedSECItem
spkiItem(SEC_ASN1EncodeItem(nullptr, nullptr, spki
, tpl
));
439 aRetVal
.Assign(spkiItem
.get());
444 CreateECPointForCoordinates(const CryptoBuffer
& aX
,
445 const CryptoBuffer
& aY
,
448 // Check that both points have the same length.
449 if (aX
.Length() != aY
.Length()) {
454 SECItem
* point
= ::SECITEM_AllocItem(aArena
, nullptr, aX
.Length() + aY
.Length() + 1);
460 point
->data
[0] = EC_POINT_FORM_UNCOMPRESSED
;
461 memcpy(point
->data
+ 1, aX
.Elements(), aX
.Length());
462 memcpy(point
->data
+ 1 + aX
.Length(), aY
.Elements(), aY
.Length());
468 PrivateKeyFromPrivateKeyTemplate(SECItem
* aObjID
,
469 CK_ATTRIBUTE
* aTemplate
,
470 CK_ULONG aTemplateSize
)
472 // Create a generic object with the contents of the key
473 ScopedPK11SlotInfo
slot(PK11_GetInternalSlot());
478 ScopedPK11GenericObject
obj(PK11_CreateGenericObject(slot
.get(),
486 // Have NSS translate the object to a private key by inspection
487 // and make a copy we can own
488 ScopedSECKEYPrivateKey
privKey(PK11_FindKeyByKeyID(slot
.get(), aObjID
,
490 if (!privKey
.get()) {
494 return SECKEY_CopyPrivateKey(privKey
.get());
498 CryptoKey::PrivateKeyFromJwk(const JsonWebKey
& aJwk
,
499 const nsNSSShutDownPreventionLock
& /*proofOfLock*/)
501 CK_OBJECT_CLASS privateKeyValue
= CKO_PRIVATE_KEY
;
502 CK_BBOOL falseValue
= CK_FALSE
;
504 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_EC
)) {
505 // Verify that all of the required parameters are present
506 CryptoBuffer x
, y
, d
;
507 if (!aJwk
.mCrv
.WasPassed() ||
508 !aJwk
.mX
.WasPassed() || NS_FAILED(x
.FromJwkBase64(aJwk
.mX
.Value())) ||
509 !aJwk
.mY
.WasPassed() || NS_FAILED(y
.FromJwkBase64(aJwk
.mY
.Value())) ||
510 !aJwk
.mD
.WasPassed() || NS_FAILED(d
.FromJwkBase64(aJwk
.mD
.Value()))) {
515 if (!NormalizeToken(aJwk
.mCrv
.Value(), namedCurve
)) {
519 ScopedPLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
524 // Create parameters.
525 SECItem
* params
= CreateECParamsForCurve(namedCurve
, arena
.get());
530 SECItem
* ecPoint
= CreateECPointForCoordinates(x
, y
, arena
.get());
535 // Compute the ID for this key
536 // This is generated with a SHA-1 hash, so unlikely to collide
537 ScopedSECItem
objID(PK11_MakeIDFromPubKey(ecPoint
));
542 // Populate template from parameters
543 CK_KEY_TYPE ecValue
= CKK_EC
;
544 CK_ATTRIBUTE keyTemplate
[9] = {
545 { CKA_CLASS
, &privateKeyValue
, sizeof(privateKeyValue
) },
546 { CKA_KEY_TYPE
, &ecValue
, sizeof(ecValue
) },
547 { CKA_TOKEN
, &falseValue
, sizeof(falseValue
) },
548 { CKA_SENSITIVE
, &falseValue
, sizeof(falseValue
) },
549 { CKA_PRIVATE
, &falseValue
, sizeof(falseValue
) },
550 { CKA_ID
, objID
->data
, objID
->len
},
551 { CKA_EC_PARAMS
, params
->data
, params
->len
},
552 { CKA_EC_POINT
, ecPoint
->data
, ecPoint
->len
},
553 { CKA_VALUE
, (void*) d
.Elements(), d
.Length() },
556 return PrivateKeyFromPrivateKeyTemplate(objID
, keyTemplate
,
557 PR_ARRAY_SIZE(keyTemplate
));
560 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_RSA
)) {
561 // Verify that all of the required parameters are present
562 CryptoBuffer n
, e
, d
, p
, q
, dp
, dq
, qi
;
563 if (!aJwk
.mN
.WasPassed() || NS_FAILED(n
.FromJwkBase64(aJwk
.mN
.Value())) ||
564 !aJwk
.mE
.WasPassed() || NS_FAILED(e
.FromJwkBase64(aJwk
.mE
.Value())) ||
565 !aJwk
.mD
.WasPassed() || NS_FAILED(d
.FromJwkBase64(aJwk
.mD
.Value())) ||
566 !aJwk
.mP
.WasPassed() || NS_FAILED(p
.FromJwkBase64(aJwk
.mP
.Value())) ||
567 !aJwk
.mQ
.WasPassed() || NS_FAILED(q
.FromJwkBase64(aJwk
.mQ
.Value())) ||
568 !aJwk
.mDp
.WasPassed() || NS_FAILED(dp
.FromJwkBase64(aJwk
.mDp
.Value())) ||
569 !aJwk
.mDq
.WasPassed() || NS_FAILED(dq
.FromJwkBase64(aJwk
.mDq
.Value())) ||
570 !aJwk
.mQi
.WasPassed() || NS_FAILED(qi
.FromJwkBase64(aJwk
.mQi
.Value()))) {
574 // Compute the ID for this key
575 // This is generated with a SHA-1 hash, so unlikely to collide
576 ScopedSECItem
nItem(n
.ToSECItem());
581 ScopedSECItem
objID(PK11_MakeIDFromPubKey(nItem
.get()));
586 // Populate template from parameters
587 CK_KEY_TYPE rsaValue
= CKK_RSA
;
588 CK_ATTRIBUTE keyTemplate
[14] = {
589 { CKA_CLASS
, &privateKeyValue
, sizeof(privateKeyValue
) },
590 { CKA_KEY_TYPE
, &rsaValue
, sizeof(rsaValue
) },
591 { CKA_TOKEN
, &falseValue
, sizeof(falseValue
) },
592 { CKA_SENSITIVE
, &falseValue
, sizeof(falseValue
) },
593 { CKA_PRIVATE
, &falseValue
, sizeof(falseValue
) },
594 { CKA_ID
, objID
->data
, objID
->len
},
595 { CKA_MODULUS
, (void*) n
.Elements(), n
.Length() },
596 { CKA_PUBLIC_EXPONENT
, (void*) e
.Elements(), e
.Length() },
597 { CKA_PRIVATE_EXPONENT
, (void*) d
.Elements(), d
.Length() },
598 { CKA_PRIME_1
, (void*) p
.Elements(), p
.Length() },
599 { CKA_PRIME_2
, (void*) q
.Elements(), q
.Length() },
600 { CKA_EXPONENT_1
, (void*) dp
.Elements(), dp
.Length() },
601 { CKA_EXPONENT_2
, (void*) dq
.Elements(), dq
.Length() },
602 { CKA_COEFFICIENT
, (void*) qi
.Elements(), qi
.Length() },
605 return PrivateKeyFromPrivateKeyTemplate(objID
, keyTemplate
,
606 PR_ARRAY_SIZE(keyTemplate
));
612 bool ReadAndEncodeAttribute(SECKEYPrivateKey
* aKey
,
613 CK_ATTRIBUTE_TYPE aAttribute
,
614 Optional
<nsString
>& aDst
)
616 ScopedSECItem
item(::SECITEM_AllocItem(nullptr, nullptr, 0));
621 if (PK11_ReadRawAttribute(PK11_TypePrivKey
, aKey
, aAttribute
, item
)
627 if (!buffer
.Assign(item
)) {
631 if (NS_FAILED(buffer
.ToJwkBase64(aDst
.Value()))) {
639 ECKeyToJwk(const PK11ObjectType aKeyType
, void* aKey
, const SECItem
* aEcParams
,
640 const SECItem
* aPublicValue
, JsonWebKey
& aRetVal
)
642 aRetVal
.mX
.Construct();
643 aRetVal
.mY
.Construct();
645 // Check that the given EC parameters are valid.
646 if (!CheckEncodedECParameters(aEcParams
)) {
650 // Construct the OID tag.
651 SECItem oid
= { siBuffer
, nullptr, 0 };
652 oid
.len
= aEcParams
->data
[1];
653 oid
.data
= aEcParams
->data
+ 2;
656 switch (SECOID_FindOIDTag(&oid
)) {
657 case SEC_OID_SECG_EC_SECP256R1
:
659 aRetVal
.mCrv
.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P256
));
661 case SEC_OID_SECG_EC_SECP384R1
:
663 aRetVal
.mCrv
.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P384
));
665 case SEC_OID_SECG_EC_SECP521R1
:
667 aRetVal
.mCrv
.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P521
));
673 // No support for compressed points.
674 if (aPublicValue
->data
[0] != EC_POINT_FORM_UNCOMPRESSED
) {
678 // Check length of uncompressed point coordinates.
679 if (aPublicValue
->len
!= (2 * flen
+ 1)) {
683 ScopedSECItem
ecPointX(::SECITEM_AllocItem(nullptr, nullptr, flen
));
684 ScopedSECItem
ecPointY(::SECITEM_AllocItem(nullptr, nullptr, flen
));
685 if (!ecPointX
|| !ecPointY
) {
689 // Extract point data.
690 memcpy(ecPointX
->data
, aPublicValue
->data
+ 1, flen
);
691 memcpy(ecPointY
->data
, aPublicValue
->data
+ 1 + flen
, flen
);
694 if (!x
.Assign(ecPointX
) || NS_FAILED(x
.ToJwkBase64(aRetVal
.mX
.Value())) ||
695 !y
.Assign(ecPointY
) || NS_FAILED(y
.ToJwkBase64(aRetVal
.mY
.Value()))) {
699 aRetVal
.mKty
= NS_LITERAL_STRING(JWK_TYPE_EC
);
704 CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey
* aPrivKey
,
706 const nsNSSShutDownPreventionLock
& /*proofOfLock*/)
708 switch (aPrivKey
->keyType
) {
710 aRetVal
.mN
.Construct();
711 aRetVal
.mE
.Construct();
712 aRetVal
.mD
.Construct();
713 aRetVal
.mP
.Construct();
714 aRetVal
.mQ
.Construct();
715 aRetVal
.mDp
.Construct();
716 aRetVal
.mDq
.Construct();
717 aRetVal
.mQi
.Construct();
719 if (!ReadAndEncodeAttribute(aPrivKey
, CKA_MODULUS
, aRetVal
.mN
) ||
720 !ReadAndEncodeAttribute(aPrivKey
, CKA_PUBLIC_EXPONENT
, aRetVal
.mE
) ||
721 !ReadAndEncodeAttribute(aPrivKey
, CKA_PRIVATE_EXPONENT
, aRetVal
.mD
) ||
722 !ReadAndEncodeAttribute(aPrivKey
, CKA_PRIME_1
, aRetVal
.mP
) ||
723 !ReadAndEncodeAttribute(aPrivKey
, CKA_PRIME_2
, aRetVal
.mQ
) ||
724 !ReadAndEncodeAttribute(aPrivKey
, CKA_EXPONENT_1
, aRetVal
.mDp
) ||
725 !ReadAndEncodeAttribute(aPrivKey
, CKA_EXPONENT_2
, aRetVal
.mDq
) ||
726 !ReadAndEncodeAttribute(aPrivKey
, CKA_COEFFICIENT
, aRetVal
.mQi
)) {
727 return NS_ERROR_DOM_OPERATION_ERR
;
730 aRetVal
.mKty
= NS_LITERAL_STRING(JWK_TYPE_RSA
);
735 ScopedSECItem
params(::SECITEM_AllocItem(nullptr, nullptr, 0));
736 SECStatus rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, aPrivKey
,
737 CKA_EC_PARAMS
, params
);
738 if (rv
!= SECSuccess
) {
739 return NS_ERROR_DOM_OPERATION_ERR
;
742 // Read public point Q.
743 ScopedSECItem
ecPoint(::SECITEM_AllocItem(nullptr, nullptr, 0));
744 rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, aPrivKey
, CKA_EC_POINT
,
746 if (rv
!= SECSuccess
) {
747 return NS_ERROR_DOM_OPERATION_ERR
;
750 if (!ECKeyToJwk(PK11_TypePrivKey
, aPrivKey
, params
, ecPoint
, aRetVal
)) {
751 return NS_ERROR_DOM_OPERATION_ERR
;
754 aRetVal
.mD
.Construct();
756 // Read private value.
757 if (!ReadAndEncodeAttribute(aPrivKey
, CKA_VALUE
, aRetVal
.mD
)) {
758 return NS_ERROR_DOM_OPERATION_ERR
;
764 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
769 CryptoKey::PublicKeyFromJwk(const JsonWebKey
& aJwk
,
770 const nsNSSShutDownPreventionLock
& /*proofOfLock*/)
772 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_RSA
)) {
773 // Verify that all of the required parameters are present
775 if (!aJwk
.mN
.WasPassed() || NS_FAILED(n
.FromJwkBase64(aJwk
.mN
.Value())) ||
776 !aJwk
.mE
.WasPassed() || NS_FAILED(e
.FromJwkBase64(aJwk
.mE
.Value()))) {
780 // Transcode to a DER RSAPublicKey structure
781 struct RSAPublicKeyData
{
785 const RSAPublicKeyData input
= {
786 { siUnsignedInteger
, n
.Elements(), (unsigned int) n
.Length() },
787 { siUnsignedInteger
, e
.Elements(), (unsigned int) e
.Length() }
789 const SEC_ASN1Template rsaPublicKeyTemplate
[] = {
790 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(RSAPublicKeyData
)},
791 {SEC_ASN1_INTEGER
, offsetof(RSAPublicKeyData
, n
),},
792 {SEC_ASN1_INTEGER
, offsetof(RSAPublicKeyData
, e
),},
796 ScopedSECItem
pkDer(SEC_ASN1EncodeItem(nullptr, nullptr, &input
,
797 rsaPublicKeyTemplate
));
802 return SECKEY_ImportDERPublicKey(pkDer
.get(), CKK_RSA
);
805 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_EC
)) {
806 // Verify that all of the required parameters are present
808 if (!aJwk
.mCrv
.WasPassed() ||
809 !aJwk
.mX
.WasPassed() || NS_FAILED(x
.FromJwkBase64(aJwk
.mX
.Value())) ||
810 !aJwk
.mY
.WasPassed() || NS_FAILED(y
.FromJwkBase64(aJwk
.mY
.Value()))) {
814 ScopedPLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
819 SECKEYPublicKey
* key
= PORT_ArenaZNew(arena
, SECKEYPublicKey
);
824 key
->keyType
= ecKey
;
825 key
->pkcs11Slot
= nullptr;
826 key
->pkcs11ID
= CK_INVALID_HANDLE
;
829 if (!NormalizeToken(aJwk
.mCrv
.Value(), namedCurve
)) {
833 // Create parameters.
834 SECItem
* params
= CreateECParamsForCurve(namedCurve
, arena
.get());
838 key
->u
.ec
.DEREncodedParams
= *params
;
841 SECItem
* point
= CreateECPointForCoordinates(x
, y
, arena
.get());
845 key
->u
.ec
.publicValue
= *point
;
847 return SECKEY_CopyPublicKey(key
);
854 CryptoKey::PublicKeyToJwk(SECKEYPublicKey
* aPubKey
,
856 const nsNSSShutDownPreventionLock
& /*proofOfLock*/)
858 switch (aPubKey
->keyType
) {
861 aRetVal
.mN
.Construct();
862 aRetVal
.mE
.Construct();
864 if (!n
.Assign(&aPubKey
->u
.rsa
.modulus
) ||
865 !e
.Assign(&aPubKey
->u
.rsa
.publicExponent
) ||
866 NS_FAILED(n
.ToJwkBase64(aRetVal
.mN
.Value())) ||
867 NS_FAILED(e
.ToJwkBase64(aRetVal
.mE
.Value()))) {
868 return NS_ERROR_DOM_OPERATION_ERR
;
871 aRetVal
.mKty
= NS_LITERAL_STRING(JWK_TYPE_RSA
);
875 if (!ECKeyToJwk(PK11_TypePubKey
, aPubKey
, &aPubKey
->u
.ec
.DEREncodedParams
,
876 &aPubKey
->u
.ec
.publicValue
, aRetVal
)) {
877 return NS_ERROR_DOM_OPERATION_ERR
;
881 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
886 CryptoKey::WriteStructuredClone(JSStructuredCloneWriter
* aWriter
) const
888 nsNSSShutDownPreventionLock locker
;
889 if (isAlreadyShutDown()) {
893 // Write in five pieces
895 // 2. Symmetric key as raw (if present)
896 // 3. Private key as pkcs8 (if present)
897 // 4. Public key as spki (if present)
898 // 5. Algorithm in whatever form it chooses
899 CryptoBuffer priv
, pub
;
902 CryptoKey::PrivateKeyToPkcs8(mPrivateKey
, priv
, locker
);
906 CryptoKey::PublicKeyToSpki(mPublicKey
, pub
, locker
);
909 return JS_WriteUint32Pair(aWriter
, mAttributes
, CRYPTOKEY_SC_VERSION
) &&
910 WriteBuffer(aWriter
, mSymKey
) &&
911 WriteBuffer(aWriter
, priv
) &&
912 WriteBuffer(aWriter
, pub
) &&
913 mAlgorithm
.WriteStructuredClone(aWriter
);
917 CryptoKey::ReadStructuredClone(JSStructuredCloneReader
* aReader
)
919 nsNSSShutDownPreventionLock locker
;
920 if (isAlreadyShutDown()) {
925 CryptoBuffer sym
, priv
, pub
;
927 bool read
= JS_ReadUint32Pair(aReader
, &mAttributes
, &version
) &&
928 (version
== CRYPTOKEY_SC_VERSION
) &&
929 ReadBuffer(aReader
, sym
) &&
930 ReadBuffer(aReader
, priv
) &&
931 ReadBuffer(aReader
, pub
) &&
932 mAlgorithm
.ReadStructuredClone(aReader
);
937 if (sym
.Length() > 0) {
940 if (priv
.Length() > 0) {
941 mPrivateKey
= CryptoKey::PrivateKeyFromPkcs8(priv
, locker
);
943 if (pub
.Length() > 0) {
944 mPublicKey
= CryptoKey::PublicKeyFromSpki(pub
, locker
);
947 // Ensure that what we've read is consistent
948 // If the attributes indicate a key type, should have a key of that type
949 if (!((GetKeyType() == SECRET
&& mSymKey
.Length() > 0) ||
950 (GetKeyType() == PRIVATE
&& mPrivateKey
) ||
951 (GetKeyType() == PUBLIC
&& mPublicKey
))) {
959 } // namespace mozilla