use aes::cipher::{KeyIvInit, StreamCipher, generic_array::GenericArray};
use hmac::Mac;
use crate::{
Aes256Ctr128BE, Error, HEADER_SIZE, HmacSha256, HmacSha256Key, HmacSha256Output, Result,
TAG_SIZE,
format::{DerivedKey, Header},
};
#[derive(Clone, Debug)]
pub struct Decryptor<'c> {
header: Header,
dk: DerivedKey,
ciphertext: &'c [u8],
mac: HmacSha256Output,
}
impl<'c> Decryptor<'c> {
#[allow(clippy::missing_panics_doc)]
pub fn new(ciphertext: &'c impl AsRef<[u8]>, passphrase: impl AsRef<[u8]>) -> Result<Self> {
let inner = |ciphertext: &'c [u8], passphrase: &[u8]| -> Result<Self> {
let mut header = Header::parse(ciphertext)?;
header.verify_checksum(&ciphertext[48..64])?;
let mut dk = [u8::default(); DerivedKey::SIZE];
scrypt::scrypt(passphrase, &header.salt(), &header.params().into(), &mut dk)
.expect("derived key size should be 64 bytes");
let dk = DerivedKey::new(dk);
header.verify_mac(&dk.mac(), ciphertext[64..HEADER_SIZE].into())?;
let (ciphertext, mac) =
ciphertext[HEADER_SIZE..].split_at(ciphertext.len() - HEADER_SIZE - TAG_SIZE);
let mac = *HmacSha256Output::from_slice(mac);
Ok(Self {
header,
dk,
ciphertext,
mac,
})
};
inner(ciphertext.as_ref(), passphrase.as_ref())
}
pub fn decrypt(&self, buf: &mut (impl AsMut<[u8]> + ?Sized)) -> Result<()> {
let inner = |decryptor: &Self, buf: &mut [u8]| -> Result<()> {
fn verify_mac(data: &[u8], key: &HmacSha256Key, tag: &HmacSha256Output) -> Result<()> {
let mut mac = HmacSha256::new_from_slice(key)
.expect("HMAC-SHA-256 key size should be 256 bits");
mac.update(data);
mac.verify(tag).map_err(Error::InvalidMac)
}
buf.copy_from_slice(decryptor.ciphertext);
let mut cipher = Aes256Ctr128BE::new(&decryptor.dk.encrypt(), &GenericArray::default());
cipher.apply_keystream(buf);
let data = [&decryptor.header.as_bytes(), decryptor.ciphertext].concat();
verify_mac(&data, &decryptor.dk.mac(), &decryptor.mac)
};
inner(self, buf.as_mut())
}
#[cfg(feature = "alloc")]
#[inline]
pub fn decrypt_to_vec(&self) -> Result<alloc::vec::Vec<u8>> {
let mut buf = vec![u8::default(); self.out_len()];
self.decrypt(&mut buf)?;
Ok(buf)
}
#[must_use]
#[inline]
pub const fn out_len(&self) -> usize {
self.ciphertext.len()
}
}
#[cfg(feature = "alloc")]
#[inline]
pub fn decrypt(
ciphertext: impl AsRef<[u8]>,
passphrase: impl AsRef<[u8]>,
) -> Result<alloc::vec::Vec<u8>> {
Decryptor::new(&ciphertext, passphrase).and_then(|c| c.decrypt_to_vec())
}