Botan 3.11.0
Crypto and TLS for C&
sp_fors.cpp
Go to the documentation of this file.
1/*
2 * FORS - Forest of Random Subsets (FIPS 205, Section 8)
3 * (C) 2023 Jack Lloyd
4 * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity
5 *
6 * Parts of this file have been adapted from https://github.com/sphincs/sphincsplus
7 *
8 * Botan is released under the Simplified BSD License (see license.txt)
9 */
10
11#include <botan/internal/sp_fors.h>
12
13#include <botan/assert.h>
14#include <botan/sp_parameters.h>
15
16#include <botan/internal/buffer_slicer.h>
17#include <botan/internal/buffer_stuffer.h>
18#include <botan/internal/sp_address.h>
19#include <botan/internal/sp_hash.h>
20#include <botan/internal/sp_treehash.h>
21#include <botan/internal/sp_types.h>
22
23namespace Botan {
24
25namespace {
26
27/// FIPS 205, Algorithm 4: base_2^b(X,b,out_len) with b = a and out_len = k (for usage in FORS)
28std::vector<TreeNodeIndex> fors_message_to_indices(std::span<const uint8_t> message, const Sphincs_Parameters& params) {
29 BOTAN_ASSERT_NOMSG((message.size() * 8) >= (params.k() * params.a()));
30
31 std::vector<TreeNodeIndex> indices(params.k());
32
33 uint32_t offset = 0;
34
35 // This is one of the few places where the logic of SPHINCS+ round 3.1 and SLH-DSA differs
36 auto update_idx = [&]() -> std::function<void(TreeNodeIndex&, uint32_t)> {
37#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE)
38 if(params.is_slh_dsa()) {
39 return [&](TreeNodeIndex& idx, uint32_t i) {
40 idx ^= (((message[offset >> 3] >> (~offset & 0x7)) & 0x1) << (params.a() - 1 - i));
41 };
42 }
43#endif
44#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
45 if(!params.is_slh_dsa()) {
46 return [&](TreeNodeIndex& idx, uint32_t i) { idx ^= (((message[offset >> 3] >> (offset & 0x7)) & 0x1) << i); };
47 }
48#endif
49 throw Internal_Error("Missing FORS index update logic for SPHINCS+ or SLH-DSA");
50 }();
51
52 for(auto& idx : indices) {
53 for(uint32_t i = 0; i < params.a(); ++i, ++offset) {
54 update_idx(idx, i);
55 }
56 }
57
58 return indices;
59}
60
61} // namespace
62
64 const SphincsHashedMessage& hashed_message,
65 const SphincsSecretSeed& secret_seed,
66 const Sphincs_Address& address,
67 const Sphincs_Parameters& params,
68 Sphincs_Hash_Functions& hashes) {
69 BOTAN_ASSERT_NOMSG(sig_out.size() == params.fors_signature_bytes());
70
71 const auto indices = fors_message_to_indices(hashed_message, params);
72
73 auto fors_tree_addr = Sphincs_Address::as_keypair_from(address);
74
75 auto fors_pk_addr = Sphincs_Address::as_keypair_from(address).set_type(Sphincs_Address::ForsTreeRootsCompression);
76
77 std::vector<uint8_t> roots_buffer(params.k() * params.n());
78 BufferStuffer roots(roots_buffer);
79 BufferStuffer sig(sig_out);
80
81 // Buffer to hold the FORS leaves during tree traversal
82 // (Avoids a secure_vector allocation/deallocation in the hot path)
83 ForsLeafSecret fors_leaf_secret(params.n());
84
85 // For each of the k FORS subtrees: Compute the secret leaf, the authentication path
86 // and the trees' root and append the signature respectively
87 BOTAN_ASSERT_NOMSG(indices.size() == params.k());
88 for(uint32_t i = 0; i < params.k(); ++i) {
89 const uint32_t idx_offset = i * (1 << params.a());
90
91 // Compute the secret leaf given by the chunk of the message and append it to the signature
92 fors_tree_addr.set_type(Sphincs_Address_Type::ForsKeyGeneration)
93 .set_tree_height(TreeLayerIndex(0))
94 .set_tree_index(indices[i] + idx_offset);
95
96 hashes.PRF(sig.next<ForsLeafSecret>(params.n()), secret_seed, fors_tree_addr);
97
98 // Compute the authentication path and root for this leaf node
99 fors_tree_addr.set_type(Sphincs_Address_Type::ForsTree);
100
101 const GenerateLeafFunction fors_gen_leaf = [&](StrongSpan<SphincsTreeNode> out_root,
102 TreeNodeIndex address_index) {
103 fors_tree_addr.set_tree_index(address_index);
104 fors_tree_addr.set_type(Sphincs_Address_Type::ForsKeyGeneration);
105
106 hashes.PRF(fors_leaf_secret, secret_seed, fors_tree_addr);
107
108 fors_tree_addr.set_type(Sphincs_Address_Type::ForsTree);
109 hashes.T(out_root, fors_tree_addr, fors_leaf_secret);
110 };
111
112 treehash(roots.next<SphincsTreeNode>(params.n()),
113 sig.next<SphincsAuthenticationPath>(params.a() * params.n()),
114 params,
115 hashes,
116 indices[i],
117 idx_offset,
118 params.a(),
119 fors_gen_leaf,
120 fors_tree_addr);
121 }
122
124 BOTAN_ASSERT_NOMSG(roots.full());
125
126 // Compute the public key by the hash of the concatenation of all roots
127 return hashes.T<SphincsTreeNode>(fors_pk_addr, roots_buffer);
128}
129
132 const Sphincs_Address& address,
133 const Sphincs_Parameters& params,
134 Sphincs_Hash_Functions& hashes) {
135 const auto indices = fors_message_to_indices(hashed_message, params);
136
137 auto fors_tree_addr = Sphincs_Address::as_keypair_from(address).set_type(Sphincs_Address::ForsTree);
138
139 auto fors_pk_addr = Sphincs_Address::as_keypair_from(address).set_type(Sphincs_Address::ForsTreeRootsCompression);
140
141 BufferSlicer s(signature);
142 std::vector<uint8_t> roots_buffer(params.k() * params.n());
143 BufferStuffer roots(roots_buffer);
144
145 // For each of the k FORS subtrees: Reconstruct the subtree's root node by using the
146 // leaf and the authentication path offered in the FORS signature.
147 BOTAN_ASSERT_NOMSG(indices.size() == params.k());
148 for(uint32_t i = 0; i < params.k(); ++i) {
149 const uint32_t idx_offset = i * (1 << params.a());
150
151 // Compute the FORS leaf by using the secret leaf contained in the signature
152 fors_tree_addr.set_tree_height(TreeLayerIndex(0)).set_tree_index(indices[i] + idx_offset);
153 auto fors_leaf_secret = s.take<ForsLeafSecret>(params.n());
154 auto auth_path = s.take<SphincsAuthenticationPath>(params.n() * params.a());
155 auto leaf = hashes.T<SphincsTreeNode>(fors_tree_addr, fors_leaf_secret);
156
157 // Reconstruct the subtree's root using the authentication path
158 compute_root(roots.next<SphincsTreeNode>(params.n()),
159 params,
160 hashes,
161 leaf,
162 indices[i],
163 idx_offset,
164 auth_path,
165 params.a(),
166 fors_tree_addr);
167 }
168
169 BOTAN_ASSERT_NOMSG(roots.full());
170
171 // Reconstruct the public key the signature creates with the hash of the concatenation of all roots
172 // Only if the signature is valid, the pk is the correct FORS pk.
173 return hashes.T<SphincsTreeNode>(fors_pk_addr, roots_buffer);
174}
175
176} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
std::span< const uint8_t > take(const size_t count)
Helper class to ease in-place marshalling of concatenated fixed-length values.
constexpr std::span< uint8_t > next(size_t bytes)
constexpr bool full() const
Sphincs_Address & set_type(Sphincs_Address_Type type)
Definition sp_address.h:71
static Sphincs_Address as_keypair_from(const Sphincs_Address &other)
Definition sp_address.h:128
void T(std::span< uint8_t > out, const Sphincs_Address &address, const BufferTs &... in)
Definition sp_hash.h:57
void PRF(StrongSpan< ForsLeafSecret > out, const SphincsSecretSeed &sk_seed, const Sphincs_Address &address)
Definition sp_hash.h:70
uint32_t fors_signature_bytes() const
decltype(auto) size() const noexcept(noexcept(this->m_span.size()))
Strong< std::vector< uint8_t >, struct SphincsTreeNode_ > SphincsTreeNode
Either an XMSS or FORS tree node or leaf.
Definition sp_types.h:70
SphincsTreeNode fors_sign_and_pkgen(StrongSpan< ForsSignature > sig_out, const SphincsHashedMessage &hashed_message, const SphincsSecretSeed &secret_seed, const Sphincs_Address &address, const Sphincs_Parameters &params, Sphincs_Hash_Functions &hashes)
FIPS 205, Algorithm 16: fors_sign (with simultaneous FORS pk generation).
Definition sp_fors.cpp:63
Strong< secure_vector< uint8_t >, struct ForsLeafSecret_ > ForsLeafSecret
Definition sp_types.h:71
Strong< secure_vector< uint8_t >, struct SphincsSecretSeed_ > SphincsSecretSeed
Definition sp_types.h:61
Strong< std::vector< uint8_t >, struct SphincsHashedMessage_ > SphincsHashedMessage
Definition sp_types.h:59
std::function< void(StrongSpan< SphincsTreeNode >, TreeNodeIndex)> GenerateLeafFunction
Definition sp_treehash.h:25
Strong< uint32_t, struct TreeNodeIndex_, EnableArithmeticWithPlainNumber > TreeNodeIndex
Index of an individual node inside an XMSS or FORS tree.
Definition sp_types.h:92
SphincsTreeNode fors_public_key_from_signature(const SphincsHashedMessage &hashed_message, StrongSpan< const ForsSignature > signature, const Sphincs_Address &address, const Sphincs_Parameters &params, Sphincs_Hash_Functions &hashes)
FIPS 205, Algorithm 17: fors_pkFromSig.
Definition sp_fors.cpp:130
Strong< std::vector< uint8_t >, struct SphincsAuthenticationPath_ > SphincsAuthenticationPath
Definition sp_types.h:67
Strong< uint32_t, struct TreeLayerIndex_, EnableArithmeticWithPlainNumber > TreeLayerIndex
Index of the layer within a FORS/XMSS tree.
Definition sp_types.h:83
void treehash(StrongSpan< SphincsTreeNode > out_root, StrongSpan< SphincsAuthenticationPath > out_auth_path, const Sphincs_Parameters &params, Sphincs_Hash_Functions &hashes, std::optional< TreeNodeIndex > leaf_idx, uint32_t idx_offset, uint32_t total_tree_height, const GenerateLeafFunction &gen_leaf, Sphincs_Address &tree_address)
void compute_root(StrongSpan< SphincsTreeNode > out, const Sphincs_Parameters &params, Sphincs_Hash_Functions &hashes, const SphincsTreeNode &leaf, TreeNodeIndex leaf_idx, uint32_t idx_offset, StrongSpan< const SphincsAuthenticationPath > authentication_path, uint32_t total_tree_height, Sphincs_Address &tree_address)