]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add SLH-DSA key generation
authorslontis <shane.lontis@oracle.com>
Wed, 6 Nov 2024 10:45:29 +0000 (21:45 +1100)
committerTomas Mraz <tomas@openssl.org>
Tue, 18 Feb 2025 09:13:53 +0000 (10:13 +0100)
Also made fromdata able to generate the public root key if the private
key seed + prf as well as the public key seed are passed to from data.

Reviewed-by: Paul Dale <ppzgs1@gmail.com>
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Reviewed-by: Tim Hudson <tjh@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25882)

crypto/slh_dsa/slh_dsa_key.c
crypto/slh_dsa/slh_dsa_key.h
crypto/slh_dsa/slh_dsa_local.h
crypto/slh_dsa/slh_wots.c
crypto/slh_dsa/slh_xmss.c
include/crypto/slh_dsa.h
providers/implementations/keymgmt/slh_dsa_kmgmt.c
test/slh_dsa.inc
test/slh_dsa_test.c
util/perl/OpenSSL/paramnames.pm

index 9dce01329002d40865cbb840db48434cb456f68d..11834e8db1a829d1dfed6cdb26ae538af449a664 100644 (file)
@@ -16,6 +16,8 @@
 #include "slh_dsa_local.h"
 #include "slh_dsa_key.h"
 
+static int slh_dsa_compute_pk_root(SLH_DSA_CTX *ctx, SLH_DSA_KEY *out);
+
 /**
  * @brief Create a new SLH_DSA_KEY object
  *
@@ -59,6 +61,7 @@ void ossl_slh_dsa_key_free(SLH_DSA_KEY *key)
         return;
     REF_ASSERT_ISNT(i < 0);
 
+    OPENSSL_cleanse(&key->priv, sizeof(key->priv));
     OPENSSL_free(key->propq);
     CRYPTO_FREE_REF(&key->references);
     OPENSSL_free(key);
@@ -100,6 +103,11 @@ int ossl_slh_dsa_key_equal(const SLH_DSA_KEY *key1, const SLH_DSA_KEY *key2,
             return 0;
         ok = (memcmp(key1->pub, key2->pub, key1->key_len) == 0);
     }
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+        ok = ok && (key1->has_priv == key2->has_priv);
+        if (key1->has_priv)
+            ok = ok && (memcmp(key1->priv, key2->priv, key1->key_len) == 0);
+    }
     return ok;
 }
 
@@ -108,16 +116,21 @@ int ossl_slh_dsa_key_has(const SLH_DSA_KEY *key, int selection)
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
         if (key->key_len == 0)
             return 0;
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
+                && key->has_priv == 0)
+            return 0;
         return 1;
     }
     return 0;
 }
 
-int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM params[])
+int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM params[],
+                              int include_private)
 {
     size_t n, key_len, len = 0;
-    const OSSL_PARAM *param_pub;
+    const OSSL_PARAM *param_priv = NULL, *param_pub, *param_pk_seed = NULL;
     void *p;
+    SLH_DSA_CTX *dsa_ctx = NULL;
 
     if (key == NULL)
         return 0;
@@ -126,18 +139,103 @@ int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM params[])
     /* Both the public and private key are composed of 2 elements of size n */
     key_len = 2 * n;
 
+    /* Private key is optional */
+    if (include_private) {
+        param_priv = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+        if (param_priv == NULL)
+            return 0;
+    }
+
+    /*
+     * There must always be a public key, since the private key cannot exist
+     * without the public key elements.
+     * If there is only the public seed then the private key MUST be present in
+     * order to compute the pk_root element.
+     */
     param_pub = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
-    if (param_pub == NULL)
-        goto err;
-    p = key->pub;
-    if (!OSSL_PARAM_get_octet_string(param_pub, &p, key_len, &len))
-        goto err;
-    if (len != key_len)
-        goto err;
+    if (param_pub == NULL) {
+        param_pk_seed = OSSL_PARAM_locate_const(params,
+                                                OSSL_PKEY_PARAM_SLH_DSA_PUB_SEED);
+        if (param_pk_seed == NULL || param_priv == NULL)
+            return 0;
+    }
+
+    if (param_pub != NULL) {
+        p = key->pub;
+        if (!OSSL_PARAM_get_octet_string(param_pub, &p, key_len, &len))
+            return 0;
+        if (len != key_len)
+            return 0;
+    }
+    if (param_priv != NULL) {
+        p = key->priv;
+        if (!OSSL_PARAM_get_octet_string(param_priv, &p, key_len, &len))
+            return 0;
+        /* This is assuming that the private component contains no public elements */
+        if (len != key_len)
+            goto err;
+        key->has_priv = 1;
+    }
+    if (param_pk_seed != NULL) {
+        /*
+         * In this case we need to generate the pk_root
+         * which requires both the private key element(s) and the public key seed.
+         */
+        p = SLH_DSA_PK_SEED(key);
+        if (!OSSL_PARAM_get_octet_string(param_pk_seed, &p, n, &len))
+            goto err;
+        if (len != n)
+            goto err;
+        /* Compute the pk_root element */
+        dsa_ctx = ossl_slh_dsa_ctx_new(key->params->alg, key->libctx, key->propq);
+        if (dsa_ctx == NULL
+                || !slh_dsa_compute_pk_root(dsa_ctx, key))
+            goto err;
+        ossl_slh_dsa_ctx_free(dsa_ctx);
+    }
     key->key_len = key_len; /* This indicates the public key is present */
     return 1;
  err:
     key->key_len = 0;
