Harden Merkle Tree against second pre-image attacks (#4925)

* merkle-tree: Harden against second pre-image attacks

* core/chacha: Bump test golden hash
This commit is contained in:
Trent Nelson 2019-07-08 19:00:06 -06:00 committed by GitHub
parent 563c42b829
commit 1feb9bea21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 10 deletions

View File

@ -133,7 +133,7 @@ mod tests {
hasher.hash(&buf[..size]);
// golden needs to be updated if blob stuff changes....
let golden: Hash = "E2HZjSC6VgH4nmEiTbMDATTeBcFjwSYz7QYvU7doGNhD"
let golden: Hash = "53P7UXH7JstJa994fZsfuyr7nrRDrDDpvS3WSPvMUs45"
.parse()
.unwrap();

View File

@ -1,4 +1,22 @@
use solana_sdk::hash::{hash, hashv, Hash};
use solana_sdk::hash::{hashv, Hash};
// We need to discern between leaf and intermediate nodes to prevent trivial second
// pre-image attacks.
// https://flawed.net.nz/2018/02/21/attacking-merkle-trees-with-a-second-preimage-attack
const LEAF_PREFIX: &[u8] = &[0];
const INTERMEDIATE_PREFIX: &[u8] = &[1];
macro_rules! hash_leaf {
{$d:ident} => {
hashv(&[LEAF_PREFIX, $d])
}
}
macro_rules! hash_intermediate {
{$l:ident, $r:ident} => {
hashv(&[INTERMEDIATE_PREFIX, $l.as_ref(), $r.as_ref()])
}
}
#[derive(Debug)]
pub struct MerkleTree {
@ -32,7 +50,7 @@ impl<'a> Proof<'a> {
let result = self.0.iter().try_fold(candidate, |candidate, pe| {
let lsib = pe.1.unwrap_or(&candidate);
let rsib = pe.2.unwrap_or(&candidate);
let hash = hashv(&[lsib.as_ref(), rsib.as_ref()]);
let hash = hash_intermediate!(lsib, rsib);
if hash == *pe.0 {
Some(hash)
@ -74,7 +92,7 @@ impl MerkleTree {
};
for item in items {
let hash = hash(item);
let hash = hash_leaf!(item);
mt.nodes.push(hash);
}
@ -93,7 +111,7 @@ impl MerkleTree {
&mt.nodes[prev_level_start + prev_level_idx]
};
let hash = hashv(&[lsib.as_ref(), rsib.as_ref()]);
let hash = hash_intermediate!(lsib, rsib);
mt.nodes.push(hash);
}
prev_level_start = level_start;
@ -168,7 +186,7 @@ mod tests {
fn test_tree_from_one() {
let input = b"test";
let mt = MerkleTree::new(&[input]);
let expected = hash(input);
let expected = hash_leaf!(input);
assert_eq!(mt.get_root(), Some(&expected));
}
@ -176,8 +194,9 @@ mod tests {
fn test_tree_from_many() {
let mt = MerkleTree::new(TEST);
// This golden hash will need to be updated whenever the contents of `TEST` change in any
// way, including addition, removal and reordering
let bytes = hex::decode("7e6791d2b9a6338e1446ad4776f267b97e7e4ed968ae2592cfd9c1607bd7dbbb")
// way, including addition, removal and reordering or any of the tree calculation algo
// changes
let bytes = hex::decode("b40c847546fdceea166f927fc46c5ca33c3638236a36275c1346d3dffb84e1bc")
.unwrap();
let expected = Hash::new(&bytes);
assert_eq!(mt.get_root(), Some(&expected));
@ -201,7 +220,7 @@ mod tests {
fn test_path_verify_good() {
let mt = MerkleTree::new(TEST);
for (i, s) in TEST.iter().enumerate() {
let hash = hash(s);
let hash = hash_leaf!(s);
let path = mt.find_path(i).unwrap();
assert!(path.verify(hash));
}
@ -211,7 +230,7 @@ mod tests {
fn test_path_verify_bad() {
let mt = MerkleTree::new(TEST);
for (i, s) in BAD.iter().enumerate() {
let hash = hash(s);
let hash = hash_leaf!(s);
let path = mt.find_path(i).unwrap();
assert!(!path.verify(hash));
}