Coverage Report

Created: 2024-11-29 06:10

/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