]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Configurable import-time PCT for ML-KEM
authorViktor Dukhovni <openssl-users@dukhovni.org>
Fri, 14 Feb 2025 17:36:25 +0000 (04:36 +1100)
committerViktor Dukhovni <openssl-users@dukhovni.org>
Thu, 20 Feb 2025 07:21:40 +0000 (18:21 +1100)
And related cleanup.

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tim Hudson <tjh@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26789)

14 files changed:
crypto/ml_dsa/ml_dsa_key.c
crypto/ml_dsa/ml_dsa_key.h
crypto/ml_kem/ml_kem.c
doc/man7/EVP_PKEY-ML-KEM.pod
include/crypto/ml_dsa.h
include/crypto/ml_kem.h
providers/implementations/encode_decode/ml_dsa_codecs.c
providers/implementations/encode_decode/ml_kem_codecs.c
providers/implementations/include/prov/ml_dsa.h [new file with mode: 0644]
providers/implementations/include/prov/ml_kem.h [new file with mode: 0644]
providers/implementations/keymgmt/ml_dsa_kmgmt.c
providers/implementations/keymgmt/ml_kem_kmgmt.c
test/recipes/15-test_ml_kem_codecs.t
util/perl/OpenSSL/paramnames.pm

index 05bf142d97901ad8df76958e02b9c2211e01fabf..be041fc4e300ea43d9d41eb4b516405c1b998660 100644 (file)
@@ -28,17 +28,12 @@ const uint8_t *ossl_ml_dsa_key_get_seed(const ML_DSA_KEY *key)
     return key->seed;
 }
 
-int ossl_ml_dsa_key_prefer_seed(const ML_DSA_KEY *key)
+int ossl_ml_dsa_key_get_prov_flags(const ML_DSA_KEY *key)
 {
-    return key->prefer_seed;
+    return key->prov_flags;
 }
 
