Add SLH-DSA key generation

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)
This commit is contained in:
slontis 2024-11-06 21:45:29 +11:00 committed by Tomas Mraz
parent 2f9e152d86
commit 8f53b9b59d
10 changed files with 587 additions and 22 deletions

View 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;
}

View File

@ -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;
};

View 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,

View 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

View File

@ -11,6 +11,54 @@
#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
*

View 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);

View 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;
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 \
}

View 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"),
};

View 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);
}

View 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