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_
|