LCOV - code coverage report
Current view: top level - dom/crypto - CryptoKey.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 659 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 62 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : // Templates taken from security/nss/lib/cryptohi/seckey.c
      17             : // These would ideally be exported by NSS and until that
      18             : // happens we have to keep our own copies.
      19             : const SEC_ASN1Template SECKEY_DHPublicKeyTemplate[] = {
      20             :     { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.publicValue), },
      21             :     { 0, }
      22             : };
      23             : const SEC_ASN1Template SECKEY_DHParamKeyTemplate[] = {
      24             :     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPublicKey) },
      25             :     { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.prime), },
      26             :     { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.base), },
      27             :     { SEC_ASN1_SKIP_REST },
      28             :     { 0, }
      29             : };
      30             : 
      31             : namespace mozilla {
      32             : namespace dom {
      33             : 
      34           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey, mGlobal)
      35           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKey)
      36           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKey)
      37           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKey)
      38           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
      39           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
      40           0 : NS_INTERFACE_MAP_END
      41             : 
      42             : nsresult
      43           0 : StringToUsage(const nsString& aUsage, CryptoKey::KeyUsage& aUsageOut)
      44             : {
      45           0 :   if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_ENCRYPT)) {
      46           0 :     aUsageOut = CryptoKey::ENCRYPT;
      47           0 :   } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DECRYPT)) {
      48           0 :     aUsageOut = CryptoKey::DECRYPT;
      49           0 :   } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_SIGN)) {
      50           0 :     aUsageOut = CryptoKey::SIGN;
      51           0 :   } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_VERIFY)) {
      52           0 :     aUsageOut = CryptoKey::VERIFY;
      53           0 :   } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEKEY)) {
      54           0 :     aUsageOut = CryptoKey::DERIVEKEY;
      55           0 :   } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEBITS)) {
      56           0 :     aUsageOut = CryptoKey::DERIVEBITS;
      57           0 :   } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_WRAPKEY)) {
      58           0 :     aUsageOut = CryptoKey::WRAPKEY;
      59           0 :   } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_UNWRAPKEY)) {
      60           0 :     aUsageOut = CryptoKey::UNWRAPKEY;
      61             :   } else {
      62           0 :     return NS_ERROR_DOM_SYNTAX_ERR;
      63             :   }
      64           0 :   return NS_OK;
      65             : }
      66             : 
      67             : // This helper function will release the memory backing a SECKEYPrivateKey and
      68             : // any resources acquired in its creation. It will leave the backing PKCS#11
      69             : // object untouched, however. This should only be called from
      70             : // PrivateKeyFromPrivateKeyTemplate.
      71             : static void
      72           0 : DestroyPrivateKeyWithoutDestroyingPKCS11Object(SECKEYPrivateKey* key)
      73             : {
      74           0 :   PK11_FreeSlot(key->pkcs11Slot);
      75           0 :   PORT_FreeArena(key->arena, PR_TRUE);
      76           0 : }
      77             : 
      78             : // To protect against key ID collisions, PrivateKeyFromPrivateKeyTemplate
      79             : // generates a random ID for each key. The given template must contain an
      80             : // attribute slot for a key ID, but it must consist of a null pointer and have a
      81             : // length of 0.
      82             : UniqueSECKEYPrivateKey
      83           0 : PrivateKeyFromPrivateKeyTemplate(CK_ATTRIBUTE* aTemplate,
      84             :                                  CK_ULONG aTemplateSize)
      85             : {
      86             :   // Create a generic object with the contents of the key
      87           0 :   UniquePK11SlotInfo slot(PK11_GetInternalSlot());
      88           0 :   if (!slot) {
      89           0 :     return nullptr;
      90             :   }
      91             : 
      92             :   // Generate a random 160-bit object ID. This ID must be unique.
      93           0 :   UniqueSECItem objID(::SECITEM_AllocItem(nullptr, nullptr, 20));
      94           0 :   SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), objID->data, objID->len);
      95           0 :   if (rv != SECSuccess) {
      96           0 :     return nullptr;
      97             :   }
      98             :   // Check if something is already using this ID.
      99           0 :   SECKEYPrivateKey* preexistingKey = PK11_FindKeyByKeyID(slot.get(),
     100             :                                                          objID.get(),
     101           0 :                                                          nullptr);
     102           0 :   if (preexistingKey) {
     103             :     // Note that we can't just call SECKEY_DestroyPrivateKey here because that
     104             :     // will destroy the PKCS#11 object that is backing a preexisting key (that
     105             :     // we still have a handle on somewhere else in memory). If that object were
     106             :     // destroyed, cryptographic operations performed by that other key would
     107             :     // fail.
     108           0 :     DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey);
     109             :     // Try again with a new ID (but only once - collisions are very unlikely).
     110           0 :     rv = PK11_GenerateRandomOnSlot(slot.get(), objID->data, objID->len);
     111           0 :     if (rv != SECSuccess) {
     112           0 :       return nullptr;
     113             :     }
     114           0 :     preexistingKey = PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr);
     115           0 :     if (preexistingKey) {
     116           0 :       DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey);
     117           0 :       return nullptr;
     118             :     }
     119             :   }
     120             : 
     121           0 :   CK_ATTRIBUTE* idAttributeSlot = nullptr;
     122           0 :   for (CK_ULONG i = 0; i < aTemplateSize; i++) {
     123           0 :     if (aTemplate[i].type == CKA_ID) {
     124           0 :       if (aTemplate[i].pValue != nullptr || aTemplate[i].ulValueLen != 0) {
     125           0 :         return nullptr;
     126             :       }
     127           0 :       idAttributeSlot = aTemplate + i;
     128           0 :       break;
     129             :     }
     130             :   }
     131           0 :   if (!idAttributeSlot) {
     132           0 :     return nullptr;
     133             :   }
     134             : 
     135           0 :   idAttributeSlot->pValue = objID->data;
     136           0 :   idAttributeSlot->ulValueLen = objID->len;
     137             :   UniquePK11GenericObject obj(PK11_CreateGenericObject(slot.get(),
     138             :                                                        aTemplate,
     139             :                                                        aTemplateSize,
     140           0 :                                                        PR_FALSE));
     141             :   // Unset the ID attribute slot's pointer and length so that data that only
     142             :   // lives for the scope of this function doesn't escape.
     143           0 :   idAttributeSlot->pValue = nullptr;
     144           0 :   idAttributeSlot->ulValueLen = 0;
     145           0 :   if (!obj) {
     146           0 :     return nullptr;
     147             :   }
     148             : 
     149             :   // Have NSS translate the object to a private key.
     150             :   return UniqueSECKEYPrivateKey(
     151           0 :     PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr));
     152             : }
     153             : 
     154           0 : CryptoKey::CryptoKey(nsIGlobalObject* aGlobal)
     155             :   : mGlobal(aGlobal)
     156             :   , mAttributes(0)
     157             :   , mSymKey()
     158             :   , mPrivateKey(nullptr)
     159           0 :   , mPublicKey(nullptr)
     160             : {
     161           0 : }
     162             : 
     163           0 : CryptoKey::~CryptoKey()
     164             : {
     165           0 :   nsNSSShutDownPreventionLock locker;
     166           0 :   if (isAlreadyShutDown()) {
     167           0 :     return;
     168             :   }
     169           0 :   destructorSafeDestroyNSSReference();
     170           0 :   shutdown(ShutdownCalledFrom::Object);
     171           0 : }
     172             : 
     173             : JSObject*
     174           0 : CryptoKey::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     175             : {
     176           0 :   return CryptoKeyBinding::Wrap(aCx, this, aGivenProto);
     177             : }
     178             : 
     179             : void
     180           0 : CryptoKey::GetType(nsString& aRetVal) const
     181             : {
     182           0 :   uint32_t type = mAttributes & TYPE_MASK;
     183           0 :   switch (type) {
     184           0 :     case PUBLIC:  aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC); break;
     185           0 :     case PRIVATE: aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE); break;
     186           0 :     case SECRET:  aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_SECRET); break;
     187             :   }
     188           0 : }
     189             : 
     190             : bool
     191           0 : CryptoKey::Extractable() const
     192             : {
     193           0 :   return (mAttributes & EXTRACTABLE);
     194             : }
     195             : 
     196             : void
     197           0 : CryptoKey::GetAlgorithm(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal,
     198             :                         ErrorResult& aRv) const
     199             : {
     200           0 :   bool converted = false;
     201           0 :   JS::RootedValue val(cx);
     202           0 :   switch (mAlgorithm.mType) {
     203             :     case KeyAlgorithmProxy::AES:
     204           0 :       converted = ToJSValue(cx, mAlgorithm.mAes, &val);
     205           0 :       break;
     206             :     case KeyAlgorithmProxy::HMAC:
     207           0 :       converted = ToJSValue(cx, mAlgorithm.mHmac, &val);
     208           0 :       break;
     209             :     case KeyAlgorithmProxy::RSA: {
     210           0 :       RootedDictionary<RsaHashedKeyAlgorithm> rsa(cx);
     211           0 :       converted = mAlgorithm.mRsa.ToKeyAlgorithm(cx, rsa);
     212           0 :       if (converted) {
     213           0 :         converted = ToJSValue(cx, rsa, &val);
     214             :       }
     215           0 :       break;
     216             :     }
     217             :     case KeyAlgorithmProxy::EC:
     218           0 :       converted = ToJSValue(cx, mAlgorithm.mEc, &val);
     219           0 :       break;
     220             :     case KeyAlgorithmProxy::DH: {
     221           0 :       RootedDictionary<DhKeyAlgorithm> dh(cx);
     222           0 :       converted = mAlgorithm.mDh.ToKeyAlgorithm(cx, dh);
     223           0 :       if (converted) {
     224           0 :         converted = ToJSValue(cx, dh, &val);
     225             :       }
     226           0 :       break;
     227             :     }
     228             :   }
     229           0 :   if (!converted) {
     230           0 :     aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
     231           0 :     return;
     232             :   }
     233             : 
     234           0 :   aRetVal.set(&val.toObject());
     235             : }
     236             : 
     237             : void
     238           0 : CryptoKey::GetUsages(nsTArray<nsString>& aRetVal) const
     239             : {
     240           0 :   if (mAttributes & ENCRYPT) {
     241           0 :     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_ENCRYPT));
     242             :   }
     243           0 :   if (mAttributes & DECRYPT) {
     244           0 :     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DECRYPT));
     245             :   }
     246           0 :   if (mAttributes & SIGN) {
     247           0 :     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_SIGN));
     248             :   }
     249           0 :   if (mAttributes & VERIFY) {
     250           0 :     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_VERIFY));
     251             :   }
     252           0 :   if (mAttributes & DERIVEKEY) {
     253           0 :     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEKEY));
     254             :   }
     255           0 :   if (mAttributes & DERIVEBITS) {
     256           0 :     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEBITS));
     257             :   }
     258           0 :   if (mAttributes & WRAPKEY) {
     259           0 :     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_WRAPKEY));
     260             :   }
     261           0 :   if (mAttributes & UNWRAPKEY) {
     262           0 :     aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_UNWRAPKEY));
     263             :   }
     264           0 : }
     265             : 
     266             : KeyAlgorithmProxy&
     267           0 : CryptoKey::Algorithm()
     268             : {
     269           0 :   return mAlgorithm;
     270             : }
     271             : 
     272             : const KeyAlgorithmProxy&
     273           0 : CryptoKey::Algorithm() const
     274             : {
     275           0 :   return mAlgorithm;
     276             : }
     277             : 
     278             : CryptoKey::KeyType
     279           0 : CryptoKey::GetKeyType() const
     280             : {
     281           0 :   return static_cast<CryptoKey::KeyType>(mAttributes & TYPE_MASK);
     282             : }
     283             : 
     284             : nsresult
     285           0 : CryptoKey::SetType(const nsString& aType)
     286             : {
     287           0 :   mAttributes &= CLEAR_TYPE;
     288           0 :   if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_SECRET)) {
     289           0 :     mAttributes |= SECRET;
     290           0 :   } else if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC)) {
     291           0 :     mAttributes |= PUBLIC;
     292           0 :   } else if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE)) {
     293           0 :     mAttributes |= PRIVATE;
     294             :   } else {
     295           0 :     mAttributes |= UNKNOWN;
     296           0 :     return NS_ERROR_DOM_SYNTAX_ERR;
     297             :   }
     298             : 
     299           0 :   return NS_OK;
     300             : }
     301             : 
     302             : void
     303           0 : CryptoKey::SetType(CryptoKey::KeyType aType)
     304             : {
     305           0 :   mAttributes &= CLEAR_TYPE;
     306           0 :   mAttributes |= aType;
     307           0 : }
     308             : 
     309             : void
     310           0 : CryptoKey::SetExtractable(bool aExtractable)
     311             : {
     312           0 :   mAttributes &= CLEAR_EXTRACTABLE;
     313           0 :   if (aExtractable) {
     314           0 :     mAttributes |= EXTRACTABLE;
     315             :   }
     316           0 : }
     317             : 
     318             : // NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the
     319             : // public value. To properly export the private key to JWK or PKCS #8 we need
     320             : // the public key data though and so we use this method to augment a private
     321             : // key with data from the given public key.
     322             : nsresult
     323           0 : CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey)
     324             : {
     325             :   // This should be a private key.
     326           0 :   MOZ_ASSERT(GetKeyType() == PRIVATE);
     327             :   // There should be a private NSS key with type 'EC'.
     328           0 :   MOZ_ASSERT(mPrivateKey && mPrivateKey->keyType == ecKey);
     329             :   // The given public key should have the same key type.
     330           0 :   MOZ_ASSERT(aPublicKey->keyType == mPrivateKey->keyType);
     331             : 
     332           0 :   nsNSSShutDownPreventionLock locker;
     333             : 
     334             :   // Read EC params.
     335           0 :   ScopedAutoSECItem params;
     336           0 :   SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey.get(),
     337           0 :                                        CKA_EC_PARAMS, &params);
     338           0 :   if (rv != SECSuccess) {
     339           0 :     return NS_ERROR_DOM_OPERATION_ERR;
     340             :   }
     341             : 
     342             :   // Read private value.
     343           0 :   ScopedAutoSECItem value;
     344           0 :   rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey.get(), CKA_VALUE,
     345           0 :                              &value);
     346           0 :   if (rv != SECSuccess) {
     347           0 :     return NS_ERROR_DOM_OPERATION_ERR;
     348             :   }
     349             : 
     350           0 :   SECItem* point = &aPublicKey->u.ec.publicValue;
     351           0 :   CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
     352           0 :   CK_BBOOL falseValue = CK_FALSE;
     353           0 :   CK_KEY_TYPE ecValue = CKK_EC;
     354             : 
     355             :   CK_ATTRIBUTE keyTemplate[9] = {
     356             :     { CKA_CLASS,            &privateKeyValue,     sizeof(privateKeyValue) },
     357             :     { CKA_KEY_TYPE,         &ecValue,             sizeof(ecValue) },
     358             :     { CKA_TOKEN,            &falseValue,          sizeof(falseValue) },
     359             :     { CKA_SENSITIVE,        &falseValue,          sizeof(falseValue) },
     360             :     { CKA_PRIVATE,          &falseValue,          sizeof(falseValue) },
     361             :     // PrivateKeyFromPrivateKeyTemplate sets the ID.
     362             :     { CKA_ID,               nullptr,              0 },
     363           0 :     { CKA_EC_PARAMS,        params.data,          params.len },
     364           0 :     { CKA_EC_POINT,         point->data,          point->len },
     365           0 :     { CKA_VALUE,            value.data,           value.len },
     366           0 :   };
     367             : 
     368           0 :   mPrivateKey = PrivateKeyFromPrivateKeyTemplate(keyTemplate,
     369           0 :                                                  ArrayLength(keyTemplate));
     370           0 :   NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR);
     371             : 
     372           0 :   return NS_OK;
     373             : }
     374             : 
     375             : void
     376           0 : CryptoKey::ClearUsages()
     377             : {
     378           0 :   mAttributes &= CLEAR_USAGES;
     379           0 : }
     380             : 
     381             : nsresult
     382           0 : CryptoKey::AddUsage(const nsString& aUsage)
     383             : {
     384           0 :   return AddUsageIntersecting(aUsage, USAGES_MASK);
     385             : }
     386             : 
     387             : nsresult
     388           0 : CryptoKey::AddUsageIntersecting(const nsString& aUsage, uint32_t aUsageMask)
     389             : {
     390             :   KeyUsage usage;
     391           0 :   if (NS_FAILED(StringToUsage(aUsage, usage))) {
     392           0 :     return NS_ERROR_DOM_SYNTAX_ERR;
     393             :   }
     394             : 
     395           0 :   if (usage & aUsageMask) {
     396           0 :     AddUsage(usage);
     397           0 :     return NS_OK;
     398             :   }
     399             : 
     400           0 :   return NS_OK;
     401             : }
     402             : 
     403             : void
     404           0 : CryptoKey::AddUsage(CryptoKey::KeyUsage aUsage)
     405             : {
     406           0 :   mAttributes |= aUsage;
     407           0 : }
     408             : 
     409             : bool
     410           0 : CryptoKey::HasAnyUsage()
     411             : {
     412           0 :   return !!(mAttributes & USAGES_MASK);
     413             : }
     414             : 
     415             : bool
     416           0 : CryptoKey::HasUsage(CryptoKey::KeyUsage aUsage)
     417             : {
     418           0 :   return !!(mAttributes & aUsage);
     419             : }
     420             : 
     421             : bool
     422           0 : CryptoKey::HasUsageOtherThan(uint32_t aUsages)
     423             : {
     424           0 :   return !!(mAttributes & USAGES_MASK & ~aUsages);
     425             : }
     426             : 
     427             : bool
     428           0 : CryptoKey::IsRecognizedUsage(const nsString& aUsage)
     429             : {
     430             :   KeyUsage dummy;
     431           0 :   nsresult rv = StringToUsage(aUsage, dummy);
     432           0 :   return NS_SUCCEEDED(rv);
     433             : }
     434             : 
     435             : bool
     436           0 : CryptoKey::AllUsagesRecognized(const Sequence<nsString>& aUsages)
     437             : {
     438           0 :   for (uint32_t i = 0; i < aUsages.Length(); ++i) {
     439           0 :     if (!IsRecognizedUsage(aUsages[i])) {
     440           0 :       return false;
     441             :     }
     442             :   }
     443           0 :   return true;
     444             : }
     445             : 
     446           0 : nsresult CryptoKey::SetSymKey(const CryptoBuffer& aSymKey)
     447             : {
     448           0 :   if (!mSymKey.Assign(aSymKey)) {
     449           0 :     return NS_ERROR_OUT_OF_MEMORY;
     450             :   }
     451             : 
     452           0 :   return NS_OK;
     453             : }
     454             : 
     455             : nsresult
     456           0 : CryptoKey::SetPrivateKey(SECKEYPrivateKey* aPrivateKey)
     457             : {
     458           0 :   nsNSSShutDownPreventionLock locker;
     459             : 
     460           0 :   if (!aPrivateKey || isAlreadyShutDown()) {
     461           0 :     mPrivateKey = nullptr;
     462           0 :     return NS_OK;
     463             :   }
     464             : 
     465           0 :   mPrivateKey = UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(aPrivateKey));
     466           0 :   return mPrivateKey ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
     467             : }
     468             : 
     469             : nsresult
     470           0 : CryptoKey::SetPublicKey(SECKEYPublicKey* aPublicKey)
     471             : {
     472           0 :   nsNSSShutDownPreventionLock locker;
     473             : 
     474           0 :   if (!aPublicKey || isAlreadyShutDown()) {
     475           0 :     mPublicKey = nullptr;
     476           0 :     return NS_OK;
     477             :   }
     478             : 
     479           0 :   mPublicKey = UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(aPublicKey));
     480           0 :   return mPublicKey ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
     481             : }
     482             : 
     483             : const CryptoBuffer&
     484           0 : CryptoKey::GetSymKey() const
     485             : {
     486           0 :   return mSymKey;
     487             : }
     488             : 
     489             : UniqueSECKEYPrivateKey
     490           0 : CryptoKey::GetPrivateKey() const
     491             : {
     492           0 :   nsNSSShutDownPreventionLock locker;
     493           0 :   if (!mPrivateKey || isAlreadyShutDown()) {
     494           0 :     return nullptr;
     495             :   }
     496           0 :   return UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(mPrivateKey.get()));
     497             : }
     498             : 
     499             : UniqueSECKEYPublicKey
     500           0 : CryptoKey::GetPublicKey() const
     501             : {
     502           0 :   nsNSSShutDownPreventionLock locker;
     503           0 :   if (!mPublicKey || isAlreadyShutDown()) {
     504           0 :     return nullptr;
     505             :   }
     506           0 :   return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(mPublicKey.get()));
     507             : }
     508             : 
     509           0 : void CryptoKey::virtualDestroyNSSReference()
     510             : {
     511           0 :   destructorSafeDestroyNSSReference();
     512           0 : }
     513             : 
     514           0 : void CryptoKey::destructorSafeDestroyNSSReference()
     515             : {
     516           0 :   mPrivateKey = nullptr;
     517           0 :   mPublicKey = nullptr;
     518           0 : }
     519             : 
     520             : 
     521             : // Serialization and deserialization convenience methods
     522             : 
     523             : UniqueSECKEYPrivateKey
     524           0 : CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
     525             :                          const nsNSSShutDownPreventionLock& /*proofOfLock*/)
     526             : {
     527           0 :   UniquePK11SlotInfo slot(PK11_GetInternalSlot());
     528           0 :   if (!slot) {
     529           0 :     return nullptr;
     530             :   }
     531             : 
     532           0 :   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
     533           0 :   if (!arena) {
     534           0 :     return nullptr;
     535             :   }
     536             : 
     537           0 :   SECItem pkcs8Item = { siBuffer, nullptr, 0 };
     538           0 :   if (!aKeyData.ToSECItem(arena.get(), &pkcs8Item)) {
     539           0 :     return nullptr;
     540             :   }
     541             : 
     542             :   // Allow everything, we enforce usage ourselves
     543           0 :   unsigned int usage = KU_ALL;
     544             : 
     545             :   SECKEYPrivateKey* privKey;
     546           0 :   SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
     547             :                  slot.get(), &pkcs8Item, nullptr, nullptr, false, false,
     548           0 :                  usage, &privKey, nullptr);
     549             : 
     550           0 :   if (rv == SECFailure) {
     551           0 :     return nullptr;
     552             :   }
     553             : 
     554           0 :   return UniqueSECKEYPrivateKey(privKey);
     555             : }
     556             : 
     557             : UniqueSECKEYPublicKey
     558           0 : CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData,
     559             :                        const nsNSSShutDownPreventionLock& /*proofOfLock*/)
     560             : {
     561           0 :   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
     562           0 :   if (!arena) {
     563           0 :     return nullptr;
     564             :   }
     565             : 
     566           0 :   SECItem spkiItem = { siBuffer, nullptr, 0 };
     567           0 :   if (!aKeyData.ToSECItem(arena.get(), &spkiItem)) {
     568           0 :     return nullptr;
     569             :   }
     570             : 
     571             :   UniqueCERTSubjectPublicKeyInfo spki(
     572           0 :     SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
     573           0 :   if (!spki) {
     574           0 :     return nullptr;
     575             :   }
     576             : 
     577           0 :   bool isECDHAlgorithm = SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH,
     578           0 :                                                &spki->algorithm.algorithm);
     579           0 :   bool isDHAlgorithm = SECITEM_ItemsAreEqual(&SEC_OID_DATA_DH_KEY_AGREEMENT,
     580           0 :                                              &spki->algorithm.algorithm);
     581             : 
     582             :   // Check for |id-ecDH| and |dhKeyAgreement|. Per the WebCrypto spec we must
     583             :   // support these OIDs but NSS does unfortunately not know about them. Let's
     584             :   // change the algorithm to |id-ecPublicKey| or |dhPublicKey| to make NSS happy.
     585           0 :   if (isECDHAlgorithm || isDHAlgorithm) {
     586           0 :     SECOidTag oid = SEC_OID_UNKNOWN;
     587           0 :     if (isECDHAlgorithm) {
     588           0 :       oid = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
     589           0 :     } else if (isDHAlgorithm) {
     590           0 :       oid = SEC_OID_X942_DIFFIE_HELMAN_KEY;
     591             :     } else {
     592           0 :       MOZ_ASSERT(false);
     593             :     }
     594             : 
     595           0 :     SECOidData* oidData = SECOID_FindOIDByTag(oid);
     596           0 :     if (!oidData) {
     597           0 :       return nullptr;
     598             :     }
     599             : 
     600           0 :     SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
     601           0 :                                     &oidData->oid);
     602           0 :     if (rv != SECSuccess) {
     603           0 :       return nullptr;
     604             :     }
     605             :   }
     606             : 
     607           0 :   UniqueSECKEYPublicKey tmp(SECKEY_ExtractPublicKey(spki.get()));
     608           0 :   if (!tmp.get() || !PublicKeyValid(tmp.get())) {
     609           0 :     return nullptr;
     610             :   }
     611             : 
     612           0 :   return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(tmp.get()));
     613             : }
     614             : 
     615             : nsresult
     616           0 : CryptoKey::PrivateKeyToPkcs8(SECKEYPrivateKey* aPrivKey,
     617             :                        CryptoBuffer& aRetVal,
     618             :                        const nsNSSShutDownPreventionLock& /*proofOfLock*/)
     619             : {
     620           0 :   UniqueSECItem pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey, nullptr));
     621           0 :   if (!pkcs8Item.get()) {
     622           0 :     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
     623             :   }
     624           0 :   if (!aRetVal.Assign(pkcs8Item.get())) {
     625           0 :     return NS_ERROR_DOM_OPERATION_ERR;
     626             :   }
     627           0 :   return NS_OK;
     628             : }
     629             : 
     630             : nsresult
     631           0 : PublicDhKeyToSpki(SECKEYPublicKey* aPubKey,
     632             :                   CERTSubjectPublicKeyInfo* aSpki)
     633             : {
     634           0 :   SECItem* params = ::SECITEM_AllocItem(aSpki->arena, nullptr, 0);
     635           0 :   if (!params) {
     636           0 :     return NS_ERROR_DOM_OPERATION_ERR;
     637             :   }
     638             : 
     639           0 :   SECItem* rvItem = SEC_ASN1EncodeItem(aSpki->arena, params, aPubKey,
     640           0 :                                        SECKEY_DHParamKeyTemplate);
     641           0 :   if (!rvItem) {
     642           0 :     return NS_ERROR_DOM_OPERATION_ERR;
     643             :   }
     644             : 
     645           0 :   SECStatus rv = SECOID_SetAlgorithmID(aSpki->arena, &aSpki->algorithm,
     646           0 :                                        SEC_OID_X942_DIFFIE_HELMAN_KEY, params);
     647           0 :   if (rv != SECSuccess) {
     648           0 :     return NS_ERROR_DOM_OPERATION_ERR;
     649             :   }
     650             : 
     651           0 :   rvItem = SEC_ASN1EncodeItem(aSpki->arena, &aSpki->subjectPublicKey, aPubKey,
     652           0 :                               SECKEY_DHPublicKeyTemplate);
     653           0 :   if (!rvItem) {
     654           0 :     return NS_ERROR_DOM_OPERATION_ERR;
     655             :   }
     656             : 
     657             :   // The public value is a BIT_STRING encoded as an INTEGER. After encoding
     658             :   // an INT we need to adjust the length to reflect the number of bits.
     659           0 :   aSpki->subjectPublicKey.len <<= 3;
     660             : 
     661           0 :   return NS_OK;
     662             : }
     663             : 
     664             : nsresult
     665           0 : CryptoKey::PublicKeyToSpki(SECKEYPublicKey* aPubKey,
     666             :                            CryptoBuffer& aRetVal,
     667             :                            const nsNSSShutDownPreventionLock& /*proofOfLock*/)
     668             : {
     669           0 :   UniqueCERTSubjectPublicKeyInfo spki;
     670             : 
     671             :   // NSS doesn't support exporting DH public keys.
     672           0 :   if (aPubKey->keyType == dhKey) {
     673             :     // Mimic the behavior of SECKEY_CreateSubjectPublicKeyInfo() and create
     674             :     // a new arena for the SPKI object.
     675           0 :     UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
     676           0 :     if (!arena) {
     677           0 :       return NS_ERROR_DOM_OPERATION_ERR;
     678             :     }
     679             : 
     680           0 :     spki.reset(PORT_ArenaZNew(arena.get(), CERTSubjectPublicKeyInfo));
     681           0 :     if (!spki) {
     682           0 :       return NS_ERROR_DOM_OPERATION_ERR;
     683             :     }
     684             : 
     685             :     // Assign |arena| to |spki| and null the variable afterwards so that the
     686             :     // arena created above that holds the SPKI object is free'd when |spki|
     687             :     // goes out of scope, not when |arena| does.
     688           0 :     spki->arena = arena.release();
     689             : 
     690           0 :     nsresult rv = PublicDhKeyToSpki(aPubKey, spki.get());
     691           0 :     NS_ENSURE_SUCCESS(rv, rv);
     692             :   } else {
     693           0 :     spki.reset(SECKEY_CreateSubjectPublicKeyInfo(aPubKey));
     694           0 :     if (!spki) {
     695           0 :       return NS_ERROR_DOM_OPERATION_ERR;
     696             :     }
     697             :   }
     698             : 
     699             :   // Per WebCrypto spec we must export ECDH SPKIs with the algorithm OID
     700             :   // id-ecDH (1.3.132.112) and DH SPKIs with OID dhKeyAgreement
     701             :   // (1.2.840.113549.1.3.1). NSS doesn't know about these OIDs and there is
     702             :   // no way to specify the algorithm to use when exporting a public key.
     703           0 :   if (aPubKey->keyType == ecKey || aPubKey->keyType == dhKey) {
     704           0 :     const SECItem* oidData = nullptr;
     705           0 :     if (aPubKey->keyType == ecKey) {
     706           0 :       oidData = &SEC_OID_DATA_EC_DH;
     707           0 :     } else if (aPubKey->keyType == dhKey) {
     708           0 :       oidData = &SEC_OID_DATA_DH_KEY_AGREEMENT;
     709             :     } else {
     710           0 :       MOZ_ASSERT(false);
     711             :     }
     712             : 
     713           0 :     SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
     714           0 :                                     oidData);
     715           0 :     if (rv != SECSuccess) {
     716           0 :       return NS_ERROR_DOM_OPERATION_ERR;
     717             :     }
     718             :   }
     719             : 
     720           0 :   const SEC_ASN1Template* tpl = SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate);
     721           0 :   UniqueSECItem spkiItem(SEC_ASN1EncodeItem(nullptr, nullptr, spki.get(), tpl));
     722             : 
     723           0 :   if (!aRetVal.Assign(spkiItem.get())) {
     724           0 :     return NS_ERROR_DOM_OPERATION_ERR;
     725             :   }
     726           0 :   return NS_OK;
     727             : }
     728             : 
     729             : SECItem*
     730           0 : CreateECPointForCoordinates(const CryptoBuffer& aX,
     731             :                             const CryptoBuffer& aY,
     732             :                             PLArenaPool* aArena)
     733             : {
     734             :   // Check that both points have the same length.
     735           0 :   if (aX.Length() != aY.Length()) {
     736           0 :     return nullptr;
     737             :   }
     738             : 
     739             :   // Create point.
     740           0 :   SECItem* point = ::SECITEM_AllocItem(aArena, nullptr, aX.Length() + aY.Length() + 1);
     741           0 :   if (!point) {
     742           0 :     return nullptr;
     743             :   }
     744             : 
     745             :   // Set point data.
     746           0 :   point->data[0] = EC_POINT_FORM_UNCOMPRESSED;
     747           0 :   memcpy(point->data + 1, aX.Elements(), aX.Length());
     748           0 :   memcpy(point->data + 1 + aX.Length(), aY.Elements(), aY.Length());
     749             : 
     750           0 :   return point;
     751             : }
     752             : 
     753             : UniqueSECKEYPrivateKey
     754           0 : CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
     755             :                              const nsNSSShutDownPreventionLock& /*proofOfLock*/)
     756             : {
     757           0 :   CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
     758           0 :   CK_BBOOL falseValue = CK_FALSE;
     759             : 
     760           0 :   if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
     761             :     // Verify that all of the required parameters are present
     762           0 :     CryptoBuffer x, y, d;
     763           0 :     if (!aJwk.mCrv.WasPassed() ||
     764           0 :         !aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) ||
     765           0 :         !aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value())) ||
     766           0 :         !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value()))) {
     767           0 :       return nullptr;
     768             :     }
     769             : 
     770           0 :     nsString namedCurve;
     771           0 :     if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
     772           0 :       return nullptr;
     773             :     }
     774             : 
     775           0 :     UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
     776           0 :     if (!arena) {
     777           0 :       return nullptr;
     778             :     }
     779             : 
     780             :     // Create parameters.
     781           0 :     SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
     782           0 :     if (!params) {
     783           0 :       return nullptr;
     784             :     }
     785             : 
     786           0 :     SECItem* ecPoint = CreateECPointForCoordinates(x, y, arena.get());
     787           0 :     if (!ecPoint) {
     788           0 :       return nullptr;
     789             :     }
     790             : 
     791             :     // Populate template from parameters
     792           0 :     CK_KEY_TYPE ecValue = CKK_EC;
     793             :     CK_ATTRIBUTE keyTemplate[9] = {
     794             :       { CKA_CLASS,            &privateKeyValue,     sizeof(privateKeyValue) },
     795             :       { CKA_KEY_TYPE,         &ecValue,             sizeof(ecValue) },
     796             :       { CKA_TOKEN,            &falseValue,          sizeof(falseValue) },
     797             :       { CKA_SENSITIVE,        &falseValue,          sizeof(falseValue) },
     798             :       { CKA_PRIVATE,          &falseValue,          sizeof(falseValue) },
     799             :       // PrivateKeyFromPrivateKeyTemplate sets the ID.
     800             :       { CKA_ID,               nullptr,              0 },
     801           0 :       { CKA_EC_PARAMS,        params->data,         params->len },
     802           0 :       { CKA_EC_POINT,         ecPoint->data,        ecPoint->len },
     803           0 :       { CKA_VALUE,            (void*) d.Elements(), (CK_ULONG) d.Length() },
     804           0 :     };
     805             : 
     806             :     return PrivateKeyFromPrivateKeyTemplate(keyTemplate,
     807           0 :                                             ArrayLength(keyTemplate));
     808             :   }
     809             : 
     810           0 :   if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) {
     811             :     // Verify that all of the required parameters are present
     812           0 :     CryptoBuffer n, e, d, p, q, dp, dq, qi;
     813           0 :     if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
     814           0 :         !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value())) ||
     815           0 :         !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value())) ||
     816           0 :         !aJwk.mP.WasPassed() || NS_FAILED(p.FromJwkBase64(aJwk.mP.Value())) ||
     817           0 :         !aJwk.mQ.WasPassed() || NS_FAILED(q.FromJwkBase64(aJwk.mQ.Value())) ||
     818           0 :         !aJwk.mDp.WasPassed() || NS_FAILED(dp.FromJwkBase64(aJwk.mDp.Value())) ||
     819           0 :         !aJwk.mDq.WasPassed() || NS_FAILED(dq.FromJwkBase64(aJwk.mDq.Value())) ||
     820           0 :         !aJwk.mQi.WasPassed() || NS_FAILED(qi.FromJwkBase64(aJwk.mQi.Value()))) {
     821           0 :       return nullptr;
     822             :     }
     823             : 
     824             :     // Populate template from parameters
     825           0 :     CK_KEY_TYPE rsaValue = CKK_RSA;
     826             :     CK_ATTRIBUTE keyTemplate[14] = {
     827             :       { CKA_CLASS,            &privateKeyValue,      sizeof(privateKeyValue) },
     828             :       { CKA_KEY_TYPE,         &rsaValue,             sizeof(rsaValue) },
     829             :       { CKA_TOKEN,            &falseValue,           sizeof(falseValue) },
     830             :       { CKA_SENSITIVE,        &falseValue,           sizeof(falseValue) },
     831             :       { CKA_PRIVATE,          &falseValue,           sizeof(falseValue) },
     832             :       // PrivateKeyFromPrivateKeyTemplate sets the ID.
     833             :       { CKA_ID,               nullptr,               0 },
     834           0 :       { CKA_MODULUS,          (void*) n.Elements(),  (CK_ULONG) n.Length() },
     835           0 :       { CKA_PUBLIC_EXPONENT,  (void*) e.Elements(),  (CK_ULONG) e.Length() },
     836           0 :       { CKA_PRIVATE_EXPONENT, (void*) d.Elements(),  (CK_ULONG) d.Length() },
     837           0 :       { CKA_PRIME_1,          (void*) p.Elements(),  (CK_ULONG) p.Length() },
     838           0 :       { CKA_PRIME_2,          (void*) q.Elements(),  (CK_ULONG) q.Length() },
     839           0 :       { CKA_EXPONENT_1,       (void*) dp.Elements(), (CK_ULONG) dp.Length() },
     840           0 :       { CKA_EXPONENT_2,       (void*) dq.Elements(), (CK_ULONG) dq.Length() },
     841           0 :       { CKA_COEFFICIENT,      (void*) qi.Elements(), (CK_ULONG) qi.Length() },
     842           0 :     };
     843             : 
     844             :     return PrivateKeyFromPrivateKeyTemplate(keyTemplate,
     845           0 :                                             ArrayLength(keyTemplate));
     846             :   }
     847             : 
     848           0 :   return nullptr;
     849             : }
     850             : 
     851           0 : bool ReadAndEncodeAttribute(SECKEYPrivateKey* aKey,
     852             :                             CK_ATTRIBUTE_TYPE aAttribute,
     853             :                             Optional<nsString>& aDst)
     854             : {
     855           0 :   ScopedAutoSECItem item;
     856           0 :   if (PK11_ReadRawAttribute(PK11_TypePrivKey, aKey, aAttribute, &item)
     857             :         != SECSuccess) {
     858           0 :     return false;
     859             :   }
     860             : 
     861           0 :   CryptoBuffer buffer;
     862           0 :   if (!buffer.Assign(&item)) {
     863           0 :     return false;
     864             :   }
     865             : 
     866           0 :   if (NS_FAILED(buffer.ToJwkBase64(aDst.Value()))) {
     867           0 :     return false;
     868             :   }
     869             : 
     870           0 :   return true;
     871             : }
     872             : 
     873             : bool
     874           0 : ECKeyToJwk(const PK11ObjectType aKeyType, void* aKey, const SECItem* aEcParams,
     875             :            const SECItem* aPublicValue, JsonWebKey& aRetVal)
     876             : {
     877           0 :   aRetVal.mX.Construct();
     878           0 :   aRetVal.mY.Construct();
     879             : 
     880             :   // Check that the given EC parameters are valid.
     881           0 :   if (!CheckEncodedECParameters(aEcParams)) {
     882           0 :     return false;
     883             :   }
     884             : 
     885             :   // Construct the OID tag.
     886           0 :   SECItem oid = { siBuffer, nullptr, 0 };
     887           0 :   oid.len = aEcParams->data[1];
     888           0 :   oid.data = aEcParams->data + 2;
     889             : 
     890             :   uint32_t flen;
     891           0 :   switch (SECOID_FindOIDTag(&oid)) {
     892             :     case SEC_OID_SECG_EC_SECP256R1:
     893           0 :       flen = 32; // bytes
     894           0 :       aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P256));
     895           0 :       break;
     896             :     case SEC_OID_SECG_EC_SECP384R1:
     897           0 :       flen = 48; // bytes
     898           0 :       aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P384));
     899           0 :       break;
     900             :     case SEC_OID_SECG_EC_SECP521R1:
     901           0 :       flen = 66; // bytes
     902           0 :       aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P521));
     903           0 :       break;
     904             :     default:
     905           0 :       return false;
     906             :   }
     907             : 
     908             :   // No support for compressed points.
     909           0 :   if (aPublicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) {
     910           0 :     return false;
     911             :   }
     912             : 
     913             :   // Check length of uncompressed point coordinates.
     914           0 :   if (aPublicValue->len != (2 * flen + 1)) {
     915           0 :     return false;
     916             :   }
     917             : 
     918           0 :   UniqueSECItem ecPointX(::SECITEM_AllocItem(nullptr, nullptr, flen));
     919           0 :   UniqueSECItem ecPointY(::SECITEM_AllocItem(nullptr, nullptr, flen));
     920           0 :   if (!ecPointX || !ecPointY) {
     921           0 :     return false;
     922             :   }
     923             : 
     924             :   // Extract point data.
     925           0 :   memcpy(ecPointX->data, aPublicValue->data + 1, flen);
     926           0 :   memcpy(ecPointY->data, aPublicValue->data + 1 + flen, flen);
     927             : 
     928           0 :   CryptoBuffer x, y;
     929           0 :   if (!x.Assign(ecPointX.get()) ||
     930           0 :       NS_FAILED(x.ToJwkBase64(aRetVal.mX.Value())) ||
     931           0 :       !y.Assign(ecPointY.get()) ||
     932           0 :       NS_FAILED(y.ToJwkBase64(aRetVal.mY.Value()))) {
     933           0 :     return false;
     934             :   }
     935             : 
     936           0 :   aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_EC);
     937           0 :   return true;
     938             : }
     939             : 
     940             : nsresult
     941           0 : CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
     942             :                            JsonWebKey& aRetVal,
     943             :                            const nsNSSShutDownPreventionLock& /*proofOfLock*/)
     944             : {
     945           0 :   switch (aPrivKey->keyType) {
     946             :     case rsaKey: {
     947           0 :       aRetVal.mN.Construct();
     948           0 :       aRetVal.mE.Construct();
     949           0 :       aRetVal.mD.Construct();
     950           0 :       aRetVal.mP.Construct();
     951           0 :       aRetVal.mQ.Construct();
     952           0 :       aRetVal.mDp.Construct();
     953           0 :       aRetVal.mDq.Construct();
     954           0 :       aRetVal.mQi.Construct();
     955             : 
     956           0 :       if (!ReadAndEncodeAttribute(aPrivKey, CKA_MODULUS, aRetVal.mN) ||
     957           0 :           !ReadAndEncodeAttribute(aPrivKey, CKA_PUBLIC_EXPONENT, aRetVal.mE) ||
     958           0 :           !ReadAndEncodeAttribute(aPrivKey, CKA_PRIVATE_EXPONENT, aRetVal.mD) ||
     959           0 :           !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_1, aRetVal.mP) ||
     960           0 :           !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_2, aRetVal.mQ) ||
     961           0 :           !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_1, aRetVal.mDp) ||
     962           0 :           !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_2, aRetVal.mDq) ||
     963           0 :           !ReadAndEncodeAttribute(aPrivKey, CKA_COEFFICIENT, aRetVal.mQi)) {
     964           0 :         return NS_ERROR_DOM_OPERATION_ERR;
     965             :       }
     966             : 
     967           0 :       aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA);
     968           0 :       return NS_OK;
     969             :     }
     970             :     case ecKey: {
     971             :       // Read EC params.
     972           0 :       ScopedAutoSECItem params;
     973             :       SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey,
     974           0 :                                            CKA_EC_PARAMS, &params);
     975           0 :       if (rv != SECSuccess) {
     976           0 :         return NS_ERROR_DOM_OPERATION_ERR;
     977             :       }
     978             : 
     979             :       // Read public point Q.
     980           0 :       ScopedAutoSECItem ecPoint;
     981             :       rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey, CKA_EC_POINT,
     982           0 :                                  &ecPoint);
     983           0 :       if (rv != SECSuccess) {
     984           0 :         return NS_ERROR_DOM_OPERATION_ERR;
     985             :       }
     986             : 
     987           0 :       if (!ECKeyToJwk(PK11_TypePrivKey, aPrivKey, &params, &ecPoint, aRetVal)) {
     988           0 :         return NS_ERROR_DOM_OPERATION_ERR;
     989             :       }
     990             : 
     991           0 :       aRetVal.mD.Construct();
     992             : 
     993             :       // Read private value.
     994           0 :       if (!ReadAndEncodeAttribute(aPrivKey, CKA_VALUE, aRetVal.mD)) {
     995           0 :         return NS_ERROR_DOM_OPERATION_ERR;
     996             :       }
     997             : 
     998           0 :       return NS_OK;
     999             :     }
    1000             :     default:
    1001           0 :       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    1002             :   }
    1003             : }
    1004             : 
    1005             : UniqueSECKEYPublicKey
    1006           0 : CreateECPublicKey(const SECItem* aKeyData, const nsString& aNamedCurve)
    1007             : {
    1008           0 :   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
    1009           0 :   if (!arena) {
    1010           0 :     return nullptr;
    1011             :   }
    1012             : 
    1013             :   // It's important that this be a UniqueSECKEYPublicKey, as this ensures that
    1014             :   // SECKEY_DestroyPublicKey will be called on it. If this doesn't happen, when
    1015             :   // CryptoKey::PublicKeyValid is called on it and it gets moved to the internal
    1016             :   // PKCS#11 slot, it will leak a reference to the slot.
    1017           0 :   UniqueSECKEYPublicKey key(PORT_ArenaZNew(arena.get(), SECKEYPublicKey));
    1018           0 :   if (!key) {
    1019           0 :     return nullptr;
    1020             :   }
    1021             : 
    1022           0 :   key->arena = nullptr; // key doesn't own the arena; it won't get double-freed
    1023           0 :   key->keyType = ecKey;
    1024           0 :   key->pkcs11Slot = nullptr;
    1025           0 :   key->pkcs11ID = CK_INVALID_HANDLE;
    1026             : 
    1027             :   // Create curve parameters.
    1028           0 :   SECItem* params = CreateECParamsForCurve(aNamedCurve, arena.get());
    1029           0 :   if (!params) {
    1030           0 :     return nullptr;
    1031             :   }
    1032           0 :   key->u.ec.DEREncodedParams = *params;
    1033             : 
    1034             :   // Set public point.
    1035           0 :   key->u.ec.publicValue = *aKeyData;
    1036             : 
    1037             :   // Ensure the given point is on the curve.
    1038           0 :   if (!CryptoKey::PublicKeyValid(key.get())) {
    1039           0 :     return nullptr;
    1040             :   }
    1041             : 
    1042           0 :   return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(key.get()));
    1043             : }
    1044             : 
    1045             : UniqueSECKEYPublicKey
    1046           0 : CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
    1047             :                             const nsNSSShutDownPreventionLock& /*proofOfLock*/)
    1048             : {
    1049           0 :   if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) {
    1050             :     // Verify that all of the required parameters are present
    1051           0 :     CryptoBuffer n, e;
    1052           0 :     if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
    1053           0 :         !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value()))) {
    1054           0 :       return nullptr;
    1055             :     }
    1056             : 
    1057             :     // Transcode to a DER RSAPublicKey structure
    1058             :     struct RSAPublicKeyData {
    1059             :       SECItem n;
    1060             :       SECItem e;
    1061             :     };
    1062             :     const RSAPublicKeyData input = {
    1063           0 :       { siUnsignedInteger, n.Elements(), (unsigned int) n.Length() },
    1064           0 :       { siUnsignedInteger, e.Elements(), (unsigned int) e.Length() }
    1065           0 :     };
    1066             :     const SEC_ASN1Template rsaPublicKeyTemplate[] = {
    1067             :       {SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(RSAPublicKeyData)},
    1068             :       {SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, n),},
    1069             :       {SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, e),},
    1070             :       {0,}
    1071           0 :     };
    1072             : 
    1073             :     UniqueSECItem pkDer(SEC_ASN1EncodeItem(nullptr, nullptr, &input,
    1074           0 :                                            rsaPublicKeyTemplate));
    1075           0 :     if (!pkDer.get()) {
    1076           0 :       return nullptr;
    1077             :     }
    1078             : 
    1079           0 :     return UniqueSECKEYPublicKey(SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA));
    1080             :   }
    1081             : 
    1082           0 :   if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
    1083             :     // Verify that all of the required parameters are present
    1084           0 :     CryptoBuffer x, y;
    1085           0 :     if (!aJwk.mCrv.WasPassed() ||
    1086           0 :         !aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) ||
    1087           0 :         !aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value()))) {
    1088           0 :       return nullptr;
    1089             :     }
    1090             : 
    1091           0 :     UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
    1092           0 :     if (!arena) {
    1093           0 :       return nullptr;
    1094             :     }
    1095             : 
    1096             :     // Create point.
    1097           0 :     SECItem* point = CreateECPointForCoordinates(x, y, arena.get());
    1098           0 :     if (!point) {
    1099           0 :       return nullptr;
    1100             :     }
    1101             : 
    1102           0 :     nsString namedCurve;
    1103           0 :     if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
    1104           0 :       return nullptr;
    1105             :     }
    1106             : 
    1107           0 :     return CreateECPublicKey(point, namedCurve);
    1108             :   }
    1109             : 
    1110           0 :   return nullptr;
    1111             : }
    1112             : 
    1113             : nsresult
    1114           0 : CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
    1115             :                           JsonWebKey& aRetVal,
    1116             :                           const nsNSSShutDownPreventionLock& /*proofOfLock*/)
    1117             : {
    1118           0 :   switch (aPubKey->keyType) {
    1119             :     case rsaKey: {
    1120           0 :       CryptoBuffer n, e;
    1121           0 :       aRetVal.mN.Construct();
    1122           0 :       aRetVal.mE.Construct();
    1123             : 
    1124           0 :       if (!n.Assign(&aPubKey->u.rsa.modulus) ||
    1125           0 :           !e.Assign(&aPubKey->u.rsa.publicExponent) ||
    1126           0 :           NS_FAILED(n.ToJwkBase64(aRetVal.mN.Value())) ||
    1127           0 :           NS_FAILED(e.ToJwkBase64(aRetVal.mE.Value()))) {
    1128           0 :         return NS_ERROR_DOM_OPERATION_ERR;
    1129             :       }
    1130             : 
    1131           0 :       aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA);
    1132           0 :       return NS_OK;
    1133             :     }
    1134             :     case ecKey:
    1135           0 :       if (!ECKeyToJwk(PK11_TypePubKey, aPubKey, &aPubKey->u.ec.DEREncodedParams,
    1136           0 :                       &aPubKey->u.ec.publicValue, aRetVal)) {
    1137           0 :         return NS_ERROR_DOM_OPERATION_ERR;
    1138             :       }
    1139           0 :       return NS_OK;
    1140             :     default:
    1141           0 :       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    1142             :   }
    1143             : }
    1144             : 
    1145             : UniqueSECKEYPublicKey
    1146           0 : CryptoKey::PublicDhKeyFromRaw(CryptoBuffer& aKeyData,
    1147             :                               const CryptoBuffer& aPrime,
    1148             :                               const CryptoBuffer& aGenerator,
    1149             :                               const nsNSSShutDownPreventionLock& /*proofOfLock*/)
    1150             : {
    1151           0 :   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
    1152           0 :   if (!arena) {
    1153           0 :     return nullptr;
    1154             :   }
    1155             : 
    1156           0 :   SECKEYPublicKey* key = PORT_ArenaZNew(arena.get(), SECKEYPublicKey);
    1157           0 :   if (!key) {
    1158           0 :     return nullptr;
    1159             :   }
    1160             : 
    1161           0 :   key->keyType = dhKey;
    1162           0 :   key->pkcs11Slot = nullptr;
    1163           0 :   key->pkcs11ID = CK_INVALID_HANDLE;
    1164             : 
    1165             :   // Set DH public key params.
    1166           0 :   if (!aPrime.ToSECItem(arena.get(), &key->u.dh.prime) ||
    1167           0 :       !aGenerator.ToSECItem(arena.get(), &key->u.dh.base) ||
    1168           0 :       !aKeyData.ToSECItem(arena.get(), &key->u.dh.publicValue)) {
    1169           0 :     return nullptr;
    1170             :   }
    1171             : 
    1172           0 :   key->u.dh.prime.type = siUnsignedInteger;
    1173           0 :   key->u.dh.base.type = siUnsignedInteger;
    1174           0 :   key->u.dh.publicValue.type = siUnsignedInteger;
    1175             : 
    1176           0 :   return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(key));
    1177             : }
    1178             : 
    1179             : nsresult
    1180           0 : CryptoKey::PublicDhKeyToRaw(SECKEYPublicKey* aPubKey,
    1181             :                             CryptoBuffer& aRetVal,
    1182             :                             const nsNSSShutDownPreventionLock& /*proofOfLock*/)
    1183             : {
    1184           0 :   if (!aRetVal.Assign(&aPubKey->u.dh.publicValue)) {
    1185           0 :     return NS_ERROR_DOM_OPERATION_ERR;
    1186             :   }
    1187           0 :   return NS_OK;
    1188             : }
    1189             : 
    1190             : UniqueSECKEYPublicKey
    1191           0 : CryptoKey::PublicECKeyFromRaw(CryptoBuffer& aKeyData,
    1192             :                               const nsString& aNamedCurve,
    1193             :                               const nsNSSShutDownPreventionLock& /*proofOfLock*/)
    1194             : {
    1195           0 :   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
    1196           0 :   if (!arena) {
    1197           0 :     return nullptr;
    1198             :   }
    1199             : 
    1200           0 :   SECItem rawItem = { siBuffer, nullptr, 0 };
    1201           0 :   if (!aKeyData.ToSECItem(arena.get(), &rawItem)) {
    1202           0 :     return nullptr;
    1203             :   }
    1204             : 
    1205             :   uint32_t flen;
    1206           0 :   if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256)) {
    1207           0 :     flen = 32; // bytes
    1208           0 :   } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384)) {
    1209           0 :     flen = 48; // bytes
    1210           0 :   } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521)) {
    1211           0 :     flen = 66; // bytes
    1212             :   } else {
    1213           0 :     return nullptr;
    1214             :   }
    1215             : 
    1216             :   // Check length of uncompressed point coordinates. There are 2 field elements
    1217             :   // and a leading point form octet (which must EC_POINT_FORM_UNCOMPRESSED).
    1218           0 :   if (rawItem.len != (2 * flen + 1)) {
    1219           0 :     return nullptr;
    1220             :   }
    1221             : 
    1222             :   // No support for compressed points.
    1223           0 :   if (rawItem.data[0] != EC_POINT_FORM_UNCOMPRESSED) {
    1224           0 :     return nullptr;
    1225             :   }
    1226             : 
    1227           0 :   return CreateECPublicKey(&rawItem, aNamedCurve);
    1228             : }
    1229             : 
    1230             : nsresult
    1231           0 : CryptoKey::PublicECKeyToRaw(SECKEYPublicKey* aPubKey,
    1232             :                             CryptoBuffer& aRetVal,
    1233             :                             const nsNSSShutDownPreventionLock& /*proofOfLock*/)
    1234             : {
    1235           0 :   if (!aRetVal.Assign(&aPubKey->u.ec.publicValue)) {
    1236           0 :     return NS_ERROR_DOM_OPERATION_ERR;
    1237             :   }
    1238           0 :   return NS_OK;
    1239             : }
    1240             : 
    1241             : bool
    1242           0 : CryptoKey::PublicKeyValid(SECKEYPublicKey* aPubKey)
    1243             : {
    1244           0 :   UniquePK11SlotInfo slot(PK11_GetInternalSlot());
    1245           0 :   if (!slot.get()) {
    1246           0 :     return false;
    1247             :   }
    1248             : 
    1249             :   // This assumes that NSS checks the validity of a public key when
    1250             :   // it is imported into a PKCS#11 module, and returns CK_INVALID_HANDLE
    1251             :   // if it is invalid.
    1252           0 :   CK_OBJECT_HANDLE id = PK11_ImportPublicKey(slot.get(), aPubKey, PR_FALSE);
    1253           0 :   if (id == CK_INVALID_HANDLE) {
    1254           0 :     return false;
    1255             :   }
    1256             : 
    1257           0 :   SECStatus rv = PK11_DestroyObject(slot.get(), id);
    1258           0 :   return (rv == SECSuccess);
    1259             : }
    1260             : 
    1261             : bool
    1262           0 : CryptoKey::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
    1263             : {
    1264           0 :   nsNSSShutDownPreventionLock locker;
    1265           0 :   if (isAlreadyShutDown()) {
    1266           0 :     return false;
    1267             :   }
    1268             : 
    1269             :   // Write in five pieces
    1270             :   // 1. Attributes
    1271             :   // 2. Symmetric key as raw (if present)
    1272             :   // 3. Private key as pkcs8 (if present)
    1273             :   // 4. Public key as spki (if present)
    1274             :   // 5. Algorithm in whatever form it chooses
    1275           0 :   CryptoBuffer priv, pub;
    1276             : 
    1277           0 :   if (mPrivateKey) {
    1278           0 :     if (NS_FAILED(CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), priv,
    1279             :                                                locker))) {
    1280           0 :       return false;
    1281             :     }
    1282             :   }
    1283             : 
    1284           0 :   if (mPublicKey) {
    1285           0 :     if (NS_FAILED(CryptoKey::PublicKeyToSpki(mPublicKey.get(), pub, locker))) {
    1286           0 :       return false;
    1287             :     }
    1288             :   }
    1289             : 
    1290           0 :   return JS_WriteUint32Pair(aWriter, mAttributes, CRYPTOKEY_SC_VERSION) &&
    1291           0 :          WriteBuffer(aWriter, mSymKey) &&
    1292           0 :          WriteBuffer(aWriter, priv) &&
    1293           0 :          WriteBuffer(aWriter, pub) &&
    1294           0 :          mAlgorithm.WriteStructuredClone(aWriter);
    1295             : }
    1296             : 
    1297             : bool
    1298           0 : CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader)
    1299             : {
    1300           0 :   nsNSSShutDownPreventionLock locker;
    1301           0 :   if (isAlreadyShutDown()) {
    1302           0 :     return false;
    1303             :   }
    1304             : 
    1305             :   // Ensure that NSS is initialized.
    1306           0 :   if (!EnsureNSSInitializedChromeOrContent()) {
    1307           0 :     return false;
    1308             :   }
    1309             : 
    1310             :   uint32_t version;
    1311           0 :   CryptoBuffer sym, priv, pub;
    1312             : 
    1313           0 :   bool read = JS_ReadUint32Pair(aReader, &mAttributes, &version) &&
    1314           0 :               (version == CRYPTOKEY_SC_VERSION) &&
    1315           0 :               ReadBuffer(aReader, sym) &&
    1316           0 :               ReadBuffer(aReader, priv) &&
    1317           0 :               ReadBuffer(aReader, pub) &&
    1318           0 :               mAlgorithm.ReadStructuredClone(aReader);
    1319           0 :   if (!read) {
    1320           0 :     return false;
    1321             :   }
    1322             : 
    1323           0 :   if (sym.Length() > 0 && !mSymKey.Assign(sym))  {
    1324           0 :     return false;
    1325             :   }
    1326           0 :   if (priv.Length() > 0) {
    1327           0 :     mPrivateKey = CryptoKey::PrivateKeyFromPkcs8(priv, locker);
    1328             :   }
    1329           0 :   if (pub.Length() > 0)  {
    1330           0 :     mPublicKey = CryptoKey::PublicKeyFromSpki(pub, locker);
    1331             :   }
    1332             : 
    1333             :   // Ensure that what we've read is consistent
    1334             :   // If the attributes indicate a key type, should have a key of that type
    1335           0 :   if (!((GetKeyType() == SECRET  && mSymKey.Length() > 0) ||
    1336           0 :         (GetKeyType() == PRIVATE && mPrivateKey) ||
    1337           0 :         (GetKeyType() == PUBLIC  && mPublicKey))) {
    1338           0 :     return false;
    1339             :   }
    1340             : 
    1341           0 :   return true;
    1342             : }
    1343             : 
    1344             : } // namespace dom
    1345             : } // namespace mozilla

Generated by: LCOV version 1.13