diff --git a/doc/api_ref/pubkey.rst b/doc/api_ref/pubkey.rst index afc98bf8736..c8299aee6f5 100644 --- a/doc/api_ref/pubkey.rst +++ b/doc/api_ref/pubkey.rst @@ -172,19 +172,34 @@ signatures, then the whole scheme becomes insecure, and signatures can be forged. HSS-LMS -------- +~~~~~~~ A post-quantum secure hash-based signature scheme similar to XMSS. Contains support for multitrees. It is stateful, meaning the private key changes after each signature. -SPHINCS+ -~~~~~~~~~ +SLH-DSA (FIPS 205) +~~~~~~~~~~~~~~~~~~ -A post-quantum secure signature scheme whose security is based (only) on the -security of a hash function. Unlike XMSS, it is a stateless signature -scheme, meaning that the private key does not change with each signature. It -has high security but very long signatures and high runtime. +The Stateless Hash-Based Digital Signature Standard (SLH-DSA) +is the FIPS 205 post-quantum secure signature scheme whose security is solely +based on the security of a hash function. Unlike XMSS, it is a stateless +signature scheme, meaning that the private key does not change with each +signature. It has high security but very long signatures and high runtime. + +Support for SLH-DSA is implemented in the modules ``slh_dsa_sha2`` and ``slh_dsa_shake``. + +Additionally, support for the pre-standardized version "SPHINCS+" is retained +for the time being. The implemented specification is commonly referred to as +version 3.1 of the SPHINCS+ submission to NIST's third round of the +PQC competition. This is not compatible with the "Initial Public Draft" version of +FIPS 205 for which Botan does not offer an implementation. Also, Botan does not +support the Haraka hash function. + +Currently, two flavors of SPHINCS+ are implemented in separate Botan modules: + + * ``sphincsplus_shake``, that uses Keccak (SHAKE) hash functions + * ``sphincsplus_sha2``, that uses SHA-256 FrodoKEM ~~~~~~~~ @@ -836,7 +851,7 @@ Botan implements the following signature algorithms: #. ML-DSA (Dilithium). Takes the optional parameter ``Deterministic`` (default) or ``Randomized``. -#. SPHINCS+. +#. SLH-DSA. Takes the optional parameter ``Deterministic`` (default) or ``Randomized``. #. XMSS. Takes no parameter. #. HSS-LMS. Takes no parameter. diff --git a/doc/api_ref/python.rst b/doc/api_ref/python.rst index d84eaa10e06..cfa7ba5be4f 100644 --- a/doc/api_ref/python.rst +++ b/doc/api_ref/python.rst @@ -309,6 +309,11 @@ Public Key Load an ML-DSA public key giving the mode as a string (like "ML-DSA-4x4") and the raw encoding of the public key. + .. py:classmethod:: load_slh_dsa(mode, raw_encoding) + + Load an SLH-DSA public key giving the mode as a string (like + "SLH-DSA-SHAKE-128f") and the raw encoding of the public key. + .. py:method:: check_key(rng_obj, strong=True): Test the key for consistency. If ``strong`` is ``True`` then @@ -408,6 +413,10 @@ Private Key Return a private ML-DSA key + .. py:classmethod:: load_slh_dsa(mode, raw_encoding) + + Return a private SLH-DSA key + .. py:method:: get_public_key() Return a public_key object diff --git a/doc/credits.rst b/doc/credits.rst index de86b59c391..6765f317b33 100644 --- a/doc/credits.rst +++ b/doc/credits.rst @@ -163,7 +163,7 @@ snail-mail address (S), and Bitcoin address (B). N: René Meusel E: rene.meusel@rohde-schwarz.com W: https://www.rohde-schwarz.com/cybersecurity - D: CI, TLS 1.3, Kyber, Dilithium, SPHINCS+, FrodoKEM + D: CI, TLS 1.3, Kyber, Dilithium, SLH-DSA, FrodoKEM S: Berlin, Germany N: Philippe Lieser @@ -175,5 +175,5 @@ snail-mail address (S), and Bitcoin address (B). N: Fabian Albert E: fabian.albert@rohde-schwarz.com W: https://www.rohde-schwarz.com/cybersecurity - D: SPHINCS+, HSS/LMS + D: SLH-DSA, HSS/LMS S: Bochum, Germany diff --git a/readme.rst b/readme.rst index cf99dbe0f74..807c3a1e4c6 100644 --- a/readme.rst +++ b/readme.rst @@ -102,8 +102,8 @@ Public Key Cryptography * RSA signatures and encryption * DH and ECDH key agreement * Signature schemes ECDSA, DSA, Ed25519, Ed448, ECGDSA, ECKCDSA, SM2, GOST 34.10 -* Post-quantum signature schemes ML-DSA (Dilithium), HSS/LMS, SPHINCS+, XMSS -* Post-quantum key agreement schemes McEliece, ML-KEM (Kyber), and FrodoKEM +* Post-quantum signature schemes ML-DSA (Dilithium), SLH-DSA (SPHINCS+), HSS/LMS, XMSS +* Post-quantum key agreement schemes ML-KEM (Kyber), McEliece, and FrodoKEM * ElGamal encryption * Padding schemes OAEP, PSS, PKCS #1 v1.5, X9.31 diff --git a/src/build-data/oids.txt b/src/build-data/oids.txt index 00b985a4876..df8914fc562 100644 --- a/src/build-data/oids.txt +++ b/src/build-data/oids.txt @@ -26,6 +26,19 @@ 2.16.840.1.101.3.4.3.18 = ML-DSA-6x5 2.16.840.1.101.3.4.3.19 = ML-DSA-8x7 +2.16.840.1.101.3.4.3.20 = SLH-DSA-SHA2-128s +2.16.840.1.101.3.4.3.21 = SLH-DSA-SHA2-128f +2.16.840.1.101.3.4.3.22 = SLH-DSA-SHA2-192s +2.16.840.1.101.3.4.3.23 = SLH-DSA-SHA2-192f +2.16.840.1.101.3.4.3.24 = SLH-DSA-SHA2-256s +2.16.840.1.101.3.4.3.25 = SLH-DSA-SHA2-256f +2.16.840.1.101.3.4.3.26 = SLH-DSA-SHAKE-128s +2.16.840.1.101.3.4.3.27 = SLH-DSA-SHAKE-128f +2.16.840.1.101.3.4.3.28 = SLH-DSA-SHAKE-192s +2.16.840.1.101.3.4.3.29 = SLH-DSA-SHAKE-192f +2.16.840.1.101.3.4.3.30 = SLH-DSA-SHAKE-256s +2.16.840.1.101.3.4.3.31 = SLH-DSA-SHAKE-256f + # FrodoKEM OIDs are currently in Botan's private arc 1.3.6.1.4.1.25258.1.14.1 = FrodoKEM-640-SHAKE 1.3.6.1.4.1.25258.1.14.2 = FrodoKEM-976-SHAKE diff --git a/src/cli/perf_pk_sig.cpp b/src/cli/perf_pk_sig.cpp index eb9fd20fb15..3319a43ca81 100644 --- a/src/cli/perf_pk_sig.cpp +++ b/src/cli/perf_pk_sig.cpp @@ -347,6 +347,41 @@ BOTAN_REGISTER_PERF_TEST("SPHINCS+", PerfTest_SPHINCSp); #endif +#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE) + +class PerfTest_SLH_DSA final : public PerfTest_PKSig { + public: + std::string algo() const override { return "SLH-DSA"; } + + std::string hash() const override { return ""; } + + std::string format_name(const std::string& alg, const std::string& param) const override { + BOTAN_UNUSED(alg); + return param; // Param already has algo in the string + } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + + return {"SLH-DSA-SHA2-128s", + "SLH-DSA-SHA2-128f", + "SLH-DSA-SHA2-192s", + "SLH-DSA-SHA2-192f", + "SLH-DSA-SHA2-256s", + "SLH-DSA-SHA2-256f", + "SLH-DSA-SHAKE-128s", + "SLH-DSA-SHAKE-128f", + "SLH-DSA-SHAKE-192s", + "SLH-DSA-SHAKE-192f", + "SLH-DSA-SHAKE-256s", + "SLH-DSA-SHAKE-256f"}; + } +}; + +BOTAN_REGISTER_PERF_TEST("SLH-DSA", PerfTest_SLH_DSA); + +#endif + #if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES) class PerfTest_Dilithium final : public PerfTest_PKSig { diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index d3b19d6c78a..cfaf943b9bc 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -285,6 +285,7 @@ class Speed final : public Command { "X448", "Kyber", "ML-KEM", + "SLH-DSA", "SPHINCS+", "FrodoKEM", "HSS-LMS", diff --git a/src/lib/asn1/oid_maps.cpp b/src/lib/asn1/oid_maps.cpp index a50f1550423..ce97ee52c03 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 2024-10-15 +* This file was automatically generated by ./src/scripts/dev_tools/gen_oids.py on 2024-10-15 * * All manual edits to this file will be lost. Edit the script * then regenerate this source file. @@ -270,7 +270,19 @@ std::unordered_map OID_Map::load_oid2str_map() { {"2.16.840.1.101.3.4.3.18", "ML-DSA-6x5"}, {"2.16.840.1.101.3.4.3.19", "ML-DSA-8x7"}, {"2.16.840.1.101.3.4.3.2", "DSA/SHA-256"}, + {"2.16.840.1.101.3.4.3.20", "SLH-DSA-SHA2-128s"}, + {"2.16.840.1.101.3.4.3.21", "SLH-DSA-SHA2-128f"}, + {"2.16.840.1.101.3.4.3.22", "SLH-DSA-SHA2-192s"}, + {"2.16.840.1.101.3.4.3.23", "SLH-DSA-SHA2-192f"}, + {"2.16.840.1.101.3.4.3.24", "SLH-DSA-SHA2-256s"}, + {"2.16.840.1.101.3.4.3.25", "SLH-DSA-SHA2-256f"}, + {"2.16.840.1.101.3.4.3.26", "SLH-DSA-SHAKE-128s"}, + {"2.16.840.1.101.3.4.3.27", "SLH-DSA-SHAKE-128f"}, + {"2.16.840.1.101.3.4.3.28", "SLH-DSA-SHAKE-192s"}, + {"2.16.840.1.101.3.4.3.29", "SLH-DSA-SHAKE-192f"}, {"2.16.840.1.101.3.4.3.3", "DSA/SHA-384"}, + {"2.16.840.1.101.3.4.3.30", "SLH-DSA-SHAKE-256s"}, + {"2.16.840.1.101.3.4.3.31", "SLH-DSA-SHAKE-256f"}, {"2.16.840.1.101.3.4.3.4", "DSA/SHA-512"}, {"2.16.840.1.101.3.4.3.5", "DSA/SHA-3(224)"}, {"2.16.840.1.101.3.4.3.6", "DSA/SHA-3(256)"}, @@ -500,6 +512,18 @@ std::unordered_map OID_Map::load_str2oid_map() { {"SHA-512-256", OID({2, 16, 840, 1, 101, 3, 4, 2, 6})}, {"SHAKE-128", OID({2, 16, 840, 1, 101, 3, 4, 2, 11})}, {"SHAKE-256", OID({2, 16, 840, 1, 101, 3, 4, 2, 12})}, + {"SLH-DSA-SHA2-128f", OID({2, 16, 840, 1, 101, 3, 4, 3, 21})}, + {"SLH-DSA-SHA2-128s", OID({2, 16, 840, 1, 101, 3, 4, 3, 20})}, + {"SLH-DSA-SHA2-192f", OID({2, 16, 840, 1, 101, 3, 4, 3, 23})}, + {"SLH-DSA-SHA2-192s", OID({2, 16, 840, 1, 101, 3, 4, 3, 22})}, + {"SLH-DSA-SHA2-256f", OID({2, 16, 840, 1, 101, 3, 4, 3, 25})}, + {"SLH-DSA-SHA2-256s", OID({2, 16, 840, 1, 101, 3, 4, 3, 24})}, + {"SLH-DSA-SHAKE-128f", OID({2, 16, 840, 1, 101, 3, 4, 3, 27})}, + {"SLH-DSA-SHAKE-128s", OID({2, 16, 840, 1, 101, 3, 4, 3, 26})}, + {"SLH-DSA-SHAKE-192f", OID({2, 16, 840, 1, 101, 3, 4, 3, 29})}, + {"SLH-DSA-SHAKE-192s", OID({2, 16, 840, 1, 101, 3, 4, 3, 28})}, + {"SLH-DSA-SHAKE-256f", OID({2, 16, 840, 1, 101, 3, 4, 3, 31})}, + {"SLH-DSA-SHAKE-256s", OID({2, 16, 840, 1, 101, 3, 4, 3, 30})}, {"SM2", OID({1, 2, 156, 10197, 1, 301, 1})}, {"SM2_Enc", OID({1, 2, 156, 10197, 1, 301, 3})}, {"SM2_Kex", OID({1, 2, 156, 10197, 1, 301, 2})}, diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index fdf85b53a80..c9341501bc1 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -1566,6 +1566,16 @@ int botan_privkey_load_ml_kem(botan_privkey_t* key, const uint8_t privkey[], siz BOTAN_FFI_EXPORT(3, 6) int botan_pubkey_load_ml_kem(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* mlkem_mode); +/* +* Algorithm specific key operations: SLH-DSA +*/ + +BOTAN_FFI_EXPORT(3, 6) +int botan_privkey_load_slh_dsa(botan_privkey_t* key, const uint8_t privkey[], size_t key_len, const char* slhdsa_mode); + +BOTAN_FFI_EXPORT(3, 6) +int botan_pubkey_load_slh_dsa(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* slhdsa_mode); + /* * Algorithm specific key operations: ECDSA and ECDH */ diff --git a/src/lib/ffi/ffi_pkey_algs.cpp b/src/lib/ffi/ffi_pkey_algs.cpp index c7c90eb7c88..316cd05beb7 100644 --- a/src/lib/ffi/ffi_pkey_algs.cpp +++ b/src/lib/ffi/ffi_pkey_algs.cpp @@ -84,6 +84,10 @@ #include #endif +#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE) + #include +#endif + namespace { #if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) @@ -1148,6 +1152,58 @@ int botan_pubkey_load_ml_dsa(botan_pubkey_t* key, const uint8_t pubkey[], size_t #endif } +/* +* Algorithm specific key operations: SLH-DSA +*/ + +int botan_privkey_load_slh_dsa(botan_privkey_t* key, const uint8_t privkey[], size_t key_len, const char* slhdsa_mode) { +#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE) + if(key == nullptr || privkey == nullptr || slhdsa_mode == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + *key = nullptr; + + return ffi_guard_thunk(__func__, [=]() -> int { + auto mode = Botan::Sphincs_Parameters::create(slhdsa_mode); + if(!mode.is_slh_dsa()) { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } + + auto slhdsa_key = std::make_unique(std::span{privkey, key_len}, mode); + *key = new botan_privkey_struct(std::move(slhdsa_key)); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(key, key_len, privkey, slhdsa_mode); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_pubkey_load_slh_dsa(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* slhdsa_mode) { +#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE) + if(key == nullptr || pubkey == nullptr || slhdsa_mode == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + *key = nullptr; + + return ffi_guard_thunk(__func__, [=]() -> int { + auto mode = Botan::Sphincs_Parameters::create(slhdsa_mode); + if(!mode.is_slh_dsa()) { + return BOTAN_FFI_ERROR_BAD_PARAMETER; + } + + auto mldsa_key = std::make_unique(std::span{pubkey, key_len}, mode); + *key = new botan_pubkey_struct(std::move(mldsa_key)); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(key, key_len, pubkey, slhdsa_mode); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + /* * Algorithm specific key operations: FrodoKEM */ diff --git a/src/lib/pubkey/pk_algs.cpp b/src/lib/pubkey/pk_algs.cpp index 1c8b4252eb3..e4977c3b04d 100644 --- a/src/lib/pubkey/pk_algs.cpp +++ b/src/lib/pubkey/pk_algs.cpp @@ -98,7 +98,7 @@ #include #endif -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) +#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) #include #endif @@ -230,8 +230,9 @@ std::unique_ptr load_public_key(const AlgorithmIdentifier& alg_id, } #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-")) { +#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) + if(alg_name == "SPHINCS+" || alg_name.starts_with("SphincsPlus-") || alg_name.starts_with("SLH-DSA-") || + alg_name.starts_with("Hash-SLH-DSA-")) { return std::make_unique(alg_id, key_bits); } #endif @@ -365,8 +366,9 @@ std::unique_ptr load_private_key(const AlgorithmIdentifier& alg_id, } #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-")) { +#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) + if(alg_name == "SPHINCS+" || alg_name.starts_with("SphincsPlus-") || alg_name.starts_with("SLH-DSA-") || + alg_name.starts_with("Hash-SLH-DSA-")) { return std::make_unique(alg_id, key_bits); } #endif @@ -533,8 +535,8 @@ std::unique_ptr create_private_key(std::string_view alg_name, } #endif -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) - if(alg_name == "SPHINCS+" || alg_name == "SphincsPlus-") { +#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) + if(alg_name == "SPHINCS+" || alg_name == "SphincsPlus" || alg_name == "SLH-DSA") { auto sphincs_params = Sphincs_Parameters::create(params); return std::make_unique(rng, sphincs_params); diff --git a/src/lib/pubkey/sphincsplus/slh_dsa_sha2/info.txt b/src/lib/pubkey/sphincsplus/slh_dsa_sha2/info.txt new file mode 100644 index 00000000000..4f5d1b9e4ea --- /dev/null +++ b/src/lib/pubkey/sphincsplus/slh_dsa_sha2/info.txt @@ -0,0 +1,11 @@ + +SLH_DSA_WITH_SHA2 -> 20240806 + + + +name -> "SLH-DSA (SHA-256)" + + + +sphincsplus_sha2_base + diff --git a/src/lib/pubkey/sphincsplus/slh_dsa_shake/info.txt b/src/lib/pubkey/sphincsplus/slh_dsa_shake/info.txt new file mode 100644 index 00000000000..d775089b0bd --- /dev/null +++ b/src/lib/pubkey/sphincsplus/slh_dsa_shake/info.txt @@ -0,0 +1,11 @@ + +SLH_DSA_WITH_SHAKE -> 20240808 + + + +name -> "SLH-DSA (SHAKE)" + + + +sphincsplus_shake_base + diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/info.txt b/src/lib/pubkey/sphincsplus/sphincsplus_common/info.txt index 11fd3284b4d..94a491d1f3e 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/info.txt +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/info.txt @@ -3,8 +3,8 @@ SPHINCS_PLUS_COMMON -> 20230426 -name -> "SPHINCS+ (common)" -brief -> "Base implementation of SPHINCS+" +name -> "SLH-DSA (common)" +brief -> "Base implementation of Stateless Hash Function DSA" type -> "Internal" diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_address.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_address.h index 3ce4ce09852..3ac70081404 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_address.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_address.h @@ -1,10 +1,10 @@ /* - * SPHINCS+ Address + * SLH-DSA Address * (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) - **/ + */ #ifndef BOTAN_SPHINCS_PLUS_ADDRESS_H_ #define BOTAN_SPHINCS_PLUS_ADDRESS_H_ @@ -28,11 +28,12 @@ enum class Sphincs_Address_Type : uint32_t { }; /** - * Representation of a SPHINCS+ hash function address as specified in - * SPHINCS+ Specification Round 3.1, Section 2.7.3 + * Representation of a SLH-DSA hash function address as specified in + * FIPS 205, Section 4.2 */ class BOTAN_TEST_API Sphincs_Address final { private: + // Offsets of the address fields in the address array. Counted in 32-bit words. static constexpr size_t layer_offset = 0; static constexpr size_t tree_offset = 1; // tree address is 3 words wide static constexpr size_t type_offset = 4; @@ -52,44 +53,46 @@ class BOTAN_TEST_API Sphincs_Address final { Sphincs_Address(std::array address) { std::copy(address.begin(), address.end(), m_address.begin()); } - Sphincs_Address& set_layer(HypertreeLayerIndex layer) { + /* Setter member functions as specified in FIPS 205, Section 4.3 */ + + Sphincs_Address& set_layer_address(HypertreeLayerIndex layer) { m_address[layer_offset] = layer.get(); return *this; } - Sphincs_Address& set_tree(XmssTreeIndexInLayer tree) { + Sphincs_Address& set_tree_address(XmssTreeIndexInLayer tree) { m_address[tree_offset + 0] = 0; // not required by all current instances m_address[tree_offset + 1] = static_cast(tree.get() >> 32); m_address[tree_offset + 2] = static_cast(tree.get()); return *this; } + /* + * Sets the type without clearing the other fields (contrary to the specs setTypeAndClear). + * This adaption is used for optimization purposes. + */ Sphincs_Address& set_type(Sphincs_Address_Type type) { m_address[type_offset] = static_cast(type); return *this; } - /* These functions are used for WOTS and FORS addresses. */ - - Sphincs_Address& set_keypair(TreeNodeIndex keypair) { + Sphincs_Address& set_keypair_address(TreeNodeIndex keypair) { m_address[keypair_offset] = keypair.get(); return *this; } - Sphincs_Address& set_chain(WotsChainIndex chain) { + Sphincs_Address& set_chain_address(WotsChainIndex chain) { m_address[chain_offset] = chain.get(); return *this; } - Sphincs_Address& set_hash(WotsHashIndex hash) { - m_address[hash_offset] = hash.get(); + Sphincs_Address& set_tree_height(TreeLayerIndex tree_height) { + m_address[tree_height_offset] = tree_height.get(); return *this; } - /* These functions are used for all hash tree addresses (including FORS). */ - - Sphincs_Address& set_tree_height(TreeLayerIndex tree_height) { - m_address[tree_height_offset] = tree_height.get(); + Sphincs_Address& set_hash_address(WotsHashIndex hash) { + m_address[hash_offset] = hash.get(); return *this; } @@ -98,6 +101,8 @@ class BOTAN_TEST_API Sphincs_Address final { return *this; } + /* Custom helper member functions */ + Sphincs_Address& copy_subtree_from(const Sphincs_Address& other) { m_address[layer_offset] = other.m_address[layer_offset]; m_address[tree_offset + 0] = other.m_address[tree_offset + 0]; diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp index 5b596572993..53a85484601 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp @@ -1,10 +1,12 @@ /* - * FORS - Forest of Random Subsets + * FORS - Forest of Random Subsets (FIPS 205, Section 8) * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * + * Parts of this file have been adapted from https://github.com/sphincs/sphincsplus + * * Botan is released under the Simplified BSD License (see license.txt) - **/ + */ #include @@ -18,13 +20,11 @@ #include #include -#include -#include - namespace Botan { namespace { +/// FIPS 205, Algorithm 4: base_2^b(X,b,out_len) with b = a and out_len = k (for usage in FORS) std::vector fors_message_to_indices(std::span message, const Sphincs_Parameters& params) { BOTAN_ASSERT_NOMSG((message.size() * 8) >= (params.k() * params.a())); @@ -32,9 +32,26 @@ std::vector fors_message_to_indices(std::span mess uint32_t offset = 0; + // This is one of the few places where the logic of SPHINCS+ round 3.1 and SLH-DSA differs + auto update_idx = [&]() -> std::function { +#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE) + if(params.is_slh_dsa()) { + return [&](TreeNodeIndex& idx, uint32_t i) { + idx ^= (((message[offset >> 3] >> (~offset & 0x7)) & 0x1) << (params.a() - 1 - i)); + }; + } +#endif +#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) + if(!params.is_slh_dsa()) { + return [&](TreeNodeIndex& idx, uint32_t i) { idx ^= (((message[offset >> 3] >> (offset & 0x7)) & 0x1) << i); }; + } +#endif + throw Internal_Error("Missing FORS index update logic for SPHINCS+ or SLH-DSA"); + }(); + for(auto& idx : indices) { for(uint32_t i = 0; i < params.a(); ++i, ++offset) { - idx ^= (((message[offset >> 3] >> (offset & 0x7)) & 0x1) << i); + update_idx(idx, i); } } @@ -72,9 +89,9 @@ SphincsTreeNode fors_sign_and_pkgen(StrongSpan sig_out, uint32_t idx_offset = i * (1 << params.a()); // Compute the secret leaf given by the chunk of the message and append it to the signature - fors_tree_addr.set_tree_height(TreeLayerIndex(0)) - .set_tree_index(indices[i] + idx_offset) - .set_type(Sphincs_Address_Type::ForsKeyGeneration); + fors_tree_addr.set_type(Sphincs_Address_Type::ForsKeyGeneration) + .set_tree_height(TreeLayerIndex(0)) + .set_tree_index(indices[i] + idx_offset); hashes.PRF(sig.next(params.n()), secret_seed, fors_tree_addr); diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.h index 31c0b455f20..2dd8e6700d8 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.h @@ -1,12 +1,12 @@ /* - * FORS - Forest of Random Subsets + * FORS - Forest of Random Subsets (FIPS 205, Section 8) * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * * 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_SPHINCS_PLUS_FORS_H_ #define BOTAN_SPHINCS_PLUS_FORS_H_ @@ -20,8 +20,10 @@ class Sphincs_Hash_Functions; class Sphincs_Parameters; /** + * @brief FIPS 205, Algorithm 16: fors_sign (with simultaneous FORS pk generation) + * * Implements a domain specific wrapper for the few-times signature scheme - * FORS (Forest of Random Subsets). It is meant to be used inside SPHINCS+ + * FORS (Forest of Random Subsets). It is meant to be used inside SLH-DSA * and does not aim to be applicable for other use cases. */ BOTAN_TEST_API SphincsTreeNode fors_sign_and_pkgen(StrongSpan sig_out, @@ -32,8 +34,10 @@ BOTAN_TEST_API SphincsTreeNode fors_sign_and_pkgen(StrongSpan sig Sphincs_Hash_Functions& hashes); /** + * @brief FIPS 205, Algorithm 17: fors_pkFromSig + * * Reconstructs the FORS public key from a given FORS @p signature and - * @p message. This is tailored for the use case in the SPHINCS+ implementation + * @p message. This is tailored for the use case in the SLH-DSA implementation * and is not meant for general usability. */ BOTAN_TEST_API SphincsTreeNode fors_public_key_from_signature(const SphincsHashedMessage& hashed_message, diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.cpp index 4517d3a7ccd..c9fee7e387c 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.cpp @@ -1,5 +1,5 @@ /* - * SPHINCS+ Hashes + * SLH-DSA Hash Function Interface * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * @@ -15,11 +15,11 @@ #include #include -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) +#if defined(BOTAN_HAS_SPHINCS_PLUS_SHAKE_BASE) #include #endif -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) +#if defined(BOTAN_HAS_SPHINCS_PLUS_SHA2_BASE) #include #endif @@ -35,21 +35,21 @@ std::unique_ptr Sphincs_Hash_Functions::create(const Sph const SphincsPublicSeed& pub_seed) { switch(sphincs_params.hash_type()) { case Sphincs_Hash_Type::Sha256: -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) +#if defined(BOTAN_HAS_SPHINCS_PLUS_SHA2_BASE) return std::make_unique(sphincs_params, pub_seed); #else - throw Not_Implemented("SPHINCS+ with SHA-256 is not available in this build"); + throw Not_Implemented("SLH-DSA (or SPHINCS+) with SHA-256 is not available in this build"); #endif case Sphincs_Hash_Type::Shake256: -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) +#if defined(BOTAN_HAS_SPHINCS_PLUS_SHAKE_BASE) return std::make_unique(sphincs_params, pub_seed); #else - throw Not_Implemented("SPHINCS+ with SHAKE is not available in this build"); + throw Not_Implemented("SLH-DSA (or SPHINCS+) with SHAKE is not available in this build"); #endif case Sphincs_Hash_Type::Haraka: - throw Not_Implemented("Haraka is not yet implemented"); + throw Not_Implemented("Haraka is not implemented"); } BOTAN_ASSERT_UNREACHABLE(); } @@ -76,11 +76,11 @@ T from_first_n_bits(const uint32_t nbits, std::span bytes) { } // namespace std::tuple Sphincs_Hash_Functions::H_msg( - StrongSpan r, const SphincsTreeNode& root, std::span message) { + StrongSpan r, const SphincsTreeNode& root, const SphincsMessageInternal& message) { const auto digest = H_msg_digest(r, root, message); // The following calculates the message digest and indices from the - // raw message digest. See Algorithm 20 (spx_sign) in SPHINCS+ 3.1 + // raw message digest. See FIPS 205, Algorithm 19, Line 5-10. const auto& p = m_sphincs_params; BufferSlicer s(digest); auto msg_hash = s.copy(p.fors_message_bytes()); diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.h index 2d63916e8bd..f80998075f9 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hash.h @@ -1,5 +1,5 @@ /* - * SPHINCS+ Hashes + * SLH-DSA Hash Function Interface * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * @@ -17,8 +17,8 @@ namespace Botan { /** - * A collection of pseudorandom hash functions required for SPHINCS+ - * computations. + * A collection of pseudorandom hash functions required for SLH-DSA + * computations. See FIPS 205, Section 11.2.1 and 11.2.2. **/ class BOTAN_TEST_API Sphincs_Hash_Functions { public: @@ -34,9 +34,10 @@ class BOTAN_TEST_API Sphincs_Hash_Functions { static std::unique_ptr create(const Sphincs_Parameters& sphincs_params, const SphincsPublicSeed& pub_seed); - public: std::tuple H_msg( - StrongSpan r, const SphincsTreeNode& root, std::span message); + StrongSpan r, + const SphincsTreeNode& root, + const SphincsMessageInternal& message); /** * Using SK.PRF, the optional randomness, and a message, computes the message random R, @@ -48,9 +49,9 @@ class BOTAN_TEST_API Sphincs_Hash_Functions { * @param msg message */ virtual void PRF_msg(StrongSpan out, - const SphincsSecretPRF& sk_prf, - const SphincsOptionalRandomness& opt_rand, - std::span msg) = 0; + StrongSpan sk_prf, + StrongSpan opt_rand, + const SphincsMessageInternal& msg) = 0; template void T(std::span out, const Sphincs_Address& address, BufferTs&&... in) { @@ -83,7 +84,7 @@ class BOTAN_TEST_API Sphincs_Hash_Functions { * Prepare the underlying hash function for hashing any given input * depending on the expected input length. * - * @param address the SPHINCS+ address of the hash to be tweaked + * @param address the SLH-DSA address of the hash to be tweaked * @param input_length the input buffer length that will be processed * with the tweaked hash (typically N or 2*N) * @returns a reference to a Botan::HashFunction that is preconditioned @@ -96,7 +97,7 @@ class BOTAN_TEST_API Sphincs_Hash_Functions { virtual std::vector H_msg_digest(StrongSpan r, const SphincsTreeNode& root, - std::span message) = 0; + const SphincsMessageInternal& message) = 0; const Sphincs_Parameters& m_sphincs_params; const SphincsPublicSeed& m_pub_seed; diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.cpp index 049ee88d7d0..95aec651de4 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.cpp @@ -1,10 +1,10 @@ /* -* Sphincs+ hypertree logic -* (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) -**/ + * SLH-DSA's Hypertree Logic (FIPS 205, Section 7) + * (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 @@ -29,7 +29,7 @@ void ht_sign(StrongSpan out_sig, BufferStuffer ht_signature(out_sig); Sphincs_Address wots_addr(Sphincs_Address_Type::WotsHash); - wots_addr.set_tree(tree_index_in_layer).set_keypair(idx_leaf); + wots_addr.set_tree_address(tree_index_in_layer).set_keypair_address(idx_leaf); Sphincs_Address tree_addr(Sphincs_Address_Type::HashTree); @@ -38,8 +38,8 @@ void ht_sign(StrongSpan out_sig, // The first XMSS tree signs the message, the others their underlying XMSS tree root const SphincsTreeNode& node_to_xmss_sign = (layer_idx == 0U) ? message_to_sign : xmss_root; - tree_addr.set_layer(layer_idx).set_tree(tree_index_in_layer); - wots_addr.copy_subtree_from(tree_addr).set_keypair(idx_leaf); + tree_addr.set_layer_address(layer_idx).set_tree_address(tree_index_in_layer); + wots_addr.copy_subtree_from(tree_addr).set_keypair_address(idx_leaf); xmss_root = xmss_sign_and_pkgen(ht_signature.next(params.xmss_signature_bytes()), node_to_xmss_sign, @@ -79,11 +79,11 @@ bool ht_verify(const SphincsTreeNode& signed_msg, // The first XMSS tree signs the message, the others their underlying XMSS tree root const SphincsTreeNode& current_root = (layer_idx == 0U) ? signed_msg : reconstructed_root; - tree_addr.set_layer(layer_idx); - tree_addr.set_tree(tree_index_in_layer); + tree_addr.set_layer_address(layer_idx); + tree_addr.set_tree_address(tree_index_in_layer); wots_addr.copy_subtree_from(tree_addr); - wots_addr.set_keypair(idx_leaf); + wots_addr.set_keypair_address(idx_leaf); wots_pk_addr.copy_keypair_from(wots_addr); diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.h index 09f8a653573..68c06093fb0 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_hypertree.h @@ -1,12 +1,12 @@ /* - * Sphincs+ hypertree logic + * SLH-DSA's Hypertree Logic (FIPS 205, Section 7) * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * * 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_SP_HYPERTREE_H_ #define BOTAN_SP_HYPERTREE_H_ @@ -20,7 +20,9 @@ class Sphincs_Hash_Functions; class Sphincs_Parameters; /** - * Creates a SPHINCS+ XMSS hypertree signature of @p message_to_sign. The signature is written + * @brief FIPS 205, Algorithm 12: ht_sign + * + * Creates a SLH-DSA XMSS hypertree signature of @p message_to_sign. The signature is written * into the buffer defined by @p out_sig. @p tree_index_in_layer and @p idx_leaf define which * XMSS tree of the hypertree and which leaf of this XMSS tree is used for signing. */ @@ -33,7 +35,9 @@ void ht_sign(StrongSpan out_sig, Sphincs_Hash_Functions& hashes); /** - * Given a message @p signed_msg the SPHINCS+ XMSS hypertree is reconstructed + * @brief FIPS 205, Algorithm 13: ht_verify + * + * Given a message @p signed_msg the SLH-DSA XMSS hypertree is reconstructed * using a hypertree signature @p ht_sig. @p tree_index_in_layer and @p idx_leaf * define which XMSS tree of the hypertree and which leaf of this XMSS tree was * used for signing. diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp index 75954f7916a..2aa26899eb6 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp @@ -1,10 +1,10 @@ /* - * SphincsPlus Parameters + * SLH-DSA Parameters * (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 @@ -18,84 +18,251 @@ namespace Botan { namespace { +/// @returns pair: (parameter set, hash type) +std::pair set_and_hash_from_name(std::string_view name) { + // SPHINCS+ Round 3.1 instances + if(name == "SphincsPlus-sha2-128s-r3.1") { + return {Sphincs_Parameter_Set::Sphincs128Small, Sphincs_Hash_Type::Sha256}; + } + if(name == "SphincsPlus-sha2-128f-r3.1") { + return {Sphincs_Parameter_Set::Sphincs128Fast, Sphincs_Hash_Type::Sha256}; + } + if(name == "SphincsPlus-sha2-192s-r3.1") { + return {Sphincs_Parameter_Set::Sphincs192Small, Sphincs_Hash_Type::Sha256}; + } + if(name == "SphincsPlus-sha2-192f-r3.1") { + return {Sphincs_Parameter_Set::Sphincs192Fast, Sphincs_Hash_Type::Sha256}; + } + if(name == "SphincsPlus-sha2-256s-r3.1") { + return {Sphincs_Parameter_Set::Sphincs256Small, Sphincs_Hash_Type::Sha256}; + } + if(name == "SphincsPlus-sha2-256f-r3.1") { + return {Sphincs_Parameter_Set::Sphincs256Fast, Sphincs_Hash_Type::Sha256}; + } -Sphincs_Parameter_Set set_from_name(std::string_view name) { - if(name == "SphincsPlus-sha2-128s-r3.1" || name == "SphincsPlus-shake-128s-r3.1" || - name == "SphincsPlus-haraka-128s-r3.1") { - return Sphincs_Parameter_Set::Sphincs128Small; + if(name == "SphincsPlus-shake-128s-r3.1") { + return {Sphincs_Parameter_Set::Sphincs128Small, Sphincs_Hash_Type::Shake256}; + } + if(name == "SphincsPlus-shake-128f-r3.1") { + return {Sphincs_Parameter_Set::Sphincs128Fast, Sphincs_Hash_Type::Shake256}; } - if(name == "SphincsPlus-sha2-128f-r3.1" || name == "SphincsPlus-shake-128f-r3.1" || - name == "SphincsPlus-haraka-128f-r3.1") { - return Sphincs_Parameter_Set::Sphincs128Fast; + if(name == "SphincsPlus-shake-192s-r3.1") { + return {Sphincs_Parameter_Set::Sphincs192Small, Sphincs_Hash_Type::Shake256}; + } + if(name == "SphincsPlus-shake-192f-r3.1") { + return {Sphincs_Parameter_Set::Sphincs192Fast, Sphincs_Hash_Type::Shake256}; + } + if(name == "SphincsPlus-shake-256s-r3.1") { + return {Sphincs_Parameter_Set::Sphincs256Small, Sphincs_Hash_Type::Shake256}; + } + if(name == "SphincsPlus-shake-256f-r3.1") { + return {Sphincs_Parameter_Set::Sphincs256Fast, Sphincs_Hash_Type::Shake256}; } - if(name == "SphincsPlus-sha2-192s-r3.1" || name == "SphincsPlus-shake-192s-r3.1" || - name == "SphincsPlus-haraka-192s-r3.1") { - return Sphincs_Parameter_Set::Sphincs192Small; + if(name == "SphincsPlus-haraka-128s-r3.1") { + return {Sphincs_Parameter_Set::Sphincs128Small, Sphincs_Hash_Type::Haraka}; + } + if(name == "SphincsPlus-haraka-128f-r3.1") { + return {Sphincs_Parameter_Set::Sphincs128Fast, Sphincs_Hash_Type::Haraka}; + } + if(name == "SphincsPlus-haraka-192s-r3.1") { + return {Sphincs_Parameter_Set::Sphincs192Small, Sphincs_Hash_Type::Haraka}; + } + if(name == "SphincsPlus-haraka-192f-r3.1") { + return {Sphincs_Parameter_Set::Sphincs192Fast, Sphincs_Hash_Type::Haraka}; } - if(name == "SphincsPlus-sha2-192f-r3.1" || name == "SphincsPlus-shake-192f-r3.1" || - name == "SphincsPlus-haraka-192f-r3.1") { - return Sphincs_Parameter_Set::Sphincs192Fast; + if(name == "SphincsPlus-haraka-256s-r3.1") { + return {Sphincs_Parameter_Set::Sphincs256Small, Sphincs_Hash_Type::Haraka}; + } + if(name == "SphincsPlus-haraka-256f-r3.1") { + return {Sphincs_Parameter_Set::Sphincs256Fast, Sphincs_Hash_Type::Haraka}; } - if(name == "SphincsPlus-sha2-256s-r3.1" || name == "SphincsPlus-shake-256s-r3.1" || - name == "SphincsPlus-haraka-256s-r3.1") { - return Sphincs_Parameter_Set::Sphincs256Small; + // SLH-DSA instances WITHOUT prehash mode + if(name == "SLH-DSA-SHA2-128s") { + return {Sphincs_Parameter_Set::SLHDSA128Small, Sphincs_Hash_Type::Sha256}; + } + if(name == "SLH-DSA-SHA2-128f") { + return {Sphincs_Parameter_Set::SLHDSA128Fast, Sphincs_Hash_Type::Sha256}; + } + if(name == "SLH-DSA-SHA2-192s") { + return {Sphincs_Parameter_Set::SLHDSA192Small, Sphincs_Hash_Type::Sha256}; + } + if(name == "SLH-DSA-SHA2-192f") { + return {Sphincs_Parameter_Set::SLHDSA192Fast, Sphincs_Hash_Type::Sha256}; } - if(name == "SphincsPlus-sha2-256f-r3.1" || name == "SphincsPlus-shake-256f-r3.1" || - name == "SphincsPlus-haraka-256f-r3.1") { - return Sphincs_Parameter_Set::Sphincs256Fast; + if(name == "SLH-DSA-SHA2-256s") { + return {Sphincs_Parameter_Set::SLHDSA256Small, Sphincs_Hash_Type::Sha256}; + } + if(name == "SLH-DSA-SHA2-256f") { + return {Sphincs_Parameter_Set::SLHDSA256Fast, Sphincs_Hash_Type::Sha256}; } - throw Lookup_Error(fmt("No SphincsPlus parameter set found for: {}", name)); -} + if(name == "SLH-DSA-SHAKE-128s") { + return {Sphincs_Parameter_Set::SLHDSA128Small, Sphincs_Hash_Type::Shake256}; + } + if(name == "SLH-DSA-SHAKE-128f") { + return {Sphincs_Parameter_Set::SLHDSA128Fast, Sphincs_Hash_Type::Shake256}; + } + if(name == "SLH-DSA-SHAKE-192s") { + return {Sphincs_Parameter_Set::SLHDSA192Small, Sphincs_Hash_Type::Shake256}; + } + if(name == "SLH-DSA-SHAKE-192f") { + return {Sphincs_Parameter_Set::SLHDSA192Fast, Sphincs_Hash_Type::Shake256}; + } + if(name == "SLH-DSA-SHAKE-256s") { + return {Sphincs_Parameter_Set::SLHDSA256Small, Sphincs_Hash_Type::Shake256}; + } + if(name == "SLH-DSA-SHAKE-256f") { + return {Sphincs_Parameter_Set::SLHDSA256Fast, Sphincs_Hash_Type::Shake256}; + } -Sphincs_Hash_Type hash_from_name(std::string_view name) { - if(name == "SphincsPlus-sha2-128s-r3.1" || name == "SphincsPlus-sha2-128f-r3.1" || - name == "SphincsPlus-sha2-192s-r3.1" || name == "SphincsPlus-sha2-192f-r3.1" || - name == "SphincsPlus-sha2-256s-r3.1" || name == "SphincsPlus-sha2-256f-r3.1") { - return Sphincs_Hash_Type::Sha256; + // SLH-DSA instances WITH prehash mode + if(name == "Hash-SLH-DSA-SHA2-128s-with-SHA256") { + return {Sphincs_Parameter_Set::SLHDSA128Small, Sphincs_Hash_Type::Sha256}; + } + if(name == "Hash-SLH-DSA-SHA2-128f-with-SHA256") { + return {Sphincs_Parameter_Set::SLHDSA128Fast, Sphincs_Hash_Type::Sha256}; } - if(name == "SphincsPlus-shake-128s-r3.1" || name == "SphincsPlus-shake-128f-r3.1" || - name == "SphincsPlus-shake-192s-r3.1" || name == "SphincsPlus-shake-192f-r3.1" || - name == "SphincsPlus-shake-256s-r3.1" || name == "SphincsPlus-shake-256f-r3.1") { - return Sphincs_Hash_Type::Shake256; + if(name == "Hash-SLH-DSA-SHA2-192s-with-SHA512") { + return {Sphincs_Parameter_Set::SLHDSA192Small, Sphincs_Hash_Type::Sha256}; } - if(name == "SphincsPlus-haraka-128s-r3.1" || name == "SphincsPlus-haraka-128f-r3.1" || - name == "SphincsPlus-haraka-192s-r3.1" || name == "SphincsPlus-haraka-192f-r3.1" || - name == "SphincsPlus-haraka-256s-r3.1" || name == "SphincsPlus-haraka-256f-r3.1") { - return Sphincs_Hash_Type::Haraka; + if(name == "Hash-SLH-DSA-SHA2-192f-with-SHA512") { + return {Sphincs_Parameter_Set::SLHDSA192Fast, Sphincs_Hash_Type::Sha256}; + } + if(name == "Hash-SLH-DSA-SHA2-256s-with-SHA512") { + return {Sphincs_Parameter_Set::SLHDSA256Small, Sphincs_Hash_Type::Sha256}; + } + if(name == "Hash-SLH-DSA-SHA2-256f-with-SHA512") { + return {Sphincs_Parameter_Set::SLHDSA256Fast, Sphincs_Hash_Type::Sha256}; } - throw Lookup_Error(fmt("No SphincsPlus hash instantiation found for: {}", name)); + if(name == "Hash-SLH-DSA-SHAKE-128s-with-SHAKE128") { + return {Sphincs_Parameter_Set::SLHDSA128Small, Sphincs_Hash_Type::Shake256}; + } + if(name == "Hash-SLH-DSA-SHAKE-128f-with-SHAKE128") { + return {Sphincs_Parameter_Set::SLHDSA128Fast, Sphincs_Hash_Type::Shake256}; + } + if(name == "Hash-SLH-DSA-SHAKE-192s-with-SHAKE256") { + return {Sphincs_Parameter_Set::SLHDSA192Small, Sphincs_Hash_Type::Shake256}; + } + if(name == "Hash-SLH-DSA-SHAKE-192f-with-SHAKE256") { + return {Sphincs_Parameter_Set::SLHDSA192Fast, Sphincs_Hash_Type::Shake256}; + } + if(name == "Hash-SLH-DSA-SHAKE-256s-with-SHAKE256") { + return {Sphincs_Parameter_Set::SLHDSA256Small, Sphincs_Hash_Type::Shake256}; + } + if(name == "Hash-SLH-DSA-SHAKE-256f-with-SHAKE256") { + return {Sphincs_Parameter_Set::SLHDSA256Fast, Sphincs_Hash_Type::Shake256}; + } + + throw Lookup_Error(fmt("No SLH-DSA (or SPHINCS+) parameter supported for: {}", name)); } -const char* as_string(Sphincs_Hash_Type type) { - switch(type) { - case Sphincs_Hash_Type::Sha256: - return "sha2"; - case Sphincs_Hash_Type::Shake256: - return "shake"; - case Sphincs_Hash_Type::Haraka: - return "haraka"; +std::string name_from_set_and_hash(Sphincs_Parameter_Set set, Sphincs_Hash_Type hash) { + if(hash == Sphincs_Hash_Type::Sha256) { + switch(set) { + case Sphincs_Parameter_Set::Sphincs128Small: + return "SphincsPlus-sha2-128s-r3.1"; + case Sphincs_Parameter_Set::Sphincs128Fast: + return "SphincsPlus-sha2-128f-r3.1"; + case Sphincs_Parameter_Set::Sphincs192Small: + return "SphincsPlus-sha2-192s-r3.1"; + case Sphincs_Parameter_Set::Sphincs192Fast: + return "SphincsPlus-sha2-192f-r3.1"; + case Sphincs_Parameter_Set::Sphincs256Small: + return "SphincsPlus-sha2-256s-r3.1"; + case Sphincs_Parameter_Set::Sphincs256Fast: + return "SphincsPlus-sha2-256f-r3.1"; + + case Sphincs_Parameter_Set::SLHDSA128Small: + return "SLH-DSA-SHA2-128s"; + case Sphincs_Parameter_Set::SLHDSA128Fast: + return "SLH-DSA-SHA2-128f"; + case Sphincs_Parameter_Set::SLHDSA192Small: + return "SLH-DSA-SHA2-192s"; + case Sphincs_Parameter_Set::SLHDSA192Fast: + return "SLH-DSA-SHA2-192f"; + case Sphincs_Parameter_Set::SLHDSA256Small: + return "SLH-DSA-SHA2-256s"; + case Sphincs_Parameter_Set::SLHDSA256Fast: + return "SLH-DSA-SHA2-256f"; + } } - BOTAN_ASSERT_UNREACHABLE(); + + if(hash == Sphincs_Hash_Type::Shake256) { + switch(set) { + case Sphincs_Parameter_Set::Sphincs128Small: + return "SphincsPlus-shake-128s-r3.1"; + case Sphincs_Parameter_Set::Sphincs128Fast: + return "SphincsPlus-shake-128f-r3.1"; + case Sphincs_Parameter_Set::Sphincs192Small: + return "SphincsPlus-shake-192s-r3.1"; + case Sphincs_Parameter_Set::Sphincs192Fast: + return "SphincsPlus-shake-192f-r3.1"; + case Sphincs_Parameter_Set::Sphincs256Small: + return "SphincsPlus-shake-256s-r3.1"; + case Sphincs_Parameter_Set::Sphincs256Fast: + return "SphincsPlus-shake-256f-r3.1"; + + case Sphincs_Parameter_Set::SLHDSA128Small: + return "SLH-DSA-SHAKE-128s"; + case Sphincs_Parameter_Set::SLHDSA128Fast: + return "SLH-DSA-SHAKE-128f"; + case Sphincs_Parameter_Set::SLHDSA192Small: + return "SLH-DSA-SHAKE-192s"; + case Sphincs_Parameter_Set::SLHDSA192Fast: + return "SLH-DSA-SHAKE-192f"; + case Sphincs_Parameter_Set::SLHDSA256Small: + return "SLH-DSA-SHAKE-256s"; + case Sphincs_Parameter_Set::SLHDSA256Fast: + return "SLH-DSA-SHAKE-256f"; + } + } + + if(hash == Sphincs_Hash_Type::Haraka) { + switch(set) { + case Sphincs_Parameter_Set::Sphincs128Small: + return "SphincsPlus-haraka-128s-r3.1"; + case Sphincs_Parameter_Set::Sphincs128Fast: + return "SphincsPlus-haraka-128f-r3.1"; + case Sphincs_Parameter_Set::Sphincs192Small: + return "SphincsPlus-haraka-192s-r3.1"; + case Sphincs_Parameter_Set::Sphincs192Fast: + return "SphincsPlus-haraka-192f-r3.1"; + case Sphincs_Parameter_Set::Sphincs256Small: + return "SphincsPlus-haraka-256s-r3.1"; + case Sphincs_Parameter_Set::Sphincs256Fast: + return "SphincsPlus-haraka-256f-r3.1"; + + case Sphincs_Parameter_Set::SLHDSA128Small: + case Sphincs_Parameter_Set::SLHDSA128Fast: + case Sphincs_Parameter_Set::SLHDSA192Small: + case Sphincs_Parameter_Set::SLHDSA192Fast: + case Sphincs_Parameter_Set::SLHDSA256Small: + case Sphincs_Parameter_Set::SLHDSA256Fast: + throw Invalid_Argument("SLH-DSA does not support Haraka"); + } + } + throw Invalid_Argument("Cannot serialize invalid parameter combination"); } -const char* as_string(Sphincs_Parameter_Set set) { +constexpr bool is_slh_dsa_set(Sphincs_Parameter_Set set) { switch(set) { + case Sphincs_Parameter_Set::SLHDSA128Small: + case Sphincs_Parameter_Set::SLHDSA128Fast: + case Sphincs_Parameter_Set::SLHDSA192Small: + case Sphincs_Parameter_Set::SLHDSA192Fast: + case Sphincs_Parameter_Set::SLHDSA256Small: + case Sphincs_Parameter_Set::SLHDSA256Fast: + return true; case Sphincs_Parameter_Set::Sphincs128Small: - return "128s-r3.1"; case Sphincs_Parameter_Set::Sphincs128Fast: - return "128f-r3.1"; case Sphincs_Parameter_Set::Sphincs192Small: - return "192s-r3.1"; case Sphincs_Parameter_Set::Sphincs192Fast: - return "192f-r3.1"; case Sphincs_Parameter_Set::Sphincs256Small: - return "256s-r3.1"; case Sphincs_Parameter_Set::Sphincs256Fast: - return "256f-r3.1"; + return false; } BOTAN_ASSERT_UNREACHABLE(); } @@ -112,31 +279,33 @@ Sphincs_Parameters::Sphincs_Parameters(Sphincs_Parameter_Set set, uint32_t w, uint32_t bitsec) : m_set(set), m_hash_type(hash_type), m_n(n), m_h(h), m_d(d), m_a(a), m_k(k), m_w(w), m_bitsec(bitsec) { + BOTAN_ARG_CHECK(!(hash_type == Sphincs_Hash_Type::Haraka && is_slh_dsa_set(set)), + "Haraka is not available for SLH-DSA"); BOTAN_ARG_CHECK(w == 4 || w == 16 || w == 256, "Winternitz parameter must be one of 4, 16, 256"); BOTAN_ARG_CHECK(n == 16 || n == 24 || n == 32, "n must be one of 16, 24, 32"); BOTAN_ARG_CHECK(m_d > 0, "d must be greater than zero"); m_xmss_tree_height = m_h / m_d; - m_log_w = ceil_log2(m_w); + m_lg_w = ceil_log2(m_w); - // base_w algorithm (as described in Sphincs+ 3.1 Section 2.5) only works + // base_2^b algorithm (Fips 205, Algorithm 4) only works // when m_log_w is a divisor of 8. - BOTAN_ASSERT_NOMSG(m_log_w <= 8 && 8 % m_log_w == 0); + BOTAN_ASSERT_NOMSG(m_lg_w <= 8 && 8 % m_lg_w == 0); - // # Winternitz blocks of the message - m_wots_len1 = (m_n * 8) / m_log_w; + // # Winternitz blocks of the message (len_1 of FIPS 205, Algorithm 1) + m_wots_len1 = (m_n * 8) / m_lg_w; - // # Winternitz blocks of the checksum - m_wots_len2 = ceil_log2(m_wots_len1 * (m_w - 1)) / m_log_w + 1; + // # Winternitz blocks of the checksum (output of FIPS 205 Algorithm 1) + m_wots_len2 = ceil_log2(m_wots_len1 * (m_w - 1)) / m_lg_w + 1; - // # Winternitz blocks in the signature + // # Winternitz blocks in the signature (len of FIPS 205, Equation 5.4) m_wots_len = m_wots_len1 + m_wots_len2; // byte length of WOTS+ signature as well as public key m_wots_bytes = m_wots_len * m_n; // # of bytes the WOTS+ checksum consists of - m_wots_checksum_bytes = ceil_tobytes(m_wots_len2 * m_log_w); + m_wots_checksum_bytes = ceil_tobytes(m_wots_len2 * m_lg_w); m_fors_sig_bytes = (m_a + 1) * m_k * m_n; @@ -152,29 +321,66 @@ Sphincs_Parameters::Sphincs_Parameters(Sphincs_Parameter_Set set, m_h_msg_digest_bytes = m_fors_message_bytes + m_tree_digest_bytes + m_leaf_digest_bytes; } -Sphincs_Parameters Sphincs_Parameters::create(Sphincs_Parameter_Set set, Sphincs_Hash_Type hash) { - // See "Table 3" in SPHINCS+ specification (NIST R3.1 submission, page 39) +bool Sphincs_Parameters::is_available() const { + [[maybe_unused]] const bool is_slh_dsa = is_slh_dsa_set(m_set); +#ifdef BOTAN_HAS_SLH_DSA_WITH_SHA2 + if(is_slh_dsa && m_hash_type == Sphincs_Hash_Type::Sha256) { + return true; + } +#endif +#ifdef BOTAN_HAS_SLH_DSA_WITH_SHAKE + if(is_slh_dsa && m_hash_type == Sphincs_Hash_Type::Shake256) { + return true; + } +#endif +#ifdef BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2 + if(!is_slh_dsa && m_hash_type == Sphincs_Hash_Type::Sha256) { + return true; + } +#endif +#ifdef BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE + if(!is_slh_dsa && m_hash_type == Sphincs_Hash_Type::Shake256) { + return true; + } +#endif + return false; +} + +Sphincs_Parameters Sphincs_Parameters::create( + Sphincs_Parameter_Set set, Sphincs_Hash_Type hash /*, SlhDsaInputMode input_mode [TODO: prehash mode]*/) { + // See FIPS 205, Table 2 switch(set) { case Sphincs_Parameter_Set::Sphincs128Small: + case Sphincs_Parameter_Set::SLHDSA128Small: return Sphincs_Parameters(set, hash, 16, 63, 7, 12, 14, 16, 133); case Sphincs_Parameter_Set::Sphincs128Fast: + case Sphincs_Parameter_Set::SLHDSA128Fast: return Sphincs_Parameters(set, hash, 16, 66, 22, 6, 33, 16, 128); case Sphincs_Parameter_Set::Sphincs192Small: + case Sphincs_Parameter_Set::SLHDSA192Small: return Sphincs_Parameters(set, hash, 24, 63, 7, 14, 17, 16, 193); case Sphincs_Parameter_Set::Sphincs192Fast: + case Sphincs_Parameter_Set::SLHDSA192Fast: return Sphincs_Parameters(set, hash, 24, 66, 22, 8, 33, 16, 194); case Sphincs_Parameter_Set::Sphincs256Small: + case Sphincs_Parameter_Set::SLHDSA256Small: return Sphincs_Parameters(set, hash, 32, 64, 8, 14, 22, 16, 255); case Sphincs_Parameter_Set::Sphincs256Fast: + case Sphincs_Parameter_Set::SLHDSA256Fast: return Sphincs_Parameters(set, hash, 32, 68, 17, 9, 35, 16, 255); } BOTAN_ASSERT_UNREACHABLE(); } Sphincs_Parameters Sphincs_Parameters::create(std::string_view name) { - return Sphincs_Parameters::create(set_from_name(name), hash_from_name(name)); + auto [param_set, hash_type] = set_and_hash_from_name(name); + return Sphincs_Parameters::create(param_set, hash_type); +} + +bool Sphincs_Parameters::is_slh_dsa() const { + return is_slh_dsa_set(m_set); } std::string Sphincs_Parameters::hash_name() const { @@ -190,7 +396,7 @@ std::string Sphincs_Parameters::hash_name() const { } std::string Sphincs_Parameters::to_string() const { - return fmt("SphincsPlus-{}-{}", as_string(m_hash_type), as_string(m_set)); + return name_from_set_and_hash(parameter_set(), hash_type()); } Sphincs_Parameters Sphincs_Parameters::create(const OID& oid) { diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.h index e48461da2e2..5cfd1b17b84 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.h @@ -1,10 +1,10 @@ /* - * SPHINCS+ Parameters + * SLH-DSA Parameters * (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) - **/ + */ #ifndef BOTAN_SP_PARAMS_H_ #define BOTAN_SP_PARAMS_H_ @@ -18,7 +18,7 @@ namespace Botan { enum class Sphincs_Hash_Type { Shake256, Sha256, - Haraka, ///< Haraka is currently not supported + Haraka BOTAN_DEPRECATED("Haraka is not and will not be supported"), ///< Haraka is currently not supported }; enum class Sphincs_Parameter_Set { @@ -28,11 +28,18 @@ enum class Sphincs_Parameter_Set { Sphincs192Fast, Sphincs256Small, Sphincs256Fast, + + SLHDSA128Small, + SLHDSA128Fast, + SLHDSA192Small, + SLHDSA192Fast, + SLHDSA256Small, + SLHDSA256Fast, }; /** - * Container for all SPHINCS+ parameters defined by a specific instance (see - * Table 3 of Sphincs+ round 3.1 spec). Also contains getters for various + * Container for all SLH-DSA parameters defined by a specific instance (see + * FIPS 205, Table 2). Also contains getters for various * parameters that are derived from the given parameters. */ class BOTAN_PUBLIC_API(3, 1) Sphincs_Parameters final { @@ -41,6 +48,13 @@ class BOTAN_PUBLIC_API(3, 1) Sphincs_Parameters final { static Sphincs_Parameters create(std::string_view name); static Sphincs_Parameters create(const OID& oid); + /** + * @returns true iff the given parameter set and hash combination is available + * in this build. Note that parameter sets can only be used if this function + * evaluates to true. + */ + bool is_available() const; + /** * @returns the OID of the algorithm specified by those parameters */ @@ -61,6 +75,11 @@ class BOTAN_PUBLIC_API(3, 1) Sphincs_Parameters final { */ Sphincs_Parameter_Set parameter_set() const { return m_set; } + /** + * @returns true for SLH-DSA parameter sets. False for SPHINCS+ Round 3.1 parameter sets. + */ + bool is_slh_dsa() const; + /** * @returns a string representation of this parameter set */ @@ -72,17 +91,17 @@ class BOTAN_PUBLIC_API(3, 1) Sphincs_Parameters final { std::string hash_name() const; /** - * @returns SPHINCS+ security parameter in bytes + * @returns SLH-DSA security parameter in bytes */ size_t n() const { return m_n; } /** - * @returns Height of the SPHINCS+ hypertree + * @returns Height of the SLH-DSA hypertree */ uint32_t h() const { return m_h; } /** - * @returns Number of XMSS layers in the SPHINCS+ hypertree + * @returns Number of XMSS layers in the SLH-DSA hypertree */ uint32_t d() const { return m_d; } @@ -128,7 +147,7 @@ class BOTAN_PUBLIC_API(3, 1) Sphincs_Parameters final { /** * @returns the base 2 logarithm of the Winternitz parameter for WOTS+ signatures */ - uint32_t log_w() const { return m_log_w; } + uint32_t log_w() const { return m_lg_w; } /** * @returns the len1 parameter for WOTS+ signatures @@ -166,7 +185,7 @@ class BOTAN_PUBLIC_API(3, 1) Sphincs_Parameters final { uint32_t fors_message_bytes() const { return m_fors_message_bytes; } /** - * @returns the byte length of a Sphincs+ signature + * @returns the byte length of a SLH-DSA signature */ uint32_t sphincs_signature_bytes() const { return m_sp_sig_bytes; } @@ -192,7 +211,7 @@ class BOTAN_PUBLIC_API(3, 1) Sphincs_Parameters final { /** * @returns the byte length of the output of H_msg. Corresponds to - * m in the specification of H_msg in Section 7.2 + * 'm' of FIPS 205, Table 2. */ uint32_t h_msg_digest_bytes() const { return m_h_msg_digest_bytes; } @@ -217,7 +236,7 @@ class BOTAN_PUBLIC_API(3, 1) Sphincs_Parameters final { uint32_t m_k; uint32_t m_w; uint32_t m_bitsec; - uint32_t m_log_w; + uint32_t m_lg_w; uint32_t m_wots_len1; uint32_t m_wots_len2; uint32_t m_wots_len; diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp index 94e7c91ee94..1a64418140d 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp @@ -1,5 +1,5 @@ /* -* Sphincs+ treehash logic +* SLH-DSA treehash logic * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.h index a5f4867d678..b68e5455600 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.h @@ -1,5 +1,5 @@ /* - * Sphincs+ treehash logic + * SLH-DSA treehash logic * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * @@ -29,7 +29,7 @@ using GenerateLeafFunction = std::function /* l * signatures. * @p gen_leaf is used to create leaf nodes in the respective trees. * Additionally XMSS uses the gen_leaf logic to store the WOTS Signature in the - * main Sphincs+ signature. The @p leaf_idx is the index of leaf to sign. If + * main SLH-DSA signature. The @p leaf_idx is the index of leaf to sign. If * only the root node must be computed (without a signature), the @p leaf_idx is * set to std::nullopt. */ @@ -46,7 +46,7 @@ BOTAN_TEST_API void treehash(StrongSpan out_root, /** * Using a leaf node and the authentication path (neighbor nodes on the way from * leaf to root), computes the the root node of the respective tree. This - * function is generic and used by FORS and XMSS in the SPHINCS+ verification + * function is generic and used by FORS and XMSS in the SLH-DSA verification * logic. */ BOTAN_TEST_API void compute_root(StrongSpan out, diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_types.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_types.h index cd61390ab1c..37abf89f01b 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_types.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_types.h @@ -1,5 +1,5 @@ /* - * SPHINCS+ Strong Type Definitions + * SLH-DSA Strong Type Definitions * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * @@ -16,18 +16,18 @@ namespace Botan { /* * The following gives an overview about the different building blocks of - * Sphincs+ and how they are connected. In general, we always consider sequences of bytes + * SLH-DSA and how they are connected. In general, we always consider sequences of bytes * that are interpreted in the following manner (flattening the || operation, i.e., * mapping the byte sequence of a strong type onto the underlying byte sequence of the containing strong type). * Only FORS indices are not seen as byte sequences. * - * Sphincs+ secret key is built up like the following: - * [SphincsSecretSeed || SphincsSecretPRF || SphincsPublicSeed || SphincsTreeNode] (the last chunk is the root node of Sphincs+' topmost XMSS tree) + * SLH-DSA secret key is built up like the following: + * [SphincsSecretSeed || SphincsSecretPRF || SphincsPublicSeed || SphincsTreeNode] (the last chunk is the root node of SLH-DSA' topmost XMSS tree) * - * Sphincs+ public key is built up like the following: - * [SphincsPublicSeed || SphincsTreeNode] (the last chunk is the root node of Sphincs+' topmost XMSS tree)] + * SLH-DSA public key is built up like the following: + * [SphincsPublicSeed || SphincsTreeNode] (the last chunk is the root node of SLH-DSA's topmost XMSS tree)] * - * Sphincs+ signature is built up like the following: + * SLH-DSA signature is built up like the following: * [SphincsMessageRandomness (n bytes) || ForsSignature (k(a+1)*n = fors_signature_bytes bytes) || SphincsHypertreeSignature]. SphincsHypertreeSignature contains a total of * d SphincsXMSSSignatures, with (h+d*len)*n = xmss_signature_bytes bytes each. * @@ -42,6 +42,20 @@ namespace Botan { * [WotsNode || ... || WotsNode] contains len WotsNodes, each of length n bytes. */ +/// The prefix appended to the message in [hash_]slh_sign and slh_verify. +/// E.g. for SLH-DSA (pure): 0x00 || |ctx| || ctx. Empty for SPHINCS+. +using SphincsMessagePrefix = Strong, struct SphincsMessagePrefix_>; +// The input to [hash_]slh_sign and [hash_]slh_verify +using SphincsInputMessage = Strong, struct SphincsInputMessage_>; + +/// M' representation of FIPS 205 (the input to slh_sign_internal and slh_verify_internal) +struct SphincsMessageInternal { + SphincsMessagePrefix prefix; + SphincsInputMessage message; +}; + +using SphincsContext = Strong, struct SphincsContext_>; + using SphincsHashedMessage = Strong, struct SphincsHashedMessage_>; using SphincsPublicSeed = Strong, struct SphincsPublicSeed_>; using SphincsSecretSeed = Strong, struct SphincsSecretSeed_>; diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp index 2f05f1002ef..be35e81bad8 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp @@ -1,5 +1,5 @@ /* -* WOTS+ - Winternitz One Time Signature+ +* WOTS+ - Winternitz One Time Signature Plus Scheme (FIPS 205, Section 5) * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * @@ -15,32 +15,35 @@ namespace Botan { namespace { /** + * @brief FIPS 205, Algorithm 5: chain + * * Computes a WOTS+ hash chain for @p steps steps beginning with value * @p wots_chain_key at index @p start. */ -void gen_chain(StrongSpan out, - StrongSpan wots_chain_key, - WotsHashIndex start, - uint8_t steps, - Sphincs_Address& addr, - Sphincs_Hash_Functions& hashes, - const Sphincs_Parameters& params) { +void chain(StrongSpan out, + StrongSpan wots_chain_key, + WotsHashIndex start, + uint8_t steps, + Sphincs_Address& addr, + Sphincs_Hash_Functions& hashes, + const Sphincs_Parameters& params) { // Initialize out with the value at position 'start'. std::copy(wots_chain_key.begin(), wots_chain_key.end(), out.begin()); // Iterate 'steps' calls to the hash function. for(WotsHashIndex i = start; i < (start + steps) && i < params.w(); i++) { - addr.set_hash(i); + addr.set_hash_address(i); hashes.T(out, addr, out); } } /** - * base_w algorithm as described in draft. - * Interprets an array of bytes as integers in base w. - * This only works when log_w is a divisor of 8. + * FIPS 205, Algorithm 4: base_2^b for WOTS+ + * + * Interprets an array of bytes as integers in base w = 2^b. + * This only works when lg_w is a divisor of 8. */ -void base_w(std::span output, std::span input, const Sphincs_Parameters& params) { +void base_2_b(std::span output, std::span input, const Sphincs_Parameters& params) { BOTAN_ASSERT_NOMSG(output.size() <= 8 * input.size() / params.log_w()); size_t input_offset = 0; @@ -58,7 +61,10 @@ void base_w(std::span output, std::span input, con } } -/** Computes the WOTS+ checksum over a message (in base_w). */ +/** + * Computes the WOTS+ checksum over a message (in base_2^b). + * Corresponds to FIPS 205, Algorithm 7 or 8, Step 7. + */ void wots_checksum(std::span output, std::span msg_base_w, const Sphincs_Parameters& params) { @@ -77,7 +83,7 @@ void wots_checksum(std::span output, const size_t csum_bytes_size = params.wots_checksum_bytes(); BOTAN_ASSERT_NOMSG(csum_bytes.size() >= csum_bytes_size); - base_w(output, std::span(csum_bytes).last(csum_bytes_size), params); + base_2_b(output, std::span(csum_bytes).last(csum_bytes_size), params); } } // namespace @@ -88,7 +94,7 @@ std::vector chain_lengths(const SphincsTreeNode& msg, const Sphin auto msg_base_w = std::span(result).first(params.wots_len_1()); auto checksum_base_w = std::span(result).last(params.wots_len_2()); - base_w(msg_base_w, msg.get(), params); + base_2_b(msg_base_w, msg.get(), params); wots_checksum(checksum_base_w, msg_base_w, params); return result; @@ -105,19 +111,19 @@ WotsPublicKey wots_public_key_from_signature(const SphincsTreeNode& hashed_messa BufferStuffer pk(pk_buffer); for(WotsChainIndex i(0); i < params.wots_len(); i++) { - address.set_chain(i); + address.set_chain_address(i); // params.w() can be one of {4, 8, 256} const WotsHashIndex start_index = lengths[i.get()]; const uint8_t steps_to_take = static_cast(params.w() - 1) - start_index.get(); - gen_chain(pk.next(params.n()), - sig.take(params.n()), - start_index, - steps_to_take, - address, - hashes, - params); + chain(pk.next(params.n()), + sig.take(params.n()), + start_index, + steps_to_take, + address, + hashes, + params); } return pk_buffer; @@ -143,8 +149,8 @@ void wots_sign_and_pkgen(StrongSpan sig_out, BufferStuffer wots_pk(wots_pk_buffer); BufferStuffer sig(sig_out); - leaf_addr.set_keypair(leaf_idx); - pk_addr.set_keypair(leaf_idx); + leaf_addr.set_keypair_address(leaf_idx); + pk_addr.set_keypair_address(leaf_idx); for(WotsChainIndex i(0); i < params.wots_len(); i++) { // If the current leaf is part of the signature wots_k stores the chain index @@ -158,8 +164,8 @@ void wots_sign_and_pkgen(StrongSpan sig_out, }(); // Start with the secret seed - leaf_addr.set_chain(i); - leaf_addr.set_hash(WotsHashIndex(0)); + leaf_addr.set_chain_address(i); + leaf_addr.set_hash_address(WotsHashIndex(0)); leaf_addr.set_type(Sphincs_Address_Type::WotsKeyGeneration); auto buffer_s = wots_pk.next(params.n()); @@ -181,7 +187,7 @@ void wots_sign_and_pkgen(StrongSpan sig_out, } // Iterate one step on the chain - leaf_addr.set_hash(k); + leaf_addr.set_hash_address(k); hashes.T(buffer_s, leaf_addr, buffer_s); } diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.h index 7d43337021c..3c483cda740 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.h @@ -1,5 +1,5 @@ /* - * WOTS+ - Winternitz One Time Signature+ + * SLH-DSA's WOTS+ - Winternitz One Time Signature Plus Scheme (FIPS 205, Section 5) * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * @@ -21,8 +21,10 @@ class Sphincs_Hash_Functions; class Sphincs_Parameters; /** + * @brief FIPS 205, Algorithm 6 and 7: wots_pkGen and wots_sign + * * Implements a domain specific wrapper for the one-time signature scheme WOTS+ - * (Winternitz OTS). It is meant to be used inside SPHINCS+ and does not aim to + * (Winternitz OTS). It is meant to be used inside SLH-DSA and does not aim to * be applicable for other use cases. If this function is not used in a signing * operation (i.e. @p sign_leaf_idx is not set), @p wots_steps may be empty. */ @@ -36,10 +38,13 @@ BOTAN_TEST_API void wots_sign_and_pkgen(StrongSpan sig_out, Sphincs_Address& pk_addr, const Sphincs_Parameters& params, Sphincs_Hash_Functions& hashes); + /** + * @brief FIPS 205, Algorithm 8: wots_pkFromSig + * * Reconstructs the WOTS public key from a given WOTS @p signature and - * @p message. This is tailored for the use case in the SPHINCS+ implementation - * and is not meant for general usability. + * @p message. This is tailored for the use case in the SLH-DSA implementation + * and is not meant for general usability in non SLH-DSA algorithms. */ BOTAN_TEST_API WotsPublicKey wots_public_key_from_signature(const SphincsTreeNode& hashed_message, StrongSpan signature, @@ -49,6 +54,8 @@ BOTAN_TEST_API WotsPublicKey wots_public_key_from_signature(const SphincsTreeNod /** * Given a @p msg construct the lengths (amount of hashes for signature) for each WOTS+ chain, including the checksum. + * + * Corresponds to FIPS 205, Algorithm 7 or 8, Step 1-7 */ BOTAN_TEST_API std::vector chain_lengths(const SphincsTreeNode& msg, const Sphincs_Parameters& params); diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.cpp index 2a5baaa5c37..b6c1d20ff0a 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.cpp @@ -1,10 +1,10 @@ /* -* Sphincs+ XMSS logic +* SLH-DSA's XMSS - eXtended Merkle Signature Scheme (FIPS 205, Section 6) * (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 @@ -17,7 +17,7 @@ namespace Botan { SphincsTreeNode xmss_sign_and_pkgen(StrongSpan out_sig, - const SphincsTreeNode& root, + const SphincsTreeNode& message, const SphincsSecretSeed& secret_seed, Sphincs_Address& wots_addr, Sphincs_Address& tree_addr, @@ -32,7 +32,7 @@ SphincsTreeNode xmss_sign_and_pkgen(StrongSpan out_sig, // if `idx_leaf` is not set, we don't want to calculate a signature and // therefore won't need to bother preparing the chain lengths either. if(idx_leaf.has_value()) { - return chain_lengths(root, params); + return chain_lengths(message, params); } else { return {}; }; @@ -67,8 +67,8 @@ SphincsTreeNode xmss_gen_root(const Sphincs_Parameters& params, Sphincs_Address top_tree_addr(Sphincs_Address_Type::HashTree); Sphincs_Address wots_addr(Sphincs_Address_Type::WotsPublicKeyCompression); - top_tree_addr.set_layer(HypertreeLayerIndex(params.d() - 1)); - wots_addr.set_layer(HypertreeLayerIndex(params.d() - 1)); + top_tree_addr.set_layer_address(HypertreeLayerIndex(params.d() - 1)); + wots_addr.set_layer_address(HypertreeLayerIndex(params.d() - 1)); SphincsTreeNode root = xmss_sign_and_pkgen(dummy_sig, dummy_root, secret_seed, wots_addr, top_tree_addr, std::nullopt, params, hashes); diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.h index 425fb66078f..fe21d7b74ac 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_xmss.h @@ -1,12 +1,12 @@ /* - * Sphincs+ XMSS logic + * SLH-DSA's XMSS - eXtended Merkle Signature Scheme (FIPS 205, Section 6) * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * * 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_SP_XMSS_H_ #define BOTAN_SP_XMSS_H_ @@ -22,14 +22,19 @@ class Sphincs_Hash_Functions; class Sphincs_Parameters; /** -* This generates a Merkle signature of @p root. The Merkle authentication path logic -* is mostly hidden in treehash_spec. The WOTS signature followed by the Merkle -* authentication path are stored in @p out_sig, the new root of the Merkle tree -* is stored in @p out_root. Set @p idx_leaf to `std::nullopt` if no signature is -* desired. -*/ + * @brief FIPS 205, Algorithm 10: xmss_sign + * + * This generates a Merkle signature of @p message (i.e. a FORS public key + * (bottom layer) or an XMSS root node). The Merkle authentication path logic + * is mostly hidden in treehash_spec. The WOTS signature followed by the Merkle + * authentication path are stored in @p out_sig. + * Set @p idx_leaf to `std::nullopt` if no signature is + * desired. + * + * @returns the XMSS public key (i.e. the root of the XMSS merkle tree) + */ SphincsTreeNode xmss_sign_and_pkgen(StrongSpan out_sig, - const SphincsTreeNode& root, + const SphincsTreeNode& message, const SphincsSecretSeed& secret_seed, Sphincs_Address& wots_addr, Sphincs_Address& tree_addr, @@ -37,7 +42,10 @@ SphincsTreeNode xmss_sign_and_pkgen(StrongSpan out_sig, const Sphincs_Parameters& params, Sphincs_Hash_Functions& hashes); -/* Compute root node of the top-most subtree. */ +/** + * Compute the XMSS public key (root node) of the top-most subtree. + * Contains logic of FIPS 205, Algorithm 18: slh_keygen_internal + */ SphincsTreeNode xmss_gen_root(const Sphincs_Parameters& params, const SphincsSecretSeed& secret_seed, Sphincs_Hash_Functions& hashes); diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp index fe8dc57d065..13e335a82c7 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp @@ -1,5 +1,5 @@ /* -* Sphincs+ +* SLH-DSA - Stateless Hash-Based Digital Signature Standard - FIPS 205 * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -19,14 +20,47 @@ #include #include -#if !defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) and !defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) +#include + +#if !defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) and !defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) and \ + !defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) and !defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE) static_assert( false, - "botan module 'sphincsplus_common' is useful only when enabling at least 'sphincsplus_sha2' or 'sphincsplus_shake'"); + "botan module 'sphincsplus_common' is useful only when enabling at least 'sphincsplus_sha2', 'sphincsplus_shake', 'slh_dsa_sha2', or 'slh_dsa_shake'"); #endif namespace Botan { +namespace { +// FIPS 205, Algorithm 22, line 8 +SphincsMessageInternal prepare_message(SphincsInputMessage msg, + const Sphincs_Parameters& params, + StrongSpan context) { + BOTAN_ARG_CHECK(params.is_slh_dsa() || context.empty(), "Context is not supported for SPHINCS+"); +#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE) + if(params.is_slh_dsa()) { + // prefix (no pre-hash mode): input mode byte + |ctx| + ctx + const uint8_t input_mode_byte = 0x00; // Pure (TODO: pre-hash mode: 0x01) + return { + .prefix = concat( + store_be(input_mode_byte), store_be(checked_cast_to(context.size())), context), + .message = std::move(msg), + }; + } +#endif +#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) + if(!params.is_slh_dsa()) { + // SPHINCS+ Round 3.1 uses the message without any prefix + return { + .prefix = {}, // SPHINCS+ has no prefix + .message = std::move(msg), + }; + } +#endif + throw Internal_Error("Missing message preparation logic for SLH-DSA or SPHINCS+"); +} +} // namespace + class SphincsPlus_PublicKeyInternal final { public: SphincsPlus_PublicKeyInternal(Sphincs_Parameters params, @@ -36,7 +70,7 @@ class SphincsPlus_PublicKeyInternal final { SphincsPlus_PublicKeyInternal(Sphincs_Parameters params, std::span key_bits) : m_params(params) { if(key_bits.size() != m_params.public_key_bytes()) { - throw Decoding_Error("Sphincs Public Key doesn't have the expected length"); + throw Decoding_Error("SLH-DSA (or SPHINCS+) Public Key doesn't have the expected length"); } BufferSlicer s(key_bits); @@ -67,7 +101,7 @@ class SphincsPlus_PrivateKeyInternal final { SphincsPlus_PrivateKeyInternal(const Sphincs_Parameters& params, std::span key_bits) { if(key_bits.size() != params.private_key_bytes() - params.public_key_bytes()) { - throw Decoding_Error("Sphincs Private Key doesn't have the expected length"); + throw Decoding_Error("SLH-DSA (or SPHINCS+) Private Key doesn't have the expected length"); } BufferSlicer s(key_bits); @@ -91,13 +125,16 @@ class SphincsPlus_PrivateKeyInternal final { SphincsPlus_PublicKey::SphincsPlus_PublicKey(std::span pub_key, Sphincs_Parameter_Set type, Sphincs_Hash_Type hash) : - m_public(std::make_shared(Sphincs_Parameters::create(type, hash), pub_key)) {} + SphincsPlus_PublicKey(pub_key, Sphincs_Parameters::create(type, hash)) {} SphincsPlus_PublicKey::SphincsPlus_PublicKey(std::span pub_key, Sphincs_Parameters params) : - m_public(std::make_shared(params, pub_key)) {} + m_public(std::make_shared(params, pub_key)) { + BOTAN_ARG_CHECK(params.is_available(), + "The selected parameter-set-hash combination is not activated in this build."); +} SphincsPlus_PublicKey::SphincsPlus_PublicKey(const AlgorithmIdentifier& alg_id, std::span key_bits) : - m_public(std::make_shared(Sphincs_Parameters::create(alg_id.oid()), key_bits)) {} + SphincsPlus_PublicKey(key_bits, Sphincs_Parameters::create(alg_id.oid())) {} SphincsPlus_PublicKey::~SphincsPlus_PublicKey() = default; @@ -105,6 +142,10 @@ size_t SphincsPlus_PublicKey::key_length() const { return m_public->parameters().n() * 8; } +std::string SphincsPlus_PublicKey::algo_name() const { + return m_public->parameters().is_slh_dsa() ? "SLH-DSA" : "SPHINCS+"; +} + size_t SphincsPlus_PublicKey::estimated_strength() const { return m_public->parameters().bitsec(); } @@ -128,7 +169,7 @@ std::vector SphincsPlus_PublicKey::raw_public_key_bits() const { std::vector SphincsPlus_PublicKey::public_key_bits() const { // Currently, there isn't a finalized definition of an ASN.1 structure for - // SPHINCS+ aka SLH-DSA public keys. Therefore, we return the raw public key bits. + // SLH-DSA or SPHINCS+ public keys. Therefore, we return the raw public key bits. return raw_public_key_bits(); } @@ -140,36 +181,48 @@ class SphincsPlus_Verification_Operation final : public PK_Ops::Verification { public: SphincsPlus_Verification_Operation(std::shared_ptr pub_key) : m_public(std::move(pub_key)), - m_hashes(Botan::Sphincs_Hash_Functions::create(m_public->parameters(), m_public->seed())) {} + m_hashes(Botan::Sphincs_Hash_Functions::create(m_public->parameters(), m_public->seed())), + m_context(/* TODO: Add API */ {}) { + BOTAN_ARG_CHECK(m_context.size() <= 255, "Context must not exceed 255 bytes"); + BOTAN_ARG_CHECK(m_public->parameters().is_available(), + "The selected SLH-DSA (or SPHINCS+) instance is not available in this build."); + } /** * Add more data to the message currently being signed * @param msg the message */ void update(std::span msg) override { - m_msg_buffer.insert(m_msg_buffer.end(), msg.begin(), msg.end()); + // TODO(For Pre-Hash Mode): We need to stream the message into a hash function. + m_msg_buffer.get().insert(m_msg_buffer.end(), msg.begin(), msg.end()); } - /* - * Perform a verification operation - */ + /** + * Perform a verification operation + */ bool is_valid_signature(std::span sig) override { + const auto internal_msg = prepare_message(std::exchange(m_msg_buffer, {}), m_public->parameters(), m_context); + return slh_verify_internal(internal_msg, sig); + } + + std::string hash_function() const override { return m_hashes->msg_hash_function_name(); } + + private: + /// FIPS 205, Algorithm 20 + bool slh_verify_internal(const SphincsMessageInternal& msg, std::span sig) { const auto& p = m_public->parameters(); if(sig.size() != p.sphincs_signature_bytes()) { - m_msg_buffer.clear(); return false; } BufferSlicer s(sig); // Compute leaf and tree index from R const auto msg_random_s = s.take(p.n()); - auto [mhash, tree_idx, leaf_idx] = m_hashes->H_msg(msg_random_s, m_public->root(), m_msg_buffer); - // Clear the message buffer, the data is not needed anymore - m_msg_buffer.clear(); + auto [mhash, tree_idx, leaf_idx] = m_hashes->H_msg(msg_random_s, m_public->root(), msg); // Reconstruct the FORS tree Sphincs_Address fors_addr(Sphincs_Address_Type::ForsTree); - fors_addr.set_tree(tree_idx).set_keypair(leaf_idx); + fors_addr.set_tree_address(tree_idx).set_keypair_address(leaf_idx); const auto fors_sig_s = s.take(p.fors_signature_bytes()); auto fors_root = fors_public_key_from_signature(mhash, fors_sig_s, fors_addr, p, *m_hashes); @@ -179,12 +232,10 @@ class SphincsPlus_Verification_Operation final : public PK_Ops::Verification { return ht_verify(fors_root, ht_sig_s, m_public->root(), tree_idx, leaf_idx, p, *m_hashes); } - std::string hash_function() const override { return m_hashes->msg_hash_function_name(); } - - private: std::shared_ptr m_public; std::unique_ptr m_hashes; - std::vector m_msg_buffer; + SphincsInputMessage m_msg_buffer; + SphincsContext m_context; }; std::unique_ptr SphincsPlus_PublicKey::create_verification_op(std::string_view /*params*/, @@ -199,7 +250,7 @@ std::unique_ptr SphincsPlus_PublicKey::create_x509_verific 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 SPHINCS+ signature"); + throw Decoding_Error("Unexpected AlgorithmIdentifier for SLH-DSA (or SPHINCS+) signature"); } return std::make_unique(m_public); } @@ -238,6 +289,8 @@ SphincsPlus_PrivateKey::SphincsPlus_PrivateKey(const AlgorithmIdentifier& alg_id SphincsPlus_PrivateKey::SphincsPlus_PrivateKey(std::span private_key, Sphincs_Parameters params) : SphincsPlus_PublicKey(slice_off_public_key(params.object_identifier(), private_key), params) { + BOTAN_ARG_CHECK(params.is_available(), + "The selected parameter-set-hash combination is not activated in this build."); const auto private_portion_bytes = params.private_key_bytes() - params.public_key_bytes(); BOTAN_ASSERT_NOMSG(private_key.size() >= private_portion_bytes); @@ -249,7 +302,10 @@ SphincsPlus_PrivateKey::SphincsPlus_PrivateKey(RandomNumberGenerator& rng, Sphincs_Hash_Type hash) : SphincsPlus_PrivateKey(rng, Sphincs_Parameters::create(type, hash)) {} +// FIPS 205, Algorithm 21 SphincsPlus_PrivateKey::SphincsPlus_PrivateKey(RandomNumberGenerator& rng, Sphincs_Parameters params) { + BOTAN_ARG_CHECK(params.is_available(), + "The selected parameter-set-hash combination is not activated in this build."); auto sk_seed = rng.random_vec(params.n()); auto sk_prf = rng.random_vec(params.n()); @@ -284,35 +340,59 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { m_private(std::move(private_key)), m_public(std::move(public_key)), m_hashes(Botan::Sphincs_Hash_Functions::create(m_public->parameters(), m_public->seed())), - m_randomized(randomized) {} + m_randomized(randomized), + m_context(/* TODO: add API for context */ {}) { + BOTAN_ARG_CHECK(m_context.size() <= 255, "Context must not exceed 255 bytes"); + BOTAN_ARG_CHECK(m_public->parameters().is_available(), + "The selected SLH-DSA (or SPHINCS+) instance is not available in this build."); + } void update(std::span msg) override { - m_msg_buffer.insert(m_msg_buffer.end(), msg.begin(), msg.end()); + // TODO(For Pre-Hash Mode): We need to stream the message into a hash function. + m_msg_buffer.get().insert(m_msg_buffer.end(), msg.begin(), msg.end()); } std::vector sign(RandomNumberGenerator& rng) override { + std::optional addrnd = std::nullopt; + if(m_randomized) { + addrnd = rng.random_vec(m_public->parameters().n()); + } + auto internal_msg = prepare_message(std::exchange(m_msg_buffer, {}), m_public->parameters(), m_context); + + return slh_sign_internal(internal_msg, addrnd); + } + + size_t signature_length() const override { return m_public->parameters().sphincs_signature_bytes(); } + + AlgorithmIdentifier algorithm_identifier() const override { + return m_public->parameters().algorithm_identifier(); + } + + std::string hash_function() const override { return m_hashes->msg_hash_function_name(); } + + private: + // FIPS 205, Algorithm 19 + std::vector slh_sign_internal(const SphincsMessageInternal& message, + std::optional> addrnd) { const auto& p = m_public->parameters(); std::vector sphincs_sig_buffer(p.sphincs_signature_bytes()); BufferStuffer sphincs_sig(sphincs_sig_buffer); // Compute and append the digest randomization value (R of spec). - SphincsOptionalRandomness opt_rand(m_public->seed()); - if(m_randomized) { - opt_rand = rng.random_vec(p.n()); - } + // Use addrng for the randomized variant. Use the public seed for the deterministic one. + const auto opt_rand = + (addrnd.has_value()) ? addrnd.value() : StrongSpan(m_public->seed()); + auto msg_random_s = sphincs_sig.next(p.n()); - m_hashes->PRF_msg(msg_random_s, m_private->prf(), opt_rand, m_msg_buffer); + m_hashes->PRF_msg(msg_random_s, m_private->prf(), opt_rand, message); // Derive the message digest and leaf index from R, PK and M. - auto [mhash, tree_idx, leaf_idx] = m_hashes->H_msg(msg_random_s, m_public->root(), m_msg_buffer); - - // Clear the message buffer, the data is not needed anymore - m_msg_buffer.clear(); + auto [mhash, tree_idx, leaf_idx] = m_hashes->H_msg(msg_random_s, m_public->root(), message); // Compute and append the FORS signature Sphincs_Address fors_addr(Sphincs_Address_Type::ForsTree); - fors_addr.set_tree(tree_idx).set_keypair(leaf_idx); + fors_addr.set_tree_address(tree_idx).set_keypair_address(leaf_idx); auto fors_root = fors_sign_and_pkgen(sphincs_sig.next(p.fors_signature_bytes()), mhash, m_private->seed(), @@ -333,20 +413,12 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { return sphincs_sig_buffer; } - size_t signature_length() const override { return m_public->parameters().sphincs_signature_bytes(); } - - AlgorithmIdentifier algorithm_identifier() const override { - return m_public->parameters().algorithm_identifier(); - } - - std::string hash_function() const override { return m_hashes->msg_hash_function_name(); } - - private: std::shared_ptr m_private; std::shared_ptr m_public; std::unique_ptr m_hashes; - std::vector m_msg_buffer; + SphincsInputMessage m_msg_buffer; bool m_randomized; + SphincsContext m_context; }; std::unique_ptr SphincsPlus_PrivateKey::create_signature_op(RandomNumberGenerator& rng, @@ -354,7 +426,7 @@ std::unique_ptr SphincsPlus_PrivateKey::create_signature_op(R std::string_view provider) const { BOTAN_UNUSED(rng); BOTAN_ARG_CHECK(params.empty() || params == "Deterministic" || params == "Randomized", - "Unexpected parameters for signing with SPHINCS+"); + "Unexpected parameters for signing with SLH-DSA (or SPHINCS+)"); const bool randomized = (params == "Randomized"); if(provider.empty() || provider == "base") { diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h index d570831567a..906c6491063 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h @@ -1,6 +1,6 @@ /* - * SPHINCS+ Hash based signature scheme - * Based on the creative commons (CC0 1.0) reference implementation by the + * SLH-DSA - Stateless Hash-Based Digital Signature Standard - FIPS 205 + * Based on the creative commons (CC0 1.0) SPHINCS+ reference implementation by the * designers (https://github.com/sphincs/sphincsplus/) * * (C) 2023 Jack Lloyd @@ -24,11 +24,9 @@ class SphincsPlus_PublicKeyInternal; class SphincsPlus_PrivateKeyInternal; /** - * This implementation is based on - * https://github.com/sphincs/sphincsplus/commit/06f42f47491085ac879a72b486ca8edb10891963 + * @brief An SLH-DSA (or SPHINCS+ Round 3.1) public key. * - * which implements SPHINCS+ Specification Round 3.1 (https://sphincs.org/data/sphincs+-r3.1-specification.pdf). - * The used tweaked hashes are implemented according to the variant 'simple' ('robust' is not supported). + * For more information see the documentation of SphincsPlus_PrivateKey. */ class BOTAN_PUBLIC_API(3, 1) SphincsPlus_PublicKey : public virtual Public_Key { public: @@ -40,7 +38,7 @@ class BOTAN_PUBLIC_API(3, 1) SphincsPlus_PublicKey : public virtual Public_Key { size_t key_length() const override; - std::string algo_name() const override { return "SPHINCS+"; } + std::string algo_name() const override; size_t estimated_strength() const override; AlgorithmIdentifier algorithm_identifier() const override; @@ -68,6 +66,27 @@ class BOTAN_PUBLIC_API(3, 1) SphincsPlus_PublicKey : public virtual Public_Key { BOTAN_DIAGNOSTIC_PUSH BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE +/** + * @brief An SLH-DSA private key. + * + * This class represents an SLH-DSA private key (or a SPHINCS+ Round 3.1 private key). + * Supported are all parameter sets defined in FIPS 205, Section 11. Parameter + * sets are specified using the Sphincs_Parameter_Set and + * Sphincs_Hash_Type enums, for example SLH-DSA-SHA2-128s is defined as + * Sphincs_Parameter_Set::SLHDSA128Small and Sphincs_Hash_Type::Sha256. + * + * For legacy usage of SPHINCS+ Round 3 (not recommended), the parameter sets + * Sphincs128Small, ..., Sphincs256Fast are used. + * + * Note that the parameter sets denoted as 'small' optimize for signature size + * at the expense of signing speed, whereas 'fast' trades larger signatures for + * faster signing speeds. + * + * This implementation is based on the SPHINCS+ + * https://github.com/sphincs/sphincsplus/commit/06f42f47491085ac879a72b486ca8edb10891963 + * which implements SPHINCS+ Specification Round 3.1 (https://sphincs.org/data/sphincs+-r3.1-specification.pdf). + * The used tweaked hashes are implemented according to the variant 'simple' ('robust' is not supported). + */ class BOTAN_PUBLIC_API(3, 1) SphincsPlus_PrivateKey final : public virtual SphincsPlus_PublicKey, public virtual Private_Key { public: diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/info.txt b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/info.txt new file mode 100644 index 00000000000..78b9864c19b --- /dev/null +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/info.txt @@ -0,0 +1,21 @@ + +SPHINCS_PLUS_SHA2_BASE -> 20240807 + + + +name -> "SLH-DSA/SPHINCS+ (SHA-256) Base" +brief -> "Base module for SLH-DSA and SPHINCS+ with SHA-2" +type -> "Internal" + + + +sha2_32 +sha2_64 +mgf1 +hmac +trunc_hash + + + +sp_hash_sha2.h + diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_sha2/sp_hash_sha2.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/sp_hash_sha2.h similarity index 88% rename from src/lib/pubkey/sphincsplus/sphincsplus_sha2/sp_hash_sha2.h rename to src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/sp_hash_sha2.h index bd143a77fd1..b8f2bcd8e47 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_sha2/sp_hash_sha2.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_sha2_base/sp_hash_sha2.h @@ -1,5 +1,5 @@ /* - * SPHINCS+ Hash Implementation for SHA-256 + * SLH-DSA Hash Implementation for SHA-256 * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * @@ -21,7 +21,7 @@ namespace Botan { /** - * Implementation of SPHINCS+ hash function abstraction for SHA2 + * Implementation of SLH-DSA hash function abstraction for SHA2 */ class Sphincs_Hash_Functions_Sha2 : public Sphincs_Hash_Functions { private: @@ -44,11 +44,12 @@ class Sphincs_Hash_Functions_Sha2 : public Sphincs_Hash_Functions { std::vector H_msg_digest(StrongSpan r, const SphincsTreeNode& root, - std::span message) override { + const SphincsMessageInternal& message) override { m_sha_x_full->update(r); m_sha_x_full->update(m_pub_seed); m_sha_x_full->update(root); - m_sha_x_full->update(message); + m_sha_x_full->update(message.prefix); + m_sha_x_full->update(message.message); auto r_pk_buffer = m_sha_x_full->final(); std::vector mgf1_input = concat>(r, m_pub_seed, r_pk_buffer); @@ -88,13 +89,14 @@ class Sphincs_Hash_Functions_Sha2 : public Sphincs_Hash_Functions { } void PRF_msg(StrongSpan out, - const SphincsSecretPRF& sk_prf, - const SphincsOptionalRandomness& opt_rand, - std::span in) override { + StrongSpan sk_prf, + StrongSpan opt_rand, + const SphincsMessageInternal& msg) override { HMAC hmac_sha_x(m_sha_x_full->new_object()); hmac_sha_x.set_key(sk_prf); hmac_sha_x.update(opt_rand); - hmac_sha_x.update(in); + hmac_sha_x.update(msg.prefix); + hmac_sha_x.update(msg.message); const auto prf = hmac_sha_x.final(); std::copy(prf.begin(), prf.begin() + out.size(), out.begin()); diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/info.txt b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/info.txt new file mode 100644 index 00000000000..6552c588bfa --- /dev/null +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/info.txt @@ -0,0 +1,17 @@ + +SPHINCS_PLUS_SHAKE_BASE -> 20240809 + + + +name -> "SLH-DSA/SPHINCS+ (SHAKE) Base" +brief -> "Base module for SLH-DSA and SPHINCS+ with SHAKE" +type -> "Internal" + + + +shake + + + +sp_hash_shake.h + diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_shake/sp_hash_shake.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/sp_hash_shake.h similarity index 75% rename from src/lib/pubkey/sphincsplus/sphincsplus_shake/sp_hash_shake.h rename to src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/sp_hash_shake.h index 6aa0ce5ce00..2d74f0685b4 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_shake/sp_hash_shake.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus_shake_base/sp_hash_shake.h @@ -1,5 +1,5 @@ /* - * SPHINCS+ Hash Implementation for SHA-256 + * SLH-DSA Hash Implementation for SHA-256 * (C) 2023 Jack Lloyd * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity * @@ -16,7 +16,7 @@ namespace Botan { /** - * Implementation of SPHINCS+ hash function abstraction for SHAKE256 + * Implementation of SLH-DSA hash function abstraction for SHAKE256 */ class Sphincs_Hash_Functions_Shake : public Sphincs_Hash_Functions { private: @@ -29,11 +29,12 @@ class Sphincs_Hash_Functions_Shake : public Sphincs_Hash_Functions { std::vector H_msg_digest(StrongSpan r, const SphincsTreeNode& root, - std::span message) override { + const SphincsMessageInternal& message) override { m_h_msg_hash.update(r); m_h_msg_hash.update(m_pub_seed); m_h_msg_hash.update(root); - m_h_msg_hash.update(message); + m_h_msg_hash.update(message.prefix); + m_h_msg_hash.update(message.message); return m_h_msg_hash.final_stdvec(); } @@ -48,12 +49,13 @@ class Sphincs_Hash_Functions_Shake : public Sphincs_Hash_Functions { } void PRF_msg(StrongSpan out, - const SphincsSecretPRF& sk_prf, - const SphincsOptionalRandomness& opt_rand, - std::span in) override { + StrongSpan sk_prf, + StrongSpan opt_rand, + const SphincsMessageInternal& msg) override { m_hash.update(sk_prf); m_hash.update(opt_rand); - m_hash.update(in); + m_hash.update(msg.prefix); + m_hash.update(msg.message); m_hash.final(out); } diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_sha2/info.txt b/src/lib/pubkey/sphincsplus/sphincsplus_sha2/info.txt index 9252a17f1a8..a0b66de0cf5 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_sha2/info.txt +++ b/src/lib/pubkey/sphincsplus/sphincsplus_sha2/info.txt @@ -7,14 +7,5 @@ name -> "SPHINCS+ (SHA-256)" -sphincsplus_common -sha2_32 -sha2_64 -mgf1 -hmac -trunc_hash +sphincsplus_sha2_base - - -sp_hash_sha2.h - diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_shake/info.txt b/src/lib/pubkey/sphincsplus/sphincsplus_shake/info.txt index 7276bb2f36f..f315ac8a25a 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_shake/info.txt +++ b/src/lib/pubkey/sphincsplus/sphincsplus_shake/info.txt @@ -7,10 +7,5 @@ name -> "SPHINCS+ (SHAKE-256)" -sphincsplus_common -shake +sphincsplus_shake_base - - -sp_hash_shake.h - diff --git a/src/lib/x509/x509_obj.cpp b/src/lib/x509/x509_obj.cpp index 90f3958ebd2..8068dc8a40c 100644 --- a/src/lib/x509/x509_obj.cpp +++ b/src/lib/x509/x509_obj.cpp @@ -170,11 +170,8 @@ std::string x509_signature_padding_for(const std::string& algo_name, return user_specified_padding.empty() ? "Pure" : std::string(user_specified_padding); } else if(algo_name.starts_with("Dilithium-") || algo_name == "ML-DSA") { return user_specified_padding.empty() ? "Randomized" : std::string(user_specified_padding); - } else if(algo_name == "XMSS") { - // XMSS does not take any padding, but if the user insists, we pass it along - return std::string(user_specified_padding); - } else if(algo_name == "HSS-LMS") { - // HSS-LMS does not take any padding, but if the user insists, we pass it along + } else if(algo_name == "XMSS" || algo_name == "HSS-LMS" || algo_name == "SLH-DSA") { + // These algorithms do not take any padding, but if the user insists, we pass it along return std::string(user_specified_padding); } else { throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name); diff --git a/src/python/botan3.py b/src/python/botan3.py index dab19feaf03..2ea33e46ed2 100755 --- a/src/python/botan3.py +++ b/src/python/botan3.py @@ -361,6 +361,8 @@ def ffi_api(fn, args, allowed_errors=None): ffi_api(dll.botan_pubkey_x448_get_pubkey, [c_void_p, c_char_p]) ffi_api(dll.botan_privkey_load_ml_dsa, [c_void_p, c_void_p, c_int, c_char_p]) ffi_api(dll.botan_pubkey_load_ml_dsa, [c_void_p, c_void_p, c_int, c_char_p]) + ffi_api(dll.botan_privkey_load_slh_dsa, [c_void_p, c_void_p, c_int, c_char_p]) + ffi_api(dll.botan_pubkey_load_slh_dsa, [c_void_p, c_void_p, c_int, c_char_p]) ffi_api(dll.botan_privkey_load_kyber, [c_void_p, c_char_p, c_int]) ffi_api(dll.botan_pubkey_load_kyber, [c_void_p, c_char_p, c_int]) ffi_api(dll.botan_privkey_view_kyber_raw_key, [c_void_p, c_void_p, VIEW_BIN_CALLBACK]) @@ -1250,6 +1252,12 @@ def load_ml_dsa(cls, mldsa_mode, key): _DLL.botan_pubkey_load_ml_dsa(byref(obj), key, len(key), _ctype_str(mldsa_mode)) return PublicKey(obj) + @classmethod + def load_slh_dsa(cls, slhdsa_mode, key): + obj = c_void_p(0) + _DLL.botan_pubkey_load_slh_dsa(byref(obj), key, len(key), _ctype_str(slhdsa_mode)) + return PublicKey(obj) + @classmethod def load_frodokem(cls, frodo_mode, key): obj = c_void_p(0) @@ -1427,6 +1435,12 @@ def load_ml_dsa(cls, mldsa_mode, key): _DLL.botan_privkey_load_ml_dsa(byref(obj), key, len(key), _ctype_str(mldsa_mode)) return PrivateKey(obj) + @classmethod + def load_slh_dsa(cls, slh_dsa, key): + obj = c_void_p(0) + _DLL.botan_privkey_load_slh_dsa(byref(obj), key, len(key), _ctype_str(slh_dsa)) + return PrivateKey(obj) + @classmethod def load_frodokem(cls, frodo_mode, key): obj = c_void_p(0) diff --git a/src/scripts/ci_build.py b/src/scripts/ci_build.py index b51dc24c7c9..3db3eed5351 100755 --- a/src/scripts/ci_build.py +++ b/src/scripts/ci_build.py @@ -272,7 +272,7 @@ def sanitize_kv(some_string): 'ffi_dh', 'ffi_dsa', 'ffi_elgamal', 'frodo_kat_tests', 'hash_nist_mc', 'hss_lms_keygen', 'hss_lms_sign', 'mce_keygen', 'passhash9', 'pbkdf', 'pcurves_arith', 'pwdhash', 'rsa_encrypt', 'rsa_pss', 'rsa_pss_raw', 'scrypt', - 'sphincsplus', 'sphincsplus_fors', 'sphincsplus_keygen', 'srp6_kat', + 'sphincsplus', 'sphincsplus_fors', 'slh_dsa_keygen', 'slh_dsa', 'srp6_kat', 'srp6_rt', 'unit_tls', 'x509_path_bsi', 'x509_path_rsa_pss', 'xmss_keygen', 'xmss_keygen_reference', 'xmss_sign', 'xmss_unit_tests', 'xmss_verify', 'xmss_verify_invalid', diff --git a/src/scripts/dev_tools/gen_sphincsplus_kat.py b/src/scripts/dev_tools/gen_sphincsplus_kat.py index 6616664accd..5a415fe1f20 100755 --- a/src/scripts/dev_tools/gen_sphincsplus_kat.py +++ b/src/scripts/dev_tools/gen_sphincsplus_kat.py @@ -83,7 +83,7 @@ def main(args = None): reader = KatReader(open(katfile, encoding="utf-8")) - hash_fn = sha256 if "sha2" in param else sha3_256 + hash_fn = sha256 if "sha2" in param.lower() else sha3_256 l = 0 for kat in reader.read_kats(): diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py index ffa2d2e412d..e3f5bac09f3 100755 --- a/src/scripts/test_cli.py +++ b/src/scripts/test_cli.py @@ -1623,7 +1623,7 @@ def cli_speed_pk_tests(_tmp_dir): pk_algos = ["ECDSA", "ECDH", "SM2", "ECKCDSA", "ECGDSA", "GOST-34.10", "DH", "DSA", "ElGamal", "Ed25519", "Ed448", "X25519", "X448", - "RSA", "RSA_keygen", "XMSS", "Kyber", "Dilithium", "SPHINCS+"] + "RSA", "RSA_keygen", "XMSS", "Kyber", "Dilithium", "SLH-DSA"] output = test_cli("speed", ["--msec=%d" % (msec)] + pk_algos, None).split('\n') diff --git a/src/scripts/test_python.py b/src/scripts/test_python.py index 5956f1aa442..33262f10556 100644 --- a/src/scripts/test_python.py +++ b/src/scripts/test_python.py @@ -965,6 +965,20 @@ def test_ml_dsa_raw_keys(self): self.assertEqual(sk_read.to_raw(), sk_bits) self.assertEqual(pk_read.to_raw(), pk_bits) + def test_slh_dsa_raw_keys(self): + slhdsa_mode = "SLH-DSA-SHAKE-128f" + sk = botan.PrivateKey.create("SLH-DSA", slhdsa_mode, botan.RandomNumberGenerator("user")) + pk = sk.get_public_key() + + sk_bits = sk.to_raw() + pk_bits = pk.to_raw() + + sk_read = botan.PrivateKey.load_slh_dsa(slhdsa_mode, sk_bits) + pk_read = botan.PublicKey.load_slh_dsa(slhdsa_mode, pk_bits) + + self.assertEqual(sk_read.to_raw(), sk_bits) + self.assertEqual(pk_read.to_raw(), pk_bits) + def test_frodokem_raw_keys(self): frodo_mode = "FrodoKEM-640-SHAKE" sk = botan.PrivateKey.create("FrodoKEM", frodo_mode, botan.RandomNumberGenerator("user")) diff --git a/src/tests/data/pubkey/api_sign.vec b/src/tests/data/pubkey/api_sign.vec index a524e11e043..cd3d1f22acc 100644 --- a/src/tests/data/pubkey/api_sign.vec +++ b/src/tests/data/pubkey/api_sign.vec @@ -54,9 +54,9 @@ SigParams = ALICE123@YAHOO.COM,SM3 AlgoParams = secp256r1 SigParams = ALICE123@YAHOO.COM,Raw -[SPHINCS+] +[SLH-DSA] -AlgoParams = SphincsPlus-shake-128s-r3.1 +AlgoParams = SLH-DSA-SHA2-128f SigParams = [XMSS] diff --git a/src/tests/data/pubkey/slh_dsa.vec b/src/tests/data/pubkey/slh_dsa.vec new file mode 100644 index 00000000000..9b099545843 --- /dev/null +++ b/src/tests/data/pubkey/slh_dsa.vec @@ -0,0 +1,101 @@ +# This test data is generated from the SLH_DSA implementation of https://github.com/mjosaarinen/py-acvp-pqc/tree/main +# See src/scripts/dev_tools/gen_pqc_dsa_kats.py +# To save space the signature is hashed with the hash function used in SPHINCS+ +# (i.e. either SHA-3 or SHA-256). + +SphincsParameterSet = SLH-DSA-SHA2-128s +seed = 23F1C88BD0E65F2C891CE865BD3275A7FFDBE4F9036E75B96AAD0412650E9CD2EA4591D4AB7131EA971B34C23A4ED245 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = 5EF56915BB461280CD7A1428BF89ECFBACE44265B801314A0D20A6006C9FF1E6 +sk = F696484048EC21F96CF50A56D0759C448F3779752F0383D37449690694CF7A685EF56915BB461280CD7A1428BF89ECFBACE44265B801314A0D20A6006C9FF1E6 +HashSigDet = 34F54432D5AF7ACC187BB6221ED7EA6863A6EEDDDA016849C1CA8B7506251003 +HashSigRand = 1C1DDB4F6EBF48CB706C765FB598CDC22C7F8543BC183FC58F230160773F7AAA + +SphincsParameterSet = SLH-DSA-SHAKE-128s +seed = 6FB79C668EB6E0F65A09135CDD243CE205D7065A0924F86024ADCD111F654A6DFE2E398A12369064C2A217229113E570 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = F08E01A57A272B738FA8EA2F465D59400B96B2F9934DAA0DA7C9873550013599 +sk = 6DE62E3465A55C9C78A07D265BE8540B3E58B0801A124D07FF12B438D5202EA0F08E01A57A272B738FA8EA2F465D59400B96B2F9934DAA0DA7C9873550013599 +HashSigDet = 3F74A7A28E32DA3BE820E7FFA9344814A3227BDFBFDEBEC60DC883B0A9F141B6 +HashSigRand = F9180D932F295F34FD4C421D5BBE9199D003D6B080BFA63BAFEDA8127BE029F4 + +SphincsParameterSet = SLH-DSA-SHA2-128f +seed = AD4D2D306639620AFCB3753A137F89331836A5ADCE1C40BEBE77B82D43A40D3249000C7AA3786406D5D5FA65462D1A57 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = 67B4319D2F67BAE52055DCD010A25A16220B1DDABBED69F503B5F07CBBA7F134 +sk = 1EAAE6BB91B27CD748C402C4111140D5A942CF3C95FF7977F88D2EF515BB26D067B4319D2F67BAE52055DCD010A25A16220B1DDABBED69F503B5F07CBBA7F134 +HashSigDet = A19E2A5AE585EC140DC8C6910847C1E67EA2A96E6707E96F3F5646C23393ED1E +HashSigRand = 210C514552736ED214130EE31982DF2BE25FBBE25FA3465EDC9096AA6D6D0037 + +SphincsParameterSet = SLH-DSA-SHAKE-128f +seed = 950684CB360FF0C011FD02A09A273C9F023D0A34AE0AA338C6F5283634AE9BEF5D100BCD48F6577E5910BEAAFE6C52C3 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = A27E9866875FD15DFB375E035774304B8C608A901A6194334B70B7C8D4E2BF82 +sk = B585D4EB01085111A172A87688D0032E3381A9E9A35FDD6EF2F8AEB3B40EB5CEA27E9866875FD15DFB375E035774304B8C608A901A6194334B70B7C8D4E2BF82 +HashSigDet = 0C63B2FE04DA937129A6649B87AB6E3BCDCB3C587D1B25567B13624D75BB4449 +HashSigRand = 56A9105B8772FA6719BE5331506F200872AB61FDDD54C3C698FC53613EFEDCFC + +SphincsParameterSet = SLH-DSA-SHA2-192s +seed = 0633CCD19471803E0CEFFC7A11B2E7B70C3329DFDDE5BE18160DF0D4C609900D67154893921376CC4B4B8C586E3EFE79 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = C9C02F34DAF335681F00668A6B4209CAAD8F0A98AFD3CB6A10AC13A54B552951257A36F005A60E3F1D4FC605C6C69CA8 +sk = A9F93C7B791356B66AFCCEB745A548C7F6B185E4F45EC1FF1A22ACDD96E7A6D8D290F4ECB0E97B42C65CE45583DA8AD8C9C02F34DAF335681F00668A6B4209CAAD8F0A98AFD3CB6A10AC13A54B552951257A36F005A60E3F1D4FC605C6C69CA8 +HashSigDet = 36A7CE4B9FAAB952245E1218098B0A8C5FC93FC672146CA49DB0C84444C3664F +HashSigRand = 902880A875BE16D2957838CC4AA45951F2B68B07F4A52034F1CF15D88ECF2266 + +SphincsParameterSet = SLH-DSA-SHAKE-192s +seed = 2605426643CAB04B2801416B3239CD046DFE7BA59874A3A05C5B32A4505F21D8B7217B5ECC8646FC6A845211AE4BC708 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = 311E8EADB8A321C1BF80F61581DF0DA196F0F694B5C335BCDF767CDAD5DAB7542ECB362183467BD0FC5B334A7D615F61 +sk = 85DA83B47129711A63C2C2F6A5BCB701237B2B0B66814EEC9FCC1C560992A5962A826338E5C2F03BDEA549E2BCCD8463311E8EADB8A321C1BF80F61581DF0DA196F0F694B5C335BCDF767CDAD5DAB7542ECB362183467BD0FC5B334A7D615F61 +HashSigDet = 2BC141EE0A76E1AD588FCA079A036A0B5B629D651020AEF36EA16AFDF55C2F2D +HashSigRand = E01FE5D4ACA562FA07EEA7995F150B933ED5ED5446DD6D70E67F6755DC64E0A8 + +SphincsParameterSet = SLH-DSA-SHA2-192f +seed = D3880D1B7802B4A3277687863C5A248BEFC95D3E4748403F3BB61F83ABCDDEAE42AE6C8624812FD38F463122D99D59A9 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = F04CA9D2E28AFCA52EA1C8178445CAD8C8F4EFC9D99F4753B1170FF7DF5359A4277F8EF072A833EE58C420A436928774 +sk = F7ECFC9143EE45E44F5E98FD9CA1455340EC5DB4FB098534365EBBFBCC57D34DDE0999AA0C3992D155B9D6BE993CB9D7F04CA9D2E28AFCA52EA1C8178445CAD8C8F4EFC9D99F4753B1170FF7DF5359A4277F8EF072A833EE58C420A436928774 +HashSigDet = 1B8CB853E473AE803E5CB46B4A1002DA48BD35EA7CEB40A3E81F5887F5934941 +HashSigRand = EC603A2DEF5A114F8836BDF29B175CD1379AF303FABC4F76E9249FA0A89DABE8 + +SphincsParameterSet = SLH-DSA-SHAKE-192f +seed = 343B81870E2721DD75EE0AD7FDB269EB5AAE0AE83A6316AD8ECE3C76D3DD3CC391F1B12B22E988C1257B5F56D905A06C +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = 5DAB087C2E696072C3A207A25C42FBC1AA96FB8EC2A5AF71CAF5215B739F48EE14FBBE4B0CE50DBF8104998CA133E19C +sk = DAA360ECBB51BCB33F5D36F92FFFE77C2DE7ED43D281DCB5FD68CFA0CE19DF2EB36398FFAEF28A6AC96AE6CEC608B0545DAB087C2E696072C3A207A25C42FBC1AA96FB8EC2A5AF71CAF5215B739F48EE14FBBE4B0CE50DBF8104998CA133E19C +HashSigDet = BF373807C8981EC8BD76C0758BA7DB9F8FB77BC7E1D23D5FD00699CF3912D9BC +HashSigRand = 7A341802EEAA6EF566C928094409BEB4824B53958E5C7F943127F2DDFEFC5B18 + +SphincsParameterSet = SLH-DSA-SHA2-256s +seed = 406D9C95F9813CA6A9E6071D3A7A63093F9E00CD15EE968C3AB28CB7368699491E5662F6B63721FE059D50295C3A7516 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = B2EA4B9157C191935629E8914AB581799F366ECC13A153BA88961EBD2D25E7B8126760D273E037DC76CFF5EDDECABEAD489F8A725BE650544C1E3145042D0C2A +sk = AD10AD3409A90C4B24AB0DA526F289209ABCB1F05C86C7E4437A144C91E1C8674BB0634876C4471D23C2F3A041A9830DAD41945642DA3487CE5D2E9E2BE9A1D4B2EA4B9157C191935629E8914AB581799F366ECC13A153BA88961EBD2D25E7B8126760D273E037DC76CFF5EDDECABEAD489F8A725BE650544C1E3145042D0C2A +HashSigDet = F2E3FB0542D8164853BC0E1BE95125F346B75508D92272E41E009B3AC55AFC0C +HashSigRand = E7FA299EEBFB885BE6B7327B20FF4414A74952DB7A4FF12B34242A5E60376F1B + + +SphincsParameterSet = SLH-DSA-SHAKE-256s +seed = B3C83C13DD15DFFE58FDB6026D32E33E5EF24B094BE16741DD5B587EA841AD19B6F026A127678849EC1B459A365FD645 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = E23D931A7E2679EB15C2D3727552A5A5686B13F319C17C48FCAE2CA136D9FE223B5F3C9B72D1318EC5C18191238914691955FBD06947CE3E7010809FD3D31832 +sk = 793871AFCCB50F4035B9788C7292B717939AA28F5094D99EE262047B15E42DEB92568F82621D1EE33A233B858296887279FF85CFDA7F5C415B714E9451CB20A6E23D931A7E2679EB15C2D3727552A5A5686B13F319C17C48FCAE2CA136D9FE223B5F3C9B72D1318EC5C18191238914691955FBD06947CE3E7010809FD3D31832 +HashSigDet = FE25C9658E9AFE5D1A67480C5202DEBC2C05C4D3FB2BFBD875723DEA9AB8D195 +HashSigRand = 66BA90FC5E104EE0D8F53835C4748B251D546DC3C40A8E18016EAD928D26F748 + +SphincsParameterSet = SLH-DSA-SHA2-256f +seed = 937BD532FF7C92404ACA9C2389E314F72D780234E1FD930F967A724937204C59877AF25D38245638FDA72B0EE74F8492 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = 36B35AEFAEEF610994159BA32295631C237F59D285E62248442D5CCCA196973D3DFE70C01BD69E90313BA13437891E8AEBBFB4D42FCCCDFDF98655B272757553 +sk = EDB1483BD9DB934A5C0970C76E96ADAC58C2786F5525CCE741206154222B1BADA4B54A813A208AD3472C64BEF72AA37AD944484A7525B3BFAB58B72D5878ADCC36B35AEFAEEF610994159BA32295631C237F59D285E62248442D5CCCA196973D3DFE70C01BD69E90313BA13437891E8AEBBFB4D42FCCCDFDF98655B272757553 +HashSigDet = 3883D4E0A47250B66D44E80CD19E4F55195A89C2C1E020CFC534347DF7D00302 +HashSigRand = A5D79BF02F5EDDAA10B9A18BC19E7F90593D67B33867D318F7A12C0BD3138C71 + +SphincsParameterSet = SLH-DSA-SHAKE-256f +seed = 075D939507DDEAB7C64FB847EDB74CF693866BEA32C93BC5C129CBD7141C0B79901B26992481236A56CB404D464DA0E1 +msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +pk = 95E8F648FB380F083398E4A3400C07724162FA30E25E8D3784A2E90FDF27CDBB4456B4892B3A49FD6DE7CDAE86D7432BF8F651E09B2920AD7D0618CCF70135E8 +sk = EC5FD75F942B76A278C3F4DF4C25B0DD5DB712A253CC8B9C22287BEFAFBEB6257E53018E4F2B21F571E02C8B128F401ED16391B74336DDD719A0306DB181135395E8F648FB380F083398E4A3400C07724162FA30E25E8D3784A2E90FDF27CDBB4456B4892B3A49FD6DE7CDAE86D7432BF8F651E09B2920AD7D0618CCF70135E8 +HashSigDet = F3A817C8FC206F5F65B3FFE1225588E74A698C58D478214CCAA80EE684D5A0EE +HashSigRand = 3B5C559EBC8AB40DACAC2214830B64EF2237F3EBFEA296021F86588759354AC8 diff --git a/src/tests/data/pubkey/slh_dsa_generic.vec b/src/tests/data/pubkey/slh_dsa_generic.vec new file mode 100644 index 00000000000..742ff962ae8 --- /dev/null +++ b/src/tests/data/pubkey/slh_dsa_generic.vec @@ -0,0 +1,13 @@ +# Used to test SLH-DSA with the generic signature test. Since SLH-DSA +# signatures are very large, we only test the smallest instance with the +# generic tests + +# This test data is generated from the SLH_DSA implementation of https://github.com/mjosaarinen/py-acvp-pqc/tree/main +# See src/scripts/dev_tools/gen_pqc_dsa_kats.py + +Instance = SLH-DSA-SHAKE-128s +Msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 +PrivateKey = 6DE62E3465A55C9C78A07D265BE8540B3E58B0801A124D07FF12B438D5202EA0F08E01A57A272B738FA8EA2F465D59400B96B2F9934DAA0DA7C9873550013599 +PublicKey = F08E01A57A272B738FA8EA2F465D59400B96B2F9934DAA0DA7C9873550013599 +Valid = 1 +Signature = 7FADFFB7BADF167352A89F6BAD17FC7A1BB20EF13D0AEF0EEF48B15FFD9A807B370893E804F81531B9A819545BB1865BD9079D807EE1AE3744FE2FF3C24EFEA8402CCB899C074BBA56336E96881C37DD502817007855B4D9BF37611B28140FD7B068F5302F38032486C93EB391747B0D90DDE7EAFDA51F25FD7A47B5CE68992AAFC5A89DBF65FA9630E300FE78FD4EEC97AF1CDB613A1C5ACF4F27503BF8F2C8D899C1243DF71DEB3635342625647A7D07A8B340CC0322F26AD7AF408D55060163A8F0404C6FEE28D9FF5C692B3A75350838579CD461F9F7033452D43C75501ACB3049D8DB5700465BAE5BE0A15D6E3ED32F09A534EA19E6F3E324E66986377E82F732A42CB61D5C70F3AC51E3AEC0037A8297A6DA047529C718AEDB12AE0B7D6B90583B687E999C38A885E50E4351AB2C0D4DF1732EF85ADA17656ABE9038A0D526D0A7A655594ED77379D30D57E0BF045461AC5834E10C8C1A6270368CA271763A481E72E27936B92320ECF0B396289F5485EF1A26B0F38755EFABCC385F0ABD0F20B8CF062024982241CEE3CBEFA02B3DF6D22932ECDE4CC736904B41D8304A576F8A93C264535BCCD69945AD632039CC611D3B34DB33B1E686739C7CBE7CE68372440E98EF987B212D6E564F8ECBFF3BC7DD6B457D672202A348E394FB71D15519472CC618557BCD6F89623D9C97DCF41C903A8F8DFE1D44571DCBDC0397E09873129BD3E5194D51A0A6117F88E75CB5EBC24CC848BAC0AF14F838368589144F3178FD99613E20963CBCB5896ED4CC30CDB3FC9CB9D64CB623010A0547C02478F3AACDA6CB85243D715399F1E111FBE19D89C2616E5888263A7665BC38BE61D2608DE6A0C850338E8CE9B8FB16B593070F366363A9E1DF052EF60EDB1667AA8484FC8796EFE3CFFEB7FDF2A65A6C8A377166BF77DCE61415CF30E58779B13DB7A1606F5FAC9A1424827D64D1F241ACC9257A62C5008BF5A68080812ECAA7DED135D30FE84FAAA031120404B1139F2C41D60BBADEFA756CA38C5D1717C3BFA77499B5975CDBD5313555420F43A653D0EF24DA827ABC0CCE42967BEDE58BB0D101F15FF93B4A087430BCC412589EB20D76D15E1321B5ED0E9EA8216B7DEF7EE78A5561F0839FB72AB048352A4329322474C0F7BE810931BA4D98F58174762C709981370162E0E7885AB68AA771D565B3B45177B7137E3D97F0D9F68717FD7421BE2F79DA722174CC38D3BA8EE904B6F34001517364FF1E5C162718B28EB425693F0D5B98BB68E30C4C7A87A17AB7874F897B31E6DEBD3F6186CD79A2C7E6D7949388E807B32F6CFBEBD7E44EC135504F95F92B683F8A418AC291C45743DB0D31052933220DC652DF583E9822A9AA0F4599EF0B06BF8D0E85EDCF4DC39CB9DB3A1430AC912551392DF8B8765EE52E5CABCC058BA50D5075FA7B4361709D3969F7F8254B1B985A0411046757D59C68A2CFA6E0C32222612D4E0D8DDEE7999FBCF5550FD1F3B7B8DF40D1284E3FFFCCCF9EE0E77FD30B62A8106E511FA051870546C0A667F440696AE9946F1BB9C642E9650FA5A4A24C265EA22FE90D2E1C04BD8A546B4CD77FA7AA8400F45648E708EB297888D4ECB0C5F4F7A2E5C99D06CA8F81E1D0C59D3B295988C04468FE0B9808D14F751760AA3F7CB28AF8F8DF8CAE6F04E9911653C34FC847B9127418F70ADF20D4C5A4BEAAE07E5B70109B4C61EAB5A1E8604D630A3A6B105AA48F7E27B1E219F6A9FAC4DF9D3D72B64978058999D7B46DEC579840363DECBCC2471185CE55F97C5865CBF8085B9B621E36A7170AC50903C5B7E6B116B4163F36A3D10B3EF7EB44C25BEF5EA5F8107A61C8AEBF161FE212CB1DA3E1FA7A0CC57DCAED03C1666781EA3675F863F91EB894836ABF6290A6A656F61CE4E830C80437B9012DAE51ABE4056F0806E1CF3EC218B4EED8228E6303B6E8ECEAEA2ACB68A93A037E98ED698C11504DF04C89E3374ECA3EAAB479838DB597310AD9D222B6284810B07204E2C492CEAF125616139F97ABA5FDA2F8FFBD1995C89F40C958D9B871753E4077744C6472316F78EAEF228ACDCD83A1F8AFEA700A848AA801407431A30DEA99277E7D0A27B4BF7DE6E84BD63E961D2A6BDDAC9678A9818609025781AEFBF9D792B2FAC3E64D370F70BB84F2E49AB37ACF09A5C61F9552D75CB3A48CBC2733E6182DCF4DCECB999300E9EA554897DB34A7061461A2F9FA1C430B005F4EEA19CC0472D0E4D2771E3C57304B61BC7B1A339FE2952908E0ABB183154883C5B4DF5CCEEBA5743E65B94CE7644690E63E1FB446D63A02221EA022F03F2EBCD121290C30638C1F380D33E6247F2076FE59096875C75F2D12A21C1C72E13214240B32EC845B893ECE4603BFE69E2BCAA198ED118207888DC44DFD6434FCA934ED9FBC5AAB73A3B2E52C89B6CF0FC05353FEB6B0531380E19C8E593F2540294CB76426BAE49770D3657DF77B2B6CBADB201993F94982EF65E8984DE28EFB3F4E1160265F017350EC67F6447AEFA9ECB3D3D69915ED3A14BEB4B9BA04D42C21C5807568A4B1A1E746AFBBCF423253F71E28AAFB063C3FA155761AADC24FFB49C4F1DFDCDC1C608804ED1B2A7F7D708B3C89ED38C8B06325DA987842532A18E24D3C3AA4F6EC0402DACA21478E871F830EFF2A98C0D00E5196D45D1F9849B477474792FDCBC866389571F57E98C0DDF03A576AC31CF7221D69D147CED82905B10763CC477B3F0AE93EF5432852606FA346131279298BA99208908BA4347C5DA544806199EB3DBC790C182C516806EB508113398996EF58DFE732DA2DEAB2AE71D1074518EA3C07B7DDEEE08B67159A0A09AC11416AA6776150DC82B2C21446CF846D6E17EC19A7DA0ED803ACDC8B17C2764088ED9D21857C51552F14C7B92DD2C8ACA9ED1D4E1123CD98EE64E56968AD1118C21547411AC11F20F97EEC6520AC0E545CAADF0F2EA347331988C925ACCD394C0BCB0253E1B460415BFC5663FABF7116D63057D6E4523C9395784C6DEE589235F46C2CE0213D765B5DE129B2C031ED24FA398526EF2F0B39D0EB35BF60BBF5936805787399C3AF613B24303584F9C5F92683B5AF82B067C9F82344253FDA1E62EF220C6003286785D46B6268704DBD04C5CAA1D5C74F946B467E59A4805ED12AB509606B16D10CCD6D57A7E87A6F16BC334D1DEF580126EC433A652565D9EA176DD91610DBF8AB6B5EC4CC67D670BE515855287E47E19B20B0FE9892381D583EA9C2617ED2078E96D92B6212E80C9B32D452801243C4500320FEDAAC087C432926AB14E03CD73E673903D42D5F81EB49CB1FB6E23E4BA4E8719E8D0E90491A529548712D55DE223EAC51BDD492C7F716383BB092EF329E4FDBD01C8B0D6FF84C892E60C17187083EF64176B83DEF6FA207851521C9F9F804937BC8D9F02BA9ECE2B164B9A5E8023995C511EFC7E0664B0320BA6EE7FC1F81DEB6586C05874E2D64249E0F7D4C096E31E7D56F672AD56F603BA7DE8FFC48EA96A6225FAE8764026541B9AE36B1E47F6F1C1FA9EB2EED49230EF7111088793AEDDD9B0EA96E089FE3E0E200DD807D868F402B87898D4D4E1867BD8682319A2C21B38B348020E0E86D570F82A6844BF618AE345A52B56EB8820955B7885C0081B99D7BEA32FCB87B396FC128B8829F628E420A13E6C227F83120967FE65668F17440C1F95C904759A44BFD8FEB6FEFD7E575B3ACBB4A839397ACA40DED3BD4FAC454CEE959CBEA2F145B065F253E036574E3F92DDFA43CA88A4CD672E064154506EE5A51FA0A20DCD9B3E5D3F03EB596CEA1940F0FDAA326AD0F015CC9833D02BD0D85612694F341B76B0EAAA1350818C6A9695D0914090DB9AC8971283201AB3E3F7AE5624471654D326FC384658F2FB17CED0880DB68363C2802CEA0DF55D22BB1C2EF9FB795F8F423A07AC11FEE29E416D5AD27046364C66152171D32AC1B7BD3EB4CC85E6EEA4363142F8DEBF9015EC064454D5229542F86F4E905D1FA7C333B24B205636AF20EB3DA3E5D5826462EB422DAB48300EE9FAE0603763D126C70E4EBDA8F99624A3DBDA20975A7C4F5BEEB132F9CEAE7C0669824F2E186DC174367EBA1A1177BDD12D899581B5C7820C6DE65B841F8462E4BEA2922F51E6A43341CE682A1A8E307B2CA9373DCF43DE8AE9A0ADDAD203FBDC24AB630EEB9BA66E4B973A9C71A4604CEDBAECB44455C348AC9E90FCFDD18ECF9111C3EC0F01249AEE2F455F78BDAB868AC5FDA1ACA41CACEA6369FC8AB68434E402BC49F054A0AC2BFF8E2D60BA899A93F73E95A133045481E44C9BD6E3957DF0C29C447C8CA0EAE5259CA11A4AE84C1E7F2947448FE6462C66B78D66B38847D77D01B47D9443A5674AA6E9E1B7BDA845A2EE5908ABFA8EE1128D9A8D1D3C90DCD5E719857E2FC34A9065B07F77BE1D8F59C3E0347A916F493A5ABF9D08ED88231D9D4029DEE8306E07F227727EFFE8A0EE69A6390112AD614EBAA643FBFC7F665F01F3745774B293595586E16C4660D4AD7E6C8E59CB744A6BA338B638FABCB6C83207097755377802AB22B57A11F40668C7299280DA19CFF54671B613588EB47C05C8E8D6ED36E8E8F5FB8CD920AFFFE0DC2825487EB57F94916EF2C4746AF53DA5FFAAD7790083857EEC9F72E3BF8FDF4245353C5A554985A74F1AD5D97962E0797407D4408A3D42D56B02707EF9F596264661D6D763205C8B55A196333E20BEDDEC9644920235A09C59C3B559FE1426CA76D41C80A0D06476C1C6A1623BE6202CD81384BDF5FB2E4EE7879F9AD932DB17E148524EC79A82A6F4E4BA19AEFE3D31C88FA0C3E13A4007C7E3F3010A8657A3EE456E4555D0D3A17884844AC9683BF6366A62DBDA1D5679A335368797652BB138320F36F6F833DB715FE65C192C40E0652DFD066FCAF65844781D5720A665EAD422B7B1A6D1FB29C76885CBDE5C128D7959063782157AD3C72E4339EE94459F6FFA2FF92DDF51295C15100E0F04542D9ECE6A3F1BD08A9A07EA574E32C6EB9DE5CFE1EFD86A388B3A36E033DF43D9D2F9C6F8B7B9BA725E813EC210FA28A467BA9E1CB9D1CDB8DDFF627C7BD3413ABA0F86AD1CB9266ABE4889CFDD281C66DCA20F349622538F0945A8AA48DE2651C85743A11A71DC026C6386FE1EC12C11DBA783360208CC1F4CC8120A2904C7B07E5A52A24C90E40034B27DB2359323A141684FDC3A9816AE8B23A92F226F7C65422CD35BAE280CF39E3642EE19E7542C98BC61A845FF399564338306EDB04F97D08F22332EA8D691D9BC49DE959BA1D3E9ED36D9EF3267BD92DE4B7247506C989DC2A9E3EE442C556276FCFA04CFD37A2E98DA99626F1B731B1E95CA790B962D460C52CAC5DE22C6D7326195B8A2CB6712C465266A2FECB50548857C790911E218151274FB71CD0AE5E9CCA8B67B3D0D08B9300A0FD9DCB057E672C7CA2587820CBD7D7E6215EF7E6C1C9042FDCF68E911DA5CDFCDD56436D414FBD08525193BC1714C4366BD6A256E488DFEF27B18EFE527F74D3B93B0F70179B8F0FB5888698968EAD9A4227FD2212E12C394519AF3CF55B327A1CAF3FD264D41FB89556A9DC3194CBBCB915C847ED7EAB5E452F1D7B475AD7C7242528093E0F7360596F496575C38F5E461D099142AFCA0AA4318D50E226CC33B6D853459AF9817DA9BFB900A61ED8CD40AB3DDE49EFE7C9A8CB3C806D861A486B52C147D9B35D5A6D1B3D4F288A315DCD76935ACB2C6C2133BB43FD8EBCCC4738373873C23A4A6B2381011F3005AE8735AD52EE47F0AEEF412A5B88C36464385247ADB9EF26CE2058029A2786F71315D8D3609C9419773DC3946B59CC3F788A6D638371E09B5A2A167D00ECF2C386EB72099E8335D81046F6E4A260D91E3983CB7CF1942A117F6C83A018DE4EC330EA5967DE3873125A6A4F81DCBD307C2DB54046A97081041FA7080F51A39B31A47D7C0EEB9034CC0D77E073980F7E1ACD2AA32BDA92E25D1687F5BE71E98D270E500C27D10D0062CD3278DBFC388D929A682AC912CCFE3D1D82B9A59F8DE0BADD313B1E5BA8EB8884AA82587834F2044E19020361A03F74B93AE44CADCF756EAEDE69B928F3279EEA7FC27F093284A7E30FCE71B40EF4E801568F4154BD4BD4C9C10AE72D2BFC30F25FB6A625ACE0B3E570A437E1A8C07C6A03DC7214C09BF3A2C8CFB2F7186448C4610034780A841F2C80C615E6E7C5ABED0B591950BCF3E44C39F1E5D524C31542830C5105B93773EB21EC25ADB738ED804237F35D07C2BA833F886A1A2F5C5CB9C2907861FE8FC41908E8975EF847BE346F6FE8300EC2FAFA2495A2E334662E69DAD30A070582CF1BCA91088A46D369B4F93E7142C279CC73270E03A75AC209F3C0BF27DA702304AC07577C0E084EECBE6CD813E337387A5AC5C7802601F71F1018CD5D4C62146C81F75DEBDBB681D7D302024B62199D61D6E86E7AF2FB4CCD5755B060CAF3B7ACBD62E0967E4D094BB353D17FC25DEA04A86F3107C15EC3B3F903E5E90D3908522D6C8EDD40293318A0129B3D3C01A0003754DAC9449DC77D45A34119B8E7605B0D6AF7E35137F3361B6F510D813F45B89215D8CE79349D9710A4E8831B22B07203F80523E3E40E2797860591C255DE44D960619FA4820B71EF7C340F1A579B04295C24503AAA7783CF56763A57C9DBC87FF607083321F423C89C3F478153ABBD47D7B17D4829DB4782725BB02817B2DBB6E694B246623ECD2D551912BC0EC968211F52F90151EBA701BE1074C55C1520B67AF2CC80E14660095C3D15523E8D526291C82B0FB809DBDC78D2E1438EC645CD7A9CCDD54E716B6158265B4EB8607D5727D0E8339BC9A0F0A0818B569135A91600ACE48C3CE4A635D5EC4D2F6A26BBE31F97F6607656C27E1577D61768D4B9E4532B0252514AC4D6C7B2F7423446D4324AECAC6CC028C0719F2414F0F5B6FD280745A678E5866B851BE109A2D8E55B0AD037BC8071438750F8BAC5A3448FAA013BBC5F40FFD854AA7CF132809782D1AD6CC8B81751E19859E732E23B569772C9585B7C277164BCD8A4F3C96DE7ABBCEB780430DD47A53C72AF9CE6ABFF4951C0778F78559DE5F04136C5EF05452F5BD985E6608C77E25E2E904D0B23A696D16284C16EFE33D60E4250472E485324A59CD8ED1529B304148BA219A014CF9F25A5A0467788455519257EA6E6EC004BB7989517B96FBB6EFF3B60FB48049BF75F4E3B5E1F690AE4C2E663A163670C1544C73E67BDB6259ADEFBE45FCCE7AF9E1E147212E1FBF0072E90096B1A1D1A96EAA6E449A3FD768D8167593AFCD0730967F63CCC8B18ED34EA548794CA1CB4849757B40C6B3665FF8D1D590C27C9EA92B7BDFF65DD281A85F48229AF3079026ED05CFF5C152FE3180557E1DD859680816BB5C0A6389104E2D223C013A7439444494B1F6A578E04A68C352C81FDA21697D84FBA6707B1B8215B2F8A89CFF726BAD6DED413DA9DEF04CFDCF12469D58F5D5E715CD868035121318A402689D7A1AC3DE2D95A4B946AE5BB131B2DBAF81B71AF7F7DAA79D45E5E713FB93D9C9979AE3E0BB65945B737A3EEBD87D000B6C768B30ADFCB05765E85B43FBD0C0CB94CC6545586FBFF4F26C61CD6C901AA5E8C420B369431EF72FA3C307BB45BC7F4AC849D3D0BB9B52074F4A1EB077B16578B5E467A1EC9C4AF2204A8516C71D0317F07F86CA533753F50C230ADF7BBFF84E8BA1B681387FA02EDDE192A577B633738BB042B1FDAC4203CDE6FF09DEF92D48D753C14625AB8C2B9122D1A31CAD7CB98F63D8A6105149A749411E80F715A8689317E2968B065C70E6EB76DDDBB1202C306086DB80249BD5A490BCC28CB7D2EFB2D398E7DDEF07560AC8D7FA1AE00CCF5CB5527902947FC7F4A4BE239A068270A3E760249198C056DF8AE6F872C82E3CA52506704033323775297908EF3D995F25CE577D75BC2886528468F1A86BCAE7F20C48869009D9C80CF06630BFBC5BC18393A587D71F02B28CAD57573EB455244EC413D6FB78448AC85B532AC2DD1D46898E59F9E830CC23E8C0004093DDE1C8F5C1DCC98C9ADDF4E51A712D7412241955C9B73B8DD1C802520F550902CC1071087C2F7F4A159572499C3E6879424C524C2256D7D509BCE1B63A960EFA63252C1E1D1C85D6153389AD23C695691D90B5B9EFB03FC68CD1360B1CE387293705E35A844E73C42FDDE9AE1AF97C465E987D04297DC9DE8967922EE87466FB4846724BD93267CD83195F6A203B7266A119F276B8477A300950C356D0920119D37CBB4D6DA5AFC604D4FFF6BC0901C01114E331E9B18D0F4805F6A121F821665A4197B64EEAD28D75948C16227C62A0968A40DFD1DBF75AAA1348D52A17B9B9C2C11438FDD94E0CCE393BB2A5D1A30D746CD7FC7CA23351C03EA8143325F708C6FB5C7ACA2B75D17BEE73A1B9602733AB3BED05499D520F6403C6BADE54BAF6E6F020697A0805F1EA11199A6AAB219D355274C1E7C9FDEB18B28E4E4EA166A1D87F74F2FEC9A438786043C23D331C4A5078603C044817309FED254B9C329AF0113A2C54E2C94F21857C58294ADE89C9780AA5112B3F66D2E3850929055DB09B92A4C1C3DA8561EC0572B2FFDB884CA24B65A854F82602C2B853EA60CFCA368CDBB310263EA038F25FE95B57DBB55087C295941D77B4BF1B671D118AC25445FA39AF3854E741086062ADC6A05078D06817D36D128BA9AF7C1A6BAAA72BDB94C0A37F609D78D0D81F965A5994C34202646A0E5CFC9AC5AF468DB596D99E261A39295A69711D70783D5DCB5BBE915598B55ED0EF89AA9BFAF03CF14990E3A4142C2306774FD2718392FCD21CB31CD617FA96B6AB4E2DE8360E35F021852D96F53BC2FEA2338A78F40FA4F06AA42DEEF2CC05943B2C5D55A072C1893055DC421EB9E9D7AED1DDDBA8AC60C62BBC422011C1A197588889247931E5CD22B0F7360A86EA5ECF11FF0F28EAA0159C135BCF0ECCE71F390C144F796CDAD8530FEDB7859315FDBD3AE5BB92E8F7DE3482EFFAF3F291851276423E130E77219B1CA8EAED5F54210F2850F141DE682550C22128024123AD6AEE04ECE1AB59A295BCFCC7D8B452AB859E5E03101DDA22627E0FCD56C0B9ADC60BD68B10B74CB7C59C562EB8AEFC9F329EBF7815ABD67C2E91C91C25F2E75F03BDA0D37D59AA72978B96AEE2FC3540FCD8E4490311A8815E98A0DC5C96C9B3695C53A7727EBE14B0929B53B315BE903EAC4DCF1164BF4302AA5BA20418F5F58B062D7EE58DB9B3C09333714C174894084F593C5A957889AF6F89101FEC529F6CBB5C2B826EE5766A70EA9F18EBF92BBD94664319A27D97E62F4BED306FE8C2E23323ACD8AC21D4FB00A48F0ED9E9307DE5A33BFA731A245833D64850EDC90FFE16D0E0D133D859F8F5034F7E63354507AA1C9B38325E0489F707484CA81308C95F77C2B34474C1FF5CBBA134B5062D13142322DF582A5EF9AD736522505904783CB53002E4A8B3DF0A19C029B449D20A5582869122E77387CB621894AF53AF206DBC8A09A4BE43EC00715A8C2D8A8D5F7BBA5BBB945B563ADE1480B5B6C563DFD7D1D972D1341B98097C58BB74F6C4A06B2287EDA6817A56371407CBEADB08168A49AE43A8395F5BCE3DEC3DBB5F26C9285492D3230517FA80811F74EAB92631A69578EEAC36297D926F26D22C8DDBF8728C4583E37B1ACDB258A980A70E940C7F6B12328364AD11DA79706C9392B033353FCB7E150F38F9DF76F76592247E8F1DF8514BF1E66DE4812EEA6586C4BDE4614CC63C5CD3B54911B49A390474B08D6BD781C1D03DEE7C205EBBE5941896A34ECC286D136D34FC1F2570836B9CF75BDCFD4DED79A09B02AC981CA9B851D745367C086D72A758E4D763F63552EB2D4DFCDB8F08A13B17050A323D0F84238FB085722E3FB9F200BD1E402437FA4024964535B88B3B4F0C60CE3ADBDCE588C703878B48CA3F67AB0C25E9335A3CE16BA8A5298E98707F6D351E0330ACCB98EBAC06DFD4FD99AE3E3867A41861AFB6F6138DE9511B3784F6466779B4FDCCBB871A7748E7CBD1B5A98370A20AAE89670C70A37E4939E635A5011A2094FCA9646ACD3C1EAE79519365B092FCFC67A9D574BD12F7D4E993EB5E56F070B696C5F06A59C0460595FF8DBD16448AF88396123002AE6A2C3C33CAC862F1F355FA0FEFB32F1DE710486469A66BFEC33A986EB3AE1FE1F8D13E7500C79DE2405C7CBEA76FFE777821AEEF998907BF644737598051434CC6012B29C51A6E52E9142BDF73E9912B93F01D4295494DA576B170ABB2CDA920683E7B32FDDB2E73EA32101AFB75D1A2F28AE844022A351BE87E1CA755A9146D6E410B8B82572AAFE7A6281BBC8DA62C785A50B78583D2C6B81BC5CAE8A4A26B76694CF5C931A1B7D39FE02CB75A68B545B9272DDD662EDE3DAED0EEA64897F77ED0CCE97D27BE9F04F81DC7CE905FA3866915B86D68E963931674BE5844D494874A611EDC6C62DBF87C2EC7752EBAFB5F0632BBEC89BAD56EB635CB94FD2A4DFA616E67658D23D6F4CDCDF41E9788A65BA14C621C001EA02526966018355D405491C1A994E0D889AB27671B6BCD1640B35B4C8183B6B2F8A7C1C1564A7180C652E089D66646F4C331D9B5D9076D398F071227003E17A3B732CB51B14A3D49262F33236C55355016D153E68A6AF19DC588CDCBB5A44ABA62DDAB1BAACBAAD517B24EDBE0EB6BFDDB7CB22F8B0EE96B0A1A3FFCA58D7104AAA42640F2F1F2A54A5B370AA36285B54E4E2D27F189249E1B95C1016D57D153E0F5423F26DF337415F7B155CAD22129B828EFC22993526BA2C2C352A36BC2B5553FC60AD342970C3A19A38DFBB03D88DBAE24DBA67FB6190868B61C7D27448B2F6FA79D2D4C48C3033234795336F4EEFF57C59D3E6B3873F211DBDF933A0012A114769489A92FD6D746141061B1CEDF14E53DE94D4C1F46D1EFC47112E32D53A7E7B30C5B422309B213834CBC1BCA641BC1EA2E35109CE33A9252324D5902F8E89A13076A30F67090F8ACEBA27A5C8BA30D688DEA92E60842F51363DA1425B2E04559ADFE7970F6B9890DF9736837FE6BE666876655A29896D2E7855E4FAF5AF785DE7A8C65DA09635 diff --git a/src/tests/data/pubkey/sphincsplus.vec b/src/tests/data/pubkey/sphincsplus.vec index 14ccc8988a6..d5b41c3e745 100644 --- a/src/tests/data/pubkey/sphincsplus.vec +++ b/src/tests/data/pubkey/sphincsplus.vec @@ -12,81 +12,81 @@ seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = B505D7CFAD1B497499323C8686325E47AFBC007BA1E2B4A138F03AA9A6195AC8 sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E47AFBC007BA1E2B4A138F03AA9A6195AC8 -HashSig = 7FAF31F05279B409B15FB258C07B666F4917C2F361ECBF996E6D4CFB8F36BCC6 +HashSigRand = 7FAF31F05279B409B15FB258C07B666F4917C2F361ECBF996E6D4CFB8F36BCC6 SphincsParameterSet = SphincsPlus-shake-128s-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = B505D7CFAD1B497499323C8686325E47AC524902FC81F5032BC27B17D9261EBD sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E47AC524902FC81F5032BC27B17D9261EBD -HashSig = 6A8EB621C4E5D927275C86CD3DE84FAB1898BCB433FE218EE71D30482BA30F54 +HashSigRand = 6A8EB621C4E5D927275C86CD3DE84FAB1898BCB433FE218EE71D30482BA30F54 SphincsParameterSet = SphincsPlus-shake-192f-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = 92F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFD9B836B00B9F458C1A193F062A9A3CDAFE7869F47546CB346 sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4792F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFD9B836B00B9F458C1A193F062A9A3CDAFE7869F47546CB346 -HashSig = 37BFD608EF6890087D3CD8DEAA61D884CFA4C2B0D46D0E9661C58B3A592056E2 +HashSigRand = 37BFD608EF6890087D3CD8DEAA61D884CFA4C2B0D46D0E9661C58B3A592056E2 SphincsParameterSet = SphincsPlus-shake-192s-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = 92F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFD0BDE2780ED4CCDAF544D88F22D41610D4EF994825CFB4D45 sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4792F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFD0BDE2780ED4CCDAF544D88F22D41610D4EF994825CFB4D45 -HashSig = 28765D857CCE9D98D9A56745B3AD2280B08A6188B08EB23ECE4974B02CE55D90 +HashSigRand = 28765D857CCE9D98D9A56745B3AD2280B08A6188B08EB23ECE4974B02CE55D90 SphincsParameterSet = SphincsPlus-shake-256f-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = 3E784CCB7EBCDCFD45542B7F6AF778742E0F4479175084AA488B3B74340678AA514264D1B7EF27574AE6933E374225CF87683DE3BFEE657200F3667C8D800722 sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4792F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFD45542B7F6AF778742E0F4479175084AA488B3B74340678AA514264D1B7EF27574AE6933E374225CF87683DE3BFEE657200F3667C8D800722 -HashSig = 9332A86A6A9C73728B70D2911C81D37D44D2612E62D925824563DCE3064036E7 +HashSigRand = 9332A86A6A9C73728B70D2911C81D37D44D2612E62D925824563DCE3064036E7 SphincsParameterSet = SphincsPlus-shake-256s-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = 3E784CCB7EBCDCFD45542B7F6AF778742E0F4479175084AA488B3B74340678AA3623940D5D834494148A661F9AC6A96BDC54AD4D0B8B0913484A9233C56212A4 sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4792F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFD45542B7F6AF778742E0F4479175084AA488B3B74340678AA3623940D5D834494148A661F9AC6A96BDC54AD4D0B8B0913484A9233C56212A4 -HashSig = D0E3EEC05FA643A3952CD718948811578C36D7086C4EEE26FD401AFC867AFCF4 +HashSigRand = D0E3EEC05FA643A3952CD718948811578C36D7086C4EEE26FD401AFC867AFCF4 SphincsParameterSet = SphincsPlus-sha2-128f-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = B505D7CFAD1B497499323C8686325E47FDF7400AB7A5D8C7ABA7350AC4092ADD sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E47FDF7400AB7A5D8C7ABA7350AC4092ADD -HashSig = 75467372C5E79953BC8D69C2A05982813F9934ADC7D0864AB0F63A65B19CDF93 +HashSigRand = 75467372C5E79953BC8D69C2A05982813F9934ADC7D0864AB0F63A65B19CDF93 SphincsParameterSet = SphincsPlus-sha2-128s-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = B505D7CFAD1B497499323C8686325E476D2E5993D919B7F288CC823133046CF9 sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E476D2E5993D919B7F288CC823133046CF9 -HashSig = 79F3F09AB540EBB40F9C7691A019D0355EC540410CB7AC7A32146256D3F20E09 +HashSigRand = 79F3F09AB540EBB40F9C7691A019D0355EC540410CB7AC7A32146256D3F20E09 SphincsParameterSet = SphincsPlus-sha2-192f-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = 92F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFDAD7EF867981E22877AA4C2A8829F657E931C55409B897D2E sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4792F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFDAD7EF867981E22877AA4C2A8829F657E931C55409B897D2E -HashSig = 535FD6592D279F7A1AA16ABD49D5FA257272C026629794DA3F6FC92B41E59146 +HashSigRand = 535FD6592D279F7A1AA16ABD49D5FA257272C026629794DA3F6FC92B41E59146 SphincsParameterSet = SphincsPlus-sha2-192s-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = 92F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFDEE27692EF30BAD87B55C4E2A25E7F47875C1F53723CE31C6 sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4792F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFDEE27692EF30BAD87B55C4E2A25E7F47875C1F53723CE31C6 -HashSig = FBC7B620E045AD67D132D2D8CDB0EA1D1CF231B85671CDB396DF5D4D608E974E +HashSigRand = FBC7B620E045AD67D132D2D8CDB0EA1D1CF231B85671CDB396DF5D4D608E974E SphincsParameterSet = SphincsPlus-sha2-256f-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = 3E784CCB7EBCDCFD45542B7F6AF778742E0F4479175084AA488B3B74340678AAD9D73F07F69A7D12C49B9A367DE0668544E8471660678A4EF7CEE8BE9638CA53 sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4792F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFD45542B7F6AF778742E0F4479175084AA488B3B74340678AAD9D73F07F69A7D12C49B9A367DE0668544E8471660678A4EF7CEE8BE9638CA53 -HashSig = 4FFFA9B69ABFEEFDC31A1D59EB503A362BFCF6FF5320CE3E8D9E0BB75BAB961D +HashSigRand = 4FFFA9B69ABFEEFDC31A1D59EB503A362BFCF6FF5320CE3E8D9E0BB75BAB961D SphincsParameterSet = SphincsPlus-sha2-256s-r3.1 seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 msg = D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8 pk = 3E784CCB7EBCDCFD45542B7F6AF778742E0F4479175084AA488B3B74340678AA026F566CDCD2987EB7AB1EC71E86B617F97B98EF63902E283FB5249119C6CEB0 sk = 7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4792F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFD45542B7F6AF778742E0F4479175084AA488B3B74340678AA026F566CDCD2987EB7AB1EC71E86B617F97B98EF63902E283FB5249119C6CEB0 -HashSig = 27A5A600A541485A34E263EFA546829F573FE215DB5DB66C1BDB1F49D9CB0A98 +HashSigRand = 27A5A600A541485A34E263EFA546829F573FE215DB5DB66C1BDB1F49D9CB0A98 diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 8e35e6d7896..647e6ad10b5 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -17,6 +17,7 @@ #include #include #include + #include #include #endif @@ -3778,6 +3779,45 @@ class FFI_ML_DSA_Test final : public FFI_Signature_Roundtrip_Test { const char* hash_algo_or_padding() const override { return ""; } }; +class FFI_SLH_DSA_Test final : public FFI_Signature_Roundtrip_Test { + public: + std::string name() const override { return "FFI SLH-DSA"; } + + private: + const char* algo() const override { return "SLH-DSA"; } + + privkey_loader_fn_t private_key_load_function() const override { return botan_privkey_load_slh_dsa; } + + pubkey_loader_fn_t public_key_load_function() const override { return botan_pubkey_load_slh_dsa; } + + std::vector modes() const override { + auto modes = std::vector{ + "SLH-DSA-SHA2-128f", + "SLH-DSA-SHAKE-128f", + "SLH-DSA-SHA2-192f", + "SLH-DSA-SHAKE-192f", + "SLH-DSA-SHA2-256f", + "SLH-DSA-SHAKE-256f", + }; + + if(Test::run_long_tests()) { + modes = Botan::concat(modes, + std::vector{ + "SLH-DSA-SHA2-128s", + "SLH-DSA-SHA2-192s", + "SLH-DSA-SHA2-256s", + "SLH-DSA-SHAKE-128s", + "SLH-DSA-SHAKE-192s", + "SLH-DSA-SHAKE-256s", + }); + } + + return modes; + } + + const char* hash_algo_or_padding() const override { return ""; } +}; + class FFI_ElGamal_Test final : public FFI_Test { public: std::string name() const override { return "FFI ElGamal"; } @@ -4021,8 +4061,9 @@ BOTAN_REGISTER_TEST("ffi", "ffi_kyber512", FFI_Kyber512_Test); BOTAN_REGISTER_TEST("ffi", "ffi_kyber768", FFI_Kyber768_Test); BOTAN_REGISTER_TEST("ffi", "ffi_kyber1024", FFI_Kyber1024_Test); BOTAN_REGISTER_TEST("ffi", "ffi_ml_kem", FFI_ML_KEM_Test); -BOTAN_REGISTER_TEST("ffi", "ffi_frodokem", FFI_FrodoKEM_Test); BOTAN_REGISTER_TEST("ffi", "ffi_ml_dsa", FFI_ML_DSA_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_slh_dsa", FFI_SLH_DSA_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_frodokem", FFI_FrodoKEM_Test); BOTAN_REGISTER_TEST("ffi", "ffi_elgamal", FFI_ElGamal_Test); BOTAN_REGISTER_TEST("ffi", "ffi_dh", FFI_DH_Test); diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp index ec9520743c1..748d2f49b75 100644 --- a/src/tests/test_pubkey.cpp +++ b/src/tests/test_pubkey.cpp @@ -612,9 +612,8 @@ std::vector PK_Key_Generation_Test::run() { oid.value().to_string(), key.object_identifier().to_string()); } else { - const bool exception = - name == "Kyber" || name == "ML-KEM" || name == "ML-DSA" || name == "FrodoKEM" || name == "SPHINCS+"; - + const bool exception = name == "Kyber" || name == "ML-KEM" || name == "ML-DSA" || name == "SLH-DSA" || + name == "FrodoKEM" || name == "SPHINCS+"; if(!exception) { result.test_failure("Keys name " + name + " does not map to an OID"); } diff --git a/src/tests/test_sphincsplus.cpp b/src/tests/test_sphincsplus.cpp index d02482a0d01..bf64b43cae5 100644 --- a/src/tests/test_sphincsplus.cpp +++ b/src/tests/test_sphincsplus.cpp @@ -8,7 +8,7 @@ #include "test_rng.h" #include "tests.h" -#if(defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)) && defined(BOTAN_HAS_AES) +#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) && defined(BOTAN_HAS_AES) #include #include @@ -25,25 +25,23 @@ namespace Botan_Tests { /** - * Test all implemented SPHINCS+ instances using the data of the KAT files - * generated by the reference implementation (Round 3.1 - * (https://github.com/sphincs/sphincsplus/)) + * Test all implemented SLH-DSA instances using the data of the KAT files. */ -class SPHINCS_Plus_Test final : public Text_Based_Test { +class SPHINCS_Plus_Test_Base : public Text_Based_Test { public: - SPHINCS_Plus_Test() : Text_Based_Test("pubkey/sphincsplus.vec", "SphincsParameterSet,seed,pk,sk,msg,HashSig") {} + SPHINCS_Plus_Test_Base(std::string_view kat_path) : + Text_Based_Test(std::string(kat_path), "SphincsParameterSet,seed,pk,sk,msg,HashSigRand", "HashSigDet") {} bool skip_this_test(const std::string&, const VarMap& vars) override { auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); - #if not defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) - if(params.hash_type() == Botan::Sphincs_Hash_Type::Shake256) { + if(!params.is_available()) { return true; } - #endif - #if not defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) - if(params.hash_type() == Botan::Sphincs_Hash_Type::Sha256) { + #if not defined(BOTAN_HAS_SHA3) + // The SLH-DSA shake-based signatures are hashed with SHA-3 in the test file. + if(params.hash_type() == Botan::Sphincs_Hash_Type::Shake256) { return true; } #endif @@ -53,27 +51,39 @@ class SPHINCS_Plus_Test final : public Text_Based_Test { case Botan::Sphincs_Parameter_Set::Sphincs128Fast: case Botan::Sphincs_Parameter_Set::Sphincs192Fast: case Botan::Sphincs_Parameter_Set::Sphincs256Fast: + case Botan::Sphincs_Parameter_Set::SLHDSA128Fast: + case Botan::Sphincs_Parameter_Set::SLHDSA192Fast: + case Botan::Sphincs_Parameter_Set::SLHDSA256Fast: return false; case Botan::Sphincs_Parameter_Set::Sphincs128Small: case Botan::Sphincs_Parameter_Set::Sphincs192Small: case Botan::Sphincs_Parameter_Set::Sphincs256Small: + case Botan::Sphincs_Parameter_Set::SLHDSA128Small: + case Botan::Sphincs_Parameter_Set::SLHDSA192Small: + case Botan::Sphincs_Parameter_Set::SLHDSA256Small: return !Test::run_long_tests(); } BOTAN_ASSERT_UNREACHABLE(); } Test::Result run_one_test(const std::string&, const VarMap& vars) final { - Test::Result result("SPHINCS+ "); - auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); + Test::Result result(params.is_slh_dsa() ? "SLH-DSA" : "SPHINCS+"); const std::vector seed_ref = vars.get_req_bin("seed"); const std::vector msg_ref = vars.get_req_bin("msg"); const std::vector pk_ref = vars.get_req_bin("pk"); const std::vector sk_ref = vars.get_req_bin("sk"); - const std::vector sig_hash = vars.get_req_bin("HashSig"); + const std::vector sig_rand_hash = vars.get_req_bin("HashSigRand"); + const auto sig_det_hash = [&]() -> std::optional> { + if(vars.has_key("HashSigDet")) { + return vars.get_opt_bin("HashSigDet"); + } else { + return std::nullopt; + } + }(); - // Depending on the SPHINCS+ configuration the resulting signature is + // Depending on the SLH-DSA configuration the resulting signature is // hashed either with SHA-3 or SHA-256 to reduce the inner dependencies // on other hash function modules. auto hash_algo_spec = [&]() -> std::string { @@ -83,14 +93,15 @@ class SPHINCS_Plus_Test final : public Text_Based_Test { return "SHA-256"; } }(); - auto hash = Botan::HashFunction::create(hash_algo_spec); + auto hash = Botan::HashFunction::create_or_throw(hash_algo_spec); /* * To get sk_seed || sk_prf || pk_seed and opt_rand from the given seed * (from KAT), we create a CTR_DRBG_AES256 rng and "simulate" the * invocation-pattern in the reference. We feed the created randomness * to the fixed output rng, to allow for our (slightly different) - * invocation-pattern. + * invocation-pattern. Be careful, some KATs are generated by 3 separate + * invocations of dbrg.random_vals which is different. */ auto kat_rng = CTR_DRBG_AES256(seed_ref); Fixed_Output_RNG fixed_rng; @@ -99,6 +110,7 @@ class SPHINCS_Plus_Test final : public Text_Based_Test { // push the entropy used for signing twice, as we want to perform two // signing operations const auto entropy_for_signing = kat_rng.random_vec>(1 * params.n()); + // Depending on the configuation, upto 2 signatures with 'Randomized' are created fixed_rng.add_entropy(entropy_for_signing); fixed_rng.add_entropy(entropy_for_signing); @@ -108,22 +120,38 @@ class SPHINCS_Plus_Test final : public Text_Based_Test { result.test_is_eq("public key bits", priv_key.public_key_bits(), pk_ref); result.test_is_eq("private key bits", unlock(priv_key.private_key_bits()), sk_ref); - // Signature with generated Keypair - auto signer = Botan::PK_Signer(priv_key, fixed_rng, "Randomized"); // TODO: No KAT for 'deterministic'? - auto signature = signer.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng); + // Signature roundtrip (Randomized mode) + auto signer_rand = Botan::PK_Signer(priv_key, fixed_rng, "Randomized"); + auto signature_rand = signer_rand.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng); - result.test_is_eq("signature creation", unlock(hash->process(signature)), sig_hash); + result.test_is_eq("signature creation randomized", unlock(hash->process(signature_rand)), sig_rand_hash); - // Verification with generated Keypair Botan::PK_Verifier verifier(*priv_key.public_key(), params.algorithm_identifier()); bool verify_success = - verifier.verify_message(msg_ref.data(), msg_ref.size(), signature.data(), signature.size()); - result.confirm("verification of valid signature", verify_success); + verifier.verify_message(msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size()); + result.confirm("verification of valid randomized signature", verify_success); + + // Signature roundtrip (Deterministic mode) - not available for all parameter sets + // For testing time reasons we only test this for some tests if not --run-long-tests + if(sig_det_hash.has_value() && + (run_long_tests() || params.parameter_set() == Botan::Sphincs_Parameter_Set::SLHDSA128Fast)) { + auto signer_det = Botan::PK_Signer(priv_key, fixed_rng, "Deterministic"); + auto signature_det = signer_det.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng); + + result.test_is_eq("signature creation deterministic", unlock(hash->process(signature_det)), *sig_det_hash); + + auto verify_success_det = + verifier.verify_message(msg_ref.data(), msg_ref.size(), signature_det.data(), signature_det.size()); + result.confirm("verification of valid deterministic signature", verify_success_det); + } + + // Verification with generated Keypair // Run additional parsing and re-verification tests on one parameter // set only to save test runtime. Given the current test vector we will // run this exactly once per hash function. - if(params.parameter_set() == Botan::Sphincs_Parameter_Set::Sphincs128Fast) { + if(params.parameter_set() == Botan::Sphincs_Parameter_Set::Sphincs128Fast || + params.parameter_set() == Botan::Sphincs_Parameter_Set::SLHDSA128Fast) { // Deserialization of Keypair from test vector Botan::SphincsPlus_PrivateKey deserialized_priv_key(sk_ref, params); Botan::SphincsPlus_PublicKey deserialized_pub_key(pk_ref, params); @@ -132,13 +160,14 @@ class SPHINCS_Plus_Test final : public Text_Based_Test { auto deserialized_signer = Botan::PK_Signer(deserialized_priv_key, fixed_rng, "Randomized"); auto deserialized_signature = deserialized_signer.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng); - result.test_is_eq( - "signature creation after deserialization", unlock(hash->process(deserialized_signature)), sig_hash); + result.test_is_eq("signature creation after deserialization", + unlock(hash->process(deserialized_signature)), + sig_rand_hash); // Verification with deserialized Keypair Botan::PK_Verifier deserialized_verifier(deserialized_pub_key, params.algorithm_identifier()); - bool verify_success_deserialized = - deserialized_verifier.verify_message(msg_ref.data(), msg_ref.size(), signature.data(), signature.size()); + bool verify_success_deserialized = deserialized_verifier.verify_message( + msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size()); result.confirm("verification of valid signature after deserialization", verify_success_deserialized); // Verification of invalid signature @@ -147,41 +176,57 @@ class SPHINCS_Plus_Test final : public Text_Based_Test { msg_ref.data(), msg_ref.size(), broken_sig.data(), broken_sig.size()); result.confirm("verification of invalid signature", !verify_fail); - bool verify_success_after_fail = - deserialized_verifier.verify_message(msg_ref.data(), msg_ref.size(), signature.data(), signature.size()); + bool verify_success_after_fail = deserialized_verifier.verify_message( + msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size()); result.confirm("verification of valid signature after broken signature", verify_success_after_fail); } + // Misc + result.confirm("parameter serialization works", params.to_string() == vars.get_req_str("SphincsParameterSet")); + return result; } }; +class SPHINCS_Plus_Test final : public SPHINCS_Plus_Test_Base { + public: + SPHINCS_Plus_Test() : SPHINCS_Plus_Test_Base("pubkey/sphincsplus.vec") {} +}; + +class SLH_DSA_Test final : public SPHINCS_Plus_Test_Base { + public: + SLH_DSA_Test() : SPHINCS_Plus_Test_Base("pubkey/slh_dsa.vec") {} +}; + class SPHINCS_Plus_Keygen_Tests final : public PK_Key_Generation_Test { public: std::vector keygen_params() const override { - // clang-format off - return { - #if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) + const std::vector short_test_params = { "SphincsPlus-shake-128s-r3.1", - "SphincsPlus-shake-128f-r3.1", - "SphincsPlus-shake-192s-r3.1", - "SphincsPlus-shake-192f-r3.1", - "SphincsPlus-shake-256s-r3.1", - "SphincsPlus-shake-256f-r3.1", - #endif - #if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) - "SphincsPlus-sha2-128s-r3.1", + "SLH-DSA-SHAKE-128s", "SphincsPlus-sha2-128f-r3.1", - "SphincsPlus-sha2-192s-r3.1", - "SphincsPlus-sha2-192f-r3.1", - "SphincsPlus-sha2-256s-r3.1", - "SphincsPlus-sha2-256f-r3.1", - #endif + "SLH-DSA-SHA2-128f", + }; + const std::vector all_params = { + "SphincsPlus-shake-128s-r3.1", "SphincsPlus-shake-128f-r3.1", "SphincsPlus-shake-192s-r3.1", + "SphincsPlus-shake-192f-r3.1", "SphincsPlus-shake-256s-r3.1", "SphincsPlus-shake-256f-r3.1", + "SLH-DSA-SHAKE-128s", "SLH-DSA-SHAKE-128f", "SLH-DSA-SHAKE-192s", + "SLH-DSA-SHAKE-192f", "SLH-DSA-SHAKE-256s", "SLH-DSA-SHAKE-256f", + "SphincsPlus-sha2-128s-r3.1", "SphincsPlus-sha2-128f-r3.1", "SphincsPlus-sha2-192s-r3.1", + "SphincsPlus-sha2-192f-r3.1", "SphincsPlus-sha2-256s-r3.1", "SphincsPlus-sha2-256f-r3.1", + "SLH-DSA-SHA2-128s", "SLH-DSA-SHA2-128f", "SLH-DSA-SHA2-192s", + "SLH-DSA-SHA2-192f", "SLH-DSA-SHA2-256s", "SLH-DSA-SHA2-256f", }; - // clang-format on + const auto& tested_params = Test::run_long_tests() ? all_params : short_test_params; + std::vector available_params; + std::copy_if(tested_params.begin(), + tested_params.end(), + std::back_inserter(available_params), + [](const std::string& param) { return Botan::Sphincs_Parameters::create(param).is_available(); }); + return available_params; } - std::string algo_name() const override { return "SPHINCS+"; } + std::string algo_name() const override { return "SLH-DSA"; } std::unique_ptr public_key_from_raw(std::string_view keygen_params, std::string_view /* provider */, @@ -191,8 +236,62 @@ class SPHINCS_Plus_Keygen_Tests final : public PK_Key_Generation_Test { } }; +class Generic_SlhDsa_Signature_Tests final : public PK_Signature_Generation_Test { + public: + Generic_SlhDsa_Signature_Tests() : + PK_Signature_Generation_Test( + "SLH-DSA", "pubkey/slh_dsa_generic.vec", "Instance,Msg,PrivateKey,PublicKey,Valid,Signature") {} + + bool clear_between_callbacks() const override { return false; } + + std::unique_ptr load_private_key(const VarMap& vars) override { + const std::string instance = vars.get_req_str("Instance"); + const std::vector privkey = vars.get_req_bin("PrivateKey"); + const std::vector pubkey = vars.get_req_bin("PublicKey"); + auto sk = + std::make_unique(privkey, Botan::Sphincs_Parameters::create(instance)); + + if(sk->public_key_bits() != pubkey) { + throw Test_Error("Invalid SLH-DSA key in test data"); + } + + return sk; + } + + std::string default_padding(const VarMap&) const override { return ""; } + + bool skip_this_test(const std::string&, const VarMap& vars) override { + return !Botan::Sphincs_Parameters::create(vars.get_req_str("Instance")).is_available(); + } +}; + +class Generic_SlhDsa_Verification_Tests final : public PK_Signature_Verification_Test { + public: + Generic_SlhDsa_Verification_Tests() : + PK_Signature_Verification_Test( + "SLH-DSA", "pubkey/slh_dsa_generic.vec", "Instance,Msg,PrivateKey,PublicKey,Valid,Signature") {} + + bool clear_between_callbacks() const override { return false; } + + std::unique_ptr load_public_key(const VarMap& vars) override { + const std::string instance = vars.get_req_str("Instance"); + const std::vector pubkey = vars.get_req_bin("PublicKey"); + + return std::make_unique(pubkey, Botan::Sphincs_Parameters::create(instance)); + } + + std::string default_padding(const VarMap&) const override { return ""; } + + bool skip_this_test(const std::string&, const VarMap& vars) override { + return !Botan::Sphincs_Parameters::create(vars.get_req_str("Instance")).is_available(); + } +}; + BOTAN_REGISTER_TEST("pubkey", "sphincsplus", SPHINCS_Plus_Test); -BOTAN_REGISTER_TEST("pubkey", "sphincsplus_keygen", SPHINCS_Plus_Keygen_Tests); +BOTAN_REGISTER_TEST("pubkey", "slh_dsa", SLH_DSA_Test); +BOTAN_REGISTER_TEST("pubkey", "slh_dsa_keygen", SPHINCS_Plus_Keygen_Tests); +BOTAN_REGISTER_TEST("pubkey", "slh_dsa_sign_generic", Generic_SlhDsa_Signature_Tests); +BOTAN_REGISTER_TEST("pubkey", "slh_dsa_verify_generic", Generic_SlhDsa_Verification_Tests); } // namespace Botan_Tests diff --git a/src/tests/test_sphincsplus_fors.cpp b/src/tests/test_sphincsplus_fors.cpp index 18fa396b00e..3673ec5ceac 100644 --- a/src/tests/test_sphincsplus_fors.cpp +++ b/src/tests/test_sphincsplus_fors.cpp @@ -7,7 +7,7 @@ #include "tests.h" -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) +#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) #include #include @@ -40,25 +40,12 @@ class SPHINCS_Plus_FORS_Test final : public Text_Based_Test { "SphincsParameterSet,Address,SecretSeed,PublicSeed,PublicKey,Msg,HashSig") {} bool skip_this_test(const std::string&, const VarMap& vars) override { - [[maybe_unused]] auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); - - #if not defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) - if(params.hash_type() == Botan::Sphincs_Hash_Type::Shake256) { - return true; - } - #endif - - #if not defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) - if(params.hash_type() == Botan::Sphincs_Hash_Type::Sha256) { - return true; - } - #endif - - return false; + auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); + return !params.is_available(); } Test::Result run_one_test(const std::string&, const VarMap& vars) final { - Test::Result result("SPHINCS+'s FORS"); + Test::Result result("SLH-DSA's FORS"); auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); @@ -67,7 +54,7 @@ class SPHINCS_Plus_FORS_Test final : public Text_Based_Test { const auto hashed_message = Botan::SphincsHashedMessage(vars.get_req_bin("Msg")); - // Depending on the SPHINCS+ configuration the resulting signature is + // Depending on the SLH-DSA's configuration the resulting signature is // hashed either with SHA-3 or SHA-256 to reduce the inner dependencies // on other hash function modules. auto hash_algo_spec = [&]() -> std::string { @@ -103,4 +90,4 @@ BOTAN_REGISTER_TEST("pubkey", "sphincsplus_fors", SPHINCS_Plus_FORS_Test); } // namespace Botan_Tests -#endif // BOTAN_HAS_SPHINCS_PLUS +#endif // BOTAN_HAS_SPHINCS_PLUS_COMMON diff --git a/src/tests/test_sphincsplus_utils.cpp b/src/tests/test_sphincsplus_utils.cpp index a3496a9ce5e..183e9f0a2b1 100644 --- a/src/tests/test_sphincsplus_utils.cpp +++ b/src/tests/test_sphincsplus_utils.cpp @@ -7,7 +7,7 @@ #include "tests.h" -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) +#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) #include #include @@ -35,11 +35,11 @@ std::vector test_sphincsplus_address() { CHECK("set up an address", [&](Test::Result& result) { Botan::Sphincs_Address a(Botan::Sphincs_Address::ForsTree); - a.set_layer(Botan::HypertreeLayerIndex(1337)) - .set_tree(Botan::XmssTreeIndexInLayer(4294967338) /* longer than 32bits */) - .set_keypair(Botan::TreeNodeIndex(131072)) - .set_chain(Botan::WotsChainIndex(67108864)) - .set_hash(Botan::WotsHashIndex(42)); + a.set_layer_address(Botan::HypertreeLayerIndex(1337)) + .set_tree_address(Botan::XmssTreeIndexInLayer(4294967338) /* longer than 32bits */) + .set_keypair_address(Botan::TreeNodeIndex(131072)) + .set_chain_address(Botan::WotsChainIndex(67108864)) + .set_hash_address(Botan::WotsHashIndex(42)); result.test_is_eq("SHA-256(a1)", sha256(a), @@ -49,9 +49,9 @@ std::vector test_sphincsplus_address() { CHECK("set up another address", [&](Test::Result& result) { Botan::Sphincs_Address a(Botan::Sphincs_Address::ForsTree); - a.set_layer(Botan::HypertreeLayerIndex(1337)) - .set_tree(Botan::XmssTreeIndexInLayer(4294967338) /* longer than 32bits */) - .set_keypair(Botan::TreeNodeIndex(131072)) + a.set_layer_address(Botan::HypertreeLayerIndex(1337)) + .set_tree_address(Botan::XmssTreeIndexInLayer(4294967338) /* longer than 32bits */) + .set_keypair_address(Botan::TreeNodeIndex(131072)) .set_tree_height(Botan::TreeLayerIndex(67108864)) .set_tree_index(Botan::TreeNodeIndex(1073741824)); result.test_is_eq("SHA-256(a2)", @@ -62,9 +62,9 @@ std::vector test_sphincsplus_address() { CHECK("copy subtree", [&](Test::Result& result) { Botan::Sphincs_Address a(Botan::Sphincs_Address::ForsTree); - a.set_layer(Botan::HypertreeLayerIndex(1337)) - .set_tree(Botan::XmssTreeIndexInLayer(4294967338) /* longer than 32bits */) - .set_keypair(Botan::TreeNodeIndex(131072)) + a.set_layer_address(Botan::HypertreeLayerIndex(1337)) + .set_tree_address(Botan::XmssTreeIndexInLayer(4294967338) /* longer than 32bits */) + .set_keypair_address(Botan::TreeNodeIndex(131072)) .set_tree_height(Botan::TreeLayerIndex(67108864)) .set_tree_index(Botan::TreeNodeIndex(1073741824)); @@ -83,11 +83,11 @@ std::vector test_sphincsplus_address() { CHECK("copy keypair", [&](Test::Result& result) { Botan::Sphincs_Address a(Botan::Sphincs_Address::ForsTree); - a.set_layer(Botan::HypertreeLayerIndex(1337)) - .set_tree(Botan::XmssTreeIndexInLayer(4294967338) /* longer than 32bits */) - .set_keypair(Botan::TreeNodeIndex(131072)) - .set_chain(Botan::WotsChainIndex(67108864)) - .set_hash(Botan::WotsHashIndex(42)); + a.set_layer_address(Botan::HypertreeLayerIndex(1337)) + .set_tree_address(Botan::XmssTreeIndexInLayer(4294967338) /* longer than 32bits */) + .set_keypair_address(Botan::TreeNodeIndex(131072)) + .set_chain_address(Botan::WotsChainIndex(67108864)) + .set_hash_address(Botan::WotsHashIndex(42)); auto keypair1 = Botan::Sphincs_Address::as_keypair_from(a); Botan::Sphincs_Address keypair2({0, 0, 0, 0, 0, 0, 0, 0}); @@ -109,4 +109,4 @@ BOTAN_REGISTER_TEST_FN("pubkey", "sphincsplus_address", test_sphincsplus_address } // namespace Botan_Tests -#endif // BOTAN_HAS_SPHINCS_PLUS +#endif // BOTAN_HAS_SPHINCS_PLUS_COMMON diff --git a/src/tests/test_sphincsplus_wots.cpp b/src/tests/test_sphincsplus_wots.cpp index 3635e3b3283..0dfa2f4c245 100644 --- a/src/tests/test_sphincsplus_wots.cpp +++ b/src/tests/test_sphincsplus_wots.cpp @@ -7,7 +7,7 @@ #include "tests.h" -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) +#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) #include #include @@ -42,24 +42,11 @@ class SPHINCS_Plus_WOTS_Test final : public Text_Based_Test { bool skip_this_test(const std::string&, const VarMap& vars) override { [[maybe_unused]] auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); - - #if not defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) - if(params.hash_type() == Botan::Sphincs_Hash_Type::Shake256) { - return true; - } - #endif - - #if not defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) - if(params.hash_type() == Botan::Sphincs_Hash_Type::Sha256) { - return true; - } - #endif - - return false; + return !params.is_available(); } Test::Result run_one_test(const std::string&, const VarMap& vars) final { - Test::Result result("SPHINCS+'s WOTS+"); + Test::Result result("SLH-DSA's WOTS+"); auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet")); @@ -72,7 +59,7 @@ class SPHINCS_Plus_WOTS_Test final : public Text_Based_Test { auto hashes = Botan::Sphincs_Hash_Functions::create(params, public_seed); - // Depending on the SPHINCS+ configuration the resulting WOTS+ signature is + // Depending on the SLH-DSA's configuration the resulting WOTS+ signature is // hashed either with SHA-3 or SHA-256 to reduce the inner dependencies // on other hash function modules. auto hash_algo_spec = [&]() -> std::string { @@ -92,7 +79,7 @@ class SPHINCS_Plus_WOTS_Test final : public Text_Based_Test { // Address used for hashing the WOTS+ public key auto pk_addr_pk_from_sig = Botan::Sphincs_Address::as_subtree_from(address).set_type( Botan::Sphincs_Address_Type::WotsPublicKeyCompression); - pk_addr_pk_from_sig.set_keypair(leaf_idx); + pk_addr_pk_from_sig.set_keypair_address(leaf_idx); // Prepare the message auto wots_steps = Botan::chain_lengths(root_to_sign, params); @@ -131,4 +118,4 @@ BOTAN_REGISTER_TEST("pubkey", "sphincsplus_wots", SPHINCS_Plus_WOTS_Test); } // namespace Botan_Tests -#endif // BOTAN_HAS_SPHINCS_PLUS +#endif // BOTAN_HAS_SPHINCS_PLUS_COMMON diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp index eb298e92554..7520453af89 100644 --- a/src/tests/unit_x509.cpp +++ b/src/tests/unit_x509.cpp @@ -112,6 +112,9 @@ std::unique_ptr make_a_private_key(const std::string& algo, if(algo == "HSS-LMS") { return "SHA-256,HW(5,4),HW(5,4)"; } + if(algo == "SLH-DSA") { + return "SLH-DSA-SHA2-128f"; + } return ""; // default "" means choose acceptable algo-specific params }(); @@ -1234,7 +1237,8 @@ Test::Result test_valid_constraints(const Botan::Private_Key& key, const std::st result.test_eq("crl sign not permitted", crl_sign.compatible_with(key), false); result.test_eq("sign", sign_everything.compatible_with(key), false); } else if(pk_algo == "DSA" || pk_algo == "ECDSA" || pk_algo == "ECGDSA" || pk_algo == "ECKCDSA" || - pk_algo == "GOST-34.10" || pk_algo == "Dilithium" || pk_algo == "ML-DSA" || pk_algo == "HSS-LMS") { + pk_algo == "GOST-34.10" || pk_algo == "Dilithium" || pk_algo == "ML-DSA" || pk_algo == "SLH-DSA" || + pk_algo == "HSS-LMS") { // these are signature algorithms only result.test_eq("all constraints not permitted", all.compatible_with(key), false); @@ -1606,6 +1610,7 @@ class X509_Cert_Unit_Tests final : public Test { "Ed448", "Dilithium", "ML-DSA", + "SLH-DSA", "HSS-LMS"}; for(const std::string& algo : sig_algos) {