+2025-02-12 Niels Möller <nisse@lysator.liu.se>
+
+ * slh-shake.c (_slh_shake_init, _slh_shake): New file, new
+ functions, moved from slh-wots.c. Update callers.
+ * slh-merkle.c (_merkle_root, _merkle_sign, _merkle_verify): New
+ file, new functions.
+ * slh-xmss.c (xmss_leaf, xmss_node, _xmss_gen, _xmss_sign)
+ (_xmss_verify): New file, new functions.
+ * slh-dsa-internal.h (struct slh_merkle_ctx_public)
+ (struct slh_merkle_ctx_secret): New structs.
+ (merkle_leaf_hash_func, merkle_node_hash_func): New typedefs.
+ * Makefile.in (nettle_SOURCES): Add slh-merkle.c, slh-shake.c and
+ slh-xmss.c.
+ * testsuite/slh-dsa-test.c: Add xmss and merkle tests.
+
2025-02-11 Niels Möller <nisse@lysator.liu.se>
* slh-wots.c (_wots_gen, _wots_sign, _wots_verify): New file, new functions.
yarrow256.c yarrow_key_event.c \
xts.c xts-aes128.c xts-aes256.c \
drbg-ctr-aes256.c \
- slh-wots.c
+ slh-merkle.c slh-shake.c slh-wots.c slh-xmss.c
hogweed_SOURCES = sexp.c sexp-format.c \
sexp-transport.c sexp-transport-format.c \
#include <stdint.h>
/* Name mangling */
+#define _slh_shake_init _nettle_slh_shake_init
+#define _slh_shake _nettle_slh_shake
#define _wots_gen _nettle_wots_gen
#define _wots_sign _nettle_wots_sign
#define _wots_verify _nettle_wots_verify
+#define _merkle_root _nettle_merkle_root
+#define _merkle_sign _nettle_merkle_sign
+#define _merkle_verify _nettle_merkle_verify
+#define _xmss_gen _nettle_xmss_gen
+#define _xmss_sign _nettle_xmss_sign
+#define _xmss_verify _nettle_xmss_verify
/* Size of a single hash, including the seed and prf parameters */
#define _SLH_DSA_128_SIZE 16
+#define SLH_DSA_D 7
+
/* Fields always big-endian */
struct slh_address_tree
{
SLH_FORS_PRF = 6,
};
+struct slh_merkle_ctx_public
+{
+ const uint8_t *seed;
+ struct slh_address_tree at;
+ unsigned keypair; /* Used only by fors_leaf and fors_node. */
+};
+
+struct slh_merkle_ctx_secret
+{
+ struct slh_merkle_ctx_public pub;
+ const uint8_t *secret_seed;
+};
+
+struct sha3_256_ctx;
+void
+_slh_shake_init (struct sha3_256_ctx *ctx, const uint8_t *public_seed,
+ const struct slh_address_tree *at, const struct slh_address_hash *ah);
+void
+_slh_shake (const uint8_t *public_seed,
+ const struct slh_address_tree *at, const struct slh_address_hash *ah,
+ const uint8_t *secret, uint8_t *out);
+
#define _WOTS_SIGNATURE_LENGTH 35
/* 560 bytes */
#define WOTS_SIGNATURE_SIZE (_WOTS_SIGNATURE_LENGTH*_SLH_DSA_128_SIZE)
_wots_verify (const uint8_t *public_seed, const struct slh_address_tree *at,
unsigned keypair, const uint8_t *msg, const uint8_t *signature, uint8_t *pub);
+/* Merkle tree functions. Could be generalized for other merkle tree
+ applications, by using const void* for the ctx argument. */
+typedef void merkle_leaf_hash_func (const struct slh_merkle_ctx_secret *ctx, unsigned index, uint8_t *out);
+typedef void merkle_node_hash_func (const struct slh_merkle_ctx_public *ctx, unsigned height, unsigned index,
+ const uint8_t *left, const uint8_t *right, uint8_t *out);
+
+void
+_merkle_root (const struct slh_merkle_ctx_secret *ctx,
+ merkle_leaf_hash_func *leaf_hash, merkle_node_hash_func *node_hash,
+ unsigned height, unsigned start, uint8_t *root,
+ /* Must have space for (height + 1) node hashes */
+ uint8_t *stack);
+
+void
+_merkle_sign (const struct slh_merkle_ctx_secret *ctx,
+ merkle_leaf_hash_func *leaf_hash, merkle_node_hash_func *node_hash,
+ unsigned height, unsigned idx, uint8_t *signature);
+
+/* The hash argument is both input (leaf hash to be verified) and output (resulting root hash). */
+void
+_merkle_verify (const struct slh_merkle_ctx_public *ctx, merkle_node_hash_func *node_hash,
+ unsigned height, unsigned idx, const uint8_t *signature, uint8_t *hash);
+
+#define XMSS_H 9
+/* Just the auth path, excluding the wots signature, 144 bytes. */
+#define XMSS_AUTH_SIZE (XMSS_H * _SLH_DSA_128_SIZE)
+#define XMSS_SIGNATURE_SIZE (WOTS_SIGNATURE_SIZE + XMSS_AUTH_SIZE)
+
+void
+_xmss_gen (const uint8_t *public_seed, const uint8_t *secret_seed,
+ uint8_t *root);
+
+/* Signs using wots, then signs wots public key using xmss. Also
+ returns the xmss public key (i.e., root hash).*/
+void
+_xmss_sign (const struct slh_merkle_ctx_secret *ctx,
+ unsigned idx, const uint8_t *msg, uint8_t *signature, uint8_t *pub);
+
+void
+_xmss_verify (const struct slh_merkle_ctx_public *ctx,
+ unsigned idx, const uint8_t *msg, const uint8_t *signature, uint8_t *pub);
+
#endif /* NETTLE_SLH_DSA_INTERNAL_H_INCLUDED */
--- /dev/null
+/* slh-merkle.c
+
+ 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 <assert.h>
+
+#include "slh-dsa-internal.h"
+
+/* Computes root hash of a tree.
+ Example for height == 2, 4 entries:
+
+ i = 0 ==> stack: [0], i = 1
+ i = 1 ==> stack: [0, 1] ==> [0|1], i = 2
+ i = 2 ==> stack: [0|1, 2], i = 3
+ i = 3 ==> stack: [0|1, 2, 3] ==> [0|1, 2|3] ==> [0|1|2|3], i = 4
+
+ The size of the stack equals popcount(i)
+*/
+void
+_merkle_root (const struct slh_merkle_ctx_secret *ctx,
+ merkle_leaf_hash_func *leaf_hash, merkle_node_hash_func *node_hash,
+ unsigned height, unsigned start, uint8_t *root,
+ /* Must have space for (height + 1) node hashes */
+ uint8_t *stack)
+{
+ unsigned stack_size = 0;
+ unsigned i;
+ assert (height > 0);
+ assert ( (start & ((1<<height) - 1)) == 0);
+
+ for (i = 0; i < (1<<height); i++)
+ {
+ /* Leaf index. */
+ unsigned idx = start + i;
+ unsigned h;
+ assert (stack_size <= height);
+
+ leaf_hash (ctx, idx, stack + stack_size++ * _SLH_DSA_128_SIZE);
+
+ for (h = 1; (idx&1); h++)
+ {
+ assert (stack_size >= 2);
+ idx >>= 1;
+ stack_size--;
+ if (h == height)
+ {
+ assert (stack_size == 1);
+ node_hash (&ctx->pub, h, idx,
+ stack + (stack_size - 1) * _SLH_DSA_128_SIZE,
+ stack + stack_size * _SLH_DSA_128_SIZE,
+ root);
+ return;
+ }
+ node_hash (&ctx->pub, h, idx,
+ stack + (stack_size - 1) * _SLH_DSA_128_SIZE,
+ stack + stack_size * _SLH_DSA_128_SIZE,
+ stack + (stack_size - 1)* _SLH_DSA_128_SIZE);
+ }
+ }
+}
+
+void
+_merkle_sign (const struct slh_merkle_ctx_secret *ctx,
+ merkle_leaf_hash_func *leaf_hash, merkle_node_hash_func *node_hash,
+ unsigned height, unsigned idx, uint8_t *signature)
+{
+ unsigned h;
+ /* By generating the path from the end, we can use the output area
+ as the temporary stack. */
+ for (h = height; --h > 0;)
+ _merkle_root (ctx, leaf_hash, node_hash, h, (idx & -(1 << h)) ^ (1 << h),
+ signature + h*_SLH_DSA_128_SIZE, signature);
+
+ leaf_hash (ctx, idx ^ 1, signature);
+}
+
+void
+_merkle_verify (const struct slh_merkle_ctx_public *ctx, merkle_node_hash_func *node_hash,
+ unsigned height, unsigned idx, const uint8_t *signature, uint8_t *hash)
+{
+ unsigned h;
+
+ for (h = 1; h <= height; h++, signature += _SLH_DSA_128_SIZE)
+ {
+ unsigned right = idx & 1;
+ idx >>= 1;
+ if (right)
+ node_hash (ctx, h, idx, signature, hash, hash);
+ else
+ node_hash (ctx, h, idx, hash, signature, hash);
+ }
+}
--- /dev/null
+/* slh-prf.c
+
+ 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 "sha3.h"
+#include "slh-dsa-internal.h"
+
+void
+_slh_shake_init (struct sha3_256_ctx *ctx, const uint8_t *public_seed,
+ const struct slh_address_tree *at, const struct slh_address_hash *ah)
+{
+ sha3_256_init (ctx);
+ sha3_256_update (ctx, _SLH_DSA_128_SIZE, public_seed);
+ sha3_256_update (ctx, sizeof(*at), (const uint8_t *) at);
+ sha3_256_update (ctx, sizeof(*ah), (const uint8_t *) ah);
+}
+
+void
+_slh_shake (const uint8_t *public_seed,
+ const struct slh_address_tree *at, const struct slh_address_hash *ah,
+ const uint8_t *secret, uint8_t *out)
+{
+ struct sha3_256_ctx ctx;
+ _slh_shake_init (&ctx, public_seed, at, ah);
+ sha3_256_update (&ctx, _SLH_DSA_128_SIZE, secret);
+ sha3_256_shake (&ctx, _SLH_DSA_128_SIZE, out);
+}
#include "sha3.h"
#include "bswap-internal.h"
-static void
-slh_shake_init (struct sha3_256_ctx *ctx, const uint8_t *public_seed,
- const struct slh_address_tree *at, const struct slh_address_hash *ah)
-{
- sha3_256_init (ctx);
- sha3_256_update (ctx, _SLH_DSA_128_SIZE, public_seed);
- sha3_256_update (ctx, sizeof(*at), (const uint8_t *) at);
- sha3_256_update (ctx, sizeof(*ah), (const uint8_t *) ah);
-}
-
-static void
-slh_prf (const uint8_t *public_seed,
- const struct slh_address_tree *at, const struct slh_address_hash *ah,
- const uint8_t *secret, uint8_t *out)
-{
- struct sha3_256_ctx ctx;
- slh_shake_init (&ctx, public_seed, at, ah);
- sha3_256_update (&ctx, _SLH_DSA_128_SIZE, secret);
- sha3_256_shake (&ctx, _SLH_DSA_128_SIZE, out);
-}
-
/* If s == 0, returns src and leaves dst unchanged. Otherwise, returns
dst. For the ah argument, leaves ah->keypair and ah->height_chain
unchanged, but overwrites the other fields. */
ah->type = bswap32_if_le (SLH_WOTS_HASH);
ah->index_hash = bswap32_if_le(i);
- slh_prf (public_seed, at, ah, src, dst);
+ _slh_shake (public_seed, at, ah, src, dst);
for (j = 1; j < s; j++)
{
ah->index_hash = bswap32_if_le(i + j);
- slh_prf (public_seed, at, ah, dst, dst);
+ _slh_shake (public_seed, at, ah, dst, dst);
}
return dst;
ah->height_chain = 0;
ah->index_hash = 0;
- slh_shake_init (ctx, public_seed, at, ah);
+ _slh_shake_init (ctx, public_seed, at, ah);
}
void
ah.type = bswap32_if_le (SLH_WOTS_PRF);
ah.height_chain = bswap32_if_le(i);
ah.index_hash = 0;
- slh_prf (public_seed, at, &ah, secret_seed, out);
+ _slh_shake (public_seed, at, &ah, secret_seed, out);
/* Hash chain. */
wots_chain (public_seed, at, &ah, 0, 15, out, out);
ah.keypair = bswap32_if_le (keypair);
ah.height_chain = bswap32_if_le(i);
ah.index_hash = 0;
- slh_prf (public_seed, at, &ah, secret_seed, sig);
+ _slh_shake (public_seed, at, &ah, secret_seed, sig);
/* Hash chain. */
wots_chain (public_seed, at, &ah, 0, msg, sig, sig);
--- /dev/null
+/* slh-xmss.c
+
+ The eXtended Merkle Signature Scheme, part of SLH-DSA (FIPS 205)
+
+ 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 "bswap-internal.h"
+#include "sha3.h"
+#include "slh-dsa-internal.h"
+
+static void
+xmss_leaf (const struct slh_merkle_ctx_secret *ctx, unsigned idx, uint8_t *leaf)
+{
+ _wots_gen (ctx->pub.seed, ctx->secret_seed, &ctx->pub.at, idx, leaf);
+}
+
+static void
+xmss_node (const struct slh_merkle_ctx_public *ctx, unsigned height, unsigned index,
+ const uint8_t *left, const uint8_t *right, uint8_t *out)
+{
+ struct sha3_256_ctx sha3;
+ struct slh_address_hash ah =
+ {
+ bswap32_if_le (SLH_XMSS_TREE),
+ 0,
+ bswap32_if_le (height),
+ bswap32_if_le (index),
+ };
+
+ _slh_shake_init (&sha3, ctx->seed, &ctx->at, &ah);
+ sha3_256_update (&sha3, _SLH_DSA_128_SIZE, left);
+ sha3_256_update (&sha3, _SLH_DSA_128_SIZE, right);
+ sha3_256_shake (&sha3, _SLH_DSA_128_SIZE, out);
+}
+
+void
+_xmss_gen (const uint8_t *public_seed, const uint8_t *secret_seed,
+ uint8_t *root)
+{
+ struct slh_merkle_ctx_secret ctx =
+ {
+ {
+ public_seed,
+ /* Everything zero, except layer and type. */
+ { bswap32_if_le(SLH_DSA_D-1), 0, 0, } ,
+ 0,
+ },
+ secret_seed
+ };
+ uint8_t stack[(XMSS_H + 1)*_SLH_DSA_128_SIZE];
+ _merkle_root (&ctx, xmss_leaf, xmss_node, XMSS_H, 0, root, stack);
+}
+
+void
+_xmss_sign (const struct slh_merkle_ctx_secret *ctx,
+ unsigned idx, const uint8_t *msg, uint8_t *signature, uint8_t *pub)
+{
+ _wots_sign (ctx->pub.seed, ctx->secret_seed, &ctx->pub.at, idx, msg, signature, pub);
+ signature += WOTS_SIGNATURE_SIZE;
+
+ _merkle_sign (ctx, xmss_leaf, xmss_node, XMSS_H, idx, signature);
+ _merkle_verify (&ctx->pub, xmss_node, XMSS_H, idx, signature, pub);
+}
+
+void
+_xmss_verify (const struct slh_merkle_ctx_public *ctx,
+ unsigned idx, const uint8_t *msg, const uint8_t *signature, uint8_t *pub)
+{
+ _wots_verify (ctx->seed, &ctx->at, idx, msg, signature, pub);
+ signature += WOTS_SIGNATURE_SIZE;
+
+ _merkle_verify (ctx, xmss_node, XMSS_H, idx, signature, pub);
+}
#include "testutils.h"
+#include "sha3.h"
#include "slh-dsa-internal.h"
#include "bswap-internal.h"
ASSERT (MEMEQ(sizeof(pub), pub, exp_pub->data));
}
+/* The xmss_leaf and xmss_node functions copied from slh-xmss.c */
+static void
+xmss_leaf (const struct slh_merkle_ctx_secret *ctx, unsigned idx, uint8_t *leaf)
+{
+ _wots_gen (ctx->pub.seed, ctx->secret_seed, &ctx->pub.at, idx, leaf);
+}
+
+static void
+xmss_node (const struct slh_merkle_ctx_public *ctx, unsigned height, unsigned index,
+ const uint8_t *left, const uint8_t *right, uint8_t *out)
+{
+ struct sha3_256_ctx sha3;
+ struct slh_address_hash ah =
+ {
+ bswap32_if_le (SLH_XMSS_TREE),
+ 0,
+ bswap32_if_le (height),
+ bswap32_if_le (index),
+ };
+
+ _slh_shake_init (&sha3, ctx->seed, &ctx->at, &ah);
+ sha3_256_update (&sha3, _SLH_DSA_128_SIZE, left);
+ sha3_256_update (&sha3, _SLH_DSA_128_SIZE, right);
+ sha3_256_shake (&sha3, _SLH_DSA_128_SIZE, out);
+}
+
+static void
+test_merkle (const struct tstring *public_seed, const struct tstring *secret_seed,
+ unsigned layer, uint64_t tree_idx, uint32_t idx, const struct tstring *msg,
+ const struct tstring *exp_pub, const struct tstring *exp_sig)
+{
+ struct slh_merkle_ctx_secret ctx =
+ {
+ {
+ public_seed->data,
+ { bswap32_if_le(layer), 0, bswap64_if_le(tree_idx) },
+ 0,
+ },
+ secret_seed->data,
+ };
+
+ uint8_t sig[XMSS_AUTH_SIZE];
+ uint8_t pub[_SLH_DSA_128_SIZE];
+ ASSERT (public_seed->length == _SLH_DSA_128_SIZE);
+ ASSERT (secret_seed->length == _SLH_DSA_128_SIZE);
+ ASSERT (msg->length == _SLH_DSA_128_SIZE);
+ ASSERT (exp_pub->length == _SLH_DSA_128_SIZE);
+ ASSERT (exp_sig->length == XMSS_AUTH_SIZE);
+
+ _merkle_sign (&ctx, xmss_leaf, xmss_node, XMSS_H, idx, sig);
+ ASSERT (MEMEQ(sizeof(sig), sig, exp_sig->data));
+
+ memcpy (pub, msg->data, sizeof(pub));
+ _merkle_verify (&ctx.pub, xmss_node, XMSS_H, idx, sig, pub);
+ ASSERT (MEMEQ(sizeof(pub), pub, exp_pub->data));
+}
+
+static void
+test_xmss_gen(const struct tstring *public_seed, const struct tstring *secret_seed,
+ const struct tstring *exp_pub)
+{
+ uint8_t pub[_SLH_DSA_128_SIZE];
+ ASSERT (public_seed->length == _SLH_DSA_128_SIZE);
+ ASSERT (secret_seed->length == _SLH_DSA_128_SIZE);
+ ASSERT (exp_pub->length == _SLH_DSA_128_SIZE);
+
+ _xmss_gen (public_seed->data, secret_seed->data, pub);
+ ASSERT (MEMEQ(sizeof(pub), pub, exp_pub->data));
+}
+
+static void
+test_xmss_sign (const struct tstring *public_seed, const struct tstring *secret_seed,
+ unsigned layer, uint64_t tree_idx, uint32_t idx, const struct tstring *msg,
+ const struct tstring *exp_pub, const struct tstring *exp_sig)
+{
+ struct slh_merkle_ctx_secret ctx =
+ {
+ {
+ public_seed->data,
+ { bswap32_if_le(layer), 0, bswap64_if_le(tree_idx) },
+ 0,
+ },
+ secret_seed->data,
+ };
+
+ uint8_t sig[XMSS_SIGNATURE_SIZE];
+ uint8_t pub[_SLH_DSA_128_SIZE];
+ ASSERT (public_seed->length == _SLH_DSA_128_SIZE);
+ ASSERT (secret_seed->length == _SLH_DSA_128_SIZE);
+ ASSERT (msg->length == _SLH_DSA_128_SIZE);
+ ASSERT (exp_pub->length == _SLH_DSA_128_SIZE);
+ ASSERT (exp_sig->length == XMSS_SIGNATURE_SIZE);
+
+ _xmss_sign (&ctx, idx, msg->data, sig, pub);
+ ASSERT (MEMEQ(sizeof(sig), sig, exp_sig->data));
+ ASSERT (MEMEQ(sizeof(pub), pub, exp_pub->data));
+
+ memset (pub, 0, sizeof(pub));
+ _xmss_verify (&ctx.pub, idx, msg->data, sig, pub);
+ ASSERT (MEMEQ(sizeof(pub), pub, exp_pub->data));
+}
+
void
test_main(void)
{
"711281599b3e05e7 5492ae3425eaa7f1 4ff8c6a9630bba6e bd236f195269a481"
"e87eb3d444825ba4 424ee5b2d9efb595 d5a338f4c253f79d e9d04535206ca6db"
"c2d4c9a1ec20849b 0db3fbe10c1446d5"));
+
+ test_merkle (public_seed, secret_seed, 0, UINT64_C(0x29877722d7c079), 0x156,
+ /* The message signed is the wots public key. */
+ SHEX("99747c3547770fa288a628ed15122d3e"),
+ SHEX("1be9523f2c90cd553ef5be5aa1c5c4fa"),
+ SHEX("612d5bac915a3996 2cdbcacee0969dcf 8ecfb830cea2206c 37749c65b8f673db"
+ "090b1e2ade6c2a2f 349b5915103a3ac7 8482c39e99ffc462 6fb4cf4a116804ab"
+ "9d93d7104660fefa 0753cf875cb22fd6 0e55dc2f303de036 47712b12067a55f7"
+ "a467897bbed0d3a0 9d50e9deaadff78d e9ac65c1fd05d076 10a79c8c465141ad"
+ "65e60340531fab08 f1f433ef823283fe"));
+
+ test_xmss_gen (public_seed, secret_seed,
+ SHEX("ac524902fc81f503 2bc27b17d9261ebd"));
+
+ test_xmss_sign(public_seed, secret_seed, 0, UINT64_C(0x29877722d7c079), 0x156,
+ /* The message signed is a fors public key. */
+ SHEX("3961b2cab15e08c633be827744a07f01"),
+ SHEX("1be9523f2c90cd553ef5be5aa1c5c4fa"),
+ SHEX(/* Embedded wots signature. */
+ "e1933de10e3fface 5fb8f8707c35ac13 74dc14ee8518481c 7e63d936ecc62f50"
+ "c7f951b87bc716dc 45e9bcfec6f6d97e 7fafdacb6db05ed3 778f21851f325e25"
+ "470da8dd81c41223 6d66cbee9ffa9c50 b86aa40baf213494 dfacca22aa0fb479"
+ "53928735ca4212cf 53a09ab0335d20a8 e62ede797c8e7493 54d636f15f3150c5"
+ "52797b76c091a41f 949f7fb57b42f744 1cca410264d6421f 4aa2c7e2ff4834a8"
+ "db0e6e7750b2e11f f1c89a42d1fbc271 8358e38325886ad1 2346cd694f9eab73"
+ "46c9a23b5ebe7637 bfd834a412318b01 188b0f29e3bd979f 8ae734acf1563af3"
+ "03d3c095e9eaeba3 5207b9df3acf9ee4 7da5c1e2652f3b86 41698f3d2260591b"
+ "07d00565e5d6be18 36033d2b7ef2c33b dc5cf3bba95b42df 6f73345b835341b2"
+ "50e2862c9f2f9cef 77cfa74cfb04c560 d8a0038c4e96cb0d a2b3e9b2cd3cecf5"
+ "22fda0d67e5f62b2 ee23bd42a61c7da4 8f0ea30b81af7ccb 6bb02cde272d2574"
+ "1325e9d91535615c 0184f2d7f226141d 79b42412721fd345 61d93663650b3c1b"
+ "6901872bc4c0bb15 bcd9038950b7717f 7f448b6126592076 a2bad2d63c55399c"
+ "243fdbdb0c8d676b 2ae455e7f0a9b18d 3fc889c43387f2cb c4dc73d7c85bfab6"
+ "b4b04463a3dd359c 3a8f61bfa6c4b042 4aeba4dd8a95ec12 43b2e36c29f82e1d"
+ "711281599b3e05e7 5492ae3425eaa7f1 4ff8c6a9630bba6e bd236f195269a481"
+ "e87eb3d444825ba4 424ee5b2d9efb595 d5a338f4c253f79d e9d04535206ca6db"
+ "c2d4c9a1ec20849b 0db3fbe10c1446d5"
+ /* Auth path aka inclusion proof. */
+ "612d5bac915a3996 2cdbcacee0969dcf 8ecfb830cea2206c 37749c65b8f673db"
+ "090b1e2ade6c2a2f 349b5915103a3ac7 8482c39e99ffc462 6fb4cf4a116804ab"
+ "9d93d7104660fefa 0753cf875cb22fd6 0e55dc2f303de036 47712b12067a55f7"
+ "a467897bbed0d3a0 9d50e9deaadff78d e9ac65c1fd05d076 10a79c8c465141ad"
+ "65e60340531fab08 f1f433ef823283fe"));
}