Skip to main content

argon2_rust/
blake2b.rs

1//! BLAKE2b and Argon2's `blake2b_long` variable-length extension.
2//!
3//! A line-by-line port of `phc-winner-argon2/src/blake2/blake2b.c`: the
4//! standard 12-round BLAKE2b with the standard IV and sigma table, with the
5//! parameter block XORed into the IV as eight little-endian `u64` words. Argon2
6//! uses it **unkeyed**, with `digest_length = outlen` and `fanout = depth = 1`.
7//!
8//! # Differences forced by Rust
9//!
10//! * The C guards every entry point against a *reused* state (`S->f[0] != 0`,
11//!   which `blake2b_final` leaves set). Here [`Blake2b::finalize`] takes `self`
12//!   by value, so reuse is a compile error and the guards are unreachable. They
13//!   are kept anyway, so the port matches the C statement for statement, and
14//!   `tests::reused_state_is_rejected` drives them directly.
15//! * `blake2b_final(S, out, outlen)` accepts any `outlen >= S->outlen` and
16//!   writes `S->outlen` bytes. [`Blake2b::finalize`] does the same with
17//!   `out.len()` in place of `outlen`.
18
19use crate::error::Error;
20use crate::memory::{clear_internal_memory, clear_internal_memory_u64};
21#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
22use core::sync::atomic::{AtomicU8, Ordering};
23
24#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
25mod avx2;
26#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
27mod avx512;
28#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
29mod sse41;
30
31/// `BLAKE2B_BLOCKBYTES`.
32pub const BLOCKBYTES: usize = 128;
33/// `BLAKE2B_OUTBYTES`.
34pub const OUTBYTES: usize = 64;
35/// `BLAKE2B_KEYBYTES`.
36pub const KEYBYTES: usize = 64;
37/// `BLAKE2B_SALTBYTES`.
38pub const SALTBYTES: usize = 16;
39/// `BLAKE2B_PERSONALBYTES`.
40pub const PERSONALBYTES: usize = 16;
41
42/// Half a digest: how many bytes `blake2b_long` emits per extension step.
43const HALFBYTES: usize = OUTBYTES / 2;
44
45/// Size of `blake2b_param`, which is `#pragma pack`ed to exactly 64 bytes.
46const PARAMBYTES: usize = 64;
47
48/// The BLAKE2b IV (`blake2b_IV` in `blake2b.c`).
49pub const IV: [u64; 8] = [
50    0x6a09e667f3bcc908,
51    0xbb67ae8584caa73b,
52    0x3c6ef372fe94f82b,
53    0xa54ff53a5f1d36f1,
54    0x510e527fade682d1,
55    0x9b05688c2b3e6c1f,
56    0x1f83d9abfb41bd6b,
57    0x5be0cd19137e2179,
58];
59
60/// The message schedule (`blake2b_sigma`). `usize` so it indexes `m` directly.
61const SIGMA: [[usize; 16]; 12] = [
62    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
63    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
64    [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
65    [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
66    [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
67    [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
68    [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
69    [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
70    [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
71    [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
72    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
73    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
74];
75
76/// `load64()` from `blake2-impl.h`, total: a short slice reads as if
77/// zero-padded, so it can never panic. Callers always pass exactly 8 bytes.
78#[inline]
79fn load64(src: &[u8]) -> u64 {
80    let mut bytes = [0u8; 8];
81    let n = if src.len() < 8 { src.len() } else { 8 };
82    bytes[..n].copy_from_slice(&src[..n]);
83    u64::from_le_bytes(bytes)
84}
85
86/// The `G` macro from `blake2b_compress`.
87///
88/// `s` is one row of [`SIGMA`], `i` the column index within the round. Every
89/// index is `< 16`, so no access here can be out of bounds.
90#[inline(always)]
91#[allow(clippy::too_many_arguments)]
92fn g(
93    v: &mut [u64; 16],
94    m: &[u64; 16],
95    s: &[usize; 16],
96    i: usize,
97    a: usize,
98    b: usize,
99    c: usize,
100    d: usize,
101) {
102    // Every `+` in the C is modular `uint64_t` arithmetic: `wrapping_add`, or
103    // this would panic in a debug build.
104    v[a] = v[a].wrapping_add(v[b]).wrapping_add(m[s[2 * i]]);
105    v[d] = (v[d] ^ v[a]).rotate_right(32);
106    v[c] = v[c].wrapping_add(v[d]);
107    v[b] = (v[b] ^ v[c]).rotate_right(24);
108    v[a] = v[a].wrapping_add(v[b]).wrapping_add(m[s[2 * i + 1]]);
109    v[d] = (v[d] ^ v[a]).rotate_right(16);
110    v[c] = v[c].wrapping_add(v[d]);
111    v[b] = (v[b] ^ v[c]).rotate_right(63);
112}
113
114/// Portable `blake2b_compress()`: absorb one parsed 128-byte block into `h`.
115fn compress_scalar(h: &mut [u64; 8], t: &[u64; 2], f: &[u64; 2], m: &[u64; 16]) {
116    let mut v = [0u64; 16];
117    v[..8].copy_from_slice(h);
118    v[8] = IV[0];
119    v[9] = IV[1];
120    v[10] = IV[2];
121    v[11] = IV[3];
122    v[12] = IV[4] ^ t[0];
123    v[13] = IV[5] ^ t[1];
124    v[14] = IV[6] ^ f[0];
125    v[15] = IV[7] ^ f[1];
126
127    for s in &SIGMA {
128        g(&mut v, m, s, 0, 0, 4, 8, 12);
129        g(&mut v, m, s, 1, 1, 5, 9, 13);
130        g(&mut v, m, s, 2, 2, 6, 10, 14);
131        g(&mut v, m, s, 3, 3, 7, 11, 15);
132        g(&mut v, m, s, 4, 0, 5, 10, 15);
133        g(&mut v, m, s, 5, 1, 6, 11, 12);
134        g(&mut v, m, s, 6, 2, 7, 8, 13);
135        g(&mut v, m, s, 7, 3, 4, 9, 14);
136    }
137
138    for (i, word) in h.iter_mut().enumerate() {
139        *word ^= v[i] ^ v[i + 8];
140    }
141}
142
143/// The compression implementations available to BLAKE2b on this target.
144///
145/// This is deliberately separate from [`crate::fill_block::Backend`]. One
146/// BLAKE2b compression has four naturally parallel `G` functions. SSE4.1
147/// handles them as two pairs; AVX2 handles all four in one register. The
148/// AVX-512 backend requires AVX2, AVX-512F and AVX-512VL, and uses a native
149/// 256-bit rotate instead of widening into half-empty ZMM registers.
150///
151/// A two-register NEON version was also measured on Apple ARM64 and rejected:
152/// it made the 72-byte digest 16% slower and the 1 KiB expansion 26% slower
153/// than scalar. On x86, upstream's SSE4.1 schedule was retained because it
154/// made Argon2's 72-to-1024-byte expansion 15-16% faster on AMD EPYC. SSE2 and
155/// SSSE3 were rejected because they regressed longer inputs substantially.
156#[derive(Copy, Clone, Debug, PartialEq, Eq)]
157#[repr(u8)]
158pub enum Blake2bBackend {
159    /// Portable scalar code. Always available.
160    Scalar = 0,
161    /// x86/x86-64 SSE4.1, two 64-bit lanes per register.
162    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
163    Sse41 = 1,
164    /// x86/x86-64 AVX2, four 64-bit lanes.
165    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
166    Avx2 = 2,
167    /// x86/x86-64 AVX2 + AVX-512F + AVX-512VL, with native rotates.
168    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
169    Avx512 = 3,
170}
171
172impl Blake2bBackend {
173    /// Every BLAKE2b backend compiled for this target.
174    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
175    pub const ALL: &'static [Blake2bBackend] = &[
176        Blake2bBackend::Scalar,
177        Blake2bBackend::Sse41,
178        Blake2bBackend::Avx2,
179        Blake2bBackend::Avx512,
180    ];
181    /// Every BLAKE2b backend compiled for this target.
182    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
183    pub const ALL: &'static [Blake2bBackend] = &[Blake2bBackend::Scalar];
184
185    /// A short diagnostic/benchmark name.
186    #[must_use]
187    pub const fn name(self) -> &'static str {
188        match self {
189            Blake2bBackend::Scalar => "scalar",
190            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
191            Blake2bBackend::Sse41 => "sse4.1",
192            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
193            Blake2bBackend::Avx2 => "avx2",
194            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
195            Blake2bBackend::Avx512 => "avx512",
196        }
197    }
198
199    /// Whether this CPU can execute the backend right now.
200    #[must_use]
201    pub fn is_available(self) -> bool {
202        match self {
203            Blake2bBackend::Scalar => true,
204            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
205            Blake2bBackend::Sse41 => have_sse41(),
206            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
207            Blake2bBackend::Avx2 => have_avx2(),
208            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
209            Blake2bBackend::Avx512 => have_avx512vl(),
210        }
211    }
212
213    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
214    const fn from_u8(value: u8) -> Blake2bBackend {
215        match value {
216            1 => Blake2bBackend::Sse41,
217            2 => Blake2bBackend::Avx2,
218            3 => Blake2bBackend::Avx512,
219            _ => Blake2bBackend::Scalar,
220        }
221    }
222}
223
224impl core::fmt::Display for Blake2bBackend {
225    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
226        f.write_str(self.name())
227    }
228}
229
230#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
231fn have_sse41() -> bool {
232    std::arch::is_x86_feature_detected!("sse4.1")
233}
234
235#[cfg(all(
236    not(feature = "std"),
237    any(target_arch = "x86", target_arch = "x86_64")
238))]
239fn have_sse41() -> bool {
240    cfg!(target_feature = "sse4.1")
241}
242
243/// Rosetta implements SSE4.1 correctly but made this exact compression path
244/// about twice as slow as scalar in matched-process measurements. Keep the
245/// backend executable for explicit differential tests, but do not select it
246/// automatically for translated x86-64 processes.
247///
248/// Gated `not(miri)` together with the only call site in
249/// [`detect_blake2b_backend`]: under Miri detection short-circuits to
250/// Scalar (no SIMD intrinsics), so compiling this helper would be dead
251/// code on `cargo miri test --test allocation_audit` (lib built without
252/// `cfg(test)`).
253#[cfg(all(
254    not(miri),
255    feature = "std",
256    target_arch = "x86_64",
257    target_os = "macos"
258))]
259fn prefer_sse41() -> bool {
260    use core::ffi::{c_char, c_int, c_void};
261
262    unsafe extern "C" {
263        fn sysctlbyname(
264            name: *const c_char,
265            oldp: *mut c_void,
266            oldlenp: *mut usize,
267            newp: *mut c_void,
268            newlen: usize,
269        ) -> c_int;
270    }
271
272    let mut translated: c_int = 0;
273    let mut translated_len = core::mem::size_of::<c_int>();
274    // SAFETY: the NUL-terminated key is static; both output pointers name a
275    // live integer and its size. An Intel Mac reports an unknown key, which is
276    // deliberately treated as "not translated".
277    let result = unsafe {
278        sysctlbyname(
279            c"sysctl.proc_translated".as_ptr(),
280            (&raw mut translated).cast(),
281            &raw mut translated_len,
282            core::ptr::null_mut(),
283            0,
284        )
285    };
286    have_sse41()
287        && !(result == 0
288            && translated_len == core::mem::size_of::<c_int>()
289            && translated == 1)
290}
291
292#[cfg(all(
293    not(miri),
294    any(target_arch = "x86", target_arch = "x86_64"),
295    not(all(feature = "std", target_arch = "x86_64", target_os = "macos"))
296))]
297fn prefer_sse41() -> bool {
298    have_sse41()
299}
300
301#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
302fn have_avx2() -> bool {
303    std::arch::is_x86_feature_detected!("avx2")
304}
305
306#[cfg(all(
307    not(feature = "std"),
308    any(target_arch = "x86", target_arch = "x86_64")
309))]
310fn have_avx2() -> bool {
311    cfg!(target_feature = "avx2")
312}
313
314#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
315fn have_avx512vl() -> bool {
316    std::arch::is_x86_feature_detected!("avx2")
317        && std::arch::is_x86_feature_detected!("avx512f")
318        && std::arch::is_x86_feature_detected!("avx512vl")
319}
320
321#[cfg(all(
322    not(feature = "std"),
323    any(target_arch = "x86", target_arch = "x86_64")
324))]
325fn have_avx512vl() -> bool {
326    cfg!(all(
327        target_feature = "avx2",
328        target_feature = "avx512f",
329        target_feature = "avx512vl"
330    ))
331}
332
333#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
334const BACKEND_UNINIT: u8 = u8::MAX;
335#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
336static DETECTED_BACKEND: AtomicU8 = AtomicU8::new(BACKEND_UNINIT);
337
338/// Detect the fastest supported BLAKE2b compression backend without touching
339/// the process-wide cache.
340#[must_use]
341pub fn detect_blake2b_backend() -> Blake2bBackend {
342    // Miri does not implement architecture intrinsics. Its job here is to
343    // exercise the portable state machine and wiping paths.
344    #[cfg(miri)]
345    return Blake2bBackend::Scalar;
346
347    #[cfg(not(miri))]
348    {
349        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
350        if have_avx512vl() {
351            return Blake2bBackend::Avx512;
352        }
353
354        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
355        if have_avx2() {
356            return Blake2bBackend::Avx2;
357        }
358
359        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
360        if prefer_sse41() {
361            return Blake2bBackend::Sse41;
362        }
363
364        Blake2bBackend::Scalar
365    }
366}
367
368/// Detect and populate the process-wide cache. Kept off the hot path.
369#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
370#[cold]
371#[inline(never)]
372fn detect_and_cache_blake2b_backend() -> Blake2bBackend {
373    let detected = detect_blake2b_backend();
374    // Relaxed is sufficient: this publishes one plain value with no associated
375    // data, and concurrent detection always computes the same result.
376    DETECTED_BACKEND.store(detected as u8, Ordering::Relaxed);
377    detected
378}
379
380/// The cached BLAKE2b backend used by newly-created states.
381#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
382#[inline]
383#[must_use]
384pub fn blake2b_backend() -> Blake2bBackend {
385    let cached = DETECTED_BACKEND.load(Ordering::Relaxed);
386    if cached == BACKEND_UNINIT {
387        detect_and_cache_blake2b_backend()
388    } else {
389        Blake2bBackend::from_u8(cached)
390    }
391}
392
393/// The portable backend used on targets with no accelerated implementation.
394#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
395#[inline]
396#[must_use]
397pub const fn blake2b_backend() -> Blake2bBackend {
398    Blake2bBackend::Scalar
399}
400
401#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
402type CompressFn = unsafe fn(&mut [u64; 8], &[u64; 2], &[u64; 2], &[u64; 16]);
403
404// A zero-sized choice preserves the old direct scalar call on targets where
405// this module has no accelerated backend. In particular, ARM must not pay an
406// indirect call for an x86-only optimization.
407#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
408#[derive(Copy, Clone)]
409struct ScalarCompress;
410#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
411type CompressFn = ScalarCompress;
412
413#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
414unsafe fn compress_scalar_backend(
415    h: &mut [u64; 8],
416    t: &[u64; 2],
417    f: &[u64; 2],
418    m: &[u64; 16],
419) {
420    compress_scalar(h, t, f, m);
421}
422
423#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
424fn compress_fn(backend: Blake2bBackend) -> CompressFn {
425    match backend {
426        Blake2bBackend::Scalar => compress_scalar_backend,
427        Blake2bBackend::Sse41 => sse41::compress,
428        Blake2bBackend::Avx2 => avx2::compress,
429        Blake2bBackend::Avx512 => avx512::compress,
430    }
431}
432
433#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
434fn compress_fn(_backend: Blake2bBackend) -> CompressFn {
435    ScalarCompress
436}
437
438/// Compress one parsed block using a backend already proved available.
439unsafe fn compress_parsed_with(
440    compress: CompressFn,
441    h: &mut [u64; 8],
442    t: &[u64; 2],
443    f: &[u64; 2],
444    m: &[u64; 16],
445) {
446    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
447    {
448        // SAFETY: transferred from this function's caller.
449        unsafe { compress(h, t, f, m) };
450    }
451
452    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
453    {
454        let ScalarCompress = compress;
455        compress_scalar(h, t, f, m);
456    }
457}
458
459/// Parse and compress one block using a backend already proved available.
460///
461/// Free-standing so the caller can pass `&mut self.h` alongside `&self.buf` as
462/// disjoint fields, without copying the block.
463fn compress_with(
464    compress: CompressFn,
465    h: &mut [u64; 8],
466    t: &[u64; 2],
467    f: &[u64; 2],
468    block: &[u8],
469) {
470    let mut m = [0u64; 16];
471    for (word, chunk) in m.iter_mut().zip(block.chunks_exact(8)) {
472        *word = load64(chunk);
473    }
474
475    // SAFETY: the function pointer enters a state only through `init_param`,
476    // whose automatic path calls `detect_blake2b_backend`. The explicit
477    // internal test/benchmark path is unsafe and transfers that proof to its
478    // caller. `m` has exactly sixteen words.
479    unsafe { compress_parsed_with(compress, h, t, f, &m) };
480}
481
482/// A streaming BLAKE2b state (`blake2b_state`).
483///
484/// `initial_hash` needs the streaming form: it feeds eleven separate pieces
485/// (the six `u32` parameters, then each length-prefixed buffer) into one digest.
486///
487/// Dropping the state wipes it, the way the C calls `clear_internal_memory` on
488/// every exit path.
489pub struct Blake2b {
490    h: [u64; 8],
491    t: [u64; 2],
492    f: [u64; 2],
493    buf: [u8; BLOCKBYTES],
494    /// Always `<= BLOCKBYTES`, which is what keeps the slicing below panic-free.
495    buflen: usize,
496    outlen: usize,
497    /// Tree-hashing flag. Argon2 never sets it — `blake2b_init0` memsets the
498    /// state to zero and nothing in this crate turns it on — but it is kept so
499    /// `set_lastblock` mirrors the C.
500    last_node: bool,
501    /// Resolved once when the state is created, not once per compressed block.
502    compress: CompressFn,
503}
504
505impl Blake2b {
506    /// `blake2b_init(S, outlen)`: unkeyed, `digest_length = outlen`,
507    /// `fanout = depth = 1`.
508    ///
509    /// # Errors
510    ///
511    /// [`Error::IncorrectParameter`] if `outlen` is 0 or greater than
512    /// [`OUTBYTES`] (the C returns -1 for both).
513    pub fn new(outlen: usize) -> Result<Blake2b, Error> {
514        Blake2b::init_param(outlen, 0, blake2b_backend())
515    }
516
517    /// Construct a state with an explicitly selected compression backend.
518    ///
519    /// This is an unstable test/benchmark hook. Normal callers must use
520    /// [`Blake2b::new`], which performs safe runtime detection.
521    ///
522    /// # Safety
523    ///
524    /// `backend` must be executable on the current CPU, as reported by
525    /// [`Blake2bBackend::is_available`].
526    pub unsafe fn new_with_backend(
527        outlen: usize,
528        backend: Blake2bBackend,
529    ) -> Result<Blake2b, Error> {
530        Blake2b::init_param(outlen, 0, backend)
531    }
532
533    /// `blake2b_init_key(S, outlen, key, keylen)`.
534    ///
535    /// Argon2 itself never keys BLAKE2b; provided for completeness and for the
536    /// official BLAKE2b test vectors.
537    ///
538    /// # Errors
539    ///
540    /// [`Error::IncorrectParameter`] for an invalid `outlen`, an empty key, or
541    /// a key longer than [`KEYBYTES`].
542    pub fn with_key(outlen: usize, key: &[u8]) -> Result<Blake2b, Error> {
543        // The C checks `outlen` first, then the key; both return -1.
544        if outlen == 0 || outlen > OUTBYTES {
545            return Err(Error::IncorrectParameter);
546        }
547        if key.is_empty() || key.len() > KEYBYTES {
548            return Err(Error::IncorrectParameter);
549        }
550
551        let mut state = Blake2b::init_param(outlen, key.len(), blake2b_backend())?;
552
553        // The key is absorbed as one zero-padded 128-byte block.
554        let mut block = [0u8; BLOCKBYTES];
555        block[..key.len()].copy_from_slice(key);
556        state.update(&block);
557        clear_internal_memory(&mut block);
558
559        Ok(state)
560    }
561
562    /// `blake2b_init_param()` for the two parameter blocks this port can build.
563    ///
564    /// `blake2b_param` is `#pragma pack`ed to 64 bytes and read back as eight
565    /// little-endian `u64`s, so the byte image is the specification:
566    ///
567    /// ```text
568    ///  0        digest_length      4..8    leaf_length  (0)
569    ///  1        key_length         8..16   node_offset  (0)
570    ///  2        fanout      (1)    16      node_depth   (0)
571    ///  3        depth       (1)    17      inner_length (0)
572    ///                              18..32  reserved     (0)
573    ///                              32..48  salt         (0)
574    ///                              48..64  personal     (0)
575    /// ```
576    fn init_param(
577        outlen: usize,
578        keylen: usize,
579        backend: Blake2bBackend,
580    ) -> Result<Blake2b, Error> {
581        if outlen == 0 || outlen > OUTBYTES {
582            return Err(Error::IncorrectParameter);
583        }
584        if keylen > KEYBYTES {
585            return Err(Error::IncorrectParameter);
586        }
587
588        let mut param = [0u8; PARAMBYTES];
589        // Both fit in a `u8`: bounded by OUTBYTES/KEYBYTES == 64 just above.
590        param[0] = outlen as u8;
591        param[1] = keylen as u8;
592        param[2] = 1; // fanout
593        param[3] = 1; // depth
594
595        // blake2b_init0 + "IV XOR Parameter Block".
596        let mut h = IV;
597        for (word, chunk) in h.iter_mut().zip(param.chunks_exact(8)) {
598            *word ^= load64(chunk);
599        }
600
601        Ok(Blake2b {
602            h,
603            t: [0; 2],
604            f: [0; 2],
605            buf: [0; BLOCKBYTES],
606            buflen: 0,
607            outlen,
608            last_node: false,
609            compress: compress_fn(backend),
610        })
611    }
612
613    /// `blake2b_increment_counter()`. The carry compares the *new* `t[0]`
614    /// against the increment, exactly as the C does.
615    #[inline]
616    fn increment_counter(&mut self, inc: u64) {
617        self.t[0] = self.t[0].wrapping_add(inc);
618        self.t[1] = self.t[1].wrapping_add(u64::from(self.t[0] < inc));
619    }
620
621    /// `blake2b_set_lastblock()`.
622    #[inline]
623    fn set_lastblock(&mut self) {
624        if self.last_node {
625            self.f[1] = !0;
626        }
627        self.f[0] = !0;
628    }
629
630    /// `blake2b_update(S, in, inlen)`.
631    ///
632    /// Note the buffering rule: a block is only compressed once
633    /// `buflen + inlen` is **strictly** greater than [`BLOCKBYTES`], so a full
634    /// 128-byte buffer is held back for [`Blake2b::finalize`] to compress with
635    /// the last-block flag set.
636    pub fn update(&mut self, input: &[u8]) {
637        if input.is_empty() {
638            return;
639        }
640        // `S->f[0] != 0` — a reused state. Unreachable: `finalize` consumes
641        // `self`. The C returns -1 without touching the state; do the same.
642        if self.f[0] != 0 {
643            return;
644        }
645
646        let mut pin = input;
647
648        if self.buflen + pin.len() > BLOCKBYTES {
649            // Complete the current block.
650            let left = self.buflen;
651            let fill = BLOCKBYTES - left;
652            self.buf[left..].copy_from_slice(&pin[..fill]);
653            self.increment_counter(BLOCKBYTES as u64);
654            compress_with(self.compress, &mut self.h, &self.t, &self.f, &self.buf);
655            self.buflen = 0;
656            pin = &pin[fill..];
657
658            // Avoid buffer copies when possible.
659            while pin.len() > BLOCKBYTES {
660                // In range: the loop guard just proved `pin.len() > BLOCKBYTES`.
661                let (block, rest) = pin.split_at(BLOCKBYTES);
662                self.increment_counter(BLOCKBYTES as u64);
663                compress_with(self.compress, &mut self.h, &self.t, &self.f, block);
664                pin = rest;
665            }
666        }
667
668        // `pin.len() <= BLOCKBYTES - buflen` here, either because the branch
669        // above ran (buflen == 0, pin.len() <= 128) or because it did not.
670        self.buf[self.buflen..self.buflen + pin.len()].copy_from_slice(pin);
671        self.buflen += pin.len();
672    }
673
674    /// `blake2b_final(S, out, outlen)`.
675    ///
676    /// Consumes the state — reuse is what the C's `f[0]` guard exists to
677    /// prevent, and here the type system does it. Writes the `outlen` bytes
678    /// given to [`Blake2b::new`] and leaves any excess capacity in `out`
679    /// untouched, matching `outlen >= S->outlen` in the C.
680    ///
681    /// # Errors
682    ///
683    /// [`Error::IncorrectParameter`] if `out` is shorter than the configured
684    /// `outlen`.
685    pub fn finalize(mut self, out: &mut [u8]) -> Result<(), Error> {
686        if out.len() < self.outlen {
687            return Err(Error::IncorrectParameter);
688        }
689        // A reused state. Unreachable here (see `update`), kept for fidelity.
690        if self.f[0] != 0 {
691            return Err(Error::IncorrectParameter);
692        }
693
694        self.increment_counter(self.buflen as u64);
695        self.set_lastblock();
696        // Padding. `buflen <= BLOCKBYTES`, so this range is always valid.
697        for byte in &mut self.buf[self.buflen..] {
698            *byte = 0;
699        }
700        compress_with(self.compress, &mut self.h, &self.t, &self.f, &self.buf);
701
702        let mut buffer = [0u8; OUTBYTES];
703        for (chunk, word) in buffer.chunks_exact_mut(8).zip(self.h.iter()) {
704            chunk.copy_from_slice(&word.to_le_bytes());
705        }
706        out[..self.outlen].copy_from_slice(&buffer[..self.outlen]);
707
708        clear_internal_memory(&mut buffer);
709        // `self` is dropped here, which wipes `buf` and `h` as the C does.
710        Ok(())
711    }
712}
713
714impl Drop for Blake2b {
715    fn drop(&mut self) {
716        // `blake2b_final` clears `buf` and `h`; `blake2b()` clears the whole
717        // state on every exit path, including the error ones. Doing it in
718        // `Drop` covers both, and covers a state abandoned mid-stream.
719        clear_internal_memory(&mut self.buf);
720        clear_internal_memory_u64(&mut self.h);
721        clear_internal_memory_u64(&mut self.t);
722        clear_internal_memory_u64(&mut self.f);
723    }
724}
725
726/// `blake2b(out, outlen, in, inlen, NULL, 0)`: the one-shot unkeyed digest.
727///
728/// The digest length is `out.len()`.
729///
730/// # Errors
731///
732/// [`Error::IncorrectParameter`] if `out.len()` is 0 or greater than 64
733/// (BLAKE2b's digest size).
734pub fn blake2b(out: &mut [u8], input: &[u8]) -> Result<(), Error> {
735    let backend = blake2b_backend();
736    // SAFETY: `blake2b_backend` only returns a backend available on this CPU.
737    unsafe { blake2b_with_backend(out, input, backend) }
738}
739
740/// One-shot BLAKE2b with an explicitly selected compression backend.
741///
742/// This is an unstable test/benchmark hook.
743///
744/// # Safety
745///
746/// `backend` must be executable on the current CPU, as reported by
747/// [`Blake2bBackend::is_available`].
748pub unsafe fn blake2b_with_backend(
749    out: &mut [u8],
750    input: &[u8],
751    backend: Blake2bBackend,
752) -> Result<(), Error> {
753    // SAFETY: transferred from this function's caller.
754    let mut state = unsafe { Blake2b::new_with_backend(out.len(), backend)? };
755    state.update(input);
756    state.finalize(out)
757}
758
759/// `blake2b_long(out, outlen, in, inlen)`: Argon2's variable-length extension.
760///
761/// The output length is `out.len()`.
762///
763/// For `outlen <= 64` it is `init(outlen)`, `update(LE32(outlen))`,
764/// `update(in)`, `final`.
765///
766/// For `outlen > 64` it produces a 64-byte `out_buffer`, emits the first 32
767/// bytes, then **while `toproduce > 64`** (strictly greater) rehashes 64 -> 64
768/// and emits 32 each time, and finally emits a `toproduce`-byte digest of the
769/// last 64-byte buffer. For `outlen = 1024` that is `32 + 29*32 + 64 = 1024`.
770/// Here `toproduce` is just the length of the not-yet-written tail of `out`.
771///
772/// # Errors
773///
774/// [`Error::IncorrectParameter`] if `out` is empty (the C's `blake2b_init`
775/// rejects a zero digest length) or longer than
776/// [`crate::params::MAX_OUTLEN`] (the C's `outlen > UINT32_MAX`).
777pub fn blake2b_long(out: &mut [u8], input: &[u8]) -> Result<(), Error> {
778    let backend = blake2b_backend();
779    // SAFETY: `blake2b_backend` only returns a backend available on this CPU.
780    unsafe { blake2b_long_with_backend(out, input, backend) }
781}
782
783/// Argon2's variable-length BLAKE2b extension with an explicitly selected
784/// compression backend.
785///
786/// This is an unstable test/benchmark hook.
787///
788/// # Safety
789///
790/// `backend` must be executable on the current CPU, as reported by
791/// [`Blake2bBackend::is_available`].
792pub unsafe fn blake2b_long_with_backend(
793    out: &mut [u8],
794    input: &[u8],
795    backend: Blake2bBackend,
796) -> Result<(), Error> {
797    // `if (outlen > UINT32_MAX) goto fail;`
798    let Ok(outlen) = u32::try_from(out.len()) else {
799        return Err(Error::IncorrectParameter);
800    };
801    let outlen_bytes = outlen.to_le_bytes();
802
803    if out.len() <= OUTBYTES {
804        // Rejects `out.len() == 0`, as `blake2b_init(&S, 0)` does in the C.
805        // SAFETY: transferred from this function's caller.
806        let mut state = unsafe { Blake2b::new_with_backend(out.len(), backend)? };
807        state.update(&outlen_bytes);
808        state.update(input);
809        return state.finalize(out);
810    }
811
812    let mut out_buffer = [0u8; OUTBYTES];
813    let mut in_buffer;
814
815    // SAFETY: transferred from this function's caller.
816    let mut state = unsafe { Blake2b::new_with_backend(OUTBYTES, backend)? };
817    state.update(&outlen_bytes);
818    state.update(input);
819    state.finalize(&mut out_buffer)?;
820
821    // `tail` is the part of `out` still to be written; `tail.len()` is the C's
822    // `toproduce`. Every split below is guarded by `tail.len() > 64 > 32`.
823    let (head, mut tail) = out.split_at_mut(HALFBYTES);
824    head.copy_from_slice(&out_buffer[..HALFBYTES]);
825
826    while tail.len() > OUTBYTES {
827        in_buffer = out_buffer;
828        // SAFETY: transferred from this function's caller.
829        unsafe { blake2b_with_backend(&mut out_buffer, &in_buffer, backend)? };
830        let (head, rest) = tail.split_at_mut(HALFBYTES);
831        head.copy_from_slice(&out_buffer[..HALFBYTES]);
832        tail = rest;
833    }
834
835    // 33 <= tail.len() <= 64: the loop only ever subtracts 32 from a length
836    // that was greater than 64, and the first split left at least 33.
837    let toproduce = tail.len();
838    in_buffer = out_buffer;
839    // SAFETY: transferred from this function's caller.
840    unsafe { blake2b_with_backend(&mut out_buffer[..toproduce], &in_buffer, backend)? };
841    tail.copy_from_slice(&out_buffer[..toproduce]);
842
843    clear_internal_memory(&mut out_buffer);
844    clear_internal_memory(&mut in_buffer);
845    Ok(())
846}
847
848#[cfg(test)]
849mod tests {
850    use super::*;
851    use alloc::string::String;
852    use alloc::vec;
853    use alloc::vec::Vec;
854
855    #[test]
856    fn detection_selects_the_preferred_executable_backend() {
857        let expected = {
858            // Mirror `detect_blake2b_backend`: Miri has no SIMD path.
859            #[cfg(miri)]
860            {
861                Blake2bBackend::Scalar
862            }
863            #[cfg(all(not(miri), any(target_arch = "x86", target_arch = "x86_64")))]
864            {
865                if have_avx512vl() {
866                    Blake2bBackend::Avx512
867                } else if have_avx2() {
868                    Blake2bBackend::Avx2
869                } else if prefer_sse41() {
870                    Blake2bBackend::Sse41
871                } else {
872                    Blake2bBackend::Scalar
873                }
874            }
875            #[cfg(all(
876                not(miri),
877                not(any(target_arch = "x86", target_arch = "x86_64"))
878            ))]
879            {
880                Blake2bBackend::Scalar
881            }
882        };
883
884        assert_eq!(detect_blake2b_backend(), expected);
885        assert_eq!(blake2b_backend(), expected);
886        assert!(expected.is_available());
887    }
888
889    /// Every compiled SIMD compression backend must agree with the portable
890    /// function for arbitrary chaining values, counters, flags and blocks —
891    /// not only for the states that happen to occur in published vectors.
892    #[test]
893    fn compression_backends_match_scalar() {
894        fn next(x: &mut u64) -> u64 {
895            *x ^= *x << 13;
896            *x ^= *x >> 7;
897            *x ^= *x << 17;
898            *x
899        }
900
901        for &backend in Blake2bBackend::ALL {
902            if !backend.is_available() {
903                continue;
904            }
905
906            let implementation = compress_fn(backend);
907            let mut seed = 0x243f_6a88_85a3_08d3;
908            for case in 0..128 {
909                let mut expected = [0u64; 8];
910                for word in &mut expected {
911                    *word = next(&mut seed);
912                }
913                let mut actual = expected;
914                let t = [next(&mut seed), next(&mut seed)];
915                let f = [
916                    if case & 1 == 0 { 0 } else { u64::MAX },
917                    if case & 2 == 0 { 0 } else { u64::MAX },
918                ];
919                let mut m = [0u64; 16];
920                for word in &mut m {
921                    *word = next(&mut seed);
922                }
923
924                compress_scalar(&mut expected, &t, &f, &m);
925                // SAFETY: unavailable backends were skipped above.
926                unsafe { compress_parsed_with(implementation, &mut actual, &t, &f, &m) };
927                assert_eq!(expected, actual, "backend={}", backend.name());
928            }
929        }
930    }
931
932    /// Exercise the complete streaming and `blake2b_long` state machines with
933    /// every backend. This catches mistakes outside the raw compression state,
934    /// such as selecting a fresh backend for the extension chain.
935    #[test]
936    fn full_backends_match_scalar() {
937        let input: Vec<u8> = (0..=255).chain(0..=31).collect();
938        let input_lengths = [0, 1, 63, 64, 65, 127, 128, 129, 255, 256, 288];
939        let output_lengths = [1, 16, 32, 63, 64, 65, 96, 127, 128, 1024];
940
941        for &backend in Blake2bBackend::ALL {
942            if !backend.is_available() {
943                continue;
944            }
945            for &input_len in &input_lengths {
946                for &output_len in &output_lengths {
947                    let mut expected = vec![0u8; output_len];
948                    let mut actual = vec![0u8; output_len];
949                    // SAFETY: scalar is always executable; every other backend
950                    // passed the availability guard above.
951                    unsafe {
952                        blake2b_long_with_backend(
953                            &mut expected,
954                            &input[..input_len],
955                            Blake2bBackend::Scalar,
956                        )
957                        .expect("valid lengths");
958                        blake2b_long_with_backend(
959                            &mut actual,
960                            &input[..input_len],
961                            backend,
962                        )
963                        .expect("valid lengths");
964                    }
965                    assert_eq!(expected, actual, "backend={} input={input_len} out={output_len}", backend.name());
966                }
967            }
968        }
969    }
970
971    // ------------------------------------------------------------------
972    // Ground truth.
973    //
974    // Every hex string below was produced by compiling
975    // `phc-winner-argon2/src/blake2/blake2b.c` unmodified against a small C
976    // harness and printing the bytes; the tables were then generated
977    // mechanically, not transcribed. EMPTY_512 and ABC_512 additionally match
978    // the published BLAKE2b-512 test vectors, and KEYED64_512[0] matches the
979    // first entry of the official `blake2b-kat.h`.
980    // ------------------------------------------------------------------
981
982    const EMPTY_512: &str = "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce";
983    const ABC_512: &str = "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923";
984
985    #[rustfmt::skip]
986    const SEQ_INPUT_512: &[(usize, &str)] = &[
987        (0, "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"),
988        (1, "2fa3f686df876995167e7c2e5d74c4c7b6e48f8068fe0e44208344d480f7904c36963e44115fe3eb2a3ac8694c28bcb4f5a0f3276f2e79487d8219057a506e4b"),
989        (63, "d10bf9a15b1c9fc8d41f89bb140bf0be08d2f3666176d13baac4d381358ad074c9d4748c300520eb026daeaea7c5b158892fde4e8ec17dc998dcd507df26eb63"),
990        (64, "2fc6e69fa26a89a5ed269092cb9b2a449a4409a7a44011eecad13d7c4b0456602d402fa5844f1a7a758136ce3d5d8d0e8b86921ffff4f692dd95bdc8e5ff0052"),
991        (127, "b6292669ccd38d5f01caae96ba272c76a879a45743afa0725d83b9ebb26665b731f1848c52f11972b6644f554c064fa90780dbbbf3a89d4fc31f67df3e5857ef"),
992        (128, "2319e3789c47e2daa5fe807f61bec2a1a6537fa03f19ff32e87eecbfd64b7e0e8ccff439ac333b040f19b0c4ddd11a61e24ac1fe0f10a039806c5dcc0da3d115"),
993        (129, "f59711d44a031d5f97a9413c065d1e614c417ede998590325f49bad2fd444d3e4418be19aec4e11449ac1a57207898bc57d76a1bcf3566292c20c683a5c4648f"),
994        (255, "5b21c5fd8868367612474fa2e70e9cfa2201ffeee8fafab5797ad58fefa17c9b5b107da4a3db6320baaf2c8617d5a51df914ae88da3867c2d41f0cc14fa67928"),
995        (256, "1ecc896f34d3f9cac484c73f75f6a5fb58ee6784be41b35f46067b9c65c63a6794d3d744112c653f73dd7deb6666204c5a9bfa5b46081fc10fdbe7884fa5cbf8"),
996        (257, "d8bfe068de0b4f9fa876a3f8024eb9f7b0029fd5dcf251199e065cee89e1a282c8dbf0442f2ade7294ac1c6be19b388dc990c34d8cb79f5f10c54fa813834fda"),
997        (383, "6af23f91ca3ca49b5c8267ea6e6e6597d34b0ae22d17634d2f48e6877f92809cc0a4f3fd9344ce154814493bd35c776923f9492e3733ac8cbc600e963dc78257"),
998        (384, "49b3d01a1f21431d4a9b65e0450bb0444b7d1deb81131d650d9cbefcad7436a0e51050445af39f3f1312dbe3e2d03601ba309d3bc3c46bc5bdc768feebe176fb"),
999    ];
1000
1001    #[rustfmt::skip]
1002    const ABC_SHORT_DIGESTS: &[(usize, &str)] = &[
1003        (1, "6b"),
1004        (20, "384264f676f39536840523f284921cdc68b6846b"),
1005        (32, "bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319"),
1006        (63, "eb5324bb0b0f9ca27381f22f5e49604d7c341b77371fe5bf61fb643c8ab481c7555ef17c9b9e7c92f0daafff6c0d748cab97d2b267bf53f8225c173ea26f3e"),
1007        (64, "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"),
1008    ];
1009
1010    #[rustfmt::skip]
1011    const KEYED64_512: &[(usize, &str)] = &[
1012        (0, "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568"),
1013        (1, "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd"),
1014        (2, "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965"),
1015        (3, "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5a17e364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1"),
1016        (64, "65676d800617972fbd87e4b9514e1c67402b7a331096d3bfac22f1abb95374abc942f16e9ab0ead33b87c91968a6e509e119ff07787b3ef483e1dcdccf6e3022"),
1017        (127, "76d2d819c92bce55fa8e092ab1bf9b9eab237a25267986cacf2b8ee14d214d730dc9a5aa2d7b596e86a1fd8fa0804c77402d2fcd45083688b218b1cdfa0dcbcb"),
1018        (128, "72065ee4dd91c2d8509fa1fc28a37c7fc9fa7d5b3f8ad3d0d7a25626b57b1b44788d4caf806290425f9890a3a2a35a905ab4b37acfd0da6e4517b2525c9651e4"),
1019        (129, "64475dfe7600d7171bea0b394e27c9b00d8e74dd1e416a79473682ad3dfdbb706631558055cfc8a40e07bd015a4540dcdea15883cbbf31412df1de1cd4152b91"),
1020        (255, "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461"),
1021    ];
1022
1023    const KEY16_ABC_512: &str = "4c76bc7ad0fc52e4bde231b38727c331172cfe3eeaf10cd2fa5c65abbdb8faeaad2da338531b382ac103f10ccaf41f74d8870d016c48b269c0d9a5cf5752c8c3";
1024
1025    #[rustfmt::skip]
1026    const LONG_EMPTY: &[(usize, &str)] = &[
1027        (1, "88"),
1028        (4, "431894c5"),
1029        (32, "547578a45cc5d4a6e6ad0b59905a85e08527c2b1420604e157772e7bf02c2672"),
1030        (63, "dc0d3ff9a4218744635fbae40331d1e69bf28200e2a268c722b8e10256ce8f75956f8f0481f99dae0ca012527475952d63614424d71e7848129d9947140c39"),
1031        (64, "7fedd5af05184f3700cb1986bf39663bc06501e6455da2b643d47bc1c01302bea32e4e9ec6b29f4d151c6348788b59d4e02e69e4199a886d5b36fc3e5200ab04"),
1032        (65, "8e8b922d272035d878f074418dd8fadac41f5865ae0dc5066383237e85104c776bcad5ae3396429e5048b46920bc0d7ad39ccbf77d99aeca1fe4702407b8603cb0"),
1033        (72, "e6e2acf2db332da1bea83127d5fb3f8aefbd9d1307efe4f09a813450828b782a157773321d11891262adfb5d1143bcf1ce3c50f7e453b0fcf2c36e08bb0480a8ec5fe940f653191d"),
1034        (96, "2933619da44b6063c6ad2f7e7caf776e62c3d5cc563e7c441cb33ec3274ef6c8b2755787b8ae7343ed71089edac2f1f07af6e830fe875a3ee6977bf71c8f8e45b0cb0ca86f192a71bf186ef9044c8022f828e1de4a1883282ea4617ffb950090"),
1035        (97, "bbc187c6e4d8525655d0ada62d16eed59f3db3ab07e04fb0483fd4ae21d88b980947a1cf35bf02acf14d5cd7587598b710c421f79f6538692f3cb9d8e8a3532b0a40bc2db816471ac4d29bb3908b4d76ffb2b6cc465720242985ac7fba5634873a"),
1036        (127, "e547a0bef8c8fa5f56835b345361b67798b31bdea276c15ce8e85b28cf4f3c9f9dc6bf1a5e8bd98aeb7357c08b4676c5470dbfeffc1f02aa59a75c2635e60e8584b718a3062fbe95c7389e1f2a039b410ae89d0d98b532e1b1e8d55a15bfde6e2e7c0a736e510a121c9583e6fe88b7fd53fb5702bb111eca534963b98eb11e"),
1037        (128, "b29368bb02ce0ae43090fe9aae30fb003364b966dce7007296855bbe48c4bacfdb0b66313b1b6d1445ec8f738605f82d14d1bdac5c08a3e82e5af784071c6390b8c1bcecf371bbb2b5518bbc3819b0bde7b4d1584fdf3a0160fcd94c4cb321ef8596457211899cbb5d60dafb837177151fe257d44642c8b6b28909c3a8272ec7"),
1038        (129, "dfb78002c8bf1fa06b57e467781f9ba22d9905fd141102c7fc20b4d2dbd39a594e25803dbbba9059c9dbce98c146902f92e8b416c6339d4e6f12d94659a545fe5cb5bde3e818926cd127e8a716ed36f845af0512a80c683193d85762e2c6b3328cc57a75b56be044869047d45184857df32585645bd643bb2b37a75d4352602c14"),
1039        (1024, "845b3c40f4155e5f90e2b72ed952c88d976b45ccdd74d419cea79a6ae874a0170481acf57ad997333f407aa75655962ef2041f129406aca1b0e03ada5e43a21e83a3c04f04af6603100772c73497d884d01ef8ad0bc756973ac54fb564516844f7dc5eb12af3240c937af768018fed5ef81da41a1ede4e2f22b82c635979a6f619d66b96d82a4b2ff6917b0c889e7c2f87dbdb98ad5d5c33f7def3962f280ea3f12f2856b0bcf3c755f761c5c076af39f73a1bc0d7c333b21655af1917dc8fb32294168224fd49712606dc5aa95dcd92d59109d02f9aeb7c3ab01017aadd0116f4fbd0ca6b8a451b66c42229cc0ceaefb29de10b8c1f26d4a87d2bc6848c859857d34920dda2140822055a7decdf1cbb9aead07cec689d891972aa463bc3677f3885dc78d91b3ccfd2137ae02a16716c454c8a59fc287afe65f95d3e238925e50191a49d329bb601b3d746ac1fd8d5f4c5983015b7760b7bd863d4d7c92152fdffb60df7c7194d7f39326cc67bd1d83204d4999f4a9e5cde6f7c8fd5bd343d0490bcb917c011ccd6f1c167b22bb3a3488083965677ee135945815f467d0bb7201a35392154e57c52750b98a7a9fca0d0789f4863c494ca1b5474f48cbda149d6a62e5c1bc087f6ac831bb32df43b5e66188dff97bb7f89f5e92c219c830450935c60a250e3ba1cc5e14f24003694cefb30219c241097f61a2110f1848e7f194ef6bb27a110c2db9b54d75909a77fb857ef2bb1a6b36a5e2a20bca79549fdfa9e668cadd0cc82d23cdc6d75cf65932a6a34a6bdb07a5ca54e16410ac0414ae9115ca776667add778094f313a3ebed2707043cbe4994846eb9c725df1cb84a42fe1311cb781408b4bb8e5645348f1fd0565005f8e17a36dcc4f12d473f600521c9dc8eb3aa495c80e9ad2b1d80e328116f13c3d0fd4cb5ec4e508fbb802368febdea52abf93dfd8aebe937b8435c01a5c701ff5348f0290b89ea2fde8f7b16c9713e8c6bdcae3bc69f73de3e3a16b4ff3d17c4b2d922d37036b5703ed3031bae4ec1abfc8bc94facb4202474acc3c659754beab08bbc5281e9b467f75c7cc5db648d7c2a566b6e95567d2a7acd70646859e243278238bdb279f1c0c16d943c230a0f56d0b74c05266a6996ac791f3e2a8e1e90b31330599cc9fc8d2973fb93535b560cca2f15208f9259c08982a81b9f0612cf42ea8c9ef613d2a7a44c4bf5debfa0d8b6ef7d0b6a858892e1be00a794540f6c227573c20b2bd7cef2a8a71c16a3592b6f7ab381b7b2545388f3879a1c81c05383242b8776fe4db9d8cc064702111796828c400609b6a7d247198c1867a9937ff9a2913f16a58b5f9c4451d853ce9b602763319df8b4911c8cf62901232768b3a5fe6323d85deac32ea6bc96022cd65eac1c760427c23c4da56812651b7e123a8a4a16ec794db78a0f5f8f107851"),
1040    ];
1041
1042    #[rustfmt::skip]
1043    const LONG_ABC: &[(usize, &str)] = &[
1044        (1, "79"),
1045        (4, "3e6c02ac"),
1046        (32, "6cfcbf5d43e547674bfbc009070570bcb84e272d359c1e9277e416d74cbbe1c6"),
1047        (63, "dcb6b07bc6dc2bc0095962ee05ccce1905972d78956e7118b050166e21d7262eea5924bcef9b3efc50ec39e6cae5edf759e9979d7bffb9898356e3a76b83ab"),
1048        (64, "f32577a3172f56657d531faaa43077bb8c9726ada7bb04dd337ec5a65454abff241ad6b87a72440e5127c6f9caa70327f2a699096e52d163eb52d9cd99620593"),
1049        (65, "77baa447fe6f777c9bcb519545ce80badaf08ecb973fbb4ff45af7d8b569ec7caad65f72669d05153cdeaa563a162bb5ae4c42214551593816008dc560c5a65391"),
1050        (72, "13ac1def3e362ae0a78cdcb810a81885c95926cabee9dee40b46f9fc31cfa58a3e4e098a7beea541ca0b9002f89434898fc85990e937d91ddd70754b10d5316ad4fd8cd64564db92"),
1051        (96, "41b5590d909a09f99c2c5475f782ba721397c7dc626736a3b5b0b369bab78af24a800ffd07bd9b94e073fe5451b20caf770b68afe0839842b4723550bdf083cf9e205e5304aaec1584f10008285f563252fcfcf207ca8c755d1652168b9024af"),
1052        (97, "c82ccb541fdbecd1ce8090f61f39905cbf4bc03cebd133a002c7babe4ea204fba111552004c5a5338c62d4c7561af5de2665d24d30de9407b9ba1a6ab372054d3e6fdd0c7aa46b48980189af43f3502d68d007a02b1c4efe04c13b9f39d932f335"),
1053        (127, "29519d9954a0791d76c47164bbedf2f4fb71a7742dc15073cbb6b5daf34adb2f8d5ddd992dda0f1abbb46838676e2e7340bc82510f3f71e97982400b085024553fcaa92f91f31442c5084d3ecd03728597818c3b6fcb1ec9dd660aa5773ebc21626d5467972bc349a08c866f4f6eb3a5f944a76ca22d39bbabcebe565cb98a"),
1054        (128, "e03f682135fde8cb7caea3c8ad7c0a7e78efb026e119732d27b1eea7ba92335a7eb8825c755809add7833e7f75e7a5915bb1b3e70eca7b61bec34cd8c486f8005b05f94166103045f120f568fa1952f24e2a032a35d96e5a61fe520090178a4b60490d839f773b71f88589442d94bf5614c401e1a49b7d4d6e34782c0130e1c2"),
1055        (129, "81795998330577749c80da69a830b48e222555c721afd94d76dd0f9f2f6c6d18a441f42634e58d17da51d9979ae5638530dbe5db52b33bf8b3f4c3bfdd00287d055c3c4ac752f4996544c83253ee7a663a3c42b8e547d1f0a83bb34eb93aa12215687105cf925cd8d26b794b9ba4eefb3a1eea534a48be42894ddd5de35f354e4c"),
1056        (1024, "4038a0ea5c85fa5a0ea62fd668347bd44afd8da63438fd92fb08bee04dbe89ace70e7b8f4e9379b208c138b444e4434164437f03da914150722aaa902eb35fa0e710cfd7239e719b99c987f6b3d6f068d76e949c68084e608d280634fae1ea8423d431b6fa6c8b9b73f5343ea98aa27b95dc901e6e237a10251620aee94ee5b1d29651f228c8ba90ca3600c8e9f5b273f1a3dd9c011222db6085a566acf250cb07268dd47a4dfdc171b99f571dc84a38dd7ae4cf6b7b3c6581c9625b27bb4a0bcca537a34cd7a8f78e0c050f0089a25c5d2cd72de4f96ee451796df7d0f116591a8b33b0ee5105df5b4ff5c372cac6a1a24fb3ede74fef08f9b9f75d5ece43f4904461a3605cba1841daadeb1bf81e94807d99d4892c8b8a3b088584695d7bc39b29d467db4fbf09971598fdf02e23bb42c2aebb5d70ce213b12e4d330b2121dde4a548c0d62bfde70ad51cd06a51181d6df33fe65aaa55fd0a4f9c25e3f8f094848c05158bf47d8f05b204527a3064d190ef4813a97c404935ecf04244075c5c8370cfb0a6ec66479a10534abe3195a95b0875e813674e6f0bd6b11ad3890cafe09740b3aad45e7629de7324fc94e76023fca6dda84a1f1eefd751234e6d6d7de72666ddb84cf0d5e2a42505f3125439926c8449da9b67ccfc16892509901c19dc32595db48866951281e91c3973295a6ab375b54233c9d0441c081b0ba8074b816b59742042198a38a519ff06a2430edd4a9ba9b8fb874f9a895973877e5a2207c86a0f78cf952188337c2bc608974040c149e355e23315a338dd64f27bee9faff79fec5e5234ec52ffeddcfbd81d2c3db2888f36ec4199a730db5befbc30e75833240999ad8f9008eeab8ac6989a5ede8f46e3190ca9aff38717aa52dbc73b609b89a6b340355e6f5f81b95bf8e54f8a69f4e2bf95bd4c2e438589e4ffe2e91f286e40f78fb5972405ad546178ca1b91908a03ebf04228665d2313ceeff42a14cb8c1105731142398cb39aac140f5bbcf64514c5b33f31f27142ac08ea189dda4124f05221fa80fae12ee8fcc476e16af15b83274fcf98ffb29291ba1adb22ec944a0e8aef1dcca32545fd8751a07ed61d071ac735e782909e3a210d9b4262f0b6e5923a034985bf81aa837d76820f9e6cbb3e17eca941c0512ddca18c4b173e89390fee8407c903a6f8ef19460d0f800155cbb95899b828f21a09cba10a6464790ee766bfbd3a43be7594678a994d950a82aac89e24c1ab0fad8c364de19d2ba1a92663f4930d13b9b8a7fbbdac83bb74c4f9ba25a0c53aee93ebaab93e677c67f963eeefc06666dbe499222c1eccb16ab17de6533ce3336aa329bd4af8a09b19078a98dd48ef08da6762f125e673682d8822123b6d0fe99c97fda5e58406c64c157c76bb0a4bd1fc3f8fe819e96fbfdc50ba0194abc940ec7cdc9fff8a0"),
1057    ];
1058
1059    #[rustfmt::skip]
1060    const LONG_SEQ72: &[(usize, &str)] = &[
1061        (1, "fa"),
1062        (4, "ca513e0a"),
1063        (32, "36592a3c3e0dfcff6e0efcf362b2a8e68eb0a1563c041ba4272309536ae39b47"),
1064        (63, "5352b4a8ebcc8d25cfbfcca3226d9ed9567deae28f887ebcb9707534f83dbbad521370e0665dd1aa052b9f2086c72c1196fe325082d221edee0b3668ebac82"),
1065        (64, "2c6f5fa62d9b0549bfaae2b39e99afca0e624754e43f71bf8b2df8ead7151e3694fb51c7b4ec6de9b4f66426863ce4a520d7f84db5051250f5b4181f04aa4949"),
1066        (65, "9321f69a406e6ab17f116b5bdc619b9e794806601069888795e1e36eb382839f6189ffa17b35028daabbc9bf1db409643b9981fb4bb1764fb33325cdb6deafbad6"),
1067        (72, "906f3255cf91dc0af4da3697e8d7f1e5e1898157e63cd3e5c5de65a52130e486134b8c5598b255b83a48ac1826fc4107f60bd5adaee8660aa92df8612817463d0f15a00ba0e5fde5"),
1068        (96, "fb57485ec7a6d6c983353a051998d1ab68014821cca42c53e4c99ebe828f880f155105341c88adc44ff28118362581b73c5d4a65cee3a2d7dfb0f74623a0451220f27107948a281513191a0aa327bfd816d6b46be21d3237b7377cfe7174b11b"),
1069        (97, "e712e0864d944a484faef1107faa299d64a416d4b0348c560fb0e358488910571c63f872540695b85212a4b39f438b3c1ad21ed4148db9232c0bfa1231e04519a81c3ac662ffae2219b320d4a29c15760633ec7a3b04d69c96542016ce5e7aea80"),
1070        (127, "bda04620f38590a5a2a218564c97544a6572828775ea75d961b5c3ddfaeb769d38631639eceadb0aeb0def8ba5617a26a9aa88682cdd323428def92841d7edf2eb3375c29f24014e9c75a07c176528d102c8fa9ae28d7d6ce190cde38b943b12a4a8c1337fef010f815ab2cf25222cef2a9b9a451f1163288bf5add7a18495"),
1071        (128, "28739f1ddc548d8fa52f866c2eceaea11c2ea05d8e17f184df19bd2dc8210fde317df70bdaf91a1482ecca54d5b9ef0557bf817c41cc528257f25ed633ac6938e5b3f417606fe55559f57ed67d6accf9e32131348cdbb073896a6452405c9b83302800ddce561f96f4bb56f489338f0958e142aee3d326caee27fbaacd33fc27"),
1072        (129, "cae345ac89bc5a3cc819e2b9aae53f06a49c606b1de4376e30b23929d239ff046d92e34a1a6eeb3e1f474ed212b545d76aa1e4d88af7f17326a8ae255bdf9582a5692934d3016ab0f7fe78f95a8bd3db1da1678b0a78a636ef91914b53765313095247aa29a410522e3a060254c7b285243e34e049864bbdf4f3518aeb676cf5b7"),
1073        (1024, "d5f4c304b4be42af53855c080628693f4c741bfe3378766e5dd6b78097ba90b7140c2f3dc0960ae214ae1820dd8b008839ba2ebce72d123662eef7bd75204b3f4a9eea9a2e4a33637bbe28d1bb8d3b201cd85b324ccb5da17e2dccf1efd6d28be4159259adfce532dd62a973cc3c98c60547aabcf6abcb9283e30e581ba7e19f4c702faf54759a6aad67e950659ab8c9a5041a9c76e58fcb3a28ca9f43a280bee1d7cc86e0af6f890285e24e989d4771fdd8177cfba4db926f5938b8da89a85dc7fc312fda86a60938b6691da0ce86a7a6f8445bf0fd0cf49aefe39d476d7c91ab92e93339e887180d4e081cda235fcb39be7369a2fd73f21481ca305d2ba0e32caa2c464a109dc3e14062ea2d7a700b4752c31a8cc504d91289e46776f8fdeb4042e6de3f90361b5de27dacd4b6404154350e83570d06d0092de308069c1d58cb9a9dd2fce2f98cf73b1eabdb207b47136c6215e3a51b56d3a9078c0b92e2ba6848e4596636e45f0f074941adfcf5a7868ccdf97b62aa3a74b0d4ecc4ca4a25aaa086ec2b741ddf38637d0b88db86348917480c74871588cfe8a73d8911068b070081d8b88201791019b45da05f082cf21019963ff08104d73876c8b654451e4ad62b9accb64d6c8f5a8c7bb32ac1aac968ac48b95fc116b563244d7c64afd05a64acab82b9affb43357cfd430404113717baff8fc1cfb258315d9764f911c88914d976475f0e97d4d01d553c2394b00576326f1268e1e84b14750349b07e260177e3e84b779e201a50668d1957b608dad8422477d1785c71cd3ae7ca97957fdb2777c15328d4097c3108cd697f8933854c9e6a16f8c49ab0323bf24284ad57b0ee3852ffdbd7b7f05836c5910aec4c2cc7ec71b613b0fed9a816ad079b37de526cd0eba925f86932d59f84535e7f5d12fc14b2776cf09d34d1e73e5762255fdfca677e4bca438b6527e3bbfde8ba590e64373d46cb26c7e6cf8636925ddd8b5e509a69640d03e1fdb4e95321f7a195487544906f39e00fcbded63a9c3a88f1acb72f44b9a804c80338c777292b040ad175581a8e76d58cbae7240754d0d8aef79321447da39f556edf32003d160faadc579692c982d0e85146e5ca1909ca42de9d2bc11538f81c525866cd6c5019319cb3f44e5d97f915ca3a8ae53337d7d90c03cf00ea2986aebc3b141e1e9f78e9f1f58e29e5a5b1c2622c0ee2e8d12a9945d8b58ac43d8d9aababa1f93d1bcb640eb5e51aafae9873f3ccd25d784ccc8aec4f7a864295b50fff28b2c4f994f8f92d7c14475e284b2fbe6cd2e04a2f9ff5f0cd002a64ec3c66e8565dd74fa8d16a54e35d393c7d50a05f0f36ed0dc7b70f8907c99865757af2055714c4c695cd3c8bbe1cb4028d0bcb444f3561445356ebd40c3ba6acc9446c5942112fd8911f41fbd98ce10f4a9415476b9336122c9127"),
1074    ];
1075
1076    #[rustfmt::skip]
1077    const LONG_SEQ1024_IN: &[(usize, &str)] = &[
1078        (4, "b9c129b4"),
1079        (32, "6d65b7906fb326296197c405be7dee4c4ab2b6e40d4a959b99ceca5dd907275d"),
1080        (64, "7cd0c105b0a21a9f0f03ff8b3a94d72c58898ab95d904d685b02314aeb94ec4a587caa362abf5c4ec14732f590b304153bb2b0198200b7b334398be40fac7fb8"),
1081        (65, "adad4ba5075f72879db895fd08670fb41fce21d2e45e502f16b16d293d802d39ec97836223e8137674097b08ebff26b52456e84d461f1b68009d59ff0bd8c5806b"),
1082        (1024, "53acc9238374ecbd78f622b71ced7e0d62c200a0581d1be678330f3b924ae12ec932bc461d07e53072da275b4826828588c077e0dbdb8814d151a1abcbe8ffaf8f3444a17f9ca7c783867763942c931a715b2e9d91af4ebaa3b27ef3142f2c373eb38e1638efc3580ab6acd7b4f95ab05370916142fab304238408ae13049818d9ed5486c4e6ef801e7e210c39815a3daf4d07a02f7183a92a3380fc329ed4e5fb4f0f398bf8b53571accadd084458d6b681c5d34b0749aed210c85f0f96df6b250d470a46f99a57ebff9fe6037c1d3012f4f9dcfd0bf541f3aafb22a466237f62978526e5ff94a21daa12c662c4f468167e942ab3a1a257c3e5e61f7df0951867f4d73e1728c4fbb3e7e22569e1f41511e530652fc5b369c6a6154ecbf4bf0133c27fa18820cae10681748548fd0b24c7718834c273efcf8ee2dc430029a5af9e71d83f0e5dd97db328c6606c947c50435b091ca956e2017d0bb8dc95c59d787fbbf62796447fae7cedc2d7adc7e26212954a527991c36726f0c0b389e9389bfb065cd5ebb8ffbac8e7dcd44d0c6a18bc25c0bb7705253ef6c130dd19be2c7ffeec1f83edab0f94a132bed6debdf6186f025b7d209f8e5391a707870d6fa87790ba3b29f8f9b2b0d4606dbe709b4a544c4fb685351e170870336308d601900c346fa0488b2f0c48dee77ee9e1a4e5805004870b7719df90840994a7793a20187cc6aa8f5e0927e43102c6d260bbbd8750a5c4873e595a738f47a18f68cea2e92ff6b62301d25d5b57cbb484c1ea5ec44f3c1bddf8bda3b1de0f62cfa5543a69c086cfd4b9cbccf0c14040973f2c8fc90e09d2f0a089463101c1b49c2a179d72ef0d2910e158d4caa706c7de2aaf23115ea7c0ba51de990eba8f80c849834d9640a0fdd8975b6ee50c0774cd4fa8a84b35d6159acd325fba6540ff6e5387b7dfeb499d3cee87e4650823457e59fc51eafdd888d8413f833abc28af35713c355381799d58b46d9caeb8235b466827fc952acd70926677f73a65239a9a4b9f42ba48250996c08080629ed749df15ac2e4334107e7ffd6916ec301717c0e47a3fd060f12f2fd8b863a720316b1ec65282d0a57c908eb0095a09f99c0c69acd66772c346d3ce325222b24cef45ca80302263e7f3e5f5bbddf194d81e542fc51540f7eaa7e4d9eca93b57c238b1f283b4203025d55cde0330a14ef84dc8bf656095045cae80fcbb25b8f05a7bc099d5d379ce5bad52429613c6ff193b01b620df850b82bec94f84a0d5a7c1523207bff87f979d71f50842f498742af12e73374d8bdf0981b2dc4ebb6530d39a05c0aa692f254e870736e3f45eed6f4131f90a372e4fd1b5a2c0d5d37470f44787a281a8526a67801ce2838b53eaa3c7665539790750cdfa15c620e5df0503c7577854bb6304f5ce270f9170584224aa992b83b35173"),
1083    ];
1084
1085    const STREAM_SEQ300: &str = "d9cf5983dc6b34c0fa1f0226926855ad3eccd2bcdcd8f8053b9a80664d33b5afcc32fd21c70ea14f4ef50ca97c3203c4d1803159f0e01bb6cb1d1c83db52b63c";
1086
1087    /// The accumulator over the 926-digest sweep in
1088    /// [`sweep_matches_c_reference`], computed independently by the same C
1089    /// harness driving `blake2b.c`.
1090    const SWEEP: &str = "88cd3776d2b82923db5e42a2050d4490522ea9b7c10ef5b83d28a2dd59df25c48ff553afac387458ce7e3d2082d976667ea0142b9a5c2c973cc501f9999ec33a";
1091
1092    // ------------------------------------------------------------------
1093    // Helpers
1094    // ------------------------------------------------------------------
1095
1096    fn hex(bytes: &[u8]) -> String {
1097        const DIGITS: [u8; 16] = *b"0123456789abcdef";
1098        let mut s = String::with_capacity(bytes.len() * 2);
1099        for byte in bytes {
1100            s.push(char::from(DIGITS[usize::from(*byte >> 4)]));
1101            s.push(char::from(DIGITS[usize::from(*byte & 0x0f)]));
1102        }
1103        s
1104    }
1105
1106    /// `0, 1, 2, ... (mod 256)`, the filler the C harness used.
1107    fn seq(n: usize) -> Vec<u8> {
1108        (0..n).map(|i| (i & 0xff) as u8).collect()
1109    }
1110
1111    fn one_shot(outlen: usize, input: &[u8]) -> Vec<u8> {
1112        let mut out = vec![0u8; outlen];
1113        blake2b(&mut out, input).expect("outlen in 1..=64");
1114        out
1115    }
1116
1117    // ------------------------------------------------------------------
1118    // Tests
1119    // ------------------------------------------------------------------
1120
1121    /// The two published BLAKE2b-512 vectors, one-shot and streamed.
1122    #[test]
1123    fn published_512_vectors() {
1124        assert_eq!(hex(&one_shot(64, b"")), EMPTY_512);
1125        assert_eq!(hex(&one_shot(64, b"abc")), ABC_512);
1126
1127        // Same, through the streaming API.
1128        let mut out = [0u8; 64];
1129        let state = Blake2b::new(64).expect("outlen 64");
1130        state.finalize(&mut out).expect("64-byte buffer");
1131        assert_eq!(hex(&out), EMPTY_512);
1132
1133        let mut state = Blake2b::new(64).expect("outlen 64");
1134        state.update(b"a");
1135        state.update(b"b");
1136        state.update(b"c");
1137        state.finalize(&mut out).expect("64-byte buffer");
1138        assert_eq!(hex(&out), ABC_512);
1139    }
1140
1141    /// Input lengths around the 128-byte block boundary. `update` must hold a
1142    /// full block back, so 128 and 256 compress *only* in `finalize`.
1143    #[test]
1144    fn block_boundary_input_lengths() {
1145        for &(len, expected) in SEQ_INPUT_512 {
1146            let input = seq(len);
1147            assert_eq!(hex(&one_shot(64, &input)), expected, "inlen {len}");
1148        }
1149    }
1150
1151    /// The parameter block changes with `digest_length`, so short digests are
1152    /// not truncations of the 64-byte one.
1153    #[test]
1154    fn short_digest_lengths() {
1155        for &(outlen, expected) in ABC_SHORT_DIGESTS {
1156            assert_eq!(hex(&one_shot(outlen, b"abc")), expected, "outlen {outlen}");
1157        }
1158        // Not a prefix of the full digest.
1159        assert_ne!(ABC_SHORT_DIGESTS[2].1, &ABC_512[..64]);
1160    }
1161
1162    /// `blake2b_init_key`: key length lands in parameter byte 1 and the key is
1163    /// absorbed as one zero-padded 128-byte block.
1164    #[test]
1165    fn keyed_vectors() {
1166        let key = seq(64);
1167        for &(len, expected) in KEYED64_512 {
1168            let input = seq(len);
1169            let mut out = [0u8; 64];
1170            let mut state = Blake2b::with_key(64, &key).expect("64-byte key");
1171            state.update(&input);
1172            state.finalize(&mut out).expect("64-byte buffer");
1173            assert_eq!(hex(&out), expected, "keyed inlen {len}");
1174        }
1175
1176        let mut out = [0u8; 64];
1177        let mut state = Blake2b::with_key(64, &key[..16]).expect("16-byte key");
1178        state.update(b"abc");
1179        state.finalize(&mut out).expect("64-byte buffer");
1180        assert_eq!(hex(&out), KEY16_ABC_512);
1181    }
1182
1183    /// Chunked updates that straddle the block boundary, including a chunk that
1184    /// fills the buffer to exactly 128 and an empty chunk.
1185    #[test]
1186    fn streaming_chunks_match_one_shot() {
1187        let input = seq(300);
1188        let mut out = [0u8; 64];
1189        let mut state = Blake2b::new(64).expect("outlen 64");
1190        state.update(&input[..1]);
1191        state.update(&input[1..127]); // buflen 127
1192        state.update(&input[127..128]); // buflen 128 — must NOT compress
1193        state.update(&input[128..128]); // empty — no-op
1194        state.update(&input[128..]);
1195        state.finalize(&mut out).expect("64-byte buffer");
1196
1197        assert_eq!(hex(&out), STREAM_SEQ300);
1198        assert_eq!(hex(&out), hex(&one_shot(64, &input)));
1199    }
1200
1201    /// `blake2b_long` across the `outlen <= 64` branch, the extension loop, and
1202    /// the 96/97 boundary that the strictly-greater loop guard decides.
1203    #[test]
1204    fn blake2b_long_vectors() {
1205        for &(outlen, expected) in LONG_EMPTY {
1206            let mut out = vec![0u8; outlen];
1207            blake2b_long(&mut out, b"").expect("valid outlen");
1208            assert_eq!(hex(&out), expected, "long empty outlen {outlen}");
1209        }
1210        for &(outlen, expected) in LONG_ABC {
1211            let mut out = vec![0u8; outlen];
1212            blake2b_long(&mut out, b"abc").expect("valid outlen");
1213            assert_eq!(hex(&out), expected, "long abc outlen {outlen}");
1214        }
1215        let seq72 = seq(72);
1216        for &(outlen, expected) in LONG_SEQ72 {
1217            let mut out = vec![0u8; outlen];
1218            blake2b_long(&mut out, &seq72).expect("valid outlen");
1219            assert_eq!(hex(&out), expected, "long seq72 outlen {outlen}");
1220        }
1221        let seq1024 = seq(1024);
1222        for &(outlen, expected) in LONG_SEQ1024_IN {
1223            let mut out = vec![0u8; outlen];
1224            blake2b_long(&mut out, &seq1024).expect("valid outlen");
1225            assert_eq!(hex(&out), expected, "long seq1024-in outlen {outlen}");
1226        }
1227    }
1228
1229    /// The `outlen <= 64` branch is *not* a prefix of the `> 64` branch: the
1230    /// two feed different `digest_length`s into the parameter block.
1231    #[test]
1232    fn blake2b_long_branches_differ() {
1233        let mut short = [0u8; 64];
1234        blake2b_long(&mut short, b"abc").expect("outlen 64");
1235        let mut long = [0u8; 65];
1236        blake2b_long(&mut long, b"abc").expect("outlen 65");
1237        assert_ne!(short[..], long[..64]);
1238    }
1239
1240    #[test]
1241    fn blake2b_long_rejects_zero_outlen() {
1242        // The C's `blake2b_init(&S, 0)` fails, so `blake2b_long` returns -1.
1243        assert_eq!(
1244            blake2b_long(&mut [], b"abc"),
1245            Err(Error::IncorrectParameter)
1246        );
1247    }
1248
1249    #[test]
1250    fn init_rejects_bad_outlen_and_key() {
1251        assert_eq!(Blake2b::new(0).err(), Some(Error::IncorrectParameter));
1252        assert_eq!(Blake2b::new(65).err(), Some(Error::IncorrectParameter));
1253        assert!(Blake2b::new(1).is_ok());
1254        assert!(Blake2b::new(64).is_ok());
1255
1256        let key = seq(65);
1257        assert_eq!(
1258            Blake2b::with_key(64, &key).err(),
1259            Some(Error::IncorrectParameter)
1260        );
1261        assert_eq!(
1262            Blake2b::with_key(64, &[]).err(),
1263            Some(Error::IncorrectParameter)
1264        );
1265        assert_eq!(
1266            Blake2b::with_key(0, &key[..1]).err(),
1267            Some(Error::IncorrectParameter)
1268        );
1269
1270        // One-shot bounds come from `Blake2b::new`.
1271        assert_eq!(
1272            blake2b(&mut [], b"abc").err(),
1273            Some(Error::IncorrectParameter)
1274        );
1275        assert_eq!(
1276            blake2b(&mut [0u8; 65], b"abc").err(),
1277            Some(Error::IncorrectParameter)
1278        );
1279    }
1280
1281    /// `blake2b_final` accepts `outlen >= S->outlen` and writes only `S->outlen`
1282    /// bytes; a shorter buffer is an error.
1283    #[test]
1284    fn finalize_writes_exactly_outlen_bytes() {
1285        let mut out = [0xAAu8; 128];
1286        let mut state = Blake2b::new(32).expect("outlen 32");
1287        state.update(b"abc");
1288        state.finalize(&mut out).expect("128 >= 32");
1289        assert_eq!(hex(&out[..32]), ABC_SHORT_DIGESTS[2].1);
1290        assert!(out[32..].iter().all(|b| *b == 0xAA));
1291
1292        let mut short = [0u8; 31];
1293        let state = Blake2b::new(32).expect("outlen 32");
1294        assert_eq!(state.finalize(&mut short), Err(Error::IncorrectParameter));
1295    }
1296
1297    /// The C's reuse guard (`S->f[0] != 0`, set by `blake2b_final`). Rust's
1298    /// `finalize(self)` makes this unreachable, so drive it by hand: the C
1299    /// returns -1 from both `blake2b_update` and `blake2b_final`.
1300    #[test]
1301    fn reused_state_is_rejected() {
1302        let mut state = Blake2b::new(64).expect("outlen 64");
1303        state.update(b"abc");
1304        state.f[0] = !0; // what `finalize` leaves behind
1305
1306        state.update(b"more"); // C: returns -1, state untouched
1307        assert_eq!(state.buflen, 3);
1308
1309        let mut out = [0u8; 64];
1310        assert_eq!(state.finalize(&mut out), Err(Error::IncorrectParameter));
1311    }
1312
1313    /// A folded sweep over 926 digests, checked against one 64-byte
1314    /// accumulator that the C harness produced from `blake2b.c`:
1315    ///
1316    /// * **A** — every input length 0..=300 at `outlen` 64.
1317    /// * **B** — every digest length 1..=64 over a fixed 200-byte input.
1318    /// * **C** — every `blake2b_long` output length 1..=300, which walks the
1319    ///   extension loop from zero iterations up to eight.
1320    /// * **D** — every two-chunk split of a 260-byte stream. This is what pins
1321    ///   the buffering rule down: a compression may happen only once
1322    ///   `buflen + inlen` exceeds 128, so a split landing exactly on 128 or 256
1323    ///   must still hold its block back.
1324    ///
1325    /// The fixed tables above cover hand-picked boundaries; this covers every
1326    /// length in between, at the cost of one constant.
1327    #[test]
1328    fn sweep_matches_c_reference() {
1329        let mut acc = Blake2b::new(64).expect("outlen 64");
1330
1331        // A
1332        for inlen in 0..=300 {
1333            acc.update(&one_shot(64, &seq(inlen)));
1334        }
1335
1336        // B
1337        let in200 = seq(200);
1338        for outlen in 1..=64 {
1339            acc.update(&one_shot(outlen, &in200));
1340        }
1341
1342        // C
1343        for outlen in 1..=300usize {
1344            let mut out = vec![0u8; outlen];
1345            blake2b_long(&mut out, &seq(outlen % 137)).expect("valid outlen");
1346            acc.update(&out);
1347        }
1348
1349        // D
1350        let in260 = seq(260);
1351        for split in 0..=260 {
1352            let mut state = Blake2b::new(64).expect("outlen 64");
1353            state.update(&in260[..split]);
1354            state.update(&in260[split..]);
1355            let mut digest = [0u8; 64];
1356            state.finalize(&mut digest).expect("64-byte buffer");
1357            acc.update(&digest);
1358        }
1359
1360        let mut out = [0u8; 64];
1361        acc.finalize(&mut out).expect("64-byte buffer");
1362        assert_eq!(hex(&out), SWEEP);
1363    }
1364
1365    /// The parameter block is XORed into the IV as eight LE `u64`s, so for an
1366    /// unkeyed digest only word 0 changes: `outlen | keylen<<8 | 1<<16 | 1<<24`.
1367    #[test]
1368    fn parameter_block_layout() {
1369        let state = Blake2b::new(32).expect("outlen 32");
1370        assert_eq!(state.h[0], IV[0] ^ 0x0101_0020);
1371        assert_eq!(state.h[1..], IV[1..]);
1372        assert_eq!(state.outlen, 32);
1373
1374        // 0x0101_1040 = depth 1, fanout 1, key_length 16, digest_length 64.
1375        let key = seq(16);
1376        let keyed = Blake2b::with_key(64, &key).expect("16-byte key");
1377        assert_eq!(keyed.h[0], IV[0] ^ 0x0101_1040);
1378        assert_eq!(keyed.h[1..], IV[1..]);
1379
1380        // The key block is *buffered*, not compressed: `update` only compresses
1381        // once `buflen + inlen` exceeds 128, so a 128-byte key block leaves `h`
1382        // and the counter untouched. Byte 0 of the buffer is the key.
1383        assert_eq!(keyed.buflen, BLOCKBYTES);
1384        assert_eq!(keyed.t, [0, 0]);
1385        assert_eq!(keyed.buf[..16], key[..]);
1386        assert!(keyed.buf[16..].iter().all(|b| *b == 0));
1387    }
1388
1389    /// The counter is bumped by 128 per compressed block and by `buflen` at
1390    /// finalisation, and it carries into `t[1]`.
1391    #[test]
1392    fn counter_increments_and_carries() {
1393        let mut state = Blake2b::new(64).expect("outlen 64");
1394        state.update(&seq(300));
1395        // 300 bytes: two full blocks compressed, 44 held back.
1396        assert_eq!(state.t, [256, 0]);
1397        assert_eq!(state.buflen, 44);
1398
1399        let mut state = Blake2b::new(64).expect("outlen 64");
1400        state.t[0] = u64::MAX - 1;
1401        state.increment_counter(2);
1402        assert_eq!(state.t, [0, 1]);
1403    }
1404}