//! Stateful streaming MAC context (init / update / sign / verify).
//!
//! Uniform `init / update / sign | verify` API across HMAC, CMAC,
//! KMAC and GMAC. Designed in the same spirit as
//! [`crate::cipher::ctx::Cipher`]: caller-provided output buffers,
//! reusable across many messages by re-calling [`Mac::init`],
//! constant-time tag comparison on `verify`, and Poly1305 left out
//! on purpose because it is a one-time MAC and would be unsafe to
//! expose behind a "init then reuse" object.
//!
//! # Cycle of life
//!
//! ```text
//!   new(algo)
//!         │
//!         ▼
//!   init(key)               (HMAC, CMAC, KMAC default)
//!   init_kmac(key, custom)  (KMAC with customization string)
//!   init_with_nonce(key, n) (GMAC)
//!         │
//!         ▼
//!   update(data) ──┐
//!         │   (0..N times)
//!         ▼
//!   sign(out)  or  verify(expected_tag)
//! ```
//!
//! # Example: HMAC-SHA-256
//!
//! ```rust,ignore
//! use arcana::mac::ctx::{Mac, Algorithm};
//!
//! let mut m = Mac::new(Algorithm::HmacSha256);
//! m.init(b"secret-key").unwrap();
//! m.update(b"hello, ").unwrap();
//! m.update(b"world!").unwrap();
//!
//! let mut tag = [0u8; 32];
//! let n = m.sign(&mut tag).unwrap();
//! assert_eq!(n, 32);
//! ```
//!
//! # Side-channel posture
//!
//! Per `arcana/doc/sca/countermeasures/hmac.rst`:
//!
//! | Family       | Threat                                                | Status     | Roadmap item                                   |
//! |--------------|-------------------------------------------------------|------------|------------------------------------------------|
//! | All families | Timing on tag verify                                  | implemented| `silentops::ct_eq` — constant-time tag compare |
//! | HMAC-SHA-2   | **Carry-based DPA** (Belenky TCHES 2023/3)            | vulnerable | `T2-D` — first-order Boolean masking of SHA-2  |
//! | HMAC-SHA-3   | DPA on Keccak (no addition → no CDPA)                 | low risk   | none scheduled (Keccak has no carry chain)     |
//! | CMAC         | Inherits AES leak (cf. [`crate::cipher::aes`])        | vulnerable | `T1-A` (AES) → `T2-G` (masked AES); CMAC inherits |
//! | KMAC         | DPA on Keccak                                         | low risk   | none scheduled                                 |
//! | GMAC         | DPA on `GF(2^128)` GHASH multiplier                   | vulnerable | `T2-H` — CT carry-less multiplier              |
//!
//! ## ⚠ HMAC-SHA-2: Belenky et al. TCHES 2023/3 (CDPA)
//!
//! `belenky2023_cdpa_hmac_sha2` proves that **any** implementation
//! of HMAC-SHA-2, even pure parallel hardware, leaks the secret
//! key in 30 K – 275 K traces under Carry-based Differential
//! Power Analysis. Software implementations leak even more
//! easily because the SHA-2 additions are explicit instructions
//! on a sequential pipeline.
//!
//! For deployments where the threat model includes a level-2
//! attacker with EM / power probes (which is the baseline of any
//! lab-class evaluation), **HMAC-SHA-2 keys in arcana must be
//! assumed extractable until `T2-D` ships**.
//!
//! `T2-D` will land a `MaskedSha256` / `MaskedSha512` behind the
//! `sca-protected` Cargo feature (mirroring quantica's masking
//! convention). The masked variant is mathematically transparent
//! (bit-identical output) and routes through this `Mac` ctx
//! transparently when the feature is on.

use crate::cipher::aes::Aes;
use crate::cipher::des::TripleDes;
use crate::cipher::modes::{gf128_mul, ghash_update};
use crate::hash::ripemd160::Ripemd160;
use crate::hash::sha1::Sha1;
use crate::hash::sha3::{KeccakState, Sha3_256, Sha3_384, Sha3_512};
use crate::hash::sha256::Sha256;
use crate::hash::sha384::Sha384;
use crate::hash::sha512::Sha512;
use crate::{BlockCipher, Hasher};

// ============================================================
// Public enums
// ============================================================

/// MAC algorithm selector.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Algorithm {
    /// HMAC-SHA-1 (RFC 2104, FIPS 198-1). 20-byte tag. Legacy.
    HmacSha1,
    /// HMAC-SHA-256. 32-byte tag.
    HmacSha256,
    /// HMAC-SHA-384. 48-byte tag.
    HmacSha384,
    /// HMAC-SHA-512. 64-byte tag.
    HmacSha512,
    /// HMAC-SHA3-256. 32-byte tag.
    HmacSha3_256,
    /// HMAC-SHA3-384. 48-byte tag.
    HmacSha3_384,
    /// HMAC-SHA3-512. 64-byte tag.
    HmacSha3_512,
    /// HMAC-RIPEMD-160. 20-byte tag. Legacy.
    HmacRipemd160,

    /// CMAC-AES-128 (NIST SP 800-38B, RFC 4493). 16-byte tag, 16-byte key.
    CmacAes128,
    /// CMAC-AES-192. 16-byte tag, 24-byte key.
    CmacAes192,
    /// CMAC-AES-256. 16-byte tag, 32-byte key.
    CmacAes256,
    /// CMAC-TripleDES (NIST SP 800-38B). 8-byte tag, 24-byte key. Legacy.
    CmacTripleDes,

    /// KMAC128 (FIPS SP 800-185). Default tag = 32 bytes (256 bits).
    Kmac128,
    /// KMAC256 (FIPS SP 800-185). Default tag = 64 bytes (512 bits).
    Kmac256,

    /// GMAC-AES-128 (NIST SP 800-38D, GCM with empty plaintext).
    /// 16-byte tag. Requires a 12-byte unique nonce per message.
    GmacAes128,
    /// GMAC-AES-192. 16-byte tag. Requires a 12-byte unique nonce per message.
    GmacAes192,
    /// GMAC-AES-256. 16-byte tag. Requires a 12-byte unique nonce per message.
    GmacAes256,
}

