Botan 3.6.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/hash.h>
15#include <botan/sp_parameters.h>
16
17#include <botan/internal/sp_address.h>
18#include <botan/internal/sp_hash.h>
19#include <botan/internal/sp_treehash.h>
20#include <botan/internal/sp_types.h>
21#include <botan/internal/stl_util.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 leafs 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 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 GenerateLeafFunction fors_gen_leaf = [&](StrongSpan<SphincsTreeNode> out_root, TreeNodeIndex address_index) {
102 fors_tree_addr.set_tree_index(address_index);
103 fors_tree_addr.set_type(Sphincs_Address_Type::ForsKeyGeneration);
104
105 hashes.PRF(fors_leaf_secret, secret_seed, fors_tree_addr);
106
107 fors_tree_addr.set_type(Sphincs_Address_Type::ForsTree);
108 hashes.T(out_root, fors_tree_addr, fors_leaf_secret);
109 };
110
111 treehash(roots.next<SphincsTreeNode>(params.n()),
112 sig.next<SphincsAuthenticationPath>(params.a() * params.n()),
113 params,
114 hashes,
115 indices[i],
116 idx_offset,
117 params.a(),
118 fors_gen_leaf,
119 fors_tree_addr);
120 }
121
123 BOTAN_ASSERT_NOMSG(roots.full());
124
125 // Compute the public key by the hash of the concatenation of all roots
126 return hashes.T<SphincsTreeNode>(fors_pk_addr, roots_buffer);
127}
128
131 const Sphincs_Address& address,
132 const Sphincs_Parameters& params,
133 Sphincs_Hash_Functions& hashes) {
134 const auto indices = fors_message_to_indices(hashed_message, params);
135
136 auto fors_tree_addr = Sphincs_Address::as_keypair_from(address).set_type(Sphincs_Address::ForsTree);
137
138 auto fors_pk_addr = Sphincs_Address::as_keypair_from(address).set_type(Sphincs_Address::ForsTreeRootsCompression);
139
140 BufferSlicer s(signature);
141 std::vector<uint8_t> roots_buffer(params.k() * params.n());
142 BufferStuffer roots(roots_buffer);
143
144 // For each of the k FORS subtrees: Reconstruct the subtree's root node by using the
145 // leaf and the authentication path offered in the FORS signature.
146 BOTAN_ASSERT_NOMSG(indices.size() == params.k());
147 for(uint32_t i = 0; i < params.k(); ++i) {
148 uint32_t idx_offset = i * (1 << params.a());
149
150 // Compute the FORS leaf by using the secret leaf contained in the signature
151 fors_tree_addr.set_tree_height(TreeLayerIndex(0)).set_tree_index(indices[i] + idx_offset);
152 auto fors_leaf_secret = s.take<ForsLeafSecret>(params.n());
153 auto auth_path = s.take<SphincsAuthenticationPath>(params.n() * params.a());
154 auto leaf = hashes.T<SphincsTreeNode>(fors_tree_addr, fors_leaf_secret);
155
156 // Reconstruct the subtree's root using the authentication path
157 compute_root(roots.next<SphincsTreeNode>(params.n()),
158 params,
159 hashes,
160 leaf,
161 indices[i],
162 idx_offset,
163 auth_path,
164 params.a(),
165 fors_tree_addr);
166 }
167
168 BOTAN_ASSERT_NOMSG(roots.full());
169
170 // Reconstruct the public key the signature creates with the hash of the concatenation of all roots
171 // Only if the signature is valid, the pk is the correct FORS pk.
172 return hashes.T<SphincsTreeNode>(fors_pk_addr, roots_buffer);
173}
174
175} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
std::span< const uint8_t > take(const size_t count)
Definition stl_util.h:98
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:142
constexpr std::span< uint8_t > next(size_t bytes)
Definition stl_util.h:150
constexpr bool full() const
Definition stl_util.h:187
Sphincs_Address & set_type(Sphincs_Address_Type type)
Definition sp_address.h:74
static Sphincs_Address as_keypair_from(const Sphincs_Address &other)
Definition sp_address.h:131
void T(std::span< uint8_t > out, const Sphincs_Address &address, 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()))
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
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:129
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)