use crate::aliases::PasswordString;
use crate::aliases::{Aes256Key32, EncryptedSessionBlock48, HmacSha256, Iv16};
use crate::constants::AESCRYPT_LATEST_VERSION;
use crate::constants::{PBKDF2_MAX_ITER, PBKDF2_MIN_ITER};
use crate::encryption::derive_setup_key;
use crate::encryption::encrypt_session_block;
use crate::encryption::stream::encrypt_stream;
use crate::encryption::write::{
write_extensions, write_header, write_hmac, write_iterations, write_octets, write_public_iv,
};
use crate::error::AescryptError;
use aes::cipher::KeyInit;
use aes::Aes256Enc;
use hmac::Mac;
use secure_gate::RevealSecret;
use std::io::{Read, Write};
#[inline(always)]
pub fn encrypt<R, W>(
mut input: R,
mut output: W,
password: &PasswordString,
kdf_iterations: u32,
) -> Result<(), AescryptError>
where
R: Read,
W: Write,
{
if password.is_empty() {
return Err(AescryptError::Header("empty password".into()));
}
if !(PBKDF2_MIN_ITER..=PBKDF2_MAX_ITER).contains(&kdf_iterations) {
return Err(AescryptError::Header("invalid KDF iterations".into()));
}
write_header(&mut output, AESCRYPT_LATEST_VERSION)?;
write_extensions(&mut output, AESCRYPT_LATEST_VERSION, None)?;
let public_iv = Iv16::from_random();
let session_iv = Iv16::from_random();
let session_key = Aes256Key32::from_random();
write_iterations(&mut output, kdf_iterations, AESCRYPT_LATEST_VERSION)?;
write_public_iv(&mut output, &public_iv)?;
let mut setup_key = Aes256Key32::new([0u8; 32]);
derive_setup_key(password, &public_iv, kdf_iterations, &mut setup_key)?;
let cipher = setup_key.with_secret(|key| Aes256Enc::new(key.into()));
let mut hmac = setup_key.with_secret(|key| <HmacSha256 as Mac>::new_from_slice(key).unwrap());
let mut enc_block = EncryptedSessionBlock48::new([0u8; 48]);
encrypt_session_block(
&cipher,
&session_iv,
&session_key,
&public_iv,
&mut enc_block,
&mut hmac,
)?;
hmac.update(&[AESCRYPT_LATEST_VERSION]);
enc_block.with_secret(|eb| write_octets(&mut output, eb))?;
write_hmac(&mut output, hmac)?;
encrypt_stream(&mut input, &mut output, &session_iv, &session_key)?;
Ok(())
}