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