Bug 1270832 - Activate standard c++ library hardening r=glandium
[gecko.git] / dom / crypto / CryptoKey.cpp
blobf2f13c1cb4a13ddb69ec03cb93ec33227b031fbf
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"
9 #include <cstddef>
10 #include <cstring>
11 #include <memory>
12 #include <new>
13 #include <utility>
14 #include "blapit.h"
15 #include "certt.h"
16 #include "js/StructuredClone.h"
17 #include "js/TypeDecls.h"
18 #include "keyhi.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"
27 #include "nsDebug.h"
28 #include "nsError.h"
29 #include "nsLiteralString.h"
30 #include "nsNSSComponent.h"
31 #include "nsStringFlags.h"
32 #include "nsTArray.h"
33 #include "pk11pub.h"
34 #include "pkcs11t.h"
35 #include "plarena.h"
36 #include "prtypes.h"
37 #include "secasn1.h"
38 #include "secasn1t.h"
39 #include "seccomon.h"
40 #include "secdert.h"
41 #include "secitem.h"
42 #include "secmodt.h"
43 #include "secoid.h"
44 #include "secoidt.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)
54 NS_INTERFACE_MAP_END
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;
73 } else {
74 return NS_ERROR_DOM_SYNTAX_ERR;
76 return NS_OK;
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
92 // length of 0.
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());
97 if (!slot) {
98 return nullptr;
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) {
105 return nullptr;
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
115 // fail.
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) {
120 return nullptr;
122 preexistingKey = PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr);
123 if (preexistingKey) {
124 DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey);
125 return nullptr;
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) {
133 return nullptr;
135 idAttributeSlot = aTemplate + i;
136 break;
139 if (!idAttributeSlot) {
140 return nullptr;
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;
151 if (!obj) {
152 return nullptr;
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)
161 : mGlobal(aGlobal),
162 mAttributes(0),
163 mSymKey(),
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;
174 switch (type) {
175 case PUBLIC:
176 aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC);
177 break;
178 case PRIVATE:
179 aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE);
180 break;
181 case SECRET:
182 aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_SECRET);
183 break;
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);
197 break;
198 case KeyAlgorithmProxy::KDF:
199 converted = ToJSValue(cx, mAlgorithm.mKDF, &val);
200 break;
201 case KeyAlgorithmProxy::HMAC:
202 converted = ToJSValue(cx, mAlgorithm.mHmac, &val);
203 break;
204 case KeyAlgorithmProxy::RSA: {
205 RootedDictionary<RsaHashedKeyAlgorithm> rsa(cx);
206 converted = mAlgorithm.mRsa.ToKeyAlgorithm(cx, rsa, aRv);
207 if (converted) {
208 converted = ToJSValue(cx, rsa, &val);
210 break;
212 case KeyAlgorithmProxy::EC:
213 converted = ToJSValue(cx, mAlgorithm.mEc, &val);
214 break;
215 case KeyAlgorithmProxy::OKP:
216 converted = ToJSValue(cx, mAlgorithm.mEd, &val);
217 break;
219 if (!converted) {
220 aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
221 return;
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;
278 } else {
279 mAttributes |= UNKNOWN;
280 return NS_ERROR_DOM_SYNTAX_ERR;
283 return NS_OK;
286 void CryptoKey::SetType(CryptoKey::KeyType aType) {
287 mAttributes &= CLEAR_TYPE;
288 mAttributes |= aType;
291 void CryptoKey::SetExtractable(bool aExtractable) {
292 mAttributes &= CLEAR_EXTRACTABLE;
293 if (aExtractable) {
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);
312 // Read EC params.
313 ScopedAutoSECItem params;
314 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey.get(),
315 CKA_EC_PARAMS, &params);
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,
323 &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.
336 CK_KEY_TYPE ecValue;
337 if (mPrivateKey->keyType == ecKey) {
338 ecValue = CKK_EC;
339 } else if (mPrivateKey->keyType == edKey) {
340 ecValue = CKK_EC_EDWARDS;
341 } else if (mPrivateKey->keyType == ecMontKey) {
342 ecValue = CKK_EC_MONTGOMERY;
343 } else {
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},
360 mPrivateKey =
361 PrivateKeyFromPrivateKeyTemplate(keyTemplate, std::size(keyTemplate));
362 NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR);
364 return NS_OK;
367 void CryptoKey::ClearUsages() { mAttributes &= CLEAR_USAGES; }
369 nsresult CryptoKey::AddUsage(const nsString& aUsage) {
370 KeyUsage usage;
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
378 AddUsage(usage);
379 return NS_OK;
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);
391 KeyUsage usage;
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) {
404 AddUsage(usage);
405 return NS_OK;
408 return NS_OK;
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) {
424 KeyUsage dummy;
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])) {
432 return false;
435 return true;
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;
467 return NS_OK;
470 nsresult CryptoKey::SetPrivateKey(SECKEYPrivateKey* aPrivateKey) {
471 if (!aPrivateKey) {
472 mPrivateKey = nullptr;
473 return NS_OK;
476 mPrivateKey = UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(aPrivateKey));
477 return mPrivateKey ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
480 nsresult CryptoKey::SetPublicKey(SECKEYPublicKey* aPublicKey) {
481 if (!aPublicKey) {
482 mPublicKey = nullptr;
483 return NS_OK;
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 {
493 if (!mPrivateKey) {
494 return nullptr;
496 return UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(mPrivateKey.get()));
499 UniqueSECKEYPublicKey CryptoKey::GetPublicKey() const {
500 if (!mPublicKey) {
501 return nullptr;
503 return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(mPublicKey.get()));
506 // Serialization and deserialization convenience methods
508 UniqueSECKEYPrivateKey CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData) {
509 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
510 if (!slot) {
511 return nullptr;
514 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
515 if (!arena) {
516 return nullptr;
519 SECItem pkcs8Item = {siBuffer, nullptr, 0};
520 if (!aKeyData.ToSECItem(arena.get(), &pkcs8Item)) {
521 return nullptr;
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,
530 nullptr);
532 if (rv == SECFailure) {
533 return nullptr;
536 return UniqueSECKEYPrivateKey(privKey);
539 UniqueSECKEYPublicKey CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData) {
540 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
541 if (!arena) {
542 return nullptr;
545 SECItem spkiItem = {siBuffer, nullptr, 0};
546 if (!aKeyData.ToSECItem(arena.get(), &spkiItem)) {
547 return nullptr;
550 UniqueCERTSubjectPublicKeyInfo spki(
551 SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
552 if (!spki) {
553 return nullptr;
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);
566 if (!oidData) {
567 return nullptr;
570 SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
571 &oidData->oid);
572 if (rv != SECSuccess) {
573 return nullptr;
577 UniqueSECKEYPublicKey tmp(SECKEY_ExtractPublicKey(spki.get()));
578 if (!tmp.get() || !PublicKeyValid(tmp.get())) {
579 return nullptr;
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;
594 return NS_OK;
597 nsresult CryptoKey::PublicKeyToSpki(SECKEYPublicKey* aPubKey,
598 CryptoBuffer& aRetVal) {
599 UniqueCERTSubjectPublicKeyInfo spki;
601 spki.reset(SECKEY_CreateSubjectPublicKeyInfo(aPubKey));
602 if (!spki) {
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;
612 return NS_OK;
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()) {
620 return nullptr;
623 // Create point.
624 SECItem* point =
625 ::SECITEM_AllocItem(aArena, nullptr, aX.Length() + aY.Length() + 1);
626 if (!point) {
627 return nullptr;
630 // Set point data.
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());
635 return point;
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
644 * NS_OK or not. */
645 return NS_ERROR_DOM_OPERATION_ERR;
648 return NS_OK;
651 SECItem* CreateEDPointForXCoordinate(const CryptoBuffer& aX,
652 PLArenaPool* aArena) {
653 if (NS_FAILED(CheckEDKeyLen(aX))) {
654 return nullptr;
657 // Create point.
658 SECItem* point = ::SECITEM_AllocItem(aArena, nullptr, aX.Length());
659 if (!point) {
660 return nullptr;
663 // Set point data.
664 memcpy(point->data, aX.Elements(), aX.Length());
665 return point;
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()))) {
679 return nullptr;
682 nsString namedCurve;
683 if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
684 return nullptr;
687 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
688 if (!arena) {
689 return nullptr;
692 // Create parameters.
693 SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
694 if (!params) {
695 return nullptr;
698 SECItem* ecPoint = CreateECPointForCoordinates(x, y, arena.get());
699 if (!ecPoint) {
700 return nullptr;
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()))) {
736 return nullptr;
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)) {
764 CryptoBuffer x, d;
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()))) {
769 return nullptr;
772 nsString namedCurve;
773 if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
774 return nullptr;
777 if (!namedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519) &&
778 !namedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519)) {
779 return nullptr;
782 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
783 if (!arena) {
784 return nullptr;
787 // Create parameters.
788 SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
789 if (!params) {
790 return nullptr;
793 SECItem* ecPoint = CreateEDPointForXCoordinate(x, arena.get());
794 if (!ecPoint) {
795 return nullptr;
798 if (CheckEDKeyLen(d) != NS_OK) {
799 return nullptr;
802 // Populate template from parameters
803 CK_KEY_TYPE ecValue;
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;
808 } else {
809 return nullptr;
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));
829 return nullptr;
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) !=
837 SECSuccess) {
838 return false;
841 CryptoBuffer buffer;
842 if (!buffer.Assign(&item)) {
843 return false;
846 if (NS_FAILED(buffer.ToJwkBase64(aDst.Value()))) {
847 return false;
850 return true;
853 bool OKPKeyToJwk(const SECItem* aEcParams, const SECItem* aPublicValue,
854 JsonWebKey& aRetVal) {
855 aRetVal.mX.Construct();
857 SECOidTag tag;
858 if (!FindOIDTagForEncodedParameters(aEcParams, &tag)) {
859 return false;
862 uint32_t flen;
863 switch (tag) {
864 case SEC_OID_ED25519_PUBLIC_KEY:
865 flen = 32;
866 aRetVal.mCrv.Construct(
867 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_ED25519));
868 break;
869 case SEC_OID_X25519:
870 flen = 32;
871 aRetVal.mCrv.Construct(
872 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_CURVE25519));
873 break;
874 default:
875 return false;
878 /* No compression is used. */
879 if (aPublicValue->len != flen) {
880 return false;
883 CryptoBuffer x;
884 if (!x.Assign(aPublicValue) || NS_FAILED(x.ToJwkBase64(aRetVal.mX.Value()))) {
885 return false;
888 aRetVal.mKty = NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_OKP);
889 return true;
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.
899 SECOidTag tag;
900 if (!FindOIDTagForEncodedParameters(aEcParams, &tag)) {
901 return false;
904 uint32_t flen;
905 switch (tag) {
906 case SEC_OID_SECG_EC_SECP256R1:
907 flen = 32; // bytes
908 aRetVal.mCrv.Construct(
909 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_P256));
910 break;
911 case SEC_OID_SECG_EC_SECP384R1:
912 flen = 48; // bytes
913 aRetVal.mCrv.Construct(
914 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_P384));
915 break;
916 case SEC_OID_SECG_EC_SECP521R1:
917 flen = 66; // bytes
918 aRetVal.mCrv.Construct(
919 NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_P521));
920 break;
921 default:
922 return false;
925 // No support for compressed points.
926 if (aPublicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) {
927 return false;
930 // Check length of uncompressed point coordinates.
931 if (aPublicValue->len != (2 * flen + 1)) {
932 return false;
935 UniqueSECItem ecPointX(::SECITEM_AllocItem(nullptr, nullptr, flen));
936 UniqueSECItem ecPointY(::SECITEM_AllocItem(nullptr, nullptr, flen));
937 if (!ecPointX || !ecPointY) {
938 return false;
941 // Extract point data.
942 memcpy(ecPointX->data, aPublicValue->data + 1, flen);
943 memcpy(ecPointY->data, aPublicValue->data + 1 + flen, flen);
945 CryptoBuffer x, y;
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()))) {
950 return false;
953 aRetVal.mKty = NS_LITERAL_STRING_FROM_CSTRING(JWK_TYPE_EC);
954 return true;
957 nsresult CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
958 JsonWebKey& aRetVal) {
959 switch (aPrivKey->keyType) {
960 case rsaKey: {
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);
982 return NS_OK;
985 case edKey:
986 case ecMontKey: {
987 // Read EC params.
988 ScopedAutoSECItem params;
989 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey,
990 CKA_EC_PARAMS, &params);
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,
998 &ecPoint);
1000 if (rv != SECSuccess) {
1001 return NS_ERROR_DOM_OPERATION_ERR;
1004 if (!OKPKeyToJwk(&params, &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;
1015 return NS_OK;
1017 case ecKey: {
1018 // Read EC params.
1019 ScopedAutoSECItem params;
1020 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey,
1021 CKA_EC_PARAMS, &params);
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,
1029 &ecPoint);
1030 if (rv != SECSuccess) {
1031 return NS_ERROR_DOM_OPERATION_ERR;
1034 if (!ECKeyToJwk(PK11_TypePrivKey, aPrivKey, &params, &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;
1045 return NS_OK;
1047 default:
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)) {
1058 t = ecKey;
1059 } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519)) {
1060 t = edKey;
1061 } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519)) {
1062 t = ecMontKey;
1064 return t;
1067 /* The function is used for EC and ED keys. */
1068 UniqueSECKEYPublicKey CreateECPublicKey(const SECItem* aKeyData,
1069 const nsAString& aNamedCurve) {
1070 if (!EnsureNSSInitializedChromeOrContent()) {
1071 return nullptr;
1074 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
1075 if (!arena) {
1076 return nullptr;
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));
1084 if (!key) {
1085 return nullptr;
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) {
1093 return nullptr;
1096 key->pkcs11Slot = nullptr;
1097 key->pkcs11ID = CK_INVALID_HANDLE;
1099 // Create curve parameters.
1100 SECItem* params = CreateECParamsForCurve(aNamedCurve, key->arena);
1101 if (!params) {
1102 return nullptr;
1104 key->u.ec.DEREncodedParams = *params;
1106 // Set public point.
1107 SECStatus ret =
1108 SECITEM_CopyItem(key->arena, &key->u.ec.publicValue, aKeyData);
1109 if (NS_WARN_IF(ret != SECSuccess)) {
1110 return nullptr;
1113 // Ensure the given point is on the curve.
1114 if (!CryptoKey::PublicKeyValid(key.get())) {
1115 return nullptr;
1118 return key;
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
1124 CryptoBuffer n, e;
1125 if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
1126 !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value()))) {
1127 return nullptr;
1130 // Transcode to a DER RSAPublicKey structure
1131 struct RSAPublicKeyData {
1132 SECItem n;
1133 SECItem e;
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)},
1141 SEC_ASN1_INTEGER,
1142 offsetof(RSAPublicKeyData, n),
1145 SEC_ASN1_INTEGER,
1146 offsetof(RSAPublicKeyData, e),
1152 UniqueSECItem pkDer(
1153 SEC_ASN1EncodeItem(nullptr, nullptr, &input, rsaPublicKeyTemplate));
1154 if (!pkDer.get()) {
1155 return nullptr;
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
1164 CryptoBuffer x, y;
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()))) {
1168 return nullptr;
1171 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
1172 if (!arena) {
1173 return nullptr;
1176 // Create point.
1177 SECItem* point = CreateECPointForCoordinates(x, y, arena.get());
1178 if (!point) {
1179 return nullptr;
1182 nsString namedCurve;
1183 if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
1184 return nullptr;
1187 return CreateECPublicKey(point, namedCurve);
1190 if (aJwk.mKty.EqualsLiteral(JWK_TYPE_OKP)) {
1191 // Verify that all of the required parameters are present
1192 CryptoBuffer x;
1193 if (!aJwk.mCrv.WasPassed() || !aJwk.mX.WasPassed() ||
1194 NS_FAILED(x.FromJwkBase64(aJwk.mX.Value()))) {
1195 return nullptr;
1198 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
1199 if (!arena) {
1200 return nullptr;
1203 // Create point.
1204 SECItem* point = CreateEDPointForXCoordinate(x, arena.get());
1205 if (!point) {
1206 return nullptr;
1209 nsString namedCurve;
1210 if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
1211 return nullptr;
1214 if (!namedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519) &&
1215 !namedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519)) {
1216 return nullptr;
1219 return CreateECPublicKey(point, namedCurve);
1222 return nullptr;
1225 nsresult CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
1226 JsonWebKey& aRetVal) {
1227 switch (aPubKey->keyType) {
1228 case rsaKey: {
1229 CryptoBuffer n, e;
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);
1241 return NS_OK;
1243 case edKey:
1244 case ecMontKey:
1245 if (!OKPKeyToJwk(&aPubKey->u.ec.DEREncodedParams,
1246 &aPubKey->u.ec.publicValue, aRetVal)) {
1247 return NS_ERROR_DOM_OPERATION_ERR;
1249 return NS_OK;
1250 case ecKey: {
1251 if (!ECKeyToJwk(PK11_TypePubKey, aPubKey, &aPubKey->u.ec.DEREncodedParams,
1252 &aPubKey->u.ec.publicValue, aRetVal)) {
1253 return NS_ERROR_DOM_OPERATION_ERR;
1255 return NS_OK;
1257 default:
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));
1265 if (!arena) {
1266 return nullptr;
1269 SECItem rawItem = {siBuffer, nullptr, 0};
1270 if (!aKeyData.ToSECItem(arena.get(), &rawItem)) {
1271 return nullptr;
1274 uint32_t flen;
1275 if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256)) {
1276 flen = 32; // bytes
1277 } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384)) {
1278 flen = 48; // bytes
1279 } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521)) {
1280 flen = 66; // bytes
1281 } else {
1282 return nullptr;
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)) {
1288 return nullptr;
1291 // No support for compressed points.
1292 if (rawItem.data[0] != EC_POINT_FORM_UNCOMPRESSED) {
1293 return nullptr;
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;
1304 return NS_OK;
1307 UniqueSECKEYPublicKey CryptoKey::PublicOKPKeyFromRaw(
1308 CryptoBuffer& aKeyData, const nsString& aNamedCurve) {
1309 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
1310 if (!arena) {
1311 return nullptr;
1314 SECItem rawItem = {siBuffer, nullptr, 0};
1315 if (!aKeyData.ToSECItem(arena.get(), &rawItem)) {
1316 return nullptr;
1319 uint32_t flen;
1320 if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_ED25519)) {
1321 flen = 32; // bytes
1322 } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_CURVE25519)) {
1323 flen = 32;
1324 } else {
1325 return nullptr;
1328 if (rawItem.len != flen) {
1329 return nullptr;
1332 return CreateECPublicKey(&rawItem, aNamedCurve);
1335 bool CryptoKey::PublicKeyValid(SECKEYPublicKey* aPubKey) {
1336 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
1337 if (!slot.get()) {
1338 return false;
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
1351 // 1. Attributes
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;
1358 if (mPrivateKey) {
1359 if (NS_FAILED(CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), priv))) {
1360 return false;
1364 if (mPublicKey) {
1365 if (NS_FAILED(CryptoKey::PublicKeyToSpki(mPublicKey.get(), pub))) {
1366 return false;
1370 return JS_WriteUint32Pair(aWriter, mAttributes, CRYPTOKEY_SC_VERSION) &&
1371 WriteBuffer(aWriter, mSymKey) && WriteBuffer(aWriter, priv) &&
1372 WriteBuffer(aWriter, pub) && mAlgorithm.WriteStructuredClone(aWriter);
1375 // static
1376 already_AddRefed<CryptoKey> CryptoKey::ReadStructuredClone(
1377 JSContext* aCx, nsIGlobalObject* aGlobal,
1378 JSStructuredCloneReader* aReader) {
1379 // Ensure that NSS is initialized.
1380 if (!EnsureNSSInitializedChromeOrContent()) {
1381 return nullptr;
1384 RefPtr<CryptoKey> key = new CryptoKey(aGlobal);
1386 uint32_t version;
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);
1393 if (!read) {
1394 return nullptr;
1397 if (sym.Length() > 0 && !key->mSymKey.Assign(sym)) {
1398 return nullptr;
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))) {
1412 return nullptr;
1415 return key.forget();
1418 } // namespace mozilla::dom