use crate::error::Error;
use aes_gcm::aead::rand_core::RngCore;
use aes_gcm::aead::{Aead, OsRng};
use aes_gcm::{Aes128Gcm, Key, KeyInit, Nonce};
use argon2::password_hash::SaltString;
use argon2::{Argon2, PasswordHasher};
use base64::Engine;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
fn derive_key(password: &str, salt: &[u8]) -> Result<[u8; 16], Error> {
let argon2 = Argon2::default();
let salt_string = SaltString::encode_b64(salt)?;
let password_hash = argon2.hash_password(password.as_bytes(), &salt_string)?;
let hash_bytes = password_hash.hash.ok_or(Error::MissingHash)?;
let mut key = [0u8; 16];
key.copy_from_slice(&hash_bytes.as_bytes()[..16]);
Ok(key)
}
pub(super) fn encrypt(enc_key: &str, plaintext: &str) -> Result<String, Error> {
let mut salt = [0u8; 16];
let mut nonce_bytes = [0u8; 12];
OsRng.fill_bytes(&mut salt);
OsRng.fill_bytes(&mut nonce_bytes);
let key_bytes = derive_key(enc_key, &salt)?;
let key = Key::<Aes128Gcm>::from_slice(&key_bytes);
let cipher = Aes128Gcm::new(key);
let nonce = Nonce::from_slice(&nonce_bytes);
let ciphertext = cipher.encrypt(nonce, plaintext.as_bytes())?;
let mut result = Vec::new();
result.extend_from_slice(&salt);
result.extend_from_slice(&nonce_bytes);
result.extend_from_slice(&ciphertext);
Ok(URL_SAFE_NO_PAD.encode(result))
}
pub(super) fn decrypt(enc_key: &str, encrypted_data: &str) -> Result<String, Error> {
let data = URL_SAFE_NO_PAD.decode(encrypted_data)?;
if data.len() < 28 {
return Err(Error::InvalidDataLength(data.len()));
}
let salt = &data[0..16];
let nonce_bytes = &data[16..28];
let ciphertext = &data[28..];
let key_bytes = derive_key(enc_key, salt)?;
let key = Key::<Aes128Gcm>::from_slice(&key_bytes);
let cipher = Aes128Gcm::new(key);
let nonce = Nonce::from_slice(nonce_bytes);
let plaintext = cipher.decrypt(nonce, ciphertext)?;
Ok(String::from_utf8(plaintext)?)
}