//! Generic block-cipher modes of operation.
//!
//! Implemented in this layer:
//!
//! - SP 800-38A confidentiality modes: ECB, CBC, CFB (full-block), CFB8, OFB, CTR
//! - SP 800-38B authentication mode: CMAC
//! - SP 800-38C authenticated mode: CCM
//! - SP 800-38D authenticated mode: GCM / GMAC
//! - SP 800-38E storage mode: XTS (128-bit block ciphers only)
//! - RFC 3394 / SP 800-38F key wrap mode: AES Key Wrap (no padding)
//! - EAX authenticated mode
//! - OCB3 authenticated mode (RFC 7253)
//! - AES-GCM-SIV misuse-resistant mode (RFC 8452)
//! - RFC 5297 misuse-resistant mode: SIV
//! - RFC 8439 AEAD: ChaCha20-Poly1305
//!
//! These adapters are generic over any `BlockCipher` in the crate, so the same
//! wrapper works with AES, DES, Camellia, PRESENT, and the other block
//! primitives exposed here.
//!
//! The point of this layer is to separate primitive choice from mode choice:
//! one block cipher implementation can be dropped into several standardized
//! operating modes without duplicating the mode logic in every cipher module.

use crate::BlockCipher;

pub mod chacha20_poly1305;
pub mod eax;
pub mod gcm_siv;
pub mod ocb;
pub mod poly1305;
pub mod siv;
pub use chacha20_poly1305::ChaCha20Poly1305;
pub use eax::Eax;
pub use gcm_siv::{Aes128GcmSiv, Aes256GcmSiv};
pub use ocb::Ocb;
pub use poly1305::Poly1305;
pub use siv::Siv;

#[inline]
fn assert_block_multiple<C: BlockCipher>(buf: &[u8]) {
    assert_eq!(
        buf.len() % C::BLOCK_LEN,
        0,
        "buffer length must be a multiple of the block length"
    );
}

#[inline]
fn xor_in_place(dst: &mut [u8], src: &[u8]) {
    for (d, s) in dst.iter_mut().zip(src.iter()) {
        *d ^= *s;
    }
}

#[inline]
fn increment_be(counter: &mut [u8]) {
    // CTR-style modes treat the IV/counter block as a single big-endian
    // integer so incrementing from the tail matches the NIST block-mode
    // specifications.
    for b in counter.iter_mut().rev() {
        let (next, carry) = b.overflowing_add(1);
        *b = next;
        if !carry {
            break;
        }
    }
}

#[inline]
fn rb_for(block_len: usize) -> u8 {
    match block_len {
        8 => 0x1b,
        16 => 0x87,
        _ => panic!("CMAC only supports 64-bit or 128-bit block ciphers"),
    }
}

fn dbl(block: &[u8]) -> Vec<u8> {
    // CMAC subkeys are generated by doubling in GF(2^n), so this shifts the
    // block left by one bit and folds the carry bit back in via the mode's
    // reduction constant (`Rb`).
    let mut out = vec![0u8; block.len()];
    let mut carry = 0u8;
    for (o, &b) in out.iter_mut().rev().zip(block.iter().rev()) {
        *o = (b << 1) | carry;
        carry = b >> 7;
    }
    if carry != 0 {
        let last = out.len() - 1;
        out[last] ^= rb_for(block.len());
    }
    out
}

#[inline]
fn assert_block_128<C: BlockCipher>() {
    assert_eq!(
        C::BLOCK_LEN,
        16,
        "this mode requires a 128-bit block cipher"
    );
}

#[inline]
fn xor_block16_in_place(dst: &mut [u8; 16], src: &[u8; 16]) {
    for i in 0..16 {
        dst[i] ^= src[i];
    }
}

#[inline]
fn increment_be32(counter: &mut [u8; 16]) {
    // GCM's fast path reserves the low 32 bits of the pre-counter block as the
    // incremented invocation field, so only the final four bytes advance here.
    for b in counter[12..].iter_mut().rev() {
        let (next, carry) = b.overflowing_add(1);
        *b = next;
        if !carry {
            break;
        }
    }
}

const GCM_MAX_COUNTER_BLOCKS: u64 = (u32::MAX as u64) - 1;
const GCM_MAX_PAYLOAD_BYTES: u64 = GCM_MAX_COUNTER_BLOCKS * 16;

#[inline]
fn gcm_payload_len_allowed_u64(len_bytes: u64) -> bool {
    // SP 800-38D bounds GCTR to at most 2^32 - 2 block invocations per key/nonce.
    len_bytes.saturating_add(15) / 16 <= GCM_MAX_COUNTER_BLOCKS
}

#[inline]
fn gcm_payload_len_allowed(len_bytes: usize) -> bool {
    gcm_payload_len_allowed_u64(u64::try_from(len_bytes).unwrap_or(u64::MAX))
}

#[inline]
fn assert_gcm_payload_len(len_bytes: usize) {
    assert!(
        gcm_payload_len_allowed(len_bytes),
        "GCM payload too large: max {} bytes per key/nonce",
        GCM_MAX_PAYLOAD_BYTES
    );
}

#[inline]
fn gf_mul_x_xts(tweak: &mut [u8; 16]) {
    // SP 800-38E treats tweaks as elements of GF(2^128) encoded little-endian.
    // Multiplication by x is a one-bit left shift across bytes; when a carry
    // leaves the top bit, reduce by x^128 + x^7 + x^2 + x + 1, which is `0x87`
    // in this byte order, so the xor lands in `tweak[0]`.
    let mut carry = 0u8;
    for b in tweak.iter_mut() {
        let next = *b >> 7;
        *b = (*b << 1) | carry;
        carry = next;
    }
    if carry != 0 {
        tweak[0] ^= 0x87;
    }
}

#[inline]
fn xex_encrypt_block<C: BlockCipher>(cipher: &C, tweak: &[u8; 16], block: &mut [u8; 16]) {
    xor_block16_in_place(block, tweak);
    cipher.encrypt(block);
    xor_block16_in_place(block, tweak);
}

#[inline]
fn xex_decrypt_block<C: BlockCipher>(cipher: &C, tweak: &[u8; 16], block: &mut [u8; 16]) {
    xor_block16_in_place(block, tweak);
    cipher.decrypt(block);
    xor_block16_in_place(block, tweak);
}

/// Multiply `x` and `y` in GF(2¹²⁸) using the GCM field — variable-time.
///
/// **Field definition.**  GCM uses GF(2¹²⁸) with the reduction polynomial
/// f(α) = α¹²⁸ + α⁷ + α² + α + 1.  Elements are 128-bit polynomials over
/// GF(2); addition is XOR; multiplication is polynomial multiply mod f.
///
/// **Bit convention.**  GCM uses a "reflected" bit order where bit 127 of the
/// `u128` represents the coefficient of α⁰ (constant term) and bit 0
/// represents α¹²⁷.  Under this convention, multiplying by α is a right-shift
/// by 1.  If the low bit (α¹²⁷) was 1, the shift overflows and must be reduced
/// by XOR-ing with `R`.
///
/// **`R` constant.**  `R = 0xe100_..._0000` encodes the remainder
/// `f(α) mod α¹²⁸ = α⁷ + α² + α + 1` in reflected bit order:
/// 0xe1 = 1110_0001 → bits 7,6,5,0 → coefficients α⁰,α¹,α²,α⁷ ✓
///
/// **Algorithm.**  Standard right-to-left binary scalar multiplication:
/// for each bit of `x` (MSB first in the reflected convention), conditionally
/// accumulate `v` into `z`, then advance `v` by one α-multiplication step.
///
/// Not constant-time — branches on bits of `x` and `v`.  Use [`ghash_mul_ct`]
/// when the inputs may be secret.
#[inline]
fn ghash_mul_vt(x: u128, y: u128) -> u128 {
    // SP 800-38D Algorithm 1 reduction constant for p(x)=x^128+x^7+x^2+x+1.
    // GHASH uses a bit-reflected representation, so the low terms
    // (x^7+x^2+x+1) encode as 0xe1 in the most-significant byte.
    const R: u128 = 0xe100_0000_0000_0000_0000_0000_0000_0000;

    let mut z = 0u128;
    let mut v = y;
    for i in 0..128 {
        if ((x >> (127 - i)) & 1) != 0 {
            z ^= v;
        }
        if (v & 1) == 0 {
            v >>= 1;
        } else {
            v = (v >> 1) ^ R;
        }
    }
    z
}

/// Multiply `x` and `y` in GF(2¹²⁸) — constant-time version of [`ghash_mul_vt`].
///
/// Same field and algorithm as `ghash_mul_vt`, but both conditional XORs are
/// replaced with branchless masking so neither the bit-pattern of `x` nor the
/// carry bit of `v` leaks through timing.
#[inline]
fn ghash_mul_ct(x: u128, y: u128) -> u128 {
    // Same reflected reduction constant as `ghash_mul_vt`: in GHASH bit order,
    // x^7 + x^2 + x + 1 maps to 0xe1 in the most-significant byte.
    const R: u128 = 0xe100_0000_0000_0000_0000_0000_0000_0000;

    let mut z = 0u128;
    let mut v = y;
    for i in 0..128 {
        // Turn one bit into an all-ones/all-zero mask and use it to
        // conditionally xor without data-dependent branches.
        let bit = u8::try_from((x >> (127 - i)) & 1).expect("single bit fits in u8");
        let bit_mask = 0u128.wrapping_sub(u128::from(bit));
        z ^= v & bit_mask;

        let lsb = u8::try_from(v & 1).expect("single bit fits in u8");
        let lsb_mask = 0u128.wrapping_sub(u128::from(lsb));
        v = (v >> 1) ^ (R & lsb_mask);
    }
    z
}

type GhashMulFn = fn(u128, u128) -> u128;

fn ghash_update(y: &mut u128, h: u128, data: &[u8], mul: GhashMulFn) {
    let mut block = [0u8; 16];
    for chunk in data.chunks(16) {
        block.fill(0);
        block[..chunk.len()].copy_from_slice(chunk);
        *y ^= u128::from_be_bytes(block);
        *y = mul(*y, h);
    }
}

fn ghash(h: u128, aad: &[u8], ciphertext: &[u8], mul: GhashMulFn) -> u128 {
    let mut y = 0u128;
    ghash_update(&mut y, h, aad, mul);
    ghash_update(&mut y, h, ciphertext, mul);

    let mut len_block = [0u8; 16];
    // SP 800-38D GHASH appends bit lengths, not byte lengths.
    len_block[..8].copy_from_slice(&((aad.len() as u64) << 3).to_be_bytes());
    len_block[8..].copy_from_slice(&((ciphertext.len() as u64) << 3).to_be_bytes());
    y ^= u128::from_be_bytes(len_block);
    mul(y, h)
}

