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    note = "Use `DES_EDE3_FOR_LEGACY_USE_ONLY` instead, which aligns with the naming used in the `cipher` module. The two constants are interchangeable."
184)]
185pub const TDES_FOR_LEGACY_USE_ONLY: Algorithm = DES_EDE3_FOR_LEGACY_USE_ONLY;
186
187/// Maximum CMAC tag length (AES block size).
188const MAX_CMAC_TAG_LEN: usize = 16;
189
190/// A CMAC tag.
191///
192/// For a given tag `t`, use `t.as_ref()` to get the tag value as a byte slice.
193#[derive(Clone, Copy, Debug)]
194pub struct Tag {
195    bytes: [u8; MAX_CMAC_TAG_LEN],
196    len: usize,
197}
198
199impl AsRef<[u8]> for Tag {
200    #[inline]
201    fn as_ref(&self) -> &[u8] {
202        &self.bytes[..self.len]
203    }
204}
205
206/// A key to use for CMAC signing.
207//
208// # FIPS
209// Use this type with one of the following algorithms:
210// * `AES_128`
211// * `AES_256`
212#[derive(Clone)]
213pub struct Key {
214    algorithm: Algorithm,
215    ctx: LcPtr<CMAC_CTX>,
216}
217
218impl Clone for LcPtr<CMAC_CTX> {
219    fn clone(&self) -> Self {
220        let mut new_ctx = LcPtr::new(unsafe { CMAC_CTX_new() }).expect("CMAC_CTX_new failed");
221        unsafe {
222            assert!(
223                1 == CMAC_CTX_copy(new_ctx.as_mut_ptr(), self.as_const_ptr()),
224                "CMAC_CTX_copy failed"
225            );
226        }
227        new_ctx
228    }
229}
230
231unsafe impl Send for Key {}
232// All uses of *mut CMAC_CTX require the creation of a Context, which will clone the Key.
233unsafe impl Sync for Key {}
234
235#[allow(clippy::missing_fields_in_debug)]
236impl core::fmt::Debug for Key {
237    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
238        f.debug_struct("Key")
239            .field("algorithm", &self.algorithm)
240            .finish()
241    }
242}
243
244impl Key {
245    /// Generate a CMAC signing key using the given algorithm with a
246    /// random value.
247    ///
248    //
249    // # FIPS
250    // Use this type with one of the following algorithms:
251    // * `AES_128`
252    // * `AES_256`
253    //
254    /// # Errors
255    /// `error::Unspecified` if random generation or key construction fails.
256    pub fn generate(algorithm: Algorithm) -> Result<Self, Unspecified> {
257        let mut key_bytes = vec![0u8; algorithm.key_len()];
258        rand::fill(&mut key_bytes)?;
259        Self::new(algorithm, &key_bytes)
260    }
261
262    /// Construct a CMAC signing key using the given algorithm and key value.
263    ///
264    /// `key_value` should be a value generated using a secure random number
265    /// generator or derived from a random key by a key derivation function.
266    ///
267    /// # Errors
268    /// `error::Unspecified` if the key length doesn't match the algorithm or if CMAC context
269    /// initialization fails.
270    pub fn new(algorithm: Algorithm, key_value: &[u8]) -> Result<Self, Unspecified> {
271        if key_value.len() != algorithm.key_len() {
272            return Err(Unspecified);
273        }
274
275        let mut ctx = LcPtr::new(unsafe { CMAC_CTX_new() })?;
276
277        unsafe {
278            let cipher = algorithm.id.evp_cipher();
279            if 1 != CMAC_Init(
280                ctx.as_mut_ptr(),
281                key_value.as_ptr().cast(),
282                key_value.len(),
283                cipher.as_const_ptr(),
284                null_mut(),
285            ) {
286                return Err(Unspecified);
287            }
288        }
289
290        Ok(Self { algorithm, ctx })
291    }
292
293    /// The algorithm for the key.
294    #[inline]
295    #[must_use]
296    pub fn algorithm(&self) -> Algorithm {
297        self.algorithm
298    }
299}
300
301/// A context for multi-step (Init-Update-Finish) CMAC signing.
302///
303/// Use `sign` for single-step CMAC signing.
304pub struct Context {
305    key: Key,
306}
307
308impl Clone for Context {
309    fn clone(&self) -> Self {
310        Self {
311            key: self.key.clone(),
312        }
313    }
314}
315
316unsafe impl Send for Context {}
317
318impl core::fmt::Debug for Context {
319    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
320        f.debug_struct("Context")
321            .field("algorithm", &self.key.algorithm)
322            .finish()
323    }
324}
325
326impl Context {
327    /// Constructs a new CMAC signing context using the given key.
328    #[inline]
329    #[must_use]
330    pub fn with_key(key: &Key) -> Self {
331        Self { key: key.clone() }
332    }
333
334    /// Updates the CMAC with all the data in `data`. `update` may be called
335    /// zero or more times until `sign` is called.
336    ///
337    /// # Errors
338    /// `error::Unspecified` if the CMAC cannot be updated.
339    pub fn update(&mut self, data: &[u8]) -> Result<(), Unspecified> {
340        unsafe {
341            if 1 != CMAC_Update(self.key.ctx.as_mut_ptr(), data.as_ptr(), data.len()) {
342                return Err(Unspecified);
343            }
344        }
345        Ok(())
346    }
347
348    /// Finalizes the CMAC calculation and returns the CMAC value. `sign`
349    /// consumes the context so it cannot be (mis-)used after `sign` has been
350    /// called.
351    ///
352    /// It is generally not safe to implement CMAC verification by comparing
353    /// the return value of `sign` to a tag. Use `verify` for verification
354    /// instead.
355    ///
356    //
357    // # FIPS
358    // Use this method with one of the following algorithms:
359    // * `AES_128`
360    // * `AES_256`
361    //
362    /// # Errors
363    /// `error::Unspecified` if the CMAC calculation cannot be finalized.
364    ///
365    /// # Panics
366    /// Panics if the CMAC tag length exceeds the maximum allowed length, indicating memory corruption.
367    pub fn sign(mut self) -> Result<Tag, Unspecified> {
368        let mut output = [0u8; MAX_CMAC_TAG_LEN];
369        let output_len = {
370            let result = internal_sign(&mut self, &mut output)?;
371            result.len()
372        };
373
374        Ok(Tag {
375            bytes: output,
376            len: output_len,
377        })
378    }
379
380    /// Finalizes the CMAC calculation and verifies whether the resulting value
381    /// equals the provided `tag`.
382    ///
383    /// `verify` consumes the context so it cannot be (mis-)used after `verify`
384    /// has been called.
385    ///
386    /// The verification is done in constant time to prevent timing attacks.
387    ///
388    /// # Errors
389    /// `error::Unspecified` if the tag does not match or if CMAC calculation fails.
390    //
391    // # FIPS
392    // Use this function with one of the following algorithms:
393    // * `AES_128`
394    // * `AES_256`
395    #[inline]
396    pub fn verify(mut self, tag: &[u8]) -> Result<(), Unspecified> {
397        let mut output = [0u8; MAX_CMAC_TAG_LEN];
398        let output_len = {
399            let result = internal_sign(&mut self, &mut output)?;
400            result.len()
401        };
402
403        constant_time::verify_slices_are_equal(&output[0..output_len], tag)
404    }
405}
406
407pub(crate) fn internal_sign<'in_out>(
408    ctx: &mut Context,
409    output: &'in_out mut [u8],
410) -> Result<&'in_out mut [u8], Unspecified> {
411    let mut out_len = MaybeUninit::<usize>::uninit();
412
413    if 1 != indicator_check!(unsafe {
414        CMAC_Final(
415            ctx.key.ctx.as_mut_ptr(),
416            output.as_mut_ptr(),
417            out_len.as_mut_ptr(),
418        )
419    }) {
420        return Err(Unspecified);
421    }
422    let actual_len = unsafe { out_len.assume_init() };
423
424    // This indicates a memory corruption.
425    debug_assert!(
426        actual_len <= MAX_CMAC_TAG_LEN,
427        "CMAC tag length {actual_len} exceeds maximum {MAX_CMAC_TAG_LEN}"
428    );
429    if actual_len != ctx.key.algorithm.tag_len() {
430        return Err(Unspecified);
431    }
432
433    Ok(&mut output[0..actual_len])
434}
435
436/// Calculates the CMAC of `data` using the key `key` in one step.
437///
438/// Use `Context` to calculate CMACs where the input is in multiple parts.
439///
440/// It is generally not safe to implement CMAC verification by comparing the
441/// return value of `sign` to a tag. Use `verify` for verification instead.
442//
443// # FIPS
444// Use this function with one of the following algorithms:
445// * `AES_128`
446// * `AES_256`
447//
448/// # Errors
449/// `error::Unspecified` if the CMAC calculation fails.
450#[inline]
451pub fn sign(key: &Key, data: &[u8]) -> Result<Tag, Unspecified> {
452    let mut ctx = Context::with_key(key);
453    ctx.update(data)?;
454    ctx.sign()
455}
456
457/// Calculates the CMAC of `data` using the key `key` in one step, writing the
458/// result into the provided `output` buffer.
459///
460/// Use `Context` to calculate CMACs where the input is in multiple parts.
461///
462/// The `output` buffer must be at least as large as the algorithm's tag length
463/// (obtainable via `key.algorithm().tag_len()`). The returned slice will be a
464/// sub-slice of `output` containing exactly the tag bytes.
465///
466/// It is generally not safe to implement CMAC verification by comparing the
467/// return value of `sign_to_buffer` to a tag. Use `verify` for verification instead.
468//
469// # FIPS
470// Use this function with one of the following algorithms:
471// * `AES_128`
472// * `AES_256`
473//
474/// # Errors
475/// `error::Unspecified` if the output buffer is too small or if the CMAC calculation fails.
476#[inline]
477pub fn sign_to_buffer<'out>(
478    key: &Key,
479    data: &[u8],
480    output: &'out mut [u8],
481) -> Result<&'out mut [u8], Unspecified> {
482    if output.len() < key.algorithm().tag_len() {
483        return Err(Unspecified);
484    }
485
486    let mut ctx = Context::with_key(key);
487    ctx.update(data)?;
488
489    internal_sign(&mut ctx, output)
490}
491
492/// Calculates the CMAC of `data` using the signing key `key`, and verifies
493/// whether the resultant value equals `tag`, in one step.
494///
495/// The verification is done in constant time to prevent timing attacks.
496///
497/// # Errors
498/// `error::Unspecified` if the tag does not match or if CMAC calculation fails.
499//
500// # FIPS
501// Use this function with one of the following algorithms:
502// * `AES_128`
503// * `AES_256`
504#[inline]
505pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), Unspecified> {
506    let mut output = [0u8; MAX_CMAC_TAG_LEN];
507    let output_len = {
508        let result = sign_to_buffer(key, data, &mut output)?;
509        result.len()
510    };
511
512    constant_time::verify_slices_are_equal(&output[0..output_len], tag)
513}
514
515#[cfg(test)]
516mod tests {
517    use super::*;
518
519    #[cfg(feature = "fips")]
520    mod fips;
521
522    #[test]
523    fn cmac_basic_test() {
524        for &algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
525            let key = Key::generate(algorithm).unwrap();
526            let data = b"hello, world";
527
528            let tag = sign(&key, data).unwrap();
529            assert!(verify(&key, data, tag.as_ref()).is_ok());
530            assert!(verify(&key, b"hello, worle", tag.as_ref()).is_err());
531        }
532    }
533
534    // Make sure that `Key::generate` and `verify` aren't completely wacky.
535    #[test]
536    pub fn cmac_signing_key_coverage() {
537        const HELLO_WORLD_GOOD: &[u8] = b"hello, world";
538        const HELLO_WORLD_BAD: &[u8] = b"hello, worle";
539
540        for algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
541            let key = Key::generate(*algorithm).unwrap();
542            let tag = sign(&key, HELLO_WORLD_GOOD).unwrap();
543            println!("{key:?}");
544            assert!(verify(&key, HELLO_WORLD_GOOD, tag.as_ref()).is_ok());
545            assert!(verify(&key, HELLO_WORLD_BAD, tag.as_ref()).is_err());
546        }
547    }
548
549    #[test]
550    fn cmac_coverage() {
551        // Something would have gone horribly wrong for this to not pass, but we test this so our
552        // coverage reports will look better.
553        assert_ne!(AES_128, AES_256);
554        assert_ne!(AES_192, AES_256);
555
556        for &alg in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
557            // Clone after updating context with message, then check if the final Tag is the same.
558            let key_bytes = vec![0u8; alg.key_len()];
559            let key = Key::new(alg, &key_bytes).unwrap();
560            let mut ctx = Context::with_key(&key);
561            ctx.update(b"hello, world").unwrap();
562            let ctx_clone = ctx.clone();
563
564            let orig_tag = ctx.sign().unwrap();
565            let clone_tag = ctx_clone.sign().unwrap();
566            assert_eq!(orig_tag.as_ref(), clone_tag.as_ref());
567            assert_eq!(orig_tag.clone().as_ref(), clone_tag.as_ref());
568        }
569    }
570
571    #[test]
572    fn cmac_context_test() {
573        let key = Key::generate(AES_192).unwrap();
574
575        let mut ctx = Context::with_key(&key);
576        ctx.update(b"hello").unwrap();
577        ctx.update(b", ").unwrap();
578        ctx.update(b"world").unwrap();
579        let tag1 = ctx.sign().unwrap();
580
581        let tag2 = sign(&key, b"hello, world").unwrap();
582        assert_eq!(tag1.as_ref(), tag2.as_ref());
583    }
584
585    #[test]
586    fn cmac_multi_part_test() {
587        let parts = ["hello", ", ", "world"];
588
589        for &algorithm in &[AES_128, AES_256] {
590            let key = Key::generate(algorithm).unwrap();
591
592            // Multi-part signing
593            let mut ctx = Context::with_key(&key);
594            for part in &parts {
595                ctx.update(part.as_bytes()).unwrap();
596            }
597            let tag = ctx.sign().unwrap();
598
599            // Verification with concatenated message
600            let mut msg = Vec::<u8>::new();
601            for part in &parts {
602                msg.extend(part.as_bytes());
603            }
604            assert!(verify(&key, &msg, tag.as_ref()).is_ok());
605        }
606    }
607
608    #[test]
609    fn cmac_key_new_test() {
610        // Test Key::new with explicit key values
611        let key_128 = [0u8; 16];
612        let key_192 = [0u8; 24];
613        let key_256 = [0u8; 32];
614        let key_3des = [0u8; 24];
615
616        let k1 = Key::new(AES_128, &key_128).unwrap();
617        let k2 = Key::new(AES_192, &key_192).unwrap();
618        let k3 = Key::new(AES_256, &key_256).unwrap();
619        let k4 = Key::new(DES_EDE3_FOR_LEGACY_USE_ONLY, &key_3des).unwrap();
620
621        let data = b"test message";
622
623        // All should produce valid tags
624        let _ = sign(&k1, data).unwrap();
625        let _ = sign(&k2, data).unwrap();
626        let _ = sign(&k3, data).unwrap();
627        let _ = sign(&k4, data).unwrap();
628    }
629
630    #[test]
631    fn cmac_key_new_wrong_length_test() {
632        let key_256 = [0u8; 32];
633        // Wrong key length should return error
634        assert!(Key::new(AES_128, &key_256).is_err());
635    }
636
637    #[test]
638    fn cmac_algorithm_properties() {
639        assert_eq!(AES_128.key_len(), 16);
640        assert_eq!(AES_128.tag_len(), 16);
641
642        assert_eq!(AES_192.key_len(), 24);
643        assert_eq!(AES_192.tag_len(), 16);
644
645        assert_eq!(AES_256.key_len(), 32);
646        assert_eq!(AES_256.tag_len(), 16);
647
648        assert_eq!(DES_EDE3_FOR_LEGACY_USE_ONLY.key_len(), 24);
649        assert_eq!(DES_EDE3_FOR_LEGACY_USE_ONLY.tag_len(), 8);
650    }
651
652    #[test]
653    fn cmac_empty_data() {
654        let key = Key::generate(AES_128).unwrap();
655
656        // CMAC should work with empty data
657        let tag = sign(&key, b"").unwrap();
658        assert!(verify(&key, b"", tag.as_ref()).is_ok());
659
660        // Context version
661        let ctx = Context::with_key(&key);
662        let tag2 = ctx.sign().unwrap();
663        assert_eq!(tag.as_ref(), tag2.as_ref());
664    }
665
666    #[test]
667    fn des_ede3_cmac_test() {
668        let key = Key::generate(DES_EDE3_FOR_LEGACY_USE_ONLY).unwrap();
669        let data = b"test data for 3DES CMAC";
670
671        let tag = sign(&key, data).unwrap();
672        assert_eq!(tag.as_ref().len(), 8); // 3DES block size
673        assert!(verify(&key, data, tag.as_ref()).is_ok());
674    }
675
676    #[test]
677    fn cmac_sign_to_buffer_test() {
678        for &algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
679            let key = Key::generate(algorithm).unwrap();
680            let data = b"hello, world";
681
682            // Test with exact size buffer
683            let mut output = vec![0u8; algorithm.tag_len()];
684            let result = sign_to_buffer(&key, data, &mut output).unwrap();
685            assert_eq!(result.len(), algorithm.tag_len());
686
687            // Verify the tag matches sign()
688            let tag = sign(&key, data).unwrap();
689            assert_eq!(result, tag.as_ref());
690
691            // Test with larger buffer
692            let mut large_output = vec![0u8; algorithm.tag_len() + 10];
693            let result2 = sign_to_buffer(&key, data, &mut large_output).unwrap();
694            assert_eq!(result2.len(), algorithm.tag_len());
695            assert_eq!(result2, tag.as_ref());
696        }
697    }
698
699    #[test]
700    fn cmac_sign_to_buffer_too_small_test() {
701        let key = Key::generate(AES_128).unwrap();
702        let data = b"hello";
703
704        // Buffer too small should fail
705        let mut small_buffer = vec![0u8; AES_128.tag_len() - 1];
706        assert!(sign_to_buffer(&key, data, &mut small_buffer).is_err());
707
708        // Empty buffer should fail
709        let mut empty_buffer = vec![];
710        assert!(sign_to_buffer(&key, data, &mut empty_buffer).is_err());
711    }
712
713    #[test]
714    fn cmac_context_verify_test() {
715        for &algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
716            let key = Key::generate(algorithm).unwrap();
717            let data = b"hello, world";
718
719            // Generate a valid tag
720            let tag = sign(&key, data).unwrap();
721
722            // Verify with Context::verify
723            let mut ctx = Context::with_key(&key);
724            ctx.update(data).unwrap();
725            assert!(ctx.verify(tag.as_ref()).is_ok());
726
727            // Verify with wrong tag should fail
728            let mut ctx2 = Context::with_key(&key);
729            ctx2.update(data).unwrap();
730            let wrong_tag = vec![0u8; algorithm.tag_len()];
731            assert!(ctx2.verify(&wrong_tag).is_err());
732
733            // Verify with different data should fail
734            let mut ctx3 = Context::with_key(&key);
735            ctx3.update(b"wrong data").unwrap();
736            assert!(ctx3.verify(tag.as_ref()).is_err());
737        }
738    }
739
740    #[test]
741    fn cmac_context_verify_multipart_test() {
742        let key = Key::generate(AES_256).unwrap();
743        let parts = ["hello", ", ", "world"];
744
745        // Create tag from concatenated message
746        let mut full_msg = Vec::new();
747        for part in &parts {
748            full_msg.extend_from_slice(part.as_bytes());
749        }
750        let tag = sign(&key, &full_msg).unwrap();
751
752        // Verify using multi-part context
753        let mut ctx = Context::with_key(&key);
754        for part in &parts {
755            ctx.update(part.as_bytes()).unwrap();
756        }
757        assert!(ctx.verify(tag.as_ref()).is_ok());
758
759        // Verify with missing part should fail
760        let mut ctx2 = Context::with_key(&key);
761        ctx2.update(parts[0].as_bytes()).unwrap();
762        ctx2.update(parts[1].as_bytes()).unwrap();
763        // Missing parts[2]
764        assert!(ctx2.verify(tag.as_ref()).is_err());
765    }
766}