Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: make PEM parsing RFC7468-compliant #23164

Closed
wants to merge 2 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 51 additions & 32 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,6 @@
#include <memory>
#include <vector>

static const char PUBLIC_KEY_PFX[] = "-----BEGIN PUBLIC KEY-----";
static const int PUBLIC_KEY_PFX_LEN = sizeof(PUBLIC_KEY_PFX) - 1;
static const char PUBRSA_KEY_PFX[] = "-----BEGIN RSA PUBLIC KEY-----";
static const int PUBRSA_KEY_PFX_LEN = sizeof(PUBRSA_KEY_PFX) - 1;
static const char CERTIFICATE_PFX[] = "-----BEGIN CERTIFICATE-----";
static const int CERTIFICATE_PFX_LEN = sizeof(CERTIFICATE_PFX) - 1;

static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
| ASN1_STRFLGS_UTF8_CONVERT
| XN_FLAG_SEP_MULTILINE
Expand Down Expand Up @@ -3650,38 +3643,64 @@ enum ParsePublicKeyResult {
kParsePublicFailed
};

static ParsePublicKeyResult TryParsePublicKey(
EVPKeyPointer* pkey,
const BIOPointer& bp,
char* name,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be const char* name, right?

// NOLINTNEXTLINE(runtime/int)
std::function<EVP_PKEY*(const unsigned char** p, long l)> parse) {
unsigned char* der_data;
long der_len; // NOLINT(runtime/int)

// This skips surrounding data and decodes PEM to DER.
{
MarkPopErrorOnReturn mark_pop_error_on_return;
if (PEM_bytes_read_bio(&der_data, &der_len, nullptr, name,
bp.get(), nullptr, nullptr) != 1)
return kParsePublicNotRecognized;
}

// OpenSSL might modify the pointer, so we need to make a copy before parsing.
const unsigned char* p = der_data;
pkey->reset(parse(&p, der_len));
OPENSSL_clear_free(der_data, der_len);

return *pkey ? kParsePublicOk : kParsePublicFailed;
}

static ParsePublicKeyResult ParsePublicKey(EVPKeyPointer* pkey,
const char* key_pem,
int key_pem_len) {
BIOPointer bp(BIO_new_mem_buf(const_cast<char*>(key_pem), key_pem_len));
if (!bp)
return kParsePublicFailed;

// Check if this is a PKCS#8 or RSA public key before trying as X.509.
if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
pkey->reset(
PEM_read_bio_PUBKEY(bp.get(), nullptr, NoPasswordCallback, nullptr));
} else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
RSAPointer rsa(PEM_read_bio_RSAPublicKey(
bp.get(), nullptr, PasswordCallback, nullptr));
if (rsa) {
pkey->reset(EVP_PKEY_new());
if (*pkey)
EVP_PKEY_set1_RSA(pkey->get(), rsa.get());
}
} else if (strncmp(key_pem, CERTIFICATE_PFX, CERTIFICATE_PFX_LEN) == 0) {
// X.509 fallback
X509Pointer x509(PEM_read_bio_X509(
bp.get(), nullptr, NoPasswordCallback, nullptr));
if (!x509)
return kParsePublicFailed;

pkey->reset(X509_get_pubkey(x509.get()));
} else {
return kParsePublicNotRecognized;
}

return *pkey ? kParsePublicOk : kParsePublicFailed;
ParsePublicKeyResult ret;

// Try PKCS#8 first.
ret = TryParsePublicKey(pkey, bp, "PUBLIC KEY",
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
return d2i_PUBKEY(nullptr, p, l);
});
if (ret != kParsePublicNotRecognized)
return ret;

// Maybe it is PKCS#1.
CHECK(BIO_reset(bp.get()));
ret = TryParsePublicKey(pkey, bp, "RSA PUBLIC KEY",
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
return d2i_PublicKey(EVP_PKEY_RSA, nullptr, p, l);
});
if (ret != kParsePublicNotRecognized)
return ret;

// X.509 fallback.
CHECK(BIO_reset(bp.get()));
return TryParsePublicKey(pkey, bp, "CERTIFICATE",
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
X509Pointer x509(d2i_X509(nullptr, p, l));
return x509 ? X509_get_pubkey(x509.get()) : nullptr;
});
}

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