Coverage Report

Created: 2026-06-28 08:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.16.3/src/cmac.rs
Line
Count
Source
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
94
use 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
};
98
use crate::error::Unspecified;
99
use crate::fips::indicator_check;
100
use crate::ptr::{ConstPointer, LcPtr};
101
use crate::{constant_time, rand};
102
use core::mem::MaybeUninit;
103
use core::ptr::null_mut;
104
105
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
106
enum AlgorithmId {
107
    Aes128,
108
    Aes192,
109
    Aes256,
110
    Tdes,
111
}
112
113
/// A CMAC algorithm.
114
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
115
pub struct Algorithm {
116
    id: AlgorithmId,
117
    key_len: usize,
118
    tag_len: usize,
119
}
120
121
impl Algorithm {
122
    /// The key length for this CMAC algorithm.
123
    #[inline]
124
    #[must_use]
125
0
    pub fn key_len(&self) -> usize {
126
0
        self.key_len
127
0
    }
128
129
    /// The tag length for this CMAC algorithm.
130
    #[inline]
131
    #[must_use]
132
0
    pub fn tag_len(&self) -> usize {
133
0
        self.tag_len
134
0
    }
135
}
136
137
impl AlgorithmId {
138
0
    fn evp_cipher(&self) -> ConstPointer<'_, EVP_CIPHER> {
139
        unsafe {
140
0
            ConstPointer::new_static(match self {
141
0
                AlgorithmId::Aes128 => EVP_aes_128_cbc(),
142
0
                AlgorithmId::Aes192 => EVP_aes_192_cbc(),
143
0
                AlgorithmId::Aes256 => EVP_aes_256_cbc(),
144
0
                AlgorithmId::Tdes => EVP_des_ede3_cbc(),
145
            })
146
0
            .unwrap()
147
        }
148
0
    }
149
}
150
151
/// CMAC using AES-128.
152
pub const AES_128: Algorithm = Algorithm {
153
    id: AlgorithmId::Aes128,
154
    key_len: 16,
155
    tag_len: 16,
156
};
157
158
/// CMAC using AES-192.
159
pub const AES_192: Algorithm = Algorithm {
160
    id: AlgorithmId::Aes192,
161
    key_len: 24,
162
    tag_len: 16,
163
};
164
165
/// CMAC using AES-256.
166
pub 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
173
pub 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).
180
const 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)]
186
pub struct Tag {
187
    bytes: [u8; MAX_CMAC_TAG_LEN],
188
    len: usize,
189
}
190
191
impl AsRef<[u8]> for Tag {
192
    #[inline]
193
0
    fn as_ref(&self) -> &[u8] {
194
0
        &self.bytes[..self.len]
195
0
    }
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)]
205
pub struct Key {
206
    algorithm: Algorithm,
207
    ctx: LcPtr<CMAC_CTX>,
208
}
209
210
impl Clone for LcPtr<CMAC_CTX> {
211
0
    fn clone(&self) -> Self {
212
0
        let mut new_ctx = LcPtr::new(unsafe { CMAC_CTX_new() }).expect("CMAC_CTX_new failed");
213
        unsafe {
214
0
            assert!(
215
0
                1 == CMAC_CTX_copy(new_ctx.as_mut_ptr(), self.as_const_ptr()),
216
0
                "CMAC_CTX_copy failed"
217
            );
218
        }
219
0
        new_ctx
220
0
    }
221
}
222
223
unsafe impl Send for Key {}
224
// All uses of *mut CMAC_CTX require the creation of a Context, which will clone the Key.
225
unsafe impl Sync for Key {}
226
227
#[allow(clippy::missing_fields_in_debug)]
228
impl core::fmt::Debug for Key {
229
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
230
0
        f.debug_struct("Key")
231
0
            .field("algorithm", &self.algorithm)
232
0
            .finish()
233
0
    }
234
}
235
236
impl 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
0
    pub fn generate(algorithm: Algorithm) -> Result<Self, Unspecified> {
249
0
        let mut key_bytes = vec![0u8; algorithm.key_len()];
250
0
        rand::fill(&mut key_bytes)?;
251
0
        Self::new(algorithm, &key_bytes)
252
0
    }
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
0
    pub fn new(algorithm: Algorithm, key_value: &[u8]) -> Result<Self, Unspecified> {
263
0
        if key_value.len() != algorithm.key_len() {
264
0
            return Err(Unspecified);
265
0
        }
266
267
0
        let mut ctx = LcPtr::new(unsafe { CMAC_CTX_new() })?;
268
269
        unsafe {
270
0
            let cipher = algorithm.id.evp_cipher();
271
0
            if 1 != CMAC_Init(
272
0
                ctx.as_mut_ptr(),
273
0
                key_value.as_ptr().cast(),
274
0
                key_value.len(),
275
0
                cipher.as_const_ptr(),
276
0
                null_mut(),
277
0
            ) {
278
0
                return Err(Unspecified);
279
0
            }
280
        }
281
282
0
        Ok(Self { algorithm, ctx })
283
0
    }
