use alloc::vec::Vec;
use aes_gcm::aead::{Aead, AeadCore, KeyInit, OsRng};
use aes_gcm::{Aes256Gcm as AesGcmCipher, Key};
pub use aes_gcm::Nonce;
use crate::error::CryptoError;
use crate::operations::encryption::{CryptoAead, NonceGeneration};
pub struct Aes256Gcm {
cipher: AesGcmCipher,
}
impl Aes256Gcm {
pub fn new(key: impl AsRef<[u8]>) -> Result<Self, CryptoError> {
let key = key.as_ref();
if key.len() != 32 {
return Err(CryptoError::InvalidKeySize);
}
let key = Key::<AesGcmCipher>::from_slice(key);
let cipher = AesGcmCipher::new(key);
Ok(Self { cipher })
}
pub fn key_size() -> usize {
32 }
pub fn nonce_size() -> usize {
12 }
pub fn tag_size() -> usize {
16 }
}
impl Aead for Aes256Gcm {
fn encrypt<'msg, 'aad>(
&self,
nonce: &aes_gcm::aead::Nonce<Self>,
plaintext: impl Into<aes_gcm::aead::Payload<'msg, 'aad>>,
) -> Result<Vec<u8>, aes_gcm::aead::Error> {
self.cipher.encrypt(nonce, plaintext)
}
fn decrypt<'msg, 'aad>(
&self,
nonce: &aes_gcm::aead::Nonce<Self>,
ciphertext: impl Into<aes_gcm::aead::Payload<'msg, 'aad>>,
) -> Result<Vec<u8>, aes_gcm::aead::Error> {
self.cipher.decrypt(nonce, ciphertext)
}
}
impl AeadCore for Aes256Gcm {
type NonceSize = <AesGcmCipher as AeadCore>::NonceSize;
type TagSize = <AesGcmCipher as AeadCore>::TagSize;
type CiphertextOverhead = <AesGcmCipher as AeadCore>::CiphertextOverhead;
}
impl CryptoAead for Aes256Gcm {}
impl NonceGeneration for Aes256Gcm {
type Nonce = aes_gcm::aead::Nonce<Self>;
fn generate_nonce() -> Self::Nonce {
AesGcmCipher::generate_nonce(&mut OsRng)
}
fn nonce_size() -> usize {
12 }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::operations::encryption::NonceGeneration;
#[test]
fn test_aes_256_gcm_basic() -> Result<(), CryptoError> {
let key = [0x42u8; 32]; let aes_gcm = Aes256Gcm::new(key)?;
let plaintext = b"Hello, AES-GCM world!";
let nonce = AesGcmCipher::generate_nonce(&mut OsRng);
let ciphertext = aes_gcm.encrypt(&nonce, plaintext.as_ref())?;
assert_ne!(ciphertext.as_slice(), plaintext); assert!(ciphertext.len() > plaintext.len());
let decrypted = aes_gcm.decrypt(&nonce, ciphertext.as_ref())?;
assert_eq!(decrypted, plaintext);
Ok(())
}
#[test]
fn test_aes_256_gcm_properties() {
assert_eq!(Aes256Gcm::key_size(), 32);
assert_eq!(Aes256Gcm::nonce_size(), 12);
assert_eq!(Aes256Gcm::tag_size(), 16);
}
#[test]
fn test_aes_256_gcm_wrong_key_size() {
let wrong_key = [0x42u8; 16]; let result = Aes256Gcm::new(wrong_key);
assert!(result.is_err());
if let Err(err) = result {
assert!(matches!(err, CryptoError::InvalidKeySize));
}
}
#[test]
fn test_aes_256_gcm_random_nonce() -> Result<(), CryptoError> {
let key = [0x42u8; 32];
let aes_gcm = Aes256Gcm::new(key)?;
let plaintext = b"Same plaintext";
let nonce1 = AesGcmCipher::generate_nonce(&mut OsRng);
let nonce2 = AesGcmCipher::generate_nonce(&mut OsRng);
let ciphertext1 = aes_gcm.encrypt(&nonce1, plaintext.as_ref())?;
let ciphertext2 = aes_gcm.encrypt(&nonce2, plaintext.as_ref())?;
assert_ne!(ciphertext1, ciphertext2);
let decrypted1 = aes_gcm.decrypt(&nonce1, ciphertext1.as_ref())?;
let decrypted2 = aes_gcm.decrypt(&nonce2, ciphertext2.as_ref())?;
assert_eq!(decrypted1, plaintext);
assert_eq!(decrypted2, plaintext);
Ok(())
}
#[test]
fn test_aes_256_gcm_authentication() -> Result<(), CryptoError> {
let key = [0x42u8; 32];
let aes_gcm = Aes256Gcm::new(key)?;
let plaintext = b"Authenticated message";
let nonce = AesGcmCipher::generate_nonce(&mut OsRng);
let mut ciphertext = aes_gcm.encrypt(&nonce, plaintext.as_ref())?;
let last_idx = ciphertext.len() - 1;
ciphertext[last_idx] ^= 0x01;
let result = aes_gcm.decrypt(&nonce, ciphertext.as_ref());
assert!(result.is_err());
Ok(())
}
#[test]
fn test_aes_256_gcm_with_aad() -> Result<(), CryptoError> {
let key = [0x42u8; 32];
let aes_gcm = Aes256Gcm::new(key)?;
let plaintext = b"Secret message";
let aad = b"additional data";
let nonce = AesGcmCipher::generate_nonce(&mut OsRng);
let payload = aes_gcm::aead::Payload { msg: plaintext.as_ref(), aad: aad.as_ref() };
let ciphertext = aes_gcm.encrypt(&nonce, payload)?;
assert_ne!(ciphertext.as_slice(), plaintext);
let payload_decrypt = aes_gcm::aead::Payload { msg: ciphertext.as_ref(), aad: aad.as_ref() };
let decrypted = aes_gcm.decrypt(&nonce, payload_decrypt)?;
assert_eq!(decrypted, plaintext);
let wrong_aad = b"wrong additional data";
let wrong_payload = aes_gcm::aead::Payload { msg: ciphertext.as_ref(), aad: wrong_aad.as_ref() };
let result = aes_gcm.decrypt(&nonce, wrong_payload);
assert!(result.is_err());
Ok(())
}
#[test]
fn test_aes_256_gcm_empty_plaintext() -> Result<(), CryptoError> {
let key = [0x42u8; 32];
let aes_gcm = Aes256Gcm::new(key)?;
let plaintext = b"";
let nonce = AesGcmCipher::generate_nonce(&mut OsRng);
let ciphertext = aes_gcm.encrypt(&nonce, plaintext.as_ref())?;
assert_eq!(ciphertext.len(), 16);
let decrypted = aes_gcm.decrypt(&nonce, ciphertext.as_ref())?;
assert_eq!(decrypted, plaintext);
Ok(())
}
#[test]
fn test_aes_256_gcm_large_data() -> Result<(), CryptoError> {
let key = [0x42u8; 32];
let aes_gcm = Aes256Gcm::new(key)?;
let plaintext = vec![0x55u8; 8192]; let nonce = AesGcmCipher::generate_nonce(&mut OsRng);
let ciphertext = aes_gcm.encrypt(&nonce, plaintext.as_ref())?;
assert_eq!(ciphertext.len(), 8192 + 16);
let decrypted = aes_gcm.decrypt(&nonce, ciphertext.as_ref())?;
assert_eq!(decrypted, plaintext);
Ok(())
}
#[test]
fn test_aes_256_gcm_nonce_generation() {
let nonce1 = <Aes256Gcm as NonceGeneration>::generate_nonce();
let nonce2 = <Aes256Gcm as NonceGeneration>::generate_nonce();
assert_ne!(&nonce1[..], &nonce2[..]);
assert_eq!(nonce1.len(), 12);
assert_eq!(nonce2.len(), 12);
assert_eq!(<Aes256Gcm as NonceGeneration>::nonce_size(), 12);
}
}