base_d/
hashing.rs

1use blake2::{Blake2b512, Blake2s256};
2use blake3::Hasher as Blake3Hasher;
3use md5::Md5;
4use sha2::{Digest, Sha224, Sha256, Sha384, Sha512};
5use sha3::{Keccak224, Keccak256, Keccak384, Keccak512, Sha3_224, Sha3_256, Sha3_384, Sha3_512};
6use std::hash::Hasher;
7use twox_hash::xxhash3_128::Hasher as Xxh3Hash128;
8use twox_hash::xxhash3_64::Hasher as Xxh3Hash64;
9use twox_hash::{XxHash32, XxHash64};
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()).expect(
287                    "XXH3 secret validation should have been done in XxHashConfig::with_secret",
288                )
289            } else {
290                Xxh3Hash64::with_seed(config.seed)
291            };
292            hasher.write(data);
293            hasher.finish().to_be_bytes().to_vec()
294        }
295        HashAlgorithm::XxHash3_128 => {
296            let mut hasher = if let Some(ref secret) = config.secret {
297                Xxh3Hash128::with_seed_and_secret(config.seed, secret.as_slice()).expect(
298                    "XXH3 secret validation should have been done in XxHashConfig::with_secret",
299                )
300            } else {
301                Xxh3Hash128::with_seed(config.seed)
302            };
303            hasher.write(data);
304            hasher.finish_128().to_be_bytes().to_vec()
305        }
306    }
307}
308
309#[cfg(test)]
310mod tests {
311    use super::*;
312
313    #[test]
314    fn test_md5() {
315        let data = b"hello world";
316        let hash = hash(data, HashAlgorithm::Md5);
317        assert_eq!(hash.len(), 16);
318        // MD5 of "hello world" is 5eb63bbbe01eeed093cb22bb8f5acdc3
319        assert_eq!(hex::encode(&hash), "5eb63bbbe01eeed093cb22bb8f5acdc3");
320    }
321
322    #[test]
323    fn test_sha256() {
324        let data = b"hello world";
325        let hash = hash(data, HashAlgorithm::Sha256);
326        assert_eq!(hash.len(), 32);
327        // SHA-256 of "hello world"
328        assert_eq!(
329            hex::encode(&hash),
330            "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
331        );
332    }
333
334    #[test]
335    fn test_sha512() {
336        let data = b"hello world";
337        let hash = hash(data, HashAlgorithm::Sha512);
338        assert_eq!(hash.len(), 64);
339    }
340
341    #[test]
342    fn test_sha3_256() {
343        let data = b"hello world";
344        let hash = hash(data, HashAlgorithm::Sha3_256);
345        assert_eq!(hash.len(), 32);
346    }
347
348    #[test]
349    fn test_blake2b() {
350        let data = b"hello world";
351        let hash = hash(data, HashAlgorithm::Blake2b);
352        assert_eq!(hash.len(), 64);
353    }
354
355    #[test]
356    fn test_blake2s() {
357        let data = b"hello world";
358        let hash = hash(data, HashAlgorithm::Blake2s);
359        assert_eq!(hash.len(), 32);
360    }
361
362    #[test]
363    fn test_blake3() {
364        let data = b"hello world";
365        let hash = hash(data, HashAlgorithm::Blake3);
366        assert_eq!(hash.len(), 32);
367    }
368
369    #[test]
370    fn test_empty_input() {
371        let data = b"";
372        let hash = hash(data, HashAlgorithm::Sha256);
373        assert_eq!(hash.len(), 32);
374        // SHA-256 of empty string
375        assert_eq!(
376            hex::encode(&hash),
377            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
378        );
379    }
380
381    #[test]
382    fn test_output_sizes() {
383        assert_eq!(HashAlgorithm::Md5.output_size(), 16);
384        assert_eq!(HashAlgorithm::Sha256.output_size(), 32);
385        assert_eq!(HashAlgorithm::Sha512.output_size(), 64);
386        assert_eq!(HashAlgorithm::Blake3.output_size(), 32);
387        assert_eq!(HashAlgorithm::Crc16.output_size(), 2);
388        assert_eq!(HashAlgorithm::Crc32.output_size(), 4);
389        assert_eq!(HashAlgorithm::Crc64.output_size(), 8);
390        assert_eq!(HashAlgorithm::XxHash32.output_size(), 4);
391        assert_eq!(HashAlgorithm::XxHash64.output_size(), 8);
392        assert_eq!(HashAlgorithm::XxHash3_64.output_size(), 8);
393        assert_eq!(HashAlgorithm::XxHash3_128.output_size(), 16);
394    }
395
396    #[test]
397    fn test_crc32() {
398        let data = b"hello world";
399        let result = hash(data, HashAlgorithm::Crc32);
400        assert_eq!(result.len(), 4);
401        // CRC32 is deterministic
402        let result2 = hash(data, HashAlgorithm::Crc32);
403        assert_eq!(result, result2);
404    }
405
406    #[test]
407    fn test_crc32c() {
408        let data = b"hello world";
409        let result = hash(data, HashAlgorithm::Crc32c);
410        assert_eq!(result.len(), 4);
411    }
412
413    #[test]
414    fn test_crc16() {
415        let data = b"hello world";
416        let result = hash(data, HashAlgorithm::Crc16);
417        assert_eq!(result.len(), 2);
418    }
419
420    #[test]
421    fn test_crc64() {
422        let data = b"hello world";
423        let result = hash(data, HashAlgorithm::Crc64);
424        assert_eq!(result.len(), 8);
425    }
426
427    #[test]
428    fn test_xxhash32() {
429        let data = b"hello world";
430        let result = hash(data, HashAlgorithm::XxHash32);
431        assert_eq!(result.len(), 4);
432        // xxHash is deterministic with same seed
433        let result2 = hash(data, HashAlgorithm::XxHash32);
434        assert_eq!(result, result2);
435    }
436
437    #[test]
438    fn test_xxhash64() {
439        let data = b"hello world";
440        let result = hash(data, HashAlgorithm::XxHash64);
441        assert_eq!(result.len(), 8);
442    }
443
444    #[test]
445    fn test_xxhash3_64() {
446        let data = b"hello world";
447        let result = hash(data, HashAlgorithm::XxHash3_64);
448        assert_eq!(result.len(), 8);
449    }
450
451    #[test]
452    fn test_xxhash3_128() {
453        let data = b"hello world";
454        let result = hash(data, HashAlgorithm::XxHash3_128);
455        assert_eq!(result.len(), 16);
456    }
457
458    #[test]
459    fn test_xxhash_config_default() {
460        let config = XxHashConfig::default();
461        assert_eq!(config.seed, 0);
462        assert!(config.secret.is_none());
463    }
464
465    #[test]
466    fn test_xxhash_config_secret_too_short() {
467        let result = XxHashConfig::with_secret(0, vec![0u8; 100]);
468        assert!(result.is_err());
469        assert!(result.unwrap_err().contains("136 bytes"));
470    }
471
472    #[test]
473    fn test_xxhash_config_secret_valid() {
474        let result = XxHashConfig::with_secret(42, vec![0u8; 136]);
475        assert!(result.is_ok());
476        let config = result.unwrap();
477        assert_eq!(config.seed, 42);
478        assert_eq!(config.secret.as_ref().unwrap().len(), 136);
479    }
480
481    #[test]
482    fn test_hash_seed_changes_output() {
483        let data = b"test";
484        let h1 = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::with_seed(0));
485        let h2 = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::with_seed(42));
486        assert_ne!(h1, h2);
487    }
488
489    #[test]
490    fn test_backward_compatibility() {
491        let data = b"test";
492        let old = hash(data, HashAlgorithm::XxHash64);
493        let new = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::default());
494        assert_eq!(old, new);
495    }
496
497    #[test]
498    fn test_xxhash3_with_seed() {
499        let data = b"test data for secret hashing";
500
501        // Test that different seeds produce different hashes
502        let h1 = hash_with_config(data, HashAlgorithm::XxHash3_64, &XxHashConfig::with_seed(0));
503        let h2 = hash_with_config(
504            data,
505            HashAlgorithm::XxHash3_64,
506            &XxHashConfig::with_seed(123),
507        );
508        assert_ne!(h1, h2, "Different seeds should produce different hashes");
509
510        // Test that same seed produces same hash
511        let h3 = hash_with_config(
512            data,
513            HashAlgorithm::XxHash3_64,
514            &XxHashConfig::with_seed(123),
515        );
516        assert_eq!(h2, h3, "Same seed should produce same hash");
517    }
518
519    #[test]
520    fn test_xxhash32_with_seed() {
521        let data = b"test";
522        let h1 = hash_with_config(data, HashAlgorithm::XxHash32, &XxHashConfig::with_seed(0));
523        let h2 = hash_with_config(data, HashAlgorithm::XxHash32, &XxHashConfig::with_seed(999));
524        assert_ne!(h1, h2);
525    }
526}