use crate::client::Client;
use crate::crypto::shared_secretbox;
use crate::self_encryption_storage::{
SEStorageError, SelfEncryptionStorage, SelfEncryptionStorageDryRun,
};
use crate::utils;
use crate::CoreError;
use bincode::{deserialize, serialize};
use log::trace;
use safe_nd::{IData, IDataAddress, PubImmutableData, UnpubImmutableData};
use self_encryption::{DataMap, SelfEncryptor, Storage};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
enum DataTypeEncoding {
Serialised(Vec<u8>),
DataMap(DataMap),
}
pub async fn create(
client: &(impl Client + 'static),
value: &[u8],
published: bool,
encryption_key: Option<shared_secretbox::Key>,
) -> Result<IData, CoreError> {
trace!("Creating conformant ImmutableData.");
let se_storage = SelfEncryptionStorage::new(client.clone(), published);
write_with_self_encryptor(se_storage, client, value, published, encryption_key).await
}
pub async fn gen_data_map(
client: &(impl Client + 'static),
value: &[u8],
published: bool,
encryption_key: Option<shared_secretbox::Key>,
) -> Result<IData, CoreError> {
trace!("Creating conformant ImmutableData data map.");
let se_storage = SelfEncryptionStorageDryRun::new(client.clone(), published);
write_with_self_encryptor(se_storage, client, value, published, encryption_key).await
}
pub async fn extract_value(
client: &(impl Client + 'static),
data: IData,
position: Option<u64>,
len: Option<u64>,
decryption_key: Option<shared_secretbox::Key>,
) -> Result<Vec<u8>, CoreError> {
let published = data.is_pub();
let se_storage = SelfEncryptionStorage::new(client.clone(), published);
let value = unpack(se_storage.clone(), data).await?;
let data_map = if let Some(key) = decryption_key {
let plain_text = utils::symmetric_decrypt(&value, &key)?;
deserialize(&plain_text)?
} else {
deserialize(&value)?
};
let self_encryptor = SelfEncryptor::new(se_storage, data_map)?;
let length = match len {
None => self_encryptor.len().await,
Some(request_length) => request_length,
};
let read_position = match position {
None => 0,
Some(pos) => pos,
};
match self_encryptor.read(read_position, length).await {
Ok(data) => Ok(data),
Err(error) => Err(CoreError::from(error)),
}
}
pub async fn get_value(
client: &(impl Client + 'static),
address: IDataAddress,
position: Option<u64>,
len: Option<u64>,
decryption_key: Option<shared_secretbox::Key>,
) -> Result<Vec<u8>, CoreError> {
let client2 = client.clone();
let data = client.get_idata(address).await?;
extract_value(&client2, data, position, len, decryption_key).await
}
async fn write_with_self_encryptor<S>(
se_storage: S,
client: &(impl Client + 'static),
value: &[u8],
published: bool,
encryption_key: Option<shared_secretbox::Key>,
) -> Result<IData, CoreError>
where
S: Storage<Error = SEStorageError> + Clone + Send + Sync + 'static,
{
let self_encryptor = SelfEncryptor::new(se_storage.clone(), DataMap::None)?;
self_encryptor.write(value, 0).await?;
let (data_map, _) = self_encryptor.close().await?;
let serialised_data_map = serialize(&data_map)?;
let value = if let Some(key) = encryption_key {
let cipher_text = utils::symmetric_encrypt(&serialised_data_map, &key, None)?;
serialize(&DataTypeEncoding::Serialised(cipher_text))?
} else {
serialize(&DataTypeEncoding::Serialised(serialised_data_map))?
};
pack(se_storage, client, value, published).await
}
async fn pack<S>(
se_storage: S,
client: &(impl Client + 'static),
mut value: Vec<u8>,
published: bool,
) -> Result<IData, CoreError>
where
S: Storage<Error = SEStorageError> + Clone + 'static + Sync + Send,
{
loop {
let data: IData = if published {
PubImmutableData::new(value).into()
} else {
UnpubImmutableData::new(value, client.public_key().await).into()
};
let serialised_data = serialize(&data)?;
if data.validate_size() {
return Ok(data);
}
let self_encryptor = SelfEncryptor::new(se_storage.clone(), DataMap::None)?;
self_encryptor.write(&serialised_data, 0).await?;
let (data_map, _) = self_encryptor.close().await?;
value = serialize(&DataTypeEncoding::DataMap(data_map))?;
}
}
async fn unpack<S>(se_storage: S, mut data: IData) -> Result<Vec<u8>, CoreError>
where
S: Storage<Error = SEStorageError> + Clone + 'static + Send + Sync,
{
loop {
match deserialize(data.value())? {
DataTypeEncoding::Serialised(value) => return Ok(value),
DataTypeEncoding::DataMap(data_map) => {
let self_encryptor = SelfEncryptor::new(se_storage.clone(), data_map)?;
let length = self_encryptor.len().await;
let serialised_data = self_encryptor.read(0, length).await?;
data = deserialize(&serialised_data)?;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::CoreError;
use safe_nd::Error as SndError;
use utils::{self, test_utils::random_client};
#[tokio::test]
async fn create_and_retrieve_1kb() -> Result<(), CoreError> {
create_and_retrieve(1024).await
}
#[tokio::test]
async fn create_and_retrieve_1mb() -> Result<(), CoreError> {
create_and_retrieve(1024 * 1024).await
}
#[tokio::test]
async fn create_and_retrieve_index_based() -> Result<(), CoreError> {
create_and_index_based_retrieve(1024).await
}
#[tokio::test]
async fn create_and_retrieve_2mb() -> Result<(), CoreError> {
create_and_retrieve(2 * 1024 * 1024).await
}
#[cfg(not(debug_assertions))]
#[tokio::test]
async fn create_and_retrieve_10mb() -> Result<(), CoreError> {
create_and_retrieve(10 * 1024 * 1024).await
}
async fn create_and_index_based_retrieve(size: usize) -> Result<(), CoreError> {
let value = utils::generate_random_vector(size)?;
{
let client = random_client()?;
let client2 = client.clone();
let client3 = client.clone();
let data = create(&client, &value, true, None).await?;
let address = *data.address();
client2.put_idata(data).await?;
let fetched_value =
get_value(&client3, address, None, Some(size as u64 / 2), None).await?;
assert_eq!(fetched_value, value[0..size / 2].to_vec());
}
let value2 = utils::generate_random_vector(size)?;
{
let client = random_client()?;
let client2 = client.clone();
let client3 = client.clone();
let data = create(&client, &value2, true, None).await?;
let address = *data.address();
client2.put_idata(data).await?;
let fetched_value = get_value(
&client3,
address,
Some(size as u64 / 2),
Some(size as u64 / 2),
None,
)
.await?;
assert_eq!(fetched_value, value2[size / 2..size].to_vec());
}
Ok(())
}
async fn create_and_retrieve(size: usize) -> Result<(), CoreError> {
gen_data_then_map_create_and_retrieve(size, true, None).await?;
gen_data_then_map_create_and_retrieve(size, false, None).await?;
{
let key = shared_secretbox::gen_key();
gen_data_then_map_create_and_retrieve(size, true, Some(key)).await?;
}
{
let key = shared_secretbox::gen_key();
gen_data_then_map_create_and_retrieve(size, false, Some(key)).await?;
}
let value = utils::generate_random_vector(size)?;
{
let value = value.clone();
let key = shared_secretbox::gen_key();
let client = random_client()?;
let client2 = client.clone();
let client3 = client.clone();
let data = create(&client, &value, true, None).await?;
let address = *data.address();
client2.put_idata(data).await?;
let res = get_value(&client3, address, None, None, Some(key)).await;
assert!(res.is_err());
}
{
let value = value.clone();
let key = shared_secretbox::gen_key();
let client = random_client()?;
let client2 = client.clone();
let client3 = client.clone();
let data = create(&client, &value, true, Some(key)).await?;
let address = *data.address();
client2.put_idata(data).await?;
let res = get_value(&client3, address, None, None, None).await;
assert!(res.is_err());
}
{
let value = value.clone();
let client = random_client()?;
let client2 = client.clone();
let client3 = client.clone();
let data = create(&client, &value, true, None).await?;
let data_name = *data.name();
client2.put_idata(data).await?;
let address = IDataAddress::Unpub(data_name);
let res = get_value(&client3, address, None, None, None).await;
assert!(res.is_err());
}
{
let client = random_client()?;
let client2 = client.clone();
let client3 = client.clone();
let data = create(&client, &value, false, None).await?;
let data_name = *data.name();
client2.put_idata(data).await?;
let address = IDataAddress::Pub(data_name);
let res = get_value(&client3, address, None, None, None).await;
assert!(res.is_err());
}
Ok(())
}
#[allow(clippy::match_wild_err_arm)]
async fn gen_data_then_map_create_and_retrieve(
size: usize,
published: bool,
key: Option<shared_secretbox::Key>,
) -> Result<(), CoreError> {
let value = utils::generate_random_vector(size)?;
let value_before = value.clone();
let value_before2 = value.clone();
let client = random_client()?;
let client2 = client.clone();
let client3 = client.clone();
let client4 = client.clone();
let client5 = client.clone();
let key2 = key.clone();
let key3 = key.clone();
let data = gen_data_map(&client, &value.clone(), published, key2.clone()).await?;
let address_before = *data.address();
let res = get_value(&client2, address_before, None, None, key2.clone()).await;
let data_map_before = match res {
Err(CoreError::DataError(SndError::NoSuchData)) => {
create(&client3, &value_before2.clone(), published, key3).await?
}
Ok(_) => panic!(
"ImmutableData unexpectedly retrieved using address generated by gen_data_map"
),
Err(_) => panic!(
"Unexpected error when ImmutableData retrieved using address generated by gen_data_map"
),
};
let address_after = *data_map_before.address();
if key2.is_none() {
assert_eq!(address_after, address_before);
} else {
assert_ne!(address_after, address_before);
}
client4.put_idata(data_map_before).await?;
let address = address_after;
let value_after = get_value(&client5, address, None, None, key).await?;
assert_eq!(value_after, value_before);
Ok(())
}
}