From 34f4cacc8fea7fd4a9616c48e951e86c5cf8c006 Mon Sep 17 00:00:00 2001 From: slontis Date: Wed, 6 Nov 2024 14:22:45 +1100 Subject: [PATCH] Add base code to load a SLH_DSA public key. This loads a SLH_DSA public key from data. A simple SLH_DSA keymanager imports this key. Initially this only has a parameter set for SLH-DSA-SHA2-128s Reviewed-by: Paul Dale Reviewed-by: Viktor Dukhovni Reviewed-by: Tim Hudson (Merged from https://github.com/openssl/openssl/pull/25882) --- crypto/build.info | 2 +- crypto/slh_dsa/build.info | 7 + crypto/slh_dsa/slh_dsa_key.c | 142 ++++++++++++++++++ crypto/slh_dsa/slh_dsa_key.h | 22 +++ crypto/slh_dsa/slh_dsa_local.h | 11 ++ crypto/slh_dsa/slh_params.c | 36 +++++ crypto/slh_dsa/slh_params.h | 31 ++++ include/crypto/slh_dsa.h | 28 ++++ include/crypto/types.h | 3 + providers/defltprov.c | 4 + .../include/prov/implementations.h | 3 + .../implementations/include/prov/names.h | 2 + providers/implementations/keymgmt/build.info | 5 + .../implementations/keymgmt/slh_dsa_kmgmt.c | 105 +++++++++++++ test/build.info | 7 + test/recipes/30-test_slh_dsa.t | 25 +++ test/slh_dsa.inc | 57 +++++++ test/slh_dsa_test.c | 123 +++++++++++++++ 18 files changed, 612 insertions(+), 1 deletion(-) create mode 100644 crypto/slh_dsa/build.info create mode 100644 crypto/slh_dsa/slh_dsa_key.c create mode 100644 crypto/slh_dsa/slh_dsa_key.h create mode 100644 crypto/slh_dsa/slh_dsa_local.h create mode 100644 crypto/slh_dsa/slh_params.c create mode 100644 crypto/slh_dsa/slh_params.h create mode 100644 include/crypto/slh_dsa.h create mode 100644 providers/implementations/keymgmt/slh_dsa_kmgmt.c create mode 100644 test/recipes/30-test_slh_dsa.t create mode 100644 test/slh_dsa.inc create mode 100644 test/slh_dsa_test.c diff --git a/crypto/build.info b/crypto/build.info index ee293d4af94..3d2375778e7 100644 --- a/crypto/build.info +++ b/crypto/build.info @@ -6,7 +6,7 @@ SUBDIRS=objects buffer bio stack lhash hashtable rand evp asn1 pem x509 conf \ siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \ seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \ err comp http ocsp cms ts srp cmac ct async ess crmf cmp encode_decode \ - ffc hpke thread ml_dsa + ffc hpke thread ml_dsa slh_dsa LIBS=../libcrypto diff --git a/crypto/slh_dsa/build.info b/crypto/slh_dsa/build.info new file mode 100644 index 00000000000..d6e37a90c14 --- /dev/null +++ b/crypto/slh_dsa/build.info @@ -0,0 +1,7 @@ +LIBS=../../libcrypto + +$COMMON=slh_dsa_key.c slh_params.c + +IF[{- !$disabled{'slh_dsa'} -}] + SOURCE[../../libcrypto]=$COMMON +ENDIF diff --git a/crypto/slh_dsa/slh_dsa_key.c b/crypto/slh_dsa/slh_dsa_key.c new file mode 100644 index 00000000000..5ccb6466909 --- /dev/null +++ b/crypto/slh_dsa/slh_dsa_key.c @@ -0,0 +1,142 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include "slh_dsa_local.h" +#include "slh_dsa_key.h" + +/** + * @brief Create a new SLH_DSA_KEY object + * + * @param libctx A OSSL_LIB_CTX object used for fetching algorithms. + * @param alg The algrithm name associated with the key type + * @returns The new SLH_DSA_KEY object on success, or NULL on malloc failure + */ +SLH_DSA_KEY *ossl_slh_dsa_key_new(OSSL_LIB_CTX *libctx, const char *alg) +{ + SLH_DSA_KEY *ret; + const SLH_DSA_PARAMS *params = ossl_slh_dsa_params_get(alg); + + if (params == NULL) + return NULL; + + ret = OPENSSL_zalloc(sizeof(*ret)); + if (ret != NULL) { + if (!CRYPTO_NEW_REF(&ret->references, 1)) { + OPENSSL_free(ret); + return NULL; + } + ret->libctx = libctx; + ret->params = params; + } + return ret; +} + +/** + * @brief Destroy a SLH_DSA_KEY object + */ +void ossl_slh_dsa_key_free(SLH_DSA_KEY *key) +{ + int i; + + if (key == NULL) + return; + + CRYPTO_DOWN_REF(&key->references, &i); + REF_PRINT_COUNT("SLH_DSA_KEY", key); + if (i > 0) + return; + REF_ASSERT_ISNT(i < 0); + + OPENSSL_free(key->propq); + CRYPTO_FREE_REF(&key->references); + OPENSSL_free(key); +} + +/* + * @brief Increase the reference count for a SLH_DSA_KEY object. + * @returns 1 on success or 0 otherwise. + */ +int ossl_slh_dsa_key_up_ref(SLH_DSA_KEY *key) +{ + int i; + + if (CRYPTO_UP_REF(&key->references, &i) <= 0) + return 0; + + REF_PRINT_COUNT("SLH_DSA_KEY", key); + REF_ASSERT_ISNT(i < 2); + return ((i > 1) ? 1 : 0); +} + +/** + * @brief Are 2 keys equal? + * + * To be equal the keys must have the same key data. + * + * @param key1 A SLH_DSA_KEY object + * @param key2 A SLH_DSA_KEY object + * @param selection to select public and/or private component comparison. + * @returns 1 if the keys are equal otherwise it returns 0. + */ +int ossl_slh_dsa_key_equal(const SLH_DSA_KEY *key1, const SLH_DSA_KEY *key2, + int selection) +{ + int ok = 1; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + if (key1->key_len != key2->key_len) + return 0; + ok = (memcmp(key1->pub, key2->pub, key1->key_len) == 0); + } + return ok; +} + +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; + return 1; + } + return 0; +} + +int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM params[]) +{ + size_t n, key_len, len = 0; + const OSSL_PARAM *param_pub; + void *p; + + if (key == NULL) + return 0; + n = key->params->n; + assert(n != 0); + /* Both the public and private key are composed of 2 elements of size n */ + key_len = 2 * n; + + 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; + key->key_len = key_len; /* This indicates the public key is present */ + return 1; + err: + key->key_len = 0; + return 0; +} diff --git a/crypto/slh_dsa/slh_dsa_key.h b/crypto/slh_dsa/slh_dsa_key.h new file mode 100644 index 00000000000..4247ebd7297 --- /dev/null +++ b/crypto/slh_dsa/slh_dsa_key.h @@ -0,0 +1,22 @@ +/* + * 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 +#include "internal/refcount.h" + +#define SLH_DSA_MAX_KEYLEN 32 * 2 /* 2 * n */ + +struct slh_dsa_key_st { + uint8_t pub[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; +}; diff --git a/crypto/slh_dsa/slh_dsa_local.h b/crypto/slh_dsa/slh_dsa_local.h new file mode 100644 index 00000000000..066318f5418 --- /dev/null +++ b/crypto/slh_dsa/slh_dsa_local.h @@ -0,0 +1,11 @@ +/* + * 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/slh_dsa.h" +#include "slh_params.h" diff --git a/crypto/slh_dsa/slh_params.c b/crypto/slh_dsa/slh_params.c new file mode 100644 index 00000000000..5034f8547cf --- /dev/null +++ b/crypto/slh_dsa/slh_params.c @@ -0,0 +1,36 @@ +/* + * 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 +#include +#include "slh_params.h" + +/* + * See FIPS 205 Section 11 Table 2 + * n h d hm a k m sc pk sig + */ +#define OSSL_SLH_DSA_128S 16, 63, 7, 9, 12, 14, 30, 1, 32, 7856 + +static const SLH_DSA_PARAMS slh_dsa_params[] = { + {"SLH-DSA-SHA2-128s", 0, OSSL_SLH_DSA_128S}, + {NULL}, +}; + +/** + * @brief A getter to convert an algorithm name into a SLH_DSA_PARAMS object + */ +const SLH_DSA_PARAMS *ossl_slh_dsa_params_get(const char *alg) +{ + const SLH_DSA_PARAMS *p; + + for (p = slh_dsa_params; p->alg != NULL; ++p) { + if (strcmp(p->alg, alg) == 0) + return p; + } + return NULL; +} diff --git a/crypto/slh_dsa/slh_params.h b/crypto/slh_dsa/slh_params.h new file mode 100644 index 00000000000..80a0ad570f9 --- /dev/null +++ b/crypto/slh_dsa/slh_params.h @@ -0,0 +1,31 @@ +/* + * 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 + +/* + * Refer to FIPS 205 Section 11 parameter sets. + * lgw has been omitted since it is 4 for all algorithms i.e log(16) + */ +typedef struct slh_dsa_params_st { + const char *alg; + int is_shake; + uint32_t n; /* Security parameter (Hash output size in bytes) (16, 24, 32) */ + uint32_t h; /* The total height of the tree (63, 64, 66, 68). #keypairs = 2^h */ + uint32_t d; /* The number of tree layers (7, 8, 17, 22) */ + uint32_t hm; /* The height (h') of each merkle tree. (h = hm * d ) */ + uint32_t a; /* Height of a FORS tree */ + uint32_t k; /* The number of FORS trees */ + uint32_t m; /* The size of H_MSG() output */ + uint32_t security_category; + uint32_t pk_len; + uint32_t sig_len; +} SLH_DSA_PARAMS; + +const SLH_DSA_PARAMS *ossl_slh_dsa_params_get(const char *alg); diff --git a/include/crypto/slh_dsa.h b/include/crypto/slh_dsa.h new file mode 100644 index 00000000000..5065aa444e9 --- /dev/null +++ b/include/crypto/slh_dsa.h @@ -0,0 +1,28 @@ +/* + * 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 + */ + +/* Internal SLH_DSA functions for other submodules, not for application use */ + +#ifndef OSSL_CRYPTO_SLH_DSA_H +# define OSSL_CRYPTO_SLH_DSA_H + +# pragma once +# include +# include +# include "crypto/types.h" + +SLH_DSA_KEY *ossl_slh_dsa_key_new(OSSL_LIB_CTX *libctx, const char *alg); +void ossl_slh_dsa_key_free(SLH_DSA_KEY *key); +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); + +#endif /* OSSL_CRYPTO_SLH_DSA_H */ diff --git a/include/crypto/types.h b/include/crypto/types.h index 43f04fd5eab..5dbcd900508 100644 --- a/include/crypto/types.h +++ b/include/crypto/types.h @@ -28,6 +28,9 @@ typedef struct dsa_st DSA; # ifndef OPENSSL_NO_EC typedef struct ecx_key_st ECX_KEY; # endif +# ifndef OPENSSL_NO_SLH_DSA +typedef struct slh_dsa_key_st SLH_DSA_KEY; +# endif typedef struct prov_skey_st PROV_SKEY; diff --git a/providers/defltprov.c b/providers/defltprov.c index 450a53a610c..6aea57219ea 100644 --- a/providers/defltprov.c +++ b/providers/defltprov.c @@ -577,6 +577,10 @@ static const OSSL_ALGORITHM deflt_keymgmt[] = { PROV_DESCS_SecP384r1MLKEM1024 }, # endif #endif +#ifndef OPENSSL_NO_SLH_DSA + { PROV_NAMES_SLH_DSA_SHA2_128S, "provider=default", ossl_slh_dsa_sha2_128s_keymgmt_functions, + PROV_DESCS_SLH_DSA_SHA2_128S }, +#endif /* OPENSSL_NO_SLH_DSA */ { NULL, NULL, NULL } }; diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h index 1dace9738d8..9f0cda93115 100644 --- a/providers/implementations/include/prov/implementations.h +++ b/providers/implementations/include/prov/implementations.h @@ -339,6 +339,9 @@ extern const OSSL_DISPATCH ossl_mlx_p256_kem_kmgmt_functions[]; extern const OSSL_DISPATCH ossl_mlx_p384_kem_kmgmt_functions[]; # endif #endif +#ifndef OPENSSL_NO_SLH_DSA +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_keymgmt_functions[]; +#endif /* OPENSSL_NO_SLH_DSA */ /* Key Exchange */ extern const OSSL_DISPATCH ossl_dh_keyexch_functions[]; diff --git a/providers/implementations/include/prov/names.h b/providers/implementations/include/prov/names.h index e20c7b6b9db..6ff25497f8a 100644 --- a/providers/implementations/include/prov/names.h +++ b/providers/implementations/include/prov/names.h @@ -409,3 +409,5 @@ #define PROV_DESCS_SecP256r1MLKEM768 "P-256+ML-KEM-768 TLS hybrid implementation" #define PROV_NAMES_SecP384r1MLKEM1024 "SecP384r1MLKEM1024" #define PROV_DESCS_SecP384r1MLKEM1024 "P-384+ML-KEM-1024 TLS hybrid implementation" +#define PROV_NAMES_SLH_DSA_SHA2_128S "SLH-DSA-SHA2-128s:2.16.840.1.101.3.4.3.20" +#define PROV_DESCS_SLH_DSA_SHA2_128S "OpenSSL SLH-DSA-SHA2-128s implementation" diff --git a/providers/implementations/keymgmt/build.info b/providers/implementations/keymgmt/build.info index 8b0b157378d..cd6c51a65a4 100644 --- a/providers/implementations/keymgmt/build.info +++ b/providers/implementations/keymgmt/build.info @@ -12,6 +12,7 @@ $TEMPLATE_GOAL=../../libtemplate.a $ML_DSA_GOAL=../../libdefault.a ../../libfips.a $ML_KEM_GOAL=../../libdefault.a ../../libfips.a $TLS_ML_KEM_HYBRID_GOAL=../../libdefault.a ../../libfips.a +$SLH_DSA_GOAL=../../libdefault.a IF[{- !$disabled{dh} -}] SOURCE[$DH_GOAL]=dh_kmgmt.c @@ -58,3 +59,7 @@ SOURCE[$TEMPLATE_GOAL]=template_kmgmt.c IF[{- !$disabled{'ml-dsa'} -}] SOURCE[$ML_DSA_GOAL]=ml_dsa_kmgmt.c ENDIF + +IF[{- !$disabled{'slh-dsa'} -}] + SOURCE[$SLH_DSA_GOAL]=slh_dsa_kmgmt.c +ENDIF diff --git a/providers/implementations/keymgmt/slh_dsa_kmgmt.c b/providers/implementations/keymgmt/slh_dsa_kmgmt.c new file mode 100644 index 00000000000..1d6bad199c8 --- /dev/null +++ b/providers/implementations/keymgmt/slh_dsa_kmgmt.c @@ -0,0 +1,105 @@ +/* + * 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 +#include +#include +#include "crypto/slh_dsa.h" +#include "internal/param_build_set.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" + +static OSSL_FUNC_keymgmt_free_fn slh_dsa_free_key; +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 void *slh_dsa_new_key(void *provctx, const char *alg) +{ + if (!ossl_prov_is_running()) + return 0; + + return ossl_slh_dsa_key_new(PROV_LIBCTX_OF(provctx), alg); +} + +static void slh_dsa_free_key(void *keydata) +{ + ossl_slh_dsa_key_free((SLH_DSA_KEY *)keydata); +} + +static int slh_dsa_has(const void *keydata, int selection) +{ + const SLH_DSA_KEY *key = keydata; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0) + return 1; /* the selection is not missing */ + + return ossl_slh_dsa_key_has(key, selection); +} + +static int slh_dsa_match(const void *keydata1, const void *keydata2, int selection) +{ + const SLH_DSA_KEY *key1 = keydata1; + const SLH_DSA_KEY *key2 = keydata2; + + if (!ossl_prov_is_running()) + return 0; + if (key1 == NULL || key2 == NULL) + return 0; + return ossl_slh_dsa_key_equal(key1, key2, selection); +} + +static int slh_dsa_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + SLH_DSA_KEY *key = keydata; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0) + return 0; + + return ossl_slh_dsa_key_fromdata(key, params); +} + +static const OSSL_PARAM slh_dsa_key_types[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *slh_dsa_imexport_types(int selection) +{ + if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0) + return NULL; + return slh_dsa_key_types; +} + +#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); \ +} \ +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 }, \ + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))slh_dsa_has }, \ + { 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_DISPATCH_END \ +} + +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-128s", sha2_128s); diff --git a/test/build.info b/test/build.info index e1fa8e928c4..8cab6deb15b 100644 --- a/test/build.info +++ b/test/build.info @@ -273,6 +273,13 @@ IF[{- !$disabled{tests} -}] INCLUDE[evp_pkey_dhkem_test]=../include ../apps/include DEPEND[evp_pkey_dhkem_test]=../libcrypto.a libtestutil.a + IF[{- !$disabled{'slh-dsa'} -}] + PROGRAMS{noinst}=slh_dsa_test + SOURCE[slh_dsa_test]=slh_dsa_test.c + INCLUDE[slh_dsa_test]=../include ../apps/include + DEPEND[slh_dsa_test]=../libcrypto.a libtestutil.a + ENDIF + IF[{- !$disabled{'deprecated-3.0'} -}] PROGRAMS{noinst}=igetest bftest casttest diff --git a/test/recipes/30-test_slh_dsa.t b/test/recipes/30-test_slh_dsa.t new file mode 100644 index 00000000000..d8179c66dd6 --- /dev/null +++ b/test/recipes/30-test_slh_dsa.t @@ -0,0 +1,25 @@ +#! /usr/bin/env perl +# 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 + +use strict; +use warnings; + +use OpenSSL::Test qw(:DEFAULT srctop_dir bldtop_dir); +use OpenSSL::Test::Utils; + +BEGIN { + setup("test_slh_dsa"); +} + +use lib srctop_dir('Configurations'); +use lib bldtop_dir('.'); + +plan skip_all => 'SLH-DSA is not supported in this build' if disabled('slh-dsa'); +plan tests => 1; + +ok(run(test(["slh_dsa_test"])), "running slh_dsa_test"); diff --git a/test/slh_dsa.inc b/test/slh_dsa.inc new file mode 100644 index 00000000000..fd2c1c3a06f --- /dev/null +++ b/test/slh_dsa.inc @@ -0,0 +1,57 @@ +/* + * 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 + */ + +typedef struct SLH_DSA_ACVP_TEST_DATA_st { + const char *alg; + const unsigned char *pub; + size_t pub_len; + const unsigned char *priv; + size_t priv_len; + const unsigned char *msg; + size_t msg_len; + /* sha256 digest of the signature - this reduces the footprint this file */ + const unsigned char *sig_digest; + size_t sig_digest_len; +} SLH_DSA_ACVP_TEST_DATA; + +#define SLH_DSA_ACVP_ITEM(name, alg) { \ + alg, \ + name##_pub, sizeof(name##_pub), \ + name##_priv, sizeof(name##_priv), \ + name##_msg, sizeof(name##_msg), \ + name##_sig_digest, sizeof(name##_sig_digest) } + +/* + * Test vectors from + * usnistgov/ACVP-Server/refs/heads/master/gen-val/json-files/SLH-DSA-sigGen-FIPS205/internalProjection.json + * + * Note that the test vectors store the private & public components in one field + * (e.g sk) + * The following data separates these fields. + */ +static const uint8_t slh_dsa_sha2_128s_0_priv[] = { + 0x62, 0xb1, 0x97, 0x3a, 0x4d, 0xe0, 0x96, 0x3d, 0x74, 0xc1, 0xcb, 0x30, 0xfc, 0x8f, 0x56, 0x75, + 0xcf, 0xc8, 0x48, 0x80, 0xe4, 0xf0, 0xe1, 0xb4, 0x46, 0xb4, 0xf5, 0xd1, 0x3b, 0x2d, 0x31, 0xcc, +}; +static const uint8_t slh_dsa_sha2_128s_0_pub[] = { + 0xcb, 0x23, 0xeb, 0x45, 0x52, 0x9e, 0x00, 0xd5, 0xf5, 0xe9, 0x51, 0x50, 0x7a, 0x9b, 0x90, 0xe9, + 0x8b, 0x6e, 0x7a, 0x28, 0x4b, 0xa3, 0xf6, 0x3a, 0x69, 0xe6, 0x9a, 0x78, 0x90, 0x83, 0xbe, 0xf6, +}; +static const uint8_t slh_dsa_sha2_128s_0_msg[] = { + 0x9D, 0xDF +}; +static const uint8_t slh_dsa_sha2_128s_0_sig_digest[] = { + 0xc7, 0xdf, 0xf0, 0xed, 0x25, 0x38, 0x49, 0xef, 0x51, 0x1e, 0x90, 0xbe, 0x0e, 0x2e, 0xb7, 0x71, + 0x65, 0x98, 0x91, 0x23, 0x17, 0x52, 0x9a, 0x61, 0xda, 0xe4, 0x32, 0x9b, 0xf1, 0x49, 0xef, 0x8b, +}; + +/* We can only use the hss tests that have a single level here */ +static SLH_DSA_ACVP_TEST_DATA slh_dsa_testdata[] = { + SLH_DSA_ACVP_ITEM(slh_dsa_sha2_128s_0, "SLH-DSA-SHA2-128s"), +}; diff --git a/test/slh_dsa_test.c b/test/slh_dsa_test.c new file mode 100644 index 00000000000..a7f5bffffeb --- /dev/null +++ b/test/slh_dsa_test.c @@ -0,0 +1,123 @@ +/* + * 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 +#include +#include "crypto/slh_dsa.h" +#include "internal/nelem.h" +#include "testutil.h" +#include "slh_dsa.inc" + +static OSSL_LIB_CTX *libctx = NULL; + +static EVP_PKEY *slh_dsa_pubkey_from_data(const char *alg, + const unsigned char *data, size_t datalen) +{ + int ret; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *key = NULL; + OSSL_PARAM params[2]; + + params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, + (unsigned char *)data, datalen); + params[1] = OSSL_PARAM_construct_end(); + ret = TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, alg, NULL)) + && TEST_int_eq(EVP_PKEY_fromdata_init(ctx), 1) + && (EVP_PKEY_fromdata(ctx, &key, EVP_PKEY_PUBLIC_KEY, params) == 1); + if (ret == 0) { + EVP_PKEY_free(key); + key = NULL; + } + EVP_PKEY_CTX_free(ctx); + return key; +} + +static int slh_dsa_bad_pub_len_test(void) +{ + int ret = 0; + SLH_DSA_ACVP_TEST_DATA *td = &slh_dsa_testdata[0]; + EVP_PKEY *pkey = NULL; + size_t pub_len = 0; + unsigned char pubdata[64 + 1]; + + if (!TEST_size_t_le(td->pub_len, sizeof(pubdata))) + goto end; + + OPENSSL_cleanse(pubdata, sizeof(pubdata)); + memcpy(pubdata, td->pub, td->pub_len); + + if (!TEST_ptr_null(pkey = slh_dsa_pubkey_from_data(td->alg, pubdata, + td->pub_len - 1)) + || !TEST_ptr_null(pkey = slh_dsa_pubkey_from_data(td->alg, pubdata, + td->pub_len + 1))) + goto end; + + ret = 1; +end: + if (ret == 0) + TEST_note("Incorrectly accepted public key of length %u (expected %u)", + (unsigned)pub_len, (unsigned)td->pub_len); + EVP_PKEY_free(pkey); + return ret == 1; +} + +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]; +#ifndef OPENSSL_NO_EC + EVP_PKEY *eckey = NULL; +#endif + + if (!TEST_ptr(key[0] = slh_dsa_pubkey_from_data(td1->alg, td1->pub, td1->pub_len)) + || !TEST_ptr(key[1] = slh_dsa_pubkey_from_data(td1->alg, td1->pub, td1->pub_len))) + goto end; + + ret = TEST_int_eq(EVP_PKEY_eq(key[0], key[1]), 1); + if (ret == 0) + goto end; + +#ifndef OPENSSL_NO_EC + if (!TEST_ptr(eckey = EVP_PKEY_Q_keygen(libctx, NULL, "EC", "P-256"))) + goto end; + ret = TEST_int_ne(EVP_PKEY_eq(key[0], eckey), 1); + EVP_PKEY_free(eckey); +#endif +end: + EVP_PKEY_free(key[1]); + EVP_PKEY_free(key[0]); + return ret; +} + +static int slh_dsa_key_validate_test(void) +{ + int ret = 0; + SLH_DSA_ACVP_TEST_DATA *td = &slh_dsa_testdata[0]; + EVP_PKEY_CTX *vctx = NULL; + EVP_PKEY *key = NULL; + + if (!TEST_ptr(key = slh_dsa_pubkey_from_data(td->alg, td->pub, td->pub_len))) + return 0; + if (!TEST_ptr(vctx = EVP_PKEY_CTX_new_from_pkey(libctx, key, NULL))) + goto end; + ret = TEST_int_eq(EVP_PKEY_check(vctx), 1); + EVP_PKEY_CTX_free(vctx); +end: + EVP_PKEY_free(key); + return ret; +} + +int setup_tests(void) +{ + ADD_TEST(slh_dsa_bad_pub_len_test); + ADD_TEST(slh_dsa_key_validate_test); + ADD_TEST(slh_dsa_key_eq_test); + return 1; +} -- 2.47.2