Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / dom / crypto / CryptoKey.cpp
blob3647ecb27b461da71717b7ce1daea5d75ee4f46b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 "pk11pub.h"
8 #include "cryptohi.h"
9 #include "ScopedNSSTypes.h"
10 #include "mozilla/dom/CryptoKey.h"
11 #include "mozilla/dom/WebCryptoCommon.h"
12 #include "mozilla/dom/SubtleCryptoBinding.h"
13 #include "mozilla/dom/ToJSValue.h"
15 namespace mozilla {
16 namespace dom {
18 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey, mGlobal)
19 NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKey)
20 NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKey)
21 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKey)
22 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
23 NS_INTERFACE_MAP_ENTRY(nsISupports)
24 NS_INTERFACE_MAP_END
26 nsresult
27 StringToUsage(const nsString& aUsage, CryptoKey::KeyUsage& aUsageOut)
29 if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_ENCRYPT)) {
30 aUsageOut = CryptoKey::ENCRYPT;
31 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DECRYPT)) {
32 aUsageOut = CryptoKey::DECRYPT;
33 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_SIGN)) {
34 aUsageOut = CryptoKey::SIGN;
35 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_VERIFY)) {
36 aUsageOut = CryptoKey::VERIFY;
37 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEKEY)) {
38 aUsageOut = CryptoKey::DERIVEKEY;
39 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEBITS)) {
40 aUsageOut = CryptoKey::DERIVEBITS;
41 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_WRAPKEY)) {
42 aUsageOut = CryptoKey::WRAPKEY;
43 } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_UNWRAPKEY)) {
44 aUsageOut = CryptoKey::UNWRAPKEY;
45 } else {
46 return NS_ERROR_DOM_SYNTAX_ERR;
48 return NS_OK;
51 CryptoKey::CryptoKey(nsIGlobalObject* aGlobal)
52 : mGlobal(aGlobal)
53 , mAttributes(0)
54 , mSymKey()
55 , mPrivateKey(nullptr)
56 , mPublicKey(nullptr)
58 SetIsDOMBinding();
61 CryptoKey::~CryptoKey()
63 nsNSSShutDownPreventionLock locker;
64 if (isAlreadyShutDown()) {
65 return;
67 destructorSafeDestroyNSSReference();
68 shutdown(calledFromObject);
71 JSObject*
72 CryptoKey::WrapObject(JSContext* aCx)
74 return CryptoKeyBinding::Wrap(aCx, this);
77 void
78 CryptoKey::GetType(nsString& aRetVal) const
80 uint32_t type = mAttributes & TYPE_MASK;
81 switch (type) {
82 case PUBLIC: aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC); break;
83 case PRIVATE: aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE); break;
84 case SECRET: aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_SECRET); break;
88 bool
89 CryptoKey::Extractable() const
91 return (mAttributes & EXTRACTABLE);
94 void
95 CryptoKey::GetAlgorithm(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal,
96 ErrorResult& aRv) const
98 bool converted = false;
99 JS::RootedValue val(cx);
100 switch (mAlgorithm.mType) {
101 case KeyAlgorithmProxy::AES:
102 converted = ToJSValue(cx, mAlgorithm.mAes, &val);
103 break;
104 case KeyAlgorithmProxy::HMAC:
105 converted = ToJSValue(cx, mAlgorithm.mHmac, &val);
106 break;
107 case KeyAlgorithmProxy::RSA: {
108 RootedDictionary<RsaHashedKeyAlgorithm> rsa(cx);
109 mAlgorithm.mRsa.ToKeyAlgorithm(cx, rsa);
110 converted = ToJSValue(cx, rsa, &val);
111 break;
113 case KeyAlgorithmProxy::EC:
114 converted = ToJSValue(cx, mAlgorithm.mEc, &val);
115 break;
117 if (!converted) {
118 aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
119 return;
122 aRetVal.set(&val.toObject());
125 void
126 CryptoKey::GetUsages(nsTArray<nsString>& aRetVal) const
128 if (mAttributes & ENCRYPT) {
129 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_ENCRYPT));
131 if (mAttributes & DECRYPT) {
132 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DECRYPT));
134 if (mAttributes & SIGN) {
135 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_SIGN));
137 if (mAttributes & VERIFY) {
138 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_VERIFY));
140 if (mAttributes & DERIVEKEY) {
141 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEKEY));
143 if (mAttributes & DERIVEBITS) {
144 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEBITS));
146 if (mAttributes & WRAPKEY) {
147 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_WRAPKEY));
149 if (mAttributes & UNWRAPKEY) {
150 aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_UNWRAPKEY));
154 KeyAlgorithmProxy&
155 CryptoKey::Algorithm()
157 return mAlgorithm;
160 const KeyAlgorithmProxy&
161 CryptoKey::Algorithm() const
163 return mAlgorithm;
166 CryptoKey::KeyType
167 CryptoKey::GetKeyType() const
169 return static_cast<CryptoKey::KeyType>(mAttributes & TYPE_MASK);
172 nsresult
173 CryptoKey::SetType(const nsString& aType)
175 mAttributes &= CLEAR_TYPE;
176 if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_SECRET)) {
177 mAttributes |= SECRET;
178 } else if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC)) {
179 mAttributes |= PUBLIC;
180 } else if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE)) {
181 mAttributes |= PRIVATE;
182 } else {
183 mAttributes |= UNKNOWN;
184 return NS_ERROR_DOM_SYNTAX_ERR;
187 return NS_OK;
190 void
191 CryptoKey::SetType(CryptoKey::KeyType aType)
193 mAttributes &= CLEAR_TYPE;
194 mAttributes |= aType;
197 void
198 CryptoKey::SetExtractable(bool aExtractable)
200 mAttributes &= CLEAR_EXTRACTABLE;
201 if (aExtractable) {
202 mAttributes |= EXTRACTABLE;
206 void
207 CryptoKey::ClearUsages()
209 mAttributes &= CLEAR_USAGES;
212 nsresult
213 CryptoKey::AddUsage(const nsString& aUsage)
215 return AddUsageIntersecting(aUsage, USAGES_MASK);
218 nsresult
219 CryptoKey::AddUsageIntersecting(const nsString& aUsage, uint32_t aUsageMask)
221 KeyUsage usage;
222 if (NS_FAILED(StringToUsage(aUsage, usage))) {
223 return NS_ERROR_DOM_SYNTAX_ERR;
226 if (usage & aUsageMask) {
227 AddUsage(usage);
228 return NS_OK;
231 return NS_OK;
234 void
235 CryptoKey::AddUsage(CryptoKey::KeyUsage aUsage)
237 mAttributes |= aUsage;
240 bool
241 CryptoKey::HasAnyUsage()
243 return !!(mAttributes & USAGES_MASK);
246 bool
247 CryptoKey::HasUsage(CryptoKey::KeyUsage aUsage)
249 return !!(mAttributes & aUsage);
252 bool
253 CryptoKey::HasUsageOtherThan(uint32_t aUsages)
255 return !!(mAttributes & USAGES_MASK & ~aUsages);
258 bool
259 CryptoKey::IsRecognizedUsage(const nsString& aUsage)
261 KeyUsage dummy;
262 nsresult rv = StringToUsage(aUsage, dummy);
263 return NS_SUCCEEDED(rv);
266 bool
267 CryptoKey::AllUsagesRecognized(const Sequence<nsString>& aUsages)
269 for (uint32_t i = 0; i < aUsages.Length(); ++i) {
270 if (!IsRecognizedUsage(aUsages[i])) {
271 return false;
274 return true;
277 void CryptoKey::SetSymKey(const CryptoBuffer& aSymKey)
279 mSymKey = aSymKey;
282 void
283 CryptoKey::SetPrivateKey(SECKEYPrivateKey* aPrivateKey)
285 nsNSSShutDownPreventionLock locker;
286 if (!aPrivateKey || isAlreadyShutDown()) {
287 mPrivateKey = nullptr;
288 return;
290 mPrivateKey = SECKEY_CopyPrivateKey(aPrivateKey);
293 void
294 CryptoKey::SetPublicKey(SECKEYPublicKey* aPublicKey)
296 nsNSSShutDownPreventionLock locker;
297 if (!aPublicKey || isAlreadyShutDown()) {
298 mPublicKey = nullptr;
299 return;
301 mPublicKey = SECKEY_CopyPublicKey(aPublicKey);
304 const CryptoBuffer&
305 CryptoKey::GetSymKey() const
307 return mSymKey;
310 SECKEYPrivateKey*
311 CryptoKey::GetPrivateKey() const
313 nsNSSShutDownPreventionLock locker;
314 if (!mPrivateKey || isAlreadyShutDown()) {
315 return nullptr;
317 return SECKEY_CopyPrivateKey(mPrivateKey.get());
320 SECKEYPublicKey*
321 CryptoKey::GetPublicKey() const
323 nsNSSShutDownPreventionLock locker;
324 if (!mPublicKey || isAlreadyShutDown()) {
325 return nullptr;
327 return SECKEY_CopyPublicKey(mPublicKey.get());
330 void CryptoKey::virtualDestroyNSSReference()
332 destructorSafeDestroyNSSReference();
335 void CryptoKey::destructorSafeDestroyNSSReference()
337 mPrivateKey.dispose();
338 mPublicKey.dispose();
342 // Serialization and deserialization convenience methods
344 SECKEYPrivateKey*
345 CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
346 const nsNSSShutDownPreventionLock& /*proofOfLock*/)
348 SECKEYPrivateKey* privKey;
349 ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
350 ScopedSECItem pkcs8Item(aKeyData.ToSECItem());
351 if (!pkcs8Item) {
352 return nullptr;
355 // Allow everything, we enforce usage ourselves
356 unsigned int usage = KU_ALL;
358 SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
359 slot.get(), pkcs8Item.get(), nullptr, nullptr, false, false,
360 usage, &privKey, nullptr);
362 if (rv == SECFailure) {
363 return nullptr;
365 return privKey;
368 SECKEYPublicKey*
369 CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData,
370 const nsNSSShutDownPreventionLock& /*proofOfLock*/)
372 ScopedSECItem spkiItem(aKeyData.ToSECItem());
373 if (!spkiItem) {
374 return nullptr;
377 ScopedCERTSubjectPublicKeyInfo spki(SECKEY_DecodeDERSubjectPublicKeyInfo(spkiItem.get()));
378 if (!spki) {
379 return nullptr;
382 // Check for id-ecDH. Per the WebCrypto spec we must support it but NSS
383 // does unfortunately not know about it. Let's change the algorithm to
384 // id-ecPublicKey to make NSS happy.
385 if (SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH, &spki->algorithm.algorithm)) {
386 // Retrieve OID data for id-ecPublicKey (1.2.840.10045.2.1).
387 SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_ANSIX962_EC_PUBLIC_KEY);
388 if (!oidData) {
389 return nullptr;
392 SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
393 &oidData->oid);
394 if (rv != SECSuccess) {
395 return nullptr;
399 return SECKEY_ExtractPublicKey(spki.get());
402 nsresult
403 CryptoKey::PrivateKeyToPkcs8(SECKEYPrivateKey* aPrivKey,
404 CryptoBuffer& aRetVal,
405 const nsNSSShutDownPreventionLock& /*proofOfLock*/)
407 ScopedSECItem pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey, nullptr));
408 if (!pkcs8Item.get()) {
409 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
411 aRetVal.Assign(pkcs8Item.get());
412 return NS_OK;
415 nsresult
416 CryptoKey::PublicKeyToSpki(SECKEYPublicKey* aPubKey,
417 CryptoBuffer& aRetVal,
418 const nsNSSShutDownPreventionLock& /*proofOfLock*/)
420 ScopedCERTSubjectPublicKeyInfo spki(SECKEY_CreateSubjectPublicKeyInfo(aPubKey));
421 if (!spki) {
422 return NS_ERROR_DOM_OPERATION_ERR;
425 // Per WebCrypto spec we must export ECDH SPKIs with the algorithm OID
426 // id-ecDH (1.3.132.112). NSS doesn't know about that OID and there is
427 // no way to specify the algorithm to use when exporting a public key.
428 if (aPubKey->keyType == ecKey) {
429 SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
430 &SEC_OID_DATA_EC_DH);
431 if (rv != SECSuccess) {
432 return NS_ERROR_DOM_OPERATION_ERR;
436 const SEC_ASN1Template* tpl = SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate);
437 ScopedSECItem spkiItem(SEC_ASN1EncodeItem(nullptr, nullptr, spki, tpl));
439 aRetVal.Assign(spkiItem.get());
440 return NS_OK;
443 SECItem*
444 CreateECPointForCoordinates(const CryptoBuffer& aX,
445 const CryptoBuffer& aY,
446 PLArenaPool* aArena)
448 // Check that both points have the same length.
449 if (aX.Length() != aY.Length()) {
450 return nullptr;
453 // Create point.
454 SECItem* point = ::SECITEM_AllocItem(aArena, nullptr, aX.Length() + aY.Length() + 1);
455 if (!point) {
456 return nullptr;
459 // Set point data.
460 point->data[0] = EC_POINT_FORM_UNCOMPRESSED;
461 memcpy(point->data + 1, aX.Elements(), aX.Length());
462 memcpy(point->data + 1 + aX.Length(), aY.Elements(), aY.Length());
464 return point;
467 SECKEYPrivateKey*
468 PrivateKeyFromPrivateKeyTemplate(SECItem* aObjID,
469 CK_ATTRIBUTE* aTemplate,
470 CK_ULONG aTemplateSize)
472 // Create a generic object with the contents of the key
473 ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
474 if (!slot.get()) {
475 return nullptr;
478 ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot.get(),
479 aTemplate,
480 aTemplateSize,
481 PR_FALSE));
482 if (!obj.get()) {
483 return nullptr;
486 // Have NSS translate the object to a private key by inspection
487 // and make a copy we can own
488 ScopedSECKEYPrivateKey privKey(PK11_FindKeyByKeyID(slot.get(), aObjID,
489 nullptr));
490 if (!privKey.get()) {
491 return nullptr;
494 return SECKEY_CopyPrivateKey(privKey.get());
497 SECKEYPrivateKey*
498 CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
499 const nsNSSShutDownPreventionLock& /*proofOfLock*/)
501 CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
502 CK_BBOOL falseValue = CK_FALSE;
504 if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
505 // Verify that all of the required parameters are present
506 CryptoBuffer x, y, d;
507 if (!aJwk.mCrv.WasPassed() ||
508 !aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) ||
509 !aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value())) ||
510 !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value()))) {
511 return nullptr;
514 nsString namedCurve;
515 if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
516 return nullptr;
519 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
520 if (!arena) {
521 return nullptr;
524 // Create parameters.
525 SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
526 if (!params) {
527 return nullptr;
530 SECItem* ecPoint = CreateECPointForCoordinates(x, y, arena.get());
531 if (!ecPoint) {
532 return nullptr;
535 // Compute the ID for this key
536 // This is generated with a SHA-1 hash, so unlikely to collide
537 ScopedSECItem objID(PK11_MakeIDFromPubKey(ecPoint));
538 if (!objID.get()) {
539 return nullptr;
542 // Populate template from parameters
543 CK_KEY_TYPE ecValue = CKK_EC;
544 CK_ATTRIBUTE keyTemplate[9] = {
545 { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) },
546 { CKA_KEY_TYPE, &ecValue, sizeof(ecValue) },
547 { CKA_TOKEN, &falseValue, sizeof(falseValue) },
548 { CKA_SENSITIVE, &falseValue, sizeof(falseValue) },
549 { CKA_PRIVATE, &falseValue, sizeof(falseValue) },
550 { CKA_ID, objID->data, objID->len },
551 { CKA_EC_PARAMS, params->data, params->len },
552 { CKA_EC_POINT, ecPoint->data, ecPoint->len },
553 { CKA_VALUE, (void*) d.Elements(), d.Length() },
556 return PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate,
557 PR_ARRAY_SIZE(keyTemplate));
560 if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) {
561 // Verify that all of the required parameters are present
562 CryptoBuffer n, e, d, p, q, dp, dq, qi;
563 if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
564 !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value())) ||
565 !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value())) ||
566 !aJwk.mP.WasPassed() || NS_FAILED(p.FromJwkBase64(aJwk.mP.Value())) ||
567 !aJwk.mQ.WasPassed() || NS_FAILED(q.FromJwkBase64(aJwk.mQ.Value())) ||
568 !aJwk.mDp.WasPassed() || NS_FAILED(dp.FromJwkBase64(aJwk.mDp.Value())) ||
569 !aJwk.mDq.WasPassed() || NS_FAILED(dq.FromJwkBase64(aJwk.mDq.Value())) ||
570 !aJwk.mQi.WasPassed() || NS_FAILED(qi.FromJwkBase64(aJwk.mQi.Value()))) {
571 return nullptr;
574 // Compute the ID for this key
575 // This is generated with a SHA-1 hash, so unlikely to collide
576 ScopedSECItem nItem(n.ToSECItem());
577 if (!nItem.get()) {
578 return nullptr;
581 ScopedSECItem objID(PK11_MakeIDFromPubKey(nItem.get()));
582 if (!objID.get()) {
583 return nullptr;
586 // Populate template from parameters
587 CK_KEY_TYPE rsaValue = CKK_RSA;
588 CK_ATTRIBUTE keyTemplate[14] = {
589 { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) },
590 { CKA_KEY_TYPE, &rsaValue, sizeof(rsaValue) },
591 { CKA_TOKEN, &falseValue, sizeof(falseValue) },
592 { CKA_SENSITIVE, &falseValue, sizeof(falseValue) },
593 { CKA_PRIVATE, &falseValue, sizeof(falseValue) },
594 { CKA_ID, objID->data, objID->len },
595 { CKA_MODULUS, (void*) n.Elements(), n.Length() },
596 { CKA_PUBLIC_EXPONENT, (void*) e.Elements(), e.Length() },
597 { CKA_PRIVATE_EXPONENT, (void*) d.Elements(), d.Length() },
598 { CKA_PRIME_1, (void*) p.Elements(), p.Length() },
599 { CKA_PRIME_2, (void*) q.Elements(), q.Length() },
600 { CKA_EXPONENT_1, (void*) dp.Elements(), dp.Length() },
601 { CKA_EXPONENT_2, (void*) dq.Elements(), dq.Length() },
602 { CKA_COEFFICIENT, (void*) qi.Elements(), qi.Length() },
605 return PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate,
606 PR_ARRAY_SIZE(keyTemplate));
609 return nullptr;
612 bool ReadAndEncodeAttribute(SECKEYPrivateKey* aKey,
613 CK_ATTRIBUTE_TYPE aAttribute,
614 Optional<nsString>& aDst)
616 ScopedSECItem item(::SECITEM_AllocItem(nullptr, nullptr, 0));
617 if (!item) {
618 return false;
621 if (PK11_ReadRawAttribute(PK11_TypePrivKey, aKey, aAttribute, item)
622 != SECSuccess) {
623 return false;
626 CryptoBuffer buffer;
627 if (!buffer.Assign(item)) {
628 return false;
631 if (NS_FAILED(buffer.ToJwkBase64(aDst.Value()))) {
632 return false;
635 return true;
638 bool
639 ECKeyToJwk(const PK11ObjectType aKeyType, void* aKey, const SECItem* aEcParams,
640 const SECItem* aPublicValue, JsonWebKey& aRetVal)
642 aRetVal.mX.Construct();
643 aRetVal.mY.Construct();
645 // Check that the given EC parameters are valid.
646 if (!CheckEncodedECParameters(aEcParams)) {
647 return false;
650 // Construct the OID tag.
651 SECItem oid = { siBuffer, nullptr, 0 };
652 oid.len = aEcParams->data[1];
653 oid.data = aEcParams->data + 2;
655 uint32_t flen;
656 switch (SECOID_FindOIDTag(&oid)) {
657 case SEC_OID_SECG_EC_SECP256R1:
658 flen = 32; // bytes
659 aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P256));
660 break;
661 case SEC_OID_SECG_EC_SECP384R1:
662 flen = 48; // bytes
663 aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P384));
664 break;
665 case SEC_OID_SECG_EC_SECP521R1:
666 flen = 66; // bytes
667 aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P521));
668 break;
669 default:
670 return false;
673 // No support for compressed points.
674 if (aPublicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) {
675 return false;
678 // Check length of uncompressed point coordinates.
679 if (aPublicValue->len != (2 * flen + 1)) {
680 return false;
683 ScopedSECItem ecPointX(::SECITEM_AllocItem(nullptr, nullptr, flen));
684 ScopedSECItem ecPointY(::SECITEM_AllocItem(nullptr, nullptr, flen));
685 if (!ecPointX || !ecPointY) {
686 return false;
689 // Extract point data.
690 memcpy(ecPointX->data, aPublicValue->data + 1, flen);
691 memcpy(ecPointY->data, aPublicValue->data + 1 + flen, flen);
693 CryptoBuffer x, y;
694 if (!x.Assign(ecPointX) || NS_FAILED(x.ToJwkBase64(aRetVal.mX.Value())) ||
695 !y.Assign(ecPointY) || NS_FAILED(y.ToJwkBase64(aRetVal.mY.Value()))) {
696 return false;
699 aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_EC);
700 return true;
703 nsresult
704 CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
705 JsonWebKey& aRetVal,
706 const nsNSSShutDownPreventionLock& /*proofOfLock*/)
708 switch (aPrivKey->keyType) {
709 case rsaKey: {
710 aRetVal.mN.Construct();
711 aRetVal.mE.Construct();
712 aRetVal.mD.Construct();
713 aRetVal.mP.Construct();
714 aRetVal.mQ.Construct();
715 aRetVal.mDp.Construct();
716 aRetVal.mDq.Construct();
717 aRetVal.mQi.Construct();
719 if (!ReadAndEncodeAttribute(aPrivKey, CKA_MODULUS, aRetVal.mN) ||
720 !ReadAndEncodeAttribute(aPrivKey, CKA_PUBLIC_EXPONENT, aRetVal.mE) ||
721 !ReadAndEncodeAttribute(aPrivKey, CKA_PRIVATE_EXPONENT, aRetVal.mD) ||
722 !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_1, aRetVal.mP) ||
723 !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_2, aRetVal.mQ) ||
724 !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_1, aRetVal.mDp) ||
725 !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_2, aRetVal.mDq) ||
726 !ReadAndEncodeAttribute(aPrivKey, CKA_COEFFICIENT, aRetVal.mQi)) {
727 return NS_ERROR_DOM_OPERATION_ERR;
730 aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA);
731 return NS_OK;
733 case ecKey: {
734 // Read EC params.
735 ScopedSECItem params(::SECITEM_AllocItem(nullptr, nullptr, 0));
736 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey,
737 CKA_EC_PARAMS, params);
738 if (rv != SECSuccess) {
739 return NS_ERROR_DOM_OPERATION_ERR;
742 // Read public point Q.
743 ScopedSECItem ecPoint(::SECITEM_AllocItem(nullptr, nullptr, 0));
744 rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey, CKA_EC_POINT,
745 ecPoint);
746 if (rv != SECSuccess) {
747 return NS_ERROR_DOM_OPERATION_ERR;
750 if (!ECKeyToJwk(PK11_TypePrivKey, aPrivKey, params, ecPoint, aRetVal)) {
751 return NS_ERROR_DOM_OPERATION_ERR;
754 aRetVal.mD.Construct();
756 // Read private value.
757 if (!ReadAndEncodeAttribute(aPrivKey, CKA_VALUE, aRetVal.mD)) {
758 return NS_ERROR_DOM_OPERATION_ERR;
761 return NS_OK;
763 default:
764 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
768 SECKEYPublicKey*
769 CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
770 const nsNSSShutDownPreventionLock& /*proofOfLock*/)
772 if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) {
773 // Verify that all of the required parameters are present
774 CryptoBuffer n, e;
775 if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
776 !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value()))) {
777 return nullptr;
780 // Transcode to a DER RSAPublicKey structure
781 struct RSAPublicKeyData {
782 SECItem n;
783 SECItem e;
785 const RSAPublicKeyData input = {
786 { siUnsignedInteger, n.Elements(), (unsigned int) n.Length() },
787 { siUnsignedInteger, e.Elements(), (unsigned int) e.Length() }
789 const SEC_ASN1Template rsaPublicKeyTemplate[] = {
790 {SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(RSAPublicKeyData)},
791 {SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, n),},
792 {SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, e),},
793 {0,}
796 ScopedSECItem pkDer(SEC_ASN1EncodeItem(nullptr, nullptr, &input,
797 rsaPublicKeyTemplate));
798 if (!pkDer.get()) {
799 return nullptr;
802 return SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA);
805 if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
806 // Verify that all of the required parameters are present
807 CryptoBuffer x, y;
808 if (!aJwk.mCrv.WasPassed() ||
809 !aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) ||
810 !aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value()))) {
811 return nullptr;
814 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
815 if (!arena) {
816 return nullptr;
819 SECKEYPublicKey* key = PORT_ArenaZNew(arena, SECKEYPublicKey);
820 if (!key) {
821 return nullptr;
824 key->keyType = ecKey;
825 key->pkcs11Slot = nullptr;
826 key->pkcs11ID = CK_INVALID_HANDLE;
828 nsString namedCurve;
829 if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
830 return nullptr;
833 // Create parameters.
834 SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
835 if (!params) {
836 return nullptr;
838 key->u.ec.DEREncodedParams = *params;
840 // Create point.
841 SECItem* point = CreateECPointForCoordinates(x, y, arena.get());
842 if (!point) {
843 return nullptr;
845 key->u.ec.publicValue = *point;
847 return SECKEY_CopyPublicKey(key);
850 return nullptr;
853 nsresult
854 CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
855 JsonWebKey& aRetVal,
856 const nsNSSShutDownPreventionLock& /*proofOfLock*/)
858 switch (aPubKey->keyType) {
859 case rsaKey: {
860 CryptoBuffer n, e;
861 aRetVal.mN.Construct();
862 aRetVal.mE.Construct();
864 if (!n.Assign(&aPubKey->u.rsa.modulus) ||
865 !e.Assign(&aPubKey->u.rsa.publicExponent) ||
866 NS_FAILED(n.ToJwkBase64(aRetVal.mN.Value())) ||
867 NS_FAILED(e.ToJwkBase64(aRetVal.mE.Value()))) {
868 return NS_ERROR_DOM_OPERATION_ERR;
871 aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA);
872 return NS_OK;
874 case ecKey:
875 if (!ECKeyToJwk(PK11_TypePubKey, aPubKey, &aPubKey->u.ec.DEREncodedParams,
876 &aPubKey->u.ec.publicValue, aRetVal)) {
877 return NS_ERROR_DOM_OPERATION_ERR;
879 return NS_OK;
880 default:
881 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
885 bool
886 CryptoKey::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
888 nsNSSShutDownPreventionLock locker;
889 if (isAlreadyShutDown()) {
890 return false;
893 // Write in five pieces
894 // 1. Attributes
895 // 2. Symmetric key as raw (if present)
896 // 3. Private key as pkcs8 (if present)
897 // 4. Public key as spki (if present)
898 // 5. Algorithm in whatever form it chooses
899 CryptoBuffer priv, pub;
901 if (mPrivateKey) {
902 CryptoKey::PrivateKeyToPkcs8(mPrivateKey, priv, locker);
905 if (mPublicKey) {
906 CryptoKey::PublicKeyToSpki(mPublicKey, pub, locker);
909 return JS_WriteUint32Pair(aWriter, mAttributes, CRYPTOKEY_SC_VERSION) &&
910 WriteBuffer(aWriter, mSymKey) &&
911 WriteBuffer(aWriter, priv) &&
912 WriteBuffer(aWriter, pub) &&
913 mAlgorithm.WriteStructuredClone(aWriter);
916 bool
917 CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader)
919 nsNSSShutDownPreventionLock locker;
920 if (isAlreadyShutDown()) {
921 return false;
924 uint32_t version;
925 CryptoBuffer sym, priv, pub;
927 bool read = JS_ReadUint32Pair(aReader, &mAttributes, &version) &&
928 (version == CRYPTOKEY_SC_VERSION) &&
929 ReadBuffer(aReader, sym) &&
930 ReadBuffer(aReader, priv) &&
931 ReadBuffer(aReader, pub) &&
932 mAlgorithm.ReadStructuredClone(aReader);
933 if (!read) {
934 return false;
937 if (sym.Length() > 0) {
938 mSymKey = sym;
940 if (priv.Length() > 0) {
941 mPrivateKey = CryptoKey::PrivateKeyFromPkcs8(priv, locker);
943 if (pub.Length() > 0) {
944 mPublicKey = CryptoKey::PublicKeyFromSpki(pub, locker);
947 // Ensure that what we've read is consistent
948 // If the attributes indicate a key type, should have a key of that type
949 if (!((GetKeyType() == SECRET && mSymKey.Length() > 0) ||
950 (GetKeyType() == PRIVATE && mPrivateKey) ||
951 (GetKeyType() == PUBLIC && mPublicKey))) {
952 return false;
955 return true;
958 } // namespace dom
959 } // namespace mozilla