use std::{fmt::Display, io::Write};
use aes::{
Aes256,
cipher::{BlockEncryptMut, KeyIvInit, block_padding::NoPadding},
};
use byteorder::{BigEndian, ByteOrder};
use cbc::Encryptor;
use flate2::{Compression, write::ZlibEncoder};
use rand::{RngCore, SeedableRng, rngs::StdRng};
use crate::EncryptionMethod;
type Aes256Cbc = Encryptor<Aes256>;
#[derive(Debug, Clone)]
pub enum EncryptionError {
InvalidKey,
InternalError {
message: String,
},
}
impl Display for EncryptionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EncryptionError::InvalidKey => write!(f, "Invalid encryption key provided"),
EncryptionError::InternalError { message } => write!(f, "{}", message),
}
}
}
pub fn encrypt(
data: &[u8],
method: EncryptionMethod,
key: &[u8],
) -> Result<Vec<u8>, EncryptionError> {
if data.is_empty() {
return Ok(vec![0; 0]);
}
match method {
EncryptionMethod::Aes256Zip => encrypt_aes_cbc_zip(data, key),
EncryptionMethod::Aes256Flat => encrypt_aes_cbc_flat(data, key),
}
}
const AES_BLOCK_SIZE: usize = 16;
const AES_IV_SIZE: usize = 16;
fn encrypt_aes_cbc_flat(data: &[u8], key: &[u8]) -> Result<Vec<u8>, EncryptionError> {
if key.len() != 32 {
return Err(EncryptionError::InvalidKey);
}
let mut method_mark: Vec<u8> = vec![0; 2];
BigEndian::write_u16(&mut method_mark, EncryptionMethod::Aes256Flat.to_u16());
let mut header: Vec<u8> = vec![0; 20];
BigEndian::write_u32(&mut header[0..4], data.len() as u32);
let mut final_data = add_padding(data, AES_BLOCK_SIZE);
let iv = generate_aes_iv();
header[4..4 + AES_IV_SIZE].copy_from_slice(&iv);
let cypher = Aes256Cbc::new(key.into(), iv.as_slice().into());
let final_data_len = final_data.len();
if let Err(e) = cypher.encrypt_padded_mut::<NoPadding>(&mut final_data, final_data_len) {
return Err(EncryptionError::InternalError {
message: e.to_string(),
});
}
Ok([method_mark, header, final_data].concat())
}
fn encrypt_aes_cbc_zip(data: &[u8], key: &[u8]) -> Result<Vec<u8>, EncryptionError> {
if key.len() != 32 {
return Err(EncryptionError::InvalidKey);
}
let mut method_mark: Vec<u8> = vec![0; 2];
BigEndian::write_u16(&mut method_mark, EncryptionMethod::Aes256Zip.to_u16());
let mut compressor = ZlibEncoder::new(Vec::new(), Compression::default());
if let Err(e) = compressor.write_all(data) {
return Err(EncryptionError::InternalError {
message: e.to_string(),
});
}
let compressed_bytes = match compressor.finish() {
Ok(b) => b,
Err(e) => {
return Err(EncryptionError::InternalError {
message: e.to_string(),
});
}
};
let mut header: Vec<u8> = vec![0; 20];
BigEndian::write_u32(&mut header[0..4], compressed_bytes.len() as u32);
let mut final_data = add_padding(&compressed_bytes, AES_BLOCK_SIZE);
let iv = generate_aes_iv();
header[4..4 + AES_IV_SIZE].copy_from_slice(&iv);
let cypher = Aes256Cbc::new(key.into(), iv.as_slice().into());
let final_data_len = final_data.len();
if let Err(e) = cypher.encrypt_padded_mut::<NoPadding>(&mut final_data, final_data_len) {
return Err(EncryptionError::InternalError {
message: e.to_string(),
});
}
Ok([method_mark, header, final_data].concat())
}
fn generate_aes_iv() -> Vec<u8> {
let mut iv: Vec<u8> = vec![0; AES_IV_SIZE];
let mut rng = StdRng::from_os_rng();
rng.fill_bytes(&mut iv);
iv
}
fn add_padding(data: &[u8], block_size: usize) -> Vec<u8> {
let padding_size = block_size - (data.len() % block_size);
let padding_bytes: Vec<u8> = vec![0; padding_size];
[data, &padding_bytes].concat()
}