/// Errors returned by [`Mac`].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
    /// CMAC / GMAC: the supplied key length does not match the algorithm.
    InvalidKeyLen,
    /// GMAC: the supplied nonce length is not 12 bytes.
    InvalidNonceLen,
    /// `update`, `sign` or `verify` was called before [`Mac::init`].
    NotInitialized,
    /// `update`, `sign` or `verify` was called after a previous
    /// `sign` / `verify` without re-initializing.
    AlreadyFinalized,
    /// `out` slice passed to `sign` is shorter than the algorithm's
    /// tag length.
    OutputBufferTooSmall {
        /// Required output length in bytes.
        needed: usize,
    },
    /// Wrong init variant for the selected algorithm
    /// (e.g. `init_with_nonce` on HMAC, or `init` on GMAC).
    WrongInitVariant,
    /// `verify` failed: tag mismatch.
    VerificationFailed,
}

// ============================================================
// Internal per-algo state
// ============================================================

enum HmacState {
    Sha1(Sha1),
    Sha256(Sha256),
    Sha384(Sha384),
    Sha512(Sha512),
    Sha3_256(Sha3_256),
    Sha3_384(Sha3_384),
    Sha3_512(Sha3_512),
    Ripemd160(Ripemd160),
}

impl HmacState {
    fn block_len(&self) -> usize {
        match self {
            HmacState::Sha1(_) => Sha1::BLOCK_LEN,
            HmacState::Sha256(_) => Sha256::BLOCK_LEN,
            HmacState::Sha384(_) => Sha384::BLOCK_LEN,
            HmacState::Sha512(_) => Sha512::BLOCK_LEN,
            HmacState::Sha3_256(_) => Sha3_256::BLOCK_LEN,
            HmacState::Sha3_384(_) => Sha3_384::BLOCK_LEN,
            HmacState::Sha3_512(_) => Sha3_512::BLOCK_LEN,
            HmacState::Ripemd160(_) => Ripemd160::BLOCK_LEN,
        }
    }

    fn output_len(&self) -> usize {
        match self {
            HmacState::Sha1(_) => Sha1::OUTPUT_LEN,
            HmacState::Sha256(_) => Sha256::OUTPUT_LEN,
            HmacState::Sha384(_) => Sha384::OUTPUT_LEN,
            HmacState::Sha512(_) => Sha512::OUTPUT_LEN,
            HmacState::Sha3_256(_) => Sha3_256::OUTPUT_LEN,
            HmacState::Sha3_384(_) => Sha3_384::OUTPUT_LEN,
            HmacState::Sha3_512(_) => Sha3_512::OUTPUT_LEN,
            HmacState::Ripemd160(_) => Ripemd160::OUTPUT_LEN,
        }
    }

    fn update(&mut self, data: &[u8]) {
        match self {
            HmacState::Sha1(h) => h.update(data),
            HmacState::Sha256(h) => h.update(data),
            HmacState::Sha384(h) => h.update(data),
            HmacState::Sha512(h) => h.update(data),
            HmacState::Sha3_256(h) => h.update(data),
            HmacState::Sha3_384(h) => h.update(data),
            HmacState::Sha3_512(h) => h.update(data),
            HmacState::Ripemd160(h) => h.update(data),
        }
    }

    /// Hash a key down to its native digest length (used by HMAC when
    /// the user-supplied key is longer than the block size).
    fn hash_key(&self, key: &[u8]) -> Vec<u8> {
        match self {
            HmacState::Sha1(_) => Sha1::hash(key),
            HmacState::Sha256(_) => Sha256::hash(key),
            HmacState::Sha384(_) => Sha384::hash(key),
            HmacState::Sha512(_) => Sha512::hash(key),
            HmacState::Sha3_256(_) => Sha3_256::hash(key),
            HmacState::Sha3_384(_) => Sha3_384::hash(key),
            HmacState::Sha3_512(_) => Sha3_512::hash(key),
            HmacState::Ripemd160(_) => Ripemd160::hash(key),
        }
    }

    /// Finalize the inner hash and return its digest as a Vec.
    fn finalize_inner(self) -> Vec<u8> {
        match self {
            HmacState::Sha1(h) => h.finalize(),
            HmacState::Sha256(h) => h.finalize(),
            HmacState::Sha384(h) => h.finalize(),
            HmacState::Sha512(h) => h.finalize(),
            HmacState::Sha3_256(h) => h.finalize(),
            HmacState::Sha3_384(h) => h.finalize(),
            HmacState::Sha3_512(h) => h.finalize(),
            HmacState::Ripemd160(h) => h.finalize(),
        }
    }

    /// One-shot inner hash of `(opad-key || inner_digest)` to produce
    /// the final HMAC tag, returned as a Vec.
    fn outer_hash(&self, opad_key: &[u8], inner_digest: &[u8]) -> Vec<u8> {
        match self {
            HmacState::Sha1(_) => {
                let mut h = Sha1::new();
                h.update(opad_key);
                h.update(inner_digest);
                h.finalize()
            }
            HmacState::Sha256(_) => {
                let mut h = Sha256::new();
                h.update(opad_key);
                h.update(inner_digest);
                h.finalize()
            }
            HmacState::Sha384(_) => {
                let mut h = Sha384::new();
                h.update(opad_key);
                h.update(inner_digest);
                h.finalize()
            }
            HmacState::Sha512(_) => {
                let mut h = Sha512::new();
                h.update(opad_key);
                h.update(inner_digest);
                h.finalize()
            }
            HmacState::Sha3_256(_) => {
                let mut h = Sha3_256::new();
                h.update(opad_key);
                h.update(inner_digest);
                h.finalize()
            }
            HmacState::Sha3_384(_) => {
                let mut h = Sha3_384::new();
                h.update(opad_key);
                h.update(inner_digest);
                h.finalize()
            }
            HmacState::Sha3_512(_) => {
                let mut h = Sha3_512::new();
                h.update(opad_key);
                h.update(inner_digest);
                h.finalize()
            }
            HmacState::Ripemd160(_) => {
                let mut h = Ripemd160::new();
                h.update(opad_key);
                h.update(inner_digest);
                h.finalize()
            }
        }
    }
}

