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// }