File: merkle_tree.h

package info (click to toggle)
golang-github-google-certificate-transparency 0.0~git20160709.0.0f6e3d1~ds1-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, buster
  • size: 5,676 kB
  • sloc: cpp: 35,278; python: 11,838; java: 1,911; sh: 1,885; makefile: 950; xml: 520; ansic: 225
file content (212 lines) | stat: -rw-r--r-- 8,085 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#ifndef CERT_TRANS_MERKLETREE_MERKLE_TREE_H_
#define CERT_TRANS_MERKLETREE_MERKLE_TREE_H_

#include <stddef.h>
#include <memory>
#include <string>
#include <vector>

#include "merkletree/merkle_tree_interface.h"
#include "merkletree/tree_hasher.h"

class SerialHasher;

// Class for manipulating Merkle Hash Trees, as specified in the
// Certificate Transparency specificationdoc/sunlight.xml
// Implement binary Merkle Hash Trees, using an arbitrary hash function
// provided by the SerialHasher interface.
// Rather than using the hash function directly, we use a TreeHasher that
// does domain separation for leaves and nodes, and thus ensures collision
// resistance.
//
// This class is thread-compatible, but not thread-safe.
class MerkleTree : public cert_trans::MerkleTreeInterface {
 public:
  // The constructor takes a pointer to some concrete hash function
  // instantiation of the SerialHasher abstract class.
  explicit MerkleTree(std::unique_ptr<SerialHasher> hasher);
  virtual ~MerkleTree();

  // Length of a node (i.e., a hash), in bytes.
  virtual size_t NodeSize() const {
    return treehasher_.DigestSize();
  };

  // Number of leaves in the tree.
  virtual size_t LeafCount() const {
    return tree_.empty() ? 0 : NodeCount(0);
  }

  // The |leaf|th leaf hash in the tree. Indexing starts from 1.
  std::string LeafHash(size_t leaf) const {
    if (leaf == 0 || leaf > LeafCount())
      return std::string();
    return Node(0, leaf - 1);
  }

  // Return the leaf hash, but do not append the data to the tree.
  virtual std::string LeafHash(const std::string& data) const {
    return treehasher_.HashLeaf(data);
  }

  // Number of levels. An empty tree has 0 levels, a tree with 1 leaf has
  // 1 level, a tree with 2 leaves has 2 levels, and a tree with n leaves has
  // ceil(log2(n)) + 1 levels.
  virtual size_t LevelCount() const {
    return level_count_;
  }

  // Add a new leaf to the hash tree. Stores the hash of the leaf data in the
  // tree structure, does not store the data itself.
  //
  // (We will evaluate the tree lazily, and not update the root here.)
  //
  // Returns the position of the leaf in the tree. Indexing starts at 1,
  // so position = number of leaves in the tree after this update.
  //
  // @param data Binary input blob
  virtual size_t AddLeaf(const std::string& data);

  // Add a new leaf to the hash tree. Stores the provided hash in the
  // tree structure.  It is the caller's responsibility to ensure that
  // the hash is correct.
  //
  // (We will evaluate the tree lazily, and not update the root here.)
  //
  // Returns the position of the leaf in the tree. Indexing starts at 1,
  // so position = number of leaves in the tree after this update.
  //
  // @param hash leaf hash
  virtual size_t AddLeafHash(const std::string& hash);

  // Get the current root of the tree.
  // Update the root to reflect the current shape of the tree,
  // and return the tree digest.
  //
  // Returns the hash of an empty string if the tree has no leaves
  // (and hence, no root).
  virtual std::string CurrentRoot();

  // Get the root of the tree for a previous snapshot,
  // where snapshot 0 is an empty tree, snapshot 1 is the tree with
  // 1 leaf, etc.
  //
  // Returns an empty string if the snapshot requested is in the future
  // (i.e., the tree is not large enough).
  //
  // @param snapshot point in time (= number of leaves at that point).
  std::string RootAtSnapshot(size_t snapshot);

  // Get the Merkle path from leaf to root.
  //
  // Returns a vector of node hashes, ordered by levels from leaf to root.
  // The first element is the sibling of the leaf hash, and the last element
  // is one below the root.
  // Returns an empty vector if the tree is not large enough
  // or the leaf index is 0.
  //
  // @param leaf the index of the leaf the path is for.
  std::vector<std::string> PathToCurrentRoot(size_t leaf);

