1use crate::polyval::Polyval;
48use ic_core::traits::{Aead, Algorithm, BlockCipher, SelfTest};
49use ic_core::{ensure, Result, Zeroize};
50
51pub const NONCE_LEN: usize = 12;
53pub const TAG_LEN: usize = 16;
55const BLOCK_LEN: usize = 16;
56
57fn derive_keys<C: BlockCipher>(
64 key_cipher: &C,
65 nonce: &[u8],
66 auth_key: &mut [u8; 16],
67 enc_key: &mut [u8],
68) -> Result<()> {
69 let mut block = [0u8; BLOCK_LEN];
70 block[4..].copy_from_slice(nonce);
71
72 let mut take = |counter: u32, out: &mut [u8]| -> Result<()> {
73 block[0..4].copy_from_slice(&counter.to_le_bytes());
74 let mut b = block;
75 key_cipher.encrypt_block(&mut b)?;
76 out.copy_from_slice(&b[..8]);
77 b.zeroize();
78 Ok(())
79 };
80
81 take(0, &mut auth_key[0..8])?;
82 take(1, &mut auth_key[8..16])?;
83 for (i, chunk) in enc_key.chunks_mut(8).enumerate() {
84 take(2 + i as u32, chunk)?;
85 }
86 block.zeroize();
87 Ok(())
88}
89
90fn authenticate(auth_key: [u8; 16], aad: &[u8], message: &[u8]) -> [u8; 16] {
92 let mut p = Polyval::new(auth_key);
93 p.update_padded(aad);
94 p.update_padded(message);
95
96 let mut lengths = [0u8; BLOCK_LEN];
99 lengths[0..8].copy_from_slice(&((aad.len() as u64) * 8).to_le_bytes());
100 lengths[8..16].copy_from_slice(&((message.len() as u64) * 8).to_le_bytes());
101 p.update_block(&lengths);
102 p.finish()
103}
104
105fn ctr_xor<C: BlockCipher>(cipher: &C, counter_block: &[u8; 16], in_out: &mut [u8]) -> Result<()> {
112 let mut block = *counter_block;
113 for chunk in in_out.chunks_mut(BLOCK_LEN) {
114 let mut keystream = block;
115 cipher.encrypt_block(&mut keystream)?;
116 for (b, k) in chunk.iter_mut().zip(keystream.iter()) {
117 *b ^= *k;
118 }
119 keystream.zeroize();
120
121 let counter = u32::from_le_bytes(block[0..4].try_into().unwrap());
122 block[0..4].copy_from_slice(&counter.wrapping_add(1).to_le_bytes());
123 }
124 block.zeroize();
125 Ok(())
126}
127
128macro_rules! gcm_siv {
130 ($name:ident, $cipher:ty, $key_len:literal, $id:literal, $disp:literal,
131 $kat_ct:literal, $kat_tag:literal) => {
132 #[doc = concat!("RFC 8452 ", $disp, ". See the module docs on verification status.")]
133 pub struct $name {
134 key: [u8; $key_len],
135 }
136
137 impl Algorithm for $name {
138 const ID: &'static str = $id;
139 const NAME: &'static str = $disp;
140 }
141
142 impl Drop for $name {
143 fn drop(&mut self) {
144 self.key.zeroize();
145 }
146 }
147
148 impl Aead for $name {
149 const KEY_LEN: usize = $key_len;
150 const NONCE_LEN: usize = NONCE_LEN;
151 const TAG_LEN: usize = TAG_LEN;
152
153 fn new(key: &[u8]) -> Result<Self> {
154 ic_core::module::operational()?;
155 ensure!(key.len() == $key_len, InvalidLength, "aes-gcm-siv key");
156 let mut k = [0u8; $key_len];
157 k.copy_from_slice(key);
158 Ok(Self { key: k })
159 }
160
161 fn seal_detached(
162 &self,
163 nonce: &[u8],
164 aad: &[u8],
165 in_out: &mut [u8],
166 tag: &mut [u8],
167 ) -> Result<()> {
168 ic_core::module::operational()?;
169 ensure!(nonce.len() == NONCE_LEN, InvalidLength, "aes-gcm-siv nonce");
170 ensure!(tag.len() == TAG_LEN, InvalidLength, "aes-gcm-siv tag");
171
172 let key_cipher = <$cipher>::new(&self.key)?;
173 let mut auth_key = [0u8; 16];
174 let mut enc_key = [0u8; $key_len];
175 derive_keys(&key_cipher, nonce, &mut auth_key, &mut enc_key)?;
176 let message_cipher = <$cipher>::new(&enc_key)?;
177 enc_key.zeroize();
178
179 let mut s = authenticate(auth_key, aad, in_out);
183 auth_key.zeroize();
184 for (b, n) in s.iter_mut().zip(nonce.iter()) {
185 *b ^= *n;
186 }
187 s[15] &= 0x7f;
188 message_cipher.encrypt_block(&mut s)?;
189 tag.copy_from_slice(&s);
190
191 let mut counter_block = s;
192 counter_block[15] |= 0x80;
193 ctr_xor(&message_cipher, &counter_block, in_out)?;
194 counter_block.zeroize();
195 s.zeroize();
196 Ok(())
197 }
198
199 fn open_detached(
200 &self,
201 nonce: &[u8],
202 aad: &[u8],
203 in_out: &mut [u8],
204 tag: &[u8],
205 ) -> Result<()> {
206 ic_core::module::operational()?;
207 ensure!(nonce.len() == NONCE_LEN, InvalidLength, "aes-gcm-siv nonce");
208 ensure!(tag.len() == TAG_LEN, InvalidLength, "aes-gcm-siv tag");
209
210 let key_cipher = <$cipher>::new(&self.key)?;
211 let mut auth_key = [0u8; 16];
212 let mut enc_key = [0u8; $key_len];
213 derive_keys(&key_cipher, nonce, &mut auth_key, &mut enc_key)?;
214 let message_cipher = <$cipher>::new(&enc_key)?;
215 enc_key.zeroize();
216
217 let mut counter_block = [0u8; BLOCK_LEN];
218 counter_block.copy_from_slice(tag);
219 counter_block[15] |= 0x80;
220 ctr_xor(&message_cipher, &counter_block, in_out)?;
221 counter_block.zeroize();
222
223 let mut s = authenticate(auth_key, aad, in_out);
224 auth_key.zeroize();
225 for (b, n) in s.iter_mut().zip(nonce.iter()) {
226 *b ^= *n;
227 }
228 s[15] &= 0x7f;
229 message_cipher.encrypt_block(&mut s)?;
230
231 let ok = ic_core::ct::verify(&s, tag);
232 s.zeroize();
233 if !ok {
234 in_out.zeroize();
236 return Err(ic_core::err!(AuthenticationFailed, $id));
237 }
238 Ok(())
239 }
240 }
241
242 impl SelfTest for $name {
243 fn self_test() -> Result<()> {
258 let mut key = [0u8; $key_len];
261 key[0] = 0x01;
262 let kat = <Self as Aead>::new(&key)?;
263
264 let mut nonce_kat = [0u8; NONCE_LEN];
265 nonce_kat[0] = 0x03;
266
267 let mut buf = [0x02u8, 0, 0, 0, 0, 0, 0, 0];
268 let mut tag = [0u8; TAG_LEN];
269 kat.seal_detached(&nonce_kat, &[0x01], &mut buf, &mut tag)?;
270
271 let mut want_ct = [0u8; 8];
272 let mut want_tag = [0u8; TAG_LEN];
273 ic_core::codec::hex_decode($kat_ct, &mut want_ct)?;
274 ic_core::codec::hex_decode($kat_tag, &mut want_tag)?;
275 ensure!(
276 ic_core::ct::verify(&want_ct, &buf) && ic_core::ct::verify(&want_tag, &tag),
277 SelfTestFailed,
278 $id
279 );
280
281 let cipher = <Self as Aead>::new(&[0x42u8; $key_len])?;
282 let nonce = [0x24u8; NONCE_LEN];
283
284 let mut a = *b"self-test message";
285 let mut tag_a = [0u8; TAG_LEN];
286 cipher.seal_detached(&nonce, b"aad", &mut a, &mut tag_a)?;
287
288 let mut b = *b"self-test message";
290 let mut tag_b = [0u8; TAG_LEN];
291 cipher.seal_detached(&nonce, b"aad", &mut b, &mut tag_b)?;
292 ensure!(a == b && tag_a == tag_b, SelfTestFailed, $id);
293
294 cipher.open_detached(&nonce, b"aad", &mut a, &tag_a)?;
296 ensure!(&a == b"self-test message", SelfTestFailed, $id);
297
298 let mut bad = tag_a;
300 bad[0] ^= 1;
301 ensure!(
302 cipher.open_detached(&nonce, b"aad", &mut b, &bad).is_err(),
303 SelfTestFailed,
304 $id
305 );
306 let mut c = *b"self-test message";
308 let mut tag_c = [0u8; TAG_LEN];
309 cipher.seal_detached(&nonce, b"aad", &mut c, &mut tag_c)?;
310 ensure!(
311 cipher
312 .open_detached(&nonce, b"other", &mut c, &tag_c)
313 .is_err(),
314 SelfTestFailed,
315 $id
316 );
317 Ok(())
318 }
319 }
320 };
321}
322
323gcm_siv!(
324 Aes128GcmSiv,
325 crate::Aes128,
326 16,
327 "aes-128-gcm-siv",
328 "AES-128-GCM-SIV",
329 b"1e6daba35669f427",
333 b"3b0a1a2560969cdf790d99759abd1508"
334);
335gcm_siv!(
336 Aes256GcmSiv,
337 crate::Aes256,
338 32,
339 "aes-256-gcm-siv",
340 "AES-256-GCM-SIV",
341 b"1de22967237a8132",
344 b"91213f267e3b452f02d01ae33e4ec854"
345);
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 fn seal(cipher: &Aes256GcmSiv, nonce: &[u8], aad: &[u8], pt: &[u8]) -> (Vec<u8>, [u8; 16]) {
352 let mut buf = pt.to_vec();
353 let mut tag = [0u8; TAG_LEN];
354 cipher
355 .seal_detached(nonce, aad, &mut buf, &mut tag)
356 .unwrap();
357 (buf, tag)
358 }
359
360 #[test]
361 fn round_trips_at_every_length_boundary() {
362 let cipher = Aes256GcmSiv::new(&[0x11u8; 32]).unwrap();
363 let nonce = [0x22u8; NONCE_LEN];
364
365 for len in [0usize, 1, 15, 16, 17, 31, 32, 33, 255, 256] {
366 let pt = vec![0x5au8; len];
367 let (mut ct, tag) = seal(&cipher, &nonce, b"aad", &pt);
368 assert_eq!(ct.len(), len, "length is preserved");
369 if len > 0 {
370 assert_ne!(ct, pt, "and the plaintext is not passed through");
371 }
372 cipher.open_detached(&nonce, b"aad", &mut ct, &tag).unwrap();
373 assert_eq!(ct, pt, "round trip at {len} bytes");
374 }
375 }
376
377 #[test]
382 fn nonce_reuse_does_not_leak_the_plaintext_xor() {
383 let cipher = Aes256GcmSiv::new(&[0x33u8; 32]).unwrap();
384 let nonce = [0x44u8; NONCE_LEN];
385
386 let p1 = vec![0xaau8; 64];
387 let mut p2 = vec![0xaau8; 64];
388 p2[0] ^= 0x01; let (c1, _) = seal(&cipher, &nonce, b"", &p1);
391 let (c2, _) = seal(&cipher, &nonce, b"", &p2);
392
393 let cipher_xor: Vec<u8> = c1.iter().zip(c2.iter()).map(|(a, b)| a ^ b).collect();
394 let plain_xor: Vec<u8> = p1.iter().zip(p2.iter()).map(|(a, b)| a ^ b).collect();
395 assert_ne!(
396 cipher_xor, plain_xor,
397 "a one-bit plaintext change must change the whole keystream"
398 );
399
400 let differing = cipher_xor.iter().filter(|b| **b != 0).count();
402 assert!(
403 differing > 48,
404 "only {differing} of 64 ciphertext bytes changed; the keystream is \
405 not depending on the plaintext"
406 );
407 }
408
409 #[test]
412 fn encryption_is_deterministic() {
413 let cipher = Aes256GcmSiv::new(&[0x55u8; 32]).unwrap();
414 let nonce = [0x66u8; NONCE_LEN];
415 let (a, ta) = seal(&cipher, &nonce, b"aad", b"same message");
416 let (b, tb) = seal(&cipher, &nonce, b"aad", b"same message");
417 assert_eq!((a, ta), (b, tb));
418 }
419
420 #[test]
421 fn every_input_is_authenticated() {
422 let cipher = Aes256GcmSiv::new(&[0x77u8; 32]).unwrap();
423 let nonce = [0x88u8; NONCE_LEN];
424 let (ct, tag) = seal(&cipher, &nonce, b"aad", b"message");
425
426 let mut buf = ct.clone();
428 let mut other_nonce = nonce;
429 other_nonce[0] ^= 1;
430 assert!(cipher
431 .open_detached(&other_nonce, b"aad", &mut buf, &tag)
432 .is_err());
433
434 let mut buf = ct.clone();
436 assert!(cipher
437 .open_detached(&nonce, b"aae", &mut buf, &tag)
438 .is_err());
439
440 let mut buf = ct.clone();
442 buf[0] ^= 1;
443 assert!(cipher
444 .open_detached(&nonce, b"aad", &mut buf, &tag)
445 .is_err());
446
447 let mut buf = ct.clone();
449 let mut bad = tag;
450 bad[15] ^= 1;
451 assert!(cipher
452 .open_detached(&nonce, b"aad", &mut buf, &bad)
453 .is_err());
454 }
455
456 #[test]
458 fn a_failed_open_clears_the_buffer() {
459 let cipher = Aes256GcmSiv::new(&[0x99u8; 32]).unwrap();
460 let nonce = [0xaau8; NONCE_LEN];
461 let (ct, tag) = seal(&cipher, &nonce, b"", b"secret plaintext");
462
463 let mut buf = ct;
464 let mut bad = tag;
465 bad[0] ^= 1;
466 assert!(cipher.open_detached(&nonce, b"", &mut buf, &bad).is_err());
467 assert!(
468 buf.iter().all(|b| *b == 0),
469 "the buffer still held data after a failed open"
470 );
471 }
472
473 #[test]
476 fn the_aad_boundary_is_authenticated() {
477 let cipher = Aes256GcmSiv::new(&[0xbbu8; 32]).unwrap();
478 let nonce = [0xccu8; NONCE_LEN];
479 let (_, tag_a) = seal(&cipher, &nonce, b"abc", b"def");
480 let (_, tag_b) = seal(&cipher, &nonce, b"ab", b"cdef");
481 assert_ne!(tag_a, tag_b, "the aad/message split must be bound in");
482 }
483
484 #[test]
485 fn key_sizes_and_lengths_are_checked() {
486 assert!(Aes128GcmSiv::new(&[0u8; 16]).is_ok());
487 assert!(Aes128GcmSiv::new(&[0u8; 32]).is_err());
488 assert!(Aes256GcmSiv::new(&[0u8; 16]).is_err());
489
490 let cipher = Aes128GcmSiv::new(&[0u8; 16]).unwrap();
491 let mut buf = [0u8; 8];
492 let mut tag = [0u8; TAG_LEN];
493 assert!(cipher
494 .seal_detached(&[0u8; 11], b"", &mut buf, &mut tag)
495 .is_err());
496 assert!(cipher
497 .seal_detached(&[0u8; 12], b"", &mut buf, &mut [0u8; 15])
498 .is_err());
499 }
500
501 #[test]
502 fn both_self_tests_pass() {
503 Aes128GcmSiv::self_test().unwrap();
504 Aes256GcmSiv::self_test().unwrap();
505 }
506
507 #[test]
510 fn the_two_key_sizes_are_distinct() {
511 let mut key = [0u8; 32];
512 key[..16].copy_from_slice(&[0x5au8; 16]);
513 let small = Aes128GcmSiv::new(&key[..16]).unwrap();
514 let large = Aes256GcmSiv::new(&key).unwrap();
515
516 let nonce = [0u8; NONCE_LEN];
517 let mut a = *b"message";
518 let mut b = *b"message";
519 let mut ta = [0u8; TAG_LEN];
520 let mut tb = [0u8; TAG_LEN];
521 small.seal_detached(&nonce, b"", &mut a, &mut ta).unwrap();
522 large.seal_detached(&nonce, b"", &mut b, &mut tb).unwrap();
523 assert_ne!((a, ta), (b, tb));
524 }
525}