#![deny(unsafe_code)]
#![deny(missing_docs)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::panic)]
#![allow(clippy::arithmetic_side_effects)]
use aes::cipher::{BlockEncrypt, KeyInit};
use aes::{Aes128, Aes192, Aes256};
use thiserror::Error;
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum CmacError {
#[error("Invalid key length: CMAC keys must be 16, 24, or 32 bytes")]
InvalidKeyLength {
actual: usize,
},
#[error("CMAC computation failed: {0}")]
ComputationError(String),
}
#[derive(Clone)]
pub struct CmacTag {
tag: [u8; 16],
}
impl std::fmt::Debug for CmacTag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CmacTag").field("tag", &"[MAC TAG]").finish()
}
}
impl CmacTag {
#[must_use]
pub fn tag(&self) -> &[u8; 16] {
&self.tag
}
}
pub type Cmac128 = CmacTag;
pub type Cmac192 = CmacTag;
pub type Cmac256 = CmacTag;
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
struct CmacSubkeys {
k1: [u8; 16],
k2: [u8; 16],
}
impl core::fmt::Debug for CmacSubkeys {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CmacSubkeys").field("k1", &"[REDACTED]").field("k2", &"[REDACTED]").finish()
}
}
#[inline(always)]
fn xor_block(a: &mut [u8; 16], b: &[u8; 16]) {
for (a_byte, b_byte) in a.iter_mut().zip(b.iter()) {
*a_byte ^= b_byte;
}
}
#[inline(always)]
#[allow(clippy::arithmetic_side_effects)]
fn left_shift_block(block: &[u8; 16]) -> ([u8; 16], u8) {
let mut result = [0u8; 16];
let mut overflow = 0u8;
for (i, ¤t_byte) in block.iter().enumerate().rev() {
let new_byte = (current_byte << 1) | overflow;
if let Some(r) = result.get_mut(i) {
*r = new_byte;
}
overflow = (current_byte >> 7) & 1;
}
(result, overflow)
}
fn generate_subkeys(key: &[u8]) -> Result<CmacSubkeys, CmacError> {
match key.len() {
16 | 24 | 32 => {}
_ => return Err(CmacError::InvalidKeyLength { actual: key.len() }),
}
let l_block = match key.len() {
16 => {
let cipher = Aes128::new_from_slice(key)
.map_err(|e| CmacError::ComputationError(e.to_string()))?;
let mut block = [0u8; 16];
cipher.encrypt_block((&mut block).into());
block
}
24 => {
let cipher = Aes192::new_from_slice(key)
.map_err(|e| CmacError::ComputationError(e.to_string()))?;
let mut block = [0u8; 16];
cipher.encrypt_block((&mut block).into());
block
}
32 => {
let cipher = Aes256::new_from_slice(key)
.map_err(|e| CmacError::ComputationError(e.to_string()))?;
let mut block = [0u8; 16];
cipher.encrypt_block((&mut block).into());
block
}
_ => return Err(CmacError::InvalidKeyLength { actual: key.len() }),
};
const RB: [u8; 16] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x87,
];
let (k1_shifted, k1_msb) = left_shift_block(&l_block);
let mut k1 = k1_shifted;
if k1_msb == 1 {
xor_block(&mut k1, &RB);
}
let (k2_shifted, k2_msb) = left_shift_block(&k1);
let mut k2 = k2_shifted;
if k2_msb == 1 {
xor_block(&mut k2, &RB);
}
Ok(CmacSubkeys { k1, k2 })
}
fn compute_cmac_internal(key: &[u8], data: &[u8]) -> Result<[u8; 16], CmacError> {
match key.len() {
16 | 24 | 32 => {}
_ => return Err(CmacError::InvalidKeyLength { actual: key.len() }),
}
let subkeys = generate_subkeys(key)?;
let cipher = match key.len() {
16 => CipherType::Aes128(
Aes128::new_from_slice(key).map_err(|e| CmacError::ComputationError(e.to_string()))?,
),
24 => CipherType::Aes192(
Aes192::new_from_slice(key).map_err(|e| CmacError::ComputationError(e.to_string()))?,
),
32 => CipherType::Aes256(
Aes256::new_from_slice(key).map_err(|e| CmacError::ComputationError(e.to_string()))?,
),
_ => return Err(CmacError::InvalidKeyLength { actual: key.len() }),
};
let data_len = data.len();
let num_complete_blocks = data_len / 16;
let incomplete_block_size = data_len % 16;
let mut c_i = [0u8; 16];
let total_blocks = if data_len == 0 { 1 } else { data_len.div_ceil(16) };
if total_blocks > 1 {
for i in 0..(total_blocks - 1) {
let mut block = [0u8; 16];
let start = i * 16;
let end = (i + 1) * 16;
let data_slice = data
.get(start..end)
.ok_or_else(|| CmacError::ComputationError(format!("Block {} out of bounds", i)))?;
block.copy_from_slice(data_slice);
xor_block(&mut block, &c_i);
match &cipher {
CipherType::Aes128(c) => c.encrypt_block((&mut block).into()),
CipherType::Aes192(c) => c.encrypt_block((&mut block).into()),
CipherType::Aes256(c) => c.encrypt_block((&mut block).into()),
}
c_i = block;
}
}
let mut final_block = [0u8; 16];
if data_len == 0 {
#[allow(clippy::indexing_slicing)] {
final_block[0] = 0x80;
}
xor_block(&mut final_block, &subkeys.k2);
} else if incomplete_block_size == 0 {
let block_idx = num_complete_blocks - 1;
let start = block_idx * 16;
let end = (block_idx + 1) * 16;
let data_slice = data
.get(start..end)
.ok_or_else(|| CmacError::ComputationError("Final block out of bounds".to_string()))?;
final_block.copy_from_slice(data_slice);
xor_block(&mut final_block, &c_i);
xor_block(&mut final_block, &subkeys.k1);
} else {
let start = num_complete_blocks * 16;
let incomplete_data = data.get(start..).ok_or_else(|| {
CmacError::ComputationError("Incomplete block out of bounds".to_string())
})?;
let dest_slice = final_block.get_mut(..incomplete_block_size).ok_or_else(|| {
CmacError::ComputationError("Final block slice out of bounds".to_string())
})?;
dest_slice.copy_from_slice(incomplete_data);
if let Some(pad_byte) = final_block.get_mut(incomplete_block_size) {
*pad_byte = 0x80; }
xor_block(&mut final_block, &c_i);
xor_block(&mut final_block, &subkeys.k2);
}
match &cipher {
CipherType::Aes128(c) => c.encrypt_block((&mut final_block).into()),
CipherType::Aes192(c) => c.encrypt_block((&mut final_block).into()),
CipherType::Aes256(c) => c.encrypt_block((&mut final_block).into()),
}
Ok(final_block)
}
enum CipherType {
Aes128(Aes128),
Aes192(Aes192),
Aes256(Aes256),
}
pub fn cmac_128(key: &[u8], data: &[u8]) -> Result<Cmac128, CmacError> {
if key.len() != 16 {
return Err(CmacError::InvalidKeyLength { actual: key.len() });
}
let tag = compute_cmac_internal(key, data)?;
Ok(Cmac128 { tag })
}
pub fn cmac_192(key: &[u8], data: &[u8]) -> Result<Cmac192, CmacError> {
if key.len() != 24 {
return Err(CmacError::InvalidKeyLength { actual: key.len() });
}
let tag = compute_cmac_internal(key, data)?;
Ok(Cmac192 { tag })
}
pub fn cmac_256(key: &[u8], data: &[u8]) -> Result<Cmac256, CmacError> {
if key.len() != 32 {
return Err(CmacError::InvalidKeyLength { actual: key.len() });
}
let tag = compute_cmac_internal(key, data)?;
Ok(Cmac256 { tag })
}
#[must_use]
pub fn verify_cmac_128(key: &[u8], data: &[u8], tag: &[u8]) -> bool {
use subtle::ConstantTimeEq;
let tag_valid: bool = tag.len().ct_eq(&16).into();
let expected_tag_result = cmac_128(key, data);
match expected_tag_result {
Ok(cmac) => {
let tags_match: bool = cmac.tag.ct_eq(tag).into();
tag_valid & tags_match
}
Err(_) => false,
}
}
#[must_use]
pub fn verify_cmac_192(key: &[u8], data: &[u8], tag: &[u8]) -> bool {
use subtle::ConstantTimeEq;
let tag_valid: bool = tag.len().ct_eq(&16).into();
let expected_tag_result = cmac_192(key, data);
match expected_tag_result {
Ok(cmac) => {
let tags_match: bool = cmac.tag.ct_eq(tag).into();
tag_valid & tags_match
}
Err(_) => false,
}
}
#[must_use]
pub fn verify_cmac_256(key: &[u8], data: &[u8], tag: &[u8]) -> bool {
use subtle::ConstantTimeEq;
let tag_valid: bool = tag.len().ct_eq(&16).into();
let expected_tag_result = cmac_256(key, data);
match expected_tag_result {
Ok(cmac) => {
let tags_match: bool = cmac.tag.ct_eq(tag).into();
tag_valid & tags_match
}
Err(_) => false,
}
}
#[cfg(test)]
#[allow(clippy::panic)] #[allow(clippy::unwrap_used)] mod tests {
use super::*;
#[test]
fn test_cmac_128_valid_key_length_succeeds() {
let key = vec![0u8; 16];
let data = b"test data";
let result = cmac_128(&key, data);
assert!(result.is_ok(), "CMAC-128 should succeed with 16-byte key");
}
#[test]
fn test_cmac_128_invalid_key_fails() {
let key = vec![0u8; 32]; let data = b"test data";
let result = cmac_128(&key, data);
assert!(result.is_err(), "CMAC-128 should fail with 32-byte key");
match result {
Err(CmacError::InvalidKeyLength { actual: 32 }) => {}
_ => panic!("Expected InvalidKeyLength error"),
}
}
#[test]
fn test_cmac_192_valid_key_length_succeeds() {
let key = vec![0u8; 24];
let data = b"test data";
let result = cmac_192(&key, data);
assert!(result.is_ok(), "CMAC-192 should succeed with 24-byte key");
}
#[test]
fn test_cmac_192_invalid_key_fails() {
let key = vec![0u8; 32]; let data = b"test data";
let result = cmac_192(&key, data);
assert!(result.is_err(), "CMAC-192 should fail with 32-byte key");
match result {
Err(CmacError::InvalidKeyLength { actual: 32 }) => {}
Err(e) => panic!("Unexpected CMAC error: {:?}", e),
Ok(_) => panic!("Expected error but got success"),
}
}
#[test]
fn test_cmac_256_valid_key_length_succeeds() {
let key = vec![0u8; 32];
let data = b"test data";
let result = cmac_256(&key, data);
assert!(result.is_ok(), "CMAC-256 should succeed with 32-byte key");
}
#[test]
fn test_cmac_256_invalid_key_fails() {
let key = vec![0u8; 16]; let data = b"test data";
let result = cmac_256(&key, data);
assert!(result.is_err(), "CMAC-256 should fail with 16-byte key");
match result {
Err(CmacError::InvalidKeyLength { actual: 16 }) => {}
Err(e) => panic!("Unexpected CMAC error: {:?}", e),
Ok(_) => panic!("Expected error but got success"),
}
}
#[test]
fn test_cmac_different_data_produces_different_tags_succeeds() {
let key = vec![0u8; 32];
let data1 = b"data one";
let data2 = b"data two";
let tag1 = cmac_256(&key, data1).unwrap().tag;
let tag2 = cmac_256(&key, data2).unwrap().tag;
assert_ne!(tag1, tag2, "Different data should produce different tags");
}
#[test]
fn test_cmac_tag_length_is_16_bytes_has_correct_size() {
let key = vec![0u8; 32];
let data = b"test data";
let result = cmac_256(&key, data);
assert!(result.is_ok(), "CMAC-256 should succeed");
let tag = result.unwrap().tag;
assert_eq!(tag.len(), 16, "Tag should be 16 bytes");
}
#[test]
fn test_cmac_same_data_different_key_sizes_produce_different_tags_has_correct_size() {
let key128 = [0u8; 16];
let key192 = [0u8; 24];
let key256 = [0u8; 32];
let data = b"test data";
let tag128 = cmac_128(&key128, data).unwrap().tag;
let tag192 = cmac_192(&key192, data).unwrap().tag;
let tag256 = cmac_256(&key256, data).unwrap().tag;
assert_ne!(tag128, tag192, "Different key sizes should produce different tags");
assert_ne!(tag192, tag256, "Different key sizes should produce different tags");
}
#[test]
fn test_verify_cmac_128_accepts_valid_rejects_invalid_fails() {
let key = vec![0u8; 16];
let data = b"test data";
let cmac = cmac_128(&key, data).unwrap();
assert!(verify_cmac_128(&key, data, &cmac.tag));
let mut invalid_tag = cmac.tag;
invalid_tag[0] ^= 0xFF;
assert!(!verify_cmac_128(&key, data, &invalid_tag));
assert!(!verify_cmac_128(&key, b"different data", &cmac.tag));
}
#[test]
fn test_verify_cmac_192_accepts_valid_rejects_invalid_fails() {
let key = vec![0u8; 24];
let data = b"test data";
let cmac = cmac_192(&key, data).unwrap();
assert!(verify_cmac_192(&key, data, &cmac.tag));
let mut invalid_tag = cmac.tag;
invalid_tag[0] ^= 0xFF;
assert!(!verify_cmac_192(&key, data, &invalid_tag));
assert!(!verify_cmac_192(&key, b"different data", &cmac.tag));
}
#[test]
fn test_verify_cmac_256_accepts_valid_rejects_invalid_fails() {
let key = vec![0u8; 32];
let data = b"test data";
let cmac = cmac_256(&key, data).unwrap();
assert!(verify_cmac_256(&key, data, &cmac.tag));
let mut invalid_tag = cmac.tag;
invalid_tag[0] ^= 0xFF;
assert!(!verify_cmac_256(&key, data, &invalid_tag));
assert!(!verify_cmac_256(&key, b"different data", &cmac.tag));
}
#[test]
fn test_cmac_empty_data_succeeds() {
let key128 = vec![0u8; 16];
let key192 = vec![0u8; 24];
let key256 = vec![0u8; 32];
let data = b"";
assert!(cmac_128(&key128, data).is_ok());
assert!(cmac_192(&key192, data).is_ok());
assert!(cmac_256(&key256, data).is_ok());
}
#[test]
fn test_cmac_block_aligned_data_succeeds() {
let key = vec![0u8; 16];
let data = [1u8; 16];
let result = cmac_128(&key, &data);
assert!(result.is_ok());
let data2 = [2u8; 32];
let result2 = cmac_128(&key, &data2);
assert!(result2.is_ok());
}
#[test]
fn test_cmac_non_block_aligned_data_succeeds() {
let key = vec![0u8; 16];
let data = [1u8; 15];
let result = cmac_128(&key, &data);
assert!(result.is_ok());
let data2 = [2u8; 17];
let result2 = cmac_128(&key, &data2);
assert!(result2.is_ok());
}
#[test]
fn test_cmac_128_nist_test_vectors_match_spec_matches_expected() {
let key = [
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
0x4f, 0x3c,
];
let data = b"";
let cmac = cmac_128(&key, data).unwrap();
let expected = [
0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75,
0x67, 0x46,
];
assert_eq!(cmac.tag, expected, "CMAC-128 test vector 1 failed");
assert!(verify_cmac_128(&key, data, &expected));
let key = [
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
0x4f, 0x3c,
];
let data = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
0x17, 0x2a,
];
let cmac = cmac_128(&key, &data).unwrap();
let expected = [
0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a,
0x28, 0x7c,
];
assert_eq!(cmac.tag, expected, "CMAC-128 test vector 2 failed");
assert!(verify_cmac_128(&key, &data, &expected));
let key = [
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
0x4f, 0x3c,
];
let data = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac,
0x45, 0xaf, 0x8e, 0x51,
];
let cmac = cmac_128(&key, &data).unwrap();
let expected = [
0xce, 0x0c, 0xbf, 0x17, 0x38, 0xf4, 0xdf, 0x64, 0x28, 0xb1, 0xd9, 0x3b, 0xf1, 0x20,
0x81, 0xc9,
];
assert_eq!(cmac.tag, expected, "CMAC-128 test vector 3 failed");
assert!(verify_cmac_128(&key, &data, &expected));
let key = [
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
0x4f, 0x3c,
];
let data = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57,
];
let cmac = cmac_128(&key, &data).unwrap();
let expected = [
0x7d, 0x85, 0x44, 0x9e, 0xa6, 0xea, 0x19, 0xc8, 0x23, 0xa7, 0xbf, 0x78, 0x83, 0x7d,
0xfa, 0xde,
];
assert_eq!(cmac.tag, expected, "CMAC-128 test vector 4 failed");
assert!(verify_cmac_128(&key, &data, &expected));
}
#[test]
fn test_cmac_192_nist_test_vectors_match_spec_matches_expected() {
let key = [
0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90,
0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
];
let data = b"";
let cmac = cmac_192(&key, data).unwrap();
let expected = [
0xd1, 0x7d, 0xdf, 0x46, 0xad, 0xaa, 0xcd, 0xe5, 0x31, 0xca, 0xc4, 0x83, 0xde, 0x7a,
0x93, 0x67,
];
assert_eq!(cmac.tag, expected, "CMAC-192 test vector 1 failed");
assert!(verify_cmac_192(&key, data, &expected));
let key = [
0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90,
0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
];
let data = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
0x17, 0x2a,
];
let cmac = cmac_192(&key, &data).unwrap();
let expected = [
0x9e, 0x99, 0xa7, 0xbf, 0x31, 0xe7, 0x10, 0x90, 0x06, 0x62, 0xf6, 0x5e, 0x61, 0x7c,
0x51, 0x84,
];
assert_eq!(cmac.tag, expected, "CMAC-192 test vector 2 failed");
assert!(verify_cmac_192(&key, &data, &expected));
let key = [
0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90,
0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
];
let data = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac,
0x45, 0xaf, 0x8e, 0x51,
];
let cmac = cmac_192(&key, &data).unwrap();
let expected = [
0x9f, 0x1d, 0x26, 0xd1, 0x76, 0x38, 0x31, 0xa5, 0x8c, 0x40, 0x16, 0xc6, 0xa9, 0x7b,
0x0d, 0x4e,
];
assert_eq!(cmac.tag, expected, "CMAC-192 test vector 3 failed");
assert!(verify_cmac_192(&key, &data, &expected));
}
#[test]
fn test_cmac_256_nist_test_vectors_match_spec_matches_expected() {
let key = [
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d,
0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3,
0x09, 0x14, 0xdf, 0xf4,
];
let data = b"";
let cmac = cmac_256(&key, data).unwrap();
let expected = [
0x02, 0x89, 0x62, 0xf6, 0x1b, 0x7b, 0xf8, 0x9e, 0xfc, 0x6b, 0x55, 0x1f, 0x46, 0x67,
0xd9, 0x83,
];
assert_eq!(cmac.tag, expected, "CMAC-256 test vector 1 failed");
assert!(verify_cmac_256(&key, data, &expected));
let key = [
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d,
0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3,
0x09, 0x14, 0xdf, 0xf4,
];
let data = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
0x17, 0x2a,
];
let cmac = cmac_256(&key, &data).unwrap();
let expected = [
0x28, 0xa7, 0x02, 0x3f, 0x45, 0x2e, 0x8f, 0x82, 0xbd, 0x4b, 0xf2, 0x8d, 0x8c, 0x37,
0xc3, 0x5c,
];
assert_eq!(cmac.tag, expected, "CMAC-256 test vector 2 failed");
assert!(verify_cmac_256(&key, &data, &expected));
let key = [
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d,
0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3,
0x09, 0x14, 0xdf, 0xf4,
];
let data = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac,
0x45, 0xaf, 0x8e, 0x51,
];
let cmac = cmac_256(&key, &data).unwrap();
let expected = [
0x5a, 0x72, 0x2d, 0x2d, 0x85, 0x16, 0xf8, 0x54, 0xb8, 0x67, 0x7a, 0x53, 0x7b, 0x1b,
0x66, 0x9a,
];
assert_eq!(cmac.tag, expected, "CMAC-256 test vector 3 failed");
assert!(verify_cmac_256(&key, &data, &expected));
}
#[test]
fn test_cmac_subkey_generation_produces_distinct_keys_are_unique() {
let key = [0u8; 16];
let subkeys = generate_subkeys(&key).unwrap();
assert_ne!(subkeys.k1, subkeys.k2, "K1 and K2 should be different");
assert!(
subkeys.k1.iter().any(|&b| b != 0) || subkeys.k2.iter().any(|&b| b != 0),
"At least one subkey should be non-zero"
);
}
#[test]
fn test_cmac_subkeys_debug_redacted_succeeds() {
let key = [
0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
0x4f, 0x3c,
];
let subkeys = generate_subkeys(&key).unwrap();
let debug_output = format!("{:?}", subkeys);
assert!(debug_output.contains("[REDACTED]"), "CmacSubkeys Debug must redact key material");
for byte in &subkeys.k1 {
let hex = format!("{:02x}", byte);
if *byte != 0 {
assert!(
!debug_output.to_lowercase().contains(&hex),
"CmacSubkeys Debug leaks K1 byte: 0x{}",
hex
);
}
}
}
}