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, &params);
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, &params);
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, &params, &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