diff --git a/doc/api_ref/pubkey.rst b/doc/api_ref/pubkey.rst index 89469c5c350..48b201a984e 100644 --- a/doc/api_ref/pubkey.rst +++ b/doc/api_ref/pubkey.rst @@ -1010,3 +1010,42 @@ signature: .. literalinclude:: /../src/examples/xmss.cpp :language: cpp + + +Hierarchical Signature System with Leighton-Micali Hash-Based Signatures (HSS-LMS) +---------------------------------------------------------------------------------- + +HSS-LMS is a stateful hash-based signature scheme which is defined in `RFC 8554 +"Leighton-Micali Hash-Based Signatures" `_. + +It is a multitree scheme, which is highly configurable. Multitree means, it consists +of multiple layers of Merkle trees, which can be defined individually. Moreover, the +used hash function and the Winternitz Parameter of the underlying one-time signature +can be chosen for each tree layer. For a sensible selection of parameters refer to +`RFC 8554 Section 6.4. `_. + +.. warning:: + + HSS-LMS is stateful, meaning the private key must be updated after + each signature. If the same private key is ever used to generate + two different signatures, then the scheme becomes insecure. For + this reason it can be challening to use HSS-LMS securely. + +HSS-LMS uses the Botan interfaces for public key cryptography. The ``params`` +argument of the HSS-LMS private key is used to define the parameter set. +The syntax of this argument must be the following: + +``HSS-LMS(,HW(,),HW(,),...)`` + +e.g. ``HSS-LMS(SHA-256,HW(5,1),HW(5,1))`` to use SHA-256 in a two-layer HSS instance +with LMS tree hights 5 and Winternitz parameter 1. This results in a +private key that can be used to create up to 2^(5+5)=1024 signatures. + +The following parameters are allowed (which are specified in +`RFC 8554 `_ and +and `draft-fluhrer-lms-more-parm-sets-11 `_): + +- hash: ``SHA-256``, ``Truncated(SHA-256,192)``, ``SHAKE-256(256)``, ``SHAKE-256(192)`` +- h: ``5``, ``10``, ``15``, ``20``, ``25`` +- w: ``1``, ``2``, ``4``, ``8`` + diff --git a/src/build-data/oids.txt b/src/build-data/oids.txt index 2e1922faaf2..5c8abc9bef6 100644 --- a/src/build-data/oids.txt +++ b/src/build-data/oids.txt @@ -1,5 +1,5 @@ -# Regenerate with ./src/scripts/oids.py oids > src/lib/asn1/oid_maps.cpp -# AND ./src/scripts/oids.py dn_ub > src/lib/x509/x509_dn_ub.cpp +# Regenerate with ./src/scripts/dev_tools/gen_oids.py oids > src/lib/asn1/oid_maps.cpp +# AND ./src/scripts/dev_tools/gen_oids.py dn_ub > src/lib/x509/x509_dn_ub.cpp # (if you modified something under [dn] # Public key types @@ -29,6 +29,10 @@ 1.3.6.1.4.1.25258.1.10.2 = Dilithium-6x5-AES-r3 1.3.6.1.4.1.25258.1.10.3 = Dilithium-8x7-AES-r3 +# HSS-LMS +# draft-gazdag-x509-hash-sigs-01 +1.2.840.113549.1.9.16.3.17 = HSS-LMS + # SPHINCS+ OIDs are currently in Botan's private arc 1.3.6.1.4.1.25258.1.12.1.1 = SphincsPlus-shake-128s-r3.1 1.3.6.1.4.1.25258.1.12.1.2 = SphincsPlus-shake-128f-r3.1 diff --git a/src/lib/asn1/oid_maps.cpp b/src/lib/asn1/oid_maps.cpp index a2fab4b0f96..94defe2b51a 100644 --- a/src/lib/asn1/oid_maps.cpp +++ b/src/lib/asn1/oid_maps.cpp @@ -1,7 +1,7 @@ /* * OID maps * -* This file was automatically generated by src/scripts/dev_tools/gen_oids.py on 2023-05-30 +* This file was automatically generated by ./src/scripts/dev_tools/gen_oids.py on 2023-08-29 * * All manual edits to this file will be lost. Edit the script * then regenerate this source file. @@ -91,6 +91,7 @@ std::unordered_map OID_Map::load_oid2str_map() { {"1.2.840.113549.1.5.13", "PBE-PKCS5v20"}, {"1.2.840.113549.1.9.1", "PKCS9.EmailAddress"}, {"1.2.840.113549.1.9.14", "PKCS9.ExtensionRequest"}, + {"1.2.840.113549.1.9.16.3.17", "HSS-LMS"}, {"1.2.840.113549.1.9.16.3.18", "ChaCha20Poly1305"}, {"1.2.840.113549.1.9.16.3.6", "KeyWrap.TripleDES"}, {"1.2.840.113549.1.9.16.3.8", "Compression.Zlib"}, @@ -384,6 +385,7 @@ std::unordered_map OID_Map::load_str2oid_map() { {"HMAC(SHA-384)", OID({1, 2, 840, 113549, 2, 10})}, {"HMAC(SHA-512)", OID({1, 2, 840, 113549, 2, 11})}, {"HMAC(SHA-512-256)", OID({1, 2, 840, 113549, 2, 13})}, + {"HSS-LMS", OID({1, 2, 840, 113549, 1, 9, 16, 3, 17})}, {"KeyWrap.AES-128", OID({2, 16, 840, 1, 101, 3, 4, 1, 5})}, {"KeyWrap.AES-192", OID({2, 16, 840, 1, 101, 3, 4, 1, 25})}, {"KeyWrap.AES-256", OID({2, 16, 840, 1, 101, 3, 4, 1, 45})}, diff --git a/src/lib/pubkey/hss_lms/hss.cpp b/src/lib/pubkey/hss_lms/hss.cpp new file mode 100644 index 00000000000..462d256fe8f --- /dev/null +++ b/src/lib/pubkey/hss_lms/hss.cpp @@ -0,0 +1,417 @@ +/** + * HSS - Hierarchical Signatures System (RFC 8554) + * (C) 2023 Jack Lloyd + * 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include +#include +#include +#include +#include +#include + +#include + +namespace Botan { + +namespace { + +/** + * @brief The maximum number of levels in a HSS-LMS tree. + * + * RFC 8554 Section 6: + * The number of levels is denoted as L and is between one + * and eight, inclusive. + */ +constexpr HSS_Level HSS_MAX_LEVELS(8); + +/** + * @brief Domain-separation parameter for generation the seed of a child LMS tree. + * + * This comes from https://github.com/cisco/hash-sigs. + */ +constexpr uint16_t SEED_CHILD_SEED = 0xfffe; + +/** + * @brief Domain-separation parameter for generation the identifier of a child LMS tree. + * + * This comes from https://github.com/cisco/hash-sigs. + */ +constexpr uint16_t SEED_CHILD_I = 0xffff; + +/** + * @brief Check that the given @p hash_name is one of the supported hash functions for HSS-LMS. + */ +constexpr bool is_supported_hash_function(std::string_view hash_name) { + return hash_name == "SHA-256" || hash_name == "Truncated(SHA-256,192)" || hash_name == "SHAKE-256(256)" || + hash_name == "SHAKE-256(192)"; +} + +/** + * Given an HSS index, i.e. the number of already created HSS signatures, return the lms leaf indices for + * the different LMS layers from root layer to bottom layer. + */ +std::vector derive_lms_leaf_indices_from_hss_index(HSS_Sig_Idx hss_idx, + const HSS_LMS_Params& hss_params) { + std::vector q(hss_params.L().get()); + for(int8_t layer_ctr = hss_params.L().get() - 1; layer_ctr >= 0; --layer_ctr) { + HSS_Level layer(layer_ctr); + const HSS_LMS_Params::LMS_LMOTS_Params_Pair& layer_params = hss_params.params_at_level(layer); + size_t layer_h = layer_params.lms_params().h(); + q.at(layer.get()) = LMS_Tree_Node_Idx(hss_idx.get() % (1ULL << layer_h)); + hss_idx = hss_idx >> layer_h; + } + BOTAN_ARG_CHECK(hss_idx == HSS_Sig_Idx(0), "HSS Tree is exhausted"); + + return q; +} + +} // namespace + +HSS_LMS_Params::HSS_LMS_Params(std::vector lm_lmots_params) : + m_lms_lmots_params(std::move(lm_lmots_params)), m_max_sig_count(calc_max_sig_count()) { + BOTAN_ARG_CHECK(!m_lms_lmots_params.empty() && m_lms_lmots_params.size() <= HSS_MAX_LEVELS, + "Invalid number of levels"); +} + +HSS_LMS_Params::HSS_LMS_Params(std::string_view algo_params) { + SCAN_Name scan(fmt("HSS-LMS({})", algo_params)); + + BOTAN_ARG_CHECK(scan.arg_count() >= 2 && scan.arg_count() <= HSS_MAX_LEVELS + 1, "Invalid number of arguments"); + std::string hash = scan.arg(0); + BOTAN_ARG_CHECK(is_supported_hash_function(hash), "Supported HSS-LMS hash function"); + + for(size_t i = 1; i < scan.arg_count(); ++i) { + SCAN_Name scan_layer(scan.arg(i)); + BOTAN_ARG_CHECK(scan_layer.algo_name() == "HW", "Invalid name for layer parameters"); + BOTAN_ARG_CHECK(scan_layer.arg_count() == 2, "Invalid number of layer parameters"); + const auto h = scan_layer.arg_as_integer(0); + const auto w = scan_layer.arg_as_integer(1); + m_lms_lmots_params.push_back({LMS_Params::create_or_throw(hash, h), LMOTS_Params::create_or_throw(hash, w)}); + } + m_max_sig_count = calc_max_sig_count(); +} + +HSS_Sig_Idx HSS_LMS_Params::calc_max_sig_count() const { + uint32_t total_hight_counter = 0; + for(HSS_Level level(0); level < L(); level++) { + total_hight_counter += params_at_level(level).lms_params().h(); + } + return HSS_Sig_Idx(1) << total_hight_counter; +} + +HSS_LMS_PrivateKeyInternal::HSS_LMS_PrivateKeyInternal(const HSS_LMS_Params& hss_params, RandomNumberGenerator& rng) : + m_hss_params(hss_params), m_current_idx(0) { + m_hss_seed = rng.random_vec(m_hss_params.params_at_level(HSS_Level(0)).lms_params().m()); + m_identifier = rng.random_vec(LMS_IDENTIFIER_LEN); +} + +std::shared_ptr HSS_LMS_PrivateKeyInternal::from_bytes_or_throw( + std::span key_bytes) { + if(key_bytes.size() < sizeof(HSS_Level) + sizeof(HSS_Sig_Idx)) { + throw Decoding_Error("To few private key bytes."); + } + BufferSlicer slicer(key_bytes); + + const auto L = slicer.copy_be(); + if(L == 0U || L > HSS_MAX_LEVELS) { + throw Decoding_Error("Invalid number of HSS layers in private HSS-LMS key."); + } + + const auto sig_idx = slicer.copy_be(); + + std::vector params; + for(size_t layer = 1; layer <= L; ++layer) { + if(slicer.remaining() < sizeof(LMS_Algorithm_Type) + sizeof(LMOTS_Algorithm_Type)) { + throw Decoding_Error("Out of bytes while parsing private HSS-LMS key."); + } + const auto lms_type = slicer.copy_be(); + const auto lmots_type = slicer.copy_be(); + params.push_back({LMS_Params::create_or_throw(lms_type), LMOTS_Params::create_or_throw(lmots_type)}); + } + std::string hash_name = params.at(0).lms_params().hash_name(); + if(!std::all_of(params.begin(), params.end(), [&hash_name](HSS_LMS_Params::LMS_LMOTS_Params_Pair& lms_lmots_params) { + bool valid_lmots_hash = lms_lmots_params.lmots_params().hash_name() == hash_name; + bool valid_lms_hash = lms_lmots_params.lms_params().hash_name() == hash_name; + return valid_lmots_hash && valid_lms_hash; + })) { + throw Decoding_Error("Inconsistent hash functions are not allowed."); + } + + if(slicer.remaining() < params.at(0).lms_params().m() + LMS_IDENTIFIER_LEN) { + throw Decoding_Error("Out of bytes while parsing private HSS-LMS key."); + } + auto hss_seed = slicer.copy(params.at(0).lms_params().m()); + auto identifier = slicer.copy(LMS_IDENTIFIER_LEN); + + if(!slicer.empty()) { + throw Decoding_Error("Private HSS-LMS key contains more bytes than expected."); + } + auto sk = std::shared_ptr( + new HSS_LMS_PrivateKeyInternal(HSS_LMS_Params(std::move(params)), std::move(hss_seed), std::move(identifier))); + + sk->set_idx(sig_idx); + return sk; +} + +secure_vector HSS_LMS_PrivateKeyInternal::to_bytes() const { + secure_vector sk_bytes(sizeof(HSS_Level) + sizeof(HSS_Sig_Idx) + + hss_params().L().get() * + (sizeof(LMS_Algorithm_Type) + sizeof(LMOTS_Algorithm_Type))); + BufferStuffer stuffer(sk_bytes); + + stuffer.append_be(hss_params().L()); + stuffer.append_be(get_idx()); + + for(HSS_Level layer(1); layer <= hss_params().L(); ++layer) { + const auto& params = hss_params().params_at_level(layer - 1); + stuffer.append_be(params.lms_params().algorithm_type()); + stuffer.append_be(params.lmots_params().algorithm_type()); + } + + sk_bytes.insert(sk_bytes.end(), seed().begin(), seed().end()); + sk_bytes.insert(sk_bytes.end(), identifier().begin(), identifier().end()); + + return sk_bytes; +} + +void HSS_LMS_PrivateKeyInternal::set_idx(HSS_Sig_Idx idx) { + if(idx >= m_hss_params.max_sig_count()) { + throw Decoding_Error("HSS private key is exhausted"); + } + m_current_idx = idx; +} + +HSS_Sig_Idx HSS_LMS_PrivateKeyInternal::reserve_next_idx() { + HSS_Sig_Idx next_idx = m_current_idx; + set_idx(m_current_idx + 1); + return next_idx; +} + +HSS_LMS_PrivateKeyInternal::HSS_LMS_PrivateKeyInternal(HSS_LMS_Params hss_params, + LMS_Seed hss_seed, + LMS_Identifier identifier) : + m_hss_params(std::move(hss_params)), + m_hss_seed(std::move(hss_seed)), + m_identifier(std::move(identifier)), + m_current_idx(0) { + BOTAN_ARG_CHECK(m_hss_seed.size() == m_hss_params.params_at_level(HSS_Level(0)).lms_params().m(), + "Invalid seed size"); + BOTAN_ARG_CHECK(m_identifier.size() == LMS_IDENTIFIER_LEN, "Invalid identifier size"); +} + +secure_vector HSS_LMS_PrivateKeyInternal::sign(std::span msg) { + secure_vector sig(HSS_Signature::size(hss_params())); + BufferStuffer sig_stuffer(sig); + sig_stuffer.append_be(hss_params().L() - 1); + + /* + + X + / \ Layer 0 + X X + / \ / \ + 00 01 10 11 00 01 10 11 + --------------- /s --- |s --- |s -- \s ----------- + X X X X Layer 1 + / \ / \ / \ / \ + 00 01 00 01 00 01 00 01 + + 00 01 02 03 04 05 06 07 + + 2 = 01 | 0 + + idx >> h + + */ + + std::vector q = derive_lms_leaf_indices_from_hss_index(reserve_next_idx(), hss_params()); + + // Derive LMS private keys and compute buffers + std::vector lms_key_at_layer; + std::vector> out_lms_sig_buffer_at_layer; + std::vector> out_child_pk_buffer_at_layer; + for(HSS_Level layer(0); layer < hss_params().L(); ++layer) { + // Generate key for current layer + const HSS_LMS_Params::LMS_LMOTS_Params_Pair& layer_params = hss_params().params_at_level(layer); + if(layer == HSS_Level(0)) { + lms_key_at_layer.push_back(hss_derive_root_lms_private_key()); + } else { + lms_key_at_layer.push_back( + hss_derive_child_lms_private_key(layer_params, lms_key_at_layer.back(), q.at(layer.get() - 1))); + out_child_pk_buffer_at_layer.push_back(sig_stuffer.next(LMS_PublicKey::size(layer_params.lms_params()))); + } + out_lms_sig_buffer_at_layer.push_back(sig_stuffer.next( + LMS_Signature::size(layer_params.lms_params(), layer_params.lmots_params()))); + } + BOTAN_ASSERT_NOMSG(sig_stuffer.full()); + + // Sign and write the signature from bottom layer to root layer + std::vector current_pk; + for(int32_t layer_it = hss_params().L().get() - 1; layer_it >= 0; --layer_it) { + HSS_Level layer(layer_it); + if(layer == hss_params().L() - 1) { + current_pk = + lms_key_at_layer.at(layer.get()) + .sign_and_get_pk(out_lms_sig_buffer_at_layer.at(layer.get()), q.at(layer.get()), LMS_Message(msg)) + .to_bytes(); + } else { + std::copy(current_pk.begin(), current_pk.end(), out_child_pk_buffer_at_layer.at(layer.get()).begin()); + current_pk = + lms_key_at_layer.at(layer.get()) + .sign_and_get_pk(out_lms_sig_buffer_at_layer.at(layer.get()), q.at(layer.get()), LMS_Message(current_pk)) + .to_bytes(); + } + } + + return sig; +} + +LMS_PrivateKey HSS_LMS_PrivateKeyInternal::hss_derive_root_lms_private_key() const { + auto& top_params = hss_params().params_at_level(HSS_Level(0)); + return LMS_PrivateKey(top_params.lms_params(), top_params.lmots_params(), identifier(), seed()); +} + +LMS_PrivateKey HSS_LMS_PrivateKeyInternal::hss_derive_child_lms_private_key( + const HSS_LMS_Params::LMS_LMOTS_Params_Pair& child_lms_lmots_params, + const LMS_PrivateKey& parent_sk, + LMS_Tree_Node_Idx parent_q) { + const auto hash = HashFunction::create_or_throw(child_lms_lmots_params.lms_params().hash_name()); + + // CHILD_SEED = H( PARENT_I || PARENT_Q || SEED_CHILD_SEED || 0xff || PARENT_SEED ) + PseudorandomKeyGeneration seed_generator(parent_sk.identifier()); + seed_generator.set_q(parent_q.get()); + seed_generator.set_i(SEED_CHILD_SEED); + seed_generator.set_j(0xff); + auto child_seed = seed_generator.gen(*hash, parent_sk.seed()); + + // CHILD_I = H( PARENT_I || PARENT_Q || SEED_CHILD_I || 0xff || PARENT_SEED ) + seed_generator.set_i(SEED_CHILD_I); + auto child_identifier = seed_generator.gen(*hash, parent_sk.seed()); + child_identifier.resize(LMS_IDENTIFIER_LEN); + + return LMS_PrivateKey(child_lms_lmots_params.lms_params(), + child_lms_lmots_params.lmots_params(), + std::move(child_identifier), + std::move(child_seed)); +} + +HSS_LMS_PublicKeyInternal HSS_LMS_PublicKeyInternal::create(const HSS_LMS_PrivateKeyInternal& hss_sk) { + auto& hss_params = hss_sk.hss_params(); + + const auto root_sk = hss_sk.hss_derive_root_lms_private_key(); + LMS_PublicKey top_pub_key = LMS_PublicKey(root_sk); + + return HSS_LMS_PublicKeyInternal(hss_params.L(), std::move(top_pub_key)); +} + +std::shared_ptr HSS_LMS_PublicKeyInternal::from_bytes_or_throw( + std::span key_bytes) { + if(key_bytes.size() < sizeof(HSS_Level)) { + throw Decoding_Error("To few public key bytes."); + } + BufferSlicer slicer(key_bytes); + + const auto L = slicer.copy_be(); + if(L > HSS_MAX_LEVELS) { + throw Decoding_Error("Invalid number of HSS layers in public HSS-LMS key."); + } + + LMS_PublicKey lms_pub_key = LMS_PublicKey::from_bytes_of_throw(slicer); + + if(!slicer.empty()) { + throw Decoding_Error("Public HSS-LMS key contains more bytes than expected."); + } + return std::make_shared(L, std::move(lms_pub_key)); +} + +std::vector HSS_LMS_PublicKeyInternal::to_bytes() const { + std::vector lms_pub_key_bytes = m_top_lms_pub_key.to_bytes(); + std::vector bytes(sizeof(m_L) + lms_pub_key_bytes.size()); + BufferStuffer stuffer(bytes); + + stuffer.append_be(m_L); + stuffer.append(lms_pub_key_bytes); + BOTAN_ASSERT_NOMSG(stuffer.full()); + + return bytes; +} + +AlgorithmIdentifier HSS_LMS_PublicKeyInternal::algorithm_identifier() const { + return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM); +} + +OID HSS_LMS_PublicKeyInternal::object_identifier() const { + return OID::from_string(algo_name()); +} + +size_t HSS_LMS_PublicKeyInternal::size() const { + return sizeof(m_L) + LMS_PublicKey::size(m_top_lms_pub_key.lms_params()); +} + +bool HSS_LMS_PublicKeyInternal::verify_signature(std::span msg, const HSS_Signature& sig) const { + if(static_cast(sig.Nspk()) + 1 != L()) { + // HSS levels in the public key does not match with the signature's + return false; + } + + const LMS_PublicKey* lms_pk = &lms_pub_key(); + + // Verify the signature by the above layer over the LMS public keys for layer 1 to Nspk. + for(uint8_t layer = 0; layer < sig.Nspk(); ++layer) { + const HSS_Signature::Signed_Pub_Key& signed_pub_key = sig.signed_pub_key(layer); + if(!lms_pk->verify_signature(LMS_Message(signed_pub_key.public_key().to_bytes()), signed_pub_key.signature())) { + return false; + } + lms_pk = &signed_pub_key.public_key(); + } + + // Verify the signature by the bottom layer over the message. + return lms_pk->verify_signature(LMS_Message(msg), sig.bottom_sig()); +} + +HSS_Signature::Signed_Pub_Key::Signed_Pub_Key(LMS_Signature sig, LMS_PublicKey pub) : + m_sig(std::move(sig)), m_pub(std::move(pub)) {} + +HSS_Signature HSS_Signature::from_bytes_or_throw(std::span sig_bytes) { + if(sig_bytes.size() < sizeof(uint32_t)) { + throw Decoding_Error("To few HSS signature bytes."); + } + BufferSlicer slicer(sig_bytes); + + const auto Nspk = slicer.copy_be(); + if(Nspk >= HSS_MAX_LEVELS) { + throw Decoding_Error("Invalid number of HSS layers in signature."); + } + + std::vector signed_pub_keys; + for(size_t i = 0; i < Nspk; ++i) { + LMS_Signature sig = LMS_Signature::from_bytes_or_throw(slicer); + LMS_PublicKey pub_key = LMS_PublicKey::from_bytes_of_throw(slicer); + signed_pub_keys.push_back(Signed_Pub_Key(std::move(sig), std::move(pub_key))); + } + + auto sig = LMS_Signature::from_bytes_or_throw(slicer); + + if(!slicer.empty()) { + throw Decoding_Error("HSS-LMS signature contains more bytes than expected."); + } + return HSS_Signature(static_cast(Nspk), std::move(signed_pub_keys), std::move(sig)); +} + +size_t HSS_Signature::size(const HSS_LMS_Params& params) { + size_t size = sizeof(uint32_t); + size += LMS_Signature::size(params.params_at_level(HSS_Level(0)).lms_params(), + params.params_at_level(HSS_Level(0)).lmots_params()); + for(HSS_Level layer(1); layer < params.L(); ++layer) { + const auto& param = params.params_at_level(layer); + size += LMS_PublicKey::size(param.lms_params()); + size += LMS_Signature::size(param.lms_params(), param.lmots_params()); + } + return size; +} + +} // namespace Botan diff --git a/src/lib/pubkey/hss_lms/hss.h b/src/lib/pubkey/hss_lms/hss.h new file mode 100644 index 00000000000..4d9799d0c15 --- /dev/null +++ b/src/lib/pubkey/hss_lms/hss.h @@ -0,0 +1,400 @@ +/** + * HSS - Hierarchical Signatures System (RFC 8554) + * (C) 2023 Jack Lloyd + * 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_HSS_H_ +#define BOTAN_HSS_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Botan { + +/** + * @brief The index of a node within a specific LMS tree layer + */ +using HSS_Sig_Idx = Strong; + +/** + * @brief The index of a node within a specific LMS tree layer + */ +using HSS_Level = Strong; + +/** + * @brief The HSS-LMS parameters. + * + * See RFC 8554 Section 6. + */ +class HSS_LMS_Params final { + public: + /** + * @brief Represents a pair of LMS and LMOTS parameters associated with one LMS tree layer. + */ + class LMS_LMOTS_Params_Pair { + public: + /** + * @brief The LMS parameters. + */ + const LMS_Params& lms_params() const { return m_lms_params; } + + /** + * @brief The LMOTS parameters. + */ + const LMOTS_Params& lmots_params() const { return m_lmots_params; } + + /** + * @brief Construct a new params pair + */ + LMS_LMOTS_Params_Pair(LMS_Params p_lms_params, LMOTS_Params p_lmots_params) : + m_lms_params(std::move(p_lms_params)), m_lmots_params(std::move(p_lmots_params)) {} + + private: + LMS_Params m_lms_params; + LMOTS_Params m_lmots_params; + }; + + /** + * @brief Construct the HSS-LMS parameters from a vector LMS and LM-OTS parameters. + */ + explicit HSS_LMS_Params(std::vector lm_lmots_params); + + /** + * @brief Construct the HSS-LMS parameters form an algorithm parameter string. + * + * The HSS/LMS instance to use for creating new keys is defined using an algorithm parameter sting, + * i.e. to define which hash function (hash), LMS tree hights (h) + * and OTS Winternitz coefficient widths (w) to use. The syntax is the following: + * + * HSS-LMS(,HW(,),HW(,),...) + * + * e.g. 'HSS-LMS(SHA-256,HW(5,1),HW(5,1))' to use SHA-256 in a two-layer HSS instance + * with a LMS tree hights 5 and w=1. The following parameters are allowed (which are + * specified in RFC 8554 and draft-fluhrer-lms-more-parm-sets-11): + * + * hash: 'SHA-256', 'Truncated(SHA-256,192)', 'SHAKE-256(256)', SHAKE-256(192) + * h: '5', '10', '15', '20', '25' + * w: '1', '2', '4', '8' + * + * Note: The selected hash function is also used for seed derivation. + */ + explicit HSS_LMS_Params(std::string_view algo_params); + + /** + * @brief Returns the LMS an LM-OTS parameters at the specified @p level of the HSS tree. + */ + const LMS_LMOTS_Params_Pair& params_at_level(HSS_Level level) const { return m_lms_lmots_params.at(level.get()); } + + /** + * @brief Returns the number of layers the HSS tree has. + */ + HSS_Level L() const { return static_cast(m_lms_lmots_params.size()); } + + /** + * @brief The maximal number of signatures allowed for these HSS parameters + */ + HSS_Sig_Idx max_sig_count() const { return m_max_sig_count; } + + private: + /** + * @brief Compute the maximal number of signatures + */ + HSS_Sig_Idx calc_max_sig_count() const; + + std::vector m_lms_lmots_params; + HSS_Sig_Idx m_max_sig_count; +}; + +/** + * @brief The internal HSS-LMS private key. + * + * Note that the format is not specified in the RFC 8554, + * and is Botan specific. + */ +class HSS_LMS_PrivateKeyInternal final { + public: + /** + * @brief Create an internal HSS-LMS private key. + * + * @param hss_params The HSS-LMS parameters for the key. + * @param rng The rng to use. + * @return The internal HSS-LMS private key. + */ + HSS_LMS_PrivateKeyInternal(const HSS_LMS_Params& hss_params, RandomNumberGenerator& rng); + + /** + * @brief Parse a private HSS-LMS key. + * + * @param key_bytes The private key bytes to parse. + * @return The internal HSS-LMS private key. + * @throws Decoding_Error If parsing the private key fails. + */ + static std::shared_ptr from_bytes_or_throw(std::span key_bytes); + + /** + * @brief Returns the used HSS-LMS parameters. + */ + const HSS_LMS_Params& hss_params() const { return m_hss_params; } + + /** + * @brief Returns the key in its encoded format. + */ + secure_vector to_bytes() const; + + /** + * @brief Set the idx of the next signature to generate. + * + * Note that creating two signatures with the same index is insecure. + * The index must be lower than hss_params().max_sig_count(). + */ + void set_idx(HSS_Sig_Idx idx); + + /** + * @brief Create a HSS-LMS signature. + * + * See RFC 8554 6.2 - Algorithm 8. + * + * For each signature creation the hypertree is computed once + * again, so no data is stored between multiple signatures. However, + * storing data between multiple signatures could be an optimization + * if applications create multiple signatures in one go. + * + * @param msg The message to sign. + * @param rng The rng to use. + * @return The HSS-LMS signature. + */ + secure_vector sign(std::span msg); + + /** + * @brief Create the HSS root LMS tree's LMS_PrivateKey using the HSS-LMS private key. + * + * We use the same generation as the reference implementation (https://github.com/cisco/hash-sigs) + * with SECRET_METHOD==2. + * + * @return The LMS private key + */ + LMS_PrivateKey hss_derive_root_lms_private_key() const; + + private: + HSS_LMS_PrivateKeyInternal(HSS_LMS_Params hss_params, LMS_Seed hss_seed, LMS_Identifier identifier); + + /** + * @brief Returns the seed contained in the private key used for key derivation + */ + const LMS_Seed& seed() const { return m_hss_seed; } + + /** + * @brief The identifier of the top level LMS tree + */ + const LMS_Identifier& identifier() const { return m_identifier; } + + /** + * @brief Get the idx of the next signature to generate. + */ + HSS_Sig_Idx get_idx() const { return m_current_idx; } + + /** + * @brief Get the index of the next signature to generate and + * increase the counter by one. + */ + HSS_Sig_Idx reserve_next_idx(); + + /** + * @brief Derive the seed and identifier of an LMS tree from its parent LMS tree. + * + * We use the same generation as the reference implementation (https://github.com/cisco/hash-sigs). + * + * @param child_lms_params The LMS parameters of the child tree. + * @param parent_sk The parent's LMS private key + * @param parent_q The LMS leaf number the child tree has in its parent tree. + * @return LMS private key + */ + static LMS_PrivateKey hss_derive_child_lms_private_key( + const HSS_LMS_Params::LMS_LMOTS_Params_Pair& child_lms_lmots_params, + const LMS_PrivateKey& parent_sk, + LMS_Tree_Node_Idx parent_q); + + HSS_LMS_Params m_hss_params; + LMS_Seed m_hss_seed; + LMS_Identifier m_identifier; + HSS_Sig_Idx m_current_idx; +}; + +class HSS_Signature; + +/** + * @brief The internal HSS-LMS public key. + * + * Format according to RFC 8554: + * u32str(L) || pub[0] + */ +class HSS_LMS_PublicKeyInternal final { + public: + /** + * @brief Create the public HSS-LMS key from its private key. + * + * @param hss_sk The private HSS-LMS key. + * @return The internal HSS-LMS public key. + */ + static HSS_LMS_PublicKeyInternal create(const HSS_LMS_PrivateKeyInternal& hss_sk); + + /** + * @brief Parse a public HSS-LMS key. + * + * @param key_bytes The public key bytes to parse. + * @return The internal HSS-LMS public key. + * @throws Decoding_Error If parsing the public key fails. + */ + static std::shared_ptr from_bytes_or_throw(std::span key_bytes); + + HSS_LMS_PublicKeyInternal(HSS_Level L, LMS_PublicKey top_lms_pub_key) : + m_L(L), m_top_lms_pub_key(std::move(top_lms_pub_key)) {} + + /** + * @brief Returns the key in its encoded format. + */ + std::vector to_bytes() const; + + /** + * @brief Returns the public LMS key of the top LMS tree. + */ + const LMS_PublicKey& lms_pub_key() const { return m_top_lms_pub_key; } + + /** + * @brief Returns the size in bytes the key would have in its encoded format. + */ + size_t size() const; + + /** + * @brief The algorithm identifier for HSS-LMS + */ + AlgorithmIdentifier algorithm_identifier() const; + + /** + * @brief The object identifier for HSS-LMS + */ + OID object_identifier() const; + + /** + * @brief The algorithm name for HSS-LMS + */ + std::string algo_name() const { return "HSS-LMS"; } + + /** + * @brief Verify a HSS-LMS signature. + * + * See RFC 8554 6.3. + * + * @param msg The signed message. + * @param sig The already parsed HSS-LMS signature. + * @return True iff the signature is valid. + */ + bool verify_signature(std::span msg, const HSS_Signature& sig) const; + + private: + /** + * @brief Returns the number of layers of LMS trees in the HSS tree. + */ + HSS_Level L() const { return m_L; } + + HSS_Level m_L; + LMS_PublicKey m_top_lms_pub_key; +}; + +/** + * @brief A HSS-LMS signature. + * + * Format according to RFC 8554: + * u32str(Nspk) || sig[0] || pub[1] || ... || sig[Nspk-1] || pub[Nspk] || sig[Nspk] + */ +class HSS_Signature final { + public: + /** + * @brief A LMS public key signed by the HSS layer above it. + * + * signed_pub_key[i] = sig[i] || pub[i+1], + * for i between 0 and Nspk-1, inclusive. + */ + class Signed_Pub_Key { + public: + /** + * @brief Constructor for a new sig-pubkey-pair + */ + Signed_Pub_Key(LMS_Signature sig, LMS_PublicKey pub); + + /** + * @brief The signature of the public key + */ + const LMS_Signature& signature() const { return m_sig; } + + /** + * @brief The signed public key + */ + const LMS_PublicKey& public_key() const { return m_pub; } + + private: + LMS_Signature m_sig; + LMS_PublicKey m_pub; + }; + + /** + * @brief Parse a HSS-LMS signature. + * + * @param sig_bytes The signature bytes to parse. + * @return The parsed HSS-LMS signature. + * @throws Decoding_Error If parsing the signature fails. + */ + static HSS_Signature from_bytes_or_throw(std::span sig_bytes); + + /** + * @brief Returns the size a signature would have in its encoded format. + * + * @param params The HSS-LMS parameters. + * @return size_t The expected size in bytes. + */ + static size_t size(const HSS_LMS_Params& params); + + /** + * @brief Returns the number of signed public keys (Nspk = L-1). + */ + uint8_t Nspk() const { return m_Nspk; } + + /** + * @brief Returns the signed LMS key signed by a specific layer. + * + * @param layer The layer by which the LMS key is signed. + * @return The LMS key and the signature by its parent layer. + */ + const Signed_Pub_Key& signed_pub_key(uint8_t layer) const { return m_signed_pub_keys.at(layer); } + + /** + * @brief Returns the LMS signature by the bottom layer of the signed message. + */ + const LMS_Signature& bottom_sig() const { return m_sig; } + + private: + /** + * @brief Private constructor using the individual signature fields. + */ + HSS_Signature(uint8_t Nspk, std::vector signed_pub_keys, LMS_Signature sig) : + m_Nspk(Nspk), m_signed_pub_keys(std::move(signed_pub_keys)), m_sig(std::move(sig)) {} + + uint8_t m_Nspk; + std::vector m_signed_pub_keys; + LMS_Signature m_sig; +}; + +} // namespace Botan + +#endif diff --git a/src/lib/pubkey/hss_lms/hss_lms.cpp b/src/lib/pubkey/hss_lms/hss_lms.cpp new file mode 100644 index 00000000000..cb393884efe --- /dev/null +++ b/src/lib/pubkey/hss_lms/hss_lms.cpp @@ -0,0 +1,170 @@ +/** +* HSS-LMS +* (C) 2023 Jack Lloyd +* 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include + +#include +#include + +namespace Botan { + +HSS_LMS_PublicKey::HSS_LMS_PublicKey(std::span pub_key) : + m_public(HSS_LMS_PublicKeyInternal::from_bytes_or_throw(pub_key)) {} + +HSS_LMS_PublicKey::~HSS_LMS_PublicKey() = default; + +size_t HSS_LMS_PublicKey::key_length() const { + return m_public->size(); +} + +size_t HSS_LMS_PublicKey::estimated_strength() const { + // draft-fluhrer-lms-more-parm-sets-11 Section 9. + // As shown in [Katz16], if we assume that the hash function can be + // modeled as a random oracle, then the security of the system is at + // least 8N-1 bits (where N is the size of the hash output in bytes); + return 8 * m_public->lms_pub_key().lms_params().m() - 1; +} + +std::string HSS_LMS_PublicKey::algo_name() const { + return m_public->algo_name(); +} + +AlgorithmIdentifier HSS_LMS_PublicKey::algorithm_identifier() const { + return m_public->algorithm_identifier(); +} + +OID HSS_LMS_PublicKey::object_identifier() const { + return m_public->object_identifier(); +} + +bool HSS_LMS_PublicKey::check_key(RandomNumberGenerator&, bool) const { + // Nothing to check. Only useful checks are already done during parsing. + return true; +} + +std::vector HSS_LMS_PublicKey::public_key_bits() const { + return m_public->to_bytes(); +} + +class HSS_LMS_Verification_Operation final : public PK_Ops::Verification { + public: + HSS_LMS_Verification_Operation(std::shared_ptr pub_key) : + m_public(std::move(pub_key)) {} + + void update(const uint8_t msg[], size_t msg_len) override { + m_msg_buffer.insert(m_msg_buffer.end(), msg, msg + msg_len); + } + + bool is_valid_signature(const uint8_t* sig, size_t sig_len) override { + std::vector message_to_verify = std::move(m_msg_buffer); + m_msg_buffer = std::vector(); + try { + const auto signature = HSS_Signature::from_bytes_or_throw({sig, sig_len}); + bool sig_valid = m_public->verify_signature(message_to_verify, signature); + return sig_valid; + } catch(const Decoding_Error& e) { + // Signature could not be decoded + return false; + } + } + + std::string hash_function() const override { return m_public->lms_pub_key().lms_params().hash_name(); } + + private: + std::shared_ptr m_public; + std::vector m_msg_buffer; +}; + +std::unique_ptr HSS_LMS_PublicKey::create_verification_op(std::string_view /*params*/, + std::string_view provider) const { + if(provider.empty() || provider == "base") { + return std::make_unique(m_public); + } + throw Provider_Not_Found(algo_name(), provider); +} + +std::unique_ptr HSS_LMS_PublicKey::create_x509_verification_op( + const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const { + if(provider.empty() || provider == "base") { + if(signature_algorithm != this->algorithm_identifier()) { + throw Decoding_Error("Unexpected AlgorithmIdentifier for HSS-LMS signature"); + } + return std::make_unique(m_public); + } + throw Provider_Not_Found(algo_name(), provider); +} + +bool HSS_LMS_PublicKey::supports_operation(PublicKeyOperation op) const { + return op == PublicKeyOperation::Signature; +} + +HSS_LMS_PrivateKey::HSS_LMS_PrivateKey(std::span private_key) { + m_private = HSS_LMS_PrivateKeyInternal::from_bytes_or_throw(private_key); + m_public = std::make_shared(HSS_LMS_PublicKeyInternal::create(*m_private)); +} + +HSS_LMS_PrivateKey::HSS_LMS_PrivateKey(RandomNumberGenerator& rng, std::string_view algo_params) { + HSS_LMS_Params hss_params(algo_params); + m_private = std::make_shared(hss_params, rng); + m_public = std::make_shared(HSS_LMS_PublicKeyInternal::create(*m_private)); +} + +HSS_LMS_PrivateKey::~HSS_LMS_PrivateKey() = default; + +secure_vector HSS_LMS_PrivateKey::private_key_bits() const { + // TODO: Do we want to private some Botan scoped pkcs8_algorithm_identifier(), instead of re-using the public one? + // As the private key format is not specified, this would make sure we recognise our own encoding. + return m_private->to_bytes(); +} + +secure_vector HSS_LMS_PrivateKey::raw_private_key_bits() const { + return private_key_bits(); +} + +std::unique_ptr HSS_LMS_PrivateKey::public_key() const { + return std::make_unique(*this); +} + +class HSS_LMS_Signature_Operation final : public PK_Ops::Signature { + public: + HSS_LMS_Signature_Operation(std::shared_ptr private_key, + std::shared_ptr public_key) : + m_private(std::move(private_key)), m_public(std::move(public_key)) {} + + void update(const uint8_t msg[], size_t msg_len) override { + m_msg_buffer.insert(m_msg_buffer.end(), msg, msg + msg_len); + } + + secure_vector sign(RandomNumberGenerator&) override { return m_private->sign(m_msg_buffer); } + + size_t signature_length() const override { return HSS_Signature::size(m_private->hss_params()); } + + AlgorithmIdentifier algorithm_identifier() const override { return m_public->algorithm_identifier(); } + + std::string hash_function() const override { return m_public->lms_pub_key().lms_params().hash_name(); } + + private: + std::shared_ptr m_private; + std::shared_ptr m_public; + std::vector m_msg_buffer; +}; + +std::unique_ptr HSS_LMS_PrivateKey::create_signature_op(RandomNumberGenerator& rng, + std::string_view params, + std::string_view provider) const { + BOTAN_UNUSED(rng); + BOTAN_ARG_CHECK(params.empty(), "Unexpected parameters for signing with HSS-LMS"); + + if(provider.empty() || provider == "base") { + return std::make_unique(m_private, m_public); + } + throw Provider_Not_Found(algo_name(), provider); +} + +} // namespace Botan diff --git a/src/lib/pubkey/hss_lms/hss_lms.h b/src/lib/pubkey/hss_lms/hss_lms.h new file mode 100644 index 00000000000..2cbd601c8bd --- /dev/null +++ b/src/lib/pubkey/hss_lms/hss_lms.h @@ -0,0 +1,165 @@ +/* + * Hierarchical Signature System (HSS) / Leighton-Micali Signature (LMS) + * hash-based signature algorithm (RFC 8554). + * + * (C) 2023 Jack Lloyd + * 2023 Philippe Lieser, Fabian Albert - Rohde & Schwarz Cybersecurity GmbH + * + * Botan is released under the Simplified BSD License (see license.txt) + **/ + +#ifndef BOTAN_HSS_LMS_H_ +#define BOTAN_HSS_LMS_H_ + +#include + +#include +#include + +/* +TODO List: + - Decide about the private key + - Check treehash with Sphincs+ + - Check that HSS index fits in 64 bit + + Tests: + - Test for exhausted key + - Hash signatures in tests + - Full list of OTS tests (possible?) + - Process TODOs in tests + + Final Cleanup: + - Minimize all includes + + + +*/ + +namespace Botan { + +class HSS_LMS_PublicKeyInternal; +class HSS_LMS_PrivateKeyInternal; + +/** + * @brief An HSS/LMS public key. + * + * Implementation of the Hierarchical Signature System (HSS) of + * Leighton-Micali Hash-Based Signatures (LMS) defined in RFC 8554 + * (https://www.rfc-editor.org/rfc/rfc8554.html). + * + * To derive seeds for single LMS trees in the HSS-multitree, the method (SECRET_METHOD 2) + * of the reference implementation (https://github.com/cisco/hash-sigs) is used. + */ +class BOTAN_PUBLIC_API(3, 2) HSS_LMS_PublicKey : public virtual Public_Key { + public: + /** + * @brief Load an existing public key using its bytes. + */ + HSS_LMS_PublicKey(std::span pub_key_bytes); + + ~HSS_LMS_PublicKey() override; + + size_t key_length() const override; + + std::string algo_name() const override; + + size_t estimated_strength() const override; + AlgorithmIdentifier algorithm_identifier() const override; + OID object_identifier() const override; + bool check_key(RandomNumberGenerator& rng, bool strong) const override; + std::vector public_key_bits() const override; + + std::unique_ptr create_verification_op(std::string_view params, + std::string_view provider) const override; + + std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, + std::string_view provider) const override; + + bool supports_operation(PublicKeyOperation op) const override; + + protected: + HSS_LMS_PublicKey() = default; + + std::shared_ptr m_public; +}; + +BOTAN_DIAGNOSTIC_PUSH +BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE + +/** + * @brief An HSS/LMS private key. + * + * HSS/LMS is a statefule hash-based signature scheme. This means the private key must + * be (securely) updated after using it for signing. Also, there is a maximal number + * of signatures that can be created using one HSS/LMS key pair, which depends on + * the number and size of LMS layers of the chosen HSS/LMS instance. For the selection + * of a sensible parameter set, refer to RFC 8554 6.4. + * + * The format of the HSS/LMS private key is not defined in + * RFC 8554. We use the following format (big endian): + * + * PrivateKey = u32str(L) || u64str(idx) || + * u32str(LMS algorithm id (root layer)) || u32str(LMOTS algorithm id (root layer)) || + * ... || + * u32str(LMS algorithm id (bottom layer)) || u32str(LMOTS algorithm id (bottom layer)) || + * HSS_SEED || HSS_Identifier + * + * L: Number of LMS layers + * Idx: Number of signatures already created using this private key + * HSS_SEED: Seed to derive LMS Seeds (see RFC 8554 Appendix A) like in SECRET_METHOD 2 of + * https://github.com/cisco/hash-sigs. As long as the hash functions output length. + * HSS_Identifier: 16 bytes long. + * + * The HSS/LMS instance to use for creating new keys is defined using an algorithm parameter sting, + * i.e. to define which hash function (hash), LMS tree hights (h) + * and OTS Winternitz coefficient widths (w) to use. The syntax is the following: + * + * HSS-LMS(,HW(,),HW(,),...) + * + * e.g. 'HSS-LMS(SHA-256,HW(5,1),HW(5,1))' to use SHA-256 in a two-layer HSS instance + * with a LMS tree hights 5 and w=1. The following parameters are allowed (which are + * specified in RFC 8554 and draft-fluhrer-lms-more-parm-sets-11): + * + * hash: 'SHA-256', 'Truncated(SHA-256,192)', 'SHAKE-256(256)', SHAKE-256(192) + * h: '5', '10', '15', '20', '25' + * w: '1', '2', '4', '8' + * + * Note: The selected hash function is also used for seed derivation. + */ +class BOTAN_PUBLIC_API(3, 2) HSS_LMS_PrivateKey final : public virtual HSS_LMS_PublicKey, + public virtual Private_Key { + public: + /** + * @brief Load an existing LMS private key using its bytes + */ + HSS_LMS_PrivateKey(std::span private_key_bytes); + + /** + * @brief Construct a new hss lms privatekey object. + * + * @param rng random number generator + * @param algo_params string is format 'HSS-LMS(,HW(,),HW(,),...)' + */ + HSS_LMS_PrivateKey(RandomNumberGenerator& rng, std::string_view algo_params); + + ~HSS_LMS_PrivateKey() override; + + secure_vector private_key_bits() const override; + secure_vector raw_private_key_bits() const override; + std::unique_ptr public_key() const override; + + bool stateful_operation() const override { return true; } + + std::unique_ptr create_signature_op(RandomNumberGenerator& rng, + std::string_view params, + std::string_view provider) const override; + + private: + std::shared_ptr m_private; +}; + +BOTAN_DIAGNOSTIC_POP + +} // namespace Botan + +#endif diff --git a/src/lib/pubkey/hss_lms/hss_lms_utils.cpp b/src/lib/pubkey/hss_lms/hss_lms_utils.cpp new file mode 100644 index 00000000000..497e997b2a2 --- /dev/null +++ b/src/lib/pubkey/hss_lms/hss_lms_utils.cpp @@ -0,0 +1,30 @@ +/** + * HSS - Hierarchical Signatures System (RFC 8554) + * (C) 2023 Jack Lloyd + * 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include +#include + +namespace Botan { +PseudorandomKeyGeneration::PseudorandomKeyGeneration(std::span identifier) { + m_input_buffer.resize(identifier.size() + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint8_t)); + BufferStuffer input_stuffer(m_input_buffer); + input_stuffer.append(identifier); + + m_q = input_stuffer.next(sizeof(uint32_t)); + m_i = input_stuffer.next(sizeof(uint16_t)); + m_j = input_stuffer.next(sizeof(uint8_t)).data(); + BOTAN_ASSERT_NOMSG(input_stuffer.full()); +} + +void PseudorandomKeyGeneration::gen(std::span out, HashFunction& hash, std::span seed) const { + hash.update(m_input_buffer); + hash.update(seed); + hash.final(out); +} + +} // namespace Botan \ No newline at end of file diff --git a/src/lib/pubkey/hss_lms/hss_lms_utils.h b/src/lib/pubkey/hss_lms/hss_lms_utils.h new file mode 100644 index 00000000000..92a2dd6522f --- /dev/null +++ b/src/lib/pubkey/hss_lms/hss_lms_utils.h @@ -0,0 +1,75 @@ +/** + * Utils for HSS/LMS + * (C) 2023 Jack Lloyd + * 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_HSS_LMS_UTILS_H_ +#define BOTAN_HSS_LMS_UTILS_H_ + +#include +#include + +namespace Botan { + +/** + * @brief Helper class used to derive secret values based in the pseudorandom key generation + * described in RFC 8554 Appendix A. + * + * This generation computes the following: + * + * Result = Hash( identifier || u32str(q) || u16str(i) || u8str(j) || SEED ) + * + * This Key Generation procedure is also used for the seed derivation function of + * SEED_METHOD 0 defined in https://github.com/cisco/hash-sigs, + */ +class PseudorandomKeyGeneration { + public: + /** + * @brief Create a PseudorandomKeyGeneration instance for a fixed @p identifier + */ + PseudorandomKeyGeneration(std::span identifier); + + /** + * @brief Specify the value for the u32str(q) hash input field + */ + void set_q(uint32_t q) { store_be(q, m_q.data()); } + + /** + * @brief Specify the value for the u16str(i) hash input field + */ + void set_i(uint16_t i) { store_be(i, m_i.data()); } + + /** + * @brief Specify the value for the u8str(j) hash input field + */ + void set_j(uint8_t j) { *m_j = j; } + + /** + * @brief Create a hash value using the preconfigured prefix and a @p seed + */ + template > + T gen(HashFunction& hash, std::span seed) const { + T output; + output.resize(hash.output_length()); + gen(output, hash, seed); + return output; + } + + /** + * @brief Create a hash value using the preconfigured prefix and a @p seed + */ + void gen(std::span out, HashFunction& hash, std::span seed) const; + + private: + std::vector m_input_buffer; + std::span m_q; + std::span m_i; + uint8_t* m_j; +}; + +} // namespace Botan + +#endif diff --git a/src/lib/pubkey/hss_lms/info.txt b/src/lib/pubkey/hss_lms/info.txt new file mode 100644 index 00000000000..70dcb9cfcf8 --- /dev/null +++ b/src/lib/pubkey/hss_lms/info.txt @@ -0,0 +1,25 @@ + +HSS_LMS -> 20230925 + + + +name -> "HSS_LMS" + + + +hss_lms.h + + + +hss_lms_utils.h +lms.h +lm_ots.h +hss.h + + + +rng +sha2_32 +shake +trunc_hash + diff --git a/src/lib/pubkey/hss_lms/lm_ots.cpp b/src/lib/pubkey/hss_lms/lm_ots.cpp new file mode 100644 index 00000000000..2f045d67f44 --- /dev/null +++ b/src/lib/pubkey/hss_lms/lm_ots.cpp @@ -0,0 +1,297 @@ +/** + * LM-OTS - Leighton-Micali One-Time Signatures + * (C) 2023 Jack Lloyd + * 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace { +constexpr uint16_t D_PBLC = 0x8080; +constexpr uint16_t D_MESG = 0x8181; +/// For derivation of C as in https://github.com/cisco/hash-sigs +constexpr uint16_t C_INDEX = 0xFFFD; + +class Chain_Generator { + public: + Chain_Generator(const LMS_Identifier& identifier, LMS_Tree_Node_Idx q) : m_gen(identifier) { + m_gen.set_q(q.get()); + } + + void process(HashFunction& hash, + uint16_t chain_idx, + uint8_t start, + uint8_t end, + std::span in, + std::span out) { + BOTAN_ARG_CHECK(start <= end, "Start value is bigger than end value"); + + if(start == end) { + copy_into(out, in); + return; + } + m_gen.set_i(chain_idx); + + // Unroll first iteration of the loop + m_gen.set_j(start++); + m_gen.gen(out, hash, in); + + for(uint8_t j = start; j < end; ++j) { + m_gen.set_j(j); + m_gen.gen(out, hash, out); + } + } + + private: + PseudorandomKeyGeneration m_gen; +}; + +// RFC 8554 3.1.1 +uint8_t byte(std::span S, uint32_t i) { + BOTAN_ARG_CHECK(i < S.size(), "Index out of range"); + return S[i]; +} + +// RFC 8554 3.1.3 +uint8_t coef(std::span S, uint32_t i, const LMOTS_Params& params) { + return params.coef_max() & + (byte(S, (i * params.w()) / 8) >> (8 - (params.w() * (i % (8 / params.w())) + params.w()))); +} + +// RFC 8554 4.4 +uint16_t checksum(const LMOTS_Params& params, std::span S) { + size_t sum = 0; + for(uint32_t i = 0; i < (params.n() * 8 / params.w()); ++i) { + sum += params.coef_max() - coef(S, i, params); + } + return checked_cast_to(sum << params.ls()); +} + +std::vector gen_Q_with_cksm(const LMOTS_Params& params, + const LMS_Identifier& identifier, + const LMS_Tree_Node_Idx& q, + std::span C, + const LMS_Message& msg) { + std::vector Q_with_cksm(params.n() + sizeof(uint16_t)); + BufferStuffer qwc_stuffer(Q_with_cksm); + const auto hash = HashFunction::create_or_throw(params.hash_name()); + hash->update(identifier); + hash->update_be(q.get()); + hash->update_be(D_MESG); + hash->update(C); + hash->update(msg); + auto Q_span = qwc_stuffer.next(params.n()); + hash->final(Q_span); + + qwc_stuffer.append_be(checksum(params, Q_span)); + + return Q_with_cksm; +} + +} // namespace + +LMOTS_Params LMOTS_Params::create_or_throw(LMOTS_Algorithm_Type type) { + uint8_t type_value = static_cast(type); + + if(type >= LMOTS_Algorithm_Type::SHA256_N32_W1 && type <= LMOTS_Algorithm_Type::SHA256_N32_W8) { + uint8_t w = 1 << (type_value - static_cast(LMOTS_Algorithm_Type::SHA256_N32_W1)); + return LMOTS_Params(type, "SHA-256", w); + } + if(type >= LMOTS_Algorithm_Type::SHA256_N24_W1 && type <= LMOTS_Algorithm_Type::SHA256_N24_W8) { + uint8_t w = 1 << (type_value - static_cast(LMOTS_Algorithm_Type::SHA256_N24_W1)); + return LMOTS_Params(type, "Truncated(SHA-256,192)", w); + } + if(type >= LMOTS_Algorithm_Type::SHAKE_N32_W1 && type <= LMOTS_Algorithm_Type::SHAKE_N32_W8) { + uint8_t w = 1 << (type_value - static_cast(LMOTS_Algorithm_Type::SHAKE_N32_W1)); + return LMOTS_Params(type, "SHAKE-256(256)", w); + } + if(type >= LMOTS_Algorithm_Type::SHAKE_N24_W1 && type <= LMOTS_Algorithm_Type::SHAKE_N24_W8) { + uint8_t w = 1 << (type_value - static_cast(LMOTS_Algorithm_Type::SHAKE_N24_W1)); + return LMOTS_Params(type, "SHAKE-256(192)", w); + } + + throw Decoding_Error("Unsupported LM-OTS algorithm type"); +} + +LMOTS_Params LMOTS_Params::create_or_throw(std::string_view hash_name, size_t w) { + BOTAN_ARG_CHECK(w == 1 || w == 2 || w == 4 || w == 8, "Invalid w value"); + auto type_offset = high_bit(w) - 1; + LMOTS_Algorithm_Type base_type; + + if(hash_name == "SHA-256") { + base_type = LMOTS_Algorithm_Type::SHA256_N32_W1; + } else if(hash_name == "Truncated(SHA-256,192)") { + base_type = LMOTS_Algorithm_Type::SHA256_N24_W1; + } else if(hash_name == "SHAKE-256(256)") { + base_type = LMOTS_Algorithm_Type::SHAKE_N32_W1; + } else if(hash_name == "SHAKE-256(192)") { + base_type = LMOTS_Algorithm_Type::SHAKE_N24_W1; + } else { + throw Decoding_Error("Unsupported hash function"); + } + auto type = static_cast(static_cast(base_type) + type_offset); + return LMOTS_Params(type, hash_name, w); +} + +LMOTS_Params::LMOTS_Params(LMOTS_Algorithm_Type algorithm_type, std::string_view hash_name, uint8_t w) : + m_algorithm_type(algorithm_type), m_w(w), m_hash_name(hash_name) { + const auto hash = HashFunction::create_or_throw(m_hash_name); + m_n = hash->output_length(); + // RFC 8553 Appendix B - Parameter Computation + auto u = ceil_division(8 * m_n, m_w); // ceil(8*n/w) + auto v = ceil_division(high_bit(((1 << m_w) - 1) * u), m_w); // ceil((floor(lg[(2^w - 1) * u]) + 1) / w) + m_ls = 16 - (v * w); + m_p = u + v; +} + +LMOTS_Signature::LMOTS_Signature(LMOTS_Algorithm_Type lmots_type, + std::vector C, + std::vector y_buffer) : + m_algorithm_type(lmots_type), m_C(std::move(C)), m_y_buffer(std::move(y_buffer)) { + LMOTS_Params params = LMOTS_Params::create_or_throw(m_algorithm_type); + + BufferSlicer y_slicer(m_y_buffer); + for(uint16_t i = 0; i < params.p(); ++i) { + m_y.push_back(y_slicer.take(params.n())); + } +} + +LMOTS_Signature LMOTS_Signature::from_bytes_or_throw(BufferSlicer& slicer) { + size_t total_remaining_bytes = slicer.remaining(); + // Alg. 6a. 1. (last 4 bytes) / Alg. 4b. 1. + if(total_remaining_bytes < sizeof(LMOTS_Algorithm_Type)) { + throw Decoding_Error("To few signature bytes while parsing LMOTS signature."); + } + // Alg. 6a. 2.b. / Alg. 4b. 2.a. + auto algorithm_type = slicer.copy_be(); + + // Alg. 6a. 2.d. / Alg. 4b. 2.c. + LMOTS_Params params = LMOTS_Params::create_or_throw(algorithm_type); + + if(total_remaining_bytes < size(params)) { + throw Decoding_Error("To few signature bytes while parsing LMOTS signature."); + } + + // Alg. 4b. 2.d. + auto C = slicer.copy_as_vector(params.n()); + // Alg. 4b. 2.e. + auto m_y_buffer = slicer.copy_as_vector(params.p() * params.n()); + + return LMOTS_Signature(algorithm_type, std::move(C), std::move(m_y_buffer)); +} + +LMOTS_Private_Key::LMOTS_Private_Key(const LMOTS_Params& params, + const LMS_Identifier& identifier, + LMS_Tree_Node_Idx q, + const LMS_Seed& seed) : + OTS_Instance(params, identifier, q), m_seed(seed) { + PseudorandomKeyGeneration gen(identifier); + const auto hash = HashFunction::create_or_throw(params.hash_name()); + + gen.set_q(q.get()); + gen.set_j(0xff); + + for(uint16_t i = 0; i < params.p(); ++i) { + gen.set_i(i); + m_ots_sk.push_back(gen.gen(*hash, seed)); + } +} + +void LMOTS_Private_Key::sign(StrongSpan out_sig, const LMS_Message& msg) const { + BOTAN_ARG_CHECK(out_sig.size() == LMOTS_Signature::size(params()), "Invalid output buffer size"); + BufferStuffer sig_stuffer(out_sig); + const auto hash = HashFunction::create_or_throw(params().hash_name()); + sig_stuffer.append_be(params().algorithm_type()); + const auto C = sig_stuffer.next(params().n()); + + // Since we do not store the signatures of the lms trees in the HSS sk, + // we need deterministic signatures to avoid reusing a OTS key to generate multiple signatures. + // See also: lm_ots_sign.c 110-119 (of cisco implementation) + derive_random_C(C, *hash); + + const auto Q_with_cksm = gen_Q_with_cksm(params(), identifier(), q(), C, msg); + + Chain_Generator chain_gen(identifier(), q()); + for(uint16_t i = 0; i < params().p(); ++i) { + const auto y_i = sig_stuffer.next(params().n()); + const uint8_t a = coef(Q_with_cksm, i, params()); + chain_gen.process(*hash, i, 0, a, chain_input(i), y_i); + } + BOTAN_ASSERT_NOMSG(sig_stuffer.full()); +} + +void LMOTS_Private_Key::derive_random_C(std::span out, HashFunction& hash) const { + PseudorandomKeyGeneration gen(identifier()); + + gen.set_q(q().get()); + gen.set_i(C_INDEX); + gen.set_j(0xff); + + gen.gen(out, hash, m_seed); +} + +LMOTS_Public_Key::LMOTS_Public_Key(const LMOTS_Private_Key& lmots_sk) : OTS_Instance(lmots_sk) { + const auto pk_hash = HashFunction::create_or_throw(lmots_sk.params().hash_name()); + pk_hash->update(lmots_sk.identifier()); + pk_hash->update_be(lmots_sk.q().get()); + pk_hash->update_be(D_PBLC); + + Chain_Generator chain_gen(lmots_sk.identifier(), lmots_sk.q()); + const auto hash = HashFunction::create_or_throw(lmots_sk.params().hash_name()); + LMOTS_Node tmp(lmots_sk.params().n()); + for(uint16_t i = 0; i < lmots_sk.params().p(); ++i) { + chain_gen.process(*hash, i, 0, lmots_sk.params().coef_max(), lmots_sk.chain_input(i), tmp); + pk_hash->update(tmp); + } + + m_K = pk_hash->final(); +} + +LMOTS_K lmots_compute_pubkey_from_sig(const LMOTS_Signature& sig, + const LMS_Message& msg, + const LMS_Identifier& identifier, + LMS_Tree_Node_Idx q) { + auto params = LMOTS_Params::create_or_throw(sig.algorithm_type()); + + // Alg. 4b 3. + + const auto Q_with_cksm = gen_Q_with_cksm(params, identifier, q, sig.C(), msg); + + // Prefill the final hash object + const auto pk_hash = HashFunction::create_or_throw(params.hash_name()); + pk_hash->update(identifier); + pk_hash->update_be(q.get()); + pk_hash->update_be(D_PBLC); + + Chain_Generator chain_gen(identifier, q); + const auto hash = HashFunction::create_or_throw(params.hash_name()); + LMOTS_Node tmp(params.n()); + for(uint16_t i = 0; i < params.p(); ++i) { + const uint8_t a = coef(Q_with_cksm, i, params); + chain_gen.process(*hash, i, a, params.coef_max(), sig.y(i), tmp); + pk_hash->update(tmp); + } + // Alg. 4b 4. + return pk_hash->final(); +} + +} // namespace Botan diff --git a/src/lib/pubkey/hss_lms/lm_ots.h b/src/lib/pubkey/hss_lms/lm_ots.h new file mode 100644 index 00000000000..4063fb7299d --- /dev/null +++ b/src/lib/pubkey/hss_lms/lm_ots.h @@ -0,0 +1,343 @@ +/** + * LM-OTS - Leighton-Micali One-Time Signatures (RFC 8554 Section 4) + * (C) 2023 Jack Lloyd + * 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_LM_OTS_H_ +#define BOTAN_LM_OTS_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +/** + * @brief Seed of the LMS tree, used to generate the LM-OTS private keys. + */ +using LMS_Seed = Strong, struct LMS_SEED_>; + +/** + * @brief One node within one LM-OTS hash chain. + */ +using LMOTS_Node = Strong, struct LMOTS_Node_>; + +/** + * @brief The K value from the LM-OTS public key. + */ +using LMOTS_K = Strong, struct LMOTS_K_>; + +/** + * @brief Byte vector of an LM-OTS signature. + */ +using LMOTS_Signature_Bytes = Strong, struct LMOTS_Signature_Bytes_>; + +/** + * @brief The index of a node within a specific LMS tree layer + */ +using LMS_Tree_Node_Idx = Strong; + +/** + * @brief The identifier of an LMS tree (I in RFC 8554) + */ +using LMS_Identifier = Strong, struct LMS_Identifier_>; + +/** + * @brief A message that is signed with an LMS tree + */ +using LMS_Message = Strong, struct LMS_Message_>; + +/** + * @brief Enum of available LM-OTS algorithm types. + * + * See RFC 8554 Section 4.1. + */ +enum class LMOTS_Algorithm_Type : uint32_t { + // --- RFC 8554 --- + RESERVED = 0x00, + + // SHA-256 based + SHA256_N32_W1 = 0x01, + SHA256_N32_W2 = 0x02, + SHA256_N32_W4 = 0x03, + SHA256_N32_W8 = 0x04, + + // --- draft-fluhrer-lms-more-parm-sets-11 --- + // SHA-256/192 based + SHA256_N24_W1 = 0x05, + SHA256_N24_W2 = 0x06, + SHA256_N24_W4 = 0x07, + SHA256_N24_W8 = 0x08, + + // SHAKE-256/256 based + SHAKE_N32_W1 = 0x09, + SHAKE_N32_W2 = 0x0a, + SHAKE_N32_W4 = 0x0b, + SHAKE_N32_W8 = 0x0c, + + // SHAKE-256/192 based + SHAKE_N24_W1 = 0x0d, + SHAKE_N24_W2 = 0x0e, + SHAKE_N24_W4 = 0x0f, + SHAKE_N24_W8 = 0x10, +}; + +/** + * @brief The LM-OTS parameters. + * + * See RFC 8554 Section 4.1. + */ +class LMOTS_Params { + public: + /** + * @brief Create the LM-OTS parameters from a known algorithm type. + * @throws Decoding_Error If the algorithm type is unknown + */ + static LMOTS_Params create_or_throw(LMOTS_Algorithm_Type type); + + /** + * @brief Create the LM-OTS parameters from a hash function and width. + * + * @param hash_name tha name of the hash function to use. + * @param w the width (in bits) of the Winternitz coefficients. + * @throws Decoding_Error If the algorithm type is unknown + */ + static LMOTS_Params create_or_throw(std::string_view hash_name, size_t w); + + /** + * @brief Returns the LM-OTS algorithm type. + */ + LMOTS_Algorithm_Type algorithm_type() const { return m_algorithm_type; } + + /** + * @brief The number of bytes of the output of the hash function. + */ + size_t n() const { return m_n; } + + /** + * @brief The width (in bits) of the Winternitz coefficients. + */ + uint8_t w() const { return m_w; } + + /** + * @brief The maximum the winternitz coefficients can have. + */ + uint8_t coef_max() const { return (1 << m_w) - 1; } + + /** + * @brief The number of n-byte string elements that make up the LM-OTS signature. + */ + uint16_t p() const { return m_p; } + + /** + * @brief The number of left-shift bits used in the checksum function Cksm. + */ + uint8_t ls() const { return m_ls; } + + /** + * @brief Name of the hash function to use. + */ + const std::string& hash_name() const { return m_hash_name; } + + private: + /** + * @brief Construct a new LM-OTS parameter object. + * + * @param algorithm_type The algorithm type. + * @param hash_name The name of the hash function to use. + * @param w The width (in bits) of the Winternitz coefficients. + */ + LMOTS_Params(LMOTS_Algorithm_Type algorithm_type, std::string_view hash_name, uint8_t w); + + LMOTS_Algorithm_Type m_algorithm_type; + size_t m_n; + uint8_t m_w; + uint16_t m_p; + uint8_t m_ls; + std::string m_hash_name; +}; + +/** + * @brief Representation of a LM-OTS signature. + */ +class LMOTS_Signature { + public: + /** + * @brief Parse a LM-OTS signature. + * + * @param slicer The private key bytes to parse. + * @return The LM-OTS signature. + * @throws Decoding_Error If parsing the signature fails. + */ + static LMOTS_Signature from_bytes_or_throw(BufferSlicer& slicer); + + /** + * @brief Returns the LM-OTS algorithm type. + */ + LMOTS_Algorithm_Type algorithm_type() const { return m_algorithm_type; } + + /** + * @brief The n-byte randomizer of the signature. + */ + std::span C() const { return m_C; } + + /** + * @brief Returns the part of the signature for @p chain_idx. + */ + StrongSpan y(uint16_t chain_idx) const { return m_y.at(chain_idx); } + + /** + * @brief The expected size of the signature. + */ + static size_t size(const LMOTS_Params& params) { return 4 + params.n() * (params.p() + 1); } + + private: + LMOTS_Signature(LMOTS_Algorithm_Type lmots_type, std::vector C, std::vector y_buffer); + + LMOTS_Algorithm_Type m_algorithm_type; + std::vector m_C; + std::vector m_y_buffer; + std::vector> m_y; +}; + +/** + * @brief Base class for LMOTS private and public key. Contains the parameters for + * the specific OTS instance + */ +class OTS_Instance { + public: + /** + * @brief Constructor storing the specific OTS parameters + */ + OTS_Instance(const LMOTS_Params& params, const LMS_Identifier& identifier, LMS_Tree_Node_Idx q) : + m_params(params), m_identifier(identifier), m_q(q) {} + + /** + * @brief The LMOTS parameters + */ + const LMOTS_Params& params() const { return m_params; } + + /** + * @brief The LMS identifier of the LMS tree containing this OTS instance ('I' in RFC 8554) + */ + const LMS_Identifier& identifier() const { return m_identifier; } + + /** + * @brief The index of the LMS tree leaf associated with this OTS instance + */ + LMS_Tree_Node_Idx q() const { return m_q; } + + private: + LMOTS_Params m_params; + LMS_Identifier m_identifier; + LMS_Tree_Node_Idx m_q; +}; + +/** + * @brief Representation of an LMOTS private key. + * + * Contains the OTS params, I, q, the secret LMS seed and its derived + * secret chain inputs (x[] in RFC 8554 4.2) + */ +class LMOTS_Private_Key : public OTS_Instance { + public: + /** + * @brief Derive a LMOTS private key for a given @p seed. + * + * Implements RFC 8554 4.2 using derivation of Appendix A + */ + LMOTS_Private_Key(const LMOTS_Params& params, + const LMS_Identifier& identifier, + LMS_Tree_Node_Idx q, + const LMS_Seed& seed); + + /** + * @brief The secret chain input at a given chain index. (x[] in RFC 8554 4.2). + */ + const LMOTS_Node& chain_input(uint16_t chain_idx) const { return m_ots_sk.at(chain_idx); } + + /** + * @brief Generate a new LMOTS signature. + * + * Defined in RFC 8554 4.5 + */ + void sign(StrongSpan out_sig, const LMS_Message& msg) const; + + private: + /** + * @brief Derive random value C + * + * Derive the randomized value C as in the reference implementation (cisco): + * C = HASH(I || Q || 0xFFFD || 0xFF || SEED) + * + * Note that this derivation is important if we do not store the signature of root LMS nodes + * in the private key. Otherwise these root nodes are signed twice with different C values, + * resulting in a broken OTS signature. + */ + void derive_random_C(std::span out, HashFunction& hash) const; + + LMS_Seed m_seed; + std::vector m_ots_sk; +}; + +/** + * @brief Representation of an OTS public key. + * + * Contains the public key bytes + * as defined in RFC 8554 4.3: + * + * u32str(type) || I || u32str(q) || K + */ +class LMOTS_Public_Key : public OTS_Instance { + public: + /** + * @brief Derivivation of an LMOTS public key using an LMOTS_Private_Key as defined + * in RFC 8554 4.3 + */ + LMOTS_Public_Key(const LMOTS_Private_Key& lmots_sk); + + /** + * @brief Construct a new LMOTS public key object using the bytes. + * + * Note that the passed params, identifier and + * q value should match with the prefix in @p pub_key_bytes. + */ + LMOTS_Public_Key(const LMOTS_Params& params, const LMS_Identifier& identifier, LMS_Tree_Node_Idx q, LMOTS_K K) : + OTS_Instance(params, identifier, q), m_K(std::move(K)) {} + + /** + * @brief The public key final hash value (K in RFC 8554 4.3 ) + * + * @return const LMOTS_K& + */ + const LMOTS_K& K() const { return m_K; } + + private: + LMOTS_K m_K; +}; + +/** + * @brief Compute a public key candidate for an OTS-signature-message pair and the OTS instance parameters. + * + * Defined in RFC 8554 4.6 - Algorithm 4b + */ +BOTAN_TEST_API LMOTS_K lmots_compute_pubkey_from_sig(const LMOTS_Signature& sig, + const LMS_Message& msg, + const LMS_Identifier& identifier, + LMS_Tree_Node_Idx q); + +} // namespace Botan + +#endif diff --git a/src/lib/pubkey/hss_lms/lms.cpp b/src/lib/pubkey/hss_lms/lms.cpp new file mode 100644 index 00000000000..f7546152334 --- /dev/null +++ b/src/lib/pubkey/hss_lms/lms.cpp @@ -0,0 +1,348 @@ +/** + * LMS - Leighton-Micali Hash-Based Signatures (RFC 8554) + * (C) 2023 Jack Lloyd + * 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include + +#include + +namespace Botan { +namespace { + +/** + * @brief Domain-separation parameter when computing the hash of the leaf of an LMS tree. + */ +constexpr uint16_t D_LEAF = 0x8282; + +/** + * @brief Domain-separation parameter when computing the hash of an interior node of an LMS tree. + */ +constexpr uint16_t D_INTR = 0x8383; + +/// Index of an individual node on a specific layer inside the tree +//using LMS_Leaf_Idx = Strong; +/// Index of the layer in a tree starting with 0 for the bottom level to the root layer +using LMS_TreeLayerIndex = Strong; + +class TreeAddress final { + public: + explicit TreeAddress(uint32_t total_tree_height) : m_h(total_tree_height) { + BOTAN_ARG_CHECK(total_tree_height > 0 && total_tree_height < 32, "Invalid tree hight"); + } + + TreeAddress& set_address(LMS_TreeLayerIndex tree_layer, LMS_Tree_Node_Idx tree_index) { + BOTAN_ARG_CHECK(tree_index.get() < (1 << m_h), "Invalid tree index"); + BOTAN_ARG_CHECK(tree_layer.get() <= m_h, "Invalid tree index"); + m_r = (1 << (m_h - tree_layer)).get() + tree_index.get(); + return *this; + } + + uint32_t r() const { return m_r; } + + bool is_leaf() const { return m_r >= (1 << m_h); } + + LMS_Tree_Node_Idx q() const { + BOTAN_STATE_CHECK(is_leaf()); + return LMS_Tree_Node_Idx(m_r - (1 << m_h.get())); + } + + private: + LMS_TreeLayerIndex m_h; + uint32_t m_r; +}; + +std::function, const TreeAddress&, StrongSpan, StrongSpan)> +get_hash_pair_func_for_identifier(const LMS_Params& lms_params, LMS_Identifier identifier) { + // hash object must be shared, otherwise std::function would not be copyable, which is not allowed + std::shared_ptr hash = HashFunction::create_or_throw(lms_params.hash_name()); + return [hash, I = std::move(identifier)](StrongSpan out, + const TreeAddress& address, + StrongSpan left, + StrongSpan right) { + auto lms_address = dynamic_cast(address); + + hash->update(I); + hash->update_be(lms_address.r()); + hash->update_be(D_INTR); + hash->update(left); + hash->update(right); + hash->final(out); + }; +} + +void lms_gen_leaf(StrongSpan out, + const LMOTS_Public_Key& lmots_pk, + const TreeAddress& tree_address, + HashFunction& hash) { + hash.update(lmots_pk.identifier()); + hash.update_be(tree_address.r()); + hash.update_be(D_LEAF); + hash.update(lmots_pk.K()); + hash.final(out); +} + +std::function out, const TreeAddress& address)> lms_gen_leaf_func( + const LMS_PrivateKey& lms_sk) { + // hash object must be shared, otherwise std::function would not be copyable, which is not allowed + std::shared_ptr hash = HashFunction::create_or_throw(lms_sk.lms_params().hash_name()); + return [lms_sk, hash](StrongSpan out, const TreeAddress& tree_address) { + auto lmots_sk = LMOTS_Private_Key(lms_sk.lmots_params(), lms_sk.identifier(), tree_address.q(), lms_sk.seed()); + auto lmots_pk = LMOTS_Public_Key(lmots_sk); + lms_gen_leaf(out, lmots_pk, tree_address, *hash); + }; +} + +void lms_treehash(StrongSpan out_root, + std::optional> out_auth_path, + std::optional leaf_idx, + const LMS_PrivateKey& lms_sk) { + auto hash_pair_func = get_hash_pair_func_for_identifier(lms_sk.lms_params(), lms_sk.identifier()); + auto gen_leaf = lms_gen_leaf_func(lms_sk); + TreeAddress lms_tree_address(lms_sk.lms_params().h()); + + treehash(out_root, + out_auth_path, + leaf_idx, + lms_sk.lms_params().m(), + LMS_TreeLayerIndex(lms_sk.lms_params().h()), + 0, + hash_pair_func, + gen_leaf, + lms_tree_address); +} + +} // namespace + +LMS_Params LMS_Params::create_or_throw(LMS_Algorithm_Type type) { + uint8_t type_value = static_cast(type); + + if(type >= LMS_Algorithm_Type::SHA256_M32_H5 && type <= LMS_Algorithm_Type::SHA256_M32_H25) { + uint8_t h = 5 * (type_value - static_cast(LMS_Algorithm_Type::SHA256_M32_H5) + 1); + return LMS_Params(type, "SHA-256", h); + } + if(type >= LMS_Algorithm_Type::SHA256_M24_H5 && type <= LMS_Algorithm_Type::SHA256_M24_H25) { + uint8_t h = 5 * (type_value - static_cast(LMS_Algorithm_Type::SHA256_M24_H5) + 1); + return LMS_Params(type, "Truncated(SHA-256,192)", h); + } + if(type >= LMS_Algorithm_Type::SHAKE_M32_H5 && type <= LMS_Algorithm_Type::SHAKE_M32_H25) { + uint8_t h = 5 * (type_value - static_cast(LMS_Algorithm_Type::SHAKE_M32_H5) + 1); + return LMS_Params(type, "SHAKE-256(256)", h); + } + if(type >= LMS_Algorithm_Type::SHAKE_M24_H5 && type <= LMS_Algorithm_Type::SHAKE_M24_H25) { + uint8_t h = 5 * (type_value - static_cast(LMS_Algorithm_Type::SHAKE_M24_H5) + 1); + return LMS_Params(type, "SHAKE-256(192)", h); + } + + throw Decoding_Error("Unsupported LMS algorithm type"); +} + +LMS_Params LMS_Params::create_or_throw(std::string_view hash_name, size_t h) { + BOTAN_ARG_CHECK(h == 5 || h == 10 || h == 15 || h == 20 || h == 25, "Invalid h value"); + auto type_offset = h / 5 - 1; + LMS_Algorithm_Type base_type; + + if(hash_name == "SHA-256") { + base_type = LMS_Algorithm_Type::SHA256_M32_H5; + } else if(hash_name == "Truncated(SHA-256,192)") { + base_type = LMS_Algorithm_Type::SHA256_M24_H5; + } else if(hash_name == "SHAKE-256(256)") { + base_type = LMS_Algorithm_Type::SHAKE_M32_H5; + } else if(hash_name == "SHAKE-256(192)") { + base_type = LMS_Algorithm_Type::SHAKE_M24_H5; + } else { + throw Decoding_Error("Unsupported hash function"); + } + auto type = static_cast(static_cast(base_type) + type_offset); + return LMS_Params(type, hash_name, h); +} + +LMS_Params::LMS_Params(LMS_Algorithm_Type algorithm_type, std::string_view hash_name, uint8_t h) : + m_algorithm_type(algorithm_type), m_h(h), m_hash_name(hash_name) { + const auto hash = HashFunction::create_or_throw(m_hash_name); + m_m = hash->output_length(); +} + +LMS_PublicKey LMS_PrivateKey::sign_and_get_pk(StrongSpan out_sig, + LMS_Tree_Node_Idx q, + const LMS_Message& msg) const { + // Pre-alloc space for the signature + BOTAN_ARG_CHECK(out_sig.size() == LMS_Signature::size(lms_params(), lmots_params()), "Invalid output buffer size"); + + BufferStuffer sig_stuffer(out_sig); + sig_stuffer.append_be(q); + const LMOTS_Private_Key lmots_sk(lmots_params(), identifier(), q, seed()); + lmots_sk.sign(sig_stuffer.next(LMOTS_Signature::size(lmots_params())), msg); + sig_stuffer.append_be(lms_params().algorithm_type()); + const auto auth_path_buffer = sig_stuffer.next(lms_params().m() * lms_params().h()); + + BOTAN_ASSERT_NOMSG(sig_stuffer.full()); + + TreeAddress lms_tree_address(lms_params().h()); + LMS_Tree_Node pk_buffer(lms_params().m()); + lms_treehash(StrongSpan(pk_buffer.get()), auth_path_buffer, q, *this); + + return LMS_PublicKey(lms_params(), lmots_params(), identifier(), std::move(pk_buffer)); +} + +LMS_PublicKey LMS_PublicKey::from_bytes_of_throw(BufferSlicer& slicer) { + size_t total_remaining_bytes = slicer.remaining(); + // Alg. 6. 1. (4 bytes are sufficient until the next check) + if(total_remaining_bytes < sizeof(LMS_Algorithm_Type)) { + throw Decoding_Error("To few bytes while parsing LMS public key."); + } + // Alg. 6. 2.a. + auto lms_type = slicer.copy_be(); + // Alg. 6. 2.c. + auto lms_params = LMS_Params::create_or_throw(lms_type); + // Alg. 6. 2.d. + if(total_remaining_bytes < size(lms_params)) { + throw Decoding_Error("To few bytes while parsing LMS public key."); + } + // Alg. 6. 2.b. + auto lmots_type = slicer.copy_be(); + auto lmots_params = LMOTS_Params::create_or_throw(lmots_type); + + // Alg. 6. 2.e. + auto I = slicer.copy(LMS_IDENTIFIER_LEN); + // Alg. 6. 2.f. + auto lms_root = slicer.copy(lms_params.m()); + + return LMS_PublicKey(std::move(lms_params), std::move(lmots_params), std::move(I), std::move(lms_root)); +} + +std::vector LMS_PublicKey::to_bytes() const { + std::vector bytes(size(lms_params())); + BufferStuffer stuffer(bytes); + + stuffer.append_be(lms_params().algorithm_type()); + stuffer.append_be(lmots_params().algorithm_type()); + stuffer.append(identifier()); + stuffer.append(m_lms_root); + BOTAN_ASSERT_NOMSG(stuffer.full()); + + return bytes; +} + +LMS_PublicKey::LMS_PublicKey(LMS_Params lms_params, + LMOTS_Params lmots_params, + LMS_Identifier I, + LMS_Tree_Node lms_root) : + LMS_Instance(std::move(lms_params), std::move(lmots_params), std::move(I)), m_lms_root(std::move(lms_root)) { + BOTAN_ARG_CHECK(identifier().size() == LMS_IDENTIFIER_LEN, "Invalid LMS identifier"); + BOTAN_ARG_CHECK(m_lms_root.size() == this->lms_params().m(), "Invalid LMS root"); +} + +size_t LMS_PublicKey::size(const LMS_Params& lms_params) { + return sizeof(LMS_Algorithm_Type) + sizeof(LMOTS_Algorithm_Type) + LMS_IDENTIFIER_LEN + lms_params.m(); +} + +LMS_Signature LMS_Signature::from_bytes_or_throw(BufferSlicer& slicer) { + size_t total_remaining_bytes = slicer.remaining(); + // Alg. 6a 1. (next 4 bytes are checked in LMOTS_Signature::from_bytes_or_throw) + if(total_remaining_bytes < sizeof(LMS_Tree_Node_Idx)) { + throw Decoding_Error("To few signature bytes while parsing LMS signature."); + } + // Alg. 6a 2.a. + auto q = slicer.copy_be(); + + // Alg. 6a 2.b.-e. + auto lmots_sig = LMOTS_Signature::from_bytes_or_throw(slicer); + LMOTS_Params lmots_params = LMOTS_Params::create_or_throw(lmots_sig.algorithm_type()); + + if(slicer.remaining() < sizeof(LMS_Algorithm_Type)) { + throw Decoding_Error("To few signature bytes while parsing LMS signature."); + } + // Alg. 6a 2.f. + auto lms_type = slicer.copy_be(); + // Alg. 6a 2.h. + LMS_Params lms_params = LMS_Params::create_or_throw(lms_type); + // Alg. 6a 2.i. (signature is not exactly [...] bytes long) + if(total_remaining_bytes < size(lms_params, lmots_params)) { + throw Decoding_Error("To few signature bytes while parsing LMS signature."); + } + + // Alg. 6a 2.j. + auto auth_path = slicer.take(lms_params.m() * lms_params.h()); + + return LMS_Signature(q, std::move(lmots_sig), lms_type, LMS_AuthenticationPath(auth_path)); +} + +LMS_PublicKey::LMS_PublicKey(const LMS_PrivateKey& sk) : LMS_Instance(sk), m_lms_root(sk.lms_params().m()) { + lms_treehash(StrongSpan(m_lms_root), std::nullopt, std::nullopt, sk); +} + +bool LMS_PublicKey::verify_signature(const LMS_Message& msg, const LMS_Signature& sig) const { + if(lms_root().size() != lms_params().m()) { + // LMS public key (T[1] part) has unexpected length + return false; + } + if(lms_params().algorithm_type() != sig.lms_type()) { + // LMS algorithm type does not match with the signature's + return false; + } + // Alg. 6a 2.g. + if(lmots_params().algorithm_type() != sig.lmots_sig().algorithm_type()) { + // LMOTS algorithm type does not match with the signature's + return false; + } + // Alg. 6a 2.i. + if(sig.q() >= (1ULL << uint64_t(lms_params().h()))) { + return false; + } + // Alg 6. 3. + std::optional Tc = lms_compute_root_from_sig(msg, sig); + if(!Tc.has_value()) { + return false; + } + // Alg 6. 4. + return Tc.value() == lms_root(); +} + +std::optional LMS_PublicKey::lms_compute_root_from_sig(const LMS_Message& msg, + const LMS_Signature& sig) const { + // Alg. 6a 2.c, 2.g + if(lms_params().algorithm_type() != sig.lms_type() || + lmots_params().algorithm_type() != sig.lmots_sig().algorithm_type()) { + return std::nullopt; + } + + const LMS_Params lms_params = LMS_Params::create_or_throw(sig.lms_type()); + const LMOTS_Signature& lmots_sig = sig.lmots_sig(); + const LMOTS_Params lmots_params = LMOTS_Params::create_or_throw(lmots_sig.algorithm_type()); + const LMOTS_K Kc = lmots_compute_pubkey_from_sig(lmots_sig, msg, identifier(), sig.q()); + const auto hash = HashFunction::create_or_throw(lms_params.hash_name()); + + auto hash_pair_func = get_hash_pair_func_for_identifier(lms_params, identifier()); + + auto lms_address = TreeAddress(lms_params.h()); + lms_address.set_address(LMS_TreeLayerIndex(0), LMS_Tree_Node_Idx(sig.q().get())); + + LMOTS_Public_Key pk_candidate(lmots_params, identifier(), sig.q(), Kc); + LMS_Tree_Node tmp(lms_params.m()); + lms_gen_leaf(tmp, pk_candidate, lms_address, *hash); + + LMS_Tree_Node root(lms_params.m()); + + compute_root(StrongSpan(root), + sig.auth_path(), + sig.q(), + StrongSpan(tmp), + lms_params.m(), + LMS_TreeLayerIndex(lms_params.h()), + 0, + hash_pair_func, + lms_address); + + return LMS_Tree_Node(root); +} + +size_t LMS_Signature::size(const LMS_Params& lms_params, const LMOTS_Params& lmots_params) { + return sizeof(uint32_t) + LMOTS_Signature::size(lmots_params) + sizeof(uint32_t) + lms_params.h() * lms_params.m(); +} + +} // namespace Botan diff --git a/src/lib/pubkey/hss_lms/lms.h b/src/lib/pubkey/hss_lms/lms.h new file mode 100644 index 00000000000..52c94f53a2a --- /dev/null +++ b/src/lib/pubkey/hss_lms/lms.h @@ -0,0 +1,343 @@ +/** + * LMS - Leighton-Micali Hash-Based Signatures (RFC 8554) + * (C) 2023 Jack Lloyd + * 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_LMS_H_ +#define BOTAN_LMS_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Botan { + +/** + * @brief Enum of available LMS algorithm types. + * + * See RFC 8554 Section 5.1. + */ +enum class LMS_Algorithm_Type : uint32_t { + // --- RFC 8554 --- + RESERVED = 0x00, + + // SHA-256 based + SHA256_M32_H5 = 0x05, + SHA256_M32_H10 = 0x06, + SHA256_M32_H15 = 0x07, + SHA256_M32_H20 = 0x08, + SHA256_M32_H25 = 0x09, + + // --- draft-fluhrer-lms-more-parm-sets-11 --- + // SHA-256/192 based + SHA256_M24_H5 = 0x0a, + SHA256_M24_H10 = 0x0b, + SHA256_M24_H15 = 0x0c, + SHA256_M24_H20 = 0x0d, + SHA256_M24_H25 = 0x0e, + + // SHAKE-256/256 based + SHAKE_M32_H5 = 0x0f, + SHAKE_M32_H10 = 0x10, + SHAKE_M32_H15 = 0x11, + SHAKE_M32_H20 = 0x12, + SHAKE_M32_H25 = 0x13, + + // SHAKE-256/192 based + SHAKE_M24_H5 = 0x14, + SHAKE_M24_H10 = 0x15, + SHAKE_M24_H15 = 0x16, + SHAKE_M24_H20 = 0x17, + SHAKE_M24_H25 = 0x18 +}; + +/** + * @brief The length in bytes of the LMS identifier (I). + */ +constexpr size_t LMS_IDENTIFIER_LEN = 16; + +/** + * @brief The authentication path of an LMS signature + */ +using LMS_AuthenticationPath = Strong, struct LMS_AuthenticationPath_>; + +/** + * @brief A node with the LMS tree + */ +using LMS_Tree_Node = Strong, struct LMS_Tree_Node_>; + +/** + * @brief Raw bytes of an LMS signature + */ +using LMS_Signature_Bytes = Strong, struct LMS_Signature_Bytes_>; + +/** + * @brief The LMS parameters. + * + * See RFC 8554 Section 5.1. + */ +class LMS_Params { + public: + /** + * @brief Create the LMS parameters from a known algorithm type. + * @throws Decoding_Error If the algorithm type is unknown + */ + static LMS_Params create_or_throw(LMS_Algorithm_Type type); + + /** + * @brief Create the LMS parameters from a hash function and tree height. + * + * @param hash_name The name of the hash function to use. + * @param h The height of the tree. + * @throws Decoding_Error If the algorithm type is unknown + */ + static LMS_Params create_or_throw(std::string_view hash_name, size_t h); + + /** + * @brief Retuns the LMS algorithm type. + */ + LMS_Algorithm_Type algorithm_type() const { return m_algorithm_type; } + + /** + * @brief Returns the height of the LMS tree. + */ + uint8_t h() const { return m_h; } + + /** + * @brief Returns the number of bytes associated with each node. + */ + size_t m() const { return m_m; } + + /** + * @brief Returns the name of the hash function to use. + */ + const std::string& hash_name() const { return m_hash_name; } + + private: + /** + * @brief Construct a new LMS parameter object. + * + * @param algorithm_type The algorithm type. + * @param hash_name The name of the hash function to use. + * @param h The height of the tree. + */ + LMS_Params(LMS_Algorithm_Type algorithm_type, std::string_view hash_name, uint8_t h); + + LMS_Algorithm_Type m_algorithm_type; + uint8_t m_h; + size_t m_m; + std::string m_hash_name; +}; + +/** + * @brief Base class for LMS private and public key. Contains public data associated with this + * LMS instance. + */ +class LMS_Instance { + public: + /** + * @brief Constructor storing the provided LMS data. + */ + LMS_Instance(LMS_Params lms_params, LMOTS_Params lmots_params, LMS_Identifier identifier) : + m_lms_params(std::move(lms_params)), + m_lmots_params(std::move(lmots_params)), + m_identifier(std::move(identifier)) {} + + /** + * @brief The LMS parameters for this LMS instance. + */ + const LMS_Params& lms_params() const { return m_lms_params; } + + /** + * @brief The LMOTS parameters used for OTS instances of this LMS instance. + */ + const LMOTS_Params& lmots_params() const { return m_lmots_params; } + + /** + * @brief The identifier of this LMS tree ('I' in RFC 8554) + */ + const LMS_Identifier& identifier() const { return m_identifier; } + + private: + LMS_Params m_lms_params; + LMOTS_Params m_lmots_params; + LMS_Identifier m_identifier; +}; + +class LMS_PublicKey; + +/** + * @brief Representation of an LMS Private key + * + * Contains the secret seed used for OTS key derivation + * as described in RFC 8554 Appendix A. + */ +class LMS_PrivateKey : public LMS_Instance { + public: + /** + * @brief Construct storing the LMS instance data and the secret seed + */ + LMS_PrivateKey(LMS_Params lms_params, LMOTS_Params lmots_params, LMS_Identifier I, LMS_Seed seed) : + LMS_Instance(std::move(lms_params), std::move(lmots_params), std::move(I)), m_seed(std::move(seed)) {} + + /** + * @brief The secret seed used for LMOTS' WOTS chain input creation (RFC 8554 Appendix A) + */ + const LMS_Seed& seed() const { return m_seed; } + + /** + * @brief Sign a message using an LMS_PrivateKey and the used leaf index (RFC 8554 5.4.1). + * + * The signature is written in the provided buffer. The LMS_PublicKey + * associated with the given private key is returned. + */ + LMS_PublicKey sign_and_get_pk(StrongSpan out_sig, + LMS_Tree_Node_Idx q, + const LMS_Message& msg) const; + + private: + LMS_Seed m_seed; +}; + +class LMS_Signature; + +/** + * @brief The LMS public key. + * + * Format according to RFC 8554: + * u32str(type) || u32str(otstype) || I || T[1] + */ +class LMS_PublicKey : public LMS_Instance { + public: + /** + * @brief Parse a public LMS key. + * + * @param key_bytes The public key bytes to parse. + * @return The LMS public key. + * @throws Decoding_Error If parsing the public key fails. + */ + static LMS_PublicKey from_bytes_of_throw(BufferSlicer& slicer); + + /** + * @brief Construct a public key for given public key data + */ + LMS_PublicKey(LMS_Params lms_params, LMOTS_Params lmots_params, LMS_Identifier I, LMS_Tree_Node lms_root); + + /** + * @brief Construct a new public key from a given LMS private key (RFC 8554 5.3). + */ + LMS_PublicKey(const LMS_PrivateKey& sk); + + /** + * @brief Bytes of the full lms public key according to 8554 5.3 + * + * pub_key_bytes = u32str(type) || u32str(otstype) || I || T[1] + */ + std::vector to_bytes() const; + + /** + * @brief The expected size of an LMS public key for given @p lms_params + */ + static size_t size(const LMS_Params& lms_params); + + /** + * @brief Verify a LMS signature. + * + * See RFC 8554 5.4.2 - Algorithm 6. + * + * @param msg The signed message. + * @param sig The already parsed LMS signature. + * @return True if the signature is valid, false otherwise. + */ + bool verify_signature(const LMS_Message& msg, const LMS_Signature& sig) const; + + private: + /** + * @brief Compute an lms public key candidate. + * + * Given the LMS public key, a LMS-Signature-LMS_Message pair, compute + * an LMS public key candidate as described in RFC 8554 5.4.2 Algorithm 6a. + */ + std::optional lms_compute_root_from_sig(const LMS_Message& msg, const LMS_Signature& sig) const; + + /** + * @brief Root node of the LMS tree ('T[1]' in RFC 8554 5.3) + */ + const LMS_Tree_Node& lms_root() const { return m_lms_root; } + + LMS_Tree_Node m_lms_root; +}; + +/** + * @brief Container for LMS Signature data. + * + * Contains a method for secure signature parsing. + */ +class LMS_Signature { + public: + /** + * @brief Parse the bytes of a lms signature into a LMS Signature object + * + * @param slicer A BufferSlicer object at the position of the LMS_Signature to parse + * @return LMS_Signature object + * @throws Decoding_Error If parsing the signature fails. + */ + static LMS_Signature from_bytes_or_throw(BufferSlicer& slicer); + + /** + * @brief The index of the signing leaf given by the signature + */ + LMS_Tree_Node_Idx q() const { return m_q; } + + /** + * @brief The LMOTS signature object containing the parsed LMOTS signature bytes + * contained in the LMS signature + */ + const LMOTS_Signature& lmots_sig() const { return m_lmots_sig; } + + /** + * @brief The LMS algorithm type given by the signature + */ + LMS_Algorithm_Type lms_type() const { return m_lms_type; } + + /** + * @brief The authentication path bytes given by the signature + * + * ('path[0] || ... || path[h-1]' in RFC 8554 5.4) + */ + StrongSpan auth_path() const { return m_auth_path; } + + /** + * @return size_t The expected size of the signature. + */ + static size_t size(const LMS_Params& lms_params, const LMOTS_Params& lmots_params); + + private: + /** + * @brief Private constructor storing the data fields individually + */ + LMS_Signature(LMS_Tree_Node_Idx q, + LMOTS_Signature lmots_sig, + LMS_Algorithm_Type lms_type, + LMS_AuthenticationPath auth_path) : + m_q(q), m_lmots_sig(std::move(lmots_sig)), m_lms_type(lms_type), m_auth_path(std::move(auth_path)) {} + + LMS_Tree_Node_Idx m_q; + LMOTS_Signature m_lmots_sig; + LMS_Algorithm_Type m_lms_type; + LMS_AuthenticationPath m_auth_path; +}; + +} // namespace Botan + +#endif diff --git a/src/lib/pubkey/pk_algs.cpp b/src/lib/pubkey/pk_algs.cpp index 42ed503367b..a42587b1144 100644 --- a/src/lib/pubkey/pk_algs.cpp +++ b/src/lib/pubkey/pk_algs.cpp @@ -70,6 +70,10 @@ #include #endif +#if defined(BOTAN_HAS_HSS_LMS) + #include +#endif + #if defined(BOTAN_HAS_XMSS_RFC8391) #include #endif @@ -190,6 +194,12 @@ std::unique_ptr load_public_key(const AlgorithmIdentifier& alg_id, } #endif +#if defined(BOTAN_HAS_HSS_LMS) + if(alg_name == "HSS-LMS") { + return std::make_unique(key_bits); + } +#endif + #if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) if(alg_name == "SPHINCS+" || alg_name.starts_with("SphincsPlus-")) { return std::make_unique(alg_id, key_bits); @@ -301,6 +311,12 @@ std::unique_ptr load_private_key(const AlgorithmIdentifier& alg_id, } #endif +#if defined(BOTAN_HAS_HSS_LMS) + if(alg_name == "HSS-LMS") { + return std::make_unique(key_bits); + } +#endif + #if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) if(alg_name == "SPHINCS+" || alg_name.starts_with("SphincsPlus-")) { return std::make_unique(alg_id, key_bits); @@ -424,6 +440,12 @@ std::unique_ptr create_private_key(std::string_view alg_name, } #endif +#if defined(BOTAN_HAS_HSS_LMS) + if(alg_name == "HSS-LMS") { + return std::make_unique(rng, params); + } +#endif + #if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) if(alg_name == "SPHINCS+" || alg_name == "SphincsPlus-") { auto sphincs_params = Sphincs_Parameters::create(params); diff --git a/src/lib/utils/info.txt b/src/lib/utils/info.txt index 20b874b7dc6..cf13472cabd 100644 --- a/src/lib/utils/info.txt +++ b/src/lib/utils/info.txt @@ -45,6 +45,7 @@ safeint.h scan_name.h stl_util.h timer.h +tree_hash.h diff --git a/src/lib/utils/tree_hash.h b/src/lib/utils/tree_hash.h new file mode 100644 index 00000000000..5deb7489e97 --- /dev/null +++ b/src/lib/utils/tree_hash.h @@ -0,0 +1,269 @@ +/** + * Treehash logic used for hash-based signatures + * (C) 2023 Jack Lloyd + * 2023 Fabian Albert, René Meusel, Amos Treiber, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH + * + * Parts of this file have been adapted from https://github.com/sphincs/sphincsplus + * + * Botan is released under the Simplified BSD License (see license.txt) + */ +#ifndef BOTAN_TREE_HASH_H_ +#define BOTAN_TREE_HASH_H_ + +#include +#include +#include + +#include +#include +#include +#include + +namespace Botan { + +namespace concepts { + +template +concept tree_node = contiguous_container; + +/** + * @brief An index of a node in a layer. + * + * This is a separate index for each layer. + * The left most node of a layer has the index 0. + */ +template +concept tree_node_index = is_strong_type_with_capability; + +/** + * @brief A layer in a Tree. + * + * The bottom layer is the layer 0. + */ +template +concept tree_layer_index = is_strong_type_with_capability; + +template +struct strong_span_type {}; + +/** + * @brief An adress in a Tree. + */ +template +concept tree_address = requires(T a, TreeLayerIndex tree_layer, TreeNodeIndex tree_index) { + requires tree_layer_index; + requires tree_node_index; + { a.set_address(tree_layer, tree_index) }; + }; + +template +concept tree_hash_node_pair = concepts::tree_node_index && concepts::tree_layer_index && + concepts::tree_address && concepts::strong_span && + requires(T func, NodeSS out, const Address& address, NodeSS a, NodeSS b) { + { func(out, address, a, b) }; + }; + +template +concept tree_gen_leaf = concepts::tree_node_index && concepts::tree_layer_index && + concepts::tree_address && concepts::strong_span && + requires(T func, NodeSS out, const Address& address) { + { func(out, address) }; + }; + +} // namespace concepts + +/** + * @brief Treehash logic to build up a merkle hash tree. + * + * Computes the root of the merkle tree. + * Can also output an authentication path necessary for a hash based signature. + * + * Given the following tree: + * Layer: + * 2 7R + * / \ + * 1 3X 6A + * / \ / \ + * 0 1X 2A 4 5 + * + * The treehash logic traverses the tree (Post-order traversal), i.e., the nodes are + * discovered in order 1,2,3,...,7. If we want to create a signature using leaf node 1, + * the authentication path is (Node 2, Node 6), since we need those to compute the + * root. + * + * @param out_root An output buffer to store the root node in (size: node_size ). + * @param out_auth_path Optional buffer to store the authentication path in (size: node_size * total_tree_height). + * @param leaf_idx The optional index of the leaf used to sign in the bottom tree layer beginning with index 0. + * nullopt if no node is signed, so we need no auth path. + * @param node_size The size of each node in the tree. + * @param total_tree_height The hight of the merkle tree to construct. + * @param idx_offset If we compute a subtree this marks the index of the leftmost leaf node in the bottom layer + * @param node_pair_hash The function to process two child nodes to compute their parent node. + * @param gen_leaf The logic to create a leaf node given the address in the tree. Probably this function + * creates a one-time/few-time-signature's public key which is hashed to be the leaf node. + * @param tree_address The address that is passed to gen_leaf or node_pair hash. This function will update the + * address accordings to the currently processed node. This object may contain further + * algorithm specific information, like the position of this merkle tree in a hypertree. + */ +template + requires concepts::tree_address +inline void treehash( + StrongSpan out_root, + std::optional out_auth_path, + std::optional leaf_idx, + size_t node_size, + TreeLayerIndex total_tree_height, + uint32_t idx_offset, + concepts::tree_hash_node_pair> auto node_pair_hash, + concepts::tree_gen_leaf> auto gen_leaf, + Address& tree_address) { + BOTAN_ASSERT_NOMSG(out_root.size() == node_size); + BOTAN_ASSERT(out_auth_path.has_value() == leaf_idx.has_value(), + "Both leaf index and auth path buffer is given or neither."); + bool is_signing = leaf_idx.has_value(); + if(is_signing) { + BOTAN_ASSERT_NOMSG(out_auth_path.value().size() == node_size * total_tree_height.get()); + } + + const TreeNodeIndex max_idx(uint32_t((1 << total_tree_height.get()) - 1)); + + std::vector stack(total_tree_height.get() * node_size); + TreeNode current_node(node_size); // Current logical node + + // Traverse the tree from the left-most leaf, matching siblings and up until + // the root (Post-order traversal). Collect the adjacent nodes to build + // the authentication path along the way. + for(TreeNodeIndex idx(0); true; ++idx) { + tree_address.set_address(TreeLayerIndex(0), idx + idx_offset); + gen_leaf(StrongSpan(current_node), tree_address); + + // Now combine the freshly generated right node with previously generated + // left ones + uint32_t internal_idx_offset = idx_offset; + TreeNodeIndex internal_idx = idx; + auto internal_leaf = leaf_idx; + + for(TreeLayerIndex h(0); true; ++h) { + // Check if we hit the top of the tree + if(h == total_tree_height) { + copy_into(out_root, current_node); + return; + } + + // Check if the node we have is a part of the authentication path; if + // it is, write it out. The XOR sum of both nodes (at internal_idx and internal_leaf) + // is 1 iff they have the same parent node in the FORS tree + if(is_signing && (internal_idx ^ internal_leaf.value()) == 0x01U) { + auto auth_path_location = out_auth_path.value().get().subspan(h.get() * node_size, node_size); + copy_into(auth_path_location, current_node); + } + + // At this point we know that we'll need to use the stack. Get a + // reference to the correct location. + auto stack_location = StrongSpan(std::span(stack).subspan(h.get() * node_size, node_size)); + + // Check if we're at a left child; if so, stop going up the stack + // Exception: if we've reached the end of the tree, keep on going (so + // we combine the last 4 nodes into the one root node in two more + // iterations) + if((internal_idx & 1) == 0U && idx < max_idx) { + // We've hit a left child; save the current for when we get the + // corresponding right child. + copy_into(stack_location, current_node); + break; + } + + // Ok, we're at a right node. Now combine the left and right logical + // nodes together. + + // Set the address of the node we're creating. + internal_idx_offset /= 2; + tree_address.set_address(h + 1, internal_idx / 2 + internal_idx_offset); + + node_pair_hash(current_node, tree_address, stack_location, current_node); + + internal_idx /= 2; + if(internal_leaf.has_value()) { + internal_leaf.value() /= 2; + } + } + } +} + +/** + * @brief Uses an authentication path and a leaf node to reconstruct the root node + * of a merkle tree. + * + * @param out_root A output buffer for the root node of the merkle tree. + * @param authentication_path The authentication path in one buffer (concatenated nodes). + * @param leaf_idx The index of the leaf used to sig in the bottom layer beginning with 0. + * @param leaf The leaf node used to sig. + * @param node_size The size of each node in the tree. + * @param total_tree_height The hight of the merkle tree to construct. + * @param idx_offset If we compute a subtree this marks the index of the leftmost leaf node in the bottom layer. + * @param node_pair_hash The function to process two child nodes to compute their parent node. + * @param tree_address The address that is passed to node_pair hash. This function will update the + * address accordings to the currently processed node. This object may contain further + * algorithm specific information, like the position of this merkle tree in a hypertree. + */ +template + requires concepts::tree_address +inline void compute_root( + StrongSpan out_root, + AuthPathSS authentication_path, + TreeNodeIndex leaf_idx, + StrongSpan leaf, + size_t node_size, + TreeLayerIndex total_tree_height, + uint32_t idx_offset, + concepts::tree_hash_node_pair> auto node_pair_hash, + Address& tree_address) { + BOTAN_ASSERT_NOMSG(out_root.size() == node_size); + BOTAN_ASSERT_NOMSG(authentication_path.size() == node_size * total_tree_height); + BOTAN_ASSERT_NOMSG(leaf.size() == node_size); + + // Use the `out` parameter as intermediate buffer for left/right nodes + // while traversing the tree. + copy_into(out_root, leaf); + + // Views into either `auth_path` or `out` depending on the tree location. + StrongSpan left; + StrongSpan right; + + BufferSlicer auth_path(authentication_path); + + // The leaf is put in the left or right buffer, depending on its indexes parity. + // Same for the first node of the authentication path + + for(TreeLayerIndex i(0); i < total_tree_height; i++) { + // The input of the hash function takes the current node and the node + // given in the authentication path. If the current node is a right node + // in the tree (i.e. its leaf index is uneven) the hash function inputs + // must be swapped. + left = out_root; + right = auth_path.take(node_size); + + if((leaf_idx & 1) == 1U) { + std::swap(left, right); + } + + leaf_idx /= 2; + idx_offset /= 2; + tree_address.set_address(i + 1, leaf_idx + idx_offset); + + node_pair_hash(out_root, tree_address, left, right); + } + + BOTAN_ASSERT_NOMSG(auth_path.empty()); +} +} // namespace Botan + +#endif // BOTAN_TREE_HASH_H_ \ No newline at end of file diff --git a/src/tests/data/pubkey/hss_lms_invalid.vec b/src/tests/data/pubkey/hss_lms_invalid.vec new file mode 100644 index 00000000000..2e9dd088305 --- /dev/null +++ b/src/tests/data/pubkey/hss_lms_invalid.vec @@ -0,0 +1,24 @@ +# Test cases created using the reference implementation https://github.com/cisco/hash-sigs + +# HSS with 2 levels: +# Root Level: LMS_SHA256_N32_H10 with LMOTS_SHA256_N32_W4 +# 2. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +# Signature signs only root level +PublicKey = 00000002000000060000000324fa7601ab93e539a3509136aca588aff0b8840fa459c3c1fd00e4212f85a280a1e3f35e9f42fa2fd14394706426eb90 +Msg = 000000050000000433d2fe4dcc7de5604114b68cb77e1daa6573aa17df94f516109ae9743c1a9300052dfc3b6b33fe27d0db3064c23daa21 +InvalidSignature = 000000000000000400000003a6391ea95c206bb1f17e1444f4557580b8d7ce580773c159c4eca8f12dc5aba7080f2e4f0af402ff44195ec2cc9ef338cf072bcb792cf8f97fd60f4c1fd171aaf741665a1924942122988c5ee7280b74c2160fb4004d957b75299838bfe3d9daa1848f3277220ad1f97578f0b44efa9e88627b3e700af1475c311245a898d3bbb708f065c3e1aa85979f5b57f94dddfe3444f1841b44438c526eb9d3456f617cec51fbad86aa39a06bfb3464c557e46d4b4e8fb1a1dd310b23466bacc9cb583f3099eb3a9bd6bb32562b3c152df94fd67e402fcb777ce8e71388fba1ed84b5f738ff9439e7e15829879e76dc36f0b9ecf3c8b4861851db88aa553025bb6bf0b6ee7445343dd575b8c8c73f5f43abe4dc543e13e59c2105d17ff12228cb03cf87a28f453cd8450fdcc107372ced5dbc4d9fde3b5bc92ee833ac475ae090cfaffda6db9e7a8d9ae795cab9a4b1bdb94dc4fc362d16bd31344a9d689301daec2041be493977f7fd8d73f1281b4d176ce0cfae46308686216793d429a71fbb9badb6aa1df8ca4020bda55f9dab9799fec922ee898e8f7dc7271653b396893abeef79217401e5bc70243b35c3bd4a9bcb4d0e83a31fb7caf3de2f547dafdd50cb82d93dfed1625f5926fec3c695885e021bd28391774b9d8310186ec5179b43a965ec84daaea6ab35c95cba4f153da37657d9c347ff5e8e67633f68b0c05a9c400e116091a9370fbc8f0736a4b85fe5e3b1b6bcd2d3729050b157019dc27bd02fd52d89959cdf74d3a0d1bf4d598e64f49bead0e3b980c0bef524aae4af1417377e2142b31518c4cceee6ba4d46b9973fa9705cfc0cc7ae41e97e66857c0b985a427bf2b20194f35f08e662c0b0ef151b89b1750c73a1ae786fdc0e03c3099fe41efe365993bb9bf13f1faf6349825d477d9c799e861704ea08337224332dde0012cd9fcbc7971eb7c10b1251431c079285dec4d3b957ccd8ebee7cad591fdc86eed8a82432b1ff52df1217008e62e614c0bda18f3fb06d247063c8ff570044f56c5b7e03693f7095babacfa81ce56f2746397a89c752cd91c532a9e468340e1557c4167316ae2eed4a483c34703fa7acf64aa700f7ef3f1480c2ffe0283d41b7b4085f4ef7210919cbc8d36c6871856a04be79a5e9893a2874efa6e5f1d9739ecac5e615b6d2f48bb3bf7c299371711021e614ea8b5253ec2f0655723e4165157a8543473d848c050e83568a6cd4f179b698b63e8fd16d50cde116cbd38b0048dc975d49afa6d6749d8efb467c5ce5bcbcf8fd9c31c6666bfab5ae2c7becde50a0f6dd7c40b054933f811c0793548675026437211b998b1befcf251648e25c0c67e1440bbb7c8e8766b2b7d994bbf8602be2acaf8869dc98cf1461e78f451d2c810216b405a38fac33051fbf7ef0239c3c0325504de5e73c5caecb9a7a8423b3174acf1d9770523d107b5379122a23439654bfe2f443a765875bdc893f00681e1ce7d127668b54e854d5194dd0968b1f7ed65f5e8908efa90928098b8a84970f6e7174f28a635850d7fa7e01d3b5f387a15237fcde359f56fe08a652e75652eba9a4bc000d3cd94176f2a71fe2ff8f63c4645eb6a406bdf95011ee9ad218b4b016875e592eeb8af6aa75806988ee984b2b0a946d220d1ebbe5ba07132c318981a30fa9e77c9f67a213f605155f0cf482c8330c214ccb4adcccf79ac3fa1f56b567b0ca1d26d9157837a040095cff4ead264b9b0a41164faa05fbeb3394bff15909f2f3a72d17eb194907622c5a350d4958df63c969bf4f8150697ec25755b39df9addc22fb057255d4e306572f3b30ec3be401101b8ba45e1e64867d85bc183786f334cebf0ad7ce7e1e5adf79e01fffae1b2eb5e5ebfef8611f1c72f6cb310cff200b6150450a396f6ad31f72f72da0498b88eb805cbd9f8e48229fab6208e3c545845880f5d7c2ce82ef53027596feab8828a85021e3d46271d1cba191e9b3b0e1773e0c9359e4e7d5ad82d60ee26b0dc5ea4676d3afecc1a7130bd4ed2150784748128e7879c04f088f9740ccde1cf286394a249d7a1867859e0f8c78e439b2b0f96a174c4346485dc9b4f27541c2bf6166e684f546b60deca9f2126705a4a4453c500c2bc4cb6002371d89f34247642b6a4d8c4bff98c63e6bcdf78dcedd0b27dd2bc0f9a2c6dcd945a56e0a2b6db5761150b6989e6f69543455ac22053068020fa6e1a84f05516ea5743fcd7b65995047e8274dadcdd4ba7708d26637855bfc2c282ac86113fa0ba5234d802b2c4f4f4b49c8ae2800d8e036573ec8e6ba509f9592d1e5f569307c266ccae492a738f084b5e19d33ae0a83687c8970326e8423baca398e3e0da4b7d1dc5e29011ca528b39e7387f676574e9e7252768f7f6e398f31233d248ef757ace08df1da275b3f463b1081cc80c2bf252dac6170940491d2024c979869be6c44f81ddde792abc728f9752c6fbe6a434d176dd6fcb5ddda6edb25c8c1342b44c5db5a63115d0c1a553e83d1468b2effa13965b30ecd36b048412ee4e94b8884b3962f70c4c90717a61285e03f839f5332f128578b99d5a5ef349370e6aca168e549afa5542a222ff7da757a179491904e27f3f7875d1b74ecb13b007ba12f8250a2a5d3c8771cb28c65d5f08b6b71ee5ec5a1caaff432e7084935efd216fc1aefbea9357a0c2169a7379a7752830823af65a69640a12099c757e5a4a36cfe16ad03d7ec8073d8c53e75ffc87f68ce41df27cb07897df1c0efc7be4b36b8e115645deaebc6da5fa2012d6e306049d7b48e2d1ed61ccc67c953177c3f6673127eb33f13983b8a5c686c16360ebbc116034f9cfd53698f6bbe4183ca5ba510301cc109058efb9ece9a497f0568bc39c6fa3408b8fa5d509346f813573312a31d47a16dd5c376f67b8c5a81c9cae72a205ca5bc53f3cac5b7983bd300b5e909eba6418c8c4b38246259bcec7f56bfdeb89395fee0673c84017201dac2f0c6e48a810e202c445715e76f6b008df3a43aa06b0ac1b9410ead0f52a68a3af94fa517f65381d7363126ca793a901cbc7fae2c3689c25eccd2b63fb0000000068400814de2eb6f52766beeb27d3cefd7246cbb6734fbd7f55f40648fb4ed8bb0ab2feddcea6451cba949f9a63e644b0a5793a49a3c3d7b1f89fcbfbec62b84e6b0ed1018d99aaf3884634d5a41ebc577991f0ed496b98e44ae25ff3df635098944abdc9e77353e61541aaafce88f05378787f8d6a4dd2cf3dee37045eb86b12114a821a12a4ad8159dd276fe85aa09a94de1b2bb7e559ca6896e19ae53eca7539d33ff6f58bf1cf097e313ad44bc6d6c5a796ff77f98c642ed82587413e162b58f8a23634fab60bf261da8058d9d2d8731f28f907cd5b8efdbeec5d83d59621daf220114dbc06a7a83be557e4c3535bce0ca9c7aa121f291daab84f361474d6a6a7e1c8be7dbce8969a6a5aeddc23fcb3b096d41ea0c523079b63c53eeab1ccb811326d7186cdf89c2f3706fdfa6d310242091cdb0c7d98de70ee5a2a03af5cf + +# Unknown OTS type +PublicKey = 00000002000000060000000324fa7601ab93e539a3509136aca588aff0b8840fa459c3c1fd00e4212f85a280a1e3f35e9f42fa2fd14394706426eb90 +Msg = deadbeef +InvalidSignature = 000000010000000408150015a6391ea95c206bb1f17e1444f4557580b8d7ce580773c159c4eca8f12dc5aba7080f2e4f0af402ff44195ec2cc9ef338cf072bcb792cf8f97fd60f4c1fd171aaf741665a1924942122988c5ee7280b74c2160fb4004d957b75299838bfe3d9daa1848f3277220ad1f97578f0b44efa9e88627b3e700af1475c311245a898d3bbb708f065c3e1aa85979f5b57f94dddfe3444f1841b44438c526eb9d3456f617cec51fbad86aa39a06bfb3464c557e46d4b4e8fb1a1dd310b23466bacc9cb583f3099eb3a9bd6bb32562b3c152df94fd67e402fcb777ce8e71388fba1ed84b5f738ff9439e7e15829879e76dc36f0b9ecf3c8b4861851db88aa553025bb6bf0b6ee7445343dd575b8c8c73f5f43abe4dc543e13e59c2105d17ff12228cb03cf87a28f453cd8450fdcc107372ced5dbc4d9fde3b5bc92ee833ac475ae090cfaffda6db9e7a8d9ae795cab9a4b1bdb94dc4fc362d16bd31344a9d689301daec2041be493977f7fd8d73f1281b4d176ce0cfae46308686216793d429a71fbb9badb6aa1df8ca4020bda55f9dab9799fec922ee898e8f7dc7271653b396893abeef79217401e5bc70243b35c3bd4a9bcb4d0e83a31fb7caf3de2f547dafdd50cb82d93dfed1625f5926fec3c695885e021bd28391774b9d8310186ec5179b43a965ec84daaea6ab35c95cba4f153da37657d9c347ff5e8e67633f68b0c05a9c400e116091a9370fbc8f0736a4b85fe5e3b1b6bcd2d3729050b157019dc27bd02fd52d89959cdf74d3a0d1bf4d598e64f49bead0e3b980c0bef524aae4af1417377e2142b31518c4cceee6ba4d46b9973fa9705cfc0cc7ae41e97e66857c0b985a427bf2b20194f35f08e662c0b0ef151b89b1750c73a1ae786fdc0e03c3099fe41efe365993bb9bf13f1faf6349825d477d9c799e861704ea08337224332dde0012cd9fcbc7971eb7c10b1251431c079285dec4d3b957ccd8ebee7cad591fdc86eed8a82432b1ff52df1217008e62e614c0bda18f3fb06d247063c8ff570044f56c5b7e03693f7095babacfa81ce56f2746397a89c752cd91c532a9e468340e1557c4167316ae2eed4a483c34703fa7acf64aa700f7ef3f1480c2ffe0283d41b7b4085f4ef7210919cbc8d36c6871856a04be79a5e9893a2874efa6e5f1d9739ecac5e615b6d2f48bb3bf7c299371711021e614ea8b5253ec2f0655723e4165157a8543473d848c050e83568a6cd4f179b698b63e8fd16d50cde116cbd38b0048dc975d49afa6d6749d8efb467c5ce5bcbcf8fd9c31c6666bfab5ae2c7becde50a0f6dd7c40b054933f811c0793548675026437211b998b1befcf251648e25c0c67e1440bbb7c8e8766b2b7d994bbf8602be2acaf8869dc98cf1461e78f451d2c810216b405a38fac33051fbf7ef0239c3c0325504de5e73c5caecb9a7a8423b3174acf1d9770523d107b5379122a23439654bfe2f443a765875bdc893f00681e1ce7d127668b54e854d5194dd0968b1f7ed65f5e8908efa90928098b8a84970f6e7174f28a635850d7fa7e01d3b5f387a15237fcde359f56fe08a652e75652eba9a4bc000d3cd94176f2a71fe2ff8f63c4645eb6a406bdf95011ee9ad218b4b016875e592eeb8af6aa75806988ee984b2b0a946d220d1ebbe5ba07132c318981a30fa9e77c9f67a213f605155f0cf482c8330c214ccb4adcccf79ac3fa1f56b567b0ca1d26d9157837a040095cff4ead264b9b0a41164faa05fbeb3394bff15909f2f3a72d17eb194907622c5a350d4958df63c969bf4f8150697ec25755b39df9addc22fb057255d4e306572f3b30ec3be401101b8ba45e1e64867d85bc183786f334cebf0ad7ce7e1e5adf79e01fffae1b2eb5e5ebfef8611f1c72f6cb310cff200b6150450a396f6ad31f72f72da0498b88eb805cbd9f8e48229fab6208e3c545845880f5d7c2ce82ef53027596feab8828a85021e3d46271d1cba191e9b3b0e1773e0c9359e4e7d5ad82d60ee26b0dc5ea4676d3afecc1a7130bd4ed2150784748128e7879c04f088f9740ccde1cf286394a249d7a1867859e0f8c78e439b2b0f96a174c4346485dc9b4f27541c2bf6166e684f546b60deca9f2126705a4a4453c500c2bc4cb6002371d89f34247642b6a4d8c4bff98c63e6bcdf78dcedd0b27dd2bc0f9a2c6dcd945a56e0a2b6db5761150b6989e6f69543455ac22053068020fa6e1a84f05516ea5743fcd7b65995047e8274dadcdd4ba7708d26637855bfc2c282ac86113fa0ba5234d802b2c4f4f4b49c8ae2800d8e036573ec8e6ba509f9592d1e5f569307c266ccae492a738f084b5e19d33ae0a83687c8970326e8423baca398e3e0da4b7d1dc5e29011ca528b39e7387f676574e9e7252768f7f6e398f31233d248ef757ace08df1da275b3f463b1081cc80c2bf252dac6170940491d2024c979869be6c44f81ddde792abc728f9752c6fbe6a434d176dd6fcb5ddda6edb25c8c1342b44c5db5a63115d0c1a553e83d1468b2effa13965b30ecd36b048412ee4e94b8884b3962f70c4c90717a61285e03f839f5332f128578b99d5a5ef349370e6aca168e549afa5542a222ff7da757a179491904e27f3f7875d1b74ecb13b007ba12f8250a2a5d3c8771cb28c65d5f08b6b71ee5ec5a1caaff432e7084935efd216fc1aefbea9357a0c2169a7379a7752830823af65a69640a12099c757e5a4a36cfe16ad03d7ec8073d8c53e75ffc87f68ce41df27cb07897df1c0efc7be4b36b8e115645deaebc6da5fa2012d6e306049d7b48e2d1ed61ccc67c953177c3f6673127eb33f13983b8a5c686c16360ebbc116034f9cfd53698f6bbe4183ca5ba510301cc109058efb9ece9a497f0568bc39c6fa3408b8fa5d509346f813573312a31d47a16dd5c376f67b8c5a81c9cae72a205ca5bc53f3cac5b7983bd300b5e909eba6418c8c4b38246259bcec7f56bfdeb89395fee0673c84017201dac2f0c6e48a810e202c445715e76f6b008df3a43aa06b0ac1b9410ead0f52a68a3af94fa517f65381d7363126ca793a901cbc7fae2c3689c25eccd2b63fb0000000068400814de2eb6f52766beeb27d3cefd7246cbb6734fbd7f55f40648fb4ed8bb0ab2feddcea6451cba949f9a63e644b0a5793a49a3c3d7b1f89fcbfbec62b84e6b0ed1018d99aaf3884634d5a41ebc577991f0ed496b98e44ae25ff3df635098944abdc9e77353e61541aaafce88f05378787f8d6a4dd2cf3dee37045eb86b12114a821a12a4ad8159dd276fe85aa09a94de1b2bb7e559ca6896e19ae53eca7539d33ff6f58bf1cf097e313ad44bc6d6c5a796ff77f98c642ed82587413e162b58f8a23634fab60bf261da8058d9d2d8731f28f907cd5b8efdbeec5d83d59621daf220114dbc06a7a83be557e4c3535bce0ca9c7aa121f291daab84f361474d6a6a7e1c8be7dbce8969a6a5aeddc23fcb3b096d41ea0c523079b63c53eeab1ccb811326d7186cdf89c2f3706fdfa6d310242091cdb0c7d98de70ee5a2a03af5cf000000050000000433d2fe4dcc7de5604114b68cb77e1daa6573aa17df94f516109ae9743c1a9300052dfc3b6b33fe27d0db3064c23daa210000000300000004e59c87e946e6fefcf4bcbbed83d7b124f907a0d383a7c8b85aafe870272afe93663038a509b97210339530250ec470aa465fcde0290622c433d97190c09b16423c4d16fad30c4352387972a189aebdd28cc145b57c93da2121d4b75a31988dbe172243b1127fbfa5cf0a8079df0837d2b31ccb9092c66a69fe629b5f9e006c19123611a288c9455c469758992de721c402f00b5b1d7af0eaf1bc635360371e3ad18eda72a58241ef0195c43da9dfc2f425d042bff122b43615174de735e69c1aee9c38ca9cfdc6da8d5e25493d5f4eaca85496fdc601b1c6c866da4c038787097390974964de80b055b573cea1b9f4e528285eb4bce23015198e3e3b26a6a7c46cbd1d3dd4ebca33165ddb03de8f12ae90e7930f28ab7787bb56c0b24dbe0d3f4b452eb0957b9dfe7d06296afdad8efafe1a2ba4ef22bf00f61aeff94183b5494d7274b05e935dbdf648b72eff38be31c238192598dbfa3ddc1f4cc320e5bdad5243c6b7ca99c6d4dfce6d61e04cc04bb83ec188ff6ddca4051999651c6f184b58f3bbeb1823c21a751d2e8b8b39b2a24e583338133a78694b56583ec326cf4f5c26cc6bdcefff450342da9a9c064875b5f5a66ac80c1faa5056eca2bfc1466a0d01484117a0fbe807fe44d0efd3d15e8f139c67fbdcc3d30ab0963ecd9f2587100deb256d59d029ed7fccbee80efca0a6621ab7875dd98285cf2303a7b1fee2899f166b7e95c66147af1c9bad3eeb80c8acff8de18848e8c050e0b61a7561ddf0274f0a0ddf9ce2659b1ff2924e20fb0765c91c811b3421c1fe42bb8b983e514e3682257b6d5a83b2ec140bfd7c7932ab2d8326670528ec0e86c12dea14cb04add94def350ac605396471b607d44d227787db956b33e3f64aec805387be0038aa5630266c3265860594bd0275a2f029431485cb98f3f40992639a71d51317c23cd58b5e4fdbc718a628a15a71dd4fd133e9e526f8a18bf9645ba45f2f0c0f0ad5b9e4e7cf1a32d78e463599d0c706a62b145769c257e5eb84492b23966857c8297602322225036a8db0dc4195ee56ff2e5895505230b5234524d1d48b4a6ee6ebeccf578300888b2b0134b6be8cbf52502de22bfbc93afc6ca43df95b25899209dda7915ba13b81ab07fc2ef79875423d551b7592c13a0173df398f920ccbeefa05264a1cb6ad755c74c4a29e827b91fb3f4629cdd4f1eabb76ac33eae31e2dd19c773666a9b2f6f154e773c2ee0c01a256cadbf46865a2d34888726b2b19da250214c62ef956d6437d6e9bb2630764c1ac002985d84a583e458bedac766fd9034379af56015da59cfd014bd38c007ae4c6600fb958ad6e60696720bfb7d6668fc787d8c550678b21636f9e43ecf4a21bd706ecd082fbc654d46bb2f26ab6891c9668a17b96469f68f1318740cbcf7e9b6fb1328ea02912e1f1aa939ee6e6777b7281e4bb7f2bb723e3c3cd46cf7fa5cf0488150e342e4a25935c443d17a249d87cf4be0488f839f9dafba77d4eeac7818db6cf9288d41f8ce6cfc6d2acc8fde24feb41810a6ac8062c4c5a19706ec58bde7b0372273238499893563cbdde0f000000057db7cde50fbf0d556f6561be52b4ad591f48c6c147e5ae8ae8c7f979344435664871517c9b54c7e0582f0bea2103038b8de8dda699a520abb877c67aaea7b595712fe986fb30de9a455ae7792922351c22a38988c36cf8a85e1f4f38bbb4b766c744dd67ee2ee37f8fdf4fc34cfcddcbef6ad9db57dd9819ce922fbd164c583a7ce62bdbfce7e730544ba22e3b17927d85643018a2e6aa981dff9fcdae43bee0 + +# Empty Sig +PublicKey = 00000002000000060000000324fa7601ab93e539a3509136aca588aff0b8840fa459c3c1fd00e4212f85a280a1e3f35e9f42fa2fd14394706426eb90 +Msg = deadbeef +InvalidSignature = + +# Too long signature +PublicKey = 00000002000000050000000461a5d57d37f5e46bfb7520806b07a1b850650e3b31fe4a773ea29a07f09cf2ea30e579f0df58ef8e298da0434cb2b878 +Msg = 54686520706f77657273206e6f742064656c65676174656420746f2074686520556e69746564205374617465732062792074686520436f6e737469747574696f6e2c206e6f722070726f6869626974656420627920697420746f20746865205374617465732c2061726520726573657276656420746f207468652053746174657320726573706563746976656c792c206f7220746f207468652070656f706c652e0a +InvalidSignature = 000000010000000500000004d32b56671d7eb98833c49b433c272586bc4a1c8a8970528ffa04b966f9426eb9965a25bfd37f196b9073f3d4a232feb69128ec45146f86292f9dff9610a7bf95a64c7f60f6261a62043f86c70324b7707f5b4a8a6e19c114c7be866d488778a0e05fd5c6509a6e61d559cf1a77a970de927d60c70d3de31a7fa0100994e162a2582e8ff1b10cd99d4e8e413ef469559f7d7ed12c838342f9b9c96b83a4943d1681d84b15357ff48ca579f19f5e71f18466f2bbef4bf660c2518eb20de2f66e3b14784269d7d876f5d35d3fbfc7039a462c716bb9f6891a7f41ad133e9e1f6d9560b960e7777c52f060492f2d7c660e1471e07e72655562035abc9a701b473ecbc3943c6b9c4f2405a3cb8bf8a691ca51d3f6ad2f428bab6f3a30f55dd9625563f0a75ee390e385e3ae0b906961ecf41ae073a0590c2eb6204f44831c26dd768c35b167b28ce8dc988a3748255230cef99ebf14e730632f27414489808afab1d1e783ed04516de012498682212b07810579b250365941bcc98142da13609e9768aaf65de7620dabec29eb82a17fde35af15ad238c73f81bdb8dec2fc0e7f932701099762b37f43c4a3c20010a3d72e2f606be108d310e639f09ce7286800d9ef8a1a40281cc5a7ea98d2adc7c7400c2fe5a101552df4e3cccfd0cbf2ddf5dc6779cbbc68fee0c3efe4ec22b83a2caa3e48e0809a0a750b73ccdcf3c79e6580c154f8a58f7f24335eec5c5eb5e0cf01dcf4439424095fceb077f66ded5bec73b27c5b9f64a2a9af2f07c05e99e5cf80f00252e39db32f6c19674f190c9fbc506d826857713afd2ca6bb85cd8c107347552f30575a5417816ab4db3f603f2df56fbc413e7d0acd8bdd81352b2471fc1bc4f1ef296fea1220403466b1afe78b94f7ecf7cc62fb92be14f18c2192384ebceaf8801afdf947f698ce9c6ceb696ed70e9e87b0144417e8d7baf25eb5f70f09f016fc925b4db048ab8d8cb2a661ce3b57ada67571f5dd546fc22cb1f97e0ebd1a65926b1234fd04f171cf469c76b884cf3115cce6f792cc84e36da58960c5f1d760f32c12faef477e94c92eb75625b6a371efc72d60ca5e908b3a7dd69fef0249150e3eebdfed39cbdc3ce9704882a2072c75e13527b7a581a556168783dc1e97545e31865ddc46b3c957835da252bb7328d3ee2062445dfb85ef8c35f8e1f3371af34023cef626e0af1e0bc017351aae2ab8f5c612ead0b729a1d059d02bfe18efa971b7300e882360a93b025ff97e9e0eec0f3f3f13039a17f88b0cf808f488431606cb13f9241f40f44e537d302c64a4f1f4ab949b9feefadcb71ab50ef27d6d6ca8510f150c85fb525bf25703df7209b6066f09c37280d59128d2f0f637c7d7d7fad4ed1c1ea04e628d221e3d8db77b7c878c9411cafc5071a34a00f4cf07738912753dfce48f07576f0d4f94f42c6d76f7ce973e9367095ba7e9a3649b7f461d9f9ac1332a4d1044c96aefee67676401b64457c54d65fef6500c59cdfb69af7b6dddfcb0f086278dd8ad0686078dfb0f3f79cd893d314168648499898fbc0ced5f95b74e8ff14d735cdea968bee7400000005d8b8112f9200a5e50c4a262165bd342cd800b8496810bc716277435ac376728d129ac6eda839a6f357b5a04387c5ce97382a78f2a4372917eefcbf93f63bb59112f5dbe400bd49e4501e859f885bf0736e90a509b30a26bfac8c17b5991c157eb5971115aa39efd8d564a6b90282c3168af2d30ef89d51bf14654510a12b8a144cca1848cf7da59cc2b3d9d0692dd2a20ba3863480e25b1b85ee860c62bf51360000000500000004d2f14ff6346af964569f7d6cb880a1b66c5004917da6eafe4d9ef6c6407b3db0e5485b122d9ebe15cda93cfec582d7ab0000000a000000040703c491e7558b35011ece3592eaa5da4d918786771233e8353bc4f62323185c95cae05b899e35dffd717054706209988ebfdf6e37960bb5c38d7657e8bffeef9bc042da4b4525650485c66d0ce19b317587c6ba4bffcc428e25d08931e72dfb6a120c5612344258b85efdb7db1db9e1865a73caf96557eb39ed3e3f426933ac9eeddb03a1d2374af7bf77185577456237f9de2d60113c23f846df26fa942008a698994c0827d90e86d43e0df7f4bfcdb09b86a373b98288b7094ad81a0185ac100e4f2c5fc38c003c1ab6fea479eb2f5ebe48f584d7159b8ada03586e65ad9c969f6aecbfe44cf356888a7b15a3ff074f771760b26f9c04884ee1faa329fbf4e61af23aee7fa5d4d9a5dfcf43c4c26ce8aea2ce8a2990d7ba7b57108b47dabfbeadb2b25b3cacc1ac0cef346cbb90fb044beee4fac2603a442bdf7e507243b7319c9944b1586e899d431c7f91bcccc8690dbf59b28386b2315f3d36ef2eaa3cf30b2b51f48b71b003dfb08249484201043f65f5a3ef6bbd61ddfee81aca9ce60081262a00000480dcbc9a3da6fbef5c1c0a55e48a0e729f9184fcb1407c31529db268f6fe50032a363c9801306837fafabdf957fd97eafc80dbd165e435d0e2dfd836a28b354023924b6fb7e48bc0b3ed95eea64c2d402f4d734c8dc26f3ac591825daef01eae3c38e3328d00a77dc657034f287ccb0f0e1c9a7cbdc828f627205e4737b84b58376551d44c12c3c215c812a0970789c83de51d6ad787271963327f0a5fbb6b5907dec02c9a90934af5a1c63b72c82653605d1dcce51596b3c2b45696689f2eb382007497557692caac4d57b5de9f5569bc2ad0137fd47fb47e664fcb6db4971f5b3e07aceda9ac130e9f38182de994cff192ec0e82fd6d4cb7f3fe00812589b7a7ce515440456433016b84a59bec6619a1c6c0b37dd1450ed4f2d8b584410ceda8025f5d2d8dd0d2176fc1cf2cc06fa8c82bed4d944e71339ece780fd025bd41ec34ebff9d4270a3224e019fcb444474d482fd2dbe75efb20389cc10cd600abb54c47ede93e08c114edb04117d714dc1d525e11bed8756192f929d15462b939ff3f52f2252da2ed64d8fae88818b1efa2c7b08c8794fb1b214aa233db3162833141ea4383f1a6f120be1db82ce3630b3429114463157a64e91234d475e2f79cbf05e4db6a9407d72c6bff7d1198b5c4d6aad2831db61274993715a0182c7dc8089e32c8531deed4f7431c07c02195eba2ef91efb5613c37af7ae0c066babc69369700e1dd26eddc0d216c781d56e4ce47e3303fa73007ff7b949ef23be2aa4dbf25206fe45c20dd888395b2526391a724996a44156beac808212858792bf8e74cba49dee5e8812e019da87454bff9e847ed83db07af313743082f880a278f682c2bd0ad6887cb59f652e155987d61bbf6a88d36ee93b6072e6656d9ccbaae3d655852e38deb3a2dcf8058dc9fb6f2ab3d3b3539eb77b248a661091d05eb6e2f297774fe6053598457cc61908318de4b826f0fc86d4bb117d33e865aa805009cc2918d9c2f840c4da43a703ad9f5b5806163d7161696b5a0adc00000005d5c0d1bebb06048ed6fe2ef2c6cef305b3ed633941ebc8b3bec9738754cddd60e1920ada52f43d055b5031cee6192520d6a5115514851ce7fd448d4a39fae2ab2335b525f484e9b40d6a4a969394843bdcf6d14c48e8015e08ab92662c05c6e9f90b65a7a6201689999f32bfd368e5e3ec9cb70ac7b8399003f175c40885081a09ab3034911fe125631051df0408b3946b0bde790911e8978ba07dd56c73e7eeaddadd diff --git a/src/tests/data/pubkey/hss_lms_sig.vec b/src/tests/data/pubkey/hss_lms_sig.vec new file mode 100644 index 00000000000..6e23eb52432 --- /dev/null +++ b/src/tests/data/pubkey/hss_lms_sig.vec @@ -0,0 +1,22 @@ +# Test cases created using the reference implementation https://github.com/cisco/hash-sigs +# with seed derivation logic Secret Method 2. + +# HSS with 2 levels: +# Root Level: LMS_SHA256_N32_H10 with LMOTS_SHA256_N32_W4 +# 2. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +PrivateKey = 0000000200000000000000830000000600000003000000050000000467c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4 +Msg = deadbeef +Signature = 0000000100000004000000033b4c17fbfa9a6ae2c471d7f26992849c4d956a81a65ee52e757f1df90b8c031d56a47a2d953108c6958b9e26da66abf4f52b8856bc93e3305f61512a04d2c7a77239318432f1bb858fd2b8af19f2ffa1713eabe3d5c7bd4eec1ae075041e87a8dec5ec762772dcda6e1a19be5ae4745ddcd8f954d8570c866a62a0ef1d5fc449c93b1dd3151fc75596b1be6b98ead724d45f9879525483101e11105aa8d94d2c42d24921f148095cd71be805bb40f319b1632a2b0a2d2f5b3cb5911531eddc36540b622d9bbf232cba12fd1508b59e47cce1d6756ff5c0a76f8f11aa09f0e156514ddde065481c527b3cd20b644c450fca3fadf589ed57c1a893cc134eea4b4c2a0668b741b707040ddc495ce60046b76547d0a2f48952126701060acf931c8bfd4edb60f0e90af071e0aa5bd54a26cd10964f4b978600e586b012546c17e2d3955219229c279c2ba05bd61be9f117dbfd512ba9ede726c0b2216fb5d972e440a75e5e9a3f72e42e986ff4ce03c5182bef6bd8b7aac80e91d9e72a1c9ef857de713cce2916f7e723954e177f93425df98a79e461d1c5c890138fd05f5721c63663877dc5cf72cb3ac1209a9282e843d1097e58bd577b5b611606f054f8c3238e315c40aef191de389c7978ce56d21f01336f346ddc6203076dfd11f2b9abe06823bd4b9331123a7774a1a8b29a89f9122891b9f3ce13fcfddf5758c77bff267f7f22215831061e5209c46149e541f108960c918fc8a9bdff2d78987cf782d5dc4a8de064e921d433c3a8f4362d340e19156564072d3ac925fe884c67434b7cc3760c203cfb2f34c21e881b4ddb26214b711ad96bfcaadab534799af504762bcfd367e34efe693a7da6e828e68979585aeab758378bd779ca8e379dcd2e2dc38c0d7f7211f58bad8bb0a72461af81712c35c9825f8b23e26625b7aa46ea71847ab67c5770e732cfc9a3948ec0772f67382bb2b9a863166693329a50f80c2a87b8547b6c11a5de36f9b10f28ed67813770fd1ae160c4b33e3a96b0dbec09a24f5a1852383b1fb066b778acb0a86302297fe67f99a488d984c2875ffa2bae331d961bcb726b818662402c3e5c9ce3ce8b0e7ae87e221e18ae35072d3dbf313f58b5d399c7e783d5be4234234391b5b9676ad6e7417aab4716ce1a7aa24db9254f325c6e4632e62ee8bbabd5885ebd19f703c6aa0199469519ec68fbfca30c7549911afa846735dde5c69865475f13ddc09b240a6d3bc50ea0940e89778e369cece235de4b1ed23433d305d7da612eec60b066e519724ad75ec00b2da20551b08723d9779015f2df6b30d1529ef52eb40383f37190c72be0c9d045b3d382eb5e56ddd3eb2028a074cb4a4b6105dfcd14ab226cc1240c681d3805e6f54260ab8aee8b8acafb483d37f7c87a4674b8043c40dad09baf64f2aacf915bea02fe01b6581b1a4d1e37d76ae7e4014cce79f7c817007f1d4dbb30057450efe38e2a843f44cc6a99e190e3199d8a1f65e120bddb0ec1df8475339b9d0fe3653a927bd915f6447ac495a0f76fc9336a77ba34e646d20fb0b63535683da47bd53d8bc3c846e4c27e199e7be6a6c2821c1a0311fa55699e88c100a0e1520cb775bf4d998915ba93f654bac743640e8c01ea15389840c49cec190420fa99080aac1d82cd917c15f454a1d3869387ccf169a5b015e3c918edb251130d773354485cda5666d8500907dc315c93467044413829526b89f567ac882fb0b868fa29687950b3c1a8a1eaafeb164df0ae598fbb204540e4cfe2ad121e28c305e8d8eb50822a345b24efa536ad999fb824a11a1574c3c80676120eab3f68552ba940df7bab21534ffd17491d7d804103671aee935c925d57c14519cfe3afa4be8870d5abaa0c26da9586136ddcbcc3818d8e9f611f6c8fde36fbe81b0b21095a9906abece667c7c15f9efbbdd30329b963d2a32b4d3230f56b762cf86bbc84c1fd50e4a65b2eb4c3ecd273b4815dc6bfb10972fa1b3bc13c7c1902448cd4873895fca5c452d2fbffd0313bb280bef50583a322f1266e86bc26debcb53ca98935a170c0f21a729f4e3996f2318b9d13d6b1099689c8c520b7cde5b1938f54172b21e8cc2703433c454a26b26f6ea33744e3991e99377b2eb3e67c7a3e83f73206293685e16b406b5117111c56113a1f703cc3bd7309e23057278354074cb127c12a918f1fa22444c972b66d04e0065740a64b9c0e190653e4845ab0ce568a18edd204e4b673093f33eb0b45be36d2240312b2e9f5dee6255a689e86167c8d059fd06a2e15e2d437b542e06a915c0858db75034a61de2ae85689c34f6a4398ea1c8d64e87ddfaafca41454548afe6c50b0645a0317b83e14f6bca49cf9fa9a0152887cdd7311faf728c2ce0f9bfd48d35ba75bdacbd77180ad0f7d0ba9e8422fa8349296a3dc6dec74cb76515211fb31d1ed51279ea004f7c529151f576da91047080f6c86dfa9d75e22b71a66331610c75af8bcb865f0d17b89fb3e2ff9c262423d00843924547b9f8d9ee7bf7cc49519028785a8b912813fb4a2524a23d211d75e2421d09f7d77d1a8cb00d637225a3509571ae50e7496de26d04ecebfc07c8c2add9d34f73d6ac593e5fe03707aadcef5f362e422ec285bebad8abec92a77b56df920a68aff30433bb2a2beca8662eec7bb053def251d42f59e97fabe4ee304f952b073fc64b04661d401f19d61c61a1594716d986bdd1749fe6244701e33916561e9d7be473c866bb4ee616d985f10d07405f82e0bdbf91499b07ab3866a21a52b8d89522acb9b410c0784ce4033acbfceed097cc7c11e0216934eef324c272374f3a947a5ba501e8db1416d54881bb0efaa2794f0d0e2592a2ed0526fb56e286e2beaf2f22a32028ba75c2a4e3690d0b7ce6e4fbeb944756b9e68db4b8cc873313484063dcbcc3cb842507e09ae5cbacb5761a9eaff5fdf8a271dea35192d4e4086760fd3d363d6a1bd7254e1f330cbd69589e61ce0fe9debe70f39cf1989979d82f4563a55aa8e609126d8d11a90db5ff0d39975781fd81adfe6b628b2ff1537eaeef692f6748068d6b300000006f0b054a497da7ad540d28c4cabc6fb47d0b99287073378d2ef81da4c970839f0676cfc7c443e335f06259e4342497e824b9a2630ac28124582e9fa8cd0dcf0ddf81b07478dd5ec5c84c7fb43158202814767556027f40fefa69ee48dcb865422346afe98f627c0b1e6c7ad2de6de92594a4acbfea6dcaad85c5f066f57cc010de83cba33ea6cd9975e1f7b32ea8184e69f1f4bc82c0c96f59e194932c133a1be2d24f1c945046e32b2e0480c70de41152603f8cc322b67cff334546d9083be11de638e7448d32cccfefd5bad504bfd59764c797f91c61400f3eed0b8c92c20fbfce05ae61377a499c7a2d364dfa4d3d5fc54f1070364e08364d71cbf3bfd1b6561553169277d221d4fe50867d1e0223a22169b78927a131b7ee42f55fe9e3eff55fb241e0006d38ff42fb4bc929f32378b17ad5de3dea117dda0eaff570466b8000000050000000470fa96c7207fd718fbbc14f206be924232934612284f800c38fe9ea986386a321704fbaecfa1996db792a8d14123e3440000000300000004d1324626d19a4f048634ec2b98dc95ce0ce192986cbed9a7faf605a7a9da476688c28e49e9e676ef0640709d23015073dab879ba93d64f71837b1b6628a43c80a3f06032c4318e03b18c252e16797afde4cc8db79abab009272475fb7e909a78742d467bfe3820f37ba4f0575abfff0a32970a246d54d794e5757ee82185ec993e3aafb598eb0d634fd0070467d60d4619a8fb5c49c82fc5df0d39ff40811ff6ec9c3ad0c87a95254566a1065099c0174317943d21a2dc9eb8c81d8c9363a686406c46caf8683f16b9f865f5063627bed345b7514e06abd2e28543b182a0cc36a09a1399788b04575d79b478dd86ce2870d8202bda4ca79d9afdaefe32398a2521e605f4adb6ed8612a1430a25f6ebe264ad37d4f401284d16924fff9d68630323c4338f2252f5780f44be76b9a97d5010e2abf846c17e811114a245424796c3c97b5992cbdb8014928723088a5f421ba067482b19c2f4890cdb0c7c15ec6eccbd8e133dc2a2c10e544ba855b2b07a2ccd982ce8c6ddf191c09a0705e73a9a492099cec2731623dc551a7425255d95e17eefe2ec160bd96c488e9f3c9985454241f4429b15f4193680dfbd30c7ccfd6920b0f5f7f1ae0a563b0fc93690807d5c6d784b8ddf24b1f6c9bce3692061de54e057e897c10323657c624294fdce227fee7c50ff9be5b9bd56022adc6515b7fb44964e9c07969c2c325b2083977f07342bb503448621a51f9f9e48b2e5bd9756371f900e1a1aca92331c1f0173e25a211c9a7083dc2135324420b4472919c6e9c6d2b18e60ed9fda9f9de23869cc428547d1030b0f27fcc0814d4a91967fb94d2b0c04873486e50a5875e21508c25f4679e04879484ffbf0a7c65c40937a246a4eceb959d8e0cf6cc6c29e95019c3d69558640a51ce187c6df658947fea5b358680ecc568902442fc159c1479bc43393683019ad7b8ceaf094ea165ea4fab3ebcdb57337e5e28c5f23e3bf7de2fc7fa9ac3a19a7eba424bfccf4d5c1614869c6085d3cbf1f77af36e7a6bbdf60dd8e3792974c7712f33107dc553dbf3f1cf4a7423b7cb1624aa40d01f4450e245530bf20056adf34b7ae3e8b17c059c9073931e46eece7b8b6a725c3aa84ef43dfc25bd23e441953aa40d8689cd34e9ec0ce7a9dfb37454b8d152877f45592eda49df265c4a6586e8de427fcbc01cadc240df08fa3fb3a36572ef8758185ee748ee063e08c6b47c031e75b799861e877019265515267f65497e3307c50d0680043fd36c9e13a83bd7b3e0892dbe41211ea50f795ab2408dac8b5e1eb7c23673b72e4c076faf93a8c71d3ad9f8570091d16667f1fde53eb5380804a5c5c78c6bea53acfff3fe673599351d9988acf0435a8c692e8d46de87af8d72389deadf9a0cca2312ab35684d06c0bd1cb362077fa53805c4d7823914656904094ffc6a1e155e52974b07ff2055fa4c14454f2dd46fa16fda77a11e131df27ad93f5c007d2354d344c1fc79a67eef198083480d62be9c8294d7f01763089c0580f74ad18471848d758e4b225d8972e20261ff3753e60630199bd43f6d41693bffbfd7921719dd7b60000000598a22fbbeb0bfc68202e2e9d480f6f1df0e4e6b75e3566ca6cd61efa6347d12bcb3448adc2c04c4de6528270236158998fd4569299325dbbc648a99c3f0d511c23006ebb7a865a20de81dd118b374aeee6494fa9169dead8d7b0d7c7b07416f259846e45169c5a83dfdcdfe6b145eb0626baf570eff8b8e6f995d2498ac74c331faf60b72418234316df461327e4ac14619b565ebcfd7e5a4b388930198b9d0d + +# HSS with 8 levels: +# Root Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +# 2. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +# 3. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +# 4. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +# 5. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +# 6. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +# 7. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +# 8. Level: LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W8 +PrivateKey = 00000008000000000000002a0000000500000004000000050000000400000005000000040000000500000004000000050000000400000005000000040000000500000004000000050000000467c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4 +Msg = 1234f006ba89f00111213baf0016171819f0 +Signature = 00000007000000000000000405d6540e1e239a4c12025ce32634d2fa91bdcb3610e756bfacf24100206cda393968e22f252ce3ebbd698ada87c64722729b47ea30b8ea2280b0a343a3291c226463d3a2bdc4f3fd3943cd41188ae66e124413cc44be8b7be7f0be1e7e2410b09d956331d2adfe21f5c9edb29ea288a51d051a57fb5063a25c9d7b5d7da96bda2e6b92e0a18322e4840ee07ac17d0cfe2423dfc8f618f09b18be76d65eb9acbad0d9b97a1c8e56f17c3394a39859f10868e7e7ab0e12ba21c08c17aabb8a06df6b81b9d88dc0b7c434f9155f770cabe3d207c4d69334c5c8964b09a58ef812f2cef94c1a4edcb24089849302527465e2d49192378a4eeb3b5b9b7bd397cd4a7afddd6e7a8678bb6184ee9262f1b3de632b4824d8ef84d4c1c84096ea1e19a5f8c335fef770df4eff1b71380bc756b365a181f8e6b2283370f0ebd3c67dc7ddd93a827344cf29713362e583d83f686b212c61444a2a7f68ae52d2ed26773a2300af3e8994f9c6be9da89df58d3e74a7d7e5a043030818dcdb1e1a7f4d99bedc296d40898f3bbaf738db43bf48029a63149f241a9b0a4f819aa90103347809ec39bec35bbae153d8534a019e218c07e3af40a05f5a434566c829181e5a34af65030960a60f6e44d18a0968cec07462f97e17b0e22cc40790dd55b157c0fc2afe50ba462cd5498065f1956c3590c7645f1fc57d4b084bfc0fbb37815b5579f7da4f1cdf37b5917e28ff9c2bd36a014581db1f37209be1765aca712edfcddd5aa73f35018817d84406203f52ac2a9a934adc5504f9b390c24b3bee7fbf8e3e14d2a69bfeb64f11fe3efa576a2f75a21652805a0c5427c4e38753f0cf249d65557e595c638175d1fa9646ccf9e337532122456a1062c6eea4a5e96a8c5ebf52712829d322b057135a77f2b271c049bdee1d468caf7b67f3dcf6d8f86a6b8e4e52a2216d887a96cc5e8108ed8b76eee7689acdd582cebe2932dfa334f0b22ffd409dbb051b69c113b6816cfe01945305712e68f898636cafdd7a5beac8ea4312e676e4d6ec08c653e497453711d68fa5222e9e6901338554fa0bb30a095cd0c81702d04bf180c1a438897bedb5a253298863074d0c44ec53d36366f9d7f8eb499e7139bf521e0ea7283d727f29a4de2f8b4f8d88990bca0b0228ef00e0fcc75554c78b2b36f84120228b13cad748b6dced7be47c3315b8841cded1039899a800b9f67443c0d541c8a7b08ed9a33edc837aa7aaca9c777c3b2e1d84041e1e5e42d40a02a43203c0be6197f44fb79c5ea1b5d30e597d5a5f2786b19ccf65f10aab43ce7e2e560a3eda27df1e621792a4c3a98f0dd7ec190d0d45cfcf54459b0add4695f8d2c2d8f4d6430c71e4c2743ead7174ccbb64ce78b98479cdaa5e579b4441273b2bd3e9e692e17356660f063e0e231911a45bcdcaeb21ae5cc1ab4fea49aaa75095f1c23804603c490a6ebfd3b831698736a3d3ee6db206548cb3ea23c7ab6503ac9964c73d4b92f4733df66b64fb6d2c6994324d14f198f2a135dcf9b58105cf3464c3bc96dbb2a563b2c37f37a9b9c046fb3887ca9a7b670325fc92d12a35b0000000054a970a2543c843b3714d001881ee29f8a2b57f2771cd395b65ef564256c1a699e43b8805dc7fd899b7cbad019c8f763dccd3199b1604fd1fd615e0b17bdcbcafeaeddf3ff41a6287db6d578914fd4f490442b5224a213e39a447e303fe773447b75b2a348a22add5309b677ff3cf401cd02feb39bd590b3848b1842dd0b40d5a4619ac8dd06c1ddaffa01c25a31cfe37b7b0e1de2631bfd93670784a7894abc20000000500000004b9e64d2247edcfeb98973062bf0fbc69b15e5cb290f5ef1ca11e972cfba495c9c7e7e48ce4fb18d69f36bbc7e2d6ef370000000000000004f1d08604e954027b6754e4983743ca61f9c2428df683144570de246e3539564a7be6b8f6b0c1b97f125cce5961ab203004cfcd6d92be8cc1b94f3adbb8e5bb4391f5a7336b69445623f8fcda6f0fbd2e671669f24d5928d466e63633b5c526fe0b5cac1d62341d2595d0294e44abc3a38470a590a258b18aafca160f688df4e98cd8c88e94086ec4115397f90554f0551d97c256a071b57ae571cd569b1ac8248ef174c29e8e97c058795bc45e7e93e4dfc40cf8c0388d59c0bfa1519e142134782663a14d40144c43444a80cd479f50f706ff7bb884236f97696931aad382f07d130cc1bb597de8c49041a44d65ce78c61dd70701d08a014a0e8f2939912f736f84016d3bf486fa3fd36ef35623cead9a2fbe2ef58ede86eb631ae2be4dc087225169bc4f04fc4b6cfbbccfe2655849e1b00ef2eed5be1136e32ac2542355dc750b6c8e6ca5082a0226139ec020c23ebde8be59971fceaa569a992520b5ff92501a72ded50694288341fb98de445f0f193e31380a66ce1d12846097f207fe30adee435a48b1c0dac3174419394719d43be334f5533f7e92ea2a1dd4541848c8e30c25bc727ddfde6eb4f32c78298bd778e7d11bb6d4c26c09519ce51421df0074d7c6cf7461b38eea57ede4584cd2d5e433bcecd70491a2323c0c5d7993eabea1de12eb6b47246506559b26c14deb84347b6986f70ae6550c7a57bacaf09ba0740df66aed4700c1a016512ce10fc4370b7d0d8c90d90188569f502f27f4499e78a2e924e3b8b2aabf3db342fb0403b7878dab41576e0539a41e94c40751ed8cb6afb35704ea8584e533bf5f4f002821dc243884b4c82e3511f57cfcb9bea544ae969b37d6c81186bf2a778c6ec4b70666ae1b38ca9d9892d5ef04880f0c749e60dc8f7a05b3db1ac8e8714090b14e5d7405265f9631b65596e8a754d4a860bde4b71350d66952410a4a2cfa5cae79fe35b9e8372076a651796f605c3427164d74567a996be2d313d4f4f22d921a3e38dbb25d8fe89cbc9488d989e31561920b552f26895bb7dbe42a2ba7ccaaad08f1463808749723fcc87cd6fbb93ed0088fd4ffd2ad775e64d748e574090ba69c861eecaf778ef591d806444327183b6948d931bf15029adde57f59340acf24023fe6ef667e239f048e198782895057f9d7517f348f60b314c2237b4259a6c7320c03c71d4d29fb0e13857f68f77dee5d1c536b44febd242fc7e2928f9e449a44b742825ec3ac3505456f2ee47696cf16dd5f4424935d9dfdcfb36b0b251e7367288cea74af922c7bd586fa5abb2ecf1a2398fc0d7810f0089bacbd32f1eb30eaaa14e90481043dfbef4c8872f4bb292cabed42e6c0895988db3a5bac8fa86e079400ab358e152d3f2b5505a980d569defd48e3d14279397e6889b3db2d6dcfe073787264019370c3d8fb5fecac4316288197cf6bf45e007a2c3241c7e043a6c78aafa3f7808b4fffca82de752807e6dfc18981d39bb1e45515090db04afde329273d7e71be0c90bff437ec907610d9315a44a8e4feeb788eca167fe51950e011da211d523332e4033506b128fa1db6c64a000000050b180d4bee3f157a6ffe348e507d39ea68b8a5744b86213cc2ef01f4d5c300611aff4114afb1c7f4aa622251da30a10b326a6855c7de57c09bfa553ce5be8e40a54a477bb21dd23634b9232f5215381fce7b8c2c611ea64e5dc2fc004746adc1e801f2a8819ad0dce9d846998a49f0df5da5a491b2c79502d66b898bdcea3bf2cd798a3fa16144abfd217798d156dd483e4dfd4ab0ba8cc27bbb5deb269de1b20000000500000004fd49b7ac89c4b8839802e7972e13167ef167a7a86042696dca85eef5ea7faeb96a7fdefd23df5ffe4f389e5ce7e7a9b30000000000000004ce8c0974ae894c9f1540a8115afac5a90505e579cf2200c1786fa4e418da82f1ef4877dbc21a69a6e7cb255ffd947851cd24072d8953c0c465de07a2b1d9efd47aba87ef02104bfefc5958b9818280732a56d6fabb8df486128d71c72733d4cc615876f3eadf450ffd5ce85439838ba01215addb18e801c75a6456c09c5224ea627e2c41f63206439d62c547faa2c2364060db8f4d20bedef5c2b048320b133057154150ef0f9bcc07f10419fc49628f4a29c856f8cae268811e52ff7a6eeb8e01960ee054d5c3f46f07faf99d62c8faf653a0bf01b3b3203cc9228d3647fea12f88528c2414d0b1c11f4b14ebf696242e74646df2317aeea1f3564a32805c3056e156071a3ba25a675dda45f99438c3a954f9e8aa87106db304f5d311bb3606e47110a50403866d1ad450e6527a13df5a4e0722173b7d6b331a9cc85683b3346ee3837030ca546ededb8a96fe7f92a5615e0503147374ef1400f483776a2de8c4e03649b5f04c426e8ea436b1d528dc916d6e6bc7fcfdaaf0d3d74f7385faf41d0b9ae6a2fbedc26f4a4b36235b18abfe287a4f194cc314c370a07eb55ad4158fd500320f4b97cfa51d8ce83e435b34de937333b81c7af2f53be8792fa0116a3242dcbd9db53ea8513bd288331dd507aea922eba376387cc52e219fc0358c0b219112fc3c8f7346d09a062f62219af3ffcc142b78cd635f6d39b96a4f81bae6750248b4dc1bb34787f71f9a8056b5111fcb90e17345a4ecb0a9c4962293c37efdb9252a2039e8211a521b3e28bd733a392477dc11f99d40e00512563e60ed0497ab99f5a08e2e0f140a279f30d799aa3eab3557df4236a4df2d8e61541f80713254564252c2e03d22ae7cfac3ee330d76afd815bb03ba103cbeacecf42575601fa11d97ef6e5f17ff7eae2b2a67bae7418bb1d9adbe8f61fcc2f9fd6af7db4e68f688599aabcfcdad97ae3214a78844302b3d3418bf5caac744dc6a7db9256d45627ff33cff2854fe5e05d82466efa389d503b07c3c99f569ae4f3a3eef8426340d404d32f89f5c8bed9edda2bc824bec18eeeaf5b065f09536acbf483b409998ed316d321ac0140baaa394bbd5c6e0ed9302dc1dcc5cb1fc1738dd220b03749cae079b4850237f4250debc2b76e37922bc0d259d8c73e92b3738b59d1b6a8d59db7d85de95c255803dadc7b60e5e799c414443c32a07aa1f6392ae819c9c7f1d59aab1370064a3c3aed346f273b4e6e690769e3087fc75a840e89d4dd939d617fa99a641c96efb1735835290e366e52a7a2f5366c6483aa59cd87b379c8b25fba3128418157fd2dc8dab35fae8855139c7b3ccb08e022fe78362b94dac9b44ad3a73d52d1833b25fbed9d2713a7e7162164b293d95c1ff2b1ac17165380d648c8d925d43bc5594ec8e3ac323245dce749ce2eff8ea586ac13ee91de2adba6cc5c3ef270373247cad05e18afed6ec0f69b3f688a6f27c04e0e3fe5fb1795bf1dfdbcb75d6bc71ecf95f4df54dd710fe5076c5636bfe9e942c05f2feea6361f1bdf7e97d45c5710064989bd3292bf745f69f823616c4a9335eb92252afec5ca7000000052fe436bb96be4b74c39a2519f21461bc40db9266d532adce3e82aec6b964d061051678af6bd0063f03f937fc382e8a0ea472b30cbbe02840ead6d3c0a1a140f6b937d519689cafd82c67ac5c294e8d82628440892d74f18c30b8bf7737573b0d812a72cae4635218766fd73578f1b8ae6201935e9d7fb121dbe8e15163e76363dbac7bf1d06204a7da2d3c09e74a648c128cf83a6f3a453e457d157624f6954a000000050000000403a98d91f5fed62e9b9c3d907c8df63fe29d053f9476167d814d36ba31bba86bbe4fb8addb172512540157ddd36c1d190000000000000004288ca511323d353ce1ec31144503e475b648e934c3f15fe9051cebbfab186a81391722b32c471fbda6f6c0b0f9c8e063f0d343bc174ed2f292730ad353cb2d9966221b6e24e8c36f287dfa65c3237111dde4897a455b950831dd38e7d7d3b2458eb528160adfb8789569008399353c1d5d63b17aee102eeb22e516d749710bb28aaaab242bb579e7ecfd5097c44b8be71edf2b5c59bed6a86a27d88605d6b7c1bf25590d3a84b6e6437fba8c7479a84252221c2e0fc6ed26a8e94a75838958e52f5aea0442eefaa67d1b183fab268c3ee26c51704962b85ee2476f46906af3a8feb5e44a07bd8f11160ba9c4b5b42c1fa02bcb94b51e15f3591784fba19ab3f8741eb1ae8c1156a21e642779dd2ebadc37cdb247131537012c2bcb45a3828ced39834c4beee1b74e884c64453e0925dc22fde064ac74d4a3283577c0327993fd862d9a1ad1adbdde2c74fb4e2d3955cb6a8044248ebd2f31fe88536d005d3ba900d017088f5b40ad8dfd64b90587ca561e4ced1dc13f4f77b253fd7db71eead5f4d9d91a31dab44c3664790554ca731898dd06a1f6b14ec1735077afd3ca12ff620c84dbb1cb27c39228d0a993dc92b7420516e0bfc410a92a182783bc6fa41c4005d75696c507c8447ca35d81e9b75d11d231bf476fe37106afcb43b347bd2086829f9569bba03aa6aa58c8e5c2176f8da4d69c9cfbe90cdb01cf961ab1b1424d53ed6f5245c147a21e9805a9709311f3b8dd8131c842133ef9afc8fbfcc909d71a3fac020830d9b92aee2de144ab52b6a833c5b1b3d2aabf021414ab3d1f1bff4453e62aacc4a3ec86b351b3ebdb06b1d8aebeb19be8398a6f8fe674886a41eef72dd72a6a99078d0b2912555f791631fd555839ebd91402094b8ee268d91dc5468d0fd9cd1ea5260cdfd08a20bd14b672e3bce567a425e503722f76c3ca0a080a6cf6307c609111b72002a60b293a3d9367b35f6faabbfee547d7337bdd75c4d90848da596d01c0cce633fbe45736d520640e8a4bf8eb91f64126f3b6e0dfd56d104101ffbe195c7602d7b1e63f9c77945cd53b5c1d6d19c4e59ddd8334643f15fa8b358857f6088152c3c2f1d9e21a56706c0bcc30f87d3900470216819a46c12bc16f3ac917ee74e651f20d8bf1e856d1ddb729ac5f7fda4488f50cdb32c2f58988fd09e3be17d058b3c09f4e61eeed0b52618f4b23d090931c8632d7d2aef40768135fd963edb736acf89679c7be29aaef385ae919a8836d5b60ab2f85334b600a9b49c27a12b5f3e09717653263ec258517672bf28bc58f3251e876dd0890ddd837361f13d176776d7db28a44992b1eefd69b02184b9b6c12cc73af90835522a76c46bcbf4aef0051bba88f94c730b071b095af2ae689a2a3afd4a2b1ad94ee7ab509724d49a9f8626455253aac13d9067b7876cb6727b97bebd2d479555b67351ee59efccb31ca217883ac230943b55a7a94a89cc579379986befaf478faa998a50eb1deb548723da7f018aa9642c5e996b20dc572e1b35de2104d757ad02f08392fc7a0f96e0a75671a001d80eddf08a4d2c998dd51e3ac1ae92a7200000005071200db237436cd7c542ff1ad5e8973f60a2f9547f24140bce52b22dba7559437b84bd023661fcad2f8aa6bb5c52340a04c6a1ad72ba2327b621a1f64833bdd3ff778b8deb63d933599a9d9659d097ecce2d0ac76d36c7f636edab0a0d57097d7ba39878dc356fe9f0736d4ac01de9f0691fb35055a7881c39728bd332febef41da6c758ed1dc545ca858b1b0fe8c46267224aa45ef5a7a019bf98aa35ff99e00000005000000044a85c34f1d5e3d5f6186b5fa093ca9819fe4743ca2db8ae3d316157de54f1c5f72babaed7497624ff7fea4f24cfd7fe30000000000000004e73fc7b3b38ce2198321c0f05704e8ca9a07dd2fd008bf32a417fe8fdefcc6f8b118b31fc93533076e29159660e4e5bab2d943a3bd29fc5589aec445c2d44eab4670cab7b78f2b7e813f8172d329d573af0868d56a3081bd22f7fa575b366b2729e3af9da748c4efbe4db8866d435674202537c82097433e4a6333dd9acf7a85844f6e36859d547d0c3f505a7952ce5aebce5d20e0a34d20a6741bdee394f119fe21405683309dd2086828d9fc86d370ced71fac710c73b5e7abaa930a82303eb8616d6fa572a0734e9bbe4a3630a61fca4ac9ebef438f66a4978050b189d65cb966d2ae978fd9dae85960dfbe589c8fe97e79fbe2cd0eda320e7507db44a5abc20511a12de217f37b6be38bde75cd8fbd6e3c7ceb95fecc58be7c41ebed985d5b9c83b67fe7dff9b62318bc8a1e6e2149727e972ca47bc63f3d446b57a6d7295dfbc19eb440ab3ec888027b0f54a585d87e1f3461be46808869382e0d3cb647eca7b29f42cdc5446006fbd58153580c42437b6d20bdd8fbe5ce02154e618df750f368d31b23f839396ea8fa2842fdbbe7aaf8dfa8df00d22d13fd1f2eff2daa7e0a193fc683b86985e0a33da498c5151d613f28d33cbbf20a0d2f8dd668f9ca667be11bf2ec4aa1848e1a9f66b3069e63d89d0c3c421bc566dd71e13157e72e4b28a489bf35c6464ca6b98ba6dc0a5e236fd1d6afeffaebcf8bb443c7a50d6cf38035a55d1f18f7b1378fcb510bf41800117085c5f276a78f853ca79f8e9121f23e2b3e4a514a78839caaae42204431068655a9e74c33caa82b5e6a677db7f71943e3321eeca123f4bd90e4e17a4abbe025684afe7fc49057fa86a64393d5ee109e6aade800c64414c4b6024ba9ea1431cfd23414f708ef15811e0d14b57fa9f06a6092996472cb55752d8f900984bf9bf9fd816ca5953491c91ad174d817d7ced49a0c2380eecc5039822617afad7b5244a451f21e76d8bd8d449e720904587e76b4e8fc771f29a4038e5bb112ef9eae477677333f1718c12472dde0a18204efe10adb117165825fd5f76fd161f027850979b4304ef64b906b8c5dbd51df63c0027d0b61820c89dd3b8d747d285e918aac6daea72c68929cb63edbb4181af2b6f50a73404415d11baee572af2b1d600f5fe63ef2a0e021e0c41a5a23333461a074a81ef92abc97b46320086a341dec3bb47ca477a01c64e7f749c6c19fae0fede2f480310916f273f2bdb2755a34a8f2fb367a6c2af642789cc17d52b29b1d8fe5b9921f06f9a0d74f871c8cca49829ca65c61cdce17b46a4dc927f95be281bc1ff72b9d4391e2fc8626c1953f284a3a310e09bdab525faa78b0b7e26859aaec194e88aaf4fe1f792924bcf8b51622e852d34d847c3d099e74145fbec1f6d8b77756c863835d7f7b82b3aef190f57233e97807ef8a4d20858e121f40c845edf12e5cb481d83d9ce50ac4be81acf5c57deece23f4c138cad107797d5756fe40ddd7bda76b4d9e5f744cf649b76d5724b97f8e6ea2c349393bf8e314b334c63f578d47ffea7dc3faec8d2e657faf8566367d66b1602975f36a82181f837e545d00000005c89ca9bb2d7db697f367fdddfd5d8bfdccbbc5b1d9f5f33c0f0d938f9aa4028760240001d43c7991dd66f9ea20183f1f292934c064d5f8df527568b995f206a86580f098fc3f9fd33d082e77232fcdba14b5d0813710ec4a5a60c16b9e2ac82df3446ade7c76f4ff4a46beff4ff30f960826a2fec9af85a842ad6076e02e259687d977d1d63506b00fde5afd9addd100b15db34b151f41a05a154296a7f1a1e3000000050000000490bfe15d1f05d95c8d253f6912ac96f14cb60f4e64fc90998ae6d74bb0b4bce0a6f3f285618e2d55db46424561b9e84f000000000000000416214e38e67788bb4cf4a76d7b93355c0538b5b1662ff60e7585952e3112f21aa9286a7706b621b63e8b88524600e101f4e56c5fea5980c2a1ddca9db1b551852f65956e852ea422835ae5c788714b5fc8296c0c4c06fdbf199251fc1cb17ee4b4937abb22e35465f88020ba330e5f7411633e6204a30709e40414b755bfb0d84986cc2bb58b8c80d630ae379a8c4adc6df29f85a52fa70ead5073fde470c9666137ebd63f7f13637da99fb978b1f16dd50df2c213a22d9a828ec060c4c90437ba3593ee61439fbcd1920dcf74d6433177881ef1e2bea125fdd625584dab5960129210d01be44d4cc5d80f0930a58f6f210db866e68b63794c1ea602a7ad5520d3888aae9f652fe6445aeeb6c9c2cad8806e3e1e3b073448753447215cff453317d58f3003068af281356a5f448dde54f7d352cd62d5da058ecb792f56e9da395acec4d0236e3c80e3b449f31f892bbf171365236289b491e0dcd67cd86302d0583a4c54ad5e15366dfc02ec74b1cd197ddd07176d588e2c9e16d159518a9e6306a2eb7a4f353759be460b1e1aeaad7880ed82010820b34f1ac46b5a05a4dcc146458b3eef5e0ec19ec9c087604271692d2697f8c04b6529063dec7b53dde54ebe42a435ce2f3f3b94a7f78aa6f0aeaee0356b5a622bc540fd81df8a5a0f9cc7baedcf6231f50f707fd39ce1e0101fb4cd79d576104f735c647f555dcc756eb868b793f961c5689107d9d8a6c628ab52cf6d552c1962e4decfb224fa1b32afe6e837ce2c584e848724d5165f4cbbe94e16482435498b787a7b16136e9bfab14275c30decb873821b7ead9cdd3304383f9c9027eed62d871d127e8fc99094430639a87cda5097bed99d6613115d9a4415dfb618889bbd9e44c34dbebd06da3e167993d388a6f106b0948d4cee011830970ee8e5719bb3cfeab6ce5a9f6415b89770b39615e98aa6348e2a0526112dffd31efc22bfe8e6f233e45ff70ed9039a0624fa290211dbe9bf965993a57f24b658879a4464164c245c6ae11476ba05fe6c7821bc5e42375556dda06f1bb3e8728b88f77b8a0a13325269eaf4c176bc822abf8b5159dcb8a4675e4eddc51827a34547e754cef9addbd2116cbae7b70c3324dd17dee271390ba6a09014930297b3abdfea220bc5dc17974e88c2992dee1ee2c1ae1e4802fed5b94120db3ce063c3acb7e625c4b4de2546aab6441b9bc84d63d43fa33557ed1e28de5076e2e3c2440b502406d2cd019a55c2f0ca457510e36cf32d09b5c92f6e600c7246567122dc2f2cb6a5bbcf6aed8ef5686523ba11a7379830d3ab78081e239cbabaa9ee320b0d3074c0306dcaa35ec63962dba95b9d861ab19ea2f5682f505e964f47ff9fcbee437dbf8689ca01b580adff8a2e35f8c4bd3004889e74b3e7ec1c7a5bc381d577fad6be1bf34ea8dbaa761b2e96ad618cdd0153ad7e6e3a5f4f855e0889aa1d321e244b2f1a5f603a456e69cc321abcc9353413f5f6a2ffeefdebe156c3b6739dcd93acb95113e5abe10435a561e25a7e016d03a18ccaf00f9c774fc1c0497c4a478be37e9abf60f221c206e47257a76d000000052c887acb0b01fec3d954155fb6c05c1cfd49a7d08f47b922ff0a037dca04512d09ebb3f25c134b43782c2bb22d4635564d7d443b84848ed576a9c6c0d41f529788e76a899c34b361795c891b2ef03cac5dd5f85df25f2d4705226e60617619a5aa86e9474a0b5fa6544dafb998af97ce7f99082fbd4296920af3dd3879b1887b058004d091cee10d0796ea20bc0683d7230169fef922d6903e9f946410b7e54700000005000000040e0d0f3221d2ac730300088e7360b2b9c57764c964e440d7e2f291c8d5d9e2ac225f58adaec5332a98b9c596e3e4e6650000000100000004a755f4c5ba2c97b47448ef2baa8adc9b4e47070af99a3d9abf25ef3b062e7b3fa3fbc54d239babb16ae961ae8e440b6df5d2440cce3fa87e19b516ffa6a022a528ab89921dc9ea6c4955d0d0b4a5068272dba7ea21e6d3629d8af2c5a902ece63f5599d9b7bc4a431dfbe553d949e571a99b5a4603038d3c03a9a6556da85c1d898b0190fd4577b3aa90478693bda15b42d07de0f90f98ee8fbd2a86ef768aa393b5bbfc7984654239b9e4f40a8bcddf34b422933566eeff3d21f88ea8dd2a3c865b9dcffb856f36016378ef08d3b53dffff17b9a1a04b1bd6b29d74d3ee0def2c231176d0d9acf93c7f61c83e569ad935266cf0d2641e0b90fbf91b04cdc635a2ea378fabd9ae6f03ca8e68c36dad3cd69293fe7c871c4fdef014ceed7483018cc2da6d29f6317f504b0490529a4776ee96048f2ba88973c6f21a6e5786be1927b53c881f79e9bfb62f0dbdd90e62ebd16bf18dbc8b062b046cb04f50efecc7d1712cf169ac2987f7eafa1d33ca3b653275b3cf8f5d5a6248e02245ec87854dc2b8da097726a26179c5960d7bd7bf62b59f89bcfba31211d04f18874470ceb8c8cff42237b15e38587342cf33fc9b54a47988e5f7897722d20f3713e80e8d75806ae7eb82b459072f28420ab2a6cc1e603f39dd0aec59a1be963aa72bb174e4330ee61ed79e1337515f6c38d4738c8c17e499aa4613469e4cc0aacf345bc634a3a3c5057bd802b252a6bfd626275039993e11889c2fd6c1e5b454aa34333cc4bfb4b24aef6dad7843781c9e81bd5d9ab528f880f25370a9f9da186b0acabec8ecaf405cab5f75f850958eacc21ae5138c80ead4c580e3a0f8ca4701ad6dcde39a9963544744de529fd1d6d67ab6674accbab6636042e90e24cb3d2eb2e017def1ad7be905d29d51d1376ded655da81cb4d04d59082fcd70fe43430a959fb01f02722fa6ed39c6c612f1bd5d4f2ce3f462efa2d07bd4b3bb20b1418e36088369d289b5e6a62ab5c71a831f635edcf4d3a112bb44ad216d8bc48062e50925ce1f260d1ae4c709c16f3c531f0e7b6da1dc9d362291f9c58b56bc6b9b16ecb4988755d29fe9f2fbffbee9f0ee0d5b431426db9c72e633b10bc6b9b51117877323e00e72b0b60c78f53fcee4851c2a89bbc3ce62704415b0ee1a78b7fb981eaf8490b284f47e488075f05d32a7630d7b6bf12adb495c853e245ae856922a764ebcb6ec194710d8b3e1145c1b573548cd76a9a567413cd5b3527797ce6feeecee8de885240a7913ac5c121ebbde05691124dce512d9c19df63f908b1d23536a93ded8316802da8348e1bf46cf80f3a58954a6acbd56efe44ee6a7e977a14fd4889b9ec57f6ba99d47eb75f197a1d234f8ed8861dc34a7376d5b8a66d3562fe92df1482979f4d8cb7f01ea4978d5071a6f2513efc6dc65ed4bbe9382b59a2bf5f86004244193e483d200d4c413f28247ff74951c9a6ffdafd71b6c6e581fa2255da48fd837b0757603f316bf95e95a1752ea0d3fb559ba090a7f306ec3ebdc55365aedb352050936298442a88e7ab27bf91a86a5a2b1e2692c469d3edacaf1d361976700000005ac67c55d81b59a4f49a3373c41941394f2df2a29bd154718cb7bf01b59f6c0d3293dfc6653da2687a2acd7b1209201d8eb54ec86e0999f9cd9911346d6bb4b5038729269c3f09560792d44602af220bba33f78757e0b1a625783d83e080a312d36b274205192b04c4945e5e541514933d1eb52724d39a2c72dfcb3d2e665250a22f411c4f9b224d38e73d63a1ac6ff2dd225846a51a691215663b0cbab31c28e000000050000000475769664d9c2a018a311b3181afeb6dc5644e3c96f758148a862eb0b9acbd9454daaba156a0dc07880367649a62f058d0000000a000000042e71818137e7a0de0913d3dbe669d8ed052eb11463e0e27944d8321827eb9ca10f6333c8f46cf967ed981ba096943ba9bb200c349c3c861e44d69e4892da1cff00c1f3f761384c17500be09af903705f6fdd58f5b6191744361565e453a0ad4db84fd6d9b05b29eecf53d42c279b449436ef411d9a3a7644093871dcf4191350611289215624c0bbfae167bd2ba5fea16b5bd267a16ce73b42ed7b7cdbc9ec90e9eda87a264346877871b136a0e15f284e953a01ce47243f4baa84b209da7097a29449c0e1dd5de4909676071e822b2cdf4020d048090d62f139efbe257cf22f5f6e260ca398e822b78ab6f369fda984e0eac0e1c406a10c2500d816e189da26c79da4f20d8d1c70b9d2a2b7cd1b35531b83f5109aea65dc33c52950190c5e91ed06e7ce01fe22d3ea8277df3b18b973e1bb29aa9385e9623c7d75d0d137257e13207b63660e88a145acc65d1c14faf9459836b1343d55b7d5a2d0e953e5a0d2f6e54321cbe661b0360372941eecd5b2d5dcc5a9fb7cc846f277260b0ddb92eb15086d7e44d43eed12e05a84c5de08bb924d9695687c63958a186bc0a4f7b284b0cf40540a3749f019565ec9facb1d5602d8485f3ff5b9e6dfa0bb96428b3f9abdec3d830b6893c682a767eff95afd5e5486bb924373ae42824d8bd3914bbe37fa2f39f5c4569e0962163af6a9621dec44d9c2c21f02d9126439caa62299d569426648ede23af52fe8f3ef62962778e12dcf6f50c82a77fc7b5b601f8304a800ad3cd17f57ce757baf8db0f12994831c204dc55b47f3aa650b096a506118aa213721d4a5ec26951d5b489d623d2341382359aaa447895b7b8b7ccffe5473f3bd4e4a4feee427307c155c34b857fc7ab7c7b4dac15d0b1bf0201e5c32702b8bcab101f5c3bb7d357f1ba37ef329210bbd95ab073b6028f3e0f5410d07cb7860d24f9431cb32ef567eefd542831a8539b321fa23f8c8c2e5c96699cac8931299672d3451d67882c91df6cbd2dcd18b8eb6d0d42d9601ea60c0469774aaa8001e36e2512bb8488315350d02650b2a7b8d614d18140d32958be9f0ff626005a0362632e9797770506e29794d78a441220d7af4f2393cd48dc3dd60dbb4ede627939e5db71aacb5a7bd66355ec4ef7afef1b6b10e84f4d46f3951f136194e2ed02e6a501f74b3933e14ffee106a814e45fde1112dbdffd75925724b48d35cbf8ca958535910bf7dd22008c2f1c929602a55e975cd394e8faabdc5b1a7d1f423d46e3544f38be84c30b786e4aa3ae3a258c76b76e439a59fc014be73d155bb616fa6743d95d6fb10ce7b6e47c825b050a61661e1e121749edca4a788957a2591dd9a4227341ae9a15ac21aa160a7d317c7a6e5473087f74c8af6559f94bef1b2ef386bff40a7dfc6db067970d161abc22068a6d16989ab890c3c71492addd96b9fff4a5d05633432f975574cc41624745fc78b732f1b0111d5c69b17847d22d3731ff262028db786829c282f8b050c5250491e87f13ad387a2523fa5255ad0af22ce6a63df5d51237b48323f5de1b7ebe8f1fb244a2e1f1a447a8f68e0225bc694b203000000053e73cf2115d7d605601febff7a660097aaccf4705112b7fa133990a734fdc6de8436508c95c94243184cb4a272e1748c983dbb0e9d6e8023ccfa07e4b1b5b0109d72ca5b6b83fb49c7b76fa9c3e88beaaba6e46364c5ed122df80ef06838ee05480ef5d99c10a2c704ae827e2c7529b1c4c7053449698987d74d0704de2d94c5c75bef86b09aa5e3633315c7c7522f98efdb912779018acd75d59392185ac4fa diff --git a/src/tests/data/pubkey/hss_lms_verify.vec b/src/tests/data/pubkey/hss_lms_verify.vec new file mode 100644 index 00000000000..49399411e80 --- /dev/null +++ b/src/tests/data/pubkey/hss_lms_verify.vec @@ -0,0 +1,24 @@ +# RFC 8554 - Test Case 1 +PublicKey = 00000002000000050000000461a5d57d37f5e46bfb7520806b07a1b850650e3b31fe4a773ea29a07f09cf2ea30e579f0df58ef8e298da0434cb2b878 +Msg = 54686520706f77657273206e6f742064656c65676174656420746f2074686520556e69746564205374617465732062792074686520436f6e737469747574696f6e2c206e6f722070726f6869626974656420627920697420746f20746865205374617465732c2061726520726573657276656420746f207468652053746174657320726573706563746976656c792c206f7220746f207468652070656f706c652e0a +Signature = 000000010000000500000004d32b56671d7eb98833c49b433c272586bc4a1c8a8970528ffa04b966f9426eb9965a25bfd37f196b9073f3d4a232feb69128ec45146f86292f9dff9610a7bf95a64c7f60f6261a62043f86c70324b7707f5b4a8a6e19c114c7be866d488778a0e05fd5c6509a6e61d559cf1a77a970de927d60c70d3de31a7fa0100994e162a2582e8ff1b10cd99d4e8e413ef469559f7d7ed12c838342f9b9c96b83a4943d1681d84b15357ff48ca579f19f5e71f18466f2bbef4bf660c2518eb20de2f66e3b14784269d7d876f5d35d3fbfc7039a462c716bb9f6891a7f41ad133e9e1f6d9560b960e7777c52f060492f2d7c660e1471e07e72655562035abc9a701b473ecbc3943c6b9c4f2405a3cb8bf8a691ca51d3f6ad2f428bab6f3a30f55dd9625563f0a75ee390e385e3ae0b906961ecf41ae073a0590c2eb6204f44831c26dd768c35b167b28ce8dc988a3748255230cef99ebf14e730632f27414489808afab1d1e783ed04516de012498682212b07810579b250365941bcc98142da13609e9768aaf65de7620dabec29eb82a17fde35af15ad238c73f81bdb8dec2fc0e7f932701099762b37f43c4a3c20010a3d72e2f606be108d310e639f09ce7286800d9ef8a1a40281cc5a7ea98d2adc7c7400c2fe5a101552df4e3cccfd0cbf2ddf5dc6779cbbc68fee0c3efe4ec22b83a2caa3e48e0809a0a750b73ccdcf3c79e6580c154f8a58f7f24335eec5c5eb5e0cf01dcf4439424095fceb077f66ded5bec73b27c5b9f64a2a9af2f07c05e99e5cf80f00252e39db32f6c19674f190c9fbc506d826857713afd2ca6bb85cd8c107347552f30575a5417816ab4db3f603f2df56fbc413e7d0acd8bdd81352b2471fc1bc4f1ef296fea1220403466b1afe78b94f7ecf7cc62fb92be14f18c2192384ebceaf8801afdf947f698ce9c6ceb696ed70e9e87b0144417e8d7baf25eb5f70f09f016fc925b4db048ab8d8cb2a661ce3b57ada67571f5dd546fc22cb1f97e0ebd1a65926b1234fd04f171cf469c76b884cf3115cce6f792cc84e36da58960c5f1d760f32c12faef477e94c92eb75625b6a371efc72d60ca5e908b3a7dd69fef0249150e3eebdfed39cbdc3ce9704882a2072c75e13527b7a581a556168783dc1e97545e31865ddc46b3c957835da252bb7328d3ee2062445dfb85ef8c35f8e1f3371af34023cef626e0af1e0bc017351aae2ab8f5c612ead0b729a1d059d02bfe18efa971b7300e882360a93b025ff97e9e0eec0f3f3f13039a17f88b0cf808f488431606cb13f9241f40f44e537d302c64a4f1f4ab949b9feefadcb71ab50ef27d6d6ca8510f150c85fb525bf25703df7209b6066f09c37280d59128d2f0f637c7d7d7fad4ed1c1ea04e628d221e3d8db77b7c878c9411cafc5071a34a00f4cf07738912753dfce48f07576f0d4f94f42c6d76f7ce973e9367095ba7e9a3649b7f461d9f9ac1332a4d1044c96aefee67676401b64457c54d65fef6500c59cdfb69af7b6dddfcb0f086278dd8ad0686078dfb0f3f79cd893d314168648499898fbc0ced5f95b74e8ff14d735cdea968bee7400000005d8b8112f9200a5e50c4a262165bd342cd800b8496810bc716277435ac376728d129ac6eda839a6f357b5a04387c5ce97382a78f2a4372917eefcbf93f63bb59112f5dbe400bd49e4501e859f885bf0736e90a509b30a26bfac8c17b5991c157eb5971115aa39efd8d564a6b90282c3168af2d30ef89d51bf14654510a12b8a144cca1848cf7da59cc2b3d9d0692dd2a20ba3863480e25b1b85ee860c62bf51360000000500000004d2f14ff6346af964569f7d6cb880a1b66c5004917da6eafe4d9ef6c6407b3db0e5485b122d9ebe15cda93cfec582d7ab0000000a000000040703c491e7558b35011ece3592eaa5da4d918786771233e8353bc4f62323185c95cae05b899e35dffd717054706209988ebfdf6e37960bb5c38d7657e8bffeef9bc042da4b4525650485c66d0ce19b317587c6ba4bffcc428e25d08931e72dfb6a120c5612344258b85efdb7db1db9e1865a73caf96557eb39ed3e3f426933ac9eeddb03a1d2374af7bf77185577456237f9de2d60113c23f846df26fa942008a698994c0827d90e86d43e0df7f4bfcdb09b86a373b98288b7094ad81a0185ac100e4f2c5fc38c003c1ab6fea479eb2f5ebe48f584d7159b8ada03586e65ad9c969f6aecbfe44cf356888a7b15a3ff074f771760b26f9c04884ee1faa329fbf4e61af23aee7fa5d4d9a5dfcf43c4c26ce8aea2ce8a2990d7ba7b57108b47dabfbeadb2b25b3cacc1ac0cef346cbb90fb044beee4fac2603a442bdf7e507243b7319c9944b1586e899d431c7f91bcccc8690dbf59b28386b2315f3d36ef2eaa3cf30b2b51f48b71b003dfb08249484201043f65f5a3ef6bbd61ddfee81aca9ce60081262a00000480dcbc9a3da6fbef5c1c0a55e48a0e729f9184fcb1407c31529db268f6fe50032a363c9801306837fafabdf957fd97eafc80dbd165e435d0e2dfd836a28b354023924b6fb7e48bc0b3ed95eea64c2d402f4d734c8dc26f3ac591825daef01eae3c38e3328d00a77dc657034f287ccb0f0e1c9a7cbdc828f627205e4737b84b58376551d44c12c3c215c812a0970789c83de51d6ad787271963327f0a5fbb6b5907dec02c9a90934af5a1c63b72c82653605d1dcce51596b3c2b45696689f2eb382007497557692caac4d57b5de9f5569bc2ad0137fd47fb47e664fcb6db4971f5b3e07aceda9ac130e9f38182de994cff192ec0e82fd6d4cb7f3fe00812589b7a7ce515440456433016b84a59bec6619a1c6c0b37dd1450ed4f2d8b584410ceda8025f5d2d8dd0d2176fc1cf2cc06fa8c82bed4d944e71339ece780fd025bd41ec34ebff9d4270a3224e019fcb444474d482fd2dbe75efb20389cc10cd600abb54c47ede93e08c114edb04117d714dc1d525e11bed8756192f929d15462b939ff3f52f2252da2ed64d8fae88818b1efa2c7b08c8794fb1b214aa233db3162833141ea4383f1a6f120be1db82ce3630b3429114463157a64e91234d475e2f79cbf05e4db6a9407d72c6bff7d1198b5c4d6aad2831db61274993715a0182c7dc8089e32c8531deed4f7431c07c02195eba2ef91efb5613c37af7ae0c066babc69369700e1dd26eddc0d216c781d56e4ce47e3303fa73007ff7b949ef23be2aa4dbf25206fe45c20dd888395b2526391a724996a44156beac808212858792bf8e74cba49dee5e8812e019da87454bff9e847ed83db07af313743082f880a278f682c2bd0ad6887cb59f652e155987d61bbf6a88d36ee93b6072e6656d9ccbaae3d655852e38deb3a2dcf8058dc9fb6f2ab3d3b3539eb77b248a661091d05eb6e2f297774fe6053598457cc61908318de4b826f0fc86d4bb117d33e865aa805009cc2918d9c2f840c4da43a703ad9f5b5806163d7161696b5a0adc00000005d5c0d1bebb06048ed6fe2ef2c6cef305b3ed633941ebc8b3bec9738754cddd60e1920ada52f43d055b5031cee6192520d6a5115514851ce7fd448d4a39fae2ab2335b525f484e9b40d6a4a969394843bdcf6d14c48e8015e08ab92662c05c6e9f90b65a7a6201689999f32bfd368e5e3ec9cb70ac7b8399003f175c40885081a09ab3034911fe125631051df0408b3946b0bde790911e8978ba07dd56c73e7ee + +# RFC 8554 - Test Case 2 +PublicKey = 000000020000000600000003d08fabd4a2091ff0a8cb4ed834e7453432a58885cd9ba0431235466bff9651c6c92124404d45fa53cf161c28f1ad5a8e +Msg = 54686520656e756d65726174696f6e20696e2074686520436f6e737469747574696f6e2c206f66206365727461696e207269676874732c207368616c6c206e6f7420626520636f6e73747275656420746f2064656e79206f7220646973706172616765206f74686572732072657461696e6564206279207468652070656f706c652e0a +Signature = 0000000100000003000000033d46bee8660f8f215d3f96408a7a64cf1c4da02b63a55f62c666ef5707a914ce0674e8cb7a55f0c48d484f31f3aa4af9719a74f22cf823b94431d01c926e2a76bb71226d279700ec81c9e95fb11a0d10d065279a5796e265ae17737c44eb8c594508e126a9a7870bf4360820bdeb9a01d9693779e416828e75bddd7d8c70d50a0ac8ba39810909d445f44cb5bb58de737e60cb4345302786ef2c6b14af212ca19edeaa3bfcfe8baa6621ce88480df2371dd37add732c9de4ea2ce0dffa53c92649a18d39a50788f4652987f226a1d48168205df6ae7c58e049a25d4907edc1aa90da8aa5e5f7671773e941d8055360215c6b60dd35463cf2240a9c06d694e9cb54e7b1e1bf494d0d1a28c0d31acc75161f4f485dfd3cb9578e836ec2dc722f37ed30872e07f2b8bd0374eb57d22c614e09150f6c0d8774a39a6e168211035dc52988ab46eaca9ec597fb18b4936e66ef2f0df26e8d1e34da28cbb3af752313720c7b345434f72d65314328bbb030d0f0f6d5e47b28ea91008fb11b05017705a8be3b2adb83c60a54f9d1d1b2f476f9e393eb5695203d2ba6ad815e6a111ea293dcc21033f9453d49c8e5a6387f588b1ea4f706217c151e05f55a6eb7997be09d56a326a32f9cba1fbe1c07bb49fa04cecf9df1a1b815483c75d7a27cc88ad1b1238e5ea986b53e087045723ce16187eda22e33b2c70709e53251025abde8939645fc8c0693e97763928f00b2e3c75af3942d8ddaee81b59a6f1f67efda0ef81d11873b59137f67800b35e81b01563d187c4a1575a1acb92d087b517a8833383f05d357ef4678de0c57ff9f1b2da61dfde5d88318bcdde4d9061cc75c2de3cd4740dd7739ca3ef66f1930026f47d9ebaa713b07176f76f953e1c2e7f8f271a6ca375dbfb83d719b1635a7d8a13891957944b1c29bb101913e166e11bd5f34186fa6c0a555c9026b256a6860f4866bd6d0b5bf90627086c6149133f8282ce6c9b3622442443d5eca959d6c14ca8389d12c4068b503e4e3c39b635bea245d9d05a2558f249c9661c0427d2e489ca5b5dde220a90333f4862aec793223c781997da98266c12c50ea28b2c438e7a379eb106eca0c7fd6006e9bf612f3ea0a454ba3bdb76e8027992e60de01e9094fddeb3349883914fb17a9621ab929d970d101e45f8278c14b032bcab02bd15692d21b6c5c204abbf077d465553bd6eda645e6c3065d33b10d518a61e15ed0f092c32226281a29c8a0f50cde0a8c66236e29c2f310a375cebda1dc6bb9a1a01dae6c7aba8ebedc6371a7d52aacb955f83bd6e4f84d2949dcc198fb77c7e5cdf6040b0f84faf82808bf985577f0a2acf2ec7ed7c0b0ae8a270e951743ff23e0b2dd12e9c3c828fb5598a22461af94d568f29240ba2820c4591f71c088f96e095dd98beae456579ebbba36f6d9ca2613d1c26eee4d8c73217ac5962b5f3147b492e8831597fd89b64aa7fde82e1974d2f6779504dc21435eb3109350756b9fdabe1c6f368081bd40b27ebcb9819a75d7df8bb07bb05db1bab705a4b7e37125186339464ad8faaa4f052cc1272919fde3e025bb64aa8e0eb1fcbfcc25acb5f718ce4f7c2182fb393a1814b0e942490e52d3bca817b2b26e90d4c9b0cc38608a6cef5eb153af0858acc867c9922aed43bb67d7b33acc519313d28d41a5c6fe6cf3595dd5ee63f0a4c4065a083590b275788bee7ad875a7f88dd73720708c6c6c0ecf1f43bbaadae6f208557fdc07bd4ed91f88ce4c0de842761c70c186bfdafafc444834bd3418be4253a71eaf41d718753ad07754ca3effd5960b0336981795721426803599ed5b2b7516920efcbe32ada4bcf6c73bd29e3fa152d9adeca36020fdeeee1b739521d3ea8c0da497003df1513897b0f54794a873670b8d93bcca2ae47e64424b7423e1f078d9554bb5232cc6de8aae9b83fa5b9510beb39ccf4b4e1d9c0f19d5e17f58e5b8705d9a6837a7d9bf99cd13387af256a8491671f1f2f22af253bcff54b673199bdb7d05d81064ef05f80f0153d0be7919684b23da8d42ff3effdb7ca0985033f389181f47659138003d712b5ec0a614d31cc7487f52de8664916af79c98456b2c94a8038083db55391e3475862250274a1de2584fec975fb09536792cfbfcf6192856cc76eb5b13dc4709e2f7301ddff26ec1b23de2d188c999166c74e1e14bbc15f457cf4e471ae13dcbdd9c50f4d646fc6278e8fe7eb6cb5c94100fa870187380b777ed19d7868fd8ca7ceb7fa7d5cc861c5bdac98e7495eb0a2ceec1924ae979f44c5390ebedddc65d6ec11287d978b8df064219bc5679f7d7b264a76ff272b2ac9f2f7cfc9fdcfb6a51428240027afd9d52a79b647c90c2709e060ed70f87299dd798d68f4fadd3da6c51d839f851f98f67840b964ebe73f8cec41572538ec6bc131034ca2894eb736b3bda93d9f5f6fa6f6c0f03ce43362b8414940355fb54d3dfdd03633ae108f3de3ebc85a3ff51efeea3bc2cf27e1658f1789ee612c83d0f5fd56f7cd071930e2946beeecaa04dccea9f97786001475e0294bc2852f62eb5d39bb9fbeef75916efe44a662ecae37ede27e9d6eadfdeb8f8b2b2dbccbf96fa6dbaf7321fb0e701f4d429c2f4dcd153a2742574126e5eaccc77686acf6e3ee48f423766e0fc466810a905ff5453ec99897b56bc55dd49b991142f65043f2d744eeb935ba7f4ef23cf80cc5a8a335d3619d781e7454826df720eec82e06034c44699b5f0c44a8787752e057fa3419b5bb0e25d30981e41cb1361322dba8f69931cf42fad3f3bce6ded5b8bfc3d20a2148861b2afc14562ddd27f12897abf0685288dcc5c4982f826026846a24bf77e383c7aacab1ab692b29ed8c018a65f3dc2b87ff619a633c41b4fadb1c78725c1f8f922f6009787b1964247df0136b1bc614ab575c59a16d089917bd4a8b6f04d95c581279a139be09fcf6e98a470a0bceca191fce476f9370021cbc05518a7efd35d89d8577c990a5e19961ba16203c959c91829ba7497cffcbb4b294546454fa5388a23a22e805a5ca35f956598848bda678615fec28afd5da61a00000006b326493313053ced3876db9d237148181b7173bc7d042cefb4dbe94d2e58cd21a769db4657a103279ba8ef3a629ca84ee836172a9c50e51f45581741cf8083150b491cb4ecbbabec128e7c81a46e62a67b57640a0a78be1cbf7dd9d419a10cd8686d16621a80816bfdb5bdc56211d72ca70b81f1117d129529a7570cf79cf52a7028a48538ecdd3b38d3d5d62d26246595c4fb73a525a5ed2c30524ebb1d8cc82e0c19bc4977c6898ff95fd3d310b0bae71696cef93c6a552456bf96e9d075e383bb7543c675842bafbfc7cdb88483b3276c29d4f0a341c2d406e40d4653b7e4d045851acf6a0a0ea9c710b805cced4635ee8c107362f0fc8d80c14d0ac49c516703d26d14752f34c1c0d2c4247581c18c2cf4de48e9ce949be7c888e9caebe4a415e291fd107d21dc1f084b1158208249f28f4f7c7e931ba7b3bd0d824a45700000000500000004215f83b7ccb9acbcd08db97b0d04dc2ba1cd035833e0e90059603f26e07ad2aad152338e7a5e5984bcd5f7bb4eba40b700000004000000040eb1ed54a2460d512388cad533138d240534e97b1e82d33bd927d201dfc24ebb11b3649023696f85150b189e50c00e98850ac343a77b3638319c347d7310269d3b7714fa406b8c35b021d54d4fdada7b9ce5d4ba5b06719e72aaf58c5aae7aca057aa0e2e74e7dcfd17a0823429db62965b7d563c57b4cec942cc865e29c1dad83cac8b4d61aacc457f336e6a10b66323f5887bf3523dfcadee158503bfaa89dc6bf59daa82afd2b5ebb2a9ca6572a6067cee7c327e9039b3b6ea6a1edc7fdc3df927aade10c1c9f2d5ff446450d2a3998d0f9f6202b5e07c3f97d2458c69d3c8190643978d7a7f4d64e97e3f1c4a08a7c5bc03fd55682c017e2907eab07e5bb2f190143475a6043d5e6d5263471f4eecf6e2575fbc6ff37edfa249d6cda1a09f797fd5a3cd53a066700f45863f04b6c8a58cfd341241e002d0d2c0217472bf18b636ae547c1771368d9f317835c9b0ef430b3df4034f6af00d0da44f4af7800bc7a5cf8a5abdb12dc718b559b74cab9090e33cc58a955300981c420c4da8ffd67df540890a062fe40dba8b2c1c548ced22473219c534911d48ccaabfb71bc71862f4a24ebd376d288fd4e6fb06ed8705787c5fedc813cd2697e5b1aac1ced45767b14ce88409eaebb601a93559aae893e143d1c395bc326da821d79a9ed41dcfbe549147f71c092f4f3ac522b5cc57290706650487bae9bb5671ecc9ccc2ce51ead87ac01985268521222fb9057df7ed41810b5ef0d4f7cc67368c90f573b1ac2ce956c365ed38e893ce7b2fae15d3685a3df2fa3d4cc098fa57dd60d2c9754a8ade980ad0f93f6787075c3f680a2ba1936a8c61d1af52ab7e21f416be09d2a8d64c3d3d8582968c2839902229f85aee297e717c094c8df4a23bb5db658dd377bf0f4ff3ffd8fba5e383a48574802ed545bbe7a6b4753533353d73706067640135a7ce517279cd683039747d218647c86e097b0daa2872d54b8f3e5085987629547b830d8118161b65079fe7bc59a99e9c3c7380e3e70b7138fe5d9be2551502b698d09ae193972f27d40f38dea264a0126e637d74ae4c92a6249fa103436d3eb0d4029ac712bfc7a5eacbdd7518d6d4fe903a5ae65527cd65bb0d4e9925ca24fd7214dc617c150544e423f450c99ce51ac8005d33acd74f1bed3b17b7266a4a3bb86da7eba80b101e15cb79de9a207852cf91249ef480619ff2af8cabca83125d1faa94cbb0a03a906f683b3f47a97c871fd513e510a7a25f283b196075778496152a91c2bf9da76ebe089f4654877f2d586ae7149c406e663eadeb2b5c7e82429b9e8cb4834c83464f079995332e4b3c8f5a72bb4b8c6f74b0d45dc6c1f79952c0b7420df525e37c15377b5f0984319c3993921e5ccd97e097592064530d33de3afad5733cbe7703c5296263f77342efbf5a04755b0b3c997c4328463e84caa2de3ffdcd297baaaacd7ae646e44b5c0f16044df38fabd296a47b3a838a913982fb2e370c078edb042c84db34ce36b46ccb76460a690cc86c302457dd1cde197ec8075e82b393d542075134e2a17ee70a5e187075d03ae3c853cff60729ba4000000054de1f6965bdabc676c5a4dc7c35f97f82cb0e31c68d04f1dad96314ff09e6b3de96aeee300d1f68bf1bca9fc58e4032336cd819aaf578744e50d1357a0e4286704d341aa0a337b19fe4bc43c2e79964d4f351089f2e0e41c7c43ae0d49e7f404b0f75be80ea3af098c9752420a8ac0ea2bbb1f4eeba05238aef0d8ce63f0c6e5e4041d95398a6f7f3e0ee97cc1591849d4ed236338b147abde9f51ef9fd4e1c1 + +# Internet-Draft draft-fluhrer-lms-more-parm-sets-11 - Test Case 1 (SHA-256/192) +PublicKey = 000000010000000a00000008202122232425262728292a2b2c2d2e2f2c571450aed99cfb4f4ac285da14882796618314508b12d2 +Msg = 54657374206d65737361676520666f72205348413235362d3139320a +Signature = 0000000000000005000000080b5040a18c1b5cabcbc85b047402ec6294a30dd8da8fc3dae13b9f0875f09361dc77fcc4481ea463c073716249719193614b835b4694c059f12d3aedd34f3db93f3580fb88743b8b3d0648c0537b7a50e433d7ea9d6672fffc5f42770feab4f98eb3f3b23fd2061e4d0b38f832860ae76673ad1a1a52a9005dcf1bfb56fe16ff723627612f9a48f790f3c47a67f870b81e919d99919c8db48168838cece0abfb683da48b9209868be8ec10c63d8bf80d36498dfc205dc45d0dd870572d6d8f1d90177cf5137b8bbf7bcb67a46f86f26cfa5a44cbcaa4e18da099a98b0b3f96d5ac8ac375d8da2a7c248004ba11d7ac775b9218359cddab4cf8ccc6d54cb7e1b35a36ddc9265c087063d2fc6742a7177876476a324b03295bfed99f2eaf1f38970583c1b2b616aad0f31cd7a4b1bb0a51e477e94a01bbb4d6f8866e2528a159df3d6ce244d2b6518d1f0212285a3c2d4a927054a1e1620b5b02aab0c8c10ed48ae518ea73cba81fcfff88bff461dac51e7ab4ca75f47a6259d24820b9995792d139f61ae2a8186ae4e3c9bfe0af2cc717f424f41aa67f03faedb0665115f2067a46843a4cbbd297d5e83bc1aafc18d1d03b3d894e8595a6526073f02ab0f08b99fd9eb208b59ff6317e5545e6f9ad5f9c183abd043d5acd6eb2dd4da3f02dbc3167b468720a4b8b92ddfe7960998bb7a0ecf2a26a37598299413f7b2aecd39a30cec527b4d9710c4473639022451f50d01c0457125da0fa4429c07dad859c846cbbd93ab5b91b01bc770b089cfede6f651e86dd7c15989c8b5321dea9ca608c71fd862323072b827cee7a7e28e4e2b999647233c3456944bb7aef9187c96b3f5b79fb98bc76c3574dd06f0e95685e5b3aef3a54c4155fe3ad817749629c30adbe897c4f4454c86c490000000ae9ca10eaa811b22ae07fb195e3590a334ea64209942fbae338d19f152182c807d3c40b189d3fcbea942f44682439b191332d33ae0b761a2a8f984b56b2ac2fd4ab08223a69ed1f7719c7aa7e9eee96504b0e60c6bb5c942d695f0493eb25f80a5871cffd131d0e04ffe5065bc7875e82d34b40b69dd9f3c1 + +# Internet-Draft draft-fluhrer-lms-more-parm-sets-11 - Test Case 2 (SHAKE256/192) +PublicKey = 000000010000001400000010505152535455565758595a5b5c5d5e5fdb54a4509901051c01e26d9990e550347986da87924ff0b1 +Msg = 54657374206d65737361676520666f72205348414b453235362d3139320a +Signature = 00000000000000060000001084219da9ce9fffb16edb94527c6d10565587db28062deac4208e62fc4fbe9d85deb3c6bd2c01640accb387d8a6093d68511234a6a1a50108091c034cb1777e02b5df466149a66969a498e4200c0a0c1bf5d100cdb97d2dd40efd3cada278acc5a570071a043956112c6deebd1eb3a7b56f5f6791515a7b5ffddb0ec2d9094bfbc889ea15c3c7b9bea953efb75ed648f535b9acab66a2e9631e426e4e99b733caa6c55963929b77fec54a7e703d8162e736875cb6a455d4a9015c7a6d8fd5fe75e402b47036dc3770f4a1dd0a559cb478c7fb1726005321be9d1ac2de94d731ee4ca79cff454c811f46d11980909f047b2005e84b6e15378446b1ca691efe491ea98acc9d3c0f785caba5e2eb3c306811c240ba22802923827d582639304a1e9783ba5bc9d69d999a7db8f749770c3c04a152856dc726d8067921465b61b3f847b13b2635a45379e5adc6ff58a99b00e60ac767f7f30175f9f7a140257e218be307954b1250c9b41902c4fa7c90d8a592945c66e86a76defcb84500b55598a1990faaa10077c74c94895731585c8f900de1a1c675bd8b0c180ebe2b5eb3ef8019ece3e1ea7223eb7906a2042b6262b4aa25c4b8a05f205c8befeef11ceff1282508d71bc2a8cfa0a99f73f3e3a74bb4b3c0d8ca2abd0e1c2c17dafe18b4ee2298e87bcfb1305b3c069e6d385569a4067ed547486dd1a50d6f4a58aab96e2fa883a9a39e1bd45541eee94efc32faa9a94be66dc8538b2dab05aee5efa6b3b2efb3fd020fe789477a93afff9a3e636dbba864a5bffa3e28d13d49bb597d94865bde88c4627f206ab2b465084d6b780666e952f8710efd748bd0f1ae8f1035087f5028f14affcc5fffe332121ae4f87ac5f1eac9062608c7d87708f1723f38b23237a4edf4b49a5cd3d700000014dd4bdc8f928fb526f6fb7cdb944a7ebaa7fb05d995b5721a27096a5007d82f79d063acd434a04e97f61552f7f81a9317b4ec7c87a5ed10c881928fc6ebce6dfce9daae9cc9dba6907ca9a9dd5f9f573704d5e6cf22a43b04e64c1ffc7e1c442ecb495ba265f465c56291a902e62a461f6dfda232457fad14 + +# Internet-Draft draft-fluhrer-lms-more-parm-sets-11 - Test Case 3 (SHAKE256/256) +PublicKey = 000000010000000f0000000c808182838485868788898a8b8c8d8e8f9bb7faee411cae806c16a466c3191a8b65d0ac31932bbf0c2d07c7a4a36379fe +Msg = 54657374206d657361676520666f72205348414b453235362d3235360a +Signature = 00000000000000070000000cb82709f0f00e83759190996233d1ee4f4ec50534473c02ffa145e8ca2874e32b16b228118c62b96c9c77678b33183730debaade8fe607f05c6697bc971519a341d69c00129680b67e75b3bd7d8aa5c8b71f02669d177a2a0eea896dcd1660f16864b302ff321f9c4b8354408d06760504f768ebd4e545a9b0ac058c575078e6c1403160fb45450d61a9c8c81f6bd69bdfa26a16e12a265baf79e9e233eb71af634ecc66dc88e10c6e0142942d4843f70a0242727bc5a2aabf7b0ec12a99090d8caeef21303f8ac58b9f200371dc9e41ab956e1a3efed9d4bbb38975b46c28d5f5b3ed19d847bd0a737177263cbc1a2262d40e80815ee149b6cce2714384c9b7fceb3bbcbd25228dda8306536376f8793ecadd6020265dab9075f64c773ef97d07352919995b74404cc69a6f3b469445c9286a6b2c9f6dc839be76618f053de763da3571ef70f805c9cc54b8e501a98b98c70785eeb61737eced78b0e380ded4f769a9d422786def59700eef3278017babbe5f9063b468ae0dd61d94f9f99d5cc36fbec4178d2bda3ad31e1644a2bcce208d72d50a7637851aa908b94dc4376120d5beab0fb805e1945c41834dd6085e6db1a3aa78fcb59f62bde68236a10618cff123abe64dae8dabb2e84ca705309c2ab986d4f8326ba0642272cb3904eb96f6f5e3bb8813997881b6a33cac0714e4b5e7a882ad87e141931f97d612b84e903e773139ae377f5ba19ac86198d485fca97742568f6ff758120a89bf19059b8a6bfe2d86b12778164436ab2659ba866767fcc435584125fb7924201ee67b535daf72c5cb31f5a0b1d926324c26e67d4c3836e301aa09bae8fb3f91f1622b1818ccf440f52ca9b5b9b99aba8a6754aae2b967c4954fa85298ad9b1e74f27a46127c36131c8991f0cc2ba57a15d35c91cf8bc48e8e20d625af4e85d8f9402ec44afbd4792b924b839332a64788a7701a30094b9ec4b9f4b648f168bf457fbb3c9594fa87920b645e42aa2fecc9e21e000ca7d3ff914e15c40a8bc533129a7fd39529376430f355aaf96a0a13d13f2419141b3cc25843e8c90d0e551a355dd90ad770ea7255214ce11238605de2f000d200104d0c3a3e35ae64ea10a3eff37ac7e9549217cdf52f307172e2f6c7a2a4543e14314036525b1ad53eeaddf0e24b1f36914ed22483f2889f61e62b6fb78f5645bdbb02c9e5bf97db7a0004e87c2a55399b61958786c97bd52fa199c27f6bb4d68c4907933562755bfec5d4fb52f06c289d6e852cf6bc773ffd4c07ee2d6cc55f57edcfbc8e8692a49ad47a121fe3c1b16cab1cc285faf6793ffad7a8c341a49c5d2dce7069e464cb90a00b2903648b23c81a68e21d748a7e7b1df8a593f3894b2477e8316947ca725d141135202a9442e1db33bbd390d2c04401c39b253b78ce297b0e14755e46ec08a146d279c67af70de256890804d83d6ec5ca3286f1fca9c72abf6ef868e7f6eb0fddda1b040ecec9bbc69e2fd8618e9db3bdb0af13dda06c6617e95afa522d6a2552de15324d99119f55e9af11ae3d5614b564c642dbfec6c644198ce80d2433ac8ee738f9d825e0000000f71d585a35c3a908379f4072d070311db5d65b242b714bc5a756ba5e228abfa0d1329978a05d5e815cf4d74c1e547ec4aa3ca956ae927df8b29fb9fab3917a7a4ae61ba57e5342e9db12caf6f6dbc5253de5268d4b0c4ce4ebe6852f012b162fc1c12b9ffc3bcb1d3ac8589777655e22cd9b99ff1e4346fd0efeaa1da044692e7ad6bfc337db69849e54411df8920c228a2b7762c11e4b1c49efb74486d3931ea \ No newline at end of file diff --git a/src/tests/data/pubkey/lmots.vec b/src/tests/data/pubkey/lmots.vec new file mode 100644 index 00000000000..ca402cabcd3 --- /dev/null +++ b/src/tests/data/pubkey/lmots.vec @@ -0,0 +1,32 @@ +### Test Cases created using the implementation: https://github.com/cisco/hash-sigs +# LMOTS_SHA256_N32_W1 +Seed = 020b020b020b020b020b020b020b020b020b020b020b020b020b020b020b020b +I = 010a010a010a010a010a010a010a010a +q = 12 +Msg = 0a0b0c0d +PublicKey = b27b082f57aa5155a33936c14541f864366b5e7eef6925cf571fb4c9ddf4d0a4 +Sig = 00000001a1e979f494da74822e07dc69af3b59b92357cfa1fa73610ef6b4b884702df2bcd54b339a89becd95eeb105cedf728fe70b61f9496e99470a1190e580bcc0def8b7f671a66079b6d83b5ccdfd4ebeb7a2bd5566fa2faccc7f495ce19b20893cea343699b6a4a6cd98200e9d1a1dbf0e005c7fa077a32c77ffabcac81d94fa17167079721a911e85a983ebea0aab91f7c9f38093d894763354244ffadc58a026d217b9b5824d76f58ddc460e89e1b7a1b2b9bd81d48e2bc0245eee6f5b77b2104d45ad1da8bece3f668641b06c6755565dfcfb9b1ec850820f60d67c79a212e4f06870ffb6787d1954739fb5722708454e461e0481cbf54cff5641bf77a88ee8923313596d855184ee76eacb8816a3c5dca8e3ba0e2644ff1955ccd35ca47c9b5e40bb4dae909ad0a647d50ec1c195a4c2c369ebeb5a4bd31727c53459aef650ae5f1ee357610f865105f34b2f9a62cb4609df5ecd306c84eb3950be29be788c41dd1a633566b52447e124881b4e798df2490e352c5837b329bf0aebd2773cbc869a5b0d86aa6e7fd137ab797e8a7b716b9a69cc0a56c4daa0b9c079ca5d7ee4e8df4f06bbc468cad1dca80e2a070ce1a662bb1324a04b93a3df5667e5149935d3a796736b69d8b7da2ec90a2afb1f409020868fb8778aeba53c7c6ac233de94cee013c365c35f4fd86b8fa5e2446b7fa929b37bc8e7300a28eb6c759787b232c0119b4ead3223f0d041690b56bbe65bd928afed95411ec48b6cadf2762bca9092ee3d1b70c6861418cf4d2d5b12837756dd3dd6c8ba751c8cd1840252cea66e4a26bf205a6711f5a8f73e431d0a31ea901e3c1a23b98136ee06ecb4c0eca230e5eddcbafae6416272efb3bca90565900d83aade799bfce12d4931ca11db096cd7aa673e3ed48d2dbb6871f90f4684645d81f8155c997842a5553a433a6a79ac46a090e3dad8394ed8a92d64d76a5a22667db46be24796b8e79e5a1f51cac44f70cb708f518fa0973b5f860462c8e734e3ce83977f1cfc1d5e0541268df01e70bdd35d095aaa074f24d15057b5aa8aded10197232d064a603a951642fe2afe0820cf85f1990fde19ef8332c1ee96c719826b102598ec55bece282e4e6d9193684e8b5796ba3cc9efe2ce4d8e383bbde797da7909253576a28002b18e621a2d08d5f7b8eb0eac03b578e5c8763164920f705262347458b7260581f8d835df55f9b48e6f9a5da7f2d9bca9d52804ab5703ba19088283ffae8d12aee44c02e12e1ab428e60979005a281390865a4eac80635720d00c132657b40ed03919f6da420bf1dc1ef8ca58f490491760ab9c9cee9032e3ce15e766faefe8fcacf25df8211aa3c9f307c4ff2af29ca636626f70ab4ac93f74ca348db3dc5352fccab33af30c72f0a3aba60b0b448f9e6d7b9fd0661a4a1e8982c5f15050eb4ea61e7770331f7ad66cc8191b62d4f04d5d2a9d9f80f1f58debc9a3c20cf7d58c347e56b52392009d184cca4c4d0185022bbb8e0d436ecea8aaf667e02c7f0ca337af2b20204de0556fd2e7e893fbef4e53216b97d6b5c3ac6cc0c00ac6612c22844c1b91a418356fae160743357d4023a77c9a5bc1e4a2e44d505de2c6e9f409bf238900d0f329b1e2375ce8c61629179e5122bf06a0a9b8f72024328e7c4eaa441b785aad18f2273c6096d44a4ab7c318c03eff251009ad52f45d538d90d8128d834657ab1d88831e34a939808950a102db6603df4e5934f420ba1c7726fa27548418c6ea744b84eb2ccd8022f9f8c0ea1c2ffcbfdea1bb95e580cd808b7681d9ed789fed43516a63b82cee4d3bf1ebb38b34c4522feb372dbcc67e589ae0a4b342262622a7b6888361437b817d76c8aff9bb87f8233da14708466883fe4853a52c168fd0517fa2654254aa11d9356c4be73e275fcc438342fb50ec5ac38eeebfc77f6f4131701f8a7876c37ef9c453ed5fadc822d0c1dd03a50e31e121b9d4d977ebf5fb16ef2260b81d18eed0acca00d1a7d5cd82394ec41e0bd4411d711d4243b4770f4285f64b4ccc24b445a8075ef714af5db851fc59da76939fc722d96549de83006006dd282c44937031c10f98a338c96e09fd27d23d9f88d369aaa2be76b31013bda776ca75dcce8057ceec99fc6730b586108bfe5942788433883b752827cfbca58f3e1b26e318fc635f585c37e66464f0399cd8b4c7e08b90aea9cfb879a0415035024333eae9d9d7b0a55f871df6a15658bff286c5dc3134250b62cf5590a2c948cddcebdbb0f69ee5a1b9a8a068e3cf564966a850cf3703316b46d06d09eddcf32bd8a7e19bb1fac2abc37ebcc77b3bafcc76c2708e5a3f19c95375357a982aaf4aad3394bc76424dc748b7407953b6c9afaa53e59ee8738261afbd473e46d72b324815710234c816d4bc1ad9c992b24e3af42783d90d0fa08497f9f107b2f6525bbbb3f4d29cab5cd8ec884c821c234a4b22c8ef31cd96760187238cb5c0fe808271ebf4e6f661ba99fdbf0ac1e0b737016d6ce2234bb6744d22e87f924dbf33cf75242c15eaa020903cac8223e47f4e02f384b2ea91254ae998c85c4fc14417aa727b09efc348eac6150de00d1783820f002317987c176cee1f5d0028b2f3ad424927a16cd8622c416cd3fde7681850173dc91c1ee030c025c9daba935665578718119b90c71f5a2bf1faea0cb434ec6e3c084d743891b3555aa8f9b77bced1994d101421d1c9d1a1d4c024433e0af176e1d0300471dbb1fd1037e65111733e99c2480b3fa624acf2ba0ac2f87fcdc94abdba2b974b43c13ae3a5566a8bc67cad0524bc9cea6704c9e284274ffab0c4eb99eaff9fbfc580c2986687f03b824eb1484548e1f1298532bb4523267904d920ed66fc46a74920bbcb450dc45875b010982850a0d396df2fcc542ad8c177935e0a16506c3cd1120ab2b89b1125f6af15e08b1a738b0099e9bf74dd2a46a9fa3e5c1288146d088208c3325fedbdf16cdc145ac8698eaa24f4017d5495be14f0a0d95835d142138d51820c420f525c08127ddd10cf50c86f9295f3bf4fd03b79c37b228e523457d1b5d05ae7b9dfe58662793f9ae9281f20301cba54e9f10fe1a959641f461038a4b9faed87615c649a768633124df0cc4548322f4e9282ae4dfaa30b9f1ecc7703ff6b0511089edcb7c114090e37ae7bfa859e95ecdc4fe760bd84d23628fbf77f123e5c117eb2a9e18363400d82edd0eead6b75a85b3b0259158b389ece0aad40e2ca88357575f2e716ec103e0578b136ca81b7c5df8a1ea7deaa6d8eb084894f7a94afcb890ec3c6dc979f25ee020955e572f4c31f636f4e55598be20fef8ce9cbcbd60ccedcfda1dc6409bbc61b7586c9779e3fb63cfe1daad3a9c9f0868c8cd594b46439be61ca32ea35e62ff3b110610af6726b630fab7ab4b084e4c49eb75ffd1ef9233b76848ee16b6edc7982291d6bee63cb3d1135b30c3413e83f4e99eb804defc6a61dc0d5f76a7dc86fe740790227f2665c3ede5b1694334a254a2e7145821e290430ca62ca03f5eaf97b0751672cea9b210ba7290af7026309e7264d5010773422ddcd01ed20535b8484c24e4e4e03ed2f257b23473099a412177827ba2bfbc42ae9c136bdd822c1481dab8fc55c41bd2384da846a084e1842cc27b769f52d20499fba494508acecfb4ccf2091e68f8baf7fd7bfa7bab88fa867bfcd08ebdc1a8b881bf39e2b5e2e41194ba8078ea3e97f4851078565cbb777e10ff2e97ff1ed4a42a1c9f40d08de8a8eb0f4b4702933fd324026c97a4e2ae6fcc79c584f04b4210e5b3babdeb7fc820d4d977d726ba292f9e9078541b30979d30b86be248d7135f32cd44c69993f2446a3b1f8be2a271628895ca0a71488fb19d8a6cdce8114fd08824f00093c09ea4118582d8fada68879a8909fb77e2abe51eb3643414646d3200425897af4c564bfc38531ad77a055269383bfeb8a9f81f4d62a9ef50fcabbc8d534b71fd2613d1b7a205b1e2b1a84fd4a70d82af208be21d83259cfb3800c1bda22b9c6382e9d27fb2ec396c01cd3f4a0ca4b1a59f8309cfec8cc3e6d4ffebf9465f13985e91a7eb5041bc7b3e64b7265d72def3855c645800ba4eb133ad0208ccecbe4f7abc52b796ae5b2fdadcc55bec759262c84816043619175e6bc77274cc2ae1780760ead6c1c790b3a1b6d66f88286afd3a84e70b71515214dc2d8e37f3a52b3ba73c90181c14fdfdfe60ba127adc63c94449fcbb3d8c7bf2d39f81f0078a2522366ede5f42f04043df2c59c4a70d8af9a0c37b2ed4365e67ba77f04f373080b0d67fa938c0df010a93f947136dd838c060bddeda69ce08716dfa180a2ade2220ac7b720d7445b04b61beb27a3a0f16ac8b16e4859a17ee3a09d15a4ff66f9dd6b57fa97b2ad0f7b8c9aa54889a8b3e47fe87627635c67b539b92786325221638a701e3cdea67e01295ea0b5138770b726873d7c9ad73235da7ca08a6207cd581c34fdcf821ebc8e04d6b8088bf8ad48f0b44eea69b97c5e50a0db835f50427606d5b6736603a24f0142b057cc61c2612a383467871ab6784aa3566bcd1d3d2292b2e554da5b7eaccc08ec6871740816e21b3e5bd3ee15b58508dac60ca451bf2c7d25aeabb6c2e890e0fefd358102f48b6e18dc93eda5a0515dd66068e0a34b24af2b88ae1d60634a06c1fd1d733d80a87d87ffa5c77b0301a7141008707fb6c0bb9c929c78d3c8f92207da29e00b5dbd25f3f9ffd94b02c5159c9f86625c51947be2ba807b596925d4e7e526e96fe92ff79e6121169851e24ac7afc7a5c4cd68be1bc21416c2a99c33b4aa5cb2b7c13e8ff690883b7a58b19a5e3ded71073c0b56f221edf8d48a3f6a4a503e9e242bfea72e8edcc62226ae547ed3ecf01f12239b3d269552612aef8774983a460f92c245154b0ed532b0dea1c2d4c39ff8257d0f37545588327fae8f5737916955e426083fd026b7212069e9a3ce7fd643f10b94d74d3f5f71647d5cced8bfec56409f50e20d3046595df6cb151f117137a233aee68dc7def2e72d3ee37d649b58b795beeef87114afa1798b58141360095af218745a768d115ea0d7b4e8cf5adb9b237d22a4eaac5ecf5b2c9b2a1e854c9e27cda79aa1ca9a0c12aa7bc2e368d501d22578629f5ffa00e62f6be066c34a8a5b644db03b2c39f426b174a9b23e75fe33921a088d2fbd3886be946093f811cb684bd7a77d67723526f3f1fd357944b72e77553f413bd4afea2efd13cd9cb8b53001e94e2b8b036830247e62198d1191167c6193a15822a1fe1a454f733bc89d7a0a2bcb8cd13ace251c82e92525dc40e1e6dafc082a9b95a66716b5352ad54830665ba3b663a14ac6fa45cc54d5370509f807ed195998be6fa75eccbed77dc84cb3f848d6aab6e57817a94cdcf5c1b0786ed3b18709089f11cabdb79696ae8c64fb6a9f58ca264cb3118f2f7a41d15436e765b25e72046daad9b8b58ba33977ee906064088ac2a4fb33df000f1a6b0cc3300e72142a881591248a8ea222ef6bf192153668c05c39d86f2a2d20910aab15e402f9408745117c859db108fc7970fc2c041a8333565164db2a1331d3c31a69f334a69f07a051b7273b2977919d31569a0a7e04c7a17df61d5f37cdef9cdc48aca224e753ec5037dd0ef4476c5be0220586ed23469f1caca0a8280696aa0ae6d120520ed3aa1de81b3233131b03dcb7e1d0653a5d23f42358052a27fb92abbedd253d80e6f44a3318219287de29fe0cb71f4ee772523a1bfeb5cc386231fe8b926dca30b2df9e7149ef98caaebaa44a6222bb7988d64c2f708956632e5af9830f9658f0ee99ea25f844924786f8c7b12ed0ad75d8f573254e13568ef258ae7d836cd5cf364b096a8de05a5fc66e8d224b9a5e154eda0dc896a9d4164880e86da25e2efe68d98b0d54fac3b5bb29dfb94a53b409fa432a437cf3d849bf0912fb8233e8272605c20e9302b7e21dc9e98aec1f962c58d87f364cdf8ce9b087da81a23b8ef955229e5b4b199f93895433bcd084b376e7cf9f7e1b68aff47435551b3a618efd6004ecbd06a3a3ff0e03eb1f13df3da84923c07d847c6f802da7ea26296767dbdca3505c522fa67aee1a66bc3600422017895008db39e983c0cd8adc4e482330dbe592b80d97e384b636a93b6d69d0d52efddc2de76aa08bd9e31dd810d5fe9c272d4ec69d866fa1065b9fd7b4233c96433baa7e9c6ac09ec875bb9401a61f220e04ce4879e8b6dfb150854f717192f1980c36fd8474ab10775900324a618f81f9fd05cbdda652d3895ec50beffc842f92a2475f01b2db4c03106802aa2649e0e60f05fb7a2fb84dd6d25d7a7d129afb13970e45f08eb06e7cd8e83edacfc0c65247e95181c7c6c0e2646d3383217ffd8300475ef83a13e39bfc5d8b48bc6d2d56a41073525c768afcc367685d8ef1fbace8c330d785d9e383b146aa5d284e45b573360f8b07b8afaaa9d71b5525a3f885fd6a91b3f39910c1960792cfb7097851384aad4f587ce1e50a14c198e66ea3683a441989dfbee2f2e9d010ca451a9ed548ae74f507de4d2ea848874fb872790e97f7efe0d1763ff11823a263990a638425499f6fe0b2dc320a44f9d4a327c78670a4286cfbc6844cda5580fceaffffad8379b3a4f9bce18932ba50f347cd7dc51a74ab9e617b69c4a489f42861c761f2245c7388b00045a59bd17b2c800c1cae7a29822c5521934c399f537df60c38dc6dcbacecf7770644cb8d88a02485bed8210089abda8ef1272b121a9150e05390a98fc52c80cde8f2388426f474db2f01574e9dab822cf535bdf2fea1ac60b5bbb1f763498179928475882f6aeabe2414c8c202406e04e0459b5439ff276164a0c3742674c38fb7836f637fa168cfa75d145c786f8e3a557ff09d4ea237b5d39c8e522b7c033a8911873acbc6e754ebaf0a07ffb821b82d7d06b75111820c670506b1ee0d6722f96424325c081ad685b5d4592d940ca6b5d36c876efa8ef1f5f927cd59a3dbbc9d449cd312ba4f2085fdf814ff68b6acb0dd7db720aada862c4d8a82ad80853d34944c58ef7f7ef2e1d0df89a3c66802c755c4b939a0726db7d51bbeb7afe67b8d7e03cf55b25a912793e1f16b6c7c6271257ccc08c43579243f886e51f876cb55d8989efa59abe4c0d9928d1100a51a8d3b0c3eb9e9c3bca0660b4c61cac7d153ee30ce89942e7c605bad854dd883daa5f40b3b07a0220f5c2a10cd826df1de04cf2d4e602d7a172d41bc7ffe1e044c41b9cabd1f6e957a03b884f8accd2c69394f646b30bffecf296052c0594e4bac1555cb05f069371533be1575fe5e29504863880b00809d7a5fcd608dac59105d7a1a7951e797499fcfc52bd4b656799aea532fc7d42983e02e0f4f8308d6b65ccdd04ecd1b19135b848eeec2c9a8d479e875af5cf826ee3523d52f2804bf5083840cb2940ccc89ef4223f9768f53b240ec8dde51539aff492a037e3cc6960a7bb754aaf245f10860e2ed8c22c26b50962ff2e61493817cda23144ccaed33f3765d856652b7e2f49dc8c4224d148744f3c9448943fe34f8f93357f3c1bfeef1d5d4ff660cb0e5a6dbc5a63af402b09d96542ddcaef78345ad538612f1c968cf57f5b2b7b78f84bf3a2e0d47936c13b9852f291473bd506ef548482de7dc65c24a5db3d52e0667ce917b797fd5188047643254e26e311f05ec639e8bd27e0f8bc1837898be1b93393c211836d39311f2a2d0181fd8a1535161ef4da278c381e9c2a95d5e2425c3398c92ba951c452598fbd9593c3eedfc33b2ef24cf897b46ee5a8900d47c61d50356782825bebf1f81a3a91990589af6713bf868b20f655cab1c67a108fa09af8f069a2fdb454e9bdc491c0d57cea9cba98dc78016376cec5cf2c1613681c5abc557f8845369008994a8a608cbd9cff6533e9bc78e35cb208b3c2c5fea950f177b86cbd06e99738a8bb85466f1410381cac1eccec0f0ea6aaba8382000c8424693ce821c4054b8ac115aff988c2a07cd8bcd820d2084f0df36d22fb50efb03bf26fa8b9bbed371db2dfe0a8039c6c878106b9382f67adbf401a5dc773db0efb2eb002dc069ed8ab45660866a6e1b2caedfd19289377f0dda328dbedf4f39ecc9599a28ea789cea7dac96e7f81291d604d61970e1cc7e65569859f02d3590a54d3520ea15d6779c08e08ec483a7b840c6a5a7ef4e616730bb158e633581767341cff888e6dfc71ba34f9fc8f5ab799b563812a15ac47e7a9c1eb8283338ced7a948ab9e8c3e39d22ad50ae5227ad0da0d30a796225475ce71c8b5c9f8742c0e5e70dc38fd3c766f0e5d5166d447d250ad5c2f831e37afcdd7991d8cf3cc3cebeda5f560d8240b3891f6577a1fb647b185a9e4a8bdf4bdea223b7ad6de98d169def0ffcc3534671629ee53c6bf7f916e723bee55f0bb58da7c9bf4e1bd7f7cce326d36c955cfa8336326b776c76337053675f624a3d0f8220df43d621299ab04abb8969f014b4b70e254a69b55ce559f675c8d1d9a0e09d42d5b1e2f24723f3296d5d5f4b17ca374e94f313c0a1a6a5da59c950435f1766cab4579f05d636b1685c015183cee74b87118df2cd5a05740c762bb8f4b04f37906b2367968f527774bd3c2bb4b177510aadbecff47f6ea2f884b0b751e7488419996956a083eed03843de0dab74f23ad33f3b45cc44131894c6b2a85dc2465aa4164c9134ef7238fee85d267fc7405f0c72c6ffd4f185e041463501baa92e3784c2e0f4346e9557c0cce27daa250b5d23318eda2fdab43f407da041f7ac959486787d58a66c0858af5497c1179947f54562bd6f18ba0f045656324ef82eb311bf953a4c4ba93546607e84d18a03d6ce34bf69cd83d43bad31c7c3c88609b2cc35a3753bf6b9ea789d28b932746809d3e0fded673196d70dfb92a9b4071a35d2f14b9fd9a5be686dd8cdd67abea78cb5b7b4788029f551bb218a7259819f345e04501bbaf4d7514eb4222154b4fb2d8a5be9cc6924ee49f02ec1d5ca05a45f1ae9971ea5716351460ed284d59e7ac887efb716a566bb2d28c60a7de1d7f049d9af8c05db1d5e4f98ffbabb1aa37d7435f8984a2a219401e20bce6c22516a027a29a55e6f040646b62831c2f5ecde0cf5f6a523d8f98fa7574fb59a4ff6b18e06544faf489ab8908a76ff0ca18f74fa31b1c279242b331107cfdb0fcd8c4de5e2a023462bcaf5f72d85db4ead38ff1b2d02608c5cfb245fbb1bd03173ce0ab4b7532434bf9ef9969ed61c46a339d1f3321a659eec1e4a502a589886489b9c927a5d2be4f5f1f851edcbf6bc95316d34b4336a0450bed35d00da8b44dde4bd4bd4e092a66390b746b898eec52450b076fda11eaae6bfd05796d0c8e71b9f0d59a8ff19b1726f1eb0c771c7d00e773170a64defa328a4d250310bce423d92e837debfdf710d10387de616649837a3e583078e017ffee66d41595f0c3ddb973832e937eff995cf123560e79fd28b3aac018e53415e1984433f57670e3287716c9f5a3de579f0de0b70bc67cb87e0cd2a95c3e7921fb9559fbf3a57e636505d8decfb5fd614b22ead1091a20bd5923eb990d2970d2d6b3aa9a054161ae54a32579537cb2d92e5f6368bd9ed99e7e07bc663780d28213740662ab2c1af65a23ca2c82cc074b50165f9a006e34aee3c7f1b44a4971c9beb2f9d619436e0c52cd02dc16a97821a877657f227f218a2658340848371b2b38bb9ed0f4054242a71ad3e8c5839310f2753cf5e29bfac5b83040dec7665b1f99f8ce13cb07349a99b63f3ba7871eae4bd592469a48740379e7b24b12b01c46551b0f3196cbd22a693adcf933c612a8c3152407bf162dd93b0eeae4fbb40e47e1a273e8267fcfe58ab87cd0907ce30a2114b70ab5d8a9133ba72f33eb0f0087e4f712c703705e3007f3a8963374d42e4c3b701d3d760dff3f3604f95a4effdc89e4dedadefc757617d4d1b92b985a59aca1e87614cd171d420181cd24dd8aa1b4882ec0eea22983a38d64d94b6a8f3290cf732c82dfe0467b987d5506f986a7768e5d6a9dd910fdb70541d86b1df6c0c4b7c0837ef05c2dda975761aa6146ad3e76d7b01f90188e90b68efbf796931ab61f1b3bc25a64f061840f98d9d126cf74eac88a1e3ea5102b4079429416c85f44042c7004518bd4edf3b3764d4f79f505308ed07ed6e50ff1474fcb7927c1334cdf1c6d83a8c194cad614f3ad046fe2a697611ee58696c2f69702254a995600e673f1d690ee2b9a80f9f1e8f2bdc81f019a1ac92c38678968daed813b2709f97d0d15725b068b5b131a5e279ceb1bb9f0598bd233af64b31eb0243326e85426349edd2b0c8bfbb4760a57578d6a8aacf21ea653ff913f34abceeba2013b2f38ddaa18a84ac472c664471555ad8b7a554723fe76f05587773efbf0da7bc42f1d564fdb0cfaf5163a3f078af59fc8447cf4e29469ca2159aa3b251a4cc864bf381515d453f23fa2e6341b08fcbe27822afea4a371b0722b8ac6d55d4f0d15a09bc7b29824cb32c913e10e094b9d9a1425b5dea281f66cd47ca2df2c66dbfca407fee547c54a92d865fcd6dae0b274d59a26ccaa7f24f64ed9f771fbad28f9e668f958b63471f54e013f58bda927f0d1e383564d27b59adc515d353672eb9b32e7203e4da077903c7e1cfe03273ba72aa5dda25b9e042159f56676ed1503be43b961af94128c45c918a72eb5e09a5e9ab5ac7331c8d973f299dd2b038576cd2e7f9bb0f560ecd24080cb3e9c182bcc4e86b94be861126b0b84a97d479c17888bf1948c8bb4095576744c9ca3814dfa5ad774e4d17b8c770b7f591afaf4b4fb90536b66023abe801a0f6f4e78174898408177f05a146a88d720eabb793d1500658a72468a8d97d093d883b1be2da12ad6e7f7f44a009319b75691865655655c1a7e3cae1cc536615b81f820f03f3b3d939e02a8e741f2c46d2da11eb01b4c5cc519f6aac9357a7ae079f4e48e52090247011887c596662f3765d155043f19abca2f942e25d1e8ac84a72dfcd050ae6cfcfa66033a5eba91e095b4e283c3d587a6c639fdf28ec03f20088ff43e2ec20d4aeecce50b4ad0b61bb10ed45cc64b5240b15983591b23059ac77e06fa8c3aebbe66c506979fcaae10e7be4fde62c47ce46fe39d4ddd096ea5b0beb56af204e8232c0cca9e836b6f586289eeb46050deb490fca44f1124bcfbc38b48b83da9dcdd4f2b43a7d89a764aad076fb1a2e51a5abe03286afbc9b004a8b4ce15a943a6b8238b7a06a08247519857f459f149641ebc8eb946ddcd8225c60f7aec1b79427aca7f02c2d6adcb583cf4e1772e58e071259cbb9eff74c3440bc389c73072d7de01e19a154ed294f6203c49c4cc9b267d6e8c7c5472a56ee496dcc142a101f516380919e08fd2a01fc0dd094da00df5a48e418357a3410800d529a92704f139248bf08fd1028d5ef9f6018b4b42239faf68fe7fd101772903c322558f4f575ea3b7dd68fc06fb60c8bf78e6586887f2795a28cb8a35f9aea12b597d082aaed54ac92cc054815dfa8b76eb4af634520b342c320232b414eba3b635f2fb221251902e16578a96ffe52ca992a58cf8b2f9f141910e0488f80a8e166415fc1de079e55c9c646652e3eb6d6024b5b29be1a39817b98702e06508854a20c54c0de12c5ae51e39fea0833705f72998d53130906ee60591dd7d2ab2e4158ff3e64bf0ffae3e69e601818efe3b27ca8883eaee1534f2796d7d78cb5a6ca4ba277f9bd8024d1c9da54dfafc7be89cb7c301dc0c286a8aad64bb80ef4432c71a6f2daa52027b4776d8a13aa49437e4dee18da1d6a053aab1e695604bb04eaa80ee126dfdd0f40ea28d4c5e1a4439d661089f59bb307889ad6c45d7b36c99fa29d1dd7751135bf809668f59e34367afc218df791e09c9356ea9070b7d88094dcf4aebaab190ee55d7f302c6fa47ba2581e9d7f09059c5b1567ff818b29168039db7d2cb6906fefe2a231f38b65da72c13077b38cca3129038016de20340726ee6ee80359b7bed27922f + +# LMOTS_SHA256_N32_W2 +Seed = 020b020b020b020b020b020b020b020b020b020b020b020b020b020b020b020b +I = 010a010a010a010a010a010a010a010a +q = 12 +Msg = 0a0b0c0d +PublicKey = 78ec03decc4eaed2d31db9df5ce0f14b7614d11e9d230f651291d71d2a1e4a27 +Sig = 00000002a1e979f494da74822e07dc69af3b59b92357cfa1fa73610ef6b4b884702df2bc2683301bee4f1733f0f0723699475cb5c2106d1d1c486470a470e0c85e91b683c9912b2749cd33b2bc2abd22bc2b72daca0502d01542eed80823ebba7857bdf92a659a9fa5964f75189a467ed69273fc60939fea06b5759962b2de20a38fef66814e7aabd4d9c1820b8a80c2792a5f58316f1b8db83fa92502bc1e95dbc37b0354f37bd3469ee28ff0da81362afc5e14f9dc37cf06f421eceb9a701438c5564b756d8e053ffcb9474fd97fc044507524bd274f356a4518f1e44db213266e463c957721cedc4021e5bf360028b4bc6e8e8e16b6312cedb65ced952041cccf766f61e7b57864ff90f135c8c07fab58ea46804d6c90ee73da50f3f92fc87d34e9900ce66dd6385746711c6ed06c5586be6d05d3bd8567904ffadd921d8ee0d320ea5f1ee357610f865105f34b2f9a62cb4609df5ecd306c84eb3950be29be788c41154c397a85493edc0e2db56983f9a14598f8eaadcf65254b173bcad8b52ec1342a652299ea822833871d7a826f20aa4c8f92fc19c39933a23f619d572313d504df4f06bbc468cad1dca80e2a070ce1a662bb1324a04b93a3df5667e5149935d3675ebf1b7a8d43e8caa0c786dd4ba8912378ecac068a7000350fac8256fe1930b05508c0754fb947fba7436c2bdd9843d49ed4bd80f6233e6d3161738f32a3cd9297422da5c602799b47c0e8290b2755574d57ee509f8f91e49e534ce67253f7ee3d1b70c6861418cf4d2d5b12837756dd3dd6c8ba751c8cd1840252cea66e4a3c62749697e887344e7fde7852a9b3b618777eceec594771c37c6bfea546e4340284b67433fa35637662b06c6ecdd571ce7e2c09c7b85d478f4720b9de571e48aa673e3ed48d2dbb6871f90f4684645d81f8155c997842a5553a433a6a79ac460a282f48f2142f47dfc2cafa49a9bfddbd126a8e4524bff2443451f34aa5d80773cbe0f98dbb977710fc55aba2f5136feb4baaa1af8384e635bb73b6daa0a6d4f9d03dd20b4dea99cd76963b4601eedbe48a778c1b3248655805552a5895e9b1cf85f1990fde19ef8332c1ee96c719826b102598ec55bece282e4e6d9193684e8b5796ba3cc9efe2ce4d8e383bbde797da7909253576a28002b18e621a2d08d5d56587b1d3d42ab1ea92744a65b751e9e6e878362734595e8611e8bdefdcaf660df1454956127cd3bfd6b610985d2f1f537082d9a8120819734dbe2afd00446a5625a91dc0956b178571aca79c2caca05f172017f89805157baf82b131ee7cc9c5555c3eb5f7f464a6ee863966eb3fa8389da11cfc38ffe0bebf4a85045fe68a614e7147a8da5ab944b50a3c34dfe080049fcb12e091d2467c6dcf64a9bd3ea2f0a3aba60b0b448f9e6d7b9fd0661a4a1e8982c5f15050eb4ea61e7770331f7a6b272b6e282425107967d24645edece3f48a18dd7a432fc1a2a2edc85db954989d184cca4c4d0185022bbb8e0d436ecea8aaf667e02c7f0ca337af2b20204de01eb926d5e60573fbd68751949552f575addeca4f1da548d9faa687a9661bc4766fae160743357d4023a77c9a5bc1e4a2e44d505de2c6e9f409bf238900d0f329b948a1bbf6a2bf083d4a0109cb2ab826ca50d25956ebf48d97296242e5e301529f0e5aa88e9b6a6c7fdc9231cbc45a9d48712b2c9c79f39a3462bd3393a69b60de088b8d1a5c57744bf0b1e6ec83d7fbeb21d74b27155a8e09d4870482e67ade84eb2ccd8022f9f8c0ea1c2ffcbfdea1bb95e580cd808b7681d9ed789fed4351422d1bdc91c0d095738bde50b83225f9c8a0467365b2f198acf4e60d70ab232b7a65c733c75330ef02000a07912944413e9fac12c0c6d96b5f696d88d3c3a58c19740bd660ae9adf479b7e4fcca344cb724c8aff5db1e4bf7c32c540d575fc825da4f9796c60fdb2dbbd47bc98d110e58374098780ade9d8cb17641c3458146f56606139cc1393f37923db0096440202038e67fd769f5cae16e1c97a12b2bc325f185f538d9435ead0e7a812aec830020cc0219fbd84d96639b380ce0ca57b5b1a48104497d3aaedd31aba83e6036726abd0e984f344e9cad0b0a8c152b5494b910d6c13bf92722e5047394d7704c71cc4103c9456a2097ea7a612edaf095e21f3fbbe6836c5710224e44eab38ee70a86fe48976d1c28ae083db636dc244cd8765fc5dd280db5db8a84347d466879e470bfbfdc2f4f4ff39fedfe76970a7d716a9042fedff936a220d1a3a4a281642bbe78a5ee2bcd76b60e24cb432e4a2ca0e2a8139ffaa6fdbbd6480eb72b26ef45d95a0d98e1b56395d2c5fff2451f48d906cae1abaf9e6c38cf1429ab426c4284f930eba9df208996dfd1b751580e05d97324815710234c816d4bc1ad9c992b24e3af42783d90d0fa08497f9f107b2f6523778e710702ffadb1e56035b8dd5b2c62135a588ca074fa9929d6a1080f003bc8271ebf4e6f661ba99fdbf0ac1e0b737016d6ce2234bb6744d22e87f924dbf33cf75242c15eaa020903cac8223e47f4e02f384b2ea91254ae998c85c4fc14417aa727b09efc348eac6150de00d1783820f002317987c176cee1f5d0028b2f3ad33dfe5efbc69ef536297b83bde642a57978c8085a959163d26361e603c8837275dfe2859ea9fac1ccccc347e89ba7240ef32f407866ee9306fb25f7f12e5ebf24c4f3c4ee455b8f86b9f00ab0485c2b1fd5d932ad2bea4480be5b2d147b9d118a236b206919b82fbd207318c84d5edf56a528a87c0a06c9797a6074dd4ed75c0ad0524bc9cea6704c9e284274ffab0c4eb99eaff9fbfc580c2986687f03b824e6c7c81075157b34f4c021e22d302219c07ef3993c5cb30c27ceb15c92f34e89010982850a0d396df2fcc542ad8c177935e0a16506c3cd1120ab2b89b1125f6af15e08b1a738b0099e9bf74dd2a46a9fa3e5c1288146d088208c3325fedbdf16c215a584365f31dae26fd5a91849fb42a925a9060bc0f12ba53f521cbcad24fb1127ddd10cf50c86f9295f3bf4fd03b79c37b228e523457d1b5d05ae7b9dfe586721b34f24d6ce7ff2853504caa87fc2307455e552854359bbf2cc01f49b278a112087dce1a2980853c93b3015f32a575634bb4e080f17773be064f47cd1659f993dde48db78d7926e13eac21412c091aad3780f444bb0ad34d14215b050b5bb57c3c1476514db7321c61aed9fea344685eba9f1c383a3c7a93084855fefc613890ab1ad1ca2886da5d38c7cdfd1fb5ff6c43d576b4036f900b8e487b29c7fb037fd54b25911697aaaded686cda7bda64738c76077bd26d5af7d6044f9f2acc4bdc6409bbc61b7586c9779e3fb63cfe1daad3a9c9f0868c8cd594b46439be61ca2bdbcab42aa14b93e8e7e4e1014c8d4a9ebe6a46736ec8055122ab1a0e3217a33aaf90f0e43d7b7305dc01dcfcb4b00df121d6b4d49ed731d62ec863d6916d5a8c5b1fc5c412b0b3bb5b387b29af954127648adaed3adcdda5831dc248694a3301be47d7e32d380940ff821bfb538df1362a41d9397ee196c49a9c6d5bdcd03edd6c6ab99b31a46a65cdb14cf032b70a3c6c12b44c9b66581aa6bae6dbb1740f151f39b1eeedf4edff3d519d8af54d325eabaa79886aae7948ee4a32daf15c29494508acecfb4ccf2091e68f8baf7fd7bfa7bab88fa867bfcd08ebdc1a8b881bf39e2b5e2e41194ba8078ea3e97f4851078565cbb777e10ff2e97ff1ed4a42a1d087f84f34a29d908eca2ed73d10d3089b1ad778563077e76db8ebaba8f0efa58662d68f071fcdce76c23bcd4fff5090d74e4b24ee698070288823b096c289602974957f9ae8ab28746eaabc60200576d9e59954250fa2b8f3826e1557f7f8504f00093c09ea4118582d8fada68879a8909fb77e2abe51eb3643414646d320045872d23ad2acc57af765a18c5179195f06c2bab4cd605c6821bc4fc183f5e3fd959bffbd252fd7c20423744add5c60f48d937817621cd4aad6a2bd331d2467dedb9d0e816e605334dc8ac13a2cb3d9c10f72bdd6246128a12182628af540652bf4355d8c2f61d472712d7df5fcda5d5c3372bc817f60903051c87ec7b2e53115cecbe4f7abc52b796ae5b2fdadcc55bec759262c84816043619175e6bc77274cc2ae1780760ead6c1c790b3a1b6d66f88286afd3a84e70b71515214dc2d8e37fb17863905ac6b2c482b7c2ac1f3f0be6f6c349f54b20ff3898b0cd260b795251a7e9117d9804e390925658da155bdcf0d755afc32c24ccf1aa469419aa20d859939ba25642920d1efa95b301948f8dca1128bd79d6fc4b153c7d752cd7f895a73de5585043303c4ea1d857398c630bec9f8b7f27c7e0b2421c025a5cc05a35c266f9dd6b57fa97b2ad0f7b8c9aa54889a8b3e47fe87627635c67b539b92786329f0b2ff95ad8e2d16d87a06dc2c3296600c1b909420153fab7da027845cb96d007cd581c34fdcf821ebc8e04d6b8088bf8ad48f0b44eea69b97c5e50a0db835ffb52bb2291b44bbb0138e86e4b908ad0e143bb4bac1d3529ea698163ed3750b07eada17a9b368f1f2eede12e3f3d559666a1e5cefe6c6df8e983303ab9c2e5d8a451bf2c7d25aeabb6c2e890e0fefd358102f48b6e18dc93eda5a0515dd66068e0a34b24af2b88ae1d60634a06c1fd1d733d80a87d87ffa5c77b0301a7141008f11964b670f1c4991468d55a9e18f9dc085c316e92df05d54a503d844ecb7063625c51947be2ba807b596925d4e7e526e96fe92ff79e6121169851e24ac7afc7fa7cad2bc718746f3376beb3dc1c913db11f6faa50586adaecd7ba74c6bcb1e171073c0b56f221edf8d48a3f6a4a503e9e242bfea72e8edcc62226ae547ed3ece07588c77f7f014fc7dff289f8c65391f78e9d662bf8b08989dfa22c3ce08eef9ff8257d0f37545588327fae8f5737916955e426083fd026b7212069e9a3ce7fd643f10b94d74d3f5f71647d5cced8bfec56409f50e20d3046595df6cb151f118b37b1dbf3862b80ad5894e3bc766ecd095b9d82f896f2f49df9ccb5f9bcd73238d91dbb2d6c48223e8ed1e6c8f6c1bffed5b0dafa64ef510c915bfb69eddf7d4027618f9ceeeeb471a8bde3702942c60ece8cfe09db21240348dc55b85bdad4c34a8a5b644db03b2c39f426b174a9b23e75fe33921a088d2fbd3886be9460930855abd190c961b6f644476577212a05b0b0f375f9c4f54a76238ad4453e58157aee52e38ce71afd302afed3a6e778aa534cafc9996e11df008a94a1bf1fe971ebe84532b5d8986a84e4b8b005f342cf8a185ae6a32cb5750ddb1661dfbd5d21b434330f226775b0da4aad52a2d3c42c6cfbcc47cb6c83da8e1acdc9474a41169710802d65cb363598179afa91ec4994fa3ca02843bbb9d5a985c14a0288bd2696ae8c64fb6a9f58ca264cb3118f2f7a41d15436e765b25e72046daad9b8b58ba33977ee906064088ac2a4fb33df000f1a6b0cc3300e72142a881591248a8ea222ef6bf192153668c05c39d86f2a2d20910aab15e402f9408745117c859db1088c286107736c338200b6602a955f1214c984ab66f90081b7a5b37eea5631f69cabaca0e3d93994bd585d6ad6453c8aeadfe92101a0ede5d5a21766386af4f35b85f3f8ed30f7fe3f6e5bfbb4bbb0e37250437eaa28f7612740c6bc3629efdb82630213c7c8ef4c3f04adfa62179d76523346cb585ff91d3c5f87045d0be25ebc03a26a464ef96659b6dc99c8284e0c2f74efb5b577c6937f2fefd1da005374cb9e48a51afc03d5639e8f9c277aaa73d984ea5a17de5178101f26ade73dc20b4586f8c7b12ed0ad75d8f573254e13568ef258ae7d836cd5cf364b096a8de05a5f0bfec6d1e03f7e6bccf9afa637a5471693111837a41920d68fe98307f8c78a413c45326d2f8f524c96cdfd3c0a35d5a3f32b88c9b05a35f7c137fbd55b48d9c11dc9e98aec1f962c58d87f364cdf8ce9b087da81a23b8ef955229e5b4b199f93ecc10b6211993a75ca39b40ff03514174701848ae638b1e3c0fda383c6ad5356 + +# LMOTS_SHA256_N32_W4 +Seed = 020b020b020b020b020b020b020b020b020b020b020b020b020b020b020b020b +I = 010a010a010a010a010a010a010a010a +q = 12 +Msg = 0a0b0c0d +PublicKey = a2cc303e27f17c386f939d90bb93a7799dce6835c9f3197ae88746695ab288d8 +Sig = 00000003a1e979f494da74822e07dc69af3b59b92357cfa1fa73610ef6b4b884702df2bc0780a14ab30f00610af3da7108bb95feb948232f771bde42d0aadee26bc8776a0e73930dba2709a6d4b30bb469a2a64d552612b61d4be7222d916be3e9617ac9b8edfca981e5703f57c2cdbd4ff94ff8d5ab5f6a0fa8f6a97268200213b1ae84c8d9b71bea8afbc91e6280305b4990d1ebd402f2daa75ae74bf6aaeb3fb95e99f0e2292733e187cca982a8a8773acde74c47f3bb9e708085200deff44d354c28f83e193ad32238dad1c65c0ebe5d5f06d16e5650177abf13a39307ca047ef0134f08f9e560449ae6a05366495e72b76f26bc9af820eeb3501c32660f5426092168aad023334bef404216b5999ba91027a2689926ae307375bf5e26e237539d6ae4f5eaa261e4bcda51a671a20da1d34be5322602276da93a42b35bc324365b5468efd1d2c4e4d7ceaadd25fb8dc395a3672222cd9ba6df131d099beab514ed50980c03c1bb212bee9e8bb579f9f12268bcacaef0a78c614c1c72b1c38d058dcc6c25901f381305d1ae2ca0947ffd3e51d7bfa8b6ba29e47b327143d1f3fe0ca559898e208cdeac9dd5c917b6dc9df4d2955fe3a0b8db61fb0d1587d1e8a28a15e480a81b13784c120ef72769efdac51680982ee325e1f21a3787f0f1af5e955b201016701bc55a13673bbc6775be5c31db7221646c4c7bfe8b3413656026d7f5ebcbb5f85cdf59bf77eea0a170025e33e8972b6bf0f9993ddcf10c6f6e720e3d80b6fbfc44eb5e392cc0320b115f57d5d7fcac95aad13adb4ea2bf3cd207053649fdf73d6b1f15004afba6f5bcbd6012f23ca531e1a01dc04fbe26e6304d547b0284b67433fa35637662b06c6ecdd571ce7e2c09c7b85d478f4720b9de571e4814facccb6ec567a0c181755019c53a2071d5aea889818962569cd8a60d0b926f7f86395817dc6835b7ccb4f3c7dd49a270a0e069dfa67f4114caadb8c37f51231e9f6bfdc0893d8172ffabe996e23e9f9866d0c5c750c3bc7bde5889e92d1613803926b56a48f8ae7e37e17e5b12c35433eed780d291477ebd1f5e7da3067d3e1256eda44c7a2eb1d7fa84c9af4a4a61ecd4d08dbf4e2e851bcd2440bd7dfc96144dc0390b715a0d43618bf907a8530d061f1e14c1f7dc10de42130e9e6f32561392130e2d597407d0be448e8e3bc743140fe2bca988b0275e15b354e17f21fa14fe12cc96b02ebd970d7a12f1d53b83aaf4685b901101fea81466307c0fd27814446f3acd8970ee72fce827c2e76287c61c9538974256cf7018be8ae7954ca294a950f26d6492be643aa4aa3e3efd028ff370ade419d4ac2ddd5152bf3c0a661e5b639534ef36fbe3ebb6c7e52b0aa286749573bd9d6559dcbf2ca1b91ae0d0816956de9fc98818b22b31159f08255c9efdc8d80f68571d7dbf45b471f668f603735a6a975f6b5d1baef4d679148c42a26930437c612d8c6a9283b2c343b3e2b01c7cdab2e467787c2164e1240f3f08050de0f155ce83b6d7c59fcd1224e42c573f39c2890a107a5edbc7f8cd2113c6b5ff0f9e1f015ab604b6f7fa89cd4a9f31a6d0ce271093f728a66c87271be96785fc66af52d14a4292e0ee3dd1c3af2fde36d1034f8ee805cd6af8e5a1f59a7351d662cde49f7662998470f50a25dfa0b0d0446d0ece9aaf57a7b48637a8ee230f2c63436967d98fce3884d22da5b1119404be32804c332800013eefe0b0e259983d0fdd9a58bd1addd5209a21d83299e53d418f1d8accbf58ab05620ca3c4cd27763dc60ac317442f99a0080a01a926565869e886ca9eaa425461aff4ff9543c52c9807067d7bee8a62186af7bdd072589df373f3444aa477072a3aa874a8a07ff3916fad5a213b50e99db4d45e2f39384b19ecbac7185605505fea2f19410315281052087d2c9d823e6da168877ca52762cc78e43d89861959f7ba35d2b36f60c6d9e59ffe83d84303599f74ede2af160d7bc846097d219751a62284ee6e7be34ebbf162758e58fd93c5c673acc8b9b5325758158d10dd628907baebc00f7c39bce65a014c86638674cd5ed2a4c3f1eeb83ba13bf4499f74a1d0db1869a109e971e56292001ef82afb34e2400a6f03f690a9cd3e8c7a185fa516313d5183d605e27b7a3e85c407cfbb2ee80c07a3e85f8d41d7eb19628f61a488a9991c748f51c904b339703e081ae6bec0e92bf2cc81ae94a82cd24fe96ed55f5f11d6332d026c5a3d5d90c2b7c80b865b557377fe788a02bafc9c03831ea740fb8bdf3cf812960a6d0695b0ae0e8e4ac2adc95b07cfecb16ec1e9884b085315220ba1a05e664d49ec2322f78b5ea5f52888388fc84a0193f45e97a52c68508cfcc1e27dc2abad7d71babb93e679ba6aa902110a28c64656561333c43021f1193008aa40accb10231f116f348b1ab83a6e666cd71d0363330024014e0e9f91b6b6b67280b92203c1d5a78dcb81c2ec843f21778b398271ebf4e6f661ba99fdbf0ac1e0b737016d6ce2234bb6744d22e87f924dbf3389d8da0890fdd70be0485b06f0a99b3a79b9b6a991a64f11eba98b990196d370ef85af5d5995c398c1bf7232c9333ff1a430f2968ca3ac97b762c453c1ba1df48ba4407e8356ada055ec481b478ebe5722628b4f24495dc1c8fead9eb59ddc4e1b43bb4c7bce2f3490365eac79488fa1481ee34e2e65d0bf5022ead9d60faf5dd2acffff62f6dde1548442065e63a36bd5f5aa7f44abae0f2859b44d3e3d68bba236b206919b82fbd207318c84d5edf56a528a87c0a06c9797a6074dd4ed75c029fb8eafd7f0bd88875ebfb4cd6b7d6efb12f4a8de167676a353a8b9f414e8497a81a232113111de6db42c55d4db4726285ae58458952aa55f30c4a287f6f6b39d18cf1b0e2fd424938b4a98fcdf9fdd38f423e7fef690ba991b2f7c68a9bf275ec9ed9e8999184da2ca42516c0dcf85ac6b8d41a2088d149f33e65bbeb54013dbad416515bfccedc9bb7347bbf7b2021c2e20f9851b1aeb1f92e2a702b81bc7dfed3fc198b00dd7154775afe888fa5c2fd62eaa98e82cba472adf4bb41cd9fb + +# LMOTS_SHA256_N32_W8 +Seed = 020b020b020b020b020b020b020b020b020b020b020b020b020b020b020b020b +I = 010a010a010a010a010a010a010a010a +q = 12 +Msg = 0a0b0c0d +PublicKey = 51f5845f2b0d9f2614d1e4af250e397d889e5634803d71b3e4f956ae06e63b2b +Sig = 00000004a1e979f494da74822e07dc69af3b59b92357cfa1fa73610ef6b4b884702df2bc66fed5127ae182bb4cd38c6d63f9199faac95dea5ad7966d0fddc1f66cc0a6922d5eaedb85d7261b84ad8535d488575f341d9cff13d1cc0be11cec180edc4ab90d4d9153478977ace781a965dcfd5c1da92b74f8a675676e6f1c8ddc3743583cf7947c83d5fad89fe318a9319c9ee1596c29ba2a96ae9b2de771aca1837eaac01cacb57ac77e08e955c48a5d2dd0be2888ed91bebb12668ad5690403b323c95c4931f678f1bc07d78c8771f6b6ba37fcfc358827e47a137d1799dc19d146f0858fddb91b30a0bc38b74db0d2f64ee128e31f85aa216fd823b2935cfe571599a779f9c4a8757f71d6fba05c72252a9c00e90c1461020e4d59a26768b61b4e0284f94356570121e2ba6293e8df50c7d870d1d0e7ce465609d2b35fbff8ac3c20065eb7a1b54cd7811bf65fcf1c56f61ea0ad07cf580de8871d77abd6c4aab970d82f9ea99f822f73e2787ce2a8789bef958fe040709217e6832035c22ea737f89112ecdac1b56900f9b22e6d607fe98c38e8956d7b6b190d49be8d07947e8515a9a50baa918a49cbb32bccc549d6bcaca2958d4f1205251b0f30e3e6334cd4c502f466454b76574e93483fe88437f5290aa5b2be2662a1a4420179ce2978d14aa67dd58bef3060ae1974fca159ca6a3500a3ff03891a960e81e5ce7a0a6008487cab3d71bc1e492ffe3e8a850ef44c3af291143aaba91acf5b10177f77ec9cfdc535bd2ff3fe50b90dfe35d996a27b75fda9fb8a71e5827a7999bd31555c7df789ca5c21dc65c47933d0307e7b8b483289743ae275c9a49011799c783228b91c57e4e64d8ff21a24389e685860ee425be0ec0f807ebfeb4fa38bad70eb7beba556c26c3821dc8c50469536a90de739750a0878430241334755891dc5809e09b69d4403198951207252fdbda34489575cdcb0c8fd39fa886d579f7263eec4d644dbd4044b3c8168eda09cad979b93570817bbd75fa0cdb4ad52e86dd027d4909f45101a41ac26ec19da96af49988f6067c4642a994f43d29a102b8488226f724e6dd1cb6f06ffb87fdf968ab5422daef1c7a9ce3595e3e100609335df25ac227c460084da2c3554afec2b380ae7539cc467526072a9f5971fe8dbe0389a841a752d427bee73ada670faaf7800bb263d86d4ab39ad177cdb35d3cce1fc0b18f18f62976f89fe0cc2c1b26aece6ba87838bd4c44fce65546ef63aad70be36064f78c30f1e7c76a9986de99cc490522d8ffbb0862cb36e00eb200da7d6f0ea70a8ffea3a50264d771ba6158bae10e66080750fd9671c94ac07cbe9e312ddd6bf0744bb585d91688bc69ca8a549f34bb036d80eac351498661b0ab1d613c359ef855451fc0a449b29b6ddd0b766875c52dbedf0d72f3fc91916866f3d2a6046c8e530fa126274545fc1c4a3c53d0444c2543733b352ba5111b96269e7cd336637d0dcc259a2a6ffddff1f9b8251f6598ce7f54b9a9c744747a998866b9c6f7e117d9c467ec12dd08df1a0841e902c7e5937b3c931ee8755360e2d7cb01fab184a0ebd08 diff --git a/src/tests/data/pubkey/lms.vec b/src/tests/data/pubkey/lms.vec new file mode 100644 index 00000000000..484f7501406 --- /dev/null +++ b/src/tests/data/pubkey/lms.vec @@ -0,0 +1,31 @@ +### Test Case 2 of RFC 8554 Appendix F. +Seed = a1c4696e2608035a886100d05cd99945eb3370731884a8235e2fb3d4d71f2547 +Msg = 54686520656e756d65726174696f6e20696e2074686520436f6e737469747574696f6e2c206f66206365727461696e207269676874732c207368616c6c206e6f7420626520636f6e73747275656420746f2064656e79206f7220646973706172616765206f74686572732072657461696e6564206279207468652070656f706c652e0a +PublicKey = 0000000500000004215f83b7ccb9acbcd08db97b0d04dc2ba1cd035833e0e90059603f26e07ad2aad152338e7a5e5984bcd5f7bb4eba40b7 +Sig = 00000004000000040eb1ed54a2460d512388cad533138d240534e97b1e82d33bd927d201dfc24ebb11b3649023696f85150b189e50c00e98850ac343a77b3638319c347d7310269d3b7714fa406b8c35b021d54d4fdada7b9ce5d4ba5b06719e72aaf58c5aae7aca057aa0e2e74e7dcfd17a0823429db62965b7d563c57b4cec942cc865e29c1dad83cac8b4d61aacc457f336e6a10b66323f5887bf3523dfcadee158503bfaa89dc6bf59daa82afd2b5ebb2a9ca6572a6067cee7c327e9039b3b6ea6a1edc7fdc3df927aade10c1c9f2d5ff446450d2a3998d0f9f6202b5e07c3f97d2458c69d3c8190643978d7a7f4d64e97e3f1c4a08a7c5bc03fd55682c017e2907eab07e5bb2f190143475a6043d5e6d5263471f4eecf6e2575fbc6ff37edfa249d6cda1a09f797fd5a3cd53a066700f45863f04b6c8a58cfd341241e002d0d2c0217472bf18b636ae547c1771368d9f317835c9b0ef430b3df4034f6af00d0da44f4af7800bc7a5cf8a5abdb12dc718b559b74cab9090e33cc58a955300981c420c4da8ffd67df540890a062fe40dba8b2c1c548ced22473219c534911d48ccaabfb71bc71862f4a24ebd376d288fd4e6fb06ed8705787c5fedc813cd2697e5b1aac1ced45767b14ce88409eaebb601a93559aae893e143d1c395bc326da821d79a9ed41dcfbe549147f71c092f4f3ac522b5cc57290706650487bae9bb5671ecc9ccc2ce51ead87ac01985268521222fb9057df7ed41810b5ef0d4f7cc67368c90f573b1ac2ce956c365ed38e893ce7b2fae15d3685a3df2fa3d4cc098fa57dd60d2c9754a8ade980ad0f93f6787075c3f680a2ba1936a8c61d1af52ab7e21f416be09d2a8d64c3d3d8582968c2839902229f85aee297e717c094c8df4a23bb5db658dd377bf0f4ff3ffd8fba5e383a48574802ed545bbe7a6b4753533353d73706067640135a7ce517279cd683039747d218647c86e097b0daa2872d54b8f3e5085987629547b830d8118161b65079fe7bc59a99e9c3c7380e3e70b7138fe5d9be2551502b698d09ae193972f27d40f38dea264a0126e637d74ae4c92a6249fa103436d3eb0d4029ac712bfc7a5eacbdd7518d6d4fe903a5ae65527cd65bb0d4e9925ca24fd7214dc617c150544e423f450c99ce51ac8005d33acd74f1bed3b17b7266a4a3bb86da7eba80b101e15cb79de9a207852cf91249ef480619ff2af8cabca83125d1faa94cbb0a03a906f683b3f47a97c871fd513e510a7a25f283b196075778496152a91c2bf9da76ebe089f4654877f2d586ae7149c406e663eadeb2b5c7e82429b9e8cb4834c83464f079995332e4b3c8f5a72bb4b8c6f74b0d45dc6c1f79952c0b7420df525e37c15377b5f0984319c3993921e5ccd97e097592064530d33de3afad5733cbe7703c5296263f77342efbf5a04755b0b3c997c4328463e84caa2de3ffdcd297baaaacd7ae646e44b5c0f16044df38fabd296a47b3a838a913982fb2e370c078edb042c84db34ce36b46ccb76460a690cc86c302457dd1cde197ec8075e82b393d542075134e2a17ee70a5e187075d03ae3c853cff60729ba4000000054de1f6965bdabc676c5a4dc7c35f97f82cb0e31c68d04f1dad96314ff09e6b3de96aeee300d1f68bf1bca9fc58e4032336cd819aaf578744e50d1357a0e4286704d341aa0a337b19fe4bc43c2e79964d4f351089f2e0e41c7c43ae0d49e7f404b0f75be80ea3af098c9752420a8ac0ea2bbb1f4eeba05238aef0d8ce63f0c6e5e4041d95398a6f7f3e0ee97cc1591849d4ed236338b147abde9f51ef9fd4e1c1 + + +### Test Cases created using the implementation: https://github.com/cisco/hash-sigs +# LMS_SHA256_N32_H5 with LMOTS_SHA256_N32_W1 +Seed = 67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a +Msg = deadbeef +PublicKey = 000000050000000166320db73158a35a255d051758e95ed4a5c8d702b44ff9b23bcae0963e9f757cb974bfaf29bbf07fa68191b53a85c264 +Sig = 00000005000000018ebea1c5c841a4fb1357d69c5f556782a1d729a2f6edbf41febc33536290b5d91ab3cb03e21bd627cb4277691af859ced64086e6df30db7c3fbc5b888c1fc981da39b8e0acabbba3876336d959e062f0352e2ff0317d739ae9573456f7bcdee2a70d0aa92327069f598ec33aeef4774893d9ac86f5123ee16f77fc52c9abf50e871479a146240f38cb73180d258d5c06127ecca4b8f7cc73217efa75d4810b2aeee58bde402cfb060b7183c59beea021bbf6b435c067e508cfcd82e6a5f60647dcf4c58ee6ebd0a5bc2b3d120299b0a39ddc1fdad9b347ecead7369abb2939d73092b0af6d6e5500ae2814ca5033b3ef138f961887ecbba80883490eb4f810deccca91fd918741a1eaf5a0846bab60752edb96c9c089c60aa21deef38fe0696dd34cadb72b26654cd72ac5a8edebbb2b7144d363508c5cfbb99eeac097eb149fa926d2519233551ab784808c3914c5dbff0ca4826ee0471f075a58c2a07cfb3c71745a6344be986c5dfde363fc51142d7dd56da76cbbdc65379b6c5ae5618ee4d08d030550273ee5a85067afda1ef00b37cfb2df0c0b9a34b4955c7eed4cf34cd20d8d903d36084b0534ae2da329762cdbe96eae910d54de5679a0dae79dde457344e67f600acdf33ebf6bf2c5ffbd401b6ea874394762a404a8399a340c2e94a87bb82c53d062cc3d7925d115c6c73af3f4ab934be8041be03faad1ace913962e2b87c5333e48fcdff8ca47ff491a3acfc289762b46b7ca71e854029aff0d9d205ea055d695626a404a1457305b1229a53b9b7ba5ee77fad30be1e7cb8b0f291ca26aaea2c1c8d925f00c78a41a718b270f82f7dfdbd7f5a73a661ba2fe66f1fe7299080d0d8b4a8ca350a887e4be898e18118bffd6cdfbb44398ac8f58a242a3d5d413c37a4a65aa79f4de37fd5c98196b9f75588ef011708c9e8a4eac36ea81c26245c58f67a24af91c5745faecbada4094b5ebaaf7852cec0f17e7d713c00dc174f0606301abd3a2ccb074aaa4af9717cbbcffb57828810f76307dfe06545711747e3b581357c7b7d38df00166a58a38d93e7e794f8c030d98feab77ddfa2df9d1e5c0150d5b45ebdec47d8408817dc569d9178e814158736b354be28987bef2e56903b587634e9a727174201df54be4e2c9fda59740871f8f5fc1cd9520472bba31ef15d92cd48e22d2c503bb93eb663bb4a97c5a03da5656d2b1b4bca953d77c6485ea7bdfd0eb00b964cdaba74ced013478768e0eded28932c37f3a117dab19e310189f1547e8c2adaabe9a7149ea231447dcfa04181884a9115bfd1a0cce2def289b5c3dbe985c672461305b13d0329812a40b248fff79362a2266f97342ca775a4442bf5fbfd47acf975e403cfa1d1082f9934a5fea3a22379ab6298d8f203e98d497a2eeb4d05c9a1f7ea4bcf4dd07ce3230492b556fa0615709ac6c9effd693b05542e17cbd5ba904e465d3d369a115deb83fba99b63c2d261e2a4030da81a336c2996160d6851e2b9502949379be16dcdfae937fa9711f5f6b3f3d455d336f41d50312102e9ad77242b56154755c0d78c0cd916defbbc224a4639502b151935f54d3d72357bdff1e7928d837b3c1dcd9b43190944d72da7e3971dcde1367bb0f4693bfa9bdd9190a9f60f3b009db299a4cc99fb63a4e6c5725952211d91c3ef769a74d45b48c4a7895db1177bcd8b1d4438e406c83bc744d27d6f4e124d9d93a181caf2ad6a56923fb0e01f91d51555271c77bc9b70ec55dc5f233a5b2584b89ffab7a275427dda336f16a0750af4ca09cf0bb0e50b2e4c7f77fde0c2e01dfa0818229507d044796e05005e66ebdc2d50e0d1fd809a1e4c86400bc02fe504cf536f3a2d4acb09ea02c2ce3b01cb0a46b9bf33041bc0fb8d2f9bc07964d3f646f48734eeb3a78de622f4ce10d64b2802ca46dedac9ab253e31d8d417966bb344d8f3fbf96329e0d251e8f8330a6a388ceb38ce45de83b6bfd0011f3f894095e16e9cb206b7ebb2ddef07f2894b451b4733552aa5d22aa3472f0639897850cfe81c6d5eed49225ac3ebf3afdf4ddaf92d499310de1705f6ef9b0107bcce2644e4b3a256af8854a8e2f74d5d55604a2342db0fdfb64aa53ffb2341ddf9157c349a9fe3f6b9b0eb9df84d361c2d3ceb2bab47e6cb249135d1f238ab624d61cd0510874076e9afca5f648fb00d8f6faca1c304ab06ee52baaaac85ef9b2860db4773c538ab74dd85b49101b451dd0691f18501edac834acc07fa17c242dc5472d272d156ca18925c37de3bbd5a62fb261082c37f0345597aad08bdfa4e36069492e738f4e8519bc816acd10dbc8ad27d0146ad1025c3dcd707e28559f400322be9042ddcaf2d3c438f933039d59285ca9e60638b0d69f9765d6b24b015395cea5256a39a65295b8e2bbc85f21216c70929e49f16d988b590c8b85ea93d499f5e99cb6959e29df5f37425cac224c450ec5a8e57969fa3c9a6a75aa767548f3d7318433a93be045ad5a837d5be8a2a02e5409575643b0d4380c32e9c36a52bd208239a48cc2c97ee808f5607d68e9b496b3be0f2a8ce7f7db9ed7ee2615123060f13eec82f45063f6f854e9ed49e8f98d642d2735f68f541bf9f338e633549605f71d0b8ef3416df6d2598b18626dfda9af24b113c4475a993021d8dcb06e272bfe8298a50169f6532b6d5330a2022d57e17bca25f0e95c9a25fea8544c7b09a1d83c65a78a1e0c9ac6c3e70a6a387ed5612a58c828674fbaae4cc37ef92e6f031428761e0faaa9789e44aa4a4b852a56f347cd5bfea40762b4c78595a5f4e04eed428d2da9046a3e073070c72c2a78bf389aa22d59746c4cd0662df13f616f0e94de5c35e91ef701f25d6ce991de13b3cea96e2d4cf8cdd1c5c93d28f380f8aac94e8b736c2e4ba47a822e24d57e4b5590a1f2948ec734336af7bf1c3d26d59970e5e556431208f393ea5458bb109b8d974f7fd67ac2bf9fc50ac482a7cc123fd04480d4037c5497275a7b83e74c3781525ceb71a78813d62cc4310a1a5f2dacd1558663098e84779d6dddb284ad4ffba820d033b63e0d0e208761e798c2de34d8c4891d145a346d339c4444f9c97bd0caee09eb3edb2f6f1da6bc09f7ae946e84b5c0b82d8e080b79aa12924218b62d3d73f807623b36a799e381600a39f97e34eef9c20e7b77b74c6869e6f00d20fd997479eed33bb87e97684239b442c1280c3e15db8eeed9eea96dd82feb8d14e031f32d4f26cd67c388721084cdea0f7fc2471b9bb2230af3981eeee11907d52be2455c1760ce22cc58e744fe0acbb6cd3ae33195db0908aa015a0e320ee7c2a39793b6e1a6eaf8ba560d0ad5e490650ba0f29ccf0eafecedaf1fd81797b4303da52da914437e05a3f09996dd9636453e423545e4cee9ac593b0d95f3b2d9993db080ffbc8afd1ebcc6f25b3a457f523a73af389701a152840c8e31975bb9372717456c4a36bd1b23f553c16bd9f889a6eba3485fd228d633c8ac68e30316546d5db2f1668c33399227edf7f476fc13267d2b35ba3e2b7a4c0be67f3252c0845fcce0e020890c2d65e9c44bf56c2655a56057f91d68ed059018909a2fe1567565fbccaef57eb7230be63d5d6810e785e3f575d253839c6b397277abf01ba38f1933b7cb5e5a57326fbc95bff22875db3ecf04699d5051e7a21ce4421ae095c339d5a27cbc4b7de9a0d66fd5629ec6d1a283f4539c50189c9b26ff0b2b69c377cb54b851fdf4b5a8152c3752c5c24feead50882d6472623bf70a19f0f9e07b6ff270a27f3a042de67f535b81aa50d25934639582eeac7e9722cf718b7371290ecc555ac0236738270364baec52b0fb803c7198039a2eb737396edeb4adf343adb2d56a6165b320db869d170a12237baccee7eb8c1946b8eb2f90103f5134a934e5eb1d074079b703197e3e9e67ae45e5de496d92f9f2ecb7447bca05cd28312c43997af64f67ad3947b3255f34a41e3f69022fc9d48b85a123becd968f79c9dbe7794b59cfdbbe6c6be15b6769bc3daaa926fe085c4614ef7085104ef1bf8c499cbdac641ae32a99e0e402c0bc558d774ba9d4decfe7c7011b5caefd60b60a1cad1230d6905d1950ca497e5e93d7f0e0cd27942be097e439cf05d660fe3c12269f80895d10aaed8acba2c17e28c4f80f8b9afa8104a9836da95dff068afa70554ffa2b095e92fd111aa6f4605b6463590ee62080350b442aa4a29bd805ca692eeb985e772376c8ceef21614e184e9a8efb77dc101f99b1987acbcaf88f622d0d77175a9e729c34e51714797285e50c7fc3f0169021a64b8f156168d2b5d17146d779736eaaa758a30f19b2f0f73fe67432beb8e8be23d4e1368850b1a9e319d7426a3c17124e38648fb0cc31817c5d971b213e7fb7d5f5a7409c297c7c07e955afd80343fd20c3ceda0c06594a8c61631036e81eba730c0bae4e914eb59b6e6ba7399bb0ba81ac82fb714bd71d2cdd7c74793a9f7256f0ca5eb73169cbf51d124cdeccba9256924d9355eabfe4c637dae4cb2128c32f4055099d71b35cd4e8fa9b2a658ff9b8d658fba43eea07c70194c511acd955d5fefcd383d36f00caa8bf4901a986522dd678290f321164155280dcfc9646e2f0f4a8b89388e944ab07179db4fdb415e613c11de97f216675aad8e841a92fd9f1fa797db74cc5f4b988d8cc400ca6ba08b703df2bd4d481070b53deed2ba72e752a2220812f4483b990a4e7b1c9d66a0d7de456eb76b63fc3e30ba4f98682da340bf3c1eb5ca67060e281ba8456ca91298fc3f1ea536d9efbcf7371ade14a87486cd1fc5efff550f7b32bf97add9fe8f14a209b29b75931406a0f1793ad270a12be5a648abef8451143fa4313f2dcaa9cd7b28b12d82b681256f4d50e47322842a80a14928a6985e24277bdfa8c28b985902290ca30f38dfef10d17c4aa8eb57e638f8610ebf33a2fa2ff6a84d806b8ee5d1039a3d557ba615c771086d2fad33ab983fa28e232a205111c5a9db5dc7ffb777b38f9a3c0eaf14b259497a7604ea646fb0972675b3004aeb7ce00faa4359f4a01c49915dea9b069ad29984b73bdacf0956431156017afa653cc1b288ccd11fa2ac936854dce158574261998fcabcafbc9b1af0c2a125268689b87ff62fb05368ef3b2e699d3c099315eec2e650ebf97983792204a747d1e505577dd8ac38903fb05761dda5386ba96da0e4f70f62f054c5d4a600fa898b5336c4876072d16122c69bd9d24a506c5c92637f28ee0520ef9b510729ba68770d113d6d827c21d69b7f7d408ed814c173d543d27a6f57176f6f9e227644c702845fa85dec028494dcc4e616fd73898466428f9f4b62ac2204ede6e4038b777549073b49efdb6f08cd70dd0b05fa352184265d99a97e8ecaf7500113b9e9f24b24784290caa577edc5dd0105afc37693989951ce8f34b9e46f7081fbf7bbabf489df5446b3e02e3d52c45f10c76d1f9ed2ca37014baa5d6131001633ea6f87b68577557328a2fb1e7411e89d9164a83cef7d2c7b0d2663a23c2cc2f48953787db417999a7144e62c025cde9866823fe71dc968f3cdf0e2288a9af77632ae722887c267f550be5c3d8b7d7c9e23eadf05a9b1fa01791a874559604283906cf57a36e6d032bb132af8037e90492204106a512d7e0738d5f1a62b935664ae40724c83d5b05e498421aa52f3e582f78c008c722ea8d1e948fdcb55ebf8507732c0f47de255dff0231172dfb9faa7e5a962024fe1ad8a2e8ba2fd608d27ce8556cf02c3f0b393cd4b7701ea63a9dea4c4bb1acac0c959ff1b7b95b97c0a074c663aa424b3f951f403586649f59c73567d43ab9e50533cfcfa3f4632bb347253c0f911c3827298b075f09efc6d5f33cb0037dd68c8090e582065799bd2df09147891039e687daf478213a60546988c39ea7241623522a7051b24fb06291255cbb90b8209d97c61863db36dc73c94d0998ec09a15b5cd8586d22b10dfe0ec24a6872a4d371d900d9afe27e015f2e65476d83661c14a040d487c93dda549e5796421d3e005393dfefa184c5385d7b7eddb274870526b7e8a634cee339939bfb0e843add1f57d049dba5ba44a15583433bd6143762860097e11f53022fea764236b298b563f02b5967c7e2e40511faa50901453bc70486db2b2d02ae34893cd364463a8071bd74d36c9f1496ed696ebcbbd7faa2c10bfdbf8c99ef4bed273178fb6b0a946fe2e2b3fffb2a73597f2cdebe99561b1614b444317599b2ecdad78a567e3d7c514ee50cfa70773e2fb35b6e5544a919f3cc6370cc002c2df4f24590eca83214f79797fc5376dc5708dab8307898fc5d1034de0cd0bc05b37f7c0c3e20e370f781979e8fde9c4b68fb0d5c517d69e7f2e551ae27583922ff1008820a8f7018c8b36f885d7791fe5e98e1a6c16220864b660601b5c548ec2f1c198718264a3ff58d5b6c3dd7888cbefeecd3275a5547f01ed70b41b038b6200c6fa2b65db70ec1dbc801222c9fca77fe5a9f2747d6eb79675d56942841236347ac5bbe09a48cc439f0b088bedbb04916c60010d14150669b3afbf9ac77a8f51b3da083e5656ff08b50bc0f59a4ecd30f5a7729b7118972b09f3706386c1588ca64636797f30acdbef84dd9b3ce3dc31d78236c0268c875f23b7d544c8897689dde236152a987166f3f995bf5149abe840880dd7fd962b8e7c8ea5a09dffad5dcc52895311ec13920b028fa6e2eff2597dd85f156511bf265604fb50220ef0234d3ad0b1132f8519297331634ca95892ee05c537fd4e3abe8528a6a4ea6f15de1cd01c5c87e245cc609807bd702473b70b4e3e386491f8b47177827dda7b089e8dbaefe7fd6c332ca1d3c138fb1e89010d769a53644e253098b7687fe9d5d24bcd4252fd4e681298d00fd76ca135fb0b43ed30f4c3c76255041927a945fc6ceacf6f3b260b8bb3ec04880c43b260777c96dd1f803133ae69656ba20e9a603b1823dfee6cdccd078b40b850103b128aef28734650a22ad3b396b69b3155dba82cc3af876a0bfd72716bf3cd096a5b22de039101fda6423bd47361ab01ce0273e503e30dad1e95bc7648a950077c136e9fb9424bfaf6a10e57e03d6136520345b48b13c1103fa3c00d66f412bf6927c3af70d4dbf277c77b5e348b53e7a4a239b94da1f4099b517de0310651b48488ff98f668b1085d722b4ca1a5cb97c97b6ce1f43b348a033cb4c892b161fbaf6d3b7c99be33b729c1cae8c9c04c6edf2ea76d5e2c5728ab6d99e491a58db42270a0c244c827d9885c5ca964f69e5e4fa982598c4575a515168560e161f0c753dc48cf0cd95ce228b5ee8be0f7361a6588cb8b63dbe946965fec93763e6ce6a9a292883bf51b8118c059fa1721dc121e715be71b3ac67dc59f1be689b3c123df2154a0fdcde80c553a430d8efda3f17cc8e0c9921df6a6cac5bf8efa475696e50b1fb3e66b0d9c183d4729ed2bce122792af436c6d197cb4b2376a5beceee1b7d40888be20f4c7361a8c77ffd5135cd03a3d6e6e77cada9c63a9715337f015e66293bcd5d0500226fc6f815980a6d54b86a771a7596e96251ea59d0a26d525994f0ef9dceeceab6980925a034132f408c06a50e67de82109595f0b6558999929ff4d2a2d4e32b7ec38381dc6a424705a14420ddf2493a9235fc966aebcd16bdd965de88e546c6c4b34b0b9d06d4727ca1e2dbac68b6e16c9020230ee37a1b806753635bc6c763bb860323ab762c3e84aa329810be1fcf5a20b1a6116ac880fb9674c5d87091d397e8e1c1c6b80436706863bafb7c62ba150c467b2b86991e746bb914b429046b03e3d317f057f34aa0dd9bc439a958b14ebae08d62da7ad5024f71c6cbf053f24c6137dcc21de4d1bfcf531203b29e97c1c6f7307bb20ba41a07ecbe002d41ce114efd54684ed3694cf1a8b7b841b0d2a92bde03fa864b39ed32ba532ed258890232109c32bb668596ed616ed6ab7cf2129205bdd6afcc6aac7b91042518e6a453bc1b824ca81aa5dfa2a3d7982dd73bee0ffbc68ffb9e578eb6d04a7cc66344ca12c0702867f7de38ba9935f4e6b64e6b72e0cc33425cf1620aca896719ac01a7dbce1fb24d2a33e414f95a9c2606adca1b48386f8bec9a7bfe5144e7fcf466959377fa0c7a335f36060360e80fc0e4981ae81f06108582ec4a83cc7ce1af323bfd80d9fcfeaa12b017e2168cdbc79810e84f5dcdc9a2cc1a5132ee7f3df31891b9edf2ccec5c714fb6eeaa7865c09a8658dcfa0f749568de8f248de3830e2afa0e6addc8975e5721a7979446410bc431178070d365ff7ce13bf6a99ebb8c4edda2353839ee574d36c0619d4491627e88aa6fc31eb41ffe53a6619377a6c3cc1aac81387a1f23de2e93856e0149de8d43fdc9c61902192842ba49b38f44db0db5751e4a942592492f67d20ad575c880c0c2be0f6f324b7d70f93b21110143f8e234e72cff89843b4f148ecc95d53a0a5baa2ef01237066ae2050daffdd645bad37d38d74b30312210b177bc1022db15056c13242dbb9d6f6fe241ebf9c73a06831b042e661721bb0238f6731b0990fafead8586506383b338c3141894012a916b547abcfa0d46fbdbfb0dc31b2b18708887c4c720f1f5ce7a2dfeb453bb52d067b643986ee653ffb07c0a47dd550526c11c3c0ae188799dd7c6ad9c4ecdfc9f05311042c27a02e445d4bdd0efb2bc2d6f9304d9abcb76aeb07f7214dd30e03c22c808e10212e047fea0dc1140ad77b9440e35a242191500053e3fea383e3abe6061270f14dca23e33c647b4553fbb1d4f2790d69717c203d41b1196994c4b933b10417714adc943519db137edf179ce4231024ae0054fd751e06f1f3721f2a960928e33a976a0bc91c52ec6332cbcb6053b5b47990919040283c39efbb953ef198f410441dba680eef0403fff3e978cad67d7abc717835354c8456a8f2f70552d3923ee7dee59f49c223ed6399db0c7b9aa62bcb656eea22217cc98f3028ebef7e42d99e0ede445fbdf563e7dc7b6991c5ca733e840f642c335e8cb7cff45eec3b3d51366c1bcd61d29fe431add0ce83c2b5cc6dd3a084c4ff401575c5b588ffcfceac9a47bd95fc9e790e5d1fc1a19b92480d45f4f733a042b2b5489a5a45c439c7ccc6e142755ab7b8b0035381b2bb8abf3e87723988d6df5282e6fc1d84003a11ca7edf2bc0b262c31022f4dacf74d157f953954470d42b5e4ffaaf0e68bfd92d61e1d99253ce13893d09e3a47f8c218650bcaba5b5f53fa54c09ea38d43ae864453a5d521bcd16e74d055eca4ed9b9730df3f8861b829f5aadc6acd61f611508f5658fd230f707e43b0940cd158fdd5e76f758c1f31b831d8636d976d7a41940e28de3aea45156136b0437c34a34de955d135d61f8cde040aebcf63525fee26c9e0c4c21859a00bbb5cc9098df0c1b2f4a9ce33f6cd1620cecc626ae115fe7b2ba2c7ab1cc9b40cc32e3a5fb89fe1ce74f49b7b436a1f781a51d5fb69bd5a4784a385b9b7bb61b08b0600d6f11de7dc4d44dd86fb1fbc7e489df3372587e6cccd47fbf3eb63390f8d2295a24afd11a2a29c3a23a13de07b1ec05315af50813ec967591efa72ac705534f95e2005dd63ad542c1bd3ca648cfe925c4df28bf48479fc3c92afacdb6597fa0dc76422f8e0a87cfa5e259f86737eebaa36ab3b277312e7b6570fa43f53d311dc196493f27187d111ce9bd983b78a275d19f5fd74ba717d0d91f100e7ec2c9d0e0e89785ec080b66b258abd0f6fab879024bf331523e287328f338ece77e542a55cb3607592729affc1c42cffb8fc1bd724bb4e680eca1b460a16e7dcbcd315434c75fda1b013f1061c8bc50f0a4391b3f9025b6d4170c13688f8b2b3558989d9c9c97d579053085847ca9fe194161c61776b0c7f68e9ce7f24abe946589bc452d097bf8d80827d9533aba2b70f12cd9bdd087e6f96556842f8be441a641e64a724ca467f9a6c02d7f573aa1fe7fd3ce07a0c58b135f6869a20673a88482e69190bf89abfd2c95ab6263701146ed458db7e29a5baff6f60470a351e9979db48e761193ac2dfb4edf4153812718d792a2a682ea7e97c8700da092122ac23d62518654b4320e207736aee1a379de7bc69e430a23060b35d3a4de5d9e25cbb800905224132e402302a338f422e4972d04f9f1dcdf806b6c4d2bdee2e633b93f624994d5a016d8e78d6b8d89f53df892a48611f68e77223b4769ec8e32d496ea348f5a163bd565ea02439ec2d0fcb8835e87065c797904a370e050ecf8d54667f838f8348c9596f6cf7686b0482ae2858e5f50a5a9d65966f9bd09110a780c40a73b50bc0b2630d93075efd0101fc4a151ba0d147e3a3886dd166df596788f67de7a4b28cf140261ee09f87476c43ce6235ef63b32e1ec936403cbe5f6caecbc134ccc35355fbefc2a8d3f8b795928fec807e10167d6b5735d032939f8d95882c67a3926ef957f433c8d50263aa725f8b121e8e9ec9a36754ecd76917d516088852a9bd4c9795dda0a4abf439a73747e70679fd1c246a99ad691879b301bd70adbeb0cd9487231548f3e1d2bba500ae029c3b7730728044ab8abbda29ba59892c7df73f1613a78d0fda9f9acc62169a7dc9dafb47b744a0e53ad9a602dd40ea99f00e893e11284c36aff9d7b5259e78c772dfe96c1db4b3f917471b9756c98b3106b667483840c92d7558b04fad0b8ea0052762d30906c49a25d1792811a3782618a3b8da9ff057367a0610c3c2f2a00ffa0fe93d63199c76073417e93fd6df605b8f5d50936644fb57a40c8090fd8d961f01ba9c7fc8dba52e6a06a4d59bd28001f291d7dceac07c03a704cc8a1434c235d60b58c5fa9d394056f1787a2dddd0f8d57d9b225d2898d4cd4be2b83f054a8a80a8620caceddf658ab17aef3bf37dc794e466483a3b03eaa8de0969d7c571151e70d9360bf811c4ee6db51fa0ab2f51975f7ada8732dc03ce4e622c6d8a0afeb553edc06299d1b6b327be8f135828c93b13b3e29719dd081c19b8d8ac6be9b7b1c90f65cfd81452b0ce2f60af02f07177395cdf09b26655eeee225f0a3ebdb56b94b98c7bf6778ca03fd6df38f2d2049f17ef161b6f74836097dab3bba34de87e6964607cfe78b562f26d49947ccb049d6f5434ab9cc97e09907f3fe0efe0ba0c4a72c11ef36eef7f3f5fb3082c0e6668f675a669227dd03fa866d98100df70f58cf54dfa6c31dd7ee1f5ff36e0a7dfa2b885174fe7b349b9882360b0b4af347f9632a91bbd3bcb2f53a3c33638f5097b125b5e97cba5167afa9ec8ec529ee5223a62f9dd772e48ef08b5ed5c722b2ebdc6f45ff759cb84de6540a2388454e692e850ff49355f5625e5300ceee09db960843a04766b78a365ab3eccc24988cf8bc637a7ab6dbb788bccdeca785a1357a464b560861570b4559e3888a588d256125a2a8d6c02a43386369f1eb1ca851f7463ed5f85bcae308cc5480d63ae2e7d0c10e5ec28c027a3a32f2fe73225e6b59c9061d45ea6de5338569bfd9b793384fc6997c5e9fd66dfbd0d789bd6dac9d007aaabbf65eba337c373b87cc55c808d8d4aba3e3242039ac6eb74a01ff3c9d4a2131dde1b87e6b64944ef17f54628651443f9bbb1cb8eea7cd77a20df364afbfb3358d8e61d91bcd60171beb9595e97919495994a4210134507e2b4d5a48d8ef0db39cff409f26dbeb6f4a841f288c604a0b6af70a2ebda1da52a1efe52a7ef34cecde0d89cd452b2f9d3461e300d37f3aa11de6bdc980d3c65488fd2d7fdc2c7cb369732875b4270192c7d5b5a1bbeaafe18b3602242bea41f6aa30f02ffbf1fa7f21e4ae8a18aeb36390b0b24c8739ff9fc0af11f12245833b2d6b4977c57abb8f6e55099d7921c8433da4e71814ccf3234e248069dade41de0def17879a9f56b694e59f50ae7e9b5131a95eab0342ef1d420750ac6d212c2b4f7ddc9fe4ac73ce860af153170da56a78eccf52956c74b2e2107df4f62b692e81b38a1486e3781108db55eee245b95be6aa68c019d7cd3b4a8296a919800000005f9b4671f0820d963f134b862d3ff4c8bc3ab8558e189a8016c0d11ce4ea6b39168bb48b598d44624ece3477aca1b4a5ecd8b2b1837d133714cea2f192cbd622dd078c1e9a6c66a3c4f5838bda2301dd6cdcf3f58d2b0a8aaaddbe2a9d9e17bf97c064a213c8f034b41891c1af0802ce7c354be0a62a8b4e04f771edfd0f0224601e91b012c2ca4c4f9d6880bd0941c4a9ed569acc1ef0bc298e8e6c5d8ec3f54 + +# # LMS_SHA256_N32_H10 with LMOTS_SHA256_N32_W2 +Seed = 67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a +Msg = caca010bb1acce55e5b100d1e55f055115 +PublicKey = 000000060000000266320db73158a35a255d051758e95ed4547c0449d8b3531f210b7e34fea7301771b6160e7cef454f4b83374d68cd1a76 +Sig = 00000005000000028ebea1c5c841a4fb1357d69c5f556782a1d729a2f6edbf41febc33536290b5d90f01226b0922c524ce26fa7dcedaaaf0fe6a51a467e486cc502048d56062b428aeaf1102e98ad5320c4308c224c93229dc13a06026a75e9d8f9761378c167fbd7d8768d366eb417f2ec725728a0666474654a23f0415901ef5a79d1fc78541d8871479a146240f38cb73180d258d5c06127ecca4b8f7cc73217efa75d4810b2a4f3d02ba74f6d6fa56a91fdb11b2c77d6bbd5a4fa7777d5bf12566aaa539d94357a1eca7d341d544a0581b87568c37729fa34877d94517f7be8fcabfdc42c186c6d69e23769343223d45b28cee4a2d30a83bc084a4be9353a57ff18b525fd41b23d546f9a3105a287e9946c5a0f8cfc606d2e5bb87ff12304da0425a3367c17c0c81ba0a68a7b5130b49ef979d7b658e1622f2c599801bb7d66fb1c08a9823eced413697baccd8e4f2bedd306c6f7295018f4857aa5ab57e4c3b9f6c7a9075a3d8e136aa3cbd41c6999a28cd5296c179ed8998fdf2de702f1bc217e7ba2544625d3849bf7d5a2884b0685fd0223ebe4d246a02214ab48e5adb27f9518d2cf4847124544fd9287d437d698f6ead56eb0b632f6952ba4978e22038a0926645b95c7344e67f600acdf33ebf6bf2c5ffbd401b6ea874394762a404a8399a340c2e94636b82d0be737ba1fbd4dfb1eb3fd9cc87159cc644dbeadeacbeaf8cc5ae9119648f4a0fcbbc968428b9778faa46c53ab9a1f2c8742297d89bb955eec859f987a780df4bd6d7f9d48800e42c67eeb11b8406d43549333b0a379d5ed77ab8c7921ca26aaea2c1c8d925f00c78a41a718b270f82f7dfdbd7f5a73a661ba2fe66f1da7c7bc35873dfdf769544f275914a1a44c41460dbfdab81282e9fb9bfa8f99ca3d5d413c37a4a65aa79f4de37fd5c98196b9f75588ef011708c9e8a4eac36ea8d9a518ec530c791f01cafb0f7d4f47d8c1a300af6f3e74b35d2fd717bcaf67b0dc174f0606301abd3a2ccb074aaa4af9717cbbcffb57828810f76307dfe0654c594b4c9c3bc9b16c73f7be4a1b59d68ae92c090b0b7be8af177d9b52ec84ace278aaf2f8b15f1718061902846c9ab801189206fc335f6065623530c891b44eb76d32c57ca2c5637c06a79381edf16d85507789d3e7e4a68f21cda3b45476164597ce4e0da6fad2ee1ac9a8a21eb8a2d67dbc63a1161a63f726e8ea893c5eb565fd515ef0f916bff1c77ab3d4e93d07e6711c68dc07fe68e0d269a21c6ca736195ab8353811e002e3c2f259f41aa0cb5e6325649b82f3870addff21296555774a8af8315013ff0ae64234f69fbff72d28424459f7974077deb68bc2e5f984ec47342ca775a4442bf5fbfd47acf975e403cfa1d1082f9934a5fea3a22379ab629e33d8143aef89c7ae5fac752743e51d4ab5b246e691d6120c3be921c10519fceb29b0ff42ba1d2c29b1ebfdeab216b77a254317214bb0ed4a621d757b7a2e546d4dffdbd7f4663e79ea7e2fa549315ddf3eea7cc2a204ad77d1eda9e8fa8ddfc3d455d336f41d50312102e9ad77242b56154755c0d78c0cd916defbbc224a4639502b151935f54d3d72357bdff1e7928d837b3c1dcd9b43190944d72da7e3971f74c547ab8c0f57f670bc50c8044b5a6360d9c120c857f28efb3e3509f493a19ab3ad542b0128ae95e2c6fd7d7433e8752a9e30b2b8c97cb4d0f5bcd7a651d6047104840dd2a8181fd934a78d1d1d3d3fba3a0b69d8dc19c1b7e7a59462d9fd233a5b2584b89ffab7a275427dda336f16a0750af4ca09cf0bb0e50b2e4c7f77fde0c2e01dfa0818229507d044796e05005e66ebdc2d50e0d1fd809a1e4c8640023760453958186395fd3bee3340fee95a196aa7eb010631f66288243df997ff7d6f19ddc404768c36f097a37f08614d5bc415287ac70b9af6aa69080e1b7546f5b837956968e919114bff157b22b3a6b434b65b29d6424197929a140455debac8fb047a1916a9e239e8885731198e01ba229e2ff50529341db8fe8725b7a7d8db4475c9dc7e791c4355bb4be4d2b32b3a55764ae4bb9ae4ac10ba34f5f46c17f7bcce2644e4b3a256af8854a8e2f74d5d55604a2342db0fdfb64aa53ffb2341df1595234ecd88bf799fa04c9280bd9e7b51582899763d76da14e8ae3a1920f92bb71e7097b99888ade37e27a4ba3650c31b278feb1b59a6e37e270be7f79b84875ec0f62a98aa0b3b1b034f904d4af13c3d69e0e209afe54b5b00e8a91363b902dc5472d272d156ca18925c37de3bbd5a62fb261082c37f0345597aad08bdfa4db77f5f7010ca65b62f50d45bcd4471ddd91406c7ec1cf38bb45a510d0d4056979ae9140ebd039b998c4ea9fb9665fcd45efcdd19994fa24b725068bffd205815395cea5256a39a65295b8e2bbc85f21216c70929e49f16d988b590c8b85ea9327114c95b3e459874016702bc00811366e403a21bcd7ce90207539a3caa07aabd785aacf5c539965d4de1ee748c18b51467c550ac40b7e348f6bb83933daceae3e6ceeab245f84d739812c419da7cc1bdb411d700858fb49e3f7aeee03b9449f0522d7c0984821049bac6ef0c2587658ef8fa667cd2069da327c963e7de851d946fbf291c4a5e70de48d2c29e3918a88eddd8d57c00edd7bb6d3d2939a75b8dd67ff7654765fadb4167c7725679cb33edafaf7fb592bed7a8f9bab9b3fb2bbe9b15158c9a56349b72dcc4d0e7bdfa6aed86f8f95d8371b5a34d5dbb91f80c3fd2e6f031428761e0faaa9789e44aa4a4b852a56f347cd5bfea40762b4c78595a54444fcb653535189372f80c43843ec5543d7fa62e919e515fd00524be04e2fcf616f0e94de5c35e91ef701f25d6ce991de13b3cea96e2d4cf8cdd1c5c93d28f37eee242b4275db9b19f25250e01eb3e5759882506ba68f54926815c146cacfe8b46bd326168a621f05a44b826223afd9df258cc014a2ef69abf8074128e8d452a4d447fc39c699b8b1c635a898d8f8d1b5bca595cb69c3b9665a0eeea94a9dc95f2dacd1558663098e84779d6dddb284ad4ffba820d033b63e0d0e208761e798c2de34d8c4891d145a346d339c4444f9c97bd0caee09eb3edb2f6f1da6bc09f78a59a53234f12dbe44598df72d3341abff79a68a8018966057ffb784775a707539f97e34eef9c20e7b77b74c6869e6f00d20fd997479eed33bb87e97684239b40f7f0bb491dfd82ad0269754a500d56b134c5a1b683f9de48f1ee74c7f89614aa45b74fd0672ec6693588a9ac860e86c85dfc3550973169fa31eeef009cb233129c635c51ef6220b7cae46dff6838aadb833b5019210df55c5c6d04b7f6054ad64a5a1fdd3157de4e39a6015155a2fbb7d087c754de92060ceb23b7d27e1a2618e39f89b43f2fb178dc8e6167cf058fee5dce01867cc4ad3a46a3c772a592caa19c70b2b880f958417a1e52373c19982286fba06827a98a36ad192d614fbd253458b7669002790e57e4db36801a6df22099f64241a003db3d6b0028487ce6a8bfc13267d2b35ba3e2b7a4c0be67f3252c0845fcce0e020890c2d65e9c44bf56c746c2e39e97b38ebe031ba2ffe501189e92f7591f54f2bda1b7f37f3da6e6add49f6584588c478c1ce8d9b431aa3bda9459d3eb96fbd6f333e4c6ccf8ddec2b87c62c5a9ad2bed9ba65964cce203fb0f5127c44fb241ceac1f159d248ff71afe382cc4ce87e876a7d23b859840bb5a70371e7b0a9d6d7f265d1266f6903f8dd54feead50882d6472623bf70a19f0f9e07b6ff270a27f3a042de67f535b81aa50f789dce1a3f0c58dc41836216111b8216483e5b43eea452e7b8cea278cf4909bdbc27f3e664cc6c5b5e749580660e0430eaa8c415781255c831de9635001ef694f623d535ab37ef009fbe403661f9426e791d2641969a8e11763c4e41566c82f57a846c57c54be4d22dd633af5a09ccbe98f1596052e46ea534cd74028f53c51036d697f41888aa235742ce2fd162d672c46b506b5e0ab15e9a29e055c9e020ec92182fabec9903e952fc610996333f334049b79ba6d43102c03f24a16de5261db14f29328effd6704ff54c23172b8d626fc61ff3877970b0a22bc6eb980cefab49107ea08f247f9a60e0953c37dcd35b1391903f53d8e5ebe9c8ebbb8119982182092cf21128345bd79a2ef648bb1875efe7da86939f259c2688c0f858bbc4a63590ee62080350b442aa4a29bd805ca692eeb985e772376c8ceef21614e184eb2158c9519eb32b037f74ab6571c3a9a74917cad24c67347e4fc0d7320900ee209e8da4d90c4dfc9a8974daed3caf3312117ba5a622aa0fe8bc23057faca3841e67432beb8e8be23d4e1368850b1a9e319d7426a3c17124e38648fb0cc31817cb9f0fe8029f66b669cfbffb2a33313947ad67c03a5cad5a9a42fdaba3882a6155f341e77672a9684d05c78f24231e20469e891fd02b964210e0eb166a0e9fcf8a2b97f74c9ef2a986206bc3477ddb37813adf809eee7ca0e0b4166429bc1ff86982abd6b5b0a768de611bf1613d33d7e674b3ab852108217f54bc6d5cfdbbb410194c511acd955d5fefcd383d36f00caa8bf4901a986522dd678290f32116415e33ca72975ad90ab93ebfd9da2b5934011ed1d5cf9712adf9adc630ce8d18a42494eefb7ccb189c4f57fd19d3856dc850f5c373aaf5bcc23e509bde4732df4020b53deed2ba72e752a2220812f4483b990a4e7b1c9d66a0d7de456eb76b63fc3cb2acd41dd7b4068ad9b5bf102103b4af9525374b96985e2d5dadbfb8ce34e6376586facdee2812c495a17362f07595897a9ddad1c513c97d634d4841b45fc9a7c0c5e89c2d538d47194177f0e293c1ad11d0c6e5048fb6539a3b6af99aba5159249752155669f25364db2c56ff16a978e7b8783d147dbaede337349fd562e1914b606165e7c0d098201e51001b5c361a03e5831c44b1b1d5bc2044d6652b617d8f87d77d1a9388664179c7c988264849866c3f18a3bfd3ec70a9601c664fac54b259497a7604ea646fb0972675b3004aeb7ce00faa4359f4a01c49915dea9b0f9fcccaa61dcf327b9ff787b9e53046b931631f4ebd6d0c15bbd6764ad2cef78b6d5301f3e13f4af1593b4455ace4694bafb8f1cddbf12a271b9f846324562e20cf7a80390d734f8da01e1855c9f0fc6380e3a2a4f1b2b0a0b9440f38c0f6404de422ae300d5f806e57643a53a4da8392638d30de690456238be74ac34c05e3ec5c92637f28ee0520ef9b510729ba68770d113d6d827c21d69b7f7d408ed814c9995bb2476d945350c393b57fc29702b93b247f9a935307af05ed2e22244b7ffea17ef59c74b6ac88a0cbdd1d9f7d9f62ef0d58b6f3025e9addcadba641795724bec568ee38f6792470573278da858a0cfd1c6dde8cab41d8d22ff309e4a05563989951ce8f34b9e46f7081fbf7bbabf489df5446b3e02e3d52c45f10c76d1f9ed2ca37014baa5d6131001633ea6f87b68577557328a2fb1e7411e89d9164a833b6e6542596983a88bfe23d2983476c8d0bd0a0c0621f4ce80a4678d848c53cd29236a06d79a31a7b2c2b0f7933d78377c74ce58e35378b78afa15b0ad400f3adf21f04f126c25e8e322f7d774bae1f720c98deac9a27a78215317901d4e3d4ea512d7e0738d5f1a62b935664ae40724c83d5b05e498421aa52f3e582f78c008c722ea8d1e948fdcb55ebf8507732c0f47de255dff0231172dfb9faa7e5a96204465b730c7178961b836213c56befd436eefe760380917e8559f3b4b79f20687acac0c959ff1b7b95b97c0a074c663aa424b3f951f403586649f59c73567d43a8fa0030ee9e3cfed1fe1a66401a26a69c061a05a74c372099810652f2ca142e83e08d5283caaeae3879d77e0486c3d2c6d63860a18fbcf007dca0098af84fd452988cb1d2539da1d8c382e66d87b66ab2b3054126c7a0ba79b2d36d7e307344f7eb8ccd3ec8eb55e45ee044da3eb41b29b7f79338f3132b8091e555058c5bb7c7e4b361dccee0071255aec112cfc71b87c615bb1b0d363d3e6265cdbf430307b00000006d88b7df8551a868b56f2362ed9cb3e4cdf4a1d74a1061c89b3b641760808f202eaa94edf0ce93a36a241c58c4910c0de6a4a8219b4c3bec025a26014717f76535962867daf69ddc8743b78dc26f3a343fc62822b383a3906d839bcdecf277a4b8bb30ec339a2f6eff79a4bbf09a34389429ee8f0e308d4124ca5df8331258e856802df7a036d9fc3266e83f592fb7b02d7819e0a2b4b5d0a248b70bd5bce48e8c34e524338a67a4968a0a6aa6e431f1961c2a123fdb3b2a64885dfe41e6e0eff82251152f32956745568ef4cc2964dfcb0acb12e5947e591fbf5a5d2df0a254351fe63acfb4819e5cd1500f53980c9d96a4966f9968f4f4252c9fac40e87fa53a2f1cda45516bc4b3f1cc6350ffcb14b03e21c0664684d76a8d54c9ad068908c254bdabefb8d976fe2291396396095188c6860308835c546cf2be142404e4190 + +# LMS_SHA256_N32_H15 with LMOTS_SHA256_N32_W4 +# Seed = 67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a +# Msg = face1e55bab1e1ab +# PublicKey = 000000070000000366320db73158a35a255d051758e95ed46f9f0e9e4f0d63a3ac194ae5e78d829bd6d67e711a5669f93b5cd70b142c9150 +# Sig = 00000005000000038ebea1c5c841a4fb1357d69c5f556782a1d729a2f6edbf41febc33536290b5d9799af1104270f4c77dcee2ee365d570cef54d850918a120bf4a53cf6a44330aae431caa87947e20687fc4ce0b8a62d8000d94ef6453b5ea22148b0ff26cb2403a70d0aa92327069f598ec33aeef4774893d9ac86f5123ee16f77fc52c9abf50e6d853cd1262549fc1ee110c2b33ec6504c20337fb7d6b72c78cbcd7d44ba272f9106d490ce9a0e9e13e533aa2b5ca48940fd8bf6f1a187d607eeb4d9d50fc54d51b73a7e7733db72d33b8a2b6e569436458d95c2662a2cb0ac1dfc64ea1840533092b0af6d6e5500ae2814ca5033b3ef138f961887ecbba80883490eb4f810deaf0976fd6caf5a981fce3b468451eab3dcb6b839d0b151c2695b3d9332998c13a9ee38937922858a01d95fdee93036e8ca662cd98377fa3c57d51f9f10422d9db63fa673d2f54763b52779f869dc24e5e680bea4dd559303f262bfc20bbbb314fa7e0ba5867dc9c7d639650f06fc395f3376c6f12fe521581231a0cf77443051313200bf7cece31db600827ea24027d26765f3041af0d4ecec208ac6cd382f1ff5e89f22f4e18de49f6b58eed5394673a801f9b5fee6caa3abaa9e9747ba7f2216565f61c040bcb1d6ef47d1ec708ab3a48df0fb268dcf3c58a2dc4a6f7c7eb4376be2511bb4feb1123047047b423314c38765cade6300bc64be2b749e473ce8b56e75674ddb67544cddb4466a156456eebb1e50a532279204d20c603633ab7db22a96d1a9b9a27505aa5da5d5118475ca654812542881eafa7594350f3843b4e5b7633f3f2c876941a635f2c073b9bb6da369cb10f711f8ee956c32987db899fe7299080d0d8b4a8ca350a887e4be898e18118bffd6cdfbb44398ac8f58a242411351ed46c7d132c4356e687a5035d9a21c84f020b0421f110caf39213bf5a96f0c67e23647458b8411d5a2de9257633dbe77278f70e8615355be9745fbc53a8750743614071dbb6c0933de373d90e0bbec6d35606d324a223da22d344ac4b3229adb30b31cbcad5b8d6fe132bad8f5f12564b95a9da3b362deb6ade42fd410f6ca533861a567bf0535b0b0f9800de5fde966c4bb097310b022635f64db447676d32c57ca2c5637c06a79381edf16d85507789d3e7e4a68f21cda3b45476164597ce4e0da6fad2ee1ac9a8a21eb8a2d67dbc63a1161a63f726e8ea893c5eb56b4361152b4681a964f06dc514c0a2ed3c1771f4aed70d85124df32b54b1bf6ee95ab8353811e002e3c2f259f41aa0cb5e6325649b82f3870addff21296555774e65a94d8c5401b91455f01a1503f587f03979f78e3c0d359d21e8897b8a9e96ce6254d42b52a56f4848d55c446297755d223c9a019c27412d8ea212588e31974e33d8143aef89c7ae5fac752743e51d4ab5b246e691d6120c3be921c10519fce43ec203325ae38c2d019fd2f72e7bb06c36c4b381d4a80d5cc74e79c7f26fac8bb310edf17615cf99fa0e7253cb4ad2475b2cfa338e443064de3fb82dc7a43550bc30ef75595334a2c909069dbdc7cc6e49b9beda49e84cd298cfe1a72cc0015842f282901020323505a8bfd8eb6b05d6303cc1ebab233fc707096de603a1fc8467e5b2e2dd799d60dcf1a250bb341691616003d44c2e8047183de0405bde742414032ee28cf1763b41a8d21e6fdc8d988fdb24b2c69e83c7ae1490026b99c0091e8191bbfff4241ca662cf31214f2344f68fd57aaa51db6b638a40444af3a10b1d651ec0cb1855649cf90436aa948de3d3239cc198864a885590c0a78ba508e379073bf630bfc18570926773bac20617218163a30e96ca4f02a6dd746dee15ee7d760a7fee9b1de5f8c1ee19f30ba162cbc047f845a59f7522895ef0b78f1ae8db2b03331c3d658bbfe740215e3b293d0fccfdb37ccbe790e4960df02d3182714b8db2cf93adc24784633dd2f7d052813a0c606e1368d1c4cba84f57a86ffa7ef0f5bfa7113cd3d4bbc376036d1482e62c64cba99ed7ccc97d5314f9cd04d439897850cfe81c6d5eed49225ac3ebf3afdf4ddaf92d499310de1705f6ef9b0107ae7a50853e18906ef7f017e6782951d21cab5d6ab243ba5991a569453a77457df9157c349a9fe3f6b9b0eb9df84d361c2d3ceb2bab47e6cb249135d1f238ab6003a8eb6e8484c8fad27fc1a37cd8a77a6ee42f40fe066856df5f2c293c4c723b2860db4773c538ab74dd85b49101b451dd0691f18501edac834acc07fa17c240eeb09c1ce9fd9b1494aca119d233720a09430d67ec2d3465883abdd8ad43d71d63806f302872ca01115961b5ec00528a9b6e2d0b16e7c600da3d83cfe39e2c195736784f658a2648d06a41fdff1155b1ded3391d1345cd54c1d2fe990c62dbcf03fb9c01eb4b9a00361116bdb74efab62ddff67e6cab9de03130f11736a109569b6386791de6fba64a10eb44953a7cae94f3082ba2f76ec4864b35c871bb8b6416a4683c4b5d898c2a93a94cdd79ef1087ab9c3aa47509747994a8e43a780fcd921a780b66e1fbcb424b61acc3ad0113f2852f73befc49fcbff537f11c65283db34ee38f818c9dfb02a59817705fed16800257f9c73cc20d04af76eb0508f2e6b84b01cd05b0799fd04539f1d85192f11dc662a5330cff1ae6694ef4f3caa960903904cab8895b9cd8f2f2f0d61f6b179ec8c1c8c3399b2adb7a41c7bf8cbdabf0ca7f6021867b930e03538a65351405c0525ec67464a7c0f6c36fafc4aa1652fa3bb9eb6b676112f0f1af0e9ab9f59bb760c6dd317332255794a87f415e345930c5ce724269fbdc789b8d47afa39de1bce2a43eb05ed7c451ee7c8d666dfd9aba98efbfc4f12d7e7c836015d94b6d35d95d79f87cc29fd7b4aa75c76151088c4ca1be76b2f1977be702a041f747450bb15ab72ecb7f9269f92d7d269e9b5455bf5e9c005af6a6ef605f187b0cf980668dee018b5a8e5688bb7170cf91de4f5bf0ae1d72c84e05648e2360c4f65e7c181098ed5e3fdaa59ed26a9c79063e18db0314cca1d01df4445af3f0ba0d38e612a2ddd2f08e0e4fcb7e22dec944c3477000000076368af4a979078e9cab336d6b0fd30989c5d450570f50aabbb9950218e262d1314bcdd201c02886a4f8b1d6d471e45b59d73fc72ea8f22e17839ba837cecdfd8bfde611b95ec8256a0be4d895ab690cf04b5bfb5899a69d08585221c67a841d18eed056f6c06820cc88c4c40ff013650377f4b8f6df42919d5214dc8d22cb69c43734badd6bb5f15bb8ae72535f7d843b9a216f6a9819f0ce05e20bce6e4227d379c500010fe8249d25227ae479e1ec7303270976a924d235a033bf0daff72555ccc8b7f2040ec3695d91351a23d5c185601291287d281c7f28f7012f8cd6de44da6f78b69589b93e1012617615e41bf963c02157b3653a470c240ac8aaee6144db424b0c9469ca2a43a7ca352e040f7d6831faf3767cf64085cb72cd184033fc4e05c687560be27a394a246159815f858afb39cc0d7d3e482e4d86d1551ea5627716e1d40e1f9b7105bb131bc11bc181dee0eccbf1484e3a375ef63505edd6d56398ec80a3fb1a1d3b39d07b5f990e787f7291eda47adb409a5c5e698612c2e542cc1f5c0bc8c3e8f825a02430ee066276d6a812bcee417c422d4a4522239856c95a094cb327994a04ac59b3a668259343e88d0aae669631ed21db81a8a80b0c90c36912c2a2b3aea312a81b686eaed72dd8bae244b423fb6bb2da43971d5c4 + +# LMS_SHA256_N32_H15 with LMOTS_SHA256_N32_W8 +# Seed = 67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a +# Msg = 1234f006ba89f00111213baf0016171819f0 +# PublicKey = 000000070000000466320db73158a35a255d051758e95ed4f570b50e199127e84d6c2b1d192c5aef3765b4811243f28e962f5dfd0f499fc5 +# Sig = 00000005000000048ebea1c5c841a4fb1357d69c5f556782a1d729a2f6edbf41febc33536290b5d936ce468542032804d06c393831ec6341267589dfa53bc4af45a82cc2f47c5773151959c68afd91455f89e00d4d8d5ae0c829917a5c68a5447cf6c4215c24f39edfb047c13538aa0984c6cde882a9897bbb3d5f880dc52d1305ff351491ddc754afe36760f17a29f02414ca00bc6c879d5a8b7e175c1e9cfd27ec0f10b5e69eacba4fb8414dd83693c3bf32e2db868ba6199df43824f2049d59707485d02bbdd7d390ca6bc926539151850dcacc27bb3ce414bf83c3c0b0b8466091a811005a89008e6bd841938ea4f4ef7d430b02f9f52f97b9d8828dd35d2093d9a95bb68619b60dfcc7817c18a2a64921088d7257e3ecd3daacc069720612bde6ba4f81e44369632a91e57b35bf8981b169d6df791d80c06fec943a8b06254f9803cb1644f254894879dca37ee87a233f5529d79b93a528160eb938b6b160f1542ffbb545dc43c4bd2fc986e778f46c9981c8b6fe6174d49438fc1cb1d3ddaae5b136a327470a67671d761d2d94c1db16563f5c29b6d6b216af2c8a35920998449efc52df499945698424a70718f5402ec985e238ebd6feb1c388884b13d41552cc6f904216a7b45bfe20774a54e797e218f8a4f67827aec7a6337eb7b7510161191db73105b957b940347171affda1f5994d17ed1f5cc5cc1b22fe451a33cf0c03725df943d2d91e31cd9821820677f2bedbd32f2a1e644921bbb8230c815073a0fabcdf94fc5f772908fa800d5441f5d0d6bd5e696b3d78ae216517f7eb0e37b7b480e92c4194bfdb094d2426512fa0c6e1049f49f62cc8733783f46a540cd82c4bd5960ae6d3131cfcbbe09303fef99db429195c86d2084f0c152d3657b04aed533cc0af51c67e89b3d0b1bb4885fd5d986066e186e6e9e9c76ca95add73881fd3639eafa62b032f177ddf9b9c87a45139a6d80028bdf2360522e0dbdc5082a59b804d67358cad2d6e9029a9184bfc000a19660add2d1ccec28084805dc5554eabe987cc11329aac8af3930343228815c77491737a80b26c81241790135072f2d54141225f7b6d89728d454263960d112d1ce06a45acabe44c0e5a4668310ab40857ec0e2f7f04ea2a484063401f09570e439787e64c31bc4b1c4c5768647bcf01a1ed0485e0ec67d8778f18cbedbccd576e46864efdd269a4c1b69d138021ffbe7ceb3868a9db00cebc00def0d55aee6e87220eab0f14025b8102577d6a14a223a65ed33076cfc3dc389dcdaa39c8e2e08b018f9afac9a39fbbff3fa76c7ed6972f6af54400c329b7eb1e1bf5f1215a352cf141d153396cc93ac0408480a0d9e099a9a71da267ff7b973fd56e44d20c279365e17bc9b9e576e802052dc7c3d116e4944d081962c411fecc8df8d011c8674d2565e663dfc0a008ecad0921a47d39bb8fe8f2b0b7c01b7a9966165cc562b8b3d6d4a503bd88c263607a4812fc8e5bde4e68a5dfefb859eb2cced9bcaf18dda26ad18f88cd7c6601616770292ab31e9c940711b5c047e5269b162c6624149b789642a2dc5df7086f6736cbdb418e179147e300000007c6dcb867c3501f7eac39d6b38d75d1297ecd1f4666a4995dbd24c96a3412394fd01cb7ab329d5d8fb55a153695fb2dff71b682d9e04ca52b9795be891f7c8a251c328b3e8da319e736fcf67a9ee222a3417041d76e2a48abffa243290bfcdebe2efe3e362020c6ca3708251b70d2e2839525310e091117622f687bcc919af14992cdc806b4ad92b3c719ef680c3bfbbe1041e0ca8479030a5cc982611da563c1e803e676fdc2a52d77f7750e67a96521c5911b918fcc7c5f114b9ad5ad911207da1a306bebfa29ccd50d04bf19bf2338769f124088e2b0d77bfde3370074012fe11df473a615163173e225a827af79850ef7c30fac456fcd54b38752444cf5f2aa2a0dc6b3caaa55fd2203e0038d52c7816d6987278e0428b20b75f393f23de7bbc9df439d9400557d80dff14749405126ed796b067547249a39fdd4e31d33f98a4059c1876445cfba12e8f2f969244b4918592c76d949e54c1e37a6a67cc61680f0d2d715731d45cda5c68e57862c32d3fcacd62b36fdfce6bbe1f70edb806cfe0cfd6877295d980160971b4f1a50fea24bfd7d04b718897ce9907629f1b541fc526c33b1696cc16e2a51af3eb5044d12cc98e3ac77b25f96926eae122d61c6752158de2610f3585222932c05fe8d008640a23eb34e3b03c6ec9fdd9e95b5b8 diff --git a/src/tests/test_hss_lms.cpp b/src/tests/test_hss_lms.cpp new file mode 100644 index 00000000000..ab3cfc61fa8 --- /dev/null +++ b/src/tests/test_hss_lms.cpp @@ -0,0 +1,226 @@ +/* +* (C) 2023 Jack Lloyd +* 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "test_pubkey.h" +#include "test_rng.h" +#include "tests.h" + +#if defined(BOTAN_HAS_HSS_LMS) + + #include + #include + #include + #include + #include + #include + + #include + #include + +namespace Botan_Tests { + +namespace { + +std::vector test_hss_lms_params_parsing() { + return { + Botan_Tests::CHECK("HSS Parameter Parsing", + [&](Test::Result& result) { + result.test_no_throw("no throw", [&] { + Botan::HSS_LMS_Params hss_params("SHA-256,HW(5,1),HW(25,8)"); + + result.test_is_eq("hss levels", hss_params.L(), Botan::HSS_Level(2)); + auto& top_lms_params = hss_params.params_at_level(Botan::HSS_Level(0)); + result.test_is_eq( + "hash name", top_lms_params.lms_params().hash_name(), std::string("SHA-256")); + result.test_is_eq("top level - lms type", + top_lms_params.lms_params().algorithm_type(), + Botan::LMS_Algorithm_Type::SHA256_M32_H5); + result.test_is_eq("top level - ots type", + top_lms_params.lmots_params().algorithm_type(), + Botan::LMOTS_Algorithm_Type::SHA256_N32_W1); + + auto& second_lms_params = hss_params.params_at_level(Botan::HSS_Level(1)); + result.test_is_eq("2nd level - lms type", + second_lms_params.lms_params().algorithm_type(), + Botan::LMS_Algorithm_Type::SHA256_M32_H25); + result.test_is_eq("2nd level - ots type", + second_lms_params.lmots_params().algorithm_type(), + Botan::LMOTS_Algorithm_Type::SHA256_N32_W8); + }); + }), + + }; +} + +// Test signature generation using the raw private key bytes +class HSS_LMS_Signature_Generation_Test final : public PK_Signature_Generation_Test { + public: + HSS_LMS_Signature_Generation_Test() : + PK_Signature_Generation_Test("HSS-LMS", "pubkey/hss_lms_sig.vec", "Msg,PrivateKey,Signature") {} + + std::string default_padding(const VarMap&) const final { return ""; } + + std::unique_ptr load_private_key(const VarMap& vars) final { + const auto sk_bytes = Botan::lock(vars.get_req_bin("PrivateKey")); + return std::make_unique(sk_bytes); + } +}; + +// Test signature verification using the raw public key bytes +class HSS_LMS_Signature_Verify_Tests final : public PK_Signature_Verification_Test { + public: + HSS_LMS_Signature_Verify_Tests() : + PK_Signature_Verification_Test("HSS-LMS", "pubkey/hss_lms_verify.vec", "Msg,PublicKey,Signature") {} + + std::string default_padding(const VarMap&) const final { return ""; } + + std::unique_ptr load_public_key(const VarMap& vars) override { + const std::vector pk_bytes = vars.get_req_bin("PublicKey"); + return std::make_unique(pk_bytes); + } +}; + +class HSS_LMS_Signature_Verify_Invalid_Tests final : public PK_Signature_NonVerification_Test { + public: + HSS_LMS_Signature_Verify_Invalid_Tests() : + PK_Signature_NonVerification_Test( + "HSS_LMS", "pubkey/hss_lms_invalid.vec", "Msg,PublicKey,InvalidSignature") {} + + std::string default_padding(const VarMap&) const override { return ""; } + + std::unique_ptr load_public_key(const VarMap& vars) override { + const std::vector raw_key = vars.get_req_bin("PublicKey"); + return std::make_unique(raw_key); + } +}; + +// Test public key creation +class HSS_LMS_Key_Generation_Test final : public PK_Key_Generation_Test { + public: + std::vector keygen_params() const final { return {"SHA-256,HW(10,4),HW(5,8)"}; } + + std::string algo_name() const final { return "HSS-LMS"; } +}; + +// Test that for too short signatures, private keys, and public keys a DecodeError appears. +class HSS_LMS_Too_Short_Test final : public Test { + Test::Result test_too_short_signature() { + Test::Result result("LMS-HSS"); + + auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "Truncated(SHA-256,192),HW(5,8)"); + + Botan::PK_Signer signer(*sk, Test::rng(), ""); + Botan::PK_Verifier verifier(*sk, ""); + + std::vector mes = {0xde, 0xad, 0xbe, 0xef}; + + signer.update(mes); + auto valid_sig = signer.signature(Test::rng()); + verifier.update(mes); + result.confirm("Entire signature is valid", verifier.check_signature(valid_sig.data(), valid_sig.size())); + for(size_t n = 0; n < valid_sig.size(); ++n) { + result.test_no_throw("Verification does not throw", [&]() { + verifier.update(mes); + bool valid = verifier.check_signature(valid_sig.data(), n); + result.confirm("Too short signature is invalid", !valid); + }); + } + + return result; + } + + Test::Result test_too_short_private_key() { + Test::Result result("LMS-HSS"); + + // HSS_LMS_PublicKey::key_length() + auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "Truncated(SHA-256,192),HW(5,8)"); + + auto sk_bytes = sk->private_key_bits(); + result.test_no_throw("Entire private key valid", + [&]() { std::make_unique(sk_bytes); }); + for(size_t n = 0; n < sk_bytes.size(); ++n) { + result.test_throws("Partial private key invalid", [&]() { + std::span partial_key = {sk_bytes.data(), n}; + std::make_unique(partial_key); + }); + } + return result; + } + + Test::Result test_too_short_public_key() { + Test::Result result("LMS-HSS"); + + // HSS_LMS_PublicKey::key_length() + auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "Truncated(SHA-256,192),HW(5,8)"); + + auto sk_bytes = sk->public_key_bits(); + result.test_no_throw("Entire public key valid", + [&]() { std::make_unique(sk_bytes); }); + for(size_t n = 0; n < sk_bytes.size(); ++n) { + result.test_throws("Partial public key invalid", [&]() { + std::span partial_key = {sk_bytes.data(), n}; + std::make_unique(partial_key); + }); + } + return result; + } + + std::vector run() final { + return {test_too_short_signature(), test_too_short_private_key(), test_too_short_public_key()}; + } +}; + +// TODO: this currently only calls APIs not covered by other tests. +// Better would be to integrate them probably into a "normal" test. +// Maybe even into the general tests. +class HSS_LMS_Missing_API_Test final : public Test { + std::vector run() final { + Test::Result result("LMS-HSS"); + + // HSS_LMS_PublicKey::key_length() + auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "SHA-256,HW(10,4)"); + sk->key_length(); + // TODO: use constant for I + result.test_gt("Public key length must be greater than the simply type information plus I", + sk->key_length(), + 3 * sizeof(uint32_t) + 16); + + // HSS_LMS_Verification_Operation::hash_function() + Botan::PK_Verifier verifier(*sk, ""); + result.test_eq("PK_Verifier should report the hash of the key", verifier.hash_function(), "SHA-256"); + + // HSS_LMS_PrivateKey::raw_private_key_bits() + result.test_eq("Our BER and raw encoding is the same", sk->raw_private_key_bits(), sk->private_key_bits()); + + // HSS_LMS_Signature_Operation::algorithm_identifier() + Botan::PK_Signer signer(*sk, Test::rng(), ""); + result.test_is_eq(signer.algorithm_identifier(), sk->algorithm_identifier()); + + // Signer/Verifier should not just hold a reference to the key + sk.reset(); + signer.sign_message({}, Test::rng()); + + // HSS_LMS_Signature_Operation::hash_function() + result.test_eq("PK_Signer should report the hash of the key", signer.hash_function(), "SHA-256"); + + return {result}; + } +}; + +BOTAN_REGISTER_TEST_FN("pubkey", "hss_lms_params_parsing", test_hss_lms_params_parsing); +BOTAN_REGISTER_TEST("pubkey", "hss_lms_sign", HSS_LMS_Signature_Generation_Test); +BOTAN_REGISTER_TEST("pubkey", "hss_lms_verify", HSS_LMS_Signature_Verify_Tests); +BOTAN_REGISTER_TEST("pubkey", "hss_lms_verify_invalid", HSS_LMS_Signature_Verify_Invalid_Tests); +BOTAN_REGISTER_TEST("pubkey", "hss_lms_keygen", HSS_LMS_Key_Generation_Test); +BOTAN_REGISTER_TEST("pubkey", "hss_lms_short", HSS_LMS_Too_Short_Test); +BOTAN_REGISTER_TEST("pubkey", "hss_lms_api", HSS_LMS_Missing_API_Test); + +} // namespace + +} // namespace Botan_Tests + +#endif // BOTAN_HAS_HSS_LMS diff --git a/src/tests/test_lmots.cpp b/src/tests/test_lmots.cpp new file mode 100644 index 00000000000..24ea255c5a5 --- /dev/null +++ b/src/tests/test_lmots.cpp @@ -0,0 +1,70 @@ +/* +* (C) 2023 Jack Lloyd +* 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "test_rng.h" +#include "tests.h" + +#if defined(BOTAN_HAS_HSS_LMS) + + #include + #include + #include + #include + +namespace Botan_Tests { + +namespace { + +class LMOTS_Test final : public Text_Based_Test { + public: + LMOTS_Test() : Text_Based_Test("pubkey/lmots.vec", "Seed,I,q,Msg,PublicKey,Sig") {} + + bool skip_this_test(const std::string&, const VarMap& vars) override { + BOTAN_UNUSED(vars); + return false; + } + + Test::Result run_one_test(const std::string&, const VarMap& vars) final { + Test::Result result("LMOTS"); + + const auto seed = Botan::LMS_Seed(vars.get_req_bin("Seed")); + const auto identifier = Botan::LMS_Identifier(vars.get_req_bin("I")); + const auto q = Botan::LMS_Tree_Node_Idx(vars.get_req_u32("q")); + const auto msg = Botan::LMS_Message(vars.get_req_bin("Msg")); + const auto pk_ref = Botan::LMOTS_K(vars.get_req_bin("PublicKey")); + const auto sig_ref = Botan::LMOTS_Signature_Bytes(vars.get_req_bin("Sig")); + + auto sig_ref_slicer = Botan::BufferSlicer(sig_ref); + auto sig_ref_obj = Botan::LMOTS_Signature::from_bytes_or_throw(sig_ref_slicer); + auto params = Botan::LMOTS_Params::create_or_throw(sig_ref_obj.algorithm_type()); + + // Test private/public OTS key creation + auto sk = Botan::LMOTS_Private_Key(params, identifier, q, seed); + const auto pk = Botan::LMOTS_Public_Key(sk); + result.test_is_eq("Public key generation", pk.K(), pk_ref); + + // Test signature creation + Botan::LMOTS_Signature_Bytes sig(Botan::LMOTS_Signature::size(params)); + sk.sign(sig, msg); + result.test_is_eq("Signature generation", sig, sig_ref); + + // Test create pubkey from signature + auto sig_slicer = Botan::BufferSlicer(sig); + auto sig_obj = Botan::LMOTS_Signature::from_bytes_or_throw(sig_slicer); + Botan::LMOTS_K pk_from_sig = Botan::lmots_compute_pubkey_from_sig(sig_obj, msg, identifier, q); + result.test_is_eq("Public key from signature", pk_from_sig, pk_ref); + + return result; + } +}; + +BOTAN_REGISTER_TEST("pubkey", "lmots", LMOTS_Test); + +} // namespace +} // namespace Botan_Tests + +#endif // BOTAN_HAS_HSS_LMS \ No newline at end of file diff --git a/src/tests/test_lms.cpp b/src/tests/test_lms.cpp new file mode 100644 index 00000000000..039b8ded8eb --- /dev/null +++ b/src/tests/test_lms.cpp @@ -0,0 +1,71 @@ +/* +* (C) 2023 Jack Lloyd +* 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "test_rng.h" +#include "tests.h" + +#if defined(BOTAN_HAS_HSS_LMS) + + #include + #include + #include + #include + +namespace Botan_Tests { + +namespace { + +class LMS_Test final : public Text_Based_Test { + public: + LMS_Test() : Text_Based_Test("pubkey/lms.vec", "Seed,Msg,PublicKey,Sig") {} + + bool skip_this_test(const std::string&, const VarMap& vars) override { + BOTAN_UNUSED(vars); + return false; + } + + Test::Result run_one_test(const std::string&, const VarMap& vars) final { + Test::Result result("LMS"); + + const auto seed = Botan::LMS_Seed(vars.get_req_bin("Seed")); + const auto msg = Botan::LMS_Message(vars.get_req_bin("Msg")); + const auto pk_ref = vars.get_req_bin("PublicKey"); + const auto sig_ref = Botan::LMS_Signature_Bytes(vars.get_req_bin("Sig")); + + auto lms_pk_ref_slicer = Botan::BufferSlicer(pk_ref); + Botan::LMS_PublicKey lms_pk_ref = Botan::LMS_PublicKey::from_bytes_of_throw(lms_pk_ref_slicer); + + // Test public key creation + auto lms_sk = + Botan::LMS_PrivateKey(lms_pk_ref.lms_params(), lms_pk_ref.lmots_params(), lms_pk_ref.identifier(), seed); + auto pub_key = Botan::LMS_PublicKey(lms_sk); + + result.test_is_eq("Public key generation", pub_key.to_bytes(), pk_ref); + + // Test signature creation and verification + auto sig_slicer = Botan::BufferSlicer(sig_ref); + auto sig_ref_obj = Botan::LMS_Signature::from_bytes_or_throw(sig_slicer); + auto q = sig_ref_obj.q(); + + auto sk = + Botan::LMS_PrivateKey(lms_pk_ref.lms_params(), lms_pk_ref.lmots_params(), lms_pk_ref.identifier(), seed); + Botan::LMS_Signature_Bytes sig(Botan::LMS_Signature::size(lms_pk_ref.lms_params(), lms_pk_ref.lmots_params())); + auto pk_from_sig = sk.sign_and_get_pk(sig, q, msg); + result.test_is_eq("Signature creation", sig, sig_ref); + + result.confirm("Signature verification", pub_key.verify_signature(msg, sig_ref_obj)); + + return result; + } +}; + +BOTAN_REGISTER_TEST("pubkey", "lms", LMS_Test); + +} // namespace +} // namespace Botan_Tests + +#endif // BOTAN_HAS_HSS_LMS