From: slontis Date: Thu, 26 Feb 2026 07:08:40 +0000 (+1100) Subject: ML_DSA/ML_KEM: Add fromdata property query support. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4a366f620c4992bb1dd5cef7d1dd14aa202192c0;p=thirdparty%2Fopenssl.git ML_DSA/ML_KEM: Add fromdata property query support. This allows ML_KEM/ML_DSA keys to set a "properties" value that is used to refetch the digests. This may be used when doing an import using EVP_PKEY_fromdata(). Note that this is not used by EVP_PKEY_new_raw_private_key_ex() or EVP_PKEY_new_raw_public_key_ex() since the propq used here is associated with the keymanager (i.e. via EVP_PKEY_CTX_new_from_name()) not the propq associated with internal fetches used by the key to fetch digest algorithms. Reviewed-by: Dmitry Belyavskiy Reviewed-by: Paul Dale MergeDate: Fri Mar 13 17:24:41 2026 (Merged from https://github.com/openssl/openssl/pull/30243) --- diff --git a/crypto/ml_dsa/ml_dsa_key.c b/crypto/ml_dsa/ml_dsa_key.c index 18a520dba72..24fa7596e2f 100644 --- a/crypto/ml_dsa/ml_dsa_key.c +++ b/crypto/ml_dsa/ml_dsa_key.c @@ -73,6 +73,20 @@ end: return ret; } +/* + * @brief Fetch digest algorithms based on a propq. + * For the import case ossl_ml_dsa_key_new() gets passed a NULL propq, + * so the propq is optionally deferred to the import using OSSL_PARAM. + */ +int ossl_ml_dsa_key_fetch_digests(ML_DSA_KEY *key, const char *propq) +{ + EVP_MD_free(key->shake128_md); + EVP_MD_free(key->shake256_md); + key->shake128_md = EVP_MD_fetch(key->libctx, "SHAKE-128", propq); + key->shake256_md = EVP_MD_fetch(key->libctx, "SHAKE-256", propq); + return (key->shake128_md != NULL && key->shake256_md != NULL); +} + /** * @brief Create a new ML_DSA_KEY object * @@ -95,9 +109,7 @@ ML_DSA_KEY *ossl_ml_dsa_key_new(OSSL_LIB_CTX *libctx, const char *propq, ret->libctx = libctx; ret->params = params; ret->prov_flags = ML_DSA_KEY_PROV_FLAGS_DEFAULT; - ret->shake128_md = EVP_MD_fetch(libctx, "SHAKE-128", propq); - ret->shake256_md = EVP_MD_fetch(libctx, "SHAKE-256", propq); - if (ret->shake128_md == NULL || ret->shake256_md == NULL) + if (!ossl_ml_dsa_key_fetch_digests(ret, propq)) goto err; } return ret; diff --git a/crypto/ml_kem/ml_kem.c b/crypto/ml_kem/ml_kem.c index 4cd9a2582d2..babc2281c13 100644 --- a/crypto/ml_kem/ml_kem.c +++ b/crypto/ml_kem/ml_kem.c @@ -1605,6 +1605,27 @@ const ML_KEM_VINFO *ossl_ml_kem_get_vinfo(int evp_type) return NULL; } +/* + * @brief Fetch digest algorithms based on a propq. + * For the import case ossl_ml_kem_key_new() gets passed a NULL propq, + * so the propq is optionally deferred to the import using OSSL_PARAM. + */ +int ossl_ml_kem_key_fetch_digest(ML_KEM_KEY *key, const char *propq) +{ + if (key->shake128_md != NULL) { + EVP_MD_free(key->shake128_md); + EVP_MD_free(key->shake256_md); + EVP_MD_free(key->sha3_256_md); + EVP_MD_free(key->sha3_512_md); + } + key->shake128_md = EVP_MD_fetch(key->libctx, "SHAKE128", propq); + key->shake256_md = EVP_MD_fetch(key->libctx, "SHAKE256", propq); + key->sha3_256_md = EVP_MD_fetch(key->libctx, "SHA3-256", propq); + key->sha3_512_md = EVP_MD_fetch(key->libctx, "SHA3-512", propq); + return (key->shake128_md != NULL && key->shake256_md != NULL + && key->sha3_256_md != NULL && key->sha3_512_md != NULL); +} + ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties, int evp_type) { @@ -1623,17 +1644,10 @@ ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties, key->vinfo = vinfo; key->libctx = libctx; key->prov_flags = ML_KEM_KEY_PROV_FLAGS_DEFAULT; - key->shake128_md = EVP_MD_fetch(libctx, "SHAKE128", properties); - key->shake256_md = EVP_MD_fetch(libctx, "SHAKE256", properties); - key->sha3_256_md = EVP_MD_fetch(libctx, "SHA3-256", properties); - key->sha3_512_md = EVP_MD_fetch(libctx, "SHA3-512", properties); key->d = key->z = key->rho = key->pkhash = key->encoded_dk = key->seedbuf = NULL; key->s = key->m = key->t = NULL; - - if (key->shake128_md != NULL - && key->shake256_md != NULL - && key->sha3_256_md != NULL - && key->sha3_512_md != NULL) + key->shake128_md = key->shake256_md = key->sha3_256_md = key->sha3_512_md = NULL; + if (ossl_ml_kem_key_fetch_digest(key, properties)) return key; ossl_ml_kem_key_free(key); diff --git a/doc/man7/EVP_PKEY-ML-DSA.pod b/doc/man7/EVP_PKEY-ML-DSA.pod index 11498c161f7..173c3ddf2fe 100644 --- a/doc/man7/EVP_PKEY-ML-DSA.pod +++ b/doc/man7/EVP_PKEY-ML-DSA.pod @@ -96,6 +96,11 @@ respective key type of B, B or B. The encoded private key value of size 2560, 4032 or 4896 bytes depending on the respective key type of B, B or B. +=item "properties" (B) + +Can be used when importing raw keys using L, +to fetch internal digest algorithms. + =back =head2 Provider configuration parameters diff --git a/doc/man7/EVP_PKEY-ML-KEM.pod b/doc/man7/EVP_PKEY-ML-KEM.pod index d8bc67022db..31f2f2f0a34 100644 --- a/doc/man7/EVP_PKEY-ML-KEM.pod +++ b/doc/man7/EVP_PKEY-ML-KEM.pod @@ -116,6 +116,11 @@ are empty. Once a public or private key component is set, no further changes are allowed. This parameter is gettable and settable (once only). +=item "properties" (B) + +Can be used when importing raw keys using L, +to fetch internal digest algorithms. + =back =head2 Provider configuration parameters diff --git a/include/crypto/ml_dsa.h b/include/crypto/ml_dsa.h index d76636e14d3..831a6740a1b 100644 --- a/include/crypto/ml_dsa.h +++ b/include/crypto/ml_dsa.h @@ -80,6 +80,7 @@ __owur ML_DSA_KEY *ossl_ml_dsa_key_new(OSSL_LIB_CTX *libctx, const char *propq, int evp_type); /* Factory reset for keys that fail initialisation */ void ossl_ml_dsa_key_reset(ML_DSA_KEY *key); +__owur int ossl_ml_dsa_key_fetch_digests(ML_DSA_KEY *key, const char *propq); __owur int ossl_ml_dsa_key_pub_alloc(ML_DSA_KEY *key); __owur int ossl_ml_dsa_key_priv_alloc(ML_DSA_KEY *key); void ossl_ml_dsa_key_free(ML_DSA_KEY *key); diff --git a/include/crypto/ml_kem.h b/include/crypto/ml_kem.h index 390144a473a..7d1f3cd6028 100644 --- a/include/crypto/ml_kem.h +++ b/include/crypto/ml_kem.h @@ -231,6 +231,8 @@ void ossl_ml_kem_key_free(ML_KEM_KEY *key); */ ML_KEM_KEY *ossl_ml_kem_key_dup(const ML_KEM_KEY *key, int selection); +__owur int ossl_ml_kem_key_fetch_digest(ML_KEM_KEY *key, const char *propq); + /* * ----- Import or generate key material. */ diff --git a/providers/implementations/keymgmt/ml_dsa_kmgmt.c b/providers/implementations/keymgmt/ml_dsa_kmgmt.c index 1c4b8d71c4b..34710a4a2f7 100644 --- a/providers/implementations/keymgmt/ml_dsa_kmgmt.c +++ b/providers/implementations/keymgmt/ml_dsa_kmgmt.c @@ -20,6 +20,9 @@ #include "prov/providercommon.h" #include "prov/provider_ctx.h" #include "prov/ml_dsa.h" + +#define ml_dsa_export_params +#define ml_dsa_export_params_decoder #include "providers/implementations/keymgmt/ml_dsa_kmgmt.inc" static OSSL_FUNC_keymgmt_free_fn ml_dsa_free_key; @@ -27,8 +30,8 @@ static OSSL_FUNC_keymgmt_has_fn ml_dsa_has; static OSSL_FUNC_keymgmt_match_fn ml_dsa_match; static OSSL_FUNC_keymgmt_import_fn ml_dsa_import; static OSSL_FUNC_keymgmt_export_fn ml_dsa_export; -static OSSL_FUNC_keymgmt_import_types_fn ml_dsa_imexport_types; -static OSSL_FUNC_keymgmt_export_types_fn ml_dsa_imexport_types; +static OSSL_FUNC_keymgmt_import_types_fn ml_dsa_import_types; +static OSSL_FUNC_keymgmt_export_types_fn ml_dsa_export_types; static OSSL_FUNC_keymgmt_dup_fn ml_dsa_dup_key; static OSSL_FUNC_keymgmt_gettable_params_fn ml_dsa_gettable_params; static OSSL_FUNC_keymgmt_validate_fn ml_dsa_validate; @@ -204,9 +207,9 @@ static int ml_dsa_key_fromdata(ML_DSA_KEY *key, const OSSL_PARAM params[], const ML_DSA_PARAMS *key_params = ossl_ml_dsa_key_params(key); const uint8_t *pk = NULL, *sk = NULL, *seed = NULL; size_t pk_len = 0, sk_len = 0, seed_len = 0; - struct ml_dsa_key_type_params_st p; + struct ml_dsa_import_params_st p; - if (!ml_dsa_key_type_params_decoder(params, &p)) + if (!ml_dsa_import_params_decoder(params, &p)) return 0; if (p.pubkey != NULL) { @@ -249,6 +252,12 @@ static int ml_dsa_key_fromdata(ML_DSA_KEY *key, const OSSL_PARAM params[], return 0; } + if (p.propq != NULL) { + if (p.propq->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + if (!ossl_ml_dsa_key_fetch_digests(key, p.propq->data)) + return 0; + } if (seed_len != 0 && (sk_len == 0 || (ossl_ml_dsa_key_get_prov_flags(key) & ML_DSA_KEY_PREFER_SEED))) { @@ -304,11 +313,18 @@ static int ml_dsa_import(void *keydata, int selection, const OSSL_PARAM params[] return res; } -static const OSSL_PARAM *ml_dsa_imexport_types(int selection) +static const OSSL_PARAM *ml_dsa_import_types(int selection) +{ + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return NULL; + return ml_dsa_import_params_list; +} + +static const OSSL_PARAM *ml_dsa_export_types(int selection) { if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) return NULL; - return ml_dsa_key_type_params_list; + return ml_dsa_export_params_list; } static const OSSL_PARAM *ml_dsa_gettable_params(void *provctx) @@ -572,9 +588,9 @@ static void ml_dsa_gen_cleanup(void *genctx) { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ml_dsa_has }, \ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ml_dsa_match }, \ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ml_dsa_import }, \ - { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ml_dsa_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ml_dsa_import_types }, \ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ml_dsa_export }, \ - { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ml_dsa_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ml_dsa_export_types }, \ DISPATCH_LOAD_FN { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))ml_dsa_get_params }, \ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))ml_dsa_gettable_params }, \ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))ml_dsa_validate }, \ diff --git a/providers/implementations/keymgmt/ml_dsa_kmgmt.inc.in b/providers/implementations/keymgmt/ml_dsa_kmgmt.inc.in index e23d4cdc9ef..9ab17f5f65f 100644 --- a/providers/implementations/keymgmt/ml_dsa_kmgmt.inc.in +++ b/providers/implementations/keymgmt/ml_dsa_kmgmt.inc.in @@ -11,10 +11,17 @@ use OpenSSL::paramnames qw(produce_param_decoder); -} -{- produce_param_decoder('ml_dsa_key_type_params', +{- produce_param_decoder('ml_dsa_import_params', (['OSSL_PKEY_PARAM_ML_DSA_SEED', 'seed', 'octet_string'], ['OSSL_PKEY_PARAM_PUB_KEY', 'pubkey', 'octet_string'], ['OSSL_PKEY_PARAM_PRIV_KEY', 'privkey', 'octet_string'], + ['OSSL_PKEY_PARAM_PROPERTIES', 'propq', 'utf8_string'], + )); -} + +{- produce_param_decoder('ml_dsa_export_params', + (['OSSL_PKEY_PARAM_ML_DSA_SEED', 'seed', 'octet_string'], + ['OSSL_PKEY_PARAM_PUB_KEY', 'pubkey', 'octet_string'], + ['OSSL_PKEY_PARAM_PRIV_KEY', 'privkey', 'octet_string'] )); -} {- produce_param_decoder('ml_dsa_get_params', diff --git a/providers/implementations/keymgmt/ml_kem_kmgmt.c b/providers/implementations/keymgmt/ml_kem_kmgmt.c index 829597593b4..237c923960d 100644 --- a/providers/implementations/keymgmt/ml_kem_kmgmt.c +++ b/providers/implementations/keymgmt/ml_kem_kmgmt.c @@ -28,6 +28,8 @@ #include "prov/provider_ctx.h" #include "prov/securitycheck.h" #include "prov/ml_kem.h" +#define ml_kem_export_params_st +#define ml_kem_export_params_decoder #include "providers/implementations/keymgmt/ml_kem_kmgmt.inc" static OSSL_FUNC_keymgmt_new_fn ml_kem_512_new; @@ -52,8 +54,8 @@ static OSSL_FUNC_keymgmt_match_fn ml_kem_match; static OSSL_FUNC_keymgmt_validate_fn ml_kem_validate; static OSSL_FUNC_keymgmt_import_fn ml_kem_import; static OSSL_FUNC_keymgmt_export_fn ml_kem_export; -static OSSL_FUNC_keymgmt_import_types_fn ml_kem_imexport_types; -static OSSL_FUNC_keymgmt_export_types_fn ml_kem_imexport_types; +static OSSL_FUNC_keymgmt_import_types_fn ml_kem_import_types; +static OSSL_FUNC_keymgmt_export_types_fn ml_kem_export_types; static OSSL_FUNC_keymgmt_dup_fn ml_kem_dup; static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS @@ -339,10 +341,17 @@ err: return ret; } -static const OSSL_PARAM *ml_kem_imexport_types(int selection) +static const OSSL_PARAM *ml_kem_import_types(int selection) { if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) - return ml_kem_key_type_params_list; + return ml_kem_import_params_list; + return NULL; +} + +static const OSSL_PARAM *ml_kem_export_types(int selection) +{ + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + return ml_kem_export_params_list; return NULL; } @@ -383,19 +392,18 @@ static int check_prvenc(const uint8_t *prvenc, ML_KEM_KEY *key) return 0; } -static int ml_kem_key_fromdata(ML_KEM_KEY *key, - const OSSL_PARAM params[], +static int ml_kem_key_fromdata(ML_KEM_KEY *key, const OSSL_PARAM params[], int include_private) { const void *pubenc = NULL, *prvenc = NULL, *seedenc = NULL; size_t publen = 0, prvlen = 0, seedlen = 0, puboff; const ML_KEM_VINFO *v; - struct ml_kem_key_type_params_st p; + struct ml_kem_import_params_st p; /* Invalid attempt to mutate a key, what is the right error to report? */ if (key == NULL || ossl_ml_kem_have_pubkey(key) - || !ml_kem_key_type_params_decoder(params, &p)) + || !ml_kem_import_params_decoder(params, &p)) return 0; v = ossl_ml_kem_key_vinfo(key); @@ -453,6 +461,12 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key, return 0; } } + if (p.propq != NULL) { + if (p.propq->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + if (!ossl_ml_kem_key_fetch_digest(key, p.propq->data)) + return 0; + } if (seedlen != 0 && (prvlen == 0 || (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) { @@ -849,9 +863,9 @@ static void *ml_kem_dup(const void *vkey, int selection) { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (OSSL_FUNC)ml_kem_gen_cleanup }, \ DISPATCH_LOAD_FN { OSSL_FUNC_KEYMGMT_DUP, (OSSL_FUNC)ml_kem_dup }, \ { OSSL_FUNC_KEYMGMT_IMPORT, (OSSL_FUNC)ml_kem_import }, \ - { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC)ml_kem_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC)ml_kem_import_types }, \ { OSSL_FUNC_KEYMGMT_EXPORT, (OSSL_FUNC)ml_kem_export }, \ - { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC)ml_kem_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC)ml_kem_export_types }, \ OSSL_DISPATCH_END \ } DECLARE_VARIANT(512); diff --git a/providers/implementations/keymgmt/ml_kem_kmgmt.inc.in b/providers/implementations/keymgmt/ml_kem_kmgmt.inc.in index 329685ae2db..76ef7fd571f 100644 --- a/providers/implementations/keymgmt/ml_kem_kmgmt.inc.in +++ b/providers/implementations/keymgmt/ml_kem_kmgmt.inc.in @@ -11,10 +11,16 @@ use OpenSSL::paramnames qw(produce_param_decoder); -} -{- produce_param_decoder('ml_kem_key_type_params', +{- produce_param_decoder('ml_kem_import_params', (['OSSL_PKEY_PARAM_ML_KEM_SEED', 'seed', 'octet_string'], ['OSSL_PKEY_PARAM_PRIV_KEY', 'privkey', 'octet_string'], ['OSSL_PKEY_PARAM_PUB_KEY', 'pubkey', 'octet_string'], + ['OSSL_PKEY_PARAM_PROPERTIES', 'propq', 'utf8_string'] + )); -} +{- produce_param_decoder('ml_kem_export_params', + (['OSSL_PKEY_PARAM_ML_KEM_SEED', 'seed', 'octet_string'], + ['OSSL_PKEY_PARAM_PRIV_KEY', 'privkey', 'octet_string'], + ['OSSL_PKEY_PARAM_PUB_KEY', 'pubkey', 'octet_string'] )); -} {- produce_param_decoder('ml_kem_get_params', diff --git a/test/ml_dsa_test.c b/test/ml_dsa_test.c index 8c3be48e8de..1553f66c8f7 100644 --- a/test/ml_dsa_test.c +++ b/test/ml_dsa_test.c @@ -56,7 +56,7 @@ static int ml_dsa_create_keypair(EVP_PKEY **pkey, const char *name, { int ret = 0, selection = 0; EVP_PKEY_CTX *ctx = NULL; - OSSL_PARAM params[3], *p = params; + OSSL_PARAM params[4], *p = params; if (priv != NULL) { *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, @@ -68,6 +68,7 @@ static int ml_dsa_create_keypair(EVP_PKEY **pkey, const char *name, (uint8_t *)pub, pub_len); selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY; } + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, "?fips=yes", 0); *p = OSSL_PARAM_construct_end(); if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(lib_ctx, name, NULL)) diff --git a/test/ml_kem_evp_extra_test.c b/test/ml_kem_evp_extra_test.c index def63903e24..877e7835441 100644 --- a/test/ml_kem_evp_extra_test.c +++ b/test/ml_kem_evp_extra_test.c @@ -401,6 +401,25 @@ static int test_non_derandomised_ml_kem(void) return ret == 0; } +static int test_ml_kem_from_data_propq(void) +{ + int ret = 0; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + OSSL_PARAM params[3]; + + params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, gen_seed, sizeof(gen_seed)); + params[1] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, "fips=no", 0); + params[2] = OSSL_PARAM_construct_end(); + + ret = TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(testctx, "ML-KEM-768", NULL)) + && TEST_int_eq(EVP_PKEY_fromdata_init(ctx), 1) + && TEST_int_eq(EVP_PKEY_fromdata(ctx, &pkey, OSSL_KEYMGMT_SELECT_KEYPAIR, params), 1); + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); + return ret; +} + int setup_tests(void) { int test_rand = 0; @@ -428,5 +447,6 @@ int setup_tests(void) } ADD_TEST(test_ml_kem); + ADD_TEST(test_ml_kem_from_data_propq); return 1; }