EVP_PKEY: null-check EVP_PKEY_ASN1_METHOD before using Most interesting things that can be done with an EVP_PKEY require `ameth` to be non-NULL. It's only ever NULL for a default initialized empty object which will have id EVP_PKEY_NONE, but since we return these from EVP_PKEY_new(), we should avoid crashing when trying to use them. Bug: 42290409 Change-Id: I31894f0377aaed8cc97fbde9d3e59fe86a6a6964 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/93629 Reviewed-by: David Benjamin <davidben@google.com> Presubmit-BoringSSL-Verified: boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/crypto/evp/evp.cc b/crypto/evp/evp.cc index 7581c39..aab4de7 100644 --- a/crypto/evp/evp.cc +++ b/crypto/evp/evp.cc
@@ -288,7 +288,7 @@ size_t *out_len) { auto *impl = FromOpaque(pkey); - if (impl->ameth->get_priv_raw == nullptr) { + if (impl->ameth == nullptr || impl->ameth->get_priv_raw == nullptr) { OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); return 0; } @@ -300,7 +300,7 @@ size_t *out_len) { auto *impl = FromOpaque(pkey); - if (impl->ameth->get_priv_seed == nullptr) { + if (impl->ameth == nullptr || impl->ameth->get_priv_seed == nullptr) { OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); return 0; } @@ -312,7 +312,7 @@ size_t *out_len) { auto *impl = FromOpaque(pkey); - if (impl->ameth->get_pub_raw == nullptr) { + if (impl->ameth == nullptr || impl->ameth->get_pub_raw == nullptr) { OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); return 0; } @@ -385,7 +385,7 @@ size_t len) { auto *impl = FromOpaque(pkey); - if (impl->ameth->set1_tls_encodedpoint == nullptr) { + if (impl->ameth == nullptr || impl->ameth->set1_tls_encodedpoint == nullptr) { OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); return 0; } @@ -396,7 +396,7 @@ size_t EVP_PKEY_get1_tls_encodedpoint(const EVP_PKEY *pkey, uint8_t **out_ptr) { auto *impl = FromOpaque(pkey); - if (impl->ameth->get1_tls_encodedpoint == nullptr) { + if (impl->ameth == nullptr || impl->ameth->get1_tls_encodedpoint == nullptr) { OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); return 0; }
diff --git a/crypto/evp/evp_extra_test.cc b/crypto/evp/evp_extra_test.cc index dcd3f95..d78c82e 100644 --- a/crypto/evp/evp_extra_test.cc +++ b/crypto/evp/evp_extra_test.cc
@@ -1416,6 +1416,37 @@ EXPECT_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_NONE); } +// An empty key shouldn't crash on operations. +TEST(EVPExtraTest, EmptyKey) { + UniquePtr<EVP_PKEY> empty(EVP_PKEY_new()); + ASSERT_TRUE(empty); + + EXPECT_FALSE(EVP_PKEY_is_opaque(empty.get())); + EXPECT_FALSE(EVP_PKEY_eq(empty.get(), empty.get())); + EXPECT_TRUE(EVP_PKEY_parameters_eq(empty.get(), empty.get())); + EXPECT_FALSE(EVP_PKEY_missing_parameters(empty.get())); + EXPECT_EQ(EVP_PKEY_size(empty.get()), 0); + EXPECT_EQ(EVP_PKEY_bits(empty.get()), 0); + EXPECT_EQ(EVP_PKEY_id(empty.get()), EVP_PKEY_NONE); + + EXPECT_EQ(EVP_PKEY_get_raw_private_key(empty.get(), nullptr, nullptr), 0); + EXPECT_TRUE(ErrorEquals(ERR_peek_last_error(), ERR_LIB_EVP, + EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE)); + EXPECT_EQ(EVP_PKEY_get_private_seed(empty.get(), nullptr, nullptr), 0); + EXPECT_TRUE(ErrorEquals(ERR_peek_last_error(), ERR_LIB_EVP, + EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE)); + EXPECT_EQ(EVP_PKEY_get_raw_public_key(empty.get(), nullptr, nullptr), 0); + EXPECT_TRUE(ErrorEquals(ERR_peek_last_error(), ERR_LIB_EVP, + EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE)); + + EXPECT_EQ(EVP_PKEY_get1_tls_encodedpoint(empty.get(), nullptr), 0u); + EXPECT_TRUE(ErrorEquals(ERR_peek_last_error(), ERR_LIB_EVP, + EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE)); + EXPECT_EQ(EVP_PKEY_set1_tls_encodedpoint(empty.get(), nullptr, 0u), 0); + EXPECT_TRUE(ErrorEquals(ERR_peek_last_error(), ERR_LIB_EVP, + EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE)); +} + // Due to an OpenSSL API flaw, it is possible to make a half-empty X25519 key. // Using a key in this state is a caller error, but we gracefully handle this // case.