Port to ring. (#15)

This commit is contained in:
Romain Ruetschi
2016-11-29 21:38:47 +01:00
committed by Frederic Jacobs
parent e621241f2f
commit 4503258724
11 changed files with 128 additions and 137 deletions

View File

@ -14,7 +14,7 @@ homepage = "https://github.com/SpinResearch/merkle.rs"
repository = "https://github.com/SpinResearch/merkle.rs"
[dependencies]
rust-crypto = "^0.2.36"
ring = "0.6.0-alpha"
protobuf = { version = "^1.0.0", optional = true }
[features]

View File

@ -6,17 +6,20 @@ extern crate test;
extern crate rand;
extern crate merkle;
extern crate crypto;
extern crate ring;
use test::Bencher;
use rand::Rng;
use ring::digest::{Algorithm, SHA512};
use merkle::MerkleTree;
use crypto::sha3::Sha3;
#[allow(non_upper_case_globals)]
static digest: &'static Algorithm = &SHA512;
#[bench]
fn bench_small_str_tree(b: &mut Bencher) {
let digest = Sha3::sha3_256();
let values = vec!["one", "two", "three", "four"];
b.iter(|| {
@ -26,7 +29,6 @@ fn bench_small_str_tree(b: &mut Bencher) {
#[bench]
fn bench_small_str_proof_gen(b: &mut Bencher) {
let digest = Sha3::sha3_256();
let values = vec!["one", "two", "three", "four"];
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap();
@ -40,7 +42,6 @@ fn bench_small_str_proof_gen(b: &mut Bencher) {
#[bench]
fn bench_small_str_proof_check(b: &mut Bencher) {
let digest = Sha3::sha3_256();
let values = vec!["one", "two", "three", "four"];
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap();
let proofs = values.iter().map(|v| tree.gen_proof(v).unwrap()).collect::<Vec<_>>();
@ -54,7 +55,6 @@ fn bench_small_str_proof_check(b: &mut Bencher) {
#[bench]
fn bench_big_rnd_tree(b: &mut Bencher) {
let digest = Sha3::sha3_256();
let mut values = vec![vec![0u8; 256]; 160];
let mut rng = rand::IsaacRng::new_unseeded();
@ -69,7 +69,6 @@ fn bench_big_rnd_tree(b: &mut Bencher) {
#[bench]
fn bench_big_rnd_proof_gen(b: &mut Bencher) {
let digest = Sha3::sha3_256();
let mut values = vec![vec![0u8; 256]; 160];
let mut rng = rand::IsaacRng::new_unseeded();
@ -89,7 +88,6 @@ fn bench_big_rnd_proof_gen(b: &mut Bencher) {
#[bench]
fn bench_big_rnd_proof_check(b: &mut Bencher) {
let digest = Sha3::sha3_256();
let mut values = vec![vec![0u8; 256]; 160];
let mut rng = rand::IsaacRng::new_unseeded();

30
src/hashutils.rs Normal file
View File

@ -0,0 +1,30 @@
use ring::digest::{ Algorithm, Context };
/// 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: &[u8]) -> Vec<u8>;
/// Compute the hash of the concatenation of `left` and `right`
fn combine_hashes(&'static self, left: &[u8], right: &[u8]) -> Vec<u8>;
}
impl HashUtils for Algorithm {
fn hash_bytes(&'static self, bytes: &[u8]) -> Vec<u8> {
let mut context = Context::new(self);
context.update(bytes);
context.finish().as_ref().into()
}
fn combine_hashes(&'static self, left: &[u8], right: &[u8]) -> Vec<u8> {
let mut context = Context::new(self);
let combined = [left, right].concat();
context.update(&combined);
context.finish().as_ref().into()
}
}

View File

@ -8,7 +8,7 @@
//! *merkle* implements a Merkle Tree in Rust.
extern crate crypto;
extern crate ring;
#[cfg(feature = "serialization-protobuf")]
extern crate protobuf;
@ -19,7 +19,7 @@ pub use merkletree::MerkleTree;
mod proof;
pub use proof::Proof;
mod merkledigest;
mod hashutils;
mod tree;

View File

@ -1,38 +0,0 @@
use crypto::digest::Digest;
/// The sole purpose of this trait is to extend the standard
/// `crypto::digest::Digest` with a couple utility functions.
pub trait MerkleDigest {
/// Compute the hash the given byte array
fn hash_bytes(&mut self, bytes: &[u8]) -> Vec<u8>;
/// Compute the hash of the concatenation of `left` and `right`
fn combine_hashes(&mut self, left: &[u8], right: &[u8]) -> Vec<u8>;
}
impl <D> MerkleDigest for D where D: Digest {
fn hash_bytes(&mut self, bytes: &[u8]) -> Vec<u8> {
let mut hash = vec![0; self.output_bytes()];
self.reset();
self.input(bytes);
self.result(&mut hash);
self.reset();
hash
}
fn combine_hashes(&mut self, left: &[u8], right: &[u8]) -> Vec<u8> {
let mut hash = vec![0; self.output_bytes()];
self.reset();
self.input(left);
self.input(right);
self.result(&mut hash);
self.reset();
hash
}
}

View File

@ -1,18 +1,19 @@
use crypto::digest::Digest;
use ring::digest::Algorithm;
use tree::Tree;
use merkledigest::MerkleDigest;
use hashutils::HashUtils;
use proof::{ Proof, Lemma };
/// A Merkle tree is a binary tree, with values of type `T` at the leafs,
/// and where every node holds the hash of the concatenation of the hashes of
/// its children nodes.
#[derive(Clone, Debug, PartialEq)]
pub struct MerkleTree<D, T> {
/// The hashing function used by this Merkle tree
digest: D,
#[derive(Clone, Debug)]
pub struct MerkleTree<T> {
/// The hashing algorithm used by this Merkle tree
pub algorithm: &'static Algorithm,
/// The root of the inner binary tree
root: Tree<T>,
@ -24,11 +25,11 @@ pub struct MerkleTree<D, T> {
count: usize
}
impl <D, T> MerkleTree<D, T> where D: Digest + Clone, T: Into<Vec<u8>> + Clone {
impl <T> MerkleTree<T> where T: Into<Vec<u8>> + Clone {
/// Constructs a Merkle Tree from a vector of data blocks.
/// Returns None if `values` is empty.
pub fn from_vec(mut digest: D, values: Vec<T>) -> Option<Self> {
pub fn from_vec(algo: &'static Algorithm, values: Vec<T>) -> Option<Self> {
if values.is_empty() {
return None
}
@ -38,7 +39,7 @@ impl <D, T> MerkleTree<D, T> where D: Digest + Clone, T: Into<Vec<u8>> + Clone {
let mut cur = Vec::with_capacity(count);
for v in values {
let leaf = Tree::make_leaf(&mut digest, v);
let leaf = Tree::make_leaf(algo, v);
cur.push(leaf);
}
@ -52,7 +53,7 @@ impl <D, T> MerkleTree<D, T> where D: Digest + Clone, T: Into<Vec<u8>> + Clone {
let left = cur.remove(0);
let right = cur.remove(0);
let combined_hash = digest.combine_hashes(
let combined_hash = algo.combine_hashes(
left.hash(),
right.hash()
);
@ -77,18 +78,13 @@ impl <D, T> MerkleTree<D, T> where D: Digest + Clone, T: Into<Vec<u8>> + Clone {
let root = cur.remove(0);
Some(MerkleTree {
digest: digest,
algorithm: algo,
root: root,
height: height,
count: count
})
}
/// Returns the hash function used in this Merkle tree
pub fn digest(&self) -> &D {
&self.digest
}
/// Returns the root hash of Merkle tree
pub fn root_hash(&self) -> &Vec<u8> {
self.root.hash()
@ -106,13 +102,12 @@ impl <D, T> MerkleTree<D, T> where D: Digest + Clone, T: Into<Vec<u8>> + Clone {
/// Generate an inclusion proof for the given value.
/// Returns `None` if the given value is not found in the tree.
pub fn gen_proof(&self, value: T) -> Option<Proof<D, T>> {
let mut digest = self.digest.clone();
pub fn gen_proof(&self, value: T) -> Option<Proof<T>> {
let root_hash = self.root_hash().clone();
let node_hash = digest.hash_bytes(&value.clone().into());
let node_hash = self.algorithm.hash_bytes(&value.clone().into());
Lemma::new(&self.root, &node_hash).map(|lemma|
Proof::new(digest, root_hash, lemma, value)
Proof::new(self.algorithm, root_hash, lemma, value)
)
}

View File

@ -1,16 +1,16 @@
use crypto::digest::Digest;
use ring::digest::Algorithm;
use tree::Tree;
use merkledigest::MerkleDigest;
use hashutils::HashUtils;
/// An inclusion proof represent the fact that a `value` is a member
/// of a `MerkleTree` with root hash `root_hash`, and hash function `digest`.
#[derive(Clone, Debug)]
pub struct Proof<D, T> {
pub struct Proof<T> {
/// The hash function used in the original `MerkleTree`
pub digest: D,
/// The hashing algorithm used in the original `MerkleTree`
pub algorithm: &'static Algorithm,
/// The hash of the root of the original `MerkleTree`
pub root_hash: Vec<u8>,
@ -22,12 +22,12 @@ pub struct Proof<D, T> {
pub value: T
}
impl <D, T> Proof<D, T> {
impl <T> Proof<T> {
/// Constructs a new `Proof`
pub fn new(digest: D, root_hash: Vec<u8>, lemma: Lemma, value: T) -> Self {
pub fn new(algo: &'static Algorithm, root_hash: Vec<u8>, lemma: Lemma, value: T) -> Self {
Proof {
digest: digest,
algorithm: algo,
root_hash: root_hash,
lemma: lemma,
value: value
@ -36,15 +36,15 @@ impl <D, T> Proof<D, T> {
/// Checks whether this inclusion proof is well-formed,
/// and whether its root hash matches the given `root_hash`.
pub fn validate(&self, root_hash: &[u8]) -> bool where D: Digest + Clone {
pub fn validate(&self, root_hash: &[u8]) -> bool {
if self.root_hash != root_hash || self.lemma.node_hash != root_hash {
return false
}
self.validate_lemma(&self.lemma, &mut self.digest.clone())
self.validate_lemma(&self.lemma)
}
fn validate_lemma(&self, lemma: &Lemma, digest: &mut D) -> bool where D: Digest {
fn validate_lemma(&self, lemma: &Lemma) -> bool {
match lemma.sub_lemma {
None =>
@ -56,13 +56,13 @@ impl <D, T> Proof<D, T> {
false,
Some(Positioned::Left(ref hash)) => {
let hashes_match = digest.combine_hashes(hash, &sub.node_hash) == lemma.node_hash;
hashes_match && self.validate_lemma(sub, digest)
let hashes_match = self.algorithm.combine_hashes(hash, &sub.node_hash) == lemma.node_hash;
hashes_match && self.validate_lemma(sub)
}
Some(Positioned::Right(ref hash)) => {
let hashes_match = digest.combine_hashes(&sub.node_hash, hash) == lemma.node_hash;
hashes_match && self.validate_lemma(sub, digest)
let hashes_match = self.algorithm.combine_hashes(&sub.node_hash, hash) == lemma.node_hash;
hashes_match && self.validate_lemma(sub)
}
}
@ -85,7 +85,9 @@ pub struct Lemma {
impl Lemma {
/// Attempts to generate a proof that the a value with hash `needle` is a member of the given `tree`.
pub fn new<T>(tree: &Tree<T>, needle: &[u8]) -> Option<Lemma> where T: Into<Vec<u8>> + Clone {
pub fn new<T>(tree: &Tree<T>, needle: &[u8]) -> Option<Lemma>
where T: Into<Vec<u8>> + Clone {
match *tree {
Tree::Leaf { ref hash, .. } =>
Lemma::new_leaf_proof(hash, needle),
@ -107,7 +109,9 @@ impl Lemma {
}
}
fn new_tree_proof<T>(hash: &[u8], needle: &[u8], left: &Tree<T>, right: &Tree<T>) -> Option<Lemma> where T: Into<Vec<u8>> + Clone {
fn new_tree_proof<T>(hash: &[u8], needle: &[u8], left: &Tree<T>, right: &Tree<T>) -> Option<Lemma>
where T: Into<Vec<u8>> + Clone {
Lemma::new(left, needle)
.map(|lemma| {
let right_hash = right.hash().clone();

View File

@ -1,6 +1,8 @@
mod proof;
use ring::digest::Algorithm;
use proof::{ Proof, Lemma, Positioned };
pub use self::proof::{ ProofProto, LemmaProto };
@ -8,13 +10,13 @@ use protobuf::Message;
use protobuf::error::ProtobufResult;
use protobuf::core::parse_from_bytes;
impl <D, T> Proof<D, T> {
impl <T> Proof<T> {
/// Constructs a `Proof` struct from its Protobuf representation.
pub fn from_protobuf(digest: D, proto: ProofProto) -> Option<Self>
pub fn from_protobuf(algorithm: &'static Algorithm, proto: ProofProto) -> Option<Self>
where T: From<Vec<u8>>
{
proto.into_proof(digest)
proto.into_proof(algorithm)
}
/// Encode this `Proof` to its Protobuf representation.
@ -25,10 +27,10 @@ impl <D, T> Proof<D, T> {
}
/// Parse a `Proof` from its Protobuf binary representation.
pub fn parse_from_bytes(bytes: &[u8], digest: D) -> ProtobufResult<Option<Proof<D, T>>>
pub fn parse_from_bytes(bytes: &[u8], algorithm: &'static Algorithm) -> ProtobufResult<Option<Self>>
where T: From<Vec<u8>>
{
parse_from_bytes::<ProofProto>(bytes).map(|proto| proto.into_proof(digest))
parse_from_bytes::<ProofProto>(bytes).map(|proto| proto.into_proof(algorithm))
}
/// Serialize this `Proof` with Protobuf.
@ -40,7 +42,7 @@ impl <D, T> Proof<D, T> {
impl ProofProto {
pub fn from_proof<D, T>(proof: Proof<D, T>) -> Self
pub fn from_proof<T>(proof: Proof<T>) -> Self
where T: Into<Vec<u8>>
{
let mut proto = Self::new();
@ -56,7 +58,7 @@ impl ProofProto {
proto
}
pub fn into_proof<D, T>(mut self, digest: D) -> Option<Proof<D, T>>
pub fn into_proof<T>(mut self, algorithm: &'static Algorithm) -> Option<Proof<T>>
where T: From<Vec<u8>>
{
if !self.has_root_hash() || !self.has_lemma() {
@ -65,7 +67,7 @@ impl ProofProto {
self.take_lemma().into_lemma().map(|lemma| {
Proof::new(
digest,
algorithm,
self.take_root_hash(),
lemma,
self.take_value().into()

View File

@ -1,16 +1,17 @@
#![cfg(test)]
use crypto::sha3::Sha3;
use ring::digest::{ Algorithm, SHA512 };
use merkletree::MerkleTree;
use merkledigest::MerkleDigest;
use hashutils::HashUtils;
use proof::Positioned;
#[allow(non_upper_case_globals)]
static digest: &'static Algorithm = &SHA512;
#[test]
fn test_from_str_vec() {
let mut digest = Sha3::sha3_256();
let values = vec!["one", "two", "three", "four"];
let hashes = vec![
@ -21,7 +22,7 @@ fn test_from_str_vec() {
];
let count = values.len();
let tree = MerkleTree::from_vec(Sha3::sha3_256(), values).unwrap();
let tree = MerkleTree::from_vec(digest, values).unwrap();
let h01 = digest.combine_hashes(&hashes[0], &hashes[1]);
let h23 = digest.combine_hashes(&hashes[2], &hashes[3]);
@ -38,16 +39,15 @@ fn test_from_str_vec() {
#[should_panic]
fn test_from_vec_empty() {
let values: Vec<Vec<u8>> = vec![];
MerkleTree::from_vec(Sha3::sha3_256(), values).unwrap();
MerkleTree::from_vec(digest, values).unwrap();
}
#[test]
fn test_from_vec1() {
let values = vec!["hello, world".to_string()];
let tree = MerkleTree::from_vec(Sha3::sha3_256(), values).unwrap();
let tree = MerkleTree::from_vec(digest, values).unwrap();
let mut d = Sha3::sha3_256();
let root_hash = &d.hash_bytes(&"hello, world".as_bytes());
let root_hash = &digest.hash_bytes(&"hello, world".as_bytes());
assert_eq!(tree.count(), 1);
assert_eq!(tree.height(), 0);
@ -58,19 +58,17 @@ fn test_from_vec1() {
#[test]
fn test_from_vec3() {
let values = vec![vec![1], vec![2], vec![3]];
let tree = MerkleTree::from_vec(Sha3::sha3_256(), values).unwrap();
let mut d = Sha3::sha3_256();
let tree = MerkleTree::from_vec(digest, values).unwrap();
let hashes = vec![
d.hash_bytes(&vec![1]),
d.hash_bytes(&vec![2]),
d.hash_bytes(&vec![3])
digest.hash_bytes(&vec![1]),
digest.hash_bytes(&vec![2]),
digest.hash_bytes(&vec![3])
];
let h01 = &d.combine_hashes(&hashes[0], &hashes[1]);
let h01 = &digest.combine_hashes(&hashes[0], &hashes[1]);
let h2 = &hashes[2];
let root_hash = &d.combine_hashes(h01, h2);
let root_hash = &digest.combine_hashes(h01, h2);
assert_eq!(tree.count(), 3);
assert_eq!(tree.height(), 2);
@ -80,22 +78,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(Sha3::sha3_256(), values.clone()).unwrap();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap();
let mut d = Sha3::sha3_256();
let hashes = values.iter().map(|v| digest.hash_bytes(v)).collect::<Vec<_>>();
let hashes = values.iter().map(|v| d.hash_bytes(v)).collect::<Vec<_>>();
let h01 = &d.combine_hashes(&hashes[0], &hashes[1]);
let h23 = &d.combine_hashes(&hashes[2], &hashes[3]);
let h45 = &d.combine_hashes(&hashes[4], &hashes[5]);
let h67 = &d.combine_hashes(&hashes[6], &hashes[7]);
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 h8 = &hashes[8];
let h0123 = &d.combine_hashes(h01, h23);
let h4567 = &d.combine_hashes(h45, h67);
let h1to7 = &d.combine_hashes(h0123, h4567);
let h0123 = digest.combine_hashes(&h01, &h23);
let h4567 = digest.combine_hashes(&h45, &h67);
let h1to7 = digest.combine_hashes(&h0123, &h4567);
let root_hash = &d.combine_hashes(h1to7, h8);
let root_hash = &digest.combine_hashes(&h1to7, &h8);
assert_eq!(tree.count(), 9);
assert_eq!(tree.height(), 4);
@ -105,7 +101,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(Sha3::sha3_256(), values.clone()).unwrap();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap();
let root_hash = tree.root_hash();
for value in values {
@ -119,7 +115,7 @@ fn test_valid_proof() {
#[test]
fn test_valid_proof_str() {
let values = vec!["Hello", "my", "name", "is", "Rusty"];
let tree = MerkleTree::from_vec(Sha3::sha3_256(), values.clone()).unwrap();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap();
let root_hash = tree.root_hash();
let value = "Rusty";
@ -133,10 +129,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(Sha3::sha3_256(), values1.clone()).unwrap();
let tree1 = MerkleTree::from_vec(digest, values1.clone()).unwrap();
let values2 = vec![vec![4], vec![5], vec![6], vec![7]];
let tree2 = MerkleTree::from_vec(Sha3::sha3_256(), values2.clone()).unwrap();
let tree2 = MerkleTree::from_vec(digest, values2.clone()).unwrap();
let root_hash = tree2.root_hash();
@ -151,7 +147,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(Sha3::sha3_256(), values.clone()).unwrap();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap();
let root_hash = tree.root_hash();
let mut i = 0;

View File

@ -1,6 +1,7 @@
use crypto::digest::Digest;
use merkledigest::MerkleDigest;
use ring::digest::Algorithm;
use hashutils::HashUtils;
pub use proof::{
Proof,
@ -34,8 +35,8 @@ impl <T> Tree<T> where T: Into<Vec<u8>> + Clone {
}
/// Create a new leaf
pub fn make_leaf<D: Digest>(digest: &mut D, value: T) -> Tree<T> {
let hash = digest.hash_bytes(&value.clone().into());
pub fn make_leaf(algo: &'static Algorithm, value: T) -> Tree<T> {
let hash = algo.hash_bytes(&value.clone().into());
Tree::new(hash, value)
}

View File

@ -1,24 +1,27 @@
#![cfg(feature="serialization-protobuf")]
extern crate crypto;
extern crate ring;
extern crate merkle;
extern crate protobuf;
use crypto::sha3::Sha3;
use ring::digest::{ Algorithm, SHA512 };
use merkle::{ MerkleTree, Proof };
#[allow(non_upper_case_globals)]
static digest: &'static Algorithm = &SHA512;
#[test]
fn test_protobuf_inverse() {
let digest = Sha3::sha3_256();
let values = (1..10).map(|x| vec![x]).collect::<Vec<_>>();
let tree = MerkleTree::from_vec(digest.clone(), values.clone()).unwrap();
let tree = MerkleTree::from_vec(digest, values.clone()).unwrap();
for value in values {
let proof = tree.gen_proof(value).unwrap();
let bytes = proof.clone().write_to_bytes().unwrap();
let res = Proof::<Sha3, Vec<u8>>::parse_from_bytes(&bytes, digest).unwrap().unwrap();
let res = Proof::<Vec<u8>>::parse_from_bytes(&bytes, digest).unwrap().unwrap();
assert_eq!(proof.root_hash, res.root_hash);
assert_eq!(proof.value, res.value);