wolf_crypto/aead/
aes_gcm.rs

1use core::ffi::c_int;
2use core::mem::MaybeUninit;
3use core::ptr::addr_of_mut;
4use wolf_crypto_sys::{
5    Aes as AesLL,
6    wc_AesFree, wc_AesGcmDecrypt, wc_AesGcmEncrypt, wc_AesGcmSetKey
7};
8use crate::buf::GenericIv;
9use crate::ptr::ConstPtr;
10use crate::{can_cast_u32, Unspecified};
11use crate::aead::{Aad, Tag};
12use crate::aes::{init_aes, Key};
13use crate::opaque_res::Res;
14
15#[inline(always)]
16#[must_use]
17pub(crate) unsafe fn aes_set_key(aes: *mut AesLL, key: ConstPtr<Key>) -> c_int {
18    wc_AesGcmSetKey(
19        aes,
20        key.as_slice().as_ptr(),
21        key.capacity() as u32
22    )
23}
24
25/// Represents an AES-GCM (Galois/Counter Mode) instance.
26#[repr(transparent)]
27pub struct AesGcm {
28    inner: AesLL,
29}
30
31impl AesGcm {
32    /// Create a new AES-GCM instance.
33    ///
34    /// # Arguments
35    ///
36    /// * `key` - The key material to use.
37    ///
38    /// # Returns
39    ///
40    /// A new AES-GCM instance.
41    ///
42    /// # Errors
43    ///
44    /// Returns an error if the key setup fails.
45    pub fn new(key: &Key) -> Result<Self, Unspecified> {
46        unsafe {
47            let (mut aes, mut res) = init_aes(MaybeUninit::<AesLL>::uninit());
48            res.ensure_0(aes_set_key(aes.as_mut_ptr(), ConstPtr::new(key)));
49            res.unit_err_with(|| Self { inner: aes.assume_init() })
50        }
51    }
52
53    /// Encrypt data using AES-GCM with compile-time known sizes.
54    ///
55    /// # Arguments
56    ///
57    /// * `nonce` - The nonce (IV) to use for encryption.
58    /// * `input` - The input data to encrypt.
59    /// * `output` - The output buffer to store the encrypted data.
60    /// * `aad` - Additional Authenticated Data.
61    ///
62    /// # Errors
63    /// 
64    /// - The size of `input` is greater than [`u32::MAX`].
65    /// - The provided AAD's length is greater than [`u32::MAX`].
66    /// 
67    /// # Returns
68    ///
69    /// The associated authentication [`Tag`].
70    ///
71    /// # Example
72    ///
73    /// ```
74    /// use wolf_crypto::{aead::{AesGcm, AadSlice}, aes::Key, buf::Nonce};
75    ///
76    /// let key = Key::Aes256([1u8; 32]);
77    /// let nonce: Nonce = [2u8; 12].into();;
78    ///
79    /// let input = [3u8; 32];
80    /// let mut output = [0u8; 32];
81    ///
82    /// let mut gcm = AesGcm::new(&key).unwrap();
83    /// let tag = gcm.encrypt_sized(nonce, &input, &mut output, AadSlice::EMPTY).unwrap();
84    ///
85    /// assert_ne!(input, output);
86    /// ```
87    #[inline]
88    pub fn encrypt_sized<const C: usize, N: GenericIv, A: Aad>(
89        &mut self, nonce: N, input: &[u8; C], output: &mut [u8; C], aad: A
90    ) -> Result<Tag, Unspecified> {
91        if !aad.is_valid_size() { return Err(Unspecified) }
92        unsafe {
93            // SAFETY:
94            //
95            // Since the input and the output are the same size (assured by the type system) there
96            // is no risk of going out of bounds on any operation.
97            //
98            // The Nonce is also ensured to be of the correct size from the type system.
99            self.encrypt_unchecked(nonce.as_slice(), input.as_slice(), output.as_mut_slice(), aad)
100        }
101    }
102
103    #[inline]
104    #[must_use]
105    fn arg_predicate<A: Aad>(input: &[u8], output: &[u8], aad: &A) -> bool {
106        input.len() <= output.len()
107            && can_cast_u32(input.len())
108            && aad.is_valid_size()
109    }
110
111    /// Try to encrypt data using AES-GCM.
112    ///
113    /// # Arguments
114    ///
115    /// * `nonce` - The nonce (IV) to use for encryption.
116    /// * `input` - The input data to encrypt.
117    /// * `output` - The output buffer to store the encrypted data.
118    /// * `aad` - Additional Authenticated Data.
119    ///
120    /// # Returns
121    ///
122    /// The authentication tag on success, or an error.
123    ///
124    /// # Errors
125    ///
126    /// - If the input buffer is larger than the output buffer.
127    /// - If the input or AAD size is greater than what can be represented by a u32.
128    ///
129    /// # Example
130    ///
131    /// ```
132    /// use wolf_crypto::{aead::{AesGcm, AadSlice}, aes::Key, buf::Nonce};
133    ///
134    /// let key = Key::Aes256([1u8; 32]);
135    /// let nonce: Nonce = [2u8; 12].into();
136    ///
137    /// let mut output = [0u8; 32];
138    /// let input = [3u8; 32];
139    ///
140    /// let mut gcm = AesGcm::new(&key).unwrap();
141    /// let tag = gcm.try_encrypt(nonce, &input, &mut output, AadSlice::EMPTY).unwrap();
142    ///
143    /// assert_ne!(input, output);
144    /// ```
145    #[inline]
146    pub fn try_encrypt<N: GenericIv, A: Aad>(
147        &mut self, nonce: N, input: &[u8], output: &mut [u8], aad: A
148    ) -> Result<Tag, Unspecified> {
149        if !Self::arg_predicate(input, output, &aad) { return Err(Unspecified) }
150
151        unsafe {
152            // SAFETY:
153            //
154            // We've guarded against the output being smaller than the input, the nonce type ensures
155            // the correct size is used.
156            self.encrypt_unchecked(nonce.as_slice(), input, output, aad)
157        }
158    }
159
160    panic_api! {
161    /// Encrypt data using AES-GCM, panicking on failure.
162    ///
163    /// # Arguments
164    ///
165    /// * `nonce` - The nonce (IV) to use for encryption.
166    /// * `input` - The input data to encrypt.
167    /// * `output` - The output buffer to store the encrypted data.
168    /// * `aad` - Additional Authenticated Data.
169    ///
170    /// # Returns
171    ///
172    /// The authentication tag.
173    ///
174    /// # Panics
175    ///
176    /// - If the input buffer is larger than the output buffer.
177    /// - If the input or AAD size is greater than what can be represented by a u32.
178    /// - If the encryption operation fails.
179    ///
180    /// # Example
181    ///
182    /// ```
183    /// use wolf_crypto::{aead::{AesGcm, AadSlice}, aes::Key, buf::Nonce};
184    ///
185    /// let key = Key::Aes256([1u8; 32]);
186    /// let nonce: Nonce = [2u8; 12].into();
187    ///
188    /// let mut output = [0u8; 32];
189    /// let input = [3u8; 32];
190    ///
191    /// let mut gcm = AesGcm::new(&key).unwrap();
192    /// let tag = gcm.encrypt(nonce, &input, &mut output, AadSlice::EMPTY);
193    ///
194    /// assert_ne!(input, output);
195    /// ```
196    #[track_caller]
197    #[inline]
198    pub fn encrypt<N: GenericIv, A: Aad>(
199        &mut self, nonce: N, input: &[u8], output: &mut [u8], aad: A
200    ) -> Tag {
201        self.try_encrypt(nonce, input, output, aad).unwrap()
202    }
203    }
204
205    pub(crate) unsafe fn encrypt_unchecked<A: Aad>(
206        &mut self, nonce: &[u8], input: &[u8], output: &mut [u8], aad: A
207    ) -> Result<Tag, Unspecified> {
208        let mut tag = Tag::new_zeroed();
209        let mut res = Res::new();
210
211        res.ensure_0(wc_AesGcmEncrypt(
212            addr_of_mut!(self.inner),
213            output.as_mut_ptr(),
214            input.as_ptr(),
215            input.len() as u32,
216            nonce.as_ptr(),
217            nonce.len() as u32,
218            tag.as_mut_ptr(),
219            Tag::CAPACITY as u32,
220            aad.ptr(),
221            aad.size()
222        ));
223
224        res.unit_err(tag)
225    }
226
227    /// Decrypt data using AES-GCM with compile-time known sizes.
228    ///
229    /// # Arguments
230    ///
231    /// * `nonce` - The nonce (IV) used for encryption.
232    /// * `input` - The input data to decrypt.
233    /// * `output` - The output buffer to store the decrypted data.
234    /// * `aad` - Additional Authenticated Data.
235    /// * `tag` - The authentication tag from encryption.
236    ///
237    /// # Returns
238    ///
239    /// A `Res` indicating success or failure.
240    ///
241    /// # Example
242    ///
243    /// ```
244    /// use wolf_crypto::{aead::{AesGcm, AadSlice}, aes::Key, buf::Nonce};
245    ///
246    /// let key = Key::Aes256([1u8; 32]);
247    /// let nonce: Nonce = [2u8; 12].into();
248    ///
249    /// let mut ciphertext = [0u8; 32];
250    /// let plaintext = [3u8; 32];
251    /// let aad = AadSlice::EMPTY;
252    ///
253    /// let mut gcm = AesGcm::new(&key).unwrap();
254    /// let tag = gcm.encrypt_sized(nonce.copy(), &plaintext, &mut ciphertext, aad).unwrap();
255    ///
256    /// let mut decrypted = [0u8; 32];
257    /// let result = gcm.decrypt_sized(nonce, &ciphertext, &mut decrypted, aad, &tag);
258    ///
259    /// assert!(result.is_ok());
260    /// assert_eq!(plaintext, decrypted);
261    /// ```
262    #[inline]
263    pub fn decrypt_sized<const C: usize, N: GenericIv, A: Aad>(
264        &mut self, nonce: N, input: &[u8; C], output: &mut [u8; C], aad: A, tag: &Tag
265    ) -> Res {
266        if !aad.is_valid_size() { return Res::ERR }
267        unsafe {
268            // SAFETY:
269            //
270            // Since the input and the output are the same size (assured by the type system) there
271            // is no risk of going out of bounds on any operation.
272            //
273            // The Nonce is also ensured to be of the correct size from the type system.
274            self.decrypt_unchecked(
275                nonce.as_slice(),
276                input.as_slice(), output.as_mut_slice(),
277                aad, tag
278            )
279        }
280    }
281
282    /// Try to decrypt data using AES-GCM.
283    ///
284    /// # Arguments
285    ///
286    /// * `nonce` - The nonce (IV) used for encryption.
287    /// * `input` - The input data to decrypt.
288    /// * `output` - The output buffer to store the decrypted data.
289    /// * `aad` - Additional Authenticated Data.
290    /// * `tag` - The authentication tag from encryption.
291    ///
292    /// # Returns
293    ///
294    /// A `Res` indicating success or failure.
295    ///
296    /// # Errors
297    ///
298    /// - If the input buffer is larger than the output buffer.
299    /// - If the input or AAD size is greater than what can be represented by a u32.
300    /// - If the decryption operation fails (including authentication failure).
301    ///
302    /// # Example
303    ///
304    /// ```
305    /// use wolf_crypto::{aead::AesGcm, aes::Key, buf::Nonce};
306    ///
307    /// let key = Key::Aes256([1u8; 32]);
308    /// let nonce: Nonce = [2u8; 12].into();
309    ///
310    /// let mut ciphertext = [0u8; 32];
311    /// let plaintext = [3u8; 32];
312    ///
313    /// let mut gcm = AesGcm::new(&key).unwrap();
314    /// let tag = gcm.try_encrypt(nonce.copy(), &plaintext, &mut ciphertext, ()).unwrap();
315    ///
316    /// let mut decrypted = [0u8; 32];
317    /// let result = gcm.try_decrypt(nonce, &ciphertext, &mut decrypted, (), &tag);
318    ///
319    /// assert!(result.is_ok());
320    /// assert_eq!(plaintext, decrypted);
321    /// ```
322    #[inline]
323    pub fn try_decrypt<N: GenericIv, A: Aad>(
324        &mut self, nonce: N, input: &[u8], output: &mut [u8], aad: A, tag: &Tag
325    ) -> Res {
326        if !Self::arg_predicate(input, output, &aad) { return Res::ERR }
327
328        unsafe {
329            // SAFETY:
330            //
331            // We've guarded against the output being smaller than the input, the nonce type ensures
332            // the correct size is used.
333            self.decrypt_unchecked(nonce.as_slice(), input, output, aad, tag)
334        }
335    }
336
337    panic_api! {
338    /// Decrypt data using AES-GCM, panicking on failure.
339    ///
340    /// # Arguments
341    ///
342    /// * `nonce` - The nonce (IV) used for encryption.
343    /// * `input` - The input data to decrypt.
344    /// * `output` - The output buffer to store the decrypted data.
345    /// * `aad` - Additional Authenticated Data.
346    /// * `tag` - The authentication tag from encryption.
347    ///
348    /// # Panics
349    ///
350    /// - If the input buffer is larger than the output buffer.
351    /// - If the input or AAD size is greater than what can be represented by a u32.
352    /// - If the decryption operation fails (including authentication failure).
353    ///
354    /// # Example
355    ///
356    /// ```
357    /// use wolf_crypto::{aead::AesGcm, aes::Key, buf::Nonce};
358    ///
359    /// let key = Key::Aes256([1u8; 32]);
360    /// let nonce: Nonce = [2u8; 12].into();
361    ///
362    /// let mut ciphertext = [0u8; 32];
363    /// let plaintext = [3u8; 32];
364    ///
365    /// let mut gcm = AesGcm::new(&key).unwrap();
366    /// let tag = gcm.encrypt(nonce.copy(), &plaintext, &mut ciphertext, ());
367    ///
368    /// let mut decrypted = [0u8; 32];
369    /// gcm.decrypt(nonce, &ciphertext, &mut decrypted, (), &tag);
370    ///
371    /// assert_eq!(plaintext, decrypted);
372    /// ```
373    #[inline]
374    #[track_caller]
375    pub fn decrypt<N: GenericIv, A: Aad>(
376        &mut self, nonce: N, input: &[u8], output: &mut [u8], aad: A, tag: &Tag
377    ) {
378        if self.try_decrypt(nonce, input, output, aad, tag).is_err() {
379            panic!("Decryption failed")
380        }
381    }
382    }
383
384    pub(crate) unsafe fn decrypt_unchecked<A: Aad>(
385        &mut self, nonce: &[u8], input: &[u8], output: &mut [u8], aad: A, tag: &Tag
386    ) -> Res {
387        let mut res = Res::new();
388
389        res.ensure_0(wc_AesGcmDecrypt(
390            addr_of_mut!(self.inner),
391            output.as_mut_ptr(),
392            input.as_ptr(),
393            input.len() as u32,
394            nonce.as_ptr(),
395            nonce.len() as u32,
396            tag.as_ptr(),
397            Tag::CAPACITY as u32,
398            aad.ptr(),
399            aad.size()
400        ));
401
402        res
403    }
404}
405
406mark_fips! { AesGcm, Sealed }
407
408impl Drop for AesGcm {
409    fn drop(&mut self) {
410        unsafe {
411            // SAFETY:
412            //
413            // We are in the drop implementation, so we are never going to be using the
414            // `Aes` type again. Since we are configured to not malloc, this simply zeroes
415            // the secrets that were copied on `wc_AesSetKey` invocation. I wish there
416            // was a way to avoid the copying as I do not like secrets living in memory
417            // more than once, but I understand the decision to do this for ensuring safety.
418            wc_AesFree(addr_of_mut!(self.inner));
419        }
420    }
421}
422
423// SAFETY:
424// All methods which mutate the underlying AES instance require a mutable reference,
425// the only way to obtain a mutable reference across thread boundaries is via synchronization or
426// unsafe in Rust (which then would be the user's responsibility).
427unsafe impl Send for AesGcm {}
428
429// SAFETY:
430// There is no providing of interior mutability in the `AesGcm`, all methods which mutate the
431// underlying AES instance require a mutable reference, thus making this safe to mark `Sync`.
432unsafe impl Sync for AesGcm {}
433
434#[cfg(test)]
435mod gcm_test_utils {
436    use alloc::vec;
437    use alloc::vec::Vec;
438    use super::*;
439    use aes_gcm::aead::Aead;
440    use aes_gcm::{Aes256Gcm, Aes128Gcm, AesGcm, KeyInit};
441    use aes_gcm::aead::consts::{U12, U16};
442    use aes_gcm::aes::Aes192;
443    use crate::buf::Nonce;
444
445    macro_rules! with_rust_crypto_gcm {
446        ($key:expr, |$aead:ident| $do:expr) => {
447            match $key {
448                Key::Aes256(buf) => {
449                    let $aead = Aes256Gcm::new_from_slice(buf.as_slice()).unwrap();
450                    $do
451                },
452                Key::Aes128(buf) => {
453                    let $aead = Aes128Gcm::new_from_slice(buf.as_slice()).unwrap();
454                    $do
455                },
456                Key::Aes192(buf) => {
457                    let $aead = AesGcm::<Aes192, U12, U16>::new_from_slice(
458                        buf.as_slice()
459                    ).unwrap();
460                    $do
461                }
462            }
463        }
464    }
465
466    fn encrypt_rust_crypto_impl(e: impl Aead, nonce: Nonce, plaintext: &[u8]) -> (Vec<u8>, Tag) {
467        let mut res = e
468            .encrypt(aes_gcm::Nonce::from_slice(nonce.as_slice()), plaintext)
469            .unwrap();
470
471        let tag = Tag::new(res.as_slice()[res.len() - 16..].try_into().unwrap());
472        res.truncate(res.len() - 16);
473
474        (res, tag)
475    }
476
477    pub fn encrypt_rust_crypto(key: &Key, nonce: Nonce, plaintext: &[u8]) -> (Vec<u8>, Tag) {
478        with_rust_crypto_gcm!(
479            key,
480            |e| encrypt_rust_crypto_impl(e, nonce, plaintext)
481        )
482    }
483
484    fn construct_cipher_payload(cipher: &[u8], tag: &Tag) -> Vec<u8> {
485        let mut cipher_space = vec![0u8; cipher.len() + Tag::CAPACITY];
486        cipher_space[..cipher.len()].copy_from_slice(cipher);
487        cipher_space[cipher.len()..].copy_from_slice(tag.as_slice());
488        cipher_space
489    }
490
491    fn decrypt_rust_crypto_impl(e: impl Aead, nonce: Nonce, cipher: &[u8], tag: &Tag) -> Vec<u8> {
492        let cipher_space = construct_cipher_payload(cipher, tag);
493        e.decrypt(aes_gcm::Nonce::from_slice(nonce.as_slice()), cipher_space.as_slice()).unwrap()
494    }
495
496    pub fn decrypt_rust_crypto(key: &Key, nonce: Nonce, cipher: &[u8], tag: &Tag) -> Vec<u8> {
497        with_rust_crypto_gcm!(
498            key,
499            |e| decrypt_rust_crypto_impl(e, nonce, cipher, tag)
500        )
501    }
502}
503
504#[cfg(all(test, feature = "can-panic"))]
505mod tests {
506    use super::*;
507    use aes_gcm::{Aes256Gcm, KeyInit};
508    use aes_gcm::aead::{Aead};
509    use aes_gcm::aead::consts::{U12, U16};
510    use crate::aead::AadSlice;
511    use crate::buf::{Iv, Nonce};
512
513    fn encrypt_rust_crypto(key: &[u8], nonce: &[u8], plaintext: &[u8]) -> (Vec<u8>, Tag) {
514        let mut res = Aes256Gcm::new_from_slice(key).unwrap()
515            .encrypt(aes_gcm::Nonce::from_slice(nonce), plaintext)
516            .unwrap();
517
518        let tag = Tag::new(res.as_slice()[res.len() - 16..].try_into().unwrap());
519        res.truncate(res.len() - 16);
520
521        (
522            res,
523            tag
524        )
525    }
526
527    fn decrypt_rust_crypto(key: &[u8], nonce: &[u8], ciphertext: &[u8], tag: &Tag) -> Vec<u8> {
528        let mut cipher_space = vec![0u8; ciphertext.len() + Tag::CAPACITY];
529        cipher_space[..ciphertext.len()].copy_from_slice(ciphertext);
530        cipher_space[ciphertext.len()..].copy_from_slice(tag.as_slice());
531
532        Aes256Gcm::new_from_slice(key).unwrap()
533            .decrypt(nonce.try_into().unwrap(), cipher_space.as_slice())
534            .unwrap()
535    }
536
537    #[allow(dead_code)]
538    #[derive(Debug, Clone)]
539    enum COut<const S: usize> {
540        GcmCrate {
541            ciphertext: Vec<u8>,
542            tag: Tag
543        },
544        Wolf {
545            ciphertext: [u8; S],
546            tag: Tag
547        }
548    }
549
550    impl<const S: usize> COut<S> {
551        fn slice(&self) -> &[u8] {
552            match self {
553                Self::Wolf { ciphertext, ..} => ciphertext.as_slice(),
554                Self::GcmCrate { ciphertext, ..} => ciphertext.as_slice()
555            }
556        }
557    }
558
559    fn compare<const S: usize>(input: &[u8; S]) -> (COut<S>, COut<S>) {
560        let mut out_buf = [0u8; S];
561        let key = Key::Aes256([7; 32]);
562        let nonce = Nonce::new([3; 12]);
563        let aad = AadSlice::EMPTY;
564
565        let tag = AesGcm::new(&key)
566            .unwrap()
567            .encrypt_sized(nonce, input, &mut out_buf, aad).unwrap();
568
569        let (o_out, o_tag) = encrypt_rust_crypto(
570            key.as_slice(), [3; 12].as_slice(), input.as_slice()
571        );
572
573        (COut::Wolf { ciphertext: out_buf, tag }, COut::GcmCrate { ciphertext: o_out, tag: o_tag })
574    }
575
576    fn find_dif_index(left: &[u8], right: &[u8]) -> Option<usize> {
577        left.iter().zip(right.iter()).position(|(l, r)| l != r)
578    }
579
580    #[test]
581    fn encrypt_smoke() {
582        let input = b"hello world";
583        let (wolf, cmp) = compare(input);
584        assert!(find_dif_index(wolf.slice(), cmp.slice()).is_none());
585    }
586
587    #[test]
588    fn encrypt_not_block_multiple() {
589        let input = [7u8; 69];
590        let (wolf, cmp) = compare(&input);
591        assert!(find_dif_index(wolf.slice(), cmp.slice()).is_none());
592    }
593
594    #[test]
595    fn self_bijective_smoke() {
596        let plain = b"hello world";
597        let mut out_buf = [0u8; 11];
598
599        let key = Key::Aes256([7; 32]);
600        let nonce = Nonce::new([3; 12]);
601        let aad = AadSlice::EMPTY;
602
603        let mut aes = AesGcm::new(&key).unwrap();
604
605        let tag = aes
606            .encrypt_sized(nonce.copy(), plain, &mut out_buf, aad)
607            .unwrap();
608
609        let mut de_out = [0u8; 11];
610
611        assert!(aes.decrypt_sized(nonce.copy(), &out_buf, &mut de_out, aad, &tag).is_ok());
612        assert_eq!(&de_out, plain);
613
614        assert!(aes.decrypt_sized(nonce, &out_buf, &mut de_out, aad, &Tag::new_zeroed()).is_err());
615    }
616
617    #[test]
618    fn aes_gcm_crate_bijective_smoke() {
619        let plain = b"hello world";
620        let mut out_buf = [0u8; 11];
621
622        let key = Key::Aes256([7; 32]);
623        let nonce = Nonce::new([3; 12]);
624        let aad = AadSlice::EMPTY;
625
626        let mut aes = AesGcm::new(&key).unwrap();
627
628        let tag = aes
629            .encrypt_sized(nonce.copy(), plain, &mut out_buf, aad)
630            .unwrap();
631
632        let de = decrypt_rust_crypto(
633            key.as_slice(), nonce.as_slice(), out_buf.as_slice(), &tag
634        );
635
636        assert_eq!(de.as_slice(), plain.as_slice());
637
638        let (cipher, tag) = encrypt_rust_crypto(
639            key.as_slice(), nonce.as_slice(), plain.as_slice()
640        );
641
642        assert_eq!(cipher.as_slice(), out_buf.as_slice());
643
644        let mut de_out = [0u8; 11];
645        assert!(aes.try_decrypt(nonce, cipher.as_slice(), &mut de_out, aad, &tag).is_ok());
646
647        assert_eq!(&de_out, plain);
648    }
649
650    #[test]
651    fn nonce_16_byte_smoke() {
652        let plain = b"hello world";
653        let mut out_buf = [0u8; 11];
654
655        let key = Key::Aes256([7; 32]);
656        let nonce = Iv::new([3; 16]);
657        let aad = AadSlice::EMPTY;
658
659        let mut aes = AesGcm::new(&key).unwrap();
660
661        let tag = aes
662            .encrypt_sized(nonce.copy(), plain, &mut out_buf, aad)
663            .unwrap();
664
665        let mut de_out = [0u8; 11];
666
667        assert!(aes.decrypt_sized(nonce.copy(), &out_buf, &mut de_out, aad, &tag).is_ok());
668        assert_eq!(&de_out, plain);
669
670        assert!(aes.decrypt_sized(nonce, &out_buf, &mut de_out, aad, &Tag::new_zeroed()).is_err());
671    }
672
673    #[test]
674    fn always_equal() {
675        let key = Key::Aes192([
676            255, 185, 147, 176, 141, 224, 225, 32, 221, 209, 0, 108, 155, 152, 162, 134, 141, 167,
677            81, 87, 13, 115, 13, 165
678        ]);
679        let nonce = Nonce::new([73, 54, 180, 151, 137, 229, 233, 133, 150, 169, 13, 99]);
680        let aad = AadSlice::EMPTY;
681        let mut aes = AesGcm::new(&key).unwrap();
682
683        for i in 0..255u8 {
684            let input = [i; 1];
685            let mut output = [0u8; 1];
686
687            let out = aes_gcm::AesGcm::<aes::Aes192, U12, U16>::new_from_slice(
688                key.as_slice()
689            )
690                .unwrap()
691                .encrypt(nonce.as_slice().try_into().unwrap(), input.as_slice())
692                .unwrap();
693
694            // encrypting 1 byte, ignore the tag, ciphertext is always equal to the plaintext.
695            assert_eq!(input[0], out[0]);
696
697            let tag = aes.encrypt(nonce.copy(), input.as_slice(), output.as_mut_slice(), aad);
698
699            assert_eq!(input, output);
700
701            let mut plain = [0u8; 1];
702            aes.decrypt(nonce.copy(), output.as_slice(), plain.as_mut_slice(), aad, &tag);
703
704            assert_eq!(plain, input);
705        }
706    }
707}
708
709#[cfg(all(test, not(miri), feature = "can-panic"))]
710mod property_tests {
711    use proptest::prelude::*;
712    use crate::aead::AadSlice;
713    use super::*;
714    use crate::aes::test_utils::*;
715    use super::gcm_test_utils::{encrypt_rust_crypto, decrypt_rust_crypto};
716    use crate::buf::Nonce;
717
718    proptest! {
719        #![proptest_config(ProptestConfig::with_cases(10000))]
720
721        #[test]
722        fn self_bijectivity(
723            input in any::<BoundList<1024>>(),
724            key in any::<Key>(),
725            nonce in any::<Nonce>()
726        ) {
727            let mut output = BoundList::<1024>::new_zeroes(input.len());
728
729            let mut aes = AesGcm::new(&key).unwrap();
730            let tag = aes.encrypt(nonce.copy(), input.as_slice(), output.as_mut_slice(), AadSlice::EMPTY);
731
732            // 1 byte the probability of a specific key and nonce that retains equivalent plaintext
733            // in ciphertext is too high. see the always_equal test for a nice example of this.
734            if input.len() >= 2 {
735                prop_assert_ne!(input, output);
736            }
737
738            let mut plain = BoundList::<1024>::new_zeroes(input.len());
739            aes.decrypt(nonce.copy(), output.as_slice(), plain.as_mut_slice(), AadSlice::EMPTY, &tag);
740
741            prop_assert_eq!(plain.as_slice(), input.as_slice());
742        }
743    }
744
745    // Ensure bijective with rust-crypto
746    proptest! {
747        #![proptest_config(ProptestConfig::with_cases(10000))]
748
749        #[test]
750        fn rust_crypto_to_wolf(
751            input in any::<BoundList<1024>>(),
752            key in any::<Key>(),
753            nonce in any::<Nonce>()
754        ) {
755            let (cipher, tag) = encrypt_rust_crypto(&key, nonce.copy(), input.as_slice());
756
757            let mut plain = BoundList::<1024>::new_zeroes(input.len());
758            AesGcm::new(&key).unwrap()
759                .decrypt(nonce, cipher.as_slice(), plain.as_mut_slice(), AadSlice::EMPTY, &tag);
760
761            prop_assert_eq!(plain, input);
762        }
763
764        #[test]
765        fn wolf_to_rust_crypto(
766            input in any::<BoundList<1024>>(),
767            key in any::<Key>(),
768            nonce in any::<Nonce>()
769        ) {
770            let mut output = BoundList::<1024>::new_zeroes(input.len());
771
772            let mut aes = AesGcm::new(&key).unwrap();
773            let tag = aes.encrypt(nonce.copy(), input.as_slice(), output.as_mut_slice(), AadSlice::EMPTY);
774
775            // 1 byte the probability of a specific key and nonce that retains equivalent plaintext
776            // in ciphertext is too high. see the always_equal test for a nice example of this.
777            if input.len() >= 2 {
778                prop_assert_ne!(input, output);
779            }
780
781            let plain = decrypt_rust_crypto(&key, nonce, output.as_slice(), &tag);
782            prop_assert_eq!(plain.as_slice(), input.as_slice());
783        }
784    }
785}
786
787// I suppose these will be nice once kani has better support for c ffi, right now kani is not
788// working. I'll be tracking the issues regarding c ffi support and see what I can do to get
789// these working in the future. For now, property testing among unit tests are the direction
790// forward.
791// #[cfg(kani)]
792// mod proofs {
793//     use kani::proof;
794//     use super::*;
795//     use crate::aes::test_utils::*;
796//
797//     #[proof]
798//     fn self_bijectivity() {
799//         let input: BoundList<1024> = kani::any();
800//         let mut output = BoundList::<1024>::new_zeroes(input.len());
801//
802//         let key: Key = kani::any();
803//         let nonce: Nonce = kani::any();
804//
805//         let mut aes = AesGcm::new(&key).unwrap();
806//         let tag = aes.encrypt(nonce.copy(), input.as_slice(), output.as_mut_slice(), Aad::EMPTY);
807//
808//         assert_ne!(input, output);
809//
810//         let mut plain = BoundList::<1024>::new_zeroes(input.len());
811//         aes.decrypt(nonce.copy(), output.as_slice(), plain.as_mut_slice(), Aad::EMPTY, &tag);
812//
813//         assert_eq!(plain, input);
814//     }
815// }