From 1a21cf13cbe9908b19c22290030868be8f6135d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 29 Sep 2018 14:26:43 +0200 Subject: [PATCH] crypto: make PEM parsing RFC7468-compliant PR-URL: https://github.com/nodejs/node/pull/23164 Fixes: https://github.com/nodejs/node/issues/13612 Fixes: https://github.com/nodejs/node/issues/22815 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell --- src/node_crypto.cc | 83 ++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 33e8053b12bcfd..1e5328f3d7afb3 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -45,13 +45,6 @@ #include #include -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 @@ -3656,6 +3649,31 @@ enum ParsePublicKeyResult { kParsePublicFailed }; +static ParsePublicKeyResult TryParsePublicKey( + EVPKeyPointer* pkey, + const BIOPointer& bp, + const char* name, + // NOLINTNEXTLINE(runtime/int) + std::function 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) { @@ -3663,31 +3681,32 @@ static ParsePublicKeyResult ParsePublicKey(EVPKeyPointer* pkey, 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 target) {