use crate::crypto::shared_secretbox;
use crate::errors::Error;
use crate::utils::{
self, symmetric_decrypt, symmetric_encrypt, SymEncKey, SymEncNonce, SYM_ENC_NONCE_LEN,
};
use serde::{Deserialize, Serialize};
use sn_data_types::{MapAddress, MapKind, MapSeqEntries, MapSeqEntryAction, MapSeqValue};
use std::collections::{BTreeMap, BTreeSet};
use tiny_keccak::{Hasher, Sha3};
use xor_name::XorName;
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct MapInfo {
pub address: MapAddress,
pub enc_info: Option<(shared_secretbox::Key, SymEncNonce)>,
pub new_enc_info: Option<(shared_secretbox::Key, SymEncNonce)>,
}
impl MapInfo {
pub fn new_private(
address: MapAddress,
enc_info: (shared_secretbox::Key, SymEncNonce),
) -> Self {
Self {
address,
enc_info: Some(enc_info),
new_enc_info: None,
}
}
pub fn new_public(address: MapAddress) -> Self {
Self {
address,
enc_info: None,
new_enc_info: None,
}
}
pub fn random_private(kind: MapKind, type_tag: u64) -> Result<Self, Error> {
let address = MapAddress::from_kind(kind, rand::random(), type_tag);
let enc_info = (shared_secretbox::gen_key(), utils::generate_nonce());
Ok(Self::new_private(address, enc_info))
}
pub fn random_public(kind: MapKind, type_tag: u64) -> Result<Self, Error> {
let address = MapAddress::from_kind(kind, rand::random(), type_tag);
Ok(Self::new_public(address))
}
pub fn name(&self) -> XorName {
*self.address.name()
}
pub fn type_tag(&self) -> u64 {
self.address.tag()
}
pub fn address(&self) -> &MapAddress {
&self.address
}
pub fn kind(&self) -> MapKind {
self.address.kind()
}
pub fn enc_key(&self) -> Option<&shared_secretbox::Key> {
self.enc_info.as_ref().map(|&(ref key, _)| key)
}
pub fn nonce(&self) -> Option<&SymEncNonce> {
self.enc_info.as_ref().map(|&(_, ref nonce)| nonce)
}
pub fn enc_entry_key(&self, plain_text: &[u8]) -> Result<Vec<u8>, Error> {
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>, Error> {
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>, Error> {
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(), utils::generate_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 encrypt_entries(info: &MapInfo, entries: &MapSeqEntries) -> Result<MapSeqEntries, Error> {
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: &MapInfo,
actions: &BTreeMap<Vec<u8>, MapSeqEntryAction>,
) -> Result<BTreeMap<Vec<u8>, MapSeqEntryAction>, Error> {
let mut output = BTreeMap::new();
for (key, action) in actions {
let encrypted_key = info.enc_entry_key(key)?;
let encrypted_action = match *action {
MapSeqEntryAction::Ins(ref value) => {
MapSeqEntryAction::Ins(encrypt_value(info, value)?)
}
MapSeqEntryAction::Update(ref value) => {
MapSeqEntryAction::Update(encrypt_value(info, value)?)
}
MapSeqEntryAction::Del(version) => MapSeqEntryAction::Del(version),
};
let _ = output.insert(encrypted_key, encrypted_action);
}
Ok(output)
}
pub fn decrypt_entries(info: &MapInfo, entries: &MapSeqEntries) -> Result<MapSeqEntries, Error> {
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: &MapInfo, keys: &BTreeSet<Vec<u8>>) -> Result<BTreeSet<Vec<u8>>, Error> {
let mut output = BTreeSet::new();
for key in keys {
let _ = output.insert(info.decrypt(key)?);
}
Ok(output)
}
pub fn decrypt_values(info: &MapInfo, values: &[MapSeqValue]) -> Result<Vec<MapSeqValue>, Error> {
let mut output = Vec::with_capacity(values.len());
for value in values {
output.push(decrypt_value(info, value)?);
}
Ok(output)
}
fn encrypt_value(info: &MapInfo, value: &MapSeqValue) -> Result<MapSeqValue, Error> {
Ok(MapSeqValue {
data: info.enc_entry_value(&value.data)?,
version: value.version,
})
}
fn decrypt_value(info: &MapInfo, value: &MapSeqValue) -> Result<MapSeqValue, Error> {
Ok(MapSeqValue {
data: info.decrypt(&value.data)?,
version: value.version,
})
}
fn enc_entry_key(plain_text: &[u8], key: &SymEncKey, seed: SymEncNonce) -> Result<Vec<u8>, Error> {
let mut pt = plain_text.to_vec();
pt.extend_from_slice(&seed[..]);
let mut hash = [0; 32];
let mut hasher = Sha3::v256();
hasher.update(&pt);
hasher.finalize(&mut hash);
let mut nonce = SymEncNonce::default();
nonce.copy_from_slice(&hash[..SYM_ENC_NONCE_LEN]);
symmetric_encrypt(plain_text, key, Some(&nonce))
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::{bail, Result};
#[test]
fn private_map_info_encrypts() -> Result<()> {
let info = MapInfo::random_private(MapKind::Seq, 0)?;
let key = Vec::from("str of key");
let val = Vec::from("other is value");
let enc_key = info.enc_entry_key(&key)?;
let enc_val = info.enc_entry_value(&val)?;
assert_ne!(enc_key, key);
assert_ne!(enc_val, val);
assert_eq!(info.decrypt(&enc_key)?, key);
assert_eq!(info.decrypt(&enc_val)?, val);
Ok(())
}
#[test]
fn public_map_info_doesnt_encrypt() -> Result<()> {
let info = MapInfo::random_public(MapKind::Seq, 0)?;
let key = Vec::from("str of key");
let val = Vec::from("other is value");
assert_eq!(info.enc_entry_key(&key)?, key);
assert_eq!(info.enc_entry_value(&val)?, val);
assert_eq!(info.decrypt(&val)?, val);
Ok(())
}
#[test]
fn decrypt() -> Result<()> {
let mut info = MapInfo::random_private(MapKind::Seq, 0)?;
let plain = Vec::from("plaintext");
let old_cipher = info.enc_entry_value(&plain)?;
info.start_new_enc_info();
let new_cipher = info.enc_entry_value(&plain)?;
assert_eq!(info.decrypt(&old_cipher)?, plain);
assert_eq!(info.decrypt(&new_cipher)?, plain);
info.commit_new_enc_info();
match info.decrypt(&old_cipher) {
Err(Error::SymmetricDecipherFailure) => (),
x => bail!("Unexpected {:?}", x),
}
assert_eq!(info.decrypt(&new_cipher)?, plain);
Ok(())
}
}