284
285
    /// The algorithm for the key.
286
    #[inline]
287
    #[must_use]
288
0
    pub fn algorithm(&self) -> Algorithm {
289
0
        self.algorithm
290
0
    }
291
}
292
293
/// A context for multi-step (Init-Update-Finish) CMAC signing.
294
///
295
/// Use `sign` for single-step CMAC signing.
296
pub struct Context {
297
    key: Key,
298
}
299
300
impl Clone for Context {
301
0
    fn clone(&self) -> Self {
302
0
        Self {
303
0
            key: self.key.clone(),
304
0
        }
305
0
    }
306
}
307
308
unsafe impl Send for Context {}
309
310
impl core::fmt::Debug for Context {
311
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
312
0
        f.debug_struct("Context")
313
0
            .field("algorithm", &self.key.algorithm)
314
0
            .finish()
315
0
    }
316
}
317
318
impl Context {
319
    /// Constructs a new CMAC signing context using the given key.
320
    #[inline]
321
    #[must_use]
322
0
    pub fn with_key(key: &Key) -> Self {
323
0
        Self { key: key.clone() }
324
0
    }
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
0
    pub fn update(&mut self, data: &[u8]) -> Result<(), Unspecified> {
332
        unsafe {
333
0
            if 1 != CMAC_Update(self.key.ctx.as_mut_ptr(), data.as_ptr(), data.len()) {
334
0
                return Err(Unspecified);
335
0
            }
336
        }
337
0
        Ok(())
338
0
    }
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
0
    pub fn sign(mut self) -> Result<Tag, Unspecified> {
360
0
        let mut output = [0u8; MAX_CMAC_TAG_LEN];
361
0
        let output_len = {
362
0
            let result = internal_sign(&mut self, &mut output)?;
363
0
            result.len()
364
        };
365
366
0
        Ok(Tag {
367
0
            bytes: output,
368
0
            len: output_len,
369
0
        })
370
0
    }
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
0
    pub fn verify(mut self, tag: &[u8]) -> Result<(), Unspecified> {
389
0
        let mut output = [0u8; MAX_CMAC_TAG_LEN];
390
0
        let output_len = {
391
0
            let result = internal_sign(&mut self, &mut output)?;
392
0
            result.len()
393
        };
394
395
0
        constant_time::verify_slices_are_equal(&output[0..output_len], tag)
396
0
    }
397
}
398
399
0
pub(crate) fn internal_sign<'in_out>(
400
0
    ctx: &mut Context,
401
0
    output: &'in_out mut [u8],
402
0
) -> Result<&'in_out mut [u8], Unspecified> {
403
0
    let mut out_len = MaybeUninit::<usize>::uninit();
404
405
0
    if 1 != indicator_check!(unsafe {
406
0
        CMAC_Final(
407
0
            ctx.key.ctx.as_mut_ptr(),
408
0
            output.as_mut_ptr(),
409
0
            out_len.as_mut_ptr(),
410
0
        )
411
0
    }) {
412
0
        return Err(Unspecified);
413
0
    }
414
0
    let actual_len = unsafe { out_len.assume_init() };
415
416
    // This indicates a memory corruption.
417
0
    debug_assert!(
418
0
        actual_len <= MAX_CMAC_TAG_LEN,
419
0
        "CMAC tag length {actual_len} exceeds maximum {MAX_CMAC_TAG_LEN}"
420
    );
421
0
    if actual_len != ctx.key.algorithm.tag_len() {
422
0
        return Err(Unspecified);
423
0
    }
424
425
0
    Ok(&mut output[0..actual_len])
426
0
}
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]
443
0
pub fn sign(key: &Key, data: &[u8]) -> Result<Tag, Unspecified> {
444
0
    let mut ctx = Context::with_key(key);
445
0
    ctx.update(data)?;
446
0
    ctx.sign()
447
0
}
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]
469
0
pub fn sign_to_buffer<'out>(
470
0
    key: &Key,
471
0
    data: &[u8],
472
0
    output: &'out mut [u8],
473
0
) -> Result<&'out mut [u8], Unspecified> {
474
0
    if output.len() < key.algorithm().tag_len() {
475
0
        return Err(Unspecified);
476
0
    }
477
478
0
    let mut ctx = Context::with_key(key);
479
0
    ctx.update(data)?;
480
481
0
    internal_sign(&mut ctx, output)
482
0
}
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]
497
0
pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), Unspecified> {
498
0
    let mut output = [0u8; MAX_CMAC_TAG_LEN];
499
0
    let output_len = {
500
0
        let result = sign_to_buffer(key, data, &mut output)?;
501
0
        result.len()
502
    };
503
504
0
    constant_time::verify_slices_are_equal(&output[0..output_len], tag)
505
0
}
506
507
#[cfg(test)]
508
mod 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
}