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