UniqueSECKEYPublicKey CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return nullptr; } SECItem spkiItem = { siBuffer, nullptr, 0 }; if (!aKeyData.ToSECItem(arena.get(), &spkiItem)) { return nullptr; } UniqueCERTSubjectPublicKeyInfo spki( SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem)); if (!spki) { return nullptr; } bool isECDHAlgorithm = SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH, &spki->algorithm.algorithm); bool isDHAlgorithm = SECITEM_ItemsAreEqual(&SEC_OID_DATA_DH_KEY_AGREEMENT, &spki->algorithm.algorithm); // Check for |id-ecDH| and |dhKeyAgreement|. Per the WebCrypto spec we must // support these OIDs but NSS does unfortunately not know about them. Let's // change the algorithm to |id-ecPublicKey| or |dhPublicKey| to make NSS happy. if (isECDHAlgorithm || isDHAlgorithm) { SECOidTag oid = SEC_OID_UNKNOWN; if (isECDHAlgorithm) { oid = SEC_OID_ANSIX962_EC_PUBLIC_KEY; } else if (isDHAlgorithm) { oid = SEC_OID_X942_DIFFIE_HELMAN_KEY; } else { MOZ_ASSERT(false); } SECOidData* oidData = SECOID_FindOIDByTag(oid); if (!oidData) { return nullptr; } SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm, &oidData->oid); if (rv != SECSuccess) { return nullptr; } } UniqueSECKEYPublicKey tmp(SECKEY_ExtractPublicKey(spki.get())); if (!tmp.get() || !PublicKeyValid(tmp.get())) { return nullptr; } return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(tmp.get())); }
SECKEYPublicKey* CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) { // Verify that all of the required parameters are present CryptoBuffer n, e; if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) || !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value()))) { return nullptr; } // Transcode to a DER RSAPublicKey structure struct RSAPublicKeyData { SECItem n; SECItem e; }; const RSAPublicKeyData input = { { siUnsignedInteger, n.Elements(), (unsigned int) n.Length() }, { siUnsignedInteger, e.Elements(), (unsigned int) e.Length() } }; const SEC_ASN1Template rsaPublicKeyTemplate[] = { {SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(RSAPublicKeyData)}, {SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, n),}, {SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, e),}, {0,} }; ScopedSECItem pkDer(SEC_ASN1EncodeItem(nullptr, nullptr, &input, rsaPublicKeyTemplate)); if (!pkDer.get()) { return nullptr; } return SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA); } if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) { // Verify that all of the required parameters are present CryptoBuffer x, y; if (!aJwk.mCrv.WasPassed() || !aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) || !aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value()))) { return nullptr; } ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return nullptr; } SECKEYPublicKey* key = PORT_ArenaZNew(arena, SECKEYPublicKey); if (!key) { return nullptr; } key->keyType = ecKey; key->pkcs11Slot = nullptr; key->pkcs11ID = CK_INVALID_HANDLE; nsString namedCurve; if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) { return nullptr; } // Create parameters. SECItem* params = CreateECParamsForCurve(namedCurve, arena.get()); if (!params) { return nullptr; } key->u.ec.DEREncodedParams = *params; // Create point. SECItem* point = CreateECPointForCoordinates(x, y, arena.get()); if (!point) { return nullptr; } key->u.ec.publicValue = *point; if (!PublicKeyValid(key)) { return nullptr; } return SECKEY_CopyPublicKey(key); } return nullptr; }