  // Get the Merkle path from leaf to the root of a previous snapshot.
  //
  // Returns a vector of node hashes, ordered by levels from leaf to
  // root.  The first element is the sibling of the leaf hash, and the
  // last element is one below the root.  Returns an empty vector if
  // the leaf index is 0, the snapshot requested is in the future or
  // the snapshot tree is not large enough.
  //
  // @param leaf the index of the leaf the path is for.
  // @param snapshot point in time (= number of leaves at that point)
  std::vector<std::string> PathToRootAtSnapshot(size_t leaf, size_t snapshot);

  // Get the Merkle consistency proof between two snapshots.
  // Returns a vector of node hashes, ordered according to levels.
  // Returns an empty vector if snapshot1 is 0, snapshot 1 >= snapshot2,
  // or one of the snapshots requested is in the future.
  //
  // @param snapshot1 the first point in time
  // @param snapshot2 the second point in time
  std::vector<std::string> SnapshotConsistency(size_t snapshot1,
                                               size_t snapshot2);

 private:
  // Update to a given snapshot, return the root.
  std::string UpdateToSnapshot(size_t snapshot);
  // Return the root of a past snapshot.
  // If node is not NULL, additionally record the rightmost node
  // for the given snapshot and node_level.
  std::string RecomputePastSnapshot(size_t snapshot, size_t node_level,
                                    std::string* node);
  // Path from a node at a given level (both indexed starting with 0)
  // to the root at a given snapshot.
  std::vector<std::string> PathFromNodeToRootAtSnapshot(size_t node_index,
                                                        size_t level,
                                                        size_t snapshot);
  // Get the |index|-th node at level |level|. Indexing starts at 0;
  // caller is responsible for ensuring tree is sufficiently up to date.
  std::string Node(size_t level, size_t index) const;

  // Get the current root (of the lazily evaluated tree).
  // Caller is responsible for keeping track of the lazy evaluation status.
  std::string Root() const;

  // Get the current node count (of the lazily evaluated tree).
  // Caller is responsible for keeping track of the lazy evaluation status.
  size_t NodeCount(size_t level) const;

  // Last node of the given level.
  std::string LastNode(size_t level) const;

  // Pop the last node of the level.
  void PopBack(size_t level);

  // Append a node to the level.
  void PushBack(size_t level, std::string node);

  // Start a new level.
  void AddLevel();

  // Current level count of the lazily evaluated tree.
  size_t LazyLevelCount() const;
  // A container for nodes, organized according to levels and sorted
  // left-to-right in each level. tree_[0] is the leaf level, etc.
  // The hash of nodes tree_[i][j] and tree_[i][j+1] (j even) is stored
  // at tree_[i+1][j/2]. When tree_[i][j] is the last node of the level with
  // no right sibling, we store its dummy copy: tree_[i+1][j/2] = tree_[i][j].
  //
  // For example, a tree with 5 leaf hashes a0, a1, a2, a3, a4
  //
  //        __ hash__
  //       |         |
  //    __ h20__     a4
  //   |        |
  //  h10     h11
  //  | |     | |
  // a0 a1   a2 a3
  //
  // is internally represented, top-down
  //
  // --------
  // | hash |                        tree_[3]
  // --------------
  // | h20  | a4  |                  tree_[2]
  // -------------------
  // | h10  | h11 | a4 |             tree_[1]
  // -----------------------------
  // | a0   | a1  | a2 | a3 | a4 |   tree_[0]
  // -----------------------------
  //
  // Since the tree is append-only from the right, at any given point in time,
  // at each level, all nodes computed so far, except possibly the last node,
  // are fixed and will no longer change.
  std::vector<std::string> tree_;
  TreeHasher treehasher_;
  // Number of leaves propagated up to the root,
  // to keep track of lazy evaluation.
  size_t leaves_processed_;
  // The "true" level count for a fully evaluated tree.
  size_t level_count_;
};

#endif  // CERT_TRANS_MERKLETREE_MERKLE_TREE_H_