Skip to main content

arcana/mac/
ctx.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Cédric Mesnil <cslashm@pm.me>
3
4//! Stateful streaming MAC context (init / update / sign / verify).
5//!
6//! Uniform `init / update / sign | verify` API across HMAC, CMAC,
7//! KMAC and GMAC. Designed in the same spirit as
8//! [`crate::cipher::ctx::Cipher`]: caller-provided output buffers,
9//! reusable across many messages by re-calling [`Mac::init`],
10//! constant-time tag comparison on `verify`, and Poly1305 left out
11//! on purpose because it is a one-time MAC and would be unsafe to
12//! expose behind a "init then reuse" object.
13//!
14//! # Cycle of life
15//!
16//! ```text
17//!   new(algo)
18//!         │
19//!         ▼
20//!   init(key)               (HMAC, CMAC, KMAC default)
21//!   init_kmac(key, custom)  (KMAC with customization string)
22//!   init_with_nonce(key, n) (GMAC)
23//!         │
24//!         ▼
25//!   update(data) ──┐
26//!         │   (0..N times)
27//!         ▼
28//!   sign(out)  or  verify(expected_tag)
29//! ```
30//!
31//! # Example: HMAC-SHA-256
32//!
33//! ```rust,ignore
34//! use arcana::mac::ctx::{Mac, Algorithm};
35//!
36//! let mut m = Mac::new(Algorithm::HmacSha256);
37//! m.init(b"secret-key").unwrap();
38//! m.update(b"hello, ").unwrap();
39//! m.update(b"world!").unwrap();
40//!
41//! let mut tag = [0u8; 32];
42//! let n = m.sign(&mut tag).unwrap();
43//! assert_eq!(n, 32);
44//! ```
45//!
46//! # Side-channel posture
47//!
48//! Per `arcana/doc/sca/countermeasures/hmac.rst`:
49//!
50//! | Family       | Threat                                                | Status     | Roadmap item                                   |
51//! |--------------|-------------------------------------------------------|------------|------------------------------------------------|
52//! | All families | Timing on tag verify                                  | implemented| `silentops::ct_eq` — constant-time tag compare |
53//! | HMAC-SHA-2   | **Carry-based DPA** (Belenky TCHES 2023/3)            | vulnerable | `T2-D` — first-order Boolean masking of SHA-2  |
54//! | HMAC-SHA-3   | DPA on Keccak (no addition → no CDPA)                 | low risk   | none scheduled (Keccak has no carry chain)     |
55//! | CMAC         | Inherits AES leak (cf. [`crate::cipher::aes`])        | vulnerable | `T1-A` (AES) → `T2-G` (masked AES); CMAC inherits |
56//! | KMAC         | DPA on Keccak                                         | low risk   | none scheduled                                 |
57//! | GMAC         | DPA on `GF(2^128)` GHASH multiplier                   | vulnerable | `T2-H` — CT carry-less multiplier              |
58//!
59//! ## ⚠ HMAC-SHA-2: Belenky et al. TCHES 2023/3 (CDPA)
60//!
61//! `belenky2023_cdpa_hmac_sha2` proves that **any** implementation
62//! of HMAC-SHA-2, even pure parallel hardware, leaks the secret
63//! key in 30 K – 275 K traces under Carry-based Differential
64//! Power Analysis. Software implementations leak even more
65//! easily because the SHA-2 additions are explicit instructions
66//! on a sequential pipeline.
67//!
68//! For deployments where the threat model includes a level-2
69//! attacker with EM / power probes (which is the baseline of any
70//! lab-class evaluation), **HMAC-SHA-2 keys in arcana must be
71//! assumed extractable until `T2-D` ships**.
72//!
73//! `T2-D` will land a `MaskedSha256` / `MaskedSha512` behind the
74//! `sca-protected` Cargo feature (mirroring quantica's masking
75//! convention). The masked variant is mathematically transparent
76//! (bit-identical output) and routes through this `Mac` ctx
77//! transparently when the feature is on.
78
79use crate::cipher::aes::Aes;
80use crate::cipher::des::TripleDes;
81use crate::cipher::modes::{gf128_mul, ghash_update};
82use crate::hash::ripemd160::Ripemd160;
83use crate::hash::sha1::Sha1;
84use crate::hash::sha3::{KeccakState, Sha3_256, Sha3_384, Sha3_512};
85use crate::hash::sha256::Sha256;
86use crate::hash::sha384::Sha384;
87use crate::hash::sha512::Sha512;
88use crate::{BlockCipher, Hasher};
89
90// ============================================================
91// Public enums
92// ============================================================
93
94/// MAC algorithm selector.
95#[derive(Clone, Copy, Debug, PartialEq, Eq)]
96pub enum Algorithm {
97    /// HMAC-SHA-1 (RFC 2104, FIPS 198-1). 20-byte tag. Legacy.
98    HmacSha1,
99    /// HMAC-SHA-256. 32-byte tag.
100    HmacSha256,
101    /// HMAC-SHA-384. 48-byte tag.
102    HmacSha384,
103    /// HMAC-SHA-512. 64-byte tag.
104    HmacSha512,
105    /// HMAC-SHA3-256. 32-byte tag.
106    HmacSha3_256,
107    /// HMAC-SHA3-384. 48-byte tag.
108    HmacSha3_384,
109    /// HMAC-SHA3-512. 64-byte tag.
110    HmacSha3_512,
111    /// HMAC-RIPEMD-160. 20-byte tag. Legacy.
112    HmacRipemd160,
113
114    /// CMAC-AES-128 (NIST SP 800-38B, RFC 4493). 16-byte tag, 16-byte key.
115    CmacAes128,
116    /// CMAC-AES-192. 16-byte tag, 24-byte key.
117    CmacAes192,
118    /// CMAC-AES-256. 16-byte tag, 32-byte key.
119    CmacAes256,
120    /// CMAC-TripleDES (NIST SP 800-38B). 8-byte tag, 24-byte key. Legacy.
121    CmacTripleDes,
122
123    /// KMAC128 (FIPS SP 800-185). Default tag = 32 bytes (256 bits).
124    Kmac128,
125    /// KMAC256 (FIPS SP 800-185). Default tag = 64 bytes (512 bits).
126    Kmac256,
127
128    /// GMAC-AES-128 (NIST SP 800-38D, GCM with empty plaintext).
129    /// 16-byte tag. Requires a 12-byte unique nonce per message.
130    GmacAes128,
131    /// GMAC-AES-192. 16-byte tag. Requires a 12-byte unique nonce per message.
132    GmacAes192,
133    /// GMAC-AES-256. 16-byte tag. Requires a 12-byte unique nonce per message.
134    GmacAes256,
135}
136
137/// Errors returned by [`Mac`].
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
139pub enum Error {
140    /// CMAC / GMAC: the supplied key length does not match the algorithm.
141    InvalidKeyLen,
142    /// GMAC: the supplied nonce length is not 12 bytes.
143    InvalidNonceLen,
144    /// `update`, `sign` or `verify` was called before [`Mac::init`].
145    NotInitialized,
146    /// `update`, `sign` or `verify` was called after a previous
147    /// `sign` / `verify` without re-initializing.
148    AlreadyFinalized,
149    /// `out` slice passed to `sign` is shorter than the algorithm's
150    /// tag length.
151    OutputBufferTooSmall {
152        /// Required output length in bytes.
153        needed: usize,
154    },
155    /// Wrong init variant for the selected algorithm
156    /// (e.g. `init_with_nonce` on HMAC, or `init` on GMAC).
157    WrongInitVariant,
158    /// `verify` failed: tag mismatch.
159    VerificationFailed,
160}
161
162// ============================================================
163// Internal per-algo state
164// ============================================================
165
166enum HmacState {
167    Sha1(Sha1),
168    Sha256(Sha256),
169    Sha384(Sha384),
170    Sha512(Sha512),
171    Sha3_256(Sha3_256),
172    Sha3_384(Sha3_384),
173    Sha3_512(Sha3_512),
174    Ripemd160(Ripemd160),
175}
176
177impl HmacState {
178    fn block_len(&self) -> usize {
179        match self {
180            HmacState::Sha1(_) => Sha1::BLOCK_LEN,
181            HmacState::Sha256(_) => Sha256::BLOCK_LEN,
182            HmacState::Sha384(_) => Sha384::BLOCK_LEN,
183            HmacState::Sha512(_) => Sha512::BLOCK_LEN,
184            HmacState::Sha3_256(_) => Sha3_256::BLOCK_LEN,
185            HmacState::Sha3_384(_) => Sha3_384::BLOCK_LEN,
186            HmacState::Sha3_512(_) => Sha3_512::BLOCK_LEN,
187            HmacState::Ripemd160(_) => Ripemd160::BLOCK_LEN,
188        }
189    }
190
191    // Kept for symmetry with the other per-variant accessors; not currently
192    // wired to a caller (the streaming ctx tracks output length elsewhere).
193    #[allow(dead_code)]
194    fn output_len(&self) -> usize {
195        match self {
196            HmacState::Sha1(_) => Sha1::OUTPUT_LEN,
197            HmacState::Sha256(_) => Sha256::OUTPUT_LEN,
198            HmacState::Sha384(_) => Sha384::OUTPUT_LEN,
199            HmacState::Sha512(_) => Sha512::OUTPUT_LEN,
200            HmacState::Sha3_256(_) => Sha3_256::OUTPUT_LEN,
201            HmacState::Sha3_384(_) => Sha3_384::OUTPUT_LEN,
202            HmacState::Sha3_512(_) => Sha3_512::OUTPUT_LEN,
203            HmacState::Ripemd160(_) => Ripemd160::OUTPUT_LEN,
204        }
205    }
206
207    fn update(&mut self, data: &[u8]) {
208        match self {
209            HmacState::Sha1(h) => h.update(data),
210            HmacState::Sha256(h) => h.update(data),
211            HmacState::Sha384(h) => h.update(data),
212            HmacState::Sha512(h) => h.update(data),
213            HmacState::Sha3_256(h) => h.update(data),
214            HmacState::Sha3_384(h) => h.update(data),
215            HmacState::Sha3_512(h) => h.update(data),
216            HmacState::Ripemd160(h) => h.update(data),
217        }
218    }
219
220    /// Hash a key down to its native digest length (used by HMAC when
221    /// the user-supplied key is longer than the block size).
222    fn hash_key(&self, key: &[u8]) -> Vec<u8> {
223        match self {
224            HmacState::Sha1(_) => Sha1::hash(key),
225            HmacState::Sha256(_) => Sha256::hash(key),
226            HmacState::Sha384(_) => Sha384::hash(key),
227            HmacState::Sha512(_) => Sha512::hash(key),
228            HmacState::Sha3_256(_) => Sha3_256::hash(key),
229            HmacState::Sha3_384(_) => Sha3_384::hash(key),
230            HmacState::Sha3_512(_) => Sha3_512::hash(key),
231            HmacState::Ripemd160(_) => Ripemd160::hash(key),
232        }
233    }
234
235    /// Finalize the inner hash and return its digest as a Vec.
236    fn finalize_inner(self) -> Vec<u8> {
237        match self {
238            HmacState::Sha1(h) => h.finalize(),
239            HmacState::Sha256(h) => h.finalize(),
240            HmacState::Sha384(h) => h.finalize(),
241            HmacState::Sha512(h) => h.finalize(),
242            HmacState::Sha3_256(h) => h.finalize(),
243            HmacState::Sha3_384(h) => h.finalize(),
244            HmacState::Sha3_512(h) => h.finalize(),
245            HmacState::Ripemd160(h) => h.finalize(),
246        }
247    }
248
249    /// One-shot inner hash of `(opad-key || inner_digest)` to produce
250    /// the final HMAC tag, returned as a Vec.
251    fn outer_hash(&self, opad_key: &[u8], inner_digest: &[u8]) -> Vec<u8> {
252        match self {
253            HmacState::Sha1(_) => {
254                let mut h = Sha1::new();
255                h.update(opad_key);
256                h.update(inner_digest);
257                h.finalize()
258            }
259            HmacState::Sha256(_) => {
260                let mut h = Sha256::new();
261                h.update(opad_key);
262                h.update(inner_digest);
263                h.finalize()
264            }
265            HmacState::Sha384(_) => {
266                let mut h = Sha384::new();
267                h.update(opad_key);
268                h.update(inner_digest);
269                h.finalize()
270            }
271            HmacState::Sha512(_) => {
272                let mut h = Sha512::new();
273                h.update(opad_key);
274                h.update(inner_digest);
275                h.finalize()
276            }
277            HmacState::Sha3_256(_) => {
278                let mut h = Sha3_256::new();
279                h.update(opad_key);
280                h.update(inner_digest);
281                h.finalize()
282            }
283            HmacState::Sha3_384(_) => {
284                let mut h = Sha3_384::new();
285                h.update(opad_key);
286                h.update(inner_digest);
287                h.finalize()
288            }
289            HmacState::Sha3_512(_) => {
290                let mut h = Sha3_512::new();
291                h.update(opad_key);
292                h.update(inner_digest);
293                h.finalize()
294            }
295            HmacState::Ripemd160(_) => {
296                let mut h = Ripemd160::new();
297                h.update(opad_key);
298                h.update(inner_digest);
299                h.finalize()
300            }
301        }
302    }
303}
304
305/// Per-algorithm internal state.
306// The variants differ in size (the CMAC key schedules are large); the `Mac`
307// ctx is a short-lived stack object, so we keep the state inline rather than
308// boxing the large variant onto the heap. Documented allow (CLAUDE.md §6).
309#[allow(clippy::large_enum_variant)]
310enum Inner {
311    None,
312    Hmac {
313        /// Inner hash being fed (already absorbed `key XOR ipad`).
314        inner: HmacState,
315        /// Saved `key XOR opad` block, used at finalize time.
316        opad_key: Vec<u8>,
317    },
318    Cmac {
319        /// AES key schedule (None for 3DES).
320        aes: Option<Aes>,
321        /// 3DES key schedule (None for AES).
322        tdes: Option<TripleDes>,
323        block_len: usize,
324        /// Subkey K1 (CMAC).
325        k1: [u8; 16],
326        /// Subkey K2 (CMAC).
327        k2: [u8; 16],
328        /// Running CBC chaining state.
329        x: [u8; 16],
330        /// Look-ahead buffer of one block (we always keep the last
331        /// pending block back so we can XOR K1/K2 at finalize).
332        buf: [u8; 16],
333        buf_len: usize,
334    },
335    Kmac {
336        /// Underlying cSHAKE-flavoured Keccak sponge already absorbed
337        /// with the prefix and the bytepad'd key.
338        sponge: KeccakState,
339        /// Default output length in bytes (32 for KMAC128, 64 for KMAC256).
340        out_len: usize,
341    },
342    Gmac {
343        /// AES key schedule.
344        aes: Aes,
345        /// Hash subkey H = E(K, 0^128).
346        h: [u8; 16],
347        /// J0 = nonce || 0x00000001 (the precomputed E(K, J0) is the
348        /// final XOR mask used at finalize time).
349        e_j0: [u8; 16],
350        /// Running GHASH accumulator.
351        y: [u8; 16],
352        /// Reliquat for partial input blocks.
353        buf: [u8; 16],
354        buf_len: usize,
355        /// Total bytes absorbed (used to encode len(C) in the
356        /// trailing GHASH length block).
357        total_len: u64,
358    },
359}
360
361// ============================================================
362// MAC object
363// ============================================================
364
365/// Stateful MAC context.
366///
367/// See the [module-level documentation](self) for the cycle of life
368/// and the buffer ownership model.
369pub struct Mac {
370    algo: Algorithm,
371    inner: Inner,
372    initialized: bool,
373    finalized: bool,
374}
375
376impl Mac {
377    /// Create a new MAC context for the given algorithm. The actual
378    /// key (and nonce / customization string for GMAC / KMAC) is
379    /// loaded later by [`Self::init`], [`Self::init_kmac`] or
380    /// [`Self::init_with_nonce`].
381    pub fn new(algo: Algorithm) -> Self {
382        Self {
383            algo,
384            inner: Inner::None,
385            initialized: false,
386            finalized: false,
387        }
388    }
389
390    /// Initialize the MAC with a key. This is the right entry point
391    /// for HMAC, CMAC and KMAC (with an empty customization string).
392    /// For GMAC, use [`Self::init_with_nonce`] instead.
393    ///
394    /// HMAC accepts arbitrary key lengths per RFC 2104 (the key is
395    /// hashed if it exceeds the hash block size and zero-padded
396    /// otherwise). CMAC requires the exact key length for the chosen
397    /// cipher. KMAC accepts arbitrary key lengths.
398    ///
399    /// # Errors
400    ///
401    /// - [`Error::InvalidKeyLen`] for CMAC if the length is wrong.
402    /// - [`Error::WrongInitVariant`] if called on GMAC.
403    pub fn init(&mut self, key: &[u8]) -> Result<(), Error> {
404        match self.algo {
405            Algorithm::GmacAes128 | Algorithm::GmacAes192 | Algorithm::GmacAes256 => Err(Error::WrongInitVariant),
406            Algorithm::Kmac128 | Algorithm::Kmac256 => self.init_kmac(key, &[]),
407            _ => self.init_internal(key, None, None),
408        }
409    }
410
411    /// Initialize KMAC with a key and a customization string.
412    ///
413    /// # Errors
414    ///
415    /// [`Error::WrongInitVariant`] if called on a non-KMAC algorithm.
416    pub fn init_kmac(&mut self, key: &[u8], custom: &[u8]) -> Result<(), Error> {
417        match self.algo {
418            Algorithm::Kmac128 | Algorithm::Kmac256 => self.init_internal(key, None, Some(custom)),
419            _ => Err(Error::WrongInitVariant),
420        }
421    }
422
423    /// Initialize GMAC with a key and a 12-byte nonce. The nonce
424    /// **must** be unique per message under a given key (reusing it
425    /// breaks GMAC catastrophically).
426    ///
427    /// # Errors
428    ///
429    /// - [`Error::WrongInitVariant`] if called on a non-GMAC algorithm.
430    /// - [`Error::InvalidKeyLen`] if the key length does not match.
431    /// - [`Error::InvalidNonceLen`] if the nonce is not 12 bytes.
432    pub fn init_with_nonce(&mut self, key: &[u8], nonce: &[u8]) -> Result<(), Error> {
433        match self.algo {
434            Algorithm::GmacAes128 | Algorithm::GmacAes192 | Algorithm::GmacAes256 => {
435                if nonce.len() != 12 {
436                    return Err(Error::InvalidNonceLen);
437                }
438                self.init_internal(key, Some(nonce), None)
439            }
440            _ => Err(Error::WrongInitVariant),
441        }
442    }
443
444    fn init_internal(&mut self, key: &[u8], nonce: Option<&[u8]>, custom: Option<&[u8]>) -> Result<(), Error> {
445        self.inner = match self.algo {
446            // ---- HMAC ----
447            Algorithm::HmacSha1
448            | Algorithm::HmacSha256
449            | Algorithm::HmacSha384
450            | Algorithm::HmacSha512
451            | Algorithm::HmacSha3_256
452            | Algorithm::HmacSha3_384
453            | Algorithm::HmacSha3_512
454            | Algorithm::HmacRipemd160 => {
455                let mut state = match self.algo {
456                    Algorithm::HmacSha1 => HmacState::Sha1(Sha1::new()),
457                    Algorithm::HmacSha256 => HmacState::Sha256(Sha256::new()),
458                    Algorithm::HmacSha384 => HmacState::Sha384(Sha384::new()),
459                    Algorithm::HmacSha512 => HmacState::Sha512(Sha512::new()),
460                    Algorithm::HmacSha3_256 => HmacState::Sha3_256(Sha3_256::new()),
461                    Algorithm::HmacSha3_384 => HmacState::Sha3_384(Sha3_384::new()),
462                    Algorithm::HmacSha3_512 => HmacState::Sha3_512(Sha3_512::new()),
463                    Algorithm::HmacRipemd160 => HmacState::Ripemd160(Ripemd160::new()),
464                    _ => unreachable!(),
465                };
466                let block_len = state.block_len();
467
468                // RFC 2104: if key is longer than block_len, hash it down.
469                let mut key_block = vec![0u8; block_len];
470                if key.len() > block_len {
471                    let hashed = state.hash_key(key);
472                    key_block[..hashed.len()].copy_from_slice(&hashed);
473                } else {
474                    key_block[..key.len()].copy_from_slice(key);
475                }
476
477                // ipad / opad keys.
478                let mut ipad_key = key_block.clone();
479                let mut opad_key = key_block;
480                for b in &mut ipad_key {
481                    *b ^= 0x36;
482                }
483                for b in &mut opad_key {
484                    *b ^= 0x5C;
485                }
486
487                state.update(&ipad_key);
488                Inner::Hmac { inner: state, opad_key }
489            }
490
491            // ---- CMAC ----
492            Algorithm::CmacAes128 | Algorithm::CmacAes192 | Algorithm::CmacAes256 | Algorithm::CmacTripleDes => {
493                let expected_key_len = match self.algo {
494                    Algorithm::CmacAes128 => 16,
495                    Algorithm::CmacAes192 => 24,
496                    Algorithm::CmacAes256 => 32,
497                    Algorithm::CmacTripleDes => 24,
498                    _ => unreachable!(),
499                };
500                if key.len() != expected_key_len {
501                    return Err(Error::InvalidKeyLen);
502                }
503
504                let (aes, tdes, block_len) = match self.algo {
505                    Algorithm::CmacAes128 | Algorithm::CmacAes192 | Algorithm::CmacAes256 => {
506                        (Some(Aes::new(key)), None, 16usize)
507                    }
508                    Algorithm::CmacTripleDes => (None, Some(TripleDes::new(key)), 8usize),
509                    _ => unreachable!(),
510                };
511
512                // Compute K1, K2 per RFC 4493 / NIST SP 800-38B.
513                let mut l = [0u8; 16];
514                if let Some(ref c) = aes {
515                    c.encrypt_block(&mut l[..16]);
516                } else if let Some(ref c) = tdes {
517                    c.encrypt_block(&mut l[..8]);
518                }
519                let k1 = cmac_double(&l[..block_len]);
520                let k2 = cmac_double(&k1[..block_len]);
521
522                let mut k1_arr = [0u8; 16];
523                let mut k2_arr = [0u8; 16];
524                k1_arr[..block_len].copy_from_slice(&k1[..block_len]);
525                k2_arr[..block_len].copy_from_slice(&k2[..block_len]);
526
527                Inner::Cmac {
528                    aes,
529                    tdes,
530                    block_len,
531                    k1: k1_arr,
532                    k2: k2_arr,
533                    x: [0u8; 16],
534                    buf: [0u8; 16],
535                    buf_len: 0,
536                }
537            }
538
539            // ---- KMAC ----
540            Algorithm::Kmac128 | Algorithm::Kmac256 => {
541                let (rate, out_len) = match self.algo {
542                    Algorithm::Kmac128 => (168usize, 32usize),
543                    Algorithm::Kmac256 => (136usize, 64usize),
544                    _ => unreachable!(),
545                };
546                // cSHAKE suffix is 0x04 (KMAC uses cSHAKE under the hood).
547                let mut sponge = KeccakState::new(rate, 0x04);
548
549                // cSHAKE prefix: bytepad(encode_string("KMAC") || encode_string(custom), rate).
550                let custom = custom.unwrap_or(&[]);
551                let mut prefix = Vec::new();
552                append_encode_string(&mut prefix, b"KMAC");
553                append_encode_string(&mut prefix, custom);
554                bytepad_to(&mut prefix, rate);
555                sponge.absorb(&prefix);
556
557                // KMAC key encoding: bytepad(encode_string(K), rate).
558                let mut keyenc = Vec::new();
559                append_encode_string(&mut keyenc, key);
560                bytepad_to(&mut keyenc, rate);
561                sponge.absorb(&keyenc);
562
563                Inner::Kmac { sponge, out_len }
564            }
565
566            // ---- GMAC ----
567            Algorithm::GmacAes128 | Algorithm::GmacAes192 | Algorithm::GmacAes256 => {
568                let expected_key_len = match self.algo {
569                    Algorithm::GmacAes128 => 16,
570                    Algorithm::GmacAes192 => 24,
571                    Algorithm::GmacAes256 => 32,
572                    _ => unreachable!(),
573                };
574                if key.len() != expected_key_len {
575                    return Err(Error::InvalidKeyLen);
576                }
577                let nonce = nonce.expect("init_with_nonce path");
578                debug_assert_eq!(nonce.len(), 12);
579
580                let aes = Aes::new(key);
581
582                // H = E(K, 0^128)
583                let mut h = [0u8; 16];
584                aes.encrypt_block(&mut h);
585
586                // J0 = nonce || 0x00000001
587                let mut j0 = [0u8; 16];
588                j0[..12].copy_from_slice(nonce);
589                j0[15] = 1;
590                let mut e_j0 = j0;
591                aes.encrypt_block(&mut e_j0);
592
593                Inner::Gmac {
594                    aes,
595                    h,
596                    e_j0,
597                    y: [0u8; 16],
598                    buf: [0u8; 16],
599                    buf_len: 0,
600                    total_len: 0,
601                }
602            }
603        };
604        self.initialized = true;
605        self.finalized = false;
606        Ok(())
607    }
608
609    /// Tag length in bytes for this MAC algorithm.
610    pub const fn tag_len(&self) -> usize {
611        match self.algo {
612            Algorithm::HmacSha1 | Algorithm::HmacRipemd160 => 20,
613            Algorithm::HmacSha256 | Algorithm::HmacSha3_256 => 32,
614            Algorithm::HmacSha384 | Algorithm::HmacSha3_384 => 48,
615            Algorithm::HmacSha512 | Algorithm::HmacSha3_512 => 64,
616            Algorithm::CmacAes128 | Algorithm::CmacAes192 | Algorithm::CmacAes256 => 16,
617            Algorithm::CmacTripleDes => 8,
618            Algorithm::Kmac128 => 32,
619            Algorithm::Kmac256 => 64,
620            Algorithm::GmacAes128 | Algorithm::GmacAes192 | Algorithm::GmacAes256 => 16,
621        }
622    }
623
624    /// Feed `data` into the MAC. May be called any number of times
625    /// between `init` and `sign` / `verify`. Empty slices are allowed.
626    pub fn update(&mut self, data: &[u8]) -> Result<(), Error> {
627        if !self.initialized {
628            return Err(Error::NotInitialized);
629        }
630        if self.finalized {
631            return Err(Error::AlreadyFinalized);
632        }
633        match &mut self.inner {
634            Inner::None => unreachable!(),
635            Inner::Hmac { inner, .. } => {
636                inner.update(data);
637            }
638            Inner::Cmac {
639                aes,
640                tdes,
641                block_len,
642                buf,
643                buf_len,
644                x,
645                ..
646            } => {
647                let bs = *block_len;
648                let mut pos = 0;
649                while pos < data.len() {
650                    // Fill the look-ahead buffer first.
651                    let space = bs - *buf_len;
652                    let take = space.min(data.len() - pos);
653                    buf[*buf_len..*buf_len + take].copy_from_slice(&data[pos..pos + take]);
654                    *buf_len += take;
655                    pos += take;
656
657                    // Drain a full block ONLY if more data follows
658                    // (we must keep the last block for finalize).
659                    if *buf_len == bs && pos < data.len() {
660                        for i in 0..bs {
661                            x[i] ^= buf[i];
662                        }
663                        if let Some(c) = aes.as_ref() {
664                            c.encrypt_block(&mut x[..16]);
665                        } else if let Some(c) = tdes.as_ref() {
666                            c.encrypt_block(&mut x[..8]);
667                        }
668                        *buf_len = 0;
669                    }
670                }
671            }
672            Inner::Kmac { sponge, .. } => {
673                sponge.absorb(data);
674            }
675            Inner::Gmac {
676                h,
677                y,
678                buf,
679                buf_len,
680                total_len,
681                ..
682            } => {
683                *total_len += data.len() as u64;
684                let mut pos = 0;
685                while pos < data.len() {
686                    let space = 16 - *buf_len;
687                    let take = space.min(data.len() - pos);
688                    buf[*buf_len..*buf_len + take].copy_from_slice(&data[pos..pos + take]);
689                    *buf_len += take;
690                    pos += take;
691                    if *buf_len == 16 {
692                        for i in 0..16 {
693                            y[i] ^= buf[i];
694                        }
695                        *y = gf128_mul(y, h);
696                        *buf_len = 0;
697                    }
698                }
699            }
700        }
701        Ok(())
702    }
703
704    /// Finalize the MAC and write the tag into `out`. Returns the
705    /// number of bytes written, which is always [`Self::tag_len`].
706    /// After this call the context is in the finalized state and
707    /// must be re-initialized before further use.
708    ///
709    /// # Errors
710    ///
711    /// - [`Error::NotInitialized`] / [`Error::AlreadyFinalized`].
712    /// - [`Error::OutputBufferTooSmall`] if `out.len() < tag_len()`.
713    pub fn sign(&mut self, out: &mut [u8]) -> Result<usize, Error> {
714        if !self.initialized {
715            return Err(Error::NotInitialized);
716        }
717        if self.finalized {
718            return Err(Error::AlreadyFinalized);
719        }
720        let tlen = self.tag_len();
721        if out.len() < tlen {
722            return Err(Error::OutputBufferTooSmall { needed: tlen });
723        }
724
725        // Replace inner with `None` so we can consume the state.
726        let inner = core::mem::replace(&mut self.inner, Inner::None);
727
728        match inner {
729            Inner::None => unreachable!(),
730            Inner::Hmac { inner, opad_key } => {
731                let inner_digest = inner.finalize_inner();
732                // We need a fresh outer hash. Build a temporary
733                // HmacState of the same shape just to get the right
734                // outer-hash dispatch — easier with a small helper.
735                let outer = match self.algo {
736                    Algorithm::HmacSha1 => HmacState::Sha1(Sha1::new()),
737                    Algorithm::HmacSha256 => HmacState::Sha256(Sha256::new()),
738                    Algorithm::HmacSha384 => HmacState::Sha384(Sha384::new()),
739                    Algorithm::HmacSha512 => HmacState::Sha512(Sha512::new()),
740                    Algorithm::HmacSha3_256 => HmacState::Sha3_256(Sha3_256::new()),
741                    Algorithm::HmacSha3_384 => HmacState::Sha3_384(Sha3_384::new()),
742                    Algorithm::HmacSha3_512 => HmacState::Sha3_512(Sha3_512::new()),
743                    Algorithm::HmacRipemd160 => HmacState::Ripemd160(Ripemd160::new()),
744                    _ => unreachable!(),
745                };
746                let tag = outer.outer_hash(&opad_key, &inner_digest);
747                out[..tlen].copy_from_slice(&tag[..tlen]);
748            }
749            Inner::Cmac {
750                aes,
751                tdes,
752                block_len,
753                k1,
754                k2,
755                mut x,
756                mut buf,
757                buf_len,
758            } => {
759                let bs = block_len;
760                // Last block: pad if needed and XOR with K1 / K2.
761                if buf_len == bs {
762                    for i in 0..bs {
763                        buf[i] ^= k1[i];
764                    }
765                } else {
766                    buf[buf_len] = 0x80;
767                    for b in &mut buf[buf_len + 1..bs] {
768                        *b = 0x00;
769                    }
770                    for i in 0..bs {
771                        buf[i] ^= k2[i];
772                    }
773                }
774                for i in 0..bs {
775                    x[i] ^= buf[i];
776                }
777                if let Some(c) = aes.as_ref() {
778                    c.encrypt_block(&mut x[..16]);
779                } else if let Some(c) = tdes.as_ref() {
780                    c.encrypt_block(&mut x[..8]);
781                }
782                out[..tlen].copy_from_slice(&x[..tlen]);
783            }
784            Inner::Kmac { mut sponge, out_len } => {
785                // KMAC: append right_encode(L) where L = output length in bits.
786                let trailer = right_encode((out_len * 8) as u64);
787                sponge.absorb(&trailer);
788                sponge.squeeze(&mut out[..tlen]);
789            }
790            Inner::Gmac {
791                aes: _aes,
792                h,
793                e_j0,
794                mut y,
795                buf,
796                buf_len,
797                total_len,
798            } => {
799                // Absorb partial trailing block (zero-padded).
800                if buf_len > 0 {
801                    let mut last = [0u8; 16];
802                    last[..buf_len].copy_from_slice(&buf[..buf_len]);
803                    for i in 0..16 {
804                        y[i] ^= last[i];
805                    }
806                    y = gf128_mul(&y, &h);
807                }
808                // Length block: len(A=msg) || len(C=0), each 64-bit BE in bits.
809                let mut len_block = [0u8; 16];
810                let a_bits = total_len * 8;
811                len_block[..8].copy_from_slice(&a_bits.to_be_bytes());
812                // C bits = 0
813                for i in 0..16 {
814                    y[i] ^= len_block[i];
815                }
816                y = gf128_mul(&y, &h);
817                // Tag = E(K, J0) XOR y.
818                let mut tag = [0u8; 16];
819                for i in 0..16 {
820                    tag[i] = y[i] ^ e_j0[i];
821                }
822                out[..tlen].copy_from_slice(&tag[..tlen]);
823                // Drop _aes by going out of scope.
824                let _ = ghash_update;
825            }
826        }
827
828        self.finalized = true;
829        Ok(tlen)
830    }
831
832    /// Finalize and verify the tag against `expected_tag` in
833    /// constant time. Supports tag truncation: `expected_tag.len()`
834    /// may be **less than or equal to** `tag_len()`. Comparison is
835    /// XOR-accumulate-then-test, with no early exit.
836    ///
837    /// # Errors
838    ///
839    /// - [`Error::NotInitialized`] / [`Error::AlreadyFinalized`].
840    /// - [`Error::VerificationFailed`] if the tag does not match (or
841    ///   `expected_tag.len() == 0` or `> tag_len()`).
842    pub fn verify(&mut self, expected_tag: &[u8]) -> Result<(), Error> {
843        let tlen = self.tag_len();
844        if expected_tag.is_empty() || expected_tag.len() > tlen {
845            // Still consume the state so the object is left finalized.
846            let mut sink = vec![0u8; tlen];
847            let _ = self.sign(&mut sink);
848            return Err(Error::VerificationFailed);
849        }
850        let mut full = vec![0u8; tlen];
851        self.sign(&mut full)?;
852
853        // Constant-time compare on the truncated prefix.
854        let mut diff = 0u8;
855        for i in 0..expected_tag.len() {
856            diff |= full[i] ^ expected_tag[i];
857        }
858        if diff == 0 {
859            Ok(())
860        } else {
861            Err(Error::VerificationFailed)
862        }
863    }
864
865    /// Allocating helper around [`Self::sign`].
866    pub fn sign_to_vec(&mut self) -> Result<Vec<u8>, Error> {
867        let tlen = self.tag_len();
868        let mut out = vec![0u8; tlen];
869        self.sign(&mut out)?;
870        Ok(out)
871    }
872}
873
874// ============================================================
875// CMAC subkey derivation
876// ============================================================
877
878/// CMAC subkey doubling: shift left by 1 bit; if MSB was 1, XOR with
879/// the irreducible polynomial constant for the block size.
880/// Rb = 0x87 for 16-byte blocks (AES), 0x1B for 8-byte blocks (DES/3DES).
881fn cmac_double(input: &[u8]) -> [u8; 16] {
882    let bs = input.len();
883    debug_assert!(bs == 8 || bs == 16);
884    let rb: u8 = if bs == 16 { 0x87 } else { 0x1B };
885    let mut out = [0u8; 16];
886    let mut carry: u8 = 0;
887    for i in (0..bs).rev() {
888        let v = input[i];
889        out[i] = (v << 1) | carry;
890        carry = (v >> 7) & 1;
891    }
892    if (input[0] & 0x80) != 0 {
893        out[bs - 1] ^= rb;
894    }
895    out
896}
897
898// ============================================================
899// KMAC / cSHAKE encoding helpers (FIPS SP 800-185 §2.3)
900// ============================================================
901
902/// `left_encode(x)`: prefix x's big-endian byte representation with
903/// the number of bytes used.
904fn left_encode(x: u64) -> Vec<u8> {
905    let mut bytes = Vec::new();
906    let be = x.to_be_bytes();
907    // Skip leading zeros, but keep at least one byte (encoding of 0
908    // is 0x01 0x00).
909    let first = be.iter().position(|&b| b != 0).unwrap_or(7);
910    let n = (8 - first) as u8;
911    bytes.push(n);
912    bytes.extend_from_slice(&be[first..]);
913    bytes
914}
915
916/// `right_encode(x)`: append the number-of-bytes byte after the
917/// big-endian representation.
918fn right_encode(x: u64) -> Vec<u8> {
919    let mut bytes = Vec::new();
920    let be = x.to_be_bytes();
921    let first = be.iter().position(|&b| b != 0).unwrap_or(7);
922    let n = (8 - first) as u8;
923    bytes.extend_from_slice(&be[first..]);
924    bytes.push(n);
925    bytes
926}
927
928/// `encode_string(S)` = `left_encode(len_in_bits(S)) || S`.
929fn append_encode_string(out: &mut Vec<u8>, s: &[u8]) {
930    out.extend_from_slice(&left_encode((s.len() as u64) * 8));
931    out.extend_from_slice(s);
932}
933
934/// `bytepad(X, w)`: prefix `left_encode(w)`, then append zero bytes
935/// until the total length is a multiple of `w`.
936fn bytepad_to(buf: &mut Vec<u8>, w: usize) {
937    // bytepad's prefix `left_encode(w)` must be the very first thing
938    // before X. Our callers build X first and then call bytepad_to,
939    // so we splice the prefix in at index 0.
940    let prefix = left_encode(w as u64);
941    let mut full = Vec::with_capacity(prefix.len() + buf.len());
942    full.extend_from_slice(&prefix);
943    full.extend_from_slice(buf);
944    let pad = (w - (full.len() % w)) % w;
945    full.extend(std::iter::repeat_n(0u8, pad));
946    *buf = full;
947}
948
949// ============================================================
950// Tests
951// ============================================================
952
953#[cfg(test)]
954mod tests {
955    use super::*;
956
957    fn hex(s: &str) -> Vec<u8> {
958        let s: String = s.chars().filter(|c| !c.is_whitespace()).collect();
959        (0..s.len())
960            .step_by(2)
961            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
962            .collect()
963    }
964
965    // ---------- HMAC-SHA-256 RFC 4231 Test Case 1 ----------
966
967    #[test]
968    fn hmac_sha256_rfc4231_tc1() {
969        let key = hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
970        let data = b"Hi There";
971        let expected = hex("b0344c61d8db38535ca8afceaf0bf12b\
972             881dc200c9833da726e9376c2e32cff7");
973
974        let mut m = Mac::new(Algorithm::HmacSha256);
975        m.init(&key).unwrap();
976        m.update(data).unwrap();
977        let tag = m.sign_to_vec().unwrap();
978        assert_eq!(tag, expected);
979    }
980
981    #[test]
982    fn hmac_sha256_rfc4231_tc2_streaming() {
983        // Key = "Jefe", data = "what do ya want for nothing?"
984        let key = b"Jefe";
985        let expected = hex("5bdcc146bf60754e6a042426089575c7\
986             5a003f089d2739839dec58b964ec3843");
987        let data = b"what do ya want for nothing?";
988
989        let mut m = Mac::new(Algorithm::HmacSha256);
990        m.init(key).unwrap();
991        for chunk in data.chunks(3) {
992            m.update(chunk).unwrap();
993        }
994        let tag = m.sign_to_vec().unwrap();
995        assert_eq!(tag, expected);
996    }
997
998    #[test]
999    fn hmac_sha256_long_key_rfc4231_tc6() {
1000        // 131-byte key (longer than 64-byte block) → must be hashed first.
1001        let key = vec![0xaau8; 131];
1002        let data = b"Test Using Larger Than Block-Size Key - Hash Key First";
1003        let expected = hex("60e431591ee0b67f0d8a26aacbf5b77f\
1004             8e0bc6213728c5140546040f0ee37f54");
1005
1006        let mut m = Mac::new(Algorithm::HmacSha256);
1007        m.init(&key).unwrap();
1008        m.update(data).unwrap();
1009        let tag = m.sign_to_vec().unwrap();
1010        assert_eq!(tag, expected);
1011    }
1012
1013    #[test]
1014    fn hmac_sha512_rfc4231_tc1() {
1015        let key = hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
1016        let data = b"Hi There";
1017        let expected = hex("87aa7cdea5ef619d4ff0b4241a1d6cb0\
1018             2379f4e2ce4ec2787ad0b30545e17cde\
1019             daa833b7d6b8a702038b274eaea3f4e4\
1020             be9d914eeb61f1702e696c203a126854");
1021
1022        let mut m = Mac::new(Algorithm::HmacSha512);
1023        m.init(&key).unwrap();
1024        m.update(data).unwrap();
1025        assert_eq!(m.sign_to_vec().unwrap(), expected);
1026    }
1027
1028    #[test]
1029    fn hmac_sha1_rfc2202_tc1() {
1030        // RFC 2202 test case 1.
1031        let key = hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
1032        let data = b"Hi There";
1033        let expected = hex("b617318655057264e28bc0b6fb378c8ef146be00");
1034
1035        let mut m = Mac::new(Algorithm::HmacSha1);
1036        m.init(&key).unwrap();
1037        m.update(data).unwrap();
1038        assert_eq!(m.sign_to_vec().unwrap(), expected);
1039    }
1040
1041    // ---------- CMAC-AES-128 RFC 4493 ----------
1042    //
1043    // M = empty
1044    #[test]
1045    fn cmac_aes128_rfc4493_empty() {
1046        let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
1047        let expected = hex("bb1d6929e95937287fa37d129b756746");
1048
1049        let mut m = Mac::new(Algorithm::CmacAes128);
1050        m.init(&key).unwrap();
1051        m.update(b"").unwrap();
1052        assert_eq!(m.sign_to_vec().unwrap(), expected);
1053    }
1054
1055    // M = first 16 bytes (one full block)
1056    #[test]
1057    fn cmac_aes128_rfc4493_one_block() {
1058        let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
1059        let m_bytes = hex("6bc1bee22e409f96e93d7e117393172a");
1060        let expected = hex("070a16b46b4d4144f79bdd9dd04a287c");
1061
1062        let mut m = Mac::new(Algorithm::CmacAes128);
1063        m.init(&key).unwrap();
1064        m.update(&m_bytes).unwrap();
1065        assert_eq!(m.sign_to_vec().unwrap(), expected);
1066    }
1067
1068    // M = first 40 bytes (2.5 blocks)
1069    #[test]
1070    fn cmac_aes128_rfc4493_40bytes() {
1071        let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
1072        let m_bytes = hex("6bc1bee22e409f96e93d7e117393172a\
1073             ae2d8a571e03ac9c9eb76fac45af8e51\
1074             30c81c46a35ce411");
1075        let expected = hex("dfa66747de9ae63030ca32611497c827");
1076
1077        let mut m = Mac::new(Algorithm::CmacAes128);
1078        m.init(&key).unwrap();
1079        // Streaming.
1080        m.update(&m_bytes[..7]).unwrap();
1081        m.update(&m_bytes[7..23]).unwrap();
1082        m.update(&m_bytes[23..]).unwrap();
1083        assert_eq!(m.sign_to_vec().unwrap(), expected);
1084    }
1085
1086    // M = first 64 bytes (4 full blocks)
1087    #[test]
1088    fn cmac_aes128_rfc4493_64bytes() {
1089        let key = hex("2b7e151628aed2a6abf7158809cf4f3c");
1090        let m_bytes = hex("6bc1bee22e409f96e93d7e117393172a\
1091             ae2d8a571e03ac9c9eb76fac45af8e51\
1092             30c81c46a35ce411e5fbc1191a0a52ef\
1093             f69f2445df4f9b17ad2b417be66c3710");
1094        let expected = hex("51f0bebf7e3b9d92fc49741779363cfe");
1095
1096        let mut m = Mac::new(Algorithm::CmacAes128);
1097        m.init(&key).unwrap();
1098        m.update(&m_bytes).unwrap();
1099        assert_eq!(m.sign_to_vec().unwrap(), expected);
1100    }
1101
1102    // ---------- KMAC128 / KMAC256 NIST sample vectors ----------
1103    //
1104    // FIPS SP 800-185, KMAC samples (Sample #1, #2, #3 for KMAC128).
1105
1106    #[test]
1107    fn kmac128_sample1() {
1108        // Key = 0x40..5F (32 bytes), Data = 0x00 0x01 0x02 0x03, S = "" (empty), L = 256
1109        let key = hex("404142434445464748494a4b4c4d4e4f\
1110             505152535455565758595a5b5c5d5e5f");
1111        let data = hex("00010203");
1112        let expected = hex("e5780b0d3ea6f7d3a429c5706aa43a00\
1113             fadbd7d49628839e3187243f456ee14e");
1114
1115        let mut m = Mac::new(Algorithm::Kmac128);
1116        m.init(&key).unwrap();
1117        m.update(&data).unwrap();
1118        assert_eq!(m.sign_to_vec().unwrap(), expected);
1119    }
1120
1121    #[test]
1122    fn kmac128_sample2_with_custom() {
1123        // Same key, same short data, S = "My Tagged Application", L = 256
1124        let key = hex("404142434445464748494a4b4c4d4e4f\
1125             505152535455565758595a5b5c5d5e5f");
1126        let data = hex("00010203");
1127        let custom = b"My Tagged Application";
1128        let expected = hex("3b1fba963cd8b0b59e8c1a6d71888b71\
1129             43651af8ba0a7070c0979e2811324aa5");
1130
1131        let mut m = Mac::new(Algorithm::Kmac128);
1132        m.init_kmac(&key, custom).unwrap();
1133        m.update(&data).unwrap();
1134        assert_eq!(m.sign_to_vec().unwrap(), expected);
1135    }
1136
1137    #[test]
1138    fn kmac256_sample4_with_custom() {
1139        // FIPS SP 800-185 KMAC256 Sample #4: same 32-byte key,
1140        // 4-byte data, S = "My Tagged Application", L = 512.
1141        let key = hex("404142434445464748494a4b4c4d4e4f\
1142             505152535455565758595a5b5c5d5e5f");
1143        let data = hex("00010203");
1144        let custom = b"My Tagged Application";
1145        let expected = hex("20c570c31346f703c9ac36c61c03cb64\
1146             c3970d0cfc787e9b79599d273a68d2f7\
1147             f69d4cc3de9d104a351689f27cf6f595\
1148             1f0103f33f4f24871024d9c27773a8dd");
1149
1150        let mut m = Mac::new(Algorithm::Kmac256);
1151        m.init_kmac(&key, custom).unwrap();
1152        m.update(&data).unwrap();
1153        assert_eq!(m.sign_to_vec().unwrap(), expected);
1154    }
1155
1156    // ---------- GMAC ----------
1157    //
1158    // GMAC = GCM with empty plaintext, AAD = message. Cross-validate
1159    // against the existing Gcm::encrypt path with empty plaintext.
1160    #[test]
1161    fn gmac_aes128_matches_gcm_empty_plaintext() {
1162        use crate::cipher::aes::Aes128;
1163        use crate::cipher::modes::Gcm;
1164        let key = hex("feffe9928665731c6d6a8f9467308308");
1165        let nonce_bytes = hex("cafebabefacedbaddecaf888");
1166        let nonce: [u8; 12] = nonce_bytes.clone().try_into().unwrap();
1167        let aad = hex("d9313225f88406e5a55909c5aff5269a\
1168             86a7a9531534f7da2e4c303d8a318a72\
1169             1c3c0c95956809532fcf0e2449a6b525");
1170
1171        let cipher = Aes128::new(&key);
1172        let (_ct, tag_ref) = Gcm::encrypt(&cipher, &nonce, &aad, &[]);
1173
1174        let mut m = Mac::new(Algorithm::GmacAes128);
1175        m.init_with_nonce(&key, &nonce_bytes).unwrap();
1176        // Streaming feed.
1177        for chunk in aad.chunks(7) {
1178            m.update(chunk).unwrap();
1179        }
1180        let tag = m.sign_to_vec().unwrap();
1181        assert_eq!(tag.len(), 16);
1182        assert_eq!(tag, tag_ref.to_vec());
1183    }
1184
1185    #[test]
1186    fn gmac_verify_truncated() {
1187        let key = hex("feffe9928665731c6d6a8f9467308308");
1188        let nonce = hex("cafebabefacedbaddecaf888");
1189        let aad = b"some authenticated data";
1190
1191        let mut signer = Mac::new(Algorithm::GmacAes128);
1192        signer.init_with_nonce(&key, &nonce).unwrap();
1193        signer.update(aad).unwrap();
1194        let full_tag = signer.sign_to_vec().unwrap();
1195
1196        // Truncate to 12 bytes (IPsec-style).
1197        let truncated = &full_tag[..12];
1198        let mut verifier = Mac::new(Algorithm::GmacAes128);
1199        verifier.init_with_nonce(&key, &nonce).unwrap();
1200        verifier.update(aad).unwrap();
1201        assert_eq!(verifier.verify(truncated), Ok(()));
1202
1203        // Tamper.
1204        let mut bad = full_tag.clone();
1205        bad[0] ^= 0xFF;
1206        let mut verifier = Mac::new(Algorithm::GmacAes128);
1207        verifier.init_with_nonce(&key, &nonce).unwrap();
1208        verifier.update(aad).unwrap();
1209        assert_eq!(verifier.verify(&bad[..12]), Err(Error::VerificationFailed));
1210    }
1211
1212    // ---------- Sign + verify round trip across all algos ----------
1213
1214    #[test]
1215    fn round_trip_all_hmac_and_cmac() {
1216        let key128 = [0x42u8; 16];
1217        let key192 = [0x42u8; 24];
1218        let key256 = [0x42u8; 32];
1219
1220        let cases: &[(Algorithm, &[u8])] = &[
1221            (Algorithm::HmacSha1, b"hmac-sha1-key-here"),
1222            (Algorithm::HmacSha256, b"hmac-sha256-key-here"),
1223            (Algorithm::HmacSha384, b"hmac-sha384-key-here"),
1224            (Algorithm::HmacSha512, b"hmac-sha512-key-here"),
1225            (Algorithm::HmacSha3_256, b"hmac-sha3-256-key"),
1226            (Algorithm::HmacSha3_384, b"hmac-sha3-384-key"),
1227            (Algorithm::HmacSha3_512, b"hmac-sha3-512-key"),
1228            (Algorithm::HmacRipemd160, b"hmac-ripemd160-key"),
1229            (Algorithm::CmacAes128, &key128),
1230            (Algorithm::CmacAes192, &key192),
1231            (Algorithm::CmacAes256, &key256),
1232            (Algorithm::CmacTripleDes, &key192),
1233        ];
1234
1235        for (algo, key) in cases {
1236            let mut signer = Mac::new(*algo);
1237            signer.init(key).unwrap();
1238            signer.update(b"the quick brown fox jumps over the lazy dog").unwrap();
1239            let tag = signer.sign_to_vec().unwrap();
1240
1241            let mut verifier = Mac::new(*algo);
1242            verifier.init(key).unwrap();
1243            verifier.update(b"the quick brown fox jumps over the lazy dog").unwrap();
1244            assert_eq!(verifier.verify(&tag), Ok(()), "{:?}", algo);
1245
1246            // Tamper rejected.
1247            let mut bad = tag.clone();
1248            bad[0] ^= 0x01;
1249            let mut verifier = Mac::new(*algo);
1250            verifier.init(key).unwrap();
1251            verifier.update(b"the quick brown fox jumps over the lazy dog").unwrap();
1252            assert_eq!(verifier.verify(&bad), Err(Error::VerificationFailed), "{:?}", algo);
1253        }
1254    }
1255
1256    // ---------- Error paths ----------
1257
1258    #[test]
1259    fn hmac_init_after_use_resets() {
1260        let mut m = Mac::new(Algorithm::HmacSha256);
1261        m.init(b"first key").unwrap();
1262        m.update(b"first message").unwrap();
1263        let _ = m.sign_to_vec().unwrap();
1264
1265        // Re-init for a fresh run.
1266        m.init(b"second key").unwrap();
1267        m.update(b"second message").unwrap();
1268        let tag = m.sign_to_vec().unwrap();
1269
1270        // Compare against an independent instance.
1271        let mut ref_m = Mac::new(Algorithm::HmacSha256);
1272        ref_m.init(b"second key").unwrap();
1273        ref_m.update(b"second message").unwrap();
1274        assert_eq!(tag, ref_m.sign_to_vec().unwrap());
1275    }
1276
1277    #[test]
1278    fn wrong_init_variant_rejected() {
1279        let mut m = Mac::new(Algorithm::GmacAes128);
1280        assert_eq!(m.init(&[0u8; 16]), Err(Error::WrongInitVariant));
1281
1282        let mut m = Mac::new(Algorithm::HmacSha256);
1283        assert_eq!(m.init_with_nonce(b"k", &[0u8; 12]), Err(Error::WrongInitVariant));
1284
1285        let mut m = Mac::new(Algorithm::HmacSha256);
1286        assert_eq!(m.init_kmac(b"k", b""), Err(Error::WrongInitVariant));
1287    }
1288
1289    #[test]
1290    fn cmac_invalid_key_len() {
1291        let mut m = Mac::new(Algorithm::CmacAes128);
1292        assert_eq!(m.init(&[0u8; 17]), Err(Error::InvalidKeyLen));
1293    }
1294
1295    #[test]
1296    fn gmac_invalid_nonce_len() {
1297        let mut m = Mac::new(Algorithm::GmacAes128);
1298        assert_eq!(m.init_with_nonce(&[0u8; 16], &[0u8; 11]), Err(Error::InvalidNonceLen));
1299    }
1300
1301    #[test]
1302    fn output_too_small() {
1303        let mut m = Mac::new(Algorithm::HmacSha256);
1304        m.init(b"k").unwrap();
1305        m.update(b"data").unwrap();
1306        let mut small = [0u8; 8];
1307        assert!(matches!(m.sign(&mut small), Err(Error::OutputBufferTooSmall { .. })));
1308    }
1309
1310    #[test]
1311    fn verify_rejects_empty_or_too_long() {
1312        let mut m = Mac::new(Algorithm::HmacSha256);
1313        m.init(b"k").unwrap();
1314        m.update(b"d").unwrap();
1315        assert_eq!(m.verify(&[]), Err(Error::VerificationFailed));
1316
1317        let mut m = Mac::new(Algorithm::HmacSha256);
1318        m.init(b"k").unwrap();
1319        m.update(b"d").unwrap();
1320        assert_eq!(m.verify(&[0u8; 33]), Err(Error::VerificationFailed));
1321    }
1322}