use super::Error;
use super::{base64_decode, base64_encode};
use aes_gcm_siv::{
Aes256GcmSiv, Nonce,
aead::{Aead, KeyInit},
};
use std::sync::LazyLock;
type Result<T, E = Error> = std::result::Result<T, E>;
#[allow(deprecated)]
static PINGAP_NONCE: LazyLock<&Nonce> =
LazyLock::new(|| Nonce::from_slice(b"pingap nonce"));
fn generate_key(key: &str) -> Vec<u8> {
let key_size = 32;
let buf = key.as_bytes();
let pos = buf.len();
if pos > key_size {
return buf[0..key_size].to_vec();
}
if pos == key_size {
return buf.to_vec();
}
let mut block: Vec<u8> = vec![0; key_size];
block[..pos].copy_from_slice(buf);
block
}
pub fn aes_encrypt(key: &str, data: &str) -> Result<String> {
let cipher =
Aes256GcmSiv::new_from_slice(&generate_key(key)).map_err(|e| {
Error::Invalid {
message: e.to_string(),
}
})?;
let cipher_text =
cipher
.encrypt(&PINGAP_NONCE, data.as_bytes())
.map_err(|e| Error::Aes {
message: e.to_string(),
})?;
Ok(base64_encode(&cipher_text))
}
pub fn aes_decrypt(key: &str, data: &str) -> Result<String> {
let cipher =
Aes256GcmSiv::new_from_slice(&generate_key(key)).map_err(|e| {
Error::Invalid {
message: e.to_string(),
}
})?;
let cipher_text =
base64_decode(data).map_err(|e| Error::Base64Decode { source: e })?;
let plaintext = cipher
.decrypt(&PINGAP_NONCE, cipher_text.as_ref())
.map_err(|e| Error::Aes {
message: e.to_string(),
})?;
let data = std::str::from_utf8(&plaintext).map_err(|e| Error::Invalid {
message: e.to_string(),
})?;
Ok(data.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_aes_encrypt() {
for key in [
"12345678901234567890123456789012",
"12345678901234567890123456789012ABC",
"1234567890123456789012345678901",
] {
let data = "hello";
let result = aes_encrypt(key, data);
assert_eq!(result.is_ok(), true);
let result = aes_decrypt(key, &result.unwrap());
assert_eq!(result.is_ok(), true);
assert_eq!(result.unwrap(), data);
}
}
}