use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm, Nonce,
};
use anyhow::{anyhow, bail, Result};
use rand::{rngs::OsRng, TryRngCore};
pub fn encrypt_bytes(plaintext: &[u8], key: &[u8]) -> Result<Vec<u8>> {
if key.len() != 32 {
bail!("Key must be exactly 32 bytes");
}
let cipher = Aes256Gcm::new(key.into());
let mut nonce_bytes = [0u8; 12];
OsRng.try_fill_bytes(&mut nonce_bytes)?;
let nonce = Nonce::from_slice(&nonce_bytes);
let ciphertext = cipher
.encrypt(nonce, plaintext)
.map_err(|e| anyhow!("Encryption failed: {}", e))?;
let mut output = nonce_bytes.to_vec();
output.extend_from_slice(&ciphertext);
Ok(output)
}
pub fn encrypt_string(plaintext: &str, key: &[u8]) -> Result<Vec<u8>> {
encrypt_bytes(plaintext.as_bytes(), key)
}
pub fn decrypt_bytes(encrypted: &[u8], key: &[u8]) -> Result<Vec<u8>> {
if key.len() != 32 {
bail!("Key must be exactly 32 bytes");
}
if encrypted.len() < 12 {
bail!("Encrypted data too short");
}
let cipher = Aes256Gcm::new(key.into());
let nonce = Nonce::from_slice(&encrypted[..12]);
let ciphertext = &encrypted[12..];
cipher
.decrypt(nonce, ciphertext)
.map_err(|e| anyhow!("Decryption failed: {}", e))
}
pub fn decrypt_string(encrypted: &[u8], key: &[u8]) -> Result<String> {
let plaintext_bytes = decrypt_bytes(encrypted, key)?;
Ok(String::from_utf8(plaintext_bytes)?)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() -> Result<()> {
let mut key = [0u8; 32];
OsRng.try_fill_bytes(&mut key)?;
let message = "Hello, world!";
let encrypted = encrypt_string(message, &key).expect("Encryption should succeed");
assert!(encrypted.len() > message.len());
assert_eq!(encrypted.len(), message.len() + 12 + 16); let decrypted = decrypt_string(&encrypted, &key).expect("Decryption should succeed");
assert_eq!(message, decrypted);
let mut wrong_key = [0u8; 32];
OsRng.try_fill_bytes(&mut wrong_key)?;
assert!(decrypt_string(&encrypted, &wrong_key).is_err());
Ok(())
}
}