tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CryptoKey.cpp (46592B)


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