]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Improved import and export
authorViktor Dukhovni <openssl-users@dukhovni.org>
Fri, 17 Jan 2025 16:28:51 +0000 (03:28 +1100)
committerTomas Mraz <tomas@openssl.org>
Fri, 14 Feb 2025 09:50:58 +0000 (10:50 +0100)
- 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)

12 files changed:
crypto/ml_kem/ml_kem.c
doc/man7/EVP_PKEY-ML-KEM.pod
include/crypto/ml_kem.h
providers/fips/self_test_kats.c
providers/implementations/encode_decode/decode_der2key.c
providers/implementations/encode_decode/encode_key2any.c
providers/implementations/encode_decode/encode_key2text.c
providers/implementations/keymgmt/ml_kem_kmgmt.c
test/evp_extra_test.c
test/evp_test.c
test/ml_kem_internal_test.c
util/perl/OpenSSL/paramnames.pm

index ed21ecdafef119712c171964c5dc944709dab343..1379d849a2be57f1cab376734c7495a7e327b24a 100644 (file)
@@ -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) {
index 7a4cb8f11d632d77be81d5f2596a1e00922b98c4..12b687902ec1a4a4e858a27f43ea01bc5fbfb2b4 100644 (file)
@@ -16,11 +16,9 @@ EVP_PKEY-ML-KEM
 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
 
@@ -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<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().
@@ -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<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
 
@@ -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<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>
 
@@ -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<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
@@ -121,12 +134,50 @@ An B<ML-KEM-768> key can be generated like this:
 
 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
 
index d3ea0aba033e8172b364ded6a903865a9f934af9..c47ff12779ea1094b52218e3d56af3e95269491b 100644 (file)
 # 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,
index 10ed646d0401218ec5d2d15ea20efe1a271cb69a..5ba0d9348bfcbc82318b5c5bd518edfdc6e3af52 100644 (file)
@@ -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);
index 4ae31ad18dd277a99b4b9259cfe3b664a0d3832a..0d30c3f08d83183a58d8b7525607c2de0ce2a997 100644 (file)
@@ -23,6 +23,7 @@
 #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() */
@@ -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)) {
index 8f5b2fa86b98708d61751fd6f423fb09092b9da5..d2370c990165368f7fbac5982ec5acc5ee7f98ed 100644 (file)
@@ -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))
index 9c7a59f45f3e1c8c311df80c44ae28f84fd07d71..3a335eb4bc8831d4953aef075118c4f0c8216e08 100644 (file)
@@ -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;
index dfb39e3813acdda4be374a7cc194e7738a28dd40..9aa0289211ada95bae7bbac2bd93494b2d7fbb96 100644 (file)
 #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;
@@ -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[]) \
index 20db61826fe46112744bf93cb611108a52dfb931..5fc9accea8068458307de9f96f0247b921d70ca7 100644 (file)
@@ -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, &params)))
+        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;
index 05040884066be851169116fbd0e328792f75309b..c0badd101b33d6083c62688fd0d2b4d3f7bf8195 100644 (file)
@@ -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)
index 78b2168f81a7026b812be1e981f4155952d2ff8c..e811237207cdcc049ee3f03df291b4c9627fd336 100644 (file)
@@ -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! */
index 3918d80d770cd9bcb85c343a8baee853b69e8248..6494cbe169df2956f9adcd1fb54a93af39e93ea1 100644 (file)
@@ -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",