/// Per-algorithm internal state.
enum Inner {
    None,
    Hmac {
        /// Inner hash being fed (already absorbed `key XOR ipad`).
        inner: HmacState,
        /// Saved `key XOR opad` block, used at finalize time.
        opad_key: Vec<u8>,
    },
    Cmac {
        /// AES key schedule (None for 3DES).
        aes: Option<Aes>,
        /// 3DES key schedule (None for AES).
        tdes: Option<TripleDes>,
        block_len: usize,
        /// Subkey K1 (CMAC).
        k1: [u8; 16],
        /// Subkey K2 (CMAC).
        k2: [u8; 16],
        /// Running CBC chaining state.
        x: [u8; 16],
        /// Look-ahead buffer of one block (we always keep the last
        /// pending block back so we can XOR K1/K2 at finalize).
        buf: [u8; 16],
        buf_len: usize,
    },
    Kmac {
        /// Underlying cSHAKE-flavoured Keccak sponge already absorbed
        /// with the prefix and the bytepad'd key.
        sponge: KeccakState,
        /// Default output length in bytes (32 for KMAC128, 64 for KMAC256).
        out_len: usize,
    },
    Gmac {
        /// AES key schedule.
        aes: Aes,
        /// Hash subkey H = E(K, 0^128).
        h: [u8; 16],
        /// J0 = nonce || 0x00000001 (the precomputed E(K, J0) is the
        /// final XOR mask used at finalize time).
        e_j0: [u8; 16],
        /// Running GHASH accumulator.
        y: [u8; 16],
        /// Reliquat for partial input blocks.
        buf: [u8; 16],
        buf_len: usize,
        /// Total bytes absorbed (used to encode len(C) in the
        /// trailing GHASH length block).
        total_len: u64,
    },
}

// ============================================================
// MAC object
// ============================================================

/// Stateful MAC context.
///
/// See the [module-level documentation](self) for the cycle of life
/// and the buffer ownership model.
pub struct Mac {
    algo: Algorithm,
    inner: Inner,
    initialized: bool,
    finalized: bool,
}

impl Mac {
    /// Create a new MAC context for the given algorithm. The actual
    /// key (and nonce / customization string for GMAC / KMAC) is
    /// loaded later by [`Self::init`], [`Self::init_kmac`] or
    /// [`Self::init_with_nonce`].
    pub fn new(algo: Algorithm) -> Self {
        Self {
            algo,
            inner: Inner::None,
            initialized: false,
            finalized: false,
        }
    }

    /// Initialize the MAC with a key. This is the right entry point
    /// for HMAC, CMAC and KMAC (with an empty customization string).
    /// For GMAC, use [`Self::init_with_nonce`] instead.
    ///
    /// HMAC accepts arbitrary key lengths per RFC 2104 (the key is
    /// hashed if it exceeds the hash block size and zero-padded
    /// otherwise). CMAC requires the exact key length for the chosen
    /// cipher. KMAC accepts arbitrary key lengths.
    ///
    /// # Errors
    ///
    /// - [`Error::InvalidKeyLen`] for CMAC if the length is wrong.
    /// - [`Error::WrongInitVariant`] if called on GMAC.
    pub fn init(&mut self, key: &[u8]) -> Result<(), Error> {
        match self.algo {
            Algorithm::GmacAes128 | Algorithm::GmacAes192 | Algorithm::GmacAes256 => Err(Error::WrongInitVariant),
            Algorithm::Kmac128 | Algorithm::Kmac256 => self.init_kmac(key, &[]),
            _ => self.init_internal(key, None, None),
        }
    }

    /// Initialize KMAC with a key and a customization string.
    ///
    /// # Errors
    ///
    /// [`Error::WrongInitVariant`] if called on a non-KMAC algorithm.
    pub fn init_kmac(&mut self, key: &[u8], custom: &[u8]) -> Result<(), Error> {
        match self.algo {
            Algorithm::Kmac128 | Algorithm::Kmac256 => self.init_internal(key, None, Some(custom)),
            _ => Err(Error::WrongInitVariant),
        }
    }

    /// Initialize GMAC with a key and a 12-byte nonce. The nonce
    /// **must** be unique per message under a given key (reusing it
    /// breaks GMAC catastrophically).
    ///
    /// # Errors
    ///
    /// - [`Error::WrongInitVariant`] if called on a non-GMAC algorithm.
    /// - [`Error::InvalidKeyLen`] if the key length does not match.
    /// - [`Error::InvalidNonceLen`] if the nonce is not 12 bytes.
    pub fn init_with_nonce(&mut self, key: &[u8], nonce: &[u8]) -> Result<(), Error> {
        match self.algo {
            Algorithm::GmacAes128 | Algorithm::GmacAes192 | Algorithm::GmacAes256 => {
                if nonce.len() != 12 {
                    return Err(Error::InvalidNonceLen);
                }
                self.init_internal(key, Some(nonce), None)
            }
            _ => Err(Error::WrongInitVariant),
        }
    }

