use super::{
medium_encryptor::MediumEncryptor, small_encryptor::SmallEncryptor, utils, SelfEncryptionError,
Storage, MAX_CHUNK_SIZE, MIN_CHUNK_SIZE,
};
use crate::data_map::{ChunkDetails, DataMap};
use std::{cmp, convert::From, mem};
use unwrap::unwrap;
pub const MIN: u64 = 3 * MAX_CHUNK_SIZE as u64 + 1;
const MAX_BUFFER_LEN: usize = (MAX_CHUNK_SIZE + MIN_CHUNK_SIZE) as usize;
pub struct LargeEncryptor<S: Storage + Send + Sync> {
storage: S,
chunks: Vec<ChunkDetails>,
original_chunks: Option<Vec<ChunkDetails>>,
chunk_0_data: Vec<u8>,
chunk_1_data: Vec<u8>,
buffer: Vec<u8>,
}
impl<S> LargeEncryptor<S>
where
S: Storage + 'static + Send + Sync,
{
#[allow(clippy::new_ret_no_self)]
pub async fn new(
storage: S,
chunks: Vec<ChunkDetails>,
) -> Result<LargeEncryptor<S>, SelfEncryptionError<S::Error>> {
debug_assert!(chunks.len() > 3);
debug_assert!(MIN <= chunks.iter().fold(0, |acc, chunk| acc + chunk.source_size));
let mut partial_details = chunks.clone();
let mut truncated_details_len = chunks.len() - 1;
let mut chunk_0_data;
let mut chunk_1_data;
let mut buffer;
let buffer_extension;
{
let mut start_iter = partial_details.iter_mut().enumerate();
let (index, chunk) = unwrap!(start_iter.next());
let pad_key_iv = utils::get_pad_key_and_iv(index, &chunks);
chunk_0_data = storage.get(&chunk.hash).await?;
chunk_0_data = utils::decrypt_chunk(&chunk_0_data, pad_key_iv)?;
chunk.hash.clear();
let (index, chunk) = unwrap!(start_iter.next());
let pad_key_iv = utils::get_pad_key_and_iv(index, &chunks);
chunk_1_data = storage.get(&chunk.hash).await?;
chunk_1_data = utils::decrypt_chunk(&chunk_1_data, pad_key_iv)?;
chunk.hash.clear();
let mut end_iter = start_iter.skip(chunks.len() - 4);
let (index, chunk) = unwrap!(end_iter.next());
buffer = if chunk.source_size < MAX_CHUNK_SIZE as u64 {
let pad_key_iv = utils::get_pad_key_and_iv(index, &chunks);
truncated_details_len -= 1;
let another_chunk_data = storage.get(&chunk.hash).await?;
utils::decrypt_chunk(&another_chunk_data, pad_key_iv)?
} else {
Vec::with_capacity(MAX_BUFFER_LEN)
};
let (index, chunk) = unwrap!(end_iter.next());
let pad_key_iv = utils::get_pad_key_and_iv(index, &chunks);
let data = storage.get(&chunk.hash).await?;
buffer_extension = utils::decrypt_chunk(&data, pad_key_iv)?
}
partial_details.truncate(truncated_details_len);
buffer.extend(buffer_extension);
Ok(LargeEncryptor {
storage,
chunks: partial_details,
original_chunks: Some(chunks),
chunk_0_data,
chunk_1_data,
buffer,
})
}
pub async fn from_medium(
medium_encryptor: MediumEncryptor<S>,
) -> Result<Self, SelfEncryptionError<S::Error>> {
let encryptor = LargeEncryptor {
storage: medium_encryptor.storage,
chunks: vec![],
original_chunks: None,
chunk_0_data: vec![],
chunk_1_data: vec![],
buffer: vec![],
};
encryptor.write(&medium_encryptor.buffer).await
}
pub async fn write(mut self, mut data: &[u8]) -> Result<Self, SelfEncryptionError<S::Error>> {
self.original_chunks = None;
data = self.fill_chunk_buffer(data, 0).await;
data = self.fill_chunk_buffer(data, 1).await;
let mut all_things = Vec::new();
while !data.is_empty() {
let amount = cmp::min(MAX_BUFFER_LEN - self.buffer.len(), data.len());
self.buffer.extend_from_slice(&data[..amount]);
data = &data[amount..];
if self.buffer.len() == MAX_BUFFER_LEN {
let mut data_to_encrypt = self.buffer.split_off(MAX_CHUNK_SIZE as usize);
mem::swap(&mut self.buffer, &mut data_to_encrypt);
let index = self.chunks.len();
all_things.push(self.encrypt_chunk(&data_to_encrypt, index).await?);
}
}
Ok(self)
}
pub async fn close(mut self) -> Result<(DataMap, S), SelfEncryptionError<S::Error>> {
if let Some(chunks) = self.original_chunks {
return Ok((DataMap::Chunks(chunks), self.storage));
}
debug_assert!(self.buffer.len() >= MIN_CHUNK_SIZE as usize);
debug_assert!(self.buffer.len() <= MAX_BUFFER_LEN);
let (first_len, need_two_chunks) = if self.buffer.len() <= MAX_CHUNK_SIZE as usize {
(self.buffer.len(), false)
} else {
((MAX_CHUNK_SIZE - MIN_CHUNK_SIZE) as usize, true)
};
let mut index = self.chunks.len();
let mut swapped_buffer = vec![];
let mut all_chunks = Vec::with_capacity(4);
mem::swap(&mut swapped_buffer, &mut self.buffer);
all_chunks.push(
self.encrypt_chunk(&swapped_buffer[..first_len], index)
.await?,
);
if need_two_chunks {
index += 1;
all_chunks.push(
self.encrypt_chunk(&swapped_buffer[first_len..], index)
.await?,
);
}
mem::swap(&mut swapped_buffer, &mut self.chunk_0_data);
all_chunks.push(self.encrypt_chunk(&swapped_buffer, 0).await?);
mem::swap(&mut swapped_buffer, &mut self.chunk_1_data);
all_chunks.push(self.encrypt_chunk(&swapped_buffer, 1).await?);
let mut swapped_chunks = vec![];
mem::swap(&mut swapped_chunks, &mut self.chunks);
Ok((DataMap::Chunks(swapped_chunks), self.storage))
}
pub fn len(&self) -> u64 {
self.chunk_0_data.len() as u64
+ self.chunk_1_data.len() as u64
+ self.buffer.len() as u64
+ ((self.chunks.len().saturating_sub(2)) * MAX_CHUNK_SIZE as usize) as u64
}
pub fn is_empty(&self) -> bool {
self.chunk_0_data.is_empty()
}
#[allow(clippy::needless_lifetimes)]
async fn fill_chunk_buffer<'b>(&mut self, mut data: &'b [u8], index: u32) -> &'b [u8] {
let buffer_ref = if index == 0 {
&mut self.chunk_0_data
} else {
&mut self.chunk_1_data
};
let amount = cmp::min(MAX_CHUNK_SIZE as usize - buffer_ref.len(), data.len());
if amount > 0 {
buffer_ref.extend_from_slice(&data[..amount]);
data = &data[amount..];
if buffer_ref.len() == MAX_CHUNK_SIZE as usize {
self.chunks.push(ChunkDetails {
chunk_num: index,
hash: vec![],
pre_hash: self.storage.generate_address(buffer_ref).await,
source_size: MAX_CHUNK_SIZE as u64,
});
}
}
data
}
async fn encrypt_chunk(
&mut self,
data: &[u8],
index: usize,
) -> Result<(), SelfEncryptionError<S::Error>> {
if index > 1 {
self.chunks.push(ChunkDetails {
chunk_num: index as u32,
hash: vec![],
pre_hash: self.storage.generate_address(data).await,
source_size: data.len() as u64,
});
}
let pad_key_iv = utils::get_pad_key_and_iv(index, &self.chunks);
let encrypted_contents = utils::encrypt_chunk(data, pad_key_iv)?;
let hash = self.storage.generate_address(&encrypted_contents).await;
self.chunks[index].hash = hash.to_vec();
self.storage
.put(hash.to_vec(), encrypted_contents.to_vec())
.await?;
Ok(())
}
}
impl<S: Storage + Send + Sync> From<SmallEncryptor<S>> for LargeEncryptor<S> {
fn from(small_encryptor: SmallEncryptor<S>) -> LargeEncryptor<S> {
LargeEncryptor {
storage: small_encryptor.storage,
chunks: vec![],
original_chunks: None,
chunk_0_data: small_encryptor.buffer,
chunk_1_data: vec![],
buffer: vec![],
}
}
}
#[cfg(test)]
mod tests {
use super::{
super::{
medium_encryptor::{self, MediumEncryptor},
small_encryptor::SmallEncryptor,
utils, MAX_CHUNK_SIZE,
},
*,
};
use crate::{
data_map::DataMap,
self_encryptor::SelfEncryptor,
test_helpers::{new_test_rng, random_bytes, Blob, SimpleStorage},
};
use rand::Rng;
#[test]
fn consts() {
assert_eq!(MIN, medium_encryptor::MAX + 1);
}
async fn basic_write_and_close(data: &[u8]) {
let (data_map, storage) = {
let storage = SimpleStorage::new();
let mut encryptor = unwrap!(SmallEncryptor::new(storage, vec![])
.await
.map(LargeEncryptor::from));
assert_eq!(encryptor.len(), 0);
assert!(encryptor.is_empty());
encryptor = unwrap!(encryptor.write(data).await);
assert_eq!(encryptor.len(), data.len() as u64);
assert!(!encryptor.is_empty());
unwrap!(encryptor.close().await)
};
match data_map {
DataMap::Chunks(ref chunks) => assert!(chunks.len() > 3),
_ => panic!("Wrong DataMap type returned."),
}
let self_encryptor = unwrap!(SelfEncryptor::new(storage, data_map));
let fetched = unwrap!(self_encryptor.read(0, data.len() as u64).await);
assert_eq!(Blob(&fetched), Blob(data));
}
async fn multiple_writes_then_close<T: Rng>(rng: &mut T, data: &[u8]) {
let mut storage = SimpleStorage::new();
let mut existing_data = vec![];
let data_pieces = utils::make_random_pieces(rng, data, MIN as usize);
let mut current_chunks = vec![];
for data in data_pieces {
let data_map = {
let mut encryptor = if current_chunks.is_empty() {
unwrap!(SmallEncryptor::new(storage, vec![])
.await
.map(LargeEncryptor::from))
} else {
unwrap!(LargeEncryptor::new(storage, current_chunks).await)
};
encryptor = unwrap!(encryptor.write(data).await);
existing_data.extend_from_slice(data);
assert_eq!(encryptor.len(), existing_data.len() as u64);
let (data_map, storage2) = unwrap!(encryptor.close().await);
storage = storage2;
data_map
};
match data_map {
DataMap::Chunks(ref chunks) => {
assert!(chunks.len() > 3);
current_chunks = chunks.clone()
}
_ => panic!("Wrong DataMap type returned."),
}
let self_encryptor = unwrap!(SelfEncryptor::new(storage, data_map));
assert_eq!(self_encryptor.len().await, existing_data.len() as u64);
let fetched = unwrap!(self_encryptor.read(0, existing_data.len() as u64).await);
assert_eq!(Blob(&fetched), Blob(&existing_data));
storage = self_encryptor.into_storage().await;
}
assert_eq!(Blob(&existing_data[..]), Blob(data));
}
#[tokio::test]
async fn all_unit() {
let mut rng = new_test_rng();
let data = random_bytes(&mut rng, 5 * MAX_CHUNK_SIZE as usize);
basic_write_and_close(&data[..MIN as usize]).await;
basic_write_and_close(&data[..(MAX_CHUNK_SIZE as usize * 4)]).await;
basic_write_and_close(&data[..=(MAX_CHUNK_SIZE as usize * 4)]).await;
basic_write_and_close(&data).await;
multiple_writes_then_close(&mut rng, &data[..(MIN as usize + 100)]).await;
multiple_writes_then_close(&mut rng, &data).await;
let (data_map, storage) = {
let storage = SimpleStorage::new();
let mut medium_encryptor = unwrap!(SmallEncryptor::new(storage, vec![])
.await
.map(MediumEncryptor::from));
medium_encryptor = unwrap!(medium_encryptor.write(&data[..(MIN as usize - 1)]).await);
let mut large_encryptor = unwrap!(LargeEncryptor::from_medium(medium_encryptor).await);
assert_eq!(large_encryptor.len(), MIN - 1);
assert!(!large_encryptor.is_empty());
large_encryptor = unwrap!(large_encryptor.write(&data[(MIN as usize - 1)..]).await);
assert_eq!(large_encryptor.len(), data.len() as u64);
assert!(!large_encryptor.is_empty());
unwrap!(large_encryptor.close().await)
};
match data_map {
DataMap::Chunks(ref chunks) => assert_eq!(chunks.len(), 5),
_ => panic!("Wrong DataMap type returned."),
}
let self_encryptor = unwrap!(SelfEncryptor::new(storage, data_map));
let fetched = unwrap!(self_encryptor.read(0, data.len() as u64).await);
assert_eq!(Blob(&fetched), Blob(&data));
}
}