+    key->has_priv = 0;
+    return 0;
+}
+
+static int slh_dsa_compute_pk_root(SLH_DSA_CTX *ctx, SLH_DSA_KEY *out)
+{
+    SLH_ADRS_FUNC_DECLARE(ctx, adrsf);
+    SLH_ADRS_DECLARE(adrs);
+    const SLH_DSA_PARAMS *params = out->params;
+
+    assert(params != NULL);
+
+    adrsf->zero(adrs);
+    adrsf->set_layer_address(adrs, params->d - 1);
+    /* Generate the ROOT public key */
+    ossl_slh_xmss_node(ctx, SLH_DSA_SK_SEED(out), 0, params->hm,
+                       SLH_DSA_PK_SEED(out), adrs, SLH_DSA_PK_ROOT(out));
+    return 1;
+}
+
+int ossl_slh_dsa_generate_key(SLH_DSA_CTX *ctx, OSSL_LIB_CTX *libctx,
+                              SLH_DSA_KEY *out)
+{
+    size_t n = ctx->params->n;
+    size_t key_len = 2 * n;
+
+    assert(ctx->params == out->params);
+
+    if (RAND_priv_bytes_ex(libctx, out->priv, key_len, 0) <= 0
+            || RAND_bytes_ex(libctx, out->pub, n, 0) <= 0
+            || !slh_dsa_compute_pk_root(ctx, out))
+        goto err;
+    out->key_len = key_len;
+    out->has_priv = 1;
+    return 1;
+err:
+    out->has_priv = 0;
+    out->key_len = 0;
+    OPENSSL_cleanse(&out->priv, sizeof(out->priv));
     return 0;
 }
 
@@ -145,3 +243,28 @@ int ossl_slh_dsa_key_type_matches(SLH_DSA_CTX *ctx, const SLH_DSA_KEY *key)
 {
     return (key->params == ctx->params);
 }
+
+int ossl_slh_dsa_key_is_private(const SLH_DSA_KEY *key)
+{
+    return key->has_priv;
+}
+
+const uint8_t *ossl_slh_dsa_key_get_pub(const SLH_DSA_KEY *key)
+{
+    return key->pub;
+}
+
+const uint8_t *ossl_slh_dsa_key_get_priv(const SLH_DSA_KEY *key)
+{
+    return key->priv;
+}
+
+size_t ossl_slh_dsa_key_get_len(const SLH_DSA_KEY *key)
+{
+    return key->key_len;
+}
+
+size_t ossl_slh_dsa_key_get_n(const SLH_DSA_KEY *key)
+{
+    return key->params->n;
+}
index fa18f8a5d39c47fddf4765f3646ef6cf06ebf7db..e2c526fce63cd5908bcfa0d11f1849ba400cbfa7 100644 (file)
 #include "internal/refcount.h"
 
 #define SLH_DSA_MAX_KEYLEN 32 * 2 /* 2 * n */
+#define SLH_DSA_SK_SEED(key) (key->priv)
+#define SLH_DSA_SK_PRF(key)  (key->priv + key->params->n)
 #define SLH_DSA_PK_SEED(key) (key->pub)
-#define SLH_DSA_PK_ROOT(key)  (key->pub + key->params->n)
+#define SLH_DSA_PK_ROOT(key) (key->pub + key->params->n)
 
 struct slh_dsa_key_st {
     uint8_t pub[SLH_DSA_MAX_KEYLEN];
+    uint8_t priv[SLH_DSA_MAX_KEYLEN];
     size_t key_len; /* This value is set to 2 * n if there is a public key */
     CRYPTO_REF_COUNT references;
     OSSL_LIB_CTX *libctx;
     char *propq;
     const SLH_DSA_PARAMS *params;
+    int has_priv;
 };
index f291665a9849dfcfb576dea1824debb911230460..48dd49e77819820ad49ece9a43fd2d868913bb1d 100644 (file)
@@ -46,11 +46,18 @@ struct slh_dsa_ctx_st {
     SLH_HASH_CTX hash_ctx;
 };
 
+void ossl_slh_wots_pk_gen(SLH_DSA_CTX *ctx,
+                          const uint8_t *sk_seed, const uint8_t *pk_seed,
+                          SLH_ADRS adrs, uint8_t *pk_out);
+
 void ossl_slh_wots_pk_from_sig(SLH_DSA_CTX *ctx,
                                const uint8_t *sig, const uint8_t *msg,
                                const uint8_t *pk_seed, uint8_t *adrs,
                                uint8_t *pk_out);
 
