+2025-09-09 Niels Möller <nisse@lysator.liu.se>
+
+ * 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 <nisse@lysator.liu.se>
* slh-dsa-shake-128s.c (_slh_dsa_128s_params): Renamed, from...
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 \
--- /dev/null
+/* 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) },
+ };
--- /dev/null
+/* 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) },
+ };
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;
};
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 */
#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)
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,
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);
}
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);
}
#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)
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,
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);
}
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);
}
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);
_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;
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);
_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;