diff --git a/Cargo.toml b/Cargo.toml index 8e2a5ec..ad9ed39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ categories = [ [dependencies] blake2-rfc = "0.2.18" byteorder = "1.2.6" +constant_time_eq = "0.1.3" ed25519-dalek = "0.8.1" failure = "0.1.2" flat-tree = "4.0.1" diff --git a/src/crypto/hash.rs b/src/crypto/hash.rs index 1888c0a..b71b01b 100644 --- a/src/crypto/hash.rs +++ b/src/crypto/hash.rs @@ -1,11 +1,14 @@ pub use blake2_rfc::blake2b::Blake2bResult; +use crate::storage::Node; use blake2_rfc::blake2b::Blake2b; use byteorder::{BigEndian, WriteBytesExt}; // use ed25519_dalek::PublicKey; -use crate::storage::Node; +use constant_time_eq::constant_time_eq; use merkle_tree_stream::Node as NodeTrait; +use pretty_hash::fmt as pretty_fmt; use std::convert::AsRef; +use std::fmt; use std::mem; use std::ops::{Deref, DerefMut}; @@ -14,11 +17,15 @@ const LEAF_TYPE: [u8; 1] = [0x00]; const PARENT_TYPE: [u8; 1] = [0x01]; const ROOT_TYPE: [u8; 1] = [0x02]; //const HYPERCORE: [u8; 9] = *b"hypercore"; +const BLAKE_2_HASH_SIZE: usize = 32; +type StoredHash = [u8; BLAKE_2_HASH_SIZE]; /// `BLAKE2b` hash. -#[derive(Debug, Clone, PartialEq)] +/// uses [blake2_rfc::blake2b::Blake2bResult] to hash its inputs on initalisation, the calculated +/// hash is then constant and can not be changed. +#[derive(Debug, Clone)] pub struct Hash { - hash: Blake2bResult, + hash: StoredHash, } impl Hash { @@ -26,19 +33,19 @@ impl Hash { pub fn from_leaf(data: &[u8]) -> Self { let size = u64_as_be(data.len() as u64); - let mut hasher = Blake2b::new(32); + let mut hasher = Blake2b::new(BLAKE_2_HASH_SIZE); hasher.update(&LEAF_TYPE); hasher.update(&size); hasher.update(data); Self { - hash: hasher.finalize(), + hash: hasher_to_stored_hash(hasher), } } /// Hash two `Leaf` nodes hashes together to form a `Parent` hash. pub fn from_hashes(left: &Node, right: &Node) -> Self { - let (node1, node2) = if left.index <= right.index { + let (node1, node2) = if left <= right { (left, right) } else { (right, left) @@ -46,21 +53,21 @@ impl Hash { let size = u64_as_be((node1.length + node2.length) as u64); - let mut hasher = Blake2b::new(32); + let mut hasher = Blake2b::new(BLAKE_2_HASH_SIZE); hasher.update(&PARENT_TYPE); hasher.update(&size); hasher.update(node1.hash()); hasher.update(node2.hash()); Self { - hash: hasher.finalize(), + hash: hasher_to_stored_hash(hasher), } } // /// Hash a public key. Useful to find the key you're looking for on a public // /// network without leaking the key itself. // pub fn from_key(public_key: PublicKey) -> Self { - // let mut hasher = Blake2b::new(32); + // let mut hasher = Blake2b::new(BLAKE_2_HASH_SIZE); // hasher.update(*HYPERCORE); // hasher.update(public_key.as_bytes()); // Self { @@ -71,7 +78,7 @@ impl Hash { /// Hash a vector of `Root` nodes. // Called `crypto.tree()` in the JS implementation. pub fn from_roots(roots: &[impl AsRef]) -> Self { - let mut hasher = Blake2b::new(32); + let mut hasher = Blake2b::new(BLAKE_2_HASH_SIZE); hasher.update(&ROOT_TYPE); for node in roots { @@ -82,14 +89,37 @@ impl Hash { } Self { - hash: hasher.finalize(), + hash: hasher_to_stored_hash(hasher), + } + } + + pub fn from_bytes(bytes: &[u8]) -> Self { + Self { + hash: slice_to_stored_hash(bytes), } } /// Returns a byte slice of this `Hash`'s contents. pub fn as_bytes(&self) -> &[u8] { - self.hash.as_bytes() + &self.hash[..] + } +} + +fn slice_to_stored_hash(slice: &[u8]) -> StoredHash { + assert!(slice.len() == BLAKE_2_HASH_SIZE); + + let mut stored_hash: StoredHash = [0; BLAKE_2_HASH_SIZE]; + let mut i = 0; + for byte in slice.iter() { + stored_hash[i] = *byte; + i = i + 1; } + + stored_hash +} + +fn hasher_to_stored_hash(hasher: Blake2b) -> StoredHash { + slice_to_stored_hash(hasher.finalize().as_bytes()) } fn u64_as_be(n: u64) -> [u8; 8] { @@ -99,7 +129,7 @@ fn u64_as_be(n: u64) -> [u8; 8] { } impl Deref for Hash { - type Target = Blake2bResult; + type Target = StoredHash; fn deref(&self) -> &Self::Target { &self.hash @@ -112,6 +142,20 @@ impl DerefMut for Hash { } } +impl fmt::Display for Hash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", pretty_fmt(&self.hash[..]).unwrap()) + } +} + +impl PartialEq for Hash { + fn eq(&self, other: &Self) -> bool { + constant_time_eq(&self.hash[..], &other.hash[..]) + } +} + +impl Eq for Hash {} + #[cfg(test)] mod tests { use super::*; @@ -143,8 +187,8 @@ mod tests { fn parent_hash() { let d1: &[u8] = &[0, 1, 2, 3, 4]; let d2: &[u8] = &[42, 43, 44, 45, 46, 47, 48]; - let node1 = Node::new(0, Hash::from_leaf(d1).as_bytes().to_vec(), d1.len()); - let node2 = Node::new(1, Hash::from_leaf(d2).as_bytes().to_vec(), d2.len()); + let node1 = Node::new(0, Hash::from_leaf(d1), d1.len()); + let node2 = Node::new(1, Hash::from_leaf(d2), d2.len()); check_hash( Hash::from_hashes(&node1, &node2), "6fac58578fa385f25a54c0637adaca71fdfddcea885d561f33d80c4487149a14", @@ -159,8 +203,8 @@ mod tests { fn root_hash() { let d1: &[u8] = &[0, 1, 2, 3, 4]; let d2: &[u8] = &[42, 43, 44, 45, 46, 47, 48]; - let node1 = Node::new(0, Hash::from_leaf(d1).as_bytes().to_vec(), d1.len()); - let node2 = Node::new(1, Hash::from_leaf(d2).as_bytes().to_vec(), d2.len()); + let node1 = Node::new(0, Hash::from_leaf(d1), d1.len()); + let node2 = Node::new(1, Hash::from_leaf(d2), d2.len()); check_hash( Hash::from_roots(&[&node1, &node2]), "2d117e0bb15c6e5236b6ce764649baed1c41890da901a015341503146cc20bcd", diff --git a/src/crypto/merkle.rs b/src/crypto/merkle.rs index 37c89ba..5b37746 100644 --- a/src/crypto/merkle.rs +++ b/src/crypto/merkle.rs @@ -6,9 +6,9 @@ use merkle_tree_stream::{ use std::rc::Rc; #[derive(Debug)] -struct H; +struct Hasher; -impl HashMethods for H { +impl HashMethods for Hasher { type Node = Node; type Hash = Hash; @@ -33,7 +33,7 @@ impl HashMethods for H { index: partial.index(), parent: partial.parent, length: partial.len(), - hash: hash.as_bytes().into(), + hash: hash, data, } } @@ -42,7 +42,7 @@ impl HashMethods for H { /// Merkle Tree Stream #[derive(Debug)] pub struct Merkle { - stream: MerkleTreeStream, + stream: MerkleTreeStream, nodes: Vec>, } @@ -58,7 +58,7 @@ impl Merkle { pub fn new() -> Self { Self { nodes: vec![], - stream: MerkleTreeStream::new(H, vec![]), + stream: MerkleTreeStream::new(Hasher, vec![]), } } diff --git a/src/feed.rs b/src/feed.rs index 96fcdff..8d95a72 100644 --- a/src/feed.rs +++ b/src/feed.rs @@ -287,11 +287,9 @@ where let mut visited = vec![]; let mut top = match data { - Some(data) => Node::new( - tree_index(index), - Hash::from_leaf(&data).as_bytes().to_owned(), - data.len(), - ), + Some(data) => { + Node::new(tree_index(index), Hash::from_leaf(&data), data.len()) + } None => proof.nodes.remove(0), }; @@ -322,7 +320,7 @@ where visited.push(top.clone()); let hash = Hash::from_hashes(&top, &node); let len = top.len() + node.len(); - top = Node::new(flat::parent(top.index), hash.as_bytes().into(), len); + top = Node::new(flat::parent(top.index), hash, len); if verify_node(&trusted_node, &top) { self.write(index, data, &visited, None)?; @@ -526,7 +524,7 @@ where let node = self.storage.get_node(2 * index)?; let data = self.storage.get_data(index)?; let data_hash = Hash::from_leaf(&data); - if node.hash == data_hash.as_bytes() { + if node.hash == data_hash { valid_blocks += 1; } else { invalid_blocks += 1; diff --git a/src/lib.rs b/src/lib.rs index f181323..f87a271 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,7 @@ extern crate failure; extern crate blake2_rfc; extern crate byteorder; +extern crate constant_time_eq; extern crate ed25519_dalek; extern crate flat_tree; extern crate merkle_tree_stream; diff --git a/src/storage/node.rs b/src/storage/node.rs index 9297a99..93148c7 100644 --- a/src/storage/node.rs +++ b/src/storage/node.rs @@ -1,12 +1,12 @@ +use crate::crypto::Hash; use crate::Result; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use flat_tree; use merkle_tree_stream::Node as NodeTrait; -use pretty_hash::fmt as pretty_fmt; use std::cmp::Ordering; use std::convert::AsRef; use std::fmt::{self, Display}; -use std::io::Cursor; +use std::io::{Cursor, Seek, SeekFrom}; /// Nodes that are persisted to disk. // TODO: replace `hash: Vec` with `hash: Hash`. This requires patching / @@ -15,7 +15,7 @@ use std::io::Cursor; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Node { pub(crate) index: usize, - pub(crate) hash: Vec, + pub(crate) hash: Hash, pub(crate) length: usize, pub(crate) parent: usize, pub(crate) data: Option>, @@ -24,7 +24,7 @@ pub struct Node { impl Node { /// Create a new instance. // TODO: ensure sizes are correct. - pub fn new(index: usize, hash: Vec, length: usize) -> Self { + pub fn new(index: usize, hash: Hash, length: usize) -> Self { Self { index, hash, @@ -41,16 +41,15 @@ impl Node { ensure!(buffer.len() == 40, "buffer should be 40 bytes"); let parent = flat_tree::parent(index); - let mut reader = Cursor::new(buffer); // TODO: subslice directly, move cursor forward. let capacity = 32; - let mut hash = Vec::with_capacity(capacity); - for _ in 0..capacity { - hash.push(reader.read_u8()?); - } + let hash = Hash::from_bytes(&buffer[..capacity]); + let mut reader = Cursor::new(buffer); + reader.seek(SeekFrom::Start(capacity as u64))?; // TODO: This will blow up on 32 bit systems, because usize can be 32 bits. + // Note: we could stop using usize on any protocol specific parts of code? let length = reader.read_u64::()? as usize; Ok(Self { hash, @@ -64,7 +63,7 @@ impl Node { /// Convert to a buffer that can be written to disk. pub fn to_bytes(&self) -> Result> { let mut writer = Vec::with_capacity(40); - writer.extend_from_slice(&self.hash); + writer.extend_from_slice(&self.hash.as_bytes()); writer.write_u64::(self.length as u64)?; Ok(writer) } @@ -78,7 +77,7 @@ impl NodeTrait for Node { #[inline] fn hash(&self) -> &[u8] { - &self.hash + &self.hash.as_bytes() } #[inline] @@ -109,9 +108,7 @@ impl Display for Node { write!( f, "Node {{ index: {}, hash: {}, length: {} }}", - self.index, - pretty_fmt(&self.hash).unwrap(), - self.length + self.index, self.hash, self.length ) } }