base_d/
hashing.rs

1use sha2::{Sha224, Sha256, Sha384, Sha512, Digest};
2use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Keccak224, Keccak256, Keccak384, Keccak512};
3use blake2::{Blake2b512, Blake2s256};
4use blake3::Hasher as Blake3Hasher;
5use md5::Md5;
6use twox_hash::{XxHash32, XxHash64};
7use twox_hash::xxhash3_64::Hasher as Xxh3Hash64;
8use twox_hash::xxhash3_128::Hasher as Xxh3Hash128;
9use std::hash::Hasher;
10
11/// Configuration for xxHash algorithms.
12#[derive(Debug, Clone, Default)]
13pub struct XxHashConfig {
14    /// Seed value (0-u64::MAX)
15    pub seed: u64,
16    /// Secret for XXH3 variants (must be >= 136 bytes)
17    pub secret: Option<Vec<u8>>,
18}
19
20impl XxHashConfig {
21    /// Create config with a custom seed.
22    pub fn with_seed(seed: u64) -> Self {
23        Self { seed, secret: None }
24    }
25
26    /// Create config with seed and secret for XXH3 variants.
27    /// Secret must be at least 136 bytes.
28    pub fn with_secret(seed: u64, secret: Vec<u8>) -> Result<Self, String> {
29        if secret.len() < 136 {
30            return Err(format!(
31                "XXH3 secret must be >= 136 bytes, got {}",
32                secret.len()
33            ));
34        }
35        Ok(Self {
36            seed,
37            secret: Some(secret),
38        })
39    }
40}
41
42/// Supported hash algorithms.
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum HashAlgorithm {
45    Md5,
46    Sha224,
47    Sha256,
48    Sha384,
49    Sha512,
50    Sha3_224,
51    Sha3_256,
52    Sha3_384,
53    Sha3_512,
54    Keccak224,
55    Keccak256,
56    Keccak384,
57    Keccak512,
58    Blake2b,
59    Blake2s,
60    Blake3,
61    // CRC variants
62    Crc32,
63    Crc32c,
64    Crc16,
65    Crc64,
66    // xxHash variants
67    XxHash32,
68    XxHash64,
69    XxHash3_64,
70    XxHash3_128,
71}
72
73impl HashAlgorithm {
74    /// Parse hash algorithm from string.
75    pub fn from_str(s: &str) -> Result<Self, String> {
76        match s.to_lowercase().as_str() {
77            "md5" => Ok(HashAlgorithm::Md5),
78            "sha224" | "sha-224" => Ok(HashAlgorithm::Sha224),
79            "sha256" | "sha-256" => Ok(HashAlgorithm::Sha256),
80            "sha384" | "sha-384" => Ok(HashAlgorithm::Sha384),
81            "sha512" | "sha-512" => Ok(HashAlgorithm::Sha512),
82            "sha3-224" | "sha3_224" => Ok(HashAlgorithm::Sha3_224),
83            "sha3-256" | "sha3_256" => Ok(HashAlgorithm::Sha3_256),
84            "sha3-384" | "sha3_384" => Ok(HashAlgorithm::Sha3_384),
85            "sha3-512" | "sha3_512" => Ok(HashAlgorithm::Sha3_512),
86            "keccak224" | "keccak-224" => Ok(HashAlgorithm::Keccak224),
87            "keccak256" | "keccak-256" => Ok(HashAlgorithm::Keccak256),
88            "keccak384" | "keccak-384" => Ok(HashAlgorithm::Keccak384),
89            "keccak512" | "keccak-512" => Ok(HashAlgorithm::Keccak512),
90            "blake2b" | "blake2b-512" => Ok(HashAlgorithm::Blake2b),
91            "blake2s" | "blake2s-256" => Ok(HashAlgorithm::Blake2s),
92            "blake3" => Ok(HashAlgorithm::Blake3),
93            "crc32" => Ok(HashAlgorithm::Crc32),
94            "crc32c" => Ok(HashAlgorithm::Crc32c),
95            "crc16" => Ok(HashAlgorithm::Crc16),
96            "crc64" => Ok(HashAlgorithm::Crc64),
97            "xxhash32" | "xxh32" => Ok(HashAlgorithm::XxHash32),
98            "xxhash64" | "xxh64" => Ok(HashAlgorithm::XxHash64),
99            "xxhash3" | "xxh3" | "xxhash3-64" | "xxh3-64" => Ok(HashAlgorithm::XxHash3_64),
100            "xxhash3-128" | "xxh3-128" => Ok(HashAlgorithm::XxHash3_128),
101            _ => Err(format!("Unknown hash algorithm: {}", s)),
102        }
103    }
104    
105    pub fn as_str(&self) -> &str {
106        match self {
107            HashAlgorithm::Md5 => "md5",
108            HashAlgorithm::Sha224 => "sha224",
109            HashAlgorithm::Sha256 => "sha256",
110            HashAlgorithm::Sha384 => "sha384",
111            HashAlgorithm::Sha512 => "sha512",
112            HashAlgorithm::Sha3_224 => "sha3-224",
113            HashAlgorithm::Sha3_256 => "sha3-256",
114            HashAlgorithm::Sha3_384 => "sha3-384",
115            HashAlgorithm::Sha3_512 => "sha3-512",
116            HashAlgorithm::Keccak224 => "keccak224",
117            HashAlgorithm::Keccak256 => "keccak256",
118            HashAlgorithm::Keccak384 => "keccak384",
119            HashAlgorithm::Keccak512 => "keccak512",
120            HashAlgorithm::Blake2b => "blake2b",
121            HashAlgorithm::Blake2s => "blake2s",
122            HashAlgorithm::Blake3 => "blake3",
123            HashAlgorithm::Crc32 => "crc32",
124            HashAlgorithm::Crc32c => "crc32c",
125            HashAlgorithm::Crc16 => "crc16",
126            HashAlgorithm::Crc64 => "crc64",
127            HashAlgorithm::XxHash32 => "xxhash32",
128            HashAlgorithm::XxHash64 => "xxhash64",
129            HashAlgorithm::XxHash3_64 => "xxhash3-64",
130            HashAlgorithm::XxHash3_128 => "xxhash3-128",
131        }
132    }
133
134    /// Get the output size in bytes for this algorithm.
135    pub fn output_size(&self) -> usize {
136        match self {
137            HashAlgorithm::Md5 => 16,
138            HashAlgorithm::Sha224 => 28,
139            HashAlgorithm::Sha256 => 32,
140            HashAlgorithm::Sha384 => 48,
141            HashAlgorithm::Sha512 => 64,
142            HashAlgorithm::Sha3_224 => 28,
143            HashAlgorithm::Sha3_256 => 32,
144            HashAlgorithm::Sha3_384 => 48,
145            HashAlgorithm::Sha3_512 => 64,
146            HashAlgorithm::Keccak224 => 28,
147            HashAlgorithm::Keccak256 => 32,
148            HashAlgorithm::Keccak384 => 48,
149            HashAlgorithm::Keccak512 => 64,
150            HashAlgorithm::Blake2b => 64,
151            HashAlgorithm::Blake2s => 32,
152            HashAlgorithm::Blake3 => 32,
153            HashAlgorithm::Crc16 => 2,
154            HashAlgorithm::Crc32 => 4,
155            HashAlgorithm::Crc32c => 4,
156            HashAlgorithm::Crc64 => 8,
157            HashAlgorithm::XxHash32 => 4,
158            HashAlgorithm::XxHash64 => 8,
159            HashAlgorithm::XxHash3_64 => 8,
160            HashAlgorithm::XxHash3_128 => 16,
161        }
162    }
163}
164
165/// Compute hash of data using the specified algorithm.
166/// Uses default configuration (seed = 0, no secret).
167pub fn hash(data: &[u8], algorithm: HashAlgorithm) -> Vec<u8> {
168    hash_with_config(data, algorithm, &XxHashConfig::default())
169}
170
171/// Compute hash of data using the specified algorithm with custom configuration.
172pub fn hash_with_config(data: &[u8], algorithm: HashAlgorithm, config: &XxHashConfig) -> Vec<u8> {
173    match algorithm {
174        HashAlgorithm::Md5 => {
175            let mut hasher = Md5::new();
176            hasher.update(data);
177            hasher.finalize().to_vec()
178        }
179        HashAlgorithm::Sha224 => {
180            let mut hasher = Sha224::new();
181            hasher.update(data);
182            hasher.finalize().to_vec()
183        }
184        HashAlgorithm::Sha256 => {
185            let mut hasher = Sha256::new();
186            hasher.update(data);
187            hasher.finalize().to_vec()
188        }
189        HashAlgorithm::Sha384 => {
190            let mut hasher = Sha384::new();
191            hasher.update(data);
192            hasher.finalize().to_vec()
193        }
194        HashAlgorithm::Sha512 => {
195            let mut hasher = Sha512::new();
196            hasher.update(data);
197            hasher.finalize().to_vec()
198        }
199        HashAlgorithm::Sha3_224 => {
200            let mut hasher = Sha3_224::new();
201            hasher.update(data);
202            hasher.finalize().to_vec()
203        }
204        HashAlgorithm::Sha3_256 => {
205            let mut hasher = Sha3_256::new();
206            hasher.update(data);
207            hasher.finalize().to_vec()
208        }
209        HashAlgorithm::Sha3_384 => {
210            let mut hasher = Sha3_384::new();
211            hasher.update(data);
212            hasher.finalize().to_vec()
213        }
214        HashAlgorithm::Sha3_512 => {
215            let mut hasher = Sha3_512::new();
216            hasher.update(data);
217            hasher.finalize().to_vec()
218        }
219        HashAlgorithm::Keccak224 => {
220            let mut hasher = Keccak224::new();
221            hasher.update(data);
222            hasher.finalize().to_vec()
223        }
224        HashAlgorithm::Keccak256 => {
225            let mut hasher = Keccak256::new();
226            hasher.update(data);
227            hasher.finalize().to_vec()
228        }
229        HashAlgorithm::Keccak384 => {
230            let mut hasher = Keccak384::new();
231            hasher.update(data);
232            hasher.finalize().to_vec()
233        }
234        HashAlgorithm::Keccak512 => {
235            let mut hasher = Keccak512::new();
236            hasher.update(data);
237            hasher.finalize().to_vec()
238        }
239        HashAlgorithm::Blake2b => {
240            let mut hasher = Blake2b512::new();
241            hasher.update(data);
242            hasher.finalize().to_vec()
243        }
244        HashAlgorithm::Blake2s => {
245            let mut hasher = Blake2s256::new();
246            hasher.update(data);
247            hasher.finalize().to_vec()
248        }
249        HashAlgorithm::Blake3 => {
250            let mut hasher = Blake3Hasher::new();
251            hasher.update(data);
252            hasher.finalize().as_bytes().to_vec()
253        }
254        HashAlgorithm::Crc16 => {
255            let crc = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
256            let result = crc.checksum(data);
257            result.to_be_bytes().to_vec()
258        }
259        HashAlgorithm::Crc32 => {
260            let crc = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
261            let result = crc.checksum(data);
262            result.to_be_bytes().to_vec()
263        }
264        HashAlgorithm::Crc32c => {
265            let crc = crc::Crc::<u32>::new(&crc::CRC_32_ISCSI);
266            let result = crc.checksum(data);
267            result.to_be_bytes().to_vec()
268        }
269        HashAlgorithm::Crc64 => {
270            let crc = crc::Crc::<u64>::new(&crc::CRC_64_ECMA_182);
271            let result = crc.checksum(data);
272            result.to_be_bytes().to_vec()
273        }
274        HashAlgorithm::XxHash32 => {
275            let mut hasher = XxHash32::with_seed(config.seed as u32);
276            hasher.write(data);
277            (hasher.finish() as u32).to_be_bytes().to_vec()
278        }
279        HashAlgorithm::XxHash64 => {
280            let mut hasher = XxHash64::with_seed(config.seed);
281            hasher.write(data);
282            hasher.finish().to_be_bytes().to_vec()
283        }
284        HashAlgorithm::XxHash3_64 => {
285            let mut hasher = if let Some(ref secret) = config.secret {
286                Xxh3Hash64::with_seed_and_secret(config.seed, secret.as_slice())
287                    .expect("XXH3 secret validation should have been done in XxHashConfig::with_secret")
288            } else {
289                Xxh3Hash64::with_seed(config.seed)
290            };
291            hasher.write(data);
292            hasher.finish().to_be_bytes().to_vec()
293        }
294        HashAlgorithm::XxHash3_128 => {
295            let mut hasher = if let Some(ref secret) = config.secret {
296                Xxh3Hash128::with_seed_and_secret(config.seed, secret.as_slice())
297                    .expect("XXH3 secret validation should have been done in XxHashConfig::with_secret")
298            } else {
299                Xxh3Hash128::with_seed(config.seed)
300            };
301            hasher.write(data);
302            hasher.finish_128().to_be_bytes().to_vec()
303        }
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310    
311    #[test]
312    fn test_md5() {
313        let data = b"hello world";
314        let hash = hash(data, HashAlgorithm::Md5);
315        assert_eq!(hash.len(), 16);
316        // MD5 of "hello world" is 5eb63bbbe01eeed093cb22bb8f5acdc3
317        assert_eq!(hex::encode(&hash), "5eb63bbbe01eeed093cb22bb8f5acdc3");
318    }
319    
320    #[test]
321    fn test_sha256() {
322        let data = b"hello world";
323        let hash = hash(data, HashAlgorithm::Sha256);
324        assert_eq!(hash.len(), 32);
325        // SHA-256 of "hello world"
326        assert_eq!(
327            hex::encode(&hash),
328            "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
329        );
330    }
331    
332    #[test]
333    fn test_sha512() {
334        let data = b"hello world";
335        let hash = hash(data, HashAlgorithm::Sha512);
336        assert_eq!(hash.len(), 64);
337    }
338    
339    #[test]
340    fn test_sha3_256() {
341        let data = b"hello world";
342        let hash = hash(data, HashAlgorithm::Sha3_256);
343        assert_eq!(hash.len(), 32);
344    }
345    
346    #[test]
347    fn test_blake2b() {
348        let data = b"hello world";
349        let hash = hash(data, HashAlgorithm::Blake2b);
350        assert_eq!(hash.len(), 64);
351    }
352    
353    #[test]
354    fn test_blake2s() {
355        let data = b"hello world";
356        let hash = hash(data, HashAlgorithm::Blake2s);
357        assert_eq!(hash.len(), 32);
358    }
359    
360    #[test]
361    fn test_blake3() {
362        let data = b"hello world";
363        let hash = hash(data, HashAlgorithm::Blake3);
364        assert_eq!(hash.len(), 32);
365    }
366    
367    #[test]
368    fn test_empty_input() {
369        let data = b"";
370        let hash = hash(data, HashAlgorithm::Sha256);
371        assert_eq!(hash.len(), 32);
372        // SHA-256 of empty string
373        assert_eq!(
374            hex::encode(&hash),
375            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
376        );
377    }
378    
379    #[test]
380    fn test_output_sizes() {
381        assert_eq!(HashAlgorithm::Md5.output_size(), 16);
382        assert_eq!(HashAlgorithm::Sha256.output_size(), 32);
383        assert_eq!(HashAlgorithm::Sha512.output_size(), 64);
384        assert_eq!(HashAlgorithm::Blake3.output_size(), 32);
385        assert_eq!(HashAlgorithm::Crc16.output_size(), 2);
386        assert_eq!(HashAlgorithm::Crc32.output_size(), 4);
387        assert_eq!(HashAlgorithm::Crc64.output_size(), 8);
388        assert_eq!(HashAlgorithm::XxHash32.output_size(), 4);
389        assert_eq!(HashAlgorithm::XxHash64.output_size(), 8);
390        assert_eq!(HashAlgorithm::XxHash3_64.output_size(), 8);
391        assert_eq!(HashAlgorithm::XxHash3_128.output_size(), 16);
392    }
393    
394    #[test]
395    fn test_crc32() {
396        let data = b"hello world";
397        let result = hash(data, HashAlgorithm::Crc32);
398        assert_eq!(result.len(), 4);
399        // CRC32 is deterministic
400        let result2 = hash(data, HashAlgorithm::Crc32);
401        assert_eq!(result, result2);
402    }
403    
404    #[test]
405    fn test_crc32c() {
406        let data = b"hello world";
407        let result = hash(data, HashAlgorithm::Crc32c);
408        assert_eq!(result.len(), 4);
409    }
410    
411    #[test]
412    fn test_crc16() {
413        let data = b"hello world";
414        let result = hash(data, HashAlgorithm::Crc16);
415        assert_eq!(result.len(), 2);
416    }
417    
418    #[test]
419    fn test_crc64() {
420        let data = b"hello world";
421        let result = hash(data, HashAlgorithm::Crc64);
422        assert_eq!(result.len(), 8);
423    }
424    
425    #[test]
426    fn test_xxhash32() {
427        let data = b"hello world";
428        let result = hash(data, HashAlgorithm::XxHash32);
429        assert_eq!(result.len(), 4);
430        // xxHash is deterministic with same seed
431        let result2 = hash(data, HashAlgorithm::XxHash32);
432        assert_eq!(result, result2);
433    }
434    
435    #[test]
436    fn test_xxhash64() {
437        let data = b"hello world";
438        let result = hash(data, HashAlgorithm::XxHash64);
439        assert_eq!(result.len(), 8);
440    }
441    
442    #[test]
443    fn test_xxhash3_64() {
444        let data = b"hello world";
445        let result = hash(data, HashAlgorithm::XxHash3_64);
446        assert_eq!(result.len(), 8);
447    }
448    
449    #[test]
450    fn test_xxhash3_128() {
451        let data = b"hello world";
452        let result = hash(data, HashAlgorithm::XxHash3_128);
453        assert_eq!(result.len(), 16);
454    }
455
456    #[test]
457    fn test_xxhash_config_default() {
458        let config = XxHashConfig::default();
459        assert_eq!(config.seed, 0);
460        assert!(config.secret.is_none());
461    }
462
463    #[test]
464    fn test_xxhash_config_secret_too_short() {
465        let result = XxHashConfig::with_secret(0, vec![0u8; 100]);
466        assert!(result.is_err());
467        assert!(result.unwrap_err().contains("136 bytes"));
468    }
469
470    #[test]
471    fn test_xxhash_config_secret_valid() {
472        let result = XxHashConfig::with_secret(42, vec![0u8; 136]);
473        assert!(result.is_ok());
474        let config = result.unwrap();
475        assert_eq!(config.seed, 42);
476        assert_eq!(config.secret.as_ref().unwrap().len(), 136);
477    }
478
479    #[test]
480    fn test_hash_seed_changes_output() {
481        let data = b"test";
482        let h1 = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::with_seed(0));
483        let h2 = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::with_seed(42));
484        assert_ne!(h1, h2);
485    }
486
487    #[test]
488    fn test_backward_compatibility() {
489        let data = b"test";
490        let old = hash(data, HashAlgorithm::XxHash64);
491        let new = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::default());
492        assert_eq!(old, new);
493    }
494
495    #[test]
496    fn test_xxhash3_with_seed() {
497        let data = b"test data for secret hashing";
498
499        // Test that different seeds produce different hashes
500        let h1 = hash_with_config(data, HashAlgorithm::XxHash3_64, &XxHashConfig::with_seed(0));
501        let h2 = hash_with_config(data, HashAlgorithm::XxHash3_64, &XxHashConfig::with_seed(123));
502        assert_ne!(h1, h2, "Different seeds should produce different hashes");
503
504        // Test that same seed produces same hash
505        let h3 = hash_with_config(data, HashAlgorithm::XxHash3_64, &XxHashConfig::with_seed(123));
506        assert_eq!(h2, h3, "Same seed should produce same hash");
507    }
508
509    #[test]
510    fn test_xxhash32_with_seed() {
511        let data = b"test";
512        let h1 = hash_with_config(data, HashAlgorithm::XxHash32, &XxHashConfig::with_seed(0));
513        let h2 = hash_with_config(data, HashAlgorithm::XxHash32, &XxHashConfig::with_seed(999));
514        assert_ne!(h1, h2);
515    }
516}