/src/botan/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * SLH-DSA treehash logic |
3 | | * (C) 2023 Jack Lloyd |
4 | | * 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity |
5 | | * |
6 | | * Botan is released under the Simplified BSD License (see license.txt) |
7 | | **/ |
8 | | |
9 | | #include <botan/internal/sp_treehash.h> |
10 | | |
11 | | #include <botan/internal/sp_address.h> |
12 | | #include <botan/internal/sp_hash.h> |
13 | | #include <botan/internal/stl_util.h> |
14 | | |
15 | | namespace Botan { |
16 | | |
17 | | void treehash(StrongSpan<SphincsTreeNode> out_root, |
18 | | StrongSpan<SphincsAuthenticationPath> out_auth_path, |
19 | | const Sphincs_Parameters& params, |
20 | | Sphincs_Hash_Functions& hashes, |
21 | | std::optional<TreeNodeIndex> leaf_idx, |
22 | | uint32_t idx_offset, |
23 | | uint32_t total_tree_height, |
24 | | const GenerateLeafFunction& gen_leaf, |
25 | 0 | Sphincs_Address& tree_address) { |
26 | 0 | BOTAN_ASSERT_NOMSG(out_root.size() == params.n()); |
27 | 0 | BOTAN_ASSERT_NOMSG(out_auth_path.size() == params.n() * total_tree_height); |
28 | |
|
29 | 0 | const TreeNodeIndex max_idx(uint32_t((1 << total_tree_height) - 1)); |
30 | |
|
31 | 0 | std::vector<uint8_t> stack(total_tree_height * params.n()); |
32 | 0 | SphincsTreeNode current_node(params.n()); // Current logical node |
33 | | |
34 | | /* Traverse the tree from the left-most leaf, matching siblings and up until |
35 | | * the root (Post-order traversal). Collect the adjacent nodes (A) to build |
36 | | * the authentication path (X) along the way. |
37 | | * |
38 | | * 7R |
39 | | * / \ |
40 | | * 3X 6A |
41 | | * / \ / \ |
42 | | * 1X 2A 4 5 |
43 | | */ |
44 | 0 | for(TreeNodeIndex idx(0); true; ++idx) { |
45 | 0 | tree_address.set_tree_height(TreeLayerIndex(0)); |
46 | 0 | gen_leaf(current_node, idx + idx_offset); |
47 | | |
48 | | // Now combine the freshly generated right node with previously generated |
49 | | // left ones |
50 | 0 | uint32_t internal_idx_offset = idx_offset; |
51 | 0 | TreeNodeIndex internal_idx = idx; |
52 | 0 | auto internal_leaf = leaf_idx; |
53 | |
|
54 | 0 | for(TreeLayerIndex h(0); true; ++h) { |
55 | | // Check if we hit the top of the tree |
56 | 0 | if(h.get() == total_tree_height) { |
57 | 0 | copy_mem(out_root, current_node); |
58 | 0 | return; |
59 | 0 | } |
60 | | |
61 | | // Check if the node we have is a part of the authentication path; if |
62 | | // it is, write it out. The XOR sum of both nodes (at internal_idx and internal_leaf) |
63 | | // is 1 iff they have the same parent node in the FORS tree |
64 | 0 | if(internal_leaf.has_value() && (internal_idx ^ internal_leaf.value()) == 0x01U) { |
65 | 0 | auto auth_path_location = out_auth_path.get().subspan(h.get() * params.n(), params.n()); |
66 | 0 | copy_mem(auth_path_location, current_node); |
67 | 0 | } |
68 | | |
69 | | // At this point we know that we'll need to use the stack. Get a |
70 | | // reference to the correct location. |
71 | 0 | auto stack_location = std::span(stack).subspan(h.get() * params.n(), params.n()); |
72 | | |
73 | | // Check if we're at a left child; if so, stop going up the stack |
74 | | // Exception: if we've reached the end of the tree, keep on going (so |
75 | | // we combine the last 4 nodes into the one root node in two more |
76 | | // iterations) |
77 | 0 | if((internal_idx & 1) == 0U && idx < max_idx) { |
78 | | // We've hit a left child; save the current for when we get the |
79 | | // corresponding right child. |
80 | 0 | copy_mem(stack_location, current_node); |
81 | 0 | break; |
82 | 0 | } |
83 | | |
84 | | // Ok, we're at a right node. Now combine the left and right logical |
85 | | // nodes together. |
86 | | |
87 | | // Set the address of the node we're creating. |
88 | 0 | internal_idx_offset /= 2; |
89 | 0 | tree_address.set_tree_height(h + 1); |
90 | 0 | tree_address.set_tree_index(internal_idx / 2 + internal_idx_offset); |
91 | |
|
92 | 0 | hashes.T(current_node, tree_address, stack_location, current_node); |
93 | |
|
94 | 0 | internal_idx /= 2; |
95 | 0 | if(internal_leaf.has_value()) { |
96 | 0 | internal_leaf.value() /= 2; |
97 | 0 | } |
98 | 0 | } |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | void compute_root(StrongSpan<SphincsTreeNode> out, |
103 | | const Sphincs_Parameters& params, |
104 | | Sphincs_Hash_Functions& hashes, |
105 | | const SphincsTreeNode& leaf, |
106 | | TreeNodeIndex leaf_idx, |
107 | | uint32_t idx_offset, |
108 | | StrongSpan<const SphincsAuthenticationPath> authentication_path, |
109 | | uint32_t total_tree_height, |
110 | 0 | Sphincs_Address& tree_address) { |
111 | 0 | BOTAN_ASSERT_NOMSG(out.size() == params.n()); |
112 | 0 | BOTAN_ASSERT_NOMSG(authentication_path.size() == params.n() * total_tree_height); |
113 | 0 | BOTAN_ASSERT_NOMSG(leaf.size() == params.n()); |
114 | | |
115 | | // Use the `out` parameter as intermediate buffer for left/right nodes |
116 | | // while traversing the tree. |
117 | 0 | copy_mem(out, leaf); |
118 | | |
119 | | // Views into either `auth_path` or `out` depending on the tree location. |
120 | 0 | StrongSpan<const SphincsTreeNode> left; |
121 | 0 | StrongSpan<const SphincsTreeNode> right; |
122 | |
|
123 | 0 | BufferSlicer auth_path(authentication_path); |
124 | | |
125 | | // The leaf is put in the left or right buffer, depending on its indexes parity. |
126 | | // Same for the first node of the authentication path |
127 | |
|
128 | 0 | for(TreeLayerIndex i(0); i < total_tree_height; i++) { |
129 | | // The input of the hash function takes the current node and the node |
130 | | // given in the authentication path. If the current node is a right node |
131 | | // in the tree (i.e. its leaf index is uneven) the hash function inputs |
132 | | // must be swapped. |
133 | 0 | left = out; |
134 | 0 | right = auth_path.take<SphincsTreeNode>(params.n()); |
135 | |
|
136 | 0 | if((leaf_idx & 1) == 1U) { |
137 | 0 | std::swap(left, right); |
138 | 0 | } |
139 | |
|
140 | 0 | leaf_idx /= 2; |
141 | 0 | idx_offset /= 2; |
142 | 0 | tree_address.set_tree_height(i + 1).set_tree_index(leaf_idx + idx_offset); |
143 | |
|
144 | 0 | hashes.T(out, tree_address, left, right); |
145 | 0 | } |
146 | |
|
147 | 0 | BOTAN_ASSERT_NOMSG(auth_path.empty()); |
148 | 0 | } |
149 | | |
150 | | } // namespace Botan |