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