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]
fn bench_small_str_proof_gen(b: &mut Bencher) {
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(|| {
for value in &values {
@ -43,7 +43,7 @@ fn bench_small_str_proof_gen(b: &mut Bencher) {
#[bench]
fn bench_small_str_proof_check(b: &mut Bencher) {
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<_>>();
b.iter(|| {
@ -63,7 +63,8 @@ fn bench_big_rnd_tree(b: &mut Bencher) {
}
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);
}
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap();
let tree = MerkleTree::from_vec(digest, values.clone());
b.iter(|| {
for value in &values {
@ -95,8 +96,10 @@ fn bench_big_rnd_proof_check(b: &mut Bencher) {
rng.fill_bytes(&mut v);
}
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap();
let proofs = values.into_iter().map(|v| tree.gen_proof(v).unwrap()).collect::<Vec<_>>();
let tree = MerkleTree::from_vec(digest, values.clone());
let proofs = values.into_iter()
.map(|v| tree.gen_proof(v).unwrap())
.collect::<Vec<_>>();
b.iter(|| {
for proof in &proofs {
@ -114,7 +117,7 @@ fn bench_big_rnd_iter(b: &mut Bencher) {
rng.fill_bytes(&mut v);
}
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap();
let tree = MerkleTree::from_vec(digest, values.clone());
b.iter(|| {
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
/// `ring::algo::Algorithm` type with a couple utility functions.
pub trait HashUtils {
/// Compute the hash the given byte array
fn hash_bytes(&'static self, bytes: &AsRef<[u8]>) -> Digest;
/// Compute the hash of the empty string
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`.
// XXX: This is overly generic temporarily to make refactoring easier.
// 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 {
fn hash_bytes(&'static self, bytes: &AsRef<[u8]>) -> Digest {
digest(self, bytes.as_ref())
fn hash_empty(&'static self) -> Digest {
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);
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(right.as_ref());
ctx.finish()

View File

@ -28,11 +28,16 @@ impl <T> MerkleTree<T> {
/// Constructs a Merkle Tree from a vector of data blocks.
/// 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]> {
if values.is_empty() {
return None
return MerkleTree {
algorithm: algorithm,
root: Tree::empty(algorithm.hash_empty()),
height: 0,
count: 0
};
}
let count = values.len();
@ -40,7 +45,7 @@ impl <T> MerkleTree<T> {
let mut cur = Vec::with_capacity(count);
for v in values {
let leaf = Tree::make_leaf(algorithm, v);
let leaf = Tree::new_leaf(algorithm, v);
cur.push(leaf);
}
@ -54,7 +59,7 @@ impl <T> MerkleTree<T> {
let left = cur.remove(0);
let right = cur.remove(0);
let combined_hash = algorithm.combine_hashes(
let combined_hash = algorithm.hash_nodes(
left.hash(),
right.hash()
);
@ -74,16 +79,16 @@ impl <T> MerkleTree<T> {
cur = next;
}
assert!(cur.len() == 1);
debug_assert!(cur.len() == 1);
let root = cur.remove(0);
Some(MerkleTree {
MerkleTree {
algorithm: algorithm,
root: root,
height: height,
count: count
})
}
}
/// Returns the root hash of Merkle tree
@ -107,9 +112,9 @@ impl <T> MerkleTree<T> {
where T: AsRef<[u8]> {
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)
)
}

View File

@ -56,13 +56,13 @@ impl <T> Proof<T> {
false,
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();
hashes_match && self.validate_lemma(sub)
}
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();
hashes_match && self.validate_lemma(sub)
}
@ -91,6 +91,9 @@ impl Lemma {
where T: AsRef<[u8]> {
match *tree {
Tree::Empty {.. } =>
None,
Tree::Leaf { ref hash, .. } =>
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 hashes = vec![
digest.hash_bytes(&values[0].as_bytes()),
digest.hash_bytes(&values[1].as_bytes()),
digest.hash_bytes(&values[2].as_bytes()),
digest.hash_bytes(&values[3].as_bytes())
digest.hash_leaf(&values[0].as_bytes()),
digest.hash_leaf(&values[1].as_bytes()),
digest.hash_leaf(&values[2].as_bytes()),
digest.hash_leaf(&values[3].as_bytes())
];
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 h23 = digest.combine_hashes(&hashes[2], &hashes[3]);
let h01 = digest.hash_nodes(&hashes[0], &hashes[1]);
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.height(), 2);
@ -36,18 +36,21 @@ fn test_from_str_vec() {
#[test]
#[should_panic]
fn test_from_vec_empty() {
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]
fn test_from_vec1() {
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.height(), 0);
@ -58,17 +61,17 @@ fn test_from_vec1() {
#[test]
fn test_from_vec3() {
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![
digest.hash_bytes(&vec![1]),
digest.hash_bytes(&vec![2]),
digest.hash_bytes(&vec![3])
digest.hash_leaf(&vec![1]),
digest.hash_leaf(&vec![2]),
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 root_hash = &digest.combine_hashes(&h01, h2);
let root_hash = &digest.hash_nodes(&h01, h2);
assert_eq!(tree.count(), 3);
assert_eq!(tree.height(), 2);
@ -78,20 +81,20 @@ fn test_from_vec3() {
#[test]
fn test_from_vec9() {
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 h23 = digest.combine_hashes(&hashes[2], &hashes[3]);
let h45 = digest.combine_hashes(&hashes[4], &hashes[5]);
let h67 = digest.combine_hashes(&hashes[6], &hashes[7]);
let h01 = digest.hash_nodes(&hashes[0], &hashes[1]);
let h23 = digest.hash_nodes(&hashes[2], &hashes[3]);
let h45 = digest.hash_nodes(&hashes[4], &hashes[5]);
let h67 = digest.hash_nodes(&hashes[6], &hashes[7]);
let h8 = &hashes[8];
let h0123 = digest.combine_hashes(&h01, &h23);
let h4567 = digest.combine_hashes(&h45, &h67);
let h1to7 = digest.combine_hashes(&h0123, &h4567);
let h0123 = digest.hash_nodes(&h01, &h23);
let h4567 = digest.hash_nodes(&h45, &h67);
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.height(), 4);
@ -101,7 +104,7 @@ fn test_from_vec9() {
#[test]
fn test_valid_proof() {
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();
for value in values {
@ -115,7 +118,7 @@ fn test_valid_proof() {
#[test]
fn test_valid_proof_str() {
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 value = "Rusty";
@ -129,10 +132,10 @@ fn test_valid_proof_str() {
#[test]
fn test_wrong_proof() {
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 tree2 = MerkleTree::from_vec(digest, values2.clone()).unwrap();
let tree2 = MerkleTree::from_vec(digest, values2.clone());
let root_hash = tree2.root_hash();
@ -147,7 +150,7 @@ fn test_wrong_proof() {
#[test]
fn test_mutate_proof_first_lemma() {
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 mut i = 0;
@ -177,7 +180,7 @@ fn test_mutate_proof_first_lemma() {
#[test]
fn test_tree_iter() {
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<_>>();
assert_eq!(values, iter);
@ -186,7 +189,7 @@ fn test_tree_iter() {
#[test]
fn test_tree_into_iter() {
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<_>>();
assert_eq!(values, iter);
@ -195,7 +198,7 @@ fn test_tree_into_iter() {
#[test]
fn test_tree_into_iter_loop() {
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();
@ -209,7 +212,7 @@ fn test_tree_into_iter_loop() {
#[test]
fn test_tree_into_iter_loop_borrow() {
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();

View File

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

View File

@ -16,7 +16,7 @@ static digest: &'static Algorithm = &SHA512;
fn test_protobuf_inverse() {
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 {
let proof = tree.gen_proof(value).unwrap();