mod dc_header;
mod dc_payload;
mod dc_ciphertext;
mod dc_hash;
mod dc_key;
use super::DevoCryptoError;
use super::Result;
use self::dc_ciphertext::{DcCiphertext, CIPHERTEXT};
use self::dc_hash::{DcHash, HASH};
use self::dc_key::{DcKey, KEY};
use self::dc_header::DcHeader;
use self::dc_payload::DcPayload;
use std;
use std::convert::TryFrom;
pub struct DcDataBlob {
header: DcHeader,
payload: DcPayload,
}
impl TryFrom<&[u8]> for DcDataBlob {
type Error = DevoCryptoError;
fn try_from(data: &[u8]) -> Result<DcDataBlob> {
if data.len() < DcHeader::len() {
return Err(DevoCryptoError::InvalidLength);
};
let header = DcHeader::try_from(&data[0..DcHeader::len()])?;
let payload = DcPayload::try_from_header(&data[DcHeader::len()..], &header)?;
Ok(DcDataBlob { header, payload })
}
}
impl From<DcDataBlob> for Vec<u8> {
fn from(blob: DcDataBlob) -> Vec<u8> {
let mut data: Vec<u8> = blob.header.into();
let mut payload: Vec<u8> = blob.payload.into();
data.append(&mut payload);
data
}
}
impl DcDataBlob {
pub fn encrypt(data: &[u8], key: &[u8], version: Option<u16>) -> Result<DcDataBlob> {
let mut header = DcHeader::new();
let payload = DcPayload::encrypt(data, key, &mut header, version)?;
Ok(DcDataBlob { header, payload })
}
pub fn decrypt(&self, key: &[u8]) -> Result<Vec<u8>> {
self.payload.decrypt(key, &self.header)
}
pub fn hash_password(password: &[u8], iterations: u32) -> Result<DcDataBlob> {
let mut header = DcHeader::new();
let payload = DcPayload::hash_password(password, iterations, &mut header)?;
Ok(DcDataBlob { header, payload })
}
pub fn verify_password(&self, password: &[u8]) -> Result<bool> {
self.payload.verify_password(password)
}
pub fn generate_key_exchange() -> Result<(DcDataBlob, DcDataBlob)> {
let mut header_private = DcHeader::new();
let mut header_public = DcHeader::new();
let (payload_private, payload_public) =
DcPayload::generate_key_exchange(&mut header_private, &mut header_public)?;
Ok((
DcDataBlob {
header: header_private,
payload: payload_private,
},
DcDataBlob {
header: header_public,
payload: payload_public,
},
))
}
pub fn mix_key_exchange(self, public: DcDataBlob) -> Result<Vec<u8>> {
self.payload.mix_key_exchange(public.payload)
}
}
#[test]
fn encrypt_decrypt_test() {
let key = "0123456789abcdefghijkl".as_bytes();
let data = "This is a very complex string of character that we need to encrypt".as_bytes();
let encrypted = DcDataBlob::encrypt(data, &key, None).unwrap();
let encrypted: Vec<u8> = encrypted.into();
let encrypted = DcDataBlob::try_from(encrypted.as_slice()).unwrap();
let decrypted = encrypted.decrypt(key).unwrap();
assert_eq!(decrypted, data);
}
#[test]
fn encrypt_v1_test() {
use base64;
let data = "testdata".as_bytes();
let key = base64::decode("Sr98VxTc424QFZDH2csZni/n5tKk2/d4ow7iGUqd5HQ=").unwrap();
let encrypted = DcDataBlob::encrypt(data, &key, Some(1)).unwrap();
assert_eq!(encrypted.header.version, 1);
let encrypted: Vec<u8> = encrypted.into();
let encrypted = DcDataBlob::try_from(encrypted.as_slice()).unwrap();
let decrypted = encrypted.decrypt(&key).unwrap();
assert_eq!(decrypted, data);
}
#[test]
fn encrypt_v2_test() {
use base64;
let data = "testdata".as_bytes();
let key = base64::decode("HOPWSC5oA9Az9SAnuwGI3nT3Dx/z2qtHBQI1k2WvVFo=").unwrap();
let encrypted = DcDataBlob::encrypt(data, &key, Some(2)).unwrap();
assert_eq!(encrypted.header.version, 2);
let encrypted: Vec<u8> = encrypted.into();
let encrypted = DcDataBlob::try_from(encrypted.as_slice()).unwrap();
let decrypted = encrypted.decrypt(&key).unwrap();
assert_eq!(decrypted, data);
}
#[test]
fn decrypt_v1_test() {
use base64;
let data = base64::decode("DQwCAAAAAQBo87jumRMVMIuTP8cFbFTgwDguKXkBvlkE/rNu4HLRRueQqfCzmXEyGR7qWAKkz4BFFyGedCmQ/xXTW4V7UnV9um1TJClz3yzQy0SQui+1UA==").unwrap();
let key = base64::decode("Xk63o/+6TeC63Z4j2HZOOdiGfqjQNJz1PTbQ3/L5nM0=").unwrap();
let encrypted = DcDataBlob::try_from(data.as_slice()).unwrap();
assert_eq!(encrypted.header.version, 1);
let decrypted = encrypted.decrypt(&key).unwrap();
assert_eq!(decrypted, "A secret v1 string".as_bytes());
}
#[test]
fn decrypt_v2_test() {
use base64;
let data = base64::decode(
"DQwCAAAAAgCcJ6yg2jWt3Zr1ZvenW4/AFi3Xj82IqfvaHmmPzMgzkrTfeKp8Shey3KLLLOhtMU4eNmYBRcAtSPfQ",
)
.unwrap();
let key = base64::decode("Dipney+DR14k+Bvz/gBJrM19yAerG/0g5iHSm/HcOJU=").unwrap();
let encrypted = DcDataBlob::try_from(data.as_slice()).unwrap();
assert_eq!(encrypted.header.version, 2);
let decrypted = encrypted.decrypt(&key).unwrap();
assert_eq!(decrypted, "A secret v2 string".as_bytes());
}
#[test]
fn password_test() {
let pass = "thisisaveryveryverystrongPa$$w0rd , //".as_bytes();
let iterations = 1234u32;
let hash = DcDataBlob::hash_password(pass, iterations).unwrap();
assert!(hash.verify_password(pass).unwrap());
assert!(!hash.verify_password("averybadpassword".as_bytes()).unwrap())
}
#[test]
fn ecdh_test() {
let (bob_priv, bob_pub) = DcDataBlob::generate_key_exchange().unwrap();
let (alice_priv, alice_pub) = DcDataBlob::generate_key_exchange().unwrap();
let bob_shared = bob_priv.mix_key_exchange(alice_pub).unwrap();
let alice_shared = alice_priv.mix_key_exchange(bob_pub).unwrap();
assert_eq!(bob_shared, alice_shared);
}