From f5877a9d0496ca22244f24ad84c012001b72be53 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Fri, 28 Nov 2025 23:56:22 +0000 Subject: [PATCH 01/28] Add freeze flag to method store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add unit test to check that after freeze, method store cannot be modified and still works as expected. Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický (Merged from https://github.com/openssl/openssl/pull/29265) --- crypto/property/property.c | 28 +++++++++++++++++++++++----- include/internal/property.h | 1 + test/property_test.c | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/crypto/property/property.c b/crypto/property/property.c index 4c35d1eb7e..f5f2934b6d 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -86,6 +86,12 @@ struct ossl_method_store_st { /* Flag: 1 if query cache entries for all algs need flushing */ int cache_need_flush; + + /* Flag: 1 if method store is frozen */ + int frozen; + + /* Property query associated with frozen state */ + char *frozen_propq; }; typedef struct { @@ -265,6 +271,7 @@ void ossl_method_store_free(OSSL_METHOD_STORE *store) ossl_sa_ALGORITHM_free(store->algs); CRYPTO_THREAD_lock_free(store->lock); CRYPTO_THREAD_lock_free(store->biglock); + OPENSSL_free(store->frozen_propq); OPENSSL_free(store); } } @@ -324,7 +331,7 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov, int ret = 0; int i; - if (nid <= 0 || method == NULL || store == NULL) + if (nid <= 0 || method == NULL || store == NULL || store->frozen == 1) return 0; if (properties == NULL) @@ -438,7 +445,7 @@ int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid, ALGORITHM *alg = NULL; int i; - if (nid <= 0 || method == NULL || store == NULL) + if (nid <= 0 || method == NULL || store == NULL || store->frozen == 1) return 0; if (!ossl_property_write_lock(store)) @@ -540,7 +547,7 @@ int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, { struct alg_cleanup_by_provider_data_st data; - if (!ossl_property_write_lock(store)) + if (store == NULL || store->frozen == 1 || !ossl_property_write_lock(store)) return 0; data.prov = prov; data.store = store; @@ -549,6 +556,17 @@ int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, return 1; } +int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *prop_query) +{ + if (store == NULL || store->frozen == 1) + return 0; + /* TODO: FREEZE: Create frozen caches */ + store->frozen = 1; + if (prop_query != NULL) + store->frozen_propq = OPENSSL_strndup(prop_query, strlen(prop_query)); + return 1; +} + static void alg_do_one(ALGORITHM *alg, IMPLEMENTATION *impl, void (*fn)(int id, void *method, void *fnarg), void *fnarg) @@ -774,7 +792,7 @@ static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid) int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store) { - if (!ossl_property_write_lock(store)) + if (store == NULL || store->frozen == 1 || !ossl_property_write_lock(store)) return 0; ossl_sa_ALGORITHM_doall(store->algs, &impl_cache_flush_alg); store->cache_nelem = 0; @@ -896,7 +914,7 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, size_t len; int res = 1; - if (nid <= 0 || store == NULL || prop_query == NULL) + if (nid <= 0 || store == NULL || prop_query == NULL || store->frozen == 1) return 0; if (!ossl_assert(prov != NULL)) diff --git a/include/internal/property.h b/include/internal/property.h index b63634c678..abd6bc9651 100644 --- a/include/internal/property.h +++ b/include/internal/property.h @@ -70,6 +70,7 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store, const OSSL_PROVIDER **prov, void **method); int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov); +int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *prop_query); /* Get the global properties associate with the specified library context */ OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *ctx, diff --git a/test/property_test.c b/test/property_test.c index eea1f28a19..f591ad5d69 100644 --- a/test/property_test.c +++ b/test/property_test.c @@ -417,6 +417,40 @@ err: return ret; } +static int test_freeze_flag(void) +{ + int ret = 0, nid = 6; + const char *prop = "position=1"; + char *impl = "a"; + OSSL_METHOD_STORE *store; + OSSL_PROVIDER prov = { 1 }; + const OSSL_PROVIDER *fetched_prov = NULL; + void *fetched_meth = NULL; + + if (!TEST_ptr(store = ossl_method_store_new(NULL)) + || !TEST_true(add_property_names("position", NULL)) + || !TEST_true(ossl_method_store_add(store, &prov, nid, prop, impl, &up_ref, &down_ref)) + || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) + || !TEST_ptr_eq(&prov, fetched_prov) + || !TEST_str_eq((char *)fetched_meth, impl) + || !TEST_true(ossl_method_store_freeze(store, NULL)) + || !TEST_false(ossl_method_store_remove(store, nid, impl)) + || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) + || !TEST_ptr_eq(&prov, fetched_prov) + || !TEST_str_eq((char *)fetched_meth, impl) + || !TEST_false(ossl_method_store_remove_all_provided(store, fetched_prov)) + || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) + || !TEST_ptr_eq(&prov, fetched_prov) + || !TEST_str_eq((char *)fetched_meth, impl) + || !TEST_false(ossl_method_store_freeze(store, NULL))) + goto err; + + ret = 1; +err: + ossl_method_store_free(store); + return ret; +} + static int test_property(void) { static OSSL_PROVIDER fake_provider1 = { 1 }; @@ -713,6 +747,7 @@ int setup_tests(void) ADD_TEST(test_property_defn_cache); ADD_ALL_TESTS(test_definition_compares, OSSL_NELEM(definition_tests)); ADD_TEST(test_register_deregister); + ADD_TEST(test_freeze_flag); ADD_TEST(test_property); ADD_TEST(test_query_cache_stochastic); ADD_TEST(test_fips_mode); From cbb3db48ac5706764b9233c878a1bfe467333808 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 2 Dec 2025 21:10:16 +0000 Subject: [PATCH 02/28] Test that adding provider after freeze will fail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický (Merged from https://github.com/openssl/openssl/pull/29265) --- test/property_test.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/property_test.c b/test/property_test.c index f591ad5d69..96a5d492f6 100644 --- a/test/property_test.c +++ b/test/property_test.c @@ -420,8 +420,8 @@ err: static int test_freeze_flag(void) { int ret = 0, nid = 6; - const char *prop = "position=1"; - char *impl = "a"; + const char *prop = "position=1", *prop2 = "position=2"; + char *impl = "a", *impl2 = "b"; OSSL_METHOD_STORE *store; OSSL_PROVIDER prov = { 1 }; const OSSL_PROVIDER *fetched_prov = NULL; @@ -442,6 +442,7 @@ static int test_freeze_flag(void) || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) || !TEST_ptr_eq(&prov, fetched_prov) || !TEST_str_eq((char *)fetched_meth, impl) + || !TEST_false(ossl_method_store_add(store, &prov, nid, prop2, impl2, &up_ref, &down_ref)) || !TEST_false(ossl_method_store_freeze(store, NULL))) goto err; From 8220d3664d8e432c531e5eed9f027acf96afbd70 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 2 Dec 2025 23:21:50 +0000 Subject: [PATCH 03/28] Remove frozen_propq from ossl_method_store_st MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický (Merged from https://github.com/openssl/openssl/pull/29265) --- crypto/property/property.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/crypto/property/property.c b/crypto/property/property.c index f5f2934b6d..1be49ef43c 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -89,9 +89,6 @@ struct ossl_method_store_st { /* Flag: 1 if method store is frozen */ int frozen; - - /* Property query associated with frozen state */ - char *frozen_propq; }; typedef struct { @@ -271,7 +268,6 @@ void ossl_method_store_free(OSSL_METHOD_STORE *store) ossl_sa_ALGORITHM_free(store->algs); CRYPTO_THREAD_lock_free(store->lock); CRYPTO_THREAD_lock_free(store->biglock); - OPENSSL_free(store->frozen_propq); OPENSSL_free(store); } } @@ -560,10 +556,8 @@ int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *prop_query) { if (store == NULL || store->frozen == 1) return 0; - /* TODO: FREEZE: Create frozen caches */ + /* TODO: FREEZE: Create frozen caches & do something with prop_query */ store->frozen = 1; - if (prop_query != NULL) - store->frozen_propq = OPENSSL_strndup(prop_query, strlen(prop_query)); return 1; } From cb97cc61e068280cbd940014518545f9f00befa9 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 3 Dec 2025 16:13:14 +0000 Subject: [PATCH 04/28] Remove prop_query for ossl_method_store_freeze MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický (Merged from https://github.com/openssl/openssl/pull/29265) --- crypto/property/property.c | 4 ++-- include/internal/property.h | 2 +- test/property_test.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crypto/property/property.c b/crypto/property/property.c index 1be49ef43c..f06587a79b 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -552,11 +552,11 @@ int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, return 1; } -int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *prop_query) +int ossl_method_store_freeze(OSSL_METHOD_STORE *store) { if (store == NULL || store->frozen == 1) return 0; - /* TODO: FREEZE: Create frozen caches & do something with prop_query */ + /* TODO: FREEZE: Create frozen caches */ store->frozen = 1; return 1; } diff --git a/include/internal/property.h b/include/internal/property.h index abd6bc9651..9a96be0ab0 100644 --- a/include/internal/property.h +++ b/include/internal/property.h @@ -70,7 +70,7 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store, const OSSL_PROVIDER **prov, void **method); int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov); -int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *prop_query); +int ossl_method_store_freeze(OSSL_METHOD_STORE *store); /* Get the global properties associate with the specified library context */ OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *ctx, diff --git a/test/property_test.c b/test/property_test.c index 96a5d492f6..d4a9d9948c 100644 --- a/test/property_test.c +++ b/test/property_test.c @@ -433,7 +433,7 @@ static int test_freeze_flag(void) || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) || !TEST_ptr_eq(&prov, fetched_prov) || !TEST_str_eq((char *)fetched_meth, impl) - || !TEST_true(ossl_method_store_freeze(store, NULL)) + || !TEST_true(ossl_method_store_freeze(store)) || !TEST_false(ossl_method_store_remove(store, nid, impl)) || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) || !TEST_ptr_eq(&prov, fetched_prov) @@ -443,7 +443,7 @@ static int test_freeze_flag(void) || !TEST_ptr_eq(&prov, fetched_prov) || !TEST_str_eq((char *)fetched_meth, impl) || !TEST_false(ossl_method_store_add(store, &prov, nid, prop2, impl2, &up_ref, &down_ref)) - || !TEST_false(ossl_method_store_freeze(store, NULL))) + || !TEST_false(ossl_method_store_freeze(store))) goto err; ret = 1; From ba4fce934af92f5a3e9dfc33b9c7cef33f82ccdb Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 3 Dec 2025 16:13:14 +0000 Subject: [PATCH 05/28] Remove prop_query for ossl_method_store_freeze MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Nikola Pajkovsky (Merged from https://github.com/openssl/openssl/pull/29331) --- include/internal/property.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/internal/property.h b/include/internal/property.h index 9a96be0ab0..b63634c678 100644 --- a/include/internal/property.h +++ b/include/internal/property.h @@ -70,7 +70,6 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store, const OSSL_PROVIDER **prov, void **method); int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov); -int ossl_method_store_freeze(OSSL_METHOD_STORE *store); /* Get the global properties associate with the specified library context */ OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *ctx, From d65db7f0e146351040b646a098b0d44e8902f937 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Sat, 6 Dec 2025 23:17:08 +0000 Subject: [PATCH 06/28] Add freeze functionality to EVP_MD_fetch() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added freeze function for OSSL_LIB_CTX Freeze implementation is just global variables. This will be fixed in a separate PR. Added unit test to ensure correct methods are fetched. This unit test should work even when actual freeze implementation is added Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Nikola Pajkovsky (Merged from https://github.com/openssl/openssl/pull/29331) --- crypto/context.c | 7 +++ crypto/evp/digest.c | 8 ++- crypto/evp/evp_fetch.c | 73 +++++++++++++++++++++++++++ crypto/evp/evp_local.h | 3 ++ crypto/property/property.c | 99 +++++++++++++++++++++++++++++++++---- include/crypto/evp.h | 1 + include/internal/property.h | 10 ++++ include/openssl/crypto.h.in | 1 + test/evp_fetch_prov_test.c | 33 +++++++++++++ test/property_test.c | 4 +- util/libcrypto.num | 1 + 11 files changed, 227 insertions(+), 13 deletions(-) diff --git a/crypto/context.c b/crypto/context.c index c25a0bd656..c6e444a9c7 100644 --- a/crypto/context.c +++ b/crypto/context.c @@ -547,6 +547,13 @@ OSSL_LIB_CTX *OSSL_LIB_CTX_set0_default(OSSL_LIB_CTX *libctx) return NULL; } +int OSSL_LIB_CTX_freeze(OSSL_LIB_CTX *ctx, const char *propq) +{ + if ((ctx = ossl_lib_ctx_get_concrete(ctx)) == NULL) + return 0; + return ossl_method_store_freeze(ctx->evp_method_store, propq); +} + void ossl_release_default_drbg_ctx(void) { /* early release of the DRBG in global default libctx */ diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index 7184f9a051..d721a997ac 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -1078,7 +1078,13 @@ static void evp_md_free(void *md) EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) { - EVP_MD *md = evp_generic_fetch(ctx, OSSL_OP_DIGEST, algorithm, properties, + EVP_MD *md = NULL; + + if (evp_generic_fetch_frozen(ctx, OSSL_OP_DIGEST, algorithm, properties, + NULL, (void **)&md)) + return md; + + md = evp_generic_fetch(ctx, OSSL_OP_DIGEST, algorithm, properties, evp_md_from_algorithm, evp_md_up_ref, evp_md_free); return md; diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c index 612e57e6f0..620b60ce21 100644 --- a/crypto/evp/evp_fetch.c +++ b/crypto/evp/evp_fetch.c @@ -388,6 +388,79 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, return method; } +/* + * Returns 1 if method store is frozen AND prop query is equal to frozen prop + * query. Only sets METHOD if found. + */ +int evp_generic_fetch_frozen(OSSL_LIB_CTX *libctx, int operation_id, + const char *name, const char *properties, + OSSL_PROVIDER *prov, void **method) +{ + OSSL_METHOD_STORE *store = get_evp_method_store(libctx); + OSSL_FROZEN_METHOD_STORE *frozen_store; + const char *store_propq; + OSSL_NAMEMAP *namemap; + uint32_t meth_id; +#ifdef FIPS_MODULE + /* + * The FIPS provider has its own internal library context where only it + * is loaded. Consequently, property queries aren't relevant because + * there is only one fetchable algorithm and it is assumed that the + * FIPS-ness is handled by the using algorithm. + */ + const char *const propq = ""; +#else + const char *const propq = properties != NULL ? properties : ""; +#endif /* FIPS_MODULE */ + + if (store == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + + /* + * If there's ever an operation_id == 0 passed, we have an internal + * programming error. + */ + if (!ossl_assert(operation_id > 0)) { + ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* Return 0 if not frozen or prop query is different than frozen prop query */ + if (!ossl_method_store_is_frozen(store) + || (frozen_store = ossl_get_frozen_method_store(store)) == NULL) + return 0; + + if (strlen(propq) != 0) { + store_propq = ossl_get_frozen_method_store_propq(frozen_store); + if (strcmp(propq, store_propq) != 0) + return 0; + } + + /* If we haven't received a name id yet, try to get one for the name */ + namemap = ossl_namemap_stored(libctx); + if (namemap == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + meth_id = name != NULL ? ossl_namemap_name2num(namemap, name) : 0; + + /* + * If we have a name id, calculate a method id with evp_method_id(). + * + * evp_method_id returns 0 if we have too many operations (more than + * about 2^8) or too many names (more than about 2^24). + * For all intents and purposes, this is an internal error. + */ + if (meth_id != 0 && (meth_id = evp_method_id(meth_id, operation_id)) == 0) { + ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); + return 0; + } + + return ossl_frozen_method_store_cache_get(store, prov, meth_id, propq, method); +} + void *evp_generic_fetch(OSSL_LIB_CTX *libctx, int operation_id, const char *name, const char *properties, void *(*new_method)(int name_id, diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index b3f9bafe23..19bae81cbb 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -302,6 +302,9 @@ void *evp_generic_fetch(OSSL_LIB_CTX *ctx, int operation_id, OSSL_PROVIDER *prov), int (*up_ref_method)(void *), void (*free_method)(void *)); +int evp_generic_fetch_frozen(OSSL_LIB_CTX *libctx, int operation_id, + const char *name, const char *properties, + OSSL_PROVIDER *prov, void **method); void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id, const char *name, const char *properties, void *(*new_method)(int name_id, diff --git a/crypto/property/property.c b/crypto/property/property.c index f06587a79b..b9970034be 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -25,6 +25,7 @@ #include "crypto/sparse_array.h" #include "property_local.h" #include "crypto/context.h" +#include "crypto/evp.h" /* * The number of elements in the query cache before we initiate a flush. @@ -62,6 +63,13 @@ typedef struct { LHASH_OF(QUERY) *cache; } ALGORITHM; +struct ossl_frozen_method_store_st { + /* Property query associated with frozen state */ + char *propq; + + /* TODO: FREEZE */ +}; + struct ossl_method_store_st { OSSL_LIB_CTX *ctx; SPARSE_ARRAY_OF(ALGORITHM) * algs; @@ -87,8 +95,8 @@ struct ossl_method_store_st { /* Flag: 1 if query cache entries for all algs need flushing */ int cache_need_flush; - /* Flag: 1 if method store is frozen */ - int frozen; + /* Non-null if method store is frozen */ + OSSL_FROZEN_METHOD_STORE *frozen_store; }; typedef struct { @@ -268,6 +276,9 @@ void ossl_method_store_free(OSSL_METHOD_STORE *store) ossl_sa_ALGORITHM_free(store->algs); CRYPTO_THREAD_lock_free(store->lock); CRYPTO_THREAD_lock_free(store->biglock); + if (store->frozen_store != NULL) + OPENSSL_free(store->frozen_store->propq); + OPENSSL_free(store->frozen_store); OPENSSL_free(store); } } @@ -327,7 +338,7 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov, int ret = 0; int i; - if (nid <= 0 || method == NULL || store == NULL || store->frozen == 1) + if (nid <= 0 || method == NULL || store == NULL || store->frozen_store != NULL) return 0; if (properties == NULL) @@ -441,7 +452,7 @@ int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid, ALGORITHM *alg = NULL; int i; - if (nid <= 0 || method == NULL || store == NULL || store->frozen == 1) + if (nid <= 0 || method == NULL || store == NULL || store->frozen_store != NULL) return 0; if (!ossl_property_write_lock(store)) @@ -543,7 +554,7 @@ int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, { struct alg_cleanup_by_provider_data_st data; - if (store == NULL || store->frozen == 1 || !ossl_property_write_lock(store)) + if (store == NULL || store->frozen_store != NULL || !ossl_property_write_lock(store)) return 0; data.prov = prov; data.store = store; @@ -552,15 +563,40 @@ int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, return 1; } -int ossl_method_store_freeze(OSSL_METHOD_STORE *store) +int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *propq) { - if (store == NULL || store->frozen == 1) + if (store == NULL || store->frozen_store != NULL) return 0; + store->frozen_store = OPENSSL_zalloc(sizeof(store->frozen_store)); + if (store->frozen_store == NULL) + return 0; + if (propq != NULL) { + store->frozen_store->propq = OPENSSL_strdup(propq); + if (store->frozen_store->propq == NULL) { + OPENSSL_free(store->frozen_store); + store->frozen_store = NULL; + return 0; + } + } /* TODO: FREEZE: Create frozen caches */ - store->frozen = 1; return 1; } +int ossl_method_store_is_frozen(OSSL_METHOD_STORE *store) +{ + return store != NULL && store->frozen_store != NULL; +} + +OSSL_FROZEN_METHOD_STORE *ossl_get_frozen_method_store(OSSL_METHOD_STORE *store) +{ + return store != NULL ? store->frozen_store : NULL; +} + +const char *ossl_get_frozen_method_store_propq(OSSL_FROZEN_METHOD_STORE *store) +{ + return (store != NULL && store->propq != NULL) ? store->propq : ""; +} + static void alg_do_one(ALGORITHM *alg, IMPLEMENTATION *impl, void (*fn)(int id, void *method, void *fnarg), void *fnarg) @@ -786,7 +822,7 @@ static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid) int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store) { - if (store == NULL || store->frozen == 1 || !ossl_property_write_lock(store)) + if (store == NULL || store->frozen_store != NULL || !ossl_property_write_lock(store)) return 0; ossl_sa_ALGORITHM_doall(store->algs, &impl_cache_flush_alg); store->cache_nelem = 0; @@ -868,6 +904,11 @@ static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store) tsan_add(&global_seed, state.seed); } +/* TODO: FREEZE: Remove these and replace with actual implementation */ +static int andrew_nid = -1; +static EVP_MD andrew_md; +static void *andrew_method; + int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void **method) { @@ -891,6 +932,13 @@ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, goto err; if (ossl_method_up_ref(&r->method)) { *method = r->method.method; + /* TODO: FREEZE: Remove this */ + if (nid != andrew_nid) { + memcpy(&andrew_md, r->method.method, sizeof(andrew_md)); + andrew_md.origin = EVP_ORIG_FROZEN; + andrew_method = &andrew_md; + andrew_nid = nid; + } res = 1; } err: @@ -898,6 +946,26 @@ err: return res; } +int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, + OSSL_PROVIDER *prov, int nid, + const char *prop_query, void **method) +{ + /* + * Query triplet (nid, prov, prop_query) from frozen store with no fallback. + */ + + /* + * TODO: FREEZE: Replace with actual implementation + * For now, just fetch from global variables + */ + if (nid == andrew_nid && andrew_method != NULL) { + *method = andrew_method; + return 1; + } + + return 0; +} + int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void *method, int (*method_up_ref)(void *), @@ -908,7 +976,7 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, size_t len; int res = 1; - if (nid <= 0 || store == NULL || prop_query == NULL || store->frozen == 1) + if (nid <= 0 || store == NULL || prop_query == NULL || store->frozen_store != NULL) return 0; if (!ossl_assert(prov != NULL)) @@ -941,6 +1009,17 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, if (!ossl_method_up_ref(&p->method)) goto err; memcpy((char *)p->query, prop_query, len + 1); + + /* TODO: FREEZE: Remove this */ + if (nid != andrew_nid) { + EVP_MD *md = (EVP_MD *)p->method.method; + + memcpy(&andrew_md, md, sizeof(andrew_md)); + andrew_md.origin = EVP_ORIG_FROZEN; + andrew_method = &andrew_md; + andrew_nid = nid; + } + if ((old = lh_QUERY_insert(alg->cache, p)) != NULL) { impl_cache_free(old); goto end; diff --git a/include/crypto/evp.h b/include/crypto/evp.h index 43d4fdc527..5d501a5889 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -252,6 +252,7 @@ struct evp_kdf_st { #define EVP_ORIG_DYNAMIC 0 #define EVP_ORIG_GLOBAL 1 #define EVP_ORIG_METH 2 +#define EVP_ORIG_FROZEN 3 struct evp_md_st { /* nid */ diff --git a/include/internal/property.h b/include/internal/property.h index b63634c678..88fcd70b85 100644 --- a/include/internal/property.h +++ b/include/internal/property.h @@ -15,6 +15,7 @@ #include "internal/cryptlib.h" typedef struct ossl_method_store_st OSSL_METHOD_STORE; +typedef struct ossl_frozen_method_store_st OSSL_FROZEN_METHOD_STORE; typedef struct ossl_property_list_st OSSL_PROPERTY_LIST; typedef enum { @@ -71,6 +72,12 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store, int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov); +/* Frozen method store related functions */ +int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *propq); +int ossl_method_store_is_frozen(OSSL_METHOD_STORE *store); +OSSL_FROZEN_METHOD_STORE *ossl_get_frozen_method_store(OSSL_METHOD_STORE *store); +const char *ossl_get_frozen_method_store_propq(OSSL_FROZEN_METHOD_STORE *store); + /* Get the global properties associate with the specified library context */ OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *ctx, int loadconfig); @@ -78,6 +85,9 @@ OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *ctx, /* property query cache functions */ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void **result); +int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, + OSSL_PROVIDER *prov, int nid, + const char *prop_query, void **result); int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void *result, int (*method_up_ref)(void *), diff --git a/include/openssl/crypto.h.in b/include/openssl/crypto.h.in index c9dc3706f3..7a6d1d7c94 100644 --- a/include/openssl/crypto.h.in +++ b/include/openssl/crypto.h.in @@ -586,6 +586,7 @@ OSSL_LIB_CTX *OSSL_LIB_CTX_get0_global_default(void); OSSL_LIB_CTX *OSSL_LIB_CTX_set0_default(OSSL_LIB_CTX *libctx); int OSSL_LIB_CTX_get_conf_diagnostics(OSSL_LIB_CTX *ctx); void OSSL_LIB_CTX_set_conf_diagnostics(OSSL_LIB_CTX *ctx, int value); +int OSSL_LIB_CTX_freeze(OSSL_LIB_CTX *ctx, const char *propq); void OSSL_sleep(uint64_t millis); diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index 2cde0049e8..15063102e2 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -20,6 +20,7 @@ #include #include "internal/sizes.h" #include "testutil.h" +#include "crypto/evp.h" static char *config_file = NULL; static char *alg = "digest"; @@ -219,6 +220,37 @@ err: return ret; } +static int test_EVP_MD_fetch_freeze(void) +{ + EVP_MD *md = NULL; + int ret = 0; + + if (!TEST_ptr(md = EVP_MD_fetch(NULL, "SHA256", NULL)) + || !TEST_true(test_md(md)) + || !TEST_int_ne(md->origin, EVP_ORIG_FROZEN)) + goto err; + + EVP_MD_free(md); + md = NULL; + + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(NULL, "?fips=true"), 1) + || !TEST_ptr(md = EVP_MD_fetch(NULL, "SHA256", NULL)) + || !TEST_true(test_md(md)) + || !TEST_int_eq(md->origin, EVP_ORIG_FROZEN) + || !TEST_ptr(md = EVP_MD_fetch(NULL, "SHA256", "?fips=true")) + || !TEST_true(test_md(md)) + || !TEST_int_eq(md->origin, EVP_ORIG_FROZEN) + || !TEST_ptr(md = EVP_MD_fetch(NULL, "SHA256", "?provider=default")) + || !TEST_true(test_md(md)) + || !TEST_int_ne(md->origin, EVP_ORIG_FROZEN)) + goto err; + + ret = 1; + err: + EVP_MD_free(md); + return ret; +} + static int test_explicit_EVP_MD_fetch_by_name(void) { return test_explicit_EVP_MD_fetch("SHA256"); @@ -402,6 +434,7 @@ int setup_tests(void) } ADD_TEST(test_legacy_provider_unloaded); if (strcmp(alg, "digest") == 0) { + ADD_TEST(test_EVP_MD_fetch_freeze); ADD_TEST(test_implicit_EVP_MD_fetch); ADD_TEST(test_explicit_EVP_MD_fetch_by_name); ADD_ALL_TESTS_NOSUBTEST(test_explicit_EVP_MD_fetch_by_X509_ALGOR, 2); diff --git a/test/property_test.c b/test/property_test.c index d4a9d9948c..96a5d492f6 100644 --- a/test/property_test.c +++ b/test/property_test.c @@ -433,7 +433,7 @@ static int test_freeze_flag(void) || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) || !TEST_ptr_eq(&prov, fetched_prov) || !TEST_str_eq((char *)fetched_meth, impl) - || !TEST_true(ossl_method_store_freeze(store)) + || !TEST_true(ossl_method_store_freeze(store, NULL)) || !TEST_false(ossl_method_store_remove(store, nid, impl)) || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) || !TEST_ptr_eq(&prov, fetched_prov) @@ -443,7 +443,7 @@ static int test_freeze_flag(void) || !TEST_ptr_eq(&prov, fetched_prov) || !TEST_str_eq((char *)fetched_meth, impl) || !TEST_false(ossl_method_store_add(store, &prov, nid, prop2, impl2, &up_ref, &down_ref)) - || !TEST_false(ossl_method_store_freeze(store))) + || !TEST_false(ossl_method_store_freeze(store, NULL))) goto err; ret = 1; diff --git a/util/libcrypto.num b/util/libcrypto.num index 155ba0f93d..5eb70de5bb 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5815,3 +5815,4 @@ OSSL_PARAM_clear_free ? 4_0_0 EXIST::FUNCTION: CMS_dataFinal_ex ? 4_0_0 EXIST::FUNCTION:CMS CMS_SignerInfo_verify_ex ? 4_0_0 EXIST::FUNCTION:CMS EVP_SIGNATURE_has_message_update ? 4_0_0 EXIST::FUNCTION: +OSSL_LIB_CTX_freeze ? 4_0_0 EXIST::FUNCTION: From f174d994efdfec2919c9925d5564038de3cd516f Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 10 Dec 2025 08:45:57 +0000 Subject: [PATCH 07/28] Add documentation for OSSL_LIB_CTX_freeze MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Nikola Pajkovsky (Merged from https://github.com/openssl/openssl/pull/29331) --- crypto/property/property.c | 13 ++----------- doc/man3/OSSL_LIB_CTX.pod | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/crypto/property/property.c b/crypto/property/property.c index b9970034be..ea5c365ad9 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -932,13 +932,6 @@ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, goto err; if (ossl_method_up_ref(&r->method)) { *method = r->method.method; - /* TODO: FREEZE: Remove this */ - if (nid != andrew_nid) { - memcpy(&andrew_md, r->method.method, sizeof(andrew_md)); - andrew_md.origin = EVP_ORIG_FROZEN; - andrew_method = &andrew_md; - andrew_nid = nid; - } res = 1; } err: @@ -1011,10 +1004,8 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, memcpy((char *)p->query, prop_query, len + 1); /* TODO: FREEZE: Remove this */ - if (nid != andrew_nid) { - EVP_MD *md = (EVP_MD *)p->method.method; - - memcpy(&andrew_md, md, sizeof(andrew_md)); + if (nid == 26113) { + memcpy(&andrew_md, p->method.method, sizeof(andrew_md)); andrew_md.origin = EVP_ORIG_FROZEN; andrew_method = &andrew_md; andrew_nid = nid; diff --git a/doc/man3/OSSL_LIB_CTX.pod b/doc/man3/OSSL_LIB_CTX.pod index 215db61820..ab2b807318 100644 --- a/doc/man3/OSSL_LIB_CTX.pod +++ b/doc/man3/OSSL_LIB_CTX.pod @@ -5,7 +5,8 @@ OSSL_LIB_CTX, OSSL_LIB_CTX_get_data, OSSL_LIB_CTX_new, OSSL_LIB_CTX_new_from_dispatch, OSSL_LIB_CTX_new_child, OSSL_LIB_CTX_free, OSSL_LIB_CTX_load_config, -OSSL_LIB_CTX_get0_global_default, OSSL_LIB_CTX_set0_default +OSSL_LIB_CTX_get0_global_default, OSSL_LIB_CTX_set0_default, +OSSL_LIB_CTX_freeze - OpenSSL library context =head1 SYNOPSIS @@ -24,6 +25,7 @@ OSSL_LIB_CTX_get0_global_default, OSSL_LIB_CTX_set0_default OSSL_LIB_CTX *OSSL_LIB_CTX_get0_global_default(void); OSSL_LIB_CTX *OSSL_LIB_CTX_set0_default(OSSL_LIB_CTX *ctx); void *OSSL_LIB_CTX_get_data(OSSL_LIB_CTX *ctx, int index); + void *OSSL_LIB_CTX_freeze(OSSL_LIB_CTX *ctx, const char* propq); =head1 DESCRIPTION @@ -121,6 +123,19 @@ If ctx is NULL then the function operates on the default library context. OSSL_LIB_CTX_get_data() returns a memory address whose interpretation depends on the index. +OSSL_LIB_CTX_freeze() freezes the method store associated with the library +context. A frozen context will speed up ONLY isolated, uncached algorithm +lookups. This is specifically designed to benefit legacy high-throughput +applications with worker threads doing isolated computations, without requiring +a code restructuring. Existing and new applications are generally recommended to +not use this feature and instead structure the application to pre-initialize +contexts where possible. + +If propq is NULL, it will only speed up method store operations with a NULL +property query. If propq is not NULL, it will also speed up method store +operations when given that exact property query. Other property queries will go +through the normal, slower lookup method. + =head1 RETURN VALUES OSSL_LIB_CTX_new(), OSSL_LIB_CTX_get0_global_default() and @@ -134,15 +149,20 @@ OSSL_LIB_CTX_load_config() returns 1 on success, 0 on error. OSSL_LIB_CTX_get_data() returns a memory address whose interpretation depends on the index. +OSSL_LIB_CTX_freeze() returns 1 on success, 0 on error. A frozen library context +cannot be frozen again. + =head1 HISTORY All of the functions described on this page were added in OpenSSL 3.0. OSSL_LIB_CTX_get_data() was introduced in OpenSSL 3.4. +OSSL_LIB_CTX_freeze() was introduced in OpenSSL 4.0. + =head1 COPYRIGHT -Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved. Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy From 8a17b8ee9e60d0dfa33f10a344fa332208fcca2e Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 10 Dec 2025 09:17:43 +0000 Subject: [PATCH 08/28] Fix temporary freeze implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Nikola Pajkovsky (Merged from https://github.com/openssl/openssl/pull/29331) --- crypto/property/property.c | 33 ++++++++++++++++++--------------- test/evp_fetch_prov_test.c | 27 ++++++++++++++++++--------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/crypto/property/property.c b/crypto/property/property.c index ea5c365ad9..4304d24936 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -567,6 +567,7 @@ int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *propq) { if (store == NULL || store->frozen_store != NULL) return 0; + store->frozen_store = OPENSSL_zalloc(sizeof(store->frozen_store)); if (store->frozen_store == NULL) return 0; @@ -905,9 +906,8 @@ static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store) } /* TODO: FREEZE: Remove these and replace with actual implementation */ -static int andrew_nid = -1; static EVP_MD andrew_md; -static void *andrew_method; +static void *andrew_method = NULL; int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void **method) @@ -940,8 +940,8 @@ err: } int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, - OSSL_PROVIDER *prov, int nid, - const char *prop_query, void **method) + OSSL_PROVIDER *prov, int nid, + const char *prop_query, void **method) { /* * Query triplet (nid, prov, prop_query) from frozen store with no fallback. @@ -949,9 +949,21 @@ int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, /* * TODO: FREEZE: Replace with actual implementation - * For now, just fetch from global variables + * For now, just store & fetch from global variables */ - if (nid == andrew_nid && andrew_method != NULL) { + EVP_MD *temp_method; + + if (andrew_method == NULL) { + if (!ossl_method_store_cache_get(store, prov, nid, prop_query, (void **)&temp_method)) + return 0; + + memcpy(&andrew_md, temp_method, sizeof(andrew_md)); + andrew_md.origin = EVP_ORIG_FROZEN; + andrew_method = &andrew_md; + EVP_MD_free(temp_method); + } + + if (andrew_method != NULL) { *method = andrew_method; return 1; } @@ -1002,15 +1014,6 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, if (!ossl_method_up_ref(&p->method)) goto err; memcpy((char *)p->query, prop_query, len + 1); - - /* TODO: FREEZE: Remove this */ - if (nid == 26113) { - memcpy(&andrew_md, p->method.method, sizeof(andrew_md)); - andrew_md.origin = EVP_ORIG_FROZEN; - andrew_method = &andrew_md; - andrew_nid = nid; - } - if ((old = lh_QUERY_insert(alg->cache, p)) != NULL) { impl_cache_free(old); goto end; diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index 15063102e2..3698b3f56e 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -224,29 +224,38 @@ static int test_EVP_MD_fetch_freeze(void) { EVP_MD *md = NULL; int ret = 0; + OSSL_LIB_CTX *ctx = OSSL_LIB_CTX_new(); - if (!TEST_ptr(md = EVP_MD_fetch(NULL, "SHA256", NULL)) + if (!TEST_ptr(ctx) + || !TEST_ptr(md = EVP_MD_fetch(ctx, "SHA256", NULL)) || !TEST_true(test_md(md)) || !TEST_int_ne(md->origin, EVP_ORIG_FROZEN)) goto err; - EVP_MD_free(md); md = NULL; - if (!TEST_int_eq(OSSL_LIB_CTX_freeze(NULL, "?fips=true"), 1) - || !TEST_ptr(md = EVP_MD_fetch(NULL, "SHA256", NULL)) + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) + || !TEST_ptr(md = EVP_MD_fetch(ctx, "SHA256", NULL)) || !TEST_true(test_md(md)) - || !TEST_int_eq(md->origin, EVP_ORIG_FROZEN) - || !TEST_ptr(md = EVP_MD_fetch(NULL, "SHA256", "?fips=true")) + || !TEST_int_eq(md->origin, EVP_ORIG_FROZEN)) + goto err; + /* Technically, frozen version doesn't need to be freed */ + EVP_MD_free(md); + + if (!TEST_ptr(md = EVP_MD_fetch(ctx, "SHA256", "?fips=true")) || !TEST_true(test_md(md)) - || !TEST_int_eq(md->origin, EVP_ORIG_FROZEN) - || !TEST_ptr(md = EVP_MD_fetch(NULL, "SHA256", "?provider=default")) + || !TEST_int_eq(md->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_MD_free(md); + + if (!TEST_ptr(md = EVP_MD_fetch(ctx, "SHA256", "?provider=default")) || !TEST_true(test_md(md)) || !TEST_int_ne(md->origin, EVP_ORIG_FROZEN)) goto err; ret = 1; - err: +err: + OSSL_LIB_CTX_free(ctx); EVP_MD_free(md); return ret; } From 5844109177748470e96895361484df261800a7dd Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 16 Dec 2025 00:44:43 +0400 Subject: [PATCH 09/28] Update documentation to only call freeze in non-threaded context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Nikola Pajkovsky (Merged from https://github.com/openssl/openssl/pull/29331) --- doc/man3/OSSL_LIB_CTX.pod | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/man3/OSSL_LIB_CTX.pod b/doc/man3/OSSL_LIB_CTX.pod index ab2b807318..3dfb319256 100644 --- a/doc/man3/OSSL_LIB_CTX.pod +++ b/doc/man3/OSSL_LIB_CTX.pod @@ -129,7 +129,8 @@ lookups. This is specifically designed to benefit legacy high-throughput applications with worker threads doing isolated computations, without requiring a code restructuring. Existing and new applications are generally recommended to not use this feature and instead structure the application to pre-initialize -contexts where possible. +contexts where possible. This function should only be called from a non-threaded +context, before any worker threads have been dispatched. If propq is NULL, it will only speed up method store operations with a NULL property query. If propq is not NULL, it will also speed up method store From 9d4f44f3e72e3c45385a8b64161a57ff9f6a54bf Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Fri, 19 Dec 2025 01:10:27 +0700 Subject: [PATCH 10/28] Remove frozen store. This is a mock up that will be replaced with the real implementation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Nikola Pajkovsky (Merged from https://github.com/openssl/openssl/pull/29331) --- crypto/evp/evp_fetch.c | 14 ++++----- crypto/property/property.c | 57 +++++++++++++++---------------------- include/internal/property.h | 4 +-- 3 files changed, 30 insertions(+), 45 deletions(-) diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c index 620b60ce21..91ea623911 100644 --- a/crypto/evp/evp_fetch.c +++ b/crypto/evp/evp_fetch.c @@ -388,6 +388,7 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, return method; } +/* TODO: FREEZE: Replace with actual implementation */ /* * Returns 1 if method store is frozen AND prop query is equal to frozen prop * query. Only sets METHOD if found. @@ -397,7 +398,6 @@ int evp_generic_fetch_frozen(OSSL_LIB_CTX *libctx, int operation_id, OSSL_PROVIDER *prov, void **method) { OSSL_METHOD_STORE *store = get_evp_method_store(libctx); - OSSL_FROZEN_METHOD_STORE *frozen_store; const char *store_propq; OSSL_NAMEMAP *namemap; uint32_t meth_id; @@ -428,14 +428,12 @@ int evp_generic_fetch_frozen(OSSL_LIB_CTX *libctx, int operation_id, } /* Return 0 if not frozen or prop query is different than frozen prop query */ - if (!ossl_method_store_is_frozen(store) - || (frozen_store = ossl_get_frozen_method_store(store)) == NULL) + if (!ossl_method_store_is_frozen(store)) + return 0; + if (strlen(propq) != 0 + && (store_propq = ossl_method_store_frozen_propq(store)) != NULL + && strcmp(propq, store_propq) != 0) { return 0; - - if (strlen(propq) != 0) { - store_propq = ossl_get_frozen_method_store_propq(frozen_store); - if (strcmp(propq, store_propq) != 0) - return 0; } /* If we haven't received a name id yet, try to get one for the name */ diff --git a/crypto/property/property.c b/crypto/property/property.c index 4304d24936..1983a3559c 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -63,13 +63,6 @@ typedef struct { LHASH_OF(QUERY) *cache; } ALGORITHM; -struct ossl_frozen_method_store_st { - /* Property query associated with frozen state */ - char *propq; - - /* TODO: FREEZE */ -}; - struct ossl_method_store_st { OSSL_LIB_CTX *ctx; SPARSE_ARRAY_OF(ALGORITHM) * algs; @@ -95,8 +88,11 @@ struct ossl_method_store_st { /* Flag: 1 if query cache entries for all algs need flushing */ int cache_need_flush; - /* Non-null if method store is frozen */ - OSSL_FROZEN_METHOD_STORE *frozen_store; + /* Flag: 1 if method store is frozen */ + int frozen; + + /* Property query associated with frozen state */ + char *frozen_propq; }; typedef struct { @@ -276,9 +272,7 @@ void ossl_method_store_free(OSSL_METHOD_STORE *store) ossl_sa_ALGORITHM_free(store->algs); CRYPTO_THREAD_lock_free(store->lock); CRYPTO_THREAD_lock_free(store->biglock); - if (store->frozen_store != NULL) - OPENSSL_free(store->frozen_store->propq); - OPENSSL_free(store->frozen_store); + OPENSSL_free(store->frozen_propq); OPENSSL_free(store); } } @@ -338,7 +332,7 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov, int ret = 0; int i; - if (nid <= 0 || method == NULL || store == NULL || store->frozen_store != NULL) + if (nid <= 0 || method == NULL || store == NULL || store->frozen == 1) return 0; if (properties == NULL) @@ -452,7 +446,7 @@ int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid, ALGORITHM *alg = NULL; int i; - if (nid <= 0 || method == NULL || store == NULL || store->frozen_store != NULL) + if (nid <= 0 || method == NULL || store == NULL || store->frozen == 1) return 0; if (!ossl_property_write_lock(store)) @@ -554,7 +548,7 @@ int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, { struct alg_cleanup_by_provider_data_st data; - if (store == NULL || store->frozen_store != NULL || !ossl_property_write_lock(store)) + if (store == NULL || store->frozen == 1 || !ossl_property_write_lock(store)) return 0; data.prov = prov; data.store = store; @@ -565,37 +559,30 @@ int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *propq) { - if (store == NULL || store->frozen_store != NULL) + if (store == NULL || store->frozen == 1) return 0; - store->frozen_store = OPENSSL_zalloc(sizeof(store->frozen_store)); - if (store->frozen_store == NULL) - return 0; if (propq != NULL) { - store->frozen_store->propq = OPENSSL_strdup(propq); - if (store->frozen_store->propq == NULL) { - OPENSSL_free(store->frozen_store); - store->frozen_store = NULL; + store->frozen_propq = OPENSSL_strdup(propq); + if (store->frozen_propq == NULL) return 0; - } } /* TODO: FREEZE: Create frozen caches */ + store->frozen = 1; + return 1; } int ossl_method_store_is_frozen(OSSL_METHOD_STORE *store) { - return store != NULL && store->frozen_store != NULL; + return store != NULL && store->frozen == 1; } -OSSL_FROZEN_METHOD_STORE *ossl_get_frozen_method_store(OSSL_METHOD_STORE *store) +const char *ossl_method_store_frozen_propq(OSSL_METHOD_STORE *store) { - return store != NULL ? store->frozen_store : NULL; -} - -const char *ossl_get_frozen_method_store_propq(OSSL_FROZEN_METHOD_STORE *store) -{ - return (store != NULL && store->propq != NULL) ? store->propq : ""; + if (store == NULL) + return NULL; + return store->frozen_propq; } static void alg_do_one(ALGORITHM *alg, IMPLEMENTATION *impl, @@ -823,7 +810,7 @@ static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid) int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store) { - if (store == NULL || store->frozen_store != NULL || !ossl_property_write_lock(store)) + if (store == NULL || store->frozen == 1 || !ossl_property_write_lock(store)) return 0; ossl_sa_ALGORITHM_doall(store->algs, &impl_cache_flush_alg); store->cache_nelem = 0; @@ -909,6 +896,7 @@ static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store) static EVP_MD andrew_md; static void *andrew_method = NULL; +/* TODO: FREEZE: Replace with actual implementation */ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void **method) { @@ -939,6 +927,7 @@ err: return res; } +/* TODO: FREEZE: Replace with actual implementation */ int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void **method) @@ -981,7 +970,7 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, size_t len; int res = 1; - if (nid <= 0 || store == NULL || prop_query == NULL || store->frozen_store != NULL) + if (nid <= 0 || store == NULL || prop_query == NULL || store->frozen == 1) return 0; if (!ossl_assert(prov != NULL)) diff --git a/include/internal/property.h b/include/internal/property.h index 88fcd70b85..1ff06f6c73 100644 --- a/include/internal/property.h +++ b/include/internal/property.h @@ -15,7 +15,6 @@ #include "internal/cryptlib.h" typedef struct ossl_method_store_st OSSL_METHOD_STORE; -typedef struct ossl_frozen_method_store_st OSSL_FROZEN_METHOD_STORE; typedef struct ossl_property_list_st OSSL_PROPERTY_LIST; typedef enum { @@ -75,8 +74,7 @@ int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, /* Frozen method store related functions */ int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *propq); int ossl_method_store_is_frozen(OSSL_METHOD_STORE *store); -OSSL_FROZEN_METHOD_STORE *ossl_get_frozen_method_store(OSSL_METHOD_STORE *store); -const char *ossl_get_frozen_method_store_propq(OSSL_FROZEN_METHOD_STORE *store); +const char *ossl_method_store_frozen_propq(OSSL_METHOD_STORE *store); /* Get the global properties associate with the specified library context */ OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *ctx, From de60bf9091f7f6c1e274f53cffbb7a1095f8ceaa Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 20 Jan 2026 11:14:48 +0100 Subject: [PATCH 11/28] property: split ossl_method_store_fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit freezing needs access to the IMPLEMENTATION but ossl_method_store_fetch() does not returns it. Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický Reviewed-by: Tomas Mraz MergeDate: Wed Feb 11 07:17:27 2026 (Merged from https://github.com/openssl/openssl/pull/29433) --- crypto/property/property.c | 71 +++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/crypto/property/property.c b/crypto/property/property.c index 1983a3559c..79215a5f40 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -645,32 +645,9 @@ void ossl_method_store_do_all(OSSL_METHOD_STORE *store, } } -/** - * @brief Fetches a method from the method store matching the given properties. - * - * This function searches the method store for an implementation of a specified - * method, identified by its id (nid), and matching the given property query. If - * successful, it returns the method and its associated provider. - * - * @param store Pointer to the OSSL_METHOD_STORE from which to fetch the method. - * Must be non-null. - * @param nid (identifier) of the method to be fetched. Must be > 0 - * @param prop_query String containing the property query to match against. - * @param prov_rw Pointer to the OSSL_PROVIDER to restrict the search to, or - * to receive the matched provider. - * @param method Pointer to receive the fetched method. Must be non-null. - * - * @return 1 if the method is successfully fetched, 0 on failure. - * - * If tracing is enabled, a message is printed indicating the property query and - * the resolved provider. - * - * NOTE: The nid parameter here is _not_ a NID in the sense of the NID_* macros. - * It is a unique internal identifier value. - */ -int ossl_method_store_fetch(OSSL_METHOD_STORE *store, +static int ossl_method_store_fetch_best_impl(OSSL_METHOD_STORE *store, int nid, const char *prop_query, - const OSSL_PROVIDER **prov_rw, void **method) + const OSSL_PROVIDER **prov_rw, IMPLEMENTATION **rbest_impl) { OSSL_PROPERTY_LIST **plp; ALGORITHM *alg; @@ -680,7 +657,7 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store, int ret = 0; int j, best = -1, score, optional; - if (nid <= 0 || method == NULL || store == NULL) + if (nid <= 0 || store == NULL || rbest_impl == NULL) return 0; #if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_AUTOLOAD_CONFIG) @@ -765,8 +742,8 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store, } } fin: - if (ret && ossl_method_up_ref(&best_impl->method)) { - *method = best_impl->method.method; + if (ret) { + *rbest_impl = best_impl; if (prov_rw != NULL) *prov_rw = best_impl->provider; } else { @@ -793,6 +770,44 @@ fin: return ret; } +/** + * @brief Fetches a method from the method store matching the given properties. + * + * This function searches the method store for an implementation of a specified + * method, identified by its id (nid), and matching the given property query. If + * successful, it returns the method and its associated provider. + * + * @param store Pointer to the OSSL_METHOD_STORE from which to fetch the method. + * Must be non-null. + * @param nid (identifier) of the method to be fetched. Must be > 0 + * @param prop_query String containing the property query to match against. + * @param prov_rw Pointer to the OSSL_PROVIDER to restrict the search to, or + * to receive the matched provider. + * @param method Pointer to receive the fetched method. Must be non-null. + * + * @return 1 if the method is successfully fetched, 0 on failure. + * + * If tracing is enabled, a message is printed indicating the property query and + * the resolved provider. + * + * NOTE: The nid parameter here is _not_ a NID in the sense of the NID_* macros. + * It is a unique internal identifier value. + */ +int ossl_method_store_fetch(OSSL_METHOD_STORE *store, + int nid, const char *prop_query, + const OSSL_PROVIDER **prov_rw, void **method) +{ + IMPLEMENTATION *best_impl = NULL; + int ret = 0; + + ret = ossl_method_store_fetch_best_impl(store, nid, prop_query, prov_rw, &best_impl); + if (ret && ossl_method_up_ref(&best_impl->method)) { + *method = best_impl->method.method; + } + + return ret; +} + static void ossl_method_cache_flush_alg(OSSL_METHOD_STORE *store, ALGORITHM *alg) { From ca5f693271acdfbbca031a2a93b2ff994dadb964 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 20 Jan 2026 11:16:58 +0100 Subject: [PATCH 12/28] evp: freezing method store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The METHOD struct contains two new callback. One that can duplicate a given object from cache, and store it into HT, and the second for freeing objects when HT is freed. frozen_algs maps (alg name, propq) -> method. OSSL_LIB_CTX_freeze() freezes the method store associated with the library context. A frozen context will speed up ONLY isolated, uncached algorithm lookups. This is specifically designed to benefit legacy high-throughput applications with worker threads doing isolated computations, without requiring a code restructuring. Existing and new applications are generally recommended to not use this feature and instead structure the application to pre-initialize contexts where possible. This function should only be called from a non-threaded context, before any worker threads have been dispatched. If propq is NULL, it will only speed up method store operations with a NULL property query. If propq is not NULL, it will also speed up method store operations when given that exact property query. Other property queries will go through the normal, slower lookup method. When ctx is not NULL, and propq is NULL, default context is frozen as well. Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický Reviewed-by: Tomas Mraz MergeDate: Wed Feb 11 07:17:29 2026 (Merged from https://github.com/openssl/openssl/pull/29433) --- crypto/context.c | 20 +- crypto/core_fetch.c | 16 +- crypto/encode_decode/decoder_meth.c | 4 +- crypto/encode_decode/encoder_meth.c | 4 +- crypto/evp/asymcipher.c | 2 +- crypto/evp/digest.c | 65 +++++- crypto/evp/evp_enc.c | 2 +- crypto/evp/evp_fetch.c | 200 +++++++++++------- crypto/evp/evp_local.h | 18 +- crypto/evp/evp_rand.c | 2 +- crypto/evp/exchange.c | 2 +- crypto/evp/kdf_meth.c | 2 +- crypto/evp/kem.c | 2 +- crypto/evp/keymgmt_meth.c | 2 +- crypto/evp/mac_meth.c | 2 +- crypto/evp/signature.c | 2 +- crypto/evp/skeymgmt_meth.c | 2 +- crypto/property/property.c | 310 +++++++++++++++++++++------- crypto/store/store_meth.c | 4 +- doc/man3/OSSL_LIB_CTX.pod | 2 +- include/internal/property.h | 23 ++- test/evp_fetch_prov_test.c | 41 +++- test/property_test.c | 18 +- 23 files changed, 527 insertions(+), 218 deletions(-) diff --git a/crypto/context.c b/crypto/context.c index c6e444a9c7..506ad84e2f 100644 --- a/crypto/context.c +++ b/crypto/context.c @@ -461,6 +461,19 @@ static int set_default_context(OSSL_LIB_CTX *defctx) } #endif +int OSSL_LIB_CTX_freeze(OSSL_LIB_CTX *ctx, const char *propq) +{ + OSSL_METHOD_STORE *store; + + store = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_EVP_METHOD_STORE_INDEX); + if (store == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + + return ossl_method_store_freeze_cache(store, propq); +} + OSSL_LIB_CTX *OSSL_LIB_CTX_new(void) { OSSL_LIB_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); @@ -547,13 +560,6 @@ OSSL_LIB_CTX *OSSL_LIB_CTX_set0_default(OSSL_LIB_CTX *libctx) return NULL; } -int OSSL_LIB_CTX_freeze(OSSL_LIB_CTX *ctx, const char *propq) -{ - if ((ctx = ossl_lib_ctx_get_concrete(ctx)) == NULL) - return 0; - return ossl_method_store_freeze(ctx->evp_method_store, propq); -} - void ossl_release_default_drbg_ctx(void) { /* early release of the DRBG in global default libctx */ diff --git a/crypto/core_fetch.c b/crypto/core_fetch.c index 3293f70372..ab8ec59bde 100644 --- a/crypto/core_fetch.c +++ b/crypto/core_fetch.c @@ -162,14 +162,16 @@ void *ossl_method_construct(OSSL_LIB_CTX *libctx, int operation_id, ossl_method_construct_postcondition, &cbdata); - /* If there is a temporary store, try there first */ - if (cbdata.store != NULL) - method = mcm->get(cbdata.store, (const OSSL_PROVIDER **)provider_rw, - mcm_data); + if (mcm->get != NULL) { + /* If there is a temporary store, try there first */ + if (cbdata.store != NULL) + method = mcm->get(cbdata.store, (const OSSL_PROVIDER **)provider_rw, + mcm_data); - /* If no method was found yet, try the global store */ - if (method == NULL) - method = mcm->get(NULL, (const OSSL_PROVIDER **)provider_rw, mcm_data); + /* If no method was found yet, try the global store */ + if (method == NULL) + method = mcm->get(NULL, (const OSSL_PROVIDER **)provider_rw, mcm_data); + } return method; } diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c index 4d460af1c2..466d910b67 100644 --- a/crypto/encode_decode/decoder_meth.c +++ b/crypto/encode_decode/decoder_meth.c @@ -202,7 +202,7 @@ static int put_decoder_in_store(void *store, void *method, return ossl_method_store_add(store, prov, id, propdef, method, ossl_decoder_up_ref, - ossl_decoder_free); + ossl_decoder_free, NULL, NULL); } /* Create and populate a decoder method */ @@ -396,7 +396,7 @@ inner_ossl_decoder_fetch(struct decoder_data_st *methdata, id = ossl_namemap_name2num(namemap, name); if (id != 0) ossl_method_store_cache_set(store, prov, id, propq, method, - up_ref_decoder, free_decoder); + up_ref_decoder, free_decoder, NULL, NULL); } /* diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c index 0bae49fa90..c87935ba65 100644 --- a/crypto/encode_decode/encoder_meth.c +++ b/crypto/encode_decode/encoder_meth.c @@ -202,7 +202,7 @@ static int put_encoder_in_store(void *store, void *method, return ossl_method_store_add(store, prov, id, propdef, method, ossl_encoder_up_ref, - ossl_encoder_free); + ossl_encoder_free, NULL, NULL); } /* Create and populate a encoder method */ @@ -401,7 +401,7 @@ inner_ossl_encoder_fetch(struct encoder_data_st *methdata, if (id == 0) id = ossl_namemap_name2num(namemap, name); ossl_method_store_cache_set(store, prov, id, propq, method, - up_ref_encoder, free_encoder); + up_ref_encoder, free_encoder, NULL, NULL); } /* diff --git a/crypto/evp/asymcipher.c b/crypto/evp/asymcipher.c index 652303483d..f04fb85c47 100644 --- a/crypto/evp/asymcipher.c +++ b/crypto/evp/asymcipher.c @@ -514,7 +514,7 @@ EVP_ASYM_CIPHER *EVP_ASYM_CIPHER_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, return evp_generic_fetch(ctx, OSSL_OP_ASYM_CIPHER, algorithm, properties, evp_asym_cipher_from_algorithm, evp_asym_cipher_up_ref, - evp_asym_cipher_free); + evp_asym_cipher_free, NULL, NULL); } EVP_ASYM_CIPHER *evp_asym_cipher_fetch_from_prov(OSSL_PROVIDER *prov, diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index d721a997ac..40a70f1993 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -20,6 +20,7 @@ #include "internal/common.h" #include "crypto/evp.h" #include "evp_local.h" +#include "openssl/crypto.h" static void cleanup_old_md_data(EVP_MD_CTX *ctx, int force) { @@ -1075,17 +1076,65 @@ static void evp_md_free(void *md) EVP_MD_free(md); } +static void *evp_md_dup_frozen(void *vin) +{ + EVP_MD *in = vin; + EVP_MD *out; + + out = OPENSSL_malloc(sizeof(*out)); + if (out == NULL) + return NULL; + memcpy(out, in, sizeof(*out)); + if (!CRYPTO_NEW_REF(&out->refcnt, 1)) + goto err; + out->type_name = OPENSSL_strdup(in->type_name); + if (out->type_name == NULL) + goto err; + out->origin = EVP_ORIG_FROZEN; + if (!ossl_provider_up_ref(out->prov)) { + OPENSSL_free(out->type_name); + goto err; + } + + return out; +err: + OPENSSL_free(out); + return NULL; +} + +static void evp_md_frozen_free(EVP_MD *md) +{ + int i; + + if (md == NULL || md->origin != EVP_ORIG_FROZEN) + return; + + CRYPTO_DOWN_REF(&md->refcnt, &i); + if (i > 0) + return; + evp_md_free_int(md); +} + +int evp_md_fetch_all(OSSL_LIB_CTX *ctx) +{ + int ret = evp_generic_fetch_all(ctx, OSSL_OP_DIGEST, + evp_md_from_algorithm, + evp_md_up_ref, + evp_md_free, + evp_md_dup_frozen, + (void (*)(void *))evp_md_frozen_free); + return ret; +} + EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) { - EVP_MD *md = NULL; - - if (evp_generic_fetch_frozen(ctx, OSSL_OP_DIGEST, algorithm, properties, - NULL, (void **)&md)) - return md; - - md = evp_generic_fetch(ctx, OSSL_OP_DIGEST, algorithm, properties, - evp_md_from_algorithm, evp_md_up_ref, evp_md_free); + EVP_MD *md = evp_generic_fetch(ctx, OSSL_OP_DIGEST, algorithm, properties, + evp_md_from_algorithm, + evp_md_up_ref, + evp_md_free, + evp_md_dup_frozen, + (void (*)(void *))evp_md_frozen_free); return md; } diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c index 7716a1dbf2..b248198856 100644 --- a/crypto/evp/evp_enc.c +++ b/crypto/evp/evp_enc.c @@ -1994,7 +1994,7 @@ EVP_CIPHER *EVP_CIPHER_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, { EVP_CIPHER *cipher = evp_generic_fetch(ctx, OSSL_OP_CIPHER, algorithm, properties, evp_cipher_from_algorithm, evp_cipher_up_ref, - evp_cipher_free); + evp_cipher_free, NULL, NULL); return cipher; } diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c index 91ea623911..5a20a6f2e2 100644 --- a/crypto/evp/evp_fetch.c +++ b/crypto/evp/evp_fetch.c @@ -39,6 +39,8 @@ struct evp_method_data_st { OSSL_PROVIDER *); int (*refcnt_up_method)(void *method); void (*destruct_method)(void *method); + void *(*dup_method)(void *method); + void (*free_frozen_method)(void *method); }; /* @@ -123,6 +125,37 @@ static uint32_t evp_method_id(int name_id, unsigned int operation_id) | (operation_id & METHOD_ID_OPERATION_MASK)); } +/* + * Reverse of evp_method_id(). + * + * Returns 1 on success and fills in |*name_id| and |*operation_id|. + * Returns 0 on invalid input. + */ +int evp_method_id2name_id_op_id(uint32_t meth_id, int *name_id, + unsigned int *operation_id) +{ + int n; + unsigned int op; + + /* Top bit must be zero (see evp_method_id() comment) */ + if ((meth_id & 0x80000000u) != 0) + return 0; + + op = (unsigned int)(meth_id & METHOD_ID_OPERATION_MASK); + n = (int)((meth_id & METHOD_ID_NAME_MASK) >> METHOD_ID_NAME_OFFSET); + + /* Sanity checks to match evp_method_id() requirements */ + if (op == 0 || op > METHOD_ID_OPERATION_MAX || n > METHOD_ID_NAME_MAX) + return 0; + + if (operation_id != NULL) + *operation_id = op; + if (name_id != NULL) + *name_id = n; + + return 1; +} + static void *get_evp_method_from_store(void *store, const OSSL_PROVIDER **prov, void *data) { @@ -200,7 +233,9 @@ static int put_evp_method_in_store(void *store, void *method, store, names, methdata->operation_id, meth_id, propdef ? propdef : ""); return ossl_method_store_add(store, prov, meth_id, propdef, method, methdata->refcnt_up_method, - methdata->destruct_method); + methdata->destruct_method, + methdata->dup_method, + methdata->free_frozen_method); } /* @@ -247,6 +282,38 @@ static void destruct_evp_method(void *method, void *data) methdata->destruct_method(method); } +static int +inner_evp_generic_fetch_all(struct evp_method_data_st *methdata, + OSSL_PROVIDER *prov) +{ + OSSL_METHOD_STORE *store = get_evp_method_store(methdata->libctx); + + if (store == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + + OSSL_METHOD_CONSTRUCT_METHOD mcm = { + get_tmp_evp_method_store, + reserve_evp_method_store, + unreserve_evp_method_store, + NULL, + put_evp_method_in_store, + construct_evp_method, + destruct_evp_method + }; + + ossl_method_construct(methdata->libctx, methdata->operation_id, + &prov, 0 /* !force_cache */, + &mcm, methdata); + if (methdata->flag_construct_error_occurred != 0) { + ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); + return 0; + } + + return 1; +} + static void * inner_evp_generic_fetch(struct evp_method_data_st *methdata, OSSL_PROVIDER *prov, int operation_id, @@ -255,7 +322,9 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov), int (*up_ref_method)(void *), - void (*free_method)(void *)) + void (*free_method)(void *), + void *(*dup_method)(void *), + void (*dup_free_method)(void *)) { OSSL_METHOD_STORE *store = get_evp_method_store(methdata->libctx); OSSL_NAMEMAP *namemap = ossl_namemap_stored(methdata->libctx); @@ -288,6 +357,15 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, return NULL; } + if (ossl_method_store_is_frozen(store)) { + const char *store_propq = ossl_method_store_frozen_propq(store); + + if (*propq == '\0' || strcmp(store_propq, propq) == 0) { + ossl_frozen_method_store_cache_get(store, name, propq, &method); + return method; + } + } + /* If we haven't received a name id yet, try to get one for the name */ name_id = name != NULL ? ossl_namemap_name2num(namemap, name) : 0; @@ -329,6 +407,8 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, methdata->method_from_algorithm = new_method; methdata->refcnt_up_method = up_ref_method; methdata->destruct_method = free_method; + methdata->free_frozen_method = dup_free_method; + methdata->dup_method = dup_method; methdata->flag_construct_error_occurred = 0; if ((method = ossl_method_construct(methdata->libctx, operation_id, &prov, 0 /* !force_cache */, @@ -357,7 +437,10 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, meth_id = evp_method_id(name_id, operation_id); if (meth_id != 0) ossl_method_store_cache_set(store, prov, meth_id, propq, - method, up_ref_method, free_method); + method, + up_ref_method, + free_method, + dup_method, dup_free_method); } } @@ -388,84 +471,15 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, return method; } -/* TODO: FREEZE: Replace with actual implementation */ -/* - * Returns 1 if method store is frozen AND prop query is equal to frozen prop - * query. Only sets METHOD if found. - */ -int evp_generic_fetch_frozen(OSSL_LIB_CTX *libctx, int operation_id, - const char *name, const char *properties, - OSSL_PROVIDER *prov, void **method) -{ - OSSL_METHOD_STORE *store = get_evp_method_store(libctx); - const char *store_propq; - OSSL_NAMEMAP *namemap; - uint32_t meth_id; -#ifdef FIPS_MODULE - /* - * The FIPS provider has its own internal library context where only it - * is loaded. Consequently, property queries aren't relevant because - * there is only one fetchable algorithm and it is assumed that the - * FIPS-ness is handled by the using algorithm. - */ - const char *const propq = ""; -#else - const char *const propq = properties != NULL ? properties : ""; -#endif /* FIPS_MODULE */ - - if (store == NULL) { - ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_INVALID_ARGUMENT); - return 0; - } - - /* - * If there's ever an operation_id == 0 passed, we have an internal - * programming error. - */ - if (!ossl_assert(operation_id > 0)) { - ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); - return 0; - } - - /* Return 0 if not frozen or prop query is different than frozen prop query */ - if (!ossl_method_store_is_frozen(store)) - return 0; - if (strlen(propq) != 0 - && (store_propq = ossl_method_store_frozen_propq(store)) != NULL - && strcmp(propq, store_propq) != 0) { - return 0; - } - - /* If we haven't received a name id yet, try to get one for the name */ - namemap = ossl_namemap_stored(libctx); - if (namemap == NULL) { - ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_INVALID_ARGUMENT); - return 0; - } - meth_id = name != NULL ? ossl_namemap_name2num(namemap, name) : 0; - - /* - * If we have a name id, calculate a method id with evp_method_id(). - * - * evp_method_id returns 0 if we have too many operations (more than - * about 2^8) or too many names (more than about 2^24). - * For all intents and purposes, this is an internal error. - */ - if (meth_id != 0 && (meth_id = evp_method_id(meth_id, operation_id)) == 0) { - ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); - return 0; - } - - return ossl_frozen_method_store_cache_get(store, prov, meth_id, propq, method); -} - void *evp_generic_fetch(OSSL_LIB_CTX *libctx, int operation_id, const char *name, const char *properties, void *(*new_method)(int name_id, const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov), int (*up_ref_method)(void *), - void (*free_method)(void *)) + void (*free_method)(void *), + void *(*dup_method)(void *), + void (*dup_free_method)(void *)) { struct evp_method_data_st methdata; void *method; @@ -474,11 +488,41 @@ void *evp_generic_fetch(OSSL_LIB_CTX *libctx, int operation_id, methdata.tmp_store = NULL; method = inner_evp_generic_fetch(&methdata, NULL, operation_id, name, properties, - new_method, up_ref_method, free_method); + new_method, + up_ref_method, + free_method, + dup_method, + dup_free_method); dealloc_tmp_evp_method_store(methdata.tmp_store); return method; } +int evp_generic_fetch_all(OSSL_LIB_CTX *libctx, int operation_id, + void *(*new_method)(int name_id, + const OSSL_ALGORITHM *algodef, + OSSL_PROVIDER *prov), + int (*up_ref_method)(void *), + void (*free_method)(void *), + void *(*dup_method)(void *), + void (*dup_free_method)(void *)) +{ + int ret; + struct evp_method_data_st methdata = { + .libctx = libctx, + .name_id = -1, + .method_from_algorithm = new_method, + .refcnt_up_method = up_ref_method, + .destruct_method = free_method, + .dup_method = dup_method, + .free_frozen_method = dup_free_method, + .operation_id = operation_id, + }; + + ret = inner_evp_generic_fetch_all(&methdata, NULL); + dealloc_tmp_evp_method_store(methdata.tmp_store); + return ret; +} + /* * evp_generic_fetch_from_prov() is special, and only returns methods from * the given provider. @@ -500,7 +544,7 @@ void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id, methdata.tmp_store = NULL; method = inner_evp_generic_fetch(&methdata, prov, operation_id, name, properties, - new_method, up_ref_method, free_method); + new_method, up_ref_method, free_method, NULL, NULL); dealloc_tmp_evp_method_store(methdata.tmp_store); return method; } @@ -713,7 +757,7 @@ void evp_generic_do_all(OSSL_LIB_CTX *libctx, int operation_id, methdata.libctx = libctx; methdata.tmp_store = NULL; (void)inner_evp_generic_fetch(&methdata, NULL, operation_id, NULL, NULL, - new_method, up_ref_method, free_method); + new_method, up_ref_method, free_method, NULL, NULL); data.operation_id = operation_id; data.user_fn = user_fn; diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 19bae81cbb..9925378261 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -301,10 +301,17 @@ void *evp_generic_fetch(OSSL_LIB_CTX *ctx, int operation_id, const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov), int (*up_ref_method)(void *), - void (*free_method)(void *)); -int evp_generic_fetch_frozen(OSSL_LIB_CTX *libctx, int operation_id, - const char *name, const char *properties, - OSSL_PROVIDER *prov, void **method); + void (*free_method)(void *), + void *(*dup_method)(void *), + void (*dup_free_method)(void *)); +int evp_generic_fetch_all(OSSL_LIB_CTX *ctx, int operation_id, + void *(*new_method)(int name_id, + const OSSL_ALGORITHM *algodef, + OSSL_PROVIDER *prov), + int (*up_ref_method)(void *), + void (*free_method)(void *), + void *(*dup_method)(void *), + void (*dup_free_method)(void *)); void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id, const char *name, const char *properties, void *(*new_method)(int name_id, @@ -413,3 +420,6 @@ int evp_names_do_all(OSSL_PROVIDER *prov, int number, void (*fn)(const char *name, void *data), void *data); int evp_cipher_cache_constants(EVP_CIPHER *cipher); +int evp_method_id2name_id_op_id(uint32_t meth_id, int *name_id, + unsigned int *operation_id); +int evp_md_fetch_all(OSSL_LIB_CTX *ctx); diff --git a/crypto/evp/evp_rand.c b/crypto/evp/evp_rand.c index 689243ef10..dfa3943253 100644 --- a/crypto/evp/evp_rand.c +++ b/crypto/evp/evp_rand.c @@ -284,7 +284,7 @@ EVP_RAND *EVP_RAND_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, { return evp_generic_fetch(libctx, OSSL_OP_RAND, algorithm, properties, evp_rand_from_algorithm, evp_rand_up_ref, - evp_rand_free); + evp_rand_free, NULL, NULL); } int EVP_RAND_up_ref(EVP_RAND *rand) diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c index de2c966638..37d714a900 100644 --- a/crypto/evp/exchange.c +++ b/crypto/evp/exchange.c @@ -192,7 +192,7 @@ EVP_KEYEXCH *EVP_KEYEXCH_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, return evp_generic_fetch(ctx, OSSL_OP_KEYEXCH, algorithm, properties, evp_keyexch_from_algorithm, evp_keyexch_up_ref, - evp_keyexch_free); + evp_keyexch_free, NULL, NULL); } EVP_KEYEXCH *evp_keyexch_fetch_from_prov(OSSL_PROVIDER *prov, diff --git a/crypto/evp/kdf_meth.c b/crypto/evp/kdf_meth.c index 5ac6a94013..3036ba75ec 100644 --- a/crypto/evp/kdf_meth.c +++ b/crypto/evp/kdf_meth.c @@ -171,7 +171,7 @@ EVP_KDF *EVP_KDF_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, { return evp_generic_fetch(libctx, OSSL_OP_KDF, algorithm, properties, evp_kdf_from_algorithm, evp_kdf_up_ref, - evp_kdf_free); + evp_kdf_free, NULL, NULL); } int EVP_KDF_up_ref(EVP_KDF *kdf) diff --git a/crypto/evp/kem.c b/crypto/evp/kem.c index 6c36164760..1d79e79dc7 100644 --- a/crypto/evp/kem.c +++ b/crypto/evp/kem.c @@ -463,7 +463,7 @@ EVP_KEM *EVP_KEM_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, return evp_generic_fetch(ctx, OSSL_OP_KEM, algorithm, properties, evp_kem_from_algorithm, evp_kem_up_ref, - evp_kem_free); + evp_kem_free, NULL, NULL); } EVP_KEM *evp_kem_fetch_from_prov(OSSL_PROVIDER *prov, const char *algorithm, diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c index 465e669a71..577423e76f 100644 --- a/crypto/evp/keymgmt_meth.c +++ b/crypto/evp/keymgmt_meth.c @@ -283,7 +283,7 @@ EVP_KEYMGMT *EVP_KEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, return evp_generic_fetch(ctx, OSSL_OP_KEYMGMT, algorithm, properties, keymgmt_from_algorithm, evp_keymgmt_up_ref, - evp_keymgmt_free); + evp_keymgmt_free, NULL, NULL); } int EVP_KEYMGMT_up_ref(EVP_KEYMGMT *keymgmt) diff --git a/crypto/evp/mac_meth.c b/crypto/evp/mac_meth.c index ee29f58b3e..0d3c48baea 100644 --- a/crypto/evp/mac_meth.c +++ b/crypto/evp/mac_meth.c @@ -177,7 +177,7 @@ EVP_MAC *EVP_MAC_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, { return evp_generic_fetch(libctx, OSSL_OP_MAC, algorithm, properties, evp_mac_from_algorithm, evp_mac_up_ref, - evp_mac_free); + evp_mac_free, NULL, NULL); } int EVP_MAC_up_ref(EVP_MAC *mac) diff --git a/crypto/evp/signature.c b/crypto/evp/signature.c index c7fbb6e706..a4a4302712 100644 --- a/crypto/evp/signature.c +++ b/crypto/evp/signature.c @@ -486,7 +486,7 @@ EVP_SIGNATURE *EVP_SIGNATURE_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, return evp_generic_fetch(ctx, OSSL_OP_SIGNATURE, algorithm, properties, evp_signature_from_algorithm, evp_signature_up_ref, - evp_signature_free); + evp_signature_free, NULL, NULL); } EVP_SIGNATURE *evp_signature_fetch_from_prov(OSSL_PROVIDER *prov, diff --git a/crypto/evp/skeymgmt_meth.c b/crypto/evp/skeymgmt_meth.c index 46ea0c5443..ab838aee77 100644 --- a/crypto/evp/skeymgmt_meth.c +++ b/crypto/evp/skeymgmt_meth.c @@ -145,7 +145,7 @@ EVP_SKEYMGMT *EVP_SKEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, return evp_generic_fetch(ctx, OSSL_OP_SKEYMGMT, algorithm, properties, skeymgmt_from_algorithm, (int (*)(void *))EVP_SKEYMGMT_up_ref, - (void (*)(void *))EVP_SKEYMGMT_free); + (void (*)(void *))EVP_SKEYMGMT_free, NULL, NULL); } int EVP_SKEYMGMT_up_ref(EVP_SKEYMGMT *skeymgmt) diff --git a/crypto/property/property.c b/crypto/property/property.c index 79215a5f40..ae6409a1b5 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -12,20 +12,20 @@ #include #include #include -#include "internal/core.h" #include "internal/property.h" #include "internal/provider.h" +#include "internal/core.h" #include "internal/tsan_assist.h" -#include "crypto/ctype.h" +#include "internal/hashtable.h" #include #include #include -#include "internal/thread_once.h" -#include "crypto/lhash.h" #include "crypto/sparse_array.h" #include "property_local.h" #include "crypto/context.h" #include "crypto/evp.h" +#include "crypto/evp/evp_local.h" +#include "internal/namemap.h" /* * The number of elements in the query cache before we initiate a flush. @@ -38,6 +38,8 @@ typedef struct { void *method; int (*up_ref)(void *); void (*free)(void *); + void *(*dup)(void *); + void (*free_dup)(void *); } METHOD; typedef struct { @@ -66,6 +68,9 @@ typedef struct { struct ossl_method_store_st { OSSL_LIB_CTX *ctx; SPARSE_ARRAY_OF(ALGORITHM) * algs; + + /* (nid, propq) -> method */ + HT *frozen_algs; /* * Lock to protect the |algs| array from concurrent writing, when * individual implementations or queries are inserted. This is used @@ -106,6 +111,12 @@ DEFINE_SPARSE_ARRAY_OF(ALGORITHM); DEFINE_STACK_OF(ALGORITHM) +HT_START_KEY_DEFN(frozen_cache_key) +HT_DEF_KEY_FIELD_CHAR_ARRAY(name, 64) +/* TODO(FREEZE): allow variable length propq */ +HT_DEF_KEY_FIELD_CHAR_ARRAY(propq, 64) +HT_END_KEY_DEFN(FROZEN_CACHE_KEY) + typedef struct ossl_global_properties_st { OSSL_PROPERTY_LIST *list; #ifndef FIPS_MODULE @@ -176,6 +187,30 @@ static void ossl_method_free(METHOD *method) (*method->free)(method->method); } +static void ossl_method_free_frozen(METHOD *method) +{ + (*method->free_dup)(method->method); +} + +static METHOD *ossl_method_dup(METHOD *method) +{ + METHOD *dup; + + dup = OPENSSL_zalloc(sizeof(*dup)); + if (dup == NULL) + return NULL; + + memcpy(dup, method, sizeof(*dup)); + + dup->method = (*method->dup)(method->method); + if (dup->method == NULL) { + OPENSSL_free(dup); + return NULL; + } + + return dup; +} + static __owur int ossl_property_read_lock(OSSL_METHOD_STORE *p) { return p != NULL ? CRYPTO_THREAD_read_lock(p->lock) : 0; @@ -269,6 +304,8 @@ void ossl_method_store_free(OSSL_METHOD_STORE *store) if (store != NULL) { if (store->algs != NULL) ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_cleanup, store); + if (store->frozen_algs != NULL) + ossl_ht_free(store->frozen_algs); ossl_sa_ALGORITHM_free(store->algs); CRYPTO_THREAD_lock_free(store->lock); CRYPTO_THREAD_lock_free(store->biglock); @@ -325,7 +362,9 @@ static int ossl_method_store_insert(OSSL_METHOD_STORE *store, ALGORITHM *alg) int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov, int nid, const char *properties, void *method, int (*method_up_ref)(void *), - void (*method_destruct)(void *)) + void (*method_destruct)(void *), + void *(*method_dup)(void *), + void (*free_frozen)(void *)) { ALGORITHM *alg = NULL; IMPLEMENTATION *impl; @@ -348,6 +387,8 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov, impl->method.method = method; impl->method.up_ref = method_up_ref; impl->method.free = method_destruct; + impl->method.dup = method_dup; + impl->method.free_dup = free_frozen; if (!ossl_method_up_ref(&impl->method)) { OPENSSL_free(impl); return 0; @@ -557,34 +598,6 @@ int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, return 1; } -int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *propq) -{ - if (store == NULL || store->frozen == 1) - return 0; - - if (propq != NULL) { - store->frozen_propq = OPENSSL_strdup(propq); - if (store->frozen_propq == NULL) - return 0; - } - /* TODO: FREEZE: Create frozen caches */ - store->frozen = 1; - - return 1; -} - -int ossl_method_store_is_frozen(OSSL_METHOD_STORE *store) -{ - return store != NULL && store->frozen == 1; -} - -const char *ossl_method_store_frozen_propq(OSSL_METHOD_STORE *store) -{ - if (store == NULL) - return NULL; - return store->frozen_propq; -} - static void alg_do_one(ALGORITHM *alg, IMPLEMENTATION *impl, void (*fn)(int id, void *method, void *fnarg), void *fnarg) @@ -800,10 +813,14 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store, IMPLEMENTATION *best_impl = NULL; int ret = 0; + if (nid <= 0 || store == NULL || method == NULL) + return 0; + ret = ossl_method_store_fetch_best_impl(store, nid, prop_query, prov_rw, &best_impl); - if (ret && ossl_method_up_ref(&best_impl->method)) { + if (ret && ossl_method_up_ref(&best_impl->method)) *method = best_impl->method.method; - } + else + ret = 0; return ret; } @@ -907,11 +924,6 @@ static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store) tsan_add(&global_seed, state.seed); } -/* TODO: FREEZE: Remove these and replace with actual implementation */ -static EVP_MD andrew_md; -static void *andrew_method = NULL; - -/* TODO: FREEZE: Replace with actual implementation */ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void **method) { @@ -942,50 +954,19 @@ err: return res; } -/* TODO: FREEZE: Replace with actual implementation */ -int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, - OSSL_PROVIDER *prov, int nid, - const char *prop_query, void **method) -{ - /* - * Query triplet (nid, prov, prop_query) from frozen store with no fallback. - */ - - /* - * TODO: FREEZE: Replace with actual implementation - * For now, just store & fetch from global variables - */ - EVP_MD *temp_method; - - if (andrew_method == NULL) { - if (!ossl_method_store_cache_get(store, prov, nid, prop_query, (void **)&temp_method)) - return 0; - - memcpy(&andrew_md, temp_method, sizeof(andrew_md)); - andrew_md.origin = EVP_ORIG_FROZEN; - andrew_method = &andrew_md; - EVP_MD_free(temp_method); - } - - if (andrew_method != NULL) { - *method = andrew_method; - return 1; - } - - return 0; -} - int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void *method, int (*method_up_ref)(void *), - void (*method_destruct)(void *)) + void (*method_destruct)(void *), + void *(*method_dup)(void *), + void (*free_dup)(void *)) { QUERY elem, *old, *p = NULL; ALGORITHM *alg; size_t len; int res = 1; - if (nid <= 0 || store == NULL || prop_query == NULL || store->frozen == 1) + if (nid <= 0 || store == NULL || prop_query == NULL) return 0; if (!ossl_assert(prov != NULL)) @@ -1015,6 +996,8 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, p->method.method = method; p->method.up_ref = method_up_ref; p->method.free = method_destruct; + p->method.dup = method_dup; + p->method.free_dup = free_dup; if (!ossl_method_up_ref(&p->method)) goto err; memcpy((char *)p->query, prop_query, len + 1); @@ -1036,3 +1019,180 @@ end: ossl_property_unlock(store); return res; } + +int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, + const char *alg_name, const char *prop_query, void **method) +{ + FROZEN_CACHE_KEY key; + HT_VALUE *val; + + if (store == NULL + || alg_name == NULL + || prop_query == NULL + || store->frozen_algs == NULL) + return 0; + + HT_INIT_KEY(&key); + HT_SET_KEY_STRING_CASE(&key, name, alg_name); + HT_SET_KEY_STRING(&key, propq, prop_query); + + val = ossl_ht_get(store->frozen_algs, TO_HT_KEY(&key)); + if (val == NULL) + return 1; + *method = ((METHOD *)val->value)->method; + + return 1; +} + +struct alg_freeze_st { + OSSL_METHOD_STORE *store; + int nid; + + int ret; +}; + +static void frozen_cache_free(HT_VALUE *val) +{ + METHOD *method = (METHOD *)val->value; + if (method == NULL || method->free_dup == NULL) + return; + ossl_method_free_frozen(method); + + OPENSSL_free(method); +} + +static int freeze_alg(OSSL_METHOD_STORE *store, ALGORITHM *alg, + const char *propq, const char *alg_name) +{ + int ret = 0; + IMPLEMENTATION *best_impl = NULL; + FROZEN_CACHE_KEY key; + HT_VALUE val = { 0 }; + const OSSL_PROVIDER *prov = NULL; + + HT_INIT_KEY(&key); + HT_SET_KEY_STRING_CASE(&key, name, alg_name); + HT_SET_KEY_STRING(&key, propq, propq); + + if (ossl_ht_get(store->frozen_algs, TO_HT_KEY(&key)) != NULL) + return 1; + + ret = ossl_method_store_fetch_best_impl(store, + alg->nid, propq, &prov, &best_impl); + if (ret == 0 || best_impl == NULL + || best_impl->method.dup == NULL + || best_impl->method.free_dup == NULL) + return 1; + + val.value = ossl_method_dup(&best_impl->method); + if (val.value == NULL) + return ret; + + ret = ossl_ht_insert(store->frozen_algs, TO_HT_KEY(&key), &val, NULL); + if (ret <= 0) { + frozen_cache_free(&val); + return 0; + } + + return 1; +} + +static void alg_freeze(ossl_uintmax_t idx, ALGORITHM *alg, void *arg) +{ + struct alg_freeze_st *af = arg; + OSSL_NAMEMAP *nm = ossl_namemap_stored(af->store->ctx); + int name_id; + const char *name; + int i = 0; + + if (alg == NULL + || !evp_method_id2name_id_op_id(alg->nid, &name_id, NULL)) { + af->ret = 0; + return; + } + + for (;;) { + name = ossl_namemap_num2name(nm, name_id, i++); + if (name == NULL) + break; + if (*af->store->frozen_propq != '\0') { + af->ret = freeze_alg(af->store, alg, af->store->frozen_propq, name); + if (!af->ret) + return; + } + af->ret = freeze_alg(af->store, alg, "", name); + if (!af->ret) + return; + } +} + +/** + * @brief Freezes the method store's cache into a hash table. + * + * This function creates a frozen copy of the method store's cache, storing it + * in a hash table for efficient retrieval. Each entry in the cache is duplicated + * to ensure that the frozen cache is independent of the original store. + * + * @param store Pointer to the OSSL_METHOD_STORE to be frozen. + * + * @return 1 on success, 0 on failure. + * + * If the store is NULL or if any duplication fails, the function returns 0. + * On success, it returns 1 after populating the frozen cache. + */ +int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq) +{ + HT_CONFIG ht_conf = { + .ht_free_fn = frozen_cache_free, + .init_neighborhoods = store->cache_nelem, + .collision_check = 1, + .no_rcu = 1, + }; + struct alg_freeze_st af = { + .store = store, + .ret = 1, + }; + propq = propq != NULL ? propq : ""; + + if (store == NULL || store->frozen == 1) + return 0; + + store->frozen_propq = OPENSSL_strdup(propq); + if (store->frozen_propq == NULL) + goto err; + + store->frozen_algs = ossl_ht_new(&ht_conf); + if (store->frozen_algs == NULL) + goto err; + + if (evp_md_fetch_all(store->ctx) <= 0) + goto err; + + ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_freeze, &af); + if (af.ret <= 0) + goto err; + + store->frozen = 1; + + return 1; + +err: + OPENSSL_free(store->frozen_propq); + ossl_ht_free(store->frozen_algs); + store->frozen_algs = NULL; + store->frozen_propq = NULL; + + return 0; +} + +int ossl_method_store_is_frozen(OSSL_METHOD_STORE *store) +{ + return store != NULL && store->frozen == 1; +} + +const char *ossl_method_store_frozen_propq(OSSL_METHOD_STORE *store) +{ + if (store == NULL) + return NULL; + return store->frozen_propq; +} diff --git a/crypto/store/store_meth.c b/crypto/store/store_meth.c index 3b60247f41..63f41741a8 100644 --- a/crypto/store/store_meth.c +++ b/crypto/store/store_meth.c @@ -172,7 +172,7 @@ static int put_loader_in_store(void *store, void *method, return 0; return ossl_method_store_add(store, prov, id, propdef, method, - up_ref_loader, free_loader); + up_ref_loader, free_loader, NULL, NULL); } static void *loader_from_algorithm(int scheme_id, const OSSL_ALGORITHM *algodef, @@ -339,7 +339,7 @@ inner_loader_fetch(struct loader_data_st *methdata, if (id == 0) id = ossl_namemap_name2num(namemap, scheme); ossl_method_store_cache_set(store, prov, id, propq, method, - up_ref_loader, free_loader); + up_ref_loader, free_loader, NULL, NULL); } /* diff --git a/doc/man3/OSSL_LIB_CTX.pod b/doc/man3/OSSL_LIB_CTX.pod index 3dfb319256..0a952cb5a6 100644 --- a/doc/man3/OSSL_LIB_CTX.pod +++ b/doc/man3/OSSL_LIB_CTX.pod @@ -25,7 +25,7 @@ OSSL_LIB_CTX_freeze OSSL_LIB_CTX *OSSL_LIB_CTX_get0_global_default(void); OSSL_LIB_CTX *OSSL_LIB_CTX_set0_default(OSSL_LIB_CTX *ctx); void *OSSL_LIB_CTX_get_data(OSSL_LIB_CTX *ctx, int index); - void *OSSL_LIB_CTX_freeze(OSSL_LIB_CTX *ctx, const char* propq); + void *OSSL_LIB_CTX_freeze(OSSL_LIB_CTX *ctx, const char *propq); =head1 DESCRIPTION diff --git a/include/internal/property.h b/include/internal/property.h index 1ff06f6c73..2980ddd261 100644 --- a/include/internal/property.h +++ b/include/internal/property.h @@ -59,7 +59,9 @@ int ossl_method_unlock_store(OSSL_METHOD_STORE *store); int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov, int nid, const char *properties, void *method, int (*method_up_ref)(void *), - void (*method_destruct)(void *)); + void (*method_destruct)(void *), + void *(*method_dup)(void *), + void (*free_frozen)(void *)); int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid, const void *method); void ossl_method_store_do_all(OSSL_METHOD_STORE *store, @@ -71,11 +73,6 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store, int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov); -/* Frozen method store related functions */ -int ossl_method_store_freeze(OSSL_METHOD_STORE *store, const char *propq); -int ossl_method_store_is_frozen(OSSL_METHOD_STORE *store); -const char *ossl_method_store_frozen_propq(OSSL_METHOD_STORE *store); - /* Get the global properties associate with the specified library context */ OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *ctx, int loadconfig); @@ -83,13 +80,12 @@ OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *ctx, /* property query cache functions */ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void **result); -int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, - OSSL_PROVIDER *prov, int nid, - const char *prop_query, void **result); int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov, int nid, const char *prop_query, void *result, int (*method_up_ref)(void *), - void (*method_destruct)(void *)); + void (*method_destruct)(void *), + void *(*method_dup)(void *), + void (*free_frozen)(void *)); __owur int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store); @@ -104,4 +100,11 @@ size_t ossl_property_list_to_string(OSSL_LIB_CTX *ctx, int ossl_global_properties_no_mirrored(OSSL_LIB_CTX *libctx); void ossl_global_properties_stop_mirroring(OSSL_LIB_CTX *libctx); +int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq); +int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, + const char *name, const char *prop_query, void **result); + +int ossl_method_store_is_frozen(OSSL_METHOD_STORE *store); +const char *ossl_method_store_frozen_propq(OSSL_METHOD_STORE *store); + #endif diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index 3698b3f56e..ba215c0814 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -222,11 +222,24 @@ err: static int test_EVP_MD_fetch_freeze(void) { +#if defined(OPENSSL_NO_CACHED_FETCH) + /* + * Test does not make sense if cached fetch is disabled. + * There's nothing to freeze, and test will fail. + */ + return 1; +#endif + EVP_MD *md = NULL; int ret = 0; - OSSL_LIB_CTX *ctx = OSSL_LIB_CTX_new(); + OSSL_LIB_CTX *ctx = NULL; + OSSL_LIB_CTX *ctx2 = OSSL_LIB_CTX_new(); + OSSL_PROVIDER *prov[2] = { NULL, NULL }; - if (!TEST_ptr(ctx) + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(ctx2) || !TEST_ptr(md = EVP_MD_fetch(ctx, "SHA256", NULL)) || !TEST_true(test_md(md)) || !TEST_int_ne(md->origin, EVP_ORIG_FROZEN)) @@ -237,6 +250,12 @@ static int test_EVP_MD_fetch_freeze(void) if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) || !TEST_ptr(md = EVP_MD_fetch(ctx, "SHA256", NULL)) || !TEST_true(test_md(md)) + || !TEST_int_eq(md->origin, EVP_ORIG_FROZEN) + || !TEST_ptr(md = EVP_MD_fetch(ctx, "SHA-256", NULL)) + || !TEST_true(test_md(md)) + || !TEST_int_eq(md->origin, EVP_ORIG_FROZEN) + || !TEST_ptr(md = EVP_MD_fetch(ctx, "2.16.840.1.101.3.4.2.1", NULL)) + || !TEST_true(test_md(md)) || !TEST_int_eq(md->origin, EVP_ORIG_FROZEN)) goto err; /* Technically, frozen version doesn't need to be freed */ @@ -248,14 +267,30 @@ static int test_EVP_MD_fetch_freeze(void) goto err; EVP_MD_free(md); + /* Falls back to slow path */ if (!TEST_ptr(md = EVP_MD_fetch(ctx, "SHA256", "?provider=default")) || !TEST_true(test_md(md)) || !TEST_int_ne(md->origin, EVP_ORIG_FROZEN)) goto err; + EVP_MD_free(md); + + if (!TEST_ptr(md = EVP_MD_fetch(ctx, "SHA1", NULL)) + || !TEST_int_eq(md->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_MD_free(md); + + if (!TEST_ptr(md = EVP_MD_fetch(ctx2, "SHA1", "?fips=true")) + || !TEST_int_ne(md->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_MD_free(md); + if (!TEST_ptr(md = EVP_MD_fetch(ctx2, "SHA1", NULL)) + || !TEST_int_ne(md->origin, EVP_ORIG_FROZEN)) + goto err; ret = 1; err: - OSSL_LIB_CTX_free(ctx); + unload_providers(&ctx, prov); + OSSL_LIB_CTX_free(ctx2); EVP_MD_free(md); return ret; } diff --git a/test/property_test.c b/test/property_test.c index 96a5d492f6..a5ae9e9d21 100644 --- a/test/property_test.c +++ b/test/property_test.c @@ -392,7 +392,7 @@ static int test_register_deregister(void) for (i = 0; i < OSSL_NELEM(impls); i++) if (!TEST_true(ossl_method_store_add(store, &prov, impls[i].nid, impls[i].prop, impls[i].impl, - &up_ref, &down_ref))) { + &up_ref, &down_ref, NULL, NULL))) { TEST_note("iteration %zd", i + 1); goto err; } @@ -429,11 +429,11 @@ static int test_freeze_flag(void) if (!TEST_ptr(store = ossl_method_store_new(NULL)) || !TEST_true(add_property_names("position", NULL)) - || !TEST_true(ossl_method_store_add(store, &prov, nid, prop, impl, &up_ref, &down_ref)) + || !TEST_true(ossl_method_store_add(store, &prov, nid, prop, impl, &up_ref, &down_ref, NULL, NULL)) || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) || !TEST_ptr_eq(&prov, fetched_prov) || !TEST_str_eq((char *)fetched_meth, impl) - || !TEST_true(ossl_method_store_freeze(store, NULL)) + || !TEST_true(ossl_method_store_freeze_cache(store, NULL)) || !TEST_false(ossl_method_store_remove(store, nid, impl)) || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) || !TEST_ptr_eq(&prov, fetched_prov) @@ -442,8 +442,8 @@ static int test_freeze_flag(void) || !TEST_true(ossl_method_store_fetch(store, nid, prop, &fetched_prov, &fetched_meth)) || !TEST_ptr_eq(&prov, fetched_prov) || !TEST_str_eq((char *)fetched_meth, impl) - || !TEST_false(ossl_method_store_add(store, &prov, nid, prop2, impl2, &up_ref, &down_ref)) - || !TEST_false(ossl_method_store_freeze(store, NULL))) + || !TEST_false(ossl_method_store_add(store, &prov, nid, prop2, impl2, &up_ref, &down_ref, NULL, NULL)) + || !TEST_false(ossl_method_store_freeze_cache(store, NULL))) goto err; ret = 1; @@ -502,7 +502,7 @@ static int test_property(void) if (!TEST_true(ossl_method_store_add(store, *impls[i].prov, impls[i].nid, impls[i].prop, impls[i].impl, - &up_ref, &down_ref))) { + &up_ref, &down_ref, NULL, NULL))) { TEST_note("iteration %zd", i + 1); goto err; } @@ -613,13 +613,13 @@ static int test_query_cache_stochastic(void) v[i] = 2 * i; BIO_snprintf(buf, sizeof(buf), "n=%d\n", i); if (!TEST_true(ossl_method_store_add(store, &prov, i, buf, "abc", - &up_ref, &down_ref)) + &up_ref, &down_ref, NULL, NULL)) || !TEST_true(ossl_method_store_cache_set(store, &prov, i, buf, v + i, - &up_ref, &down_ref)) + &up_ref, &down_ref, NULL, NULL)) || !TEST_true(ossl_method_store_cache_set(store, &prov, i, "n=1234", "miss", - &up_ref, &down_ref))) { + &up_ref, &down_ref, NULL, NULL))) { TEST_note("iteration %d", i); goto err; } From 901e728fdad06660f9cc0b6efe21264f0bfbad3d Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 11 Feb 2026 09:38:53 +0100 Subject: [PATCH 13/28] EVP_CIPHER: add required freezing callbacks Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Wed Feb 18 10:13:43 2026 (Merged from https://github.com/openssl/openssl/pull/30002) --- crypto/evp/evp_enc.c | 52 ++++++++++++++++++++++++++++++++++++++ crypto/evp/evp_local.h | 1 + crypto/property/property.c | 2 ++ 3 files changed, 55 insertions(+) diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c index b248198856..f5267014ad 100644 --- a/crypto/evp/evp_enc.c +++ b/crypto/evp/evp_enc.c @@ -1989,6 +1989,58 @@ static void evp_cipher_free(void *cipher) EVP_CIPHER_free(cipher); } +static void *evp_cipher_dup_frozen(void *vin) +{ + EVP_CIPHER *in = vin; + EVP_CIPHER *out; + + out = OPENSSL_malloc(sizeof(*out)); + if (out == NULL) + return NULL; + memcpy(out, in, sizeof(*out)); + if (!CRYPTO_NEW_REF(&out->refcnt, 1)) + goto err; + out->type_name = OPENSSL_strdup(in->type_name); + if (out->type_name == NULL) + goto err; + out->origin = EVP_ORIG_FROZEN; + if (!ossl_provider_up_ref(out->prov)) { + OPENSSL_free(out->type_name); + goto err; + } + return out; +err: + OPENSSL_free(out); + return NULL; +} + +static void evp_cipher_frozen_free(void *vin) +{ + EVP_CIPHER *cipher = vin; + int i; + + if (cipher == NULL || cipher->origin != EVP_ORIG_FROZEN) + return; + + CRYPTO_DOWN_REF(&cipher->refcnt, &i); + if (i > 0) + return; + evp_cipher_free_int(cipher); +} + +int evp_cipher_fetch_all(OSSL_LIB_CTX *ctx) +{ + int ret = evp_generic_fetch_all(ctx, + OSSL_OP_CIPHER, + evp_cipher_from_algorithm, + evp_cipher_up_ref, + evp_cipher_free, + evp_cipher_dup_frozen, + evp_cipher_frozen_free); + + return ret; +} + EVP_CIPHER *EVP_CIPHER_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) { diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 9925378261..1dd5815952 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -423,3 +423,4 @@ int evp_cipher_cache_constants(EVP_CIPHER *cipher); int evp_method_id2name_id_op_id(uint32_t meth_id, int *name_id, unsigned int *operation_id); int evp_md_fetch_all(OSSL_LIB_CTX *ctx); +int evp_cipher_fetch_all(OSSL_LIB_CTX *ctx); diff --git a/crypto/property/property.c b/crypto/property/property.c index ae6409a1b5..c5570dbbaf 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -1167,6 +1167,8 @@ int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq) if (evp_md_fetch_all(store->ctx) <= 0) goto err; + if (evp_cipher_fetch_all(store->ctx) <= 0) + goto err; ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_freeze, &af); if (af.ret <= 0) From aa70893e25aee2d199540f958fb0f76c8f851978 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Thu, 8 Jan 2026 01:10:20 +0900 Subject: [PATCH 14/28] Add freeze functionality to EVP_CIPHER_fetch Add unit test to check functionality Reviewed-by: Neil Horman Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Wed Feb 18 10:13:45 2026 (Merged from https://github.com/openssl/openssl/pull/30002) --- crypto/evp/evp_enc.c | 12 ++++--- test/evp_fetch_prov_test.c | 69 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c index f5267014ad..14cb7a4224 100644 --- a/crypto/evp/evp_enc.c +++ b/crypto/evp/evp_enc.c @@ -2044,11 +2044,13 @@ int evp_cipher_fetch_all(OSSL_LIB_CTX *ctx) EVP_CIPHER *EVP_CIPHER_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) { - EVP_CIPHER *cipher = evp_generic_fetch(ctx, OSSL_OP_CIPHER, algorithm, properties, - evp_cipher_from_algorithm, evp_cipher_up_ref, - evp_cipher_free, NULL, NULL); - - return cipher; + return evp_generic_fetch(ctx, OSSL_OP_CIPHER, + algorithm, properties, + evp_cipher_from_algorithm, + evp_cipher_up_ref, + evp_cipher_free, + evp_cipher_dup_frozen, + evp_cipher_frozen_free); } EVP_CIPHER *evp_cipher_fetch_from_prov(OSSL_PROVIDER *prov, diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index ba215c0814..67c2ab3c03 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -368,6 +368,74 @@ static int test_cipher(const EVP_CIPHER *cipher) && TEST_true(encrypt_decrypt(cipher, testmsg, sizeof(testmsg))); } +static int test_EVP_CIPHER_fetch_freeze(void) +{ +#if defined(OPENSSL_NO_CACHED_FETCH) + /* + * Test does not make sense if cached fetch is disabled. + * There's nothing to freeze, and test will fail. + */ + return 1; +#endif + + EVP_CIPHER *cipher = NULL; + int ret = 0; + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(cipher = EVP_CIPHER_fetch(ctx, "AES-128-CBC", NULL)) + || !TEST_true(test_cipher(cipher)) + || !TEST_int_ne(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_CIPHER_free(cipher); + + if (!TEST_ptr(cipher = EVP_CIPHER_fetch(ctx, "AES-128-CBC", "?provider=default")) + || !TEST_true(test_cipher(cipher)) + || !TEST_int_ne(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_CIPHER_free(cipher); + cipher = NULL; + + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) + || !TEST_ptr(cipher = EVP_CIPHER_fetch(ctx, "AES-128-CBC", NULL)) + || !TEST_true(test_cipher(cipher)) + || !TEST_int_eq(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + /* Technically, frozen version doesn't need to be freed */ + EVP_CIPHER_free(cipher); + + if (!TEST_ptr(cipher = EVP_CIPHER_fetch(ctx, "AES-128-CBC", "?fips=true")) + || !TEST_true(test_cipher(cipher)) + || !TEST_int_eq(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_CIPHER_free(cipher); + + if (!TEST_ptr(cipher = EVP_CIPHER_fetch(ctx, "AES-128-CBC", "?provider=default")) + || !TEST_true(test_cipher(cipher)) + || !TEST_int_ne(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_CIPHER_free(cipher); + + if (!TEST_ptr(cipher = EVP_CIPHER_fetch(ctx, "AES-128-ECB", "?fips=true")) + || !TEST_true(test_cipher(cipher)) + || !TEST_int_eq(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_CIPHER_free(cipher); + if (!TEST_ptr(cipher = EVP_CIPHER_fetch(ctx, "AES-128-ECB", NULL)) + || !TEST_true(test_cipher(cipher)) + || !TEST_int_eq(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + + ret = 1; +err: + EVP_CIPHER_free(cipher); + unload_providers(&ctx, prov); + return ret; +} + static int test_implicit_EVP_CIPHER_fetch(void) { OSSL_LIB_CTX *ctx = NULL; @@ -483,6 +551,7 @@ int setup_tests(void) ADD_TEST(test_explicit_EVP_MD_fetch_by_name); ADD_ALL_TESTS_NOSUBTEST(test_explicit_EVP_MD_fetch_by_X509_ALGOR, 2); } else { + ADD_TEST(test_EVP_CIPHER_fetch_freeze); ADD_TEST(test_implicit_EVP_CIPHER_fetch); ADD_TEST(test_explicit_EVP_CIPHER_fetch_by_name); ADD_ALL_TESTS_NOSUBTEST(test_explicit_EVP_CIPHER_fetch_by_X509_ALGOR, 2); From 37bb988a0ef8a27778926af6169d89816492f04d Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Fri, 13 Feb 2026 13:01:39 +0100 Subject: [PATCH 15/28] evp: add freeze callbacks to evp_generic_fetch_from_prov when RAND_bytes() is called before the freeze it tries to fetch AES-256-CTR via evp_generic_fetch_from_prov without passing freezing callbacks. Stacktrace: inner_evp_generic_fetch (operation_id=2, name=0xfffff7744ae8 "AES-256-CTR", dup_method=0x0, dup_free_method=0x0) evp_generic_fetch_from_prov evp_cipher_fetch_from_prov (prov=0xfd0ff57e0540, algorithm=0xfffff7744ae8 "AES-256-CTR" drbg_ctr_set_ctx_params_locked drbg_ctr_instantiate_wrapper evp_rand_instantiate_locked EVP_RAND_instantiate rand_new_drbg rand_get0_primary rand_get0_public RAND_bytes_ex RAND_bytes Calling evp_cipher_fetch_all() does not have any effects once method store is filled for operations, and therefore freezing ciphers fails because freezing filters dup_method == NULL || dup_free_method == NULL. Signed-off-by: Nikola Pajkovsky Reviewed-by: Neil Horman Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz MergeDate: Wed Feb 18 10:13:47 2026 (Merged from https://github.com/openssl/openssl/pull/30002) --- crypto/evp/asymcipher.c | 4 +++- crypto/evp/digest.c | 4 +++- crypto/evp/evp_enc.c | 4 +++- crypto/evp/evp_fetch.c | 10 ++++++++-- crypto/evp/evp_local.h | 4 +++- crypto/evp/exchange.c | 4 +++- crypto/evp/kem.c | 4 +++- crypto/evp/keymgmt_meth.c | 4 +++- crypto/evp/mac_meth.c | 4 +++- crypto/evp/signature.c | 4 +++- crypto/evp/skeymgmt_meth.c | 4 +++- 11 files changed, 38 insertions(+), 12 deletions(-) diff --git a/crypto/evp/asymcipher.c b/crypto/evp/asymcipher.c index f04fb85c47..d7048c6a9e 100644 --- a/crypto/evp/asymcipher.c +++ b/crypto/evp/asymcipher.c @@ -525,7 +525,9 @@ EVP_ASYM_CIPHER *evp_asym_cipher_fetch_from_prov(OSSL_PROVIDER *prov, algorithm, properties, evp_asym_cipher_from_algorithm, evp_asym_cipher_up_ref, - evp_asym_cipher_free); + evp_asym_cipher_free, + NULL, + NULL); } int EVP_ASYM_CIPHER_is_a(const EVP_ASYM_CIPHER *cipher, const char *name) diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index 40a70f1993..3d9ff07821 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -1178,7 +1178,9 @@ EVP_MD *evp_digest_fetch_from_prov(OSSL_PROVIDER *prov, algorithm, properties, evp_md_from_algorithm, evp_md_up_ref, - evp_md_free); + evp_md_free, + evp_md_dup_frozen, + (void (*)(void *))evp_md_frozen_free); } typedef struct { diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c index 14cb7a4224..8233eac141 100644 --- a/crypto/evp/evp_enc.c +++ b/crypto/evp/evp_enc.c @@ -2061,7 +2061,9 @@ EVP_CIPHER *evp_cipher_fetch_from_prov(OSSL_PROVIDER *prov, algorithm, properties, evp_cipher_from_algorithm, evp_cipher_up_ref, - evp_cipher_free); + evp_cipher_free, + evp_cipher_dup_frozen, + evp_cipher_frozen_free); } int EVP_CIPHER_can_pipeline(const EVP_CIPHER *cipher, int enc) diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c index 5a20a6f2e2..fc6ba9c307 100644 --- a/crypto/evp/evp_fetch.c +++ b/crypto/evp/evp_fetch.c @@ -535,7 +535,9 @@ void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id, const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov), int (*up_ref_method)(void *), - void (*free_method)(void *)) + void (*free_method)(void *), + void *(*dup_method)(void *), + void (*dup_free_method)(void *)) { struct evp_method_data_st methdata; void *method; @@ -544,7 +546,11 @@ void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id, methdata.tmp_store = NULL; method = inner_evp_generic_fetch(&methdata, prov, operation_id, name, properties, - new_method, up_ref_method, free_method, NULL, NULL); + new_method, + up_ref_method, + free_method, + dup_method, + dup_free_method); dealloc_tmp_evp_method_store(methdata.tmp_store); return method; } diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 1dd5815952..67899b9f4e 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -318,7 +318,9 @@ void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id, const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov), int (*up_ref_method)(void *), - void (*free_method)(void *)); + void (*free_method)(void *), + void *(*dup_method)(void *), + void (*dup_free_method)(void *)); void evp_generic_do_all_prefetched(OSSL_LIB_CTX *libctx, int operation_id, void (*user_fn)(void *method, void *arg), void *user_arg); diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c index 37d714a900..384f0366bc 100644 --- a/crypto/evp/exchange.c +++ b/crypto/evp/exchange.c @@ -203,7 +203,9 @@ EVP_KEYEXCH *evp_keyexch_fetch_from_prov(OSSL_PROVIDER *prov, algorithm, properties, evp_keyexch_from_algorithm, evp_keyexch_up_ref, - evp_keyexch_free); + evp_keyexch_free, + NULL, + NULL); } int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx) diff --git a/crypto/evp/kem.c b/crypto/evp/kem.c index 1d79e79dc7..101890b4c4 100644 --- a/crypto/evp/kem.c +++ b/crypto/evp/kem.c @@ -472,7 +472,9 @@ EVP_KEM *evp_kem_fetch_from_prov(OSSL_PROVIDER *prov, const char *algorithm, return evp_generic_fetch_from_prov(prov, OSSL_OP_KEM, algorithm, properties, evp_kem_from_algorithm, evp_kem_up_ref, - evp_kem_free); + evp_kem_free, + NULL, + NULL); } int EVP_KEM_is_a(const EVP_KEM *kem, const char *name) diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c index 577423e76f..f418823c71 100644 --- a/crypto/evp/keymgmt_meth.c +++ b/crypto/evp/keymgmt_meth.c @@ -274,7 +274,9 @@ EVP_KEYMGMT *evp_keymgmt_fetch_from_prov(OSSL_PROVIDER *prov, name, properties, keymgmt_from_algorithm, evp_keymgmt_up_ref, - evp_keymgmt_free); + evp_keymgmt_free, + NULL, + NULL); } EVP_KEYMGMT *EVP_KEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, diff --git a/crypto/evp/mac_meth.c b/crypto/evp/mac_meth.c index 0d3c48baea..dc3b1e28ab 100644 --- a/crypto/evp/mac_meth.c +++ b/crypto/evp/mac_meth.c @@ -259,5 +259,7 @@ EVP_MAC *evp_mac_fetch_from_prov(OSSL_PROVIDER *prov, algorithm, properties, evp_mac_from_algorithm, evp_mac_up_ref, - evp_mac_free); + evp_mac_free, + NULL, + NULL); } diff --git a/crypto/evp/signature.c b/crypto/evp/signature.c index a4a4302712..2e7a18f229 100644 --- a/crypto/evp/signature.c +++ b/crypto/evp/signature.c @@ -497,7 +497,9 @@ EVP_SIGNATURE *evp_signature_fetch_from_prov(OSSL_PROVIDER *prov, algorithm, properties, evp_signature_from_algorithm, evp_signature_up_ref, - evp_signature_free); + evp_signature_free, + NULL, + NULL); } int EVP_SIGNATURE_is_a(const EVP_SIGNATURE *signature, const char *name) diff --git a/crypto/evp/skeymgmt_meth.c b/crypto/evp/skeymgmt_meth.c index ab838aee77..a46bbe3f02 100644 --- a/crypto/evp/skeymgmt_meth.c +++ b/crypto/evp/skeymgmt_meth.c @@ -136,7 +136,9 @@ EVP_SKEYMGMT *evp_skeymgmt_fetch_from_prov(OSSL_PROVIDER *prov, name, properties, skeymgmt_from_algorithm, (int (*)(void *))EVP_SKEYMGMT_up_ref, - (void (*)(void *))EVP_SKEYMGMT_free); + (void (*)(void *))EVP_SKEYMGMT_free, + NULL, + NULL); } EVP_SKEYMGMT *EVP_SKEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, From e77def380a0395ae021ebed1be4fb3815f7b4256 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Tue, 24 Feb 2026 14:13:27 +0100 Subject: [PATCH 16/28] evp: fix refcount leak in frozen digest/cipher dup error paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nikola Pajkovsky Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs Reviewed-by: Paul Dale MergeDate: Thu Feb 26 15:10:57 2026 (Merged from https://github.com/openssl/openssl/pull/30154) --- crypto/evp/digest.c | 1 + crypto/evp/evp_enc.c | 1 + 2 files changed, 2 insertions(+) diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index 3d9ff07821..5e5bd2523d 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -1098,6 +1098,7 @@ static void *evp_md_dup_frozen(void *vin) return out; err: + CRYPTO_FREE_REF(&out->refcnt); OPENSSL_free(out); return NULL; } diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c index 8233eac141..bce9d8d2cb 100644 --- a/crypto/evp/evp_enc.c +++ b/crypto/evp/evp_enc.c @@ -2010,6 +2010,7 @@ static void *evp_cipher_dup_frozen(void *vin) } return out; err: + CRYPTO_FREE_REF(&out->refcnt); OPENSSL_free(out); return NULL; } From ccd9445247347bcae57dcc66eb3e21ff9e67d73f Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 24 Feb 2026 13:17:20 +0700 Subject: [PATCH 17/28] Add freeze functionality to EVP_RAND_fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add unit tests to check functionality Move EVP_RAND to evp_local.h Reviewed-by: Nikola Pajkovsky Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs MergeDate: Fri Feb 27 17:24:53 2026 (Merged from https://github.com/openssl/openssl/pull/30151) --- crypto/evp/evp_local.h | 31 +++++++ crypto/evp/evp_rand.c | 105 +++++++++++++++--------- crypto/property/property.c | 6 +- test/evp_fetch_prov_test.c | 111 +++++++++++++++++++++++++- test/recipes/30-test_evp_fetch_prov.t | 2 +- 5 files changed, 213 insertions(+), 42 deletions(-) diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 67899b9f4e..1bd77ef47e 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -197,6 +197,36 @@ struct evp_signature_st { OSSL_FUNC_signature_query_key_types_fn *query_key_types; } /* EVP_SIGNATURE */; +struct evp_rand_st { + OSSL_PROVIDER *prov; + int name_id; + int origin; + char *type_name; + const char *description; + CRYPTO_REF_COUNT refcnt; + + const OSSL_DISPATCH *dispatch; + OSSL_FUNC_rand_newctx_fn *newctx; + OSSL_FUNC_rand_freectx_fn *freectx; + OSSL_FUNC_rand_instantiate_fn *instantiate; + OSSL_FUNC_rand_uninstantiate_fn *uninstantiate; + OSSL_FUNC_rand_generate_fn *generate; + OSSL_FUNC_rand_reseed_fn *reseed; + OSSL_FUNC_rand_nonce_fn *nonce; + OSSL_FUNC_rand_enable_locking_fn *enable_locking; + OSSL_FUNC_rand_lock_fn *lock; + OSSL_FUNC_rand_unlock_fn *unlock; + OSSL_FUNC_rand_gettable_params_fn *gettable_params; + OSSL_FUNC_rand_gettable_ctx_params_fn *gettable_ctx_params; + OSSL_FUNC_rand_settable_ctx_params_fn *settable_ctx_params; + OSSL_FUNC_rand_get_params_fn *get_params; + OSSL_FUNC_rand_get_ctx_params_fn *get_ctx_params; + OSSL_FUNC_rand_set_ctx_params_fn *set_ctx_params; + OSSL_FUNC_rand_verify_zeroization_fn *verify_zeroization; + OSSL_FUNC_rand_get_seed_fn *get_seed; + OSSL_FUNC_rand_clear_seed_fn *clear_seed; +} /* EVP_RAND */; + struct evp_skeymgmt_st { int name_id; char *type_name; @@ -426,3 +456,4 @@ int evp_method_id2name_id_op_id(uint32_t meth_id, int *name_id, unsigned int *operation_id); int evp_md_fetch_all(OSSL_LIB_CTX *ctx); int evp_cipher_fetch_all(OSSL_LIB_CTX *ctx); +int evp_rand_fetch_all(OSSL_LIB_CTX *ctx); diff --git a/crypto/evp/evp_rand.c b/crypto/evp/evp_rand.c index dfa3943253..4d5036b2a6 100644 --- a/crypto/evp/evp_rand.c +++ b/crypto/evp/evp_rand.c @@ -21,41 +21,20 @@ #include "crypto/evp.h" #include "evp_local.h" -struct evp_rand_st { - OSSL_PROVIDER *prov; - int name_id; - char *type_name; - const char *description; - CRYPTO_REF_COUNT refcnt; - - const OSSL_DISPATCH *dispatch; - OSSL_FUNC_rand_newctx_fn *newctx; - OSSL_FUNC_rand_freectx_fn *freectx; - OSSL_FUNC_rand_instantiate_fn *instantiate; - OSSL_FUNC_rand_uninstantiate_fn *uninstantiate; - OSSL_FUNC_rand_generate_fn *generate; - OSSL_FUNC_rand_reseed_fn *reseed; - OSSL_FUNC_rand_nonce_fn *nonce; - OSSL_FUNC_rand_enable_locking_fn *enable_locking; - OSSL_FUNC_rand_lock_fn *lock; - OSSL_FUNC_rand_unlock_fn *unlock; - OSSL_FUNC_rand_gettable_params_fn *gettable_params; - OSSL_FUNC_rand_gettable_ctx_params_fn *gettable_ctx_params; - OSSL_FUNC_rand_settable_ctx_params_fn *settable_ctx_params; - OSSL_FUNC_rand_get_params_fn *get_params; - OSSL_FUNC_rand_get_ctx_params_fn *get_ctx_params; - OSSL_FUNC_rand_set_ctx_params_fn *set_ctx_params; - OSSL_FUNC_rand_verify_zeroization_fn *verify_zeroization; - OSSL_FUNC_rand_get_seed_fn *get_seed; - OSSL_FUNC_rand_clear_seed_fn *clear_seed; -} /* EVP_RAND */; +static void evp_rand_free_int(EVP_RAND *rand) +{ + OPENSSL_free(rand->type_name); + ossl_provider_free(rand->prov); + CRYPTO_FREE_REF(&rand->refcnt); + OPENSSL_free(rand); +} static int evp_rand_up_ref(void *vrand) { EVP_RAND *rand = (EVP_RAND *)vrand; int ref = 0; - if (rand != NULL) + if (rand != NULL && rand->origin == EVP_ORIG_DYNAMIC) return CRYPTO_UP_REF(&rand->refcnt, &ref); return 1; } @@ -65,15 +44,12 @@ static void evp_rand_free(void *vrand) EVP_RAND *rand = (EVP_RAND *)vrand; int ref = 0; - if (rand == NULL) + if (rand == NULL || rand->origin != EVP_ORIG_DYNAMIC) return; CRYPTO_DOWN_REF(&rand->refcnt, &ref); if (ref > 0) return; - OPENSSL_free(rand->type_name); - ossl_provider_free(rand->prov); - CRYPTO_FREE_REF(&rand->refcnt); - OPENSSL_free(rand); + evp_rand_free_int(rand); } static void *evp_rand_new(void) @@ -90,6 +66,47 @@ static void *evp_rand_new(void) return rand; } +static void *evp_rand_dup_frozen(void *vin) +{ + EVP_RAND *in = vin; + EVP_RAND *out; + + out = OPENSSL_malloc(sizeof(*out)); + if (out == NULL) + return NULL; + memcpy(out, in, sizeof(*out)); + if (!CRYPTO_NEW_REF(&out->refcnt, 1)) + goto err; + out->type_name = OPENSSL_strdup(in->type_name); + if (out->type_name == NULL) + goto err; + out->origin = EVP_ORIG_FROZEN; + if (out->prov == NULL || !ossl_provider_up_ref(out->prov)) { + OPENSSL_free(out->type_name); + goto err; + } + return out; + +err: + CRYPTO_FREE_REF(&out->refcnt); + OPENSSL_free(out); + return NULL; +} + +static void evp_rand_frozen_free(void *vin) +{ + EVP_RAND *rand = vin; + int ref = 0; + + if (rand == NULL || rand->origin != EVP_ORIG_FROZEN) + return; + + CRYPTO_DOWN_REF(&rand->refcnt, &ref); + if (ref > 0) + return; + evp_rand_free_int(rand); +} + /* Enable locking of the underlying DRBG/RAND if available */ int EVP_RAND_enable_locking(EVP_RAND_CTX *rand) { @@ -282,9 +299,23 @@ static void *evp_rand_from_algorithm(int name_id, EVP_RAND *EVP_RAND_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, const char *properties) { - return evp_generic_fetch(libctx, OSSL_OP_RAND, algorithm, properties, - evp_rand_from_algorithm, evp_rand_up_ref, - evp_rand_free, NULL, NULL); + return evp_generic_fetch(libctx, OSSL_OP_RAND, + algorithm, properties, + evp_rand_from_algorithm, + evp_rand_up_ref, + evp_rand_free, + evp_rand_dup_frozen, + evp_rand_frozen_free); +} + +int evp_rand_fetch_all(OSSL_LIB_CTX *ctx) +{ + return evp_generic_fetch_all(ctx, OSSL_OP_RAND, + evp_rand_from_algorithm, + evp_rand_up_ref, + evp_rand_free, + evp_rand_dup_frozen, + evp_rand_frozen_free); } int EVP_RAND_up_ref(EVP_RAND *rand) diff --git a/crypto/property/property.c b/crypto/property/property.c index c5570dbbaf..2292075202 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -1165,9 +1165,9 @@ int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq) if (store->frozen_algs == NULL) goto err; - if (evp_md_fetch_all(store->ctx) <= 0) - goto err; - if (evp_cipher_fetch_all(store->ctx) <= 0) + if (evp_md_fetch_all(store->ctx) <= 0 + || evp_cipher_fetch_all(store->ctx) <= 0 + || evp_rand_fetch_all(store->ctx) <= 0) goto err; ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_freeze, &af); diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index 67c2ab3c03..32e1ad9936 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -21,6 +21,7 @@ #include "internal/sizes.h" #include "testutil.h" #include "crypto/evp.h" +#include "../crypto/evp/evp_local.h" static char *config_file = NULL; static char *alg = "digest"; @@ -516,6 +517,107 @@ end: return ret; } +static int test_EVP_RAND_fetch_freeze(void) +{ +#if defined(OPENSSL_NO_CACHED_FETCH) + /* + * Test does not make sense if cached fetch is disabled. + * There's nothing to freeze, and test will fail. + */ + return 1; +#endif + + EVP_RAND *rand = NULL; + int ret = 0; + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(rand = EVP_RAND_fetch(ctx, "HASH-DRBG", NULL)) + || !TEST_int_ne(rand->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_RAND_free(rand); + rand = NULL; + + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) + || !TEST_ptr(rand = EVP_RAND_fetch(ctx, "HASH-DRBG", NULL)) + || !TEST_int_eq(rand->origin, EVP_ORIG_FROZEN)) + goto err; + /* Technically, frozen version doesn't need to be freed */ + EVP_RAND_free(rand); + rand = NULL; + + if (!TEST_ptr(rand = EVP_RAND_fetch(ctx, "HASH-DRBG", "?fips=true")) + || !TEST_int_eq(rand->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_RAND_free(rand); + rand = NULL; + + /* Falls back to slow path */ + if (!TEST_ptr(rand = EVP_RAND_fetch(ctx, "HASH-DRBG", "?provider=default")) + || !TEST_int_ne(rand->origin, EVP_ORIG_FROZEN)) + goto err; + + ret = 1; +err: + EVP_RAND_free(rand); + unload_providers(&ctx, prov); + return ret; +} + +static int test_implicit_EVP_RAND_fetch(void) +{ + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + EVP_RAND *rand = NULL; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(rand = EVP_RAND_fetch(ctx, "HASH-DRBG", NULL))) + goto err; + ret = 1; +err: + EVP_RAND_free(rand); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_RAND_fetch(const char *id) +{ + OSSL_LIB_CTX *ctx = NULL; + EVP_RAND *rand = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + rand = EVP_RAND_fetch(ctx, id, fetch_property); + if (expected_fetch_result != 0) { + if (!TEST_true(EVP_RAND_up_ref(rand))) + goto err; + /* Ref count should now be 2. Release first one here */ + EVP_RAND_free(rand); + } else { + if (!TEST_ptr_null(rand)) + goto err; + } + ret = 1; +err: + EVP_RAND_free(rand); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_RAND_fetch_by_name(void) +{ + return test_explicit_EVP_RAND_fetch("HASH-DRBG"); +} + int setup_tests(void) { OPTION_CHOICE o; @@ -550,11 +652,18 @@ int setup_tests(void) ADD_TEST(test_implicit_EVP_MD_fetch); ADD_TEST(test_explicit_EVP_MD_fetch_by_name); ADD_ALL_TESTS_NOSUBTEST(test_explicit_EVP_MD_fetch_by_X509_ALGOR, 2); - } else { + } else if (strcmp(alg, "cipher") == 0) { ADD_TEST(test_EVP_CIPHER_fetch_freeze); ADD_TEST(test_implicit_EVP_CIPHER_fetch); ADD_TEST(test_explicit_EVP_CIPHER_fetch_by_name); ADD_ALL_TESTS_NOSUBTEST(test_explicit_EVP_CIPHER_fetch_by_X509_ALGOR, 2); + } else if (strcmp(alg, "rand") == 0) { + ADD_TEST(test_EVP_RAND_fetch_freeze); + ADD_TEST(test_implicit_EVP_RAND_fetch); + ADD_TEST(test_explicit_EVP_RAND_fetch_by_name); + } else { + TEST_error("Unknown fetch type: %s", alg); + return 0; } return 1; } diff --git a/test/recipes/30-test_evp_fetch_prov.t b/test/recipes/30-test_evp_fetch_prov.t index 63082dd311..5f870ccac4 100644 --- a/test/recipes/30-test_evp_fetch_prov.t +++ b/test/recipes/30-test_evp_fetch_prov.t @@ -21,7 +21,7 @@ use lib bldtop_dir('.'); my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); -my @types = ( "digest", "cipher" ); +my @types = ( "digest", "cipher", "rand" ); my @testdata = ( { config => srctop_file("test", "default.cnf"), From 9200ab5c157bc2833e412e298f8b7d35c63e7dc3 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Fri, 20 Feb 2026 14:27:17 +0100 Subject: [PATCH 18/28] evp: freeze MAC fetch cache and expand MAC fetch tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add frozen-method support for EVP_MAC fetches by wiring dup/free callbacks and populating MAC entries during OSSL_LIB_CTX_freeze(). Signed-off-by: Nikola Pajkovsky Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs Reviewed-by: Neil Horman MergeDate: Sat Feb 28 13:21:07 2026 (Merged from https://github.com/openssl/openssl/pull/30142) --- crypto/evp/evp_local.h | 1 + crypto/evp/mac_meth.c | 84 ++++++++++++-- crypto/property/property.c | 2 + include/crypto/evp.h | 1 + test/evp_fetch_prov_test.c | 157 ++++++++++++++++++++++++++ test/recipes/30-test_evp_fetch_prov.t | 2 +- 6 files changed, 235 insertions(+), 12 deletions(-) diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 1bd77ef47e..30dc68ecf4 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -457,3 +457,4 @@ int evp_method_id2name_id_op_id(uint32_t meth_id, int *name_id, int evp_md_fetch_all(OSSL_LIB_CTX *ctx); int evp_cipher_fetch_all(OSSL_LIB_CTX *ctx); int evp_rand_fetch_all(OSSL_LIB_CTX *ctx); +int evp_mac_fetch_all(OSSL_LIB_CTX *ctx); diff --git a/crypto/evp/mac_meth.c b/crypto/evp/mac_meth.c index dc3b1e28ab..201ebf7852 100644 --- a/crypto/evp/mac_meth.c +++ b/crypto/evp/mac_meth.c @@ -11,17 +11,27 @@ #include #include #include +#include #include "internal/provider.h" #include "internal/core.h" #include "crypto/evp.h" #include "evp_local.h" +static void evp_mac_free_int(EVP_MAC *mac) +{ + OPENSSL_free(mac->type_name); + ossl_provider_free(mac->prov); + CRYPTO_FREE_REF(&mac->refcnt); + OPENSSL_free(mac); +} + static int evp_mac_up_ref(void *vmac) { EVP_MAC *mac = vmac; int ref = 0; - CRYPTO_UP_REF(&mac->refcnt, &ref); + if (mac->origin == EVP_ORIG_DYNAMIC) + CRYPTO_UP_REF(&mac->refcnt, &ref); return 1; } @@ -30,16 +40,54 @@ static void evp_mac_free(void *vmac) EVP_MAC *mac = vmac; int ref = 0; - if (mac == NULL) + if (mac == NULL || mac->origin != EVP_ORIG_DYNAMIC) return; CRYPTO_DOWN_REF(&mac->refcnt, &ref); if (ref > 0) return; - OPENSSL_free(mac->type_name); - ossl_provider_free(mac->prov); - CRYPTO_FREE_REF(&mac->refcnt); - OPENSSL_free(mac); + evp_mac_free_int(mac); +} + +static void *evp_mac_dup_frozen(void *vin) +{ + EVP_MAC *in = vin; + EVP_MAC *out; + + out = OPENSSL_malloc(sizeof(*out)); + if (out == NULL) + return NULL; + memcpy(out, in, sizeof(*out)); + if (!CRYPTO_NEW_REF(&out->refcnt, 1)) + goto err; + out->type_name = OPENSSL_strdup(in->type_name); + if (out->type_name == NULL) + goto err; + out->origin = EVP_ORIG_FROZEN; + if (out->prov == NULL || !ossl_provider_up_ref(out->prov)) { + OPENSSL_free(out->type_name); + goto err; + } + return out; + +err: + CRYPTO_FREE_REF(&out->refcnt); + OPENSSL_free(out); + return NULL; +} + +static void evp_mac_frozen_free(void *vin) +{ + EVP_MAC *mac = vin; + int ref = 0; + + if (mac == NULL || mac->origin != EVP_ORIG_FROZEN) + return; + + CRYPTO_DOWN_REF(&mac->refcnt, &ref); + if (ref > 0) + return; + evp_mac_free_int(mac); } static void *evp_mac_new(void) @@ -175,9 +223,23 @@ err: EVP_MAC *EVP_MAC_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, const char *properties) { - return evp_generic_fetch(libctx, OSSL_OP_MAC, algorithm, properties, - evp_mac_from_algorithm, evp_mac_up_ref, - evp_mac_free, NULL, NULL); + return evp_generic_fetch(libctx, OSSL_OP_MAC, + algorithm, properties, + evp_mac_from_algorithm, + evp_mac_up_ref, + evp_mac_free, + evp_mac_dup_frozen, + evp_mac_frozen_free); +} + +int evp_mac_fetch_all(OSSL_LIB_CTX *ctx) +{ + return evp_generic_fetch_all(ctx, OSSL_OP_MAC, + evp_mac_from_algorithm, + evp_mac_up_ref, + evp_mac_free, + evp_mac_dup_frozen, + evp_mac_frozen_free); } int EVP_MAC_up_ref(EVP_MAC *mac) @@ -260,6 +322,6 @@ EVP_MAC *evp_mac_fetch_from_prov(OSSL_PROVIDER *prov, evp_mac_from_algorithm, evp_mac_up_ref, evp_mac_free, - NULL, - NULL); + evp_mac_dup_frozen, + evp_mac_frozen_free); } diff --git a/crypto/property/property.c b/crypto/property/property.c index 2292075202..ec853f48b6 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -1169,6 +1169,8 @@ int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq) || evp_cipher_fetch_all(store->ctx) <= 0 || evp_rand_fetch_all(store->ctx) <= 0) goto err; + if (evp_mac_fetch_all(store->ctx) <= 0) + goto err; ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_freeze, &af); if (af.ret <= 0) diff --git a/include/crypto/evp.h b/include/crypto/evp.h index 5d501a5889..7ede5c5a0c 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -207,6 +207,7 @@ const EVP_PKEY_METHOD *ossl_rsa_pss_pkey_method(void); struct evp_mac_st { OSSL_PROVIDER *prov; int name_id; + int origin; char *type_name; const char *description; diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index 32e1ad9936..cb00128d58 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "internal/sizes.h" #include "testutil.h" @@ -333,6 +334,158 @@ end: return ret; } +static int calculate_mac(const EVP_MAC *mac, const unsigned char *msg, size_t len, + const unsigned char *expected, size_t expected_len) +{ + unsigned char out[EVP_MAX_MD_SIZE]; + const unsigned char key[] = "0123456789abcd"; + EVP_MAC_CTX *ctx = NULL; + OSSL_PARAM params[2], *p = params; + size_t out_len = 0; + int ret = 0; + + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, "SHA256", 0); + *p = OSSL_PARAM_construct_end(); + + if (!TEST_ptr(ctx = EVP_MAC_CTX_new((EVP_MAC *)mac)) + || !TEST_true(EVP_MAC_init(ctx, key, sizeof(key) - 1, params)) + || !TEST_true(EVP_MAC_update(ctx, msg, len)) + || !TEST_true(EVP_MAC_final(ctx, out, &out_len, sizeof(out))) + || !TEST_size_t_eq(out_len, expected_len) + || !TEST_mem_eq(out, out_len, expected, expected_len)) + goto err; + + ret = 1; +err: + EVP_MAC_CTX_free(ctx); + return ret; +} + +static int test_mac(const EVP_MAC *mac) +{ + const unsigned char testmsg[] = "Hello world"; + const unsigned char expected[] = { + 0xf2, 0x71, 0x5a, 0xad, 0x1d, 0x68, 0x3c, 0xdd, + 0xbc, 0xa7, 0x5a, 0x1e, 0x79, 0xed, 0xac, 0x57, + 0xf6, 0xb0, 0xd4, 0xbb, 0x15, 0xe1, 0x7f, 0x6b, + 0x47, 0x62, 0x58, 0xb1, 0xbc, 0x0d, 0xf4, 0x4f + }; + + return TEST_ptr(mac) + && TEST_true(EVP_MAC_is_a(mac, "HMAC")) + && TEST_true(calculate_mac(mac, testmsg, sizeof(testmsg) - 1, + expected, sizeof(expected))); +} + +static int test_EVP_MAC_fetch_freeze(void) +{ +#if defined(OPENSSL_NO_CACHED_FETCH) + /* + * Test does not make sense if cached fetch is disabled. + * There's nothing to freeze, and test will fail. + */ + return 1; +#endif + + EVP_MAC *mac = NULL; + int ret = 0; + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(mac = EVP_MAC_fetch(ctx, "HMAC", NULL)) + || !TEST_true(test_mac(mac)) + || !TEST_int_ne(mac->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_MAC_free(mac); + mac = NULL; + + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) + || !TEST_ptr(mac = EVP_MAC_fetch(ctx, "HMAC", NULL)) + || !TEST_true(test_mac(mac)) + || !TEST_int_eq(mac->origin, EVP_ORIG_FROZEN)) + goto err; + /* Technically, frozen version doesn't need to be freed */ + EVP_MAC_free(mac); + mac = NULL; + + if (!TEST_ptr(mac = EVP_MAC_fetch(ctx, "HMAC", "?fips=true")) + || !TEST_true(test_mac(mac)) + || !TEST_int_eq(mac->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_MAC_free(mac); + mac = NULL; + + /* Falls back to slow path */ + if (!TEST_ptr(mac = EVP_MAC_fetch(ctx, "HMAC", "?provider=default")) + || !TEST_true(test_mac(mac)) + || !TEST_int_ne(mac->origin, EVP_ORIG_FROZEN)) + goto err; + + ret = 1; +err: + EVP_MAC_free(mac); + unload_providers(&ctx, prov); + return ret; +} + +static int test_implicit_EVP_MAC_fetch(void) +{ + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + EVP_MAC *mac = NULL; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(mac = EVP_MAC_fetch(ctx, "HMAC", NULL)) + || !TEST_true(test_mac(mac))) + goto err; + ret = 1; +err: + EVP_MAC_free(mac); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_MAC_fetch(const char *id) +{ + OSSL_LIB_CTX *ctx = NULL; + EVP_MAC *mac = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + mac = EVP_MAC_fetch(ctx, id, fetch_property); + if (expected_fetch_result != 0) { + if (!test_mac(mac)) + goto err; + + if (!TEST_true(EVP_MAC_up_ref(mac))) + goto err; + /* Ref count should now be 2. Release first one here */ + EVP_MAC_free(mac); + } else { + if (!TEST_ptr_null(mac)) + goto err; + } + ret = 1; +err: + EVP_MAC_free(mac); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_MAC_fetch_by_name(void) +{ + return test_explicit_EVP_MAC_fetch("HMAC"); +} + /* * Test EVP_CIPHER_fetch() */ @@ -661,6 +814,10 @@ int setup_tests(void) ADD_TEST(test_EVP_RAND_fetch_freeze); ADD_TEST(test_implicit_EVP_RAND_fetch); ADD_TEST(test_explicit_EVP_RAND_fetch_by_name); + } else if (strcmp(alg, "mac") == 0) { + ADD_TEST(test_EVP_MAC_fetch_freeze); + ADD_TEST(test_implicit_EVP_MAC_fetch); + ADD_TEST(test_explicit_EVP_MAC_fetch_by_name); } else { TEST_error("Unknown fetch type: %s", alg); return 0; diff --git a/test/recipes/30-test_evp_fetch_prov.t b/test/recipes/30-test_evp_fetch_prov.t index 5f870ccac4..b6d4841ab5 100644 --- a/test/recipes/30-test_evp_fetch_prov.t +++ b/test/recipes/30-test_evp_fetch_prov.t @@ -21,7 +21,7 @@ use lib bldtop_dir('.'); my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); -my @types = ( "digest", "cipher", "rand" ); +my @types = ( "digest", "cipher", "rand", "mac" ); my @testdata = ( { config => srctop_file("test", "default.cnf"), From 302edc39e0e6abf6738f9fa72a967ec120a9b0a1 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Tue, 24 Feb 2026 15:16:59 -0700 Subject: [PATCH 19/28] OSSL_LIB_CTX.pod: Tweak the docs for libcontext freeze Reviewed-by: Paul Dale Reviewed-by: Nikola Pajkovsky MergeDate: Wed Mar 4 17:40:10 2026 (Merged from https://github.com/openssl/openssl/pull/30168) --- doc/man3/OSSL_LIB_CTX.pod | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/doc/man3/OSSL_LIB_CTX.pod b/doc/man3/OSSL_LIB_CTX.pod index 0a952cb5a6..d9516195eb 100644 --- a/doc/man3/OSSL_LIB_CTX.pod +++ b/doc/man3/OSSL_LIB_CTX.pod @@ -123,19 +123,31 @@ If ctx is NULL then the function operates on the default library context. OSSL_LIB_CTX_get_data() returns a memory address whose interpretation depends on the index. -OSSL_LIB_CTX_freeze() freezes the method store associated with the library -context. A frozen context will speed up ONLY isolated, uncached algorithm -lookups. This is specifically designed to benefit legacy high-throughput -applications with worker threads doing isolated computations, without requiring -a code restructuring. Existing and new applications are generally recommended to -not use this feature and instead structure the application to pre-initialize -contexts where possible. This function should only be called from a non-threaded -context, before any worker threads have been dispatched. +OSSL_LIB_CTX_freeze() freezes the method store associated with the +library context, and builds a fast lookup cache which will be used by +subsequent EVP calls to fetch implementations. A frozen method store +may no longer be modified, and operations attempting to do so (such as +loading a new provider) will fail. This function must only be called +from a non-threaded context, before any worker threads have been +dispatched. A frozen method store can not be un-frozen, or frozen again. -If propq is NULL, it will only speed up method store operations with a NULL -property query. If propq is not NULL, it will also speed up method store -operations when given that exact property query. Other property queries will go -through the normal, slower lookup method. +OSSL_LIB_CTX_freeze() builds the lookup cache using an optional propq +query string argument. OSSL_LIB_CTX_freeze() will build a cache of +methods for all algorithms for the NULL property query, and if propq +is non-NULL, it will also cache the methods for all algorithms +matching the propq query string. Once frozen, future method store +lookups using the the NULL propq or the provided propq will be +answered from the cache. Any method store lookups for an algorithm +using a different propq query string will not be answered from the +cache, and will be looked up by the normal slower method. + +OSSL_LIB_CTX_freeze() is intended for use where applications with +worker threads are not able to be structured to pre-fetch and retain +the algorithm methods from the method store prior to use, and which +therefore may pay a considerable performance penalty for the method +store lookup every time an algorithm is used. Applications which can +pre-fetch the methods they need before their use should not use +OSSL_LIB_CTX_freeze(). =head1 RETURN VALUES From f457bd93c549f269771eebb6b2762190b6ef0cc4 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 18 Feb 2026 11:27:04 +0100 Subject: [PATCH 20/28] Add operation_id to frozen method store cache key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The frozen method store cache was keyed only by algorithm name and property query, which could cause incorrect cache hits when different operation types (e.g., KEM vs signature) share the same algorithm name. Include the operation_id in the FROZEN_CACHE_KEY and thread it through ossl_frozen_method_store_cache_get(), freeze_alg(), and alg_freeze() so that lookups correctly distinguish between operations on identically named algorithms. Signed-off-by: Nikola Pajkovsky Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Thu Mar 5 17:21:22 2026 (Merged from https://github.com/openssl/openssl/pull/30078) --- crypto/evp/evp_fetch.c | 2 +- crypto/property/property.c | 15 ++++++++++----- include/internal/property.h | 3 ++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c index fc6ba9c307..aae8f45a3a 100644 --- a/crypto/evp/evp_fetch.c +++ b/crypto/evp/evp_fetch.c @@ -361,7 +361,7 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, const char *store_propq = ossl_method_store_frozen_propq(store); if (*propq == '\0' || strcmp(store_propq, propq) == 0) { - ossl_frozen_method_store_cache_get(store, name, propq, &method); + ossl_frozen_method_store_cache_get(store, name, propq, operation_id, &method); return method; } } diff --git a/crypto/property/property.c b/crypto/property/property.c index ec853f48b6..334d5f0eda 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -115,6 +115,7 @@ HT_START_KEY_DEFN(frozen_cache_key) HT_DEF_KEY_FIELD_CHAR_ARRAY(name, 64) /* TODO(FREEZE): allow variable length propq */ HT_DEF_KEY_FIELD_CHAR_ARRAY(propq, 64) +HT_DEF_KEY_FIELD(op_id, unsigned int) HT_END_KEY_DEFN(FROZEN_CACHE_KEY) typedef struct ossl_global_properties_st { @@ -1021,7 +1022,8 @@ end: } int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, - const char *alg_name, const char *prop_query, void **method) + const char *alg_name, const char *prop_query, unsigned int operation_id, + void **method) { FROZEN_CACHE_KEY key; HT_VALUE *val; @@ -1035,6 +1037,7 @@ int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, HT_INIT_KEY(&key); HT_SET_KEY_STRING_CASE(&key, name, alg_name); HT_SET_KEY_STRING(&key, propq, prop_query); + HT_SET_KEY_FIELD(&key, op_id, operation_id); val = ossl_ht_get(store->frozen_algs, TO_HT_KEY(&key)); if (val == NULL) @@ -1062,7 +1065,7 @@ static void frozen_cache_free(HT_VALUE *val) } static int freeze_alg(OSSL_METHOD_STORE *store, ALGORITHM *alg, - const char *propq, const char *alg_name) + const char *propq, const char *alg_name, int operation_id) { int ret = 0; IMPLEMENTATION *best_impl = NULL; @@ -1073,6 +1076,7 @@ static int freeze_alg(OSSL_METHOD_STORE *store, ALGORITHM *alg, HT_INIT_KEY(&key); HT_SET_KEY_STRING_CASE(&key, name, alg_name); HT_SET_KEY_STRING(&key, propq, propq); + HT_SET_KEY_FIELD(&key, op_id, operation_id); if (ossl_ht_get(store->frozen_algs, TO_HT_KEY(&key)) != NULL) return 1; @@ -1102,11 +1106,12 @@ static void alg_freeze(ossl_uintmax_t idx, ALGORITHM *alg, void *arg) struct alg_freeze_st *af = arg; OSSL_NAMEMAP *nm = ossl_namemap_stored(af->store->ctx); int name_id; + unsigned int op_id; const char *name; int i = 0; if (alg == NULL - || !evp_method_id2name_id_op_id(alg->nid, &name_id, NULL)) { + || !evp_method_id2name_id_op_id(alg->nid, &name_id, &op_id)) { af->ret = 0; return; } @@ -1116,11 +1121,11 @@ static void alg_freeze(ossl_uintmax_t idx, ALGORITHM *alg, void *arg) if (name == NULL) break; if (*af->store->frozen_propq != '\0') { - af->ret = freeze_alg(af->store, alg, af->store->frozen_propq, name); + af->ret = freeze_alg(af->store, alg, af->store->frozen_propq, name, op_id); if (!af->ret) return; } - af->ret = freeze_alg(af->store, alg, "", name); + af->ret = freeze_alg(af->store, alg, "", name, op_id); if (!af->ret) return; } diff --git a/include/internal/property.h b/include/internal/property.h index 2980ddd261..2c7b5034ea 100644 --- a/include/internal/property.h +++ b/include/internal/property.h @@ -102,7 +102,8 @@ void ossl_global_properties_stop_mirroring(OSSL_LIB_CTX *libctx); int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq); int ossl_frozen_method_store_cache_get(OSSL_METHOD_STORE *store, - const char *name, const char *prop_query, void **result); + const char *name, const char *prop_query, unsigned int operation_id, + void **result); int ossl_method_store_is_frozen(OSSL_METHOD_STORE *store); const char *ossl_method_store_frozen_propq(OSSL_METHOD_STORE *store); From 6441e227f65ba4133a97b49d13f57253fde6a82e Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 18 Feb 2026 15:06:05 +0100 Subject: [PATCH 21/28] evp: freeze KEYMGMT fetch cache and expand kmgmt fetch tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add frozen-method support for EVP_KEYMGMT fetches by wiring dup/free callbacks and populating KEYMGMT entries during OSSL_LIB_CTX_freeze(). Resolves https://github.com/openssl/project/issues/1876 Signed-off-by: Nikola Pajkovsky Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Thu Mar 5 17:21:24 2026 (Merged from https://github.com/openssl/openssl/pull/30078) --- crypto/evp/evp_local.h | 2 + crypto/evp/keymgmt_meth.c | 77 +++++++-- crypto/property/property.c | 2 + test/evp_fetch_prov_test.c | 219 ++++++++++++++++++++++++++ test/recipes/30-test_evp_fetch_prov.t | 2 +- 5 files changed, 292 insertions(+), 10 deletions(-) diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 30dc68ecf4..874f2e2129 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -97,6 +97,7 @@ struct evp_keymgmt_st { const char *description; OSSL_PROVIDER *prov; CRYPTO_REF_COUNT refcnt; + int origin; /* Constructor(s), destructor, information */ OSSL_FUNC_keymgmt_new_fn *new; @@ -458,3 +459,4 @@ int evp_md_fetch_all(OSSL_LIB_CTX *ctx); int evp_cipher_fetch_all(OSSL_LIB_CTX *ctx); int evp_rand_fetch_all(OSSL_LIB_CTX *ctx); int evp_mac_fetch_all(OSSL_LIB_CTX *ctx); +int evp_keymgmt_fetch_all(OSSL_LIB_CTX *ctx); diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c index f418823c71..23ee404929 100644 --- a/crypto/evp/keymgmt_meth.c +++ b/crypto/evp/keymgmt_meth.c @@ -7,6 +7,7 @@ * https://www.openssl.org/source/license.html */ +#include #include #include #include @@ -22,11 +23,59 @@ static void evp_keymgmt_free(void *data) EVP_KEYMGMT_free(data); } +static void evp_keymgmt_free_int(EVP_KEYMGMT *keymgmt) +{ + OPENSSL_free(keymgmt->type_name); + ossl_provider_free(keymgmt->prov); + CRYPTO_FREE_REF(&keymgmt->refcnt); + OPENSSL_free(keymgmt); +} + static int evp_keymgmt_up_ref(void *data) { return EVP_KEYMGMT_up_ref(data); } +static void *evp_keymgmt_dup_frozen(void *vin) +{ + EVP_KEYMGMT *in = vin; + EVP_KEYMGMT *out; + + out = OPENSSL_malloc(sizeof(*out)); + if (out == NULL) + return NULL; + memcpy(out, in, sizeof(*out)); + if (!CRYPTO_NEW_REF(&out->refcnt, 1)) + goto err; + out->type_name = OPENSSL_strdup(in->type_name); + if (out->type_name == NULL) + goto err; + out->origin = EVP_ORIG_FROZEN; + if (out->prov != NULL && !ossl_provider_up_ref(out->prov)) { + OPENSSL_free(out->type_name); + goto err; + } + return out; +err: + CRYPTO_FREE_REF(&out->refcnt); + OPENSSL_free(out); + return NULL; +} + +static void evp_keymgmt_frozen_free(void *vin) +{ + EVP_KEYMGMT *keymgmt = vin; + int ref = 0; + + if (keymgmt == NULL || keymgmt->origin != EVP_ORIG_FROZEN) + return; + + CRYPTO_DOWN_REF(&keymgmt->refcnt, &ref); + if (ref > 0) + return; + evp_keymgmt_free_int(keymgmt); +} + static void *keymgmt_new(void) { EVP_KEYMGMT *keymgmt = NULL; @@ -275,8 +324,18 @@ EVP_KEYMGMT *evp_keymgmt_fetch_from_prov(OSSL_PROVIDER *prov, keymgmt_from_algorithm, evp_keymgmt_up_ref, evp_keymgmt_free, - NULL, - NULL); + evp_keymgmt_dup_frozen, + evp_keymgmt_frozen_free); +} + +int evp_keymgmt_fetch_all(OSSL_LIB_CTX *ctx) +{ + return evp_generic_fetch_all(ctx, OSSL_OP_KEYMGMT, + keymgmt_from_algorithm, + evp_keymgmt_up_ref, + evp_keymgmt_free, + evp_keymgmt_dup_frozen, + evp_keymgmt_frozen_free); } EVP_KEYMGMT *EVP_KEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, @@ -285,14 +344,17 @@ EVP_KEYMGMT *EVP_KEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, return evp_generic_fetch(ctx, OSSL_OP_KEYMGMT, algorithm, properties, keymgmt_from_algorithm, evp_keymgmt_up_ref, - evp_keymgmt_free, NULL, NULL); + evp_keymgmt_free, + evp_keymgmt_dup_frozen, + evp_keymgmt_frozen_free); } int EVP_KEYMGMT_up_ref(EVP_KEYMGMT *keymgmt) { int ref = 0; - CRYPTO_UP_REF(&keymgmt->refcnt, &ref); + if (keymgmt->origin == EVP_ORIG_DYNAMIC) + CRYPTO_UP_REF(&keymgmt->refcnt, &ref); return 1; } @@ -300,16 +362,13 @@ void EVP_KEYMGMT_free(EVP_KEYMGMT *keymgmt) { int ref = 0; - if (keymgmt == NULL) + if (keymgmt == NULL || keymgmt->origin != EVP_ORIG_DYNAMIC) return; CRYPTO_DOWN_REF(&keymgmt->refcnt, &ref); if (ref > 0) return; - OPENSSL_free(keymgmt->type_name); - ossl_provider_free(keymgmt->prov); - CRYPTO_FREE_REF(&keymgmt->refcnt); - OPENSSL_free(keymgmt); + evp_keymgmt_free_int(keymgmt); } const OSSL_PROVIDER *EVP_KEYMGMT_get0_provider(const EVP_KEYMGMT *keymgmt) diff --git a/crypto/property/property.c b/crypto/property/property.c index 334d5f0eda..23edfed23d 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -1176,6 +1176,8 @@ int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq) goto err; if (evp_mac_fetch_all(store->ctx) <= 0) goto err; + if (evp_keymgmt_fetch_all(store->ctx) <= 0) + goto err; ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_freeze, &af); if (af.ret <= 0) diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index cb00128d58..ca165544fd 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -522,6 +522,220 @@ static int test_cipher(const EVP_CIPHER *cipher) && TEST_true(encrypt_decrypt(cipher, testmsg, sizeof(testmsg))); } +static int test_ec_keyexch(OSSL_LIB_CTX *libctx, const char *propq) +{ + EVP_PKEY_CTX *gctx1 = NULL, *gctx2 = NULL, *dctx = NULL; + EVP_PKEY *key1 = NULL, *key2 = NULL; + unsigned char secret1[256], secret2[256]; + size_t secret1_len = 0, secret2_len = 0; + OSSL_PARAM params[2]; + int ret = 0; + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, + "prime256v1", 0); + params[1] = OSSL_PARAM_construct_end(); + + if (!TEST_ptr(gctx1 = EVP_PKEY_CTX_new_from_name(libctx, "EC", propq)) + || !TEST_true(EVP_PKEY_keygen_init(gctx1)) + || !TEST_true(EVP_PKEY_CTX_set_params(gctx1, params)) + || !TEST_true(EVP_PKEY_keygen(gctx1, &key1)) + || !TEST_ptr(gctx2 = EVP_PKEY_CTX_new_from_name(libctx, "EC", propq)) + || !TEST_true(EVP_PKEY_keygen_init(gctx2)) + || !TEST_true(EVP_PKEY_CTX_set_params(gctx2, params)) + || !TEST_true(EVP_PKEY_keygen(gctx2, &key2)) + || !TEST_ptr(dctx = EVP_PKEY_CTX_new_from_pkey(libctx, key1, propq)) + || !TEST_true(EVP_PKEY_derive_init(dctx)) + || !TEST_true(EVP_PKEY_derive_set_peer(dctx, key2)) + || !TEST_true(EVP_PKEY_derive(dctx, NULL, &secret1_len)) + || !TEST_size_t_le(secret1_len, sizeof(secret1)) + || !TEST_true(EVP_PKEY_derive(dctx, secret1, &secret1_len))) + goto end; + + EVP_PKEY_CTX_free(dctx); + dctx = NULL; + + if (!TEST_ptr(dctx = EVP_PKEY_CTX_new_from_pkey(libctx, key2, propq)) + || !TEST_true(EVP_PKEY_derive_init(dctx)) + || !TEST_true(EVP_PKEY_derive_set_peer(dctx, key1)) + || !TEST_true(EVP_PKEY_derive(dctx, NULL, &secret2_len)) + || !TEST_size_t_le(secret2_len, sizeof(secret2)) + || !TEST_true(EVP_PKEY_derive(dctx, secret2, &secret2_len)) + || !TEST_size_t_eq(secret1_len, secret2_len) + || !TEST_mem_eq(secret1, secret1_len, secret2, secret2_len)) + goto end; + + ret = 1; +end: + EVP_PKEY_CTX_free(dctx); + EVP_PKEY_CTX_free(gctx1); + EVP_PKEY_CTX_free(gctx2); + EVP_PKEY_free(key1); + EVP_PKEY_free(key2); + return ret; +} + +static int test_keymgmt(OSSL_LIB_CTX *libctx, const char *propq, + const EVP_KEYMGMT *keymgmt, const char *name) +{ + return TEST_ptr(keymgmt) + && TEST_ptr(EVP_KEYMGMT_get0_provider(keymgmt)) + && TEST_true(EVP_KEYMGMT_is_a(keymgmt, name)) + && test_ec_keyexch(libctx, propq); +} + +static int test_EVP_KEYMGMT_fetch_freeze(void) +{ +#if defined(OPENSSL_NO_CACHED_FETCH) || defined(OPENSSL_NO_EC) + /* + * Test does not make sense if cached fetch is disabled. + * There's nothing to freeze, and test will fail. + */ + return 1; +#endif + + EVP_KEYMGMT *keymgmt = NULL; + int ret = 0; + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(keymgmt = EVP_KEYMGMT_fetch(ctx, "EC", NULL)) + || !TEST_true(test_keymgmt(ctx, NULL, keymgmt, "EC")) + || !TEST_int_ne(keymgmt->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_KEYMGMT_free(keymgmt); + keymgmt = NULL; + + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) + || !TEST_ptr(keymgmt = EVP_KEYMGMT_fetch(ctx, "EC", NULL)) + || !TEST_true(test_keymgmt(ctx, NULL, keymgmt, "EC")) + || !TEST_int_eq(keymgmt->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_KEYMGMT_free(keymgmt); + keymgmt = NULL; + + if (!TEST_ptr(keymgmt = EVP_KEYMGMT_fetch(ctx, "EC", "?fips=true")) + || !TEST_true(test_keymgmt(ctx, "?fips=true", keymgmt, "EC")) + || !TEST_int_eq(keymgmt->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_KEYMGMT_free(keymgmt); + keymgmt = NULL; + + if (!TEST_ptr(keymgmt = EVP_KEYMGMT_fetch(ctx, "RSA", "?fips=true")) + || !TEST_int_eq(keymgmt->origin, EVP_ORIG_FROZEN)) + goto err; + if (!TEST_ptr(keymgmt = EVP_KEYMGMT_fetch(ctx, "RSA", NULL)) + || !TEST_int_eq(keymgmt->origin, EVP_ORIG_FROZEN)) + goto err; + + /* + * A mismatched propq should use the regular fetch path rather than the + * frozen fast path. + */ + if (!TEST_ptr(keymgmt = EVP_KEYMGMT_fetch(ctx, "EC", "?provider=default")) + || !TEST_true(test_keymgmt(ctx, "?provider=default", keymgmt, "EC")) + || !TEST_int_ne(keymgmt->origin, EVP_ORIG_FROZEN)) + goto err; + + ret = 1; +err: + EVP_KEYMGMT_free(keymgmt); + unload_providers(&ctx, prov); + return ret; +} + +static int test_implicit_EVP_KEYMGMT_fetch(void) +{ +#if defined(OPENSSL_NO_EC) + return 1; +#endif + + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + ret = test_ec_keyexch(ctx, NULL); +err: + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_KEYMGMT_fetch(const char *id) +{ +#if defined(OPENSSL_NO_EC) + return 1; +#endif + + OSSL_LIB_CTX *ctx = NULL; + EVP_KEYMGMT *keymgmt = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + keymgmt = EVP_KEYMGMT_fetch(ctx, id, fetch_property); + if (expected_fetch_result != 0) { + if (!test_keymgmt(ctx, fetch_property, keymgmt, id)) + goto err; + + if (!TEST_true(EVP_KEYMGMT_up_ref(keymgmt))) + goto err; + /* Ref count should now be 2. Release first one here */ + EVP_KEYMGMT_free(keymgmt); + } else { + if (!TEST_ptr_null(keymgmt)) + goto err; + } + ret = 1; +err: + EVP_KEYMGMT_free(keymgmt); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_KEYMGMT_fetch_by_name(void) +{ + return test_explicit_EVP_KEYMGMT_fetch("EC"); +} + +static int test_explicit_EVP_KEYMGMT_fetch_by_X509_ALGOR(int idx) +{ +#if defined(OPENSSL_NO_EC) + return 1; +#endif + + int ret = 0; + X509_ALGOR *algor = make_algor(NID_X9_62_id_ecPublicKey); + const ASN1_OBJECT *obj; + char id[OSSL_MAX_NAME_SIZE] = { 0 }; + + if (algor == NULL) + return 0; + + X509_ALGOR_get0(&obj, NULL, NULL, algor); + switch (idx) { + case 0: + if (!TEST_int_gt(OBJ_obj2txt(id, sizeof(id), obj, 0), 0)) + goto end; + break; + case 1: + if (!TEST_int_gt(OBJ_obj2txt(id, sizeof(id), obj, 1), 0)) + goto end; + break; + } + + ret = test_explicit_EVP_KEYMGMT_fetch(id); +end: + X509_ALGOR_free(algor); + return ret; +} + static int test_EVP_CIPHER_fetch_freeze(void) { #if defined(OPENSSL_NO_CACHED_FETCH) @@ -818,6 +1032,11 @@ int setup_tests(void) ADD_TEST(test_EVP_MAC_fetch_freeze); ADD_TEST(test_implicit_EVP_MAC_fetch); ADD_TEST(test_explicit_EVP_MAC_fetch_by_name); + } else if (strcmp(alg, "kmgmt") == 0) { + ADD_TEST(test_EVP_KEYMGMT_fetch_freeze); + ADD_TEST(test_implicit_EVP_KEYMGMT_fetch); + ADD_TEST(test_explicit_EVP_KEYMGMT_fetch_by_name); + ADD_ALL_TESTS_NOSUBTEST(test_explicit_EVP_KEYMGMT_fetch_by_X509_ALGOR, 2); } else { TEST_error("Unknown fetch type: %s", alg); return 0; diff --git a/test/recipes/30-test_evp_fetch_prov.t b/test/recipes/30-test_evp_fetch_prov.t index b6d4841ab5..090bf38480 100644 --- a/test/recipes/30-test_evp_fetch_prov.t +++ b/test/recipes/30-test_evp_fetch_prov.t @@ -21,7 +21,7 @@ use lib bldtop_dir('.'); my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); -my @types = ( "digest", "cipher", "rand", "mac" ); +my @types = ( "digest", "cipher", "rand", "mac", "kmgmt" ); my @testdata = ( { config => srctop_file("test", "default.cnf"), From 19056245f507bb33878f20ca678d055aadd47dc5 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Wed, 18 Feb 2026 16:56:26 +0100 Subject: [PATCH 22/28] evp: freeze KEM fetch cache and expand KEM fetch tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add frozen-method support for EVP_KEM fetches by wiring dup/free callbacks and populating KEM entries during OSSL_LIB_CTX_freeze(). https://github.com/openssl/project/issues/1833 Signed-off-by: Nikola Pajkovsky Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Thu Mar 5 17:21:25 2026 (Merged from https://github.com/openssl/openssl/pull/30078) --- crypto/evp/evp_local.h | 2 + crypto/evp/kem.c | 78 ++++++++-- crypto/property/property.c | 2 + test/evp_fetch_prov_test.c | 200 ++++++++++++++++++++++++++ test/recipes/30-test_evp_fetch_prov.t | 2 +- 5 files changed, 274 insertions(+), 10 deletions(-) diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 874f2e2129..6c6ba0a5dc 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -273,6 +273,7 @@ struct evp_asym_cipher_st { struct evp_kem_st { int name_id; + int origin; char *type_name; const char *description; OSSL_PROVIDER *prov; @@ -460,3 +461,4 @@ int evp_cipher_fetch_all(OSSL_LIB_CTX *ctx); int evp_rand_fetch_all(OSSL_LIB_CTX *ctx); int evp_mac_fetch_all(OSSL_LIB_CTX *ctx); int evp_keymgmt_fetch_all(OSSL_LIB_CTX *ctx); +int evp_kem_fetch_all(OSSL_LIB_CTX *ctx); diff --git a/crypto/evp/kem.c b/crypto/evp/kem.c index 101890b4c4..3e6de42a2c 100644 --- a/crypto/evp/kem.c +++ b/crypto/evp/kem.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include "internal/cryptlib.h" @@ -22,11 +23,60 @@ static void evp_kem_free(void *data) EVP_KEM_free(data); } +static void evp_kem_free_int(EVP_KEM *kem) +{ + OPENSSL_free(kem->type_name); + ossl_provider_free(kem->prov); + CRYPTO_FREE_REF(&kem->refcnt); + OPENSSL_free(kem); +} + static int evp_kem_up_ref(void *data) { return EVP_KEM_up_ref(data); } +static void *evp_kem_dup_frozen(void *vin) +{ + EVP_KEM *in = vin; + EVP_KEM *out; + + out = OPENSSL_malloc(sizeof(*out)); + if (out == NULL) + return NULL; + memcpy(out, in, sizeof(*out)); + if (!CRYPTO_NEW_REF(&out->refcnt, 1)) + goto err; + out->type_name = OPENSSL_strdup(in->type_name); + if (out->type_name == NULL) + goto err; + out->origin = EVP_ORIG_FROZEN; + if (out->prov != NULL && !ossl_provider_up_ref(out->prov)) { + OPENSSL_free(out->type_name); + goto err; + } + return out; + +err: + CRYPTO_FREE_REF(&out->refcnt); + OPENSSL_free(out); + return NULL; +} + +static void evp_kem_frozen_free(void *vin) +{ + EVP_KEM *kem = vin; + int ref = 0; + + if (kem == NULL || kem->origin != EVP_ORIG_FROZEN) + return; + + CRYPTO_DOWN_REF(&kem->refcnt, &ref); + if (ref > 0) + return; + evp_kem_free_int(kem); +} + static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation, const OSSL_PARAM params[], EVP_PKEY *authkey) { @@ -432,23 +482,21 @@ void EVP_KEM_free(EVP_KEM *kem) { int i; - if (kem == NULL) + if (kem == NULL || kem->origin != EVP_ORIG_DYNAMIC) return; CRYPTO_DOWN_REF(&kem->refcnt, &i); if (i > 0) return; - OPENSSL_free(kem->type_name); - ossl_provider_free(kem->prov); - CRYPTO_FREE_REF(&kem->refcnt); - OPENSSL_free(kem); + evp_kem_free_int(kem); } int EVP_KEM_up_ref(EVP_KEM *kem) { int ref = 0; - CRYPTO_UP_REF(&kem->refcnt, &ref); + if (kem->origin == EVP_ORIG_DYNAMIC) + CRYPTO_UP_REF(&kem->refcnt, &ref); return 1; } @@ -463,7 +511,9 @@ EVP_KEM *EVP_KEM_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, return evp_generic_fetch(ctx, OSSL_OP_KEM, algorithm, properties, evp_kem_from_algorithm, evp_kem_up_ref, - evp_kem_free, NULL, NULL); + evp_kem_free, + evp_kem_dup_frozen, + evp_kem_frozen_free); } EVP_KEM *evp_kem_fetch_from_prov(OSSL_PROVIDER *prov, const char *algorithm, @@ -473,8 +523,18 @@ EVP_KEM *evp_kem_fetch_from_prov(OSSL_PROVIDER *prov, const char *algorithm, evp_kem_from_algorithm, evp_kem_up_ref, evp_kem_free, - NULL, - NULL); + evp_kem_dup_frozen, + evp_kem_frozen_free); +} + +int evp_kem_fetch_all(OSSL_LIB_CTX *ctx) +{ + return evp_generic_fetch_all(ctx, OSSL_OP_KEM, + evp_kem_from_algorithm, + evp_kem_up_ref, + evp_kem_free, + evp_kem_dup_frozen, + evp_kem_frozen_free); } int EVP_KEM_is_a(const EVP_KEM *kem, const char *name) diff --git a/crypto/property/property.c b/crypto/property/property.c index 23edfed23d..ccba87a78c 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -1178,6 +1178,8 @@ int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq) goto err; if (evp_keymgmt_fetch_all(store->ctx) <= 0) goto err; + if (evp_kem_fetch_all(store->ctx) <= 0) + goto err; ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_freeze, &af); if (af.ret <= 0) diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index ca165544fd..9ca05ac08e 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -583,6 +583,66 @@ static int test_keymgmt(OSSL_LIB_CTX *libctx, const char *propq, && test_ec_keyexch(libctx, propq); } +static int test_ml_kem_keyexch(OSSL_LIB_CTX *libctx, const char *propq) +{ + int ret = 0; + EVP_PKEY *privkey = NULL; + EVP_PKEY_CTX *pctx = NULL; + unsigned char *ciphertext = NULL, *secret_enc = NULL, *secret_dec = NULL; + size_t ciphertext_len = 0, secret_enc_len = 0, secret_dec_len = 0; + + if (!TEST_ptr(privkey = EVP_PKEY_Q_keygen(libctx, propq, "ML-KEM-768")) + || !TEST_ptr(pctx = EVP_PKEY_CTX_new_from_pkey(libctx, privkey, propq)) + || !TEST_int_eq(EVP_PKEY_encapsulate_init(pctx, NULL), 1) + || !TEST_int_eq(EVP_PKEY_encapsulate(pctx, NULL, &ciphertext_len, NULL, &secret_enc_len), 1)) + goto err; + + ciphertext = OPENSSL_zalloc(ciphertext_len); + secret_enc = OPENSSL_zalloc(secret_enc_len); + if (!TEST_ptr(ciphertext) + || !TEST_ptr(secret_enc) + || !TEST_int_gt(EVP_PKEY_encapsulate(pctx, ciphertext, &ciphertext_len, + secret_enc, &secret_enc_len), + 0)) + goto err; + + EVP_PKEY_CTX_free(pctx); + pctx = NULL; + + if (!TEST_ptr(pctx = EVP_PKEY_CTX_new_from_pkey(libctx, privkey, propq)) + || !TEST_int_eq(EVP_PKEY_decapsulate_init(pctx, NULL), 1) + || !TEST_int_eq(EVP_PKEY_decapsulate(pctx, NULL, &secret_dec_len, ciphertext, ciphertext_len), 1) + || !TEST_size_t_eq(secret_dec_len, secret_enc_len)) + goto err; + + secret_dec = OPENSSL_zalloc(secret_dec_len); + if (!TEST_ptr(secret_dec) + || !TEST_int_gt(EVP_PKEY_decapsulate(pctx, secret_dec, &secret_dec_len, + ciphertext, ciphertext_len), + 0) + || !TEST_size_t_eq(secret_dec_len, secret_enc_len) + || !TEST_mem_eq(secret_dec, secret_dec_len, secret_enc, secret_enc_len)) + goto err; + ret = 1; +err: + EVP_PKEY_free(privkey); + OPENSSL_free(ciphertext); + OPENSSL_free(secret_enc); + OPENSSL_free(secret_dec); + EVP_PKEY_CTX_free(pctx); + + return ret; +} + +static int test_kem(OSSL_LIB_CTX *libctx, const char *propq, + const EVP_KEM *kem, const char *name) +{ + return TEST_ptr(kem) + && TEST_ptr(EVP_KEM_get0_provider(kem)) + && TEST_true(EVP_KEM_is_a(kem, name)) + && test_ml_kem_keyexch(libctx, propq); +} + static int test_EVP_KEYMGMT_fetch_freeze(void) { #if defined(OPENSSL_NO_CACHED_FETCH) || defined(OPENSSL_NO_EC) @@ -736,6 +796,142 @@ end: return ret; } +static int test_EVP_KEM_fetch_freeze(void) +{ +#if defined(OPENSSL_NO_CACHED_FETCH) || defined(OPENSSL_NO_ML_KEM) + /* + * Test does not make sense if cached fetch is disabled. + * There's nothing to freeze, and test will fail. + */ + return 1; +#endif + + EVP_KEM *kem = NULL; + int ret = 0; + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(kem = EVP_KEM_fetch(ctx, "ML-KEM-768", NULL)) + || !TEST_true(test_kem(ctx, NULL, kem, "ML-KEM-768")) + || !TEST_int_ne(kem->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_KEM_free(kem); + kem = NULL; + + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) + || !TEST_ptr(kem = EVP_KEM_fetch(ctx, "ML-KEM-768", NULL)) + || !TEST_true(test_kem(ctx, NULL, kem, "ML-KEM-768")) + || !TEST_int_eq(kem->origin, EVP_ORIG_FROZEN)) + goto err; + /* Technically, frozen version doesn't need to be freed */ + EVP_KEM_free(kem); + kem = NULL; + + if (!TEST_ptr(kem = EVP_KEM_fetch(ctx, "ML-KEM-768", "?fips=true")) + || !TEST_true(test_kem(ctx, "?fips=true", kem, "ML-KEM-768")) + || !TEST_int_eq(kem->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_KEM_free(kem); + kem = NULL; + + /* + * A mismatched propq should use the regular fetch path rather than the + * frozen fast path. + */ + if (!TEST_ptr(kem = EVP_KEM_fetch(ctx, "ML-KEM-768", "?provider=default")) + || !TEST_true(test_kem(ctx, "?provider=default", kem, "ML-KEM-768")) + || !TEST_int_ne(kem->origin, EVP_ORIG_FROZEN)) + goto err; + + ret = 1; +err: + EVP_KEM_free(kem); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_KEM_fetch(const char *id) +{ +#if defined(OPENSSL_NO_ML_KEM) + return 1; +#endif + + OSSL_LIB_CTX *ctx = NULL; + EVP_KEM *kem = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + kem = EVP_KEM_fetch(ctx, id, fetch_property); + if (expected_fetch_result != 0) { + if (!test_kem(ctx, fetch_property, kem, id)) + goto err; + + if (!TEST_true(EVP_KEM_up_ref(kem))) + goto err; + /* Ref count should now be 2. Release first one here */ + EVP_KEM_free(kem); + } else { + if (!TEST_ptr_null(kem)) + goto err; + } + ret = 1; +err: + EVP_KEM_free(kem); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_KEM_fetch_by_name(void) +{ +#if defined(OPENSSL_NO_ML_KEM) + return 1; +#endif + + return test_explicit_EVP_KEM_fetch("ML-KEM-768"); +} + +/* + * idx 0: Allow names from OBJ_obj2txt() + * idx 1: Force an OID in text form from OBJ_obj2txt() + */ +static int test_explicit_EVP_KEM_fetch_by_X509_ALGOR(int idx) +{ +#if defined(OPENSSL_NO_ML_KEM) + return 1; +#endif + + int ret = 0; + X509_ALGOR *algor = make_algor(NID_ML_KEM_768); + const ASN1_OBJECT *obj; + char id[OSSL_MAX_NAME_SIZE] = { 0 }; + + if (algor == NULL) + return 0; + + X509_ALGOR_get0(&obj, NULL, NULL, algor); + switch (idx) { + case 0: + if (!TEST_int_gt(OBJ_obj2txt(id, sizeof(id), obj, 0), 0)) + goto end; + break; + case 1: + if (!TEST_int_gt(OBJ_obj2txt(id, sizeof(id), obj, 1), 0)) + goto end; + break; + } + + ret = test_explicit_EVP_KEM_fetch(id); +end: + X509_ALGOR_free(algor); + return ret; +} + static int test_EVP_CIPHER_fetch_freeze(void) { #if defined(OPENSSL_NO_CACHED_FETCH) @@ -1037,6 +1233,10 @@ int setup_tests(void) ADD_TEST(test_implicit_EVP_KEYMGMT_fetch); ADD_TEST(test_explicit_EVP_KEYMGMT_fetch_by_name); ADD_ALL_TESTS_NOSUBTEST(test_explicit_EVP_KEYMGMT_fetch_by_X509_ALGOR, 2); + } else if (strcmp(alg, "kem") == 0) { + ADD_TEST(test_EVP_KEM_fetch_freeze); + ADD_TEST(test_explicit_EVP_KEM_fetch_by_name); + ADD_ALL_TESTS_NOSUBTEST(test_explicit_EVP_KEM_fetch_by_X509_ALGOR, 2); } else { TEST_error("Unknown fetch type: %s", alg); return 0; diff --git a/test/recipes/30-test_evp_fetch_prov.t b/test/recipes/30-test_evp_fetch_prov.t index 090bf38480..e429d8882d 100644 --- a/test/recipes/30-test_evp_fetch_prov.t +++ b/test/recipes/30-test_evp_fetch_prov.t @@ -21,7 +21,7 @@ use lib bldtop_dir('.'); my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); -my @types = ( "digest", "cipher", "rand", "mac", "kmgmt" ); +my @types = ( "digest", "cipher", "rand", "mac", "kmgmt", "kem" ); my @testdata = ( { config => srctop_file("test", "default.cnf"), From a21cbce238cbe807445667731973d2007495b097 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Thu, 19 Feb 2026 12:45:39 +0100 Subject: [PATCH 23/28] evp: freeze KDF fetch cache and expand KDF fetch tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add frozen-method support for EVP_KDF fetches by wiring dup/free callbacks and populating KDF entries during OSSL_LIB_CTX_freeze(). perftools shows no performance gains $ ./evp_kdf -o evp_isolated 64 -f Average time per computation: 8181.213888us $ ./evp_kdf -o evp_isolated 64 Average time per computation: 8307.157135us Resolves https://github.com/openssl/project/issues/1834 Signed-off-by: Nikola Pajkovsky Reviewed-by: Saša Nedvědický Reviewed-by: Norbert Pocs MergeDate: Wed Mar 11 12:15:18 2026 (Merged from https://github.com/openssl/openssl/pull/30081) --- crypto/evp/evp_local.h | 1 + crypto/evp/kdf_meth.c | 71 ++++++++++-- crypto/property/property.c | 9 +- include/crypto/evp.h | 1 + test/evp_fetch_prov_test.c | 159 ++++++++++++++++++++++++++ test/recipes/30-test_evp_fetch_prov.t | 2 +- 6 files changed, 230 insertions(+), 13 deletions(-) diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 6c6ba0a5dc..49a62f4276 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -458,6 +458,7 @@ int evp_method_id2name_id_op_id(uint32_t meth_id, int *name_id, unsigned int *operation_id); int evp_md_fetch_all(OSSL_LIB_CTX *ctx); int evp_cipher_fetch_all(OSSL_LIB_CTX *ctx); +int evp_kdf_fetch_all(OSSL_LIB_CTX *ctx); int evp_rand_fetch_all(OSSL_LIB_CTX *ctx); int evp_mac_fetch_all(OSSL_LIB_CTX *ctx); int evp_keymgmt_fetch_all(OSSL_LIB_CTX *ctx); diff --git a/crypto/evp/kdf_meth.c b/crypto/evp/kdf_meth.c index 3036ba75ec..bbf045b250 100644 --- a/crypto/evp/kdf_meth.c +++ b/crypto/evp/kdf_meth.c @@ -12,17 +12,27 @@ #include #include #include +#include #include "internal/provider.h" #include "internal/core.h" #include "crypto/evp.h" #include "evp_local.h" +static void evp_kdf_free_int(EVP_KDF *kdf) +{ + OPENSSL_free(kdf->type_name); + ossl_provider_free(kdf->prov); + CRYPTO_FREE_REF(&kdf->refcnt); + OPENSSL_free(kdf); +} + static int evp_kdf_up_ref(void *vkdf) { EVP_KDF *kdf = (EVP_KDF *)vkdf; int ref = 0; - CRYPTO_UP_REF(&kdf->refcnt, &ref); + if (kdf->origin == EVP_ORIG_DYNAMIC) + CRYPTO_UP_REF(&kdf->refcnt, &ref); return 1; } @@ -31,16 +41,52 @@ static void evp_kdf_free(void *vkdf) EVP_KDF *kdf = (EVP_KDF *)vkdf; int ref = 0; - if (kdf == NULL) + if (kdf == NULL || kdf->origin != EVP_ORIG_DYNAMIC) return; CRYPTO_DOWN_REF(&kdf->refcnt, &ref); if (ref > 0) return; - OPENSSL_free(kdf->type_name); - ossl_provider_free(kdf->prov); - CRYPTO_FREE_REF(&kdf->refcnt); - OPENSSL_free(kdf); + evp_kdf_free_int(kdf); +} + +static void *evp_kdf_dup_frozen(void *vin) +{ + EVP_KDF *in = vin; + EVP_KDF *out; + + out = OPENSSL_malloc(sizeof(*out)); + if (out == NULL) + return NULL; + memcpy(out, in, sizeof(*out)); + if (!CRYPTO_NEW_REF(&out->refcnt, 1)) + goto err; + out->type_name = OPENSSL_strdup(in->type_name); + if (out->type_name == NULL) + goto err; + out->origin = EVP_ORIG_FROZEN; + if (out->prov == NULL || !ossl_provider_up_ref(out->prov)) { + OPENSSL_free(out->type_name); + goto err; + } + return out; +err: + CRYPTO_FREE_REF(&out->refcnt); + OPENSSL_free(out); + return NULL; +} + +static void evp_kdf_frozen_free(EVP_KDF *kdf) +{ + int ref; + + if (kdf == NULL || kdf->origin != EVP_ORIG_FROZEN) + return; + + CRYPTO_DOWN_REF(&kdf->refcnt, &ref); + if (ref > 0) + return; + evp_kdf_free_int(kdf); } static void *evp_kdf_new(void) @@ -171,7 +217,18 @@ EVP_KDF *EVP_KDF_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, { return evp_generic_fetch(libctx, OSSL_OP_KDF, algorithm, properties, evp_kdf_from_algorithm, evp_kdf_up_ref, - evp_kdf_free, NULL, NULL); + evp_kdf_free, evp_kdf_dup_frozen, + (void (*)(void *))evp_kdf_frozen_free); +} + +int evp_kdf_fetch_all(OSSL_LIB_CTX *ctx) +{ + return evp_generic_fetch_all(ctx, OSSL_OP_KDF, + evp_kdf_from_algorithm, + evp_kdf_up_ref, + evp_kdf_free, + evp_kdf_dup_frozen, + (void (*)(void *))evp_kdf_frozen_free); } int EVP_KDF_up_ref(EVP_KDF *kdf) diff --git a/crypto/property/property.c b/crypto/property/property.c index ccba87a78c..f99bef4fa9 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -1172,11 +1172,10 @@ int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq) if (evp_md_fetch_all(store->ctx) <= 0 || evp_cipher_fetch_all(store->ctx) <= 0 - || evp_rand_fetch_all(store->ctx) <= 0) - goto err; - if (evp_mac_fetch_all(store->ctx) <= 0) - goto err; - if (evp_keymgmt_fetch_all(store->ctx) <= 0) + || evp_rand_fetch_all(store->ctx) <= 0 + || evp_mac_fetch_all(store->ctx) <= 0 + || evp_keymgmt_fetch_all(store->ctx) <= 0 + || evp_kdf_fetch_all(store->ctx) <= 0) goto err; if (evp_kem_fetch_all(store->ctx) <= 0) goto err; diff --git a/include/crypto/evp.h b/include/crypto/evp.h index 7ede5c5a0c..805ae38ff9 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -231,6 +231,7 @@ struct evp_mac_st { struct evp_kdf_st { OSSL_PROVIDER *prov; int name_id; + int origin; char *type_name; const char *description; CRYPTO_REF_COUNT refcnt; diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index 9ca05ac08e..c5b1e9c6ad 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "internal/sizes.h" #include "testutil.h" @@ -486,6 +487,160 @@ static int test_explicit_EVP_MAC_fetch_by_name(void) return test_explicit_EVP_MAC_fetch("HMAC"); } +static int derive_pbkdf2(EVP_KDF *kdf) +{ + int ret = 0; + EVP_KDF_CTX *ctx = NULL; + unsigned char out[25]; + static const unsigned char password[] = "passwordPASSWORDpassword"; + static const unsigned char salt[] = "saltSALTsaltSALTsaltSALTsaltSALTsalt"; + unsigned int iterations = 4096; + int mode = 0; + OSSL_PARAM params[6], *p = params; + static const unsigned char expected[sizeof(out)] = { + 0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f, + 0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf, + 0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18, + 0x1c + }; + + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD, + (void *)password, + sizeof(password) - 1); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, + (void *)salt, sizeof(salt) - 1); + *p++ = OSSL_PARAM_construct_uint(OSSL_KDF_PARAM_ITER, &iterations); + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, "sha256", 0); + *p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_PKCS5, &mode); + *p = OSSL_PARAM_construct_end(); + + if (!TEST_ptr(ctx = EVP_KDF_CTX_new(kdf)) + || !TEST_int_gt(EVP_KDF_derive(ctx, out, sizeof(out), params), 0) + || !TEST_mem_eq(out, sizeof(out), expected, sizeof(expected))) + goto err; + ret = 1; +err: + EVP_KDF_CTX_free(ctx); + return ret; +} + +static int test_kdf(EVP_KDF *kdf, const char *name) +{ + return TEST_ptr(kdf) + && TEST_ptr(EVP_KDF_get0_provider(kdf)) + && TEST_true(EVP_KDF_is_a(kdf, name)) + && TEST_true(derive_pbkdf2(kdf)); +} + +static int test_implicit_EVP_KDF_fetch(void) +{ + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + EVP_KDF *kdf = NULL; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(kdf = EVP_KDF_fetch(ctx, OSSL_KDF_NAME_PBKDF2, NULL)) + || !TEST_true(test_kdf(kdf, OSSL_KDF_NAME_PBKDF2))) + goto err; + ret = 1; +err: + EVP_KDF_free(kdf); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_KDF_fetch(const char *id) +{ + OSSL_LIB_CTX *ctx = NULL; + EVP_KDF *kdf = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + kdf = EVP_KDF_fetch(ctx, id, fetch_property); + if (expected_fetch_result != 0) { + if (!test_kdf(kdf, id)) + goto err; + + if (!TEST_true(EVP_KDF_up_ref(kdf))) + goto err; + /* Ref count should now be 2. Release first one here */ + EVP_KDF_free(kdf); + } else { + if (!TEST_ptr_null(kdf)) + goto err; + } + ret = 1; +err: + EVP_KDF_free(kdf); + unload_providers(&ctx, prov); + return ret; +} + +static int test_EVP_KDF_fetch_freeze(void) +{ +#if defined(OPENSSL_NO_CACHED_FETCH) + /* + * Test does not make sense if cached fetch is disabled. + * There's nothing to freeze, and test will fail. + */ + return 1; +#endif + + EVP_KDF *kdf = NULL; + int ret = 0; + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(kdf = EVP_KDF_fetch(ctx, "PBKDF2", NULL)) + || !TEST_true(test_kdf(kdf, "PBKDF2")) + || !TEST_int_ne(kdf->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_KDF_free(kdf); + kdf = NULL; + + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) + || !TEST_ptr(kdf = EVP_KDF_fetch(ctx, "PBKDF2", NULL)) + || !TEST_true(test_kdf(kdf, "PBKDF2")) + || !TEST_int_eq(kdf->origin, EVP_ORIG_FROZEN)) + goto err; + /* Technically, frozen version doesn't need to be freed */ + EVP_KDF_free(kdf); + kdf = NULL; + + if (!TEST_ptr(kdf = EVP_KDF_fetch(ctx, "PBKDF2", "?fips=true")) + || !TEST_true(test_kdf(kdf, "PBKDF2")) + || !TEST_int_eq(kdf->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_KDF_free(kdf); + kdf = NULL; + + /* Falls back to slow path */ + if (!TEST_ptr(kdf = EVP_KDF_fetch(ctx, "PBKDF2", "?provider=default")) + || !TEST_true(test_kdf(kdf, "PBKDF2")) + || !TEST_int_ne(kdf->origin, EVP_ORIG_FROZEN)) + goto err; + + ret = 1; +err: + EVP_KDF_free(kdf); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_KDF_fetch_by_name(void) +{ + return test_explicit_EVP_KDF_fetch("PBKDF2"); +} + /* * Test EVP_CIPHER_fetch() */ @@ -1220,6 +1375,10 @@ int setup_tests(void) ADD_TEST(test_implicit_EVP_CIPHER_fetch); ADD_TEST(test_explicit_EVP_CIPHER_fetch_by_name); ADD_ALL_TESTS_NOSUBTEST(test_explicit_EVP_CIPHER_fetch_by_X509_ALGOR, 2); + } else if (strcmp(alg, "kdf") == 0) { + ADD_TEST(test_EVP_KDF_fetch_freeze); + ADD_TEST(test_implicit_EVP_KDF_fetch); + ADD_TEST(test_explicit_EVP_KDF_fetch_by_name); } else if (strcmp(alg, "rand") == 0) { ADD_TEST(test_EVP_RAND_fetch_freeze); ADD_TEST(test_implicit_EVP_RAND_fetch); diff --git a/test/recipes/30-test_evp_fetch_prov.t b/test/recipes/30-test_evp_fetch_prov.t index e429d8882d..8685d3d6bb 100644 --- a/test/recipes/30-test_evp_fetch_prov.t +++ b/test/recipes/30-test_evp_fetch_prov.t @@ -21,7 +21,7 @@ use lib bldtop_dir('.'); my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); -my @types = ( "digest", "cipher", "rand", "mac", "kmgmt", "kem" ); +my @types = ( "digest", "cipher", "rand", "mac", "kmgmt", "kem", "kdf" ); my @testdata = ( { config => srctop_file("test", "default.cnf"), From b9970647dfd40058c9b9b75b7d14ef056e3b2eb0 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 10 Mar 2026 18:41:12 -0700 Subject: [PATCH 24/28] Add freeze functionality to EVP_ASYM_CIPHER_fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add unit tests to check functionality Fixes https://github.com/openssl/project/issues/1888 Reviewed-by: Saša Nedvědický Reviewed-by: Nikola Pajkovsky MergeDate: Tue Mar 17 09:54:30 2026 (Merged from https://github.com/openssl/openssl/pull/30357) --- crypto/evp/asymcipher.c | 83 +++++++++++++++++--- crypto/evp/evp_local.h | 2 + crypto/property/property.c | 6 +- test/evp_fetch_prov_test.c | 108 ++++++++++++++++++++++++++ test/recipes/30-test_evp_fetch_prov.t | 2 +- 5 files changed, 187 insertions(+), 14 deletions(-) diff --git a/crypto/evp/asymcipher.c b/crypto/evp/asymcipher.c index d7048c6a9e..0860f1d8a6 100644 --- a/crypto/evp/asymcipher.c +++ b/crypto/evp/asymcipher.c @@ -17,6 +17,14 @@ #include "crypto/evp.h" #include "evp_local.h" +static void evp_asym_cipher_free_int(EVP_ASYM_CIPHER *cipher) +{ + OPENSSL_free(cipher->type_name); + ossl_provider_free(cipher->prov); + CRYPTO_FREE_REF(&cipher->refcnt); + OPENSSL_free(cipher); +} + static void evp_asym_cipher_free(void *data) { EVP_ASYM_CIPHER_free(data); @@ -363,6 +371,47 @@ static EVP_ASYM_CIPHER *evp_asym_cipher_new(OSSL_PROVIDER *prov) return cipher; } +static void *evp_asym_cipher_dup_frozen(void *vin) +{ + EVP_ASYM_CIPHER *in = vin; + EVP_ASYM_CIPHER *out; + + out = OPENSSL_malloc(sizeof(*out)); + if (out == NULL) + return NULL; + memcpy(out, in, sizeof(*out)); + if (!CRYPTO_NEW_REF(&out->refcnt, 1)) + goto err; + out->type_name = OPENSSL_strdup(in->type_name); + if (out->type_name == NULL) + goto err; + out->origin = EVP_ORIG_FROZEN; + if (out->prov == NULL || !ossl_provider_up_ref(out->prov)) { + OPENSSL_free(out->type_name); + goto err; + } + return out; + +err: + CRYPTO_FREE_REF(&out->refcnt); + OPENSSL_free(out); + return NULL; +} + +static void evp_asym_cipher_frozen_free(void *vin) +{ + EVP_ASYM_CIPHER *rand = vin; + int ref = 0; + + if (rand == NULL || rand->origin != EVP_ORIG_FROZEN) + return; + + CRYPTO_DOWN_REF(&rand->refcnt, &ref); + if (ref > 0) + return; + evp_asym_cipher_free_int(rand); +} + static void *evp_asym_cipher_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov) @@ -484,22 +533,20 @@ void EVP_ASYM_CIPHER_free(EVP_ASYM_CIPHER *cipher) { int i; - if (cipher == NULL) + if (cipher == NULL || cipher->origin != EVP_ORIG_DYNAMIC) return; CRYPTO_DOWN_REF(&cipher->refcnt, &i); if (i > 0) return; - OPENSSL_free(cipher->type_name); - ossl_provider_free(cipher->prov); - CRYPTO_FREE_REF(&cipher->refcnt); - OPENSSL_free(cipher); + evp_asym_cipher_free_int(cipher); } int EVP_ASYM_CIPHER_up_ref(EVP_ASYM_CIPHER *cipher) { int ref = 0; - CRYPTO_UP_REF(&cipher->refcnt, &ref); + if (cipher->origin == EVP_ORIG_DYNAMIC) + CRYPTO_UP_REF(&cipher->refcnt, &ref); return 1; } @@ -511,10 +558,26 @@ OSSL_PROVIDER *EVP_ASYM_CIPHER_get0_provider(const EVP_ASYM_CIPHER *cipher) EVP_ASYM_CIPHER *EVP_ASYM_CIPHER_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) { - return evp_generic_fetch(ctx, OSSL_OP_ASYM_CIPHER, algorithm, properties, + return evp_generic_fetch(ctx, + OSSL_OP_ASYM_CIPHER, + algorithm, + properties, evp_asym_cipher_from_algorithm, evp_asym_cipher_up_ref, - evp_asym_cipher_free, NULL, NULL); + evp_asym_cipher_free, + evp_asym_cipher_dup_frozen, + evp_asym_cipher_frozen_free); +} + +int evp_asym_cipher_fetch_all(OSSL_LIB_CTX *ctx) +{ + return evp_generic_fetch_all(ctx, + OSSL_OP_ASYM_CIPHER, + evp_asym_cipher_from_algorithm, + evp_asym_cipher_up_ref, + evp_asym_cipher_free, + evp_asym_cipher_dup_frozen, + evp_asym_cipher_frozen_free); } EVP_ASYM_CIPHER *evp_asym_cipher_fetch_from_prov(OSSL_PROVIDER *prov, @@ -526,8 +589,8 @@ EVP_ASYM_CIPHER *evp_asym_cipher_fetch_from_prov(OSSL_PROVIDER *prov, evp_asym_cipher_from_algorithm, evp_asym_cipher_up_ref, evp_asym_cipher_free, - NULL, - NULL); + evp_asym_cipher_dup_frozen, + evp_asym_cipher_frozen_free); } int EVP_ASYM_CIPHER_is_a(const EVP_ASYM_CIPHER *cipher, const char *name) diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 49a62f4276..37ebfc3687 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -253,6 +253,7 @@ struct evp_skeymgmt_st { struct evp_asym_cipher_st { int name_id; + int origin; char *type_name; const char *description; OSSL_PROVIDER *prov; @@ -463,3 +464,4 @@ int evp_rand_fetch_all(OSSL_LIB_CTX *ctx); int evp_mac_fetch_all(OSSL_LIB_CTX *ctx); int evp_keymgmt_fetch_all(OSSL_LIB_CTX *ctx); int evp_kem_fetch_all(OSSL_LIB_CTX *ctx); +int evp_asym_cipher_fetch_all(OSSL_LIB_CTX *ctx); diff --git a/crypto/property/property.c b/crypto/property/property.c index f99bef4fa9..8d01abc629 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -1175,9 +1175,9 @@ int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq) || evp_rand_fetch_all(store->ctx) <= 0 || evp_mac_fetch_all(store->ctx) <= 0 || evp_keymgmt_fetch_all(store->ctx) <= 0 - || evp_kdf_fetch_all(store->ctx) <= 0) - goto err; - if (evp_kem_fetch_all(store->ctx) <= 0) + || evp_kdf_fetch_all(store->ctx) <= 0 + || evp_kem_fetch_all(store->ctx) <= 0 + || evp_asym_cipher_fetch_all(store->ctx) <= 0) goto err; ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_freeze, &af); diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index c5b1e9c6ad..6a008807d5 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -1336,6 +1336,110 @@ static int test_explicit_EVP_RAND_fetch_by_name(void) return test_explicit_EVP_RAND_fetch("HASH-DRBG"); } +static int test_EVP_ASYM_CIPHER_fetch_freeze(void) +{ +#if defined(OPENSSL_NO_CACHED_FETCH) || defined(OPENSSL_NO_ML_KEM) + /* + * Test does not make sense if cached fetch is disabled. + * There's nothing to freeze, and test will fail. + */ + return 1; +#endif + + EVP_ASYM_CIPHER *cipher = NULL; + int ret = 0; + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(cipher = EVP_ASYM_CIPHER_fetch(ctx, "RSA", NULL)) + || !TEST_int_ne(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_ASYM_CIPHER_free(cipher); + cipher = NULL; + + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) + || !TEST_ptr(cipher = EVP_ASYM_CIPHER_fetch(ctx, "RSA", NULL)) + || !TEST_int_eq(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + /* Technically, frozen version doesn't need to be freed */ + EVP_ASYM_CIPHER_free(cipher); + cipher = NULL; + + if (!TEST_ptr(cipher = EVP_ASYM_CIPHER_fetch(ctx, "RSA", "?fips=true")) + || !TEST_int_eq(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_ASYM_CIPHER_free(cipher); + cipher = NULL; + + /* + * A mismatched propq should use the regular fetch path rather than the + * frozen fast path. + */ + if (!TEST_ptr(cipher = EVP_ASYM_CIPHER_fetch(ctx, "RSA", "?provider=default")) + || !TEST_int_ne(cipher->origin, EVP_ORIG_FROZEN)) + goto err; + + ret = 1; +err: + EVP_ASYM_CIPHER_free(cipher); + unload_providers(&ctx, prov); + return ret; +} + +static int test_implicit_EVP_ASYM_CIPHER_fetch(void) +{ + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + EVP_ASYM_CIPHER *cipher = NULL; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(cipher = EVP_ASYM_CIPHER_fetch(ctx, "RSA", NULL))) + goto err; + ret = 1; +err: + EVP_ASYM_CIPHER_free(cipher); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_ASYM_CIPHER_fetch(const char *id) +{ + OSSL_LIB_CTX *ctx = NULL; + EVP_ASYM_CIPHER *cipher = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + int ret = 0; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + cipher = EVP_ASYM_CIPHER_fetch(ctx, id, fetch_property); + if (expected_fetch_result != 0) { + if (!TEST_true(EVP_ASYM_CIPHER_up_ref(cipher))) + goto err; + /* Ref count should now be 2. Release first one here */ + EVP_ASYM_CIPHER_free(cipher); + } else { + if (!TEST_ptr_null(cipher)) + goto err; + } + ret = 1; +err: + EVP_ASYM_CIPHER_free(cipher); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_ASYM_CIPHER_fetch_by_name(void) +{ + return test_explicit_EVP_ASYM_CIPHER_fetch("RSA"); +} + int setup_tests(void) { OPTION_CHOICE o; @@ -1396,6 +1500,10 @@ int setup_tests(void) ADD_TEST(test_EVP_KEM_fetch_freeze); ADD_TEST(test_explicit_EVP_KEM_fetch_by_name); ADD_ALL_TESTS_NOSUBTEST(test_explicit_EVP_KEM_fetch_by_X509_ALGOR, 2); + } else if (strcmp(alg, "asymcipher") == 0) { + ADD_TEST(test_EVP_ASYM_CIPHER_fetch_freeze); + ADD_TEST(test_implicit_EVP_ASYM_CIPHER_fetch); + ADD_TEST(test_explicit_EVP_ASYM_CIPHER_fetch_by_name); } else { TEST_error("Unknown fetch type: %s", alg); return 0; diff --git a/test/recipes/30-test_evp_fetch_prov.t b/test/recipes/30-test_evp_fetch_prov.t index 8685d3d6bb..deb795430d 100644 --- a/test/recipes/30-test_evp_fetch_prov.t +++ b/test/recipes/30-test_evp_fetch_prov.t @@ -21,7 +21,7 @@ use lib bldtop_dir('.'); my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); -my @types = ( "digest", "cipher", "rand", "mac", "kmgmt", "kem", "kdf" ); +my @types = ( "digest", "cipher", "rand", "mac", "kmgmt", "kem", "kdf", "asymcipher" ); my @testdata = ( { config => srctop_file("test", "default.cnf"), From 79439ccba0a7220810a3d00c6f1d05f9b941bab4 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Thu, 12 Mar 2026 09:34:27 -0700 Subject: [PATCH 25/28] Update test for EVP_ASYM_CIPHER freeze MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Saša Nedvědický Reviewed-by: Nikola Pajkovsky MergeDate: Tue Mar 17 09:54:32 2026 (Merged from https://github.com/openssl/openssl/pull/30357) --- test/evp_fetch_prov_test.c | 59 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index 6a008807d5..ff38812c0b 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -798,6 +798,59 @@ static int test_kem(OSSL_LIB_CTX *libctx, const char *propq, && test_ml_kem_keyexch(libctx, propq); } +static int test_rsa_enc_dec(OSSL_LIB_CTX *libctx, const char *propq) +{ + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + unsigned char plaintext[] = "Hello world"; + unsigned char *ciphertext = NULL, *decrypted = NULL; + size_t ciphertext_len, decrypted_len; + int ret = 0; + + /* Encrypt */ + if (!TEST_ptr(pkey = EVP_PKEY_Q_keygen(libctx, propq, "RSA", 4096)) + || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, propq)) + || !TEST_int_eq(EVP_PKEY_encrypt_init(ctx), 1) + || !TEST_int_eq(EVP_PKEY_encrypt(ctx, NULL, &ciphertext_len, + plaintext, sizeof(plaintext)), + 1) + || !TEST_ptr(ciphertext = OPENSSL_malloc(ciphertext_len)) + || !TEST_int_eq(EVP_PKEY_encrypt(ctx, ciphertext, &ciphertext_len, + plaintext, sizeof(plaintext)), + 1)) + goto err; + + /* Decrypt */ + if (!TEST_int_eq(EVP_PKEY_decrypt_init(ctx), 1) + || !TEST_int_eq(EVP_PKEY_decrypt(ctx, NULL, &decrypted_len, ciphertext, + ciphertext_len), + 1) + || !TEST_ptr(decrypted = OPENSSL_malloc(decrypted_len)) + || !TEST_int_eq(EVP_PKEY_decrypt(ctx, decrypted, &decrypted_len, + ciphertext, ciphertext_len), + 1) + || !TEST_int_eq(strcasecmp((const char *)plaintext, (const char *)decrypted), 0)) + goto err; + + ret = 1; +err: + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + OPENSSL_free(ciphertext); + OPENSSL_free(decrypted); + + return ret; +} + +static int test_asym_cipher(EVP_ASYM_CIPHER *cipher, const char *name, + OSSL_LIB_CTX *ctx, const char *propq) +{ + return TEST_ptr(cipher) + && TEST_ptr(EVP_ASYM_CIPHER_get0_provider(cipher)) + && TEST_true(EVP_ASYM_CIPHER_is_a(cipher, name)) + && TEST_true(test_rsa_enc_dec(ctx, propq)); +} + static int test_EVP_KEYMGMT_fetch_freeze(void) { #if defined(OPENSSL_NO_CACHED_FETCH) || defined(OPENSSL_NO_EC) @@ -1355,6 +1408,7 @@ static int test_EVP_ASYM_CIPHER_fetch_freeze(void) goto err; if (!TEST_ptr(cipher = EVP_ASYM_CIPHER_fetch(ctx, "RSA", NULL)) + || !TEST_true(test_asym_cipher(cipher, "RSA", ctx, NULL)) || !TEST_int_ne(cipher->origin, EVP_ORIG_FROZEN)) goto err; EVP_ASYM_CIPHER_free(cipher); @@ -1362,23 +1416,24 @@ static int test_EVP_ASYM_CIPHER_fetch_freeze(void) if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) || !TEST_ptr(cipher = EVP_ASYM_CIPHER_fetch(ctx, "RSA", NULL)) + || !TEST_true(test_asym_cipher(cipher, "RSA", ctx, NULL)) || !TEST_int_eq(cipher->origin, EVP_ORIG_FROZEN)) goto err; /* Technically, frozen version doesn't need to be freed */ EVP_ASYM_CIPHER_free(cipher); - cipher = NULL; if (!TEST_ptr(cipher = EVP_ASYM_CIPHER_fetch(ctx, "RSA", "?fips=true")) + || !TEST_true(test_asym_cipher(cipher, "RSA", ctx, "?fips=true")) || !TEST_int_eq(cipher->origin, EVP_ORIG_FROZEN)) goto err; EVP_ASYM_CIPHER_free(cipher); - cipher = NULL; /* * A mismatched propq should use the regular fetch path rather than the * frozen fast path. */ if (!TEST_ptr(cipher = EVP_ASYM_CIPHER_fetch(ctx, "RSA", "?provider=default")) + || !TEST_true(test_asym_cipher(cipher, "RSA", ctx, "?provider=default")) || !TEST_int_ne(cipher->origin, EVP_ORIG_FROZEN)) goto err; From fd185f596fd1d515e228f330e73a49bc891549ef Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Thu, 12 Mar 2026 21:37:41 -0700 Subject: [PATCH 26/28] Fix unit test with FIPS install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Saša Nedvědický Reviewed-by: Nikola Pajkovsky MergeDate: Tue Mar 17 09:54:33 2026 (Merged from https://github.com/openssl/openssl/pull/30357) --- test/evp_fetch_prov_test.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index ff38812c0b..c28b16dfc4 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -811,6 +811,7 @@ static int test_rsa_enc_dec(OSSL_LIB_CTX *libctx, const char *propq) if (!TEST_ptr(pkey = EVP_PKEY_Q_keygen(libctx, propq, "RSA", 4096)) || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, propq)) || !TEST_int_eq(EVP_PKEY_encrypt_init(ctx), 1) + || !TEST_int_eq(EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING), 1) || !TEST_int_eq(EVP_PKEY_encrypt(ctx, NULL, &ciphertext_len, plaintext, sizeof(plaintext)), 1) @@ -822,6 +823,7 @@ static int test_rsa_enc_dec(OSSL_LIB_CTX *libctx, const char *propq) /* Decrypt */ if (!TEST_int_eq(EVP_PKEY_decrypt_init(ctx), 1) + || !TEST_int_eq(EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING), 1) || !TEST_int_eq(EVP_PKEY_decrypt(ctx, NULL, &decrypted_len, ciphertext, ciphertext_len), 1) From d1cae65c4a509498f38e32b245096857d975aae7 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 17 Mar 2026 07:54:42 -0700 Subject: [PATCH 27/28] Add freeze functionality to EVP_KEYEXCH_fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add unit tests to check functionality Reviewed-by: Nikola Pajkovsky Reviewed-by: Saša Nedvědický MergeDate: Fri Mar 27 16:28:10 2026 (Merged from https://github.com/openssl/openssl/pull/30462) --- crypto/evp/evp_local.h | 2 + crypto/evp/exchange.c | 83 +++++++++-- crypto/property/property.c | 3 +- test/evp_fetch_prov_test.c | 200 ++++++++++++++++++++++++++ test/recipes/30-test_evp_fetch_prov.t | 2 +- 5 files changed, 278 insertions(+), 12 deletions(-) diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 37ebfc3687..5653eec11c 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -137,6 +137,7 @@ struct evp_keymgmt_st { struct evp_keyexch_st { int name_id; + int origin; char *type_name; const char *description; OSSL_PROVIDER *prov; @@ -465,3 +466,4 @@ int evp_mac_fetch_all(OSSL_LIB_CTX *ctx); int evp_keymgmt_fetch_all(OSSL_LIB_CTX *ctx); int evp_kem_fetch_all(OSSL_LIB_CTX *ctx); int evp_asym_cipher_fetch_all(OSSL_LIB_CTX *ctx); +int evp_keyexch_fetch_all(OSSL_LIB_CTX *ctx); diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c index 384f0366bc..1346b4d4ae 100644 --- a/crypto/evp/exchange.c +++ b/crypto/evp/exchange.c @@ -19,6 +19,14 @@ #include "crypto/evp.h" #include "evp_local.h" +static void evp_keyexch_free_int(EVP_KEYEXCH *exchange) +{ + OPENSSL_free(exchange->type_name); + ossl_provider_free(exchange->prov); + CRYPTO_FREE_REF(&exchange->refcnt); + OPENSSL_free(exchange); +} + static void evp_keyexch_free(void *data) { EVP_KEYEXCH_free(data); @@ -47,6 +55,47 @@ static EVP_KEYEXCH *evp_keyexch_new(OSSL_PROVIDER *prov) return exchange; } +static void *evp_keyexch_dup_frozen(void *vin) +{ + EVP_KEYEXCH *in = vin; + EVP_KEYEXCH *out; + + out = OPENSSL_malloc(sizeof(*out)); + if (out == NULL) + return NULL; + memcpy(out, in, sizeof(*out)); + if (!CRYPTO_NEW_REF(&out->refcnt, 1)) + goto err; + out->type_name = OPENSSL_strdup(in->type_name); + if (out->type_name == NULL) + goto err; + out->origin = EVP_ORIG_FROZEN; + if (out->prov == NULL || !ossl_provider_up_ref(out->prov)) { + OPENSSL_free(out->type_name); + goto err; + } + return out; + +err: + CRYPTO_FREE_REF(&out->refcnt); + OPENSSL_free(out); + return NULL; +} + +static void evp_keyexch_frozen_free(void *vin) +{ + EVP_KEYEXCH *rand = vin; + int ref = 0; + + if (rand == NULL || rand->origin != EVP_ORIG_FROZEN) + return; + + CRYPTO_DOWN_REF(&rand->refcnt, &ref); + if (ref > 0) + return; + evp_keyexch_free_int(rand); +} + static void *evp_keyexch_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov) @@ -162,22 +211,20 @@ void EVP_KEYEXCH_free(EVP_KEYEXCH *exchange) { int i; - if (exchange == NULL) + if (exchange == NULL || exchange->origin != EVP_ORIG_DYNAMIC) return; CRYPTO_DOWN_REF(&exchange->refcnt, &i); if (i > 0) return; - OPENSSL_free(exchange->type_name); - ossl_provider_free(exchange->prov); - CRYPTO_FREE_REF(&exchange->refcnt); - OPENSSL_free(exchange); + evp_keyexch_free_int(exchange); } int EVP_KEYEXCH_up_ref(EVP_KEYEXCH *exchange) { int ref = 0; - CRYPTO_UP_REF(&exchange->refcnt, &ref); + if (exchange->origin == EVP_ORIG_DYNAMIC) + CRYPTO_UP_REF(&exchange->refcnt, &ref); return 1; } @@ -189,10 +236,26 @@ OSSL_PROVIDER *EVP_KEYEXCH_get0_provider(const EVP_KEYEXCH *exchange) EVP_KEYEXCH *EVP_KEYEXCH_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) { - return evp_generic_fetch(ctx, OSSL_OP_KEYEXCH, algorithm, properties, + return evp_generic_fetch(ctx, + OSSL_OP_KEYEXCH, + algorithm, + properties, evp_keyexch_from_algorithm, evp_keyexch_up_ref, - evp_keyexch_free, NULL, NULL); + evp_keyexch_free, + evp_keyexch_dup_frozen, + evp_keyexch_frozen_free); +} + +int evp_keyexch_fetch_all(OSSL_LIB_CTX *ctx) +{ + return evp_generic_fetch_all(ctx, + OSSL_OP_KEYEXCH, + evp_keyexch_from_algorithm, + evp_keyexch_up_ref, + evp_keyexch_free, + evp_keyexch_dup_frozen, + evp_keyexch_frozen_free); } EVP_KEYEXCH *evp_keyexch_fetch_from_prov(OSSL_PROVIDER *prov, @@ -204,8 +267,8 @@ EVP_KEYEXCH *evp_keyexch_fetch_from_prov(OSSL_PROVIDER *prov, evp_keyexch_from_algorithm, evp_keyexch_up_ref, evp_keyexch_free, - NULL, - NULL); + evp_keyexch_dup_frozen, + evp_keyexch_frozen_free); } int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx) diff --git a/crypto/property/property.c b/crypto/property/property.c index 8d01abc629..f577c73931 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -1177,7 +1177,8 @@ int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq) || evp_keymgmt_fetch_all(store->ctx) <= 0 || evp_kdf_fetch_all(store->ctx) <= 0 || evp_kem_fetch_all(store->ctx) <= 0 - || evp_asym_cipher_fetch_all(store->ctx) <= 0) + || evp_asym_cipher_fetch_all(store->ctx) <= 0 + || evp_keyexch_fetch_all(store->ctx) <= 0) goto err; ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_freeze, &af); diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index c28b16dfc4..3225b8df83 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -1497,6 +1497,202 @@ static int test_explicit_EVP_ASYM_CIPHER_fetch_by_name(void) return test_explicit_EVP_ASYM_CIPHER_fetch("RSA"); } +static EVP_PKEY *generate_dh_key(void) +{ + EVP_PKEY_CTX *ctx; + EVP_PKEY *pkey = NULL; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string("group", "ffdhe4096", 0), + OSSL_PARAM_construct_end() + }; + + if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL)) + || !TEST_int_eq(EVP_PKEY_keygen_init(ctx), 1) + || !TEST_int_eq(EVP_PKEY_CTX_set_params(ctx, params), 1) + || !TEST_int_eq(EVP_PKEY_keygen(ctx, &pkey), 1)) { + EVP_PKEY_CTX_free(ctx); + return NULL; + } + + EVP_PKEY_CTX_free(ctx); + return pkey; +} + +static int derive_secret(EVP_PKEY *priv, EVP_PKEY *peer, + unsigned char **secret, size_t *secret_len) +{ + EVP_PKEY_CTX *ctx; + int ret = 0; + + *secret = NULL; + if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(NULL, priv, NULL)) + || !TEST_int_eq(EVP_PKEY_derive_init(ctx), 1) + || !TEST_int_eq(EVP_PKEY_derive_set_peer(ctx, peer), 1) + || !TEST_int_eq(EVP_PKEY_derive(ctx, NULL, secret_len), 1) + || !TEST_ptr(*secret = OPENSSL_malloc(*secret_len)) + || !TEST_int_eq(EVP_PKEY_derive(ctx, *secret, secret_len), 1)) { + OPENSSL_free(*secret); + *secret = NULL; + *secret_len = 0; + goto err; + } + + ret = 1; +err: + EVP_PKEY_CTX_free(ctx); + return ret; +} + +static int test_dh_derive(OSSL_LIB_CTX *libctx, const char *propq) +{ + EVP_PKEY *alice = NULL, *bob = NULL; + unsigned char *secret1 = NULL, *secret2 = NULL; + size_t secret1_len = 0, secret2_len = 0; + int ret = 0; + + if (!TEST_ptr(alice = generate_dh_key()) + || !TEST_ptr(bob = generate_dh_key()) + || !TEST_int_eq(derive_secret(alice, bob, &secret1, &secret1_len), 1) + || !TEST_size_t_gt(secret1_len, 0) + || !TEST_int_eq(derive_secret(bob, alice, &secret2, &secret2_len), 1) + || !TEST_size_t_gt(secret2_len, 0) + || !TEST_size_t_eq(secret1_len, secret2_len) + || !TEST_int_eq(memcmp(secret1, secret2, secret1_len), 0)) + goto err; + + ret = 1; +err: + EVP_PKEY_free(alice); + EVP_PKEY_free(bob); + OPENSSL_free(secret1); + OPENSSL_free(secret2); + + return ret; +} + +static int test_keyexch(OSSL_LIB_CTX *ctx, const char *propq, EVP_KEYEXCH *exchange, const char *name) +{ + return TEST_ptr(exchange) + && TEST_ptr(EVP_KEYEXCH_get0_provider(exchange)) + && TEST_true(EVP_KEYEXCH_is_a(exchange, name)) + && TEST_true(test_dh_derive(ctx, propq)); +} + +static int test_EVP_KEYEXCH_fetch_freeze(void) +{ +#if defined(OPENSSL_NO_CACHED_FETCH) || defined(OPENSSL_NO_DH) + /* + * Test does not make sense if cached fetch is disabled. + * There's nothing to freeze, and test will fail. + */ + return 1; +#endif + + EVP_KEYEXCH *exchange = NULL; + int ret = 0; + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(exchange = EVP_KEYEXCH_fetch(ctx, "DH", NULL)) + || !TEST_true(test_keyexch(ctx, NULL, exchange, "DH")) + || !TEST_int_ne(exchange->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_KEYEXCH_free(exchange); + exchange = NULL; + + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) + || !TEST_ptr(exchange = EVP_KEYEXCH_fetch(ctx, "DH", NULL)) + || !TEST_true(test_keyexch(ctx, NULL, exchange, "DH")) + || !TEST_int_eq(exchange->origin, EVP_ORIG_FROZEN)) + goto err; + /* Technically, frozen version doesn't need to be freed */ + EVP_KEYEXCH_free(exchange); + + if (!TEST_ptr(exchange = EVP_KEYEXCH_fetch(ctx, "DH", "?fips=true")) + || !TEST_true(test_keyexch(ctx, "?fips=true", exchange, "DH")) + || !TEST_int_eq(exchange->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_KEYEXCH_free(exchange); + + /* + * A mismatched propq should use the regular fetch path rather than the + * frozen fast path. + */ + if (!TEST_ptr(exchange = EVP_KEYEXCH_fetch(ctx, "DH", "?provider=default")) + || !TEST_true(test_keyexch(ctx, "?provider=default", exchange, "DH")) + || !TEST_int_ne(exchange->origin, EVP_ORIG_FROZEN)) + goto err; + + ret = 1; +err: + EVP_KEYEXCH_free(exchange); + unload_providers(&ctx, prov); + return ret; +} + +static int test_implicit_EVP_KEYEXCH_fetch(void) +{ +#if defined(OPENSSL_NO_DH) + return 1; +#endif + + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + EVP_KEYEXCH *exchange = NULL; + int ret = 0; + + if (use_default_ctx == 0 && !TEST_true(load_providers(&ctx, prov))) + goto err; + + if (!TEST_ptr(exchange = EVP_KEYEXCH_fetch(ctx, "DH", NULL))) + goto err; + + ret = 1; +err: + EVP_KEYEXCH_free(exchange); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_KEYEXCH_fetch(const char *id) +{ + OSSL_LIB_CTX *ctx = NULL; + EVP_KEYEXCH *exchange = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + int ret = 0; + + if (use_default_ctx == 0 && !TEST_true(load_providers(&ctx, prov))) + goto err; + + exchange = EVP_KEYEXCH_fetch(ctx, id, fetch_property); + if (expected_fetch_result != 0) { + if (!TEST_true(EVP_KEYEXCH_up_ref(exchange))) + goto err; + /* Ref count should now be 2. Release first one here */ + EVP_KEYEXCH_free(exchange); + } else { + if (!TEST_ptr_null(exchange)) + goto err; + } + ret = 1; +err: + EVP_KEYEXCH_free(exchange); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_KEYEXCH_fetch_by_name(void) +{ +#if defined(OPENSSL_NO_DH) + return 1; +#endif + + return test_explicit_EVP_KEYEXCH_fetch("DH"); +} + int setup_tests(void) { OPTION_CHOICE o; @@ -1561,6 +1757,10 @@ int setup_tests(void) ADD_TEST(test_EVP_ASYM_CIPHER_fetch_freeze); ADD_TEST(test_implicit_EVP_ASYM_CIPHER_fetch); ADD_TEST(test_explicit_EVP_ASYM_CIPHER_fetch_by_name); + } else if (strcmp(alg, "evp_keyexch") == 0) { + ADD_TEST(test_EVP_KEYEXCH_fetch_freeze); + ADD_TEST(test_implicit_EVP_KEYEXCH_fetch); + ADD_TEST(test_explicit_EVP_KEYEXCH_fetch_by_name); } else { TEST_error("Unknown fetch type: %s", alg); return 0; diff --git a/test/recipes/30-test_evp_fetch_prov.t b/test/recipes/30-test_evp_fetch_prov.t index deb795430d..388be86fca 100644 --- a/test/recipes/30-test_evp_fetch_prov.t +++ b/test/recipes/30-test_evp_fetch_prov.t @@ -21,7 +21,7 @@ use lib bldtop_dir('.'); my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); -my @types = ( "digest", "cipher", "rand", "mac", "kmgmt", "kem", "kdf", "asymcipher" ); +my @types = ( "digest", "cipher", "rand", "mac", "kmgmt", "kem", "kdf", "asymcipher", "evp_keyexch" ); my @testdata = ( { config => srctop_file("test", "default.cnf"), From 42f8690c2cefc9f5fc29d18be69197d07ea35034 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Tue, 31 Mar 2026 17:29:39 +0700 Subject: [PATCH 28/28] Add freeze functionality to EVP_SKEYMGMT_fetch Add unit tests to check functionality Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs MergeDate: Fri Apr 17 11:12:10 2026 (Merged from https://github.com/openssl/openssl/pull/30642) --- crypto/evp/evp_local.h | 2 + crypto/evp/skeymgmt_meth.c | 79 +++++++++++++-- crypto/property/property.c | 3 +- test/evp_fetch_prov_test.c | 136 ++++++++++++++++++++++++++ test/recipes/30-test_evp_fetch_prov.t | 2 +- 5 files changed, 211 insertions(+), 11 deletions(-) diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 5653eec11c..2f002a2412 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -231,6 +231,7 @@ struct evp_rand_st { struct evp_skeymgmt_st { int name_id; + int origin; char *type_name; const char *description; OSSL_PROVIDER *prov; @@ -467,3 +468,4 @@ int evp_keymgmt_fetch_all(OSSL_LIB_CTX *ctx); int evp_kem_fetch_all(OSSL_LIB_CTX *ctx); int evp_asym_cipher_fetch_all(OSSL_LIB_CTX *ctx); int evp_keyexch_fetch_all(OSSL_LIB_CTX *ctx); +int evp_skeymgmt_fetch_all(OSSL_LIB_CTX *ctx); diff --git a/crypto/evp/skeymgmt_meth.c b/crypto/evp/skeymgmt_meth.c index a46bbe3f02..c225151d3f 100644 --- a/crypto/evp/skeymgmt_meth.c +++ b/crypto/evp/skeymgmt_meth.c @@ -7,6 +7,7 @@ * https://www.openssl.org/source/license.html */ +#include #include #include #include @@ -17,6 +18,55 @@ #include "crypto/evp.h" #include "evp_local.h" +static void evp_skeymgmt_free_int(EVP_SKEYMGMT *skeymgmt) +{ + OPENSSL_free(skeymgmt->type_name); + ossl_provider_free(skeymgmt->prov); + CRYPTO_FREE_REF(&skeymgmt->refcnt); + OPENSSL_free(skeymgmt); +} + +static void *evp_skeymgmt_dup_frozen(void *vin) +{ + EVP_SKEYMGMT *in = vin; + EVP_SKEYMGMT *out; + + out = OPENSSL_malloc(sizeof(*out)); + if (out == NULL) + return NULL; + memcpy(out, in, sizeof(*out)); + if (!CRYPTO_NEW_REF(&out->refcnt, 1)) + goto err; + out->type_name = OPENSSL_strdup(in->type_name); + if (out->type_name == NULL) + goto err; + out->origin = EVP_ORIG_FROZEN; + if (out->prov == NULL || !ossl_provider_up_ref(out->prov)) { + OPENSSL_free(out->type_name); + goto err; + } + return out; + +err: + CRYPTO_FREE_REF(&out->refcnt); + OPENSSL_free(out); + return NULL; +} + +static void evp_skeymgmt_frozen_free(void *vin) +{ + EVP_SKEYMGMT *skeymgmt = vin; + int ref = 0; + + if (skeymgmt == NULL || skeymgmt->origin != EVP_ORIG_FROZEN) + return; + + CRYPTO_DOWN_REF(&skeymgmt->refcnt, &ref); + if (ref > 0) + return; + evp_skeymgmt_free_int(skeymgmt); +} + void *evp_skeymgmt_generate(const EVP_SKEYMGMT *skeymgmt, const OSSL_PARAM params[]) { void *provctx = ossl_provider_ctx(EVP_SKEYMGMT_get0_provider(skeymgmt)); @@ -137,8 +187,8 @@ EVP_SKEYMGMT *evp_skeymgmt_fetch_from_prov(OSSL_PROVIDER *prov, skeymgmt_from_algorithm, (int (*)(void *))EVP_SKEYMGMT_up_ref, (void (*)(void *))EVP_SKEYMGMT_free, - NULL, - NULL); + evp_skeymgmt_dup_frozen, + evp_skeymgmt_frozen_free); } EVP_SKEYMGMT *EVP_SKEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, @@ -147,14 +197,28 @@ EVP_SKEYMGMT *EVP_SKEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, return evp_generic_fetch(ctx, OSSL_OP_SKEYMGMT, algorithm, properties, skeymgmt_from_algorithm, (int (*)(void *))EVP_SKEYMGMT_up_ref, - (void (*)(void *))EVP_SKEYMGMT_free, NULL, NULL); + (void (*)(void *))EVP_SKEYMGMT_free, + evp_skeymgmt_dup_frozen, + evp_skeymgmt_frozen_free); +} + +int evp_skeymgmt_fetch_all(OSSL_LIB_CTX *ctx) +{ + return evp_generic_fetch_all(ctx, + OSSL_OP_SKEYMGMT, + skeymgmt_from_algorithm, + (int (*)(void *))EVP_SKEYMGMT_up_ref, + (void (*)(void *))EVP_SKEYMGMT_free, + evp_skeymgmt_dup_frozen, + evp_skeymgmt_frozen_free); } int EVP_SKEYMGMT_up_ref(EVP_SKEYMGMT *skeymgmt) { int ref = 0; - CRYPTO_UP_REF(&skeymgmt->refcnt, &ref); + if (skeymgmt->origin == EVP_ORIG_DYNAMIC) + CRYPTO_UP_REF(&skeymgmt->refcnt, &ref); return 1; } @@ -162,16 +226,13 @@ void EVP_SKEYMGMT_free(EVP_SKEYMGMT *skeymgmt) { int ref = 0; - if (skeymgmt == NULL) + if (skeymgmt == NULL || skeymgmt->origin != EVP_ORIG_DYNAMIC) return; CRYPTO_DOWN_REF(&skeymgmt->refcnt, &ref); if (ref > 0) return; - OPENSSL_free(skeymgmt->type_name); - ossl_provider_free(skeymgmt->prov); - CRYPTO_FREE_REF(&skeymgmt->refcnt); - OPENSSL_free(skeymgmt); + evp_skeymgmt_free_int(skeymgmt); } const OSSL_PROVIDER *EVP_SKEYMGMT_get0_provider(const EVP_SKEYMGMT *skeymgmt) diff --git a/crypto/property/property.c b/crypto/property/property.c index f577c73931..b086d0c056 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c @@ -1178,7 +1178,8 @@ int ossl_method_store_freeze_cache(OSSL_METHOD_STORE *store, const char *propq) || evp_kdf_fetch_all(store->ctx) <= 0 || evp_kem_fetch_all(store->ctx) <= 0 || evp_asym_cipher_fetch_all(store->ctx) <= 0 - || evp_keyexch_fetch_all(store->ctx) <= 0) + || evp_keyexch_fetch_all(store->ctx) <= 0 + || evp_skeymgmt_fetch_all(store->ctx) <= 0) goto err; ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_freeze, &af); diff --git a/test/evp_fetch_prov_test.c b/test/evp_fetch_prov_test.c index 3225b8df83..78116e0a32 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c @@ -1693,6 +1693,138 @@ static int test_explicit_EVP_KEYEXCH_fetch_by_name(void) return test_explicit_EVP_KEYEXCH_fetch("DH"); } +static int test_aes_skey(OSSL_LIB_CTX *libctx, const char *propq) +{ + unsigned char raw_key[16] = { 0 }; + EVP_SKEY *skey = NULL; + int ret = 0; + + if (!TEST_ptr(skey = EVP_SKEY_import_raw_key(libctx, "AES", raw_key, + sizeof(raw_key), propq))) + goto err; + + ret = 1; +err: + EVP_SKEY_free(skey); + return ret; +} + +static int test_skeymgmt(OSSL_LIB_CTX *libctx, const char *propq, + EVP_SKEYMGMT *skeymgmt, const char *name) +{ + return TEST_ptr(skeymgmt) + && TEST_ptr(EVP_SKEYMGMT_get0_provider(skeymgmt)) + && TEST_true(EVP_SKEYMGMT_is_a(skeymgmt, name)) + && TEST_true(test_aes_skey(libctx, propq)); +} + +static int test_EVP_SKEYMGMT_fetch_freeze(void) +{ +#if defined(OPENSSL_NO_CACHED_FETCH) + /* + * Test does not make sense if cached fetch is disabled. + * There's nothing to freeze, and test will fail. + */ + return 1; +#endif + + EVP_SKEYMGMT *skeymgmt = NULL; + int ret = 0; + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[2] = { NULL, NULL }; + + if (use_default_ctx == 0 && !load_providers(&ctx, prov)) + goto err; + + if (!TEST_ptr(skeymgmt = EVP_SKEYMGMT_fetch(ctx, "AES", NULL)) + || !TEST_true(test_skeymgmt(ctx, NULL, skeymgmt, "AES")) + || !TEST_int_ne(skeymgmt->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_SKEYMGMT_free(skeymgmt); + skeymgmt = NULL; + + if (!TEST_int_eq(OSSL_LIB_CTX_freeze(ctx, "?fips=true"), 1) + || !TEST_ptr(skeymgmt = EVP_SKEYMGMT_fetch(ctx, "AES", NULL)) + || !TEST_true(test_skeymgmt(ctx, NULL, skeymgmt, "AES")) + || !TEST_int_eq(skeymgmt->origin, EVP_ORIG_FROZEN)) + goto err; + /* Technically, frozen version doesn't need to be freed */ + EVP_SKEYMGMT_free(skeymgmt); + + if (!TEST_ptr(skeymgmt = EVP_SKEYMGMT_fetch(ctx, "AES", "?fips=true")) + || !TEST_true(test_skeymgmt(ctx, "?fips=true", skeymgmt, "AES")) + || !TEST_int_eq(skeymgmt->origin, EVP_ORIG_FROZEN)) + goto err; + EVP_SKEYMGMT_free(skeymgmt); + + /* + * A mismatched propq should use the regular fetch path rather than the + * frozen fast path. + */ + if (!TEST_ptr(skeymgmt = EVP_SKEYMGMT_fetch(ctx, "AES", "?provider=default")) + || !TEST_true(test_skeymgmt(ctx, "?provider=default", skeymgmt, "AES")) + || !TEST_int_ne(skeymgmt->origin, EVP_ORIG_FROZEN)) + goto err; + + ret = 1; +err: + EVP_SKEYMGMT_free(skeymgmt); + unload_providers(&ctx, prov); + return ret; +} + +static int test_implicit_EVP_SKEYMGMT_fetch(void) +{ + OSSL_LIB_CTX *ctx = NULL; + OSSL_PROVIDER *prov[] = { NULL, NULL }; + EVP_SKEYMGMT *skeymgmt = NULL; + int ret = 0; + + if (use_default_ctx == 0 && !TEST_true(load_providers(&ctx, prov))) + goto err; + + if (!TEST_ptr(skeymgmt = EVP_SKEYMGMT_fetch(ctx, "AES", NULL))) + goto err; + + ret = 1; +err: + EVP_SKEYMGMT_free(skeymgmt); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_SKEYMGMT_fetch(const char *id) +{ + OSSL_LIB_CTX *ctx = NULL; + EVP_SKEYMGMT *skeymgmt = NULL; + OSSL_PROVIDER *prov[] = { NULL, NULL }; + int ret = 0; + + if (use_default_ctx == 0 && !TEST_true(load_providers(&ctx, prov))) + goto err; + + skeymgmt = EVP_SKEYMGMT_fetch(ctx, id, fetch_property); + if (expected_fetch_result != 0) { + if (!TEST_true(EVP_SKEYMGMT_up_ref(skeymgmt))) + goto err; + /* Ref count should now be 2. Release first one here */ + EVP_SKEYMGMT_free(skeymgmt); + } else { + if (!TEST_ptr_null(skeymgmt)) + goto err; + } + ret = 1; +err: + EVP_SKEYMGMT_free(skeymgmt); + unload_providers(&ctx, prov); + return ret; +} + +static int test_explicit_EVP_SKEYMGMT_fetch_by_name(void) +{ + return test_explicit_EVP_SKEYMGMT_fetch("AES"); +} + int setup_tests(void) { OPTION_CHOICE o; @@ -1761,6 +1893,10 @@ int setup_tests(void) ADD_TEST(test_EVP_KEYEXCH_fetch_freeze); ADD_TEST(test_implicit_EVP_KEYEXCH_fetch); ADD_TEST(test_explicit_EVP_KEYEXCH_fetch_by_name); + } else if (strcmp(alg, "skeymgmt") == 0) { + ADD_TEST(test_EVP_SKEYMGMT_fetch_freeze); + ADD_TEST(test_implicit_EVP_SKEYMGMT_fetch); + ADD_TEST(test_explicit_EVP_SKEYMGMT_fetch_by_name); } else { TEST_error("Unknown fetch type: %s", alg); return 0; diff --git a/test/recipes/30-test_evp_fetch_prov.t b/test/recipes/30-test_evp_fetch_prov.t index 388be86fca..85c7b794a7 100644 --- a/test/recipes/30-test_evp_fetch_prov.t +++ b/test/recipes/30-test_evp_fetch_prov.t @@ -21,7 +21,7 @@ use lib bldtop_dir('.'); my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); -my @types = ( "digest", "cipher", "rand", "mac", "kmgmt", "kem", "kdf", "asymcipher", "evp_keyexch" ); +my @types = ( "digest", "cipher", "rand", "mac", "kmgmt", "kem", "kdf", "asymcipher", "evp_keyexch", "skeymgmt" ); my @testdata = ( { config => srctop_file("test", "default.cnf"),