From: Viktor Dukhovni Date: Fri, 17 Jan 2025 16:28:51 +0000 (+1100) Subject: Improved import and export X-Git-Tag: openssl-3.5.0-alpha1~524 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=869903c07c56c8c44d4b7362fd56244e4de77d6b;p=thirdparty%2Fopenssl.git Improved import and export - On import, if a seed is provided, the keys are regenerated. - The seed is exported as a separate "seed" parameter, when available. The "ml-kem.retain_seed" parameter is also exported, when false. - The seed is optionally dropped after key generation. * When the "ml-kem.retain_seed" keygen parameter is set to zero. * When the "ml-kem.retain_seed" keygen parameter is not set to 1, and the "ml-kem.retain_seed" provider config property is set explictly false. - The exported private key parameter "priv" is always the FIPS 203 |dk|. - Private key decoding from PKCS#8 produces a transient "seed-only" form of the key, in which "retain_seed" is set to false when the "ml-kem.retain_seed" provider config property is set explictly false. The full key is generated during "load" and the seed is retained or not as specified. - Import honours the "ml-kem.retain_seed" parameter when specified, or otherwise honours the provider's "ml-kem.retain_seed" property. Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/26512) --- diff --git a/crypto/ml_kem/ml_kem.c b/crypto/ml_kem/ml_kem.c index ed21ecdafef..1379d849a2b 100644 --- a/crypto/ml_kem/ml_kem.c +++ b/crypto/ml_kem/ml_kem.c @@ -1337,8 +1337,7 @@ static int parse_prvkey(const uint8_t *in, EVP_MD_CTX *mdctx, ML_KEM_KEY *key) * and correctly encoded) ciphertext inputs. */ static __owur -int genkey(const uint8_t d[ML_KEM_RANDOM_BYTES], - const uint8_t z[ML_KEM_RANDOM_BYTES], +int genkey(const uint8_t seed[ML_KEM_SEED_BYTES], EVP_MD_CTX *mdctx, uint8_t *pubenc, ML_KEM_KEY *key) { uint8_t hashed[2 * ML_KEM_RANDOM_BYTES]; @@ -1354,7 +1353,7 @@ int genkey(const uint8_t d[ML_KEM_RANDOM_BYTES], * Use the "d" seed salted with the rank to derive the public and private * seeds rho and sigma. */ - memcpy(augmented_seed, d, ML_KEM_RANDOM_BYTES); + memcpy(augmented_seed, seed, ML_KEM_RANDOM_BYTES); augmented_seed[ML_KEM_RANDOM_BYTES] = (uint8_t) rank; if (!hash_g(hashed, augmented_seed, sizeof(augmented_seed), mdctx, key)) goto end; @@ -1384,9 +1383,16 @@ int genkey(const uint8_t d[ML_KEM_RANDOM_BYTES], } /* Save |z| portion of seed for "implicit rejection" on failure. */ - memcpy(key->z, z, ML_KEM_RANDOM_BYTES); - /* Also save |d| portion, in suport of likely alternative key format. */ - memcpy(key->d = key->z + ML_KEM_RANDOM_BYTES, d, ML_KEM_RANDOM_BYTES); + memcpy(key->z, seed + ML_KEM_RANDOM_BYTES, ML_KEM_RANDOM_BYTES); + + /* Optionally save the |d| portion of the seed */ + key->d = key->z + ML_KEM_RANDOM_BYTES; + if (key->retain_seed) { + memcpy(key->d, seed, ML_KEM_RANDOM_BYTES); + } else { + OPENSSL_cleanse(key->d, ML_KEM_RANDOM_BYTES); + key->d = NULL; + } ret = 1; end: @@ -1494,8 +1500,19 @@ int add_storage(scalar *p, int private, ML_KEM_KEY *key) if (p == NULL) return 0; + + /* + * We're adding key material, the seed buffer will now hold |rho| and + * |pkhash|. + */ + memset(key->seedbuf, 0, sizeof(key->seedbuf)); + key->rho = key->seedbuf; + key->pkhash = key->seedbuf + ML_KEM_RANDOM_BYTES; + key->d = key->z = NULL; + /* A public key needs space for |t| and |m| */ key->m = (key->t = p) + rank; + /* * A private key also needs space for |s| and |z|. * The |z| buffer always includes additional space for |d|, but a key's |d| @@ -1521,10 +1538,14 @@ free_storage(ML_KEM_KEY *key) * Cleanse any sensitive data: * - The private vector |s| is immediately followed by the FO failure * secret |z|, and seed |d|, we can cleanse all three in one call. + * + * - Otherwise, when key->d is set, cleanse the stashed seed. */ if (ossl_ml_kem_have_prvkey(key)) OPENSSL_cleanse(key->s, key->vinfo->vector_bytes + 2 * ML_KEM_RANDOM_BYTES); + else if (ossl_ml_kem_have_seed(key)) + OPENSSL_cleanse(key->seedbuf, sizeof(key->seedbuf)); OPENSSL_free(key->t); key->d = key->z = (uint8_t *)(key->s = key->m = key->t = NULL); } @@ -1550,8 +1571,12 @@ const ML_KEM_VINFO *ossl_ml_kem_get_vinfo(int evp_type) return NULL; } +/* + * The |retain_seed| parameter indicates whether the seed should be retained + * once the key is generated. + */ ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties, - int evp_type) + int retain_seed, int evp_type) { const ML_KEM_VINFO *vinfo = ossl_ml_kem_get_vinfo(evp_type); ML_KEM_KEY *key; @@ -1564,11 +1589,13 @@ ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties, key->vinfo = vinfo; key->libctx = libctx; + key->retain_seed = retain_seed; 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 = (uint8_t *)(key->s = key->m = key->t = NULL); + key->d = key->z = key->rho = key->pkhash = NULL; + key->s = key->m = key->t = NULL; if (key->shake128_md != NULL && key->shake256_md != NULL @@ -1588,9 +1615,17 @@ ML_KEM_KEY *ossl_ml_kem_key_dup(const ML_KEM_KEY *key, int selection) if (key == NULL || (ret = OPENSSL_memdup(key, sizeof(*key))) == NULL) return NULL; - ret->d = ret->z = NULL; + ret->d = ret->z = ret->rho = ret->pkhash = NULL; ret->s = ret->m = ret->t = NULL; + /* Handle seed-only keys */ + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0 + && !ossl_ml_kem_have_prvkey(key) + && ossl_ml_kem_have_seed(key)) { + ret->z = ret->seedbuf; + ret->d = ret->z + ML_KEM_RANDOM_BYTES; + } + /* Clear selection bits we can't fulfill */ if (!ossl_ml_kem_have_pubkey(key)) selection = 0; @@ -1603,6 +1638,8 @@ ML_KEM_KEY *ossl_ml_kem_key_dup(const ML_KEM_KEY *key, int selection) break; case OSSL_KEYMGMT_SELECT_PUBLIC_KEY: ok = add_storage(OPENSSL_memdup(key->t, key->vinfo->puballoc), 0, ret); + ret->rho = ret->seedbuf; + ret->pkhash = ret->rho + ML_KEM_RANDOM_BYTES; break; case OSSL_KEYMGMT_SELECT_PRIVATE_KEY: ok = add_storage(OPENSSL_memdup(key->t, key->vinfo->prvalloc), 1, ret); @@ -1661,17 +1698,45 @@ int ossl_ml_kem_encode_private_key(uint8_t *out, size_t len, return 1; } -int ossl_ml_kem_encode_key_seed(uint8_t *out, size_t len, - const ML_KEM_KEY *key) +int ossl_ml_kem_encode_seed(uint8_t *out, size_t len, + const ML_KEM_KEY *key) { if (key == NULL || key->d == NULL || len != ML_KEM_SEED_BYTES) return 0; + /* + * Both in the seed buffer, and in the allocated storage, the |d| component + * of the seed is stored last, so we must copy each separately. + */ memcpy(out, key->d, ML_KEM_RANDOM_BYTES); out += ML_KEM_RANDOM_BYTES; memcpy(out, key->z, ML_KEM_RANDOM_BYTES); return 1; } +/* + * Stash the seed without (yet) performing a keygen, used during decoding, to + * avoid an extra keygen if we're only going to export the key again to load + * into another provider. + */ +ML_KEM_KEY *ossl_ml_kem_set_seed(const uint8_t *seed, size_t seedlen, ML_KEM_KEY *key) +{ + if (key == NULL + || ossl_ml_kem_have_pubkey(key) + || ossl_ml_kem_have_seed(key) + || seedlen != ML_KEM_SEED_BYTES) + return NULL; + /* + * With no public or private key material on hand, we can use the seed + * buffer for |z| and |d|, in that order. + */ + key->z = key->seedbuf; + key->d = key->z + ML_KEM_RANDOM_BYTES; + memcpy(key->d, seed, ML_KEM_RANDOM_BYTES); + seed += ML_KEM_RANDOM_BYTES; + memcpy(key->z, seed, ML_KEM_RANDOM_BYTES); + return key; +} + /* Parse input as a public key */ int ossl_ml_kem_parse_public_key(const uint8_t *in, size_t len, ML_KEM_KEY *key) { @@ -1724,14 +1789,12 @@ int ossl_ml_kem_parse_private_key(const uint8_t *in, size_t len, } /* - * Generate a new keypair either from the input seeds (when non-null), yielding - * a deterministic result for running tests, or securely generated random data. + * Generate a new keypair, either from the saved seed (when non-null), or from + * the RNG. */ -int ossl_ml_kem_genkey(const uint8_t *d, const uint8_t *z, - uint8_t *pubenc, size_t publen, - ML_KEM_KEY *key) +int ossl_ml_kem_genkey(uint8_t *pubenc, size_t publen, ML_KEM_KEY *key) { - uint8_t tmpseed[2 * ML_KEM_RANDOM_BYTES]; + uint8_t seed[ML_KEM_SEED_BYTES]; EVP_MD_CTX *mdctx = NULL; const ML_KEM_VINFO *vinfo; int ret = 0; @@ -1740,32 +1803,30 @@ int ossl_ml_kem_genkey(const uint8_t *d, const uint8_t *z, return 0; vinfo = key->vinfo; - /* Both seeds or neither must be NULL */ - if (((d == NULL) ^ (z == NULL)) != 0 - || (pubenc != NULL && publen != vinfo->pubkey_bytes) + if ((pubenc != NULL && publen != vinfo->pubkey_bytes) || (mdctx = EVP_MD_CTX_new()) == NULL) return 0; - if (d == NULL) { - if (RAND_priv_bytes_ex(key->libctx, tmpseed, 2 * ML_KEM_RANDOM_BYTES, - key->vinfo->secbits) <= 0) - return 0; - d = tmpseed; - z = tmpseed + ML_KEM_RANDOM_BYTES; + if (ossl_ml_kem_have_seed(key)) { + ossl_ml_kem_encode_seed(seed, sizeof(seed), key); + key->d = key->z = NULL; + } else if (RAND_priv_bytes_ex(key->libctx, seed, sizeof(seed), + key->vinfo->secbits) <= 0) { + return 0; } + /* * Data derived from (d, z) defaults secret, and to avoid side-channel * leaks should not influence control flow. */ - CONSTTIME_SECRET(d, ML_KEM_RANDOM_BYTES); - CONSTTIME_SECRET(z, ML_KEM_RANDOM_BYTES); + CONSTTIME_SECRET(seed, ML_KEM_SEED_BYTES); if (add_storage(OPENSSL_malloc(vinfo->prvalloc), 1, key)) - ret = genkey(d, z, mdctx, pubenc, key); + ret = genkey(seed, mdctx, pubenc, key); + OPENSSL_cleanse(seed, sizeof(seed)); /* Declassify secret inputs and derived outputs before returning control */ - CONSTTIME_DECLASSIFY(d, ML_KEM_RANDOM_BYTES); - CONSTTIME_DECLASSIFY(z, ML_KEM_RANDOM_BYTES); + CONSTTIME_DECLASSIFY(seed, ML_KEM_SEED_BYTES); EVP_MD_CTX_free(mdctx); if (!ret) { diff --git a/doc/man7/EVP_PKEY-ML-KEM.pod b/doc/man7/EVP_PKEY-ML-KEM.pod index 7a4cb8f11d6..12b687902ec 100644 --- a/doc/man7/EVP_PKEY-ML-KEM.pod +++ b/doc/man7/EVP_PKEY-ML-KEM.pod @@ -16,11 +16,9 @@ EVP_PKEY-ML-KEM The B, B, and B keytypes are implemented in OpenSSL's default and FIPS providers. -=for comment (TODO(ML-KEM): Add FIPS support). - =head2 Keygen Parameters -By default, no parameters are required for generating a key pair. +No mandatory parameters are required for generating a key pair. =over 4 @@ -28,16 +26,48 @@ By default, no parameters are required for generating a key pair. Internally, ML-KEM generates keys using a 64-byte random value (seed), which is the concatenation of the 32-byte I and I parameters described in FIPS 203. -The optional parameter can be used to set a pre-determined seed prior to +This optional parameter can be used to set a pre-determined seed prior to keypair generation. -According to FIPS 203, section 3.3, this parameter should only be used for test -purposes and be treated with the same care as private key material. -This parameter is only settable. +Generated keys default to retaining the seed used. +The seed is also by default retained when keys are loaded from B files +in the seed format. +When available, the seed parameter is also used during key export and import, +with keys regenerated from the seed even if separately provided on import. + +When the seed is retained, it is also available as a B parameter, +and private key output to B files will be in seed format. +When the seed is not available, because not included in the B file, not +available on import, or not retained, B private key files will have the +private key in FIPS 203 C format, without DER octet-string wrapping. + +Retention of the seed can be disabled by setting the C +parameter of the C provider to C. +See the description of the B<-provparam> option in L to learn +how to set provider options in the command line tools. +See L to learn how to set provider +configuration options programmatically. +When using the FIPS provider, the parameter may need to be set in the C +provider instead (or perhaps in all providers) if the intent is to retain seeds +read from PKCS#8 files. + +In addition to its use in key generation, this parameter is also gettable and +settable. See L for further information. +=item C (B) + +This parameter is used only in key generation. +When keys are generated, by default the seed is retained and used as the +private key form on output when encoding. +When this parameter is set to a nonzero value, the seed is retained during key +generation, when set to 0, the seed is not retained. +When this parameter is simply omitted, and the C provider +configuration parameter is set to a false boolean value, the seed is not +retained, otherwise it is retained (see also L). + =back Use EVP_PKEY_CTX_set_params() after calling EVP_PKEY_keygen_init(). @@ -45,8 +75,8 @@ Use EVP_PKEY_CTX_set_params() after calling EVP_PKEY_keygen_init(). =head2 Common parameters In addition to the common parameters that all keytypes should support (see -L), the implementation of these keytypes -support the following. +L), B keys +keys support the following. =over 4 @@ -58,25 +88,17 @@ This parameter is used when importing or exporting the public key value with the EVP_PKEY_fromdata() and EVP_PKEY_todata() functions. The same underlying FIPS 203 (Algorithm 16: B) B public key format is used for import, export, get and set operations. +It is otherwise only gettable. =item "priv" (B) -The private key, in seed form when available. +The private key value. This parameter is used when importing or exporting the private key value with the EVP_PKEY_fromdata() and EVP_PKEY_todata() functions. -On import, the private key key length is used to infer the key format. -If the key length is 64 bytes, the key format is assumed to be the 64-byte -input seed of FIPS 203, Algorithm 16: B, with the B -and B values concatenated in that order. -If, on the other hand, the key length is that of the FIPS 203 (Algorithm 16: -B) B private key for the given ML-KEM variant, then -the key is assumed to be in that format. - -Keys originally imported in the B FIPS 203 form cannot be exported in seed -form as the B seed is not available in that case, and the exported key will -then be the FIPS 203 B format in which the key was imported. -Generated keys retain the seed value, and are exported in seed form. +The key length and content must be that of the FIPS 203 (Algorithm 16: +B) B private key for the given ML-KEM variant. +It is otherwise only gettable. =item "encoded-pub-key" (B) @@ -89,15 +111,6 @@ Once a public or private key component is set, no further changes are allowed. This parameter is gettable and settable. -=item "encoded-priv-key" (B) - -Used for getting the I encoding of a private key. -The key format is that of B in FIPS 203, Algorithm 16: B. -The private key format returned from key export is the B<(d, z)> seed, when -available, but is otherwise the same as the encoded form. - -This parameter is only gettable. - =back =head1 CONFORMING TO @@ -121,12 +134,50 @@ An B key can be generated like this: Equivalent calls are available for B and B. +An B private key in seed format can be converted to a key in expanded +FIPS 203 B format by running: + + $ openssl pkey -provparam ml-kem.retain_seed=no -in seed.pem -out long.pem + +To generate an, e.g., B key, in expanded format, you can run: + + $ openssl genpkey -provparam ml-kem.retain_seed=no \ + -algorithm ml-kem-768 -out long.pem + +In the B file, this looks like: + + openssl_conf = openssl_init + + [openssl_init] + providers = providers_sect + + # Can be referenced in one or more provider sections + [ml_kem_sect] + retain_seed = no + + [providers_sect] + default = default_sect + base = base_sect + + [default_sect] + ml-kem = ml_kem_sect + + [base_sect] + ml-kem = ml_kem_sect + =head1 SEE ALSO +L, +L, +L, L, L, +L, +L, +L, L, -L +L, +LOSSL_PROVIDER_add_conf_parameter(3)> =head1 HISTORY diff --git a/include/crypto/ml_kem.h b/include/crypto/ml_kem.h index d3ea0aba033..c47ff12779e 100644 --- a/include/crypto/ml_kem.h +++ b/include/crypto/ml_kem.h @@ -49,6 +49,10 @@ # define ML_KEM_PKHASH_BYTES 32 /* Salts the shared-secret */ # define ML_KEM_SHARED_SECRET_BYTES 32 +# if ML_KEM_PKHASH_BYTES != ML_KEM_RANDOM_BYTES +# error "unexpected ML-KEM public key hash size" +# endif + /*- * The ML-KEM specification can be found in * https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf @@ -159,15 +163,22 @@ typedef struct ossl_ml_kem_key_st { * storage is allocated once a public or private key is specified, at * which point the key becomes immutable. */ + uint8_t *rho; /* Public matrix seed */ + uint8_t *pkhash; /* Public key hash */ struct ossl_ml_kem_scalar_st *t; /* Public key vector */ struct ossl_ml_kem_scalar_st *m; /* Pre-computed pubkey matrix */ struct ossl_ml_kem_scalar_st *s; /* Private key secret vector */ uint8_t *z; /* Private key FO failure secret */ uint8_t *d; /* Private key seed */ + int retain_seed; /* Retain the seed after keygen? */ - /* Fixed-size/offset built-ins */ - uint8_t rho[ML_KEM_RANDOM_BYTES]; /* Matrix recovery seed */ - uint8_t pkhash[ML_KEM_PKHASH_BYTES]; /* Hash of wire-form public key */ + /* + * Fixed-size built-in buffer, which holds the |rho| and the public key + * |pkhash| in that order, once we have expanded key material. + * With seed-only keys, that are not yet expanded, this instead holds the + * |z| and |d| components in that order. + */ + uint8_t seedbuf[64]; /* |rho| + |pkhash| / |z| + |d| */ } ML_KEM_KEY; /* The public key is always present, when the private is */ @@ -186,7 +197,7 @@ typedef struct ossl_ml_kem_key_st { */ ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties, - int evp_type); + int retain_seed, int evp_type); /* Deallocate the key */ void ossl_ml_kem_key_free(ML_KEM_KEY *key); /* @@ -212,10 +223,10 @@ int ossl_ml_kem_parse_public_key(const uint8_t *in, size_t len, __owur int ossl_ml_kem_parse_private_key(const uint8_t *in, size_t len, ML_KEM_KEY *key); +ML_KEM_KEY *ossl_ml_kem_set_seed(const uint8_t *seed, size_t seedlen, + ML_KEM_KEY *key); __owur -int ossl_ml_kem_genkey(const uint8_t *d, const uint8_t *z, - uint8_t *pubenc, size_t publen, - ML_KEM_KEY *key); +int ossl_ml_kem_genkey(uint8_t *pubenc, size_t publen, ML_KEM_KEY *key); /* * Perform an ML-KEM operation with a given ML-KEM key. The key can generally @@ -228,9 +239,8 @@ int ossl_ml_kem_encode_public_key(uint8_t *out, size_t len, __owur int ossl_ml_kem_encode_private_key(uint8_t *out, size_t len, const ML_KEM_KEY *key); -__owur -int ossl_ml_kem_encode_key_seed(uint8_t *out, size_t len, - const ML_KEM_KEY *key); +int ossl_ml_kem_encode_seed(uint8_t *out, size_t len, + const ML_KEM_KEY *key); __owur int ossl_ml_kem_encap_seed(uint8_t *ctext, size_t clen, diff --git a/providers/fips/self_test_kats.c b/providers/fips/self_test_kats.c index 10ed646d040..5ba0d9348bf 100644 --- a/providers/fips/self_test_kats.c +++ b/providers/fips/self_test_kats.c @@ -773,7 +773,7 @@ static EVP_PKEY *self_test_kem_keygen(const ST_KAT_KEM *t, OSSL_SELF_TEST *st, /* Compare outputs */ *params = OSSL_PARAM_construct_octet_string( - OSSL_PKEY_PARAM_ENCODED_PRIVATE_KEY, buf, s); + OSSL_PKEY_PARAM_PRIV_KEY, buf, s); if (!EVP_PKEY_get_params(r, params)) goto err; OSSL_SELF_TEST_oncorrupt_byte(st, buf); diff --git a/providers/implementations/encode_decode/decode_der2key.c b/providers/implementations/encode_decode/decode_der2key.c index 4ae31ad18dd..0d30c3f08d8 100644 --- a/providers/implementations/encode_decode/decode_der2key.c +++ b/providers/implementations/encode_decode/decode_der2key.c @@ -23,6 +23,7 @@ #include #include /* PEM_BUFSIZE and public PEM functions */ #include +#include #include #include #include "internal/cryptlib.h" /* ossl_assert() */ @@ -625,6 +626,7 @@ ml_kem_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx) ML_KEM_PRIV_FMT fmt; int plen, ptype, privlen, pairlen; uint16_t keylen; + int retain; /* Extract the key OID and any parameters (we want none of those) */ if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, der, der_len)) == NULL) @@ -664,13 +666,14 @@ ml_kem_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx) "unexpected PKCS#8 private key length: %d", plen); break; case SEED_FMT: - if ((key = ossl_ml_kem_key_new(libctx, ctx->propq, - ctx->desc->evp_type)) != NULL - && ossl_ml_kem_genkey(p, p + ML_KEM_RANDOM_BYTES, NULL, 0, key)) - ret = key; + retain = ossl_prov_ctx_get_bool_param( + ctx->provctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1); + if ((key = ossl_ml_kem_key_new(libctx, ctx->propq, retain, + ctx->desc->evp_type)) != NULL) + ret = ossl_ml_kem_set_seed(p, plen, key); else - ERR_raise_data(ERR_LIB_PROV, PROV_R_GENERATE_ERROR, - "error generating %s private key from seed", + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "error storing %s private key seed", vinfo->algorithm_name); break; case ASN1_FMT: @@ -682,7 +685,7 @@ ml_kem_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx) /* fallthrough */ case LONG_FMT: /* Check public key consistency if provided? */ - if ((key = ossl_ml_kem_key_new(libctx, ctx->propq, + if ((key = ossl_ml_kem_key_new(libctx, ctx->propq, 1, ctx->desc->evp_type)) != NULL && ossl_ml_kem_parse_private_key(p, vinfo->prvkey_bytes, key)) ret = key; @@ -755,7 +758,7 @@ ml_kem_d2i_PUBKEY(const uint8_t **der, long der_len, goto end; } - ret = ossl_ml_kem_key_new(libctx, ctx->propq, ctx->desc->evp_type); + ret = ossl_ml_kem_key_new(libctx, ctx->propq, 1, ctx->desc->evp_type); if (ret == NULL || !ossl_ml_kem_parse_public_key(spki->pubkey->data, spki->pubkey->length, ret)) { diff --git a/providers/implementations/encode_decode/encode_key2any.c b/providers/implementations/encode_decode/encode_key2any.c index 8f5b2fa86b9..d2370c99016 100644 --- a/providers/implementations/encode_decode/encode_key2any.c +++ b/providers/implementations/encode_decode/encode_key2any.c @@ -936,7 +936,7 @@ static int ml_kem_pki_priv_to_der(const void *vkey, unsigned char **pder, return 0; if (ossl_ml_kem_have_seed(key)) { - if (ossl_ml_kem_encode_key_seed(*pder, len, key)) + if (ossl_ml_kem_encode_seed(*pder, len, key)) return len; } else { if (ossl_ml_kem_encode_private_key(*pder, len, key)) diff --git a/providers/implementations/encode_decode/encode_key2text.c b/providers/implementations/encode_decode/encode_key2text.c index 9c7a59f45f3..3a335eb4bc8 100644 --- a/providers/implementations/encode_decode/encode_key2text.c +++ b/providers/implementations/encode_decode/encode_key2text.c @@ -473,7 +473,7 @@ static int ml_kem_to_text(BIO *out, const void *vkey, int selection) return 0; if (ossl_ml_kem_have_seed(key)) { - if (!ossl_ml_kem_encode_key_seed(seed, sizeof(seed), key)) + if (!ossl_ml_kem_encode_seed(seed, sizeof(seed), key)) goto end; if (!ossl_bio_print_labeled_buf(out, "seed:", seed, sizeof(seed))) goto end; diff --git a/providers/implementations/keymgmt/ml_kem_kmgmt.c b/providers/implementations/keymgmt/ml_kem_kmgmt.c index dfb39e3813a..9aa0289211a 100644 --- a/providers/implementations/keymgmt/ml_kem_kmgmt.c +++ b/providers/implementations/keymgmt/ml_kem_kmgmt.c @@ -12,15 +12,16 @@ #include #include #include +#include #include #include -#include "crypto/ml_kem.h" -#include "internal/param_build_set.h" #include +#include "crypto/ml_kem.h" #include "prov/implementations.h" #include "prov/providercommon.h" #include "prov/provider_ctx.h" #include "prov/securitycheck.h" +#include "internal/param_build_set.h" static OSSL_FUNC_keymgmt_new_fn ml_kem_512_new; static OSSL_FUNC_keymgmt_new_fn ml_kem_768_new; @@ -54,6 +55,7 @@ typedef struct ml_kem_gen_ctx_st { OSSL_LIB_CTX *libctx; char *propq; int selection; + int retain_seed; int evp_type; uint8_t seedbuf[ML_KEM_SEED_BYTES]; uint8_t *seed; @@ -125,11 +127,19 @@ err: } #endif /* FIPS_MODULE */ -static void *ml_kem_new(OSSL_LIB_CTX *libctx, char *propq, int evp_type) +static void *ml_kem_new(PROV_CTX *ctx, int evp_type) { + OSSL_LIB_CTX *libctx = NULL; + int retain_seed = 1; + if (!ossl_prov_is_running()) return NULL; - return ossl_ml_kem_key_new(libctx, propq, evp_type); + if (ctx != NULL) { + libctx = PROV_LIBCTX_OF(ctx); + retain_seed = ossl_prov_ctx_get_bool_param( + ctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1); + } + return ossl_ml_kem_key_new(libctx, NULL, retain_seed, evp_type); } static int ml_kem_has(const void *vkey, int selection) @@ -171,10 +181,9 @@ static int ml_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb, ML_KEM_KEY *key = vkey; OSSL_PARAM_BLD *tmpl = NULL; OSSL_PARAM *params = NULL; - uint8_t *pubenc = NULL; - uint8_t *prvenc = NULL; const ML_KEM_VINFO *v; - size_t prvlen = 0; + uint8_t *pubenc = NULL, *prvenc = NULL, *seedenc = NULL; + size_t prvlen = 0, seedlen = 0; int ret = 0; if (!ossl_prov_is_running() || key == NULL) @@ -183,33 +192,34 @@ static int ml_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb, if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) return 0; - /* Fail when no key material has yet been provided */ - if (!ossl_ml_kem_have_pubkey(key)) { - ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); - return 0; - } v = ossl_ml_kem_key_vinfo(key); - - if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + if (!ossl_ml_kem_have_pubkey(key)) { + /* Fail when no key material can be returned */ + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0 + || !ossl_ml_kem_have_seed(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { pubenc = OPENSSL_malloc(v->pubkey_bytes); if (pubenc == NULL || !ossl_ml_kem_encode_public_key(pubenc, v->pubkey_bytes, key)) goto err; } - if (ossl_ml_kem_have_prvkey(key) - && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { /* - * Allocated on the secure heap if configured, this is detected in - * ossl_param_build_set_octet_string(), which will then also use the - * secure heap. + * The seed and/or private key material are allocated on the secure + * heap if configured, ossl_param_build_set_octet_string(), will then + * also use the secure heap. */ if (ossl_ml_kem_have_seed(key)) { - prvlen = ML_KEM_SEED_BYTES; - if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL - || !ossl_ml_kem_encode_key_seed(prvenc, prvlen, key)) + seedlen = ML_KEM_SEED_BYTES; + if ((seedenc = OPENSSL_secure_zalloc(seedlen)) == NULL + || !ossl_ml_kem_encode_seed(seedenc, seedlen, key)) goto err; - } else { + } + if (ossl_ml_kem_have_prvkey(key)) { prvlen = v->prvkey_bytes; if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL || !ossl_ml_kem_encode_private_key(prvenc, prvlen, key)) @@ -221,21 +231,26 @@ static int ml_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb, if (tmpl == NULL) goto err; - /* The public key on request; it is always available when either is */ - if (pubenc != NULL - && !ossl_param_build_set_octet_string( - tmpl, params, OSSL_PKEY_PARAM_PUB_KEY, pubenc, v->pubkey_bytes)) - goto err; + /* The (d, z) seed, when available and private keys are requested. */ + if (seedenc != NULL + && (!ossl_param_build_set_octet_string( + tmpl, params, OSSL_PKEY_PARAM_ML_KEM_SEED, seedenc, seedlen) + || (!key->retain_seed && !ossl_param_build_set_int( + tmpl, params, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 0)))) + goto err; - /*- - * The private key on request, in the (d, z) seed format, when available, - * otherwise in the FIPS 203 |dk| format. - */ + /* The private key in the FIPS 203 |dk| format, when requested. */ if (prvenc != NULL && !ossl_param_build_set_octet_string( tmpl, params, OSSL_PKEY_PARAM_PRIV_KEY, prvenc, prvlen)) goto err; + /* The public key on request; it is always available when either is */ + if (pubenc != NULL + && !ossl_param_build_set_octet_string( + tmpl, params, OSSL_PKEY_PARAM_PUB_KEY, pubenc, v->pubkey_bytes)) + goto err; + params = OSSL_PARAM_BLD_to_param(tmpl); if (params == NULL) goto err; @@ -245,6 +260,7 @@ static int ml_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb, err: OSSL_PARAM_BLD_free(tmpl); + OPENSSL_secure_clear_free(seedenc, seedlen); OPENSSL_secure_clear_free(prvenc, prvlen); OPENSSL_free(pubenc); return ret; @@ -253,8 +269,10 @@ err: static const OSSL_PARAM *ml_kem_imexport_types(int selection) { static const OSSL_PARAM key_types[] = { - OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, NULL), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0), OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), OSSL_PARAM_END }; @@ -268,9 +286,8 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key, int include_private) { const OSSL_PARAM *p = NULL; - const uint8_t *d = NULL, *z = NULL; - const void *pubenc = NULL, *prvenc = NULL; - size_t publen = 0, prvlen = 0; + const void *pubenc = NULL, *prvenc = NULL, *seedenc = NULL; + size_t publen = 0, prvlen = 0, seedlen = 0; const ML_KEM_VINFO *v; /* Invalid attempt to mutate a key, what is the right error to report? */ @@ -278,59 +295,61 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key, return 0; v = ossl_ml_kem_key_vinfo(key); - /* What does the caller want to set? */ - p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY); - if (p != NULL && - OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen) != 1) + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED); + if (p != NULL + && !(OSSL_PARAM_get_int(p, &key->retain_seed))) return 0; /* - * Accept private keys in either expanded or seed form, distinguished by - * length alone. Accept either the "raw" or "encoded" parameters as the - * input source, preferring the raw, which is expected to be the seed if - * the caller supports seeds as a key format. + * When a seed is provided, the private and public keys will be ignored, + * after validating just their lengths. Comparing encodings or hashes + * when applicable is possible, but not currently implemented. + */ + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_SEED); + if (p != NULL + && OSSL_PARAM_get_octet_string_ptr(p, &seedenc, &seedlen) != 1) + return 0; + if (seedlen == ML_KEM_SEED_BYTES) { + ossl_ml_kem_set_seed((uint8_t *)seedenc, seedlen, key); + } else if (seedlen != 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH); + return 0; + } + + /* + * When a private key is provided, without a seed, any public key also + * provided will be ignored (apart from length), just as with the seed. */ if (include_private) { p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY); - if (p == NULL) - p = OSSL_PARAM_locate_const(params, - OSSL_PKEY_PARAM_ENCODED_PRIVATE_KEY); if (p != NULL && OSSL_PARAM_get_octet_string_ptr(p, &prvenc, &prvlen) != 1) return 0; - if (prvlen == ML_KEM_SEED_BYTES) { - d = (uint8_t *)prvenc; - z = d + ML_KEM_RANDOM_BYTES; - } else if (prvlen != 0 && prvlen != v->prvkey_bytes) { + if (prvlen != 0 && prvlen != v->prvkey_bytes) { ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); return 0; } } - /* The caller MUST specify at least one of the public or private keys. */ - if (publen == 0 && prvlen == 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + /* Used only when no seed or private key is provided. */ + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY); + if (p != NULL + && OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen) != 1) return 0; - } - - /* - * When a pubkey is provided, its length MUST be correct, if a private key - * is also provided, the public key will be otherwise ignored. We could - * look for a matching encoded block, but unclear this is useful. - */ if (publen != 0 && publen != v->pubkey_bytes) { ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); return 0; } - /* - * If the private key is given, we'll ignore the public key data, taking - * the embedded public key as authoritative. For import, the private key - * is in either (d, z) seed format or the FIPS 203 expanded format. - */ - if (d != NULL) - return ossl_ml_kem_genkey(d, z, NULL, 0, key); - if (prvlen != 0) + /* The caller MUST specify at least one of seed, private or public keys. */ + if (seedlen == 0 && publen == 0 && prvlen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + if (seedlen != 0) + return ossl_ml_kem_genkey(NULL, 0, key); + else if (prvlen != 0) return ossl_ml_kem_parse_private_key(prvenc, prvlen, key); return ossl_ml_kem_parse_public_key(pubenc, publen, key); } @@ -364,8 +383,14 @@ static const OSSL_PARAM *ml_kem_gettable_params(void *provctx) OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + /* Exported for import */ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0), + /* Exported to EVP_PKEY_get_raw_private_key() */ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + /* Exported to EVP_PKEY_get_raw_public_key() */ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + /* Needed by EVP_PKEY_get1_encoded_public_key() */ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), - OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PRIVATE_KEY, NULL, 0), OSSL_PARAM_END }; @@ -382,6 +407,15 @@ void *ml_kem_load(const void *reference, size_t reference_sz) key = *(ML_KEM_KEY **)reference; /* We grabbed, so we detach it */ *(ML_KEM_KEY **)reference = NULL; + if (ossl_ml_kem_have_pubkey(key)) + return key; + /* Generate the key now, if it holds only a stashed seed. */ + if (ossl_ml_kem_have_seed(key)) { + if (!ossl_ml_kem_genkey(NULL, 0, key)) { + ossl_ml_kem_key_free(key); + return NULL; + } + } return key; } return NULL; @@ -396,6 +430,11 @@ static int ml_kem_get_params(void *vkey, OSSL_PARAM params[]) ML_KEM_KEY *key = vkey; const ML_KEM_VINFO *v = ossl_ml_kem_key_vinfo(key); OSSL_PARAM *p; + const char *pubparams[] = { + OSSL_PKEY_PARAM_PUB_KEY, + OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY + }; + int i; p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS); if (p != NULL) @@ -412,20 +451,31 @@ static int ml_kem_get_params(void *vkey, OSSL_PARAM params[]) if (!OSSL_PARAM_set_int(p, v->ctext_bytes)) return 0; - p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); - if (p != NULL && ossl_ml_kem_have_pubkey(key)) { - if (p->data_type != OSSL_PARAM_OCTET_STRING) - return 0; - p->return_size = v->pubkey_bytes; - if (p->data != NULL) { + if (ossl_ml_kem_have_pubkey(key)) { + uint8_t *pubenc = NULL; + + for (i = 0; i < 2; ++i) { + p = OSSL_PARAM_locate(params, pubparams[i]); + if (p == NULL) + continue; + if (p->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + p->return_size = v->pubkey_bytes; + if (p->data == NULL) + continue; if (p->data_size < p->return_size) return 0; + if (pubenc != NULL) { + memcpy(p->data, pubenc, p->return_size); + continue; + } if (!ossl_ml_kem_encode_public_key(p->data, p->return_size, key)) return 0; + pubenc = p->data; } } - p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PRIVATE_KEY); + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY); if (p != NULL && ossl_ml_kem_have_prvkey(key)) { if (p->data_type != OSSL_PARAM_OCTET_STRING) return 0; @@ -438,14 +488,27 @@ static int ml_kem_get_params(void *vkey, OSSL_PARAM params[]) } } + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ML_KEM_SEED); + if (p != NULL && ossl_ml_kem_have_seed(key)) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + p->return_size = ML_KEM_SEED_BYTES; + if (p->data != NULL) { + if (p->data_size < p->return_size) + return 0; + if (!ossl_ml_kem_encode_seed(p->data, p->return_size, key)) + return 0; + } + } + return 1; } static const OSSL_PARAM *ml_kem_settable_params(void *provctx) { static const OSSL_PARAM arr[] = { + /* Used in TLS via EVP_PKEY_set1_encoded_public_key(). */ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), - OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PRIVATE_KEY, NULL, 0), OSSL_PARAM_END }; @@ -456,31 +519,21 @@ static int ml_kem_set_params(void *vkey, const OSSL_PARAM params[]) { ML_KEM_KEY *key = vkey; const OSSL_PARAM *p; - const void *pubenc = NULL, *prvenc = NULL; - size_t publen = 0, prvlen = 0; + const void *pubenc = NULL; + size_t publen = 0; if (ossl_param_is_empty(params)) return 1; - p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PRIVATE_KEY); + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); if (p != NULL - && (OSSL_PARAM_get_octet_string_ptr(p, &prvenc, &prvlen) != 1 - || prvlen != key->vinfo->prvkey_bytes)) { + && (OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen) != 1 + || publen != key->vinfo->pubkey_bytes)) { ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); return 0; } - if (prvlen == 0) { - p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); - if (p != NULL - && (OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen) != 1 - || publen != key->vinfo->pubkey_bytes)) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); - return 0; - } - } - - if (publen == 0 && prvlen == 0) + if (publen == 0) return 1; /* Key mutation is reportedly generally not allowed */ @@ -491,10 +544,7 @@ static int ml_kem_set_params(void *vkey, const OSSL_PARAM params[]) return 0; } - if (prvlen) - return ossl_ml_kem_parse_private_key(prvenc, prvlen, key); - else - return ossl_ml_kem_parse_public_key(pubenc, publen, key); + return ossl_ml_kem_parse_public_key(pubenc, publen, key); } static int ml_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[]) @@ -530,6 +580,12 @@ static int ml_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[]) gctx->seed = NULL; return 0; } + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED); + if (p != NULL + && !(OSSL_PARAM_get_int(p, &gctx->retain_seed))) + return 0; + return 1; } @@ -549,6 +605,8 @@ static void *ml_kem_gen_init(void *provctx, int selection, gctx->selection = selection; gctx->evp_type = evp_type; + gctx->retain_seed = ossl_prov_ctx_get_bool_param( + provctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1); if (provctx != NULL) gctx->libctx = PROV_LIBCTX_OF(provctx); if (ml_kem_gen_set_params(gctx, params)) @@ -574,20 +632,23 @@ static void *ml_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg) ML_KEM_KEY *key; uint8_t *nopub = NULL; uint8_t *seed = gctx->seed; - uint8_t *d = seed != NULL ? seed : NULL; - uint8_t *z = seed != NULL ? seed + ML_KEM_RANDOM_BYTES : NULL; int genok = 0; if (gctx == NULL || (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == - OSSL_KEYMGMT_SELECT_PUBLIC_KEY - || (key = ml_kem_new(gctx->libctx, gctx->propq, gctx->evp_type)) == NULL) + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + return NULL; + key = ossl_ml_kem_key_new(gctx->libctx, gctx->propq, + gctx->retain_seed, gctx->evp_type); + if (key == NULL) return NULL; if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) return key; - genok = ossl_ml_kem_genkey(d, z, nopub, 0, key); + if (seed != NULL && !ossl_ml_kem_set_seed(seed, ML_KEM_SEED_BYTES, key)) + return NULL; + genok = ossl_ml_kem_genkey(nopub, 0, key); /* Erase the single-use seed */ if (seed != NULL) @@ -639,8 +700,7 @@ static void *ml_kem_dup(const void *vkey, int selection) #define DECLARE_VARIANT(bits) \ static void *ml_kem_##bits##_new(void *provctx) \ { \ - return ml_kem_new(provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx), \ - NULL, EVP_PKEY_ML_KEM_##bits); \ + return ml_kem_new(provctx, EVP_PKEY_ML_KEM_##bits); \ } \ static void *ml_kem_##bits##_gen_init(void *provctx, int selection, \ const OSSL_PARAM params[]) \ diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 20db61826fe..5fc9accea80 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -2575,7 +2575,7 @@ done: #endif #ifndef OPENSSL_NO_ML_KEM -static const uint8_t ml_kem_prvkey[] = { +static const uint8_t ml_kem_seed[] = { 0x7c, 0x99, 0x35, 0xa0, 0xb0, 0x76, 0x94, 0xaa, 0x0c, 0x6d, 0x10, 0xe4, 0xdb, 0x6b, 0x1a, 0xdd, 0x2f, 0xd8, 0x1a, 0x25, 0xcc, 0xb1, 0x48, 0x03, 0x2d, 0xcd, 0x73, 0x99, 0x36, 0x73, 0x7f, 0x2d, 0x86, 0x26, 0xed, 0x79, @@ -2946,18 +2946,59 @@ static struct keys_st { #endif #ifndef OPENSSL_NO_ML_KEM { - NID_undef, ml_kem_prvkey, ml_kem_512_pubkey, + NID_undef, ml_kem_seed, ml_kem_512_pubkey, "ML-KEM-512", ML_KEM_SEED_BYTES, sizeof(ml_kem_512_pubkey) }, { - NID_undef, ml_kem_prvkey, ml_kem_768_pubkey, + NID_undef, ml_kem_seed, ml_kem_768_pubkey, "ML-KEM-768", ML_KEM_SEED_BYTES, sizeof(ml_kem_768_pubkey) }, { - NID_undef, ml_kem_prvkey, ml_kem_1024_pubkey, + NID_undef, ml_kem_seed, ml_kem_1024_pubkey, "ML-KEM-1024", ML_KEM_SEED_BYTES, sizeof(ml_kem_1024_pubkey) }, #endif }; +#ifndef OPENSSL_NO_ML_KEM +static int +ml_kem_seed_to_priv(const char *alg, const unsigned char *seed, int seedlen, + unsigned char **ret, size_t *retlen) +{ + OSSL_PARAM parr[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + const OSSL_PARAM *p; + OSSL_PARAM *params = NULL; + int selection = OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + int ok = 0; + + /* Import the seed to generate a key */ + ctx = EVP_PKEY_CTX_new_from_name(testctx, alg, NULL); + if (!TEST_ptr(ctx) + || !TEST_int_gt(EVP_PKEY_fromdata_init(ctx), 0)) + goto done; + parr[0] = OSSL_PARAM_construct_octet_string( + OSSL_PKEY_PARAM_ML_KEM_SEED, (unsigned char *)seed, seedlen); + if (!TEST_int_gt(EVP_PKEY_fromdata(ctx, &pkey, selection, parr), 0)) + goto done; + + /* Export the key to get the encoded form */ + if (!TEST_true(EVP_PKEY_todata(pkey, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, ¶ms))) + goto done; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY); + if (!TEST_ptr(p) + || !TEST_true(OSSL_PARAM_get_octet_string(p, (void **)ret, 0, retlen))) + goto done; + ok = 1; + + done: + EVP_PKEY_free(pkey); + OSSL_PARAM_free(params); + EVP_PKEY_CTX_free(ctx); + return ok; +} +#endif + static int test_set_get_raw_keys_int(int tst, int pub, int uselibctx) { int ret = 0; @@ -2965,7 +3006,8 @@ static int test_set_get_raw_keys_int(int tst, int pub, int uselibctx) const uint8_t *in; uint8_t shortbuf[1]; size_t inlen, len = 0, shortlen = sizeof(shortbuf); - EVP_PKEY *pkey; + EVP_PKEY *pkey = NULL; + unsigned char *privalloc = NULL; const char *name; /* Check if this algorithm supports public keys */ @@ -2997,6 +3039,14 @@ static int test_set_get_raw_keys_int(int tst, int pub, int uselibctx) } else { inlen = keys[tst].privlen; in = keys[tst].priv; +#ifndef OPENSSL_NO_ML_KEM + if (in == ml_kem_seed) { + if (!TEST_true(ml_kem_seed_to_priv(name, in, inlen, + &privalloc, &inlen))) + goto done; + in = privalloc; + } +#endif if (uselibctx || keys[tst].name != NULL) { pkey = EVP_PKEY_new_raw_private_key_ex( testctx, @@ -3038,6 +3088,7 @@ static int test_set_get_raw_keys_int(int tst, int pub, int uselibctx) ret = 1; done: + OPENSSL_free(privalloc); OPENSSL_free(buf); EVP_PKEY_free(pkey); return ret; diff --git a/test/evp_test.c b/test/evp_test.c index 05040884066..c0badd101b3 100644 --- a/test/evp_test.c +++ b/test/evp_test.c @@ -4379,7 +4379,7 @@ static int keygen_test_run(EVP_TEST *t) private_keys = key; rv = 1; } else if (keygen->seed != NULL) { - const char *prvparam = OSSL_PKEY_PARAM_ENCODED_PRIVATE_KEY; + const char *prvparam = OSSL_PKEY_PARAM_PRIV_KEY; rv = 0; if (!TEST_int_eq(EVP_PKEY_get_octet_string_param(pkey, prvparam, NULL, 0, &priv_len), 1) diff --git a/test/ml_kem_internal_test.c b/test/ml_kem_internal_test.c index 78b2168f81a..e811237207c 100644 --- a/test/ml_kem_internal_test.c +++ b/test/ml_kem_internal_test.c @@ -137,8 +137,8 @@ static int sanity_test(void) if (!TEST_true(EVP_RAND_CTX_set_params(privctx, params))) return 0; - public_key = ossl_ml_kem_key_new(NULL, NULL, alg[i]); - private_key = ossl_ml_kem_key_new(NULL, NULL, alg[i]); + public_key = ossl_ml_kem_key_new(NULL, NULL, 0, alg[i]); + private_key = ossl_ml_kem_key_new(NULL, NULL, 0, alg[i]); if (private_key == NULL || public_key == NULL || (v = ossl_ml_kem_key_vinfo(public_key)) == NULL) goto done; @@ -150,8 +150,8 @@ static int sanity_test(void) ret2 = -2; /* Generate a private key */ - if (!ossl_ml_kem_genkey(NULL, NULL, encoded_public_key, - v->pubkey_bytes, private_key)) + if (!ossl_ml_kem_genkey(encoded_public_key, v->pubkey_bytes, + private_key)) goto done; /* Check that no more entropy is available! */ diff --git a/util/perl/OpenSSL/paramnames.pm b/util/perl/OpenSSL/paramnames.pm index 3918d80d770..6494cbe169d 100644 --- a/util/perl/OpenSSL/paramnames.pm +++ b/util/perl/OpenSSL/paramnames.pm @@ -299,7 +299,6 @@ my %params = ( 'PKEY_PARAM_MGF1_DIGEST' => "mgf1-digest", 'PKEY_PARAM_MGF1_PROPERTIES' => "mgf1-properties", 'PKEY_PARAM_ENCODED_PUBLIC_KEY' => "encoded-pub-key", - 'PKEY_PARAM_ENCODED_PRIVATE_KEY' => "encoded-priv-key", 'PKEY_PARAM_GROUP_NAME' => "group", 'PKEY_PARAM_DIST_ID' => "distid", 'PKEY_PARAM_PUB_KEY' => "pub", @@ -420,6 +419,7 @@ my %params = ( # ML-KEM parameters 'PKEY_PARAM_ML_KEM_SEED' => "seed", + 'PKEY_PARAM_ML_KEM_RETAIN_SEED' => "ml-kem.retain_seed", # Key generation parameters 'PKEY_PARAM_FFC_TYPE' => "type",