Separate the leafs hash domain from the internal nodes' one (#22)

* Separate the leafs hash domain from the internal nodes' one.

This change prevents an internal node from having the same hash as a leaf,
and thus from potentially ending up with two different trees with the same root hash.

Close #20

* Align with the CT spec, and allows building a tree from an empty dataset.

Fixes #20. Prepares #24.
This commit is contained in:
Romain Ruetschi
2016-12-19 16:14:41 +01:00
committed by Frederic Jacobs
parent bebbcde461
commit bce59c9f39
7 changed files with 114 additions and 67 deletions

View File

@ -30,7 +30,7 @@ fn bench_small_str_tree(b: &mut Bencher) {
#[bench] #[bench]
fn bench_small_str_proof_gen(b: &mut Bencher) { fn bench_small_str_proof_gen(b: &mut Bencher) {
let values = vec!["one", "two", "three", "four"]; let values = vec!["one", "two", "three", "four"];
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
b.iter(|| { b.iter(|| {
for value in &values { for value in &values {
@ -43,7 +43,7 @@ fn bench_small_str_proof_gen(b: &mut Bencher) {
#[bench] #[bench]
fn bench_small_str_proof_check(b: &mut Bencher) { fn bench_small_str_proof_check(b: &mut Bencher) {
let values = vec!["one", "two", "three", "four"]; let values = vec!["one", "two", "three", "four"];
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
let proofs = values.iter().map(|v| tree.gen_proof(v).unwrap()).collect::<Vec<_>>(); let proofs = values.iter().map(|v| tree.gen_proof(v).unwrap()).collect::<Vec<_>>();
b.iter(|| { b.iter(|| {
@ -63,7 +63,8 @@ fn bench_big_rnd_tree(b: &mut Bencher) {
} }
b.iter(|| { b.iter(|| {
MerkleTree::from_vec(digest, values.clone()).unwrap() let tree = MerkleTree::from_vec(digest, values.clone());
test::black_box(tree)
}); });
} }
@ -76,7 +77,7 @@ fn bench_big_rnd_proof_gen(b: &mut Bencher) {
rng.fill_bytes(&mut v); rng.fill_bytes(&mut v);
} }
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
b.iter(|| { b.iter(|| {
for value in &values { for value in &values {
@ -95,8 +96,10 @@ fn bench_big_rnd_proof_check(b: &mut Bencher) {
rng.fill_bytes(&mut v); rng.fill_bytes(&mut v);
} }
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
let proofs = values.into_iter().map(|v| tree.gen_proof(v).unwrap()).collect::<Vec<_>>(); let proofs = values.into_iter()
.map(|v| tree.gen_proof(v).unwrap())
.collect::<Vec<_>>();
b.iter(|| { b.iter(|| {
for proof in &proofs { for proof in &proofs {
@ -114,7 +117,7 @@ fn bench_big_rnd_iter(b: &mut Bencher) {
rng.fill_bytes(&mut v); rng.fill_bytes(&mut v);
} }
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
b.iter(|| { b.iter(|| {
for value in &tree { for value in &tree {

View File

@ -1,27 +1,38 @@
use ring::digest::{ Algorithm, Context, digest, Digest }; use ring::digest::{ Algorithm, Context, Digest, digest };
/// The sole purpose of this trait is to extend the standard /// The sole purpose of this trait is to extend the standard
/// `ring::algo::Algorithm` type with a couple utility functions. /// `ring::algo::Algorithm` type with a couple utility functions.
pub trait HashUtils { pub trait HashUtils {
/// Compute the hash the given byte array /// Compute the hash of the empty string
fn hash_bytes(&'static self, bytes: &AsRef<[u8]>) -> Digest; fn hash_empty(&'static self) -> Digest;
/// Compute the hash of the given leaf
fn hash_leaf(&'static self, bytes: &AsRef<[u8]>) -> Digest;
/// Compute the hash of the concatenation of `left` and `right`. /// Compute the hash of the concatenation of `left` and `right`.
// XXX: This is overly generic temporarily to make refactoring easier. // XXX: This is overly generic temporarily to make refactoring easier.
// TODO: Give `left` and `right` type &Digest. // TODO: Give `left` and `right` type &Digest.
fn combine_hashes(&'static self, left: &AsRef<[u8]>, right: &AsRef<[u8]>) -> Digest; fn hash_nodes(&'static self, left: &AsRef<[u8]>, right: &AsRef<[u8]>) -> Digest;
} }
impl HashUtils for Algorithm { impl HashUtils for Algorithm {
fn hash_bytes(&'static self, bytes: &AsRef<[u8]>) -> Digest { fn hash_empty(&'static self) -> Digest {
digest(self, bytes.as_ref()) digest(self, &[])
} }
fn combine_hashes(&'static self, left: &AsRef<[u8]>, right: &AsRef<[u8]>) -> Digest { fn hash_leaf(&'static self, leaf: &AsRef<[u8]>) -> Digest {
let mut ctx = Context::new(self); let mut ctx = Context::new(self);
ctx.update(&[0x00]);
ctx.update(leaf.as_ref());
ctx.finish()
}
fn hash_nodes(&'static self, left: &AsRef<[u8]>, right: &AsRef<[u8]>) -> Digest {
let mut ctx = Context::new(self);
ctx.update(&[0x01]);
ctx.update(left.as_ref()); ctx.update(left.as_ref());
ctx.update(right.as_ref()); ctx.update(right.as_ref());
ctx.finish() ctx.finish()

View File

@ -28,11 +28,16 @@ impl <T> MerkleTree<T> {
/// Constructs a Merkle Tree from a vector of data blocks. /// Constructs a Merkle Tree from a vector of data blocks.
/// Returns `None` if `values` is empty. /// Returns `None` if `values` is empty.
pub fn from_vec(algorithm: &'static Algorithm, values: Vec<T>) -> Option<Self> pub fn from_vec(algorithm: &'static Algorithm, values: Vec<T>) -> Self
where T: AsRef<[u8]> { where T: AsRef<[u8]> {
if values.is_empty() { if values.is_empty() {
return None return MerkleTree {
algorithm: algorithm,
root: Tree::empty(algorithm.hash_empty()),
height: 0,
count: 0
};
} }
let count = values.len(); let count = values.len();
@ -40,7 +45,7 @@ impl <T> MerkleTree<T> {
let mut cur = Vec::with_capacity(count); let mut cur = Vec::with_capacity(count);
for v in values { for v in values {
let leaf = Tree::make_leaf(algorithm, v); let leaf = Tree::new_leaf(algorithm, v);
cur.push(leaf); cur.push(leaf);
} }
@ -54,7 +59,7 @@ impl <T> MerkleTree<T> {
let left = cur.remove(0); let left = cur.remove(0);
let right = cur.remove(0); let right = cur.remove(0);
let combined_hash = algorithm.combine_hashes( let combined_hash = algorithm.hash_nodes(
left.hash(), left.hash(),
right.hash() right.hash()
); );
@ -74,16 +79,16 @@ impl <T> MerkleTree<T> {
cur = next; cur = next;
} }
assert!(cur.len() == 1); debug_assert!(cur.len() == 1);
let root = cur.remove(0); let root = cur.remove(0);
Some(MerkleTree { MerkleTree {
algorithm: algorithm, algorithm: algorithm,
root: root, root: root,
height: height, height: height,
count: count count: count
}) }
} }
/// Returns the root hash of Merkle tree /// Returns the root hash of Merkle tree
@ -107,9 +112,9 @@ impl <T> MerkleTree<T> {
where T: AsRef<[u8]> { where T: AsRef<[u8]> {
let root_hash = self.root_hash().clone(); let root_hash = self.root_hash().clone();
let node_hash = self.algorithm.hash_bytes(&value.as_ref()); let leaf_hash = self.algorithm.hash_leaf(&value.as_ref());
Lemma::new(&self.root, node_hash.as_ref()).map(|lemma| Lemma::new(&self.root, leaf_hash.as_ref()).map(|lemma|
Proof::new(self.algorithm, root_hash, lemma, value) Proof::new(self.algorithm, root_hash, lemma, value)
) )
} }

View File

@ -56,13 +56,13 @@ impl <T> Proof<T> {
false, false,
Some(Positioned::Left(ref hash)) => { Some(Positioned::Left(ref hash)) => {
let combined = self.algorithm.combine_hashes(hash, &sub.node_hash); let combined = self.algorithm.hash_nodes(hash, &sub.node_hash);
let hashes_match = combined.as_ref() == lemma.node_hash.as_slice(); let hashes_match = combined.as_ref() == lemma.node_hash.as_slice();
hashes_match && self.validate_lemma(sub) hashes_match && self.validate_lemma(sub)
} }
Some(Positioned::Right(ref hash)) => { Some(Positioned::Right(ref hash)) => {
let combined = self.algorithm.combine_hashes(&sub.node_hash, hash); let combined = self.algorithm.hash_nodes(&sub.node_hash, hash);
let hashes_match = combined.as_ref() == lemma.node_hash.as_slice(); let hashes_match = combined.as_ref() == lemma.node_hash.as_slice();
hashes_match && self.validate_lemma(sub) hashes_match && self.validate_lemma(sub)
} }
@ -91,6 +91,9 @@ impl Lemma {
where T: AsRef<[u8]> { where T: AsRef<[u8]> {
match *tree { match *tree {
Tree::Empty {.. } =>
None,
Tree::Leaf { ref hash, .. } => Tree::Leaf { ref hash, .. } =>
Lemma::new_leaf_proof(hash, needle), Lemma::new_leaf_proof(hash, needle),

View File

@ -15,19 +15,19 @@ fn test_from_str_vec() {
let values = vec!["one", "two", "three", "four"]; let values = vec!["one", "two", "three", "four"];
let hashes = vec![ let hashes = vec![
digest.hash_bytes(&values[0].as_bytes()), digest.hash_leaf(&values[0].as_bytes()),
digest.hash_bytes(&values[1].as_bytes()), digest.hash_leaf(&values[1].as_bytes()),
digest.hash_bytes(&values[2].as_bytes()), digest.hash_leaf(&values[2].as_bytes()),
digest.hash_bytes(&values[3].as_bytes()) digest.hash_leaf(&values[3].as_bytes())
]; ];
let count = values.len(); let count = values.len();
let tree = MerkleTree::from_vec(digest, values).unwrap(); let tree = MerkleTree::from_vec(digest, values);
let h01 = digest.combine_hashes(&hashes[0], &hashes[1]); let h01 = digest.hash_nodes(&hashes[0], &hashes[1]);
let h23 = digest.combine_hashes(&hashes[2], &hashes[3]); let h23 = digest.hash_nodes(&hashes[2], &hashes[3]);
let root_hash = digest.combine_hashes(&h01, &h23); let root_hash = digest.hash_nodes(&h01, &h23);
assert_eq!(tree.count(), count); assert_eq!(tree.count(), count);
assert_eq!(tree.height(), 2); assert_eq!(tree.height(), 2);
@ -36,18 +36,21 @@ fn test_from_str_vec() {
#[test] #[test]
#[should_panic]
fn test_from_vec_empty() { fn test_from_vec_empty() {
let values: Vec<Vec<u8>> = vec![]; let values: Vec<Vec<u8>> = vec![];
MerkleTree::from_vec(digest, values).unwrap(); let tree = MerkleTree::from_vec(digest, values);
let empty_hash: Vec<u8> = digest.hash_empty().as_ref().into();
let root_hash= tree.root_hash().clone();
assert_eq!(root_hash, empty_hash);
} }
#[test] #[test]
fn test_from_vec1() { fn test_from_vec1() {
let values = vec!["hello, world".to_string()]; let values = vec!["hello, world".to_string()];
let tree = MerkleTree::from_vec(digest, values).unwrap(); let tree = MerkleTree::from_vec(digest, values);
let root_hash = &digest.hash_bytes(&"hello, world".as_bytes()); let root_hash = &digest.hash_leaf(&"hello, world".as_bytes());
assert_eq!(tree.count(), 1); assert_eq!(tree.count(), 1);
assert_eq!(tree.height(), 0); assert_eq!(tree.height(), 0);
@ -58,17 +61,17 @@ fn test_from_vec1() {
#[test] #[test]
fn test_from_vec3() { fn test_from_vec3() {
let values = vec![vec![1], vec![2], vec![3]]; let values = vec![vec![1], vec![2], vec![3]];
let tree = MerkleTree::from_vec(digest, values).unwrap(); let tree = MerkleTree::from_vec(digest, values);
let hashes = vec![ let hashes = vec![
digest.hash_bytes(&vec![1]), digest.hash_leaf(&vec![1]),
digest.hash_bytes(&vec![2]), digest.hash_leaf(&vec![2]),
digest.hash_bytes(&vec![3]) digest.hash_leaf(&vec![3])
]; ];
let h01 = digest.combine_hashes(&hashes[0], &hashes[1]); let h01 = digest.hash_nodes(&hashes[0], &hashes[1]);
let h2 = &hashes[2]; let h2 = &hashes[2];
let root_hash = &digest.combine_hashes(&h01, h2); let root_hash = &digest.hash_nodes(&h01, h2);
assert_eq!(tree.count(), 3); assert_eq!(tree.count(), 3);
assert_eq!(tree.height(), 2); assert_eq!(tree.height(), 2);
@ -78,20 +81,20 @@ fn test_from_vec3() {
#[test] #[test]
fn test_from_vec9() { fn test_from_vec9() {
let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>(); let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
let hashes = values.iter().map(|v| digest.hash_bytes(v)).collect::<Vec<_>>(); let hashes = values.iter().map(|v| digest.hash_leaf(v)).collect::<Vec<_>>();
let h01 = digest.combine_hashes(&hashes[0], &hashes[1]); let h01 = digest.hash_nodes(&hashes[0], &hashes[1]);
let h23 = digest.combine_hashes(&hashes[2], &hashes[3]); let h23 = digest.hash_nodes(&hashes[2], &hashes[3]);
let h45 = digest.combine_hashes(&hashes[4], &hashes[5]); let h45 = digest.hash_nodes(&hashes[4], &hashes[5]);
let h67 = digest.combine_hashes(&hashes[6], &hashes[7]); let h67 = digest.hash_nodes(&hashes[6], &hashes[7]);
let h8 = &hashes[8]; let h8 = &hashes[8];
let h0123 = digest.combine_hashes(&h01, &h23); let h0123 = digest.hash_nodes(&h01, &h23);
let h4567 = digest.combine_hashes(&h45, &h67); let h4567 = digest.hash_nodes(&h45, &h67);
let h1to7 = digest.combine_hashes(&h0123, &h4567); let h1to7 = digest.hash_nodes(&h0123, &h4567);
let root_hash = &digest.combine_hashes(&h1to7, h8); let root_hash = &digest.hash_nodes(&h1to7, h8);
assert_eq!(tree.count(), 9); assert_eq!(tree.count(), 9);
assert_eq!(tree.height(), 4); assert_eq!(tree.height(), 4);
@ -101,7 +104,7 @@ fn test_from_vec9() {
#[test] #[test]
fn test_valid_proof() { fn test_valid_proof() {
let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>(); let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
let root_hash = tree.root_hash(); let root_hash = tree.root_hash();
for value in values { for value in values {
@ -115,7 +118,7 @@ fn test_valid_proof() {
#[test] #[test]
fn test_valid_proof_str() { fn test_valid_proof_str() {
let values = vec!["Hello", "my", "name", "is", "Rusty"]; let values = vec!["Hello", "my", "name", "is", "Rusty"];
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
let root_hash = tree.root_hash(); let root_hash = tree.root_hash();
let value = "Rusty"; let value = "Rusty";
@ -129,10 +132,10 @@ fn test_valid_proof_str() {
#[test] #[test]
fn test_wrong_proof() { fn test_wrong_proof() {
let values1 = vec![vec![1], vec![2], vec![3], vec![4]]; let values1 = vec![vec![1], vec![2], vec![3], vec![4]];
let tree1 = MerkleTree::from_vec(digest, values1.clone()).unwrap(); let tree1 = MerkleTree::from_vec(digest, values1.clone());
let values2 = vec![vec![4], vec![5], vec![6], vec![7]]; let values2 = vec![vec![4], vec![5], vec![6], vec![7]];
let tree2 = MerkleTree::from_vec(digest, values2.clone()).unwrap(); let tree2 = MerkleTree::from_vec(digest, values2.clone());
let root_hash = tree2.root_hash(); let root_hash = tree2.root_hash();
@ -147,7 +150,7 @@ fn test_wrong_proof() {
#[test] #[test]
fn test_mutate_proof_first_lemma() { fn test_mutate_proof_first_lemma() {
let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>(); let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
let root_hash = tree.root_hash(); let root_hash = tree.root_hash();
let mut i = 0; let mut i = 0;
@ -177,7 +180,7 @@ fn test_mutate_proof_first_lemma() {
#[test] #[test]
fn test_tree_iter() { fn test_tree_iter() {
let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>(); let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
let iter = tree.iter().map(|x| x.clone()).collect::<Vec<_>>(); let iter = tree.iter().map(|x| x.clone()).collect::<Vec<_>>();
assert_eq!(values, iter); assert_eq!(values, iter);
@ -186,7 +189,7 @@ fn test_tree_iter() {
#[test] #[test]
fn test_tree_into_iter() { fn test_tree_into_iter() {
let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>(); let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
let iter = tree.into_iter().map(|x| x.clone()).collect::<Vec<_>>(); let iter = tree.into_iter().map(|x| x.clone()).collect::<Vec<_>>();
assert_eq!(values, iter); assert_eq!(values, iter);
@ -195,7 +198,7 @@ fn test_tree_into_iter() {
#[test] #[test]
fn test_tree_into_iter_loop() { fn test_tree_into_iter_loop() {
let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>(); let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
let mut collected = Vec::new(); let mut collected = Vec::new();
@ -209,7 +212,7 @@ fn test_tree_into_iter_loop() {
#[test] #[test]
fn test_tree_into_iter_loop_borrow() { fn test_tree_into_iter_loop_borrow() {
let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>(); let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
let mut collected = Vec::new(); let mut collected = Vec::new();

View File

@ -12,6 +12,10 @@ pub use proof::{
/// Binary Tree where leaves hold a stand-alone value. /// Binary Tree where leaves hold a stand-alone value.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Tree<T> { pub enum Tree<T> {
Empty {
hash: Vec<u8>
},
Leaf { Leaf {
hash: Vec<u8>, hash: Vec<u8>,
value: T value: T
@ -26,6 +30,13 @@ pub enum Tree<T> {
impl <T> Tree<T> { impl <T> Tree<T> {
/// Create an empty tree
pub fn empty(hash: Digest) -> Self {
Tree::Empty {
hash: hash.as_ref().into()
}
}
/// Create a new tree /// Create a new tree
pub fn new(hash: Digest, value: T) -> Self { pub fn new(hash: Digest, value: T) -> Self {
Tree::Leaf { Tree::Leaf {
@ -35,18 +46,19 @@ impl <T> Tree<T> {
} }
/// Create a new leaf /// Create a new leaf
pub fn make_leaf(algo: &'static Algorithm, value: T) -> Tree<T> pub fn new_leaf(algo: &'static Algorithm, value: T) -> Tree<T>
where T: AsRef<[u8]> { where T: AsRef<[u8]> {
let hash = algo.hash_bytes(&value.as_ref()); let hash = algo.hash_leaf(&value.as_ref());
Tree::new(hash, value) Tree::new(hash, value)
} }
/// Returns a hash from the tree. /// Returns a hash from the tree.
pub fn hash(&self) -> &Vec<u8> { pub fn hash(&self) -> &Vec<u8> {
match *self { match *self {
Tree::Leaf { ref hash, .. } | Tree::Node { ref hash, .. } => Tree::Empty { ref hash } => hash,
hash Tree::Leaf { ref hash, .. } => hash,
Tree::Node { ref hash, .. } => hash
} }
} }
@ -81,6 +93,11 @@ impl <'a, T> LeavesIterator<'a, T> {
fn add_left(&mut self, mut tree: &'a Tree<T>) { fn add_left(&mut self, mut tree: &'a Tree<T>) {
loop { loop {
match *tree { match *tree {
Tree::Empty { .. } => {
self.current_value = None;
break;
},
Tree::Node { ref left, ref right, .. } => { Tree::Node { ref left, ref right, .. } => {
self.right_nodes.push(right); self.right_nodes.push(right);
tree = left; tree = left;
@ -135,6 +152,11 @@ impl <T> LeavesIntoIterator<T> {
fn add_left(&mut self, mut tree: Tree<T>) { fn add_left(&mut self, mut tree: Tree<T>) {
loop { loop {
match tree { match tree {
Tree::Empty { .. } => {
self.current_value = None;
break;
},
Tree::Node { left, right, .. } => { Tree::Node { left, right, .. } => {
self.right_nodes.push(*right); self.right_nodes.push(*right);
tree = *left; tree = *left;

View File

@ -16,7 +16,7 @@ static digest: &'static Algorithm = &SHA512;
fn test_protobuf_inverse() { fn test_protobuf_inverse() {
let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>(); let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap(); let tree = MerkleTree::from_vec(digest, values.clone());
for value in values { for value in values {
let proof = tree.gen_proof(value).unwrap(); let proof = tree.gen_proof(value).unwrap();