| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_LIB_DIGEST_MERKLE_TREE_H_ |
| #define SRC_LIB_DIGEST_MERKLE_TREE_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <zircon/compiler.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <memory> |
| |
| #include <fbl/alloc_checker.h> |
| #include <fbl/macros.h> |
| |
| #include "src/lib/digest/digest.h" |
| #include "src/lib/digest/hash-list.h" |
| |
| namespace digest { |
| namespace internal { |
| |
| // |digest::internal::MerkleTree| contains common Merkle tree code. Callers MUST NOT use this |
| // class directly. See |digest::MerkleTreeCreator| and |digest::MerkleTreeVerifier| below. |
| template <typename T, typename VP, class MT, class HL> |
| class MerkleTree { |
| public: |
| MerkleTree() = default; |
| virtual ~MerkleTree() = default; |
| DISALLOW_COPY_ASSIGN_AND_MOVE(MerkleTree); |
| |
| size_t GetNodeSize() const { return hash_list_.GetNodeSize(); } |
| void SetNodeSize(size_t node_size) { hash_list_.SetNodeSize(node_size); } |
| |
| // When set to |true| the hash lists in the tree will not contain padding. |
| void SetUseCompactFormat(bool use_compact_format); |
| bool GetUseCompactFormat() { return use_compact_format_; } |
| |
| // Returns true if |data_off| is aligned to a node boundary. |
| bool IsAligned(size_t data_off) const { return hash_list_.IsAligned(data_off); } |
| |
| // Modifies |data_off| and |buf_len| to be aligned to the minimum number of nodes that covered |
| // their original range. |
| zx_status_t Align(size_t *data_off, size_t *buf_len) const { |
| return hash_list_.Align(data_off, buf_len); |
| } |
| |
| // Sets the length of data this hash list will represent. This will allocate all levels of the |
| // Merkle tree, including the root digest. |
| zx_status_t SetDataLength(size_t data_len); |
| |
| // Returns the minimum size needed to hold a Merkle tree for the given |data_len|. The tree |
| // consists of all the nodes containing the digests of child nodes. It does NOT include the root |
| // digest, which must be passed to |Verify| after a trust decision has been made. This means that |
| // when the |data_len| is less than |NodeSize|, this method will return 0. |
| size_t GetTreeLength() const; |
| |
| // Registers |tree| as a Merkle tree for |data_len_| bytes of data, rooted by a digest of given by |
| // |root|. |
| zx_status_t SetTree(VP tree, size_t tree_len, VP root, size_t root_len); |
| |
| protected: |
| // The Merkle tree can be thought of as a singly linked list of HashLists. Each |hash_list_| reads |
| // data to produce a list of digests, which in turn becomes the data for the |hash_list_| in the |
| // |next_| layer of the tree, until the last layer, which produces the root digest. |
| HL hash_list_; |
| std::unique_ptr<MT> next_; |
| |
| private: |
| // Whether the hash lists should be stored compactly or with padding. |
| bool use_compact_format_ = false; |
| }; |
| |
| } // namespace internal |
| |
| // |digest::MerkleTreeCreator| creates Merkle trees for data. |
| // Example (without error checking): |
| // MerkleTreeCreator creator; |
| // creator.SetDataLength(data_len); |
| // size_t tree_len = creator.GetTreeLength(); |
| // uint8_t *tree = malloc(tree_len); // or other allocation routine |
| // uint8_t root[Digest::kLength]; // for storing the resulting root digest |
| // creator.SetTree(tree, tree_len, root, sizeof(root)); |
| // creator.Append(&data[0], partial_len1); |
| // creator.Append(&data[partial_len1], partial_len2); |
| class MerkleTreeCreator |
| : public internal::MerkleTree<uint8_t, void *, MerkleTreeCreator, HashListCreator> { |
| public: |
| // Convenience method to create and return a Merkle tree for the given |data| via |out_tree| and |
| // |out_root|. |
| static zx_status_t Create(const void *data, size_t data_len, std::unique_ptr<uint8_t[]> *out_tree, |
| size_t *out_tree_len, Digest *out_root); |
| |
| // Reads |buf_len| bytes of data from |buf| and appends digests to the hash |list|. |
| zx_status_t Append(const void *buf, size_t buf_len); |
| }; |
| |
| // |digest::MerkleTreeVerifier| verifies data against a Merkle tree. |
| // Example (without error checking): |
| // MerkleTreeVerifier verifier; |
| // verifier.SetDataLength(data_len); |
| // verifier.SetTree(tree, tree_len, root.get(), root.len()); |
| // verifier.Align(&data_off, &partial_len); |
| // return verifier.Verify(&data[data_off], partial_len) == ZX_OK; |
| class MerkleTreeVerifier : public internal::MerkleTree<const uint8_t, const void *, |
| MerkleTreeVerifier, HashListVerifier> { |
| public: |
| // Convenience method to verify the integrity of the node-aligned |buf| at |data_off| using the |
| // Merkle |tree| and |root|. |
| static zx_status_t Verify(const void *buf, size_t buf_len, size_t data_off, size_t data_len, |
| const void *tree, size_t tree_len, const Digest &root); |
| |
| // Reads |buf_len| bytes of data from |buf|, calculates digests for each node of data, and |
| // compares them to the digests stored in the Merkle tree. |data_off| must be node-aligned. |
| // |buf_len| must be node-aligned, or reach the end of the data. See also |Align|. |
| zx_status_t Verify(const void *buf, size_t buf_len, size_t data_off); |
| }; |
| |
| // Convenience method for calculating the minimum size needed to hold a Merkle tree for the given |
| // |data_size|. It does NOT include room for the root digest. |
| // |
| // Panics if |node_size| does not satisfy |NodeDigest::IsValidNodeSize|. |
| // |use_compact_format| specifies if the size should not include padding in the hash lists. |
| size_t CalculateMerkleTreeSize(size_t data_size, size_t node_size, bool use_compact_format); |
| |
| } // namespace digest |
| |
| #endif // SRC_LIB_DIGEST_MERKLE_TREE_H_ |