use failure::Fail;
use tox_crypto::pwhash::{
MEMLIMIT_INTERACTIVE, OPSLIMIT_INTERACTIVE,
Salt, OpsLimit,
gen_salt, derive_key
};
use tox_crypto::{
NONCEBYTES, MACBYTES,
Nonce, PrecomputedKey,
gen_nonce,
secretbox, sha256
};
pub use tox_crypto::pwhash::SALTBYTES as SALT_LENGTH;
pub use tox_crypto::PRECOMPUTEDKEYBYTES as KEY_LENGTH;
pub const MAGIC_LENGTH: usize = 8;
pub const MAGIC_NUMBER: &[u8; MAGIC_LENGTH] = b"toxEsave";
pub const EXTRA_LENGTH: usize = MAGIC_LENGTH + SALT_LENGTH + NONCEBYTES + MACBYTES;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PassKey {
salt: Box<Salt>,
key: Box<PrecomputedKey>
}
impl PassKey {
pub fn from_passphrase(passphrase: &[u8]) -> Result<PassKey, KeyDerivationError> {
PassKey::with_salt(passphrase, gen_salt())
}
pub fn with_salt(passphrase: &[u8], salt: Salt) -> Result<PassKey, KeyDerivationError> {
if passphrase.is_empty() { return Err(KeyDerivationError::Null) };
let sha256::Digest(passhash) = sha256::hash(passphrase);
let OpsLimit(ops) = OPSLIMIT_INTERACTIVE;
let mut key = secretbox::Key([0; secretbox::KEYBYTES]);
let maybe_key = PrecomputedKey::from_slice({
let secretbox::Key(ref mut kb) = key;
derive_key(
kb,
&passhash,
&salt,
OpsLimit(ops * 2),
MEMLIMIT_INTERACTIVE
).or(Err(KeyDerivationError::Failed))?
});
let salt = Box::new(salt);
let key = Box::new(maybe_key.ok_or(KeyDerivationError::Failed)?);
Ok(PassKey { salt, key })
}
pub fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>, EncryptionError> {
if data.is_empty() { return Err(EncryptionError::Null) };
let mut output = Vec::with_capacity(EXTRA_LENGTH + data.len());
let nonce = gen_nonce();
output.extend_from_slice(MAGIC_NUMBER);
output.extend_from_slice(&self.salt.0);
output.extend_from_slice(&nonce.0);
output.append(&mut tox_crypto::encrypt_data_symmetric(
&self.key,
&nonce,
data
));
Ok(output)
}
pub fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, DecryptionError> {
if data.is_empty() { return Err(DecryptionError::Null) };
if data.len() <= EXTRA_LENGTH { return Err(DecryptionError::InvalidLength) };
if !is_encrypted(data) { return Err(DecryptionError::BadFormat) };
let nonce = Nonce::from_slice(&data[
MAGIC_LENGTH+SALT_LENGTH..MAGIC_LENGTH+SALT_LENGTH+NONCEBYTES
]).ok_or(DecryptionError::BadFormat)?;
let output = tox_crypto::decrypt_data_symmetric(
&self.key,
&nonce,
&data[MAGIC_LENGTH+SALT_LENGTH+NONCEBYTES..]
).or(Err(DecryptionError::Failed))?;
Ok(output)
}
}
#[inline]
pub fn is_encrypted(data: &[u8]) -> bool {
data.starts_with(MAGIC_NUMBER)
}
pub fn pass_encrypt(data: &[u8], passphrase: &[u8]) -> Result<Vec<u8>, EncryptionError> {
PassKey::from_passphrase(passphrase)?.encrypt(data)
}
pub fn pass_decrypt(data: &[u8], passphrase: &[u8]) -> Result<Vec<u8>, DecryptionError> {
if data.is_empty() { return Err(DecryptionError::Null) }
if data.len() <= EXTRA_LENGTH { return Err(DecryptionError::InvalidLength) }
if !is_encrypted(data) { return Err(DecryptionError::BadFormat) }
let salt = get_salt(data).ok_or(KeyDerivationError::Failed)?;
PassKey::with_salt(passphrase, salt)?.decrypt(data)
}
pub fn get_salt(data: &[u8]) -> Option<Salt> {
if is_encrypted(data)
&& data.len() >= MAGIC_LENGTH + SALT_LENGTH
{
Salt::from_slice(&data[MAGIC_LENGTH..MAGIC_LENGTH+SALT_LENGTH])
} else {
None
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Fail)]
pub enum KeyDerivationError {
#[fail(display = "Provided passphrase is empty")]
Null,
#[fail(display = "Failed to derive key, most likely due to OOM")]
Failed
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Fail)]
pub enum EncryptionError {
#[fail(display = "Data provided for encryption is empty")]
Null,
#[fail(display = "Failed to derive key: {}", _0)]
KeyDerivation(KeyDerivationError),
}
impl From<KeyDerivationError> for EncryptionError {
fn from(err: KeyDerivationError) -> EncryptionError {
EncryptionError::KeyDerivation(err)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Fail)]
pub enum DecryptionError {
#[fail(display = "Data to be decrypted is empty")]
Null,
#[fail(display = "There's not enough data to decrypt")]
InvalidLength,
#[fail(display = "Provided data has invalid format, incompatible with TES")]
BadFormat,
#[fail(display = "Deriving key failed: {}", _0)]
KeyDerivation(KeyDerivationError),
#[fail(display = "Failure due to encrypted data being invalid")]
Failed
}
impl From<KeyDerivationError> for DecryptionError {
fn from(err: KeyDerivationError) -> DecryptionError {
DecryptionError::KeyDerivation(err)
}
}
#[test]
fn pass_key_new_test() {
let passwd = [42; 123];
let pk = PassKey::from_passphrase(&passwd).expect("Failed to unwrap PassKey!");
assert_ne!(pk.salt.0.as_ref(), &passwd as &[u8]);
assert_ne!(pk.salt.0.as_ref(), [0; SALT_LENGTH].as_ref());
assert_ne!(pk.key.0.as_ref(), &passwd as &[u8]);
assert_ne!(pk.key.0, [0; KEY_LENGTH]);
}
#[test]
fn pass_key_with_salt_test() {
let passwd = [42; 123];
let salt = gen_salt();
let pk = PassKey::with_salt(&passwd, salt).unwrap();
assert_eq!(&*pk.salt, &salt);
assert_ne!(pk.key.0.as_ref(), &passwd as &[u8]);
assert_ne!(pk.key.0, [0; KEY_LENGTH]);
}