#[inline]
fn ghash_iv(h: u128, iv: &[u8], mul: GhashMulFn) -> [u8; 16] {
    // SP 800-38D §7.1 fast path: for 96-bit IVs, J0 = IV || 0^31 || 1.
    if iv.len() == 12 {
        let mut j0 = [0u8; 16];
        j0[..12].copy_from_slice(iv);
        j0[15] = 1;
        return j0;
    }
    // Non-96-bit IVs are GHASHed with the standard length block.
    ghash(h, &[], iv, mul).to_be_bytes()
}

#[inline]
fn gcm_hash_subkey<C: BlockCipher>(cipher: &C) -> u128 {
    // GCM hash subkey H = E_K(0^128) per SP 800-38D.
    let mut h = [0u8; 16];
    cipher.encrypt(&mut h);
    u128::from_be_bytes(h)
}

#[inline]
fn counter_keystream<C: BlockCipher>(cipher: &C, counter: &[u8; 16]) -> [u8; 16] {
    let mut out = *counter;
    cipher.encrypt(&mut out);
    out
}

fn gcm_compute_tag<C: BlockCipher>(
    cipher: &C,
    nonce: &[u8],
    aad: &[u8],
    ciphertext: &[u8],
    mul: GhashMulFn,
) -> [u8; 16] {
    assert_block_128::<C>();
    assert_gcm_payload_len(ciphertext.len());
    let h = gcm_hash_subkey(cipher);
    let j0 = ghash_iv(h, nonce, mul);
    let s = ghash(h, aad, ciphertext, mul);
    let tag_mask = u128::from_be_bytes(counter_keystream(cipher, &j0));
    (s ^ tag_mask).to_be_bytes()
}

fn gcm_compute_tag_with_h<C: BlockCipher>(
    cipher: &C,
    h: u128,
    nonce: &[u8],
    aad: &[u8],
    ciphertext: &[u8],
    mul: GhashMulFn,
) -> [u8; 16] {
    assert_gcm_payload_len(ciphertext.len());
    let j0 = ghash_iv(h, nonce, mul);
    let s = ghash(h, aad, ciphertext, mul);
    let tag_mask = u128::from_be_bytes(counter_keystream(cipher, &j0));
    (s ^ tag_mask).to_be_bytes()
}

#[inline]
fn ccm_l_from_nonce(nonce: &[u8]) -> usize {
    assert!(
        (7..=13).contains(&nonce.len()),
        "CCM nonce length must be in 7..=13 bytes"
    );
    15 - nonce.len()
}

#[inline]
fn assert_ccm_tag_len(tag_len: usize) {
    assert!(
        (4..=16).contains(&tag_len) && tag_len.is_multiple_of(2),
        "CCM tag length must be one of {{4,6,8,10,12,14,16}}"
    );
}

#[inline]
fn ccm_pack_len(block: &mut [u8; 16], l: usize, value: u64) {
    let needed_bits = l * 8;
    assert!(
        needed_bits >= 64 || value < (1u64 << needed_bits),
        "CCM length/counter does not fit in L bytes"
    );
    for i in 0..l {
        block[15 - i] = u8::try_from((value >> (8 * i)) & 0xff).expect("single byte");
    }
}

#[inline]
fn ccm_b0(nonce: &[u8], msg_len: usize, aad_len: usize, tag_len: usize) -> [u8; 16] {
    let l = ccm_l_from_nonce(nonce);
    assert_ccm_tag_len(tag_len);
    let msg_len_u64 = u64::try_from(msg_len).expect("message length fits u64");

    let mut b0 = [0u8; 16];
    let aad_flag = u8::from(aad_len != 0) << 6;
    let t_field = u8::try_from((tag_len - 2) / 2).expect("CCM tag field fits u8") << 3;
    let l_field = u8::try_from(l - 1).expect("CCM L field fits u8");
    b0[0] = aad_flag | t_field | l_field;
    b0[1..1 + nonce.len()].copy_from_slice(nonce);
    ccm_pack_len(&mut b0, l, msg_len_u64);
    b0
}

#[inline]
fn ccm_counter_block(nonce: &[u8], counter: u64) -> [u8; 16] {
    let l = ccm_l_from_nonce(nonce);
    let mut ctr = [0u8; 16];
    ctr[0] = u8::try_from(l - 1).expect("CCM L field fits u8");
    ctr[1..1 + nonce.len()].copy_from_slice(nonce);
    ccm_pack_len(&mut ctr, l, counter);
    ctr
}

fn ccm_encode_aad(aad: &[u8]) -> Vec<u8> {
    if aad.is_empty() {
        return Vec::new();
    }

    let mut out = Vec::with_capacity(aad.len() + 16);
    let aad_len = aad.len() as u64;
    if aad_len < ((1u64 << 16) - (1u64 << 8)) {
        out.extend_from_slice(
            &u16::try_from(aad_len)
                .expect("length range checked")
                .to_be_bytes(),
        );
    } else if aad_len < (1u64 << 32) {
        out.extend_from_slice(&[0xff, 0xfe]);
        out.extend_from_slice(&(aad_len as u32).to_be_bytes());
    } else {
        out.extend_from_slice(&[0xff, 0xff]);
        out.extend_from_slice(&aad_len.to_be_bytes());
    }
    out.extend_from_slice(aad);
    if !out.len().is_multiple_of(16) {
        out.resize(out.len().next_multiple_of(16), 0);
    }
    out
}

fn ccm_cbc_mac<C: BlockCipher>(
    cipher: &C,
    nonce: &[u8],
    aad: &[u8],
    plaintext: &[u8],
    tag_len: usize,
) -> [u8; 16] {
    assert_block_128::<C>();
    let mut y = [0u8; 16];

    let b0 = ccm_b0(nonce, plaintext.len(), aad.len(), tag_len);
    xor_block16_in_place(&mut y, &b0);
    cipher.encrypt(&mut y);

    let aad_encoded = ccm_encode_aad(aad);
    for chunk in aad_encoded.chunks(16) {
        let mut block = [0u8; 16];
        block.copy_from_slice(chunk);
        xor_block16_in_place(&mut y, &block);
        cipher.encrypt(&mut y);
    }

    for chunk in plaintext.chunks(16) {
        let mut block = [0u8; 16];
        block[..chunk.len()].copy_from_slice(chunk);
        xor_block16_in_place(&mut y, &block);
        cipher.encrypt(&mut y);
    }

    y
}

fn ccm_apply_ctr<C: BlockCipher>(cipher: &C, nonce: &[u8], data: &mut [u8]) {
    for (i, chunk) in data.chunks_mut(16).enumerate() {
        let ctr = ccm_counter_block(nonce, u64::try_from(i + 1).expect("counter fits u64"));
        let stream = counter_keystream(cipher, &ctr);
        xor_in_place(chunk, &stream[..chunk.len()]);
    }
}

const AES_KEY_WRAP_DEFAULT_IV: [u8; 8] = [0xA6; 8];

#[inline]
fn xor_aes_kw_t(a: &mut [u8; 8], t: u64) {
    let t_be = t.to_be_bytes();
    for i in 0..8 {
        a[i] ^= t_be[i];
    }
}

/// AES Key Wrap (RFC 3394) over 64-bit semiblocks with the default IV.
///
/// This is the no-padding variant standardized in RFC 3394 and SP 800-38F.
/// Inputs must be a multiple of 8 bytes and at least 16 bytes long.
pub struct AesKeyWrap<C> {
    cipher: C,
}

impl<C> AesKeyWrap<C> {
    /// Wrap an AES cipher instance for RFC 3394 key wrap operations.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped AES cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C: BlockCipher> AesKeyWrap<C> {
    /// Wrap key material with the RFC 3394 default IV (`A6A6A6A6A6A6A6A6`).
    ///
    /// Returns `None` when `key_data` is not a multiple of 8 bytes or is
    /// shorter than 16 bytes.
    pub fn wrap_key(&self, key_data: &[u8]) -> Option<Vec<u8>> {
        self.wrap_key_with_iv(key_data, &AES_KEY_WRAP_DEFAULT_IV)
    }

    /// Wrap key material with an explicit 64-bit initial register value.
    ///
    /// Returns `None` when `key_data` is not a multiple of 8 bytes or is
    /// shorter than 16 bytes.
    pub fn wrap_key_with_iv(&self, key_data: &[u8], iv: &[u8; 8]) -> Option<Vec<u8>> {
        assert_block_128::<C>();
        if !key_data.len().is_multiple_of(8) || key_data.len() < 16 {
            return None;
        }

        let n = key_data.len() / 8;
        let mut a = *iv;
        let mut r = Vec::with_capacity(n);
        for chunk in key_data.chunks_exact(8) {
            let mut block = [0u8; 8];
            block.copy_from_slice(chunk);
            r.push(block);
        }

        for j in 0..6usize {
            for (i, ri) in r.iter_mut().enumerate() {
                let mut b = [0u8; 16];
                b[..8].copy_from_slice(&a);
                b[8..].copy_from_slice(ri);
                self.cipher.encrypt(&mut b);

                a.copy_from_slice(&b[..8]);
                let t =
                    u64::try_from(j * n + i + 1).expect("AES-KW step index must fit in 64 bits");
                xor_aes_kw_t(&mut a, t);
                ri.copy_from_slice(&b[8..]);
            }
        }

        let mut wrapped = Vec::with_capacity((n + 1) * 8);
        wrapped.extend_from_slice(&a);
        for ri in r {
            wrapped.extend_from_slice(&ri);
        }
        Some(wrapped)
    }

    /// Unwrap RFC 3394 key material and verify the default IV integrity check.
    ///
    /// Returns `None` when input length is invalid or the integrity check
    /// fails.
    pub fn unwrap_key(&self, wrapped: &[u8]) -> Option<Vec<u8>> {
        self.unwrap_key_with_iv(wrapped, &AES_KEY_WRAP_DEFAULT_IV)
    }

