Add freeze functionality to EVP_SKEYMGMT_fetch Add unit tests to check functionality Reviewed-by: Neil Horman <nhorman@openssl.org> Reviewed-by: Norbert Pocs <norbertp@openssl.org> MergeDate: Fri Apr 17 11:12:10 2026 (Merged from https://github.com/openssl/openssl/pull/30642)
diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index 5653eec..2f002a2 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h
@@ -231,6 +231,7 @@ struct evp_skeymgmt_st { int name_id; + int origin; char *type_name; const char *description; OSSL_PROVIDER *prov; @@ -467,3 +468,4 @@ 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 a46bbe3..c225151 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 <string.h> #include <openssl/crypto.h> #include <openssl/core_dispatch.h> #include <openssl/evp.h> @@ -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 @@ 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 @@ 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 @@ { 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 f577c73..b086d0c 100644 --- a/crypto/property/property.c +++ b/crypto/property/property.c
@@ -1178,7 +1178,8 @@ || 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 3225b8d..78116e0 100644 --- a/test/evp_fetch_prov_test.c +++ b/test/evp_fetch_prov_test.c
@@ -1693,6 +1693,138 @@ 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 @@ 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 388be86..85c7b79 100644 --- a/test/recipes/30-test_evp_fetch_prov.t +++ b/test/recipes/30-test_evp_fetch_prov.t
@@ -21,7 +21,7 @@ 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"),