From: Daniel Kubec Date: Fri, 19 Sep 2025 13:48:41 +0000 (+0200) Subject: Fix EVP_PKEY_can_sign() handling of NULL from query_operation_name() X-Git-Tag: openssl-3.0.18~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4951f04a4e46d6bf910500d6d8859b8aa7516ac5;p=thirdparty%2Fopenssl.git Fix EVP_PKEY_can_sign() handling of NULL from query_operation_name() EVP_PKEY_can_sign() assumed query_operation_name(OSSL_OP_SIGNATURE) always returns a non-NULL string. According to the documentation, query_operation_name() may return NULL, in which case EVP_KEYMGMT_get0_name() should be used as a fallback. Fixes #27790 Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/28641) (cherry picked from commit 0c2d67f417a2c2e0a63272e8d7753489b4958c0b) --- diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index 6ff7eb7e02c..8d0071c3ec9 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -1095,15 +1095,14 @@ int EVP_PKEY_can_sign(const EVP_PKEY *pkey) } else { const OSSL_PROVIDER *prov = EVP_KEYMGMT_get0_provider(pkey->keymgmt); OSSL_LIB_CTX *libctx = ossl_provider_libctx(prov); - const char *supported_sig = - pkey->keymgmt->query_operation_name != NULL - ? pkey->keymgmt->query_operation_name(OSSL_OP_SIGNATURE) - : EVP_KEYMGMT_get0_name(pkey->keymgmt); - EVP_SIGNATURE *signature = NULL; - - signature = EVP_SIGNATURE_fetch(libctx, supported_sig, NULL); - if (signature != NULL) { - EVP_SIGNATURE_free(signature); + EVP_SIGNATURE *sig; + const char *name; + + name = evp_keymgmt_util_query_operation_name(pkey->keymgmt, + OSSL_OP_SIGNATURE); + sig = EVP_SIGNATURE_fetch(libctx, name, NULL); + if (sig != NULL) { + EVP_SIGNATURE_free(sig); return 1; } } diff --git a/test/fake_rsaprov.c b/test/fake_rsaprov.c index be08bfd3998..8b386416843 100644 --- a/test/fake_rsaprov.c +++ b/test/fake_rsaprov.c @@ -31,6 +31,8 @@ static int imptypes_selection; static int exptypes_selection; static int query_id; +unsigned fake_rsa_query_operation_name = 0; + struct fake_rsa_keydata { int selection; int status; @@ -71,7 +73,7 @@ static const char *fake_rsa_keymgmt_query(int id) /* record global for checking */ query_id = id; - return "RSA"; + return fake_rsa_query_operation_name ? NULL: "RSA"; } static int fake_rsa_keymgmt_import(void *keydata, int selection, diff --git a/test/fake_rsaprov.h b/test/fake_rsaprov.h index 190c46a285c..71a32985c6a 100644 --- a/test/fake_rsaprov.h +++ b/test/fake_rsaprov.h @@ -12,4 +12,13 @@ /* Fake RSA provider implementation */ OSSL_PROVIDER *fake_rsa_start(OSSL_LIB_CTX *libctx); void fake_rsa_finish(OSSL_PROVIDER *p); + OSSL_PARAM *fake_rsa_key_params(int priv); + +/* + * When fake_rsa_query_operation_name is set to a non-zero value, + * query_operation_name() will return NULL. + * + * By default, it is 0, in which case query_operation_name() will return "RSA". + */ +extern unsigned fake_rsa_query_operation_name; diff --git a/test/provider_pkey_test.c b/test/provider_pkey_test.c index 249e9babcfa..081457734a8 100644 --- a/test/provider_pkey_test.c +++ b/test/provider_pkey_test.c @@ -237,6 +237,77 @@ end: return ret; } +static int test_pkey_can_sign(void) +{ + OSSL_PROVIDER *fake_rsa = NULL; + EVP_PKEY *pkey_fake = NULL; + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM *params = NULL; + int ret = 0; + + if (!TEST_ptr(fake_rsa = fake_rsa_start(libctx))) + return 0; + + /* + * Ensure other tests did not forget to reset fake_rsa_query_operation_name + * to its default value: 0 + */ + if (!TEST_int_eq(fake_rsa_query_operation_name, 0)) + goto end; + + if (!TEST_ptr(params = fake_rsa_key_params(0)) + || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA", + "provider=fake-rsa")) + || !TEST_true(EVP_PKEY_fromdata_init(ctx)) + || !TEST_true(EVP_PKEY_fromdata(ctx, &pkey_fake, EVP_PKEY_PUBLIC_KEY, + params)) + || !TEST_true(EVP_PKEY_can_sign(pkey_fake)) + || !TEST_ptr(pkey_fake)) + goto end; + + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + EVP_PKEY_free(pkey_fake); + pkey_fake = NULL; + OSSL_PARAM_free(params); + params = NULL; + + /* + * Documented behavior for OSSL_FUNC_keymgmt_query_operation_name() + * allows it to return NULL, in which case the fallback should be to use + * EVP_KEYMGMT_get0_name(). That is exactly the thing we are testing here. + */ + fake_rsa_query_operation_name = 1; + + if (!TEST_ptr(params = fake_rsa_key_params(0)) + || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA", + "provider=fake-rsa")) + || !TEST_true(EVP_PKEY_fromdata_init(ctx)) + || !TEST_true(EVP_PKEY_fromdata(ctx, &pkey_fake, EVP_PKEY_PUBLIC_KEY, + params)) + || !TEST_true(EVP_PKEY_can_sign(pkey_fake)) + || !TEST_ptr(pkey_fake)) + goto end; + + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + EVP_PKEY_free(pkey_fake); + pkey_fake = NULL; + OSSL_PARAM_free(params); + params = NULL; + + ret = 1; +end: + + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey_fake); + OSSL_PARAM_free(params); + fake_rsa_query_operation_name = 0; + + fake_rsa_finish(fake_rsa); + return ret; +} + static int test_pkey_store(int idx) { OSSL_PROVIDER *deflt = NULL; @@ -297,6 +368,7 @@ int setup_tests(void) ADD_TEST(test_pkey_sig); ADD_TEST(test_alternative_keygen_init); ADD_TEST(test_pkey_eq); + ADD_TEST(test_pkey_can_sign); ADD_ALL_TESTS(test_pkey_store, 2); return 1;