1use crate::BlockCipher;
7
8#[inline]
9fn increment_be(counter: &mut [u8; 16]) {
10 for b in counter.iter_mut().rev() {
11 let (next, carry) = b.overflowing_add(1);
12 *b = next;
13 if !carry {
14 break;
15 }
16 }
17}
18
19#[inline]
20fn xor_in_place(dst: &mut [u8], src: &[u8]) {
21 for (d, s) in dst.iter_mut().zip(src.iter()) {
22 *d ^= *s;
23 }
24}
25
26#[inline]
27fn rb_for(block_len: usize) -> u8 {
28 match block_len {
29 8 => 0x1b,
30 16 => 0x87,
31 _ => panic!("CMAC only supports 64-bit or 128-bit block ciphers"),
32 }
33}
34
35fn dbl(block: &[u8]) -> Vec<u8> {
36 let mut out = vec![0u8; block.len()];
37 let mut carry = 0u8;
38 for (o, &b) in out.iter_mut().rev().zip(block.iter().rev()) {
39 *o = (b << 1) | carry;
40 carry = b >> 7;
41 }
42 if carry != 0 {
43 let last = out.len() - 1;
44 out[last] ^= rb_for(block.len());
45 }
46 out
47}
48
49fn cmac_compute<C: BlockCipher>(cipher: &C, data: &[u8]) -> [u8; 16] {
50 assert_eq!(C::BLOCK_LEN, 16, "EAX requires a 128-bit block cipher");
51 let blk = C::BLOCK_LEN;
52 let mut l = vec![0u8; blk];
53 cipher.encrypt(&mut l);
54 let k1 = dbl(&l);
55 let k2 = dbl(&k1);
56
57 let n = if data.is_empty() {
58 1
59 } else {
60 data.len().div_ceil(blk)
61 };
62 let last_complete = !data.is_empty() && data.len().is_multiple_of(blk);
63
64 let mut x = vec![0u8; blk];
65 let mut y = vec![0u8; blk];
66
67 for block in data.chunks(blk).take(n.saturating_sub(1)) {
68 y.copy_from_slice(&x);
69 xor_in_place(&mut y, block);
70 cipher.encrypt(&mut y);
71 x.copy_from_slice(&y);
72 }
73
74 let mut m_last = vec![0u8; blk];
75 if last_complete {
76 let start = (n - 1) * blk;
77 m_last.copy_from_slice(&data[start..start + blk]);
78 xor_in_place(&mut m_last, &k1);
79 } else {
80 let start = (n - 1) * blk;
81 let rem = data.len().saturating_sub(start);
82 if rem != 0 {
83 m_last[..rem].copy_from_slice(&data[start..]);
84 }
85 m_last[rem] = 0x80;
86 xor_in_place(&mut m_last, &k2);
87 }
88
89 xor_in_place(&mut m_last, &x);
90 cipher.encrypt(&mut m_last);
91 m_last.try_into().expect("CMAC output is one block")
92}
93
94fn eax_omac<C: BlockCipher>(cipher: &C, domain: u8, data: &[u8]) -> [u8; 16] {
95 let mut prefixed = Vec::with_capacity(16 + data.len());
96 prefixed.extend_from_slice(&[0u8; 15]);
97 prefixed.push(domain);
98 prefixed.extend_from_slice(data);
99 cmac_compute(cipher, &prefixed)
100}
101
102fn ctr_apply<C: BlockCipher>(cipher: &C, initial_counter: &[u8; 16], data: &mut [u8]) {
103 let mut counter = *initial_counter;
104 for chunk in data.chunks_mut(16) {
105 let mut stream = counter;
106 cipher.encrypt(&mut stream);
107 for i in 0..chunk.len() {
108 chunk[i] ^= stream[i];
109 }
110 increment_be(&mut counter);
111 }
112}
113
114pub struct Eax<C> {
116 cipher: C,
117}
118
119impl<C> Eax<C> {
120 pub fn new(cipher: C) -> Self {
122 Self { cipher }
123 }
124
125 pub fn cipher(&self) -> &C {
127 &self.cipher
128 }
129}
130
131impl<C: BlockCipher> Eax<C> {
132 #[must_use]
134 pub fn encrypt(&self, nonce: &[u8], aad: &[u8], data: &mut [u8]) -> [u8; 16] {
135 assert_eq!(C::BLOCK_LEN, 16, "EAX requires a 128-bit block cipher");
136
137 let n_tag = eax_omac(&self.cipher, 0, nonce);
138 let h_tag = eax_omac(&self.cipher, 1, aad);
139
140 ctr_apply(&self.cipher, &n_tag, data);
141 let c_tag = eax_omac(&self.cipher, 2, data);
142
143 let mut tag = [0u8; 16];
144 for i in 0..16 {
145 tag[i] = n_tag[i] ^ h_tag[i] ^ c_tag[i];
146 }
147 tag
148 }
149
150 pub fn decrypt(&self, nonce: &[u8], aad: &[u8], data: &mut [u8], tag: &[u8; 16]) -> bool {
154 assert_eq!(C::BLOCK_LEN, 16, "EAX requires a 128-bit block cipher");
155
156 let n_tag = eax_omac(&self.cipher, 0, nonce);
157 let h_tag = eax_omac(&self.cipher, 1, aad);
158 let c_tag = eax_omac(&self.cipher, 2, data);
159 let mut expected = [0u8; 16];
160 for i in 0..16 {
161 expected[i] = n_tag[i] ^ h_tag[i] ^ c_tag[i];
162 }
163
164 if crate::ct::constant_time_eq_mask(&expected, tag) != u8::MAX {
165 return false;
166 }
167
168 ctr_apply(&self.cipher, &n_tag, data);
169 true
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::Eax;
176 use crate::Aes128;
177
178 fn unhex_ws(input: &str) -> Vec<u8> {
179 let compact: String = input.chars().filter(|c| !c.is_whitespace()).collect();
180 let mut out = Vec::with_capacity(compact.len() / 2);
181 let bytes = compact.as_bytes();
182 let mut i = 0usize;
183 while i + 1 < bytes.len() {
184 let hi = (bytes[i] as char).to_digit(16).expect("hex") as u8;
185 let lo = (bytes[i + 1] as char).to_digit(16).expect("hex") as u8;
186 out.push((hi << 4) | lo);
187 i += 2;
188 }
189 out
190 }
191
192 #[test]
193 fn eax_aes128_eprint_2003_069_known_vectors() {
194 let vectors = [
200 (
201 "233952dee4d5ed5f9b9c6d6ff80ff478",
202 "62ec67f9c3a4a407fcb2a8c49031a8b3",
203 "6bfb914fd07eae6b",
204 "",
205 "",
206 "e037830e8389f27b025a2d6527e79d01",
207 ),
208 (
209 "91945d3f4dcbee0bf45ef52255f095a4",
210 "becaf043b0a23d843194ba972c66debd",
211 "fa3bfd4806eb53fa",
212 "f7fb",
213 "19dd",
214 "5c4c9331049d0bdab0277408f67967e5",
215 ),
216 (
217 "01f74ad64077f2e704c0f60ada3dd523",
218 "70c3db4f0d26368400a10ed05d2bff5e",
219 "234a3463c1264ac6",
220 "1a47cb4933",
221 "d851d5bae0",
222 "3a59f238a23e39199dc9266626c40f80",
223 ),
224 (
225 "d07cf6cbb7f313bdde66b727afd3c5e8",
226 "8408dfff3c1a2b1292dc199e46b7d617",
227 "33cce2eabff5a79d",
228 "481c9e39b1",
229 "632a9d131a",
230 "d4c168a4225d8e1ff755939974a7bede",
231 ),
232 (
233 "35b6d0580005bbc12b0587124557d2c2",
234 "fdb6b06676eedc5c61d74276e1f8e816",
235 "aeb96eaebe2970e9",
236 "40d0c07da5e4",
237 "071dfe16c675",
238 "cb0677e536f73afe6a14b74ee49844dd",
239 ),
240 (
241 "bd8e6e11475e60b268784c38c62feb22",
242 "6eac5c93072d8e8513f750935e46da1b",
243 "d4482d1ca78dce0f",
244 "4de3b35c3fc039245bd1fb7d",
245 "835bb4f15d743e350e728414",
246 "abb8644fd6ccb86947c5e10590210a4f",
247 ),
248 (
249 "7c77d6e813bed5ac98baa417477a2e7d",
250 "1a8c98dcd73d38393b2bf1569deefc19",
251 "65d2017990d62528",
252 "8b0a79306c9ce7ed99dae4f87f8dd61636",
253 "02083e3979da014812f59f11d52630da30",
254 "137327d10649b0aa6e1c181db617d7f2",
255 ),
256 (
257 "5fff20cafab119ca2fc73549e20f5b0d",
258 "dde59b97d722156d4d9aff2bc7559826",
259 "54b9f04e6a09189a",
260 "1bda122bce8a8dbaf1877d962b8592dd2d56",
261 "2ec47b2c4954a489afc7ba4897edcdae8cc3",
262 "3b60450599bd02c96382902aef7f832a",
263 ),
264 (
265 "a4a4782bcffd3ec5e7ef6d8c34a56123",
266 "b781fcf2f75fa5a8de97a9ca48e522ec",
267 "899a175897561d7e",
268 "6cf36720872b8513f6eab1a8a44438d5ef11",
269 "0de18fd0fdd91e7af19f1d8ee8733938b1e8",
270 "e7f6d2231618102fdb7fe55ff1991700",
271 ),
272 (
273 "8395fcf1e95bebd697bd010bc766aac3",
274 "22e7add93cfc6393c57ec0b3c17d6b44",
275 "126735fcc320d25a",
276 "ca40d7446e545ffaed3bd12a740a659ffbbb3ceab7",
277 "cb8920f87a6c75cff39627b56e3ed197c552d295a7",
278 "cfc46afc253b4652b1af3795b124ab6e",
279 ),
280 ];
281
282 for (idx, (key_hex, nonce_hex, aad_hex, pt_hex, ct_hex, tag_hex)) in
283 vectors.iter().enumerate()
284 {
285 let key = <[u8; 16]>::try_from(unhex_ws(key_hex)).expect("16-byte key");
286 let nonce = unhex_ws(nonce_hex);
287 let aad = unhex_ws(aad_hex);
288 let mut plaintext = unhex_ws(pt_hex);
289 let expected_ciphertext = unhex_ws(ct_hex);
290 let expected_tag = <[u8; 16]>::try_from(unhex_ws(tag_hex)).expect("16-byte tag");
291
292 let eax = Eax::new(Aes128::new(&key));
293 let tag = eax.encrypt(&nonce, &aad, &mut plaintext);
294 assert_eq!(
295 plaintext, expected_ciphertext,
296 "ciphertext mismatch for EAX KAT #{idx}"
297 );
298 assert_eq!(tag, expected_tag, "tag mismatch for EAX KAT #{idx}");
299
300 assert!(
301 eax.decrypt(&nonce, &aad, &mut plaintext, &tag),
302 "decrypt failed for EAX KAT #{idx}"
303 );
304 assert_eq!(
305 plaintext,
306 unhex_ws(pt_hex),
307 "plaintext mismatch after decrypt for EAX KAT #{idx}"
308 );
309 }
310 }
311
312 #[test]
313 fn eax_tamper_rejected() {
314 let key = [0x11u8; 16];
315 let nonce = [0x22u8; 16];
316 let aad = b"aad";
317 let mut data = b"eax data".to_vec();
318 let eax = Eax::new(Aes128::new(&key));
319 let tag = eax.encrypt(&nonce, aad, &mut data);
320
321 data[0] ^= 1;
322 let snapshot = data.clone();
323 assert!(!eax.decrypt(&nonce, aad, &mut data, &tag));
324 assert_eq!(data, snapshot);
325 }
326
327 #[test]
328 fn eax_roundtrip_various_lengths() {
329 let key = [0x42u8; 16];
330 let eax = Eax::new(Aes128::new(&key));
331 for msg_len in [0usize, 1, 2, 15, 16, 17, 31, 32, 33] {
332 let nonce = vec![0x24; 13];
333 let aad = vec![0x35; 11];
334 let mut data = vec![0u8; msg_len];
335 for (i, b) in data.iter_mut().enumerate() {
336 *b = u8::try_from(i & 0xff).expect("byte");
337 }
338 let original = data.clone();
339 let tag = eax.encrypt(&nonce, &aad, &mut data);
340 assert!(eax.decrypt(&nonce, &aad, &mut data, &tag));
341 assert_eq!(data, original);
342 }
343 }
344}