Skip to main content

aws_lc_rs/
cmac.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0 OR ISC
3
4//! CMAC is specified in [RFC 4493] and [NIST SP 800-38B].
5//!
6//! After a `Key` is constructed, it can be used for multiple signing or
7//! verification operations. Separating the construction of the key from the
8//! rest of the CMAC operation allows the per-key precomputation to be done
9//! only once, instead of it being done in every CMAC operation.
10//!
11//! Frequently all the data to be signed in a message is available in a single
12//! contiguous piece. In that case, the module-level `sign` function can be
13//! used. Otherwise, if the input is in multiple parts, `Context` should be
14//! used.
15//!
16//! # Examples:
17//!
18//! ## Signing a value and verifying it wasn't tampered with
19//!
20//! ```
21//! use aws_lc_rs::cmac;
22//!
23//! let key = cmac::Key::generate(cmac::AES_128)?;
24//!
25//! let msg = "hello, world";
26//!
27//! let tag = cmac::sign(&key, msg.as_bytes())?;
28//!
29//! // [We give access to the message to an untrusted party, and they give it
30//! // back to us. We need to verify they didn't tamper with it.]
31//!
32//! cmac::verify(&key, msg.as_bytes(), tag.as_ref())?;
33//!
34//! # Ok::<(), aws_lc_rs::error::Unspecified>(())
35//! ```
36//!
37//! ## Using the one-shot API:
38//!
39//! ```
40//! use aws_lc_rs::{cmac, rand};
41//!
42//! let msg = "hello, world";
43//!
44//! // The sender generates a secure key value and signs the message with it.
45//! // Note that in a real protocol, a key agreement protocol would be used to
46//! // derive `key_value`.
47//! let rng = rand::SystemRandom::new();
48//! let key_value: [u8; 16] = rand::generate(&rng)?.expose();
49//!
50//! let s_key = cmac::Key::new(cmac::AES_128, key_value.as_ref())?;
51//! let tag = cmac::sign(&s_key, msg.as_bytes())?;
52//!
53//! // The receiver (somehow!) knows the key value, and uses it to verify the
54//! // integrity of the message.
55//! let v_key = cmac::Key::new(cmac::AES_128, key_value.as_ref())?;
56//! cmac::verify(&v_key, msg.as_bytes(), tag.as_ref())?;
57//!
58//! # Ok::<(), aws_lc_rs::error::Unspecified>(())
59//! ```
60//!
61//! ## Using the multi-part API:
62//! ```
63//! use aws_lc_rs::{cmac, rand};
64//!
65//! let parts = ["hello", ", ", "world"];
66//!
67//! // The sender generates a secure key value and signs the message with it.
68//! // Note that in a real protocol, a key agreement protocol would be used to
69//! // derive `key_value`.
70//! let rng = rand::SystemRandom::new();
71//! let key_value: [u8; 32] = rand::generate(&rng)?.expose();
72//!
73//! let s_key = cmac::Key::new(cmac::AES_256, key_value.as_ref())?;
74//! let mut s_ctx = cmac::Context::with_key(&s_key);
75//! for part in &parts {
76//!     s_ctx.update(part.as_bytes())?;
77//! }
78//! let tag = s_ctx.sign()?;
79//!
80//! // The receiver (somehow!) knows the key value, and uses it to verify the
81//! // integrity of the message.
82//! let v_key = cmac::Key::new(cmac::AES_256, key_value.as_ref())?;
83//! let mut msg = Vec::<u8>::new();
84//! for part in &parts {
85//!     msg.extend(part.as_bytes());
86//! }
87//! cmac::verify(&v_key, &msg.as_ref(), tag.as_ref())?;
88//!
89//! # Ok::<(), aws_lc_rs::error::Unspecified>(())
90//! ```
91//! [RFC 4493]: https://tools.ietf.org/html/rfc4493
92//! [NIST SP 800-38B]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38b.pdf
93
94use crate::aws_lc::{
95    CMAC_CTX_copy, CMAC_CTX_new, CMAC_Final, CMAC_Init, CMAC_Update, EVP_aes_128_cbc,
96    EVP_aes_192_cbc, EVP_aes_256_cbc, EVP_des_ede3_cbc, CMAC_CTX, EVP_CIPHER,
97};
98use crate::error::Unspecified;
99use crate::fips::indicator_check;
100use crate::ptr::{ConstPointer, LcPtr};
101use crate::{constant_time, rand};
102use core::mem::MaybeUninit;
103use core::ptr::null_mut;
104
105#[derive(Clone, Copy, PartialEq, Eq, Debug)]
106enum AlgorithmId {
107    Aes128,
108    Aes192,
109    Aes256,
110    Tdes,
111}
112
113/// A CMAC algorithm.
114#[derive(Clone, Copy, PartialEq, Eq, Debug)]
115pub struct Algorithm {
116    id: AlgorithmId,
117    key_len: usize,
118    tag_len: usize,
119}
120
121impl Algorithm {
122    /// The key length for this CMAC algorithm.
123    #[inline]
124    #[must_use]
125    pub fn key_len(&self) -> usize {
126        self.key_len
127    }
128
129    /// The tag length for this CMAC algorithm.
130    #[inline]
131    #[must_use]
132    pub fn tag_len(&self) -> usize {
133        self.tag_len
134    }
135}
136
137impl AlgorithmId {
138    fn evp_cipher(&self) -> ConstPointer<'_, EVP_CIPHER> {
139        unsafe {
140            ConstPointer::new_static(match self {
141                AlgorithmId::Aes128 => EVP_aes_128_cbc(),
142                AlgorithmId::Aes192 => EVP_aes_192_cbc(),
143                AlgorithmId::Aes256 => EVP_aes_256_cbc(),
144                AlgorithmId::Tdes => EVP_des_ede3_cbc(),
145            })
146            .unwrap()
147        }
148    }
149}
150
151/// CMAC using AES-128.
152pub const AES_128: Algorithm = Algorithm {
153    id: AlgorithmId::Aes128,
154    key_len: 16,
155    tag_len: 16,
156};
157
158/// CMAC using AES-192.
159pub const AES_192: Algorithm = Algorithm {
160    id: AlgorithmId::Aes192,
161    key_len: 24,
162    tag_len: 16,
163};
164
165/// CMAC using AES-256.
166pub const AES_256: Algorithm = Algorithm {
167    id: AlgorithmId::Aes256,
168    key_len: 32,
169    tag_len: 16,
170};
171
172/// CMAC using 3-key Triple DES (DES-EDE3). For legacy interoperability only;
173/// new designs must use an AES-based algorithm.
174pub const DES_EDE3_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm {
175    id: AlgorithmId::Tdes,
176    key_len: 24,
177    tag_len: 8,
178};
179
180/// CMAC using 3-key Triple DES (DES-EDE3). For legacy interoperability only;
181/// new designs must use an AES-based algorithm.
182#[deprecated(
183    since = "1.17.0",
184    note = "Use `DES_EDE3_FOR_LEGACY_USE_ONLY` instead, which aligns with the naming used in the `cipher` module. The two constants are interchangeable."
185)]
186pub const TDES_FOR_LEGACY_USE_ONLY: Algorithm = DES_EDE3_FOR_LEGACY_USE_ONLY;
187
188/// Maximum CMAC tag length (AES block size).
189const MAX_CMAC_TAG_LEN: usize = 16;
190
191/// A CMAC tag.
192///
193/// For a given tag `t`, use `t.as_ref()` to get the tag value as a byte slice.
194#[derive(Clone, Copy, Debug)]
195pub struct Tag {
196    bytes: [u8; MAX_CMAC_TAG_LEN],
197    len: usize,
198}
199
200impl AsRef<[u8]> for Tag {
201    #[inline]
202    fn as_ref(&self) -> &[u8] {
203        &self.bytes[..self.len]
204    }
205}
206
207/// A key to use for CMAC signing.
208//
209// # FIPS
210// Use this type with one of the following algorithms:
211// * `AES_128`
212// * `AES_256`
213#[derive(Clone)]
214pub struct Key {
215    algorithm: Algorithm,
216    ctx: LcPtr<CMAC_CTX>,
217}
218
219impl Clone for LcPtr<CMAC_CTX> {
220    fn clone(&self) -> Self {
221        let mut new_ctx = LcPtr::new(unsafe { CMAC_CTX_new() }).expect("CMAC_CTX_new failed");
222        unsafe {
223            assert!(
224                1 == CMAC_CTX_copy(new_ctx.as_mut_ptr(), self.as_const_ptr()),
225                "CMAC_CTX_copy failed"
226            );
227        }
228        new_ctx
229    }
230}
231
232unsafe impl Send for Key {}
233// All uses of *mut CMAC_CTX require the creation of a Context, which will clone the Key.
234unsafe impl Sync for Key {}
235
236#[allow(clippy::missing_fields_in_debug)]
237impl core::fmt::Debug for Key {
238    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
239        f.debug_struct("Key")
240            .field("algorithm", &self.algorithm)
241            .finish()
242    }
243}
244
245impl Key {
246    /// Generate a CMAC signing key using the given algorithm with a
247    /// random value.
248    ///
249    //
250    // # FIPS
251    // Use this type with one of the following algorithms:
252    // * `AES_128`
253    // * `AES_256`
254    //
255    /// # Errors
256    /// `error::Unspecified` if random generation or key construction fails.
257    pub fn generate(algorithm: Algorithm) -> Result<Self, Unspecified> {
258        let mut key_bytes = vec![0u8; algorithm.key_len()];
259        rand::fill(&mut key_bytes)?;
260        Self::new(algorithm, &key_bytes)
261    }
262
263    /// Construct a CMAC signing key using the given algorithm and key value.
264    ///
265    /// `key_value` should be a value generated using a secure random number
266    /// generator or derived from a random key by a key derivation function.
267    ///
268    /// # Errors
269    /// `error::Unspecified` if the key length doesn't match the algorithm or if CMAC context
270    /// initialization fails.
271    pub fn new(algorithm: Algorithm, key_value: &[u8]) -> Result<Self, Unspecified> {
272        if key_value.len() != algorithm.key_len() {
273            return Err(Unspecified);
274        }
275
276        let mut ctx = LcPtr::new(unsafe { CMAC_CTX_new() })?;
277
278        unsafe {
279            let cipher = algorithm.id.evp_cipher();
280            if 1 != CMAC_Init(
281                ctx.as_mut_ptr(),
282                key_value.as_ptr().cast(),
283                key_value.len(),
284                cipher.as_const_ptr(),
285                null_mut(),
286            ) {
287                return Err(Unspecified);
288            }
289        }
290
291        Ok(Self { algorithm, ctx })
292    }
293
294    /// The algorithm for the key.
295    #[inline]
296    #[must_use]
297    pub fn algorithm(&self) -> Algorithm {
298        self.algorithm
299    }
300}
301
302/// A context for multi-step (Init-Update-Finish) CMAC signing.
303///
304/// Use `sign` for single-step CMAC signing.
305pub struct Context {
306    key: Key,
307}
308
309impl Clone for Context {
310    fn clone(&self) -> Self {
311        Self {
312            key: self.key.clone(),
313        }
314    }
315}
316
317unsafe impl Send for Context {}
318
319impl core::fmt::Debug for Context {
320    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
321        f.debug_struct("Context")
322            .field("algorithm", &self.key.algorithm)
323            .finish()
324    }
325}
326
327impl Context {
328    /// Constructs a new CMAC signing context using the given key.
329    #[inline]
330    #[must_use]
331    pub fn with_key(key: &Key) -> Self {
332        Self { key: key.clone() }
333    }
334
335    /// Updates the CMAC with all the data in `data`. `update` may be called
336    /// zero or more times until `sign` is called.
337    ///
338    /// # Errors
339    /// `error::Unspecified` if the CMAC cannot be updated.
340    pub fn update(&mut self, data: &[u8]) -> Result<(), Unspecified> {
341        unsafe {
342            if 1 != CMAC_Update(self.key.ctx.as_mut_ptr(), data.as_ptr(), data.len()) {
343                return Err(Unspecified);
344            }
345        }
346        Ok(())
347    }
348
349    /// Finalizes the CMAC calculation and returns the CMAC value. `sign`
350    /// consumes the context so it cannot be (mis-)used after `sign` has been
351    /// called.
352    ///
353    /// It is generally not safe to implement CMAC verification by comparing
354    /// the return value of `sign` to a tag. Use `verify` for verification
355    /// instead.
356    ///
357    //
358    // # FIPS
359    // Use this method with one of the following algorithms:
360    // * `AES_128`
361    // * `AES_256`
362    //
363    /// # Errors
364    /// `error::Unspecified` if the CMAC calculation cannot be finalized.
365    ///
366    /// # Panics
367    /// Panics if the CMAC tag length exceeds the maximum allowed length, indicating memory corruption.
368    pub fn sign(mut self) -> Result<Tag, Unspecified> {
369        let mut output = [0u8; MAX_CMAC_TAG_LEN];
370        let output_len = {
371            let result = internal_sign(&mut self, &mut output)?;
372            result.len()
373        };
374
375        Ok(Tag {
376            bytes: output,
377            len: output_len,
378        })
379    }
380
381    /// Finalizes the CMAC calculation and verifies whether the resulting value
382    /// equals the provided `tag`.
383    ///
384    /// `verify` consumes the context so it cannot be (mis-)used after `verify`
385    /// has been called.
386    ///
387    /// The verification is done in constant time to prevent timing attacks.
388    ///
389    /// # Errors
390    /// `error::Unspecified` if the tag does not match or if CMAC calculation fails.
391    //
392    // # FIPS
393    // Use this function with one of the following algorithms:
394    // * `AES_128`
395    // * `AES_256`
396    #[inline]
397    pub fn verify(mut self, tag: &[u8]) -> Result<(), Unspecified> {
398        let mut output = [0u8; MAX_CMAC_TAG_LEN];
399        let output_len = {
400            let result = internal_sign(&mut self, &mut output)?;
401            result.len()
402        };
403
404        constant_time::verify_slices_are_equal(&output[0..output_len], tag)
405    }
406}
407
408pub(crate) fn internal_sign<'in_out>(
409    ctx: &mut Context,
410    output: &'in_out mut [u8],
411) -> Result<&'in_out mut [u8], Unspecified> {
412    let mut out_len = MaybeUninit::<usize>::uninit();
413
414    if 1 != indicator_check!(unsafe {
415        CMAC_Final(
416            ctx.key.ctx.as_mut_ptr(),
417            output.as_mut_ptr(),
418            out_len.as_mut_ptr(),
419        )
420    }) {
421        return Err(Unspecified);
422    }
423    let actual_len = unsafe { out_len.assume_init() };
424
425    // This indicates a memory corruption.
426    debug_assert!(
427        actual_len <= MAX_CMAC_TAG_LEN,
428        "CMAC tag length {actual_len} exceeds maximum {MAX_CMAC_TAG_LEN}"
429    );
430    if actual_len != ctx.key.algorithm.tag_len() {
431        return Err(Unspecified);
432    }
433
434    Ok(&mut output[0..actual_len])
435}
436
437/// Calculates the CMAC of `data` using the key `key` in one step.
438///
439/// Use `Context` to calculate CMACs where the input is in multiple parts.
440///
441/// It is generally not safe to implement CMAC verification by comparing the
442/// return value of `sign` to a tag. Use `verify` for verification instead.
443//
444// # FIPS
445// Use this function with one of the following algorithms:
446// * `AES_128`
447// * `AES_256`
448//
449/// # Errors
450/// `error::Unspecified` if the CMAC calculation fails.
451#[inline]
452pub fn sign(key: &Key, data: &[u8]) -> Result<Tag, Unspecified> {
453    let mut ctx = Context::with_key(key);
454    ctx.update(data)?;
455    ctx.sign()
456}
457
458/// Calculates the CMAC of `data` using the key `key` in one step, writing the
459/// result into the provided `output` buffer.
460///
461/// Use `Context` to calculate CMACs where the input is in multiple parts.
462///
463/// The `output` buffer must be at least as large as the algorithm's tag length
464/// (obtainable via `key.algorithm().tag_len()`). The returned slice will be a
465/// sub-slice of `output` containing exactly the tag bytes.
466///
467/// It is generally not safe to implement CMAC verification by comparing the
468/// return value of `sign_to_buffer` to a tag. Use `verify` for verification instead.
469//
470// # FIPS
471// Use this function with one of the following algorithms:
472// * `AES_128`
473// * `AES_256`
474//
475/// # Errors
476/// `error::Unspecified` if the output buffer is too small or if the CMAC calculation fails.
477#[inline]
478pub fn sign_to_buffer<'out>(
479    key: &Key,
480    data: &[u8],
481    output: &'out mut [u8],
482) -> Result<&'out mut [u8], Unspecified> {
483    if output.len() < key.algorithm().tag_len() {
484        return Err(Unspecified);
485    }
486
487    let mut ctx = Context::with_key(key);
488    ctx.update(data)?;
489
490    internal_sign(&mut ctx, output)
491}
492
493/// Calculates the CMAC of `data` using the signing key `key`, and verifies
494/// whether the resultant value equals `tag`, in one step.
495///
496/// The verification is done in constant time to prevent timing attacks.
497///
498/// # Errors
499/// `error::Unspecified` if the tag does not match or if CMAC calculation fails.
500//
501// # FIPS
502// Use this function with one of the following algorithms:
503// * `AES_128`
504// * `AES_256`
505#[inline]
506pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), Unspecified> {
507    let mut output = [0u8; MAX_CMAC_TAG_LEN];
508    let output_len = {
509        let result = sign_to_buffer(key, data, &mut output)?;
510        result.len()
511    };
512
513    constant_time::verify_slices_are_equal(&output[0..output_len], tag)
514}
515
516#[cfg(test)]
517mod tests {
518    use super::*;
519
520    #[cfg(feature = "fips")]
521    mod fips;
522
523    #[test]
524    fn cmac_basic_test() {
525        for &algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
526            let key = Key::generate(algorithm).unwrap();
527            let data = b"hello, world";
528
529            let tag = sign(&key, data).unwrap();
530            assert!(verify(&key, data, tag.as_ref()).is_ok());
531            assert!(verify(&key, b"hello, worle", tag.as_ref()).is_err());
532        }
533    }
534
535    // Make sure that `Key::generate` and `verify` aren't completely wacky.
536    #[test]
537    pub fn cmac_signing_key_coverage() {
538        const HELLO_WORLD_GOOD: &[u8] = b"hello, world";
539        const HELLO_WORLD_BAD: &[u8] = b"hello, worle";
540
541        for algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
542            let key = Key::generate(*algorithm).unwrap();
543            let tag = sign(&key, HELLO_WORLD_GOOD).unwrap();
544            println!("{key:?}");
545            assert!(verify(&key, HELLO_WORLD_GOOD, tag.as_ref()).is_ok());
546            assert!(verify(&key, HELLO_WORLD_BAD, tag.as_ref()).is_err());
547        }
548    }
549
550    #[test]
551    fn cmac_coverage() {
552        // Something would have gone horribly wrong for this to not pass, but we test this so our
553        // coverage reports will look better.
554        assert_ne!(AES_128, AES_256);
555        assert_ne!(AES_192, AES_256);
556
557        for &alg in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
558            // Clone after updating context with message, then check if the final Tag is the same.
559            let key_bytes = vec![0u8; alg.key_len()];
560            let key = Key::new(alg, &key_bytes).unwrap();
561            let mut ctx = Context::with_key(&key);
562            ctx.update(b"hello, world").unwrap();
563            let ctx_clone = ctx.clone();
564
565            let orig_tag = ctx.sign().unwrap();
566            let clone_tag = ctx_clone.sign().unwrap();
567            assert_eq!(orig_tag.as_ref(), clone_tag.as_ref());
568            assert_eq!(orig_tag.clone().as_ref(), clone_tag.as_ref());
569        }
570    }
571
572    #[test]
573    fn cmac_context_test() {
574        let key = Key::generate(AES_192).unwrap();
575
576        let mut ctx = Context::with_key(&key);
577        ctx.update(b"hello").unwrap();
578        ctx.update(b", ").unwrap();
579        ctx.update(b"world").unwrap();
580        let tag1 = ctx.sign().unwrap();
581
582        let tag2 = sign(&key, b"hello, world").unwrap();
583        assert_eq!(tag1.as_ref(), tag2.as_ref());
584    }
585
586    #[test]
587    fn cmac_multi_part_test() {
588        let parts = ["hello", ", ", "world"];
589
590        for &algorithm in &[AES_128, AES_256] {
591            let key = Key::generate(algorithm).unwrap();
592
593            // Multi-part signing
594            let mut ctx = Context::with_key(&key);
595            for part in &parts {
596                ctx.update(part.as_bytes()).unwrap();
597            }
598            let tag = ctx.sign().unwrap();
599
600            // Verification with concatenated message
601            let mut msg = Vec::<u8>::new();
602            for part in &parts {
603                msg.extend(part.as_bytes());
604            }
605            assert!(verify(&key, &msg, tag.as_ref()).is_ok());
606        }
607    }
608
609    #[test]
610    fn cmac_key_new_test() {
611        // Test Key::new with explicit key values
612        let key_128 = [0u8; 16];
613        let key_192 = [0u8; 24];
614        let key_256 = [0u8; 32];
615        let key_3des = [0u8; 24];
616
617        let k1 = Key::new(AES_128, &key_128).unwrap();
618        let k2 = Key::new(AES_192, &key_192).unwrap();
619        let k3 = Key::new(AES_256, &key_256).unwrap();
620        let k4 = Key::new(DES_EDE3_FOR_LEGACY_USE_ONLY, &key_3des).unwrap();
621
622        let data = b"test message";
623
624        // All should produce valid tags
625        let _ = sign(&k1, data).unwrap();
626        let _ = sign(&k2, data).unwrap();
627        let _ = sign(&k3, data).unwrap();
628        let _ = sign(&k4, data).unwrap();
629    }
630
631    #[test]
632    fn cmac_key_new_wrong_length_test() {
633        let key_256 = [0u8; 32];
634        // Wrong key length should return error
635        assert!(Key::new(AES_128, &key_256).is_err());
636    }
637
638    #[test]
639    fn cmac_algorithm_properties() {
640        assert_eq!(AES_128.key_len(), 16);
641        assert_eq!(AES_128.tag_len(), 16);
642
643        assert_eq!(AES_192.key_len(), 24);
644        assert_eq!(AES_192.tag_len(), 16);
645
646        assert_eq!(AES_256.key_len(), 32);
647        assert_eq!(AES_256.tag_len(), 16);
648
649        assert_eq!(DES_EDE3_FOR_LEGACY_USE_ONLY.key_len(), 24);
650        assert_eq!(DES_EDE3_FOR_LEGACY_USE_ONLY.tag_len(), 8);
651    }
652
653    #[test]
654    fn cmac_empty_data() {
655        let key = Key::generate(AES_128).unwrap();
656
657        // CMAC should work with empty data
658        let tag = sign(&key, b"").unwrap();
659        assert!(verify(&key, b"", tag.as_ref()).is_ok());
660
661        // Context version
662        let ctx = Context::with_key(&key);
663        let tag2 = ctx.sign().unwrap();
664        assert_eq!(tag.as_ref(), tag2.as_ref());
665    }
666
667    #[test]
668    fn des_ede3_cmac_test() {
669        let key = Key::generate(DES_EDE3_FOR_LEGACY_USE_ONLY).unwrap();
670        let data = b"test data for 3DES CMAC";
671
672        let tag = sign(&key, data).unwrap();
673        assert_eq!(tag.as_ref().len(), 8); // 3DES block size
674        assert!(verify(&key, data, tag.as_ref()).is_ok());
675    }
676
677    #[test]
678    fn cmac_sign_to_buffer_test() {
679        for &algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
680            let key = Key::generate(algorithm).unwrap();
681            let data = b"hello, world";
682
683            // Test with exact size buffer
684            let mut output = vec![0u8; algorithm.tag_len()];
685            let result = sign_to_buffer(&key, data, &mut output).unwrap();
686            assert_eq!(result.len(), algorithm.tag_len());
687
688            // Verify the tag matches sign()
689            let tag = sign(&key, data).unwrap();
690            assert_eq!(result, tag.as_ref());
691
692            // Test with larger buffer
693            let mut large_output = vec![0u8; algorithm.tag_len() + 10];
694            let result2 = sign_to_buffer(&key, data, &mut large_output).unwrap();
695            assert_eq!(result2.len(), algorithm.tag_len());
696            assert_eq!(result2, tag.as_ref());
697        }
698    }
699
700    #[test]
701    fn cmac_sign_to_buffer_too_small_test() {
702        let key = Key::generate(AES_128).unwrap();
703        let data = b"hello";
704
705        // Buffer too small should fail
706        let mut small_buffer = vec![0u8; AES_128.tag_len() - 1];
707        assert!(sign_to_buffer(&key, data, &mut small_buffer).is_err());
708
709        // Empty buffer should fail
710        let mut empty_buffer = vec![];
711        assert!(sign_to_buffer(&key, data, &mut empty_buffer).is_err());
712    }
713
714    #[test]
715    fn cmac_context_verify_test() {
716        for &algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
717            let key = Key::generate(algorithm).unwrap();
718            let data = b"hello, world";
719
720            // Generate a valid tag
721            let tag = sign(&key, data).unwrap();
722
723            // Verify with Context::verify
724            let mut ctx = Context::with_key(&key);
725            ctx.update(data).unwrap();
726            assert!(ctx.verify(tag.as_ref()).is_ok());
727
728            // Verify with wrong tag should fail
729            let mut ctx2 = Context::with_key(&key);
730            ctx2.update(data).unwrap();
731            let wrong_tag = vec![0u8; algorithm.tag_len()];
732            assert!(ctx2.verify(&wrong_tag).is_err());
733
734            // Verify with different data should fail
735            let mut ctx3 = Context::with_key(&key);
736            ctx3.update(b"wrong data").unwrap();
737            assert!(ctx3.verify(tag.as_ref()).is_err());
738        }
739    }
740
741    #[test]
742    fn cmac_context_verify_multipart_test() {
743        let key = Key::generate(AES_256).unwrap();
744        let parts = ["hello", ", ", "world"];
745
746        // Create tag from concatenated message
747        let mut full_msg = Vec::new();
748        for part in &parts {
749            full_msg.extend_from_slice(part.as_bytes());
750        }
751        let tag = sign(&key, &full_msg).unwrap();
752
753        // Verify using multi-part context
754        let mut ctx = Context::with_key(&key);
755        for part in &parts {
756            ctx.update(part.as_bytes()).unwrap();
757        }
758        assert!(ctx.verify(tag.as_ref()).is_ok());
759
760        // Verify with missing part should fail
761        let mut ctx2 = Context::with_key(&key);
762        ctx2.update(parts[0].as_bytes()).unwrap();
763        ctx2.update(parts[1].as_bytes()).unwrap();
764        // Missing parts[2]
765        assert!(ctx2.verify(tag.as_ref()).is_err());
766    }
767}