    fn init_internal(&mut self, key: &[u8], nonce: Option<&[u8]>, custom: Option<&[u8]>) -> Result<(), Error> {
        self.inner = match self.algo {
            // ---- HMAC ----
            Algorithm::HmacSha1
            | Algorithm::HmacSha256
            | Algorithm::HmacSha384
            | Algorithm::HmacSha512
            | Algorithm::HmacSha3_256
            | Algorithm::HmacSha3_384
            | Algorithm::HmacSha3_512
            | Algorithm::HmacRipemd160 => {
                let mut state = match self.algo {
                    Algorithm::HmacSha1 => HmacState::Sha1(Sha1::new()),
                    Algorithm::HmacSha256 => HmacState::Sha256(Sha256::new()),
                    Algorithm::HmacSha384 => HmacState::Sha384(Sha384::new()),
                    Algorithm::HmacSha512 => HmacState::Sha512(Sha512::new()),
                    Algorithm::HmacSha3_256 => HmacState::Sha3_256(Sha3_256::new()),
                    Algorithm::HmacSha3_384 => HmacState::Sha3_384(Sha3_384::new()),
                    Algorithm::HmacSha3_512 => HmacState::Sha3_512(Sha3_512::new()),
                    Algorithm::HmacRipemd160 => HmacState::Ripemd160(Ripemd160::new()),
                    _ => unreachable!(),
                };
                let block_len = state.block_len();

                // RFC 2104: if key is longer than block_len, hash it down.
                let mut key_block = vec![0u8; block_len];
                if key.len() > block_len {
                    let hashed = state.hash_key(key);
                    key_block[..hashed.len()].copy_from_slice(&hashed);
                } else {
                    key_block[..key.len()].copy_from_slice(key);
                }

                // ipad / opad keys.
                let mut ipad_key = key_block.clone();
                let mut opad_key = key_block;
                for b in &mut ipad_key {
                    *b ^= 0x36;
                }
                for b in &mut opad_key {
                    *b ^= 0x5C;
                }

                state.update(&ipad_key);
                Inner::Hmac { inner: state, opad_key }
            }

            // ---- CMAC ----
            Algorithm::CmacAes128 | Algorithm::CmacAes192 | Algorithm::CmacAes256 | Algorithm::CmacTripleDes => {
                let expected_key_len = match self.algo {
                    Algorithm::CmacAes128 => 16,
                    Algorithm::CmacAes192 => 24,
                    Algorithm::CmacAes256 => 32,
                    Algorithm::CmacTripleDes => 24,
                    _ => unreachable!(),
                };
                if key.len() != expected_key_len {
                    return Err(Error::InvalidKeyLen);
                }

                let (aes, tdes, block_len) = match self.algo {
                    Algorithm::CmacAes128 | Algorithm::CmacAes192 | Algorithm::CmacAes256 => {
                        (Some(Aes::new(key)), None, 16usize)
                    }
                    Algorithm::CmacTripleDes => (None, Some(TripleDes::new(key)), 8usize),
                    _ => unreachable!(),
                };

                // Compute K1, K2 per RFC 4493 / NIST SP 800-38B.
                let mut l = [0u8; 16];
                if let Some(ref c) = aes {
                    c.encrypt_block(&mut l[..16]);
                } else if let Some(ref c) = tdes {
                    c.encrypt_block(&mut l[..8]);
                }
                let k1 = cmac_double(&l[..block_len]);
                let k2 = cmac_double(&k1[..block_len]);

                let mut k1_arr = [0u8; 16];
                let mut k2_arr = [0u8; 16];
                k1_arr[..block_len].copy_from_slice(&k1[..block_len]);
                k2_arr[..block_len].copy_from_slice(&k2[..block_len]);

                Inner::Cmac {
                    aes,
                    tdes,
                    block_len,
                    k1: k1_arr,
                    k2: k2_arr,
                    x: [0u8; 16],
                    buf: [0u8; 16],
                    buf_len: 0,
                }
            }

            // ---- KMAC ----
            Algorithm::Kmac128 | Algorithm::Kmac256 => {
                let (rate, out_len) = match self.algo {
                    Algorithm::Kmac128 => (168usize, 32usize),
                    Algorithm::Kmac256 => (136usize, 64usize),
                    _ => unreachable!(),
                };
                // cSHAKE suffix is 0x04 (KMAC uses cSHAKE under the hood).
                let mut sponge = KeccakState::new(rate, 0x04);

                // cSHAKE prefix: bytepad(encode_string("KMAC") || encode_string(custom), rate).
                let custom = custom.unwrap_or(&[]);
                let mut prefix = Vec::new();
                append_encode_string(&mut prefix, b"KMAC");
                append_encode_string(&mut prefix, custom);
                bytepad_to(&mut prefix, rate);
                sponge.absorb(&prefix);

                // KMAC key encoding: bytepad(encode_string(K), rate).
                let mut keyenc = Vec::new();
                append_encode_string(&mut keyenc, key);
                bytepad_to(&mut keyenc, rate);
                sponge.absorb(&keyenc);

                Inner::Kmac { sponge, out_len }
            }

            // ---- GMAC ----
            Algorithm::GmacAes128 | Algorithm::GmacAes192 | Algorithm::GmacAes256 => {
                let expected_key_len = match self.algo {
                    Algorithm::GmacAes128 => 16,
                    Algorithm::GmacAes192 => 24,
                    Algorithm::GmacAes256 => 32,
                    _ => unreachable!(),
                };
                if key.len() != expected_key_len {
                    return Err(Error::InvalidKeyLen);
                }
                let nonce = nonce.expect("init_with_nonce path");
                debug_assert_eq!(nonce.len(), 12);

                let aes = Aes::new(key);

                // H = E(K, 0^128)
                let mut h = [0u8; 16];
                aes.encrypt_block(&mut h);

                // J0 = nonce || 0x00000001
                let mut j0 = [0u8; 16];
                j0[..12].copy_from_slice(nonce);
                j0[15] = 1;
                let mut e_j0 = j0;
                aes.encrypt_block(&mut e_j0);

                Inner::Gmac {
                    aes,
                    h,
                    e_j0,
                    y: [0u8; 16],
                    buf: [0u8; 16],
                    buf_len: 0,
                    total_len: 0,
                }
            }
        };
        self.initialized = true;
        self.finalized = false;
        Ok(())
    }

    /// Tag length in bytes for this MAC algorithm.
    pub const fn tag_len(&self) -> usize {
        match self.algo {
            Algorithm::HmacSha1 | Algorithm::HmacRipemd160 => 20,
            Algorithm::HmacSha256 | Algorithm::HmacSha3_256 => 32,
            Algorithm::HmacSha384 | Algorithm::HmacSha3_384 => 48,
            Algorithm::HmacSha512 | Algorithm::HmacSha3_512 => 64,
            Algorithm::CmacAes128 | Algorithm::CmacAes192 | Algorithm::CmacAes256 => 16,
            Algorithm::CmacTripleDes => 8,
            Algorithm::Kmac128 => 32,
            Algorithm::Kmac256 => 64,
            Algorithm::GmacAes128 | Algorithm::GmacAes192 | Algorithm::GmacAes256 => 16,
        }
    }