-int ossl_ml_dsa_key_retain_seed(const ML_DSA_KEY *key)
-{
-    return key->retain_seed;
-}
-
-int ossl_ml_dsa_set_prekey(ML_DSA_KEY *key, int prefer_seed, int retain_seed,
+int ossl_ml_dsa_set_prekey(ML_DSA_KEY *key, int flags_set, int flags_clr,
                            const uint8_t *seed, size_t seed_len,
                            const uint8_t *sk, size_t sk_len)
 {
@@ -58,10 +53,8 @@ int ossl_ml_dsa_set_prekey(ML_DSA_KEY *key, int prefer_seed, int retain_seed,
     if (seed != NULL
         && (key->seed = OPENSSL_memdup(seed, seed_len)) == NULL)
         goto end;
-    if (prefer_seed >= 0)
-        key->prefer_seed = prefer_seed;
-    if (retain_seed >= 0)
-        key->retain_seed = retain_seed;
+    key->prov_flags |= flags_set;
+    key->prov_flags &= ~flags_clr;
     ret = 1;
 
   end:
@@ -94,8 +87,7 @@ ML_DSA_KEY *ossl_ml_dsa_key_new(OSSL_LIB_CTX *libctx, const char *propq,
     if (ret != NULL) {
         ret->libctx = libctx;
         ret->params = params;
-        ret->prefer_seed = 1;
-        ret->retain_seed = 1;
+        ret->prov_flags = ML_DSA_KEY_PROV_FLAGS_DEFAULT;
         ret->shake128_md = EVP_MD_fetch(libctx, "SHAKE-128", propq);
         ret->shake256_md = EVP_MD_fetch(libctx, "SHAKE-256", propq);
         if (ret->shake128_md == NULL || ret->shake256_md == NULL)
@@ -190,8 +182,7 @@ ML_DSA_KEY *ossl_ml_dsa_key_dup(const ML_DSA_KEY *src, int selection)
     if (ret != NULL) {
         ret->libctx = src->libctx;
         ret->params = src->params;
-        ret->retain_seed = src->retain_seed;
-        ret->prefer_seed = src->prefer_seed;
+        ret->prov_flags = src->prov_flags;
         if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
             if (src->pub_encoding != NULL) {
                 /* The public components are present if the private key is present */
@@ -443,7 +434,7 @@ static int keygen_internal(ML_DSA_KEY *out)
         && ossl_ml_dsa_sk_encode(out);
 
 err:
-    if (out->seed != NULL && !out->retain_seed) {
+    if (out->seed != NULL && (out->prov_flags & ML_DSA_KEY_RETAIN_SEED) == 0) {
         OPENSSL_clear_free(out->seed, ML_DSA_SEED_BYTES);
         out->seed = NULL;
     }
index 10d61cb54518ffd2ed9a0d060751fd5765c998d5..5a495ce3c44d32c069ad7c0677f554d92205f630 100644 (file)
@@ -34,8 +34,7 @@ struct ml_dsa_key_st {
     uint8_t *pub_encoding;
     uint8_t *priv_encoding;
     uint8_t *seed;
-    int retain_seed;
-    int prefer_seed;
+    int prov_flags;
 
     /*
      * t1 is the Polynomial encoding of the 10 MSB of each coefficient of the
index a37951f4282cbb54382b0b07bae4df2294a181a1..bace597c80ed73ae9bfdb7053a107b635ca98cb7 100644 (file)
@@ -1394,7 +1394,7 @@ int genkey(const uint8_t seed[ML_KEM_SEED_BYTES],
 
     /* Optionally save the |d| portion of the seed */
     key->d = key->z + ML_KEM_RANDOM_BYTES;
-    if (key->retain_seed) {
+    if (key->prov_flags & ML_KEM_KEY_RETAIN_SEED) {
         memcpy(key->d, seed, ML_KEM_RANDOM_BYTES);
     } else {
         OPENSSL_cleanse(key->d, ML_KEM_RANDOM_BYTES);
@@ -1576,10 +1576,6 @@ 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)
 {
@@ -1594,8 +1590,7 @@ ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties,
 
     key->vinfo = vinfo;
     key->libctx = libctx;
-    key->prefer_seed = 1;
-    key->retain_seed = 1;
+    key->prov_flags = ML_KEM_KEY_PROV_FLAGS_DEFAULT;
     key->shake128_md = EVP_MD_fetch(libctx, "SHAKE128", properties);
     key->shake256_md = EVP_MD_fetch(libctx, "SHAKE256", properties);
     key->sha3_256_md = EVP_MD_fetch(libctx, "SHA3-256", properties);
index f1cbf9104d4fa450eb265c9752cbb0061adf0501..b83f5fb90525935ab921525b9412c12db478b854 100644 (file)
@@ -108,6 +108,17 @@ configuration options programmatically.
 
 =over 4
 
+=item C<ml-kem.import_pct_type> (B<OSSL_PKEY_PARAM_ML_KEM_IMPORT_PCT_TYPE>) <UTF8 string>
+
+When an B<ML-KEM> key is imported as an explict FIPS 203 B<dk> decapsulation
+key, rather than a seed, a pairwise consistency test (PCT) is optionally
+performed.
+By default, or when this parameter is set explicitly to C<random>, the PCT
+is performed with a random entropy value for the encapsulation step.
+Setting the parameter to C<fixed>, still runs the test, but the encapsulation
+entropy is a fixed 32 byte value.
+Specifying any other value of the parameter, e.g. C<none>, skips the test.
+
 =item C<ml-kem.retain_seed> (B<OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED>) <UTF8 string>
 
 When set to a string representing a false boolean value (see
index f66d93d06d35b663a9b091e102fd8510b41c5ee1..fd21a40889339a039ee59e23a7c25ed145dd7a40 100644 (file)
 # define MAX_ML_DSA_PUB_LEN ML_DSA_87_PUB_LEN
 # define MAX_ML_DSA_SIG_LEN ML_DSA_87_SIG_LEN
 
+# define ML_DSA_KEY_PREFER_SEED (1 << 0)
+# define ML_DSA_KEY_RETAIN_SEED (1 << 1)
+/* Default provider flags */
+# define ML_DSA_KEY_PROV_FLAGS_DEFAULT \
+    (ML_DSA_KEY_PREFER_SEED | ML_DSA_KEY_RETAIN_SEED)
+
 /*
  * Refer to FIPS 204 Section 4 Parameter sets.
  * Fields that are shared between all algorithms (such as q & d) have been omitted.
@@ -86,9 +92,8 @@ __owur size_t ossl_ml_dsa_key_get_pub_len(const ML_DSA_KEY *key);
 __owur const uint8_t *ossl_ml_dsa_key_get_priv(const ML_DSA_KEY *key);
 __owur size_t ossl_ml_dsa_key_get_priv_len(const ML_DSA_KEY *key);
 __owur const uint8_t *ossl_ml_dsa_key_get_seed(const ML_DSA_KEY *key);
-__owur int ossl_ml_dsa_key_prefer_seed(const ML_DSA_KEY *key);
-__owur int ossl_ml_dsa_key_retain_seed(const ML_DSA_KEY *key);
-int ossl_ml_dsa_set_prekey(ML_DSA_KEY *key, int prefer_seed, int retain_seed,
+__owur int ossl_ml_dsa_key_get_prov_flags(const ML_DSA_KEY *key);
+int ossl_ml_dsa_set_prekey(ML_DSA_KEY *key, int flags_set, int flags_clr,
                            const uint8_t *seed, size_t seed_len,
                            const uint8_t *sk, size_t sk_len);
 __owur size_t ossl_ml_dsa_key_get_collision_strength_bits(const ML_DSA_KEY *key);
index d2df667f51c69a378c7d6622e1a536d3d41744b0..e9915c102131a7bbddbdf5c13b38a75a70554735 100644 (file)
 # define ML_KEM_1024_DV         5
 # define ML_KEM_1024_SECBITS    256
 
+# define ML_KEM_KEY_RANDOM_PCT  (1 << 0)
+# define ML_KEM_KEY_FIXED_PCT   (1 << 1)
+# define ML_KEM_KEY_PREFER_SEED (1 << 2)
+# define ML_KEM_KEY_RETAIN_SEED (1 << 3)
+/* Mask to check whether PCT on import is enabled */
+# define ML_KEM_KEY_PCT_TYPE \
+    (ML_KEM_KEY_RANDOM_PCT | ML_KEM_KEY_FIXED_PCT)
+/* Default provider flags */
+# define ML_KEM_KEY_PROV_FLAGS_DEFAULT \
+    (ML_KEM_KEY_RANDOM_PCT | ML_KEM_KEY_PREFER_SEED | ML_KEM_KEY_RETAIN_SEED)
+
 /*
  * External variant-specific API
  * -----------------------------
@@ -171,8 +182,7 @@ typedef struct ossl_ml_kem_key_st {
     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 prefer_seed;                        /* Given seed and key use seed? */
-    int retain_seed;                        /* Retain the seed after keygen? */
+    int prov_flags;                         /* prefer/retain seed and PCT flags */
 
     /*
      * Fixed-size built-in buffer, which holds the |rho| and the public key
index a0944d8a11c49f081ec76d6c0615d8f209a6daba..baa5a656fc2a64f47a1d6811a29626c71f83f4b2 100644 (file)
@@ -14,6 +14,7 @@
 #include <openssl/x509.h>
 #include <openssl/core_names.h>
 #include "internal/encoder.h"
+#include "prov/ml_dsa.h"
 #include "ml_dsa_codecs.h"
 
 /*-
@@ -139,7 +140,6 @@ ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
                       int evp_type, PROV_CTX *provctx,
                       const char *propq)
 {
-    OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
     const ML_DSA_PARAMS *v;
     const ML_COMMON_CODEC *codec;
     ML_COMMON_PKCS8_FMT_PREF *fmt_slots = NULL, *slot;
@@ -149,7 +149,7 @@ ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
     const uint8_t *buf, *pos;
     const X509_ALGOR *alg = NULL;
     const char *formats;
-    int len, ptype, retain, prefer;
+    int len, ptype;
     uint32_t magic;
     uint16_t seed_magic;
     const uint8_t *seed = NULL;
@@ -244,7 +244,7 @@ ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
      * Collect the seed and/or key into a "decoded" private key object,
      * to be turned into a real key on provider "load" or "import".
      */
-    if ((key = ossl_ml_dsa_key_new(libctx, propq, evp_type)) == NULL)
+    if ((key = ossl_prov_ml_dsa_new(provctx, propq, evp_type)) == NULL)
         goto end;
     if (p8fmt->seed_length > 0)
         seed = buf + p8fmt->seed_offset;
@@ -252,19 +252,7 @@ ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
         priv = buf + p8fmt->priv_offset;
     /* Any OQS public key content is ignored */
 
-    /*
-     * If the key ends up "loaded" into the same provider, these are the
-     * correct config settings, otherwise, new values will be assigned on
-     * import into a different provider.  The "load" API does not pass along
-     * the provider context.
-     */
-    retain =
-        ossl_prov_ctx_get_bool_param(
-            provctx, OSSL_PKEY_PARAM_ML_DSA_RETAIN_SEED, 1);
-    prefer =
-        ossl_prov_ctx_get_bool_param(
-            provctx, OSSL_PKEY_PARAM_ML_DSA_PREFER_SEED, 1);
-    if (ossl_ml_dsa_set_prekey(key, prefer, retain,
+    if (ossl_ml_dsa_set_prekey(key, 0, 0,
                                seed, ML_DSA_SEED_BYTES, priv, v->sk_len))
         ret = key;
 
index 7a50210a7d246731a60c80983bf9dd9f7d0b4dfe..d5b0fb5f9517426c857231dcfbddfb31b9803081 100644 (file)
@@ -13,6 +13,7 @@
 #include <openssl/x509.h>
 #include <openssl/core_names.h>
 #include "internal/encoder.h"
+#include "prov/ml_kem.h"
 #include "ml_kem_codecs.h"
 
 /* Tables describing supported ASN.1 input/output formats. */
@@ -138,7 +139,6 @@ ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
                       int evp_type, PROV_CTX *provctx,
                       const char *propq)
 {
-    OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
     const ML_KEM_VINFO *v;
     const ML_COMMON_CODEC *codec;
     ML_COMMON_PKCS8_FMT_PREF *fmt_slots = NULL, *slot;
@@ -241,12 +241,9 @@ ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
      * Collect the seed and/or key into a "decoded" private key object,
      * to be turned into a real key on provider "load" or "import".
      */
-    if ((key = ossl_ml_kem_key_new(libctx, propq, evp_type)) == NULL)
+    if ((key = ossl_prov_ml_kem_new(provctx, propq, evp_type)) == NULL)
         goto end;
-    key->retain_seed = ossl_prov_ctx_get_bool_param(
-        provctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1);
-    key->prefer_seed = ossl_prov_ctx_get_bool_param(
-        provctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1);
+
     if (p8fmt->seed_length > 0) {
         if (!ossl_ml_kem_set_seed(buf + p8fmt->seed_offset,
                                   ML_KEM_SEED_BYTES, key)) {
diff --git a/providers/implementations/include/prov/ml_dsa.h b/providers/implementations/include/prov/ml_dsa.h
new file mode 100644 (file)
index 0000000..0d65c7e
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/ml_dsa.h"
+#include "prov/provider_ctx.h"
+
+ML_DSA_KEY *
+ossl_prov_ml_dsa_new(PROV_CTX *provctx, const char *propq, int evp_type);
diff --git a/providers/implementations/include/prov/ml_kem.h b/providers/implementations/include/prov/ml_kem.h
new file mode 100644 (file)
index 0000000..6e20492
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/ml_kem.h"
+#include "prov/provider_ctx.h"
+
+ML_KEM_KEY *
+ossl_prov_ml_kem_new(PROV_CTX *provctx, const char *propq, int evp_type);
index fc1000c3e1aa40ac67b4b8ce30ab93dfb3072017..2c80d4f2b5677a1142dce508cb78e3e13d1b1f82 100644 (file)
@@ -18,6 +18,7 @@
 #include "prov/implementations.h"
 #include "prov/providercommon.h"
 #include "prov/provider_ctx.h"
+#include "prov/ml_dsa.h"
 
 static OSSL_FUNC_keymgmt_free_fn ml_dsa_free_key;
 static OSSL_FUNC_keymgmt_has_fn ml_dsa_has;
@@ -93,22 +94,36 @@ static int ml_dsa_pairwise_test(const ML_DSA_KEY *key)
 }
 #endif
 
-static void *ml_dsa_new_key(void *provctx, const char *propq,
-                            int evp_type)
+ML_DSA_KEY *ossl_prov_ml_dsa_new(PROV_CTX *ctx, const char *propq, int evp_type)
 {
     ML_DSA_KEY *key;
-    int prefer, retain;
 
     if (!ossl_prov_is_running())
         return 0;
 
-    key = ossl_ml_dsa_key_new(PROV_LIBCTX_OF(provctx), propq, evp_type);
+    key = ossl_ml_dsa_key_new(PROV_LIBCTX_OF(ctx), propq, evp_type);
+    /*
+     * When decoding, if the key ends up "loaded" into the same provider, these
+     * are the correct config settings, otherwise, new values will be assigned
+     * on import into a different provider.  The "load" API does not pass along
+     * the provider context.
+     */
     if (key != NULL) {
-        prefer = ossl_prov_ctx_get_bool_param(
-            provctx, OSSL_PKEY_PARAM_ML_DSA_PREFER_SEED, 1);
-        retain = ossl_prov_ctx_get_bool_param(
-            provctx, OSSL_PKEY_PARAM_ML_DSA_RETAIN_SEED, 1);
-        ossl_ml_dsa_set_prekey(key, prefer, retain, NULL, 0, NULL, 0);
+        int flags_set = 0, flags_clr = 0;
+
+        if (ossl_prov_ctx_get_bool_param(
+                ctx, OSSL_PKEY_PARAM_ML_DSA_RETAIN_SEED, 1))
+            flags_set |= ML_DSA_KEY_RETAIN_SEED;
+        else
+            flags_clr = ML_DSA_KEY_RETAIN_SEED;
+
+        if (ossl_prov_ctx_get_bool_param(
+                ctx, OSSL_PKEY_PARAM_ML_DSA_PREFER_SEED, 1))
+            flags_set |= ML_DSA_KEY_PREFER_SEED;
+        else
+            flags_clr |= ML_DSA_KEY_PREFER_SEED;
+
+        ossl_ml_dsa_set_prekey(key, flags_set, flags_clr, NULL, 0, NULL, 0);
     }
     return key;
 }
@@ -217,8 +232,9 @@ static int ml_dsa_key_fromdata(ML_DSA_KEY *key, const OSSL_PARAM params[],
     }
 
     if (seed_len != 0
-        && (sk_len == 0 || ossl_ml_dsa_key_prefer_seed(key))) {
-        if (!ossl_ml_dsa_set_prekey(key, -1, -1, seed, seed_len, NULL, 0))
+        && (sk_len == 0
+            || (ossl_ml_dsa_key_get_prov_flags(key) & ML_DSA_KEY_PREFER_SEED))) {
+        if (!ossl_ml_dsa_set_prekey(key, 0, 0, seed, seed_len, NULL, 0))
             return 0;
         if (!ossl_ml_dsa_generate_key(key)) {
             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
@@ -373,7 +389,8 @@ static int ml_dsa_export(void *keydata, int selection,
 #ifndef FIPS_MODULE
 static void *ml_dsa_load(const void *reference, size_t reference_sz)
 {
-    ML_DSA_KEY *ret = NULL, *key = NULL;
+    ML_DSA_KEY *key = NULL;
+    const ML_DSA_PARAMS *key_params;
     const uint8_t *sk, *seed;
 
     if (ossl_prov_is_running() && reference_sz == sizeof(key)) {
@@ -388,18 +405,25 @@ static void *ml_dsa_load(const void *reference, size_t reference_sz)
         sk = ossl_ml_dsa_key_get_priv(key);
         seed = ossl_ml_dsa_key_get_seed(key);
         if (seed != NULL
-            && (sk == NULL || ossl_ml_dsa_key_prefer_seed(key))) {
+            && (sk == NULL || (ossl_ml_dsa_key_get_prov_flags(key)
+                               & ML_DSA_KEY_PREFER_SEED))) {
             if (ossl_ml_dsa_generate_key(key))
-                ret = key;
+                return key;
         } else if (sk != NULL) {
             if (ossl_ml_dsa_sk_decode(key, sk,
                                       ossl_ml_dsa_key_get_priv_len(key)))
-                ret = key;
+                return key;
+            key_params = ossl_ml_dsa_key_params(key);
+            ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+                           "error parsing %s private key",
+                           key_params->alg);
+        } else {
+            return key;
         }
     }
-    if (ret == NULL)
-        ossl_ml_dsa_key_free(key);
-    return ret;
+
+    ossl_ml_dsa_key_free(key);
+    return NULL;
 }
 #endif
 
@@ -428,11 +452,11 @@ static void *ml_dsa_gen(void *genctx, int evp_type)
 
     if (!ossl_prov_is_running())
         return NULL;
-    key = ml_dsa_new_key(gctx->provctx, gctx->propq, evp_type);
+    key = ossl_prov_ml_dsa_new(gctx->provctx, gctx->propq, evp_type);
     if (key == NULL)
         return NULL;
     if (gctx->entropy_len != 0
-        && !ossl_ml_dsa_set_prekey(key, -1, -1,
+        && !ossl_ml_dsa_set_prekey(key, 0, 0,
                                    gctx->entropy, gctx->entropy_len, NULL, 0))
         goto err;
     if (!ossl_ml_dsa_generate_key(key)) {
@@ -510,7 +534,7 @@ static void ml_dsa_gen_cleanup(void *genctx)
     static OSSL_FUNC_keymgmt_gen_fn ml_dsa_##alg##_gen;                        \
     static void *ml_dsa_##alg##_new_key(void *provctx)                         \
     {                                                                          \
-        return ml_dsa_new_key(provctx, NULL, EVP_PKEY_ML_DSA_##alg);           \
+        return ossl_prov_ml_dsa_new(provctx, NULL, EVP_PKEY_ML_DSA_##alg);     \
     }                                                                          \
     static void *ml_dsa_##alg##_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)\
     {                                                                          \
index a2fb4ad939a13c14a27cf30f0e8ddea1634ef018..5a007f3dc5e8c2c3773ceaae5c6b89b2d45cb34f 100644 (file)
 #include <openssl/self_test.h>
 #include <openssl/param_build.h>
 #include "crypto/ml_kem.h"
+#include "internal/param_build_set.h"
 #include "prov/implementations.h"
 #include "prov/providercommon.h"
 #include "prov/provider_ctx.h"
 #include "prov/securitycheck.h"
-#include "internal/param_build_set.h"
+#include "prov/ml_kem.h"
 
 static OSSL_FUNC_keymgmt_new_fn ml_kem_512_new;
 static OSSL_FUNC_keymgmt_new_fn ml_kem_768_new;
@@ -61,14 +62,14 @@ typedef struct ml_kem_gen_ctx_st {
     uint8_t *seed;
 } PROV_ML_KEM_GEN_CTX;
 
-static int ml_kem_pairwise_test(const ML_KEM_KEY *key)
+static int ml_kem_pairwise_test(const ML_KEM_KEY *key, int key_flags)
 {
 #ifdef FIPS_MODULE
     OSSL_SELF_TEST *st = NULL;
     OSSL_CALLBACK *cb = NULL;
     void *cbarg = NULL;
-    unsigned char entropy[ML_KEM_RANDOM_BYTES];
 #endif
+    unsigned char entropy[ML_KEM_RANDOM_BYTES];
     unsigned char secret[ML_KEM_SHARED_SECRET_BYTES];
     unsigned char out[ML_KEM_SHARED_SECRET_BYTES];
     unsigned char *ctext = NULL;
@@ -77,9 +78,10 @@ static int ml_kem_pairwise_test(const ML_KEM_KEY *key)
     int ret = 0;
 
     /* Unless we have both a public and private key, we can't do the test */
-    if (!ossl_ml_kem_have_prvkey(key) || !ossl_ml_kem_have_pubkey(key))
+    if (!ossl_ml_kem_have_prvkey(key)
+        || !ossl_ml_kem_have_pubkey(key)
+        || (key_flags & ML_KEM_KEY_PCT_TYPE) == 0)
         return 1;
-
 #ifdef FIPS_MODULE
     /*
      * The functions `OSSL_SELF_TEST_*` will return directly if parameter `st`
@@ -100,22 +102,21 @@ static int ml_kem_pairwise_test(const ML_KEM_KEY *key)
         goto err;
 
     memset(out, 0, sizeof(out));
-#ifdef FIPS_MODULE
+
     /*
-     * The FIPS module does a PCT on power-on, and would leak the RNG
-     * handle if use random entropy here.  So we use fixed entropy in
-     * the FIPS case.  Ideally, the leak will be fixed, and the test
-     * will also use random entropy in FIPS mode.
+     * The pairwise test is skipped unless either RANDOM or FIXED entropy PCTs
+     * are enabled.
      */
-    memset(entropy, 0125, sizeof(entropy));
-    operation_result = ossl_ml_kem_encap_seed(ctext, v->ctext_bytes,
-                                              secret, sizeof(secret),
-                                              entropy, sizeof(entropy),
-                                              key);
-#else
-    operation_result = ossl_ml_kem_encap_rand(ctext, v->ctext_bytes,
-                                              secret, sizeof(secret), key);
-#endif
+    if (key_flags & ML_KEM_KEY_RANDOM_PCT) {
+        operation_result = ossl_ml_kem_encap_rand(ctext, v->ctext_bytes,
+                                                  secret, sizeof(secret), key);
+    } else {
+        memset(entropy, 0125, sizeof(entropy));
+        operation_result = ossl_ml_kem_encap_seed(ctext, v->ctext_bytes,
+                                                  secret, sizeof(secret),
+                                                  entropy, sizeof(entropy),
+                                                  key);
+    }
     if (operation_result != 1)
         goto err;
 
@@ -144,17 +145,38 @@ err:
     return ret;
 }
 
-static void *ml_kem_new(PROV_CTX *ctx, const char *propq, int evp_type)
+ML_KEM_KEY *ossl_prov_ml_kem_new(PROV_CTX *ctx, const char *propq, int evp_type)
 {
     ML_KEM_KEY *key;
 
     if (!ossl_prov_is_running())
         return NULL;
+    /*
+     * When decoding, if the key ends up "loaded" into the same provider, these
+     * are the correct config settings, otherwise, new values will be assigned
+     * on import into a different provider.  The "load" API does not pass along
+     * the provider context.
+     */
     if ((key = ossl_ml_kem_key_new(PROV_LIBCTX_OF(ctx), propq, evp_type)) != NULL) {
-        key->retain_seed = ossl_prov_ctx_get_bool_param(
-            ctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1);
-        key->prefer_seed = ossl_prov_ctx_get_bool_param(
-            ctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1);
+        const char *pct_type = ossl_prov_ctx_get_param(
+            ctx, OSSL_PKEY_PARAM_ML_KEM_IMPORT_PCT_TYPE, "random");
+
+        if (ossl_prov_ctx_get_bool_param(
+            ctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1))
+            key->prov_flags |= ML_KEM_KEY_RETAIN_SEED;
+        else
+            key->prov_flags &= ~ML_KEM_KEY_RETAIN_SEED;
+        if (ossl_prov_ctx_get_bool_param(
+            ctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1))
+            key->prov_flags |= ML_KEM_KEY_PREFER_SEED;
+        else
+            key->prov_flags &= ~ML_KEM_KEY_PREFER_SEED;
+        if (OPENSSL_strcasecmp(pct_type, "random") == 0)
+            key->prov_flags |= ML_KEM_KEY_RANDOM_PCT;
+        else if (OPENSSL_strcasecmp(pct_type, "fixed") == 0)
+            key->prov_flags |= ML_KEM_KEY_FIXED_PCT;
+        else
+            key->prov_flags &= ~ML_KEM_KEY_PCT_TYPE;
     }
     return key;
 }
@@ -200,7 +222,7 @@ static int ml_kem_validate(const void *vkey, int selection, int check_type)
         return 0;
 
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR)
-        return ml_kem_pairwise_test(key);
+        return ml_kem_pairwise_test(key, ML_KEM_KEY_RANDOM_PCT);
     return 1;
 }
 
@@ -412,7 +434,8 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key,
         }
     }
 
-    if (seedlen != 0 && (prvlen == 0 || key->prefer_seed)) {
+    if (seedlen != 0
+        && (prvlen == 0 || (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) {
         if (prvlen != 0 && !check_seed(seedenc, prvenc, key))
             return 0;
         if (!ossl_ml_kem_set_seed(seedenc, seedlen, key)
@@ -439,7 +462,8 @@ static int ml_kem_import(void *vkey, int selection, const OSSL_PARAM params[])
 
     include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
     res = ml_kem_key_fromdata(key, params, include_private);
-    if (res > 0 && include_private && !ml_kem_pairwise_test(key)) {
+    if (res > 0 && include_private
+        && !ml_kem_pairwise_test(key, key->prov_flags)) {
 #ifdef FIPS_MODULE
         ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
 #endif
@@ -488,8 +512,9 @@ void *ml_kem_load(const void *reference, size_t reference_sz)
             && !check_seed(seed, encoded_dk, key))
             goto err;
         /* Generate the key now, if it holds only a stashed seed. */
-        if (ossl_ml_kem_have_seed(key) &&
-            (encoded_dk == NULL || key->prefer_seed)) {
+        if (ossl_ml_kem_have_seed(key)
+            && (encoded_dk == NULL
+                || (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) {
             if (!ossl_ml_kem_genkey(NULL, 0, key)
                 || (encoded_dk != NULL && !check_pkhash(encoded_dk, key)))
                 goto err;
@@ -501,7 +526,7 @@ void *ml_kem_load(const void *reference, size_t reference_sz)
                                key->vinfo->algorithm_name);
                 goto err;
             }
-            if (!ml_kem_pairwise_test(key))
+            if (!ml_kem_pairwise_test(key, key->prov_flags))
                 goto err;
         }
         OPENSSL_free(encoded_dk);
@@ -724,7 +749,7 @@ static void *ml_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)
             OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
         return NULL;
     seed = gctx->seed;
-    key = ml_kem_new(gctx->provctx, gctx->propq, gctx->evp_type);
+    key = ossl_prov_ml_kem_new(gctx->provctx, gctx->propq, gctx->evp_type);
     if (key == NULL)
         return NULL;
 
@@ -742,7 +767,7 @@ static void *ml_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)
 
     if (genok) {
 #ifdef FIPS_MODULE
-        if (!ml_kem_pairwise_test(key)) {
+        if (!ml_kem_pairwise_test(key, ML_KEM_KEY_FIXED_PCT)) {
             ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
             ossl_ml_kem_key_free(key);
             return NULL;
@@ -785,7 +810,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, EVP_PKEY_ML_KEM_##bits); \
+        return ossl_prov_ml_kem_new(provctx, NULL, EVP_PKEY_ML_KEM_##bits); \
     } \
     static void *ml_kem_##bits##_gen_init(void *provctx, int selection, \
                                           const OSSL_PARAM params[]) \
index 03e6c56a3668ac26bce12b986fa2b182f879250b..88e3d4deab08b64ca521029d04cc0bc0104cb668 100644 (file)
@@ -25,7 +25,7 @@ my @formats = qw(seed-priv priv-only seed-only oqskeypair bare-seed bare-priv);
 plan skip_all => "ML-KEM isn't supported in this build"
     if disabled("ml-kem");
 
-plan tests => @algs * (24 + 10 * @formats);
+plan tests => @algs * (25 + 10 * @formats);
 my $seed = join ("", map {sprintf "%02x", $_} (0..63));
 my $weed = join ("", map {sprintf "%02x", $_} (1..64));
 my $ikme = join ("", map {sprintf "%02x", $_} (0..31));
@@ -160,7 +160,7 @@ foreach my $alg (@algs) {
             sprintf("text form private key: %s with %s", $alg, $f));
     }
 
-    # (5 tests): Test import/load PCT failure
+    # (6 tests): Test import/load PCT failure
     my $real = sprintf('real-%s.der', $alg);
     my $fake = sprintf('fake-%s.der', $alg);
     my $mixt = sprintf('mixt-%s.der', $alg);
@@ -171,35 +171,50 @@ foreach my $alg (@algs) {
     ok(run(app([qw(openssl genpkey -algorithm), "ml-kem-$alg",
                 qw(-provparam ml-kem.output_formats=seed-priv -pkeyopt),
                 "hexseed:$seed", qw(-outform DER -out), $real])),
-                sprintf("create real private key: %s", $alg));
+        sprintf("create real private key: %s", $alg));
     ok(run(app([qw(openssl genpkey -algorithm), "ml-kem-$alg",
                 qw(-provparam ml-kem.output_formats=seed-priv -pkeyopt),
                 "hexseed:$weed", qw(-outform DER -out), $fake])),
-                sprintf("create fake private key: %s", $alg));
+        sprintf("create fake private key: %s", $alg));
     my $realfh = IO::File->new($real, "r");
     my $fakefh = IO::File->new($fake, "r");
     local $/ = undef;
     my $realder = <$realfh>;
     my $fakeder = <$fakefh>;
-    ok (length($realder) == 28 + (2 + 64) + (4 + $slen + $plen + $zlen)
+    #
+    # - 20 bytes PKCS8 fixed overhead,
+    # - 4 byte private key octet string tag + length
+    # - 4 byte seed + key sequence tag + length
+    #   - 2 byte seed tag + length
+    #     - 64 byte seed
+    #   - 4 byte key tag + length
+    #     - |dk| 's' vector
+    #     - |ek| public key ('t' vector || 'rho')
+    #     - implicit rejection 'z' seed component
+    #
+    ok(length($realder) == 28 + (2 + 64) + (4 + $slen + $plen + $zlen)
         && length($fakeder) == 28 + (2 + 64) + (4 + $slen + $plen + $zlen));
     my $mixtder = substr($realder, 0, 28 + 66 + 4 + $slen)
         . substr($fakeder, 28 + 66 + 4 + $slen, $plen)
         . substr($realder, 28 + 66 + 4 + $slen + $plen, $zlen);
-    my $mixtfh = IO::File->new($mixt, "w");
+    my $mixtfh = IO::File->new($mixt, ">:raw");
     print $mixtfh $mixtder;
     $mixtfh->close();
-    ok(run(app([qw(openssl pkey -inform DER -noout -in), $real],
-               sprintf("accept valid keypair: %s", $alg))));
+    ok(run(app([qw(openssl pkey -inform DER -noout -in), $real])),
+        sprintf("accept valid keypair: %s", $alg));
     ok(!run(app([qw(openssl pkey -provparam ml-kem.prefer_seed=no),
                  qw(-inform DER -noout -in), $mixt])),
-                sprintf("reject real private and fake public: %s", $alg));
+        sprintf("reject real private and fake public: %s", $alg));
+    ok(run(app([qw(openssl pkey -provparam ml-kem.prefer_seed=no),
+                 qw(-provparam ml-kem.import_pct_type=none),
+                 qw(-inform DER -noout -in), $mixt])),
+        sprintf("Absent PCT accept fake public: %s", $alg));
     # Mutate the public key hash
     my $mashder = $realder;
     substr($mashder, -64, 1) =~ s{(.)}{chr(ord($1)^1)}es;
-    my $mashfh = IO::File->new($mash, "w");
+    my $mashfh = IO::File->new($mash, ">:raw");
     print $mashfh $mashder;
     $mashfh->close();
     ok(!run(app([qw(openssl pkey -inform DER -noout -in), $mash])),
-                sprintf("reject real private and mutated public: %s", $alg));
+        sprintf("reject real private and mutated public: %s", $alg));
 }
index 91bff80e3fa8b1988a2170748d2e54e8b806f316..9d536f3f5597ff183ea6b127530e61f1119b6666 100644 (file)
@@ -423,6 +423,7 @@ my %params = (
     'PKEY_PARAM_ML_KEM_RETAIN_SEED' => "ml-kem.retain_seed",
     'PKEY_PARAM_ML_KEM_INPUT_FORMATS' => "ml-kem.input_formats",
     'PKEY_PARAM_ML_KEM_OUTPUT_FORMATS' => "ml-kem.output_formats",
+    'PKEY_PARAM_ML_KEM_IMPORT_PCT_TYPE' => "ml-kem.import_pct_type",
 
 # Key generation parameters
     'PKEY_PARAM_FFC_TYPE' =>         "type",