Skip to main content

crypto/aes/
aes_gcm.rs

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/// AES-128-GCM authenticated cipher.
13///
14/// Create a new cipher with [`new`](Aes128Gcm::new).
15/// [`encrypt_in_place`](Aead::encrypt_in_place) and
16/// [`decrypt_in_place`](Aead::decrypt_in_place) for authenticated encryption.
17#[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    /// Create a new AES-128-GCM instance from a 16-byte key.
24    ///
25    /// Precomputes the target-specific round keys and GHASH powers (H, H², H³, H⁴)
26    /// using software GF(2¹²⁸) multiplication, so `new()` is safe on any CPU
27    /// and does not require hardware feature detection.
28    #[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/// AES-256-GCM authenticated cipher.
50///
51/// Create a new cipher with [`new`](Aes256Gcm::new).
52/// [`encrypt_in_place`](Aead::encrypt_in_place) and
53/// [`decrypt_in_place`](Aead::decrypt_in_place) for authenticated encryption.
54#[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    /// Create a new AES-256-GCM instance from a 32-byte key.
61    ///
62    /// Precomputes the target-specific round keys and GHASH powers (H, H², H³, H⁴)
63    /// using software GF(2¹²⁸) multiplication, so `new()` is safe on any CPU
64    /// and does not require hardware feature detection.
65    #[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////////////////////////////////////////////////////////////////////////////////////////////////////
87////////////////////////////////////////////////////////////////////////////////////////////////////
88
89/// AES-GCM authenticated cipher, generic over the number of round keys.
90///
91/// `N = 11` for AES-128-GCM, `N = 15` for AES-256-GCM.
92///
93/// On x86-64 machines with AES-NI + PCLMULQDQ the methods automatically
94/// dispatch to the hardware-accelerated path (see `aes_gcm_amd64`).
95///
96/// The struct stores **only** the round keys native to the target architecture.
97/// - x86_64: stores `round_keys_ni` (`[__m128i; N]`) + precomputed GHASH powers
98/// - aarch64: stores `round_keys_arm` (`[uint8x16_t; N]`) + precomputed GHASH powers
99/// - other: stores `round_keys` (`[[u8; 16]; N]`)
100#[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    /// Pure-Rust encrypt implementation.
250    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    /// Pure-Rust decrypt implementation.
273    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]; // 12 bytes
379        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}