use crypto::shared_secretbox;
use errors::CoreError;
use ffi::MDataInfo as FfiMDataInfo;
use ffi::arrays::{SymNonce, SymSecretKey};
use ffi_utils::ReprC;
use ipc::IpcError;
use rand::{OsRng, Rng};
use routing::{EntryAction, Value, XorName};
use rust_sodium::crypto::secretbox;
use std::collections::{BTreeMap, BTreeSet};
use tiny_keccak::sha3_256;
use utils::{symmetric_decrypt, symmetric_encrypt};
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct MDataInfo {
pub name: XorName,
pub type_tag: u64,
pub enc_info: Option<(shared_secretbox::Key, secretbox::Nonce)>,
pub new_enc_info: Option<(shared_secretbox::Key, secretbox::Nonce)>,
}
impl MDataInfo {
pub fn new_private(
name: XorName,
type_tag: u64,
enc_info: (shared_secretbox::Key, secretbox::Nonce),
) -> Self {
MDataInfo {
name,
type_tag,
enc_info: Some(enc_info),
new_enc_info: None,
}
}
pub fn new_public(name: XorName, type_tag: u64) -> Self {
MDataInfo {
name,
type_tag,
enc_info: None,
new_enc_info: None,
}
}
pub fn random_private(type_tag: u64) -> Result<Self, CoreError> {
let mut rng = os_rng()?;
let enc_info = (shared_secretbox::gen_key(), secretbox::gen_nonce());
Ok(Self::new_private(rng.gen(), type_tag, enc_info))
}
pub fn random_public(type_tag: u64) -> Result<Self, CoreError> {
let mut rng = os_rng()?;
Ok(Self::new_public(rng.gen(), type_tag))
}
pub fn enc_key(&self) -> Option<&shared_secretbox::Key> {
self.enc_info.as_ref().map(|&(ref key, _)| key)
}
pub fn nonce(&self) -> Option<&secretbox::Nonce> {
self.enc_info.as_ref().map(|&(_, ref nonce)| nonce)
}
pub fn enc_entry_key(&self, plain_text: &[u8]) -> Result<Vec<u8>, CoreError> {
if let Some((ref key, seed)) = self.new_enc_info {
enc_entry_key(plain_text, key, seed)
} else if let Some((ref key, seed)) = self.enc_info {
enc_entry_key(plain_text, key, seed)
} else {
Ok(plain_text.to_vec())
}
}
pub fn enc_entry_value(&self, plain_text: &[u8]) -> Result<Vec<u8>, CoreError> {
if let Some((ref key, _)) = self.new_enc_info {
symmetric_encrypt(plain_text, key, None)
} else if let Some((ref key, _)) = self.enc_info {
symmetric_encrypt(plain_text, key, None)
} else {
Ok(plain_text.to_vec())
}
}
pub fn decrypt(&self, cipher: &[u8]) -> Result<Vec<u8>, CoreError> {
if let Some((ref key, _)) = self.new_enc_info {
if let Ok(plain) = symmetric_decrypt(cipher, key) {
return Ok(plain);
}
}
if let Some((ref key, _)) = self.enc_info {
symmetric_decrypt(cipher, key)
} else {
Ok(cipher.to_vec())
}
}
pub fn start_new_enc_info(&mut self) {
if self.enc_info.is_some() && self.new_enc_info.is_none() {
self.new_enc_info = Some((shared_secretbox::gen_key(), secretbox::gen_nonce()));
}
}
pub fn commit_new_enc_info(&mut self) {
if let Some(new_enc_info) = self.new_enc_info.take() {
self.enc_info = Some(new_enc_info);
}
}
pub fn into_repr_c(self) -> FfiMDataInfo {
let (has_enc_info, enc_key, enc_nonce) = enc_info_into_repr_c(self.enc_info);
let (has_new_enc_info, new_enc_key, new_enc_nonce) =
enc_info_into_repr_c(self.new_enc_info);
FfiMDataInfo {
name: self.name.0,
type_tag: self.type_tag,
has_enc_info,
enc_key,
enc_nonce,
has_new_enc_info,
new_enc_key,
new_enc_nonce,
}
}
}
fn os_rng() -> Result<OsRng, CoreError> {
OsRng::new().map_err(|_| CoreError::RandomDataGenerationFailure)
}
pub fn encrypt_entries(
info: &MDataInfo,
entries: &BTreeMap<Vec<u8>, Value>,
) -> Result<BTreeMap<Vec<u8>, Value>, CoreError> {
let mut output = BTreeMap::new();
for (key, value) in entries {
let encrypted_key = info.enc_entry_key(key)?;
let encrypted_value = encrypt_value(info, value)?;
let _ = output.insert(encrypted_key, encrypted_value);
}
Ok(output)
}
pub fn encrypt_entry_actions(
info: &MDataInfo,
actions: &BTreeMap<Vec<u8>, EntryAction>,
) -> Result<BTreeMap<Vec<u8>, EntryAction>, CoreError> {
let mut output = BTreeMap::new();
for (key, action) in actions {
let encrypted_key = info.enc_entry_key(key)?;
let encrypted_action = match *action {
EntryAction::Ins(ref value) => EntryAction::Ins(encrypt_value(info, value)?),
EntryAction::Update(ref value) => EntryAction::Update(encrypt_value(info, value)?),
EntryAction::Del(version) => EntryAction::Del(version),
};
let _ = output.insert(encrypted_key, encrypted_action);
}
Ok(output)
}
pub fn decrypt_entries(
info: &MDataInfo,
entries: &BTreeMap<Vec<u8>, Value>,
) -> Result<BTreeMap<Vec<u8>, Value>, CoreError> {
let mut output = BTreeMap::new();
for (key, value) in entries {
let decrypted_key = info.decrypt(key)?;
let decrypted_value = decrypt_value(info, value)?;
let _ = output.insert(decrypted_key, decrypted_value);
}
Ok(output)
}
pub fn decrypt_keys(
info: &MDataInfo,
keys: &BTreeSet<Vec<u8>>,
) -> Result<BTreeSet<Vec<u8>>, CoreError> {
let mut output = BTreeSet::new();
for key in keys {
let _ = output.insert(info.decrypt(key)?);
}
Ok(output)
}
pub fn decrypt_values(info: &MDataInfo, values: &[Value]) -> Result<Vec<Value>, CoreError> {
let mut output = Vec::with_capacity(values.len());
for value in values {
output.push(decrypt_value(info, value)?);
}
Ok(output)
}
fn encrypt_value(info: &MDataInfo, value: &Value) -> Result<Value, CoreError> {
Ok(Value {
content: info.enc_entry_value(&value.content)?,
entry_version: value.entry_version,
})
}
fn decrypt_value(info: &MDataInfo, value: &Value) -> Result<Value, CoreError> {
Ok(Value {
content: info.decrypt(&value.content)?,
entry_version: value.entry_version,
})
}
fn enc_entry_key(
plain_text: &[u8],
key: &secretbox::Key,
seed: secretbox::Nonce,
) -> Result<Vec<u8>, CoreError> {
let nonce = {
let secretbox::Nonce(ref nonce) = seed;
let mut pt = plain_text.to_vec();
pt.extend_from_slice(&nonce[..]);
unwrap!(secretbox::Nonce::from_slice(
&sha3_256(&pt)[..secretbox::NONCEBYTES],
))
};
symmetric_encrypt(plain_text, key, Some(&nonce))
}
impl ReprC for MDataInfo {
type C = *const FfiMDataInfo;
type Error = IpcError;
#[allow(unsafe_code)]
unsafe fn clone_from_repr_c(c: Self::C) -> Result<Self, Self::Error> {
let c = &*c;
Ok(MDataInfo {
name: XorName(c.name),
type_tag: c.type_tag,
enc_info: enc_info_from_repr_c(c.has_enc_info, c.enc_key, c.enc_nonce),
new_enc_info: enc_info_from_repr_c(c.has_new_enc_info, c.new_enc_key, c.new_enc_nonce),
})
}
}
fn enc_info_into_repr_c(
info: Option<(shared_secretbox::Key, secretbox::Nonce)>,
) -> (bool, SymSecretKey, SymNonce) {
if let Some((key, nonce)) = info {
(true, key.0, nonce.0)
} else {
(false, Default::default(), Default::default())
}
}
fn enc_info_from_repr_c(
is_set: bool,
key: SymSecretKey,
nonce: SymNonce,
) -> Option<(shared_secretbox::Key, secretbox::Nonce)> {
if is_set {
Some((
shared_secretbox::Key::from_raw(&key),
secretbox::Nonce(nonce),
))
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn private_mdata_info_encrypts() {
let info = unwrap!(MDataInfo::random_private(0));
let key = Vec::from("str of key");
let val = Vec::from("other is value");
let enc_key = unwrap!(info.enc_entry_key(&key));
let enc_val = unwrap!(info.enc_entry_value(&val));
assert_ne!(enc_key, key);
assert_ne!(enc_val, val);
assert_eq!(unwrap!(info.decrypt(&enc_key)), key);
assert_eq!(unwrap!(info.decrypt(&enc_val)), val);
}
#[test]
fn public_mdata_info_doesnt_encrypt() {
let info = unwrap!(MDataInfo::random_public(0));
let key = Vec::from("str of key");
let val = Vec::from("other is value");
assert_eq!(unwrap!(info.enc_entry_key(&key)), key);
assert_eq!(unwrap!(info.enc_entry_value(&val)), val);
assert_eq!(unwrap!(info.decrypt(&val)), val);
}
#[test]
fn decrypt() {
let mut info = unwrap!(MDataInfo::random_private(0));
let plain = Vec::from("plaintext");
let old_cipher = unwrap!(info.enc_entry_value(&plain));
info.start_new_enc_info();
let new_cipher = unwrap!(info.enc_entry_value(&plain));
assert_eq!(unwrap!(info.decrypt(&old_cipher)), plain);
assert_eq!(unwrap!(info.decrypt(&new_cipher)), plain);
info.commit_new_enc_info();
match info.decrypt(&old_cipher) {
Err(CoreError::SymmetricDecipherFailure) => (),
x => panic!("Unexpected {:?}", x),
}
assert_eq!(unwrap!(info.decrypt(&new_cipher)), plain);
}
}