diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp index e5a9a80c7cf..c0dd1ef2f35 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_fors.cpp @@ -32,14 +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) or 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) or 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) { - // This is one of the few places where SPHINCS+ round 3.1 and SLH-DSA differ - if(params.is_slh_dsa()) { - idx ^= (((message[offset >> 3] >> (~offset & 0x7)) & 0x1) << (params.a() - 1 - i)); - } else { - idx ^= (((message[offset >> 3] >> (offset & 0x7)) & 0x1) << i); - } + update_idx(idx, i); } } diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp index bb793254fa4..272f8ef8338 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp @@ -37,20 +37,23 @@ 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+"); - SphincsMessageInternal msg_internal{.prefix = SphincsMessagePrefix(), .message = std::move(msg)}; + [[maybe_unused]] SphincsMessageInternal msg_internal{.prefix = SphincsMessagePrefix(), .message = std::move(msg)}; +#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) or defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE) if(params.is_slh_dsa()) { // prefix (no pre-hash mode): input mode byte + |ctx| + ctx - msg_internal.prefix = SphincsMessagePrefix(1 + 1 + context.size()); - BufferStuffer prefix_stuffer(msg_internal.prefix); const uint8_t input_mode_byte = 0x00; // Pure (TODO: pre-hash mode: 0x01) - prefix_stuffer.append(input_mode_byte); - prefix_stuffer.append(checked_cast_to(context.size())); - prefix_stuffer.append(context); - - BOTAN_ASSERT_NOMSG(prefix_stuffer.full()); + msg_internal.prefix = concat( + store_be(input_mode_byte), store_be(checked_cast_to(context.size())), context); + return msg_internal; + } +#endif +#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) or defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) + if(!params.is_slh_dsa()) { + // SPHINCS+ Round 3.1 uses the message without any prefix + return msg_internal; } - // else: SPHINCS+ Round 3.1 uses the message without any prefix - return msg_internal; +#endif + throw Internal_Error("Missing message preparation logic for SLH-DSA or SPHINCS+"); } } // namespace @@ -63,7 +66,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("SLH-DSA(or 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); @@ -94,7 +97,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("SLH-DSA(or 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); @@ -175,7 +178,11 @@ class SphincsPlus_Verification_Operation final : public PK_Ops::Verification { 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_context(/* TODO: Add API */ {}) {} + 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 @@ -239,7 +246,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 SLH-DSA(or SPHINCS+) signature"); + throw Decoding_Error("Unexpected AlgorithmIdentifier for SLH-DSA (or SPHINCS+) signature"); } return std::make_unique(m_public); } @@ -332,6 +339,8 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { 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 { @@ -368,7 +377,7 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { // Compute and append the digest randomization value (R of spec). // Use addrng for the randomized variant. Use the public seed for the deterministic one. - auto opt_rand = + const auto opt_rand = (addrnd.has_value()) ? addrnd.value() : StrongSpan(m_public->seed()); auto msg_random_s = sphincs_sig.next(p.n()); @@ -413,7 +422,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 SLH-DSA(or SPHINCS+)"); + "Unexpected parameters for signing with SLH-DSA (or SPHINCS+)"); const bool randomized = (params == "Randomized"); if(provider.empty() || provider == "base") {