ipfrs_core/
hash.rs

1//! Hardware-accelerated hashing with SIMD support
2//!
3//! This module provides a pluggable hash algorithm system with:
4//! - Runtime CPU feature detection
5//! - SIMD-optimized implementations (AVX2 for x86_64, NEON for ARM)
6//! - Fallback to standard implementations
7//! - Modern hash algorithms (BLAKE3)
8//!
9//! ## Example
10//!
11//! ```rust
12//! use ipfrs_core::hash::{HashEngine, Sha256Engine, Sha512Engine, Blake3Engine};
13//!
14//! let data = b"hello world";
15//!
16//! // SHA2-256 (32 bytes)
17//! let sha256 = Sha256Engine::new();
18//! let hash256 = sha256.digest(data);
19//!
20//! // SHA2-512 (64 bytes) - stronger security
21//! let sha512 = Sha512Engine::new();
22//! let hash512 = sha512.digest(data);
23//!
24//! // BLAKE3 is fastest with built-in SIMD
25//! let blake3 = Blake3Engine::new();
26//! let blake3_hash = blake3.digest(data);
27//! ```
28
29use crate::error::Result;
30use blake2::{Blake2b512 as Blake2b512Impl, Blake2s256 as Blake2s256Impl, Digest as Blake2Digest};
31use multihash_codetable::Code;
32#[allow(unused_imports)]
33use sha2::{Digest, Sha256 as Sha256Impl, Sha512 as Sha512Impl};
34use sha3::{Sha3_256 as Sha3_256Impl, Sha3_512 as Sha3_512Impl};
35use std::sync::Arc;
36
37/// Trait for hardware-accelerated hash computation
38pub trait HashEngine: Send + Sync {
39    /// Compute hash of data
40    fn digest(&self, data: &[u8]) -> Vec<u8>;
41
42    /// Get the multihash code for this algorithm
43    fn code(&self) -> Code;
44
45    /// Get the name of this hash algorithm
46    fn name(&self) -> &'static str;
47
48    /// Check if SIMD acceleration is enabled
49    #[inline]
50    fn is_simd_enabled(&self) -> bool {
51        false
52    }
53}
54
55/// CPU feature detection result
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub struct CpuFeatures {
58    /// AVX2 support (x86_64)
59    pub avx2: bool,
60    /// NEON support (ARM)
61    pub neon: bool,
62}
63
64impl CpuFeatures {
65    /// Detect CPU features at runtime
66    pub fn detect() -> Self {
67        #[cfg(target_arch = "x86_64")]
68        {
69            Self {
70                avx2: is_x86_feature_detected!("avx2"),
71                neon: false,
72            }
73        }
74
75        #[cfg(target_arch = "aarch64")]
76        {
77            Self {
78                avx2: false,
79                // NEON is always available on aarch64
80                neon: true,
81            }
82        }
83
84        #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
85        {
86            Self {
87                avx2: false,
88                neon: false,
89            }
90        }
91    }
92}
93
94/// SHA2-256 hash engine with SIMD support
95pub struct Sha256Engine {
96    features: CpuFeatures,
97}
98
99impl Sha256Engine {
100    /// Create a new SHA2-256 hash engine
101    pub fn new() -> Self {
102        Self {
103            features: CpuFeatures::detect(),
104        }
105    }
106
107    /// Compute hash using AVX2 instructions (x86_64)
108    ///
109    /// The sha2 crate automatically uses SIMD instructions when available.
110    /// This includes SHA-NI (SHA extensions), AVX2, and SSE4.1 on x86_64.
111    #[cfg(target_arch = "x86_64")]
112    #[target_feature(enable = "avx2")]
113    unsafe fn digest_avx2(&self, data: &[u8]) -> Vec<u8> {
114        // The sha2 crate automatically uses hardware SHA extensions (SHA-NI)
115        // when available, falling back to AVX2/SSE optimizations
116        let mut hasher = Sha256Impl::new();
117        hasher.update(data);
118        hasher.finalize().to_vec()
119    }
120
121    /// Compute hash using NEON instructions (ARM)
122    ///
123    /// The sha2 crate uses NEON intrinsics on ARM architectures for better performance.
124    #[cfg(target_arch = "aarch64")]
125    fn digest_neon(&self, data: &[u8]) -> Vec<u8> {
126        // The sha2 crate automatically uses NEON intrinsics on ARM
127        let mut hasher = Sha256Impl::new();
128        hasher.update(data);
129        hasher.finalize().to_vec()
130    }
131
132    /// Fallback scalar implementation
133    ///
134    /// Even the "scalar" implementation in sha2 is highly optimized.
135    fn digest_scalar(&self, data: &[u8]) -> Vec<u8> {
136        let mut hasher = Sha256Impl::new();
137        hasher.update(data);
138        hasher.finalize().to_vec()
139    }
140}
141
142impl Default for Sha256Engine {
143    fn default() -> Self {
144        Self::new()
145    }
146}
147
148impl HashEngine for Sha256Engine {
149    fn digest(&self, data: &[u8]) -> Vec<u8> {
150        #[cfg(target_arch = "x86_64")]
151        {
152            if self.features.avx2 {
153                // Safety: We checked that AVX2 is available
154                unsafe { self.digest_avx2(data) }
155            } else {
156                self.digest_scalar(data)
157            }
158        }
159
160        #[cfg(target_arch = "aarch64")]
161        {
162            if self.features.neon {
163                self.digest_neon(data)
164            } else {
165                self.digest_scalar(data)
166            }
167        }
168
169        #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
170        {
171            self.digest_scalar(data)
172        }
173    }
174
175    #[inline]
176    fn code(&self) -> Code {
177        Code::Sha2_256
178    }
179
180    #[inline]
181    fn name(&self) -> &'static str {
182        "sha2-256"
183    }
184
185    #[inline]
186    fn is_simd_enabled(&self) -> bool {
187        self.features.avx2 || self.features.neon
188    }
189}
190
191/// SHA-512 hash engine with SIMD support
192///
193/// Similar to SHA2-256 but produces 512-bit (64-byte) hashes.
194/// Provides stronger security margin for applications requiring it.
195pub struct Sha512Engine {
196    features: CpuFeatures,
197}
198
199impl Sha512Engine {
200    /// Create a new SHA-512 hash engine
201    pub fn new() -> Self {
202        Self {
203            features: CpuFeatures::detect(),
204        }
205    }
206
207    /// Compute SHA-512 hash using scalar implementation
208    fn digest_scalar(&self, data: &[u8]) -> Vec<u8> {
209        let mut hasher = Sha512Impl::new();
210        hasher.update(data);
211        hasher.finalize().to_vec()
212    }
213}
214
215impl Default for Sha512Engine {
216    fn default() -> Self {
217        Self::new()
218    }
219}
220
221impl HashEngine for Sha512Engine {
222    fn digest(&self, data: &[u8]) -> Vec<u8> {
223        // SHA-512 uses same SIMD optimizations as SHA-256
224        self.digest_scalar(data)
225    }
226
227    #[inline]
228    fn code(&self) -> Code {
229        Code::Sha2_512
230    }
231
232    #[inline]
233    fn name(&self) -> &'static str {
234        "sha2-512"
235    }
236
237    #[inline]
238    fn is_simd_enabled(&self) -> bool {
239        self.features.avx2 || self.features.neon
240    }
241}
242
243/// SHA3-256 hash engine
244///
245/// SHA3 uses the Keccak sponge construction and is optimized
246/// differently than SHA2. The sha3 crate provides optimized implementations.
247pub struct Sha3_256Engine;
248
249impl Sha3_256Engine {
250    /// Create a new SHA3-256 hash engine
251    pub fn new() -> Self {
252        Self
253    }
254
255    /// Compute SHA3-256 hash
256    ///
257    /// The sha3 crate provides optimized Keccak implementation.
258    fn digest_impl(&self, data: &[u8]) -> Vec<u8> {
259        let mut hasher = Sha3_256Impl::new();
260        hasher.update(data);
261        hasher.finalize().to_vec()
262    }
263}
264
265impl Default for Sha3_256Engine {
266    fn default() -> Self {
267        Self::new()
268    }
269}
270
271impl HashEngine for Sha3_256Engine {
272    fn digest(&self, data: &[u8]) -> Vec<u8> {
273        self.digest_impl(data)
274    }
275
276    #[inline]
277    fn code(&self) -> Code {
278        Code::Sha3_256
279    }
280
281    #[inline]
282    fn name(&self) -> &'static str {
283        "sha3-256"
284    }
285
286    #[inline]
287    fn is_simd_enabled(&self) -> bool {
288        // SHA3 implementations are optimized but don't typically
289        // use explicit SIMD like SHA2 does
290        false
291    }
292}
293
294/// SHA3-512 hash engine
295///
296/// SHA3-512 provides 512-bit (64-byte) output using the Keccak sponge construction.
297/// Offers higher security margin than SHA3-256 for applications requiring it.
298pub struct Sha3_512Engine;
299
300impl Sha3_512Engine {
301    /// Create a new SHA3-512 hash engine
302    pub fn new() -> Self {
303        Self
304    }
305
306    /// Compute SHA3-512 hash
307    fn digest_impl(&self, data: &[u8]) -> Vec<u8> {
308        let mut hasher = Sha3_512Impl::new();
309        hasher.update(data);
310        hasher.finalize().to_vec()
311    }
312}
313
314impl Default for Sha3_512Engine {
315    fn default() -> Self {
316        Self::new()
317    }
318}
319
320impl HashEngine for Sha3_512Engine {
321    fn digest(&self, data: &[u8]) -> Vec<u8> {
322        self.digest_impl(data)
323    }
324
325    #[inline]
326    fn code(&self) -> Code {
327        Code::Sha3_512
328    }
329
330    #[inline]
331    fn name(&self) -> &'static str {
332        "sha3-512"
333    }
334
335    #[inline]
336    fn is_simd_enabled(&self) -> bool {
337        // SHA3 implementations are optimized but don't typically
338        // use explicit SIMD like SHA2 does
339        false
340    }
341}
342
343/// BLAKE3 hash engine with built-in SIMD support
344///
345/// BLAKE3 is a modern, extremely fast cryptographic hash function that:
346/// - Has built-in SIMD optimizations (AVX2, AVX-512, NEON)
347/// - Is faster than SHA2-256 and SHA3-256
348/// - Provides 256-bit output (32 bytes)
349/// - Is designed for modern CPUs
350///
351/// BLAKE3 automatically uses the best available SIMD instructions
352/// for the current CPU architecture.
353pub struct Blake3Engine;
354
355impl Blake3Engine {
356    /// Create a new BLAKE3 hash engine
357    pub fn new() -> Self {
358        Self
359    }
360}
361
362impl Default for Blake3Engine {
363    fn default() -> Self {
364        Self::new()
365    }
366}
367
368impl HashEngine for Blake3Engine {
369    fn digest(&self, data: &[u8]) -> Vec<u8> {
370        // BLAKE3 automatically uses SIMD when available
371        let mut hasher = blake3::Hasher::new();
372        hasher.update(data);
373        hasher.finalize().as_bytes().to_vec()
374    }
375
376    #[inline]
377    fn code(&self) -> Code {
378        // BLAKE3-256 is natively supported in multihash-codetable
379        Code::Blake3_256
380    }
381
382    #[inline]
383    fn name(&self) -> &'static str {
384        "blake3"
385    }
386
387    #[inline]
388    fn is_simd_enabled(&self) -> bool {
389        // BLAKE3 always uses SIMD when available
390        true
391    }
392}
393
394/// BLAKE2b-256 hash engine
395///
396/// BLAKE2b is a cryptographic hash function optimized for 64-bit platforms.
397/// This variant produces 256-bit (32-byte) hashes.
398///
399/// BLAKE2b features:
400/// - Faster than MD5, SHA-1, SHA-2, and SHA-3
401/// - At least as secure as SHA-3
402/// - No known attacks better than brute force
403/// - Simple design with no padding required
404#[derive(Debug, Clone, Copy, Default)]
405pub struct Blake2b256Engine;
406
407impl Blake2b256Engine {
408    /// Create a new BLAKE2b-256 hash engine
409    pub fn new() -> Self {
410        Self
411    }
412}
413
414impl HashEngine for Blake2b256Engine {
415    fn digest(&self, data: &[u8]) -> Vec<u8> {
416        use blake2::digest::FixedOutput;
417
418        // BLAKE2b-256: Use Blake2b512 and truncate to 32 bytes
419        let mut hasher = Blake2b512Impl::new();
420        Blake2Digest::update(&mut hasher, data);
421        let result = hasher.finalize_fixed();
422        result[..32].to_vec()
423    }
424
425    #[inline]
426    fn code(&self) -> Code {
427        Code::Blake2b256
428    }
429
430    #[inline]
431    fn name(&self) -> &'static str {
432        "blake2b-256"
433    }
434
435    #[inline]
436    fn is_simd_enabled(&self) -> bool {
437        // BLAKE2 implementations typically use SIMD when available
438        true
439    }
440}
441
442/// BLAKE2b-512 hash engine
443///
444/// BLAKE2b is a cryptographic hash function optimized for 64-bit platforms.
445/// This variant produces 512-bit (64-byte) hashes.
446///
447/// This is the full-length BLAKE2b output, providing maximum security.
448#[derive(Debug, Clone, Copy, Default)]
449pub struct Blake2b512Engine;
450
451impl Blake2b512Engine {
452    /// Create a new BLAKE2b-512 hash engine
453    pub fn new() -> Self {
454        Self
455    }
456}
457
458impl HashEngine for Blake2b512Engine {
459    fn digest(&self, data: &[u8]) -> Vec<u8> {
460        let mut hasher = Blake2b512Impl::new();
461        Blake2Digest::update(&mut hasher, data);
462        hasher.finalize().to_vec()
463    }
464
465    #[inline]
466    fn code(&self) -> Code {
467        Code::Blake2b512
468    }
469
470    #[inline]
471    fn name(&self) -> &'static str {
472        "blake2b-512"
473    }
474
475    #[inline]
476    fn is_simd_enabled(&self) -> bool {
477        // BLAKE2 implementations typically use SIMD when available
478        true
479    }
480}
481
482/// BLAKE2s-256 hash engine
483///
484/// BLAKE2s is a cryptographic hash function optimized for 8-32 bit platforms.
485/// This variant produces 256-bit (32-byte) hashes.
486///
487/// BLAKE2s is optimized for smaller architectures and embedded systems,
488/// while still providing strong cryptographic security.
489#[derive(Debug, Clone, Copy, Default)]
490pub struct Blake2s256Engine;
491
492impl Blake2s256Engine {
493    /// Create a new BLAKE2s-256 hash engine
494    pub fn new() -> Self {
495        Self
496    }
497}
498
499impl HashEngine for Blake2s256Engine {
500    fn digest(&self, data: &[u8]) -> Vec<u8> {
501        let mut hasher = Blake2s256Impl::new();
502        Blake2Digest::update(&mut hasher, data);
503        hasher.finalize().to_vec()
504    }
505
506    #[inline]
507    fn code(&self) -> Code {
508        Code::Blake2s256
509    }
510
511    #[inline]
512    fn name(&self) -> &'static str {
513        "blake2s-256"
514    }
515
516    #[inline]
517    fn is_simd_enabled(&self) -> bool {
518        // BLAKE2 implementations typically use SIMD when available
519        true
520    }
521}
522
523/// Hash algorithm registry for pluggable hash support
524pub struct HashRegistry {
525    algorithms: std::collections::HashMap<u64, Arc<dyn HashEngine>>,
526}
527
528impl HashRegistry {
529    /// Create a new hash registry with default algorithms
530    pub fn new() -> Self {
531        let mut registry = Self {
532            algorithms: std::collections::HashMap::new(),
533        };
534
535        // Register default algorithms
536        registry.register(Arc::new(Sha256Engine::new()));
537        registry.register(Arc::new(Sha512Engine::new()));
538        registry.register(Arc::new(Sha3_256Engine::new()));
539        registry.register(Arc::new(Sha3_512Engine::new()));
540        registry.register(Arc::new(Blake2b256Engine::new()));
541        registry.register(Arc::new(Blake2b512Engine::new()));
542        registry.register(Arc::new(Blake2s256Engine::new()));
543        registry.register(Arc::new(Blake3Engine::new()));
544
545        registry
546    }
547
548    /// Register a hash algorithm
549    pub fn register(&mut self, engine: Arc<dyn HashEngine>) {
550        let code_u64 = engine.code() as u64;
551        self.algorithms.insert(code_u64, engine);
552    }
553
554    /// Get a hash engine by code
555    pub fn get(&self, code: Code) -> Option<Arc<dyn HashEngine>> {
556        let code_u64 = code as u64;
557        self.algorithms.get(&code_u64).cloned()
558    }
559
560    /// Compute hash using the specified algorithm
561    pub fn digest(&self, code: Code, data: &[u8]) -> Result<Vec<u8>> {
562        let engine = self.get(code).ok_or_else(|| {
563            crate::error::Error::InvalidInput(format!("Unsupported hash algorithm: {:?}", code))
564        })?;
565        Ok(engine.digest(data))
566    }
567}
568
569impl Default for HashRegistry {
570    fn default() -> Self {
571        Self::new()
572    }
573}
574
575/// Global hash registry instance
576static HASH_REGISTRY: once_cell::sync::Lazy<HashRegistry> =
577    once_cell::sync::Lazy::new(HashRegistry::new);
578
579/// Get the global hash registry
580pub fn global_hash_registry() -> &'static HashRegistry {
581    &HASH_REGISTRY
582}
583
584#[cfg(test)]
585mod tests {
586    use super::*;
587
588    #[test]
589    fn test_cpu_feature_detection() {
590        let features = CpuFeatures::detect();
591
592        #[cfg(target_arch = "x86_64")]
593        {
594            // AVX2 may or may not be available
595            assert!(!features.neon);
596        }
597
598        #[cfg(target_arch = "aarch64")]
599        {
600            assert!(features.neon);
601            assert!(!features.avx2);
602        }
603    }
604
605    #[test]
606    fn test_sha256_engine() {
607        let engine = Sha256Engine::new();
608        let hash = engine.digest(b"hello world");
609
610        // SHA256 produces 32-byte hashes
611        assert_eq!(hash.len(), 32);
612
613        // Same input should produce same hash
614        let hash2 = engine.digest(b"hello world");
615        assert_eq!(hash, hash2);
616
617        // Different input should produce different hash
618        let hash3 = engine.digest(b"hello mars");
619        assert_ne!(hash, hash3);
620    }
621
622    #[test]
623    fn test_sha3_256_engine() {
624        let engine = Sha3_256Engine::new();
625        let hash = engine.digest(b"test data");
626
627        // SHA3-256 produces 32-byte hashes
628        assert_eq!(hash.len(), 32);
629
630        // Deterministic
631        let hash2 = engine.digest(b"test data");
632        assert_eq!(hash, hash2);
633    }
634
635    #[test]
636    fn test_hash_registry() {
637        let registry = HashRegistry::new();
638
639        // Should have SHA256
640        let sha256 = registry.get(Code::Sha2_256);
641        assert!(sha256.is_some());
642
643        // Should have SHA512
644        let sha512 = registry.get(Code::Sha2_512);
645        assert!(sha512.is_some());
646
647        // Should have SHA3-256
648        let sha3_256 = registry.get(Code::Sha3_256);
649        assert!(sha3_256.is_some());
650
651        // Should have SHA3-512
652        let sha3_512 = registry.get(Code::Sha3_512);
653        assert!(sha3_512.is_some());
654    }
655
656    #[test]
657    fn test_registry_digest() {
658        let registry = HashRegistry::new();
659
660        let hash = registry.digest(Code::Sha2_256, b"test").unwrap();
661        assert_eq!(hash.len(), 32);
662    }
663
664    #[test]
665    fn test_global_registry() {
666        let registry = global_hash_registry();
667
668        let hash = registry.digest(Code::Sha2_256, b"global test").unwrap();
669        assert_eq!(hash.len(), 32);
670    }
671
672    #[test]
673    fn test_sha256_deterministic() {
674        let engine = Sha256Engine::new();
675
676        // Test with various data sizes
677        for size in [0, 1, 64, 256, 1024, 4096] {
678            let data = vec![42u8; size];
679            let hash1 = engine.digest(&data);
680            let hash2 = engine.digest(&data);
681            assert_eq!(hash1, hash2, "Failed for size {}", size);
682        }
683    }
684
685    #[test]
686    fn test_sha512_engine() {
687        let engine = Sha512Engine::new();
688        let hash = engine.digest(b"hello world");
689
690        // SHA-512 produces 64-byte hashes
691        assert_eq!(hash.len(), 64);
692
693        // Same input should produce same hash
694        let hash2 = engine.digest(b"hello world");
695        assert_eq!(hash, hash2);
696
697        // Different input should produce different hash
698        let hash3 = engine.digest(b"hello mars");
699        assert_ne!(hash, hash3);
700    }
701
702    #[test]
703    fn test_sha3_512_engine() {
704        let engine = Sha3_512Engine::new();
705        let hash = engine.digest(b"hello world");
706
707        // SHA3-512 produces 64-byte hashes
708        assert_eq!(hash.len(), 64);
709
710        // Same input should produce same hash
711        let hash2 = engine.digest(b"hello world");
712        assert_eq!(hash, hash2);
713
714        // Different input should produce different hash
715        let hash3 = engine.digest(b"hello mars");
716        assert_ne!(hash, hash3);
717    }
718
719    #[test]
720    fn test_sha512_deterministic() {
721        let engine = Sha512Engine::new();
722
723        // Test with various data sizes
724        for size in [0, 1, 64, 256, 1024, 4096] {
725            let data = vec![42u8; size];
726            let hash1 = engine.digest(&data);
727            let hash2 = engine.digest(&data);
728            assert_eq!(hash1, hash2, "Failed for size {}", size);
729        }
730    }
731
732    #[test]
733    fn test_sha3_512_deterministic() {
734        let engine = Sha3_512Engine::new();
735
736        // Test with various data sizes
737        for size in [0, 1, 64, 256, 1024, 4096] {
738            let data = vec![42u8; size];
739            let hash1 = engine.digest(&data);
740            let hash2 = engine.digest(&data);
741            assert_eq!(hash1, hash2, "Failed for size {}", size);
742        }
743    }
744
745    #[test]
746    fn test_sha512_vs_sha256() {
747        let sha512 = Sha512Engine::new();
748        let sha256 = Sha256Engine::new();
749
750        let data = b"test data";
751        let hash512 = sha512.digest(data);
752        let hash256 = sha256.digest(data);
753
754        // SHA-512 should produce 64 bytes, SHA-256 should produce 32 bytes
755        assert_eq!(hash512.len(), 64);
756        assert_eq!(hash256.len(), 32);
757
758        // The hashes should be different
759        assert_ne!(&hash512[..32], &hash256[..]);
760    }
761
762    #[test]
763    fn test_sha3_512_vs_sha3_256() {
764        let sha3_512 = Sha3_512Engine::new();
765        let sha3_256 = Sha3_256Engine::new();
766
767        let data = b"test data";
768        let hash512 = sha3_512.digest(data);
769        let hash256 = sha3_256.digest(data);
770
771        // SHA3-512 should produce 64 bytes, SHA3-256 should produce 32 bytes
772        assert_eq!(hash512.len(), 64);
773        assert_eq!(hash256.len(), 32);
774
775        // The hashes should be different
776        assert_ne!(&hash512[..32], &hash256[..]);
777    }
778
779    #[test]
780    fn test_blake3_engine() {
781        let engine = Blake3Engine::new();
782        let hash = engine.digest(b"hello world");
783
784        // BLAKE3 produces 32-byte hashes
785        assert_eq!(hash.len(), 32);
786
787        // Same input should produce same hash
788        let hash2 = engine.digest(b"hello world");
789        assert_eq!(hash, hash2);
790
791        // Different input should produce different hash
792        let hash3 = engine.digest(b"hello mars");
793        assert_ne!(hash, hash3);
794    }
795
796    #[test]
797    fn test_blake3_deterministic() {
798        let engine = Blake3Engine::new();
799
800        // Test with various data sizes
801        for size in [0, 1, 64, 256, 1024, 4096] {
802            let data = vec![42u8; size];
803            let hash1 = engine.digest(&data);
804            let hash2 = engine.digest(&data);
805            assert_eq!(hash1, hash2, "Failed for size {}", size);
806        }
807    }
808
809    #[test]
810    fn test_blake3_simd_enabled() {
811        let engine = Blake3Engine::new();
812        // BLAKE3 should always report SIMD as enabled
813        assert!(engine.is_simd_enabled());
814    }
815
816    #[test]
817    fn test_blake3_vs_sha256() {
818        let blake3 = Blake3Engine::new();
819        let sha256 = Sha256Engine::new();
820
821        let data = b"test data for comparison";
822
823        let blake3_hash = blake3.digest(data);
824        let sha256_hash = sha256.digest(data);
825
826        // Both should produce 32-byte hashes
827        assert_eq!(blake3_hash.len(), 32);
828        assert_eq!(sha256_hash.len(), 32);
829
830        // But the hashes should be different (different algorithms)
831        assert_ne!(blake3_hash, sha256_hash);
832    }
833
834    #[test]
835    fn test_blake3_empty_input() {
836        let engine = Blake3Engine::new();
837        let hash = engine.digest(b"");
838
839        // BLAKE3 hash of empty string
840        assert_eq!(hash.len(), 32);
841
842        // Empty input should be deterministic
843        let hash2 = engine.digest(b"");
844        assert_eq!(hash, hash2);
845    }
846
847    #[test]
848    fn test_blake2b256_engine() {
849        let engine = Blake2b256Engine::new();
850        let hash = engine.digest(b"hello world");
851
852        // BLAKE2b-256 produces 32-byte hashes
853        assert_eq!(hash.len(), 32);
854
855        // Same input should produce same hash
856        let hash2 = engine.digest(b"hello world");
857        assert_eq!(hash, hash2);
858
859        // Different input should produce different hash
860        let hash3 = engine.digest(b"hello mars");
861        assert_ne!(hash, hash3);
862    }
863
864    #[test]
865    fn test_blake2b512_engine() {
866        let engine = Blake2b512Engine::new();
867        let hash = engine.digest(b"hello world");
868
869        // BLAKE2b-512 produces 64-byte hashes
870        assert_eq!(hash.len(), 64);
871
872        // Same input should produce same hash
873        let hash2 = engine.digest(b"hello world");
874        assert_eq!(hash, hash2);
875
876        // Different input should produce different hash
877        let hash3 = engine.digest(b"hello mars");
878        assert_ne!(hash, hash3);
879    }
880
881    #[test]
882    fn test_blake2s256_engine() {
883        let engine = Blake2s256Engine::new();
884        let hash = engine.digest(b"test data");
885
886        // BLAKE2s-256 produces 32-byte hashes
887        assert_eq!(hash.len(), 32);
888
889        // Deterministic
890        let hash2 = engine.digest(b"test data");
891        assert_eq!(hash, hash2);
892    }
893
894    #[test]
895    fn test_blake2b256_deterministic() {
896        let engine = Blake2b256Engine::new();
897
898        // Test with various data sizes
899        for size in [0, 1, 64, 256, 1024, 4096] {
900            let data = vec![42u8; size];
901            let hash1 = engine.digest(&data);
902            let hash2 = engine.digest(&data);
903            assert_eq!(hash1, hash2, "Failed for size {}", size);
904        }
905    }
906
907    #[test]
908    fn test_blake2b512_deterministic() {
909        let engine = Blake2b512Engine::new();
910
911        // Test with various data sizes
912        for size in [0, 1, 64, 256, 1024, 4096] {
913            let data = vec![42u8; size];
914            let hash1 = engine.digest(&data);
915            let hash2 = engine.digest(&data);
916            assert_eq!(hash1, hash2, "Failed for size {}", size);
917            assert_eq!(hash1.len(), 64);
918        }
919    }
920
921    #[test]
922    fn test_blake2s256_deterministic() {
923        let engine = Blake2s256Engine::new();
924
925        // Test with various data sizes
926        for size in [0, 1, 64, 256, 1024] {
927            let data = vec![42u8; size];
928            let hash1 = engine.digest(&data);
929            let hash2 = engine.digest(&data);
930            assert_eq!(hash1, hash2, "Failed for size {}", size);
931        }
932    }
933
934    #[test]
935    fn test_blake2b_vs_blake2s() {
936        let blake2b = Blake2b256Engine::new();
937        let blake2s = Blake2s256Engine::new();
938
939        let data = b"test data for comparison";
940
941        let blake2b_hash = blake2b.digest(data);
942        let blake2s_hash = blake2s.digest(data);
943
944        // Both should produce 32-byte hashes
945        assert_eq!(blake2b_hash.len(), 32);
946        assert_eq!(blake2s_hash.len(), 32);
947
948        // But the hashes should be different (different algorithms)
949        assert_ne!(blake2b_hash, blake2s_hash);
950    }
951
952    #[test]
953    fn test_blake2_empty_input() {
954        let blake2b256 = Blake2b256Engine::new();
955        let blake2b512 = Blake2b512Engine::new();
956        let blake2s = Blake2s256Engine::new();
957
958        let hash256 = blake2b256.digest(b"");
959        let hash512 = blake2b512.digest(b"");
960        let hashs = blake2s.digest(b"");
961
962        assert_eq!(hash256.len(), 32);
963        assert_eq!(hash512.len(), 64);
964        assert_eq!(hashs.len(), 32);
965
966        // Empty input should be deterministic
967        assert_eq!(hash256, blake2b256.digest(b""));
968        assert_eq!(hash512, blake2b512.digest(b""));
969        assert_eq!(hashs, blake2s.digest(b""));
970    }
971
972    #[test]
973    fn test_blake2_simd_enabled() {
974        let blake2b256 = Blake2b256Engine::new();
975        let blake2b512 = Blake2b512Engine::new();
976        let blake2s = Blake2s256Engine::new();
977
978        // BLAKE2 should report SIMD as enabled
979        assert!(blake2b256.is_simd_enabled());
980        assert!(blake2b512.is_simd_enabled());
981        assert!(blake2s.is_simd_enabled());
982    }
983
984    #[test]
985    fn test_hash_registry_blake2() {
986        let registry = HashRegistry::new();
987
988        // Should have BLAKE2b-256
989        let blake2b256 = registry.get(Code::Blake2b256);
990        assert!(blake2b256.is_some());
991
992        // Should have BLAKE2b-512
993        let blake2b512 = registry.get(Code::Blake2b512);
994        assert!(blake2b512.is_some());
995
996        // Should have BLAKE2s-256
997        let blake2s256 = registry.get(Code::Blake2s256);
998        assert!(blake2s256.is_some());
999    }
1000
1001    #[test]
1002    fn test_registry_digest_blake2() {
1003        let registry = HashRegistry::new();
1004
1005        let hash256 = registry.digest(Code::Blake2b256, b"test").unwrap();
1006        assert_eq!(hash256.len(), 32);
1007
1008        let hash512 = registry.digest(Code::Blake2b512, b"test").unwrap();
1009        assert_eq!(hash512.len(), 64);
1010
1011        let hashs = registry.digest(Code::Blake2s256, b"test").unwrap();
1012        assert_eq!(hashs.len(), 32);
1013    }
1014
1015    #[test]
1016    fn test_blake2_names() {
1017        assert_eq!(Blake2b256Engine::new().name(), "blake2b-256");
1018        assert_eq!(Blake2b512Engine::new().name(), "blake2b-512");
1019        assert_eq!(Blake2s256Engine::new().name(), "blake2s-256");
1020    }
1021
1022    #[test]
1023    fn test_blake2_codes() {
1024        assert_eq!(Blake2b256Engine::new().code(), Code::Blake2b256);
1025        assert_eq!(Blake2b512Engine::new().code(), Code::Blake2b512);
1026        assert_eq!(Blake2s256Engine::new().code(), Code::Blake2s256);
1027    }
1028}