1use 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#[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 #[inline]
124 #[must_use]
125 pub fn key_len(&self) -> usize {
126 self.key_len
127 }
128
129 #[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
151pub const AES_128: Algorithm = Algorithm {
153 id: AlgorithmId::Aes128,
154 key_len: 16,
155 tag_len: 16,
156};
157
158pub const AES_192: Algorithm = Algorithm {
160 id: AlgorithmId::Aes192,
161 key_len: 24,
162 tag_len: 16,
163};
164
165pub const AES_256: Algorithm = Algorithm {
167 id: AlgorithmId::Aes256,
168 key_len: 32,
169 tag_len: 16,
170};
171
172pub const DES_EDE3_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm {
175 id: AlgorithmId::Tdes,
176 key_len: 24,
177 tag_len: 8,
178};
179
180#[deprecated(
183 since = "1.17.0",
184 note = "Use `DES_EDE3_FOR_LEGACY_USE_ONLY` instead, which aligns with the naming used in the `cipher` module. The two constants are interchangeable."
185)]
186pub const TDES_FOR_LEGACY_USE_ONLY: Algorithm = DES_EDE3_FOR_LEGACY_USE_ONLY;
187
188const MAX_CMAC_TAG_LEN: usize = 16;
190
191#[derive(Clone, Copy, Debug)]
195pub struct Tag {
196 bytes: [u8; MAX_CMAC_TAG_LEN],
197 len: usize,
198}
199
200impl AsRef<[u8]> for Tag {
201 #[inline]
202 fn as_ref(&self) -> &[u8] {
203 &self.bytes[..self.len]
204 }
205}
206
207#[derive(Clone)]
214pub struct Key {
215 algorithm: Algorithm,
216 ctx: LcPtr<CMAC_CTX>,
217}
218
219impl Clone for LcPtr<CMAC_CTX> {
220 fn clone(&self) -> Self {
221 let mut new_ctx = LcPtr::new(unsafe { CMAC_CTX_new() }).expect("CMAC_CTX_new failed");
222 unsafe {
223 assert!(
224 1 == CMAC_CTX_copy(new_ctx.as_mut_ptr(), self.as_const_ptr()),
225 "CMAC_CTX_copy failed"
226 );
227 }
228 new_ctx
229 }
230}
231
232unsafe impl Send for Key {}
233unsafe impl Sync for Key {}
235
236#[allow(clippy::missing_fields_in_debug)]
237impl core::fmt::Debug for Key {
238 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
239 f.debug_struct("Key")
240 .field("algorithm", &self.algorithm)
241 .finish()
242 }
243}
244
245impl Key {
246 pub fn generate(algorithm: Algorithm) -> Result<Self, Unspecified> {
258 let mut key_bytes = vec![0u8; algorithm.key_len()];
259 rand::fill(&mut key_bytes)?;
260 Self::new(algorithm, &key_bytes)
261 }
262
263 pub fn new(algorithm: Algorithm, key_value: &[u8]) -> Result<Self, Unspecified> {
272 if key_value.len() != algorithm.key_len() {
273 return Err(Unspecified);
274 }
275
276 let mut ctx = LcPtr::new(unsafe { CMAC_CTX_new() })?;
277
278 unsafe {
279 let cipher = algorithm.id.evp_cipher();
280 if 1 != CMAC_Init(
281 ctx.as_mut_ptr(),
282 key_value.as_ptr().cast(),
283 key_value.len(),
284 cipher.as_const_ptr(),
285 null_mut(),
286 ) {
287 return Err(Unspecified);
288 }
289 }
290
291 Ok(Self { algorithm, ctx })
292 }
293
294 #[inline]
296 #[must_use]
297 pub fn algorithm(&self) -> Algorithm {
298 self.algorithm
299 }
300}
301
302pub struct Context {
306 key: Key,
307}
308
309impl Clone for Context {
310 fn clone(&self) -> Self {
311 Self {
312 key: self.key.clone(),
313 }
314 }
315}
316
317unsafe impl Send for Context {}
318
319impl core::fmt::Debug for Context {
320 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
321 f.debug_struct("Context")
322 .field("algorithm", &self.key.algorithm)
323 .finish()
324 }
325}
326
327impl Context {
328 #[inline]
330 #[must_use]
331 pub fn with_key(key: &Key) -> Self {
332 Self { key: key.clone() }
333 }
334
335 pub fn update(&mut self, data: &[u8]) -> Result<(), Unspecified> {
341 unsafe {
342 if 1 != CMAC_Update(self.key.ctx.as_mut_ptr(), data.as_ptr(), data.len()) {
343 return Err(Unspecified);
344 }
345 }
346 Ok(())
347 }
348
349 pub fn sign(mut self) -> Result<Tag, Unspecified> {
369 let mut output = [0u8; MAX_CMAC_TAG_LEN];
370 let output_len = {
371 let result = internal_sign(&mut self, &mut output)?;
372 result.len()
373 };
374
375 Ok(Tag {
376 bytes: output,
377 len: output_len,
378 })
379 }
380
381 #[inline]
397 pub fn verify(mut self, tag: &[u8]) -> Result<(), Unspecified> {
398 let mut output = [0u8; MAX_CMAC_TAG_LEN];
399 let output_len = {
400 let result = internal_sign(&mut self, &mut output)?;
401 result.len()
402 };
403
404 constant_time::verify_slices_are_equal(&output[0..output_len], tag)
405 }
406}
407
408pub(crate) fn internal_sign<'in_out>(
409 ctx: &mut Context,
410 output: &'in_out mut [u8],
411) -> Result<&'in_out mut [u8], Unspecified> {
412 let mut out_len = MaybeUninit::<usize>::uninit();
413
414 if 1 != indicator_check!(unsafe {
415 CMAC_Final(
416 ctx.key.ctx.as_mut_ptr(),
417 output.as_mut_ptr(),
418 out_len.as_mut_ptr(),
419 )
420 }) {
421 return Err(Unspecified);
422 }
423 let actual_len = unsafe { out_len.assume_init() };
424
425 debug_assert!(
427 actual_len <= MAX_CMAC_TAG_LEN,
428 "CMAC tag length {actual_len} exceeds maximum {MAX_CMAC_TAG_LEN}"
429 );
430 if actual_len != ctx.key.algorithm.tag_len() {
431 return Err(Unspecified);
432 }
433
434 Ok(&mut output[0..actual_len])
435}
436
437#[inline]
452pub fn sign(key: &Key, data: &[u8]) -> Result<Tag, Unspecified> {
453 let mut ctx = Context::with_key(key);
454 ctx.update(data)?;
455 ctx.sign()
456}
457
458#[inline]
478pub fn sign_to_buffer<'out>(
479 key: &Key,
480 data: &[u8],
481 output: &'out mut [u8],
482) -> Result<&'out mut [u8], Unspecified> {
483 if output.len() < key.algorithm().tag_len() {
484 return Err(Unspecified);
485 }
486
487 let mut ctx = Context::with_key(key);
488 ctx.update(data)?;
489
490 internal_sign(&mut ctx, output)
491}
492
493#[inline]
506pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), Unspecified> {
507 let mut output = [0u8; MAX_CMAC_TAG_LEN];
508 let output_len = {
509 let result = sign_to_buffer(key, data, &mut output)?;
510 result.len()
511 };
512
513 constant_time::verify_slices_are_equal(&output[0..output_len], tag)
514}
515
516#[cfg(test)]
517mod tests {
518 use super::*;
519
520 #[cfg(feature = "fips")]
521 mod fips;
522
523 #[test]
524 fn cmac_basic_test() {
525 for &algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
526 let key = Key::generate(algorithm).unwrap();
527 let data = b"hello, world";
528
529 let tag = sign(&key, data).unwrap();
530 assert!(verify(&key, data, tag.as_ref()).is_ok());
531 assert!(verify(&key, b"hello, worle", tag.as_ref()).is_err());
532 }
533 }
534
535 #[test]
537 pub fn cmac_signing_key_coverage() {
538 const HELLO_WORLD_GOOD: &[u8] = b"hello, world";
539 const HELLO_WORLD_BAD: &[u8] = b"hello, worle";
540
541 for algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
542 let key = Key::generate(*algorithm).unwrap();
543 let tag = sign(&key, HELLO_WORLD_GOOD).unwrap();
544 println!("{key:?}");
545 assert!(verify(&key, HELLO_WORLD_GOOD, tag.as_ref()).is_ok());
546 assert!(verify(&key, HELLO_WORLD_BAD, tag.as_ref()).is_err());
547 }
548 }
549
550 #[test]
551 fn cmac_coverage() {
552 assert_ne!(AES_128, AES_256);
555 assert_ne!(AES_192, AES_256);
556
557 for &alg in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
558 let key_bytes = vec![0u8; alg.key_len()];
560 let key = Key::new(alg, &key_bytes).unwrap();
561 let mut ctx = Context::with_key(&key);
562 ctx.update(b"hello, world").unwrap();
563 let ctx_clone = ctx.clone();
564
565 let orig_tag = ctx.sign().unwrap();
566 let clone_tag = ctx_clone.sign().unwrap();
567 assert_eq!(orig_tag.as_ref(), clone_tag.as_ref());
568 assert_eq!(orig_tag.clone().as_ref(), clone_tag.as_ref());
569 }
570 }
571
572 #[test]
573 fn cmac_context_test() {
574 let key = Key::generate(AES_192).unwrap();
575
576 let mut ctx = Context::with_key(&key);
577 ctx.update(b"hello").unwrap();
578 ctx.update(b", ").unwrap();
579 ctx.update(b"world").unwrap();
580 let tag1 = ctx.sign().unwrap();
581
582 let tag2 = sign(&key, b"hello, world").unwrap();
583 assert_eq!(tag1.as_ref(), tag2.as_ref());
584 }
585
586 #[test]
587 fn cmac_multi_part_test() {
588 let parts = ["hello", ", ", "world"];
589
590 for &algorithm in &[AES_128, AES_256] {
591 let key = Key::generate(algorithm).unwrap();
592
593 let mut ctx = Context::with_key(&key);
595 for part in &parts {
596 ctx.update(part.as_bytes()).unwrap();
597 }
598 let tag = ctx.sign().unwrap();
599
600 let mut msg = Vec::<u8>::new();
602 for part in &parts {
603 msg.extend(part.as_bytes());
604 }
605 assert!(verify(&key, &msg, tag.as_ref()).is_ok());
606 }
607 }
608
609 #[test]
610 fn cmac_key_new_test() {
611 let key_128 = [0u8; 16];
613 let key_192 = [0u8; 24];
614 let key_256 = [0u8; 32];
615 let key_3des = [0u8; 24];
616
617 let k1 = Key::new(AES_128, &key_128).unwrap();
618 let k2 = Key::new(AES_192, &key_192).unwrap();
619 let k3 = Key::new(AES_256, &key_256).unwrap();
620 let k4 = Key::new(DES_EDE3_FOR_LEGACY_USE_ONLY, &key_3des).unwrap();
621
622 let data = b"test message";
623
624 let _ = sign(&k1, data).unwrap();
626 let _ = sign(&k2, data).unwrap();
627 let _ = sign(&k3, data).unwrap();
628 let _ = sign(&k4, data).unwrap();
629 }
630
631 #[test]
632 fn cmac_key_new_wrong_length_test() {
633 let key_256 = [0u8; 32];
634 assert!(Key::new(AES_128, &key_256).is_err());
636 }
637
638 #[test]
639 fn cmac_algorithm_properties() {
640 assert_eq!(AES_128.key_len(), 16);
641 assert_eq!(AES_128.tag_len(), 16);
642
643 assert_eq!(AES_192.key_len(), 24);
644 assert_eq!(AES_192.tag_len(), 16);
645
646 assert_eq!(AES_256.key_len(), 32);
647 assert_eq!(AES_256.tag_len(), 16);
648
649 assert_eq!(DES_EDE3_FOR_LEGACY_USE_ONLY.key_len(), 24);
650 assert_eq!(DES_EDE3_FOR_LEGACY_USE_ONLY.tag_len(), 8);
651 }
652
653 #[test]
654 fn cmac_empty_data() {
655 let key = Key::generate(AES_128).unwrap();
656
657 let tag = sign(&key, b"").unwrap();
659 assert!(verify(&key, b"", tag.as_ref()).is_ok());
660
661 let ctx = Context::with_key(&key);
663 let tag2 = ctx.sign().unwrap();
664 assert_eq!(tag.as_ref(), tag2.as_ref());
665 }
666
667 #[test]
668 fn des_ede3_cmac_test() {
669 let key = Key::generate(DES_EDE3_FOR_LEGACY_USE_ONLY).unwrap();
670 let data = b"test data for 3DES CMAC";
671
672 let tag = sign(&key, data).unwrap();
673 assert_eq!(tag.as_ref().len(), 8); assert!(verify(&key, data, tag.as_ref()).is_ok());
675 }
676
677 #[test]
678 fn cmac_sign_to_buffer_test() {
679 for &algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
680 let key = Key::generate(algorithm).unwrap();
681 let data = b"hello, world";
682
683 let mut output = vec![0u8; algorithm.tag_len()];
685 let result = sign_to_buffer(&key, data, &mut output).unwrap();
686 assert_eq!(result.len(), algorithm.tag_len());
687
688 let tag = sign(&key, data).unwrap();
690 assert_eq!(result, tag.as_ref());
691
692 let mut large_output = vec![0u8; algorithm.tag_len() + 10];
694 let result2 = sign_to_buffer(&key, data, &mut large_output).unwrap();
695 assert_eq!(result2.len(), algorithm.tag_len());
696 assert_eq!(result2, tag.as_ref());
697 }
698 }
699
700 #[test]
701 fn cmac_sign_to_buffer_too_small_test() {
702 let key = Key::generate(AES_128).unwrap();
703 let data = b"hello";
704
705 let mut small_buffer = vec![0u8; AES_128.tag_len() - 1];
707 assert!(sign_to_buffer(&key, data, &mut small_buffer).is_err());
708
709 let mut empty_buffer = vec![];
711 assert!(sign_to_buffer(&key, data, &mut empty_buffer).is_err());
712 }
713
714 #[test]
715 fn cmac_context_verify_test() {
716 for &algorithm in &[AES_128, AES_192, AES_256, DES_EDE3_FOR_LEGACY_USE_ONLY] {
717 let key = Key::generate(algorithm).unwrap();
718 let data = b"hello, world";
719
720 let tag = sign(&key, data).unwrap();
722
723 let mut ctx = Context::with_key(&key);
725 ctx.update(data).unwrap();
726 assert!(ctx.verify(tag.as_ref()).is_ok());
727
728 let mut ctx2 = Context::with_key(&key);
730 ctx2.update(data).unwrap();
731 let wrong_tag = vec![0u8; algorithm.tag_len()];
732 assert!(ctx2.verify(&wrong_tag).is_err());
733
734 let mut ctx3 = Context::with_key(&key);
736 ctx3.update(b"wrong data").unwrap();
737 assert!(ctx3.verify(tag.as_ref()).is_err());
738 }
739 }
740
741 #[test]
742 fn cmac_context_verify_multipart_test() {
743 let key = Key::generate(AES_256).unwrap();
744 let parts = ["hello", ", ", "world"];
745
746 let mut full_msg = Vec::new();
748 for part in &parts {
749 full_msg.extend_from_slice(part.as_bytes());
750 }
751 let tag = sign(&key, &full_msg).unwrap();
752
753 let mut ctx = Context::with_key(&key);
755 for part in &parts {
756 ctx.update(part.as_bytes()).unwrap();
757 }
758 assert!(ctx.verify(tag.as_ref()).is_ok());
759
760 let mut ctx2 = Context::with_key(&key);
762 ctx2.update(parts[0].as_bytes()).unwrap();
763 ctx2.update(parts[1].as_bytes()).unwrap();
764 assert!(ctx2.verify(tag.as_ref()).is_err());
766 }
767}