Skip to content

Commit 1a21cf1

Browse files
tniessentargos
authored andcommitted
crypto: make PEM parsing RFC7468-compliant
PR-URL: #23164 Fixes: #13612 Fixes: #22815 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 9c96573 commit 1a21cf1

File tree

1 file changed

+51
-32
lines changed

1 file changed

+51
-32
lines changed

src/node_crypto.cc

+51-32
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,6 @@
4545
#include <memory>
4646
#include <vector>
4747

48-
static const char PUBLIC_KEY_PFX[] = "-----BEGIN PUBLIC KEY-----";
49-
static const int PUBLIC_KEY_PFX_LEN = sizeof(PUBLIC_KEY_PFX) - 1;
50-
static const char PUBRSA_KEY_PFX[] = "-----BEGIN RSA PUBLIC KEY-----";
51-
static const int PUBRSA_KEY_PFX_LEN = sizeof(PUBRSA_KEY_PFX) - 1;
52-
static const char CERTIFICATE_PFX[] = "-----BEGIN CERTIFICATE-----";
53-
static const int CERTIFICATE_PFX_LEN = sizeof(CERTIFICATE_PFX) - 1;
54-
5548
static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
5649
| ASN1_STRFLGS_UTF8_CONVERT
5750
| XN_FLAG_SEP_MULTILINE
@@ -3656,38 +3649,64 @@ enum ParsePublicKeyResult {
36563649
kParsePublicFailed
36573650
};
36583651

3652+
static ParsePublicKeyResult TryParsePublicKey(
3653+
EVPKeyPointer* pkey,
3654+
const BIOPointer& bp,
3655+
const char* name,
3656+
// NOLINTNEXTLINE(runtime/int)
3657+
std::function<EVP_PKEY*(const unsigned char** p, long l)> parse) {
3658+
unsigned char* der_data;
3659+
long der_len; // NOLINT(runtime/int)
3660+
3661+
// This skips surrounding data and decodes PEM to DER.
3662+
{
3663+
MarkPopErrorOnReturn mark_pop_error_on_return;
3664+
if (PEM_bytes_read_bio(&der_data, &der_len, nullptr, name,
3665+
bp.get(), nullptr, nullptr) != 1)
3666+
return kParsePublicNotRecognized;
3667+
}
3668+
3669+
// OpenSSL might modify the pointer, so we need to make a copy before parsing.
3670+
const unsigned char* p = der_data;
3671+
pkey->reset(parse(&p, der_len));
3672+
OPENSSL_clear_free(der_data, der_len);
3673+
3674+
return *pkey ? kParsePublicOk : kParsePublicFailed;
3675+
}
3676+
36593677
static ParsePublicKeyResult ParsePublicKey(EVPKeyPointer* pkey,
36603678
const char* key_pem,
36613679
int key_pem_len) {
36623680
BIOPointer bp(BIO_new_mem_buf(const_cast<char*>(key_pem), key_pem_len));
36633681
if (!bp)
36643682
return kParsePublicFailed;
36653683

3666-
// Check if this is a PKCS#8 or RSA public key before trying as X.509.
3667-
if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
3668-
pkey->reset(
3669-
PEM_read_bio_PUBKEY(bp.get(), nullptr, NoPasswordCallback, nullptr));
3670-
} else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
3671-
RSAPointer rsa(PEM_read_bio_RSAPublicKey(
3672-
bp.get(), nullptr, PasswordCallback, nullptr));
3673-
if (rsa) {
3674-
pkey->reset(EVP_PKEY_new());
3675-
if (*pkey)
3676-
EVP_PKEY_set1_RSA(pkey->get(), rsa.get());
3677-
}
3678-
} else if (strncmp(key_pem, CERTIFICATE_PFX, CERTIFICATE_PFX_LEN) == 0) {
3679-
// X.509 fallback
3680-
X509Pointer x509(PEM_read_bio_X509(
3681-
bp.get(), nullptr, NoPasswordCallback, nullptr));
3682-
if (!x509)
3683-
return kParsePublicFailed;
3684-
3685-
pkey->reset(X509_get_pubkey(x509.get()));
3686-
} else {
3687-
return kParsePublicNotRecognized;
3688-
}
3689-
3690-
return *pkey ? kParsePublicOk : kParsePublicFailed;
3684+
ParsePublicKeyResult ret;
3685+
3686+
// Try PKCS#8 first.
3687+
ret = TryParsePublicKey(pkey, bp, "PUBLIC KEY",
3688+
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
3689+
return d2i_PUBKEY(nullptr, p, l);
3690+
});
3691+
if (ret != kParsePublicNotRecognized)
3692+
return ret;
3693+
3694+
// Maybe it is PKCS#1.
3695+
CHECK(BIO_reset(bp.get()));
3696+
ret = TryParsePublicKey(pkey, bp, "RSA PUBLIC KEY",
3697+
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
3698+
return d2i_PublicKey(EVP_PKEY_RSA, nullptr, p, l);
3699+
});
3700+
if (ret != kParsePublicNotRecognized)
3701+
return ret;
3702+
3703+
// X.509 fallback.
3704+
CHECK(BIO_reset(bp.get()));
3705+
return TryParsePublicKey(pkey, bp, "CERTIFICATE",
3706+
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
3707+
X509Pointer x509(d2i_X509(nullptr, p, l));
3708+
return x509 ? X509_get_pubkey(x509.get()) : nullptr;
3709+
});
36913710
}
36923711

36933712
void Verify::Initialize(Environment* env, v8::Local<Object> target) {

0 commit comments

Comments
 (0)