+void ossl_slh_xmss_node(SLH_DSA_CTX *ctx, const uint8_t *sk_seed,
+                        uint32_t node_id, uint32_t height,
+                        const uint8_t *pk_seed, SLH_ADRS adrs, uint8_t *pk_out);
 void ossl_slh_xmss_pk_from_sig(SLH_DSA_CTX *ctx, uint32_t node_id,
                                const uint8_t *sig, const uint8_t *msg,
                                const uint8_t *pk_seed, SLH_ADRS adrs,
index 616f713ce1a1e1f7dae5fdb7d7f4db22dabdd949..a0b6e92e751bc86237ae284891110e058c39c85a 100644 (file)
@@ -109,6 +109,52 @@ static void slh_wots_chain(SLH_DSA_CTX *ctx, const uint8_t *in,
     }
 }
 
+/**
+ * @brief WOTS+ Public key generation. See FIPS 205 Section 5.1
+ *
+ * @param ctx Contains SLH_DSA algorithm functions and constants.
+ * @param sk_seed A private key seed of size |n|
+ * @param pk_seed A public key seed of size |n|
+ * @param adrs An ADRS object containing the layer address, tree address and
+ *             keypair address of the WOTS+ public key to generate.
+ * @param pk_out The generated public key of size |n|
+ */
+void ossl_slh_wots_pk_gen(SLH_DSA_CTX *ctx,
+                          const uint8_t *sk_seed, const uint8_t *pk_seed,
+                          SLH_ADRS adrs, uint8_t *pk_out)
+{
+    SLH_HASH_FUNC_DECLARE(ctx, hashf, hctx);
+    SLH_ADRS_FUNC_DECLARE(ctx, adrsf);
+    SLH_HASH_FN_DECLARE(hashf, PRF);
+    SLH_ADRS_FN_DECLARE(adrsf, set_chain_address);
+    SLH_ADRS_DECLARE(sk_adrs);
+    SLH_ADRS_DECLARE(wots_pk_adrs);
+    size_t i, len;
+    size_t n = ctx->params->n;
+    uint8_t tmp[SLH_WOTS_LEN_MAX * SLH_MAX_N], *ptmp = tmp;
+    uint8_t sk[32];
+
+    adrsf->copy(sk_adrs, adrs);
+    adrsf->set_type_and_clear(sk_adrs, SLH_ADRS_TYPE_WOTS_PRF);
+    adrsf->copy_keypair_address(sk_adrs, adrs);
+
+    len = SLH_WOTS_LEN(n); /* See Section 5 intro */
+    for (i = 0; i < len; ++i) {
+        set_chain_address(sk_adrs, i);
+        PRF(hctx, pk_seed, sk_seed, sk_adrs, sk);
+
+        set_chain_address(adrs, i);
+        slh_wots_chain(ctx, sk, 0, NIBBLE_MASK, pk_seed, adrs, ptmp);
+        ptmp += n;
+    }
+
+    len = ptmp - tmp; /* should be n * (2 * n + 3) */
+    adrsf->copy(wots_pk_adrs, adrs);
+    adrsf->set_type_and_clear(wots_pk_adrs, SLH_ADRS_TYPE_WOTS_PK);
+    adrsf->copy_keypair_address(wots_pk_adrs, adrs);
+    hashf->T(hctx, pk_seed, wots_pk_adrs, tmp, len, pk_out);
+}
+
 /**
  * @brief Compute a candidate WOTS+ public key from a message and signature
  * See FIPS 205 Section 5.2 Algorithm 7
index ab2e0b30461c6314990fa86737f80195b1353e71..b596922221119a6420194435565a6c6ebeba62c5 100644 (file)
 #include <string.h>
 #include "slh_dsa_local.h"
 
+/**
+ * @brief Compute the root Public key of a XMSS tree.
+ * See FIPS 205 Section 6.1 Algorithm 9.
+ * This is a recursive function that starts at an leaf index, that calculates
+ * the hash of each parent using 2 child nodes.
+ *
+ * @param sk_seed A private key seed
+ * @param pk_seed A public key seed
+ * @param n The size of |sk_seed|, |pk_seed| and |pk_out|
+ * @param adrs An ADRS object containing the layer address and tree address set
+ *              to the XMSS treewithing which the XMSS tree is being computed.
+ * @param nodeid The index of the target node being computed
+ *               (which must be < 2^(hm - height)
+ * @param height The height within the tree of the node being computed.
+ *               (which must be <= hm) (hm is one of 3, 4, 8 or 9)
+ *               At height=0 There are 2^hm leaf nodes,
+ *               and the root node is at height = hm)
+ * @param pk_out The generated public key of size |n|
+ */
+void ossl_slh_xmss_node(SLH_DSA_CTX *ctx,
+                        const uint8_t *sk_seed,
+                        uint32_t node_id,
+                        uint32_t h,
+                        const uint8_t *pk_seed,
+                        SLH_ADRS adrs,
+                        uint8_t *pk_out)
+{
+    SLH_ADRS_FUNC_DECLARE(ctx, adrsf);
+
+    if (h == 0) {
+        /* For leaf nodes generate the public key */
+        adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_WOTS_HASH);
+        adrsf->set_keypair_address(adrs, node_id);
+        ossl_slh_wots_pk_gen(ctx, sk_seed, pk_seed, adrs, pk_out);
+    } else {
+        uint8_t lnode[SLH_MAX_N], rnode[SLH_MAX_N];
+
+        ossl_slh_xmss_node(ctx, sk_seed, 2 * node_id, h - 1, pk_seed, adrs,
+                           lnode);
+        ossl_slh_xmss_node(ctx, sk_seed, 2 * node_id + 1, h - 1, pk_seed, adrs,
+                           rnode);
+        adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_TREE);
+        adrsf->set_tree_height(adrs, h);
+        adrsf->set_tree_index(adrs, node_id);
+        ctx->hash_func->H(&ctx->hash_ctx, pk_seed, adrs, lnode, rnode, pk_out);
+    }
+}
+
 /**
  * @brief Compute a candidate XMSS public key from a message and XMSS signature
  *
index 62b30f0d9126ac1b75db02feb485882bd9a89a20..b3d0241ae60643e75c2e66ebb52638899c6a622a 100644 (file)
@@ -27,7 +27,15 @@ int ossl_slh_dsa_key_up_ref(SLH_DSA_KEY *key);
 int ossl_slh_dsa_key_equal(const SLH_DSA_KEY *key1, const SLH_DSA_KEY *key2,
                            int selection);
 int ossl_slh_dsa_key_has(const SLH_DSA_KEY *key, int selection);
-int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM *params);
+int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM *params,
+                              int include_private);
+int ossl_slh_dsa_generate_key(SLH_DSA_CTX *ctx, OSSL_LIB_CTX *libctx,
+                              SLH_DSA_KEY *out);
+int ossl_slh_dsa_key_is_private(const SLH_DSA_KEY *key);
+const uint8_t *ossl_slh_dsa_key_get_pub(const SLH_DSA_KEY *key);
+const uint8_t *ossl_slh_dsa_key_get_priv(const SLH_DSA_KEY *key);
+size_t ossl_slh_dsa_key_get_len(const SLH_DSA_KEY *key);
+size_t ossl_slh_dsa_key_get_n(const SLH_DSA_KEY *key);
 
 int ossl_slh_dsa_key_type_matches(SLH_DSA_CTX *ctx, const SLH_DSA_KEY *key);
 
index 1d6bad199c83facdcd8fbd126d5033873ed6baf9..31155e6b89661d37d4e554a3d49eb4356c7addfd 100644 (file)
@@ -21,8 +21,20 @@ static OSSL_FUNC_keymgmt_has_fn slh_dsa_has;
 static OSSL_FUNC_keymgmt_match_fn slh_dsa_match;
 static OSSL_FUNC_keymgmt_import_fn slh_dsa_import;
 static OSSL_FUNC_keymgmt_import_types_fn slh_dsa_imexport_types;
-
-#define SLH_DSA_POSSIBLE_SELECTIONS (OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
+static OSSL_FUNC_keymgmt_get_params_fn slh_dsa_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn slh_dsa_gettable_params;
+static OSSL_FUNC_keymgmt_gen_init_fn slh_dsa_gen_init;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn slh_dsa_gen_cleanup;
+static OSSL_FUNC_keymgmt_gen_set_params_fn slh_dsa_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn slh_dsa_gen_settable_params;
+
+#define SLH_DSA_POSSIBLE_SELECTIONS (OSSL_KEYMGMT_SELECT_KEYPAIR)
+
+struct slh_dsa_gen_ctx {
+    OSSL_LIB_CTX *libctx;
+    char *propq;
+    SLH_DSA_CTX *ctx;
+};
 
 static void *slh_dsa_new_key(void *provctx, const char *alg)
 {
@@ -64,6 +76,7 @@ static int slh_dsa_match(const void *keydata1, const void *keydata2, int selecti
 static int slh_dsa_import(void *keydata, int selection, const OSSL_PARAM params[])
 {
     SLH_DSA_KEY *key = keydata;
+    int include_priv;
 
     if (!ossl_prov_is_running() || key == NULL)
         return 0;
@@ -71,11 +84,13 @@ static int slh_dsa_import(void *keydata, int selection, const OSSL_PARAM params[
     if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0)
         return 0;
 
-    return ossl_slh_dsa_key_fromdata(key, params);
+    include_priv = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0);
+    return ossl_slh_dsa_key_fromdata(key, params, include_priv);
 }
 
 static const OSSL_PARAM slh_dsa_key_types[] = {
     OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
     OSSL_PARAM_END
 };
 
@@ -86,12 +101,132 @@ static const OSSL_PARAM *slh_dsa_imexport_types(int selection)
     return slh_dsa_key_types;
 }
 
+static const OSSL_PARAM *slh_dsa_gettable_params(void *provctx)
+{
+    return slh_dsa_key_types;
+}
+
+static int key_to_params(SLH_DSA_KEY *key, OSSL_PARAM_BLD *tmpl,
+                         OSSL_PARAM params[], int include_private)
+{
+    if (key == NULL)
+        return 0;
+
+    if (!ossl_param_build_set_octet_string(tmpl, params,
+                                           OSSL_PKEY_PARAM_PUB_KEY,
+                                           ossl_slh_dsa_key_get_pub(key),
+                                           ossl_slh_dsa_key_get_len(key)))
+        return 0;
+
+    if (include_private
+        && ossl_slh_dsa_key_is_private(key)
+        && !ossl_param_build_set_octet_string(tmpl, params,
+                                              OSSL_PKEY_PARAM_PRIV_KEY,
+                                              ossl_slh_dsa_key_get_priv(key),
+                                              ossl_slh_dsa_key_get_len(key)))
+        return 0;
+
+    return 1;
+}
+
+static int slh_dsa_get_params(void *keydata, OSSL_PARAM params[])
+{
+    SLH_DSA_KEY *key = keydata;
+
+    return key_to_params(key, NULL, params, 1);
+}
+
+static void *slh_dsa_gen_init(void *provctx, int selection,
+                              const OSSL_PARAM params[])
+{
+    OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+    struct slh_dsa_gen_ctx *gctx = NULL;
+
+    if (!ossl_prov_is_running())
+        return NULL;
+
+    if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+        gctx->libctx = libctx;
+        if (!slh_dsa_gen_set_params(gctx, params)) {
+            OPENSSL_free(gctx);
+            gctx = NULL;
+        }
+    }
+    return gctx;
+}
+
+static void *slh_dsa_gen(void *genctx, const char *alg)
+{
+    struct slh_dsa_gen_ctx *gctx = genctx;
+    SLH_DSA_KEY *key = NULL;
+    SLH_DSA_CTX *ctx = NULL;
+
+    if (!ossl_prov_is_running())
+        return NULL;
+    ctx = ossl_slh_dsa_ctx_new(alg, gctx->libctx, gctx->propq);
+    if (ctx == NULL)
+        return NULL;
+    key = ossl_slh_dsa_key_new(gctx->libctx, alg);
+    if (key == NULL)
+        return NULL;
+    if (!ossl_slh_dsa_generate_key(ctx, gctx->libctx, key))
+        goto err;
+    ossl_slh_dsa_ctx_free(ctx);
+    return key;
+ err:
+    ossl_slh_dsa_ctx_free(ctx);
+    ossl_slh_dsa_key_free(key);
+    return NULL;
+}
+
+static int slh_dsa_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+    struct slh_dsa_gen_ctx *gctx = genctx;
+    const OSSL_PARAM *p;
+
+    if (gctx == NULL)
+        return 0;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_PROPERTIES);
+    if (p != NULL) {
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        OPENSSL_free(gctx->propq);
+        gctx->propq = OPENSSL_strdup(p->data);
+        if (gctx->propq == NULL)
+            return 0;
+    }
+    return 1;
+}
+
+static const OSSL_PARAM *slh_dsa_gen_settable_params(ossl_unused void *genctx,
+                                                     ossl_unused void *provctx)
+{
+    static OSSL_PARAM settable[] = {
+        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+        OSSL_PARAM_END
+    };
+    return settable;
+}
+
+static void slh_dsa_gen_cleanup(void *genctx)
+{
+    struct slh_dsa_gen_ctx *gctx = genctx;
+
+    OPENSSL_free(gctx->propq);
+    OPENSSL_free(gctx);
+}
+
 #define MAKE_KEYMGMT_FUNCTIONS(alg, fn)                                        \
 static OSSL_FUNC_keymgmt_new_fn slh_dsa_##fn##_new_key;                        \
 static void *slh_dsa_##fn##_new_key(void *provctx)                             \
 {                                                                              \
     return slh_dsa_new_key(provctx, alg);                                      \
 }                                                                              \
+static void *slh_dsa_##fn##_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)\
+{                                                                              \
+    return slh_dsa_gen(genctx, alg);                                           \
+}                                                                              \
 const OSSL_DISPATCH ossl_slh_dsa_##fn##_keymgmt_functions[] = {                \
     { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))slh_dsa_##fn##_new_key },         \
     { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))slh_dsa_free_key },              \
@@ -99,6 +234,15 @@ const OSSL_DISPATCH ossl_slh_dsa_##fn##_keymgmt_functions[] = {                \
     { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))slh_dsa_match },                \
     { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))slh_dsa_import },              \
     { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))slh_dsa_imexport_types },\
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))slh_dsa_get_params },     \
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))slh_dsa_gettable_params }, \
+    { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))slh_dsa_gen_init },          \
+    { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))slh_dsa_##fn##_gen },             \
+    { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))slh_dsa_gen_cleanup },    \
+    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS,                                        \
+      (void (*)(void))slh_dsa_gen_set_params },                                \
+    { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,                                   \
+      (void (*)(void))slh_dsa_gen_settable_params },                           \
     OSSL_DISPATCH_END                                                          \
 }
 
index ac10c7eb84e41313eb6a52234f8a664d8b86b3c6..f06915c757a92dce856a5c7b2540f718afae10e5 100644 (file)
@@ -7,7 +7,7 @@
  * https://www.openssl.org/source/license.html
  */
 