    /// Unwrap RFC 3394 key material and verify against an explicit IV value.
    ///
    /// Returns `None` when input length is invalid or the integrity check
    /// fails.
    pub fn unwrap_key_with_iv(&self, wrapped: &[u8], iv: &[u8; 8]) -> Option<Vec<u8>> {
        assert_block_128::<C>();
        if !wrapped.len().is_multiple_of(8) || wrapped.len() < 24 {
            return None;
        }

        let n = (wrapped.len() / 8) - 1;
        let mut a = [0u8; 8];
        a.copy_from_slice(&wrapped[..8]);

        let mut r = Vec::with_capacity(n);
        for chunk in wrapped[8..].chunks_exact(8) {
            let mut block = [0u8; 8];
            block.copy_from_slice(chunk);
            r.push(block);
        }

        for j in (0..6usize).rev() {
            for i in (0..n).rev() {
                let t =
                    u64::try_from(j * n + i + 1).expect("AES-KW step index must fit in 64 bits");
                let mut a_xor_t = a;
                xor_aes_kw_t(&mut a_xor_t, t);

                let mut b = [0u8; 16];
                b[..8].copy_from_slice(&a_xor_t);
                b[8..].copy_from_slice(&r[i]);
                self.cipher.decrypt(&mut b);

                a.copy_from_slice(&b[..8]);
                r[i].copy_from_slice(&b[8..]);
            }
        }

        if crate::ct::constant_time_eq_mask(&a, iv) != u8::MAX {
            return None;
        }

        let mut key_data = Vec::with_capacity(n * 8);
        for ri in r {
            key_data.extend_from_slice(&ri);
        }
        Some(key_data)
    }
}

/// Electronic Codebook (ECB) mode.
///
/// This is included because SP 800-38A defines it, but it should only be used
/// for single-block operations or controlled test vectors. It leaks repeated
/// plaintext patterns.
pub struct Ecb<C> {
    cipher: C,
}

impl<C> Ecb<C> {
    /// Wrap a block cipher in SP 800-38A ECB mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C: BlockCipher> Ecb<C> {
    /// Encrypt block-aligned data in place without padding.
    pub fn encrypt_nopad(&self, data: &mut [u8]) {
        assert_block_multiple::<C>(data);
        for block in data.chunks_exact_mut(C::BLOCK_LEN) {
            self.cipher.encrypt(block);
        }
    }

    /// Decrypt block-aligned data in place without padding.
    pub fn decrypt_nopad(&self, data: &mut [u8]) {
        assert_block_multiple::<C>(data);
        for block in data.chunks_exact_mut(C::BLOCK_LEN) {
            self.cipher.decrypt(block);
        }
    }
}

/// Cipher Block Chaining (CBC) mode.
pub struct Cbc<C> {
    cipher: C,
}

impl<C> Cbc<C> {
    /// Wrap a block cipher in SP 800-38A CBC mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C: BlockCipher> Cbc<C> {
    /// # Panics
    ///
    /// Panics if `iv.len()` does not match the block size, or if `data.len()`
    /// is not an exact multiple of the block size.
    pub fn encrypt_nopad(&self, iv: &[u8], data: &mut [u8]) {
        assert_eq!(iv.len(), C::BLOCK_LEN, "wrong IV length");
        assert_block_multiple::<C>(data);

        let mut prev = iv.to_vec();
        for block in data.chunks_exact_mut(C::BLOCK_LEN) {
            xor_in_place(block, &prev);
            self.cipher.encrypt(block);
            prev.copy_from_slice(block);
        }
    }

    /// # Panics
    ///
    /// Panics if `iv.len()` does not match the block size, or if `data.len()`
    /// is not an exact multiple of the block size.
    pub fn decrypt_nopad(&self, iv: &[u8], data: &mut [u8]) {
        assert_eq!(iv.len(), C::BLOCK_LEN, "wrong IV length");
        assert_block_multiple::<C>(data);

        let mut prev = iv.to_vec();
        let mut tmp = vec![0u8; C::BLOCK_LEN];

        for block in data.chunks_exact_mut(C::BLOCK_LEN) {
            tmp.copy_from_slice(block);
            self.cipher.decrypt(block);
            xor_in_place(block, &prev);
            prev.copy_from_slice(&tmp);
        }
    }
}

/// Cipher Feedback (CFB) mode with a segment size equal to the full block.
pub struct Cfb<C> {
    cipher: C,
}

impl<C> Cfb<C> {
    /// Wrap a block cipher in SP 800-38A CFB mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C: BlockCipher> Cfb<C> {
    /// # Panics
    ///
    /// Panics if `iv.len()` does not match the block size, or if `data.len()`
    /// is not an exact multiple of the block size.
    pub fn encrypt_nopad(&self, iv: &[u8], data: &mut [u8]) {
        assert_eq!(iv.len(), C::BLOCK_LEN, "wrong IV length");
        assert_block_multiple::<C>(data);

        let mut feedback = iv.to_vec();
        let mut keystream = feedback.clone();

        for block in data.chunks_exact_mut(C::BLOCK_LEN) {
            keystream.copy_from_slice(&feedback);
            self.cipher.encrypt(&mut keystream);
            xor_in_place(block, &keystream);
            feedback.copy_from_slice(block);
        }
    }

    /// # Panics
    ///
    /// Panics if `iv.len()` does not match the block size, or if `data.len()`
    /// is not an exact multiple of the block size.
    pub fn decrypt_nopad(&self, iv: &[u8], data: &mut [u8]) {
        assert_eq!(iv.len(), C::BLOCK_LEN, "wrong IV length");
        assert_block_multiple::<C>(data);

        let mut feedback = iv.to_vec();
        let mut keystream = feedback.clone();
        let mut tmp = vec![0u8; C::BLOCK_LEN];

        for block in data.chunks_exact_mut(C::BLOCK_LEN) {
            tmp.copy_from_slice(block);
            keystream.copy_from_slice(&feedback);
            self.cipher.encrypt(&mut keystream);
            xor_in_place(block, &keystream);
            feedback.copy_from_slice(&tmp);
        }
    }
}

/// Cipher Feedback (CFB) mode with an 8-bit segment size (CFB8).
pub struct Cfb8<C> {
    cipher: C,
}

impl<C> Cfb8<C> {
    /// Wrap a block cipher in CFB8 mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C: BlockCipher> Cfb8<C> {
    /// # Panics
    ///
    /// Panics if `iv.len()` does not match the block size.
    pub fn encrypt(&self, iv: &[u8], data: &mut [u8]) {
        assert_eq!(iv.len(), C::BLOCK_LEN, "wrong IV length");
        let mut state = iv.to_vec();
        let mut stream = vec![0u8; C::BLOCK_LEN];

        for byte in data.iter_mut() {
            stream.copy_from_slice(&state);
            self.cipher.encrypt(&mut stream);
            let ct = *byte ^ stream[0];
            state.rotate_left(1);
            state[C::BLOCK_LEN - 1] = ct;
            *byte = ct;
        }
    }

    /// # Panics
    ///
    /// Panics if `iv.len()` does not match the block size.
    pub fn decrypt(&self, iv: &[u8], data: &mut [u8]) {
        assert_eq!(iv.len(), C::BLOCK_LEN, "wrong IV length");
        let mut state = iv.to_vec();
        let mut stream = vec![0u8; C::BLOCK_LEN];

        for byte in data.iter_mut() {
            let ct = *byte;
            stream.copy_from_slice(&state);
            self.cipher.encrypt(&mut stream);
            *byte = ct ^ stream[0];
            state.rotate_left(1);
            state[C::BLOCK_LEN - 1] = ct;
        }
    }
}

/// Output Feedback (OFB) mode.
pub struct Ofb<C> {
    cipher: C,
}

impl<C> Ofb<C> {
    /// Wrap a block cipher in SP 800-38A OFB mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C: BlockCipher> Ofb<C> {
    /// # Panics
    ///
    /// Panics if `iv.len()` does not match the block size.
    pub fn apply_keystream(&self, iv: &[u8], data: &mut [u8]) {
        assert_eq!(iv.len(), C::BLOCK_LEN, "wrong IV length");

        let mut feedback = iv.to_vec();
        for chunk in data.chunks_mut(C::BLOCK_LEN) {
            self.cipher.encrypt(&mut feedback);
            xor_in_place(chunk, &feedback[..chunk.len()]);
        }
    }
}

/// Counter (CTR) mode with a big-endian incrementing counter block.
pub struct Ctr<C> {
    cipher: C,
}

impl<C> Ctr<C> {
    /// Wrap a block cipher in SP 800-38A CTR mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C: BlockCipher> Ctr<C> {
    /// # Panics
    ///
    /// Panics if `counter.len()` does not match the block size.
    pub fn apply_keystream(&self, counter: &[u8], data: &mut [u8]) {
        assert_eq!(counter.len(), C::BLOCK_LEN, "wrong counter length");

        let mut ctr = counter.to_vec();
        let mut stream = ctr.clone();

        for chunk in data.chunks_mut(C::BLOCK_LEN) {
            stream.copy_from_slice(&ctr);
            self.cipher.encrypt(&mut stream);
            xor_in_place(chunk, &stream[..chunk.len()]);
            increment_be(&mut ctr);
        }
    }
}

/// XEX-based Tweaked `CodeBook` mode with ciphertext Stealing (XTS).
///
/// This implementation supports 128-bit block ciphers, which is the case
/// covered by SP 800-38E / XTS-AES.
pub struct Xts<C> {
    data_cipher: C,
    tweak_cipher: C,
}

impl<C> Xts<C> {
    /// Wrap a pair of block ciphers in SP 800-38E XTS mode.
    ///
    /// `data_cipher` encrypts the sector payload blocks. `tweak_cipher`
    /// derives the per-sector tweak stream from the caller-supplied tweak.
    pub fn new(data_cipher: C, tweak_cipher: C) -> Self {
        Self {
            data_cipher,
            tweak_cipher,
        }
    }

    /// Borrow the data-encryption cipher.
    pub fn data_cipher(&self) -> &C {
        &self.data_cipher
    }

    /// Borrow the tweak-derivation cipher.
    pub fn tweak_cipher(&self) -> &C {
        &self.tweak_cipher
    }
}

