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 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
187const MAX_CMAC_TAG_LEN: usize = 16;
189
190#[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#[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 {}
232unsafe 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 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 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 #[inline]
295 #[must_use]
296 pub fn algorithm(&self) -> Algorithm {
297 self.algorithm
298 }
299}
300
301pub 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 #[inline]
329 #[must_use]
330 pub fn with_key(key: &Key) -> Self {
331 Self { key: key.clone() }
332 }
333
334 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 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 #[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 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#[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#[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#[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 #[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 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 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 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 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 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 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 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 let tag = sign(&key, b"").unwrap();
658 assert!(verify(&key, b"", tag.as_ref()).is_ok());
659
660 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); 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 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 let tag = sign(&key, data).unwrap();
689 assert_eq!(result, tag.as_ref());
690
691 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 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 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 let tag = sign(&key, data).unwrap();
721
722 let mut ctx = Context::with_key(&key);
724 ctx.update(data).unwrap();
725 assert!(ctx.verify(tag.as_ref()).is_ok());
726
727 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 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 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 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 let mut ctx2 = Context::with_key(&key);
761 ctx2.update(parts[0].as_bytes()).unwrap();
762 ctx2.update(parts[1].as_bytes()).unwrap();
763 assert!(ctx2.verify(tag.as_ref()).is_err());
765 }
766}