From: Niels Möller Date: Wed, 12 Feb 2025 11:34:32 +0000 (+0100) Subject: Implement xmss, part of slh-dsa. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=974d6c9be01ee762c83b92d9806559ba809ed621;p=thirdparty%2Fnettle.git Implement xmss, part of slh-dsa. --- diff --git a/ChangeLog b/ChangeLog index 51283b09..542255e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2025-02-12 Niels Möller + + * 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 (hogweed_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 * slh-wots.c (_wots_gen, _wots_sign, _wots_verify): New file, new functions. diff --git a/Makefile.in b/Makefile.in index 43170453..f38a5779 100644 --- a/Makefile.in +++ b/Makefile.in @@ -227,7 +227,7 @@ hogweed_SOURCES = sexp.c sexp-format.c \ ed25519-sha512-sign.c ed25519-sha512-verify.c \ ed448-shake256.c ed448-shake256-pubkey.c \ ed448-shake256-sign.c ed448-shake256-verify.c \ - slh-wots.c + slh-merkle.c slh-shake.c slh-wots.c slh-xmss.c OPT_SOURCES = fat-arm.c fat-arm64.c fat-ppc.c fat-s390x.c fat-x86_64.c mini-gmp.c diff --git a/slh-dsa-internal.h b/slh-dsa-internal.h index 623dad5e..862155a9 100644 --- a/slh-dsa-internal.h +++ b/slh-dsa-internal.h @@ -35,13 +35,23 @@ #include /* 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 { @@ -72,6 +82,28 @@ enum slh_addr_type 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) @@ -89,4 +121,46 @@ void _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 */ diff --git a/slh-merkle.c b/slh-merkle.c new file mode 100644 index 00000000..5a611d4e --- /dev/null +++ b/slh-merkle.c @@ -0,0 +1,123 @@ +/* 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 + +#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<= 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); + } +} diff --git a/slh-shake.c b/slh-shake.c new file mode 100644 index 00000000..83dbe9de --- /dev/null +++ b/slh-shake.c @@ -0,0 +1,58 @@ +/* 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); +} diff --git a/slh-wots.c b/slh-wots.c index f5dd7920..a74d84dd 100644 --- a/slh-wots.c +++ b/slh-wots.c @@ -40,27 +40,6 @@ #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. */ @@ -78,12 +57,12 @@ wots_chain (const uint8_t *public_seed, const struct slh_address_tree *at, 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; @@ -98,7 +77,7 @@ wots_pk_init (const uint8_t *public_seed, const struct slh_address_tree *at, 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 @@ -119,7 +98,7 @@ _wots_gen (const uint8_t *public_seed, const uint8_t *secret_seed, const struct 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); @@ -145,7 +124,7 @@ wots_sign_one (const uint8_t *public_seed, 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); diff --git a/slh-xmss.c b/slh-xmss.c new file mode 100644 index 00000000..1f0a36bc --- /dev/null +++ b/slh-xmss.c @@ -0,0 +1,104 @@ +/* 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); +} diff --git a/testsuite/slh-dsa-test.c b/testsuite/slh-dsa-test.c index a0b4d0f3..3c00878b 100644 --- a/testsuite/slh-dsa-test.c +++ b/testsuite/slh-dsa-test.c @@ -31,6 +31,7 @@ #include "testutils.h" +#include "sha3.h" #include "slh-dsa-internal.h" #include "bswap-internal.h" @@ -78,6 +79,108 @@ test_wots_sign (const struct tstring *public_seed, const struct tstring *secret_ 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) { @@ -115,4 +218,47 @@ 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")); }