    /// Feed `data` into the MAC. May be called any number of times
    /// between `init` and `sign` / `verify`. Empty slices are allowed.
    pub fn update(&mut self, data: &[u8]) -> Result<(), Error> {
        if !self.initialized {
            return Err(Error::NotInitialized);
        }
        if self.finalized {
            return Err(Error::AlreadyFinalized);
        }
        match &mut self.inner {
            Inner::None => unreachable!(),
            Inner::Hmac { inner, .. } => {
                inner.update(data);
            }
            Inner::Cmac {
                aes,
                tdes,
                block_len,
                buf,
                buf_len,
                x,
                ..
            } => {
                let bs = *block_len;
                let mut pos = 0;
                while pos < data.len() {
                    // Fill the look-ahead buffer first.
                    let space = bs - *buf_len;
                    let take = space.min(data.len() - pos);
                    buf[*buf_len..*buf_len + take].copy_from_slice(&data[pos..pos + take]);
                    *buf_len += take;
                    pos += take;

                    // Drain a full block ONLY if more data follows
                    // (we must keep the last block for finalize).
                    if *buf_len == bs && pos < data.len() {
                        for i in 0..bs {
                            x[i] ^= buf[i];
                        }
                        if let Some(c) = aes.as_ref() {
                            c.encrypt_block(&mut x[..16]);
                        } else if let Some(c) = tdes.as_ref() {
                            c.encrypt_block(&mut x[..8]);
                        }
                        *buf_len = 0;
                    }
                }
            }
            Inner::Kmac { sponge, .. } => {
                sponge.absorb(data);
            }
            Inner::Gmac {
                h,
                y,
                buf,
                buf_len,
                total_len,
                ..
            } => {
                *total_len += data.len() as u64;
                let mut pos = 0;
                while pos < data.len() {
                    let space = 16 - *buf_len;
                    let take = space.min(data.len() - pos);
                    buf[*buf_len..*buf_len + take].copy_from_slice(&data[pos..pos + take]);
                    *buf_len += take;
                    pos += take;
                    if *buf_len == 16 {
                        for i in 0..16 {
                            y[i] ^= buf[i];
                        }
                        *y = gf128_mul(y, h);
                        *buf_len = 0;
                    }
                }
            }
        }
        Ok(())
    }

    /// Finalize the MAC and write the tag into `out`. Returns the
    /// number of bytes written, which is always [`Self::tag_len`].
    /// After this call the context is in the finalized state and
    /// must be re-initialized before further use.
    ///
    /// # Errors
    ///
    /// - [`Error::NotInitialized`] / [`Error::AlreadyFinalized`].
    /// - [`Error::OutputBufferTooSmall`] if `out.len() < tag_len()`.
    pub fn sign(&mut self, out: &mut [u8]) -> Result<usize, Error> {
        if !self.initialized {
            return Err(Error::NotInitialized);
        }
        if self.finalized {
            return Err(Error::AlreadyFinalized);
        }
        let tlen = self.tag_len();
        if out.len() < tlen {
            return Err(Error::OutputBufferTooSmall { needed: tlen });
        }

        // Replace inner with `None` so we can consume the state.
        let inner = core::mem::replace(&mut self.inner, Inner::None);

        match inner {
            Inner::None => unreachable!(),
            Inner::Hmac { inner, opad_key } => {
                let inner_digest = inner.finalize_inner();
                // We need a fresh outer hash. Build a temporary
                // HmacState of the same shape just to get the right
                // outer-hash dispatch — easier with a small helper.
                let outer = match self.algo {
                    Algorithm::HmacSha1 => HmacState::Sha1(Sha1::new()),
                    Algorithm::HmacSha256 => HmacState::Sha256(Sha256::new()),
                    Algorithm::HmacSha384 => HmacState::Sha384(Sha384::new()),
                    Algorithm::HmacSha512 => HmacState::Sha512(Sha512::new()),
                    Algorithm::HmacSha3_256 => HmacState::Sha3_256(Sha3_256::new()),
                    Algorithm::HmacSha3_384 => HmacState::Sha3_384(Sha3_384::new()),
                    Algorithm::HmacSha3_512 => HmacState::Sha3_512(Sha3_512::new()),
                    Algorithm::HmacRipemd160 => HmacState::Ripemd160(Ripemd160::new()),
                    _ => unreachable!(),
                };
                let tag = outer.outer_hash(&opad_key, &inner_digest);
                out[..tlen].copy_from_slice(&tag[..tlen]);
            }
            Inner::Cmac {
                aes,
                tdes,
                block_len,
                k1,
                k2,
                mut x,
                mut buf,
                buf_len,
            } => {
                let bs = block_len;
                // Last block: pad if needed and XOR with K1 / K2.
                if buf_len == bs {
                    for i in 0..bs {
                        buf[i] ^= k1[i];
                    }
                } else {
                    buf[buf_len] = 0x80;
                    for b in &mut buf[buf_len + 1..bs] {
                        *b = 0x00;
                    }
                    for i in 0..bs {
                        buf[i] ^= k2[i];
                    }
                }
                for i in 0..bs {
                    x[i] ^= buf[i];
                }
                if let Some(c) = aes.as_ref() {
                    c.encrypt_block(&mut x[..16]);
                } else if let Some(c) = tdes.as_ref() {
                    c.encrypt_block(&mut x[..8]);
                }
                out[..tlen].copy_from_slice(&x[..tlen]);
            }
            Inner::Kmac { mut sponge, out_len } => {
                // KMAC: append right_encode(L) where L = output length in bits.
                let trailer = right_encode((out_len * 8) as u64);
                sponge.absorb(&trailer);
                sponge.squeeze(&mut out[..tlen]);
            }
            Inner::Gmac {
                aes: _aes,
                h,
                e_j0,
                mut y,
                buf,
                buf_len,
                total_len,
            } => {
                // Absorb partial trailing block (zero-padded).
                if buf_len > 0 {
                    let mut last = [0u8; 16];
                    last[..buf_len].copy_from_slice(&buf[..buf_len]);
                    for i in 0..16 {
                        y[i] ^= last[i];
                    }
                    y = gf128_mul(&y, &h);
                }
                // Length block: len(A=msg) || len(C=0), each 64-bit BE in bits.
                let mut len_block = [0u8; 16];
                let a_bits = total_len * 8;
                len_block[..8].copy_from_slice(&a_bits.to_be_bytes());
                // C bits = 0
                for i in 0..16 {
                    y[i] ^= len_block[i];
                }
                y = gf128_mul(&y, &h);
                // Tag = E(K, J0) XOR y.
                let mut tag = [0u8; 16];
                for i in 0..16 {
                    tag[i] = y[i] ^ e_j0[i];
                }
                out[..tlen].copy_from_slice(&tag[..tlen]);
                // Drop _aes by going out of scope.
                let _ = ghash_update;
            }
        }

        self.finalized = true;
        Ok(tlen)
    }