impl<C: BlockCipher> Xts<C> {
    /// # Panics
    ///
    /// Panics if the wrapped cipher does not have a 128-bit block size, or if
    /// `data` is shorter than one complete block.
    pub fn encrypt_sector(&self, tweak_value: &[u8; 16], data: &mut [u8]) {
        assert_block_128::<C>();
        assert!(
            data.len() >= 16,
            "XTS requires at least one complete block in each data unit"
        );

        let full_blocks = data.len() / 16;
        let rem = data.len() % 16;

        let mut tweak = *tweak_value;
        self.tweak_cipher.encrypt(&mut tweak);

        if rem == 0 {
            for block in data.chunks_exact_mut(16) {
                let mut tmp = [0u8; 16];
                tmp.copy_from_slice(block);
                xex_encrypt_block(&self.data_cipher, &tweak, &mut tmp);
                block.copy_from_slice(&tmp);
                gf_mul_x_xts(&mut tweak);
            }
            return;
        }

        for block in data[..(full_blocks - 1) * 16].chunks_exact_mut(16) {
            let mut tmp = [0u8; 16];
            tmp.copy_from_slice(block);
            xex_encrypt_block(&self.data_cipher, &tweak, &mut tmp);
            block.copy_from_slice(&tmp);
            gf_mul_x_xts(&mut tweak);
        }

        let last_full_start = (full_blocks - 1) * 16;
        let mut cc = [0u8; 16];
        cc.copy_from_slice(&data[last_full_start..last_full_start + 16]);
        xex_encrypt_block(&self.data_cipher, &tweak, &mut cc);

        let mut pp = [0u8; 16];
        pp[..rem].copy_from_slice(&data[last_full_start + 16..]);
        pp[rem..].copy_from_slice(&cc[rem..]);
        data[last_full_start + 16..].copy_from_slice(&cc[..rem]);

        let mut next_tweak = tweak;
        gf_mul_x_xts(&mut next_tweak);
        xex_encrypt_block(&self.data_cipher, &next_tweak, &mut pp);
        data[last_full_start..last_full_start + 16].copy_from_slice(&pp);
    }

    /// # Panics
    ///
    /// Panics if the wrapped cipher does not have a 128-bit block size, or if
    /// `data` is shorter than one complete block.
    pub fn decrypt_sector(&self, tweak_value: &[u8; 16], data: &mut [u8]) {
        assert_block_128::<C>();
        assert!(
            data.len() >= 16,
            "XTS requires at least one complete block in each data unit"
        );

        let full_blocks = data.len() / 16;
        let rem = data.len() % 16;

        let mut tweak = *tweak_value;
        self.tweak_cipher.encrypt(&mut tweak);

        if rem == 0 {
            for block in data.chunks_exact_mut(16) {
                let mut tmp = [0u8; 16];
                tmp.copy_from_slice(block);
                xex_decrypt_block(&self.data_cipher, &tweak, &mut tmp);
                block.copy_from_slice(&tmp);
                gf_mul_x_xts(&mut tweak);
            }
            return;
        }

        for block in data[..(full_blocks - 1) * 16].chunks_exact_mut(16) {
            let mut tmp = [0u8; 16];
            tmp.copy_from_slice(block);
            xex_decrypt_block(&self.data_cipher, &tweak, &mut tmp);
            block.copy_from_slice(&tmp);
            gf_mul_x_xts(&mut tweak);
        }

        let last_full_start = (full_blocks - 1) * 16;
        let mut next_tweak = tweak;
        gf_mul_x_xts(&mut next_tweak);

        let mut pp = [0u8; 16];
        pp.copy_from_slice(&data[last_full_start..last_full_start + 16]);
        xex_decrypt_block(&self.data_cipher, &next_tweak, &mut pp);

        let mut cc = [0u8; 16];
        cc[..rem].copy_from_slice(&data[last_full_start + 16..]);
        cc[rem..].copy_from_slice(&pp[rem..]);

        let mut last_full = cc;
        xex_decrypt_block(&self.data_cipher, &tweak, &mut last_full);

        data[last_full_start..last_full_start + 16].copy_from_slice(&last_full);
        data[last_full_start + 16..].copy_from_slice(&pp[..rem]);
    }
}

/// Cipher-based Message Authentication Code (CMAC).
pub struct Cmac<C> {
    cipher: C,
}

impl<C> Cmac<C> {
    /// Wrap a block cipher in SP 800-38B CMAC mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

/// Counter with CBC-MAC (CCM) with compile-time detached tag length.
///
/// `TAG_LEN` must be one of RFC 3610's valid lengths:
/// `{4, 6, 8, 10, 12, 14, 16}`.
pub struct Ccm<C, const TAG_LEN: usize = 16> {
    cipher: C,
}

impl<C, const TAG_LEN: usize> Ccm<C, TAG_LEN> {
    /// Wrap a 128-bit block cipher in SP 800-38C CCM mode.
    pub fn new(cipher: C) -> Self {
        assert_ccm_tag_len(TAG_LEN);
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }

    /// Return the detached authentication tag length in bytes.
    pub fn tag_len(&self) -> usize {
        TAG_LEN
    }
}

impl<C: BlockCipher, const TAG_LEN: usize> Ccm<C, TAG_LEN> {
    /// Compute a detached CCM tag over `plaintext` and associated data.
    #[must_use]
    pub fn compute_tag(&self, nonce: &[u8], aad: &[u8], plaintext: &[u8]) -> [u8; TAG_LEN] {
        let t = ccm_cbc_mac(&self.cipher, nonce, aad, plaintext, TAG_LEN);
        let s0 = counter_keystream(&self.cipher, &ccm_counter_block(nonce, 0));
        let mut tag = [0u8; TAG_LEN];
        for i in 0..TAG_LEN {
            tag[i] = t[i] ^ s0[i];
        }
        tag
    }

    /// Encrypt `data` in place and return the detached CCM authentication tag.
    #[must_use]
    pub fn encrypt(&self, nonce: &[u8], aad: &[u8], data: &mut [u8]) -> [u8; TAG_LEN] {
        assert_block_128::<C>();
        let tag = self.compute_tag(nonce, aad, data);
        ccm_apply_ctr(&self.cipher, nonce, data);
        tag
    }

    /// Verify `tag` and decrypt in place on success.
    ///
    /// Returns `false` and leaves `data` unchanged when verification fails.
    pub fn decrypt(&self, nonce: &[u8], aad: &[u8], data: &mut [u8], tag: &[u8; TAG_LEN]) -> bool {
        assert_block_128::<C>();

        // In CCM, authentication is over plaintext, so decrypt to a temporary
        // buffer first and only commit if tag verification succeeds.
        let mut plaintext = data.to_vec();
        ccm_apply_ctr(&self.cipher, nonce, &mut plaintext);

        let expected = self.compute_tag(nonce, aad, &plaintext);
        if crate::ct::constant_time_eq_mask(&expected, tag) != u8::MAX {
            // Zeroize the decrypted buffer before dropping: CCM must decrypt
            // before authenticating (MAC is over plaintext), so on auth failure
            // the plaintext briefly exists on the heap and must be wiped.
            crate::ct::zeroize_slice(&mut plaintext);
            return false;
        }

        data.copy_from_slice(&plaintext);
        true
    }
}

/// Galois/Counter Mode (GCM) with a full 128-bit authentication tag.
///
/// Per NIST SP 800-38D, this implementation enforces a per-call payload limit
/// of `(2^32 - 2)` counter blocks (`68_719_476_704` bytes) so the 32-bit
/// counter field cannot wrap.
///
/// Callers must still ensure nonce uniqueness per key; this API is stateless and
/// cannot enforce global `(key, nonce)` uniqueness.
///
/// # Examples
///
/// ```rust
/// use cryptography::{Aes256, Gcm};
///
/// let key = [0x11u8; 32];
/// let nonce = [0x22u8; 12];
/// let aad = b"hdr";
/// let mut ciphertext = *b"payload";
///
/// let aead = Gcm::new(Aes256::new(&key));
/// let tag = aead.encrypt(&nonce, aad, &mut ciphertext);
///
/// let mut recovered = ciphertext;
/// assert!(aead.decrypt(&nonce, aad, &mut recovered, &tag));
/// assert_eq!(recovered, *b"payload");
/// ```
pub struct Gcm<C> {
    cipher: C,
}

/// Variable-time Galois/Counter Mode (GCM) reference path.
///
/// This keeps the historical GHASH implementation for comparison and legacy
/// profiling. Use [`Gcm`] for the default constant-time software GHASH path.
///
/// It enforces the same SP 800-38D payload bound as [`Gcm`]:
/// `(2^32 - 2)` counter blocks (`68_719_476_704` bytes) per call.
pub struct GcmVt<C> {
    cipher: C,
}

impl<C> Gcm<C> {
    /// Wrap a 128-bit block cipher in SP 800-38D GCM mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C> GcmVt<C> {
    /// Wrap a 128-bit block cipher in variable-time SP 800-38D GCM mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C: BlockCipher> Gcm<C> {
    /// Compute the GCM authentication tag over `aad` and `ciphertext`.
    ///
    /// Panics if `ciphertext.len()` exceeds the SP 800-38D per-call bound of
    /// `68_719_476_704` bytes.
    #[must_use]
    pub fn compute_tag(&self, nonce: &[u8], aad: &[u8], ciphertext: &[u8]) -> [u8; 16] {
        gcm_compute_tag(&self.cipher, nonce, aad, ciphertext, ghash_mul_ct)
    }

    /// Encrypt in place and return the 128-bit authentication tag.
    ///
    /// Panics if `data.len()` exceeds the SP 800-38D per-call bound of
    /// `68_719_476_704` bytes.
    #[must_use]
    pub fn encrypt(&self, nonce: &[u8], aad: &[u8], data: &mut [u8]) -> [u8; 16] {
        assert_block_128::<C>();
        assert_gcm_payload_len(data.len());
        let mut h = [0u8; 16];
        self.cipher.encrypt(&mut h);
        let h = u128::from_be_bytes(h);
        let j0 = ghash_iv(h, nonce, ghash_mul_ct);
        let mut counter = j0;
        increment_be32(&mut counter);

        for chunk in data.chunks_mut(16) {
            let stream = counter_keystream(&self.cipher, &counter);
            xor_in_place(chunk, &stream[..chunk.len()]);
            increment_be32(&mut counter);
        }

        let s = ghash(h, aad, data, ghash_mul_ct);
        let tag_mask = u128::from_be_bytes(counter_keystream(&self.cipher, &j0));
        (s ^ tag_mask).to_be_bytes()
    }

