1use crate::BlockCipher;
8
9#[inline]
10fn dbl_block(block: [u8; 16]) -> [u8; 16] {
11 let mut out = [0u8; 16];
12 let mut carry = 0u8;
13 for i in (0..16).rev() {
14 out[i] = (block[i] << 1) | carry;
15 carry = block[i] >> 7;
16 }
17 if carry != 0 {
18 out[15] ^= 0x87;
19 }
20 out
21}
22
23#[inline]
24fn xor_block(a: [u8; 16], b: [u8; 16]) -> [u8; 16] {
25 let mut out = [0u8; 16];
26 for i in 0..16 {
27 out[i] = a[i] ^ b[i];
28 }
29 out
30}
31
32#[inline]
33fn xor_in_place(dst: &mut [u8], src: &[u8]) {
34 for (d, s) in dst.iter_mut().zip(src.iter()) {
35 *d ^= *s;
36 }
37}
38
39#[inline]
40fn rb_for(block_len: usize) -> u8 {
41 match block_len {
42 8 => 0x1b,
43 16 => 0x87,
44 _ => panic!("CMAC only supports 64-bit or 128-bit block ciphers"),
45 }
46}
47
48fn dbl(block: &[u8]) -> Vec<u8> {
49 let mut out = vec![0u8; block.len()];
50 let mut carry = 0u8;
51 for (o, &b) in out.iter_mut().rev().zip(block.iter().rev()) {
52 *o = (b << 1) | carry;
53 carry = b >> 7;
54 }
55 if carry != 0 {
56 let last = out.len() - 1;
57 out[last] ^= rb_for(block.len());
58 }
59 out
60}
61
62fn cmac_compute<C: BlockCipher>(cipher: &C, data: &[u8]) -> [u8; 16] {
63 assert_eq!(C::BLOCK_LEN, 16, "SIV requires a 128-bit block cipher");
64 let blk = C::BLOCK_LEN;
65 let mut l = vec![0u8; blk];
66 cipher.encrypt(&mut l);
67 let k1 = dbl(&l);
68 let k2 = dbl(&k1);
69
70 let n = if data.is_empty() {
71 1
72 } else {
73 data.len().div_ceil(blk)
74 };
75 let last_complete = !data.is_empty() && data.len().is_multiple_of(blk);
76
77 let mut x = vec![0u8; blk];
78 let mut y = vec![0u8; blk];
79
80 for block in data.chunks(blk).take(n.saturating_sub(1)) {
81 y.copy_from_slice(&x);
82 xor_in_place(&mut y, block);
83 cipher.encrypt(&mut y);
84 x.copy_from_slice(&y);
85 }
86
87 let mut m_last = vec![0u8; blk];
88 if last_complete {
89 let start = (n - 1) * blk;
90 m_last.copy_from_slice(&data[start..start + blk]);
91 xor_in_place(&mut m_last, &k1);
92 } else {
93 let start = (n - 1) * blk;
94 let rem = data.len().saturating_sub(start);
95 if rem != 0 {
96 m_last[..rem].copy_from_slice(&data[start..]);
97 }
98 m_last[rem] = 0x80;
99 xor_in_place(&mut m_last, &k2);
100 }
101
102 xor_in_place(&mut m_last, &x);
103 cipher.encrypt(&mut m_last);
104 m_last.try_into().expect("CMAC output is one block")
105}
106
107#[inline]
108fn cmac_block<C: BlockCipher>(cipher: &C, data: &[u8]) -> [u8; 16] {
109 cmac_compute(cipher, data)
110}
111
112fn s2v<C: BlockCipher>(mac_cipher: &C, components: &[&[u8]], plaintext: &[u8]) -> [u8; 16] {
113 let mut d = cmac_block(mac_cipher, &[0u8; 16]);
115 for component in components {
116 d = xor_block(dbl_block(d), cmac_block(mac_cipher, component));
117 }
118
119 let t = if plaintext.len() >= 16 {
120 let mut t = plaintext.to_vec();
121 let start = t.len() - 16;
122 for i in 0..16 {
123 t[start + i] ^= d[i];
124 }
125 t
126 } else {
127 let mut padded = [0u8; 16];
128 padded[..plaintext.len()].copy_from_slice(plaintext);
129 padded[plaintext.len()] = 0x80;
130 let mixed = xor_block(dbl_block(d), padded);
131 mixed.to_vec()
132 };
133
134 cmac_block(mac_cipher, &t)
135}
136
137#[inline]
138fn clear_siv_ctr_bits(counter: &mut [u8; 16]) {
139 counter[8] &= 0x7f;
141 counter[12] &= 0x7f;
142}
143
144#[inline]
145fn increment_be32(block: &mut [u8; 16]) {
146 for b in block[12..].iter_mut().rev() {
147 let (next, carry) = b.overflowing_add(1);
148 *b = next;
149 if !carry {
150 break;
151 }
152 }
153}
154
155fn ctr_apply<C: BlockCipher>(cipher: &C, initial_counter: &[u8; 16], data: &mut [u8]) {
156 let mut counter = *initial_counter;
157 for chunk in data.chunks_mut(16) {
158 let mut stream = counter;
159 cipher.encrypt(&mut stream);
160 for i in 0..chunk.len() {
161 chunk[i] ^= stream[i];
162 }
163 increment_be32(&mut counter);
164 }
165}
166
167pub struct Siv<C> {
169 mac_cipher: C,
170 ctr_cipher: C,
171}
172
173impl<C> Siv<C> {
174 pub fn new(mac_cipher: C, ctr_cipher: C) -> Self {
176 Self {
177 mac_cipher,
178 ctr_cipher,
179 }
180 }
181
182 pub fn mac_cipher(&self) -> &C {
184 &self.mac_cipher
185 }
186
187 pub fn ctr_cipher(&self) -> &C {
189 &self.ctr_cipher
190 }
191}
192
193impl<C: BlockCipher> Siv<C> {
194 pub fn encrypt_with_components(
196 &self,
197 components: &[&[u8]],
198 plaintext: &[u8],
199 ) -> (Vec<u8>, [u8; 16]) {
200 let tag = s2v(&self.mac_cipher, components, plaintext);
201 let mut counter = tag;
202 clear_siv_ctr_bits(&mut counter);
203
204 let mut ciphertext = plaintext.to_vec();
205 ctr_apply(&self.ctr_cipher, &counter, &mut ciphertext);
206 (ciphertext, tag)
207 }
208
209 pub fn decrypt_with_components(
211 &self,
212 components: &[&[u8]],
213 ciphertext: &mut [u8],
214 tag: &[u8; 16],
215 ) -> bool {
216 let mut counter = *tag;
217 clear_siv_ctr_bits(&mut counter);
218
219 let mut plaintext = ciphertext.to_vec();
220 ctr_apply(&self.ctr_cipher, &counter, &mut plaintext);
221 let expected = s2v(&self.mac_cipher, components, &plaintext);
222 if crate::ct::constant_time_eq_mask(&expected, tag) != u8::MAX {
223 crate::ct::zeroize_slice(&mut plaintext);
226 return false;
227 }
228 ciphertext.copy_from_slice(&plaintext);
229 true
230 }
231
232 pub fn encrypt(&self, nonce: &[u8], aad: &[u8], plaintext: &[u8]) -> (Vec<u8>, [u8; 16]) {
236 if nonce.is_empty() {
237 self.encrypt_with_components(&[aad], plaintext)
238 } else {
239 self.encrypt_with_components(&[aad, nonce], plaintext)
240 }
241 }
242
243 pub fn decrypt(&self, nonce: &[u8], aad: &[u8], ciphertext: &mut [u8], tag: &[u8; 16]) -> bool {
247 if nonce.is_empty() {
248 self.decrypt_with_components(&[aad], ciphertext, tag)
249 } else {
250 self.decrypt_with_components(&[aad, nonce], ciphertext, tag)
251 }
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::Siv;
258 use crate::Aes128;
259
260 fn unhex_ws(input: &str) -> Vec<u8> {
261 let compact: String = input.chars().filter(|c| !c.is_whitespace()).collect();
262 let mut out = Vec::with_capacity(compact.len() / 2);
263 let bytes = compact.as_bytes();
264 let mut i = 0usize;
265 while i + 1 < bytes.len() {
266 let hi = (bytes[i] as char).to_digit(16).expect("hex") as u8;
267 let lo = (bytes[i + 1] as char).to_digit(16).expect("hex") as u8;
268 out.push((hi << 4) | lo);
269 i += 2;
270 }
271 out
272 }
273
274 #[test]
275 fn rfc5297_a1_deterministic_vector() {
276 let key = <[u8; 32]>::try_from(unhex_ws(
278 "fffefdfc fbfaf9f8 f7f6f5f4 f3f2f1f0
279 f0f1f2f3 f4f5f6f7 f8f9fafb fcfdfeff",
280 ))
281 .expect("key");
282 let k1: [u8; 16] = key[..16].try_into().expect("k1");
283 let k2: [u8; 16] = key[16..].try_into().expect("k2");
284 let aad = unhex_ws("10111213 14151617 18191a1b 1c1d1e1f 20212223 24252627");
285 let nonce: [u8; 0] = [];
286 let plaintext = unhex_ws("11223344 55667788 99aabbcc ddee");
287 let expected_tag =
288 <[u8; 16]>::try_from(unhex_ws("85632d07 c6e8f37f 950acd32 0a2ecc93")).expect("tag");
289 let expected_ct = unhex_ws("40c02b96 90c4dc04 daef7f6a fe5c");
290
291 let siv = Siv::new(Aes128::new(&k1), Aes128::new(&k2));
292 let (ct, tag) = siv.encrypt(&nonce, &aad, &plaintext);
293 assert_eq!(tag, expected_tag);
294 assert_eq!(ct, expected_ct);
295
296 let mut roundtrip = ct.clone();
297 assert!(siv.decrypt(&nonce, &aad, &mut roundtrip, &tag));
298 assert_eq!(roundtrip, plaintext);
299 }
300
301 #[test]
302 fn rfc5297_a2_nonce_based_vector() {
303 let key = <[u8; 32]>::try_from(unhex_ws(
305 "7f7e7d7c 7b7a7978 77767574 73727170
306 40414243 44454647 48494a4b 4c4d4e4f",
307 ))
308 .expect("key");
309 let k1: [u8; 16] = key[..16].try_into().expect("k1");
310 let k2: [u8; 16] = key[16..].try_into().expect("k2");
311 let ad1 = unhex_ws(
312 "00112233 44556677 8899aabb ccddeeff
313 deaddada deaddada ffeeddcc bbaa9988
314 77665544 33221100",
315 );
316 let ad2 = unhex_ws("10203040 50607080 90a0");
317 let nonce = unhex_ws("09f91102 9d74e35b d84156c5 635688c0");
318 let plaintext = unhex_ws(
319 "74686973 20697320 736f6d65 20706c61
320 696e7465 78742074 6f20656e 63727970
321 74207573 696e6720 5349562d 414553",
322 );
323 let expected_tag =
324 <[u8; 16]>::try_from(unhex_ws("7bdb6e3b 432667eb 06f4d14b ff2fbd0f")).expect("tag");
325 let expected_ct = unhex_ws(
326 "cb900f2f ddbe4043 26601965 c889bf17
327 dba77ceb 094fa663 b7a3f748 ba8af829
328 ea64ad54 4a272e9c 485b62a3 fd5c0d",
329 );
330
331 let siv = Siv::new(Aes128::new(&k1), Aes128::new(&k2));
332 let (ct, tag) = siv.encrypt_with_components(&[&ad1, &ad2, &nonce], &plaintext);
333 assert_eq!(tag, expected_tag);
334 assert_eq!(ct, expected_ct);
335
336 let mut roundtrip = ct.clone();
337 assert!(siv.decrypt_with_components(&[&ad1, &ad2, &nonce], &mut roundtrip, &tag));
338 assert_eq!(roundtrip, plaintext);
339 }
340
341 #[test]
342 fn tamper_rejected_without_plaintext_commit() {
343 let k1 = [0x11u8; 16];
344 let k2 = [0x22u8; 16];
345 let nonce = [0x33u8; 16];
346 let aad = b"aad";
347 let plaintext = b"siv plaintext".to_vec();
348 let siv = Siv::new(Aes128::new(&k1), Aes128::new(&k2));
349 let (mut ct, tag) = siv.encrypt(&nonce, aad, &plaintext);
350
351 ct[0] ^= 1;
352 let snapshot = ct.clone();
353 assert!(!siv.decrypt(&nonce, aad, &mut ct, &tag));
354 assert_eq!(ct, snapshot);
355 }
356}