1use digest::Digest;
2use sha2::{Sha256, Sha512};
3use sha3::Sha3_256;
4use blake2::Blake2b512;
5use std::fmt;
6
7#[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 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
42pub 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
72pub 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
78pub fn verify_hash(data: &[u8], expected: &str, algorithm: HashAlgorithm) -> bool {
80 compute_hash(data, algorithm) == expected
81}
82
83pub fn detect_algorithm(hash_str: &str) -> Option<HashAlgorithm> {
86 match hash_str.len() {
87 64 => Some(HashAlgorithm::Sha256),
88 128 => Some(HashAlgorithm::Sha512), _ => 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}