Skip to main content

oxicrypto_hash/
lib.rs

1#![forbid(unsafe_code)]
2#![no_std]
3
4//! Pure Rust hash function implementations for the OxiCrypto stack.
5//!
6//! Provides [`Hash`]-trait wrappers and [`StreamingHash`] adapters for:
7//! - SHA-2: SHA-256, SHA-384, SHA-512, SHA-512/256 (FIPS 180-4)
8//! - SHA-3: SHA3-256, SHA3-384, SHA3-512 (FIPS 202)
9//! - BLAKE2: BLAKE2b-256, BLAKE2b-512, BLAKE2s-256 (RFC 7693)
10//! - BLAKE3: standard, keyed-hash, key-derivation, XOF (blake3 spec)
11//! - SHAKE128/256, cSHAKE128/256, TupleHash128/256 (NIST SP 800-185)
12//! - BLAKE2b keyed-hash mode (RFC 7693 MAC mode)
13//!
14//! All streaming adapters implement [`StreamingHash`] via the generic
15//! [`DigestStreamingAdapter`] wrapper which works with any `digest::Digest +
16//! Default` type.
17
18extern crate alloc;
19
20#[cfg(feature = "std")]
21extern crate std;
22
23mod hash_builder;
24mod parallelhash;
25mod xof;
26pub use hash_builder::{DynStreamingHash, HashAlgorithm, HashBuilder, StreamingHashBuilder};
27pub use parallelhash::{
28    parallel_hash128, parallel_hash128_xof, parallel_hash256, parallel_hash256_xof,
29    ParallelHash128, ParallelHash256,
30};
31pub use xof::{
32    blake2b_keyed, cshake128, cshake256, shake128, shake128_start, shake256, shake256_start,
33    tuple_hash128, tuple_hash256, Blake2bKeyed, Shake128Reader, Shake256Reader,
34};
35
36#[cfg(feature = "std")]
37pub use xof::{hash_file_blake3, hash_file_sha256, hash_file_sha512};
38
39use alloc::vec::Vec;
40
41use digest::Digest;
42use oxicrypto_core::{CryptoError, Hash, StreamingHash};
43
44// ── Generic streaming adapter ────────────────────────────────────────────────
45
46/// Generic streaming adapter wrapping any `digest::Digest + Default` type.
47///
48/// Implements [`StreamingHash`] for all `digest 0.11`-compatible hash
49/// functions (SHA-2, SHA-3, BLAKE2). BLAKE3 uses a separate impl.
50pub struct DigestStreamingAdapter<D: Digest + Default> {
51    inner: D,
52}
53
54impl<D: Digest + Default> DigestStreamingAdapter<D> {
55    /// Create a new adapter in the initial (empty) state.
56    pub fn new() -> Self {
57        Self {
58            inner: D::default(),
59        }
60    }
61}
62
63impl<D: Digest + Default> Default for DigestStreamingAdapter<D> {
64    fn default() -> Self {
65        Self::new()
66    }
67}
68
69impl<D: Digest + Default + Clone> Clone for DigestStreamingAdapter<D> {
70    fn clone(&self) -> Self {
71        Self {
72            inner: self.inner.clone(),
73        }
74    }
75}
76
77impl<D: Digest + Default + Send> StreamingHash for DigestStreamingAdapter<D> {
78    fn update(&mut self, data: &[u8]) {
79        Digest::update(&mut self.inner, data);
80    }
81
82    fn finalize(self, out: &mut [u8]) -> Result<(), CryptoError> {
83        let result = Digest::finalize(self.inner);
84        if out.len() < result.len() {
85            return Err(CryptoError::BufferTooSmall);
86        }
87        out[..result.len()].copy_from_slice(&result);
88        Ok(())
89    }
90
91    fn reset(&mut self) {
92        self.inner = D::default();
93    }
94}
95
96// ── SHA-2 one-shot ───────────────────────────────────────────────────────────
97
98/// SHA-256 hash function (32-byte output, FIPS 180-4 §6.2).
99#[derive(Debug, Default, Clone, Copy)]
100pub struct Sha256;
101
102/// SHA-384 hash function (48-byte output, FIPS 180-4 §6.5).
103#[derive(Debug, Default, Clone, Copy)]
104pub struct Sha384;
105
106/// SHA-512 hash function (64-byte output, FIPS 180-4 §6.4).
107#[derive(Debug, Default, Clone, Copy)]
108pub struct Sha512;
109
110/// SHA-512/256 truncated hash function (32-byte output, FIPS 180-4 §6.7).
111#[derive(Debug, Default, Clone, Copy)]
112pub struct Sha512_256;
113
114impl Hash for Sha256 {
115    fn name(&self) -> &'static str {
116        "SHA-256"
117    }
118    fn output_len(&self) -> usize {
119        32
120    }
121    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
122        if out.len() < 32 {
123            return Err(CryptoError::BufferTooSmall);
124        }
125        let digest = sha2::Sha256::digest(msg);
126        out[..32].copy_from_slice(&digest);
127        Ok(())
128    }
129}
130
131impl Hash for Sha384 {
132    fn name(&self) -> &'static str {
133        "SHA-384"
134    }
135    fn output_len(&self) -> usize {
136        48
137    }
138    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
139        if out.len() < 48 {
140            return Err(CryptoError::BufferTooSmall);
141        }
142        let digest = sha2::Sha384::digest(msg);
143        out[..48].copy_from_slice(&digest);
144        Ok(())
145    }
146}
147
148impl Hash for Sha512 {
149    fn name(&self) -> &'static str {
150        "SHA-512"
151    }
152    fn output_len(&self) -> usize {
153        64
154    }
155    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
156        if out.len() < 64 {
157            return Err(CryptoError::BufferTooSmall);
158        }
159        let digest = sha2::Sha512::digest(msg);
160        out[..64].copy_from_slice(&digest);
161        Ok(())
162    }
163}
164
165impl Hash for Sha512_256 {
166    fn name(&self) -> &'static str {
167        "SHA-512/256"
168    }
169    fn output_len(&self) -> usize {
170        32
171    }
172    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
173        if out.len() < 32 {
174            return Err(CryptoError::BufferTooSmall);
175        }
176        let digest = sha2::Sha512_256::digest(msg);
177        out[..32].copy_from_slice(&digest);
178        Ok(())
179    }
180}
181
182// ── SHA-2 digest length constants ────────────────────────────────────────────
183
184impl Sha256 {
185    /// Byte length of the SHA-256 digest output.
186    pub const DIGEST_LEN: usize = 32;
187    /// SHA-256 block size in bytes (FIPS 180-4).
188    pub const BLOCK_SIZE: usize = 64;
189}
190
191impl Sha384 {
192    /// Byte length of the SHA-384 digest output.
193    pub const DIGEST_LEN: usize = 48;
194    /// SHA-384 block size in bytes (FIPS 180-4).
195    pub const BLOCK_SIZE: usize = 128;
196}
197
198impl Sha512 {
199    /// Byte length of the SHA-512 digest output.
200    pub const DIGEST_LEN: usize = 64;
201    /// SHA-512 block size in bytes (FIPS 180-4).
202    pub const BLOCK_SIZE: usize = 128;
203}
204
205impl Sha512_256 {
206    /// Byte length of the SHA-512/256 digest output.
207    pub const DIGEST_LEN: usize = 32;
208    /// SHA-512/256 block size in bytes (FIPS 180-4).
209    pub const BLOCK_SIZE: usize = 128;
210}
211
212// ── SHA-2 streaming ──────────────────────────────────────────────────────────
213
214/// Streaming SHA-256 hasher.
215pub type Sha256Streaming = DigestStreamingAdapter<sha2::Sha256>;
216/// Streaming SHA-384 hasher.
217pub type Sha384Streaming = DigestStreamingAdapter<sha2::Sha384>;
218/// Streaming SHA-512 hasher.
219pub type Sha512Streaming = DigestStreamingAdapter<sha2::Sha512>;
220/// Streaming SHA-512/256 hasher.
221pub type Sha512_256Streaming = DigestStreamingAdapter<sha2::Sha512_256>;
222
223// ── SHA-3 one-shot ───────────────────────────────────────────────────────────
224
225/// SHA3-256 hash function (32-byte output, FIPS 202).
226#[derive(Debug, Default, Clone, Copy)]
227pub struct Sha3_256;
228
229/// SHA3-384 hash function (48-byte output, FIPS 202).
230#[derive(Debug, Default, Clone, Copy)]
231pub struct Sha3_384;
232
233/// SHA3-512 hash function (64-byte output, FIPS 202).
234#[derive(Debug, Default, Clone, Copy)]
235pub struct Sha3_512;
236
237impl Hash for Sha3_256 {
238    fn name(&self) -> &'static str {
239        "SHA3-256"
240    }
241    fn output_len(&self) -> usize {
242        32
243    }
244    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
245        if out.len() < 32 {
246            return Err(CryptoError::BufferTooSmall);
247        }
248        let digest = sha3::Sha3_256::digest(msg);
249        out[..32].copy_from_slice(&digest);
250        Ok(())
251    }
252}
253
254impl Hash for Sha3_384 {
255    fn name(&self) -> &'static str {
256        "SHA3-384"
257    }
258    fn output_len(&self) -> usize {
259        48
260    }
261    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
262        if out.len() < 48 {
263            return Err(CryptoError::BufferTooSmall);
264        }
265        let digest = sha3::Sha3_384::digest(msg);
266        out[..48].copy_from_slice(&digest);
267        Ok(())
268    }
269}
270
271impl Hash for Sha3_512 {
272    fn name(&self) -> &'static str {
273        "SHA3-512"
274    }
275    fn output_len(&self) -> usize {
276        64
277    }
278    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
279        if out.len() < 64 {
280            return Err(CryptoError::BufferTooSmall);
281        }
282        let digest = sha3::Sha3_512::digest(msg);
283        out[..64].copy_from_slice(&digest);
284        Ok(())
285    }
286}
287
288// ── SHA-3 digest length constants ────────────────────────────────────────────
289
290impl Sha3_256 {
291    /// Byte length of the SHA3-256 digest output.
292    pub const DIGEST_LEN: usize = 32;
293    /// SHA3-256 block (rate) size in bytes (FIPS 202).
294    pub const BLOCK_SIZE: usize = 136;
295}
296
297impl Sha3_384 {
298    /// Byte length of the SHA3-384 digest output.
299    pub const DIGEST_LEN: usize = 48;
300    /// SHA3-384 block (rate) size in bytes (FIPS 202).
301    pub const BLOCK_SIZE: usize = 104;
302}
303
304impl Sha3_512 {
305    /// Byte length of the SHA3-512 digest output.
306    pub const DIGEST_LEN: usize = 64;
307    /// SHA3-512 block (rate) size in bytes (FIPS 202).
308    pub const BLOCK_SIZE: usize = 72;
309}
310
311// ── SHA-3 streaming ──────────────────────────────────────────────────────────
312
313/// Streaming SHA3-256 hasher.
314pub type Sha3_256Streaming = DigestStreamingAdapter<sha3::Sha3_256>;
315/// Streaming SHA3-384 hasher.
316pub type Sha3_384Streaming = DigestStreamingAdapter<sha3::Sha3_384>;
317/// Streaming SHA3-512 hasher.
318pub type Sha3_512Streaming = DigestStreamingAdapter<sha3::Sha3_512>;
319
320// ── BLAKE2 one-shot ──────────────────────────────────────────────────────────
321
322/// BLAKE2b-256 hash function (32-byte output, RFC 7693).
323#[derive(Debug, Default, Clone, Copy)]
324pub struct Blake2b256;
325
326/// BLAKE2b-512 hash function (64-byte output, RFC 7693).
327#[derive(Debug, Default, Clone, Copy)]
328pub struct Blake2b512;
329
330/// BLAKE2s-256 hash function (32-byte output, RFC 7693).
331#[derive(Debug, Default, Clone, Copy)]
332pub struct Blake2s256;
333
334impl Hash for Blake2b256 {
335    fn name(&self) -> &'static str {
336        "BLAKE2b-256"
337    }
338    fn output_len(&self) -> usize {
339        32
340    }
341    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
342        if out.len() < 32 {
343            return Err(CryptoError::BufferTooSmall);
344        }
345        let result = blake2::Blake2b256::digest(msg);
346        out[..32].copy_from_slice(&result);
347        Ok(())
348    }
349}
350
351impl Hash for Blake2b512 {
352    fn name(&self) -> &'static str {
353        "BLAKE2b-512"
354    }
355    fn output_len(&self) -> usize {
356        64
357    }
358    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
359        if out.len() < 64 {
360            return Err(CryptoError::BufferTooSmall);
361        }
362        let result = blake2::Blake2b512::digest(msg);
363        out[..64].copy_from_slice(&result);
364        Ok(())
365    }
366}
367
368impl Hash for Blake2s256 {
369    fn name(&self) -> &'static str {
370        "BLAKE2s-256"
371    }
372    fn output_len(&self) -> usize {
373        32
374    }
375    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
376        if out.len() < 32 {
377            return Err(CryptoError::BufferTooSmall);
378        }
379        let result = blake2::Blake2s256::digest(msg);
380        out[..32].copy_from_slice(&result);
381        Ok(())
382    }
383}
384
385// ── BLAKE2 digest length constants ───────────────────────────────────────────
386
387impl Blake2b256 {
388    /// Byte length of the BLAKE2b-256 digest output.
389    pub const DIGEST_LEN: usize = 32;
390    /// BLAKE2b block size in bytes (RFC 7693).
391    pub const BLOCK_SIZE: usize = 128;
392}
393
394impl Blake2b512 {
395    /// Byte length of the BLAKE2b-512 digest output.
396    pub const DIGEST_LEN: usize = 64;
397    /// BLAKE2b block size in bytes (RFC 7693).
398    pub const BLOCK_SIZE: usize = 128;
399}
400
401impl Blake2s256 {
402    /// Byte length of the BLAKE2s-256 digest output.
403    pub const DIGEST_LEN: usize = 32;
404    /// BLAKE2s block size in bytes (RFC 7693).
405    pub const BLOCK_SIZE: usize = 64;
406}
407
408// ── BLAKE2 streaming ─────────────────────────────────────────────────────────
409
410/// Streaming BLAKE2b-256 hasher (32-byte output).
411pub type Blake2b256Streaming = DigestStreamingAdapter<blake2::Blake2b256>;
412/// Streaming BLAKE2b-512 hasher (64-byte output).
413pub type Blake2b512Streaming = DigestStreamingAdapter<blake2::Blake2b512>;
414/// Streaming BLAKE2s-256 hasher (32-byte output).
415pub type Blake2s256Streaming = DigestStreamingAdapter<blake2::Blake2s256>;
416
417// ── BLAKE3 one-shot ───────────────────────────────────────────────────────────
418
419/// BLAKE3 hash function (32-byte output by default).
420#[derive(Debug, Default, Clone, Copy)]
421pub struct Blake3;
422
423impl Hash for Blake3 {
424    fn name(&self) -> &'static str {
425        "BLAKE3"
426    }
427    fn output_len(&self) -> usize {
428        32
429    }
430    fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
431        if out.len() < 32 {
432            return Err(CryptoError::BufferTooSmall);
433        }
434        let digest = blake3::hash(msg);
435        out[..32].copy_from_slice(digest.as_bytes());
436        Ok(())
437    }
438}
439
440impl Blake3 {
441    /// Byte length of the BLAKE3 default digest output.
442    pub const DIGEST_LEN: usize = 32;
443    /// BLAKE3 block size in bytes.
444    pub const BLOCK_SIZE: usize = 64;
445}
446
447// ── BLAKE3 streaming ─────────────────────────────────────────────────────────
448
449/// Streaming BLAKE3 hasher (32-byte output).
450///
451/// Uses `blake3::Hasher` directly since blake3 has its own incremental API.
452pub struct Blake3Streaming {
453    inner: blake3::Hasher,
454}
455
456impl Blake3Streaming {
457    /// Create a new BLAKE3 streaming hasher.
458    pub fn new() -> Self {
459        Self {
460            inner: blake3::Hasher::new(),
461        }
462    }
463}
464
465impl Default for Blake3Streaming {
466    fn default() -> Self {
467        Self::new()
468    }
469}
470
471impl Clone for Blake3Streaming {
472    fn clone(&self) -> Self {
473        Self {
474            inner: self.inner.clone(),
475        }
476    }
477}
478
479impl StreamingHash for Blake3Streaming {
480    fn update(&mut self, data: &[u8]) {
481        self.inner.update(data);
482    }
483
484    fn finalize(self, out: &mut [u8]) -> Result<(), CryptoError> {
485        if out.len() < 32 {
486            return Err(CryptoError::BufferTooSmall);
487        }
488        let result = self.inner.finalize();
489        out[..32].copy_from_slice(result.as_bytes());
490        Ok(())
491    }
492
493    fn reset(&mut self) {
494        self.inner.reset();
495    }
496}
497
498// ── std::io::Write for streaming hashers ─────────────────────────────────────
499
500/// Implement `std::io::Write` for [`DigestStreamingAdapter`] so that streaming
501/// hashers can be used as sinks in generic I/O pipelines.
502#[cfg(feature = "std")]
503impl<D: Digest + Default + Send> std::io::Write for DigestStreamingAdapter<D> {
504    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
505        self.update(buf);
506        Ok(buf.len())
507    }
508
509    fn flush(&mut self) -> std::io::Result<()> {
510        Ok(())
511    }
512}
513
514/// Implement `std::io::Write` for [`Blake3Streaming`] so that it can be used
515/// as a sink in generic I/O pipelines.
516#[cfg(feature = "std")]
517impl std::io::Write for Blake3Streaming {
518    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
519        self.update(buf);
520        Ok(buf.len())
521    }
522
523    fn flush(&mut self) -> std::io::Result<()> {
524        Ok(())
525    }
526}
527
528// ── Hex-digest convenience functions ─────────────────────────────────────────
529
530/// Hash `msg` with SHA-256 and return a lowercase hex string.
531///
532/// This function requires the `std` feature because it allocates a `String`.
533#[cfg(feature = "std")]
534pub fn sha256_hex(msg: &[u8]) -> std::string::String {
535    let digest = sha2::Sha256::digest(msg);
536    bytes_to_hex(digest.as_ref())
537}
538
539/// Hash `msg` with SHA-384 and return a lowercase hex string.
540#[cfg(feature = "std")]
541pub fn sha384_hex(msg: &[u8]) -> std::string::String {
542    let digest = sha2::Sha384::digest(msg);
543    bytes_to_hex(digest.as_ref())
544}
545
546/// Hash `msg` with SHA-512 and return a lowercase hex string.
547#[cfg(feature = "std")]
548pub fn sha512_hex(msg: &[u8]) -> std::string::String {
549    let digest = sha2::Sha512::digest(msg);
550    bytes_to_hex(digest.as_ref())
551}
552
553/// Hash `msg` with SHA3-256 and return a lowercase hex string.
554#[cfg(feature = "std")]
555pub fn sha3_256_hex(msg: &[u8]) -> std::string::String {
556    let digest = sha3::Sha3_256::digest(msg);
557    bytes_to_hex(digest.as_ref())
558}
559
560/// Hash `msg` with BLAKE3 and return a lowercase hex string.
561#[cfg(feature = "std")]
562pub fn blake3_hex(msg: &[u8]) -> std::string::String {
563    let digest = blake3::hash(msg);
564    bytes_to_hex(digest.as_bytes())
565}
566
567/// Convert a byte slice to a lowercase hexadecimal string.
568#[cfg(feature = "std")]
569fn bytes_to_hex(bytes: &[u8]) -> std::string::String {
570    bytes.iter().fold(
571        std::string::String::with_capacity(bytes.len() * 2),
572        |mut s, b| {
573            let _ = std::fmt::write(&mut s, format_args!("{b:02x}"));
574            s
575        },
576    )
577}
578
579// ── BLAKE3 keyed-hash mode ───────────────────────────────────────────────────
580
581/// BLAKE3 keyed-hash (MAC-like): deterministic 32-byte output under a 32-byte key.
582///
583/// Produces a unique output for each (key, message) pair. Keys are 32 bytes
584/// and must be kept secret for MAC use cases.
585pub struct Blake3Keyed {
586    key: [u8; 32],
587}
588
589impl Blake3Keyed {
590    /// Create a keyed hasher with the given 32-byte key.
591    pub fn new(key: [u8; 32]) -> Self {
592        Self { key }
593    }
594
595    /// Hash `msg` under this key; returns 32 bytes.
596    pub fn hash(&self, msg: &[u8]) -> [u8; 32] {
597        *blake3::keyed_hash(&self.key, msg).as_bytes()
598    }
599}
600
601/// Hash `msg` under `key` with BLAKE3 keyed-hash mode; returns 32 bytes.
602///
603/// Convenience free function — equivalent to `Blake3Keyed::new(key).hash(msg)`.
604pub fn blake3_keyed_hash(key: &[u8; 32], msg: &[u8]) -> [u8; 32] {
605    *blake3::keyed_hash(key, msg).as_bytes()
606}
607
608// ── BLAKE3 key-derivation mode ───────────────────────────────────────────────
609
610/// Derive a 32-byte key using BLAKE3 key-derivation mode.
611///
612/// `context` must be a globally unique, hardcoded string describing the
613/// purpose (e.g. `"MyApp 2024-01 file encryption key"`).  Key material
614/// may be any length, including zero.
615pub fn blake3_derive_key(context: &str, key_material: &[u8]) -> [u8; 32] {
616    blake3::derive_key(context, key_material)
617}
618
619// ── BLAKE3 XOF (extendable output) ───────────────────────────────────────────
620
621/// Hash `msg` with BLAKE3 and return `output_len` bytes.
622///
623/// The first 32 bytes are identical to the standard BLAKE3 hash of `msg`.
624/// Requesting more than 32 bytes extends the output using BLAKE3's XOF
625/// (extendable output function) mode.
626pub fn blake3_xof(msg: &[u8], output_len: usize) -> Vec<u8> {
627    let mut out = alloc::vec![0u8; output_len];
628    let mut reader = blake3::Hasher::new().update(msg).finalize_xof();
629    reader.fill(&mut out);
630    out
631}
632
633// ── Tests ─────────────────────────────────────────────────────────────────────
634
635#[cfg(test)]
636mod tests {
637    use super::*;
638
639    fn hex_decode(s: &str) -> Vec<u8> {
640        (0..s.len())
641            .step_by(2)
642            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).expect("valid hex"))
643            .collect()
644    }
645
646    // ── SHA-256 ──────────────────────────────────────────────────────────────
647
648    #[test]
649    fn sha256_empty() {
650        let hasher = Sha256;
651        let result = hasher.hash_to_vec(b"").unwrap();
652        let expected =
653            hex_decode("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
654        assert_eq!(result, expected, "SHA-256 of empty string mismatch");
655    }
656
657    #[test]
658    fn sha256_abc() {
659        let hasher = Sha256;
660        let result = hasher.hash_to_vec(b"abc").unwrap();
661        // NIST FIPS 180-4 test vector: SHA-256("abc")
662        let expected =
663            hex_decode("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
664        assert_eq!(result, expected, "SHA-256 of 'abc' mismatch");
665    }
666
667    #[test]
668    fn sha384_abc() {
669        let hasher = Sha384;
670        let result = hasher.hash_to_vec(b"abc").unwrap();
671        assert_eq!(result.len(), 48);
672    }
673
674    #[test]
675    fn sha512_abc() {
676        let hasher = Sha512;
677        let result = hasher.hash_to_vec(b"abc").unwrap();
678        assert_eq!(result.len(), 64);
679    }
680
681    #[test]
682    fn sha3_256_output_len() {
683        let hasher = Sha3_256;
684        assert_eq!(hasher.output_len(), 32);
685        let result = hasher.hash_to_vec(b"abc").unwrap();
686        assert_eq!(result.len(), 32);
687    }
688
689    #[test]
690    fn sha3_384_output_len() {
691        let hasher = Sha3_384;
692        assert_eq!(hasher.output_len(), 48);
693        let result = hasher.hash_to_vec(b"abc").unwrap();
694        assert_eq!(result.len(), 48);
695    }
696
697    #[test]
698    fn sha3_512_output_len() {
699        let hasher = Sha3_512;
700        assert_eq!(hasher.output_len(), 64);
701        let result = hasher.hash_to_vec(b"abc").unwrap();
702        assert_eq!(result.len(), 64);
703    }
704
705    #[test]
706    fn blake3_abc() {
707        let hasher = Blake3;
708        let result = hasher.hash_to_vec(b"abc").unwrap();
709        // Official BLAKE3 test vector for "abc":
710        // https://github.com/BLAKE3-team/BLAKE3/blob/master/test_vectors/test_vectors.json
711        let expected =
712            hex_decode("6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85");
713        assert_eq!(result, expected, "BLAKE3 of 'abc' mismatch");
714    }
715
716    #[test]
717    fn buffer_too_small_error() {
718        let hasher = Sha256;
719        let mut out = [0u8; 16];
720        let err = hasher.hash(b"test", &mut out).unwrap_err();
721        assert_eq!(err, CryptoError::BufferTooSmall);
722    }
723
724    // ── Streaming: SHA-256 equivalence ───────────────────────────────────────
725
726    #[test]
727    fn sha256_streaming_hello_world() {
728        // Streaming "hello" + " world" must equal one-shot "hello world"
729        let one_shot = Sha256.hash_to_vec(b"hello world").unwrap();
730
731        let mut streamer = Sha256Streaming::new();
732        StreamingHash::update(&mut streamer, b"hello");
733        StreamingHash::update(&mut streamer, b" world");
734        let mut buf = [0u8; 32];
735        StreamingHash::finalize(streamer, &mut buf).unwrap();
736
737        assert_eq!(buf.as_ref(), one_shot.as_slice());
738    }
739
740    #[test]
741    fn sha256_streaming_one_byte_chunks() {
742        // Feed "abc" one byte at a time; must equal one-shot SHA-256("abc")
743        let one_shot = Sha256.hash_to_vec(b"abc").unwrap();
744
745        let mut streamer = Sha256Streaming::new();
746        for byte in b"abc" {
747            StreamingHash::update(&mut streamer, core::slice::from_ref(byte));
748        }
749        let mut buf = [0u8; 32];
750        StreamingHash::finalize(streamer, &mut buf).unwrap();
751
752        assert_eq!(buf.as_ref(), one_shot.as_slice());
753    }
754
755    #[test]
756    fn sha256_streaming_reset() {
757        // After reset, streaming should produce a fresh hash
758        let expected = Sha256.hash_to_vec(b"world").unwrap();
759
760        let mut streamer = Sha256Streaming::new();
761        StreamingHash::update(&mut streamer, b"hello");
762        StreamingHash::reset(&mut streamer);
763        StreamingHash::update(&mut streamer, b"world");
764        let mut buf = [0u8; 32];
765        StreamingHash::finalize(streamer, &mut buf).unwrap();
766
767        assert_eq!(buf.as_ref(), expected.as_slice());
768    }
769
770    #[test]
771    fn sha256_streaming_buffer_too_small() {
772        let mut streamer = Sha256Streaming::new();
773        StreamingHash::update(&mut streamer, b"test");
774        let mut buf = [0u8; 16];
775        let err = StreamingHash::finalize(streamer, &mut buf).unwrap_err();
776        assert_eq!(err, CryptoError::BufferTooSmall);
777    }
778
779    // ── Streaming: BLAKE3 ────────────────────────────────────────────────────
780
781    #[test]
782    fn blake3_streaming_equivalence() {
783        let one_shot = Blake3.hash_to_vec(b"hello world").unwrap();
784
785        let mut streamer = Blake3Streaming::new();
786        StreamingHash::update(&mut streamer, b"hello");
787        StreamingHash::update(&mut streamer, b" world");
788        let mut buf = [0u8; 32];
789        StreamingHash::finalize(streamer, &mut buf).unwrap();
790
791        assert_eq!(buf.as_ref(), one_shot.as_slice());
792    }
793
794    #[test]
795    fn blake3_streaming_reset() {
796        let expected = Blake3.hash_to_vec(b"world").unwrap();
797
798        let mut streamer = Blake3Streaming::new();
799        StreamingHash::update(&mut streamer, b"hello");
800        StreamingHash::reset(&mut streamer);
801        StreamingHash::update(&mut streamer, b"world");
802        let mut buf = [0u8; 32];
803        StreamingHash::finalize(streamer, &mut buf).unwrap();
804
805        assert_eq!(buf.as_ref(), expected.as_slice());
806    }
807
808    // ── BLAKE2b-256 ──────────────────────────────────────────────────────────
809
810    #[test]
811    fn blake2b256_output_len() {
812        let hasher = Blake2b256;
813        assert_eq!(hasher.output_len(), 32);
814        let result = hasher.hash_to_vec(b"abc").unwrap();
815        assert_eq!(result.len(), 32);
816    }
817
818    #[test]
819    fn blake2b256_empty_nonzero() {
820        // BLAKE2b("")  -- just verify non-zero output of correct length
821        let result = Blake2b256.hash_to_vec(b"").unwrap();
822        assert_eq!(result.len(), 32);
823        assert!(
824            result.iter().any(|&b| b != 0),
825            "BLAKE2b-256 of empty should be non-zero"
826        );
827    }
828
829    #[test]
830    fn blake2b256_streaming_equivalence() {
831        let one_shot = Blake2b256.hash_to_vec(b"hello world").unwrap();
832
833        let mut streamer = Blake2b256Streaming::new();
834        StreamingHash::update(&mut streamer, b"hello");
835        StreamingHash::update(&mut streamer, b" world");
836        let mut buf = [0u8; 32];
837        StreamingHash::finalize(streamer, &mut buf).unwrap();
838
839        assert_eq!(buf.as_ref(), one_shot.as_slice());
840    }
841
842    // ── BLAKE2b-512 ──────────────────────────────────────────────────────────
843
844    #[test]
845    fn blake2b512_output_len() {
846        let hasher = Blake2b512;
847        assert_eq!(hasher.output_len(), 64);
848        let result = hasher.hash_to_vec(b"abc").unwrap();
849        assert_eq!(result.len(), 64);
850    }
851
852    #[test]
853    fn blake2b512_streaming_equivalence() {
854        let one_shot = Blake2b512.hash_to_vec(b"hello world").unwrap();
855
856        let mut streamer = Blake2b512Streaming::new();
857        StreamingHash::update(&mut streamer, b"hello");
858        StreamingHash::update(&mut streamer, b" world");
859        let mut buf = [0u8; 64];
860        StreamingHash::finalize(streamer, &mut buf).unwrap();
861
862        assert_eq!(buf.as_ref(), one_shot.as_slice());
863    }
864
865    // ── BLAKE2s-256 ──────────────────────────────────────────────────────────
866
867    #[test]
868    fn blake2s256_output_len() {
869        let hasher = Blake2s256;
870        assert_eq!(hasher.output_len(), 32);
871        let result = hasher.hash_to_vec(b"abc").unwrap();
872        assert_eq!(result.len(), 32);
873    }
874
875    #[test]
876    fn blake2s256_streaming_equivalence() {
877        let one_shot = Blake2s256.hash_to_vec(b"hello world").unwrap();
878
879        let mut streamer = Blake2s256Streaming::new();
880        StreamingHash::update(&mut streamer, b"hello");
881        StreamingHash::update(&mut streamer, b" world");
882        let mut buf = [0u8; 32];
883        StreamingHash::finalize(streamer, &mut buf).unwrap();
884
885        assert_eq!(buf.as_ref(), one_shot.as_slice());
886    }
887
888    // ── SHA-512/256 ──────────────────────────────────────────────────────────
889
890    #[test]
891    fn sha512_256_output_len() {
892        let hasher = Sha512_256;
893        assert_eq!(hasher.output_len(), 32);
894        let result = hasher.hash_to_vec(b"abc").unwrap();
895        assert_eq!(result.len(), 32);
896    }
897
898    #[test]
899    fn sha512_256_known_vector() {
900        // FIPS 180-4 known value: SHA-512/256("abc")
901        let result = Sha512_256.hash_to_vec(b"abc").unwrap();
902        let expected =
903            hex_decode("53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23");
904        assert_eq!(result, expected, "SHA-512/256 of 'abc' mismatch");
905    }
906
907    #[test]
908    fn sha512_256_streaming_equivalence() {
909        let one_shot = Sha512_256.hash_to_vec(b"hello world").unwrap();
910
911        let mut streamer = Sha512_256Streaming::new();
912        StreamingHash::update(&mut streamer, b"hello");
913        StreamingHash::update(&mut streamer, b" world");
914        let mut buf = [0u8; 32];
915        StreamingHash::finalize(streamer, &mut buf).unwrap();
916
917        assert_eq!(buf.as_ref(), one_shot.as_slice());
918    }
919
920    // ── Blake3Keyed ──────────────────────────────────────────────────────────
921
922    #[test]
923    fn blake3_keyed_different_keys() {
924        let key1 = [1u8; 32];
925        let key2 = [2u8; 32];
926        let msg = b"same message";
927
928        let out1 = Blake3Keyed::new(key1).hash(msg);
929        let out2 = Blake3Keyed::new(key2).hash(msg);
930
931        assert_ne!(out1, out2, "Different keys must produce different outputs");
932    }
933
934    #[test]
935    fn blake3_keyed_different_messages() {
936        let key = [42u8; 32];
937        let out1 = Blake3Keyed::new(key).hash(b"message1");
938        let out2 = Blake3Keyed::new(key).hash(b"message2");
939
940        assert_ne!(
941            out1, out2,
942            "Different messages must produce different outputs"
943        );
944    }
945
946    #[test]
947    fn blake3_keyed_deterministic() {
948        let key = [7u8; 32];
949        let msg = b"deterministic";
950        let out1 = Blake3Keyed::new(key).hash(msg);
951        let out2 = blake3_keyed_hash(&key, msg);
952
953        assert_eq!(out1, out2, "Method and free function must agree");
954    }
955
956    // ── blake3_derive_key ────────────────────────────────────────────────────
957
958    #[test]
959    fn blake3_derive_key_different_contexts() {
960        let material = b"shared key material";
961        let out1 = blake3_derive_key("context A", material);
962        let out2 = blake3_derive_key("context B", material);
963
964        assert_ne!(
965            out1, out2,
966            "Different contexts must produce different derived keys"
967        );
968    }
969
970    #[test]
971    fn blake3_derive_key_deterministic() {
972        let material = b"deterministic material";
973        let out1 = blake3_derive_key("test context", material);
974        let out2 = blake3_derive_key("test context", material);
975
976        assert_eq!(out1, out2, "derive_key must be deterministic");
977    }
978
979    #[test]
980    fn blake3_derive_key_output_len() {
981        let out = blake3_derive_key("oxicrypto test", b"material");
982        assert_eq!(out.len(), 32);
983    }
984
985    // ── blake3_xof ───────────────────────────────────────────────────────────
986
987    #[test]
988    fn blake3_xof_64_bytes() {
989        let out = blake3_xof(b"hello", 64);
990        assert_eq!(out.len(), 64);
991    }
992
993    #[test]
994    fn blake3_xof_first_32_match_standard_hash() {
995        let msg = b"xof test";
996        let standard = Blake3.hash_to_vec(msg).unwrap();
997        let extended = blake3_xof(msg, 64);
998
999        assert_eq!(
1000            &extended[..32],
1001            standard.as_slice(),
1002            "First 32 bytes of XOF must match standard BLAKE3 hash"
1003        );
1004    }
1005
1006    #[test]
1007    fn blake3_xof_prefix_consistency() {
1008        let msg = b"prefix test";
1009        let out64 = blake3_xof(msg, 64);
1010        let out128 = blake3_xof(msg, 128);
1011
1012        assert_eq!(
1013            &out128[..64],
1014            out64.as_slice(),
1015            "128-byte XOF must be prefixed by 64-byte XOF"
1016        );
1017    }
1018
1019    #[test]
1020    fn blake3_xof_zero_len() {
1021        let out = blake3_xof(b"anything", 0);
1022        assert!(out.is_empty());
1023    }
1024
1025    // ── DIGEST_LEN / BLOCK_SIZE constants ────────────────────────────────────
1026
1027    #[test]
1028    fn sha256_digest_len_constant() {
1029        assert_eq!(Sha256::DIGEST_LEN, 32);
1030        assert_eq!(Sha256::BLOCK_SIZE, 64);
1031    }
1032
1033    #[test]
1034    fn sha384_digest_len_constant() {
1035        assert_eq!(Sha384::DIGEST_LEN, 48);
1036        assert_eq!(Sha384::BLOCK_SIZE, 128);
1037    }
1038
1039    #[test]
1040    fn sha512_digest_len_constant() {
1041        assert_eq!(Sha512::DIGEST_LEN, 64);
1042        assert_eq!(Sha512::BLOCK_SIZE, 128);
1043    }
1044
1045    #[test]
1046    fn sha3_256_digest_len_constant() {
1047        assert_eq!(Sha3_256::DIGEST_LEN, 32);
1048    }
1049
1050    #[test]
1051    fn blake3_digest_len_constant() {
1052        assert_eq!(Blake3::DIGEST_LEN, 32);
1053        assert_eq!(Blake3::BLOCK_SIZE, 64);
1054    }
1055
1056    #[test]
1057    fn blake2b256_digest_len_constant() {
1058        assert_eq!(Blake2b256::DIGEST_LEN, 32);
1059        assert_eq!(Blake2b256::BLOCK_SIZE, 128);
1060    }
1061
1062    #[test]
1063    fn blake2b512_digest_len_constant() {
1064        assert_eq!(Blake2b512::DIGEST_LEN, 64);
1065        assert_eq!(Blake2b512::BLOCK_SIZE, 128);
1066    }
1067
1068    #[test]
1069    fn blake2s256_digest_len_constant() {
1070        assert_eq!(Blake2s256::DIGEST_LEN, 32);
1071        assert_eq!(Blake2s256::BLOCK_SIZE, 64);
1072    }
1073
1074    // ── Constants match runtime output_len ───────────────────────────────────
1075
1076    #[test]
1077    fn constants_match_runtime_output_len() {
1078        assert_eq!(Sha256::DIGEST_LEN, Sha256.output_len());
1079        assert_eq!(Sha384::DIGEST_LEN, Sha384.output_len());
1080        assert_eq!(Sha512::DIGEST_LEN, Sha512.output_len());
1081        assert_eq!(Blake3::DIGEST_LEN, Blake3.output_len());
1082        assert_eq!(Blake2b256::DIGEST_LEN, Blake2b256.output_len());
1083        assert_eq!(Blake2b512::DIGEST_LEN, Blake2b512.output_len());
1084        assert_eq!(Blake2s256::DIGEST_LEN, Blake2s256.output_len());
1085    }
1086
1087    // ── Clone for streaming types ─────────────────────────────────────────────
1088
1089    #[test]
1090    fn sha256_streaming_clone_independent() {
1091        let mut streamer = Sha256Streaming::new();
1092        StreamingHash::update(&mut streamer, b"hello");
1093        let mut cloned = streamer.clone();
1094        // Feed different data to original vs clone
1095        StreamingHash::update(&mut streamer, b" world");
1096        StreamingHash::update(&mut cloned, b" clone");
1097
1098        let mut buf1 = [0u8; 32];
1099        let mut buf2 = [0u8; 32];
1100        StreamingHash::finalize(streamer, &mut buf1).expect("finalize original");
1101        StreamingHash::finalize(cloned, &mut buf2).expect("finalize clone");
1102
1103        // Different suffixes → different digests
1104        assert_ne!(
1105            buf1, buf2,
1106            "cloned streamer with different data must differ"
1107        );
1108    }
1109
1110    #[test]
1111    fn blake3_streaming_clone_independent() {
1112        let mut streamer = Blake3Streaming::new();
1113        StreamingHash::update(&mut streamer, b"hello");
1114        let mut cloned = streamer.clone();
1115        StreamingHash::update(&mut streamer, b" a");
1116        StreamingHash::update(&mut cloned, b" b");
1117
1118        let mut buf1 = [0u8; 32];
1119        let mut buf2 = [0u8; 32];
1120        StreamingHash::finalize(streamer, &mut buf1).expect("finalize original");
1121        StreamingHash::finalize(cloned, &mut buf2).expect("finalize clone");
1122
1123        assert_ne!(
1124            buf1, buf2,
1125            "cloned Blake3Streaming with different data must differ"
1126        );
1127    }
1128
1129    // ── hex_digest functions ──────────────────────────────────────────────────
1130
1131    #[cfg(feature = "std")]
1132    #[test]
1133    fn sha256_hex_known_vector() {
1134        // SHA-256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
1135        let hex = sha256_hex(b"");
1136        assert_eq!(
1137            hex,
1138            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
1139        );
1140    }
1141
1142    #[cfg(feature = "std")]
1143    #[test]
1144    fn sha256_hex_length() {
1145        let hex = sha256_hex(b"abc");
1146        assert_eq!(hex.len(), 64, "SHA-256 hex string must be 64 characters");
1147        // Must be lowercase hex
1148        assert!(hex
1149            .chars()
1150            .all(|c| c.is_ascii_digit() || ('a'..='f').contains(&c)));
1151    }
1152
1153    #[cfg(feature = "std")]
1154    #[test]
1155    fn sha512_hex_length() {
1156        let hex = sha512_hex(b"abc");
1157        assert_eq!(hex.len(), 128, "SHA-512 hex string must be 128 characters");
1158    }
1159
1160    #[cfg(feature = "std")]
1161    #[test]
1162    fn blake3_hex_known_vector() {
1163        // BLAKE3("abc") = 6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85
1164        let hex = blake3_hex(b"abc");
1165        assert_eq!(
1166            hex,
1167            "6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85"
1168        );
1169    }
1170
1171    // ── io::Write for streaming hashers ──────────────────────────────────────
1172
1173    #[cfg(feature = "std")]
1174    #[test]
1175    fn sha256_streaming_io_write() {
1176        use std::io::Write;
1177        let mut streamer = Sha256Streaming::new();
1178        streamer.write_all(b"hello").expect("write_all hello");
1179        streamer.write_all(b" world").expect("write_all world");
1180        let mut buf = [0u8; 32];
1181        StreamingHash::finalize(streamer, &mut buf).expect("finalize");
1182
1183        let expected = Sha256.hash_to_vec(b"hello world").expect("one-shot");
1184        assert_eq!(
1185            &buf[..],
1186            expected.as_slice(),
1187            "io::Write result must match one-shot"
1188        );
1189    }
1190
1191    #[cfg(feature = "std")]
1192    #[test]
1193    fn blake3_streaming_io_write() {
1194        use std::io::Write;
1195        let mut streamer = Blake3Streaming::new();
1196        streamer.write_all(b"test data").expect("write_all");
1197        let mut buf = [0u8; 32];
1198        StreamingHash::finalize(streamer, &mut buf).expect("finalize");
1199
1200        let expected = Blake3.hash_to_vec(b"test data").expect("one-shot");
1201        assert_eq!(
1202            &buf[..],
1203            expected.as_slice(),
1204            "io::Write result must match one-shot"
1205        );
1206    }
1207}