From: Matt Caswell Date: Fri, 6 Feb 2026 14:51:42 +0000 (+0000) Subject: Pass low level RSA objects to the default provider X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3db19c56ae08ea1ea455a42aa4c4b151405280a2;p=thirdparty%2Fopenssl.git Pass low level RSA objects to the default provider If a low level RSA object has been assigned a custom RSA_METHOD and is then assigned to an EVP_PKEY object, then we still want the default provider to use that RSA_METHOD. To ensure this occurs we pass the low level object across the provider boundary. We can only get away with this because it is the default provider. Reviewed-by: Shane Lontis Reviewed-by: Tomas Mraz MergeDate: Fri Feb 13 07:58:21 2026 (Merged from https://github.com/openssl/openssl/pull/29960) --- diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index b784e243df0..b91094504b8 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -46,6 +46,7 @@ #endif #include "internal/provider.h" #include "internal/common.h" +#include "internal/threads_common.h" #include "evp_local.h" static int pkey_set_type(EVP_PKEY *pkey, int type, const char *str, @@ -1897,11 +1898,23 @@ void *evp_pkey_export_to_provider(EVP_PKEY *pk, OSSL_LIB_CTX *libctx, if (!EVP_KEYMGMT_is_a(tmp_keymgmt, OBJ_nid2sn(pk->type))) goto end; - if ((keydata = evp_keymgmt_newdata(tmp_keymgmt)) == NULL) + /* We attempt to pass the low-level object to the keymgmt via a thread + * local. This will actually only succeed in the case that we are using + * the default provider. Otherwise it will export in the normal way. + */ + if (!CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_LOW_LEVEL_OBJECT, libctx, pk->pkey.ptr)) + goto end; + keydata = evp_keymgmt_newdata(tmp_keymgmt); + if (!CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_LOW_LEVEL_OBJECT, libctx, NULL)) + goto end; + if (keydata == NULL) goto end; - if (!pk->ameth->export_to(pk, keydata, tmp_keymgmt->import, - libctx, propquery)) { + /* + * We skip the export if the key data we got back is actually the same + * as the low level object we passed in + */ + if (keydata != pk->pkey.ptr && !pk->ameth->export_to(pk, keydata, tmp_keymgmt->import, libctx, propquery)) { evp_keymgmt_freedata(tmp_keymgmt, keydata); keydata = NULL; goto end; diff --git a/include/internal/threads_common.h b/include/internal/threads_common.h index 660fb1e5c65..6c9a5c271d2 100644 --- a/include/internal/threads_common.h +++ b/include/internal/threads_common.h @@ -20,6 +20,7 @@ typedef enum { CRYPTO_THREAD_LOCAL_TEVENT_KEY, CRYPTO_THREAD_LOCAL_TANDEM_ID_KEY, CRYPTO_THREAD_LOCAL_FIPS_DEFERRED_KEY, + CRYPTO_THREAD_LOCAL_LOW_LEVEL_OBJECT, CRYPTO_THREAD_LOCAL_KEY_MAX } CRYPTO_THREAD_LOCAL_KEY_ID; diff --git a/providers/implementations/keymgmt/rsa_kmgmt.c b/providers/implementations/keymgmt/rsa_kmgmt.c index f8e72b0f19e..2d768823b84 100644 --- a/providers/implementations/keymgmt/rsa_kmgmt.c +++ b/providers/implementations/keymgmt/rsa_kmgmt.c @@ -27,6 +27,7 @@ #include "crypto/cryptlib.h" #include "internal/fips.h" #include "internal/param_build_set.h" +#include "internal/threads_common.h" static OSSL_FUNC_keymgmt_new_fn rsa_newdata; static OSSL_FUNC_keymgmt_new_fn rsapss_newdata; @@ -75,35 +76,74 @@ static int pss_params_fromdata(RSA_PSS_PARAMS_30 *pss_params, int *defaults_set, return 1; } +/* + * If the application actually created a legacy RSA object and assigned it to + * the EVP_PKEY, then we get hold of that object here. We return 0 if we hit + * a fatal error or 1 otherwise. We may return 1 but with *rsa set to NULL. + */ +static int get_legacy_rsa_object(OSSL_LIB_CTX *libctx, RSA **rsa) +{ +#ifndef FIPS_MODULE + /* + * This only works because we are in the default provider. We are not + * normally allowed to pass complex objects across the provider boundary + * like this. + */ + *rsa = CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_LOW_LEVEL_OBJECT, libctx); + if (*rsa != NULL) { + if (ossl_lib_ctx_get_concrete(ossl_rsa_get0_libctx(*rsa)) != ossl_lib_ctx_get_concrete(libctx)) { + *rsa = NULL; + return 1; + } + if (!RSA_up_ref(*rsa)) + return 0; + } +#endif + + return 1; +} + static void *rsa_newdata(void *provctx) { OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); - RSA *rsa; + RSA *rsa = NULL; if (!ossl_prov_is_running()) return NULL; - rsa = ossl_rsa_new_with_ctx(libctx); - if (rsa != NULL) { - RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK); - RSA_set_flags(rsa, RSA_FLAG_TYPE_RSA); + if (!get_legacy_rsa_object(libctx, &rsa)) + return NULL; + + if (rsa == NULL) { + rsa = ossl_rsa_new_with_ctx(libctx); + if (rsa != NULL) { + RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK); + RSA_set_flags(rsa, RSA_FLAG_TYPE_RSA); + } } + return rsa; } static void *rsapss_newdata(void *provctx) { OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); - RSA *rsa; + RSA *rsa = NULL; if (!ossl_prov_is_running()) return NULL; - rsa = ossl_rsa_new_with_ctx(libctx); - if (rsa != NULL) { - RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK); - RSA_set_flags(rsa, RSA_FLAG_TYPE_RSASSAPSS); + if (!get_legacy_rsa_object(libctx, &rsa)) + return NULL; + + if (rsa == NULL) { + rsa = ossl_rsa_new_with_ctx(libctx); + if (rsa != NULL) { + RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK); + RSA_set_flags(rsa, RSA_FLAG_TYPE_RSASSAPSS); + } } + return rsa; }