use crypto::buffer::{BufferResult, ReadBuffer, WriteBuffer};
use crypto::{aes, blockmodes, buffer};
use rand::prelude::*;
use serde::{Deserialize, Serialize};
use std::cmp;
use zeroize::Zeroize;
#[cfg(feature = "profile")]
use flamer::*;
/// Tag for the encryption algorthim and IV used by a particular chunk
#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
pub enum Encryption {
AES256CBC { iv: [u8; 16] },
AES256CTR { iv: [u8; 16] },
NoEncryption,
}
impl Encryption {
/// Creates an AES256CBC with a random, securely generated IV
pub fn new_aes256cbc() -> Encryption {
let mut iv: [u8; 16] = [0; 16];
thread_rng().fill_bytes(&mut iv);
Encryption::AES256CBC { iv }
}
/// Creates a new AES256CTR with a random securely generated IV
pub fn new_aes256ctr() -> Encryption {
let mut iv: [u8; 16] = [0; 16];
thread_rng().fill_bytes(&mut iv);
Encryption::AES256CTR { iv }
}
/// Returns the key length of this encryption method in bytes
pub fn key_length(&self) -> usize {
match self {
Encryption::NoEncryption => 0,
Encryption::AES256CBC { .. } => 32,
Encryption::AES256CTR { .. } => 32,
}
}
#[cfg_attr(feature = "profile", flame)]
/// Encrypts a bytestring using the algrothim specified in the tag, and the
/// given key.
///
/// Still requires a key in the event of no encryption, but it does not read this
/// key, so any value can be used. Will pad key with zeros if it is too short
///
/// Will panic on encryption failure
pub fn encrypt(&self, data: &[u8], key: &[u8]) -> Vec<u8> {
match self {
Encryption::NoEncryption => data.to_vec(),
Encryption::AES256CBC { iv } => {
// Create a key of the correct length, and fill it with
// zeros to start with
let mut proper_key: [u8; 32] = [0; 32];
proper_key[..cmp::min(key.len(), 32)]
.clone_from_slice(&key[..cmp::min(key.len(), 32)]);
let mut encryptor = aes::cbc_encryptor(
aes::KeySize::KeySize256,
&proper_key,
iv,
blockmodes::PkcsPadding,
);
let mut final_result = Vec::new();
let mut read_buffer = buffer::RefReadBuffer::new(data);
let mut buffer = [0; 4096];
let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);
loop {
let result = encryptor
.encrypt(&mut read_buffer, &mut write_buffer, true)
.unwrap();
final_result.extend(
write_buffer
.take_read_buffer()
.take_remaining()
.iter()
.cloned(),
);
match result {
BufferResult::BufferUnderflow => break,
BufferResult::BufferOverflow => {}
}
}
// Zeroize key
proper_key.zeroize();
final_result
}
Encryption::AES256CTR { iv } => {
let mut proper_key: [u8; 32] = [0; 32];
proper_key[..cmp::min(key.len(), 32)]
.clone_from_slice(&key[..cmp::min(key.len(), 32)]);
let mut encryptor = aes::ctr(aes::KeySize::KeySize256, &proper_key, &iv[..]);
let mut final_result = vec![0_u8; data.len()];
encryptor.process(&data, &mut final_result);
proper_key.zeroize();
final_result
}
}
}
#[cfg_attr(feature = "profile", flame)]
/// Decrypts a bytestring with the given key
///
/// Still requires a key in the event of no encryption, but it does not read this key,
/// so any value can be used. Will pad key with zeros if it is too short.
///
/// Will return None on encryption failure
pub fn decrypt(&self, data: &[u8], key: &[u8]) -> Option<Vec<u8>> {
match self {
Encryption::NoEncryption => Some(data.to_vec()),
Encryption::AES256CBC { iv } => {
// Creates a key of the correct length, and fills it with
// zeros to start with
let mut proper_key: [u8; 32] = [0; 32];
// Copy key into proper key
proper_key[..cmp::min(key.len(), 32)]
.clone_from_slice(&key[..cmp::min(key.len(), 32)]);
let mut decryptor = aes::cbc_decryptor(
aes::KeySize::KeySize256,
&proper_key,
iv,
blockmodes::PkcsPadding,
);
let mut final_result = Vec::<u8>::new();
let mut read_buffer = buffer::RefReadBuffer::new(data);
let mut buffer = [0; 4096];
let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);
loop {
let result = decryptor.decrypt(&mut read_buffer, &mut write_buffer, true);
match result {
Err(_) => {
return {
proper_key.zeroize();
None
}
}
Ok(result) => {
final_result.extend(
write_buffer
.take_read_buffer()
.take_remaining()
.iter()
.cloned(),
);
match result {
BufferResult::BufferUnderflow => break,
BufferResult::BufferOverflow => {}
}
}
}
}
// Zeroize key
proper_key.zeroize();
Some(final_result)
}
Encryption::AES256CTR { iv } => {
let mut proper_key: [u8; 32] = [0; 32];
proper_key[..cmp::min(key.len(), 32)]
.clone_from_slice(&key[..cmp::min(key.len(), 32)]);
let mut encryptor = aes::ctr(aes::KeySize::KeySize256, &proper_key, &iv[..]);
let mut final_result = vec![0_u8; data.len()];
encryptor.process(&data, &mut final_result);
proper_key.zeroize();
Some(final_result)
}
}
}
/// Conviencence function to get a new tag from an old one, specifying the
/// same algorithim, but with a new, securely generated IV
pub fn new_iv(self) -> Encryption {
match self {
Encryption::NoEncryption => Encryption::NoEncryption,
Encryption::AES256CBC { .. } => Encryption::new_aes256cbc(),
Encryption::AES256CTR { .. } => Encryption::new_aes256ctr(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str;
fn test_encryption(enc: Encryption) {
let mut key: [u8; 32] = [0; 32];
thread_rng().fill_bytes(&mut key);
let data_string =
"The quick brown fox jumps over the lazy dog. Jackdaws love my big sphinx of quartz.";
let encrypted_string = enc.encrypt(data_string.as_bytes(), &key);
let decrypted_bytes = enc.decrypt(&encrypted_string, &key).unwrap();
let decrypted_string = str::from_utf8(&decrypted_bytes).unwrap();;
println!("Input string: {}", data_string);
println!("Input bytes: \n{:X?}", data_string.as_bytes());
println!("Encrypted bytes: \n{:X?}", encrypted_string);
println!("Decrypted bytes: \n{:X?}", decrypted_bytes);
println!("Decrypted string: {}", decrypted_string);
assert_eq!(data_string, decrypted_string);
}
#[test]
fn test_aes256cbc() {
let enc = Encryption::new_aes256cbc();
test_encryption(enc);
}
#[test]
fn test_aes256ctr() {
let enc = Encryption::new_aes256ctr();
test_encryption(enc);
}
}