aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Dinh <[email protected]>2026-03-31 12:29:39 +0200
committerNorbert Pocs <[email protected]>2026-04-17 13:12:08 +0200
commit42f8690c2cefc9f5fc29d18be69197d07ea35034 (patch)
tree5c58a9a0efc94e8d008b83275f704be97b6673e4
parentAdd freeze functionality to EVP_KEYEXCH_fetch (diff)
downloadopenssl-feature/method-store-freeze.tar.gz
openssl-feature/method-store-freeze.zip
Add freeze functionality to EVP_SKEYMGMT_fetchfeature/method-store-freeze
Add unit tests to check functionality Reviewed-by: Neil Horman <[email protected]> Reviewed-by: Norbert Pocs <[email protected]> MergeDate: Fri Apr 17 11:12:10 2026 (Merged from https://github.com/openssl/openssl/pull/30642)
-rw-r--r--crypto/evp/evp_local.h2
-rw-r--r--crypto/evp/skeymgmt_meth.c79
-rw-r--r--crypto/property/property.c3
-rw-r--r--test/evp_fetch_prov_test.c136
-rw-r--r--test/recipes/30-test_evp_fetch_prov.t2
5 files changed, 211 insertions, 11 deletions
diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h
index 5653eec11c5..2f002a24129 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 a46bbe3f020..c225151d3f9 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 @@ 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 f577c739316..b086d0c056e 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 3225b8df831..78116e0a326 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 388be86fcac..85c7b794a74 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"),