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, ¶ms); 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, ¶ms); 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(¶ms, &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, ¶ms); 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, ¶ms, &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