1use constant_time_eq::constant_time_eq;
2
3use super::{
4 aes::{GCM_MAX_LEN, encrypt_block, expand_key},
5 ghash::{GHashPowers, compute_tag, precompute_ghash_powers, precompute_ghash_table},
6};
7use crate::{
8 Aead, AeadError, Hash,
9 aes::{RoundKeys, aes::RoundKeysSoftware, aes_ctr::AesCtr, ghash::GhashTable},
10};
11
12#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
18pub struct Aes128Gcm(pub(crate) AesGcm<11>);
19
20impl Aes128Gcm {
21 pub const KEY_SIZE: usize = 16;
22
23 #[inline]
29 pub fn new(key: &[u8; Self::KEY_SIZE]) -> Self {
30 Self(AesGcm::<11>::new(key))
31 }
32}
33
34impl Aead for Aes128Gcm {
35 const TAG_SIZE: usize = 16;
36 const NONCE_SIZE: usize = 12;
37
38 #[inline]
39 fn encrypt_in_place(&self, in_out: &mut [u8], nonce: &[u8], aad: &[u8]) -> Hash {
40 self.0.encrypt_in_place(in_out, nonce, aad)
41 }
42
43 #[inline]
44 fn decrypt_in_place(&self, in_out: &mut [u8], nonce: &[u8], aad: &[u8], tag: &[u8]) -> Result<(), AeadError> {
45 self.0.decrypt_in_place(in_out, nonce, aad, tag)
46 }
47}
48
49#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
55pub struct Aes256Gcm(pub(crate) AesGcm<15>);
56
57impl Aes256Gcm {
58 pub const KEY_SIZE: usize = 32;
59
60 #[inline]
66 pub fn new(key: &[u8; Self::KEY_SIZE]) -> Self {
67 Self(AesGcm::<15>::new(key))
68 }
69}
70
71impl Aead for Aes256Gcm {
72 const TAG_SIZE: usize = 16;
73 const NONCE_SIZE: usize = 12;
74
75 #[inline]
76 fn encrypt_in_place(&self, in_out: &mut [u8], nonce: &[u8], aad: &[u8]) -> Hash {
77 self.0.encrypt_in_place(in_out, nonce, aad)
78 }
79
80 #[inline]
81 fn decrypt_in_place(&self, in_out: &mut [u8], nonce: &[u8], aad: &[u8], tag: &[u8]) -> Result<(), AeadError> {
82 self.0.decrypt_in_place(in_out, nonce, aad, tag)
83 }
84}
85
86#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
101pub(crate) struct AesGcm<const N: usize> {
102 pub(crate) round_keys: RoundKeys<N>,
103 pub(crate) h_powers: GHashPowers,
104}
105
106impl AesGcm<11> {
107 pub const KEY_SIZE: usize = 16;
108
109 pub fn new(key: &[u8; Self::KEY_SIZE]) -> Self {
110 Self::new_inner(key)
111 }
112}
113
114impl AesGcm<15> {
115 pub const KEY_SIZE: usize = 32;
116
117 pub fn new(key: &[u8; Self::KEY_SIZE]) -> Self {
118 Self::new_inner(key)
119 }
120}
121
122impl<const N: usize> AesGcm<N> {
123 const TAG_SIZE: usize = 16;
124
125 fn new_inner(key: &[u8]) -> Self {
126 const { assert!(N == 11 || N == 15) };
127
128 let round_keys_software = expand_key(key);
129
130 #[cfg(target_arch = "aarch64")]
131 {
132 use core::arch::aarch64::*;
133
134 let (h_powers_bytes, _h) = precompute_ghash_powers::<N>(key);
135 let mut h_powers = [unsafe { vdupq_n_u8(0) }; 8];
136 for i in 0..8 {
137 h_powers[i] = unsafe { vld1q_u8(h_powers_bytes[i].as_ptr()) };
138 }
139 let h_powers = GHashPowers::Armv8(h_powers);
140
141 #[cfg(feature = "std")]
142 if std::arch::is_aarch64_feature_detected!("aes") {
143 return AesGcm {
144 round_keys: RoundKeys::Armv8(super::aes_arm64::expand_key_armv8(round_keys_software)),
145 h_powers,
146 };
147 }
148
149 #[cfg(all(not(feature = "std"), target_feature = "aes"))]
150 return AesGcm {
151 round_keys: RoundKeys::Armv8(super::aes_arm64::expand_key_armv8(round_keys_software)),
152 h_powers,
153 };
154 }
155
156 #[cfg(target_arch = "x86_64")]
157 {
158 use core::arch::x86_64::*;
159
160 let (h_powers_bytes, _h) = precompute_ghash_powers::<N>(key);
161 let mut h_powers = unsafe { [_mm_setzero_si128(); 8] };
162 for i in 0..8 {
163 h_powers[i] = unsafe { _mm_loadu_si128(h_powers_bytes[i].as_ptr().cast()) };
164 }
165
166 #[cfg(feature = "std")]
167 if std::arch::is_x86_feature_detected!("aes")
168 && std::arch::is_x86_feature_detected!("pclmulqdq")
169 && std::arch::is_x86_feature_detected!("ssse3")
170 && std::arch::is_x86_feature_detected!("sse4.1")
171 {
172 return AesGcm {
173 round_keys: RoundKeys::X86_64(super::aes_amd64::expand_key_x86_64(round_keys_software)),
174 h_powers: GHashPowers::X86_64(h_powers),
175 };
176 }
177
178 #[cfg(all(
179 not(feature = "std"),
180 target_feature = "aes",
181 target_feature = "pclmulqdq",
182 target_feature = "ssse3",
183 target_feature = "sse4.1"
184 ))]
185 return AesGcm {
186 round_keys: RoundKeys::X86_64(super::super::aes_amd64::expand_key_x86_64(round_keys_software)),
187 h_powers: GHashPowers::X86_64(h_powers),
188 };
189 }
190
191 AesGcm {
192 round_keys: RoundKeys::Software(round_keys_software),
193 h_powers: GHashPowers::Software(precompute_ghash_table(&round_keys_software)),
194 }
195 }
196
197 fn encrypt_in_place(&self, in_out: &mut [u8], nonce: &[u8], aad: &[u8]) -> Hash {
198 assert!(
199 in_out.len() as u64 <= GCM_MAX_LEN,
200 "GCM plaintext exceeds maximum allowed length (2^32 - 2 blocks)"
201 );
202
203 let nonce_arr: &[u8; 12] = nonce.try_into().expect("AES-GCM nonce must be 12 bytes");
204
205 match (&self.round_keys, &self.h_powers) {
206 #[cfg(target_arch = "aarch64")]
207 (RoundKeys::Armv8(round_keys), GHashPowers::Armv8(h_powers)) => unsafe {
208 use crate::aes::aes_gcm_arm64::gcm_encrypt_armv8;
209 gcm_encrypt_armv8(round_keys, &h_powers, in_out, nonce_arr, aad)
210 },
211 #[cfg(target_arch = "x86_64")]
212 (RoundKeys::X86_64(round_keys), GHashPowers::X86_64(h_powers)) => unsafe {
213 use crate::aes::aes_gcm_amd64::gcm_encrypt_aesni;
214 gcm_encrypt_aesni(&round_keys, &h_powers, in_out, nonce_arr, aad)
215 },
216 (RoundKeys::Software(round_keys), GHashPowers::Software(ghash_table)) => {
217 self.encrypt_in_place_soft(in_out, round_keys, ghash_table, nonce_arr, aad)
218 }
219 _ => unreachable!(),
220 }
221 }
222
223 fn decrypt_in_place(&self, in_out: &mut [u8], nonce: &[u8], aad: &[u8], tag: &[u8]) -> Result<(), AeadError> {
224 if in_out.len() as u64 > GCM_MAX_LEN + Self::TAG_SIZE as u64 {
225 return Err(AeadError::InvalidCiphertext);
226 }
227
228 let nonce_arr: &[u8; 12] = nonce.try_into().map_err(|_| AeadError::InvalidNonce)?;
229 let tag_arr: &[u8; 16] = tag.try_into().expect("AES-GCM tag must be 16 bytes");
230
231 match (&self.round_keys, &self.h_powers) {
232 #[cfg(target_arch = "aarch64")]
233 (RoundKeys::Armv8(round_keys), GHashPowers::Armv8(h_powers)) => unsafe {
234 use crate::aes::aes_gcm_arm64::gcm_decrypt_armv8;
235 gcm_decrypt_armv8(round_keys, &h_powers, in_out, tag_arr, nonce_arr, aad)
236 },
237 #[cfg(target_arch = "x86_64")]
238 (RoundKeys::X86_64(round_keys), GHashPowers::X86_64(h_powers)) => unsafe {
239 use crate::aes::aes_gcm_amd64::gcm_decrypt_aesni;
240 gcm_decrypt_aesni(&round_keys, &h_powers, in_out, tag_arr, nonce_arr, aad)
241 },
242 (RoundKeys::Software(round_keys), GHashPowers::Software(ghash_table)) => {
243 self.decrypt_in_place_soft(in_out, round_keys, ghash_table, tag_arr, nonce_arr, aad)
244 }
245 _ => unreachable!(),
246 }
247 }
248
249 pub(crate) fn encrypt_in_place_soft(
251 &self,
252 in_out: &mut [u8],
253 round_keys: &RoundKeysSoftware<N>,
254 ghash_table: &GhashTable,
255 nonce: &[u8; 12],
256 aad: &[u8],
257 ) -> Hash {
258 let mut j0 = [0u8; 16];
259 j0[..12].copy_from_slice(nonce);
260 j0[15] = 1;
261
262 let ej0 = encrypt_block(&round_keys, &j0);
263
264 j0[15] = 2;
265
266 let mut aes_ctr = AesCtr::from_round_keys(self.round_keys.clone());
267 aes_ctr.set_counter(&j0);
268 aes_ctr.xor_keystream(in_out);
269 compute_tag(&ghash_table, aad, in_out, &ej0)
270 }
271
272 pub(crate) fn decrypt_in_place_soft(
274 &self,
275 in_out: &mut [u8],
276 round_keys: &RoundKeysSoftware<N>,
277 ghash_table: &GhashTable,
278 tag: &[u8; 16],
279 nonce: &[u8; 12],
280 aad: &[u8],
281 ) -> Result<(), AeadError> {
282 let mut j0 = [0u8; 16];
283 j0[..12].copy_from_slice(nonce);
284 j0[15] = 1;
285
286 let ej0 = encrypt_block(&round_keys, &j0);
287
288 let expected_tag = compute_tag(&ghash_table, aad, in_out, &ej0);
289
290 if !constant_time_eq(tag, &expected_tag) {
291 return Err(AeadError::InvalidCiphertext);
292 }
293
294 j0[15] = 2;
295 let mut aes_ctr = AesCtr::from_round_keys(self.round_keys.clone());
296 aes_ctr.set_counter(&j0);
297 aes_ctr.xor_keystream(in_out);
298
299 Ok(())
300 }
301}
302
303#[cfg(test)]
304mod tests_128 {
305 use hex;
306
307 use super::*;
308 use crate::{
309 Aead,
310 aes::{
311 aes::{TE0, TE1, TE2, TE3, encrypt_block, expand_key},
312 ghash::precompute_ghash_table,
313 },
314 };
315
316 include!("aes_gcm_128_vectors.rs");
317
318 #[test]
319 fn aes128_encrypt_block_vector() {
320 let key = [0u8; 16];
321 let pt = [0u8; 16];
322 let rk: [[u8; 16]; 11] = expand_key::<11>(&key);
323 let ct = encrypt_block::<11>(&rk, &pt);
324 let expected = hex::decode_array::<16>(b"66e94bd4ef8a2c3b884cfa59ca342b2e").unwrap();
325 assert_eq!(ct, expected, "AES-128 encrypt K=0, P=0 mismatch");
326 }
327
328 #[test]
329 fn aes128_fips197_vector() {
330 let key = hex::decode_array::<16>(b"2b7e151628aed2a6abf7158809cf4f3c").unwrap();
331 let pt = hex::decode_array::<16>(b"3243f6a8885a308d313198a2e0370734").unwrap();
332 let rk: [[u8; 16]; 11] = expand_key::<11>(&key);
333
334 let rk_hex: Vec<String> = rk.iter().map(|rk_i| hex::encode(rk_i)).collect();
335 assert_eq!(rk_hex[0], "2b7e151628aed2a6abf7158809cf4f3c", "rk0");
336 assert_eq!(rk_hex[1], "a0fafe1788542cb123a339392a6c7605", "rk1");
337 assert_eq!(rk_hex[2], "f2c295f27a96b9435935807a7359f67f", "rk2");
338
339 let ct = encrypt_block::<11>(&rk, &pt);
340 let ct_hex = hex::encode(&ct);
341 assert_eq!(
342 ct_hex, "3925841d02dc09fbdc118597196a0b32",
343 "AES-128 FIPS 197 encrypt failed, rk3={}",
344 rk_hex[3]
345 );
346 }
347
348 fn run_gcm_vector(v: &Gcm128Vector) {
349 let key: [u8; 16] = hex::decode_array::<16>(v.key.as_bytes()).unwrap();
350 let nonce: [u8; 12] = hex::decode_array::<12>(v.nonce.as_bytes()).unwrap();
351 let pt = hex::decode(v.pt).unwrap();
352 let aad = hex::decode(v.aad).unwrap();
353 let expected_ct = hex::decode(v.ct).unwrap();
354 let expected_tag: [u8; 16] = hex::decode_array::<16>(v.tag.as_bytes()).unwrap();
355 let round_keys = expand_key::<11>(&key);
356 let ghash_table = precompute_ghash_table(&round_keys);
357
358 let cipher = Aes128Gcm::new(&key);
359
360 let mut buf = pt.clone();
361 let tag = cipher
362 .0
363 .encrypt_in_place_soft(&mut buf, &round_keys, &ghash_table, &nonce, &aad);
364 assert_eq!(buf, expected_ct, "ciphertext mismatch for key={}", v.key);
365 assert_eq!(tag.as_ref(), &expected_tag[..], "tag mismatch for key={}", v.key);
366
367 let mut buf2 = expected_ct.clone();
368 cipher
369 .0
370 .decrypt_in_place_soft(&mut buf2, &round_keys, &ghash_table, &expected_tag, &nonce, &aad)
371 .expect("decrypt failed");
372 assert_eq!(buf2, pt, "plaintext mismatch after decrypt for key={}", v.key);
373 }
374
375 #[test]
376 fn aes128_gcm_roundtrip() {
377 let key = [0xabu8; 16];
378 let nonce = [0x01u8; 16 - 4]; let aad = b"additional data";
380 let plaintext: Vec<u8> = (0u8..=255u8).cycle().take(1024).collect();
381
382 let cipher = Aes128Gcm::new(&key);
383 let mut buf = plaintext.clone();
384 let tag = cipher.encrypt_in_place(&mut buf, &nonce, aad);
385 let tag_bytes: [u8; 16] = tag.as_ref().try_into().unwrap();
386 cipher
387 .decrypt_in_place(&mut buf, &nonce, aad, &tag_bytes)
388 .expect("decrypt failed");
389 assert_eq!(buf, plaintext);
390 }
391
392 #[test]
393 fn aes128_gcm_empty_plaintext() {
394 let key = [0x01u8; 16];
395 let nonce = [0x02u8; 12];
396 let aad = b"test";
397 let cipher = Aes128Gcm::new(&key);
398 let mut buf: Vec<u8> = vec![];
399 let tag = cipher.encrypt_in_place(&mut buf, &nonce, aad);
400 let tag_bytes: [u8; 16] = tag.as_ref().try_into().unwrap();
401 cipher
402 .decrypt_in_place(&mut buf, &nonce, aad, &tag_bytes)
403 .expect("decrypt failed");
404 }
405
406 #[test]
407 fn aes128_gcm_tag_mismatch_returns_error() {
408 let key = [0u8; 16];
409 let nonce = [0u8; 12];
410 let cipher = Aes128Gcm::new(&key);
411 let mut buf = b"hello world".to_vec();
412 let tag = cipher.encrypt_in_place(&mut buf, &nonce, &[]);
413 let mut bad_tag: [u8; 16] = tag.as_ref().try_into().unwrap();
414 bad_tag[0] ^= 0xff;
415 let mut buf2 = buf.clone();
416 assert!(cipher.0.decrypt_in_place(&mut buf2, &bad_tag, &nonce, &[]).is_err());
417 }
418
419 #[test]
420 fn aes128_gcm_nist_vectors() {
421 for v in NIST_GCM_128_VECTORS.iter() {
422 run_gcm_vector(v);
423 }
424 }
425
426 #[test]
427 fn aes128_block_encrypt_k0_j0() {
428 let key = [0u8; 16];
429 let j0: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
430 let rk: [[u8; 16]; 11] = expand_key::<11>(&key);
431 let ct = encrypt_block::<11>(&rk, &j0);
432 let expected = hex::decode_array::<16>(b"58e2fccefa7e3061367f1d57a4e7455a").unwrap();
433 assert_eq!(ct, expected, "AES(K=0, J0=0...01) mismatch");
434 }
435
436 #[test]
437 fn aes128_debug_round_state() {
438 let key = [0u8; 16];
439 let pt = [0u8; 16];
440 let rk: [[u8; 16]; 11] = expand_key::<11>(&key);
441
442 let mut s = pt;
443 for i in 0..16 {
444 s[i] ^= rk[0][i];
445 }
446 assert_eq!(s, [0; 16], "after AddRoundKey with K=0, state should be all zeros");
447
448 let r = 1usize;
449 let t0 = TE0[s[0] as usize]
450 ^ TE1[s[5] as usize]
451 ^ TE2[s[10] as usize]
452 ^ TE3[s[15] as usize]
453 ^ u32::from_ne_bytes(rk[r][0..4].try_into().unwrap());
454
455 let rk1word0 = u32::from_ne_bytes([0x62, 0x63, 0x63, 0x63]);
456 assert_eq!(rk1word0, 0x63636362, "rk[1] word 0 on LE should be 0x63636362");
457 let te0_0 = TE0[0];
458 let te1_0 = TE1[0];
459 let te2_0 = TE2[0];
460 let te3_0 = TE3[0];
461 assert_eq!(te0_0, 0xa56363c6, "TE0[0]");
462 assert_eq!(te1_0, 0x6363c6a5, "TE1[0]");
463 assert_eq!(te2_0, 0x63c6a563, "TE2[0]");
464 assert_eq!(te3_0, 0xc6a56363, "TE3[0]");
465
466 let expected_t0 = 0xa56363c6u32 ^ 0x6363c6a5u32 ^ 0x63c6a563u32 ^ 0xc6a56363u32;
467 assert_eq!(expected_t0, 0x63636363u32, "TE0^TE1^TE2^TE3 should be 0x63636363");
468 assert_eq!(t0, 0x63636363u32 ^ 0x63636362u32, "t0 after XOR with rk");
469 assert_eq!(t0, 0x00000001u32, "t0 should be 1");
470
471 let ct = encrypt_block::<11>(&rk, &pt);
472 let ct_hex = hex::encode(&ct);
473 assert_eq!(ct_hex, "66e94bd4ef8a2c3b884cfa59ca342b2e", "AES-128 K=0 P=0 full encrypt fails");
474 }
475
476 #[test]
477 fn wycheproof_gcm_vectors() {
478 let data: serde_json::Value =
479 serde_json::from_str(include_str!("../../testdata/wycheproof/testvectors_v1/aes_gcm_test.json")).unwrap();
480 let mut valid_tested = 0u64;
481 let mut invalid_tested = 0u64;
482 for group in data["testGroups"].as_array().unwrap() {
483 if group["keySize"].as_u64() != Some(128) {
484 continue;
485 }
486 if group["ivSize"].as_u64() != Some(96) {
487 continue;
488 }
489 if group["tagSize"].as_u64() != Some(128) {
490 continue;
491 }
492 for test in group["tests"].as_array().unwrap() {
493 let key_hex = test["key"].as_str().unwrap();
494 let iv_hex = test["iv"].as_str().unwrap();
495 let msg_hex = test["msg"].as_str().unwrap();
496 let aad_hex = test["aad"].as_str().unwrap();
497 let ct_hex = test["ct"].as_str().unwrap();
498 let tag_hex = test["tag"].as_str().unwrap();
499 let result = test["result"].as_str().unwrap();
500
501 let key = hex::decode_array::<16>(key_hex.as_bytes()).unwrap();
502 let nonce = hex::decode_array::<12>(iv_hex.as_bytes()).unwrap();
503 let expected_ct = hex::decode(ct_hex).unwrap();
504 let expected_tag = hex::decode_array::<16>(tag_hex.as_bytes()).unwrap();
505 let pt = hex::decode(msg_hex).unwrap();
506 let aad = hex::decode(aad_hex).unwrap();
507
508 let cipher = Aes128Gcm::new(&key);
509
510 if result == "valid" {
511 let mut buf = pt.clone();
512 let tag = cipher.encrypt_in_place(&mut buf, &nonce, &aad);
513 assert_eq!(buf, expected_ct, "wycheproof tcId={} ct mismatch", test["tcId"]);
514 assert_eq!(tag.as_ref(), &expected_tag[..], "wycheproof tcId={} tag mismatch", test["tcId"]);
515
516 let mut buf2 = expected_ct.clone();
517 cipher
518 .decrypt_in_place(&mut buf2, &nonce, &aad, &expected_tag[..])
519 .expect("wycheproof decrypt failed");
520 assert_eq!(buf2, pt, "wycheproof tcId={} pt mismatch", test["tcId"]);
521 valid_tested += 1;
522 } else {
523 let mut buf = expected_ct.clone();
524 let result = cipher.decrypt_in_place(&mut buf, &nonce, &aad, &expected_tag[..]);
525 assert!(result.is_err(), "wycheproof tcId={} expected invalid but passed", test["tcId"]);
526 invalid_tested += 1;
527 }
528 }
529 }
530 assert!(valid_tested > 0, "no valid AES-128-GCM wycheproof tests were run");
531 assert!(invalid_tested > 0, "no invalid AES-128-GCM wycheproof tests were run");
532 }
533}
534
535#[cfg(test)]
536mod tests_256 {
537 use hex;
538
539 use super::*;
540 use crate::{
541 Aead,
542 aes::{expand_key, ghash::precompute_ghash_table},
543 };
544
545 include!("aes_gcm_256_vectors.rs");
546
547 fn run_gcm_vector_soft(v: &GcmVector) {
548 let key: [u8; 32] = hex::decode_array::<32>(v.key.as_bytes()).unwrap();
549 let nonce: [u8; 12] = hex::decode_array::<12>(v.nonce.as_bytes()).unwrap();
550 let pt = hex::decode(v.pt).unwrap();
551 let aad = hex::decode(v.aad).unwrap();
552 let expected_ct = hex::decode(v.ct).unwrap();
553 let expected_tag: [u8; 16] = hex::decode_array::<16>(v.tag.as_bytes()).unwrap();
554 let round_keys = expand_key(&key);
555 let ghash_table = precompute_ghash_table(&round_keys);
556
557 let cipher = Aes256Gcm::new(&key);
558
559 let mut buf = pt.clone();
560 let tag = cipher
561 .0
562 .encrypt_in_place_soft(&mut buf, &round_keys, &ghash_table, &nonce, &aad);
563 assert_eq!(buf, expected_ct, "ciphertext mismatch for key={}", v.key);
564 assert_eq!(tag.as_ref(), &expected_tag[..], "tag mismatch for key={}", v.key);
565
566 let mut buf2 = expected_ct.clone();
567 cipher
568 .0
569 .decrypt_in_place_soft(&mut buf2, &round_keys, &ghash_table, &expected_tag, &nonce, &aad)
570 .expect("decrypt failed");
571 assert_eq!(buf2, pt, "plaintext mismatch after decrypt for key={}", v.key);
572 }
573
574 #[test]
575 fn nist_gcm_test_vectors_soft() {
576 for v in NIST_GCM_VECTORS.iter().chain(EXTRA_GCM_VECTORS.iter()) {
577 run_gcm_vector_soft(v);
578 }
579 }
580
581 #[test]
582 fn gcm_tag_mismatch_returns_error_soft() {
583 let key = [0u8; 32];
584 let nonce = [0u8; 12];
585 let cipher = Aes256Gcm::new(&key);
586 let mut buf = b"hello world".to_vec();
587 let tag = cipher.encrypt_in_place(&mut buf, &nonce, &[]);
588 let mut bad_tag: [u8; 16] = tag.as_ref().try_into().unwrap();
589 bad_tag[0] ^= 0xff;
590 let mut buf2 = buf.clone();
591 assert!(cipher.decrypt_in_place(&mut buf2, &bad_tag, &nonce, &[]).is_err());
592 }
593
594 #[test]
595 fn gcm_encrypt_decrypt_large_soft() {
596 let key = [0xabu8; 32];
597 let nonce = [0x01u8; 12];
598 let aad = b"additional data";
599 let plaintext: Vec<u8> = (0u8..=255u8).cycle().take(1024).collect();
600 let round_keys = expand_key(&key);
601 let ghash_table = precompute_ghash_table(&round_keys);
602
603 let cipher = Aes256Gcm::new(&key);
604 let mut buf = plaintext.clone();
605 let tag = cipher
606 .0
607 .encrypt_in_place_soft(&mut buf, &round_keys, &ghash_table, &nonce, aad);
608 let tag_bytes: [u8; 16] = tag.as_ref().try_into().unwrap();
609 cipher
610 .0
611 .decrypt_in_place_soft(&mut buf, &round_keys, &ghash_table, &tag_bytes, &nonce, aad)
612 .expect("decrypt failed");
613 assert_eq!(buf, plaintext);
614 }
615
616 #[test]
617 fn gcm_empty_plaintext_nonempty_aad_soft() {
618 let key: [u8; 32] =
619 hex::decode_array::<32>(b"feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308").unwrap();
620 let nonce: [u8; 12] = hex::decode_array::<12>(b"cafebabefacedbaddecaf888").unwrap();
621 let aad = hex::decode("feedfacedeadbeeffeedfacedeadbeef").unwrap();
622 let round_keys = expand_key(&key);
623 let ghash_table = precompute_ghash_table(&round_keys);
624 let cipher = Aes256Gcm::new(&key);
625 let mut buf: Vec<u8> = vec![];
626 let tag = cipher
627 .0
628 .encrypt_in_place_soft(&mut buf, &round_keys, &ghash_table, &nonce, &aad);
629 let tag_bytes: [u8; 16] = tag.as_ref().try_into().unwrap();
630 cipher
631 .0
632 .decrypt_in_place_soft(&mut buf, &round_keys, &ghash_table, &tag_bytes, &nonce, &aad)
633 .expect("decrypt failed");
634 }
635
636 #[test]
637 fn nist_gcm_test_vectors_dispatch() {
638 for v in NIST_GCM_VECTORS.iter().chain(EXTRA_GCM_VECTORS.iter()) {
639 let key: [u8; 32] = hex::decode_array::<32>(v.key.as_bytes()).unwrap();
640 let nonce: [u8; 12] = hex::decode_array::<12>(v.nonce.as_bytes()).unwrap();
641 let pt = hex::decode(v.pt).unwrap();
642 let aad = hex::decode(v.aad).unwrap();
643 let expected_ct = hex::decode(v.ct).unwrap();
644 let expected_tag: [u8; 16] = hex::decode_array::<16>(v.tag.as_bytes()).unwrap();
645
646 let cipher = Aes256Gcm::new(&key);
647
648 let mut buf = pt.clone();
649 let tag = cipher.encrypt_in_place(&mut buf, &nonce[..], &aad);
650 assert_eq!(&buf[..], &expected_ct[..], "dispatch ciphertext mismatch key={}", v.key);
651 assert_eq!(tag.as_ref(), &expected_tag[..], "dispatch tag mismatch key={}", v.key);
652
653 let mut buf2 = expected_ct.clone();
654 cipher
655 .decrypt_in_place(&mut buf2, &nonce[..], &aad, &expected_tag)
656 .expect("dispatch decrypt failed");
657 assert_eq!(buf2, pt);
658 }
659 }
660
661 #[test]
662 fn wycheproof_gcm_vectors() {
663 let data: serde_json::Value =
664 serde_json::from_str(include_str!("../../testdata/wycheproof/testvectors_v1/aes_gcm_test.json")).unwrap();
665 let mut valid_tested = 0u64;
666 let mut invalid_tested = 0u64;
667 for group in data["testGroups"].as_array().unwrap() {
668 if group["keySize"].as_u64() != Some(256) {
669 continue;
670 }
671 if group["ivSize"].as_u64() != Some(96) {
672 continue;
673 }
674 if group["tagSize"].as_u64() != Some(128) {
675 continue;
676 }
677 for test in group["tests"].as_array().unwrap() {
678 let key_hex = test["key"].as_str().unwrap();
679 let iv_hex = test["iv"].as_str().unwrap();
680 let msg_hex = test["msg"].as_str().unwrap();
681 let aad_hex = test["aad"].as_str().unwrap();
682 let ct_hex = test["ct"].as_str().unwrap();
683 let tag_hex = test["tag"].as_str().unwrap();
684 let result = test["result"].as_str().unwrap();
685
686 let key = hex::decode_array::<32>(key_hex.as_bytes()).unwrap();
687 let nonce = hex::decode_array::<12>(iv_hex.as_bytes()).unwrap();
688 let expected_ct = hex::decode(ct_hex).unwrap();
689 let expected_tag = hex::decode_array::<16>(tag_hex.as_bytes()).unwrap();
690 let pt = hex::decode(msg_hex).unwrap();
691 let aad = hex::decode(aad_hex).unwrap();
692
693 let cipher = Aes256Gcm::new(&key);
694
695 if result == "valid" {
696 let mut buf = pt.clone();
697 let tag = cipher.encrypt_in_place(&mut buf, &nonce[..], &aad);
698 assert_eq!(buf, expected_ct, "wycheproof GCM tcId={} ct mismatch", test["tcId"]);
699 assert_eq!(
700 tag.as_ref(),
701 &expected_tag[..],
702 "wycheproof GCM tcId={} tag mismatch",
703 test["tcId"]
704 );
705
706 let mut buf2 = expected_ct.clone();
707 cipher
708 .decrypt_in_place(&mut buf2, &nonce[..], &aad, &expected_tag[..])
709 .expect("wycheproof GCM decrypt failed");
710 assert_eq!(buf2, pt, "wycheproof GCM tcId={} pt mismatch", test["tcId"]);
711 valid_tested += 1;
712 } else {
713 let mut buf = expected_ct.clone();
714 let result = cipher.decrypt_in_place(&mut buf, &nonce[..], &aad, &expected_tag[..]);
715 assert!(
716 result.is_err(),
717 "wycheproof GCM tcId={} expected invalid but passed",
718 test["tcId"]
719 );
720 invalid_tested += 1;
721 }
722 }
723 }
724 assert!(valid_tested > 0, "no valid AES-GCM wycheproof tests were run");
725 assert!(invalid_tested > 0, "no invalid AES-GCM wycheproof tests were run");
726 }
727}