    /// Verify the tag and, if valid, decrypt in place.
    ///
    /// Returns `false` and leaves `data` unchanged if tag verification fails.
    ///
    /// Panics if `data.len()` exceeds the SP 800-38D per-call bound of
    /// `68_719_476_704` bytes.
    pub fn decrypt(&self, nonce: &[u8], aad: &[u8], data: &mut [u8], tag: &[u8]) -> bool {
        assert_block_128::<C>();
        assert_gcm_payload_len(data.len());
        let h = gcm_hash_subkey(&self.cipher);
        let expected = gcm_compute_tag_with_h(&self.cipher, h, nonce, aad, data, ghash_mul_ct);
        if crate::ct::constant_time_eq_mask(&expected, tag) != u8::MAX {
            return false;
        }
        let j0 = ghash_iv(h, nonce, ghash_mul_ct);
        let mut counter = j0;
        increment_be32(&mut counter);

        for chunk in data.chunks_mut(16) {
            let stream = counter_keystream(&self.cipher, &counter);
            xor_in_place(chunk, &stream[..chunk.len()]);
            increment_be32(&mut counter);
        }

        true
    }
}

impl<C: BlockCipher> GcmVt<C> {
    /// Compute the GCM authentication tag over `aad` and `ciphertext`.
    ///
    /// Panics if `ciphertext.len()` exceeds the SP 800-38D per-call bound of
    /// `68_719_476_704` bytes.
    #[must_use]
    pub fn compute_tag(&self, nonce: &[u8], aad: &[u8], ciphertext: &[u8]) -> [u8; 16] {
        gcm_compute_tag(&self.cipher, nonce, aad, ciphertext, ghash_mul_vt)
    }

    /// Encrypt in place and return the 128-bit authentication tag.
    ///
    /// Panics if `data.len()` exceeds the SP 800-38D per-call bound of
    /// `68_719_476_704` bytes.
    #[must_use]
    pub fn encrypt(&self, nonce: &[u8], aad: &[u8], data: &mut [u8]) -> [u8; 16] {
        assert_block_128::<C>();
        assert_gcm_payload_len(data.len());
        let mut h = [0u8; 16];
        self.cipher.encrypt(&mut h);
        let h = u128::from_be_bytes(h);
        let j0 = ghash_iv(h, nonce, ghash_mul_vt);
        let mut counter = j0;
        increment_be32(&mut counter);

        for chunk in data.chunks_mut(16) {
            let stream = counter_keystream(&self.cipher, &counter);
            xor_in_place(chunk, &stream[..chunk.len()]);
            increment_be32(&mut counter);
        }

        let s = ghash(h, aad, data, ghash_mul_vt);
        let tag_mask = u128::from_be_bytes(counter_keystream(&self.cipher, &j0));
        (s ^ tag_mask).to_be_bytes()
    }

    /// Verify the tag and, if valid, decrypt in place.
    ///
    /// Returns `false` and leaves `data` unchanged if tag verification fails.
    ///
    /// Panics if `data.len()` exceeds the SP 800-38D per-call bound of
    /// `68_719_476_704` bytes.
    pub fn decrypt(&self, nonce: &[u8], aad: &[u8], data: &mut [u8], tag: &[u8]) -> bool {
        assert_block_128::<C>();
        assert_gcm_payload_len(data.len());
        let h = gcm_hash_subkey(&self.cipher);
        let expected = gcm_compute_tag_with_h(&self.cipher, h, nonce, aad, data, ghash_mul_vt);
        if crate::ct::constant_time_eq_mask(&expected, tag) != u8::MAX {
            return false;
        }
        let j0 = ghash_iv(h, nonce, ghash_mul_vt);
        let mut counter = j0;
        increment_be32(&mut counter);

        for chunk in data.chunks_mut(16) {
            let stream = counter_keystream(&self.cipher, &counter);
            xor_in_place(chunk, &stream[..chunk.len()]);
            increment_be32(&mut counter);
        }

        true
    }
}

/// Galois Message Authentication Code (GMAC).
pub struct Gmac<C> {
    cipher: C,
}

/// Variable-time Galois Message Authentication Code (GMAC) reference path.
///
/// This keeps the historical variable-time GHASH backend for comparison.
/// Use [`Gmac`] for the constant-time default.
pub struct GmacVt<C> {
    cipher: C,
}

impl<C> Gmac<C> {
    /// Wrap a 128-bit block cipher in SP 800-38D GMAC mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C> GmacVt<C> {
    /// Wrap a 128-bit block cipher in variable-time SP 800-38D GMAC mode.
    pub fn new(cipher: C) -> Self {
        Self { cipher }
    }

    /// Borrow the wrapped block cipher.
    pub fn cipher(&self) -> &C {
        &self.cipher
    }
}

impl<C: BlockCipher> Gmac<C> {
    /// Compute a GMAC tag over associated data only.
    #[must_use]
    pub fn compute(&self, nonce: &[u8], aad: &[u8]) -> [u8; 16] {
        gcm_compute_tag(&self.cipher, nonce, aad, &[], ghash_mul_ct)
    }

    /// Verify a GMAC tag in constant time.
    pub fn verify(&self, nonce: &[u8], aad: &[u8], tag: &[u8]) -> bool {
        crate::ct::constant_time_eq_mask(&self.compute(nonce, aad), tag) == u8::MAX
    }
}

impl<C: BlockCipher> GmacVt<C> {
    /// Compute a GMAC tag over associated data only.
    #[must_use]
    pub fn compute(&self, nonce: &[u8], aad: &[u8]) -> [u8; 16] {
        gcm_compute_tag(&self.cipher, nonce, aad, &[], ghash_mul_vt)
    }

    /// Verify a GMAC tag in constant time.
    pub fn verify(&self, nonce: &[u8], aad: &[u8], tag: &[u8]) -> bool {
        crate::ct::constant_time_eq_mask(&self.compute(nonce, aad), tag) == u8::MAX
    }
}

impl<C: BlockCipher> Cmac<C> {
    /// Compute a CMAC tag over arbitrary-length input.
    pub fn compute(&self, data: &[u8]) -> Vec<u8> {
        let blk = C::BLOCK_LEN;
        let mut l = vec![0u8; blk];
        self.cipher.encrypt(&mut l);
        let k1 = dbl(&l);
        let k2 = dbl(&k1);

        let n = if data.is_empty() {
            1
        } else {
            data.len().div_ceil(blk)
        };
        let last_complete = !data.is_empty() && data.len().is_multiple_of(blk);

        let mut x = vec![0u8; blk];
        let mut y = vec![0u8; blk];

        for block in data.chunks(blk).take(n.saturating_sub(1)) {
            y.copy_from_slice(&x);
            xor_in_place(&mut y, block);
            self.cipher.encrypt(&mut y);
            x.copy_from_slice(&y);
        }

        let mut m_last = vec![0u8; blk];
        if last_complete {
            let start = (n - 1) * blk;
            m_last.copy_from_slice(&data[start..start + blk]);
            xor_in_place(&mut m_last, &k1);
        } else {
            let start = (n - 1) * blk;
            let rem = data.len().saturating_sub(start);
            if rem != 0 {
                m_last[..rem].copy_from_slice(&data[start..]);
            }
            m_last[rem] = 0x80;
            xor_in_place(&mut m_last, &k2);
        }

        xor_in_place(&mut m_last, &x);
        self.cipher.encrypt(&mut m_last);
        m_last
    }

