- 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 <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26512)
* 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];
* 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;
}
/* 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:
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|
* 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);
}
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;
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
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;
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);
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)
{
}
/*
- * 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;
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) {
The B<ML-KEM-512>, B<ML-KEM-768>, and B<ML-KEM-1024> 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
Internally, ML-KEM generates keys using a 64-byte random value (seed), which is
the concatenation of the 32-byte I<d> and I<z> 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<PKCS#8> 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<gettable> parameter,
+and private key output to B<PKCS#8> files will be in seed format.
+When the seed is not available, because not included in the B<PKCS#8> file, not
+available on import, or not retained, B<PKCS#8> private key files will have the
+private key in FIPS 203 C<dk> format, without DER octet-string wrapping.
+
+Retention of the seed can be disabled by setting the C<pkey_seed_retain>
+parameter of the C<default> provider to C<no>.
+See the description of the B<-provparam> option in L<openssl(1)> to learn
+how to set provider options in the command line tools.
+See L<OSSL_PROVIDER_add_conf_parameter(3)> to learn how to set provider
+configuration options programmatically.
+When using the FIPS provider, the parameter may need to be set in the C<base>
+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<provider-keymgmt(7)/Common Information Parameters> for further
information.
+=item C<ml-kem.retain_seed> (B<OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED>) <int>
+
+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<ml-kem.retain_seed> provider
+configuration parameter is set to a false boolean value, the seed is not
+retained, otherwise it is retained (see also L<OSSL_PROVIDER_conf_get_bool(3)>).
+
=back
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<provider-keymgmt(7)/Common parameters>), the implementation of these keytypes
-support the following.
+L<provider-keymgmt(7)/Common Information Parameters>), B<ML-KEM> keys
+keys support the following.
=over 4
the EVP_PKEY_fromdata() and EVP_PKEY_todata() functions. The same underlying
FIPS 203 (Algorithm 16: B<ML-KEM.KeyGen_internal>) B<ek> public key format is
used for import, export, get and set operations.
+It is otherwise only gettable.
=item "priv" (B<OSSL_PKEY_PARAM_PRIV_KEY>) <octet string>
-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<ML-KEM.KeyGen_internal>, with the B<d>
-and B<z> values concatenated in that order.
-If, on the other hand, the key length is that of the FIPS 203 (Algorithm 16:
-B<ML-KEM.KeyGen_internal>) B<dk> private key for the given ML-KEM variant, then
-the key is assumed to be in that format.
-
-Keys originally imported in the B<dk> FIPS 203 form cannot be exported in seed
-form as the B<d> seed is not available in that case, and the exported key will
-then be the FIPS 203 B<dk> 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<ML-KEM.KeyGen_internal>) B<dk> private key for the given ML-KEM variant.
+It is otherwise only gettable.
=item "encoded-pub-key" (B<OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY>) <octet string>
This parameter is gettable and settable.
-=item "encoded-priv-key" (B<PKEY_PARAM_ENCODED_PRIVATE_KEY>) <octet string>
-
-Used for getting the I<expanded> encoding of a private key.
-The key format is that of B<dk> in FIPS 203, Algorithm 16: B<ML-KEM.KeyGen_internal>.
-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
Equivalent calls are available for B<ML-KEM-512> and B<ML-KEM-1024>.
+An B<ML-KEM> private key in seed format can be converted to a key in expanded
+FIPS 203 B<dk> format by running:
+
+ $ openssl pkey -provparam ml-kem.retain_seed=no -in seed.pem -out long.pem
+
+To generate an, e.g., B<ML-KEM-768> 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<openssl.cnf> 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<openssl(1)>,
+L<openssl-pkey(1)>,
+L<openssl-genpkey(1)>,
L<EVP_KEYMGMT(3)>,
L<EVP_PKEY(3)>,
+L<EVP_PKEY_get_raw_private_key(3)>,
+L<EVP_PKEY_get_raw_public_key(3)>,
+L<EVP_PKEY_get1_encoded_public_key(3)>,
L<provider-keymgmt(7)>,
-L<EVP_KEM-ML-KEM(7)>
+L<EVP_KEM-ML-KEM(7)>,
+LOSSL_PROVIDER_add_conf_parameter(3)>
=head1 HISTORY
# 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
* 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 */
*/
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);
/*
__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
__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,
/* 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);
#include <openssl/params.h>
#include <openssl/pem.h> /* PEM_BUFSIZE and public PEM functions */
#include <openssl/pkcs12.h>
+#include <openssl/provider.h>
#include <openssl/x509.h>
#include <openssl/proverr.h>
#include "internal/cryptlib.h" /* ossl_assert() */
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)
"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:
/* 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;
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)) {
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))
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;
#include <openssl/params.h>
#include <openssl/err.h>
#include <openssl/proverr.h>
+#include <openssl/provider.h>
#include <openssl/rand.h>
#include <openssl/self_test.h>
-#include "crypto/ml_kem.h"
-#include "internal/param_build_set.h"
#include <openssl/param_build.h>
+#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;
OSSL_LIB_CTX *libctx;
char *propq;
int selection;
+ int retain_seed;
int evp_type;
uint8_t seedbuf[ML_KEM_SEED_BYTES];
uint8_t *seed;
}
#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)
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)
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))
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;
err:
OSSL_PARAM_BLD_free(tmpl);
+ OPENSSL_secure_clear_free(seedenc, seedlen);
OPENSSL_secure_clear_free(prvenc, prvlen);
OPENSSL_free(pubenc);
return ret;
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
};
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? */
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);
}
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
};
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;
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)
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;
}
}
+ 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
};
{
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 */
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[])
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;
}
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))
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)
#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[]) \
#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,
#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;
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 */
} 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,
ret = 1;
done:
+ OPENSSL_free(privalloc);
OPENSSL_free(buf);
EVP_PKEY_free(pkey);
return ret;
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)
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;
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! */
'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",
# 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",