use argon2::Argon2;
use chacha20poly1305::{AeadInPlace, KeyInit, Tag, XChaCha20Poly1305};
use crate::{
AAD, Error, HEADER_SIZE, Result, TAG_SIZE,
format::{DerivedKey, Header},
};
#[derive(Clone, Debug)]
pub struct Decryptor<'c> {
header: Header,
dk: DerivedKey,
ciphertext: &'c [u8],
tag: Tag,
}
impl<'c> Decryptor<'c> {
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)?;
let mut dk = [u8::default(); DerivedKey::SIZE];
let argon2 = Argon2::new(
header.argon2_type().into(),
header.argon2_version().into(),
header.params().into(),
);
#[cfg(feature = "alloc")]
argon2
.hash_password_into(passphrase, &header.salt(), &mut dk)
.map_err(Error::InvalidArgon2Context)?;
#[cfg(not(feature = "alloc"))]
{
let mut memory_blocks = crate::MEMORY_BLOCKS;
argon2
.hash_password_into_with_memory(
passphrase,
&header.salt(),
&mut dk,
&mut memory_blocks,
)
.map_err(Error::InvalidArgon2Context)?;
}
let dk = DerivedKey::new(dk);
header.verify_mac(&dk.mac(), ciphertext[84..HEADER_SIZE].into())?;
let (ciphertext, tag) =
ciphertext[HEADER_SIZE..].split_at(ciphertext.len() - HEADER_SIZE - TAG_SIZE);
let tag = *Tag::from_slice(tag);
Ok(Self {
header,
dk,
ciphertext,
tag,
})
};
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<()> {
buf.copy_from_slice(decryptor.ciphertext);
let cipher = XChaCha20Poly1305::new(&decryptor.dk.encrypt());
cipher.decrypt_in_place_detached(
&decryptor.header.nonce(),
AAD,
buf,
&decryptor.tag,
)?;
Ok(())
};
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())
}