Skip to main content

hanfei_fa/
hasher.rs

1use digest::Digest;
2use sha2::{Sha256, Sha512};
3use sha3::Sha3_256;
4use blake2::Blake2b512;
5use std::fmt;
6
7/// Supported hash algorithms.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum HashAlgorithm {
10    Sha256,
11    Sha512,
12    Sha3_256,
13    Blake2b,
14    Blake3,
15}
16
17impl HashAlgorithm {
18    /// Expected hex digest length.
19    pub fn hash_length(&self) -> usize {
20        match self {
21            Self::Sha256 => 64,
22            Self::Sha512 => 128,
23            Self::Sha3_256 => 64,
24            Self::Blake2b => 128,
25            Self::Blake3 => 64,
26        }
27    }
28}
29
30impl fmt::Display for HashAlgorithm {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        match self {
33            Self::Sha256 => write!(f, "sha256"),
34            Self::Sha512 => write!(f, "sha512"),
35            Self::Sha3_256 => write!(f, "sha3_256"),
36            Self::Blake2b => write!(f, "blake2b"),
37            Self::Blake3 => write!(f, "blake3"),
38        }
39    }
40}
41
42/// Compute the hex-encoded hash of data.
43pub fn compute_hash(data: &[u8], algorithm: HashAlgorithm) -> String {
44    match algorithm {
45        HashAlgorithm::Sha256 => {
46            let mut h = Sha256::new();
47            h.update(data);
48            hex::encode(h.finalize())
49        }
50        HashAlgorithm::Sha512 => {
51            let mut h = Sha512::new();
52            h.update(data);
53            hex::encode(h.finalize())
54        }
55        HashAlgorithm::Sha3_256 => {
56            let mut h = Sha3_256::new();
57            h.update(data);
58            hex::encode(h.finalize())
59        }
60        HashAlgorithm::Blake2b => {
61            let mut h = Blake2b512::new();
62            h.update(data);
63            hex::encode(h.finalize())
64        }
65        HashAlgorithm::Blake3 => {
66            let h = blake3::hash(data);
67            h.to_hex().to_string()
68        }
69    }
70}
71
72/// Hash a pair of child hashes to produce a parent hash (Merkle internal node).
73pub fn hash_pair(left: &str, right: &str, algorithm: HashAlgorithm) -> String {
74    let combined = format!("{}{}", left, right);
75    compute_hash(combined.as_bytes(), algorithm)
76}
77
78/// Verify that data matches an expected hash.
79pub fn verify_hash(data: &[u8], expected: &str, algorithm: HashAlgorithm) -> bool {
80    compute_hash(data, algorithm) == expected
81}
82
83/// Detect algorithm from hex digest length.
84/// Returns SHA-256 for ambiguous 64-char digests (SHA-256/SHA3-256/BLAKE3).
85pub fn detect_algorithm(hash_str: &str) -> Option<HashAlgorithm> {
86    match hash_str.len() {
87        64 => Some(HashAlgorithm::Sha256),
88        128 => Some(HashAlgorithm::Sha512), // or Blake2b — ambiguous
89        _ => None,
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_sha256_deterministic() {
99        let h1 = compute_hash(b"hello", HashAlgorithm::Sha256);
100        let h2 = compute_hash(b"hello", HashAlgorithm::Sha256);
101        assert_eq!(h1, h2);
102        assert_eq!(h1.len(), 64);
103    }
104
105    #[test]
106    fn test_different_data_different_hash() {
107        let h1 = compute_hash(b"a", HashAlgorithm::Sha256);
108        let h2 = compute_hash(b"b", HashAlgorithm::Sha256);
109        assert_ne!(h1, h2);
110    }
111
112    #[test]
113    fn test_all_algorithms() {
114        for algo in [
115            HashAlgorithm::Sha256,
116            HashAlgorithm::Sha512,
117            HashAlgorithm::Sha3_256,
118            HashAlgorithm::Blake2b,
119            HashAlgorithm::Blake3,
120        ] {
121            let h = compute_hash(b"test", algo);
122            assert_eq!(h.len(), algo.hash_length(), "wrong length for {algo}");
123        }
124    }
125
126    #[test]
127    fn test_hash_pair_order_matters() {
128        let a = compute_hash(b"left", HashAlgorithm::Sha256);
129        let b = compute_hash(b"right", HashAlgorithm::Sha256);
130        assert_ne!(hash_pair(&a, &b, HashAlgorithm::Sha256), hash_pair(&b, &a, HashAlgorithm::Sha256));
131    }
132
133    #[test]
134    fn test_verify_hash() {
135        let h = compute_hash(b"payload", HashAlgorithm::Sha256);
136        assert!(verify_hash(b"payload", &h, HashAlgorithm::Sha256));
137        assert!(!verify_hash(b"wrong", &h, HashAlgorithm::Sha256));
138    }
139
140    #[test]
141    fn test_blake3_differs_from_sha256() {
142        let b3 = compute_hash(b"hello", HashAlgorithm::Blake3);
143        let sha = compute_hash(b"hello", HashAlgorithm::Sha256);
144        assert_ne!(b3, sha);
145        assert_eq!(b3.len(), 64);
146    }
147}