    /// Finalize and verify the tag against `expected_tag` in
    /// constant time. Supports tag truncation: `expected_tag.len()`
    /// may be **less than or equal to** `tag_len()`. Comparison is
    /// XOR-accumulate-then-test, with no early exit.
    ///
    /// # Errors
    ///
    /// - [`Error::NotInitialized`] / [`Error::AlreadyFinalized`].
    /// - [`Error::VerificationFailed`] if the tag does not match (or
    ///   `expected_tag.len() == 0` or `> tag_len()`).
    pub fn verify(&mut self, expected_tag: &[u8]) -> Result<(), Error> {
        let tlen = self.tag_len();
        if expected_tag.is_empty() || expected_tag.len() > tlen {
            // Still consume the state so the object is left finalized.
            let mut sink = vec![0u8; tlen];
            let _ = self.sign(&mut sink);
            return Err(Error::VerificationFailed);
        }
        let mut full = vec![0u8; tlen];
        self.sign(&mut full)?;

        // Constant-time compare on the truncated prefix.
        let mut diff = 0u8;
        for i in 0..expected_tag.len() {
            diff |= full[i] ^ expected_tag[i];
        }
        if diff == 0 {
            Ok(())
        } else {
            Err(Error::VerificationFailed)
        }
    }

    /// Allocating helper around [`Self::sign`].
    pub fn sign_to_vec(&mut self) -> Result<Vec<u8>, Error> {
        let tlen = self.tag_len();
        let mut out = vec![0u8; tlen];
        self.sign(&mut out)?;
        Ok(out)
    }
}

// ============================================================
// CMAC subkey derivation
// ============================================================

/// CMAC subkey doubling: shift left by 1 bit; if MSB was 1, XOR with
/// the irreducible polynomial constant for the block size.
/// Rb = 0x87 for 16-byte blocks (AES), 0x1B for 8-byte blocks (DES/3DES).
fn cmac_double(input: &[u8]) -> [u8; 16] {
    let bs = input.len();
    debug_assert!(bs == 8 || bs == 16);
    let rb: u8 = if bs == 16 { 0x87 } else { 0x1B };
    let mut out = [0u8; 16];
    let mut carry: u8 = 0;
    for i in (0..bs).rev() {
        let v = input[i];
        out[i] = (v << 1) | carry;
        carry = (v >> 7) & 1;
    }
    if (input[0] & 0x80) != 0 {
        out[bs - 1] ^= rb;
    }
    out
}

// ============================================================
// KMAC / cSHAKE encoding helpers (FIPS SP 800-185 §2.3)
// ============================================================

/// `left_encode(x)`: prefix x's big-endian byte representation with
/// the number of bytes used.
fn left_encode(x: u64) -> Vec<u8> {
    let mut bytes = Vec::new();
    let be = x.to_be_bytes();
    // Skip leading zeros, but keep at least one byte (encoding of 0
    // is 0x01 0x00).
    let first = be.iter().position(|&b| b != 0).unwrap_or(7);
    let n = (8 - first) as u8;
    bytes.push(n);
    bytes.extend_from_slice(&be[first..]);
    bytes
}

/// `right_encode(x)`: append the number-of-bytes byte after the
/// big-endian representation.
fn right_encode(x: u64) -> Vec<u8> {
    let mut bytes = Vec::new();
    let be = x.to_be_bytes();
    let first = be.iter().position(|&b| b != 0).unwrap_or(7);
    let n = (8 - first) as u8;
    bytes.extend_from_slice(&be[first..]);
    bytes.push(n);
    bytes
}

/// `encode_string(S)` = `left_encode(len_in_bits(S)) || S`.
fn append_encode_string(out: &mut Vec<u8>, s: &[u8]) {
    out.extend_from_slice(&left_encode((s.len() as u64) * 8));
    out.extend_from_slice(s);
}

/// `bytepad(X, w)`: prefix `left_encode(w)`, then append zero bytes
/// until the total length is a multiple of `w`.
fn bytepad_to(buf: &mut Vec<u8>, w: usize) {
    // bytepad's prefix `left_encode(w)` must be the very first thing
    // before X. Our callers build X first and then call bytepad_to,
    // so we splice the prefix in at index 0.
    let prefix = left_encode(w as u64);
    let mut full = Vec::with_capacity(prefix.len() + buf.len());
    full.extend_from_slice(&prefix);
    full.extend_from_slice(buf);
    let pad = (w - (full.len() % w)) % w;
    full.extend(core::iter::repeat(0u8).take(pad));
    *buf = full;
}