    /// Verify a CMAC tag in constant time.
    pub fn verify(&self, data: &[u8], tag: &[u8]) -> bool {
        crate::ct::constant_time_eq_mask(&self.compute(data), tag) == u8::MAX
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{Aes128, Aes192, Aes256};

    fn parse<const N: usize>(s: &str) -> [u8; N] {
        let mut out = [0u8; N];
        assert_eq!(s.len(), 2 * N);
        for i in 0..N {
            out[i] = u8::from_str_radix(&s[2 * i..2 * i + 2], 16).unwrap();
        }
        out
    }

    fn parse_vec(s: &str) -> Vec<u8> {
        assert_eq!(s.len() % 2, 0);
        let mut out = Vec::with_capacity(s.len() / 2);
        let bytes = s.as_bytes();
        let mut i = 0usize;
        while i + 1 < bytes.len() {
            let hi =
                u8::from_str_radix(std::str::from_utf8(&bytes[i..i + 1]).unwrap(), 16).unwrap();
            let lo =
                u8::from_str_radix(std::str::from_utf8(&bytes[i + 1..i + 2]).unwrap(), 16).unwrap();
            out.push((hi << 4) | lo);
            i += 2;
        }
        out
    }

    #[test]
    fn ecb_aes128_sp800_38a() {
        let key = parse::<16>("2b7e151628aed2a6abf7158809cf4f3c");
        let mut data = [
            parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
            parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
            parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
            parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
        ]
        .concat();
        let expected = [
            parse::<16>("3ad77bb40d7a3660a89ecaf32466ef97"),
            parse::<16>("f5d3d58503b9699de785895a96fdbaaf"),
            parse::<16>("43b1cd7f598ece23881b00e3ed030688"),
            parse::<16>("7b0c785e27e8ad3f8223207104725dd4"),
        ]
        .concat();

        Ecb::new(Aes128::new(&key)).encrypt_nopad(&mut data);
        assert_eq!(data, expected);
        Ecb::new(Aes128::new(&key)).decrypt_nopad(&mut data);
        assert_eq!(
            data,
            [
                parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
                parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
                parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
                parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
            ]
            .concat()
        );
    }

    #[test]
    fn ecb_aes256_sp800_38a() {
        // NIST SP 800-38A, F.1.5 ECB-AES256.
        let key = parse::<32>("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
        let mut data = [
            parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
            parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
            parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
            parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
        ]
        .concat();
        let expected = [
            parse::<16>("f3eed1bdb5d2a03c064b5a7e3db181f8"),
            parse::<16>("591ccb10d410ed26dc5ba74a31362870"),
            parse::<16>("b6ed21b99ca6f4f9f153e7b1beafed1d"),
            parse::<16>("23304b7a39f9f3ff067d8d8f9e24ecc7"),
        ]
        .concat();

        Ecb::new(Aes256::new(&key)).encrypt_nopad(&mut data);
        assert_eq!(data, expected);
        Ecb::new(Aes256::new(&key)).decrypt_nopad(&mut data);
        assert_eq!(
            data,
            [
                parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
                parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
                parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
                parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
            ]
            .concat()
        );
    }

    #[test]
    fn cbc_aes128_sp800_38a() {
        let key = parse::<16>("2b7e151628aed2a6abf7158809cf4f3c");
        let iv = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let mut data = [
            parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
            parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
            parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
            parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
        ]
        .concat();
        let expected = [
            parse::<16>("7649abac8119b246cee98e9b12e9197d"),
            parse::<16>("5086cb9b507219ee95db113a917678b2"),
            parse::<16>("73bed6b8e3c1743b7116e69e22229516"),
            parse::<16>("3ff1caa1681fac09120eca307586e1a7"),
        ]
        .concat();

        let mode = Cbc::new(Aes128::new(&key));
        mode.encrypt_nopad(&iv, &mut data);
        assert_eq!(data, expected);
        mode.decrypt_nopad(&iv, &mut data);
        assert_eq!(
            data,
            [
                parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
                parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
                parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
                parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
            ]
            .concat()
        );
    }

    #[test]
    fn cbc_aes256_sp800_38a() {
        // NIST SP 800-38A, F.2.5 CBC-AES256.
        let key = parse::<32>("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
        let iv = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let mut data = [
            parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
            parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
            parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
            parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
        ]
        .concat();
        let expected = [
            parse::<16>("f58c4c04d6e5f1ba779eabfb5f7bfbd6"),
            parse::<16>("9cfc4e967edb808d679f777bc6702c7d"),
            parse::<16>("39f23369a9d9bacfa530e26304231461"),
            parse::<16>("b2eb05e2c39be9fcda6c19078c6a9d1b"),
        ]
        .concat();

        let mode = Cbc::new(Aes256::new(&key));
        mode.encrypt_nopad(&iv, &mut data);
        assert_eq!(data, expected);
        mode.decrypt_nopad(&iv, &mut data);
        assert_eq!(
            data,
            [
                parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
                parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
                parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
                parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
            ]
            .concat()
        );
    }

    #[test]
    fn cfb_aes128_sp800_38a() {
        let key = parse::<16>("2b7e151628aed2a6abf7158809cf4f3c");
        let iv = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let mut data = [
            parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
            parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
            parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
            parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
        ]
        .concat();
        let expected = [
            parse::<16>("3b3fd92eb72dad20333449f8e83cfb4a"),
            parse::<16>("c8a64537a0b3a93fcde3cdad9f1ce58b"),
            parse::<16>("26751f67a3cbb140b1808cf187a4f4df"),
            parse::<16>("c04b05357c5d1c0eeac4c66f9ff7f2e6"),
        ]
        .concat();

        let mode = Cfb::new(Aes128::new(&key));
        mode.encrypt_nopad(&iv, &mut data);
        assert_eq!(data, expected);
        mode.decrypt_nopad(&iv, &mut data);
        assert_eq!(
            data,
            [
                parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
                parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
                parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
                parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
            ]
            .concat()
        );
    }

    #[test]
    fn cfb_aes256_sp800_38a() {
        // NIST SP 800-38A, F.3.15 CFB128-AES256.
        let key = parse::<32>("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
        let iv = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let mut data = [
            parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
            parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
            parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
            parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
        ]
        .concat();
        let expected = [
            parse::<16>("dc7e84bfda79164b7ecd8486985d3860"),
            parse::<16>("39ffed143b28b1c832113c6331e5407b"),
            parse::<16>("df10132415e54b92a13ed0a8267ae2f9"),
            parse::<16>("75a385741ab9cef82031623d55b1e471"),
        ]
        .concat();

        let mode = Cfb::new(Aes256::new(&key));
        mode.encrypt_nopad(&iv, &mut data);
        assert_eq!(data, expected);
        mode.decrypt_nopad(&iv, &mut data);
        assert_eq!(
            data,
            [
                parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
                parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
                parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
                parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
            ]
            .concat()
        );
    }

    #[test]
    fn cfb8_aes128_roundtrip() {
        let key = parse::<16>("2b7e151628aed2a6abf7158809cf4f3c");
        let iv = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let plaintext = *b"cfb8 mode roundtrip check";
        let mut data = plaintext;

        let mode = Cfb8::new(Aes128::new(&key));
        mode.encrypt(&iv, &mut data);
        assert_ne!(data, plaintext);
        mode.decrypt(&iv, &mut data);
        assert_eq!(data, plaintext);
    }

    #[test]
    fn cfb8_aes128_sp800_38a_prefix_vector() {
        // NIST SP 800-38A, F.3.7 (first 18 CFB8 segments).
        let key = parse::<16>("2b7e151628aed2a6abf7158809cf4f3c");
        let iv = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let mut data = parse_vec("6bc1bee22e409f96e93d7e117393172aae2d");
        let expected = parse_vec("3b79424c9c0dd436bace9e0ed4586a4f32b9");

        let mode = Cfb8::new(Aes128::new(&key));
        mode.encrypt(&iv, &mut data);
        assert_eq!(data, expected);
        mode.decrypt(&iv, &mut data);
        assert_eq!(data, parse_vec("6bc1bee22e409f96e93d7e117393172aae2d"));
    }

    #[test]
    fn ofb_aes128_sp800_38a() {
        let key = parse::<16>("2b7e151628aed2a6abf7158809cf4f3c");
        let iv = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let mut data = [
            parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
            parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
            parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
            parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
        ]
        .concat();
        let expected = [
            parse::<16>("3b3fd92eb72dad20333449f8e83cfb4a"),
            parse::<16>("7789508d16918f03f53c52dac54ed825"),
            parse::<16>("9740051e9c5fecf64344f7a82260edcc"),
            parse::<16>("304c6528f659c77866a510d9c1d6ae5e"),
        ]
        .concat();

        let mode = Ofb::new(Aes128::new(&key));
        mode.apply_keystream(&iv, &mut data);
        assert_eq!(data, expected);
        mode.apply_keystream(&iv, &mut data);
        assert_eq!(
            data,
            [
                parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
                parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
                parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
                parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
            ]
            .concat()
        );
    }

    #[test]
    fn ofb_aes256_sp800_38a() {
        // NIST SP 800-38A, F.4.5 OFB-AES256.
        let key = parse::<32>("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
        let iv = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let mut data = [
            parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
            parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
            parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
            parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
        ]
        .concat();
        let expected = [
            parse::<16>("dc7e84bfda79164b7ecd8486985d3860"),
            parse::<16>("4febdc6740d20b3ac88f6ad82a4fb08d"),
            parse::<16>("71ab47a086e86eedf39d1c5bba97c408"),
            parse::<16>("0126141d67f37be8538f5a8be740e484"),
        ]
        .concat();

        let mode = Ofb::new(Aes256::new(&key));
        mode.apply_keystream(&iv, &mut data);
        assert_eq!(data, expected);
        mode.apply_keystream(&iv, &mut data);
        assert_eq!(
            data,
            [
                parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
                parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
                parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
                parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
            ]
            .concat()
        );
    }

    #[test]
    fn ctr_aes128_sp800_38a() {
        let key = parse::<16>("2b7e151628aed2a6abf7158809cf4f3c");
        let ctr = parse::<16>("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
        let mut data = [
            parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
            parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
            parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
            parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
        ]
        .concat();
        let expected = [
            parse::<16>("874d6191b620e3261bef6864990db6ce"),
            parse::<16>("9806f66b7970fdff8617187bb9fffdff"),
            parse::<16>("5ae4df3edbd5d35e5b4f09020db03eab"),
            parse::<16>("1e031dda2fbe03d1792170a0f3009cee"),
        ]
        .concat();

        let mode = Ctr::new(Aes128::new(&key));
        mode.apply_keystream(&ctr, &mut data);
        assert_eq!(data, expected);
        mode.apply_keystream(&ctr, &mut data);
        assert_eq!(
            data,
            [
                parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
                parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
                parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
                parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
            ]
            .concat()
        );
    }

    #[test]
    fn ctr_aes256_sp800_38a() {
        // NIST SP 800-38A, F.5.5 CTR-AES256.
        let key = parse::<32>("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
        let ctr = parse::<16>("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
        let mut data = [
            parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
            parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
            parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
            parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
        ]
        .concat();
        let expected = [
            parse::<16>("601ec313775789a5b7a7f504bbf3d228"),
            parse::<16>("f443e3ca4d62b59aca84e990cacaf5c5"),
            parse::<16>("2b0930daa23de94ce87017ba2d84988d"),
            parse::<16>("dfc9c58db67aada613c2dd08457941a6"),
        ]
        .concat();

        let mode = Ctr::new(Aes256::new(&key));
        mode.apply_keystream(&ctr, &mut data);
        assert_eq!(data, expected);
        mode.apply_keystream(&ctr, &mut data);
        assert_eq!(
            data,
            [
                parse::<16>("6bc1bee22e409f96e93d7e117393172a"),
                parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"),
                parse::<16>("30c81c46a35ce411e5fbc1191a0a52ef"),
                parse::<16>("f69f2445df4f9b17ad2b417be66c3710"),
            ]
            .concat()
        );
    }

    #[test]
    fn cmac_aes128_sp800_38b() {
        let key = parse::<16>("2b7e151628aed2a6abf7158809cf4f3c");
        let mode = Cmac::new(Aes128::new(&key));

        assert_eq!(
            mode.compute(&[]),
            parse::<16>("bb1d6929e95937287fa37d129b756746").to_vec()
        );
        assert_eq!(
            mode.compute(&parse::<16>("6bc1bee22e409f96e93d7e117393172a")),
            parse::<16>("070a16b46b4d4144f79bdd9dd04a287c").to_vec()
        );
        let mut msg = Vec::with_capacity(40);
        msg.extend_from_slice(&parse::<16>("6bc1bee22e409f96e93d7e117393172a"));
        msg.extend_from_slice(&parse::<16>("ae2d8a571e03ac9c9eb76fac45af8e51"));
        msg.extend_from_slice(&parse::<8>("30c81c46a35ce411"));
        assert_eq!(
            mode.compute(&msg),
            parse::<16>("dfa66747de9ae63030ca32611497c827").to_vec()
        );
        assert!(mode.verify(&msg, &parse::<16>("dfa66747de9ae63030ca32611497c827")));
    }

    #[test]
    fn xts_aes128_two_block_matches_openssl() {
        let key1 = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let key2 = parse::<16>("101112131415161718191a1b1c1d1e1f");
        let tweak = [0u8; 16];
        let mut data =
            parse::<32>("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
        let expected =
            parse::<32>("74a109aabf1937c022d19da4b96cbc40b8ddc9c0653a7fb0dc8425c7ef276dea");

        let mode = Xts::new(Aes128::new(&key1), Aes128::new(&key2));
        mode.encrypt_sector(&tweak, &mut data);
        assert_eq!(data, expected);
        mode.decrypt_sector(&tweak, &mut data);
        assert_eq!(
            data,
            parse::<32>("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
        );
    }

    #[test]
    fn xts_aes128_ciphertext_stealing_matches_openssl() {
        let key1 = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let key2 = parse::<16>("101112131415161718191a1b1c1d1e1f");
        let tweak = [0u8; 16];
        let mut data =
            parse::<31>("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e");
        let expected =
            parse::<31>("03ab02ee0037b6327b1110429d562a8674a109aabf1937c022d19da4b96cbc");

        let mode = Xts::new(Aes128::new(&key1), Aes128::new(&key2));
        mode.encrypt_sector(&tweak, &mut data);
        assert_eq!(data, expected);
        mode.decrypt_sector(&tweak, &mut data);
        assert_eq!(
            data,
            parse::<31>("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e")
        );
    }

    #[test]
    fn xts_aes128_runtime_cross_check_with_openssl() {
        let key1 = parse::<16>("000102030405060708090a0b0c0d0e0f");
        let key2 = parse::<16>("101112131415161718191a1b1c1d1e1f");
        let tweak = [0u8; 16];
        let plaintext =
            parse::<31>("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e");

        let Some(expected) = crate::test_utils::run_openssl(
            &[
                "enc",
                "-aes-128-xts",
                "-e",
                "-nopad",
                "-K",
                "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
                "-iv",
                "00000000000000000000000000000000",
            ],
            &plaintext,
        ) else {
            return;
        };

        let mut data = plaintext;
        let mode = Xts::new(Aes128::new(&key1), Aes128::new(&key2));
        mode.encrypt_sector(&tweak, &mut data);
        assert_eq!(data.as_slice(), expected.as_slice());
    }

    #[test]
    fn xts_aes128_nist_cavp_vector() {
        // NIST CAVP XTSGenAES128.rsp, "format tweak value input - 128 hex str",
        // ENCRYPT, COUNT = 1.
        let key1 = parse::<16>("a1b90cba3f06ac353b2c343876081762");
        let key2 = parse::<16>("090923026e91771815f29dab01932f2f");
        let tweak = parse::<16>("4faef7117cda59c66e4b92013e768ad5");
        let mut data = parse::<16>("ebabce95b14d3c8d6fb350390790311c");
        let expected = parse::<16>("778ae8b43cb98d5a825081d5be471c63");

        let mode = Xts::new(Aes128::new(&key1), Aes128::new(&key2));
        mode.encrypt_sector(&tweak, &mut data);
        assert_eq!(data, expected);
        mode.decrypt_sector(&tweak, &mut data);
        assert_eq!(data, parse::<16>("ebabce95b14d3c8d6fb350390790311c"));
    }

    #[test]
    fn ctr_des_roundtrip_generic() {
        let key = parse::<8>("133457799bbcdff1");
        let counter = parse::<8>("0123456789abcdef");
        let original = *b"generic DES mode path!";
        let mut data = original;

        let mode = Ctr::new(crate::Des::new(&key).expect("non-weak DES test key"));
        mode.apply_keystream(&counter, &mut data);
        assert_ne!(data, original);
        mode.apply_keystream(&counter, &mut data);
        assert_eq!(data, original);
    }

    #[test]
    fn gcm_aes128_empty_plaintext_nist() {
        let key = [0u8; 16];
        let iv = [0u8; 12];
        let mut data = Vec::new();
        let mode = Gcm::new(Aes128::new(&key));

        let tag = mode.encrypt(&iv, &[], &mut data);
        assert_eq!(data, Vec::<u8>::new());
        assert_eq!(tag, parse::<16>("58e2fccefa7e3061367f1d57a4e7455a"));
        assert!(mode.decrypt(&iv, &[], &mut data, &tag));
    }

    #[test]
    fn gcm_aes128_single_block_nist() {
        let key = [0u8; 16];
        let iv = [0u8; 12];
        let mut data = [0u8; 16];
        let expected_ct = parse::<16>("0388dace60b6a392f328c2b971b2fe78");
        let expected_tag = parse::<16>("ab6e47d42cec13bdf53a67b21257bddf");
        let mode = Gcm::new(Aes128::new(&key));

        let tag = mode.encrypt(&iv, &[], &mut data);
        assert_eq!(data, expected_ct);
        assert_eq!(tag, expected_tag);
        assert!(mode.decrypt(&iv, &[], &mut data, &tag));
        assert_eq!(data, [0u8; 16]);
    }

    #[test]
    fn gcm_aes128_with_aad_nist() {
        let key = parse::<16>("feffe9928665731c6d6a8f9467308308");
        let iv = parse::<12>("cafebabefacedbaddecaf888");
        let aad = parse::<20>("feedfacedeadbeeffeedfacedeadbeefabaddad2");
        let mut data = parse::<64>(
            "d9313225f88406e5a55909c5aff5269a\
             86a7a9531534f7da2e4c303d8a318a72\
             1c3c0c95956809532fcf0e2449a6b525\
             b16aedf5aa0de657ba637b391aafd255",
        );
        let expected_ct = parse::<64>(
            "42831ec2217774244b7221b784d0d49c\
             e3aa212f2c02a4e035c17e2329aca12e\
             21d514b25466931c7d8f6a5aac84aa05\
             1ba30b396a0aac973d58e091473f5985",
        );
        let expected_tag = parse::<16>("da80ce830cfda02da2a218a1744f4c76");
        let mode = Gcm::new(Aes128::new(&key));

        let tag = mode.encrypt(&iv, &aad, &mut data);
        assert_eq!(data, expected_ct);
        assert_eq!(tag, expected_tag);
        assert!(mode.decrypt(&iv, &aad, &mut data, &tag));
        assert_eq!(
            data,
            parse::<64>(
                "d9313225f88406e5a55909c5aff5269a\
                 86a7a9531534f7da2e4c303d8a318a72\
                 1c3c0c95956809532fcf0e2449a6b525\
                 b16aedf5aa0de657ba637b391aafd255",
            )
        );
    }

    #[test]
    fn gcm_aes256_single_block_cavp() {
        // NIST CAVP gcmEncryptExtIV256.rsp
        // [Keylen=256, IVlen=96, PTlen=128, AADlen=0, Taglen=128], Count=0.
        let key = parse::<32>("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22");
        let iv = parse::<12>("0d18e06c7c725ac9e362e1ce");
        let mut data = parse::<16>("2db5168e932556f8089a0622981d017d");
        let expected_ct = parse::<16>("fa4362189661d163fcd6a56d8bf0405a");
        let expected_tag = parse::<16>("d636ac1bbedd5cc3ee727dc2ab4a9489");

        let mode = Gcm::new(Aes256::new(&key));
        let tag = mode.encrypt(&iv, &[], &mut data);
        assert_eq!(data, expected_ct);
        assert_eq!(tag, expected_tag);
        assert!(mode.decrypt(&iv, &[], &mut data, &tag));
        assert_eq!(data, parse::<16>("2db5168e932556f8089a0622981d017d"));
    }

    #[test]
    fn gcm_aes256_non_96bit_iv_auth_only_cavp() {
        // NIST CAVP gcmEncryptExtIV256.rsp
        // [Keylen=256, IVlen=8, PTlen=0, AADlen=128, Taglen=128], Count=0.
        let key = parse::<32>("c639f716597a86afd12319199e21a62b1fc0277a70e3ca120bd3ff745be88604");
        let iv = parse::<1>("29");
        let aad = parse::<16>("20fda1db6911d160121dc3c48e5f19b2");
        let mut data: [u8; 0] = [];
        let expected_tag = parse::<16>("221a3398f20d0d9fe913f33a6cd413d3");

        let mode = Gcm::new(Aes256::new(&key));
        let tag = mode.encrypt(&iv, &aad, &mut data);
        assert_eq!(tag, expected_tag);
        assert!(mode.decrypt(&iv, &aad, &mut data, &tag));
    }

    #[test]
    fn gcm_rejects_wrong_tag_without_decrypting() {
        let key = [0u8; 16];
        let iv = [0u8; 12];
        let mut data = [0u8; 16];
        let mode = Gcm::new(Aes128::new(&key));
        let tag = mode.encrypt(&iv, &[], &mut data);
        let mut bad_tag = tag;
        bad_tag[0] ^= 1;
        let ciphertext = data;

        assert!(!mode.decrypt(&iv, &[], &mut data, &bad_tag));
        assert_eq!(data, ciphertext);
    }

    #[test]
    fn gcm_ct_and_vt_backends_match() {
        let key = parse::<16>("feffe9928665731c6d6a8f9467308308");
        let iv = parse::<12>("cafebabefacedbaddecaf888");
        let aad = parse::<20>("feedfacedeadbeeffeedfacedeadbeefabaddad2");
        let plaintext = parse::<64>(
            "d9313225f88406e5a55909c5aff5269a\
             86a7a9531534f7da2e4c303d8a318a72\
             1c3c0c95956809532fcf0e2449a6b525\
             b16aedf5aa0de657ba637b391aafd255",
        );

        let gcm_ct = Gcm::new(Aes128::new(&key));
        let gcm_vt = GcmVt::new(Aes128::new(&key));

        let mut ct_data = plaintext;
        let mut vt_data = plaintext;
        let ct_tag = gcm_ct.encrypt(&iv, &aad, &mut ct_data);
        let vt_tag = gcm_vt.encrypt(&iv, &aad, &mut vt_data);

        assert_eq!(ct_data, vt_data);
        assert_eq!(ct_tag, vt_tag);
        assert!(gcm_ct.decrypt(&iv, &aad, &mut ct_data, &ct_tag));
        assert!(gcm_vt.decrypt(&iv, &aad, &mut vt_data, &vt_tag));
        assert_eq!(ct_data, plaintext);
        assert_eq!(vt_data, plaintext);
    }

    #[test]
    fn gcm_payload_limit_matches_sp800_38d_bound() {
        assert!(gcm_payload_len_allowed_u64(0));
        assert!(gcm_payload_len_allowed_u64(1));
        assert!(gcm_payload_len_allowed_u64(16));
        assert!(gcm_payload_len_allowed_u64(GCM_MAX_PAYLOAD_BYTES));
        assert!(!gcm_payload_len_allowed_u64(GCM_MAX_PAYLOAD_BYTES + 1));
    }

    #[test]
    fn gmac_matches_gcm_on_empty_plaintext() {
        let key = parse::<16>("feffe9928665731c6d6a8f9467308308");
        let iv = parse::<12>("cafebabefacedbaddecaf888");
        let aad = parse::<20>("feedfacedeadbeeffeedfacedeadbeefabaddad2");

        let gcm = Gcm::new(Aes128::new(&key));
        let gmac = Gmac::new(Aes128::new(&key));
        let tag = gmac.compute(&iv, &aad);

        assert_eq!(tag, gcm.compute_tag(&iv, &aad, &[]));
        assert!(gmac.verify(&iv, &aad, &tag));
    }

    #[test]
    fn gmac_ct_and_vt_backends_match() {
        let key = parse::<16>("feffe9928665731c6d6a8f9467308308");
        let iv = parse::<12>("cafebabefacedbaddecaf888");
        let aad = parse::<20>("feedfacedeadbeeffeedfacedeadbeefabaddad2");

        let gmac_ct = Gmac::new(Aes128::new(&key));
        let gmac_vt = GmacVt::new(Aes128::new(&key));

        let tag_ct = gmac_ct.compute(&iv, &aad);
        let tag_vt = gmac_vt.compute(&iv, &aad);

        assert_eq!(tag_ct, tag_vt);
        assert!(gmac_ct.verify(&iv, &aad, &tag_ct));
        assert!(gmac_vt.verify(&iv, &aad, &tag_vt));
    }

    #[test]
    fn ccm_aes128_rfc3610_packet_vector_1() {
        // RFC 3610, section 8, Packet Vector #1.
        let key = parse::<16>("c0c1c2c3c4c5c6c7c8c9cacbcccdcecf");
        let nonce = parse::<13>("00000003020100a0a1a2a3a4a5");
        let aad = parse::<8>("0001020304050607");
        let mut msg = parse::<23>("08090a0b0c0d0e0f101112131415161718191a1b1c1d1e");
        let expected_ct = parse::<23>("588c979a61c663d2f066d0c2c0f989806d5f6b61dac384");
        let expected_tag = parse::<8>("17e8d12cfdf926e0");

        let mode = Ccm::<_, 8>::new(Aes128::new(&key));
        let tag = mode.encrypt(&nonce, &aad, &mut msg);
        assert_eq!(msg, expected_ct);
        assert_eq!(tag, expected_tag);

        assert!(mode.decrypt(&nonce, &aad, &mut msg, &tag));
        assert_eq!(
            msg,
            parse::<23>("08090a0b0c0d0e0f101112131415161718191a1b1c1d1e")
        );
    }

    #[test]
    fn ccm_aes128_rfc3610_packet_vector_2() {
        // RFC 3610, section 8, Packet Vector #2.
        let key = parse::<16>("c0c1c2c3c4c5c6c7c8c9cacbcccdcecf");
        let nonce = parse::<13>("00000004030201a0a1a2a3a4a5");
        let aad = parse::<8>("0001020304050607");
        let mut msg = parse::<24>("08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
        let expected_ct = parse::<24>("72c91a36e135f8cf291ca894085c87e3cc15c439c9e43a3b");
        let expected_tag = parse::<8>("a091d56e10400916");

        let mode = Ccm::<_, 8>::new(Aes128::new(&key));
        let tag = mode.encrypt(&nonce, &aad, &mut msg);
        assert_eq!(msg, expected_ct);
        assert_eq!(tag, expected_tag);

        assert!(mode.decrypt(&nonce, &aad, &mut msg, &tag));
        assert_eq!(
            msg,
            parse::<24>("08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
        );
    }

    #[test]
    fn ccm_tag_mismatch_rejected() {
        let key = [0x11u8; 16];
        let nonce = [0x22u8; 13];
        let aad = b"header";
        let mode = Ccm::<_, 12>::new(Aes128::new(&key));
        let mut data = *b"ccm plaintext data";
        let tag = mode.encrypt(&nonce, aad, &mut data);
        let ciphertext = data;

        let mut bad_tag = tag;
        bad_tag[0] ^= 0x80;
        assert!(!mode.decrypt(&nonce, aad, &mut data, &bad_tag));
        assert_eq!(data, ciphertext);

        assert!(mode.decrypt(&nonce, aad, &mut data, &tag));
        assert_eq!(data, *b"ccm plaintext data");
    }

    #[test]
    fn ccm_aes128_cavp_tlen_4_vector() {
        // NIST CAVP VTT128.rsp, [Tlen = 4], Count = 0.
        let key = parse::<16>("43b1a6bc8d0d22d6d1ca95c18593cca5");
        let nonce = parse::<13>("9882578e750b9682c6ca7f8f86");
        let aad = parse::<32>("2084f3861c9ad0ccee7c63a7e05aece5db8b34bd8724cc06b4ca99a7f9c4914f");
        let mut msg = parse::<24>("a2b381c7d1545c408fe29817a21dc435a154c87256346b05");
        let expected_ct = parse::<24>("cc69ed76985e0ed4c8365a72775e5a19bfccc71aeb116c85");
        let expected_tag = parse::<4>("a8c74677");

        let mode = Ccm::<_, 4>::new(Aes128::new(&key));
        let tag = mode.encrypt(&nonce, &aad, &mut msg);
        assert_eq!(msg, expected_ct);
        assert_eq!(tag, expected_tag);
        assert!(mode.decrypt(&nonce, &aad, &mut msg, &tag));
        assert_eq!(
            msg,
            parse::<24>("a2b381c7d1545c408fe29817a21dc435a154c87256346b05")
        );
    }

    #[test]
    fn ccm_aes128_cavp_tlen_16_vector() {
        // NIST CAVP VTT128.rsp, [Tlen = 16], Count = 0.
        let key = parse::<16>("4189351b5caea375a0299e81c621bf43");
        let nonce = parse::<13>("48c0906930561e0ab0ef4cd972");
        let aad = parse::<32>("40a27c1d1e23ea3dbe8056b2774861a4a201cce49f19997d19206d8c8a343951");
        let mut msg = parse::<24>("4535d12b4377928a7c0a61c9f825a48671ea05910748c8ef");
        let expected_ct = parse::<24>("26c56961c035a7e452cce61bc6ee220d77b3f94d18fd10b6");
        let expected_tag = parse::<16>("d80e8bf80f4a46cab06d4313f0db9be9");

        let mode = Ccm::<_, 16>::new(Aes128::new(&key));
        let tag = mode.encrypt(&nonce, &aad, &mut msg);
        assert_eq!(msg, expected_ct);
        assert_eq!(tag, expected_tag);
        assert!(mode.decrypt(&nonce, &aad, &mut msg, &tag));
        assert_eq!(
            msg,
            parse::<24>("4535d12b4377928a7c0a61c9f825a48671ea05910748c8ef")
        );
    }

    #[test]
    fn aes_key_wrap_rfc3394_4_1() {
        let kek = parse::<16>("000102030405060708090A0B0C0D0E0F");
        let key_data = parse::<16>("00112233445566778899AABBCCDDEEFF");
        let expected = parse::<24>("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5");
        let kw = AesKeyWrap::new(Aes128::new(&kek));

        assert_eq!(kw.wrap_key(&key_data), Some(expected.to_vec()));
        assert_eq!(kw.unwrap_key(&expected), Some(key_data.to_vec()));
    }

    #[test]
    fn aes_key_wrap_rfc3394_4_2() {
        let kek = parse::<24>("000102030405060708090A0B0C0D0E0F1011121314151617");
        let key_data = parse::<16>("00112233445566778899AABBCCDDEEFF");
        let expected = parse::<24>("96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D");
        let kw = AesKeyWrap::new(Aes192::new(&kek));

        assert_eq!(kw.wrap_key(&key_data), Some(expected.to_vec()));
        assert_eq!(kw.unwrap_key(&expected), Some(key_data.to_vec()));
    }

    #[test]
    fn aes_key_wrap_rfc3394_4_3() {
        let kek = parse::<32>("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
        let key_data = parse::<16>("00112233445566778899AABBCCDDEEFF");
        let expected = parse::<24>("64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7");
        let kw = AesKeyWrap::new(Aes256::new(&kek));

        assert_eq!(kw.wrap_key(&key_data), Some(expected.to_vec()));
        assert_eq!(kw.unwrap_key(&expected), Some(key_data.to_vec()));
    }

    #[test]
    fn aes_key_wrap_rfc3394_4_4() {
        let kek = parse::<24>("000102030405060708090A0B0C0D0E0F1011121314151617");
        let key_data = parse::<24>("00112233445566778899AABBCCDDEEFF0001020304050607");
        let expected =
            parse::<32>("031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2");
        let kw = AesKeyWrap::new(Aes192::new(&kek));

        assert_eq!(kw.wrap_key(&key_data), Some(expected.to_vec()));
        assert_eq!(kw.unwrap_key(&expected), Some(key_data.to_vec()));
    }

    #[test]
    fn aes_key_wrap_rfc3394_4_5() {
        let kek = parse::<32>("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
        let key_data = parse::<24>("00112233445566778899AABBCCDDEEFF0001020304050607");
        let expected =
            parse::<32>("A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1");
        let kw = AesKeyWrap::new(Aes256::new(&kek));

        assert_eq!(kw.wrap_key(&key_data), Some(expected.to_vec()));
        assert_eq!(kw.unwrap_key(&expected), Some(key_data.to_vec()));
    }

    #[test]
    fn aes_key_wrap_rfc3394_4_6() {
        let kek = parse::<32>("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
        let key_data =
            parse::<32>("00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F");
        let expected = parse::<40>(
            "28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21",
        );
        let kw = AesKeyWrap::new(Aes256::new(&kek));

        assert_eq!(kw.wrap_key(&key_data), Some(expected.to_vec()));
        assert_eq!(kw.unwrap_key(&expected), Some(key_data.to_vec()));
    }

    #[test]
    fn aes_key_wrap_rejects_bad_lengths_and_tampering() {
        let kw = AesKeyWrap::new(Aes128::new(&[0u8; 16]));

        assert!(kw.wrap_key(&[]).is_none());
        assert!(kw.wrap_key(&[0u8; 8]).is_none());
        assert!(kw.wrap_key(&[0u8; 15]).is_none());
        assert!(kw.unwrap_key(&[]).is_none());
        assert!(kw.unwrap_key(&[0u8; 16]).is_none());

        let mut wrapped = kw.wrap_key(&[0u8; 16]).expect("wrap");
        wrapped[0] ^= 1;
        assert!(kw.unwrap_key(&wrapped).is_none());
    }
}
