use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
use rand::RngCore;
use crate::{
error::{Error, Result},
PrivateKey,
};
pub trait Aes {
fn encrypt(&self, data: &str) -> String;
fn encrypt_with_nonce(&self, data: &str, nonce: &str) -> Result<String>;
fn decrypt(&self, data: &str) -> Result<String>;
fn decrypt_with_nonce(&self, data: &str, nonce: &str) -> Result<String>;
}
impl From<&PrivateKey> for Aes256Gcm {
fn from(value: &PrivateKey) -> Self {
Self::new_from_slice(value.as_slice()).unwrap()
}
}
impl Aes for PrivateKey {
fn encrypt(&self, data: &str) -> String {
let mut nonce_bytes = [0u8; 12];
rand::thread_rng().fill_bytes(&mut nonce_bytes);
let nonce = Nonce::from_slice(&nonce_bytes);
let key: Aes256Gcm = self.into();
let ciphertext = key.encrypt(nonce, data.as_bytes()).unwrap();
let mut result = vec![];
result.extend_from_slice(&nonce_bytes);
result.extend_from_slice(&ciphertext);
hex::encode(result)
}
fn encrypt_with_nonce(&self, data: &str, nonce: &str) -> Result<String> {
let nonce = Nonce::from_slice(nonce.as_bytes());
let key: Aes256Gcm = self.into();
let ciphertext = key
.encrypt(nonce, data.as_bytes())
.map_err(|e| Error::EncryptError(e.to_string()))?;
Ok(hex::encode(ciphertext))
}
fn decrypt(&self, data: &str) -> Result<String> {
let data = hex::decode(data)?;
if data.len() < 12 {
return Err(Error::LengthError(
"Encrypted data is too short".to_string(),
));
}
let nonce = Nonce::from_slice(&data[0..12]);
let key: Aes256Gcm = self.into();
let plaintext = key
.decrypt(nonce, &data[12..])
.map_err(|e| Error::DecryptError(e.to_string()))?;
Ok(String::from_utf8(plaintext).unwrap())
}
fn decrypt_with_nonce(&self, data: &str, nonce: &str) -> Result<String> {
let data = hex::decode(data)?;
let nonce = Nonce::from_slice(nonce.as_bytes());
let key: Aes256Gcm = self.into();
let plaintext = key
.decrypt(nonce, data.as_ref())
.map_err(|e| Error::DecryptError(e.to_string()))?;
Ok(String::from_utf8(plaintext).unwrap())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_aes() {
let text = "Hello World!";
let key = PrivateKey::new();
let encrypted = key.encrypt(text);
let decrypted = key.decrypt(&encrypted).unwrap();
assert_eq!(text, decrypted);
let nonce = "unique nonce";
let encrypted = key.encrypt_with_nonce(text, nonce).unwrap();
let decrypted = key.decrypt_with_nonce(&encrypted, nonce).unwrap();
assert_eq!(text, decrypted);
}
}