Skip to content

Commit ad07e44

Browse files
committed
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 0a1c650 commit ad07e44

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
@@ -3650,38 +3643,64 @@ enum ParsePublicKeyResult {
36503643
kParsePublicFailed
36513644
};
36523645

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

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

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

0 commit comments

Comments
 (0)