use DIR_TAG;
use client::MDataInfo;
use crypto::{shared_box, shared_secretbox, shared_sign};
use errors::CoreError;
use maidsafe_utilities::serialisation::{deserialise, serialise};
use routing::{FullId, XOR_NAME_LEN, XorName};
use rust_sodium::crypto::{box_, pwhash, secretbox, sign};
use rust_sodium::crypto::sign::Seed;
use tiny_keccak::sha3_256;
#[derive(Debug, PartialEq, Deserialize, Serialize)]
pub struct Account {
pub maid_keys: ClientKeys,
pub access_container: MDataInfo,
pub config_root: MDataInfo,
pub root_dirs_created: bool,
}
impl Account {
pub fn new(maid_keys: ClientKeys) -> Result<Self, CoreError> {
Ok(Account {
maid_keys,
access_container: MDataInfo::random_private(DIR_TAG)?,
config_root: MDataInfo::random_private(DIR_TAG)?,
root_dirs_created: false,
})
}
pub fn encrypt(&self, password: &[u8], pin: &[u8]) -> Result<Vec<u8>, CoreError> {
let serialised_self = serialise(self)?;
let (key, nonce) = Self::generate_crypto_keys(password, pin)?;
Ok(secretbox::seal(&serialised_self, &nonce, &key))
}
pub fn decrypt(encrypted_self: &[u8], password: &[u8], pin: &[u8]) -> Result<Self, CoreError> {
let (key, nonce) = Self::generate_crypto_keys(password, pin)?;
let decrypted_self = secretbox::open(encrypted_self, &nonce, &key).map_err(|_| {
CoreError::SymmetricDecipherFailure
})?;
Ok(deserialise(&decrypted_self)?)
}
pub fn generate_network_id(keyword: &[u8], pin: &[u8]) -> Result<XorName, CoreError> {
let mut id = XorName([0; XOR_NAME_LEN]);
Self::derive_key(&mut id.0[..], keyword, pin)?;
Ok(id)
}
fn generate_crypto_keys(
password: &[u8],
pin: &[u8],
) -> Result<(secretbox::Key, secretbox::Nonce), CoreError> {
let mut output = [0; secretbox::KEYBYTES + secretbox::NONCEBYTES];
Self::derive_key(&mut output[..], password, pin)?;
let key = unwrap!(secretbox::Key::from_slice(&output[..secretbox::KEYBYTES]));
let nonce = unwrap!(secretbox::Nonce::from_slice(&output[secretbox::KEYBYTES..]));
Ok((key, nonce))
}
fn derive_key(output: &mut [u8], input: &[u8], user_salt: &[u8]) -> Result<(), CoreError> {
let mut salt = pwhash::Salt([0; pwhash::SALTBYTES]);
{
let pwhash::Salt(ref mut salt_bytes) = salt;
if salt_bytes.len() == 32 {
let hashed_pin = sha3_256(user_salt);
for it in salt_bytes.iter_mut().enumerate() {
*it.1 = hashed_pin[it.0];
}
} else {
return Err(CoreError::UnsupportedSaltSizeForPwHash);
}
}
pwhash::derive_key(
output,
input,
&salt,
pwhash::OPSLIMIT_INTERACTIVE,
pwhash::MEMLIMIT_INTERACTIVE,
).map(|_| ())
.map_err(|_| CoreError::UnsuccessfulPwHash)
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct ClientKeys {
pub sign_pk: sign::PublicKey,
pub sign_sk: shared_sign::SecretKey,
pub enc_pk: box_::PublicKey,
pub enc_sk: shared_box::SecretKey,
pub enc_key: shared_secretbox::Key,
}
impl ClientKeys {
pub fn new(seed: Option<&Seed>) -> Self {
let sign = match seed {
Some(s) => shared_sign::keypair_from_seed(s),
None => shared_sign::gen_keypair(),
};
let enc = shared_box::gen_keypair();
let enc_key = shared_secretbox::gen_key();
ClientKeys {
sign_pk: sign.0,
sign_sk: sign.1,
enc_pk: enc.0,
enc_sk: enc.1,
enc_key: enc_key,
}
}
}
impl Default for ClientKeys {
fn default() -> Self {
Self::new(None)
}
}
impl Into<FullId> for ClientKeys {
fn into(self) -> FullId {
let enc_sk = (*self.enc_sk).clone();
let sign_sk = (*self.sign_sk).clone();
FullId::with_keys((self.enc_pk, enc_sk), (self.sign_pk, sign_sk))
}
}
#[cfg(test)]
mod tests {
use super::*;
use maidsafe_utilities::serialisation::{deserialise, serialise};
use std::u32;
#[test]
fn generate_network_id() {
let keyword1 = b"user1";
let user1_id1 = unwrap!(Account::generate_network_id(keyword1, b"0"));
let user1_id2 = unwrap!(Account::generate_network_id(keyword1, b"1234"));
let user1_id3 = unwrap!(Account::generate_network_id(
keyword1,
u32::MAX.to_string().as_bytes(),
));
assert_ne!(user1_id1, user1_id2);
assert_ne!(user1_id1, user1_id3);
assert_ne!(user1_id2, user1_id3);
assert_eq!(
user1_id1,
unwrap!(Account::generate_network_id(keyword1, b"0"))
);
assert_eq!(
user1_id2,
unwrap!(Account::generate_network_id(keyword1, b"1234"))
);
assert_eq!(
user1_id3,
unwrap!(Account::generate_network_id(
keyword1,
u32::MAX.to_string().as_bytes(),
))
);
let keyword2 = b"user2";
let user1_id = unwrap!(Account::generate_network_id(keyword1, b"248"));
let user2_id = unwrap!(Account::generate_network_id(keyword2, b"248"));
assert_ne!(user1_id, user2_id);
}
#[test]
fn generate_crypto_keys() {
let password1 = b"super great password";
let password2 = b"even better password";
let keys1 = unwrap!(Account::generate_crypto_keys(password1, b"0"));
let keys2 = unwrap!(Account::generate_crypto_keys(password1, b"1234"));
let keys3 = unwrap!(Account::generate_crypto_keys(
password1,
u32::MAX.to_string().as_bytes(),
));
assert_ne!(keys1, keys2);
assert_ne!(keys1, keys3);
assert_ne!(keys2, keys3);
let keys1 = unwrap!(Account::generate_crypto_keys(password1, b"0"));
let keys2 = unwrap!(Account::generate_crypto_keys(password2, b"0"));
assert_ne!(keys1, keys2);
let keys1 = unwrap!(Account::generate_crypto_keys(password1, b"0"));
let keys2 = unwrap!(Account::generate_crypto_keys(password1, b"0"));
assert_eq!(keys1, keys2);
}
#[test]
fn serialisation() {
let account = unwrap!(Account::new(ClientKeys::new(None)));
let encoded = unwrap!(serialise(&account));
let decoded: Account = unwrap!(deserialise(&encoded));
assert_eq!(decoded, account);
}
#[test]
fn encryption() {
let account = unwrap!(Account::new(ClientKeys::new(None)));
let password = b"impossible to guess";
let pin = b"1000";
let encrypted = unwrap!(account.encrypt(password, pin));
let encoded = unwrap!(serialise(&account));
assert!(!encrypted.is_empty());
assert_ne!(encrypted, encoded);
let decrypted = unwrap!(Account::decrypt(&encrypted, password, pin));
assert_eq!(account, decrypted);
}
}