base_d/features/
hashing.rs

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