//! BLAKE2 (RFC 7693): BLAKE2b (64-bit) and BLAKE2s (32-bit), unkeyed.
//!
//! The digest length is a parameter folded into the initial state, so the same
//! core produces BLAKE2b-256/384/512 and BLAKE2s-256. Keyed mode, salt, and
//! personalization are not exposed.

use super::{Digest, Mac};
use crate::ct::{Choice, ConstantTimeEq};

/// Message-word schedule (12 rounds). BLAKE2b uses all 12; BLAKE2s uses the
/// first 10.
#[rustfmt::skip]
const SIGMA: [[usize; 16]; 12] = [
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
    [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
    [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
    [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
    [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
    [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
    [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
    [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
    [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
];

// ---- BLAKE2b (64-bit words, 128-byte blocks, 12 rounds) ----

const IV_B: [u64; 8] = [
    0x6a09_e667_f3bc_c908,
    0xbb67_ae85_84ca_a73b,
    0x3c6e_f372_fe94_f82b,
    0xa54f_f53a_5f1d_36f1,
    0x510e_527f_ade6_82d1,
    0x9b05_688c_2b3e_6c1f,
    0x1f83_d9ab_fb41_bd6b,
    0x5be0_cd19_137e_2179,
];

fn compress_b(h: &mut [u64; 8], block: &[u8; 128], t0: u64, t1: u64, last: bool) {
    let mut m = [0u64; 16];
    for (word, chunk) in m.iter_mut().zip(block.chunks_exact(8)) {
        *word = u64::from_le_bytes(chunk.try_into().unwrap());
    }

    let mut v = [0u64; 16];
    v[..8].copy_from_slice(h);
    v[8..].copy_from_slice(&IV_B);
    v[12] ^= t0;
    v[13] ^= t1;
    if last {
        v[14] ^= !0u64;
    }

    macro_rules! g {
        ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $y:expr) => {
            v[$a] = v[$a].wrapping_add(v[$b]).wrapping_add($x);
            v[$d] = (v[$d] ^ v[$a]).rotate_right(32);
            v[$c] = v[$c].wrapping_add(v[$d]);
            v[$b] = (v[$b] ^ v[$c]).rotate_right(24);
            v[$a] = v[$a].wrapping_add(v[$b]).wrapping_add($y);
            v[$d] = (v[$d] ^ v[$a]).rotate_right(16);
            v[$c] = v[$c].wrapping_add(v[$d]);
            v[$b] = (v[$b] ^ v[$c]).rotate_right(63);
        };
    }

    for s in SIGMA.iter() {
        g!(0, 4, 8, 12, m[s[0]], m[s[1]]);
        g!(1, 5, 9, 13, m[s[2]], m[s[3]]);
        g!(2, 6, 10, 14, m[s[4]], m[s[5]]);
        g!(3, 7, 11, 15, m[s[6]], m[s[7]]);
        g!(0, 5, 10, 15, m[s[8]], m[s[9]]);
        g!(1, 6, 11, 12, m[s[10]], m[s[11]]);
        g!(2, 7, 8, 13, m[s[12]], m[s[13]]);
        g!(3, 4, 9, 14, m[s[14]], m[s[15]]);
    }

    for i in 0..8 {
        h[i] ^= v[i] ^ v[i + 8];
    }
}

#[derive(Clone)]
struct Blake2bState {
    h: [u64; 8],
    t0: u64,
    t1: u64,
    buf: [u8; 128],
    buf_len: usize,
}

/// Builds a 64-byte BLAKE2b parameter block.
#[allow(clippy::too_many_arguments)]
fn param_b(
    digest_length: u8,
    key_length: u8,
    fanout: u8,
    depth: u8,
    leaf_length: u32,
    node_offset: u32,
    xof_length: u32,
    node_depth: u8,
    inner_length: u8,
) -> [u8; 64] {
    let mut p = [0u8; 64];
    p[0] = digest_length;
    p[1] = key_length;
    p[2] = fanout;
    p[3] = depth;
    p[4..8].copy_from_slice(&leaf_length.to_le_bytes());
    p[8..12].copy_from_slice(&node_offset.to_le_bytes());
    p[12..16].copy_from_slice(&xof_length.to_le_bytes());
    p[16] = node_depth;
    p[17] = inner_length;
    p
}

/// Initial state = IV XOR the parameter block, word by word.
fn iv_from_param_b(p: &[u8; 64]) -> [u64; 8] {
    let mut h = IV_B;
    for (i, hw) in h.iter_mut().enumerate() {
        *hw ^= u64::from_le_bytes(p[8 * i..8 * i + 8].try_into().unwrap());
    }
    h
}

impl Blake2bState {
    fn from_h(h: [u64; 8]) -> Self {
        Blake2bState {
            h,
            t0: 0,
            t1: 0,
            buf: [0u8; 128],
            buf_len: 0,
        }
    }

    fn new(outlen: usize) -> Self {
        Self::from_h(iv_from_param_b(&param_b(
            outlen as u8,
            0,
            1,
            1,
            0,
            0,
            0,
            0,
            0,
        )))
    }

    /// Keyed init: the key is processed as a zero-padded first block.
    fn with_key(outlen: usize, key: &[u8]) -> Self {
        let mut s = Self::from_h(iv_from_param_b(&param_b(
            outlen as u8,
            key.len() as u8,
            1,
            1,
            0,
            0,
            0,
            0,
            0,
        )));
        let mut block = [0u8; 128];
        block[..key.len()].copy_from_slice(key);
        s.update(&block);
        s
    }

    fn inc(&mut self, n: u64) {
        let (t0, carry) = self.t0.overflowing_add(n);
        self.t0 = t0;
        if carry {
            self.t1 = self.t1.wrapping_add(1);
        }
    }

    /// Best-effort wipe of the chaining value and buffered block.
    fn zeroize(&mut self) {
        super::zeroize::zero_words(&mut self.h);
        super::zeroize::zero_bytes(&mut self.buf);
        self.t0 = 0;
        self.t1 = 0;
        self.buf_len = 0;
    }

    fn update(&mut self, mut data: &[u8]) {
        let fill = 128 - self.buf_len;
        // Only compress a full buffer when more data is known to follow, so the
        // final block is processed with the last-block flag in `finalize`.
        if data.len() > fill {
            self.buf[self.buf_len..].copy_from_slice(&data[..fill]);
            self.inc(128);
            compress_b(&mut self.h, &self.buf, self.t0, self.t1, false);
            self.buf_len = 0;
            data = &data[fill..];
            while data.len() > 128 {
                let block: &[u8; 128] = data[..128].try_into().unwrap();
                self.inc(128);
                compress_b(&mut self.h, block, self.t0, self.t1, false);
                data = &data[128..];
            }
        }
        self.buf[self.buf_len..self.buf_len + data.len()].copy_from_slice(data);
        self.buf_len += data.len();
    }

    fn finalize_into(&mut self, out: &mut [u8]) {
        self.inc(self.buf_len as u64);
        for b in self.buf[self.buf_len..].iter_mut() {
            *b = 0;
        }
        compress_b(&mut self.h, &self.buf, self.t0, self.t1, true);

        let mut bytes = [0u8; 64];
        for (chunk, word) in bytes.chunks_exact_mut(8).zip(self.h.iter()) {
            chunk.copy_from_slice(&word.to_le_bytes());
        }
        out.copy_from_slice(&bytes[..out.len()]);
    }
}

// ---- BLAKE2s (32-bit words, 64-byte blocks, 10 rounds) ----

const IV_S: [u32; 8] = [
    0x6a09_e667,
    0xbb67_ae85,
    0x3c6e_f372,
    0xa54f_f53a,
    0x510e_527f,
    0x9b05_688c,
    0x1f83_d9ab,
    0x5be0_cd19,
];

fn compress_s(h: &mut [u32; 8], block: &[u8; 64], t0: u32, t1: u32, last: bool) {
    let mut m = [0u32; 16];
    for (word, chunk) in m.iter_mut().zip(block.chunks_exact(4)) {
        *word = u32::from_le_bytes(chunk.try_into().unwrap());
    }

    let mut v = [0u32; 16];
    v[..8].copy_from_slice(h);
    v[8..].copy_from_slice(&IV_S);
    v[12] ^= t0;
    v[13] ^= t1;
    if last {
        v[14] ^= !0u32;
    }

    macro_rules! g {
        ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $y:expr) => {
            v[$a] = v[$a].wrapping_add(v[$b]).wrapping_add($x);
            v[$d] = (v[$d] ^ v[$a]).rotate_right(16);
            v[$c] = v[$c].wrapping_add(v[$d]);
            v[$b] = (v[$b] ^ v[$c]).rotate_right(12);
            v[$a] = v[$a].wrapping_add(v[$b]).wrapping_add($y);
            v[$d] = (v[$d] ^ v[$a]).rotate_right(8);
            v[$c] = v[$c].wrapping_add(v[$d]);
            v[$b] = (v[$b] ^ v[$c]).rotate_right(7);
        };
    }

    for s in SIGMA.iter().take(10) {
        g!(0, 4, 8, 12, m[s[0]], m[s[1]]);
        g!(1, 5, 9, 13, m[s[2]], m[s[3]]);
        g!(2, 6, 10, 14, m[s[4]], m[s[5]]);
        g!(3, 7, 11, 15, m[s[6]], m[s[7]]);
        g!(0, 5, 10, 15, m[s[8]], m[s[9]]);
        g!(1, 6, 11, 12, m[s[10]], m[s[11]]);
        g!(2, 7, 8, 13, m[s[12]], m[s[13]]);
        g!(3, 4, 9, 14, m[s[14]], m[s[15]]);
    }

    for i in 0..8 {
        h[i] ^= v[i] ^ v[i + 8];
    }
}

#[derive(Clone)]
struct Blake2sState {
    h: [u32; 8],
    t0: u32,
    t1: u32,
    buf: [u8; 64],
    buf_len: usize,
}

/// Builds a 32-byte BLAKE2s parameter block (`xof_length` is 16-bit here).
#[allow(clippy::too_many_arguments)]
fn param_s(
    digest_length: u8,
    key_length: u8,
    fanout: u8,
    depth: u8,
    leaf_length: u32,
    node_offset: u32,
    xof_length: u16,
    node_depth: u8,
    inner_length: u8,
) -> [u8; 32] {
    let mut p = [0u8; 32];
    p[0] = digest_length;
    p[1] = key_length;
    p[2] = fanout;
    p[3] = depth;
    p[4..8].copy_from_slice(&leaf_length.to_le_bytes());
    p[8..12].copy_from_slice(&node_offset.to_le_bytes());
    p[12..14].copy_from_slice(&xof_length.to_le_bytes());
    p[14] = node_depth;
    p[15] = inner_length;
    p
}

/// Initial state = IV XOR the parameter block, word by word.
fn iv_from_param_s(p: &[u8; 32]) -> [u32; 8] {
    let mut h = IV_S;
    for (i, hw) in h.iter_mut().enumerate() {
        *hw ^= u32::from_le_bytes(p[4 * i..4 * i + 4].try_into().unwrap());
    }
    h
}

impl Blake2sState {
    fn from_h(h: [u32; 8]) -> Self {
        Blake2sState {
            h,
            t0: 0,
            t1: 0,
            buf: [0u8; 64],
            buf_len: 0,
        }
    }

    fn new(outlen: usize) -> Self {
        Self::from_h(iv_from_param_s(&param_s(
            outlen as u8,
            0,
            1,
            1,
            0,
            0,
            0,
            0,
            0,
        )))
    }

    /// Keyed init: the key is processed as a zero-padded first block.
    fn with_key(outlen: usize, key: &[u8]) -> Self {
        let mut s = Self::from_h(iv_from_param_s(&param_s(
            outlen as u8,
            key.len() as u8,
            1,
            1,
            0,
            0,
            0,
            0,
            0,
        )));
        let mut block = [0u8; 64];
        block[..key.len()].copy_from_slice(key);
        s.update(&block);
        s
    }

    fn inc(&mut self, n: u32) {
        let (t0, carry) = self.t0.overflowing_add(n);
        self.t0 = t0;
        if carry {
            self.t1 = self.t1.wrapping_add(1);
        }
    }

    /// Best-effort wipe of the chaining value and buffered block.
    fn zeroize(&mut self) {
        super::zeroize::zero_words(&mut self.h);
        super::zeroize::zero_bytes(&mut self.buf);
        self.t0 = 0;
        self.t1 = 0;
        self.buf_len = 0;
    }

    fn update(&mut self, mut data: &[u8]) {
        let fill = 64 - self.buf_len;
        if data.len() > fill {
            self.buf[self.buf_len..].copy_from_slice(&data[..fill]);
            self.inc(64);
            compress_s(&mut self.h, &self.buf, self.t0, self.t1, false);
            self.buf_len = 0;
            data = &data[fill..];
            while data.len() > 64 {
                let block: &[u8; 64] = data[..64].try_into().unwrap();
                self.inc(64);
                compress_s(&mut self.h, block, self.t0, self.t1, false);
                data = &data[64..];
            }
        }
        self.buf[self.buf_len..self.buf_len + data.len()].copy_from_slice(data);
        self.buf_len += data.len();
    }

    fn finalize_into(&mut self, out: &mut [u8]) {
        self.inc(self.buf_len as u32);
        for b in self.buf[self.buf_len..].iter_mut() {
            *b = 0;
        }
        compress_s(&mut self.h, &self.buf, self.t0, self.t1, true);

        let mut bytes = [0u8; 32];
        for (chunk, word) in bytes.chunks_exact_mut(4).zip(self.h.iter()) {
            chunk.copy_from_slice(&word.to_le_bytes());
        }
        out.copy_from_slice(&bytes[..out.len()]);
    }
}

/// Defines a BLAKE2b variant of the given output length.
macro_rules! blake2b {
    ($name:ident, $func:ident, $out:expr, $doc:literal) => {
        #[doc = $doc]
        #[derive(Clone)]
        pub struct $name {
            state: Blake2bState,
        }
        impl Digest for $name {
            type Output = [u8; $out];
            type Block = [u8; 128];
            const OUTPUT_LEN: usize = $out;
            const BLOCK_LEN: usize = 128;
            #[inline]
            fn new() -> Self {
                $name {
                    state: Blake2bState::new($out),
                }
            }
            #[inline]
            fn zeroed_block() -> [u8; 128] {
                [0u8; 128]
            }
            #[inline]
            fn zeroed_output() -> [u8; $out] {
                [0u8; $out]
            }
            #[inline]
            fn update(&mut self, data: &[u8]) {
                self.state.update(data);
            }
            #[inline]
            fn finalize(mut self) -> [u8; $out] {
                let mut out = [0u8; $out];
                self.state.finalize_into(&mut out);
                out
            }
            #[inline]
            fn zeroize(&mut self) {
                self.state.zeroize();
            }
        }
        #[doc = $doc]
        #[inline]
        pub fn $func(data: &[u8]) -> [u8; $out] {
            $name::digest(data)
        }
    };
}

blake2b!(Blake2b256, blake2b256, 32, "BLAKE2b with a 256-bit digest.");
blake2b!(Blake2b384, blake2b384, 48, "BLAKE2b with a 384-bit digest.");
blake2b!(Blake2b512, blake2b512, 64, "BLAKE2b with a 512-bit digest.");

/// BLAKE2s with a 256-bit digest.
#[derive(Clone)]
pub struct Blake2s256 {
    state: Blake2sState,
}
impl Digest for Blake2s256 {
    type Output = [u8; 32];
    type Block = [u8; 64];
    const OUTPUT_LEN: usize = 32;
    const BLOCK_LEN: usize = 64;
    #[inline]
    fn new() -> Self {
        Blake2s256 {
            state: Blake2sState::new(32),
        }
    }
    #[inline]
    fn zeroed_block() -> [u8; 64] {
        [0u8; 64]
    }
    #[inline]
    fn zeroed_output() -> [u8; 32] {
        [0u8; 32]
    }
    #[inline]
    fn update(&mut self, data: &[u8]) {
        self.state.update(data);
    }
    #[inline]
    fn finalize(mut self) -> [u8; 32] {
        let mut out = [0u8; 32];
        self.state.finalize_into(&mut out);
        out
    }
    #[inline]
    fn zeroize(&mut self) {
        self.state.zeroize();
    }
}

/// BLAKE2s-256 of `data`.
#[inline]
pub fn blake2s256(data: &[u8]) -> [u8; 32] {
    Blake2s256::digest(data)
}

// ---- Keyed BLAKE2 (MAC) ----

/// BLAKE2b in keyed mode — a MAC with a caller-chosen key and output length.
#[derive(Clone)]
pub struct Blake2bMac {
    state: Blake2bState,
    out_len: usize,
}

impl Blake2bMac {
    /// A keyed BLAKE2b producing `out_len` bytes (1..=64); `key` ≤ 64 bytes.
    ///
    /// # Panics
    /// Panics if `out_len` is 0 or > 64, or if `key.len() > 64`. RFC 7693
    /// §2.5 restricts the digest length to that range; an out-of-range
    /// value would otherwise be silently truncated by the BLAKE2 parameter
    /// block and surface later as a buffer overflow in `finalize_into` /
    /// `verify`. The same section caps the key at half a block (64 bytes);
    /// a longer key would wrap the parameter-block length field and panic
    /// out-of-bounds while padding the key block.
    pub fn new(key: &[u8], out_len: usize) -> Self {
        assert!(
            (1..=64).contains(&out_len),
            "Blake2bMac out_len must be in 1..=64 (RFC 7693 §2.5)",
        );
        assert!(
            key.len() <= 64,
            "Blake2bMac key must be <= 64 bytes (RFC 7693 §2.5)",
        );
        Blake2bMac {
            state: Blake2bState::with_key(out_len, key),
            out_len,
        }
    }

    /// Unkeyed BLAKE2b with variable `out_len` (1..=64). Equivalent to
    /// `Blake2b<N>` for N ≤ 64 but with `out_len` chosen at runtime — useful
    /// for variable-length expansion routines like Argon2's `H′`.
    ///
    /// # Panics
    /// Panics if `out_len` is 0 or > 64.
    pub fn new_unkeyed(out_len: usize) -> Self {
        assert!(
            (1..=64).contains(&out_len),
            "Blake2bMac out_len must be in 1..=64 (RFC 7693 §2.5)",
        );
        Blake2bMac {
            state: Blake2bState::new(out_len),
            out_len,
        }
    }
    /// Feeds message bytes.
    pub fn update(&mut self, data: &[u8]) {
        self.state.update(data);
    }
    /// Writes the `out_len`-byte tag into `out` (`out.len()` must equal the
    /// `out_len` given to [`new`](Self::new)).
    pub fn finalize_into(mut self, out: &mut [u8]) {
        debug_assert_eq!(out.len(), self.out_len);
        self.state.finalize_into(out);
    }
    /// Consumes the MAC and checks it against `expected` in constant time.
    pub fn verify(mut self, expected: &[u8]) -> Choice {
        let mut buf = [0u8; 64];
        let n = self.out_len;
        self.state.finalize_into(&mut buf[..n]);
        // Fails closed if `expected.len() != out_len`.
        let eq = buf[..n].ct_eq(expected);
        super::zeroize::zero_bytes(&mut buf);
        eq
    }
}

impl Mac for Blake2bMac {
    fn update(&mut self, data: &[u8]) {
        Blake2bMac::update(self, data);
    }
    fn finalize_into(self, out: &mut [u8]) {
        Blake2bMac::finalize_into(self, out);
    }
    fn verify(self, expected: &[u8]) -> Choice {
        Blake2bMac::verify(self, expected)
    }
}

impl Drop for Blake2bMac {
    fn drop(&mut self) {
        self.state.zeroize();
    }
}

/// BLAKE2s in keyed mode — a MAC with a caller-chosen key and output length.
#[derive(Clone)]
pub struct Blake2sMac {
    state: Blake2sState,
    out_len: usize,
}

impl Blake2sMac {
    /// A keyed BLAKE2s producing `out_len` bytes (1..=32); `key` ≤ 32 bytes.
    ///
    /// # Panics
    /// Panics if `out_len` is 0 or > 32, or if `key.len() > 32`. RFC 7693
    /// §2.5 restricts the digest length to that range and caps the key at
    /// half a block (32 bytes); a longer key would wrap the parameter-block
    /// length field and panic out-of-bounds while padding the key block.
    pub fn new(key: &[u8], out_len: usize) -> Self {
        assert!(
            (1..=32).contains(&out_len),
            "Blake2sMac out_len must be in 1..=32 (RFC 7693 §2.5)",
        );
        assert!(
            key.len() <= 32,
            "Blake2sMac key must be <= 32 bytes (RFC 7693 §2.5)",
        );
        Blake2sMac {
            state: Blake2sState::with_key(out_len, key),
            out_len,
        }
    }
    /// Feeds message bytes.
    pub fn update(&mut self, data: &[u8]) {
        self.state.update(data);
    }
    /// Writes the `out_len`-byte tag into `out`.
    pub fn finalize_into(mut self, out: &mut [u8]) {
        debug_assert_eq!(out.len(), self.out_len);
        self.state.finalize_into(out);
    }
    /// Consumes the MAC and checks it against `expected` in constant time.
    pub fn verify(mut self, expected: &[u8]) -> Choice {
        let mut buf = [0u8; 32];
        let n = self.out_len;
        self.state.finalize_into(&mut buf[..n]);
        // Fails closed if `expected.len() != out_len`.
        let eq = buf[..n].ct_eq(expected);
        super::zeroize::zero_bytes(&mut buf);
        eq
    }
}

impl Mac for Blake2sMac {
    fn update(&mut self, data: &[u8]) {
        Blake2sMac::update(self, data);
    }
    fn finalize_into(self, out: &mut [u8]) {
        Blake2sMac::finalize_into(self, out);
    }
    fn verify(self, expected: &[u8]) -> Choice {
        Blake2sMac::verify(self, expected)
    }
}

impl Drop for Blake2sMac {
    fn drop(&mut self) {
        self.state.zeroize();
    }
}

// ---- BLAKE2X (extendable output) ----

/// BLAKE2Xb: an extendable-output function built on BLAKE2b. The total output
/// length is declared up front (folded into the parameter block).
#[derive(Clone)]
pub struct Blake2xb {
    state: Blake2bState,
    xof_len: u32,
}

/// Output reader for [`Blake2xb`].
#[derive(Clone)]
pub struct Blake2xbReader {
    b0: [u8; 64],
    xof_len: u32,
    pos: u32,
    node: [u8; 64],
    node_idx: u32,
    node_len: usize,
}

impl Blake2xb {
    /// A BLAKE2Xb producing `out_len` output bytes in total.
    pub fn new(out_len: usize) -> Self {
        let xof_len = out_len as u32;
        Blake2xb {
            state: Blake2bState::from_h(iv_from_param_b(&param_b(
                64, 0, 1, 1, 0, 0, xof_len, 0, 0,
            ))),
            xof_len,
        }
    }
    /// Feeds input.
    pub fn update(&mut self, data: &[u8]) {
        self.state.update(data);
    }
    /// Finalizes and returns the output reader.
    pub fn finalize_xof(mut self) -> Blake2xbReader {
        let mut b0 = [0u8; 64];
        self.state.finalize_into(&mut b0);
        Blake2xbReader {
            b0,
            xof_len: self.xof_len,
            pos: 0,
            node: [0u8; 64],
            node_idx: u32::MAX,
            node_len: 0,
        }
    }
    /// Finalizes and squeezes `out.len()` bytes.
    pub fn finalize_into(self, out: &mut [u8]) {
        use super::XofReader;
        self.finalize_xof().read(out);
    }
}

impl super::XofReader for Blake2xbReader {
    fn read(&mut self, out: &mut [u8]) {
        let mut i = 0;
        while i < out.len() {
            let idx = self.pos / 64;
            let within = (self.pos % 64) as usize;
            if self.node_idx != idx {
                let block_len = (self.xof_len - idx * 64).min(64) as usize;
                let h = iv_from_param_b(&param_b(
                    block_len as u8,
                    0,
                    0,
                    0,
                    64,
                    idx,
                    self.xof_len,
                    0,
                    64,
                ));
                let mut st = Blake2bState::from_h(h);
                st.update(&self.b0);
                self.node = [0u8; 64];
                st.finalize_into(&mut self.node[..block_len]);
                self.node_len = block_len;
                self.node_idx = idx;
            }
            let take = (out.len() - i).min(self.node_len - within);
            out[i..i + take].copy_from_slice(&self.node[within..within + take]);
            i += take;
            self.pos += take as u32;
        }
    }
}

/// BLAKE2Xs: an extendable-output function built on BLAKE2s.
#[derive(Clone)]
pub struct Blake2xs {
    state: Blake2sState,
    xof_len: u16,
}

/// Output reader for [`Blake2xs`].
#[derive(Clone)]
pub struct Blake2xsReader {
    b0: [u8; 32],
    xof_len: u16,
    pos: u32,
    node: [u8; 32],
    node_idx: u32,
    node_len: usize,
}

impl Blake2xs {
    /// A BLAKE2Xs producing `out_len` output bytes in total (≤ 65535).
    pub fn new(out_len: usize) -> Self {
        let xof_len = out_len as u16;
        Blake2xs {
            state: Blake2sState::from_h(iv_from_param_s(&param_s(
                32, 0, 1, 1, 0, 0, xof_len, 0, 0,
            ))),
            xof_len,
        }
    }
    /// Feeds input.
    pub fn update(&mut self, data: &[u8]) {
        self.state.update(data);
    }
    /// Finalizes and returns the output reader.
    pub fn finalize_xof(mut self) -> Blake2xsReader {
        let mut b0 = [0u8; 32];
        self.state.finalize_into(&mut b0);
        Blake2xsReader {
            b0,
            xof_len: self.xof_len,
            pos: 0,
            node: [0u8; 32],
            node_idx: u32::MAX,
            node_len: 0,
        }
    }
    /// Finalizes and squeezes `out.len()` bytes.
    pub fn finalize_into(self, out: &mut [u8]) {
        use super::XofReader;
        self.finalize_xof().read(out);
    }
}

impl super::XofReader for Blake2xsReader {
    fn read(&mut self, out: &mut [u8]) {
        let mut i = 0;
        while i < out.len() {
            let idx = self.pos / 32;
            let within = (self.pos % 32) as usize;
            if self.node_idx != idx {
                let block_len = (self.xof_len as u32 - idx * 32).min(32) as usize;
                let h = iv_from_param_s(&param_s(
                    block_len as u8,
                    0,
                    0,
                    0,
                    32,
                    idx,
                    self.xof_len,
                    0,
                    32,
                ));
                let mut st = Blake2sState::from_h(h);
                st.update(&self.b0);
                self.node = [0u8; 32];
                st.finalize_into(&mut self.node[..block_len]);
                self.node_len = block_len;
                self.node_idx = idx;
            }
            let take = (out.len() - i).min(self.node_len - within);
            out[i..i + take].copy_from_slice(&self.node[within..within + take]);
            i += take;
            self.pos += take as u32;
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_util::from_hex;

    // RFC 7693 Appendix A/E reference vectors for "abc".
    #[test]
    fn rfc7693_abc() {
        assert_eq!(
            blake2b512(b"abc"),
            from_hex::<64>(
                "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"
            )
        );
        assert_eq!(
            blake2s256(b"abc"),
            from_hex::<32>("508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982")
        );
    }

    // Vectors generated with `b2sum -l <bits>` / `openssl dgst`.
    #[test]
    fn empty_and_lengths() {
        assert_eq!(
            blake2b512(b""),
            from_hex::<64>(
                "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"
            )
        );
        assert_eq!(
            blake2s256(b""),
            from_hex::<32>("69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9")
        );
        assert_eq!(
            blake2b256(b"abc"),
            from_hex::<32>("bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319")
        );
        assert_eq!(
            blake2b384(b"abc"),
            from_hex::<48>(
                "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4"
            )
        );
    }

    #[test]
    fn streaming_matches_oneshot() {
        let msg = [0xa5u8; 300];
        let oneshot = blake2b512(&msg);
        let mut h = Blake2b512::new();
        h.update(&msg[..1]);
        h.update(&msg[1..128]); // exactly fills a block at byte 129 boundary
        h.update(&msg[128..256]);
        h.update(&msg[256..]);
        assert_eq!(h.finalize(), oneshot);

        let oneshot_s = blake2s256(&msg);
        let mut hs = Blake2s256::new();
        for c in msg.chunks(7) {
            hs.update(c);
        }
        assert_eq!(hs.finalize(), oneshot_s);
    }

    // Official BLAKE2 keyed KAT (key = 00..3f / 00..1f, empty message).
    #[test]
    fn keyed_macs() {
        let key64: [u8; 64] = core::array::from_fn(|i| i as u8);
        let key32: [u8; 32] = core::array::from_fn(|i| i as u8);

        let mut out = [0u8; 64];
        Blake2bMac::new(&key64, 64).finalize_into(&mut out);
        assert_eq!(
            out,
            from_hex::<64>(
                "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568"
            )
        );

        let mut out = [0u8; 32];
        Blake2sMac::new(&key32, 32).finalize_into(&mut out);
        assert_eq!(
            out,
            from_hex::<32>("48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49")
        );
    }

    #[test]
    #[should_panic(expected = "Blake2bMac key must be <= 64 bytes")]
    fn blake2b_mac_rejects_overlong_key() {
        // A 65-byte key exceeds the RFC 7693 §2.5 cap. Without the length
        // check this would wrap the parameter-block length field and panic
        // out-of-bounds while padding the key block; the assert rejects it
        // cleanly up front instead.
        let key = [0u8; 65];
        let _ = Blake2bMac::new(&key, 32);
    }

    #[test]
    #[should_panic(expected = "Blake2sMac key must be <= 32 bytes")]
    fn blake2s_mac_rejects_overlong_key() {
        let key = [0u8; 33];
        let _ = Blake2sMac::new(&key, 32);
    }

    #[test]
    fn keyed_mac_verify_constant_time() {
        let key = b"a secret key";
        let msg = b"authenticate me";

        let mut tag = [0u8; 32];
        let mut m = Blake2bMac::new(key, 32);
        m.update(msg);
        m.finalize_into(&mut tag);

        let mut m = Blake2bMac::new(key, 32);
        m.update(msg);
        assert!(bool::from(m.verify(&tag)));

        let mut bad = tag;
        bad[7] ^= 0x80;
        let mut m = Blake2bMac::new(key, 32);
        m.update(msg);
        assert!(!bool::from(m.verify(&bad)));

        // Wrong length fails closed.
        let mut m = Blake2bMac::new(key, 32);
        m.update(msg);
        assert!(!bool::from(m.verify(&tag[..16])));

        // BLAKE2s MAC round-trip.
        let mut tag = [0u8; 16];
        let mut m = Blake2sMac::new(key, 16);
        m.update(msg);
        m.finalize_into(&mut tag);
        let mut m = Blake2sMac::new(key, 16);
        m.update(msg);
        assert!(bool::from(m.verify(&tag)));
    }

    // Official BLAKE2X KAT: input = 00..ff (256 bytes), 256-byte output
    // (spans several output nodes). Vectors from BLAKE2/testvectors.
    #[test]
    fn blake2x_vectors() {
        let input: [u8; 256] = core::array::from_fn(|i| i as u8);

        let mut out = [0u8; 256];
        let mut xb = Blake2xb::new(256);
        xb.update(&input);
        xb.finalize_into(&mut out);
        assert_eq!(
            out,
            from_hex::<256>(
                "59f8eea01a07a2670f2fe464bd755d8cde620cb4bac6006556a8663d2d9625c62fe63b6b68adba279ab287c04d3de6c4c17e6428dff30e9b2524fea1e869e42485c03a9f48af40d12d5cba0d13abac272ee36efeb8bd098ce0e1da8233ef6e6b3e96c9e05a7fedb79ae44e698640e6b8f26c43674e2c32ef17b4d7b005554ec4fd8aa1dac0f975fc888bec5bd7a06fbf29ae09f2d37c5eb7d0f67c9c77d5caf7afe681ae336fb3fccd97ecdec0348cdea4787a4e9de4df4bbfb209eeb642ce8f92730d598a71c94259e648d0a4dd89079a06c4b463ba1d175476337d553b0401d2b6f0c32639e3edcdd8c225c61e0afa5cd103b5d26a56afe3ac9462df794dc0"
            )
        );

        let mut out = [0u8; 256];
        let mut xs = Blake2xs::new(256);
        xs.update(&input);
        xs.finalize_into(&mut out);
        assert_eq!(
            out,
            from_hex::<256>(
                "d4a23a17b657fa3ddc2df61eefce362f048b9dd156809062997ab9d5b1fb26b8542b1a638f517fcbad72a6fb23de0754db7bb488b75c12ac826dcced9806d7873e6b31922097ef7b42506275ccc54caf86918f9d1c6cdb9bad2bacf123c0380b2e5dc3e98de83a159ee9e10a8444832c371e5b72039b31c38621261aa04d8271598b17dba0d28c20d1858d879038485ab069bdb58733b5495f934889658ae81b7536bcf601cfcc572060863c1ff2202d2ea84c800482dbe777335002204b7c1f70133e4d8a6b7516c66bb433ad31030a7a9a9a6b9ea69890aa40662d908a5acfe8328802595f0284c51a000ce274a985823de9ee74250063a879a3787fca23a6"
            )
        );
    }
}
