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 "mozilla/ArrayUtils.h"
11 #include "mozilla/dom/RootedDictionary.h"
12 #include "mozilla/dom/SubtleCryptoBinding.h"
13 #include "mozilla/dom/ToJSValue.h"
14 #include "nsNSSComponent.h"
20 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey
, mGlobal
)
21 NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKey
)
22 NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKey
)
23 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKey
)
24 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
25 NS_INTERFACE_MAP_ENTRY(nsISupports
)
28 nsresult
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 // This helper function will release the memory backing a SECKEYPrivateKey and
52 // any resources acquired in its creation. It will leave the backing PKCS#11
53 // object untouched, however. This should only be called from
54 // PrivateKeyFromPrivateKeyTemplate.
55 static void DestroyPrivateKeyWithoutDestroyingPKCS11Object(
56 SECKEYPrivateKey
* key
) {
57 PK11_FreeSlot(key
->pkcs11Slot
);
58 PORT_FreeArena(key
->arena
, PR_TRUE
);
61 // To protect against key ID collisions, PrivateKeyFromPrivateKeyTemplate
62 // generates a random ID for each key. The given template must contain an
63 // attribute slot for a key ID, but it must consist of a null pointer and have a
65 UniqueSECKEYPrivateKey
PrivateKeyFromPrivateKeyTemplate(
66 CK_ATTRIBUTE
* aTemplate
, CK_ULONG aTemplateSize
) {
67 // Create a generic object with the contents of the key
68 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
73 // Generate a random 160-bit object ID. This ID must be unique.
74 UniqueSECItem
objID(::SECITEM_AllocItem(nullptr, nullptr, 20));
75 SECStatus rv
= PK11_GenerateRandomOnSlot(slot
.get(), objID
->data
, objID
->len
);
76 if (rv
!= SECSuccess
) {
79 // Check if something is already using this ID.
80 SECKEYPrivateKey
* preexistingKey
=
81 PK11_FindKeyByKeyID(slot
.get(), objID
.get(), nullptr);
83 // Note that we can't just call SECKEY_DestroyPrivateKey here because that
84 // will destroy the PKCS#11 object that is backing a preexisting key (that
85 // we still have a handle on somewhere else in memory). If that object were
86 // destroyed, cryptographic operations performed by that other key would
88 DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey
);
89 // Try again with a new ID (but only once - collisions are very unlikely).
90 rv
= PK11_GenerateRandomOnSlot(slot
.get(), objID
->data
, objID
->len
);
91 if (rv
!= SECSuccess
) {
94 preexistingKey
= PK11_FindKeyByKeyID(slot
.get(), objID
.get(), nullptr);
96 DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey
);
101 CK_ATTRIBUTE
* idAttributeSlot
= nullptr;
102 for (CK_ULONG i
= 0; i
< aTemplateSize
; i
++) {
103 if (aTemplate
[i
].type
== CKA_ID
) {
104 if (aTemplate
[i
].pValue
!= nullptr || aTemplate
[i
].ulValueLen
!= 0) {
107 idAttributeSlot
= aTemplate
+ i
;
111 if (!idAttributeSlot
) {
115 idAttributeSlot
->pValue
= objID
->data
;
116 idAttributeSlot
->ulValueLen
= objID
->len
;
117 UniquePK11GenericObject
obj(
118 PK11_CreateGenericObject(slot
.get(), aTemplate
, aTemplateSize
, PR_FALSE
));
119 // Unset the ID attribute slot's pointer and length so that data that only
120 // lives for the scope of this function doesn't escape.
121 idAttributeSlot
->pValue
= nullptr;
122 idAttributeSlot
->ulValueLen
= 0;
127 // Have NSS translate the object to a private key.
128 return UniqueSECKEYPrivateKey(
129 PK11_FindKeyByKeyID(slot
.get(), objID
.get(), nullptr));
132 CryptoKey::CryptoKey(nsIGlobalObject
* aGlobal
)
136 mPrivateKey(nullptr),
137 mPublicKey(nullptr) {}
139 JSObject
* CryptoKey::WrapObject(JSContext
* aCx
,
140 JS::Handle
<JSObject
*> aGivenProto
) {
141 return CryptoKey_Binding::Wrap(aCx
, this, aGivenProto
);
144 void CryptoKey::GetType(nsString
& aRetVal
) const {
145 uint32_t type
= mAttributes
& TYPE_MASK
;
148 aRetVal
.AssignLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC
);
151 aRetVal
.AssignLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE
);
154 aRetVal
.AssignLiteral(WEBCRYPTO_KEY_TYPE_SECRET
);
159 bool CryptoKey::Extractable() const { return (mAttributes
& EXTRACTABLE
); }
161 void CryptoKey::GetAlgorithm(JSContext
* cx
,
162 JS::MutableHandle
<JSObject
*> aRetVal
,
163 ErrorResult
& aRv
) const {
164 bool converted
= false;
165 JS::RootedValue
val(cx
);
166 switch (mAlgorithm
.mType
) {
167 case KeyAlgorithmProxy::AES
:
168 converted
= ToJSValue(cx
, mAlgorithm
.mAes
, &val
);
170 case KeyAlgorithmProxy::HMAC
:
171 converted
= ToJSValue(cx
, mAlgorithm
.mHmac
, &val
);
173 case KeyAlgorithmProxy::RSA
: {
174 RootedDictionary
<RsaHashedKeyAlgorithm
> rsa(cx
);
175 converted
= mAlgorithm
.mRsa
.ToKeyAlgorithm(cx
, rsa
);
177 converted
= ToJSValue(cx
, rsa
, &val
);
181 case KeyAlgorithmProxy::EC
:
182 converted
= ToJSValue(cx
, mAlgorithm
.mEc
, &val
);
186 aRv
.Throw(NS_ERROR_DOM_OPERATION_ERR
);
190 aRetVal
.set(&val
.toObject());
193 void CryptoKey::GetUsages(nsTArray
<nsString
>& aRetVal
) const {
194 if (mAttributes
& ENCRYPT
) {
195 aRetVal
.AppendElement(
196 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_ENCRYPT
));
198 if (mAttributes
& DECRYPT
) {
199 aRetVal
.AppendElement(
200 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_DECRYPT
));
202 if (mAttributes
& SIGN
) {
203 aRetVal
.AppendElement(
204 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_SIGN
));
206 if (mAttributes
& VERIFY
) {
207 aRetVal
.AppendElement(
208 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_VERIFY
));
210 if (mAttributes
& DERIVEKEY
) {
211 aRetVal
.AppendElement(
212 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_DERIVEKEY
));
214 if (mAttributes
& DERIVEBITS
) {
215 aRetVal
.AppendElement(
216 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_DERIVEBITS
));
218 if (mAttributes
& WRAPKEY
) {
219 aRetVal
.AppendElement(
220 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_WRAPKEY
));
222 if (mAttributes
& UNWRAPKEY
) {
223 aRetVal
.AppendElement(
224 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_KEY_USAGE_UNWRAPKEY
));
228 KeyAlgorithmProxy
& CryptoKey::Algorithm() { return mAlgorithm
; }
230 const KeyAlgorithmProxy
& CryptoKey::Algorithm() const { return mAlgorithm
; }
232 CryptoKey::KeyType
CryptoKey::GetKeyType() const {
233 return static_cast<CryptoKey::KeyType
>(mAttributes
& TYPE_MASK
);
236 nsresult
CryptoKey::SetType(const nsString
& aType
) {
237 mAttributes
&= CLEAR_TYPE
;
238 if (aType
.EqualsLiteral(WEBCRYPTO_KEY_TYPE_SECRET
)) {
239 mAttributes
|= SECRET
;
240 } else if (aType
.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC
)) {
241 mAttributes
|= PUBLIC
;
242 } else if (aType
.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE
)) {
243 mAttributes
|= PRIVATE
;
245 mAttributes
|= UNKNOWN
;
246 return NS_ERROR_DOM_SYNTAX_ERR
;
252 void CryptoKey::SetType(CryptoKey::KeyType aType
) {
253 mAttributes
&= CLEAR_TYPE
;
254 mAttributes
|= aType
;
257 void CryptoKey::SetExtractable(bool aExtractable
) {
258 mAttributes
&= CLEAR_EXTRACTABLE
;
260 mAttributes
|= EXTRACTABLE
;
264 // NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the
265 // public value. To properly export the private key to JWK or PKCS #8 we need
266 // the public key data though and so we use this method to augment a private
267 // key with data from the given public key.
268 nsresult
CryptoKey::AddPublicKeyData(SECKEYPublicKey
* aPublicKey
) {
269 // This should be a private key.
270 MOZ_ASSERT(GetKeyType() == PRIVATE
);
271 // There should be a private NSS key with type 'EC'.
272 MOZ_ASSERT(mPrivateKey
&& mPrivateKey
->keyType
== ecKey
);
273 // The given public key should have the same key type.
274 MOZ_ASSERT(aPublicKey
->keyType
== mPrivateKey
->keyType
);
277 ScopedAutoSECItem params
;
278 SECStatus rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, mPrivateKey
.get(),
279 CKA_EC_PARAMS
, ¶ms
);
280 if (rv
!= SECSuccess
) {
281 return NS_ERROR_DOM_OPERATION_ERR
;
284 // Read private value.
285 ScopedAutoSECItem value
;
286 rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, mPrivateKey
.get(), CKA_VALUE
,
288 if (rv
!= SECSuccess
) {
289 return NS_ERROR_DOM_OPERATION_ERR
;
292 SECItem
* point
= &aPublicKey
->u
.ec
.publicValue
;
293 CK_OBJECT_CLASS privateKeyValue
= CKO_PRIVATE_KEY
;
294 CK_BBOOL falseValue
= CK_FALSE
;
295 CK_KEY_TYPE ecValue
= CKK_EC
;
297 CK_ATTRIBUTE keyTemplate
[9] = {
298 {CKA_CLASS
, &privateKeyValue
, sizeof(privateKeyValue
)},
299 {CKA_KEY_TYPE
, &ecValue
, sizeof(ecValue
)},
300 {CKA_TOKEN
, &falseValue
, sizeof(falseValue
)},
301 {CKA_SENSITIVE
, &falseValue
, sizeof(falseValue
)},
302 {CKA_PRIVATE
, &falseValue
, sizeof(falseValue
)},
303 // PrivateKeyFromPrivateKeyTemplate sets the ID.
304 {CKA_ID
, nullptr, 0},
305 {CKA_EC_PARAMS
, params
.data
, params
.len
},
306 {CKA_EC_POINT
, point
->data
, point
->len
},
307 {CKA_VALUE
, value
.data
, value
.len
},
311 PrivateKeyFromPrivateKeyTemplate(keyTemplate
, ArrayLength(keyTemplate
));
312 NS_ENSURE_TRUE(mPrivateKey
, NS_ERROR_DOM_OPERATION_ERR
);
317 void CryptoKey::ClearUsages() { mAttributes
&= CLEAR_USAGES
; }
319 nsresult
CryptoKey::AddUsage(const nsString
& aUsage
) {
321 if (NS_FAILED(StringToUsage(aUsage
, usage
))) {
322 return NS_ERROR_DOM_SYNTAX_ERR
;
325 MOZ_ASSERT(usage
& USAGES_MASK
, "Usages should be valid");
327 // This is harmless if usage is 0, so we don't repeat the assertion check
332 nsresult
CryptoKey::AddAllowedUsage(const nsString
& aUsage
,
333 const nsString
& aAlgorithm
) {
334 return AddAllowedUsageIntersecting(aUsage
, aAlgorithm
, USAGES_MASK
);
337 nsresult
CryptoKey::AddAllowedUsageIntersecting(const nsString
& aUsage
,
338 const nsString
& aAlgorithm
,
339 uint32_t aUsageMask
) {
340 uint32_t allowedUsages
= GetAllowedUsagesForAlgorithm(aAlgorithm
);
342 if (NS_FAILED(StringToUsage(aUsage
, usage
))) {
343 return NS_ERROR_DOM_SYNTAX_ERR
;
346 if ((usage
& allowedUsages
) != usage
) {
347 return NS_ERROR_DOM_SYNTAX_ERR
;
350 MOZ_ASSERT(usage
& USAGES_MASK
, "Usages should be valid");
352 // This is harmless if usage is 0, so we don't repeat the assertion check
353 if (usage
& aUsageMask
) {
361 void CryptoKey::AddUsage(CryptoKey::KeyUsage aUsage
) { mAttributes
|= aUsage
; }
363 bool CryptoKey::HasAnyUsage() { return !!(mAttributes
& USAGES_MASK
); }
365 bool CryptoKey::HasUsage(CryptoKey::KeyUsage aUsage
) {
366 return !!(mAttributes
& aUsage
);
369 bool CryptoKey::HasUsageOtherThan(uint32_t aUsages
) {
370 return !!(mAttributes
& USAGES_MASK
& ~aUsages
);
373 bool CryptoKey::IsRecognizedUsage(const nsString
& aUsage
) {
375 nsresult rv
= StringToUsage(aUsage
, dummy
);
376 return NS_SUCCEEDED(rv
);
379 bool CryptoKey::AllUsagesRecognized(const Sequence
<nsString
>& aUsages
) {
380 for (uint32_t i
= 0; i
< aUsages
.Length(); ++i
) {
381 if (!IsRecognizedUsage(aUsages
[i
])) {
388 uint32_t CryptoKey::GetAllowedUsagesForAlgorithm(const nsString
& aAlgorithm
) {
389 uint32_t allowedUsages
= 0;
390 if (aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_AES_CTR
) ||
391 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_AES_CBC
) ||
392 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_AES_GCM
) ||
393 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP
)) {
394 allowedUsages
= ENCRYPT
| DECRYPT
| WRAPKEY
| UNWRAPKEY
;
395 } else if (aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_AES_KW
)) {
396 allowedUsages
= WRAPKEY
| UNWRAPKEY
;
397 } else if (aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_HMAC
) ||
398 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1
) ||
399 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS
) ||
400 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_ECDSA
)) {
401 allowedUsages
= SIGN
| VERIFY
;
402 } else if (aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_ECDH
) ||
403 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_HKDF
) ||
404 aAlgorithm
.EqualsASCII(WEBCRYPTO_ALG_PBKDF2
)) {
405 allowedUsages
= DERIVEBITS
| DERIVEKEY
;
407 return allowedUsages
;
410 nsresult
CryptoKey::SetSymKey(const CryptoBuffer
& aSymKey
) {
411 if (!mSymKey
.Assign(aSymKey
)) {
412 return NS_ERROR_OUT_OF_MEMORY
;
418 nsresult
CryptoKey::SetPrivateKey(SECKEYPrivateKey
* aPrivateKey
) {
420 mPrivateKey
= nullptr;
424 mPrivateKey
= UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(aPrivateKey
));
425 return mPrivateKey
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
428 nsresult
CryptoKey::SetPublicKey(SECKEYPublicKey
* aPublicKey
) {
430 mPublicKey
= nullptr;
434 mPublicKey
= UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(aPublicKey
));
435 return mPublicKey
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
438 const CryptoBuffer
& CryptoKey::GetSymKey() const { return mSymKey
; }
440 UniqueSECKEYPrivateKey
CryptoKey::GetPrivateKey() const {
444 return UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(mPrivateKey
.get()));
447 UniqueSECKEYPublicKey
CryptoKey::GetPublicKey() const {
451 return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(mPublicKey
.get()));
454 // Serialization and deserialization convenience methods
456 UniqueSECKEYPrivateKey
CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer
& aKeyData
) {
457 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
462 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
467 SECItem pkcs8Item
= {siBuffer
, nullptr, 0};
468 if (!aKeyData
.ToSECItem(arena
.get(), &pkcs8Item
)) {
472 // Allow everything, we enforce usage ourselves
473 unsigned int usage
= KU_ALL
;
475 SECKEYPrivateKey
* privKey
;
476 SECStatus rv
= PK11_ImportDERPrivateKeyInfoAndReturnKey(
477 slot
.get(), &pkcs8Item
, nullptr, nullptr, false, false, usage
, &privKey
,
480 if (rv
== SECFailure
) {
484 return UniqueSECKEYPrivateKey(privKey
);
487 UniqueSECKEYPublicKey
CryptoKey::PublicKeyFromSpki(CryptoBuffer
& aKeyData
) {
488 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
493 SECItem spkiItem
= {siBuffer
, nullptr, 0};
494 if (!aKeyData
.ToSECItem(arena
.get(), &spkiItem
)) {
498 UniqueCERTSubjectPublicKeyInfo
spki(
499 SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem
));
504 bool isECDHAlgorithm
=
505 SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH
, &spki
->algorithm
.algorithm
);
507 // Check for |id-ecDH|. Per old versions of the WebCrypto spec we must
508 // support this OID but NSS does unfortunately not know it. Let's
509 // change the algorithm to |id-ecPublicKey| to make NSS happy.
510 if (isECDHAlgorithm
) {
511 SECOidTag oid
= SEC_OID_ANSIX962_EC_PUBLIC_KEY
;
513 SECOidData
* oidData
= SECOID_FindOIDByTag(oid
);
518 SECStatus rv
= SECITEM_CopyItem(spki
->arena
, &spki
->algorithm
.algorithm
,
520 if (rv
!= SECSuccess
) {
525 UniqueSECKEYPublicKey
tmp(SECKEY_ExtractPublicKey(spki
.get()));
526 if (!tmp
.get() || !PublicKeyValid(tmp
.get())) {
530 return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(tmp
.get()));
533 nsresult
CryptoKey::PrivateKeyToPkcs8(SECKEYPrivateKey
* aPrivKey
,
534 CryptoBuffer
& aRetVal
) {
535 UniqueSECItem
pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey
, nullptr));
536 if (!pkcs8Item
.get()) {
537 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
539 if (!aRetVal
.Assign(pkcs8Item
.get())) {
540 return NS_ERROR_DOM_OPERATION_ERR
;
545 nsresult
CryptoKey::PublicKeyToSpki(SECKEYPublicKey
* aPubKey
,
546 CryptoBuffer
& aRetVal
) {
547 UniqueCERTSubjectPublicKeyInfo spki
;
549 spki
.reset(SECKEY_CreateSubjectPublicKeyInfo(aPubKey
));
551 return NS_ERROR_DOM_OPERATION_ERR
;
554 const SEC_ASN1Template
* tpl
= SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate
);
555 UniqueSECItem
spkiItem(SEC_ASN1EncodeItem(nullptr, nullptr, spki
.get(), tpl
));
557 if (!aRetVal
.Assign(spkiItem
.get())) {
558 return NS_ERROR_DOM_OPERATION_ERR
;
563 SECItem
* CreateECPointForCoordinates(const CryptoBuffer
& aX
,
564 const CryptoBuffer
& aY
,
565 PLArenaPool
* aArena
) {
566 // Check that both points have the same length.
567 if (aX
.Length() != aY
.Length()) {
573 ::SECITEM_AllocItem(aArena
, nullptr, aX
.Length() + aY
.Length() + 1);
579 point
->data
[0] = EC_POINT_FORM_UNCOMPRESSED
;
580 memcpy(point
->data
+ 1, aX
.Elements(), aX
.Length());
581 memcpy(point
->data
+ 1 + aX
.Length(), aY
.Elements(), aY
.Length());
586 UniqueSECKEYPrivateKey
CryptoKey::PrivateKeyFromJwk(const JsonWebKey
& aJwk
) {
587 CK_OBJECT_CLASS privateKeyValue
= CKO_PRIVATE_KEY
;
588 CK_BBOOL falseValue
= CK_FALSE
;
590 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_EC
)) {
591 // Verify that all of the required parameters are present
592 CryptoBuffer x
, y
, d
;
593 if (!aJwk
.mCrv
.WasPassed() || !aJwk
.mX
.WasPassed() ||
594 NS_FAILED(x
.FromJwkBase64(aJwk
.mX
.Value())) || !aJwk
.mY
.WasPassed() ||
595 NS_FAILED(y
.FromJwkBase64(aJwk
.mY
.Value())) || !aJwk
.mD
.WasPassed() ||
596 NS_FAILED(d
.FromJwkBase64(aJwk
.mD
.Value()))) {
601 if (!NormalizeToken(aJwk
.mCrv
.Value(), namedCurve
)) {
605 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
610 // Create parameters.
611 SECItem
* params
= CreateECParamsForCurve(namedCurve
, arena
.get());
616 SECItem
* ecPoint
= CreateECPointForCoordinates(x
, y
, arena
.get());
621 // Populate template from parameters
622 CK_KEY_TYPE ecValue
= CKK_EC
;
623 CK_ATTRIBUTE keyTemplate
[9] = {
624 {CKA_CLASS
, &privateKeyValue
, sizeof(privateKeyValue
)},
625 {CKA_KEY_TYPE
, &ecValue
, sizeof(ecValue
)},
626 {CKA_TOKEN
, &falseValue
, sizeof(falseValue
)},
627 {CKA_SENSITIVE
, &falseValue
, sizeof(falseValue
)},
628 {CKA_PRIVATE
, &falseValue
, sizeof(falseValue
)},
629 // PrivateKeyFromPrivateKeyTemplate sets the ID.
630 {CKA_ID
, nullptr, 0},
631 {CKA_EC_PARAMS
, params
->data
, params
->len
},
632 {CKA_EC_POINT
, ecPoint
->data
, ecPoint
->len
},
633 {CKA_VALUE
, (void*)d
.Elements(), (CK_ULONG
)d
.Length()},
636 return PrivateKeyFromPrivateKeyTemplate(keyTemplate
,
637 ArrayLength(keyTemplate
));
640 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_RSA
)) {
641 // Verify that all of the required parameters are present
642 CryptoBuffer n
, e
, d
, p
, q
, dp
, dq
, qi
;
643 if (!aJwk
.mN
.WasPassed() || NS_FAILED(n
.FromJwkBase64(aJwk
.mN
.Value())) ||
644 !aJwk
.mE
.WasPassed() || NS_FAILED(e
.FromJwkBase64(aJwk
.mE
.Value())) ||
645 !aJwk
.mD
.WasPassed() || NS_FAILED(d
.FromJwkBase64(aJwk
.mD
.Value())) ||
646 !aJwk
.mP
.WasPassed() || NS_FAILED(p
.FromJwkBase64(aJwk
.mP
.Value())) ||
647 !aJwk
.mQ
.WasPassed() || NS_FAILED(q
.FromJwkBase64(aJwk
.mQ
.Value())) ||
648 !aJwk
.mDp
.WasPassed() ||
649 NS_FAILED(dp
.FromJwkBase64(aJwk
.mDp
.Value())) ||
650 !aJwk
.mDq
.WasPassed() ||
651 NS_FAILED(dq
.FromJwkBase64(aJwk
.mDq
.Value())) ||
652 !aJwk
.mQi
.WasPassed() ||
653 NS_FAILED(qi
.FromJwkBase64(aJwk
.mQi
.Value()))) {
657 // Populate template from parameters
658 CK_KEY_TYPE rsaValue
= CKK_RSA
;
659 CK_ATTRIBUTE keyTemplate
[14] = {
660 {CKA_CLASS
, &privateKeyValue
, sizeof(privateKeyValue
)},
661 {CKA_KEY_TYPE
, &rsaValue
, sizeof(rsaValue
)},
662 {CKA_TOKEN
, &falseValue
, sizeof(falseValue
)},
663 {CKA_SENSITIVE
, &falseValue
, sizeof(falseValue
)},
664 {CKA_PRIVATE
, &falseValue
, sizeof(falseValue
)},
665 // PrivateKeyFromPrivateKeyTemplate sets the ID.
666 {CKA_ID
, nullptr, 0},
667 {CKA_MODULUS
, (void*)n
.Elements(), (CK_ULONG
)n
.Length()},
668 {CKA_PUBLIC_EXPONENT
, (void*)e
.Elements(), (CK_ULONG
)e
.Length()},
669 {CKA_PRIVATE_EXPONENT
, (void*)d
.Elements(), (CK_ULONG
)d
.Length()},
670 {CKA_PRIME_1
, (void*)p
.Elements(), (CK_ULONG
)p
.Length()},
671 {CKA_PRIME_2
, (void*)q
.Elements(), (CK_ULONG
)q
.Length()},
672 {CKA_EXPONENT_1
, (void*)dp
.Elements(), (CK_ULONG
)dp
.Length()},
673 {CKA_EXPONENT_2
, (void*)dq
.Elements(), (CK_ULONG
)dq
.Length()},
674 {CKA_COEFFICIENT
, (void*)qi
.Elements(), (CK_ULONG
)qi
.Length()},
677 return PrivateKeyFromPrivateKeyTemplate(keyTemplate
,
678 ArrayLength(keyTemplate
));
684 bool ReadAndEncodeAttribute(SECKEYPrivateKey
* aKey
,
685 CK_ATTRIBUTE_TYPE aAttribute
,
686 Optional
<nsString
>& aDst
) {
687 ScopedAutoSECItem item
;
688 if (PK11_ReadRawAttribute(PK11_TypePrivKey
, aKey
, aAttribute
, &item
) !=
694 if (!buffer
.Assign(&item
)) {
698 if (NS_FAILED(buffer
.ToJwkBase64(aDst
.Value()))) {
705 bool ECKeyToJwk(const PK11ObjectType aKeyType
, void* aKey
,
706 const SECItem
* aEcParams
, const SECItem
* aPublicValue
,
707 JsonWebKey
& aRetVal
) {
708 aRetVal
.mX
.Construct();
709 aRetVal
.mY
.Construct();
711 // Check that the given EC parameters are valid.
712 if (!CheckEncodedECParameters(aEcParams
)) {
716 // Construct the OID tag.
717 SECItem oid
= {siBuffer
, nullptr, 0};
718 oid
.len
= aEcParams
->data
[1];
719 oid
.data
= aEcParams
->data
+ 2;
722 switch (SECOID_FindOIDTag(&oid
)) {
723 case SEC_OID_SECG_EC_SECP256R1
:
725 aRetVal
.mCrv
.Construct(
726 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_P256
));
728 case SEC_OID_SECG_EC_SECP384R1
:
730 aRetVal
.mCrv
.Construct(
731 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_P384
));
733 case SEC_OID_SECG_EC_SECP521R1
:
735 aRetVal
.mCrv
.Construct(
736 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_P521
));
742 // No support for compressed points.
743 if (aPublicValue
->data
[0] != EC_POINT_FORM_UNCOMPRESSED
) {
747 // Check length of uncompressed point coordinates.
748 if (aPublicValue
->len
!= (2 * flen
+ 1)) {
752 UniqueSECItem
ecPointX(::SECITEM_AllocItem(nullptr, nullptr, flen
));
753 UniqueSECItem
ecPointY(::SECITEM_AllocItem(nullptr, nullptr, flen
));
754 if (!ecPointX
|| !ecPointY
) {
758 // Extract point data.
759 memcpy(ecPointX
->data
, aPublicValue
->data
+ 1, flen
);
760 memcpy(ecPointY
->data
, aPublicValue
->data
+ 1 + flen
, flen
);
763 if (!x
.Assign(ecPointX
.get()) ||
764 NS_FAILED(x
.ToJwkBase64(aRetVal
.mX
.Value())) ||
765 !y
.Assign(ecPointY
.get()) ||
766 NS_FAILED(y
.ToJwkBase64(aRetVal
.mY
.Value()))) {
770 aRetVal
.mKty
= NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_EC
);
774 nsresult
CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey
* aPrivKey
,
775 JsonWebKey
& aRetVal
) {
776 switch (aPrivKey
->keyType
) {
778 aRetVal
.mN
.Construct();
779 aRetVal
.mE
.Construct();
780 aRetVal
.mD
.Construct();
781 aRetVal
.mP
.Construct();
782 aRetVal
.mQ
.Construct();
783 aRetVal
.mDp
.Construct();
784 aRetVal
.mDq
.Construct();
785 aRetVal
.mQi
.Construct();
787 if (!ReadAndEncodeAttribute(aPrivKey
, CKA_MODULUS
, aRetVal
.mN
) ||
788 !ReadAndEncodeAttribute(aPrivKey
, CKA_PUBLIC_EXPONENT
, aRetVal
.mE
) ||
789 !ReadAndEncodeAttribute(aPrivKey
, CKA_PRIVATE_EXPONENT
, aRetVal
.mD
) ||
790 !ReadAndEncodeAttribute(aPrivKey
, CKA_PRIME_1
, aRetVal
.mP
) ||
791 !ReadAndEncodeAttribute(aPrivKey
, CKA_PRIME_2
, aRetVal
.mQ
) ||
792 !ReadAndEncodeAttribute(aPrivKey
, CKA_EXPONENT_1
, aRetVal
.mDp
) ||
793 !ReadAndEncodeAttribute(aPrivKey
, CKA_EXPONENT_2
, aRetVal
.mDq
) ||
794 !ReadAndEncodeAttribute(aPrivKey
, CKA_COEFFICIENT
, aRetVal
.mQi
)) {
795 return NS_ERROR_DOM_OPERATION_ERR
;
798 aRetVal
.mKty
= NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_RSA
);
803 ScopedAutoSECItem params
;
804 SECStatus rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, aPrivKey
,
805 CKA_EC_PARAMS
, ¶ms
);
806 if (rv
!= SECSuccess
) {
807 return NS_ERROR_DOM_OPERATION_ERR
;
810 // Read public point Q.
811 ScopedAutoSECItem ecPoint
;
812 rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, aPrivKey
, CKA_EC_POINT
,
814 if (rv
!= SECSuccess
) {
815 return NS_ERROR_DOM_OPERATION_ERR
;
818 if (!ECKeyToJwk(PK11_TypePrivKey
, aPrivKey
, ¶ms
, &ecPoint
, aRetVal
)) {
819 return NS_ERROR_DOM_OPERATION_ERR
;
822 aRetVal
.mD
.Construct();
824 // Read private value.
825 if (!ReadAndEncodeAttribute(aPrivKey
, CKA_VALUE
, aRetVal
.mD
)) {
826 return NS_ERROR_DOM_OPERATION_ERR
;
832 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
836 UniqueSECKEYPublicKey
CreateECPublicKey(const SECItem
* aKeyData
,
837 const nsString
& aNamedCurve
) {
838 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
843 // It's important that this be a UniqueSECKEYPublicKey, as this ensures that
844 // SECKEY_DestroyPublicKey will be called on it. If this doesn't happen, when
845 // CryptoKey::PublicKeyValid is called on it and it gets moved to the internal
846 // PKCS#11 slot, it will leak a reference to the slot.
847 UniqueSECKEYPublicKey
key(PORT_ArenaZNew(arena
.get(), SECKEYPublicKey
));
852 key
->arena
= nullptr; // key doesn't own the arena; it won't get double-freed
853 key
->keyType
= ecKey
;
854 key
->pkcs11Slot
= nullptr;
855 key
->pkcs11ID
= CK_INVALID_HANDLE
;
857 // Create curve parameters.
858 SECItem
* params
= CreateECParamsForCurve(aNamedCurve
, arena
.get());
862 key
->u
.ec
.DEREncodedParams
= *params
;
865 key
->u
.ec
.publicValue
= *aKeyData
;
867 // Ensure the given point is on the curve.
868 if (!CryptoKey::PublicKeyValid(key
.get())) {
872 return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(key
.get()));
875 UniqueSECKEYPublicKey
CryptoKey::PublicKeyFromJwk(const JsonWebKey
& aJwk
) {
876 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_RSA
)) {
877 // Verify that all of the required parameters are present
879 if (!aJwk
.mN
.WasPassed() || NS_FAILED(n
.FromJwkBase64(aJwk
.mN
.Value())) ||
880 !aJwk
.mE
.WasPassed() || NS_FAILED(e
.FromJwkBase64(aJwk
.mE
.Value()))) {
884 // Transcode to a DER RSAPublicKey structure
885 struct RSAPublicKeyData
{
889 const RSAPublicKeyData input
= {
890 {siUnsignedInteger
, n
.Elements(), (unsigned int)n
.Length()},
891 {siUnsignedInteger
, e
.Elements(), (unsigned int)e
.Length()}};
892 const SEC_ASN1Template rsaPublicKeyTemplate
[] = {
893 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(RSAPublicKeyData
)},
896 offsetof(RSAPublicKeyData
, n
),
900 offsetof(RSAPublicKeyData
, e
),
907 SEC_ASN1EncodeItem(nullptr, nullptr, &input
, rsaPublicKeyTemplate
));
912 return UniqueSECKEYPublicKey(
913 SECKEY_ImportDERPublicKey(pkDer
.get(), CKK_RSA
));
916 if (aJwk
.mKty
.EqualsLiteral(JWK_TYPE_EC
)) {
917 // Verify that all of the required parameters are present
919 if (!aJwk
.mCrv
.WasPassed() || !aJwk
.mX
.WasPassed() ||
920 NS_FAILED(x
.FromJwkBase64(aJwk
.mX
.Value())) || !aJwk
.mY
.WasPassed() ||
921 NS_FAILED(y
.FromJwkBase64(aJwk
.mY
.Value()))) {
925 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
931 SECItem
* point
= CreateECPointForCoordinates(x
, y
, arena
.get());
937 if (!NormalizeToken(aJwk
.mCrv
.Value(), namedCurve
)) {
941 return CreateECPublicKey(point
, namedCurve
);
947 nsresult
CryptoKey::PublicKeyToJwk(SECKEYPublicKey
* aPubKey
,
948 JsonWebKey
& aRetVal
) {
949 switch (aPubKey
->keyType
) {
952 aRetVal
.mN
.Construct();
953 aRetVal
.mE
.Construct();
955 if (!n
.Assign(&aPubKey
->u
.rsa
.modulus
) ||
956 !e
.Assign(&aPubKey
->u
.rsa
.publicExponent
) ||
957 NS_FAILED(n
.ToJwkBase64(aRetVal
.mN
.Value())) ||
958 NS_FAILED(e
.ToJwkBase64(aRetVal
.mE
.Value()))) {
959 return NS_ERROR_DOM_OPERATION_ERR
;
962 aRetVal
.mKty
= NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_RSA
);
966 if (!ECKeyToJwk(PK11_TypePubKey
, aPubKey
, &aPubKey
->u
.ec
.DEREncodedParams
,
967 &aPubKey
->u
.ec
.publicValue
, aRetVal
)) {
968 return NS_ERROR_DOM_OPERATION_ERR
;
972 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
976 UniqueSECKEYPublicKey
CryptoKey::PublicECKeyFromRaw(
977 CryptoBuffer
& aKeyData
, const nsString
& aNamedCurve
) {
978 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
983 SECItem rawItem
= {siBuffer
, nullptr, 0};
984 if (!aKeyData
.ToSECItem(arena
.get(), &rawItem
)) {
989 if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256
)) {
991 } else if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384
)) {
993 } else if (aNamedCurve
.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521
)) {
999 // Check length of uncompressed point coordinates. There are 2 field elements
1000 // and a leading point form octet (which must EC_POINT_FORM_UNCOMPRESSED).
1001 if (rawItem
.len
!= (2 * flen
+ 1)) {
1005 // No support for compressed points.
1006 if (rawItem
.data
[0] != EC_POINT_FORM_UNCOMPRESSED
) {
1010 return CreateECPublicKey(&rawItem
, aNamedCurve
);
1013 nsresult
CryptoKey::PublicECKeyToRaw(SECKEYPublicKey
* aPubKey
,
1014 CryptoBuffer
& aRetVal
) {
1015 if (!aRetVal
.Assign(&aPubKey
->u
.ec
.publicValue
)) {
1016 return NS_ERROR_DOM_OPERATION_ERR
;
1021 bool CryptoKey::PublicKeyValid(SECKEYPublicKey
* aPubKey
) {
1022 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
1027 // This assumes that NSS checks the validity of a public key when
1028 // it is imported into a PKCS#11 module, and returns CK_INVALID_HANDLE
1029 // if it is invalid.
1030 CK_OBJECT_HANDLE id
= PK11_ImportPublicKey(slot
.get(), aPubKey
, PR_FALSE
);
1031 if (id
== CK_INVALID_HANDLE
) {
1035 SECStatus rv
= PK11_DestroyObject(slot
.get(), id
);
1036 return (rv
== SECSuccess
);
1039 bool CryptoKey::WriteStructuredClone(JSContext
* aCX
,
1040 JSStructuredCloneWriter
* aWriter
) const {
1041 // Write in five pieces
1043 // 2. Symmetric key as raw (if present)
1044 // 3. Private key as pkcs8 (if present)
1045 // 4. Public key as spki (if present)
1046 // 5. Algorithm in whatever form it chooses
1047 CryptoBuffer priv
, pub
;
1050 if (NS_FAILED(CryptoKey::PrivateKeyToPkcs8(mPrivateKey
.get(), priv
))) {
1056 if (NS_FAILED(CryptoKey::PublicKeyToSpki(mPublicKey
.get(), pub
))) {
1061 return JS_WriteUint32Pair(aWriter
, mAttributes
, CRYPTOKEY_SC_VERSION
) &&
1062 WriteBuffer(aWriter
, mSymKey
) && WriteBuffer(aWriter
, priv
) &&
1063 WriteBuffer(aWriter
, pub
) && mAlgorithm
.WriteStructuredClone(aWriter
);
1067 already_AddRefed
<CryptoKey
> CryptoKey::ReadStructuredClone(
1068 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
1069 JSStructuredCloneReader
* aReader
) {
1070 // Ensure that NSS is initialized.
1071 if (!EnsureNSSInitializedChromeOrContent()) {
1075 RefPtr
<CryptoKey
> key
= new CryptoKey(aGlobal
);
1078 CryptoBuffer sym
, priv
, pub
;
1080 bool read
= JS_ReadUint32Pair(aReader
, &key
->mAttributes
, &version
) &&
1081 (version
== CRYPTOKEY_SC_VERSION
) && ReadBuffer(aReader
, sym
) &&
1082 ReadBuffer(aReader
, priv
) && ReadBuffer(aReader
, pub
) &&
1083 key
->mAlgorithm
.ReadStructuredClone(aReader
);
1088 if (sym
.Length() > 0 && !key
->mSymKey
.Assign(sym
)) {
1091 if (priv
.Length() > 0) {
1092 key
->mPrivateKey
= CryptoKey::PrivateKeyFromPkcs8(priv
);
1094 if (pub
.Length() > 0) {
1095 key
->mPublicKey
= CryptoKey::PublicKeyFromSpki(pub
);
1098 // Ensure that what we've read is consistent
1099 // If the attributes indicate a key type, should have a key of that type
1100 if (!((key
->GetKeyType() == SECRET
&& key
->mSymKey
.Length() > 0) ||
1101 (key
->GetKeyType() == PRIVATE
&& key
->mPrivateKey
) ||
1102 (key
->GetKeyType() == PUBLIC
&& key
->mPublicKey
))) {
1106 return key
.forget();
1110 } // namespace mozilla