-typedef struct SLH_DSA_ACVP_TEST_DATA_st {
+typedef struct slh_dsa_sig_test_data_st {
     const char *alg;
     const unsigned char *pub;
     size_t pub_len;
@@ -17,15 +17,32 @@ typedef struct SLH_DSA_ACVP_TEST_DATA_st {
     size_t msg_len;
     const unsigned char *sig;
     size_t sig_len;
-} SLH_DSA_ACVP_TEST_DATA;
+} SLH_DSA_SIG_TEST_DATA;
 
-#define SLH_DSA_ACVP_ITEM(name, alg) {                                         \
+typedef struct slh_dsa_keygen_test_data_st {
+    const char *name;
+    const uint8_t *priv;
+    size_t priv_len;
+    const uint8_t *pub_seed;
+    size_t pub_seed_len;
+    const uint8_t *pub_expected;
+    size_t pub_expected_len;
+} SLH_DSA_KEYGEN_TEST_DATA;
+
+#define SLH_DSA_SIG_TEST_ITEM(name, alg) {                                     \
     alg,                                                                       \
     name##_pub, sizeof(name##_pub),                                            \
     name##_priv, sizeof(name##_priv),                                          \
     name##_msg, sizeof(name##_msg),                                            \
     name##_sig, sizeof(name##_sig) }
 
+#define SLH_DSA_KEYGEN_TEST_ITEM(name, alg) {                                  \
+    alg,                                                                       \
+    name##_keygen_priv, sizeof(name##_keygen_priv),                            \
+    name##_keygen_pub_seed, sizeof(name##_keygen_pub_seed),                    \
+    name##_keygen_pub_expected, sizeof(name##_keygen_pub_expected)             \
+}
+
 /*
  * Test vectors from
  * usnistgov/ACVP-Server/refs/heads/master/gen-val/json-files/SLH-DSA-sigGen-FIPS205/internalProjection.json
@@ -171,6 +188,31 @@ static const uint8_t slh_dsa_sha2_128s_0_sig[] = {
     0xe0,0xd7,0x42,0x24,0xb6,0xfe,0xd7,0x6e,0xa8,0x3a,0x09,0x52,0x8f,0xb2,0x88,0xab,0x2a,0x88,0x67,0xdd,0x0a,0x9e,0x80,0x10,0x27,0x4b,0x21,0x09,0xa5,0x31,0x23,0x8e,0x34,0x51,0x23,0xc8,0xe0,0xd8,0xfd,0x4d,0x46,0xbf,0xc8,0x88,0xe4,0xd9,0x50,0xac,
 };
 
-static SLH_DSA_ACVP_TEST_DATA slh_dsa_testdata[] = {
-    SLH_DSA_ACVP_ITEM(slh_dsa_sha2_128s_0, "SLH-DSA-SHA2-128s"),
+static SLH_DSA_SIG_TEST_DATA slh_dsa_sig_testdata[] = {
+    SLH_DSA_SIG_TEST_ITEM(slh_dsa_sha2_128s_0, "SLH-DSA-SHA2-128s"),
+};
+
+
+/* skSeed || skPrf */
+static const uint8_t slh_dsa_sha2_128s_0_keygen_priv[] = {
+    0x2F, 0x89, 0x6D, 0x61, 0xD9, 0xCD, 0x90, 0x38,
+    0xCA, 0x30, 0x33, 0x94, 0xFA, 0xDA, 0xA2, 0x2A,
+    0x24, 0xAC, 0x5E, 0xC1, 0xD8, 0x6A, 0x98, 0x9C,
+    0xA2, 0x19, 0x6C, 0x3C, 0x86, 0x32, 0x41, 0x9C,
+};
+/* pkSeed */
+static const uint8_t slh_dsa_sha2_128s_0_keygen_pub_seed[] = {
+    0x1A, 0x05, 0xA4, 0x2F, 0xE3, 0x00, 0xE8, 0x7B,
+    0x16, 0xAE, 0xE1, 0x16, 0xCB, 0x2E, 0x23, 0x63,
+};
+/* pkSeed || pkRoot */
+static const uint8_t slh_dsa_sha2_128s_0_keygen_pub_expected[] = {
+    0x1A, 0x05, 0xA4, 0x2F, 0xE3, 0x00, 0xE8, 0x7B,
+    0x16, 0xAE, 0xE1, 0x16, 0xCB, 0x2E, 0x23, 0x63,
+    0x58, 0xE2, 0xC3, 0xE6, 0x26, 0x32, 0xC9, 0xDE,
+    0x03, 0xD0, 0x8A, 0x53, 0x5A, 0x0E, 0xB7, 0xE7,
+};
+
+static const SLH_DSA_KEYGEN_TEST_DATA slh_dsa_keygen_testdata[] = {
+    SLH_DSA_KEYGEN_TEST_ITEM(slh_dsa_sha2_128s_0, "SLH-DSA-SHA2-128s"),
 };
