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:
@ -133,7 +133,7 @@ mod tests {
|
|||||||
hasher.hash(&buf[..size]);
|
hasher.hash(&buf[..size]);
|
||||||
|
|
||||||
// golden needs to be updated if blob stuff changes....
|
// golden needs to be updated if blob stuff changes....
|
||||||
let golden: Hash = "E2HZjSC6VgH4nmEiTbMDATTeBcFjwSYz7QYvU7doGNhD"
|
let golden: Hash = "53P7UXH7JstJa994fZsfuyr7nrRDrDDpvS3WSPvMUs45"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -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)]
|
#[derive(Debug)]
|
||||||
pub struct MerkleTree {
|
pub struct MerkleTree {
|
||||||
@ -32,7 +50,7 @@ impl<'a> Proof<'a> {
|
|||||||
let result = self.0.iter().try_fold(candidate, |candidate, pe| {
|
let result = self.0.iter().try_fold(candidate, |candidate, pe| {
|
||||||
let lsib = pe.1.unwrap_or(&candidate);
|
let lsib = pe.1.unwrap_or(&candidate);
|
||||||
let rsib = pe.2.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 {
|
if hash == *pe.0 {
|
||||||
Some(hash)
|
Some(hash)
|
||||||
@ -74,7 +92,7 @@ impl MerkleTree {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
let hash = hash(item);
|
let hash = hash_leaf!(item);
|
||||||
mt.nodes.push(hash);
|
mt.nodes.push(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +111,7 @@ impl MerkleTree {
|
|||||||
&mt.nodes[prev_level_start + prev_level_idx]
|
&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);
|
mt.nodes.push(hash);
|
||||||
}
|
}
|
||||||
prev_level_start = level_start;
|
prev_level_start = level_start;
|
||||||
@ -168,7 +186,7 @@ mod tests {
|
|||||||
fn test_tree_from_one() {
|
fn test_tree_from_one() {
|
||||||
let input = b"test";
|
let input = b"test";
|
||||||
let mt = MerkleTree::new(&[input]);
|
let mt = MerkleTree::new(&[input]);
|
||||||
let expected = hash(input);
|
let expected = hash_leaf!(input);
|
||||||
assert_eq!(mt.get_root(), Some(&expected));
|
assert_eq!(mt.get_root(), Some(&expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,8 +194,9 @@ mod tests {
|
|||||||
fn test_tree_from_many() {
|
fn test_tree_from_many() {
|
||||||
let mt = MerkleTree::new(TEST);
|
let mt = MerkleTree::new(TEST);
|
||||||
// This golden hash will need to be updated whenever the contents of `TEST` change in any
|
// This golden hash will need to be updated whenever the contents of `TEST` change in any
|
||||||
// way, including addition, removal and reordering
|
// way, including addition, removal and reordering or any of the tree calculation algo
|
||||||
let bytes = hex::decode("7e6791d2b9a6338e1446ad4776f267b97e7e4ed968ae2592cfd9c1607bd7dbbb")
|
// changes
|
||||||
|
let bytes = hex::decode("b40c847546fdceea166f927fc46c5ca33c3638236a36275c1346d3dffb84e1bc")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let expected = Hash::new(&bytes);
|
let expected = Hash::new(&bytes);
|
||||||
assert_eq!(mt.get_root(), Some(&expected));
|
assert_eq!(mt.get_root(), Some(&expected));
|
||||||
@ -201,7 +220,7 @@ mod tests {
|
|||||||
fn test_path_verify_good() {
|
fn test_path_verify_good() {
|
||||||
let mt = MerkleTree::new(TEST);
|
let mt = MerkleTree::new(TEST);
|
||||||
for (i, s) in TEST.iter().enumerate() {
|
for (i, s) in TEST.iter().enumerate() {
|
||||||
let hash = hash(s);
|
let hash = hash_leaf!(s);
|
||||||
let path = mt.find_path(i).unwrap();
|
let path = mt.find_path(i).unwrap();
|
||||||
assert!(path.verify(hash));
|
assert!(path.verify(hash));
|
||||||
}
|
}
|
||||||
@ -211,7 +230,7 @@ mod tests {
|
|||||||
fn test_path_verify_bad() {
|
fn test_path_verify_bad() {
|
||||||
let mt = MerkleTree::new(TEST);
|
let mt = MerkleTree::new(TEST);
|
||||||
for (i, s) in BAD.iter().enumerate() {
|
for (i, s) in BAD.iter().enumerate() {
|
||||||
let hash = hash(s);
|
let hash = hash_leaf!(s);
|
||||||
let path = mt.find_path(i).unwrap();
|
let path = mt.find_path(i).unwrap();
|
||||||
assert!(!path.verify(hash));
|
assert!(!path.verify(hash));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user