// ============================================================
// Tests
// ============================================================

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

    fn hex(s: &str) -> Vec<u8> {
        let s: String = s.chars().filter(|c| !c.is_whitespace()).collect();
        (0..s.len())
            .step_by(2)
            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
            .collect()
    }

    // ---------- HMAC-SHA-256 RFC 4231 Test Case 1 ----------

    #[test]
    fn hmac_sha256_rfc4231_tc1() {
        let key = hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
        let data = b"Hi There";
        let expected = hex("b0344c61d8db38535ca8afceaf0bf12b\
             881dc200c9833da726e9376c2e32cff7");

        let mut m = Mac::new(Algorithm::HmacSha256);
        m.init(&key).unwrap();
        m.update(data).unwrap();
        let tag = m.sign_to_vec().unwrap();
        assert_eq!(tag, expected);
    }

    #[test]
    fn hmac_sha256_rfc4231_tc2_streaming() {
        // Key = "Jefe", data = "what do ya want for nothing?"
        let key = b"Jefe";
        let expected = hex("5bdcc146bf60754e6a042426089575c7\
             5a003f089d2739839dec58b964ec3843");
        let data = b"what do ya want for nothing?";

        let mut m = Mac::new(Algorithm::HmacSha256);
        m.init(key).unwrap();
        for chunk in data.chunks(3) {
            m.update(chunk).unwrap();
        }
        let tag = m.sign_to_vec().unwrap();
        assert_eq!(tag, expected);
    }

    #[test]
    fn hmac_sha256_long_key_rfc4231_tc6() {
        // 131-byte key (longer than 64-byte block) → must be hashed first.
        let key = vec![0xaau8; 131];
        let data = b"Test Using Larger Than Block-Size Key - Hash Key First";
        let expected = hex("60e431591ee0b67f0d8a26aacbf5b77f\
             8e0bc6213728c5140546040f0ee37f54");

        let mut m = Mac::new(Algorithm::HmacSha256);
        m.init(&key).unwrap();
        m.update(data).unwrap();
        let tag = m.sign_to_vec().unwrap();
        assert_eq!(tag, expected);
    }

    #[test]
    fn hmac_sha512_rfc4231_tc1() {
        let key = hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
        let data = b"Hi There";
        let expected = hex("87aa7cdea5ef619d4ff0b4241a1d6cb0\
             2379f4e2ce4ec2787ad0b30545e17cde\
             daa833b7d6b8a702038b274eaea3f4e4\
             be9d914eeb61f1702e696c203a126854");

        let mut m = Mac::new(Algorithm::HmacSha512);
        m.init(&key).unwrap();
        m.update(data).unwrap();
        assert_eq!(m.sign_to_vec().unwrap(), expected);
    }

    #[test]
    fn hmac_sha1_rfc2202_tc1() {
        // RFC 2202 test case 1.
        let key = hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
        let data = b"Hi There";
        let expected = hex("b617318655057264e28bc0b6fb378c8ef146be00");

        let mut m = Mac::new(Algorithm::HmacSha1);
        m.init(&key).unwrap();
        m.update(data).unwrap();
        assert_eq!(m.sign_to_vec().unwrap(), expected);
    }

    // ---------- CMAC-AES-128 RFC 4493 ----------
    //
    // M = empty
    #[test]
    fn cmac_aes128_rfc4493_empty() {
        let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
        let expected = hex("bb1d6929e95937287fa37d129b756746");

        let mut m = Mac::new(Algorithm::CmacAes128);
        m.init(&key).unwrap();
        m.update(b"").unwrap();
        assert_eq!(m.sign_to_vec().unwrap(), expected);
    }

    // M = first 16 bytes (one full block)
    #[test]
    fn cmac_aes128_rfc4493_one_block() {
        let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
        let m_bytes = hex("6bc1bee22e409f96e93d7e117393172a");
        let expected = hex("070a16b46b4d4144f79bdd9dd04a287c");

        let mut m = Mac::new(Algorithm::CmacAes128);
        m.init(&key).unwrap();
        m.update(&m_bytes).unwrap();
        assert_eq!(m.sign_to_vec().unwrap(), expected);
    }

    // M = first 40 bytes (2.5 blocks)
    #[test]
    fn cmac_aes128_rfc4493_40bytes() {
        let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
        let m_bytes = hex("6bc1bee22e409f96e93d7e117393172a\
             ae2d8a571e03ac9c9eb76fac45af8e51\
             30c81c46a35ce411");
        let expected = hex("dfa66747de9ae63030ca32611497c827");

        let mut m = Mac::new(Algorithm::CmacAes128);
        m.init(&key).unwrap();
        // Streaming.
        m.update(&m_bytes[..7]).unwrap();
        m.update(&m_bytes[7..23]).unwrap();
        m.update(&m_bytes[23..]).unwrap();
        assert_eq!(m.sign_to_vec().unwrap(), expected);
    }

    // M = first 64 bytes (4 full blocks)
    #[test]
    fn cmac_aes128_rfc4493_64bytes() {
        let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
        let m_bytes = hex("6bc1bee22e409f96e93d7e117393172a\
             ae2d8a571e03ac9c9eb76fac45af8e51\
             30c81c46a35ce411e5fbc1191a0a52ef\
             f69f2445df4f9b17ad2b417be66c3710");
        let expected = hex("51f0bebf7e3b9d92fc49741779363cfe");

        let mut m = Mac::new(Algorithm::CmacAes128);
        m.init(&key).unwrap();
        m.update(&m_bytes).unwrap();
        assert_eq!(m.sign_to_vec().unwrap(), expected);
    }

    // ---------- KMAC128 / KMAC256 NIST sample vectors ----------
    //
    // FIPS SP 800-185, KMAC samples (Sample #1, #2, #3 for KMAC128).

    #[test]
    fn kmac128_sample1() {
        // Key = 0x40..5F (32 bytes), Data = 0x00 0x01 0x02 0x03, S = "" (empty), L = 256
        let key = hex("404142434445464748494a4b4c4d4e4f\
             505152535455565758595a5b5c5d5e5f");
        let data = hex("00010203");
        let expected = hex("e5780b0d3ea6f7d3a429c5706aa43a00\
             fadbd7d49628839e3187243f456ee14e");

        let mut m = Mac::new(Algorithm::Kmac128);
        m.init(&key).unwrap();
        m.update(&data).unwrap();
        assert_eq!(m.sign_to_vec().unwrap(), expected);
    }

    #[test]
    fn kmac128_sample2_with_custom() {
        // Same key, same short data, S = "My Tagged Application", L = 256
        let key = hex("404142434445464748494a4b4c4d4e4f\
             505152535455565758595a5b5c5d5e5f");
        let data = hex("00010203");
        let custom = b"My Tagged Application";
        let expected = hex("3b1fba963cd8b0b59e8c1a6d71888b71\
             43651af8ba0a7070c0979e2811324aa5");

        let mut m = Mac::new(Algorithm::Kmac128);
        m.init_kmac(&key, custom).unwrap();
        m.update(&data).unwrap();
        assert_eq!(m.sign_to_vec().unwrap(), expected);
    }

    #[test]
    fn kmac256_sample4_with_custom() {
        // FIPS SP 800-185 KMAC256 Sample #4: same 32-byte key,
        // 4-byte data, S = "My Tagged Application", L = 512.
        let key = hex("404142434445464748494a4b4c4d4e4f\
             505152535455565758595a5b5c5d5e5f");
        let data = hex("00010203");
        let custom = b"My Tagged Application";
        let expected = hex("20c570c31346f703c9ac36c61c03cb64\
             c3970d0cfc787e9b79599d273a68d2f7\
             f69d4cc3de9d104a351689f27cf6f595\
             1f0103f33f4f24871024d9c27773a8dd");

        let mut m = Mac::new(Algorithm::Kmac256);
        m.init_kmac(&key, custom).unwrap();
        m.update(&data).unwrap();
        assert_eq!(m.sign_to_vec().unwrap(), expected);
    }

    // ---------- GMAC ----------
    //
    // GMAC = GCM with empty plaintext, AAD = message. Cross-validate
    // against the existing Gcm::encrypt path with empty plaintext.
    #[test]
    fn gmac_aes128_matches_gcm_empty_plaintext() {
        use crate::cipher::aes::Aes128;
        use crate::cipher::modes::Gcm;
        let key = hex("feffe9928665731c6d6a8f9467308308");
        let nonce_bytes = hex("cafebabefacedbaddecaf888");
        let nonce: [u8; 12] = nonce_bytes.clone().try_into().unwrap();
        let aad = hex("d9313225f88406e5a55909c5aff5269a\
             86a7a9531534f7da2e4c303d8a318a72\
             1c3c0c95956809532fcf0e2449a6b525");

        let cipher = Aes128::new(&key);
        let (_ct, tag_ref) = Gcm::encrypt(&cipher, &nonce, &aad, &[]);

        let mut m = Mac::new(Algorithm::GmacAes128);
        m.init_with_nonce(&key, &nonce_bytes).unwrap();
        // Streaming feed.
        for chunk in aad.chunks(7) {
            m.update(chunk).unwrap();
        }
        let tag = m.sign_to_vec().unwrap();
        assert_eq!(tag.len(), 16);
        assert_eq!(tag, tag_ref.to_vec());
    }

    #[test]
    fn gmac_verify_truncated() {
        let key = hex("feffe9928665731c6d6a8f9467308308");
        let nonce = hex("cafebabefacedbaddecaf888");
        let aad = b"some authenticated data";

        let mut signer = Mac::new(Algorithm::GmacAes128);
        signer.init_with_nonce(&key, &nonce).unwrap();
        signer.update(aad).unwrap();
        let full_tag = signer.sign_to_vec().unwrap();

        // Truncate to 12 bytes (IPsec-style).
        let truncated = &full_tag[..12];
        let mut verifier = Mac::new(Algorithm::GmacAes128);
        verifier.init_with_nonce(&key, &nonce).unwrap();
        verifier.update(aad).unwrap();
        assert_eq!(verifier.verify(truncated), Ok(()));

        // Tamper.
        let mut bad = full_tag.clone();
        bad[0] ^= 0xFF;
        let mut verifier = Mac::new(Algorithm::GmacAes128);
        verifier.init_with_nonce(&key, &nonce).unwrap();
        verifier.update(aad).unwrap();
        assert_eq!(verifier.verify(&bad[..12]), Err(Error::VerificationFailed));
    }

    // ---------- Sign + verify round trip across all algos ----------

    #[test]
    fn round_trip_all_hmac_and_cmac() {
        let key128 = [0x42u8; 16];
        let key192 = [0x42u8; 24];
        let key256 = [0x42u8; 32];

        let cases: &[(Algorithm, &[u8])] = &[
            (Algorithm::HmacSha1, b"hmac-sha1-key-here"),
            (Algorithm::HmacSha256, b"hmac-sha256-key-here"),
            (Algorithm::HmacSha384, b"hmac-sha384-key-here"),
            (Algorithm::HmacSha512, b"hmac-sha512-key-here"),
            (Algorithm::HmacSha3_256, b"hmac-sha3-256-key"),
            (Algorithm::HmacSha3_384, b"hmac-sha3-384-key"),
            (Algorithm::HmacSha3_512, b"hmac-sha3-512-key"),
            (Algorithm::HmacRipemd160, b"hmac-ripemd160-key"),
            (Algorithm::CmacAes128, &key128),
            (Algorithm::CmacAes192, &key192),
            (Algorithm::CmacAes256, &key256),
            (Algorithm::CmacTripleDes, &key192),
        ];

        for (algo, key) in cases {
            let mut signer = Mac::new(*algo);
            signer.init(key).unwrap();
            signer.update(b"the quick brown fox jumps over the lazy dog").unwrap();
            let tag = signer.sign_to_vec().unwrap();

            let mut verifier = Mac::new(*algo);
            verifier.init(key).unwrap();
            verifier.update(b"the quick brown fox jumps over the lazy dog").unwrap();
            assert_eq!(verifier.verify(&tag), Ok(()), "{:?}", algo);

            // Tamper rejected.
            let mut bad = tag.clone();
            bad[0] ^= 0x01;
            let mut verifier = Mac::new(*algo);
            verifier.init(key).unwrap();
            verifier.update(b"the quick brown fox jumps over the lazy dog").unwrap();
            assert_eq!(verifier.verify(&bad), Err(Error::VerificationFailed), "{:?}", algo);
        }
    }

    // ---------- Error paths ----------

    #[test]
    fn hmac_init_after_use_resets() {
        let mut m = Mac::new(Algorithm::HmacSha256);
        m.init(b"first key").unwrap();
        m.update(b"first message").unwrap();
        let _ = m.sign_to_vec().unwrap();

        // Re-init for a fresh run.
        m.init(b"second key").unwrap();
        m.update(b"second message").unwrap();
        let tag = m.sign_to_vec().unwrap();

        // Compare against an independent instance.
        let mut ref_m = Mac::new(Algorithm::HmacSha256);
        ref_m.init(b"second key").unwrap();
        ref_m.update(b"second message").unwrap();
        assert_eq!(tag, ref_m.sign_to_vec().unwrap());
    }

    #[test]
    fn wrong_init_variant_rejected() {
        let mut m = Mac::new(Algorithm::GmacAes128);
        assert_eq!(m.init(&[0u8; 16]), Err(Error::WrongInitVariant));

        let mut m = Mac::new(Algorithm::HmacSha256);
        assert_eq!(m.init_with_nonce(b"k", &[0u8; 12]), Err(Error::WrongInitVariant));

        let mut m = Mac::new(Algorithm::HmacSha256);
        assert_eq!(m.init_kmac(b"k", b""), Err(Error::WrongInitVariant));
    }

    #[test]
    fn cmac_invalid_key_len() {
        let mut m = Mac::new(Algorithm::CmacAes128);
        assert_eq!(m.init(&[0u8; 17]), Err(Error::InvalidKeyLen));
    }

    #[test]
    fn gmac_invalid_nonce_len() {
        let mut m = Mac::new(Algorithm::GmacAes128);
        assert_eq!(m.init_with_nonce(&[0u8; 16], &[0u8; 11]), Err(Error::InvalidNonceLen));
    }

    #[test]
    fn output_too_small() {
        let mut m = Mac::new(Algorithm::HmacSha256);
        m.init(b"k").unwrap();
        m.update(b"data").unwrap();
        let mut small = [0u8; 8];
        assert!(matches!(m.sign(&mut small), Err(Error::OutputBufferTooSmall { .. })));
    }

    #[test]
    fn verify_rejects_empty_or_too_long() {
        let mut m = Mac::new(Algorithm::HmacSha256);
        m.init(b"k").unwrap();
        m.update(b"d").unwrap();
        assert_eq!(m.verify(&[]), Err(Error::VerificationFailed));

        let mut m = Mac::new(Algorithm::HmacSha256);
        m.init(b"k").unwrap();
        m.update(b"d").unwrap();
        assert_eq!(m.verify(&[0u8; 33]), Err(Error::VerificationFailed));
    }
}
