rust_crypto_utils/
hashing.rs

1//! Cryptographic hashing module v2.0
2//!
3//! Provides multiple hash algorithms: SHA-256, SHA-3, and BLAKE3.
4
5use serde::{Deserialize, Serialize};
6use sha2::{Sha256, Sha512, Digest as Sha2Digest};
7use sha3::{Sha3_256, Sha3_512};
8use thiserror::Error;
9
10/// Hashing errors
11#[derive(Error, Debug)]
12pub enum HashError {
13    #[error("Invalid hash length")]
14    InvalidLength,
15
16    #[error("Unsupported algorithm")]
17    UnsupportedAlgorithm,
18}
19
20/// Supported hash algorithms
21#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
22pub enum HashAlgorithm {
23    Sha256,
24    Sha512,
25    Sha3_256,
26    Sha3_512,
27    Blake3,
28}
29
30impl HashAlgorithm {
31    /// Get output length in bytes
32    pub fn output_length(&self) -> usize {
33        match self {
34            HashAlgorithm::Sha256 => 32,
35            HashAlgorithm::Sha512 => 64,
36            HashAlgorithm::Sha3_256 => 32,
37            HashAlgorithm::Sha3_512 => 64,
38            HashAlgorithm::Blake3 => 32,
39        }
40    }
41
42    /// Get algorithm name
43    pub fn name(&self) -> &'static str {
44        match self {
45            HashAlgorithm::Sha256 => "SHA-256",
46            HashAlgorithm::Sha512 => "SHA-512",
47            HashAlgorithm::Sha3_256 => "SHA3-256",
48            HashAlgorithm::Sha3_512 => "SHA3-512",
49            HashAlgorithm::Blake3 => "BLAKE3",
50        }
51    }
52}
53
54/// Hash output wrapper
55#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
56pub struct HashOutput {
57    algorithm: HashAlgorithm,
58    bytes: Vec<u8>,
59}
60
61impl HashOutput {
62    /// Create from raw bytes
63    pub fn from_bytes(algorithm: HashAlgorithm, bytes: Vec<u8>) -> Result<Self, HashError> {
64        if bytes.len() != algorithm.output_length() {
65            return Err(HashError::InvalidLength);
66        }
67        Ok(Self { algorithm, bytes })
68    }
69
70    /// Get raw bytes
71    pub fn as_bytes(&self) -> &[u8] {
72        &self.bytes
73    }
74
75    /// Convert to hex string
76    pub fn to_hex(&self) -> String {
77        hex::encode(&self.bytes)
78    }
79
80    /// Get algorithm used
81    pub fn algorithm(&self) -> HashAlgorithm {
82        self.algorithm
83    }
84
85    /// Verify against expected hash (constant-time)
86    pub fn verify(&self, expected: &[u8]) -> bool {
87        constant_time_eq::constant_time_eq(&self.bytes, expected)
88    }
89
90    /// Verify against hex string (constant-time)
91    pub fn verify_hex(&self, expected_hex: &str) -> bool {
92        if let Ok(expected) = hex::decode(expected_hex) {
93            self.verify(&expected)
94        } else {
95            false
96        }
97    }
98}
99
100impl std::fmt::Debug for HashOutput {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        write!(f, "HashOutput({}: {})", self.algorithm.name(), &self.to_hex()[..16])
103    }
104}
105
106/// Universal hasher supporting multiple algorithms
107pub struct Hasher {
108    algorithm: HashAlgorithm,
109}
110
111impl Hasher {
112    /// Create a new hasher with the specified algorithm
113    pub fn new(algorithm: HashAlgorithm) -> Self {
114        Self { algorithm }
115    }
116
117    /// Hash data and return output
118    pub fn hash(&self, data: &[u8]) -> HashOutput {
119        let bytes = match self.algorithm {
120            HashAlgorithm::Sha256 => {
121                let mut hasher = Sha256::new();
122                hasher.update(data);
123                hasher.finalize().to_vec()
124            }
125            HashAlgorithm::Sha512 => {
126                let mut hasher = Sha512::new();
127                hasher.update(data);
128                hasher.finalize().to_vec()
129            }
130            HashAlgorithm::Sha3_256 => {
131                let mut hasher = Sha3_256::new();
132                hasher.update(data);
133                hasher.finalize().to_vec()
134            }
135            HashAlgorithm::Sha3_512 => {
136                let mut hasher = Sha3_512::new();
137                hasher.update(data);
138                hasher.finalize().to_vec()
139            }
140            HashAlgorithm::Blake3 => {
141                let hash = blake3::hash(data);
142                hash.as_bytes().to_vec()
143            }
144        };
145
146        HashOutput {
147            algorithm: self.algorithm,
148            bytes,
149        }
150    }
151
152    /// Hash string data
153    pub fn hash_str(&self, data: &str) -> HashOutput {
154        self.hash(data.as_bytes())
155    }
156
157    /// Hash and return hex string
158    pub fn hash_to_hex(&self, data: &[u8]) -> String {
159        self.hash(data).to_hex()
160    }
161}
162
163impl Default for Hasher {
164    fn default() -> Self {
165        Self::new(HashAlgorithm::Sha256)
166    }
167}
168
169/// Incremental hasher for streaming data
170pub struct IncrementalHasher {
171    state: IncrementalState,
172    algorithm: HashAlgorithm,
173}
174
175enum IncrementalState {
176    Sha256(Sha256),
177    Sha512(Sha512),
178    Sha3_256(Sha3_256),
179    Sha3_512(Sha3_512),
180    Blake3(blake3::Hasher),
181}
182
183impl IncrementalHasher {
184    /// Create a new incremental hasher
185    pub fn new(algorithm: HashAlgorithm) -> Self {
186        let state = match algorithm {
187            HashAlgorithm::Sha256 => IncrementalState::Sha256(Sha256::new()),
188            HashAlgorithm::Sha512 => IncrementalState::Sha512(Sha512::new()),
189            HashAlgorithm::Sha3_256 => IncrementalState::Sha3_256(Sha3_256::new()),
190            HashAlgorithm::Sha3_512 => IncrementalState::Sha3_512(Sha3_512::new()),
191            HashAlgorithm::Blake3 => IncrementalState::Blake3(blake3::Hasher::new()),
192        };
193        Self { state, algorithm }
194    }
195
196    /// Update with more data
197    pub fn update(&mut self, data: &[u8]) {
198        match &mut self.state {
199            IncrementalState::Sha256(h) => h.update(data),
200            IncrementalState::Sha512(h) => h.update(data),
201            IncrementalState::Sha3_256(h) => h.update(data),
202            IncrementalState::Sha3_512(h) => h.update(data),
203            IncrementalState::Blake3(h) => { h.update(data); }
204        }
205    }
206
207    /// Finalize and return hash output
208    pub fn finalize(self) -> HashOutput {
209        let bytes = match self.state {
210            IncrementalState::Sha256(h) => h.finalize().to_vec(),
211            IncrementalState::Sha512(h) => h.finalize().to_vec(),
212            IncrementalState::Sha3_256(h) => h.finalize().to_vec(),
213            IncrementalState::Sha3_512(h) => h.finalize().to_vec(),
214            IncrementalState::Blake3(h) => h.finalize().as_bytes().to_vec(),
215        };
216
217        HashOutput {
218            algorithm: self.algorithm,
219            bytes,
220        }
221    }
222}
223
224/// Convenience functions
225pub fn sha256(data: &[u8]) -> HashOutput {
226    Hasher::new(HashAlgorithm::Sha256).hash(data)
227}
228
229pub fn sha3_256(data: &[u8]) -> HashOutput {
230    Hasher::new(HashAlgorithm::Sha3_256).hash(data)
231}
232
233pub fn blake3(data: &[u8]) -> HashOutput {
234    Hasher::new(HashAlgorithm::Blake3).hash(data)
235}
236
237#[cfg(test)]
238mod tests {
239    use super::*;
240
241    #[test]
242    fn test_sha256() {
243        let hasher = Hasher::new(HashAlgorithm::Sha256);
244        let hash = hasher.hash(b"hello world");
245        assert_eq!(hash.as_bytes().len(), 32);
246        assert_eq!(hash.algorithm(), HashAlgorithm::Sha256);
247    }
248
249    #[test]
250    fn test_sha3_256() {
251        let hasher = Hasher::new(HashAlgorithm::Sha3_256);
252        let hash = hasher.hash(b"hello world");
253        assert_eq!(hash.as_bytes().len(), 32);
254        assert_eq!(hash.algorithm(), HashAlgorithm::Sha3_256);
255    }
256
257    #[test]
258    fn test_blake3() {
259        let hasher = Hasher::new(HashAlgorithm::Blake3);
260        let hash = hasher.hash(b"hello world");
261        assert_eq!(hash.as_bytes().len(), 32);
262        assert_eq!(hash.algorithm(), HashAlgorithm::Blake3);
263    }
264
265    #[test]
266    fn test_different_algorithms_different_output() {
267        let data = b"test data";
268        let sha256 = Hasher::new(HashAlgorithm::Sha256).hash(data);
269        let sha3 = Hasher::new(HashAlgorithm::Sha3_256).hash(data);
270        let blake = Hasher::new(HashAlgorithm::Blake3).hash(data);
271
272        assert_ne!(sha256.as_bytes(), sha3.as_bytes());
273        assert_ne!(sha256.as_bytes(), blake.as_bytes());
274        assert_ne!(sha3.as_bytes(), blake.as_bytes());
275    }
276
277    #[test]
278    fn test_verify() {
279        let hasher = Hasher::new(HashAlgorithm::Sha256);
280        let hash = hasher.hash(b"password");
281
282        assert!(hash.verify(hash.as_bytes()));
283        assert!(!hash.verify(b"wrong hash"));
284    }
285
286    #[test]
287    fn test_verify_hex() {
288        let hasher = Hasher::new(HashAlgorithm::Sha256);
289        let hash = hasher.hash(b"test");
290        let hex = hash.to_hex();
291
292        assert!(hash.verify_hex(&hex));
293        assert!(!hash.verify_hex("0000"));
294    }
295
296    #[test]
297    fn test_incremental_hasher() {
298        let data = b"hello world";
299
300        // One-shot hash
301        let one_shot = Hasher::new(HashAlgorithm::Sha256).hash(data);
302
303        // Incremental hash
304        let mut incremental = IncrementalHasher::new(HashAlgorithm::Sha256);
305        incremental.update(b"hello ");
306        incremental.update(b"world");
307        let result = incremental.finalize();
308
309        assert_eq!(one_shot.as_bytes(), result.as_bytes());
310    }
311
312    #[test]
313    fn test_convenience_functions() {
314        let data = b"test";
315
316        let h1 = sha256(data);
317        let h2 = sha3_256(data);
318        let h3 = blake3(data);
319
320        assert_eq!(h1.algorithm(), HashAlgorithm::Sha256);
321        assert_eq!(h2.algorithm(), HashAlgorithm::Sha3_256);
322        assert_eq!(h3.algorithm(), HashAlgorithm::Blake3);
323    }
324
325    #[test]
326    fn test_hex_conversion() {
327        let hash = sha256(b"test");
328        let hex = hash.to_hex();
329        assert_eq!(hex.len(), 64); // 32 bytes = 64 hex chars
330    }
331}