diff --git a/.vscode/settings.json b/.vscode/settings.json index 03f2751..cc49285 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "hasher", "Merkle", "Precertificate" ], diff --git a/src/merkle/hash.rs b/src/merkle/hash.rs index 8b13789..52a8751 100644 --- a/src/merkle/hash.rs +++ b/src/merkle/hash.rs @@ -1 +1,37 @@ +use sha2::{Digest, Sha256}; +use super::types::MerkleTreeLeaf; + +pub enum MerkleTreeNode<'a> { + Leaf(MerkleTreeLeaf<'a>), + Intermediate(String) +} + +/// Hashes a merkle leaf list. This takes in a slice of u8 slices, each +/// representing the binary form of a merkle tree leaf. This implementation is +/// not optimized for extra-long lists as its implementation is recursive, +/// however, the current-largest CT log as of 11/5/24 would theoretically only +/// go 32 levels deep. +pub fn hash_merkle_leaf_list(leaves: &[&[u8]]) -> [u8; 32] { + let mut hasher = Sha256::default(); + + // The hash of an empty list is the empty hash, no branch necessary + if leaves.len() == 1 { + // The hash of one element is 0x00 + the hash of its data + hasher.update([0x00u8]); + hasher.update(leaves[0]); + } else if leaves.len() > 1 { + // For n > 1, let k be the largest power of two smaller than n (i.e., k < n <= + // 2k) + // MTH(D[n]) = SHA-256(0x01 || MTH(D[0:k]) || MTH(D[k:n])) + + // This can be calculated by taking log_2(n), flooring it, then taking 2 + // to the power of it again + let k = 2usize.pow(leaves.len().ilog2()); + hasher.update([0x01u8]); + hasher.update(hash_merkle_leaf_list(&leaves[0..k])); + hasher.update(hash_merkle_leaf_list(&leaves[k..leaves.len()])); + } + + return hasher.finalize().into(); +}