diff --git a/crypto/slh_dsa/slh_dsa_key.c b/crypto/slh_dsa/slh_dsa_key.c index 9dce013290..11834e8db1 100644 --- a/crypto/slh_dsa/slh_dsa_key.c +++ b/crypto/slh_dsa/slh_dsa_key.c @@ -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; +} diff --git a/crypto/slh_dsa/slh_dsa_key.h b/crypto/slh_dsa/slh_dsa_key.h index fa18f8a5d3..e2c526fce6 100644 --- a/crypto/slh_dsa/slh_dsa_key.h +++ b/crypto/slh_dsa/slh_dsa_key.h @@ -11,14 +11,18 @@ #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; }; diff --git a/crypto/slh_dsa/slh_dsa_local.h b/crypto/slh_dsa/slh_dsa_local.h index f291665a98..48dd49e778 100644 --- a/crypto/slh_dsa/slh_dsa_local.h +++ b/crypto/slh_dsa/slh_dsa_local.h @@ -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, diff --git a/crypto/slh_dsa/slh_wots.c b/crypto/slh_dsa/slh_wots.c index 616f713ce1..a0b6e92e75 100644 --- a/crypto/slh_dsa/slh_wots.c +++ b/crypto/slh_dsa/slh_wots.c @@ -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 diff --git a/crypto/slh_dsa/slh_xmss.c b/crypto/slh_dsa/slh_xmss.c index ab2e0b3046..b596922221 100644 --- a/crypto/slh_dsa/slh_xmss.c +++ b/crypto/slh_dsa/slh_xmss.c @@ -11,6 +11,54 @@ #include #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 * diff --git a/include/crypto/slh_dsa.h b/include/crypto/slh_dsa.h index 62b30f0d91..b3d0241ae6 100644 --- a/include/crypto/slh_dsa.h +++ b/include/crypto/slh_dsa.h @@ -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); diff --git a/providers/implementations/keymgmt/slh_dsa_kmgmt.c b/providers/implementations/keymgmt/slh_dsa_kmgmt.c index 1d6bad199c..31155e6b89 100644 --- a/providers/implementations/keymgmt/slh_dsa_kmgmt.c +++ b/providers/implementations/keymgmt/slh_dsa_kmgmt.c @@ -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; +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_PUBLIC_KEY) +#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 \ } diff --git a/test/slh_dsa.inc b/test/slh_dsa.inc index ac10c7eb84..f06915c757 100644 --- a/test/slh_dsa.inc +++ b/test/slh_dsa.inc @@ -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"), }; diff --git a/test/slh_dsa_test.c b/test/slh_dsa_test.c index 95a411a18e..7f2901a49c 100644 --- a/test/slh_dsa_test.c +++ b/test/slh_dsa_test.c @@ -9,12 +9,42 @@ #include #include +#include +#include #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); +} diff --git a/util/perl/OpenSSL/paramnames.pm b/util/perl/OpenSSL/paramnames.pm index c18a3f866c..372758db3c 100644 --- a/util/perl/OpenSSL/paramnames.pm +++ b/util/perl/OpenSSL/paramnames.pm @@ -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