SECKEYPublicKey* CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { ScopedSECItem spkiItem(aKeyData.ToSECItem()); if (!spkiItem) { return nullptr; } ScopedCERTSubjectPublicKeyInfo spki(SECKEY_DecodeDERSubjectPublicKeyInfo(spkiItem.get())); if (!spki) { return nullptr; } // Check for id-ecDH. Per the WebCrypto spec we must support it but NSS // does unfortunately not know about it. Let's change the algorithm to // id-ecPublicKey to make NSS happy. if (SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH, &spki->algorithm.algorithm)) { // Retrieve OID data for id-ecPublicKey (1.2.840.10045.2.1). SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_ANSIX962_EC_PUBLIC_KEY); if (!oidData) { return nullptr; } SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm, &oidData->oid); if (rv != SECSuccess) { return nullptr; } } return SECKEY_ExtractPublicKey(spki.get()); }
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())); }
static SECKEYPublicKey* getPublicKeyFromBytes(uint8_t *publicKeyBytes) { uint8_t spkiBytes[TACK_PUBKEY_LENGTH + sizeof(SPKI_P256)]; memcpy(spkiBytes, SPKI_P256, sizeof(SPKI_P256)); memcpy(spkiBytes + sizeof(SPKI_P256), publicKeyBytes, TACK_PUBKEY_LENGTH); SECItem spkiItem; spkiItem.data = spkiBytes; spkiItem.len = sizeof(spkiBytes); CERTSubjectPublicKeyInfo *spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem); SECKEYPublicKey *publicKey = SECKEY_ExtractPublicKey(spki); SECKEY_DestroySubjectPublicKeyInfo(spki); return publicKey; }
SECKEYPublicKey* CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { ScopedSECItem spkiItem(aKeyData.ToSECItem()); if (!spkiItem) { return nullptr; } ScopedCERTSubjectPublicKeyInfo spki(SECKEY_DecodeDERSubjectPublicKeyInfo(spkiItem.get())); if (!spki) { return nullptr; } return SECKEY_ExtractPublicKey(spki.get()); }
SECStatus CheckPublicKeySize(const SECItem& subjectPublicKeyInfo, /*out*/ ScopedSECKeyPublicKey& publicKey) { ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo> spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfo)); if (!spki) { return SECFailure; } publicKey = SECKEY_ExtractPublicKey(spki.get()); if (!publicKey) { return SECFailure; } static const unsigned int MINIMUM_NON_ECC_BITS = 1024; switch (publicKey.get()->keyType) { case ecKey: // TODO(bug 622859): We should check which curve. return SECSuccess; case dsaKey: // fall through case rsaKey: // TODO(bug 622859): Enforce a minimum of 2048 bits for EV certs. if (SECKEY_PublicKeyStrengthInBits(publicKey.get()) < MINIMUM_NON_ECC_BITS) { // TODO(bug 1031946): Create a new error code. PR_SetError(SEC_ERROR_INVALID_KEY, 0); return SECFailure; } break; case nullKey: case fortezzaKey: case dhKey: case keaKey: case rsaPssKey: case rsaOaepKey: default: PR_SetError(SEC_ERROR_UNSUPPORTED_KEYALG, 0); return SECFailure; } return SECSuccess; }
NS_IMETHODIMP nsDataSignatureVerifier::VerifyData(const nsACString & aData, const nsACString & aSignature, const nsACString & aPublicKey, bool *_retval) { // Allocate an arena to handle the majority of the allocations PRArenaPool *arena; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) return NS_ERROR_OUT_OF_MEMORY; // Base 64 decode the key SECItem keyItem; PORT_Memset(&keyItem, 0, sizeof(SECItem)); if (!NSSBase64_DecodeBuffer(arena, &keyItem, nsPromiseFlatCString(aPublicKey).get(), aPublicKey.Length())) { PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Extract the public key from the data CERTSubjectPublicKeyInfo *pki = SECKEY_DecodeDERSubjectPublicKeyInfo(&keyItem); if (!pki) { PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } SECKEYPublicKey *publicKey = SECKEY_ExtractPublicKey(pki); SECKEY_DestroySubjectPublicKeyInfo(pki); pki = nullptr; if (!publicKey) { PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Base 64 decode the signature SECItem signatureItem; PORT_Memset(&signatureItem, 0, sizeof(SECItem)); if (!NSSBase64_DecodeBuffer(arena, &signatureItem, nsPromiseFlatCString(aSignature).get(), aSignature.Length())) { SECKEY_DestroyPublicKey(publicKey); PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Decode the signature and algorithm CERTSignedData sigData; PORT_Memset(&sigData, 0, sizeof(CERTSignedData)); SECStatus ss = SEC_QuickDERDecodeItem(arena, &sigData, CERT_SignatureDataTemplate, &signatureItem); if (ss != SECSuccess) { SECKEY_DestroyPublicKey(publicKey); PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Perform the final verification DER_ConvertBitString(&(sigData.signature)); ss = VFY_VerifyDataWithAlgorithmID((const unsigned char*)nsPromiseFlatCString(aData).get(), aData.Length(), publicKey, &(sigData.signature), &(sigData.signatureAlgorithm), NULL, NULL); // Clean up remaining objects SECKEY_DestroyPublicKey(publicKey); PORT_FreeArena(arena, false); *_retval = (ss == SECSuccess); return NS_OK; }