1#![allow(clippy::needless_range_loop)]
11
12use ic_cipher::aes::{Aes128, Aes192, Aes256, BLOCK_LEN};
13use ic_core::traits::{Algorithm, BlockCipher, Mac, SelfTest};
14use ic_core::{ensure, Result, Zeroize};
15
16const RB: u8 = 0x87;
18
19fn dbl(block: &mut [u8; BLOCK_LEN]) {
21 let msb = block[0] >> 7;
22 let mut carry = 0u8;
23 for byte in block.iter_mut().rev() {
24 let next = *byte >> 7;
25 *byte = (*byte << 1) | carry;
26 carry = next;
27 }
28 block[BLOCK_LEN - 1] ^= RB & msb.wrapping_neg();
29}
30
31#[derive(Clone)]
33pub struct Cmac<C: BlockCipher + Clone> {
34 cipher: C,
35 k1: [u8; BLOCK_LEN],
36 k2: [u8; BLOCK_LEN],
37 acc: [u8; BLOCK_LEN],
38 buf: [u8; BLOCK_LEN],
39 buffered: usize,
40}
41
42impl<C: BlockCipher + Clone> Drop for Cmac<C> {
43 fn drop(&mut self) {
44 self.k1.zeroize();
45 self.k2.zeroize();
46 self.acc.zeroize();
47 self.buf.zeroize();
48 }
49}
50
51impl<C: BlockCipher + Clone> Cmac<C> {
52 fn build(cipher: C) -> Result<Self> {
53 let mut l = [0u8; BLOCK_LEN];
55 cipher.encrypt_block(&mut l)?;
56 let mut k1 = l;
57 dbl(&mut k1);
58 let mut k2 = k1;
59 dbl(&mut k2);
60 l.zeroize();
61 Ok(Self {
62 cipher,
63 k1,
64 k2,
65 acc: [0u8; BLOCK_LEN],
66 buf: [0u8; BLOCK_LEN],
67 buffered: 0,
68 })
69 }
70
71 fn absorb(&mut self, block: &[u8]) -> Result<()> {
72 for i in 0..BLOCK_LEN {
73 self.acc[i] ^= block[i];
74 }
75 let mut tmp = self.acc;
76 self.cipher.encrypt_block(&mut tmp)?;
77 self.acc = tmp;
78 Ok(())
79 }
80
81 fn absorb_buffered(&mut self) {
82 let block = self.buf;
83 if self.absorb(&block).is_err() {
87 self.acc = [0xFFu8; BLOCK_LEN];
88 }
89 self.buffered = 0;
90 }
91}
92
93pub type CmacTag = [u8; BLOCK_LEN];
95
96macro_rules! cmac_variant {
97 (
98 $name:ident, $inner:ty, $id:literal, $disp:literal, $keylen:literal,
99 $kat_key:literal, $kat_tag:literal
100 ) => {
101 #[doc = concat!($disp, " (SP 800-38B).")]
102 pub type $name = Cmac<$inner>;
103
104 impl Algorithm for Cmac<$inner> {
105 const ID: &'static str = $id;
106 const NAME: &'static str = $disp;
107 }
108
109 impl SelfTest for Cmac<$inner> {
110 fn self_test() -> Result<()> {
111 let mut key = [0u8; $keylen];
113 ic_core::codec::hex_decode($kat_key.as_bytes(), &mut key)?;
114 let tag = <Self as Mac>::mac(&key, b"")?;
115 let mut want = [0u8; BLOCK_LEN];
116 ic_core::codec::hex_decode($kat_tag.as_bytes(), &mut want)?;
117 key.zeroize();
118 ensure!(ic_core::ct::verify(&want, &tag), SelfTestFailed, $id);
119 Ok(())
120 }
121 }
122 };
123}
124
125impl<C: BlockCipher + Clone> Mac for Cmac<C>
126where
127 Cmac<C>: Algorithm,
128{
129 type Tag = CmacTag;
130 const TAG_LEN: usize = BLOCK_LEN;
131
132 fn new(key: &[u8]) -> Result<Self> {
133 ic_core::module::operational()?;
134 ensure!(
135 C::BLOCK_LEN == BLOCK_LEN,
136 InvalidParameter,
137 "cmac needs a 128-bit block"
138 );
139 Self::build(C::new(key)?)
140 }
141
142 fn update(&mut self, mut data: &[u8]) {
143 if self.buffered > 0 {
144 let take = core::cmp::min(BLOCK_LEN - self.buffered, data.len());
145 self.buf[self.buffered..self.buffered + take].copy_from_slice(&data[..take]);
146 self.buffered += take;
147 data = &data[take..];
148 if self.buffered < BLOCK_LEN || data.is_empty() {
151 return;
152 }
153 self.absorb_buffered();
154 }
155 while data.len() > BLOCK_LEN {
156 let (block, rest) = data.split_at(BLOCK_LEN);
157 let _ = self.absorb(block);
158 data = rest;
159 }
160 self.buf[..data.len()].copy_from_slice(data);
161 self.buffered = data.len();
162 }
163
164 fn finalize(mut self) -> CmacTag {
165 let mut last = self.buf;
166 if self.buffered == BLOCK_LEN {
167 for i in 0..BLOCK_LEN {
169 last[i] ^= self.k1[i];
170 }
171 } else {
172 last[self.buffered] = 0x80;
174 for b in last[self.buffered + 1..].iter_mut() {
175 *b = 0;
176 }
177 for i in 0..BLOCK_LEN {
178 last[i] ^= self.k2[i];
179 }
180 }
181 if self.absorb(&last).is_err() {
182 return [0xFFu8; BLOCK_LEN];
183 }
184 last.zeroize();
185 self.acc
186 }
187}
188
189cmac_variant!(
190 CmacAes128,
191 Aes128,
192 "cmac-aes-128",
193 "CMAC-AES-128",
194 16,
195 "2b7e151628aed2a6abf7158809cf4f3c",
196 "bb1d6929e95937287fa37d129b756746"
197);
198cmac_variant!(
199 CmacAes192,
200 Aes192,
201 "cmac-aes-192",
202 "CMAC-AES-192",
203 24,
204 "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
205 "d17ddf46adaacde531cac483de7a9367"
206);
207cmac_variant!(
208 CmacAes256,
209 Aes256,
210 "cmac-aes-256",
211 "CMAC-AES-256",
212 32,
213 "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
214 "028962f61b7bf89efc6b551f4667d983"
215);
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220 use ic_core::codec::{hex, unhex};
221
222 const MSG: &str = "6bc1bee22e409f96e93d7e117393172a\
224 ae2d8a571e03ac9c9eb76fac45af8e51\
225 30c81c46a35ce411e5fbc1191a0a52ef\
226 f69f2445df4f9b17ad2b417be66c3710";
227
228 fn msg_prefix(len: usize) -> Vec<u8> {
229 unhex(&MSG.replace(char::is_whitespace, "")).unwrap()[..len].to_vec()
230 }
231
232 #[test]
233 fn sp800_38b_aes128_examples() {
234 let key = unhex("2b7e151628aed2a6abf7158809cf4f3c").unwrap();
235 for (len, want) in [
236 (0usize, "bb1d6929e95937287fa37d129b756746"),
237 (16, "070a16b46b4d4144f79bdd9dd04a287c"),
238 (40, "dfa66747de9ae63030ca32611497c827"),
239 (64, "51f0bebf7e3b9d92fc49741779363cfe"),
240 ] {
241 let tag = CmacAes128::mac(&key, &msg_prefix(len)).unwrap();
242 assert_eq!(hex(&tag), want, "AES-128 CMAC over {len} bytes");
243 }
244 }
245
246 #[test]
247 fn sp800_38b_aes192_examples() {
248 let key = unhex("8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b").unwrap();
249 for (len, want) in [
250 (0usize, "d17ddf46adaacde531cac483de7a9367"),
251 (16, "9e99a7bf31e710900662f65e617c5184"),
252 (64, "a1d5df0eed790f794d77589659f39a11"),
253 ] {
254 let tag = CmacAes192::mac(&key, &msg_prefix(len)).unwrap();
255 assert_eq!(hex(&tag), want, "AES-192 CMAC over {len} bytes");
256 }
257 }
258
259 #[test]
260 fn sp800_38b_aes256_examples() {
261 let key =
262 unhex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4").unwrap();
263 for (len, want) in [
264 (0usize, "028962f61b7bf89efc6b551f4667d983"),
265 (16, "28a7023f452e8f82bd4bf28d8c37c35c"),
266 (64, "e1992190549f6ed5696a2c056c315410"),
267 ] {
268 let tag = CmacAes256::mac(&key, &msg_prefix(len)).unwrap();
269 assert_eq!(hex(&tag), want, "AES-256 CMAC over {len} bytes");
270 }
271 }
272
273 #[test]
274 fn streaming_matches_one_shot() {
275 let key = unhex("2b7e151628aed2a6abf7158809cf4f3c").unwrap();
276 let data = msg_prefix(64);
277 for split in [0usize, 1, 15, 16, 17, 32, 63, 64] {
278 let mut m = CmacAes128::new(&key).unwrap();
279 m.update(&data[..split]);
280 m.update(&data[split..]);
281 assert_eq!(
282 m.finalize(),
283 CmacAes128::mac(&key, &data).unwrap(),
284 "split at {split}"
285 );
286 }
287 }
288
289 #[test]
290 fn subkey_doubling_reduces() {
291 let mut b = [0u8; BLOCK_LEN];
293 b[0] = 0x80;
294 dbl(&mut b);
295 assert_eq!(b[BLOCK_LEN - 1], RB);
296 assert_eq!(b[0], 0);
297 }
298
299 #[test]
300 fn verify_detects_tampering() {
301 let key = unhex("2b7e151628aed2a6abf7158809cf4f3c").unwrap();
302 let tag = CmacAes128::mac(&key, b"data").unwrap();
303 CmacAes128::verify(&key, b"data", &tag).unwrap();
304 assert!(CmacAes128::verify(&key, b"datb", &tag).is_err());
305 }
306
307 #[test]
308 fn self_tests_pass() {
309 CmacAes128::self_test().unwrap();
310 CmacAes192::self_test().unwrap();
311 CmacAes256::self_test().unwrap();
312 }
313}