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 mut out_len = MaybeUninit::<usize>::uninit();
362
363        unsafe {
364            if 1 != indicator_check!(CMAC_Final(
365                self.key.ctx.as_mut_ptr(),
366                output.as_mut_ptr(),
367                out_len.as_mut_ptr(),
368            )) {
369                return Err(Unspecified);
370            }
371
372            let actual_len = out_len.assume_init();
373            // This indicates a memory corruption.
374            assert!(
375                actual_len <= MAX_CMAC_TAG_LEN,
376                "CMAC tag length {actual_len} exceeds maximum {MAX_CMAC_TAG_LEN}"
377            );
378            if actual_len != self.key.algorithm.tag_len() {
379                return Err(Unspecified);
380            }
381
382            Ok(Tag {
383                bytes: output,
384                len: actual_len,
385            })
386        }
387    }
388}
389
390/// Calculates the CMAC of `data` using the key `key` in one step.
391///
392/// Use `Context` to calculate CMACs where the input is in multiple parts.
393///
394/// It is generally not safe to implement CMAC verification by comparing the
395/// return value of `sign` to a tag. Use `verify` for verification instead.
396//
397// # FIPS
398// Use this function with one of the following algorithms:
399// * `AES_128`
400// * `AES_256`
401//
402/// # Errors
403/// `error::Unspecified` if the CMAC calculation fails.
404#[inline]
405pub fn sign(key: &Key, data: &[u8]) -> Result<Tag, Unspecified> {
406    let mut ctx = Context::with_key(key);
407    ctx.update(data)?;
408    ctx.sign()
409}
410
411/// Calculates the CMAC of `data` using the signing key `key`, and verifies
412/// whether the resultant value equals `tag`, in one step.
413///
414/// The verification will be done in constant time to prevent timing attacks.
415///
416/// # Errors
417/// `error::Unspecified` if the inputs are not verified or CMAC calculation fails.
418//
419// # FIPS
420// Use this function with one of the following algorithms:
421// * `AES_128`
422// * `AES_256`
423#[inline]
424pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), Unspecified> {
425    let computed_tag = sign(key, data)?;
426    constant_time::verify_slices_are_equal(computed_tag.as_ref(), tag)
427}
428
429#[cfg(test)]
430mod tests {
431    use super::*;
432
433    #[cfg(feature = "fips")]
434    mod fips;
435
436    #[test]
437    fn cmac_basic_test() {
438        for &algorithm in &[AES_128, AES_192, AES_256, TDES_FOR_LEGACY_USE_ONLY] {
439            let key = Key::generate(algorithm).unwrap();
440            let data = b"hello, world";
441
442            let tag = sign(&key, data).unwrap();
443            assert!(verify(&key, data, tag.as_ref()).is_ok());
444            assert!(verify(&key, b"hello, worle", tag.as_ref()).is_err());
445        }
446    }
447
448    // Make sure that `Key::generate` and `verify` aren't completely wacky.
449    #[test]
450    pub fn cmac_signing_key_coverage() {
451        const HELLO_WORLD_GOOD: &[u8] = b"hello, world";
452        const HELLO_WORLD_BAD: &[u8] = b"hello, worle";
453
454        for algorithm in &[AES_128, AES_192, AES_256, TDES_FOR_LEGACY_USE_ONLY] {
455            let key = Key::generate(*algorithm).unwrap();
456            let tag = sign(&key, HELLO_WORLD_GOOD).unwrap();
457            println!("{key:?}");
458            assert!(verify(&key, HELLO_WORLD_GOOD, tag.as_ref()).is_ok());
459            assert!(verify(&key, HELLO_WORLD_BAD, tag.as_ref()).is_err());
460        }
461    }
462
463    #[test]
464    fn cmac_coverage() {
465        // Something would have gone horribly wrong for this to not pass, but we test this so our
466        // coverage reports will look better.
467        assert_ne!(AES_128, AES_256);
468        assert_ne!(AES_192, AES_256);
469
470        for &alg in &[AES_128, AES_192, AES_256, TDES_FOR_LEGACY_USE_ONLY] {
471            // Clone after updating context with message, then check if the final Tag is the same.
472            let key_bytes = vec![0u8; alg.key_len()];
473            let key = Key::new(alg, &key_bytes).unwrap();
474            let mut ctx = Context::with_key(&key);
475            ctx.update(b"hello, world").unwrap();
476            let ctx_clone = ctx.clone();
477
478            let orig_tag = ctx.sign().unwrap();
479            let clone_tag = ctx_clone.sign().unwrap();
480            assert_eq!(orig_tag.as_ref(), clone_tag.as_ref());
481            assert_eq!(orig_tag.clone().as_ref(), clone_tag.as_ref());
482        }
483    }
484
485    #[test]
486    fn cmac_context_test() {
487        let key = Key::generate(AES_192).unwrap();
488
489        let mut ctx = Context::with_key(&key);
490        ctx.update(b"hello").unwrap();
491        ctx.update(b", ").unwrap();
492        ctx.update(b"world").unwrap();
493        let tag1 = ctx.sign().unwrap();
494
495        let tag2 = sign(&key, b"hello, world").unwrap();
496        assert_eq!(tag1.as_ref(), tag2.as_ref());
497    }
498
499    #[test]
500    fn cmac_multi_part_test() {
501        let parts = ["hello", ", ", "world"];
502
503        for &algorithm in &[AES_128, AES_256] {
504            let key = Key::generate(algorithm).unwrap();
505
506            // Multi-part signing
507            let mut ctx = Context::with_key(&key);
508            for part in &parts {
509                ctx.update(part.as_bytes()).unwrap();
510            }
511            let tag = ctx.sign().unwrap();
512
513            // Verification with concatenated message
514            let mut msg = Vec::<u8>::new();
515            for part in &parts {
516                msg.extend(part.as_bytes());
517            }
518            assert!(verify(&key, &msg, tag.as_ref()).is_ok());
519        }
520    }
521
522    #[test]
523    fn cmac_key_new_test() {
524        // Test Key::new with explicit key values
525        let key_128 = [0u8; 16];
526        let key_192 = [0u8; 24];
527        let key_256 = [0u8; 32];
528        let key_3des = [0u8; 24];
529
530        let k1 = Key::new(AES_128, &key_128).unwrap();
531        let k2 = Key::new(AES_192, &key_192).unwrap();
532        let k3 = Key::new(AES_256, &key_256).unwrap();
533        let k4 = Key::new(TDES_FOR_LEGACY_USE_ONLY, &key_3des).unwrap();
534
535        let data = b"test message";
536
537        // All should produce valid tags
538        let _ = sign(&k1, data).unwrap();
539        let _ = sign(&k2, data).unwrap();
540        let _ = sign(&k3, data).unwrap();
541        let _ = sign(&k4, data).unwrap();
542    }
543
544    #[test]
545    fn cmac_key_new_wrong_length_test() {
546        let key_256 = [0u8; 32];
547        // Wrong key length should return error
548        assert!(Key::new(AES_128, &key_256).is_err());
549    }
550
551    #[test]
552    fn cmac_algorithm_properties() {
553        assert_eq!(AES_128.key_len(), 16);
554        assert_eq!(AES_128.tag_len(), 16);
555
556        assert_eq!(AES_192.key_len(), 24);
557        assert_eq!(AES_192.tag_len(), 16);
558
559        assert_eq!(AES_256.key_len(), 32);
560        assert_eq!(AES_256.tag_len(), 16);
561
562        assert_eq!(TDES_FOR_LEGACY_USE_ONLY.key_len(), 24);
563        assert_eq!(TDES_FOR_LEGACY_USE_ONLY.tag_len(), 8);
564    }
565
566    #[test]
567    fn cmac_empty_data() {
568        let key = Key::generate(AES_128).unwrap();
569
570        // CMAC should work with empty data
571        let tag = sign(&key, b"").unwrap();
572        assert!(verify(&key, b"", tag.as_ref()).is_ok());
573
574        // Context version
575        let ctx = Context::with_key(&key);
576        let tag2 = ctx.sign().unwrap();
577        assert_eq!(tag.as_ref(), tag2.as_ref());
578    }
579
580    #[test]
581    fn des_ede3_cmac_test() {
582        let key = Key::generate(TDES_FOR_LEGACY_USE_ONLY).unwrap();
583        let data = b"test data for 3DES CMAC";
584
585        let tag = sign(&key, data).unwrap();
586        assert_eq!(tag.as_ref().len(), 8); // 3DES block size
587        assert!(verify(&key, data, tag.as_ref()).is_ok());
588    }
589}