//! Bitcoin key pair. use hash::{H264, H520}; use network::Network; use secp256k1::key; use std::fmt; use {Address, Error, Private, Public, Secret, Type, SECP256K1}; pub struct KeyPair { private: Private, public: Public, } impl fmt::Debug for KeyPair { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(self.private.fmt(f)); writeln!(f, "public: {:?}", self.public) } } impl fmt::Display for KeyPair { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(writeln!(f, "private: {}", self.private)); writeln!(f, "public: {}", self.public) } } impl KeyPair { pub fn private(&self) -> &Private { &self.private } pub fn public(&self) -> &Public { &self.public } pub fn from_private(private: Private) -> Result { let context = &SECP256K1; let s: key::SecretKey = try!(key::SecretKey::from_slice(context, &*private.secret)); let pub_key = try!(key::PublicKey::from_secret_key(context, &s)); let serialized = pub_key.serialize_vec(context, private.compressed); let public = if private.compressed { let mut public = H264::default(); public.copy_from_slice(&serialized[0..33]); Public::Compressed(public) } else { let mut public = H520::default(); public.copy_from_slice(&serialized[0..65]); Public::Normal(public) }; let keypair = KeyPair { private: private, public: public, }; Ok(keypair) } pub fn from_keypair(sec: key::SecretKey, public: key::PublicKey, network: Network) -> Self { let context = &SECP256K1; let serialized = public.serialize_vec(context, false); let mut secret = Secret::default(); secret.copy_from_slice(&sec[0..32]); let mut public = H520::default(); public.copy_from_slice(&serialized[0..65]); KeyPair { private: Private { network: network, secret: secret, compressed: false, }, public: Public::Normal(public), } } pub fn address(&self) -> Address { Address { kind: Type::P2PKH, network: self.private.network, hash: self.public.address_hash(), } } } #[cfg(test)] mod tests { use super::KeyPair; use zebra_crypto::dhash256; use Public; /// Tests from: /// https://github.com/zcash/zcash/blob/66e39a0dd6ffd77bcbede1943195dd456f859cd6/src/test/key_tests.cpp#L25 const SECRET_1: &'static str = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; const SECRET_2: &'static str = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"; const SECRET_1C: &'static str = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; const SECRET_2C: &'static str = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"; const ADDRESS_1: &'static str = "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe"; const ADDRESS_2: &'static str = "t1Xxa5ZVPKvs9bGMn7aWTiHjyHvR31XkUst"; const ADDRESS_1C: &'static str = "t1ffus9J1vhxvFqLoExGBRPjE7BcJxiSCTC"; const ADDRESS_2C: &'static str = "t1VJL2dPUyXK7avDRGqhqQA5bw2eEMdhyg6"; const SIGN_1: &'static str = "304402205dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d022014ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6"; const SIGN_2: &'static str = "3044022052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd5022061d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d"; const SIGN_COMPACT_1: &'static str = "1c5dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d14ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6"; const SIGN_COMPACT_1C: &'static str = "205dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d14ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6"; const SIGN_COMPACT_2: &'static str = "1c52d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd561d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d"; const SIGN_COMPACT_2C: &'static str = "2052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd561d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d"; fn check_addresses(secret: &'static str, address: &'static str) -> bool { let kp = KeyPair::from_private(secret.into()).unwrap(); kp.address() == address.into() } fn check_compressed(secret: &'static str, compressed: bool) -> bool { let kp = KeyPair::from_private(secret.into()).unwrap(); kp.private().compressed == compressed } fn check_sign(secret: &'static str, raw_message: &[u8], signature: &'static str) -> bool { let message = dhash256(raw_message); let kp = KeyPair::from_private(secret.into()).unwrap(); kp.private().sign(&message).unwrap() == signature.into() } fn check_verify(secret: &'static str, raw_message: &[u8], signature: &'static str) -> bool { let message = dhash256(raw_message); let kp = KeyPair::from_private(secret.into()).unwrap(); kp.public().verify(&message, &signature.into()).unwrap() } fn check_sign_compact( secret: &'static str, raw_message: &[u8], signature: &'static str, ) -> bool { let message = dhash256(raw_message); let kp = KeyPair::from_private(secret.into()).unwrap(); kp.private().sign_compact(&message).unwrap() == signature.into() } fn check_recover_compact(secret: &'static str, raw_message: &[u8]) -> bool { let message = dhash256(raw_message); let kp = KeyPair::from_private(secret.into()).unwrap(); let signature = kp.private().sign_compact(&message).unwrap(); let recovered = Public::recover_compact(&message, &signature).unwrap(); kp.public() == &recovered } #[test] fn test_keypair_address() { assert!(check_addresses(SECRET_1, ADDRESS_1)); assert!(check_addresses(SECRET_2, ADDRESS_2)); assert!(check_addresses(SECRET_1C, ADDRESS_1C)); assert!(check_addresses(SECRET_2C, ADDRESS_2C)); } #[test] fn test_keypair_is_compressed() { assert!(check_compressed(SECRET_1, false)); assert!(check_compressed(SECRET_2, false)); assert!(check_compressed(SECRET_1C, true)); assert!(check_compressed(SECRET_2C, true)); } #[test] fn test_sign() { let message = b"Very deterministic message"; assert!(check_sign(SECRET_1, message, SIGN_1)); assert!(check_sign(SECRET_1C, message, SIGN_1)); assert!(check_sign(SECRET_2, message, SIGN_2)); assert!(check_sign(SECRET_2C, message, SIGN_2)); assert!(!check_sign(SECRET_2C, b"", SIGN_2)); } #[test] fn test_verify() { let message = b"Very deterministic message"; assert!(check_verify(SECRET_1, message, SIGN_1)); assert!(check_verify(SECRET_1C, message, SIGN_1)); assert!(check_verify(SECRET_2, message, SIGN_2)); assert!(check_verify(SECRET_2C, message, SIGN_2)); assert!(!check_verify(SECRET_2C, b"", SIGN_2)); } #[test] fn test_sign_compact() { let message = b"Very deterministic message"; assert!(check_sign_compact(SECRET_1, message, SIGN_COMPACT_1)); assert!(check_sign_compact(SECRET_1C, message, SIGN_COMPACT_1C)); assert!(check_sign_compact(SECRET_2, message, SIGN_COMPACT_2)); assert!(check_sign_compact(SECRET_2C, message, SIGN_COMPACT_2C)); assert!(!check_sign_compact(SECRET_2C, b"", SIGN_COMPACT_2C)); } #[test] fn test_recover_compact() { let message = b"Very deterministic message"; assert!(check_recover_compact(SECRET_1, message)); assert!(check_recover_compact(SECRET_1C, message)); assert!(check_recover_compact(SECRET_2, message)); assert!(check_recover_compact(SECRET_2C, message)); } }