1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/CryptoKey.h"
16 #include "js/StructuredClone.h"
17 #include "js/TypeDecls.h"
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/ErrorResult.h"
21 #include "mozilla/MacroForEach.h"
22 #include "mozilla/dom/KeyAlgorithmBinding.h"
23 #include "mozilla/dom/RootedDictionary.h"
24 #include "mozilla/dom/SubtleCryptoBinding.h"
25 #include "mozilla/dom/ToJSValue.h"
26 #include "mozilla/dom/WebCryptoCommon.h"
29 #include "nsLiteralString.h"
30 #include "nsNSSComponent.h"
31 #include "nsStringFlags.h"
46 namespace mozilla::dom
{
48 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey
, mGlobal
)
49 NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKey
)
50 NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKey
)
51 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKey
)
52 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
53 NS_INTERFACE_MAP_ENTRY(nsISupports
)
56 nsresult
StringToUsage(const nsString
& aUsage
, CryptoKey::KeyUsage
& aUsageOut
) {
57 if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_ENCRYPT
)) {
58 aUsageOut
= CryptoKey::ENCRYPT
;
59 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DECRYPT
)) {
60 aUsageOut
= CryptoKey::DECRYPT
;
61 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_SIGN
)) {
62 aUsageOut
= CryptoKey::SIGN
;
63 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_VERIFY
)) {
64 aUsageOut
= CryptoKey::VERIFY
;
65 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEKEY
)) {
66 aUsageOut
= CryptoKey::DERIVEKEY
;
67 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEBITS
)) {
68 aUsageOut
= CryptoKey::DERIVEBITS
;
69 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_WRAPKEY
)) {
70 aUsageOut
= CryptoKey::WRAPKEY
;
71 } else if (aUsage
.EqualsLiteral(WEBCRYPTO_KEY_USAGE_UNWRAPKEY
)) {
72 aUsageOut
= CryptoKey::UNWRAPKEY
;
74 return NS_ERROR_DOM_SYNTAX_ERR
;
79 // This helper function will release the memory backing a SECKEYPrivateKey and
80 // any resources acquired in its creation. It will leave the backing PKCS#11
81 // object untouched, however. This should only be called from
82 // PrivateKeyFromPrivateKeyTemplate.
83 static void DestroyPrivateKeyWithoutDestroyingPKCS11Object(
84 SECKEYPrivateKey
* key
) {
85 PK11_FreeSlot(key
->pkcs11Slot
);
86 PORT_FreeArena(key
->arena
, PR_TRUE
);
89 // To protect against key ID collisions, PrivateKeyFromPrivateKeyTemplate
90 // generates a random ID for each key. The given template must contain an
91 // attribute slot for a key ID, but it must consist of a null pointer and have a
93 UniqueSECKEYPrivateKey
PrivateKeyFromPrivateKeyTemplate(
94 CK_ATTRIBUTE
* aTemplate
, CK_ULONG aTemplateSize
) {
95 // Create a generic object with the contents of the key
96 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
101 // Generate a random 160-bit object ID. This ID must be unique.
102 UniqueSECItem
objID(::SECITEM_AllocItem(nullptr, nullptr, 20));
103 SECStatus rv
= PK11_GenerateRandomOnSlot(slot
.get(), objID
->data
, objID
->len
);
104 if (rv
!= SECSuccess
) {
107 // Check if something is already using this ID.
108 SECKEYPrivateKey
* preexistingKey
=
109 PK11_FindKeyByKeyID(slot
.get(), objID
.get(), nullptr);
110 if (preexistingKey
) {
111 // Note that we can't just call SECKEY_DestroyPrivateKey here because that
112 // will destroy the PKCS#11 object that is backing a preexisting key (that
113 // we still have a handle on somewhere else in memory). If that object were
114 // destroyed, cryptographic operations performed by that other key would
116 DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey
);
117 // Try again with a new ID (but only once - collisions are very unlikely).
118 rv
= PK11_GenerateRandomOnSlot(slot
.get(), objID
->data
, objID
->len
);
119 if (rv
!= SECSuccess
) {
122 preexistingKey
= PK11_FindKeyByKeyID(slot
.get(), objID
.get(), nullptr);
123 if (preexistingKey
) {
124 DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey
);
129 CK_ATTRIBUTE
* idAttributeSlot
= nullptr;
130 for (CK_ULONG i
= 0; i
< aTemplateSize
; i
++) {
131 if (aTemplate
[i
].type
== CKA_ID
) {
132 if (aTemplate
[i
].pValue
!= nullptr || aTemplate
[i
].ulValueLen
!= 0) {
135 idAttributeSlot
= aTemplate
+ i
;
139 if (!idAttributeSlot
) {
143 idAttributeSlot
->pValue
= objID
->data
;
144 idAttributeSlot
->ulValueLen
= objID
->len
;
145 UniquePK11GenericObject
obj(
146 PK11_CreateGenericObject(slot
.get(), aTemplate
, aTemplateSize
, PR_FALSE
));
147 // Unset the ID attribute slot's pointer and length so that data that only
148 // lives for the scope of this function doesn't escape.
149 idAttributeSlot
->pValue
= nullptr;
150 idAttributeSlot
->ulValueLen
= 0;
155 // Have NSS translate the object to a private key.
156 return UniqueSECKEYPrivateKey(
157 PK11_FindKeyByKeyID(slot
.get(), objID
.get(), nullptr));
160 CryptoKey::CryptoKey(nsIGlobalObject
* aGlobal
)
164 mPrivateKey(nullptr),
165 mPublicKey(nullptr) {}
167 JSObject
* CryptoKey::WrapObject(JSContext
* aCx
,
168 JS::Handle
<JSObject
*> aGivenProto
) {
169 return CryptoKey_Binding::Wrap(aCx
, this, aGivenProto
);
172 void CryptoKey::GetType(nsString
& aRetVal
) const {
173 uint32_t type
= mAttributes
& TYPE_MASK
;
176 aRetVal
.AssignLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC
);
179 aRetVal
.AssignLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE
);
182 aRetVal
.AssignLiteral(WEBCRYPTO_KEY_TYPE_SECRET
);
187 bool CryptoKey::Extractable() const { return (mAttributes
& EXTRACTABLE
); }
189 void CryptoKey::GetAlgorithm(JSContext
* cx
,
190 JS::MutableHandle
<JSObject
*> aRetVal
,
191 ErrorResult
& aRv
) const {
192 bool converted
= false;
193 JS::Rooted
<JS::Value
> val(cx
);
194 switch (mAlgorithm
.mType
) {
195 case KeyAlgorithmProxy::AES
:
196 converted
= ToJSValue(cx
, mAlgorithm
.mAes
, &val
);
198 case KeyAlgorithmProxy::KDF
:
199 converted
= ToJSValue(cx
, mAlgorithm
.mKDF
, &val
);
201 case KeyAlgorithmProxy::HMAC
:
202 converted
= ToJSValue(cx
, mAlgorithm
.mHmac
, &val
);
204 case KeyAlgorithmProxy::RSA
: {
205 RootedDictionary
<RsaHashedKeyAlgorithm
> rsa(cx
);
206 converted
= mAlgorithm
.mRsa
.ToKeyAlgorithm(cx
, rsa
, aRv
);
208 converted
= ToJSValue(cx
, rsa
, &val
);
212 case KeyAlgorithmProxy::EC
:
213 converted
= ToJSValue(cx
, mAlgorithm
.mEc
, &val
);
215 case KeyAlgorithmProxy::OKP
:
216 converted
= ToJSValue(cx
, mAlgorithm
.mEd
, &val
);
220 aRv
.Throw(NS_ERROR_DOM_OPERATION_ERR
);
224 aRetVal
.set(&val
.toObject());
227 void CryptoKey::GetUsages(nsTArray
<nsString
>& aRetVal
) const {
228 if (mAttributes
& ENCRYPT
) {
229 aRetVal
.AppendElement(
230 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_ENCRYPT
));
232 if (mAttributes
& DECRYPT
) {
233 aRetVal
.AppendElement(
234 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_DECRYPT
));
236 if (mAttributes
& SIGN
) {
237 aRetVal
.AppendElement(
238 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_SIGN
));
240 if (mAttributes
& VERIFY
) {
241 aRetVal
.AppendElement(
242 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_VERIFY
));
244 if (mAttributes
& DERIVEKEY
) {
245 aRetVal
.AppendElement(
246 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_DERIVEKEY
));
248 if (mAttributes
& DERIVEBITS
) {
249 aRetVal
.AppendElement(
250 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_DERIVEBITS
));
252 if (mAttributes
& WRAPKEY
) {
253 aRetVal
.AppendElement(
254 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_WRAPKEY
));
256 if (mAttributes
& UNWRAPKEY
) {
257 aRetVal
.AppendElement(
258 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_UNWRAPKEY
));
262 KeyAlgorithmProxy
& CryptoKey::Algorithm() { return mAlgorithm
; }
264 const KeyAlgorithmProxy
& CryptoKey::Algorithm() const { return mAlgorithm
; }
266 CryptoKey::KeyType
CryptoKey::GetKeyType() const {
267 return static_cast<CryptoKey::KeyType
>(mAttributes
& TYPE_MASK
);
270 nsresult
CryptoKey::SetType(const nsString
& aType
) {
271 mAttributes
&= CLEAR_TYPE
;
272 if (aType
.EqualsLiteral(WEBCRYPTO_KEY_TYPE_SECRET
)) {
273 mAttributes
|= SECRET
;
274 } else if (aType
.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC
)) {
275 mAttributes
|= PUBLIC
;
276 } else if (aType
.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE
)) {
277 mAttributes
|= PRIVATE
;
279 mAttributes
|= UNKNOWN
;
280 return NS_ERROR_DOM_SYNTAX_ERR
;
286 void CryptoKey::SetType(CryptoKey::KeyType aType
) {
287 mAttributes
&= CLEAR_TYPE
;
288 mAttributes
|= aType
;
291 void CryptoKey::SetExtractable(bool aExtractable
) {
292 mAttributes
&= CLEAR_EXTRACTABLE
;
294 mAttributes
|= EXTRACTABLE
;
298 // NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the
299 // public value. To properly export the private key to JWK or PKCS #8 we need
300 // the public key data though and so we use this method to augment a private
301 // key with data from the given public key.
302 nsresult
CryptoKey::AddPublicKeyData(SECKEYPublicKey
* aPublicKey
) {
303 // This should be a private key.
304 MOZ_ASSERT(GetKeyType() == PRIVATE
);
305 // There should be a private NSS key with type 'EC', 'EC Montgomery' or 'ED'.
306 MOZ_ASSERT(mPrivateKey
&&
307 (mPrivateKey
->keyType
== ecKey
|| mPrivateKey
->keyType
== edKey
||
308 mPrivateKey
->keyType
== ecMontKey
));
309 // The given public key should have the same key type.
310 MOZ_ASSERT(aPublicKey
->keyType
== mPrivateKey
->keyType
);
313 ScopedAutoSECItem params
;
314 SECStatus rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, mPrivateKey
.get(),
315 CKA_EC_PARAMS
, ¶ms
);
316 if (rv
!= SECSuccess
) {
317 return NS_ERROR_DOM_OPERATION_ERR
;
320 // Read private value.
321 ScopedAutoSECItem value
;
322 rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, mPrivateKey
.get(), CKA_VALUE
,
324 if (rv
!= SECSuccess
) {
325 return NS_ERROR_DOM_OPERATION_ERR
;
328 SECItem
* point
= &aPublicKey
->u
.ec
.publicValue
;
329 CK_OBJECT_CLASS privateKeyValue
= CKO_PRIVATE_KEY
;
330 CK_BBOOL falseValue
= CK_FALSE
;
332 // ecKey corresponds to CKK_EC;
333 // edKey corresponds to CKK_EC_EDWARDS key,
334 // ecMontKey corresponds to CKK_EC_MONTGOMERY.
335 // The other key types are not allowed.
337 if (mPrivateKey
->keyType
== ecKey
) {
339 } else if (mPrivateKey
->keyType
== edKey
) {
340 ecValue
= CKK_EC_EDWARDS
;
341 } else if (mPrivateKey
->keyType
== ecMontKey
) {
342 ecValue
= CKK_EC_MONTGOMERY
;
344 return NS_ERROR_DOM_OPERATION_ERR
;
347 CK_ATTRIBUTE keyTemplate
[9] = {
348 {CKA_CLASS
, &privateKeyValue
, sizeof(privateKeyValue
)},
349 {CKA_KEY_TYPE
, &ecValue
, sizeof(ecValue
)},
350 {CKA_TOKEN
, &falseValue
, sizeof(falseValue
)},
351 {CKA_SENSITIVE
, &falseValue
, sizeof(falseValue
)},
352 {CKA_PRIVATE
, &falseValue
, sizeof(falseValue
)},
353 // PrivateKeyFromPrivateKeyTemplate sets the ID.
354 {CKA_ID
, nullptr, 0},
355 {CKA_EC_PARAMS
, params
.data
, params
.len
},
356 {CKA_EC_POINT
, point
->data
, point
->len
},
357 {CKA_VALUE
, value
.data
, value
.len
},
361 PrivateKeyFromPrivateKeyTemplate(keyTemplate
, std::size(keyTemplate
));
362 NS_ENSURE_TRUE(mPrivateKey
, NS_ERROR_DOM_OPERATION_ERR
);
367 void CryptoKey::ClearUsages() { mAttributes
&= CLEAR_USAGES
; }
369 nsresult
CryptoKey::AddUsage(const nsString
& aUsage
) {
371 if (NS_FAILED(StringToUsage(aUsage
, usage
))) {
372 return NS_ERROR_DOM_SYNTAX_ERR
;
375 MOZ_ASSERT(usage
& USAGES_MASK
, "Usages should be valid");
377 // This is harmless if usage is 0, so we don't repeat the assertion check
382 nsresult
CryptoKey::AddAllowedUsage(const nsString
& aUsage
,
383 const nsString
& aAlgorithm
) {
384 return AddAllowedUsageIntersecting(aUsage
, aAlgorithm
, USAGES_MASK
);
387 nsresult
CryptoKey::AddAllowedUsageIntersecting(const nsString
& aUsage
,
388 const nsString
& aAlgorithm
,
389 uint32_t aUsageMask
) {
390 uint32_t allowedUsages
= GetAllowedUsagesForAlgorithm(aAlgorithm
);
392 if (NS_FAILED(StringToUsage(aUsage
, usage
))) {
393 return NS_ERROR_DOM_SYNTAX_ERR
;
396 if ((usage
& allowedUsages
) != usage
) {
397 return NS_ERROR_DOM_SYNTAX_ERR
;
400 MOZ_ASSERT(usage
& USAGES_MASK
, "Usages should be valid");
402 // This is harmless if usage is 0, so we don't repeat the assertion check
403 if (usage
& aUsageMask
) {
411 void CryptoKey::AddUsage(CryptoKey::KeyUsage aUsage
) { mAttributes
|= aUsage
; }
413 bool CryptoKey::HasAnyUsage() { return !!(mAttributes
& USAGES_MASK
); }
415 bool CryptoKey::HasUsage(CryptoKey::KeyUsage aUsage
) {
416 return !!(mAttributes
& aUsage
);
419 bool CryptoKey::HasUsageOtherThan(uint32_t aUsages
) {
420 return !!(mAttributes
& USAGES_MASK
& ~aUsages
);
423 bool CryptoKey::IsRecognizedUsage(const nsString
& aUsage
) {
425 nsresult rv
= StringToUsage(aUsage
, dummy
);
426 return NS_SUCCEEDED(rv
);
429 bool CryptoKey::AllUsagesRecognized(const Sequence
<nsString
>& aUsages
) {
430 for (uint32_t i
= 0; i
< aUsages
.Length(); ++i
) {
431 if (!IsRecognizedUsage(aUsages
[i
])) {
438 uint32_t CryptoKey::GetAllowedUsagesForAlgorithm(const nsString
& aAlgorithm
) {
439 uint32_t allowedUsages
= 0;
440 if (aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_AES_CTR
) ||
441 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_AES_CBC
) ||
442 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_AES_GCM
) ||
443 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP
)) {
444 allowedUsages
= ENCRYPT
| DECRYPT
| WRAPKEY
| UNWRAPKEY
;
445 } else if (aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_AES_KW
)) {
446 allowedUsages
= WRAPKEY
| UNWRAPKEY
;
447 } else if (aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_HMAC
) ||
448 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
449 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS
) ||
450 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_ECDSA
) ||
451 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_ED25519
)) {
452 allowedUsages
= SIGN
| VERIFY
;
453 } else if (aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_ECDH
) ||
454 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_HKDF
) ||
455 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_PBKDF2
) ||
456 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_X25519
)) {
457 allowedUsages
= DERIVEBITS
| DERIVEKEY
;
459 return allowedUsages
;
462 nsresult
CryptoKey::SetSymKey(const CryptoBuffer
& aSymKey
) {
463 if (!mSymKey
.Assign(aSymKey
)) {
464 return NS_ERROR_OUT_OF_MEMORY
;
470 nsresult
CryptoKey::SetPrivateKey(SECKEYPrivateKey
* aPrivateKey
) {
472 mPrivateKey
= nullptr;
476 mPrivateKey
= UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(aPrivateKey
));
477 return mPrivateKey
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
480 nsresult
CryptoKey::SetPublicKey(SECKEYPublicKey
* aPublicKey
) {
482 mPublicKey
= nullptr;
486 mPublicKey
= UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(aPublicKey
));
487 return mPublicKey
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
490 const CryptoBuffer
& CryptoKey::GetSymKey() const { return mSymKey
; }
492 UniqueSECKEYPrivateKey
CryptoKey::GetPrivateKey() const {
496 return UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(mPrivateKey
.get()));
499 UniqueSECKEYPublicKey
CryptoKey::GetPublicKey() const {
503 return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(mPublicKey
.get()));
506 // Serialization and deserialization convenience methods
508 UniqueSECKEYPrivateKey
CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer
& aKeyData
) {
509 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
514 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
519 SECItem pkcs8Item
= {siBuffer
, nullptr, 0};
520 if (!aKeyData
.ToSECItem(arena
.get(), &pkcs8Item
)) {
524 // Allow everything, we enforce usage ourselves
525 unsigned int usage
= KU_ALL
;
527 SECKEYPrivateKey
* privKey
;
528 SECStatus rv
= PK11_ImportDERPrivateKeyInfoAndReturnKey(
529 slot
.get(), &pkcs8Item
, nullptr, nullptr, false, false, usage
, &privKey
,
532 if (rv
== SECFailure
) {
536 return UniqueSECKEYPrivateKey(privKey
);
539 UniqueSECKEYPublicKey
CryptoKey::PublicKeyFromSpki(CryptoBuffer
& aKeyData
) {
540 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
545 SECItem spkiItem
= {siBuffer
, nullptr, 0};
546 if (!aKeyData
.ToSECItem(arena
.get(), &spkiItem
)) {
550 UniqueCERTSubjectPublicKeyInfo
spki(
551 SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem
));
556 bool isECDHAlgorithm
=
557 SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH
, &spki
->algorithm
.algorithm
);
559 // Check for |id-ecDH|. Per old versions of the WebCrypto spec we must
560 // support this OID but NSS does unfortunately not know it. Let's
561 // change the algorithm to |id-ecPublicKey| to make NSS happy.
562 if (isECDHAlgorithm
) {
563 SECOidTag oid
= SEC_OID_ANSIX962_EC_PUBLIC_KEY
;
565 SECOidData
* oidData
= SECOID_FindOIDByTag(oid
);
570 SECStatus rv
= SECITEM_CopyItem(spki
->arena
, &spki
->algorithm
.algorithm
,
572 if (rv
!= SECSuccess
) {
577 UniqueSECKEYPublicKey
tmp(SECKEY_ExtractPublicKey(spki
.get()));
578 if (!tmp
.get() || !PublicKeyValid(tmp
.get())) {
582 return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(tmp
.get()));
585 nsresult
CryptoKey::PrivateKeyToPkcs8(SECKEYPrivateKey
* aPrivKey
,
586 CryptoBuffer
& aRetVal
) {
587 UniqueSECItem
pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey
, nullptr));
588 if (!pkcs8Item
.get()) {
589 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
591 if (!aRetVal
.Assign(pkcs8Item
.get())) {
592 return NS_ERROR_DOM_OPERATION_ERR
;
597 nsresult
CryptoKey::PublicKeyToSpki(SECKEYPublicKey
* aPubKey
,
598 CryptoBuffer
& aRetVal
) {
599 UniqueCERTSubjectPublicKeyInfo spki
;
601 spki
.reset(SECKEY_CreateSubjectPublicKeyInfo(aPubKey
));
603 return NS_ERROR_DOM_OPERATION_ERR
;
606 const SEC_ASN1Template
* tpl
= SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate
);
607 UniqueSECItem
spkiItem(SEC_ASN1EncodeItem(nullptr, nullptr, spki
.get(), tpl
));
609 if (!aRetVal
.Assign(spkiItem
.get())) {
610 return NS_ERROR_DOM_OPERATION_ERR
;
615 SECItem
* CreateECPointForCoordinates(const CryptoBuffer
& aX
,
616 const CryptoBuffer
& aY
,
617 PLArenaPool
* aArena
) {
618 // Check that both points have the same length.
619 if (aX
.Length() != aY
.Length()) {
625 ::SECITEM_AllocItem(aArena
, nullptr, aX
.Length() + aY
.Length() + 1);
631 point
->data
[0] = EC_POINT_FORM_UNCOMPRESSED
;
632 memcpy(point
->data
+ 1, aX
.Elements(), aX
.Length());
633 memcpy(point
->data
+ 1 + aX
.Length(), aY
.Elements(), aY
.Length());
638 nsresult
CheckEDKeyLen(const CryptoBuffer
& p
) {
639 /* Ed25519 keys are 32 bytes long.
640 See: https://datatracker.ietf.org/doc/html/rfc8032 Introduction. */
641 uint32_t lengthEDPrivatePublicKey
= 32;
642 if (p
.Length() != lengthEDPrivatePublicKey
) {
643 /* We do not use this error code, we only check is the function returns
645 return NS_ERROR_DOM_OPERATION_ERR
;
651 SECItem
* CreateEDPointForXCoordinate(const CryptoBuffer
& aX
,
652 PLArenaPool
* aArena
) {
653 if (NS_FAILED(CheckEDKeyLen(aX
))) {
658 SECItem
* point
= ::SECITEM_AllocItem(aArena
, nullptr, aX
.Length());
664 memcpy(point
->data
, aX
.Elements(), aX
.Length());
668 UniqueSECKEYPrivateKey
CryptoKey::PrivateKeyFromJwk(const JsonWebKey
& aJwk
) {
669 CK_OBJECT_CLASS privateKeyValue
= CKO_PRIVATE_KEY
;
670 CK_BBOOL falseValue
= CK_FALSE
;
672 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_EC
)) {
673 // Verify that all of the required parameters are present
674 CryptoBuffer x
, y
, d
;
675 if (!aJwk
.mCrv
.WasPassed() || !aJwk
.mX
.WasPassed() ||
676 NS_FAILED(x
.FromJwkBase64(aJwk
.mX
.Value())) || !aJwk
.mY
.WasPassed() ||
677 NS_FAILED(y
.FromJwkBase64(aJwk
.mY
.Value())) || !aJwk
.mD
.WasPassed() ||
678 NS_FAILED(d
.FromJwkBase64(aJwk
.mD
.Value()))) {
683 if (!NormalizeToken(aJwk
.mCrv
.Value(), namedCurve
)) {
687 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
692 // Create parameters.
693 SECItem
* params
= CreateECParamsForCurve(namedCurve
, arena
.get());
698 SECItem
* ecPoint
= CreateECPointForCoordinates(x
, y
, arena
.get());
703 // Populate template from parameters
704 CK_KEY_TYPE ecValue
= CKK_EC
;
705 CK_ATTRIBUTE keyTemplate
[9] = {
706 {CKA_CLASS
, &privateKeyValue
, sizeof(privateKeyValue
)},
707 {CKA_KEY_TYPE
, &ecValue
, sizeof(ecValue
)},
708 {CKA_TOKEN
, &falseValue
, sizeof(falseValue
)},
709 {CKA_SENSITIVE
, &falseValue
, sizeof(falseValue
)},
710 {CKA_PRIVATE
, &falseValue
, sizeof(falseValue
)},
711 // PrivateKeyFromPrivateKeyTemplate sets the ID.
712 {CKA_ID
, nullptr, 0},
713 {CKA_EC_PARAMS
, params
->data
, params
->len
},
714 {CKA_EC_POINT
, ecPoint
->data
, ecPoint
->len
},
715 {CKA_VALUE
, (void*)d
.Elements(), (CK_ULONG
)d
.Length()},
718 return PrivateKeyFromPrivateKeyTemplate(keyTemplate
,
719 std::size(keyTemplate
));
722 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_RSA
)) {
723 // Verify that all of the required parameters are present
724 CryptoBuffer n
, e
, d
, p
, q
, dp
, dq
, qi
;
725 if (!aJwk
.mN
.WasPassed() || NS_FAILED(n
.FromJwkBase64(aJwk
.mN
.Value())) ||
726 !aJwk
.mE
.WasPassed() || NS_FAILED(e
.FromJwkBase64(aJwk
.mE
.Value())) ||
727 !aJwk
.mD
.WasPassed() || NS_FAILED(d
.FromJwkBase64(aJwk
.mD
.Value())) ||
728 !aJwk
.mP
.WasPassed() || NS_FAILED(p
.FromJwkBase64(aJwk
.mP
.Value())) ||
729 !aJwk
.mQ
.WasPassed() || NS_FAILED(q
.FromJwkBase64(aJwk
.mQ
.Value())) ||
730 !aJwk
.mDp
.WasPassed() ||
731 NS_FAILED(dp
.FromJwkBase64(aJwk
.mDp
.Value())) ||
732 !aJwk
.mDq
.WasPassed() ||
733 NS_FAILED(dq
.FromJwkBase64(aJwk
.mDq
.Value())) ||
734 !aJwk
.mQi
.WasPassed() ||
735 NS_FAILED(qi
.FromJwkBase64(aJwk
.mQi
.Value()))) {
739 // Populate template from parameters
740 CK_KEY_TYPE rsaValue
= CKK_RSA
;
741 CK_ATTRIBUTE keyTemplate
[14] = {
742 {CKA_CLASS
, &privateKeyValue
, sizeof(privateKeyValue
)},
743 {CKA_KEY_TYPE
, &rsaValue
, sizeof(rsaValue
)},
744 {CKA_TOKEN
, &falseValue
, sizeof(falseValue
)},
745 {CKA_SENSITIVE
, &falseValue
, sizeof(falseValue
)},
746 {CKA_PRIVATE
, &falseValue
, sizeof(falseValue
)},
747 // PrivateKeyFromPrivateKeyTemplate sets the ID.
748 {CKA_ID
, nullptr, 0},
749 {CKA_MODULUS
, (void*)n
.Elements(), (CK_ULONG
)n
.Length()},
750 {CKA_PUBLIC_EXPONENT
, (void*)e
.Elements(), (CK_ULONG
)e
.Length()},
751 {CKA_PRIVATE_EXPONENT
, (void*)d
.Elements(), (CK_ULONG
)d
.Length()},
752 {CKA_PRIME_1
, (void*)p
.Elements(), (CK_ULONG
)p
.Length()},
753 {CKA_PRIME_2
, (void*)q
.Elements(), (CK_ULONG
)q
.Length()},
754 {CKA_EXPONENT_1
, (void*)dp
.Elements(), (CK_ULONG
)dp
.Length()},
755 {CKA_EXPONENT_2
, (void*)dq
.Elements(), (CK_ULONG
)dq
.Length()},
756 {CKA_COEFFICIENT
, (void*)qi
.Elements(), (CK_ULONG
)qi
.Length()},
759 return PrivateKeyFromPrivateKeyTemplate(keyTemplate
,
760 std::size(keyTemplate
));
763 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_OKP
)) {
766 if (!aJwk
.mCrv
.WasPassed() || !aJwk
.mX
.WasPassed() ||
767 NS_FAILED(x
.FromJwkBase64(aJwk
.mX
.Value())) || !aJwk
.mD
.WasPassed() ||
768 NS_FAILED(d
.FromJwkBase64(aJwk
.mD
.Value()))) {
773 if (!NormalizeToken(aJwk
.mCrv
.Value(), namedCurve
)) {
777 if (!namedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519
) &&
778 !namedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519
)) {
782 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
787 // Create parameters.
788 SECItem
* params
= CreateECParamsForCurve(namedCurve
, arena
.get());
793 SECItem
* ecPoint
= CreateEDPointForXCoordinate(x
, arena
.get());
798 if (CheckEDKeyLen(d
) != NS_OK
) {
802 // Populate template from parameters
804 if (namedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519
)) {
805 ecValue
= CKK_EC_EDWARDS
;
806 } else if (namedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519
)) {
807 ecValue
= CKK_EC_MONTGOMERY
;
812 CK_ATTRIBUTE keyTemplate
[9] = {
813 {CKA_CLASS
, &privateKeyValue
, sizeof(privateKeyValue
)},
814 {CKA_KEY_TYPE
, &ecValue
, sizeof(ecValue
)},
815 {CKA_TOKEN
, &falseValue
, sizeof(falseValue
)},
816 {CKA_SENSITIVE
, &falseValue
, sizeof(falseValue
)},
817 {CKA_PRIVATE
, &falseValue
, sizeof(falseValue
)},
818 // PrivateKeyFromPrivateKeyTemplate sets the ID.
819 {CKA_ID
, nullptr, 0},
820 {CKA_EC_PARAMS
, params
->data
, params
->len
},
821 {CKA_EC_POINT
, ecPoint
->data
, ecPoint
->len
},
822 {CKA_VALUE
, (void*)d
.Elements(), (CK_ULONG
)d
.Length()},
825 return PrivateKeyFromPrivateKeyTemplate(keyTemplate
,
826 std::size(keyTemplate
));
832 bool ReadAndEncodeAttribute(SECKEYPrivateKey
* aKey
,
833 CK_ATTRIBUTE_TYPE aAttribute
,
834 Optional
<nsString
>& aDst
) {
835 ScopedAutoSECItem item
;
836 if (PK11_ReadRawAttribute(PK11_TypePrivKey
, aKey
, aAttribute
, &item
) !=
842 if (!buffer
.Assign(&item
)) {
846 if (NS_FAILED(buffer
.ToJwkBase64(aDst
.Value()))) {
853 bool OKPKeyToJwk(const SECItem
* aEcParams
, const SECItem
* aPublicValue
,
854 JsonWebKey
& aRetVal
) {
855 aRetVal
.mX
.Construct();
858 if (!FindOIDTagForEncodedParameters(aEcParams
, &tag
)) {
864 case SEC_OID_ED25519_PUBLIC_KEY
:
866 aRetVal
.mCrv
.Construct(
867 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_ED25519
));
871 aRetVal
.mCrv
.Construct(
872 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_CURVE25519
));
878 /* No compression is used. */
879 if (aPublicValue
->len
!= flen
) {
884 if (!x
.Assign(aPublicValue
) || NS_FAILED(x
.ToJwkBase64(aRetVal
.mX
.Value()))) {
888 aRetVal
.mKty
= NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_OKP
);
892 bool ECKeyToJwk(const PK11ObjectType aKeyType
, void* aKey
,
893 const SECItem
* aEcParams
, const SECItem
* aPublicValue
,
894 JsonWebKey
& aRetVal
) {
895 aRetVal
.mX
.Construct();
896 aRetVal
.mY
.Construct();
898 // Check that the given EC parameters are valid.
900 if (!FindOIDTagForEncodedParameters(aEcParams
, &tag
)) {
906 case SEC_OID_SECG_EC_SECP256R1
:
908 aRetVal
.mCrv
.Construct(
909 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_P256
));
911 case SEC_OID_SECG_EC_SECP384R1
:
913 aRetVal
.mCrv
.Construct(
914 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_P384
));
916 case SEC_OID_SECG_EC_SECP521R1
:
918 aRetVal
.mCrv
.Construct(
919 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_P521
));
925 // No support for compressed points.
926 if (aPublicValue
->data
[0] != EC_POINT_FORM_UNCOMPRESSED
) {
930 // Check length of uncompressed point coordinates.
931 if (aPublicValue
->len
!= (2 * flen
+ 1)) {
935 UniqueSECItem
ecPointX(::SECITEM_AllocItem(nullptr, nullptr, flen
));
936 UniqueSECItem
ecPointY(::SECITEM_AllocItem(nullptr, nullptr, flen
));
937 if (!ecPointX
|| !ecPointY
) {
941 // Extract point data.
942 memcpy(ecPointX
->data
, aPublicValue
->data
+ 1, flen
);
943 memcpy(ecPointY
->data
, aPublicValue
->data
+ 1 + flen
, flen
);
946 if (!x
.Assign(ecPointX
.get()) ||
947 NS_FAILED(x
.ToJwkBase64(aRetVal
.mX
.Value())) ||
948 !y
.Assign(ecPointY
.get()) ||
949 NS_FAILED(y
.ToJwkBase64(aRetVal
.mY
.Value()))) {
953 aRetVal
.mKty
= NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_EC
);
957 nsresult
CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey
* aPrivKey
,
958 JsonWebKey
& aRetVal
) {
959 switch (aPrivKey
->keyType
) {
961 aRetVal
.mN
.Construct();
962 aRetVal
.mE
.Construct();
963 aRetVal
.mD
.Construct();
964 aRetVal
.mP
.Construct();
965 aRetVal
.mQ
.Construct();
966 aRetVal
.mDp
.Construct();
967 aRetVal
.mDq
.Construct();
968 aRetVal
.mQi
.Construct();
970 if (!ReadAndEncodeAttribute(aPrivKey
, CKA_MODULUS
, aRetVal
.mN
) ||
971 !ReadAndEncodeAttribute(aPrivKey
, CKA_PUBLIC_EXPONENT
, aRetVal
.mE
) ||
972 !ReadAndEncodeAttribute(aPrivKey
, CKA_PRIVATE_EXPONENT
, aRetVal
.mD
) ||
973 !ReadAndEncodeAttribute(aPrivKey
, CKA_PRIME_1
, aRetVal
.mP
) ||
974 !ReadAndEncodeAttribute(aPrivKey
, CKA_PRIME_2
, aRetVal
.mQ
) ||
975 !ReadAndEncodeAttribute(aPrivKey
, CKA_EXPONENT_1
, aRetVal
.mDp
) ||
976 !ReadAndEncodeAttribute(aPrivKey
, CKA_EXPONENT_2
, aRetVal
.mDq
) ||
977 !ReadAndEncodeAttribute(aPrivKey
, CKA_COEFFICIENT
, aRetVal
.mQi
)) {
978 return NS_ERROR_DOM_OPERATION_ERR
;
981 aRetVal
.mKty
= NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_RSA
);
988 ScopedAutoSECItem params
;
989 SECStatus rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, aPrivKey
,
990 CKA_EC_PARAMS
, ¶ms
);
991 if (rv
!= SECSuccess
) {
992 return NS_ERROR_DOM_OPERATION_ERR
;
995 // Read public point Q.
996 ScopedAutoSECItem ecPoint
;
997 rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, aPrivKey
, CKA_EC_POINT
,
1000 if (rv
!= SECSuccess
) {
1001 return NS_ERROR_DOM_OPERATION_ERR
;
1004 if (!OKPKeyToJwk(¶ms
, &ecPoint
, aRetVal
)) {
1005 return NS_ERROR_DOM_OPERATION_ERR
;
1008 aRetVal
.mD
.Construct();
1010 // Read private value.
1011 if (!ReadAndEncodeAttribute(aPrivKey
, CKA_VALUE
, aRetVal
.mD
)) {
1012 return NS_ERROR_DOM_OPERATION_ERR
;
1019 ScopedAutoSECItem params
;
1020 SECStatus rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, aPrivKey
,
1021 CKA_EC_PARAMS
, ¶ms
);
1022 if (rv
!= SECSuccess
) {
1023 return NS_ERROR_DOM_OPERATION_ERR
;
1026 // Read public point Q.
1027 ScopedAutoSECItem ecPoint
;
1028 rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, aPrivKey
, CKA_EC_POINT
,
1030 if (rv
!= SECSuccess
) {
1031 return NS_ERROR_DOM_OPERATION_ERR
;
1034 if (!ECKeyToJwk(PK11_TypePrivKey
, aPrivKey
, ¶ms
, &ecPoint
, aRetVal
)) {
1035 return NS_ERROR_DOM_OPERATION_ERR
;
1038 aRetVal
.mD
.Construct();
1040 // Read private value.
1041 if (!ReadAndEncodeAttribute(aPrivKey
, CKA_VALUE
, aRetVal
.mD
)) {
1042 return NS_ERROR_DOM_OPERATION_ERR
;
1048 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1052 /* The function is used to determine a key type from the curve. */
1053 KeyType
KeyTypeFromCurveName(const nsAString
& aNamedCurve
) {
1054 KeyType t
= nullKey
;
1055 if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256
) ||
1056 aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384
) ||
1057 aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521
)) {
1059 } else if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519
)) {
1061 } else if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519
)) {
1067 /* The function is used for EC and ED keys. */
1068 UniqueSECKEYPublicKey
CreateECPublicKey(const SECItem
* aKeyData
,
1069 const nsAString
& aNamedCurve
) {
1070 if (!EnsureNSSInitializedChromeOrContent()) {
1074 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
1079 // It's important that this be a UniqueSECKEYPublicKey, as this ensures that
1080 // SECKEY_DestroyPublicKey will be called on it. If this doesn't happen, when
1081 // CryptoKey::PublicKeyValid is called on it and it gets moved to the internal
1082 // PKCS#11 slot, it will leak a reference to the slot.
1083 UniqueSECKEYPublicKey
key(PORT_ArenaZNew(arena
.get(), SECKEYPublicKey
));
1088 // Transfer arena ownership to the key.
1089 key
->arena
= arena
.release();
1090 key
->keyType
= KeyTypeFromCurveName(aNamedCurve
);
1091 if (key
->keyType
!= ecKey
&& key
->keyType
!= edKey
&&
1092 key
->keyType
!= ecMontKey
) {
1096 key
->pkcs11Slot
= nullptr;
1097 key
->pkcs11ID
= CK_INVALID_HANDLE
;
1099 // Create curve parameters.
1100 SECItem
* params
= CreateECParamsForCurve(aNamedCurve
, key
->arena
);
1104 key
->u
.ec
.DEREncodedParams
= *params
;
1106 // Set public point.
1108 SECITEM_CopyItem(key
->arena
, &key
->u
.ec
.publicValue
, aKeyData
);
1109 if (NS_WARN_IF(ret
!= SECSuccess
)) {
1113 // Ensure the given point is on the curve.
1114 if (!CryptoKey::PublicKeyValid(key
.get())) {
1121 UniqueSECKEYPublicKey
CryptoKey::PublicKeyFromJwk(const JsonWebKey
& aJwk
) {
1122 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_RSA
)) {
1123 // Verify that all of the required parameters are present
1125 if (!aJwk
.mN
.WasPassed() || NS_FAILED(n
.FromJwkBase64(aJwk
.mN
.Value())) ||
1126 !aJwk
.mE
.WasPassed() || NS_FAILED(e
.FromJwkBase64(aJwk
.mE
.Value()))) {
1130 // Transcode to a DER RSAPublicKey structure
1131 struct RSAPublicKeyData
{
1135 const RSAPublicKeyData input
= {
1136 {siUnsignedInteger
, n
.Elements(), (unsigned int)n
.Length()},
1137 {siUnsignedInteger
, e
.Elements(), (unsigned int)e
.Length()}};
1138 const SEC_ASN1Template rsaPublicKeyTemplate
[] = {
1139 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(RSAPublicKeyData
)},
1142 offsetof(RSAPublicKeyData
, n
),
1146 offsetof(RSAPublicKeyData
, e
),
1152 UniqueSECItem
pkDer(
1153 SEC_ASN1EncodeItem(nullptr, nullptr, &input
, rsaPublicKeyTemplate
));
1158 return UniqueSECKEYPublicKey(
1159 SECKEY_ImportDERPublicKey(pkDer
.get(), CKK_RSA
));
1162 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_EC
)) {
1163 // Verify that all of the required parameters are present
1165 if (!aJwk
.mCrv
.WasPassed() || !aJwk
.mX
.WasPassed() ||
1166 NS_FAILED(x
.FromJwkBase64(aJwk
.mX
.Value())) || !aJwk
.mY
.WasPassed() ||
1167 NS_FAILED(y
.FromJwkBase64(aJwk
.mY
.Value()))) {
1171 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
1177 SECItem
* point
= CreateECPointForCoordinates(x
, y
, arena
.get());
1182 nsString namedCurve
;
1183 if (!NormalizeToken(aJwk
.mCrv
.Value(), namedCurve
)) {
1187 return CreateECPublicKey(point
, namedCurve
);
1190 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_OKP
)) {
1191 // Verify that all of the required parameters are present
1193 if (!aJwk
.mCrv
.WasPassed() || !aJwk
.mX
.WasPassed() ||
1194 NS_FAILED(x
.FromJwkBase64(aJwk
.mX
.Value()))) {
1198 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
1204 SECItem
* point
= CreateEDPointForXCoordinate(x
, arena
.get());
1209 nsString namedCurve
;
1210 if (!NormalizeToken(aJwk
.mCrv
.Value(), namedCurve
)) {
1214 if (!namedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519
) &&
1215 !namedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519
)) {
1219 return CreateECPublicKey(point
, namedCurve
);
1225 nsresult
CryptoKey::PublicKeyToJwk(SECKEYPublicKey
* aPubKey
,
1226 JsonWebKey
& aRetVal
) {
1227 switch (aPubKey
->keyType
) {
1230 aRetVal
.mN
.Construct();
1231 aRetVal
.mE
.Construct();
1233 if (!n
.Assign(&aPubKey
->u
.rsa
.modulus
) ||
1234 !e
.Assign(&aPubKey
->u
.rsa
.publicExponent
) ||
1235 NS_FAILED(n
.ToJwkBase64(aRetVal
.mN
.Value())) ||
1236 NS_FAILED(e
.ToJwkBase64(aRetVal
.mE
.Value()))) {
1237 return NS_ERROR_DOM_OPERATION_ERR
;
1240 aRetVal
.mKty
= NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_RSA
);
1245 if (!OKPKeyToJwk(&aPubKey
->u
.ec
.DEREncodedParams
,
1246 &aPubKey
->u
.ec
.publicValue
, aRetVal
)) {
1247 return NS_ERROR_DOM_OPERATION_ERR
;
1251 if (!ECKeyToJwk(PK11_TypePubKey
, aPubKey
, &aPubKey
->u
.ec
.DEREncodedParams
,
1252 &aPubKey
->u
.ec
.publicValue
, aRetVal
)) {
1253 return NS_ERROR_DOM_OPERATION_ERR
;
1258 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1262 UniqueSECKEYPublicKey
CryptoKey::PublicECKeyFromRaw(
1263 CryptoBuffer
& aKeyData
, const nsString
& aNamedCurve
) {
1264 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
1269 SECItem rawItem
= {siBuffer
, nullptr, 0};
1270 if (!aKeyData
.ToSECItem(arena
.get(), &rawItem
)) {
1275 if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256
)) {
1277 } else if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384
)) {
1279 } else if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521
)) {
1285 // Check length of uncompressed point coordinates. There are 2 field elements
1286 // and a leading point form octet (which must EC_POINT_FORM_UNCOMPRESSED).
1287 if (rawItem
.len
!= (2 * flen
+ 1)) {
1291 // No support for compressed points.
1292 if (rawItem
.data
[0] != EC_POINT_FORM_UNCOMPRESSED
) {
1296 return CreateECPublicKey(&rawItem
, aNamedCurve
);
1299 nsresult
CryptoKey::PublicECKeyToRaw(SECKEYPublicKey
* aPubKey
,
1300 CryptoBuffer
& aRetVal
) {
1301 if (!aRetVal
.Assign(&aPubKey
->u
.ec
.publicValue
)) {
1302 return NS_ERROR_DOM_OPERATION_ERR
;
1307 UniqueSECKEYPublicKey
CryptoKey::PublicOKPKeyFromRaw(
1308 CryptoBuffer
& aKeyData
, const nsString
& aNamedCurve
) {
1309 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
1314 SECItem rawItem
= {siBuffer
, nullptr, 0};
1315 if (!aKeyData
.ToSECItem(arena
.get(), &rawItem
)) {
1320 if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519
)) {
1322 } else if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519
)) {
1328 if (rawItem
.len
!= flen
) {
1332 return CreateECPublicKey(&rawItem
, aNamedCurve
);
1335 bool CryptoKey::PublicKeyValid(SECKEYPublicKey
* aPubKey
) {
1336 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
1341 // This assumes that NSS checks the validity of a public key when
1342 // it is imported into a PKCS#11 module, and returns CK_INVALID_HANDLE
1343 // if it is invalid.
1344 CK_OBJECT_HANDLE id
= PK11_ImportPublicKey(slot
.get(), aPubKey
, PR_FALSE
);
1345 return id
!= CK_INVALID_HANDLE
;
1348 bool CryptoKey::WriteStructuredClone(JSContext
* aCX
,
1349 JSStructuredCloneWriter
* aWriter
) const {
1350 // Write in five pieces
1352 // 2. Symmetric key as raw (if present)
1353 // 3. Private key as pkcs8 (if present)
1354 // 4. Public key as spki (if present)
1355 // 5. Algorithm in whatever form it chooses
1356 CryptoBuffer priv
, pub
;
1359 if (NS_FAILED(CryptoKey::PrivateKeyToPkcs8(mPrivateKey
.get(), priv
))) {
1365 if (NS_FAILED(CryptoKey::PublicKeyToSpki(mPublicKey
.get(), pub
))) {
1370 return JS_WriteUint32Pair(aWriter
, mAttributes
, CRYPTOKEY_SC_VERSION
) &&
1371 WriteBuffer(aWriter
, mSymKey
) && WriteBuffer(aWriter
, priv
) &&
1372 WriteBuffer(aWriter
, pub
) && mAlgorithm
.WriteStructuredClone(aWriter
);
1376 already_AddRefed
<CryptoKey
> CryptoKey::ReadStructuredClone(
1377 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
1378 JSStructuredCloneReader
* aReader
) {
1379 // Ensure that NSS is initialized.
1380 if (!EnsureNSSInitializedChromeOrContent()) {
1384 RefPtr
<CryptoKey
> key
= new CryptoKey(aGlobal
);
1387 CryptoBuffer sym
, priv
, pub
;
1389 bool read
= JS_ReadUint32Pair(aReader
, &key
->mAttributes
, &version
) &&
1390 (version
== CRYPTOKEY_SC_VERSION
) && ReadBuffer(aReader
, sym
) &&
1391 ReadBuffer(aReader
, priv
) && ReadBuffer(aReader
, pub
) &&
1392 key
->mAlgorithm
.ReadStructuredClone(aReader
);
1397 if (sym
.Length() > 0 && !key
->mSymKey
.Assign(sym
)) {
1400 if (priv
.Length() > 0) {
1401 key
->mPrivateKey
= CryptoKey::PrivateKeyFromPkcs8(priv
);
1403 if (pub
.Length() > 0) {
1404 key
->mPublicKey
= CryptoKey::PublicKeyFromSpki(pub
);
1407 // Ensure that what we've read is consistent
1408 // If the attributes indicate a key type, should have a key of that type
1409 if (!((key
->GetKeyType() == SECRET
&& key
->mSymKey
.Length() > 0) ||
1410 (key
->GetKeyType() == PRIVATE
&& key
->mPrivateKey
) ||
1411 (key
->GetKeyType() == PUBLIC
&& key
->mPublicKey
))) {
1415 return key
.forget();
1418 } // namespace mozilla::dom