index 95a411a18e8c03c1db93a7fa6b8ac036bc46aa71..7f2901a49c5a1a3d7109d31a44b6dfa145f496bd 100644 (file)
@@ -9,12 +9,42 @@
 
 #include <openssl/core_names.h>
 #include <openssl/evp.h>
+#include <openssl/param_build.h>
+#include <openssl/rand.h>
 #include "crypto/slh_dsa.h"
 #include "internal/nelem.h"
 #include "testutil.h"
 #include "slh_dsa.inc"
 
 static OSSL_LIB_CTX *libctx = NULL;
+static OSSL_PROVIDER *fake_rand = NULL;
+
+static size_t entropy_pos = 0;
+static size_t entropy_sz = 0;
+static uint8_t entropy[128];
+
+static int set_entropy(const uint8_t *ent1, size_t ent1_len,
+                       const uint8_t *ent2, size_t ent2_len)
+{
+    if ((ent1_len + ent2_len) > sizeof(entropy))
+        return 0;
+    entropy_pos = 0;
+    entropy_sz += (ent1_len + ent2_len);
+    memcpy(entropy, ent1, ent1_len);
+    if (ent2 != NULL)
+        memcpy(entropy + ent1_len, ent2, ent2_len);
+    return 1;
+}
+
+static int fake_rand_cb(unsigned char *buf, size_t num,
+                        ossl_unused const char *name, EVP_RAND_CTX *ctx)
+{
+    if ((entropy_pos + num) > entropy_sz)
+        return 0;
+    memcpy(buf, entropy + entropy_pos, num);
+    entropy_pos += num;
+    return 1;
+}
 
 static EVP_PKEY *slh_dsa_pubkey_from_data(const char *alg,
                                           const unsigned char *data, size_t datalen)
