use super::constants::{AES_BLOCK_SIZE, CBC_PADDING_BYTE};
use crate::{Error, Key};
use aes::Aes256;
use cbc::{Decryptor, Encryptor};
use cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use rand::RngCore;
type Aes256CbcEnc = Encryptor<Aes256>;
type Aes256CbcDec = Decryptor<Aes256>;
const KEY_OFFSET: usize = 8;
const KEY_LEN: usize = 32;
const IV_SIZE: usize = 16;
#[inline]
pub fn encrypt(plaintext: &[u8], key: &Key) -> Result<Vec<u8>, Error> {
if plaintext.is_empty() {
return Err(Error::EmptyPlaintext);
}
let key_arr = key.subkey::<KEY_OFFSET, KEY_LEN>();
let data_len = plaintext.len();
let padding_size = (AES_BLOCK_SIZE - (data_len % AES_BLOCK_SIZE)) % AES_BLOCK_SIZE;
let total_len = data_len + padding_size;
let mut buffer = Vec::with_capacity(IV_SIZE + total_len);
buffer.resize(IV_SIZE, 0);
rand::thread_rng().fill_bytes(&mut buffer[..IV_SIZE]);
buffer.extend_from_slice(plaintext);
buffer.resize(IV_SIZE + total_len, CBC_PADDING_BYTE);
let cipher = Aes256CbcEnc::new(key_arr.into(), buffer[..IV_SIZE].into());
cipher
.encrypt_padded_mut::<cipher::block_padding::NoPadding>(&mut buffer[IV_SIZE..], total_len)
.map_err(|_| Error::EncryptionFailed)?;
Ok(buffer)
}
#[inline]
pub fn encrypt_into(plaintext: &[u8], key: &Key, out: &mut Vec<u8>) -> Result<(), Error> {
if plaintext.is_empty() {
return Err(Error::EmptyPlaintext);
}
let key_arr = key.subkey::<KEY_OFFSET, KEY_LEN>();
let data_len = plaintext.len();
let padding_size = (AES_BLOCK_SIZE - (data_len % AES_BLOCK_SIZE)) % AES_BLOCK_SIZE;
let total_len = data_len + padding_size;
let start = out.len();
out.reserve(IV_SIZE + total_len);
out.resize(start + IV_SIZE, 0);
rand::thread_rng().fill_bytes(&mut out[start..start + IV_SIZE]);
out.extend_from_slice(plaintext);
out.resize(start + IV_SIZE + total_len, CBC_PADDING_BYTE);
let mut iv = [0u8; IV_SIZE];
iv.copy_from_slice(&out[start..start + IV_SIZE]);
let cipher = Aes256CbcEnc::new(key_arr.into(), (&iv).into());
cipher
.encrypt_padded_mut::<cipher::block_padding::NoPadding>(
&mut out[start + IV_SIZE..start + IV_SIZE + total_len],
total_len,
)
.map_err(|_| Error::EncryptionFailed)?;
Ok(())
}
#[inline]
pub fn decrypt(ciphertext: &[u8], key: &Key) -> Result<Vec<u8>, Error> {
if ciphertext.len() < 2 * IV_SIZE {
return Err(Error::PayloadTooShort);
}
if (ciphertext.len() - IV_SIZE) % AES_BLOCK_SIZE != 0 {
return Err(Error::InvalidBlockLength);
}
let key_arr = key.subkey::<KEY_OFFSET, KEY_LEN>();
let iv = &ciphertext[..IV_SIZE];
let mut buf = ciphertext[IV_SIZE..].to_vec();
let cipher = Aes256CbcDec::new(key_arr.into(), iv.into());
cipher
.decrypt_padded_mut::<cipher::block_padding::NoPadding>(&mut buf)
.map_err(|_| Error::DecryptionFailed)?;
let mut end = buf.len();
while end > 0 && buf[end - 1] == CBC_PADDING_BYTE {
end -= 1;
}
buf.truncate(end);
Ok(buf)
}
#[inline]
pub fn decrypt_into(ciphertext: &[u8], key: &Key, out: &mut Vec<u8>) -> Result<(), Error> {
if ciphertext.len() < 2 * IV_SIZE {
return Err(Error::PayloadTooShort);
}
let key_arr = key.subkey::<KEY_OFFSET, KEY_LEN>();
if (ciphertext.len() - IV_SIZE) % AES_BLOCK_SIZE != 0 {
return Err(Error::InvalidBlockLength);
}
let mut iv = [0u8; IV_SIZE];
iv.copy_from_slice(&ciphertext[..IV_SIZE]);
let ct_start = out.len();
out.extend_from_slice(&ciphertext[IV_SIZE..]);
let cipher = Aes256CbcDec::new(key_arr.into(), (&iv).into());
cipher
.decrypt_padded_mut::<cipher::block_padding::NoPadding>(&mut out[ct_start..])
.map_err(|_| Error::DecryptionFailed)?;
let mut end = out.len();
while end > ct_start && out[end - 1] == CBC_PADDING_BYTE {
end -= 1;
}
out.truncate(end);
Ok(())
}