use super::{MAX_CHUNK_SIZE, MIN_CHUNK_SIZE, SelfEncryptionError, Storage, utils};
use super::small_encryptor::SmallEncryptor;
use data_map::{ChunkDetails, DataMap};
use futures::{future, Future};
use std::convert::From;
use tiny_keccak::sha3_256;
use util::{BoxFuture, FutureExt};
pub const MIN: u64 = 3 * MIN_CHUNK_SIZE as u64;
pub const MAX: u64 = 3 * MAX_CHUNK_SIZE as u64;
pub struct MediumEncryptor<S> {
pub storage: S,
pub buffer: Vec<u8>,
original_chunks: Option<Vec<ChunkDetails>>,
}
impl<S> MediumEncryptor<S>
where
S: Storage + 'static,
{
pub fn new(
storage: S,
chunks: Vec<ChunkDetails>,
) -> BoxFuture<MediumEncryptor<S>, SelfEncryptionError<S::Error>> {
debug_assert_eq!(chunks.len(), 3);
debug_assert!(MIN <= chunks.iter().fold(0, |acc, chunk| acc + chunk.source_size));
debug_assert!(chunks.iter().fold(0, |acc, chunk| acc + chunk.source_size) <= MAX);
let mut futures = Vec::with_capacity(chunks.len());
for (index, chunk) in chunks.iter().enumerate() {
let pad_key_iv = utils::get_pad_key_and_iv(index, &chunks);
futures.push(storage.get(&chunk.hash).map_err(From::from).and_then(
move |data| {
utils::decrypt_chunk(&data, pad_key_iv)
},
));
}
future::join_all(futures)
.map(move |data| {
let init = Vec::with_capacity(MAX as usize);
let buffer = data.into_iter().fold(init, |mut buffer, data| {
buffer.extend(data);
buffer
});
MediumEncryptor {
storage: storage,
buffer: buffer,
original_chunks: Some(chunks),
}
})
.into_box()
}
pub fn write(mut self, data: &[u8]) -> BoxFuture<Self, SelfEncryptionError<S::Error>> {
debug_assert!(data.len() as u64 + self.len() <= MAX);
self.original_chunks = None;
self.buffer.extend_from_slice(data);
future::ok(self).into_box()
}
pub fn close(mut self) -> BoxFuture<(DataMap, S), SelfEncryptionError<S::Error>> {
if let Some(chunks) = self.original_chunks {
return future::ok((DataMap::Chunks(chunks), self.storage)).into_box();
}
let mut futures;
let mut chunk_details;
{
let chunk_contents = vec![
&self.buffer[..(self.buffer.len() / 3)],
&self.buffer[(self.buffer.len() / 3)..
(2 * (self.buffer.len() / 3))],
&self.buffer[(2 * (self.buffer.len() / 3))..],
];
chunk_details = vec![];
for (index, contents) in chunk_contents.iter().enumerate() {
chunk_details.push(ChunkDetails {
chunk_num: index as u32,
hash: vec![],
pre_hash: sha3_256(contents).to_vec(),
source_size: contents.len() as u64,
});
}
let partial_details = chunk_details.clone();
futures = Vec::with_capacity(chunk_contents.len());
for (index, (contents, mut details)) in
chunk_contents
.iter()
.zip(chunk_details.iter_mut())
.enumerate()
{
let pad_key_iv = utils::get_pad_key_and_iv(index, &partial_details);
let future = match utils::encrypt_chunk(contents, pad_key_iv) {
Ok(encrypted_contents) => {
let hash = sha3_256(&encrypted_contents);
details.hash = hash.to_vec();
self.storage
.put(hash.to_vec(), encrypted_contents)
.map_err(From::from)
.into_box()
}
Err(error) => future::err(error).into_box(),
};
futures.push(future);
}
}
future::join_all(futures)
.map(move |_| (DataMap::Chunks(chunk_details), self.storage))
.into_box()
}
pub fn len(&self) -> u64 {
self.buffer.len() as u64
}
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
}
impl<S: Storage> From<SmallEncryptor<S>> for MediumEncryptor<S> {
fn from(small_encryptor: SmallEncryptor<S>) -> MediumEncryptor<S> {
MediumEncryptor {
storage: small_encryptor.storage,
buffer: small_encryptor.buffer,
original_chunks: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::{MAX_CHUNK_SIZE, utils};
use super::super::small_encryptor::{self, SmallEncryptor};
use data_map::DataMap;
use futures::Future;
use itertools::Itertools;
use maidsafe_utilities::SeededRng;
use rand::Rng;
use self_encryptor::SelfEncryptor;
use test_helpers::{Blob, SimpleStorage};
#[test]
fn consts() {
assert_eq!(MIN, small_encryptor::MAX + 1);
}
fn basic_write_and_close(data: &[u8]) {
let (data_map, storage) = {
let storage = SimpleStorage::new();
let mut encryptor = unwrap!(
SmallEncryptor::new(storage, vec![])
.map(MediumEncryptor::from)
.wait()
);
assert_eq!(encryptor.len(), 0);
assert!(encryptor.is_empty());
encryptor = unwrap!(encryptor.write(data).wait());
assert_eq!(encryptor.len(), data.len() as u64);
assert!(!encryptor.is_empty());
unwrap!(encryptor.close().wait())
};
match data_map {
DataMap::Chunks(ref chunks) => assert_eq!(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).wait());
assert_eq!(Blob(&fetched), Blob(data));
}
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![])
.map(MediumEncryptor::from)
.wait()
)
} else {
unwrap!(MediumEncryptor::new(storage, current_chunks).wait())
};
encryptor = unwrap!(encryptor.write(data).wait());
existing_data.extend_from_slice(data);
assert_eq!(encryptor.len(), existing_data.len() as u64);
let (data_map, storage2) = unwrap!(encryptor.close().wait());
storage = storage2;
data_map
};
match data_map {
DataMap::Chunks(ref chunks) => {
assert_eq!(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(), existing_data.len() as u64);
let fetched = unwrap!(self_encryptor.read(0, existing_data.len() as u64).wait());
assert_eq!(fetched, existing_data);
storage = self_encryptor.into_storage();
}
assert_eq!(Blob(&existing_data[..]), Blob(data));
}
#[test]
fn all_unit() {
let mut rng = SeededRng::new();
let data = rng.gen_iter().take(MAX as usize).collect_vec();
basic_write_and_close(&data[..MIN as usize]);
basic_write_and_close(&data[..MAX_CHUNK_SIZE as usize]);
basic_write_and_close(&data[..(MAX_CHUNK_SIZE as usize * 2)]);
basic_write_and_close(&data);
multiple_writes_then_close(&mut rng, &data[..(MIN as usize * 2)]);
multiple_writes_then_close(&mut rng, &data[..MAX_CHUNK_SIZE as usize]);
multiple_writes_then_close(&mut rng, &data[..(MAX_CHUNK_SIZE as usize * 2)]);
multiple_writes_then_close(&mut rng, &data);
}
}