@@ -38,10 +68,45 @@ static EVP_PKEY *slh_dsa_pubkey_from_data(const char *alg,
     return key;
 }
 
+static int slh_dsa_create_keypair(EVP_PKEY **pkey, const char *name,
+                                  const uint8_t *priv, size_t priv_len,
+                                  const uint8_t *pub, size_t pub_len)
+{
+    int ret = 0;
+    EVP_PKEY_CTX *ctx = NULL;
+    OSSL_PARAM_BLD *bld = NULL;
+    OSSL_PARAM *params = NULL;
+    const char *pub_name = OSSL_PKEY_PARAM_PUB_KEY;
+
+    if (pub_len != priv_len)
+        pub_name = OSSL_PKEY_PARAM_SLH_DSA_PUB_SEED;
+
+    if (!TEST_ptr(bld = OSSL_PARAM_BLD_new())
+            || !TEST_true(OSSL_PARAM_BLD_push_octet_string(bld,
+                                                           OSSL_PKEY_PARAM_PRIV_KEY,
+                                                           priv, priv_len) > 0)
+            || !TEST_true(OSSL_PARAM_BLD_push_octet_string(bld,
+                                                           pub_name,
+                                                           pub, pub_len) > 0)
+            || !TEST_ptr(params = OSSL_PARAM_BLD_to_param(bld))
+            || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, name, NULL))
+            || !TEST_int_eq(EVP_PKEY_fromdata_init(ctx), 1)
+            || !TEST_int_eq(EVP_PKEY_fromdata(ctx, pkey, EVP_PKEY_KEYPAIR,
+                                              params), 1))
+        goto err;
+
+    ret = 1;
+err:
+    OSSL_PARAM_free(params);
+    OSSL_PARAM_BLD_free(bld);
+    EVP_PKEY_CTX_free(ctx);
+    return ret;
+}
+
 static int slh_dsa_bad_pub_len_test(void)
 {
     int ret = 0;
-    SLH_DSA_ACVP_TEST_DATA *td = &slh_dsa_testdata[0];
+    SLH_DSA_SIG_TEST_DATA *td = &slh_dsa_sig_testdata[0];
     EVP_PKEY *pkey = NULL;
     size_t pub_len = 0;
     unsigned char pubdata[64 + 1];
@@ -71,7 +136,7 @@ static int slh_dsa_key_eq_test(void)
 {
     int ret = 0;
     EVP_PKEY *key[2] = { NULL, NULL };
-    SLH_DSA_ACVP_TEST_DATA *td1 = &slh_dsa_testdata[0];
+    SLH_DSA_SIG_TEST_DATA *td1 = &slh_dsa_sig_testdata[0];
 #ifndef OPENSSL_NO_EC
     EVP_PKEY *eckey = NULL;
 #endif
@@ -99,7 +164,7 @@ end:
 static int slh_dsa_key_validate_test(void)
 {
     int ret = 0;
-    SLH_DSA_ACVP_TEST_DATA *td = &slh_dsa_testdata[0];
+    SLH_DSA_SIG_TEST_DATA *td = &slh_dsa_sig_testdata[0];
     EVP_PKEY_CTX *vctx = NULL;
     EVP_PKEY *key = NULL;
 
@@ -117,7 +182,7 @@ end:
 static int slh_dsa_sig_verify_test(void)
 {
     int ret = 0;
-    SLH_DSA_ACVP_TEST_DATA *td = &slh_dsa_testdata[0];
+    SLH_DSA_SIG_TEST_DATA *td = &slh_dsa_sig_testdata[0];
     EVP_PKEY_CTX *vctx = NULL;
     EVP_PKEY *key = NULL;
     EVP_SIGNATURE *sig_alg = NULL;
@@ -145,11 +210,87 @@ err:
     return ret;
 }
 
+static int slh_dsa_keygen_test(void)
+{
+    int ret = 0;
+    const SLH_DSA_KEYGEN_TEST_DATA *tst = &slh_dsa_keygen_testdata[0];
+    EVP_PKEY *pkey = NULL;
+    uint8_t priv[32 * 2], pub[32 * 2];
+    size_t priv_len, pub_len;
+
+    if (!TEST_true(set_entropy(tst->priv, tst->priv_len,
+                               tst->pub_seed, tst->pub_seed_len)))
+        goto err;
+
+    fake_rand_set_callback(RAND_get0_private(NULL), &fake_rand_cb);
+    fake_rand_set_callback(RAND_get0_public(NULL), &fake_rand_cb);
+
+    if (!TEST_ptr(pkey = EVP_PKEY_Q_keygen(libctx, NULL, tst->name)))
+        goto err;
+    if (!TEST_true(EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY,
+                                                   priv, sizeof(priv), &priv_len)))
+        goto err;
+    if (!TEST_true(EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
+                                                   pub, sizeof(pub), &pub_len)))
+        goto err;
+    if (!TEST_size_t_eq(priv_len, tst->priv_len)
+            || !TEST_size_t_eq(pub_len, tst->priv_len))
+        goto err;
+    ret = 1;
+err:
+    fake_rand_set_callback(RAND_get0_public(NULL), NULL);
+    fake_rand_set_callback(RAND_get0_private(NULL), NULL);
+    EVP_PKEY_free(pkey);
+    return ret;
+}
+
+/*
+ * Given raw values for the private key + public key seed
+ * generate the public root also when using from data.
+ */
+static int slh_dsa_pub_root_from_data_test(void)
+{
+    int ret = 0;
+    uint8_t priv[64], pub[64];
+    size_t priv_len = 0, pub_len = 0;
+    EVP_PKEY *pkey = NULL;
+    const SLH_DSA_KEYGEN_TEST_DATA *tst = &slh_dsa_keygen_testdata[0];
+
+    if (!slh_dsa_create_keypair(&pkey, tst->name, tst->priv, tst->priv_len,
+                                tst->pub_seed, tst->pub_seed_len))
+        goto err;
+
+    if (!TEST_true(EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY,
+                                                   priv, sizeof(priv), &priv_len)))
+        goto err;
+    if (!TEST_true(EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
+                                                   pub, sizeof(pub), &pub_len)))
+        goto err;
+    if (!TEST_mem_eq(pub, pub_len, tst->pub_expected, tst->pub_expected_len))
+        goto err;
+    ret = 1;
+err:
+    OPENSSL_cleanse(priv, priv_len);
+    EVP_PKEY_free(pkey);
+    return ret;
+}
+
 int setup_tests(void)
 {
+    fake_rand = fake_rand_start(NULL);
+    if (fake_rand == NULL)
+        return 0;
+
     ADD_TEST(slh_dsa_bad_pub_len_test);
     ADD_TEST(slh_dsa_key_validate_test);
     ADD_TEST(slh_dsa_key_eq_test);
     ADD_TEST(slh_dsa_sig_verify_test);
+    ADD_TEST(slh_dsa_keygen_test);
+    ADD_TEST(slh_dsa_pub_root_from_data_test);
     return 1;
 }
+
+void cleanup_tests(void)
+{
+    fake_rand_finish(fake_rand);
+}
index c18a3f866ca6b03a2771fe863a349670d3f0faf6..372758db3c38a128994d2066dc71bd7a5123a1ab 100644 (file)
@@ -444,6 +444,8 @@ my %params = (
     'PKEY_PARAM_ML_DSA_PREFER_SEED' =>      "ml-dsa.prefer_seed",
     'PKEY_PARAM_ML_DSA_INPUT_FORMATS' =>    "ml-dsa.input_formats",
     'PKEY_PARAM_ML_DSA_OUTPUT_FORMATS' =>   "ml-dsa.output_formats",
+# SLH_DSA parameters
+    'PKEY_PARAM_SLH_DSA_PUB_SEED' => "pk-seed",
 
 # Key Exchange parameters
     'EXCHANGE_PARAM_PAD' =>                   "pad",# uint