Skip to main content

arcana/mac/
ctx.rs

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