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/. */
6
7 #include "CryptoKey.h"
8
9 #include "cryptohi.h"
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/dom/SubtleCryptoBinding.h"
12 #include "mozilla/dom/ToJSValue.h"
13 #include "nsNSSComponent.h"
14 #include "pk11pub.h"
15
16 namespace mozilla {
17 namespace dom {
18
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey,mGlobal)19 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey, mGlobal)
20 NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKey)
21 NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKey)
22 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKey)
23 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
24 NS_INTERFACE_MAP_ENTRY(nsISupports)
25 NS_INTERFACE_MAP_END
26
27 nsresult StringToUsage(const nsString& aUsage, CryptoKey::KeyUsage& aUsageOut) {
28 if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_ENCRYPT)) {
29 aUsageOut = CryptoKey::ENCRYPT;
30 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DECRYPT)) {
31 aUsageOut = CryptoKey::DECRYPT;
32 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_SIGN)) {
33 aUsageOut = CryptoKey::SIGN;
34 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_VERIFY)) {
35 aUsageOut = CryptoKey::VERIFY;
36 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEKEY)) {
37 aUsageOut = CryptoKey::DERIVEKEY;
38 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEBITS)) {
39 aUsageOut = CryptoKey::DERIVEBITS;
40 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_WRAPKEY)) {
41 aUsageOut = CryptoKey::WRAPKEY;
42 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_UNWRAPKEY)) {
43 aUsageOut = CryptoKey::UNWRAPKEY;
44 } else {
45 return NS_ERROR_DOM_SYNTAX_ERR;
46 }
47 return NS_OK;
48 }
49
50 // This helper function will release the memory backing a SECKEYPrivateKey and
51 // any resources acquired in its creation. It will leave the backing PKCS#11
52 // object untouched, however. This should only be called from
53 // PrivateKeyFromPrivateKeyTemplate.
DestroyPrivateKeyWithoutDestroyingPKCS11Object(SECKEYPrivateKey * key)54 static void DestroyPrivateKeyWithoutDestroyingPKCS11Object(
55 SECKEYPrivateKey* key) {
56 PK11_FreeSlot(key->pkcs11Slot);
57 PORT_FreeArena(key->arena, PR_TRUE);
58 }
59
60 // To protect against key ID collisions, PrivateKeyFromPrivateKeyTemplate
61 // generates a random ID for each key. The given template must contain an
62 // attribute slot for a key ID, but it must consist of a null pointer and have a
63 // length of 0.
PrivateKeyFromPrivateKeyTemplate(CK_ATTRIBUTE * aTemplate,CK_ULONG aTemplateSize)64 UniqueSECKEYPrivateKey PrivateKeyFromPrivateKeyTemplate(
65 CK_ATTRIBUTE* aTemplate, CK_ULONG aTemplateSize) {
66 // Create a generic object with the contents of the key
67 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
68 if (!slot) {
69 return nullptr;
70 }
71
72 // Generate a random 160-bit object ID. This ID must be unique.
73 UniqueSECItem objID(::SECITEM_AllocItem(nullptr, nullptr, 20));
74 SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), objID->data, objID->len);
75 if (rv != SECSuccess) {
76 return nullptr;
77 }
78 // Check if something is already using this ID.
79 SECKEYPrivateKey* preexistingKey =
80 PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr);
81 if (preexistingKey) {
82 // Note that we can't just call SECKEY_DestroyPrivateKey here because that
83 // will destroy the PKCS#11 object that is backing a preexisting key (that
84 // we still have a handle on somewhere else in memory). If that object were
85 // destroyed, cryptographic operations performed by that other key would
86 // fail.
87 DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey);
88 // Try again with a new ID (but only once - collisions are very unlikely).
89 rv = PK11_GenerateRandomOnSlot(slot.get(), objID->data, objID->len);
90 if (rv != SECSuccess) {
91 return nullptr;
92 }
93 preexistingKey = PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr);
94 if (preexistingKey) {
95 DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey);
96 return nullptr;
97 }
98 }
99
100 CK_ATTRIBUTE* idAttributeSlot = nullptr;
101 for (CK_ULONG i = 0; i < aTemplateSize; i++) {
102 if (aTemplate[i].type == CKA_ID) {
103 if (aTemplate[i].pValue != nullptr || aTemplate[i].ulValueLen != 0) {
104 return nullptr;
105 }
106 idAttributeSlot = aTemplate + i;
107 break;
108 }
109 }
110 if (!idAttributeSlot) {
111 return nullptr;
112 }
113
114 idAttributeSlot->pValue = objID->data;
115 idAttributeSlot->ulValueLen = objID->len;
116 UniquePK11GenericObject obj(
117 PK11_CreateGenericObject(slot.get(), aTemplate, aTemplateSize, PR_FALSE));
118 // Unset the ID attribute slot's pointer and length so that data that only
119 // lives for the scope of this function doesn't escape.
120 idAttributeSlot->pValue = nullptr;
121 idAttributeSlot->ulValueLen = 0;
122 if (!obj) {
123 return nullptr;
124 }
125
126 // Have NSS translate the object to a private key.
127 return UniqueSECKEYPrivateKey(
128 PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr));
129 }
130
CryptoKey(nsIGlobalObject * aGlobal)131 CryptoKey::CryptoKey(nsIGlobalObject* aGlobal)
132 : mGlobal(aGlobal),
133 mAttributes(0),
134 mSymKey(),
135 mPrivateKey(nullptr),
136 mPublicKey(nullptr) {}
137
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)138 JSObject* CryptoKey::WrapObject(JSContext* aCx,
139 JS::Handle<JSObject*> aGivenProto) {
140 return CryptoKey_Binding::Wrap(aCx, this, aGivenProto);
141 }
142
GetType(nsString & aRetVal) const143 void CryptoKey::GetType(nsString& aRetVal) const {
144 uint32_t type = mAttributes & TYPE_MASK;
145 switch (type) {
146 case PUBLIC:
147 aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC);
148 break;
149 case PRIVATE:
150 aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE);
151 break;
152 case SECRET:
153 aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_SECRET);
154 break;
155 }
156 }
157
Extractable() const158 bool CryptoKey::Extractable() const { return (mAttributes & EXTRACTABLE); }
159
GetAlgorithm(JSContext * cx,JS::MutableHandle<JSObject * > aRetVal,ErrorResult & aRv) const160 void CryptoKey::GetAlgorithm(JSContext* cx,
161 JS::MutableHandle<JSObject*> aRetVal,
162 ErrorResult& aRv) const {
163 bool converted = false;
164 JS::RootedValue val(cx);
165 switch (mAlgorithm.mType) {
166 case KeyAlgorithmProxy::AES:
167 converted = ToJSValue(cx, mAlgorithm.mAes, &val);
168 break;
169 case KeyAlgorithmProxy::HMAC:
170 converted = ToJSValue(cx, mAlgorithm.mHmac, &val);
171 break;
172 case KeyAlgorithmProxy::RSA: {
173 RootedDictionary<RsaHashedKeyAlgorithm> rsa(cx);
174 converted = mAlgorithm.mRsa.ToKeyAlgorithm(cx, rsa);
175 if (converted) {
176 converted = ToJSValue(cx, rsa, &val);
177 }
178 break;
179 }
180 case KeyAlgorithmProxy::EC:
181 converted = ToJSValue(cx, mAlgorithm.mEc, &val);
182 break;
183 }
184 if (!converted) {
185 aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
186 return;
187 }
188
189 aRetVal.set(&val.toObject());
190 }
191
GetUsages(nsTArray<nsString> & aRetVal) const192 void CryptoKey::GetUsages(nsTArray<nsString>& aRetVal) const {
193 if (mAttributes & ENCRYPT) {
194 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_ENCRYPT));
195 }
196 if (mAttributes & DECRYPT) {
197 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DECRYPT));
198 }
199 if (mAttributes & SIGN) {
200 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_SIGN));
201 }
202 if (mAttributes & VERIFY) {
203 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_VERIFY));
204 }
205 if (mAttributes & DERIVEKEY) {
206 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEKEY));
207 }
208 if (mAttributes & DERIVEBITS) {
209 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEBITS));
210 }
211 if (mAttributes & WRAPKEY) {
212 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_WRAPKEY));
213 }
214 if (mAttributes & UNWRAPKEY) {
215 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_UNWRAPKEY));
216 }
217 }
218
Algorithm()219 KeyAlgorithmProxy& CryptoKey::Algorithm() { return mAlgorithm; }
220
Algorithm() const221 const KeyAlgorithmProxy& CryptoKey::Algorithm() const { return mAlgorithm; }
222
GetKeyType() const223 CryptoKey::KeyType CryptoKey::GetKeyType() const {
224 return static_cast<CryptoKey::KeyType>(mAttributes & TYPE_MASK);
225 }
226
SetType(const nsString & aType)227 nsresult CryptoKey::SetType(const nsString& aType) {
228 mAttributes &= CLEAR_TYPE;
229 if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_SECRET)) {
230 mAttributes |= SECRET;
231 } else if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC)) {
232 mAttributes |= PUBLIC;
233 } else if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE)) {
234 mAttributes |= PRIVATE;
235 } else {
236 mAttributes |= UNKNOWN;
237 return NS_ERROR_DOM_SYNTAX_ERR;
238 }
239
240 return NS_OK;
241 }
242
SetType(CryptoKey::KeyType aType)243 void CryptoKey::SetType(CryptoKey::KeyType aType) {
244 mAttributes &= CLEAR_TYPE;
245 mAttributes |= aType;
246 }
247
SetExtractable(bool aExtractable)248 void CryptoKey::SetExtractable(bool aExtractable) {
249 mAttributes &= CLEAR_EXTRACTABLE;
250 if (aExtractable) {
251 mAttributes |= EXTRACTABLE;
252 }
253 }
254
255 // NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the
256 // public value. To properly export the private key to JWK or PKCS #8 we need
257 // the public key data though and so we use this method to augment a private
258 // key with data from the given public key.
AddPublicKeyData(SECKEYPublicKey * aPublicKey)259 nsresult CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey) {
260 // This should be a private key.
261 MOZ_ASSERT(GetKeyType() == PRIVATE);
262 // There should be a private NSS key with type 'EC'.
263 MOZ_ASSERT(mPrivateKey && mPrivateKey->keyType == ecKey);
264 // The given public key should have the same key type.
265 MOZ_ASSERT(aPublicKey->keyType == mPrivateKey->keyType);
266
267 // Read EC params.
268 ScopedAutoSECItem params;
269 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey.get(),
270 CKA_EC_PARAMS, ¶ms);
271 if (rv != SECSuccess) {
272 return NS_ERROR_DOM_OPERATION_ERR;
273 }
274
275 // Read private value.
276 ScopedAutoSECItem value;
277 rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey.get(), CKA_VALUE,
278 &value);
279 if (rv != SECSuccess) {
280 return NS_ERROR_DOM_OPERATION_ERR;
281 }
282
283 SECItem* point = &aPublicKey->u.ec.publicValue;
284 CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
285 CK_BBOOL falseValue = CK_FALSE;
286 CK_KEY_TYPE ecValue = CKK_EC;
287
288 CK_ATTRIBUTE keyTemplate[9] = {
289 {CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue)},
290 {CKA_KEY_TYPE, &ecValue, sizeof(ecValue)},
291 {CKA_TOKEN, &falseValue, sizeof(falseValue)},
292 {CKA_SENSITIVE, &falseValue, sizeof(falseValue)},
293 {CKA_PRIVATE, &falseValue, sizeof(falseValue)},
294 // PrivateKeyFromPrivateKeyTemplate sets the ID.
295 {CKA_ID, nullptr, 0},
296 {CKA_EC_PARAMS, params.data, params.len},
297 {CKA_EC_POINT, point->data, point->len},
298 {CKA_VALUE, value.data, value.len},
299 };
300
301 mPrivateKey =
302 PrivateKeyFromPrivateKeyTemplate(keyTemplate, ArrayLength(keyTemplate));
303 NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR);
304
305 return NS_OK;
306 }
307
ClearUsages()308 void CryptoKey::ClearUsages() { mAttributes &= CLEAR_USAGES; }
309
AddUsage(const nsString & aUsage)310 nsresult CryptoKey::AddUsage(const nsString& aUsage) {
311 return AddUsageIntersecting(aUsage, USAGES_MASK);
312 }
313
AddUsageIntersecting(const nsString & aUsage,uint32_t aUsageMask)314 nsresult CryptoKey::AddUsageIntersecting(const nsString& aUsage,
315 uint32_t aUsageMask) {
316 KeyUsage usage;
317 if (NS_FAILED(StringToUsage(aUsage, usage))) {
318 return NS_ERROR_DOM_SYNTAX_ERR;
319 }
320
321 if (usage & aUsageMask) {
322 AddUsage(usage);
323 return NS_OK;
324 }
325
326 return NS_OK;
327 }
328
AddUsage(CryptoKey::KeyUsage aUsage)329 void CryptoKey::AddUsage(CryptoKey::KeyUsage aUsage) { mAttributes |= aUsage; }
330
HasAnyUsage()331 bool CryptoKey::HasAnyUsage() { return !!(mAttributes & USAGES_MASK); }
332
HasUsage(CryptoKey::KeyUsage aUsage)333 bool CryptoKey::HasUsage(CryptoKey::KeyUsage aUsage) {
334 return !!(mAttributes & aUsage);
335 }
336
HasUsageOtherThan(uint32_t aUsages)337 bool CryptoKey::HasUsageOtherThan(uint32_t aUsages) {
338 return !!(mAttributes & USAGES_MASK & ~aUsages);
339 }
340
IsRecognizedUsage(const nsString & aUsage)341 bool CryptoKey::IsRecognizedUsage(const nsString& aUsage) {
342 KeyUsage dummy;
343 nsresult rv = StringToUsage(aUsage, dummy);
344 return NS_SUCCEEDED(rv);
345 }
346
AllUsagesRecognized(const Sequence<nsString> & aUsages)347 bool CryptoKey::AllUsagesRecognized(const Sequence<nsString>& aUsages) {
348 for (uint32_t i = 0; i < aUsages.Length(); ++i) {
349 if (!IsRecognizedUsage(aUsages[i])) {
350 return false;
351 }
352 }
353 return true;
354 }
355
SetSymKey(const CryptoBuffer & aSymKey)356 nsresult CryptoKey::SetSymKey(const CryptoBuffer& aSymKey) {
357 if (!mSymKey.Assign(aSymKey)) {
358 return NS_ERROR_OUT_OF_MEMORY;
359 }
360
361 return NS_OK;
362 }
363
SetPrivateKey(SECKEYPrivateKey * aPrivateKey)364 nsresult CryptoKey::SetPrivateKey(SECKEYPrivateKey* aPrivateKey) {
365 if (!aPrivateKey) {
366 mPrivateKey = nullptr;
367 return NS_OK;
368 }
369
370 mPrivateKey = UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(aPrivateKey));
371 return mPrivateKey ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
372 }
373
SetPublicKey(SECKEYPublicKey * aPublicKey)374 nsresult CryptoKey::SetPublicKey(SECKEYPublicKey* aPublicKey) {
375 if (!aPublicKey) {
376 mPublicKey = nullptr;
377 return NS_OK;
378 }
379
380 mPublicKey = UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(aPublicKey));
381 return mPublicKey ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
382 }
383
GetSymKey() const384 const CryptoBuffer& CryptoKey::GetSymKey() const { return mSymKey; }
385
GetPrivateKey() const386 UniqueSECKEYPrivateKey CryptoKey::GetPrivateKey() const {
387 if (!mPrivateKey) {
388 return nullptr;
389 }
390 return UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(mPrivateKey.get()));
391 }
392
GetPublicKey() const393 UniqueSECKEYPublicKey CryptoKey::GetPublicKey() const {
394 if (!mPublicKey) {
395 return nullptr;
396 }
397 return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(mPublicKey.get()));
398 }
399
400 // Serialization and deserialization convenience methods
401
PrivateKeyFromPkcs8(CryptoBuffer & aKeyData)402 UniqueSECKEYPrivateKey CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData) {
403 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
404 if (!slot) {
405 return nullptr;
406 }
407
408 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
409 if (!arena) {
410 return nullptr;
411 }
412
413 SECItem pkcs8Item = {siBuffer, nullptr, 0};
414 if (!aKeyData.ToSECItem(arena.get(), &pkcs8Item)) {
415 return nullptr;
416 }
417
418 // Allow everything, we enforce usage ourselves
419 unsigned int usage = KU_ALL;
420
421 SECKEYPrivateKey* privKey;
422 SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
423 slot.get(), &pkcs8Item, nullptr, nullptr, false, false, usage, &privKey,
424 nullptr);
425
426 if (rv == SECFailure) {
427 return nullptr;
428 }
429
430 return UniqueSECKEYPrivateKey(privKey);
431 }
432
PublicKeyFromSpki(CryptoBuffer & aKeyData)433 UniqueSECKEYPublicKey CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData) {
434 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
435 if (!arena) {
436 return nullptr;
437 }
438
439 SECItem spkiItem = {siBuffer, nullptr, 0};
440 if (!aKeyData.ToSECItem(arena.get(), &spkiItem)) {
441 return nullptr;
442 }
443
444 UniqueCERTSubjectPublicKeyInfo spki(
445 SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
446 if (!spki) {
447 return nullptr;
448 }
449
450 bool isECDHAlgorithm =
451 SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH, &spki->algorithm.algorithm);
452
453 // Check for |id-ecDH|. Per old versions of the WebCrypto spec we must
454 // support this OID but NSS does unfortunately not know it. Let's
455 // change the algorithm to |id-ecPublicKey| to make NSS happy.
456 if (isECDHAlgorithm) {
457 SECOidTag oid = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
458
459 SECOidData* oidData = SECOID_FindOIDByTag(oid);
460 if (!oidData) {
461 return nullptr;
462 }
463
464 SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
465 &oidData->oid);
466 if (rv != SECSuccess) {
467 return nullptr;
468 }
469 }
470
471 UniqueSECKEYPublicKey tmp(SECKEY_ExtractPublicKey(spki.get()));
472 if (!tmp.get() || !PublicKeyValid(tmp.get())) {
473 return nullptr;
474 }
475
476 return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(tmp.get()));
477 }
478
PrivateKeyToPkcs8(SECKEYPrivateKey * aPrivKey,CryptoBuffer & aRetVal)479 nsresult CryptoKey::PrivateKeyToPkcs8(SECKEYPrivateKey* aPrivKey,
480 CryptoBuffer& aRetVal) {
481 UniqueSECItem pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey, nullptr));
482 if (!pkcs8Item.get()) {
483 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
484 }
485 if (!aRetVal.Assign(pkcs8Item.get())) {
486 return NS_ERROR_DOM_OPERATION_ERR;
487 }
488 return NS_OK;
489 }
490
PublicKeyToSpki(SECKEYPublicKey * aPubKey,CryptoBuffer & aRetVal)491 nsresult CryptoKey::PublicKeyToSpki(SECKEYPublicKey* aPubKey,
492 CryptoBuffer& aRetVal) {
493 UniqueCERTSubjectPublicKeyInfo spki;
494
495 spki.reset(SECKEY_CreateSubjectPublicKeyInfo(aPubKey));
496 if (!spki) {
497 return NS_ERROR_DOM_OPERATION_ERR;
498 }
499
500 const SEC_ASN1Template* tpl = SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate);
501 UniqueSECItem spkiItem(SEC_ASN1EncodeItem(nullptr, nullptr, spki.get(), tpl));
502
503 if (!aRetVal.Assign(spkiItem.get())) {
504 return NS_ERROR_DOM_OPERATION_ERR;
505 }
506 return NS_OK;
507 }
508
CreateECPointForCoordinates(const CryptoBuffer & aX,const CryptoBuffer & aY,PLArenaPool * aArena)509 SECItem* CreateECPointForCoordinates(const CryptoBuffer& aX,
510 const CryptoBuffer& aY,
511 PLArenaPool* aArena) {
512 // Check that both points have the same length.
513 if (aX.Length() != aY.Length()) {
514 return nullptr;
515 }
516
517 // Create point.
518 SECItem* point =
519 ::SECITEM_AllocItem(aArena, nullptr, aX.Length() + aY.Length() + 1);
520 if (!point) {
521 return nullptr;
522 }
523
524 // Set point data.
525 point->data[0] = EC_POINT_FORM_UNCOMPRESSED;
526 memcpy(point->data + 1, aX.Elements(), aX.Length());
527 memcpy(point->data + 1 + aX.Length(), aY.Elements(), aY.Length());
528
529 return point;
530 }
531
PrivateKeyFromJwk(const JsonWebKey & aJwk)532 UniqueSECKEYPrivateKey CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk) {
533 CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
534 CK_BBOOL falseValue = CK_FALSE;
535
536 if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
537 // Verify that all of the required parameters are present
538 CryptoBuffer x, y, d;
539 if (!aJwk.mCrv.WasPassed() || !aJwk.mX.WasPassed() ||
540 NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) || !aJwk.mY.WasPassed() ||
541 NS_FAILED(y.FromJwkBase64(aJwk.mY.Value())) || !aJwk.mD.WasPassed() ||
542 NS_FAILED(d.FromJwkBase64(aJwk.mD.Value()))) {
543 return nullptr;
544 }
545
546 nsString namedCurve;
547 if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
548 return nullptr;
549 }
550
551 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
552 if (!arena) {
553 return nullptr;
554 }
555
556 // Create parameters.
557 SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
558 if (!params) {
559 return nullptr;
560 }
561
562 SECItem* ecPoint = CreateECPointForCoordinates(x, y, arena.get());
563 if (!ecPoint) {
564 return nullptr;
565 }
566
567 // Populate template from parameters
568 CK_KEY_TYPE ecValue = CKK_EC;
569 CK_ATTRIBUTE keyTemplate[9] = {
570 {CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue)},
571 {CKA_KEY_TYPE, &ecValue, sizeof(ecValue)},
572 {CKA_TOKEN, &falseValue, sizeof(falseValue)},
573 {CKA_SENSITIVE, &falseValue, sizeof(falseValue)},
574 {CKA_PRIVATE, &falseValue, sizeof(falseValue)},
575 // PrivateKeyFromPrivateKeyTemplate sets the ID.
576 {CKA_ID, nullptr, 0},
577 {CKA_EC_PARAMS, params->data, params->len},
578 {CKA_EC_POINT, ecPoint->data, ecPoint->len},
579 {CKA_VALUE, (void*)d.Elements(), (CK_ULONG)d.Length()},
580 };
581
582 return PrivateKeyFromPrivateKeyTemplate(keyTemplate,
583 ArrayLength(keyTemplate));
584 }
585
586 if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) {
587 // Verify that all of the required parameters are present
588 CryptoBuffer n, e, d, p, q, dp, dq, qi;
589 if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
590 !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value())) ||
591 !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value())) ||
592 !aJwk.mP.WasPassed() || NS_FAILED(p.FromJwkBase64(aJwk.mP.Value())) ||
593 !aJwk.mQ.WasPassed() || NS_FAILED(q.FromJwkBase64(aJwk.mQ.Value())) ||
594 !aJwk.mDp.WasPassed() ||
595 NS_FAILED(dp.FromJwkBase64(aJwk.mDp.Value())) ||
596 !aJwk.mDq.WasPassed() ||
597 NS_FAILED(dq.FromJwkBase64(aJwk.mDq.Value())) ||
598 !aJwk.mQi.WasPassed() ||
599 NS_FAILED(qi.FromJwkBase64(aJwk.mQi.Value()))) {
600 return nullptr;
601 }
602
603 // Populate template from parameters
604 CK_KEY_TYPE rsaValue = CKK_RSA;
605 CK_ATTRIBUTE keyTemplate[14] = {
606 {CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue)},
607 {CKA_KEY_TYPE, &rsaValue, sizeof(rsaValue)},
608 {CKA_TOKEN, &falseValue, sizeof(falseValue)},
609 {CKA_SENSITIVE, &falseValue, sizeof(falseValue)},
610 {CKA_PRIVATE, &falseValue, sizeof(falseValue)},
611 // PrivateKeyFromPrivateKeyTemplate sets the ID.
612 {CKA_ID, nullptr, 0},
613 {CKA_MODULUS, (void*)n.Elements(), (CK_ULONG)n.Length()},
614 {CKA_PUBLIC_EXPONENT, (void*)e.Elements(), (CK_ULONG)e.Length()},
615 {CKA_PRIVATE_EXPONENT, (void*)d.Elements(), (CK_ULONG)d.Length()},
616 {CKA_PRIME_1, (void*)p.Elements(), (CK_ULONG)p.Length()},
617 {CKA_PRIME_2, (void*)q.Elements(), (CK_ULONG)q.Length()},
618 {CKA_EXPONENT_1, (void*)dp.Elements(), (CK_ULONG)dp.Length()},
619 {CKA_EXPONENT_2, (void*)dq.Elements(), (CK_ULONG)dq.Length()},
620 {CKA_COEFFICIENT, (void*)qi.Elements(), (CK_ULONG)qi.Length()},
621 };
622
623 return PrivateKeyFromPrivateKeyTemplate(keyTemplate,
624 ArrayLength(keyTemplate));
625 }
626
627 return nullptr;
628 }
629
ReadAndEncodeAttribute(SECKEYPrivateKey * aKey,CK_ATTRIBUTE_TYPE aAttribute,Optional<nsString> & aDst)630 bool ReadAndEncodeAttribute(SECKEYPrivateKey* aKey,
631 CK_ATTRIBUTE_TYPE aAttribute,
632 Optional<nsString>& aDst) {
633 ScopedAutoSECItem item;
634 if (PK11_ReadRawAttribute(PK11_TypePrivKey, aKey, aAttribute, &item) !=
635 SECSuccess) {
636 return false;
637 }
638
639 CryptoBuffer buffer;
640 if (!buffer.Assign(&item)) {
641 return false;
642 }
643
644 if (NS_FAILED(buffer.ToJwkBase64(aDst.Value()))) {
645 return false;
646 }
647
648 return true;
649 }
650
ECKeyToJwk(const PK11ObjectType aKeyType,void * aKey,const SECItem * aEcParams,const SECItem * aPublicValue,JsonWebKey & aRetVal)651 bool ECKeyToJwk(const PK11ObjectType aKeyType, void* aKey,
652 const SECItem* aEcParams, const SECItem* aPublicValue,
653 JsonWebKey& aRetVal) {
654 aRetVal.mX.Construct();
655 aRetVal.mY.Construct();
656
657 // Check that the given EC parameters are valid.
658 if (!CheckEncodedECParameters(aEcParams)) {
659 return false;
660 }
661
662 // Construct the OID tag.
663 SECItem oid = {siBuffer, nullptr, 0};
664 oid.len = aEcParams->data[1];
665 oid.data = aEcParams->data + 2;
666
667 uint32_t flen;
668 switch (SECOID_FindOIDTag(&oid)) {
669 case SEC_OID_SECG_EC_SECP256R1:
670 flen = 32; // bytes
671 aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P256));
672 break;
673 case SEC_OID_SECG_EC_SECP384R1:
674 flen = 48; // bytes
675 aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P384));
676 break;
677 case SEC_OID_SECG_EC_SECP521R1:
678 flen = 66; // bytes
679 aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P521));
680 break;
681 default:
682 return false;
683 }
684
685 // No support for compressed points.
686 if (aPublicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) {
687 return false;
688 }
689
690 // Check length of uncompressed point coordinates.
691 if (aPublicValue->len != (2 * flen + 1)) {
692 return false;
693 }
694
695 UniqueSECItem ecPointX(::SECITEM_AllocItem(nullptr, nullptr, flen));
696 UniqueSECItem ecPointY(::SECITEM_AllocItem(nullptr, nullptr, flen));
697 if (!ecPointX || !ecPointY) {
698 return false;
699 }
700
701 // Extract point data.
702 memcpy(ecPointX->data, aPublicValue->data + 1, flen);
703 memcpy(ecPointY->data, aPublicValue->data + 1 + flen, flen);
704
705 CryptoBuffer x, y;
706 if (!x.Assign(ecPointX.get()) ||
707 NS_FAILED(x.ToJwkBase64(aRetVal.mX.Value())) ||
708 !y.Assign(ecPointY.get()) ||
709 NS_FAILED(y.ToJwkBase64(aRetVal.mY.Value()))) {
710 return false;
711 }
712
713 aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_EC);
714 return true;
715 }
716
PrivateKeyToJwk(SECKEYPrivateKey * aPrivKey,JsonWebKey & aRetVal)717 nsresult CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
718 JsonWebKey& aRetVal) {
719 switch (aPrivKey->keyType) {
720 case rsaKey: {
721 aRetVal.mN.Construct();
722 aRetVal.mE.Construct();
723 aRetVal.mD.Construct();
724 aRetVal.mP.Construct();
725 aRetVal.mQ.Construct();
726 aRetVal.mDp.Construct();
727 aRetVal.mDq.Construct();
728 aRetVal.mQi.Construct();
729
730 if (!ReadAndEncodeAttribute(aPrivKey, CKA_MODULUS, aRetVal.mN) ||
731 !ReadAndEncodeAttribute(aPrivKey, CKA_PUBLIC_EXPONENT, aRetVal.mE) ||
732 !ReadAndEncodeAttribute(aPrivKey, CKA_PRIVATE_EXPONENT, aRetVal.mD) ||
733 !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_1, aRetVal.mP) ||
734 !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_2, aRetVal.mQ) ||
735 !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_1, aRetVal.mDp) ||
736 !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_2, aRetVal.mDq) ||
737 !ReadAndEncodeAttribute(aPrivKey, CKA_COEFFICIENT, aRetVal.mQi)) {
738 return NS_ERROR_DOM_OPERATION_ERR;
739 }
740
741 aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA);
742 return NS_OK;
743 }
744 case ecKey: {
745 // Read EC params.
746 ScopedAutoSECItem params;
747 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey,
748 CKA_EC_PARAMS, ¶ms);
749 if (rv != SECSuccess) {
750 return NS_ERROR_DOM_OPERATION_ERR;
751 }
752
753 // Read public point Q.
754 ScopedAutoSECItem ecPoint;
755 rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey, CKA_EC_POINT,
756 &ecPoint);
757 if (rv != SECSuccess) {
758 return NS_ERROR_DOM_OPERATION_ERR;
759 }
760
761 if (!ECKeyToJwk(PK11_TypePrivKey, aPrivKey, ¶ms, &ecPoint, aRetVal)) {
762 return NS_ERROR_DOM_OPERATION_ERR;
763 }
764
765 aRetVal.mD.Construct();
766
767 // Read private value.
768 if (!ReadAndEncodeAttribute(aPrivKey, CKA_VALUE, aRetVal.mD)) {
769 return NS_ERROR_DOM_OPERATION_ERR;
770 }
771
772 return NS_OK;
773 }
774 default:
775 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
776 }
777 }
778
CreateECPublicKey(const SECItem * aKeyData,const nsString & aNamedCurve)779 UniqueSECKEYPublicKey CreateECPublicKey(const SECItem* aKeyData,
780 const nsString& aNamedCurve) {
781 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
782 if (!arena) {
783 return nullptr;
784 }
785
786 // It's important that this be a UniqueSECKEYPublicKey, as this ensures that
787 // SECKEY_DestroyPublicKey will be called on it. If this doesn't happen, when
788 // CryptoKey::PublicKeyValid is called on it and it gets moved to the internal
789 // PKCS#11 slot, it will leak a reference to the slot.
790 UniqueSECKEYPublicKey key(PORT_ArenaZNew(arena.get(), SECKEYPublicKey));
791 if (!key) {
792 return nullptr;
793 }
794
795 key->arena = nullptr; // key doesn't own the arena; it won't get double-freed
796 key->keyType = ecKey;
797 key->pkcs11Slot = nullptr;
798 key->pkcs11ID = CK_INVALID_HANDLE;
799
800 // Create curve parameters.
801 SECItem* params = CreateECParamsForCurve(aNamedCurve, arena.get());
802 if (!params) {
803 return nullptr;
804 }
805 key->u.ec.DEREncodedParams = *params;
806
807 // Set public point.
808 key->u.ec.publicValue = *aKeyData;
809
810 // Ensure the given point is on the curve.
811 if (!CryptoKey::PublicKeyValid(key.get())) {
812 return nullptr;
813 }
814
815 return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(key.get()));
816 }
817
PublicKeyFromJwk(const JsonWebKey & aJwk)818 UniqueSECKEYPublicKey CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk) {
819 if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) {
820 // Verify that all of the required parameters are present
821 CryptoBuffer n, e;
822 if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
823 !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value()))) {
824 return nullptr;
825 }
826
827 // Transcode to a DER RSAPublicKey structure
828 struct RSAPublicKeyData {
829 SECItem n;
830 SECItem e;
831 };
832 const RSAPublicKeyData input = {
833 {siUnsignedInteger, n.Elements(), (unsigned int)n.Length()},
834 {siUnsignedInteger, e.Elements(), (unsigned int)e.Length()}};
835 const SEC_ASN1Template rsaPublicKeyTemplate[] = {
836 {SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(RSAPublicKeyData)},
837 {
838 SEC_ASN1_INTEGER,
839 offsetof(RSAPublicKeyData, n),
840 },
841 {
842 SEC_ASN1_INTEGER,
843 offsetof(RSAPublicKeyData, e),
844 },
845 {
846 0,
847 }};
848
849 UniqueSECItem pkDer(
850 SEC_ASN1EncodeItem(nullptr, nullptr, &input, rsaPublicKeyTemplate));
851 if (!pkDer.get()) {
852 return nullptr;
853 }
854
855 return UniqueSECKEYPublicKey(
856 SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA));
857 }
858
859 if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
860 // Verify that all of the required parameters are present
861 CryptoBuffer x, y;
862 if (!aJwk.mCrv.WasPassed() || !aJwk.mX.WasPassed() ||
863 NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) || !aJwk.mY.WasPassed() ||
864 NS_FAILED(y.FromJwkBase64(aJwk.mY.Value()))) {
865 return nullptr;
866 }
867
868 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
869 if (!arena) {
870 return nullptr;
871 }
872
873 // Create point.
874 SECItem* point = CreateECPointForCoordinates(x, y, arena.get());
875 if (!point) {
876 return nullptr;
877 }
878
879 nsString namedCurve;
880 if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
881 return nullptr;
882 }
883
884 return CreateECPublicKey(point, namedCurve);
885 }
886
887 return nullptr;
888 }
889
PublicKeyToJwk(SECKEYPublicKey * aPubKey,JsonWebKey & aRetVal)890 nsresult CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
891 JsonWebKey& aRetVal) {
892 switch (aPubKey->keyType) {
893 case rsaKey: {
894 CryptoBuffer n, e;
895 aRetVal.mN.Construct();
896 aRetVal.mE.Construct();
897
898 if (!n.Assign(&aPubKey->u.rsa.modulus) ||
899 !e.Assign(&aPubKey->u.rsa.publicExponent) ||
900 NS_FAILED(n.ToJwkBase64(aRetVal.mN.Value())) ||
901 NS_FAILED(e.ToJwkBase64(aRetVal.mE.Value()))) {
902 return NS_ERROR_DOM_OPERATION_ERR;
903 }
904
905 aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA);
906 return NS_OK;
907 }
908 case ecKey:
909 if (!ECKeyToJwk(PK11_TypePubKey, aPubKey, &aPubKey->u.ec.DEREncodedParams,
910 &aPubKey->u.ec.publicValue, aRetVal)) {
911 return NS_ERROR_DOM_OPERATION_ERR;
912 }
913 return NS_OK;
914 default:
915 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
916 }
917 }
918
PublicECKeyFromRaw(CryptoBuffer & aKeyData,const nsString & aNamedCurve)919 UniqueSECKEYPublicKey CryptoKey::PublicECKeyFromRaw(
920 CryptoBuffer& aKeyData, const nsString& aNamedCurve) {
921 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
922 if (!arena) {
923 return nullptr;
924 }
925
926 SECItem rawItem = {siBuffer, nullptr, 0};
927 if (!aKeyData.ToSECItem(arena.get(), &rawItem)) {
928 return nullptr;
929 }
930
931 uint32_t flen;
932 if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256)) {
933 flen = 32; // bytes
934 } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384)) {
935 flen = 48; // bytes
936 } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521)) {
937 flen = 66; // bytes
938 } else {
939 return nullptr;
940 }
941
942 // Check length of uncompressed point coordinates. There are 2 field elements
943 // and a leading point form octet (which must EC_POINT_FORM_UNCOMPRESSED).
944 if (rawItem.len != (2 * flen + 1)) {
945 return nullptr;
946 }
947
948 // No support for compressed points.
949 if (rawItem.data[0] != EC_POINT_FORM_UNCOMPRESSED) {
950 return nullptr;
951 }
952
953 return CreateECPublicKey(&rawItem, aNamedCurve);
954 }
955
PublicECKeyToRaw(SECKEYPublicKey * aPubKey,CryptoBuffer & aRetVal)956 nsresult CryptoKey::PublicECKeyToRaw(SECKEYPublicKey* aPubKey,
957 CryptoBuffer& aRetVal) {
958 if (!aRetVal.Assign(&aPubKey->u.ec.publicValue)) {
959 return NS_ERROR_DOM_OPERATION_ERR;
960 }
961 return NS_OK;
962 }
963
PublicKeyValid(SECKEYPublicKey * aPubKey)964 bool CryptoKey::PublicKeyValid(SECKEYPublicKey* aPubKey) {
965 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
966 if (!slot.get()) {
967 return false;
968 }
969
970 // This assumes that NSS checks the validity of a public key when
971 // it is imported into a PKCS#11 module, and returns CK_INVALID_HANDLE
972 // if it is invalid.
973 CK_OBJECT_HANDLE id = PK11_ImportPublicKey(slot.get(), aPubKey, PR_FALSE);
974 if (id == CK_INVALID_HANDLE) {
975 return false;
976 }
977
978 SECStatus rv = PK11_DestroyObject(slot.get(), id);
979 return (rv == SECSuccess);
980 }
981
WriteStructuredClone(JSContext * aCX,JSStructuredCloneWriter * aWriter) const982 bool CryptoKey::WriteStructuredClone(JSContext* aCX,
983 JSStructuredCloneWriter* aWriter) const {
984 // Write in five pieces
985 // 1. Attributes
986 // 2. Symmetric key as raw (if present)
987 // 3. Private key as pkcs8 (if present)
988 // 4. Public key as spki (if present)
989 // 5. Algorithm in whatever form it chooses
990 CryptoBuffer priv, pub;
991
992 if (mPrivateKey) {
993 if (NS_FAILED(CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), priv))) {
994 return false;
995 }
996 }
997
998 if (mPublicKey) {
999 if (NS_FAILED(CryptoKey::PublicKeyToSpki(mPublicKey.get(), pub))) {
1000 return false;
1001 }
1002 }
1003
1004 return JS_WriteUint32Pair(aWriter, mAttributes, CRYPTOKEY_SC_VERSION) &&
1005 WriteBuffer(aWriter, mSymKey) && WriteBuffer(aWriter, priv) &&
1006 WriteBuffer(aWriter, pub) && mAlgorithm.WriteStructuredClone(aWriter);
1007 }
1008
1009 // static
ReadStructuredClone(JSContext * aCx,nsIGlobalObject * aGlobal,JSStructuredCloneReader * aReader)1010 already_AddRefed<CryptoKey> CryptoKey::ReadStructuredClone(
1011 JSContext* aCx, nsIGlobalObject* aGlobal,
1012 JSStructuredCloneReader* aReader) {
1013 // Ensure that NSS is initialized.
1014 if (!EnsureNSSInitializedChromeOrContent()) {
1015 return nullptr;
1016 }
1017
1018 RefPtr<CryptoKey> key = new CryptoKey(aGlobal);
1019
1020 uint32_t version;
1021 CryptoBuffer sym, priv, pub;
1022
1023 bool read = JS_ReadUint32Pair(aReader, &key->mAttributes, &version) &&
1024 (version == CRYPTOKEY_SC_VERSION) && ReadBuffer(aReader, sym) &&
1025 ReadBuffer(aReader, priv) && ReadBuffer(aReader, pub) &&
1026 key->mAlgorithm.ReadStructuredClone(aReader);
1027 if (!read) {
1028 return nullptr;
1029 }
1030
1031 if (sym.Length() > 0 && !key->mSymKey.Assign(sym)) {
1032 return nullptr;
1033 }
1034 if (priv.Length() > 0) {
1035 key->mPrivateKey = CryptoKey::PrivateKeyFromPkcs8(priv);
1036 }
1037 if (pub.Length() > 0) {
1038 key->mPublicKey = CryptoKey::PublicKeyFromSpki(pub);
1039 }
1040
1041 // Ensure that what we've read is consistent
1042 // If the attributes indicate a key type, should have a key of that type
1043 if (!((key->GetKeyType() == SECRET && key->mSymKey.Length() > 0) ||
1044 (key->GetKeyType() == PRIVATE && key->mPrivateKey) ||
1045 (key->GetKeyType() == PUBLIC && key->mPublicKey))) {
1046 return nullptr;
1047 }
1048
1049 return key.forget();
1050 }
1051
1052 } // namespace dom
1053 } // namespace mozilla
1054