// Rust Bitcoin Library // Written in 2014 by // Andrew Poelstra // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to // the public domain worldwide. This software is distributed without // any warranty. // // You should have received a copy of the CC0 Public Domain Dedication // along with this software. // If not, see . // //! Private key //! //! A private key represents the secret data associated with its proposed use //! use std::fmt::{self, Write}; use std::str::FromStr; use secp256k1::{self, Secp256k1}; use secp256k1::key::{PublicKey, SecretKey}; use consensus::encode; use network::constants::Network; use util::base58; #[derive(Clone, PartialEq, Eq)] /// A Bitcoin ECDSA private key pub struct PrivateKey { /// Whether this private key represents a compressed address pub compressed: bool, /// The network on which this key should be used pub network: Network, /// The actual ECDSA key pub key: SecretKey } impl PrivateKey { /// Computes the public key as supposed to be used with this secret pub fn public_key(&self, secp: &Secp256k1) -> PublicKey { PublicKey::from_secret_key(secp, &self.key) } /// Format the private key to WIF format. pub fn fmt_wif(&self, fmt: &mut fmt::Write) -> fmt::Result { let mut ret = [0; 34]; ret[0] = match self.network { Network::Bitcoin => 128, Network::Testnet | Network::Regtest => 239, }; ret[1..33].copy_from_slice(&self.key[..]); let privkey = if self.compressed { ret[33] = 1; base58::check_encode_slice(&ret[..]) } else { base58::check_encode_slice(&ret[..33]) }; fmt.write_str(&privkey) } /// Get WIF encoding of this private key. pub fn to_wif(&self) -> String { let mut buf = String::new(); buf.write_fmt(format_args!("{}", self)).unwrap(); buf.shrink_to_fit(); buf } /// Parse WIF encoded private key. pub fn from_wif(wif: &str) -> Result { let data = base58::from_check(wif)?; let compressed = match data.len() { 33 => false, 34 => true, _ => { return Err(encode::Error::Base58(base58::Error::InvalidLength(data.len()))); } }; let network = match data[0] { 128 => Network::Bitcoin, 239 => Network::Testnet, x => { return Err(encode::Error::Base58(base58::Error::InvalidVersion(vec![x]))); } }; let key = SecretKey::from_slice(&data[1..33]) .map_err(|_| base58::Error::Other("Secret key out of range".to_owned()))?; Ok(PrivateKey { compressed: compressed, network: network, key: key }) } } impl fmt::Display for PrivateKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_wif(f) } } impl fmt::Debug for PrivateKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[private key data]") } } impl FromStr for PrivateKey { type Err = encode::Error; fn from_str(s: &str) -> Result { PrivateKey::from_wif(s) } } #[cfg(test)] mod tests { use super::PrivateKey; use secp256k1::Secp256k1; use std::str::FromStr; use network::constants::Network::Testnet; use network::constants::Network::Bitcoin; use util::address::Address; #[test] fn test_key_derivation() { // testnet compressed let sk = PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap(); assert_eq!(sk.network, Testnet); assert_eq!(sk.compressed, true); assert_eq!(&sk.to_wif(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"); let secp = Secp256k1::new(); let pk = Address::p2pkh(&sk.public_key(&secp), sk.network); assert_eq!(&pk.to_string(), "mqwpxxvfv3QbM8PU8uBx2jaNt9btQqvQNx"); // test string conversion assert_eq!(&sk.to_string(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"); let sk_str = PrivateKey::from_str("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap(); assert_eq!(&sk.to_wif(), &sk_str.to_wif()); // mainnet uncompressed let sk = PrivateKey::from_wif("5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3").unwrap(); assert_eq!(sk.network, Bitcoin); assert_eq!(sk.compressed, false); assert_eq!(&sk.to_wif(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3"); let secp = Secp256k1::new(); let pk = Address::p2upkh(&sk.public_key(&secp), sk.network); assert_eq!(&pk.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8"); } }