From: Niels Möller Date: Tue, 9 Sep 2025 18:29:52 +0000 (+0200) Subject: New files slh-dsa-128s.c and slh-dsa-128f.c. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c2a463703eb93422d6dadc3ac508b2f6498ca144;p=thirdparty%2Fnettle.git New files slh-dsa-128s.c and slh-dsa-128f.c. Move params structs and parse_digest functions, since they are the same for shake and sha2. --- diff --git a/ChangeLog b/ChangeLog index b7a7f193..30cbaccc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2025-09-09 Niels Möller + + * slh-dsa-internal.h (slh_parse_digest_func): New typedef. + (struct slh_dsa_params): New function pointer parse_digest. + + * slh-dsa.c (_slh_dsa_sign): Delete tree_idx and leaf_idx + arguments. Instead, call params->parse_digest to get them from the + digest. Introduce c99 mid-block declarations, for convenience. + (_slh_dsa_verify): Likewise. + + * slh-dsa-128s.c: New file. + (_slh_dsa_128s_params): Params struct moved here. + (parse_digest): Corresponding function to extract tree and leaf + indices from digest. + * slh-dsa-shake-128s.c (_slh_dsa_128s_params, parse_digest): ... + old location. + + * slh-dsa-128f.c: New file. + (_slh_dsa_128f_params): Params struct moved here. + (parse_digest): Corresponding function to extract tree and leaf + indices from digest. + * slh-dsa-shake-128f.c (_slh_dsa_128f_params, parse_digest): ... + old location. + + * Makefile.in (nettle_SOURCES): Add slh-dsa-128s.c slh-dsa-128f.c. + 2025-09-08 Niels Möller * slh-dsa-shake-128s.c (_slh_dsa_128s_params): Renamed, from... diff --git a/Makefile.in b/Makefile.in index 88ecf211..e3396e33 100644 --- a/Makefile.in +++ b/Makefile.in @@ -177,7 +177,8 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt-table.c \ xts.c xts-aes128.c xts-aes256.c \ drbg-ctr-aes256.c \ slh-fors.c slh-merkle.c slh-shake.c slh-wots.c slh-xmss.c \ - slh-dsa.c slh-dsa-shake-128s.c slh-dsa-shake-128f.c + slh-dsa.c slh-dsa-128s.c slh-dsa-128f.c \ + slh-dsa-shake-128s.c slh-dsa-shake-128f.c hogweed_SOURCES = sexp.c sexp-format.c \ sexp-transport.c sexp-transport-format.c \ diff --git a/slh-dsa-128f.c b/slh-dsa-128f.c new file mode 100644 index 00000000..47b87fed --- /dev/null +++ b/slh-dsa-128f.c @@ -0,0 +1,83 @@ +/* slh-dsa-128f.c + + SLH-DSA (FIPS 205) signatures. + + Copyright (C) 2025 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "slh-dsa.h" +#include "slh-dsa-internal.h" + +#define SLH_DSA_D 22 +#define XMSS_H 3 + +/* Use k Merkle trees, each of size 2^a. Signs messages of size + k * a = 198 bits or 25 octets (with 2 left-over bits). */ +#define FORS_A 6 +#define FORS_K 33 +#define FORS_MSG_SIZE 25 + +static void +parse_digest (const uint8_t *digest, uint64_t *tree_idx, unsigned *leaf_idx) +{ + uint64_t x; + unsigned i; + + /* Split digest as + +----+------+-----+ + | md | tree | leaf| + +----+------+-----+ + 25 8 1 + + The first 25 octets are the digest signed with fors (and not + processed by this function), the next 8 octets represent 63 bits + selecting the tree, the last octet represent 3 bits selecting + the key in that tree. + + Left over high bits are discarded. + */ + x = digest[0] & 0x7f; /* Discard high-most bit of 64 */ + for (i = 1; i < 8; i++) + x = (x << 8) + digest[i]; + *tree_idx = x; + /* Discard 5 high-most bits */ + *leaf_idx = digest[8] & 7; +} + +const struct slh_dsa_params +_slh_dsa_128f_params = + { + parse_digest, + { SLH_DSA_D, XMSS_H, XMSS_SIGNATURE_SIZE (XMSS_H) }, + { FORS_A, FORS_K, FORS_MSG_SIZE, FORS_SIGNATURE_SIZE (FORS_A, FORS_K) }, + }; diff --git a/slh-dsa-128s.c b/slh-dsa-128s.c new file mode 100644 index 00000000..cc764f74 --- /dev/null +++ b/slh-dsa-128s.c @@ -0,0 +1,83 @@ +/* slh-dsa-128s.c + + SLH-DSA (FIPS 205) signatures. + + Copyright (C) 2025 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "slh-dsa.h" +#include "slh-dsa-internal.h" + +#define SLH_DSA_D 7 +#define XMSS_H 9 + +/* Use k Merkle trees, each of size 2^a. Signs messages of size + k * a = 168 bits or 21 octets. */ +#define FORS_A 12 +#define FORS_K 14 +#define FORS_MSG_SIZE 21 + +static void +parse_digest (const uint8_t *digest, uint64_t *tree_idx, unsigned *leaf_idx) +{ + uint64_t x; + unsigned i; + + /* Split digest as + +----+------+-----+ + | md | tree | leaf| + +----+------+-----+ + 21 7 2 + + The first 21 octets are the digest signed with fors (and skipped by + this function), the next 7 octets represent 54 bits selecting the + tree, the last 2 octets represent 9 bits selecting the key in that + tree. + + Left over high bits are discarded. + */ + x = digest[0] & 0x3f; /* Discard 2 high-most bits of 56 */ + for (i = 1; i < 7; i++) + x = (x << 8) + digest[i]; + *tree_idx = x; + /* Discard 7 high-most bits of 16 */ + *leaf_idx = ((digest[7] & 1) << 8) + digest[8]; +} + +const struct slh_dsa_params +_slh_dsa_128s_params = + { + parse_digest, + { SLH_DSA_D, XMSS_H, XMSS_SIGNATURE_SIZE (XMSS_H) }, + { FORS_A, FORS_K, FORS_MSG_SIZE, FORS_SIGNATURE_SIZE (FORS_A, FORS_K) }, + }; diff --git a/slh-dsa-internal.h b/slh-dsa-internal.h index 516e068e..9b413197 100644 --- a/slh-dsa-internal.h +++ b/slh-dsa-internal.h @@ -111,8 +111,11 @@ struct slh_fors_params unsigned short signature_size; }; +typedef void slh_parse_digest_func (const uint8_t *digest, uint64_t *tree_idx, unsigned *leaf_idx); + struct slh_dsa_params { + slh_parse_digest_func *parse_digest; struct slh_xmss_params xmss; struct slh_fors_params fors; }; @@ -218,13 +221,10 @@ _slh_dsa_digest (const uint8_t *randomizer, const uint8_t *pub, void _slh_dsa_sign (const struct slh_dsa_params *params, const uint8_t *pub, const uint8_t *priv, - const uint8_t *digest, - uint64_t tree_idx, unsigned leaf_idx, - uint8_t *signature); + const uint8_t *digest, uint8_t *signature); int _slh_dsa_verify (const struct slh_dsa_params *params, const uint8_t *pub, - const uint8_t *digest, uint64_t tree_idx, unsigned leaf_idx, - const uint8_t *signature); + const uint8_t *digest, const uint8_t *signature); #endif /* NETTLE_SLH_DSA_INTERNAL_H_INCLUDED */ diff --git a/slh-dsa-shake-128f.c b/slh-dsa-shake-128f.c index 100c04a2..7ddcf1ca 100644 --- a/slh-dsa-shake-128f.c +++ b/slh-dsa-shake-128f.c @@ -40,22 +40,8 @@ #define SLH_DSA_M 34 -#define SLH_DSA_D 22 #define XMSS_H 3 -/* Use k Merkle trees, each of size 2^a. Signs messages of size - k * a = 198 bits or 25 octets (with 2 left-over bits). */ -#define FORS_A 6 -#define FORS_K 33 -#define FORS_MSG_SIZE 25 - -const struct slh_dsa_params -_slh_dsa_128f_params = - { - { SLH_DSA_D, XMSS_H, XMSS_SIGNATURE_SIZE (XMSS_H) }, - { FORS_A, FORS_K, FORS_MSG_SIZE, FORS_SIGNATURE_SIZE (FORS_A, FORS_K) }, - }; - void slh_dsa_shake_128f_root (const uint8_t *public_seed, const uint8_t *private_seed, uint8_t *root) @@ -73,33 +59,6 @@ slh_dsa_shake_128f_generate_keypair (uint8_t *pub, uint8_t *priv, slh_dsa_shake_128f_root (pub, priv, pub + SLH_DSA_128_SEED_SIZE); } -static void -parse_digest (const uint8_t *digest, uint64_t *tree_idx, unsigned *leaf_idx) -{ - uint64_t x; - unsigned i; - - /* Split digest as - +----+------+-----+ - | md | tree | leaf| - +----+------+-----+ - 25 8 1 - - The first 25 octets are the digest signed with fors (and not - processed by this function), the next 8 octets represent 63 bits - selecting the tree, the last octet represent 3 bits selecting - the key in that tree. - - Left over high bits are discarded. - */ - x = digest[0] & 0x7f; /* Discard high-most bit of 64 */ - for (i = 1; i < 8; i++) - x = (x << 8) + digest[i]; - *tree_idx = x; - /* Discard 5 high-most bits */ - *leaf_idx = digest[8] & 7; -} - /* Only the "pure" and deterministic variant. */ void slh_dsa_shake_128f_sign (const uint8_t *pub, const uint8_t *priv, @@ -107,14 +66,10 @@ slh_dsa_shake_128f_sign (const uint8_t *pub, const uint8_t *priv, uint8_t *signature) { uint8_t digest[SLH_DSA_M]; - uint64_t tree_idx; - unsigned leaf_idx; _slh_dsa_randomizer (pub, priv + _SLH_DSA_128_SIZE, length, msg, signature); _slh_dsa_digest (signature, pub, length, msg, SLH_DSA_M, digest); - parse_digest (digest + FORS_MSG_SIZE, &tree_idx, &leaf_idx); - - _slh_dsa_sign (&_slh_dsa_128f_params, pub, priv, digest, tree_idx, leaf_idx, + _slh_dsa_sign (&_slh_dsa_128f_params, pub, priv, digest, signature + _SLH_DSA_128_SIZE); } @@ -124,11 +79,8 @@ slh_dsa_shake_128f_verify (const uint8_t *pub, const uint8_t *signature) { uint8_t digest[SLH_DSA_M]; - uint64_t tree_idx; - unsigned leaf_idx; _slh_dsa_digest (signature, pub, length, msg, SLH_DSA_M,digest); - parse_digest (digest + FORS_MSG_SIZE, &tree_idx, &leaf_idx); - return _slh_dsa_verify (&_slh_dsa_128f_params, pub, digest, tree_idx, leaf_idx, + return _slh_dsa_verify (&_slh_dsa_128f_params, pub, digest, signature + _SLH_DSA_128_SIZE); } diff --git a/slh-dsa-shake-128s.c b/slh-dsa-shake-128s.c index 9fdea141..8fa48839 100644 --- a/slh-dsa-shake-128s.c +++ b/slh-dsa-shake-128s.c @@ -40,22 +40,8 @@ #define SLH_DSA_M 30 -#define SLH_DSA_D 7 #define XMSS_H 9 -/* Use k Merkle trees, each of size 2^a. Signs messages of size - k * a = 168 bits or 21 octets. */ -#define FORS_A 12 -#define FORS_K 14 -#define FORS_MSG_SIZE 21 - -const struct slh_dsa_params -_slh_dsa_128s_params = - { - { SLH_DSA_D, XMSS_H, XMSS_SIGNATURE_SIZE (XMSS_H) }, - { FORS_A, FORS_K, FORS_MSG_SIZE, FORS_SIGNATURE_SIZE (FORS_A, FORS_K) }, - }; - void slh_dsa_shake_128s_root (const uint8_t *public_seed, const uint8_t *private_seed, uint8_t *root) @@ -73,33 +59,6 @@ slh_dsa_shake_128s_generate_keypair (uint8_t *pub, uint8_t *priv, slh_dsa_shake_128s_root (pub, priv, pub + SLH_DSA_128_SEED_SIZE); } -static void -parse_digest (const uint8_t *digest, uint64_t *tree_idx, unsigned *leaf_idx) -{ - uint64_t x; - unsigned i; - - /* Split digest as - +----+------+-----+ - | md | tree | leaf| - +----+------+-----+ - 21 7 2 - - The first 21 octets are the digest signed with fors (and not - processed by this function), the next 7 octets represent 54 bits - selecting the tree, the last 2 octets represent 9 bits selecting - the key in that tree. - - Left over high bits are discarded. - */ - x = digest[0] & 0x3f; /* Discard 2 high-most bits of 56 */ - for (i = 1; i < 7; i++) - x = (x << 8) + digest[i]; - *tree_idx = x; - /* Discard 7 high-most bits of 16 */ - *leaf_idx = ((digest[7] & 1) << 8) + digest[8]; -} - /* Only the "pure" and deterministic variant. */ void slh_dsa_shake_128s_sign (const uint8_t *pub, const uint8_t *priv, @@ -107,14 +66,10 @@ slh_dsa_shake_128s_sign (const uint8_t *pub, const uint8_t *priv, uint8_t *signature) { uint8_t digest[SLH_DSA_M]; - uint64_t tree_idx; - unsigned leaf_idx; _slh_dsa_randomizer (pub, priv + _SLH_DSA_128_SIZE, length, msg, signature); _slh_dsa_digest (signature, pub, length, msg, SLH_DSA_M, digest); - parse_digest (digest + FORS_MSG_SIZE, &tree_idx, &leaf_idx); - - _slh_dsa_sign (&_slh_dsa_128s_params, pub, priv, digest, tree_idx, leaf_idx, + _slh_dsa_sign (&_slh_dsa_128s_params, pub, priv, digest, signature + _SLH_DSA_128_SIZE); } @@ -124,11 +79,8 @@ slh_dsa_shake_128s_verify (const uint8_t *pub, const uint8_t *signature) { uint8_t digest[SLH_DSA_M]; - uint64_t tree_idx; - unsigned leaf_idx; _slh_dsa_digest (signature, pub, length, msg, SLH_DSA_M,digest); - parse_digest (digest + FORS_MSG_SIZE, &tree_idx, &leaf_idx); - return _slh_dsa_verify (&_slh_dsa_128s_params, pub, digest, tree_idx, leaf_idx, + return _slh_dsa_verify (&_slh_dsa_128s_params, pub, digest, signature + _SLH_DSA_128_SIZE); } diff --git a/slh-dsa.c b/slh-dsa.c index d5b0afab..cec779d3 100644 --- a/slh-dsa.c +++ b/slh-dsa.c @@ -79,19 +79,21 @@ _slh_dsa_digest (const uint8_t *randomizer, const uint8_t *pub, void _slh_dsa_sign (const struct slh_dsa_params *params, const uint8_t *pub, const uint8_t *priv, - const uint8_t *digest, - uint64_t tree_idx, unsigned leaf_idx, - uint8_t *signature) + const uint8_t *digest, uint8_t *signature) { - uint8_t root[_SLH_DSA_128_SIZE]; - int i; + uint64_t tree_idx; + unsigned leaf_idx; + + params->parse_digest (digest + params->fors.msg_size, &tree_idx, &leaf_idx); struct sha3_ctx tree_ctx; - struct slh_merkle_ctx_secret merkle_ctx = + const struct slh_merkle_ctx_secret merkle_ctx = { { &tree_ctx, leaf_idx }, priv, }; + uint8_t root[_SLH_DSA_128_SIZE]; + _slh_shake_init (&tree_ctx, pub, 0, tree_idx); _fors_sign (&merkle_ctx, ¶ms->fors, digest, signature, root); @@ -99,7 +101,7 @@ _slh_dsa_sign (const struct slh_dsa_params *params, _xmss_sign (&merkle_ctx, params->xmss.h, leaf_idx, root, signature, root); - for (i = 1; i < params->xmss.d; i++) + for (unsigned i = 1; i < params->xmss.d; i++) { signature += params->xmss.signature_size; @@ -115,16 +117,19 @@ _slh_dsa_sign (const struct slh_dsa_params *params, int _slh_dsa_verify (const struct slh_dsa_params *params, const uint8_t *pub, - const uint8_t *digest, uint64_t tree_idx, unsigned leaf_idx, - const uint8_t *signature) + const uint8_t *digest, const uint8_t *signature) { - uint8_t root[_SLH_DSA_128_SIZE]; - int i; + uint64_t tree_idx; + unsigned leaf_idx; + + params->parse_digest (digest + params->fors.msg_size, &tree_idx, &leaf_idx); struct sha3_ctx tree_ctx; - struct slh_merkle_ctx_public merkle_ctx = + const struct slh_merkle_ctx_public merkle_ctx = { &tree_ctx, leaf_idx }; + uint8_t root[_SLH_DSA_128_SIZE]; + _slh_shake_init (&tree_ctx, pub, 0, tree_idx); _fors_verify (&merkle_ctx, ¶ms->fors, digest, signature, root); @@ -132,7 +137,7 @@ _slh_dsa_verify (const struct slh_dsa_params *params, const uint8_t *pub, _xmss_verify (&merkle_ctx, params->xmss.h, leaf_idx, root, signature, root); - for (i = 1; i < params->xmss.d; i++) + for (unsigned i = 1; i < params->xmss.d; i++) { signature += params->xmss.signature_size;