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 { 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> { 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::::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); } }