use client::Client;
use event_loop::CoreFuture;
use futures::Future;
use maidsafe_utilities::serialisation::{deserialise, serialise};
use routing::{ImmutableData, XorName};
use rust_sodium::crypto::secretbox;
use self_encryption::{DataMap, SelfEncryptor};
use self_encryption_storage::SelfEncryptionStorage;
use utils::{self, FutureExt};
#[derive(Serialize, Deserialize)]
enum DataTypeEncoding {
Serialised(Vec<u8>),
DataMap(DataMap),
}
pub fn create<T: 'static>(
client: &Client<T>,
value: &[u8],
encryption_key: Option<secretbox::Key>,
) -> Box<CoreFuture<ImmutableData>> {
trace!("Creating conformant ImmutableData.");
let client = client.clone();
let storage = SelfEncryptionStorage::new(client.clone());
let self_encryptor = fry!(SelfEncryptor::new(storage, DataMap::None));
self_encryptor
.write(value, 0)
.and_then(move |_| self_encryptor.close())
.map_err(From::from)
.and_then(move |(data_map, _)| {
let serialised_data_map = fry!(serialise(&data_map));
let value = if let Some(key) = encryption_key {
let cipher_text = fry!(utils::symmetric_encrypt(&serialised_data_map, &key, None));
fry!(serialise(&DataTypeEncoding::Serialised(cipher_text)))
} else {
fry!(serialise(
&DataTypeEncoding::Serialised(serialised_data_map),
))
};
pack(client, value)
})
.into_box()
}
pub fn extract_value<T: 'static>(
client: &Client<T>,
data: &ImmutableData,
decryption_key: Option<secretbox::Key>,
) -> Box<CoreFuture<Vec<u8>>> {
let client = client.clone();
unpack(client.clone(), data)
.and_then(move |value| {
let data_map = if let Some(key) = decryption_key {
let plain_text = utils::symmetric_decrypt(&value, &key)?;
deserialise(&plain_text)?
} else {
deserialise(&value)?
};
let storage = SelfEncryptionStorage::new(client);
Ok(SelfEncryptor::new(storage, data_map)?)
})
.and_then(|self_encryptor| {
let length = self_encryptor.len();
self_encryptor.read(0, length).map_err(From::from)
})
.into_box()
}
pub fn get_value<T: 'static>(
client: &Client<T>,
name: &XorName,
decryption_key: Option<secretbox::Key>,
) -> Box<CoreFuture<Vec<u8>>> {
let client2 = client.clone();
client
.get_idata(*name)
.and_then(move |data| extract_value(&client2, &data, decryption_key))
.into_box()
}
fn pack<T: 'static>(client: Client<T>, value: Vec<u8>) -> Box<CoreFuture<ImmutableData>> {
let data = ImmutableData::new(value);
let serialised_data = fry!(serialise(&data));
if !data.validate_size() {
let storage = SelfEncryptionStorage::new(client.clone());
let self_encryptor = fry!(SelfEncryptor::new(storage, DataMap::None));
self_encryptor
.write(&serialised_data, 0)
.and_then(move |_| self_encryptor.close())
.map_err(From::from)
.and_then(move |(data_map, _)| {
let value = fry!(serialise(&DataTypeEncoding::DataMap(data_map)));
pack(client, value)
})
.into_box()
} else {
ok!(data)
}
}
fn unpack<T: 'static>(client: Client<T>, data: &ImmutableData) -> Box<CoreFuture<Vec<u8>>> {
match fry!(deserialise(data.value())) {
DataTypeEncoding::Serialised(value) => ok!(value),
DataTypeEncoding::DataMap(data_map) => {
let storage = SelfEncryptionStorage::new(client.clone());
let self_encryptor = fry!(SelfEncryptor::new(storage, data_map));
let length = self_encryptor.len();
self_encryptor
.read(0, length)
.map_err(From::from)
.and_then(move |serialised_data| {
let data = fry!(deserialise(&serialised_data));
unpack(client, &data)
})
.into_box()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::Future;
use rust_sodium::crypto::secretbox;
use utils;
use utils::test_utils::{finish, random_client};
#[test]
fn create_and_retrieve_1kb() {
create_and_retrieve(1024)
}
#[test]
fn create_and_retrieve_1mb() {
create_and_retrieve(1024 * 1024)
}
#[test]
fn create_and_retrieve_2mb() {
create_and_retrieve(2 * 1024 * 1024)
}
#[cfg(not(debug_assertions))]
#[test]
fn create_and_retrieve_10mb() {
create_and_retrieve(10 * 1024 * 1024)
}
fn create_and_retrieve(size: usize) {
let value = unwrap!(utils::generate_random_vector(size));
{
let value_before = value.clone();
random_client(move |client| {
let client2 = client.clone();
let client3 = client.clone();
create(client, &value_before.clone(), None)
.then(move |res| {
let data_before = unwrap!(res);
let data_name = *data_before.name();
client2.put_idata(data_before).map(move |_| data_name)
})
.then(move |res| {
let data_name = unwrap!(res);
get_value(&client3, &data_name, None)
})
.then(move |res| {
let value_after = unwrap!(res);
assert_eq!(value_after, value_before);
finish()
})
})
}
{
let value_before = value.clone();
let key = secretbox::gen_key();
random_client(move |client| {
let client2 = client.clone();
let client3 = client.clone();
create(client, &value_before.clone(), Some(key.clone()))
.then(move |res| {
let data_before = unwrap!(res);
let data_name = *data_before.name();
client2.put_idata(data_before).map(move |_| data_name)
})
.then(move |res| {
let data_name = unwrap!(res);
get_value(&client3, &data_name, Some(key))
})
.then(move |res| {
let value_after = unwrap!(res);
assert_eq!(value_after, value_before);
finish()
})
})
}
{
let value = value.clone();
let key = secretbox::gen_key();
random_client(move |client| {
let client2 = client.clone();
let client3 = client.clone();
create(client, &value, None)
.then(move |res| {
let data = unwrap!(res);
let data_name = *data.name();
client2.put_idata(data).map(move |_| data_name)
})
.then(move |res| {
let data_name = unwrap!(res);
get_value(&client3, &data_name, Some(key))
})
.then(|res| {
assert!(res.is_err());
finish()
})
})
}
{
let value = value.clone();
let key = secretbox::gen_key();
random_client(move |client| {
let client2 = client.clone();
let client3 = client.clone();
create(client, &value, Some(key))
.then(move |res| {
let data = unwrap!(res);
let data_name = *data.name();
client2.put_idata(data).map(move |_| data_name)
})
.then(move |res| {
let data_name = unwrap!(res);
get_value(&client3, &data_name, None)
})
.then(|res| {
assert!(res.is_err());
finish()
})
})
}
}
}