use crate::crypto::KEY_LEN;
use super::SALT_LEN;
use anyhow::{Result, anyhow};
use chacha20poly1305::{
Key, XChaCha20Poly1305, XNonce,
aead::{Aead, KeyInit, Payload},
};
use getrandom::fill;
use zeroize::Zeroizing;
pub const NONCE_LEN: usize = 24;
fn secure_random(buf: &mut [u8]) -> Result<()> {
fill(buf).map_err(|_| anyhow!("OS random generator unavailable"))
}
pub fn generate_salt() -> Result<[u8; SALT_LEN]> {
let mut salt = [0u8; SALT_LEN];
secure_random(&mut salt)?;
Ok(salt)
}
pub fn encrypt(key: &[u8], plaintext: &[u8], aad: &[u8]) -> Result<(Vec<u8>, Vec<u8>)> {
if key.len() != KEY_LEN {
return Err(anyhow!("invalid key length"));
}
let cipher = XChaCha20Poly1305::new(Key::from_slice(key));
let mut nonce = vec![0u8; NONCE_LEN];
secure_random(&mut nonce)?;
let ciphertext = cipher
.encrypt(
XNonce::from_slice(&nonce),
Payload {
msg: plaintext,
aad,
},
)
.map_err(|_| anyhow!("encryption failed"))?;
Ok((ciphertext, nonce))
}
pub fn decrypt(
key: &[u8],
nonce: &[u8],
ciphertext: &[u8],
aad: &[u8],
) -> Result<Zeroizing<Vec<u8>>> {
if key.len() != KEY_LEN {
return Err(anyhow!("invalid key length"));
}
if nonce.len() != NONCE_LEN {
return Err(anyhow!("invalid nonce length"));
}
let cipher = XChaCha20Poly1305::new(Key::from_slice(key));
let plaintext = cipher
.decrypt(
XNonce::from_slice(nonce),
Payload {
msg: ciphertext,
aad,
},
)
.map_err(|_| anyhow!("Invalid password or corrupted data"))?;
Ok(Zeroizing::new(plaintext))
}