use aes::cipher::KeyInit;
use aes::cipher::{generic_array::GenericArray, BlockDecrypt, BlockEncrypt};
use aes::Aes256;
use rand::RngCore;
use ripemd::Ripemd160;
use scrypt::Params;
use secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
use sha2::Digest;
use unicode_normalization::UnicodeNormalization;
const LEN_EKEY: usize = 58;
const LEN_WIFC: usize = 52;
const LEN_WIFU: usize = 51;
const LEN_SCRY: usize = 64;
const NBBY_PUBC: usize = 33;
const NBBY_PUBU: usize = 65;
const NBBY_WIFC: usize = 34;
const NBBY_WIFU: usize = 33;
const PRE_EC: [u8; 2] = [0x01, 0x43];
const PRE_EKEY: &str = "6P";
const PRE_NON_EC: [u8; 2] = [0x01, 0x42];
const PRE_WIFC: &str = "KL";
const PRE_WIFB: u8 = 0x80;
const PRE_WIFU: &str = "5";
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
pub enum Error {
Base58,
Checksum,
EcMul,
EncKey,
NbPubB,
Pass,
PrvKey,
PubKey,
ScryptFn,
ScryptParam,
WifKey,
}
trait BytesManipulation {
fn encode_base58ck(&self) -> String;
fn hash160(&self) -> [u8; 20];
fn hash256(&self) -> [u8; 32];
fn p2wpkh(&self) -> Result<String, Error>;
}
pub trait Decrypt {
fn decrypt(&self, pass: &str) -> Result<([u8; 32], bool), Error>;
fn decrypt_to_wif(&self, pass: &str) -> Result<String, Error>;
}
pub trait Encrypt {
fn encrypt(&self, pass: &str, compress: bool) -> Result<String, Error>;
}
pub trait EncryptWif {
fn encrypt_wif(&self, pass: &str) -> Result<String, Error>;
}
pub trait Generate {
fn generate(&self, compress: bool) -> Result<String, Error>;
}
trait PrivateKeyManipulation {
fn public(&self, compress: bool) -> Result<Vec<u8>, Error>;
fn wif(&self, compress: bool) -> String;
}
trait StringManipulation {
fn decode_base58ck(&self) -> Result<Vec<u8>, Error>;
fn decode_wif(&self) -> Result<([u8; 32], bool), Error>;
fn decrypt_ec(&self, pass: &str) -> Result<([u8; 32], bool), Error>;
fn decrypt_non_ec(&self, pass: &str) -> Result<([u8; 32], bool), Error>;
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Error::Base58 => write!(f, "invalid base58 string"),
Error::Checksum => write!(f, "invalid checksum"),
Error::EcMul => write!(f, "invalid elliptic curve multiplication"),
Error::EncKey => write!(f, "invalid encrypted private key"),
Error::NbPubB => write!(f, "invalid number of public key bytes"),
Error::Pass => write!(f, "invalid passphrase"),
Error::PrvKey => write!(f, "invalid private key"),
Error::PubKey => write!(f, "invalid public key"),
Error::ScryptFn => write!(f, "failure on scrypt function"),
Error::ScryptParam => write!(f, "invalid scrypt parameter"),
Error::WifKey => write!(f, "invalid wif private key"),
}
}
}
impl BytesManipulation for [u8] {
#[inline]
fn encode_base58ck(&self) -> String {
let mut decoded = self.to_vec();
decoded.append(&mut decoded.hash256()[..4].to_vec());
bs58::encode(decoded).into_string()
}
#[inline]
fn hash160(&self) -> [u8; 20] {
let mut result = [0x00; 20];
result[..].copy_from_slice(&Ripemd160::digest(&sha2::Sha256::digest(self)));
result
}
#[inline]
fn hash256(&self) -> [u8; 32] {
let mut result = [0x00; 32];
result[..].copy_from_slice(&sha2::Sha256::digest(&sha2::Sha256::digest(self)));
result
}
#[inline]
fn p2wpkh(&self) -> Result<String, Error> {
if self.len() != NBBY_PUBC && self.len() != NBBY_PUBU {
return Err(Error::NbPubB);
}
let mut address_bytes = vec![0x00];
address_bytes.append(&mut self.hash160().to_vec());
Ok(address_bytes.encode_base58ck())
}
}
impl Decrypt for str {
#[inline]
fn decrypt(&self, pass: &str) -> Result<([u8; 32], bool), Error> {
Ok(
if self.len() != LEN_EKEY || (self.is_char_boundary(2) && &self[..2] != PRE_EKEY) {
return Err(Error::EncKey);
} else if self.decode_base58ck()?[..2] == PRE_NON_EC {
self.decrypt_non_ec(pass)?
} else if self.decode_base58ck()?[..2] == PRE_EC {
self.decrypt_ec(pass)?
} else {
return Err(Error::EncKey);
},
)
}
#[inline]
fn decrypt_to_wif(&self, pass: &str) -> Result<String, Error> {
let raw = self.decrypt(pass)?;
let wif = raw.0.wif(raw.1);
Ok(wif)
}
}
impl EncryptWif for str {
#[inline]
fn encrypt_wif(&self, pass: &str) -> Result<String, Error> {
let raw_prvk = self.decode_wif()?;
let eprvk = raw_prvk.0.encrypt(pass, raw_prvk.1)?;
Ok(eprvk)
}
}
impl Encrypt for [u8; 32] {
#[inline]
fn encrypt(&self, pass: &str, compress: bool) -> Result<String, Error> {
let address = self.public(compress)?.p2wpkh()?;
let checksum = &address.as_bytes().hash256()[..4];
let mut scrypt_key = [0x00; 64];
scrypt::scrypt(
pass.nfc().collect::<String>().as_bytes(),
checksum,
&Params::new(14, 8, 8, LEN_SCRY).map_err(|_| Error::ScryptParam)?,
&mut scrypt_key,
)
.map_err(|_| Error::ScryptFn)?;
let mut half1 = [0x00; 32];
half1[..].copy_from_slice(&scrypt_key[..32]);
let cipher = Aes256::new(GenericArray::from_slice(&scrypt_key[32..]));
for idx in 0..32 {
half1[idx] ^= self[idx];
}
let mut part1 = GenericArray::clone_from_slice(&half1[..16]);
let mut part2 = GenericArray::clone_from_slice(&half1[16..]);
cipher.encrypt_block(&mut part1);
cipher.encrypt_block(&mut part2);
let mut buffer = [0x00; 39];
buffer[..2].copy_from_slice(&PRE_NON_EC);
buffer[2] = if compress { 0xe0 } else { 0xc0 };
buffer[3..7].copy_from_slice(checksum);
buffer[7..23].copy_from_slice(&part1);
buffer[23..].copy_from_slice(&part2);
Ok(buffer.encode_base58ck())
}
}
impl Generate for str {
#[inline]
fn generate(&self, compress: bool) -> Result<String, Error> {
let mut owner_salt = [0x00; 8];
let mut pass_factor = [0x00; 32];
let mut seed_b = [0x00; 24];
rand::thread_rng().fill_bytes(&mut owner_salt);
scrypt::scrypt(
self.nfc().collect::<String>().as_bytes(),
&owner_salt,
&Params::new(14, 8, 8, LEN_SCRY).map_err(|_| Error::ScryptParam)?,
&mut pass_factor,
)
.map_err(|_| Error::ScryptFn)?;
let pass_point = pass_factor.public(true)?;
let mut pass_point_mul = PublicKey::from_slice(&pass_point).map_err(|_| Error::PubKey)?;
rand::thread_rng().fill_bytes(&mut seed_b);
let factor_b = Scalar::from_be_bytes(seed_b.hash256()).map_err(|_| Error::EcMul)?;
pass_point_mul = pass_point_mul
.mul_tweak(&Secp256k1::new(), &factor_b)
.map_err(|_| Error::EcMul)?;
let pubk = if compress {
pass_point_mul.serialize().to_vec()
} else {
pass_point_mul.serialize_uncompressed().to_vec()
};
let address = pubk.p2wpkh()?;
let address_hash = &address.as_bytes().hash256()[..4];
let mut salt = [0x00; 12];
let mut seed_b_pass = [0x00; 64];
salt[..4].copy_from_slice(address_hash);
salt[4..].copy_from_slice(&owner_salt);
scrypt::scrypt(
&pass_point,
&salt,
&Params::new(10, 1, 1, LEN_SCRY).map_err(|_| Error::ScryptParam)?,
&mut seed_b_pass,
)
.map_err(|_| Error::ScryptFn)?;
let derived_half1 = &seed_b_pass[..32];
let derived_half2 = &seed_b_pass[32..];
let en_p1 = &mut seed_b[..16];
for idx in 0..16 {
en_p1[idx] ^= derived_half1[idx];
}
let cipher = Aes256::new(GenericArray::from_slice(derived_half2));
let mut encrypted_part1 = GenericArray::clone_from_slice(en_p1);
cipher.encrypt_block(&mut encrypted_part1);
let mut en_p2 = [0x00; 16];
en_p2[..8].copy_from_slice(&encrypted_part1[8..]);
en_p2[8..].copy_from_slice(&seed_b[16..]);
for idx in 0..16 {
en_p2[idx] ^= derived_half1[idx + 16];
}
let mut encrypted_part2 = GenericArray::clone_from_slice(&en_p2);
cipher.encrypt_block(&mut encrypted_part2);
let flag = if compress { 0x20 } else { 0x00 };
let mut result_bytes = [0x00; 39];
result_bytes[..2].copy_from_slice(&PRE_EC);
result_bytes[2] = flag;
result_bytes[3..7].copy_from_slice(address_hash);
result_bytes[7..15].copy_from_slice(&owner_salt);
result_bytes[15..23].copy_from_slice(&encrypted_part1[..8]);
result_bytes[23..].copy_from_slice(&encrypted_part2);
Ok(result_bytes.encode_base58ck())
}
}
impl PrivateKeyManipulation for [u8; 32] {
#[inline]
fn public(&self, compress: bool) -> Result<Vec<u8>, Error> {
let secp_pub = PublicKey::from_secret_key(
&Secp256k1::new(),
&SecretKey::from_slice(self).map_err(|_| Error::PrvKey)?,
);
if compress {
Ok(secp_pub.serialize().to_vec())
} else {
Ok(secp_pub.serialize_uncompressed().to_vec())
}
}
#[inline]
fn wif(&self, compress: bool) -> String {
let mut decoded: Vec<u8> = vec![PRE_WIFB];
decoded.append(&mut self.to_vec());
if compress {
decoded.push(0x01);
}
decoded.encode_base58ck()
}
}
impl StringManipulation for str {
#[inline]
fn decode_base58ck(&self) -> Result<Vec<u8>, Error> {
let raw = bs58::decode(self).into_vec().map_err(|_| Error::Base58)?;
if raw[raw.len() - 4..] == raw[..raw.len() - 4].hash256()[..4] {
Ok(raw[..(raw.len() - 4)].to_vec())
} else {
Err(Error::Checksum)
}
}
#[inline]
fn decode_wif(&self) -> Result<([u8; 32], bool), Error> {
if (!self.is_char_boundary(1) || !PRE_WIFC.contains(&self[..1]) || self.len() != LEN_WIFC)
&& (!self.starts_with(PRE_WIFU) || self.len() != LEN_WIFU)
{
return Err(Error::WifKey);
}
let raw_bytes = self.decode_base58ck()?;
if (raw_bytes.len() != NBBY_WIFC && raw_bytes.len() != NBBY_WIFU)
|| raw_bytes[0] != PRE_WIFB
{
return Err(Error::WifKey);
}
let mut result = [0x00; 32];
result[..].copy_from_slice(&raw_bytes[1..33]);
Ok((result, raw_bytes.len() == NBBY_WIFC))
}
#[inline]
fn decrypt_ec(&self, pass: &str) -> Result<([u8; 32], bool), Error> {
let eprvk = self.decode_base58ck()?;
if eprvk[..2] != PRE_EC {
return Err(Error::EncKey);
}
let address_hash = &eprvk[3..7];
let encrypted_p1 = &eprvk[15..23];
let encrypted_p2 = &eprvk[23..39];
let flag_byte: u8 = eprvk[2];
let compress = (flag_byte & 0x20) == 0x20;
let has_lot = (flag_byte & 0x04) == 0x04;
let owner_entropy = &eprvk[7..15];
let owner_salt = &eprvk[7..15 - (flag_byte & 0x04) as usize];
let mut pre_factor = [0x00; 32];
let mut pass_factor = [0x00; 32];
scrypt::scrypt(
pass.nfc().collect::<String>().as_bytes(),
owner_salt,
&Params::new(14, 8, 8, LEN_SCRY).map_err(|_| Error::ScryptParam)?,
&mut pre_factor,
)
.map_err(|_| Error::ScryptFn)?;
if has_lot {
let mut tmp: Vec<u8> = Vec::new();
tmp.append(&mut pre_factor.to_vec());
tmp.append(&mut owner_entropy.to_vec());
pass_factor[..].copy_from_slice(&tmp.hash256());
} else {
pass_factor = pre_factor;
}
let pass_point = pass_factor.public(true)?;
let mut seed_b_pass = [0x00; 64];
scrypt::scrypt(
&pass_point,
&eprvk[3..15], &Params::new(10, 1, 1, LEN_SCRY).map_err(|_| Error::ScryptParam)?,
&mut seed_b_pass,
)
.map_err(|_| Error::ScryptFn)?;
let derived_half1 = &seed_b_pass[..32];
let derived_half2 = &seed_b_pass[32..];
let cipher = Aes256::new(GenericArray::from_slice(derived_half2));
let mut de_p2 = GenericArray::clone_from_slice(encrypted_p2);
cipher.decrypt_block(&mut de_p2);
for idx in 0..16 {
de_p2[idx] ^= derived_half1[idx + 16];
}
let seed_b_part2 = &de_p2[8..];
let mut tmp = [0x00; 16];
tmp[..8].copy_from_slice(encrypted_p1);
tmp[8..].copy_from_slice(&de_p2[..8]);
let mut de_p1 = GenericArray::clone_from_slice(&tmp);
cipher.decrypt_block(&mut de_p1);
for idx in 0..16 {
de_p1[idx] ^= derived_half1[idx];
}
let mut seed_b = [0x00; 24];
seed_b[..16].copy_from_slice(&de_p1);
seed_b[16..].copy_from_slice(seed_b_part2);
let factor_b = seed_b.hash256();
let mut prv = SecretKey::from_slice(&pass_factor).map_err(|_| Error::PrvKey)?;
prv = prv
.mul_tweak(&Scalar::from_be_bytes(factor_b).map_err(|_| Error::EcMul)?)
.map_err(|_| Error::PrvKey)?;
let mut result = [0x00; 32];
result[..].copy_from_slice(&prv[..]);
let address = result.public(compress)?.p2wpkh()?;
let checksum = &address.as_bytes().hash256()[..4];
if checksum != address_hash {
return Err(Error::Pass);
}
Ok((result, compress))
}
#[inline]
fn decrypt_non_ec(&self, pass: &str) -> Result<([u8; 32], bool), Error> {
let eprvk = self.decode_base58ck()?;
if eprvk[..2] != PRE_NON_EC {
return Err(Error::EncKey);
}
let compress = (eprvk[2] & 0x20) == 0x20;
let mut scrypt_key = [0x00; 64];
scrypt::scrypt(
pass.nfc().collect::<String>().as_bytes(),
&eprvk[3..7], &Params::new(14, 8, 8, LEN_SCRY).map_err(|_| Error::ScryptParam)?,
&mut scrypt_key,
)
.map_err(|_| Error::ScryptFn)?;
let cipher = Aes256::new(GenericArray::from_slice(&scrypt_key[32..]));
let mut derived_half1 = GenericArray::clone_from_slice(&eprvk[7..23]);
let mut derived_half2 = GenericArray::clone_from_slice(&eprvk[23..39]);
cipher.decrypt_block(&mut derived_half1);
cipher.decrypt_block(&mut derived_half2);
for idx in 0..16 {
derived_half1[idx] ^= scrypt_key[idx];
derived_half2[idx] ^= scrypt_key[idx + 16];
}
let mut prvk = [0x00; 32];
prvk[..16].copy_from_slice(&derived_half1);
prvk[16..].copy_from_slice(&derived_half2);
let address = prvk.public(compress)?.p2wpkh()?;
let checksum = &address.as_bytes().hash256()[..4];
if checksum != &eprvk[3..7] {
return Err(Error::Pass);
}
Ok((prvk, compress))
}
}
#[cfg(test)]
mod tests {
use super::*;
const TV_ENCRYPTED: [&str; 9] = [
"6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg",
"6PRNFFkZc2NZ6dJqFfhRoFNMR9Lnyj7dYGrzdgXXVMXcxoKTePPX1dWByq",
"6PRW5o9FLp4gJDDVqJQKJFTpMvdsSGJxMYHtHaQBF3ooa8mwD69bapcDQn",
"6PYNKZ1EAgYgmQfmNVamxyXVWHzK5s6DGhwP4J5o44cvXdoY7sRzhtpUeo",
"6PYLtMnXvfG3oJde97zRyLYFZCYizPU5T3LwgdYJz1fRhh16bU7u6PPmY7",
"6PfQu77ygVyJLZjfvMLyhLMQbYnu5uguoJJ4kMCLqWwPEdfpwANVS76gTX",
"6PfLGnQs6VZnrNpmVKfjotbnQuaJK4KZoPFrAjx1JMJUa1Ft8gnf5WxfKd",
"6PgNBNNzDkKdhkT6uJntUXwwzQV8Rr2tZcbkDcuC9DZRsS6AtHts4Ypo1j",
"6PgGWtx25kUg8QWvwuJAgorN6k9FbE25rv5dMRwu5SKMnfpfVe5mar2ngH",
];
const TV_KEY: [[u8; 32]; 9] = [
[
0xcb, 0xf4, 0xb9, 0xf7, 0x04, 0x70, 0x85, 0x6b, 0xb4, 0xf4, 0x0f, 0x80, 0xb8, 0x7e,
0xdb, 0x90, 0x86, 0x59, 0x97, 0xff, 0xee, 0x6d, 0xf3, 0x15, 0xab, 0x16, 0x6d, 0x71,
0x3a, 0xf4, 0x33, 0xa5,
],
[
0x09, 0xc2, 0x68, 0x68, 0x80, 0x09, 0x5b, 0x1a, 0x4c, 0x24, 0x9e, 0xe3, 0xac, 0x4e,
0xea, 0x8a, 0x01, 0x4f, 0x11, 0xe6, 0xf9, 0x86, 0xd0, 0xb5, 0x02, 0x5a, 0xc1, 0xf3,
0x9a, 0xfb, 0xd9, 0xae,
],
[
0x64, 0xee, 0xab, 0x5f, 0x9b, 0xe2, 0xa0, 0x1a, 0x83, 0x65, 0xa5, 0x79, 0x51, 0x1e,
0xb3, 0x37, 0x3c, 0x87, 0xc4, 0x0d, 0xa6, 0xd2, 0xa2, 0x5f, 0x05, 0xbd, 0xa6, 0x8f,
0xe0, 0x77, 0xb6, 0x6e,
],
[
0xcb, 0xf4, 0xb9, 0xf7, 0x04, 0x70, 0x85, 0x6b, 0xb4, 0xf4, 0x0f, 0x80, 0xb8, 0x7e,
0xdb, 0x90, 0x86, 0x59, 0x97, 0xff, 0xee, 0x6d, 0xf3, 0x15, 0xab, 0x16, 0x6d, 0x71,
0x3a, 0xf4, 0x33, 0xa5,
],
[
0x09, 0xc2, 0x68, 0x68, 0x80, 0x09, 0x5b, 0x1a, 0x4c, 0x24, 0x9e, 0xe3, 0xac, 0x4e,
0xea, 0x8a, 0x01, 0x4f, 0x11, 0xe6, 0xf9, 0x86, 0xd0, 0xb5, 0x02, 0x5a, 0xc1, 0xf3,
0x9a, 0xfb, 0xd9, 0xae,
],
[
0xa4, 0x3a, 0x94, 0x05, 0x77, 0xf4, 0xe9, 0x7f, 0x5c, 0x4d, 0x39, 0xeb, 0x14, 0xff,
0x08, 0x3a, 0x98, 0x18, 0x7c, 0x64, 0xea, 0x7c, 0x99, 0xef, 0x7c, 0xe4, 0x60, 0x83,
0x39, 0x59, 0xa5, 0x19,
],
[
0xc2, 0xc8, 0x03, 0x6d, 0xf2, 0x68, 0xf4, 0x98, 0x09, 0x93, 0x50, 0x71, 0x8c, 0x4a,
0x3e, 0xf3, 0x98, 0x4d, 0x2b, 0xe8, 0x46, 0x18, 0xc2, 0x65, 0x0f, 0x51, 0x71, 0xdc,
0xc5, 0xeb, 0x66, 0x0a,
],
[
0x44, 0xea, 0x95, 0xaf, 0xbf, 0x13, 0x83, 0x56, 0xa0, 0x5e, 0xa3, 0x21, 0x10, 0xdf,
0xd6, 0x27, 0x23, 0x2d, 0x0f, 0x29, 0x91, 0xad, 0x22, 0x11, 0x87, 0xbe, 0x35, 0x6f,
0x19, 0xfa, 0x81, 0x90,
],
[
0xca, 0x27, 0x59, 0xaa, 0x4a, 0xdb, 0x0f, 0x96, 0xc4, 0x14, 0xf3, 0x6a, 0xbe, 0xb8,
0xdb, 0x59, 0x34, 0x29, 0x85, 0xbe, 0x9f, 0xa5, 0x0f, 0xaa, 0xc2, 0x28, 0xc8, 0xe7,
0xd9, 0x0e, 0x30, 0x06,
],
];
const TV_PASS: [&str; 9] = [
"TestingOneTwoThree",
"Satoshi",
"\u{03d2}\u{0301}\u{0000}\u{010400}\u{01f4a9}",
"TestingOneTwoThree",
"Satoshi",
"TestingOneTwoThree",
"Satoshi",
"MOLON LABE",
"ΜΟΛΩΝ ΛΑΒΕ",
];
const TV_WIF: [&str; 9] = [
"5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR",
"5HtasZ6ofTHP6HCwTqTkLDuLQisYPah7aUnSKfC7h4hMUVw2gi5",
"5Jajm8eQ22H3pGWLEVCXyvND8dQZhiQhoLJNKjYXk9roUFTMSZ4",
"L44B5gGEpqEDRS9vVPz7QT35jcBG2r3CZwSwQ4fCewXAhAhqGVpP",
"KwYgW8gcxj1JWJXhPSu4Fqwzfhp5Yfi42mdYmMa4XqK7NJxXUSK7",
"5K4caxezwjGCGfnoPTZ8tMcJBLB7Jvyjv4xxeacadhq8nLisLR2",
"5KJ51SgxWaAYR13zd9ReMhJpwrcX47xTJh2D3fGPG9CM8vkv5sH",
"5JLdxTtcTHcfYcmJsNVy1v2PMDx432JPoYcBTVVRHpPaxUrdtf8",
"5KMKKuUmAkiNbA3DazMQiLfDq47qs8MAEThm4yL8R2PhV1ov33D",
];
#[test]
fn test_decode_wif() {
assert_eq!(
"KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp".decode_wif(),
Ok(([0x11; 32], true))
);
assert_eq!(
"5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh".decode_wif(),
Ok(([0x11; 32], false))
);
assert_eq!(
"KzkcmnPaJd7mqT47Rnk9XMGRfW2wfo7ar2M2o6Yoe6Rdgbg2bHM9".decode_wif(),
Ok(([0x69; 32], true))
);
assert_eq!(
"5JciBbkdYdjKKE9rwZ7c1XscwwcLBbv9aJyeZeWQi2gZnHeiX57".decode_wif(),
Ok(([0x69; 32], false))
);
assert_eq!(
["KzkcmnPaJd7mqT47Rnk9XMGRfW2wfo7ar2M2o6Yoe6Rdgbg2bHM9", "a"]
.concat()
.decode_wif(),
Err(Error::WifKey)
);
assert_eq!(
"KzkcmnPaJd7mqT47Rnk9XMGRfW2wfo7ar2M2o6Yoe6Rdgbg2bHM9"
.replace("d", "b")
.decode_wif(),
Err(Error::Checksum)
);
assert_eq!(["a"; 51].concat().decode_wif(), Err(Error::WifKey));
assert_eq!(["a"; 52].concat().decode_wif(), Err(Error::WifKey));
}
#[test]
fn test_decrypt() {
let mut compress = false;
for (idx, ekey) in TV_ENCRYPTED.iter().enumerate() {
if idx > 2 {
compress = true
}
if idx > 4 {
compress = false
}
assert_eq!(ekey.decrypt(TV_PASS[idx]), Ok((TV_KEY[idx], compress)));
}
assert!(TV_ENCRYPTED[1].decrypt("Satoshi").is_ok());
assert_eq!(TV_ENCRYPTED[1].decrypt("wrong"), Err(Error::Pass));
assert_eq!(
TV_ENCRYPTED[1].replace("X", "x").decrypt("Satoshi"),
Err(Error::Checksum)
);
assert_eq!(TV_ENCRYPTED[1][1..].decrypt("Satoshi"), Err(Error::EncKey));
}
#[test]
fn test_decrypt_to_wif() {
for (idx, ekey) in TV_ENCRYPTED.iter().enumerate() {
assert_eq!(ekey.decrypt_to_wif(TV_PASS[idx]).unwrap(), TV_WIF[idx]);
}
assert!(TV_ENCRYPTED[1].decrypt_to_wif("Satoshi").is_ok());
assert_eq!(TV_ENCRYPTED[1].decrypt_to_wif("wrong"), Err(Error::Pass));
assert_eq!(
TV_ENCRYPTED[1].replace('X', "x").decrypt_to_wif("Satoshi"),
Err(Error::Checksum)
);
assert_eq!(
TV_ENCRYPTED[1][1..].decrypt_to_wif("Satoshi"),
Err(Error::EncKey)
);
}
#[test]
fn test_encode_base58ck() {
assert_eq!("a".as_bytes().encode_base58ck(), "C2dGTwc");
assert_eq!("abc".as_bytes().encode_base58ck(), "4h3c6RH52R");
}
#[test]
fn test_encrypt() {
let mut compress = false;
for (idx, key) in TV_KEY[..5].iter().enumerate() {
if idx > 2 {
compress = true
}
assert_eq!(
key.encrypt(TV_PASS[idx], compress).unwrap(),
TV_ENCRYPTED[idx]
);
}
assert_eq!(
[0x00; 32].encrypt("I'm_a_passphrase", true),
Err(Error::PrvKey)
);
}
#[test]
fn test_encrypt_wif() {
for (idx, wif) in TV_WIF[..5].iter().enumerate() {
assert_eq!(wif.encrypt_wif(TV_PASS[idx]).unwrap(), TV_ENCRYPTED[idx]);
}
assert_eq!(
[TV_WIF[0], "a"].concat().encrypt_wif(TV_PASS[0]),
Err(Error::WifKey)
);
assert_eq!(
TV_WIF[0].replace('X', "!").encrypt_wif(TV_PASS[0]),
Err(Error::Base58)
);
assert_eq!(
TV_WIF[0].replace('X', "x").encrypt_wif(TV_PASS[0]),
Err(Error::Checksum)
);
assert_eq!(
"KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73NUBByJr".encrypt_wif("pass"), Err(Error::PrvKey)
);
}
#[test]
fn test_generate() {
assert!("バンドメイド"
.generate(true)
.unwrap()
.decrypt("バンドメイド")
.is_ok());
assert!("くるっぽー!"
.generate(false)
.unwrap()
.decrypt("くるっぽー!")
.is_ok());
assert_eq!(
"something_really_dumb"
.generate(true)
.unwrap()
.decrypt("rocket_science"),
Err(Error::Pass)
);
assert_eq!("a".generate(false).unwrap().decrypt("b"), Err(Error::Pass));
}
#[test]
fn test_hash160() {
assert_eq!(
"a".as_bytes().hash160(),
[
0x99, 0x43, 0x55, 0x19, 0x9e, 0x51, 0x6f, 0xf7, 0x6c, 0x4f, 0xa4, 0xaa, 0xb3, 0x93,
0x37, 0xb9, 0xd8, 0x4c, 0xf1, 0x2b
]
);
}
#[test]
fn test_hash256() {
assert_eq!(
"a".as_bytes().hash256(),
[
0xbf, 0x5d, 0x3a, 0xff, 0xb7, 0x3e, 0xfd, 0x2e, 0xc6, 0xc3, 0x6a, 0xd3, 0x11, 0x2d,
0xd9, 0x33, 0xef, 0xed, 0x63, 0xc4, 0xe1, 0xcb, 0xff, 0xcf, 0xa8, 0x8e, 0x27, 0x59,
0xc1, 0x44, 0xf2, 0xd8
]
);
}
#[test]
fn test_p2wpkh() {
assert_eq!(
[
0x03, 0x4f, 0x35, 0x5b, 0xdc, 0xb7, 0xcc, 0x0a, 0xf7, 0x28, 0xef, 0x3c, 0xce, 0xb9,
0x61, 0x5d, 0x90, 0x68, 0x4b, 0xb5, 0xb2, 0xca, 0x5f, 0x85, 0x9a, 0xb0, 0xf0, 0xb7,
0x04, 0x07, 0x58, 0x71, 0xaa
]
.p2wpkh()
.unwrap(),
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"
);
assert_eq!(
[
0x02, 0x66, 0x6b, 0xdf, 0x20, 0x25, 0xe3, 0x2f, 0x41, 0x08, 0x88, 0x99, 0xf2, 0xbc,
0xb4, 0xbf, 0x69, 0x83, 0x18, 0x7f, 0x38, 0x0e, 0x72, 0xfc, 0x7d, 0xee, 0x11, 0x5b,
0x1f, 0x99, 0x57, 0xcc, 0x72
]
.p2wpkh()
.unwrap(),
"1N7qxowv8SnfdBYhmvpxZxyjsYQDPd88ES"
);
assert_eq!(
[
0x04, 0x4f, 0x35, 0x5b, 0xdc, 0xb7, 0xcc, 0x0a, 0xf7, 0x28, 0xef, 0x3c, 0xce, 0xb9,
0x61, 0x5d, 0x90, 0x68, 0x4b, 0xb5, 0xb2, 0xca, 0x5f, 0x85, 0x9a, 0xb0, 0xf0, 0xb7,
0x04, 0x07, 0x58, 0x71, 0xaa, 0x38, 0x5b, 0x6b, 0x1b, 0x8e, 0xad, 0x80, 0x9c, 0xa6,
0x74, 0x54, 0xd9, 0x68, 0x3f, 0xcf, 0x2b, 0xa0, 0x34, 0x56, 0xd6, 0xfe, 0x2c, 0x4a,
0xbe, 0x2b, 0x07, 0xf0, 0xfb, 0xdb, 0xb2, 0xf1, 0xc1
]
.p2wpkh()
.unwrap(),
"1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a"
);
assert_eq!(
[
0x04, 0x66, 0x6b, 0xdf, 0x20, 0x25, 0xe3, 0x2f, 0x41, 0x08, 0x88, 0x99, 0xf2, 0xbc,
0xb4, 0xbf, 0x69, 0x83, 0x18, 0x7f, 0x38, 0x0e, 0x72, 0xfc, 0x7d, 0xee, 0x11, 0x5b,
0x1f, 0x99, 0x57, 0xcc, 0x72, 0x9d, 0xd9, 0x76, 0x13, 0x1c, 0x4c, 0x8e, 0x12, 0xab,
0x10, 0x83, 0xca, 0x06, 0x54, 0xca, 0x5f, 0xdb, 0xca, 0xc8, 0xd3, 0x19, 0x8d, 0xaf,
0x90, 0xf5, 0x81, 0xb5, 0x91, 0xd5, 0x63, 0x79, 0xca
]
.p2wpkh()
.unwrap(),
"17iS4e5ib2t2Bj2UFjPbxSDdmecHNnCAwy"
);
}
#[test]
fn test_public() {
assert_eq!(
[0x11; 32].public(true).unwrap(),
[
0x03, 0x4f, 0x35, 0x5b, 0xdc, 0xb7, 0xcc, 0x0a, 0xf7, 0x28, 0xef, 0x3c, 0xce, 0xb9,
0x61, 0x5d, 0x90, 0x68, 0x4b, 0xb5, 0xb2, 0xca, 0x5f, 0x85, 0x9a, 0xb0, 0xf0, 0xb7,
0x04, 0x07, 0x58, 0x71, 0xaa
]
);
assert_eq!(
[0x69; 32].public(true).unwrap(),
[
0x02, 0x66, 0x6b, 0xdf, 0x20, 0x25, 0xe3, 0x2f, 0x41, 0x08, 0x88, 0x99, 0xf2, 0xbc,
0xb4, 0xbf, 0x69, 0x83, 0x18, 0x7f, 0x38, 0x0e, 0x72, 0xfc, 0x7d, 0xee, 0x11, 0x5b,
0x1f, 0x99, 0x57, 0xcc, 0x72
]
);
assert_eq!(
[0x11; 32].public(false).unwrap(),
[
0x04, 0x4f, 0x35, 0x5b, 0xdc, 0xb7, 0xcc, 0x0a, 0xf7, 0x28, 0xef, 0x3c, 0xce, 0xb9,
0x61, 0x5d, 0x90, 0x68, 0x4b, 0xb5, 0xb2, 0xca, 0x5f, 0x85, 0x9a, 0xb0, 0xf0, 0xb7,
0x04, 0x07, 0x58, 0x71, 0xaa, 0x38, 0x5b, 0x6b, 0x1b, 0x8e, 0xad, 0x80, 0x9c, 0xa6,
0x74, 0x54, 0xd9, 0x68, 0x3f, 0xcf, 0x2b, 0xa0, 0x34, 0x56, 0xd6, 0xfe, 0x2c, 0x4a,
0xbe, 0x2b, 0x07, 0xf0, 0xfb, 0xdb, 0xb2, 0xf1, 0xc1
]
);
assert_eq!(
[0x69; 32].public(false).unwrap(),
[
0x04, 0x66, 0x6b, 0xdf, 0x20, 0x25, 0xe3, 0x2f, 0x41, 0x08, 0x88, 0x99, 0xf2, 0xbc,
0xb4, 0xbf, 0x69, 0x83, 0x18, 0x7f, 0x38, 0x0e, 0x72, 0xfc, 0x7d, 0xee, 0x11, 0x5b,
0x1f, 0x99, 0x57, 0xcc, 0x72, 0x9d, 0xd9, 0x76, 0x13, 0x1c, 0x4c, 0x8e, 0x12, 0xab,
0x10, 0x83, 0xca, 0x06, 0x54, 0xca, 0x5f, 0xdb, 0xca, 0xc8, 0xd3, 0x19, 0x8d, 0xaf,
0x90, 0xf5, 0x81, 0xb5, 0x91, 0xd5, 0x63, 0x79, 0xca
]
);
}
#[test]
fn test_wif() {
assert_eq!(
[0x11; 32].wif(true),
"KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp"
);
assert_eq!(
[0x11; 32].wif(false),
"5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh"
);
assert_eq!(
[0x69; 32].wif(true),
"KzkcmnPaJd7mqT47Rnk9XMGRfW2wfo7ar2M2o6Yoe6Rdgbg2bHM9"
);
assert_eq!(
[0x69; 32].wif(false),
"5JciBbkdYdjKKE9rwZ7c1XscwwcLBbv9aJyeZeWQi2gZnHeiX57"
);
assert_eq!(
[0xd0; 32].wif(true),
"L4DczGWRanBGXnun83Fs9HcPCaXXq7ngxZrBY13Phdsw36WU1rQA"
);
}
}