use crate::version::Version;
use crate::version::Version::{Google, RfcDraft14};
use ring::digest;
use super::{TREE_LEAF_TWEAK, TREE_NODE_TWEAK};
type Data = Vec<u8>;
type Hash = Data;
pub struct MerkleTree {
levels: Vec<Vec<Data>>,
algorithm: &'static digest::Algorithm,
version: Version,
}
impl MerkleTree {
pub fn new(version: Version) -> MerkleTree {
match version {
Google => MerkleTree::new_sha512_google(),
RfcDraft14 => MerkleTree::new_sha512_ietf(),
}
}
fn new_sha512_ietf() -> MerkleTree {
MerkleTree {
levels: vec![vec![]],
algorithm: &digest::SHA512,
version: RfcDraft14,
}
}
fn new_sha512_google() -> MerkleTree {
MerkleTree {
levels: vec![vec![]],
algorithm: &digest::SHA512,
version: Google,
}
}
pub fn push_leaf(&mut self, data: &[u8]) {
let hash = self.hash_leaf(data);
self.levels[0].push(hash);
}
pub fn get_paths(&self, mut index: usize) -> Vec<u8> {
let mut paths = Vec::with_capacity(self.levels.len() * self.algorithm.output_len());
let mut level = 0;
while !self.levels[level].is_empty() {
let sibling = if index % 2 == 0 { index + 1 } else { index - 1 };
paths.extend(self.levels[level][sibling].clone());
level += 1;
index /= 2;
}
assert!(level <= 32, "impossible: PATH depth {} exceeds 32", level);
paths
}
pub fn compute_root(&mut self) -> Hash {
assert!(
!self.levels[0].is_empty(),
"Must have at least one leaf to hash!"
);
let mut level = 0;
let mut node_count = self.levels[0].len();
while node_count > 1 {
level += 1;
if self.levels.len() < level + 1 {
self.levels.push(vec![]);
}
if node_count % 2 != 0 {
self.levels[level - 1].push(vec![0; self.algorithm.output_len()]);
node_count += 1;
}
node_count /= 2;
for i in 0..node_count {
let hash = self.hash_nodes(
&self.levels[level - 1][i * 2],
&self.levels[level - 1][(i * 2) + 1],
);
self.levels[level].push(hash);
}
}
assert_eq!(self.levels[level].len(), 1);
let result = self.levels[level].pop().unwrap();
self.finalize_output(result)
}
pub fn reset(&mut self) {
for level in &mut self.levels {
level.clear();
}
}
pub fn is_empty(&self) -> bool {
self.levels[0].is_empty()
}
fn hash_leaf(&self, leaf: &[u8]) -> Data {
self.hash(&[TREE_LEAF_TWEAK, leaf])
}
fn hash_nodes(&self, first: &[u8], second: &[u8]) -> Data {
self.hash(&[TREE_NODE_TWEAK, first, second])
}
fn hash(&self, to_hash: &[&[u8]]) -> Data {
let mut ctx = digest::Context::new(self.algorithm);
for data in to_hash {
ctx.update(data);
}
Data::from(ctx.finish().as_ref())
}
pub fn root_from_paths(&self, mut index: usize, data: &[u8], paths: &[u8]) -> Hash {
let mut hash = self.hash_leaf(data);
assert_eq!(paths.len() % self.algorithm.output_len(), 0);
for path in paths.chunks(self.algorithm.output_len()) {
let mut ctx = digest::Context::new(self.algorithm);
ctx.update(TREE_NODE_TWEAK);
if index & 1 == 0 {
ctx.update(&hash);
ctx.update(path);
} else {
ctx.update(path);
ctx.update(&hash);
}
hash = Hash::from(ctx.finish().as_ref());
index >>= 1;
}
self.finalize_output(hash)
}
#[inline]
fn finalize_output(&self, data: Hash) -> Hash {
match self.version {
RfcDraft14 => data[0..32].into(),
Google => data,
}
}
}
#[cfg(test)]
mod test {
use crate::merkle::*;
fn for_both_versions<F>(mut test_fn: F)
where
F: FnMut(MerkleTree),
{
test_fn(MerkleTree::new_sha512_ietf());
test_fn(MerkleTree::new_sha512_google());
}
fn test_paths_with_num(num: usize) {
for_both_versions(|mut tree| {
for i in 0..num {
tree.push_leaf(&[i as u8]);
}
let root = tree.compute_root();
for i in 0..num {
let paths: Vec<u8> = tree.get_paths(i);
let computed_root = tree.root_from_paths(i, &[i as u8], &paths);
assert_eq!(
root, computed_root,
"inequality: {:?} {:?} {:?}",
root, computed_root, i
);
}
});
}
#[test]
fn power_of_two() {
test_paths_with_num(2);
test_paths_with_num(4);
test_paths_with_num(8);
test_paths_with_num(16);
}
#[test]
fn not_power_of_two() {
test_paths_with_num(1);
test_paths_with_num(20);
}
#[test]
#[should_panic(expected = "Must have at least one leaf to hash!")]
fn test_empty_tree_google_panics() {
let mut tree = MerkleTree::new_sha512_google();
tree.compute_root(); }
#[test]
#[should_panic(expected = "Must have at least one leaf to hash!")]
fn test_empty_tree_ietf_panics() {
let mut tree = MerkleTree::new_sha512_ietf();
tree.compute_root(); }
#[test]
fn test_single_leaf() {
for_both_versions(|mut tree| {
let test_data = vec![1, 2, 3, 4];
tree.push_leaf(&test_data);
let root = tree.compute_root();
let mut expected = tree.hash_leaf(&test_data);
expected = tree.finalize_output(expected);
assert_eq!(
root, expected,
"Root of single-leaf tree should be the leaf hash"
);
})
}
#[test]
fn test_different_leaf_order() {
for_both_versions(|mut tree| {
tree.push_leaf(&[1, 2, 3]);
tree.push_leaf(&[4, 5, 6]);
let root1 = tree.compute_root();
tree.reset();
tree.push_leaf(&[4, 5, 6]);
tree.push_leaf(&[1, 2, 3]);
let root2 = tree.compute_root();
assert_ne!(
root1, root2,
"Trees with different leaf order should have different roots"
);
})
}
#[test]
fn test_tree_reset() {
for_both_versions(|mut tree| {
tree.push_leaf(&[1, 2, 3]);
tree.push_leaf(&[4, 5, 6]);
let root = tree.compute_root();
assert!(!root.is_empty(), "Root of tree has data");
tree.reset();
assert!(tree.is_empty(), "Root of reset tree should be empty");
})
}
#[test]
fn test_path_verification() {
for_both_versions(|mut tree| {
let leaves = vec![
vec![1, 2, 3, 4],
vec![5, 6, 7, 8],
vec![9, 10, 11, 12],
vec![13, 14, 15, 16],
];
for leaf in &leaves {
tree.push_leaf(leaf);
}
let expected_root = tree.compute_root();
for (idx, leaf) in leaves.iter().enumerate() {
let paths = tree.get_paths(idx);
let verified_root = tree.root_from_paths(idx, leaf, &paths);
assert_eq!(
verified_root, expected_root,
"Root derived from paths for leaf {} should match the tree root",
idx
);
}
})
}
#[test]
fn test_many_leaves() {
for_both_versions(|mut tree| {
for i in 0..1000 {
tree.push_leaf(&[i as u8, (i >> 8) as u8, (i >> 16) as u8, (i >> 24) as u8]);
}
let root = tree.compute_root();
assert!(
!root.is_empty(),
"Root of tree with many leaves should not be empty"
);
})
}
#[test]
fn test_add_leaves_after_computing_root() {
for_both_versions(|mut tree| {
tree.push_leaf(&[1, 2, 3]);
let root1 = tree.compute_root();
tree.push_leaf(&[4, 5, 6]);
let root2 = tree.compute_root();
assert_ne!(root1, root2, "Root should change after adding new leaves");
})
}
}