Botan 3.11.0
Crypto and TLS for C&
tree_hash.h
Go to the documentation of this file.
1/**
2 * Treehash logic used for hash-based signatures
3 * (C) 2023 Jack Lloyd
4 * 2023 Fabian Albert, René Meusel, Amos Treiber, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH
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#ifndef BOTAN_TREE_HASH_H_
11#define BOTAN_TREE_HASH_H_
12
13#include <botan/strong_type.h>
14#include <botan/internal/buffer_slicer.h>
15#include <optional>
16#include <vector>
17
18namespace Botan {
19
20namespace concepts {
21
22template <typename T>
24
25/**
26 * @brief An index of a node in a layer.
27 *
28 * This is a separate index for each layer.
29 * The left most node of a layer has the index 0.
30 */
31template <typename T>
33
34/**
35 * @brief A layer in a Tree.
36 *
37 * The bottom layer is the layer 0.
38 */
39template <typename T>
41
42template <typename T>
44
45/**
46 * @brief An address in a Tree.
47 */
48template <typename T, typename TreeLayerIndex, typename TreeNodeIndex>
49concept tree_address = requires(T a, TreeLayerIndex tree_layer, TreeNodeIndex tree_index) {
52 { a.set_address(tree_layer, tree_index) };
53};
54
55template <typename T, typename NodeIdx, typename LayerIdx, typename Address, typename NodeSS>
58 requires(T func, NodeSS out, const Address& address, NodeSS a, NodeSS b) {
59 { func(out, address, a, b) };
60 };
61
62template <typename T, typename NodeIdx, typename LayerIdx, typename Address, typename NodeSS>
65 requires(T func, NodeSS out, const Address& address) {
66 { func(out, address) };
67 };
68
69} // namespace concepts
70
71/**
72 * @brief Treehash logic to build up a merkle hash tree.
73 *
74 * Computes the root of the merkle tree.
75 * Can also output an authentication path necessary for a hash based signature.
76 *
77 * Given the following tree:
78 * Layer:
79 * 2 7R
80 * / \
81 * 1 3X 6A
82 * / \ / \
83 * 0 1X 2A 4 5
84 *
85 * The treehash logic traverses the tree (Post-order traversal), i.e., the nodes are
86 * discovered in order 1,2,3,...,7. If we want to create a signature using leaf node 1,
87 * the authentication path is (Node 2, Node 6), since we need those to compute the
88 * root.
89 *
90 * @param out_root An output buffer to store the root node in (size: node_size ).
91 * @param out_auth_path Optional buffer to store the authentication path in (size: node_size * total_tree_height).
92 * @param leaf_idx The optional index of the leaf used to sign in the bottom tree layer beginning with index 0.
93 * nullopt if no node is signed, so we need no auth path.
94 * @param node_size The size of each node in the tree.
95 * @param total_tree_height The height of the merkle tree to construct.
96 * @param idx_offset If we compute a subtree this marks the index of the leftmost leaf node in the bottom layer
97 * @param node_pair_hash The function to process two child nodes to compute their parent node.
98 * @param gen_leaf The logic to create a leaf node given the address in the tree. Probably this function
99 * creates a one-time/few-time-signature's public key which is hashed to be the leaf node.
100 * @param tree_address The address that is passed to gen_leaf or node_pair hash. This function will update the
101 * address accordings to the currently processed node. This object may contain further
102 * algorithm specific information, like the position of this merkle tree in a hypertree.
103 */
104template <concepts::contiguous_strong_type TreeNode,
105 concepts::strong_span AuthPathSS,
108 typename Address>
110inline void treehash(
111 StrongSpan<TreeNode> out_root,
112 std::optional<AuthPathSS> out_auth_path,
113 std::optional<TreeNodeIndex> leaf_idx,
114 size_t node_size,
115 TreeLayerIndex total_tree_height,
116 uint32_t idx_offset,
119 Address& tree_address) {
120 BOTAN_ASSERT_NOMSG(out_root.size() == node_size);
121 BOTAN_ASSERT(out_auth_path.has_value() == leaf_idx.has_value(),
122 "Both leaf index and auth path buffer is given or neither.");
123 const bool is_signing = leaf_idx.has_value();
124 BOTAN_ASSERT_NOMSG(!is_signing || out_auth_path.value().size() == node_size * total_tree_height.get());
125
126 const TreeNodeIndex max_idx(uint32_t((1 << total_tree_height.get()) - 1));
127
128 std::vector<TreeNode> last_visited_left_child_at_layer(total_tree_height.get(), TreeNode(node_size));
129
130 TreeNode current_node(node_size); // Current logical node
131
132 // Traverse the tree from the left-most leaf, matching siblings and up until
133 // the root (Post-order traversal). Collect the adjacent nodes to build
134 // the authentication path along the way.
135 for(TreeNodeIndex idx(0); true; ++idx) {
136 tree_address.set_address(TreeLayerIndex(0), idx + idx_offset);
137 gen_leaf(StrongSpan<TreeNode>(current_node), tree_address);
138
139 // Now combine the freshly generated right node with previously generated
140 // left ones
141 uint32_t internal_idx_offset = idx_offset;
142 TreeNodeIndex internal_idx = idx;
143 auto internal_leaf = leaf_idx;
144
145 for(TreeLayerIndex h(0); true; ++h) {
146 // Check if we hit the top of the tree
147 if(h == total_tree_height) {
148 copy_mem(out_root, current_node);
149 return;
150 }
151
152 // Check if the node we have is a part of the authentication path; if
153 // it is, write it out. The XOR sum of both nodes (at internal_idx and internal_leaf)
154 // is 1 iff they have the same parent node in the FORS tree
155 if(is_signing && (internal_idx ^ internal_leaf.value()) == 0x01U) {
156 auto auth_path_location = out_auth_path.value().get().subspan(h.get() * node_size, node_size);
157 copy_mem(auth_path_location, current_node);
158 }
159
160 // Check if we're at a left child; if so, stop going up the tree
161 // Exception: if we've reached the end of the tree, keep on going (so
162 // we combine the last 4 nodes into the one root node in two more
163 // iterations)
164 if((internal_idx & 1) == 0U && idx < max_idx) {
165 // We've hit a left child; save the current for when we get the
166 // corresponding right child.
167 copy_mem(last_visited_left_child_at_layer.at(h.get()), current_node);
168 break;
169 }
170
171 // Ok, we're at a right node. Now combine the left and right logical
172 // nodes together.
173
174 // Set the address of the node we're creating.
175 internal_idx_offset /= 2;
176 tree_address.set_address(h + 1, internal_idx / 2 + internal_idx_offset);
177
178 node_pair_hash(current_node, tree_address, last_visited_left_child_at_layer.at(h.get()), current_node);
179
180 internal_idx /= 2;
181 if(internal_leaf.has_value()) {
182 internal_leaf.value() /= 2;
183 }
184 }
185 }
186}
187
188/**
189 * @brief Uses an authentication path and a leaf node to reconstruct the root node
190 * of a merkle tree.
191 *
192 * @param out_root A output buffer for the root node of the merkle tree.
193 * @param authentication_path The authentication path in one buffer (concatenated nodes).
194 * @param leaf_idx The index of the leaf used to sig in the bottom layer beginning with 0.
195 * @param leaf The leaf node used to sig.
196 * @param node_size The size of each node in the tree.
197 * @param total_tree_height The height of the merkle tree to construct.
198 * @param idx_offset If we compute a subtree this marks the index of the leftmost leaf node in the bottom layer.
199 * @param node_pair_hash The function to process two child nodes to compute their parent node.
200 * @param tree_address The address that is passed to node_pair hash. This function will update the
201 * address accordings to the currently processed node. This object may contain further
202 * algorithm specific information, like the position of this merkle tree in a hypertree.
203 */
204template <concepts::contiguous_strong_type TreeNode,
205 concepts::strong_span AuthPathSS,
206 concepts::tree_node_index TreeNodeIndex,
207 concepts::tree_layer_index TreeLayerIndex,
208 typename Address>
209 requires concepts::tree_address<Address, TreeLayerIndex, TreeNodeIndex>
210inline void compute_root(
211 StrongSpan<TreeNode> out_root,
212 AuthPathSS authentication_path,
213 TreeNodeIndex leaf_idx,
215 size_t node_size,
216 TreeLayerIndex total_tree_height,
217 uint32_t idx_offset,
219 Address& tree_address) {
220 BOTAN_ASSERT_NOMSG(out_root.size() == node_size);
221 BOTAN_ASSERT_NOMSG(authentication_path.size() == node_size * static_cast<size_t>(total_tree_height.get()));
222 BOTAN_ASSERT_NOMSG(leaf.size() == node_size);
223
224 // Use the `out` parameter as intermediate buffer for left/right nodes
225 // while traversing the tree.
226 copy_mem(out_root, leaf);
227
228 // Views into either `auth_path` or `out` depending on the tree location.
231
232 BufferSlicer auth_path(authentication_path);
233
234 // The leaf is put in the left or right buffer, depending on its indexes parity.
235 // Same for the first node of the authentication path
236
237 for(TreeLayerIndex i(0); i < total_tree_height; i++) {
238 // The input of the hash function takes the current node and the node
239 // given in the authentication path. If the current node is a right node
240 // in the tree (i.e. its leaf index is uneven) the hash function inputs
241 // must be swapped.
242 left = out_root;
243 right = auth_path.take<TreeNode>(node_size);
244
245 if((leaf_idx & 1) == 1U) {
246 std::swap(left, right);
247 }
248
249 leaf_idx /= 2;
250 idx_offset /= 2;
251 tree_address.set_address(i + 1, leaf_idx + idx_offset);
252
253 node_pair_hash(out_root, tree_address, left, right);
254 }
255
256 BOTAN_ASSERT_NOMSG(auth_path.empty());
257}
258} // namespace Botan
259
260#endif // BOTAN_TREE_HASH_H_
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
std::span< const uint8_t > take(const size_t count)
decltype(auto) size() const noexcept(noexcept(this->m_span.size()))
constexpr T & get() &
Definition strong_type.h:85
An address in a Tree.
Definition tree_hash.h:49
An index of a node in a layer.
Definition tree_hash.h:32
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
constexpr bool is_strong_span_v
Strong< uint32_t, struct TreeNodeIndex_, EnableArithmeticWithPlainNumber > TreeNodeIndex
Index of an individual node inside an XMSS or FORS tree.
Definition sp_types.h:92
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)