use anyhow::Result;
use aes_gcm::{
aead::{Aead, KeyInit, OsRng},
Aes256Gcm, Nonce,
};
use rand::RngCore;
pub fn generate_session_key() -> [u8; 32] {
let mut key = [0u8; 32];
OsRng.fill_bytes(&mut key);
key
}
pub fn encrypt(plaintext: &[u8], key: &[u8; 32]) -> Result<Vec<u8>> {
let cipher = Aes256Gcm::new(key.into());
let mut nonce_bytes = [0u8; 12];
OsRng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::from_slice(&nonce_bytes);
let ciphertext = cipher
.encrypt(nonce, plaintext)
.map_err(|e| anyhow::anyhow!("encrypt failed: {:?}", e))?;
let mut out = Vec::with_capacity(12 + ciphertext.len());
out.extend_from_slice(&nonce_bytes);
out.extend_from_slice(&ciphertext);
Ok(out)
}
pub fn decrypt(data: &[u8], key: &[u8; 32]) -> Result<Vec<u8>> {
if data.len() < 12 {
anyhow::bail!("ciphertext too short");
}
let (nonce_bytes, ciphertext) = data.split_at(12);
let cipher = Aes256Gcm::new(key.into());
let nonce = Nonce::from_slice(nonce_bytes);
cipher
.decrypt(nonce, ciphertext)
.map_err(|e| anyhow::anyhow!("decrypt failed: {:?}", e))
}
pub fn derive_key(password: &str, salt: &[u8]) -> [u8; 32] {
use hmac::Hmac;
use sha2::Sha256;
let mut key = [0u8; 32];
pbkdf2::pbkdf2::<Hmac<Sha256>>(
password.as_bytes(),
salt,
100_000,
&mut key,
).unwrap_or(());
key
}
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Vec<u8> {
use hmac::{Hmac, Mac};
use sha2::Sha256;
let mut mac = <Hmac<Sha256> as hmac::Mac>::new_from_slice(key).expect("HMAC init");
mac.update(data);
mac.finalize().into_bytes().to_vec()
}
pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
a.iter().zip(b.iter()).fold(0u8, |acc, (x, y)| acc | (x ^ y)) == 0
}