extern crate emerald_core as emerald;
extern crate rand;
extern crate rustc_serialize;
extern crate hex;
extern crate uuid;
extern crate tempdir;
use emerald::{Address, KECCAK256_BYTES};
use emerald::keystore::{CIPHER_IV_BYTES, Cipher, CoreCrypto, CryptoType, HdwalletCrypto, Iv,
KDF_SALT_BYTES, Kdf, KdfDepthLevel, KeyFile, Mac, Prf, Salt};
use hex::FromHex;
use rustc_serialize::json;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use tempdir::TempDir;
use uuid::Uuid;
const PRJ_DIR: Option<&'static str> = option_env!("CARGO_MANIFEST_DIR");
macro_rules! arr {
($bytes: expr, $num: expr) => ({
let mut arr = [0u8; $num];
arr.copy_from_slice($bytes);
arr
})
}
pub fn temp_dir() -> PathBuf {
let dir = TempDir::new("emerald").unwrap();
File::create(dir.path()).ok();
dir.into_path()
}
pub fn file_content<P: AsRef<Path>>(path: P) -> String {
let mut text = String::new();
File::open(path)
.expect("Expect read file content")
.read_to_string(&mut text)
.ok();
text
}
pub fn keyfile_path(name: &str) -> PathBuf {
let mut path = keystore_path();
path.push(name);
path
}
pub fn keystore_path() -> PathBuf {
let mut buf = PathBuf::from(PRJ_DIR.expect("Expect project directory"));
buf.push("tests/keystore/serialize");
buf
}
#[test]
fn should_decrypt_private_key_protected_by_scrypt() {
let path = keyfile_path(
"UTC--2017-03-17T10-52-08.229Z--0047201aed0b69875b24b614dda0270bcd9f11cc",
);
let keyfile = KeyFile::decode(file_content(path)).unwrap();
assert!(keyfile.decrypt_key("_").is_err());
assert_eq!(
keyfile.decrypt_key("1234567890").unwrap().to_string(),
"0xfa384e6fe915747cd13faa1022044b0def5e6bec4238bec53166487a5cca569f"
);
}
#[test]
fn should_decrypt_private_key_protected_by_pbkdf2() {
let path = keyfile_path(
"UTC--2017-03-20T17-03-12Z--37e0d14f-7269-7ca0-4419-d7b13abfeea9",
);
let keyfile = KeyFile::decode(file_content(path)).unwrap();
assert!(keyfile.decrypt_key("_").is_err());
assert_eq!(
keyfile.decrypt_key("1234567890").unwrap().to_string(),
"0x00b413b37c71bfb92719d16e28d7329dea5befa0d0b8190742f89e55617991cf"
);
}
#[test]
fn should_decode_keyfile_without_address() {
let path = keyfile_path(
"UTC--2017-03-20T17-03-12Z--37e0d14f-7269-7ca0-4419-d7b13abfeea9",
);
let mut crypto = CoreCrypto::default();
crypto.kdfparams_dklen = 32;
crypto.kdf = Kdf::Pbkdf2 {
prf: Prf::default(),
c: 10240,
};
crypto.kdfparams_salt = Salt::from(arr!(
&Vec::from_hex(
"095a4028fa2474bb2191f9fc1d876c79a9ff76ed029aa7150d37da785a00175b",
).unwrap(),
KDF_SALT_BYTES
));
crypto.cipher = Cipher::default();
crypto.cipher_text = Vec::from_hex(
"9c9e3ebbf01a512f3bea41ac6fe7676344c0da77236b38847c02718ec9b66126",
).unwrap();
crypto.cipher_params.iv = Iv::from(arr!(
&Vec::from_hex("58d54158c3e27131b0a0f2b91201aedc").unwrap(),
CIPHER_IV_BYTES
));
crypto.mac = Mac::from(arr!(
&Vec::from_hex(
"83c175d2ef1229ab10eb6726500a4303ab729e6e44dfaac274fe75c870b23a63",
).unwrap(),
KECCAK256_BYTES
));
let exp = KeyFile {
visible: None,
name: Some("".to_string()),
description: None,
address: Address::from_str("0x4c4cfc6470a1dc26916585ef03dfec42deb936ff").unwrap(),
uuid: Uuid::from_str("37e0d14f-7269-7ca0-4419-d7b13abfeea9").unwrap(),
crypto: CryptoType::Core(crypto),
};
let key = KeyFile::decode(file_content(path)).unwrap();
let key = KeyFile::decode(json::encode(&key).unwrap()).unwrap();
if let CryptoType::Core(ref exp_core) = exp.crypto {
if let CryptoType::Core(ref recv_core) = key.crypto {
assert_eq!(key, exp);
assert_eq!(key.visible, exp.visible);
assert_eq!(recv_core.kdfparams_dklen, exp_core.kdfparams_dklen);
assert_eq!(recv_core.kdf, exp_core.kdf);
assert_eq!(recv_core.kdfparams_salt, exp_core.kdfparams_salt);
assert_eq!(recv_core.cipher_text, exp_core.cipher_text);
assert_eq!(recv_core.cipher_params.iv, exp_core.cipher_params.iv);
assert_eq!(recv_core.mac, exp_core.mac);
} else {
assert!(false, "Invalid Crypto type")
}
}
}
#[test]
fn should_decode_keyfile_with_address() {
let path = keyfile_path(
"UTC--2017-03-17T10-52-08.229Z--0047201aed0b69875b24b614dda0270bcd9f11cc",
);
let mut crypto = CoreCrypto::default();
crypto.kdfparams_dklen = 32;
crypto.kdf = Kdf::Scrypt {
n: 1024,
r: 8,
p: 1,
};
crypto.kdfparams_salt = Salt::from(arr!(
&Vec::from_hex(
"fd4acb81182a2c8fa959d180967b374277f2ccf2f7f401cb08d042cc785464b4",
).unwrap(),
KDF_SALT_BYTES
));
crypto.cipher = Cipher::default();
crypto.cipher_text = Vec::from_hex(
"c3dfc95ca91dce73fe8fc4ddbaed33bad522e04a6aa1af62bba2a0bb90092fa1",
).unwrap();
crypto.cipher_params.iv = Iv::from(arr!(
&Vec::from_hex("9df1649dd1c50f2153917e3b9e7164e9").unwrap(),
CIPHER_IV_BYTES
));
crypto.mac = Mac::from(arr!(
&Vec::from_hex(
"9f8a85347fd1a81f14b99f69e2b401d68fb48904efe6a66b357d8d1d61ab14e5",
).unwrap(),
KECCAK256_BYTES
));
let exp = KeyFile {
visible: None,
name: None,
description: None,
address: Address::from_str("0x0047201aed0b69875b24b614dda0270bcd9f11cc").unwrap(),
uuid: Uuid::from_str("f7ab2bfa-e336-4f45-a31f-beb3dd0689f3").unwrap(),
crypto: CryptoType::Core(crypto),
};
let key = KeyFile::decode(file_content(path)).unwrap();
let key = KeyFile::decode(json::encode(&key).unwrap()).unwrap();
if let CryptoType::Core(ref exp_core) = exp.crypto {
if let CryptoType::Core(ref recv_core) = key.crypto {
assert_eq!(key, exp);
assert_eq!(key.visible, exp.visible);
assert_eq!(recv_core.kdfparams_dklen, exp_core.kdfparams_dklen);
assert_eq!(recv_core.kdf, exp_core.kdf);
assert_eq!(recv_core.kdfparams_salt, exp_core.kdfparams_salt);
assert_eq!(recv_core.cipher_text, exp_core.cipher_text);
assert_eq!(recv_core.cipher_params.iv, exp_core.cipher_params.iv);
assert_eq!(recv_core.mac, exp_core.mac);
} else {
assert!(false, "Invalid Crypto type")
}
}
}
#[test]
fn should_decode_hd_wallet_keyfile() {
let path = keyfile_path(
"UTC--2017-05-30T06-16-46Z--a928d7c2-b37b-464c-a70b-b9979d59fac5",
);
let mut crypto = HdwalletCrypto::default();
crypto.cipher = "hardware".to_string();
crypto.hardware = "ledger-nano-s:v1".to_string();
crypto.hd_path = "44'/61'/0'/0/0".to_string();
let exp = KeyFile {
visible: None,
name: None,
description: None,
address: Address::from_str("01234567890abcdef1234567890abcdef1234567").unwrap(),
uuid: Uuid::from_str("a928d7c2-b37b-464c-a70b-b9979d59fac5").unwrap(),
crypto: CryptoType::HdWallet(crypto),
};
let key = KeyFile::decode(file_content(path)).unwrap();
let key = KeyFile::decode(json::encode(&key).unwrap()).unwrap();
if let CryptoType::HdWallet(ref exp_hd) = exp.crypto {
if let CryptoType::HdWallet(ref recv_hd) = key.crypto {
assert_eq!(key, exp);
assert_eq!(key.visible, exp.visible);
assert_eq!(recv_hd.cipher, exp_hd.cipher);
assert_eq!(recv_hd.hardware, exp_hd.hardware);
assert_eq!(recv_hd.hd_path, exp_hd.hd_path);
} else {
assert!(false, "Invalid Crypto type")
}
}
}
#[test]
#[cfg(not(target_os = "windows"))]
fn should_use_security_level() {
let sec = KdfDepthLevel::Normal;
let kf = KeyFile::new("1234567890", &sec, None, None).unwrap();
if let CryptoType::Core(ref core) = kf.crypto {
assert_eq!(core.kdf, Kdf::from(sec));
} else {
assert!(false, "Invalid Crypto type")
}
let sec = KdfDepthLevel::High;
let kf = KeyFile::new("1234567890", &sec, Some("s".to_string()), None).unwrap();
if let CryptoType::Core(ref core) = kf.crypto {
assert_eq!(core.kdf, Kdf::from(sec));
} else {
assert!(false, "Invalid Crypto type")
}
}
#[test]
fn should_flush_to_file() {
let kf = KeyFile::new("1234567890", &KdfDepthLevel::Normal, None, None).unwrap();
assert!(kf.flush(temp_dir().as_path(), None).is_ok());
}
#[test]
fn should_search_by_address() {
let addr = "0x0047201aed0b69875b24b614dda0270bcd9f11cc"
.parse::<Address>()
.unwrap();
let (_, kf) = KeyFile::search_by_address(&addr, &keystore_path()).unwrap();
assert_eq!(
kf.uuid,
"f7ab2bfa-e336-4f45-a31f-beb3dd0689f3".parse().unwrap()
);
}