2016-11-08 12:01:25 -08:00
|
|
|
//! Bitcoin key pair.
|
2016-08-18 03:56:18 -07:00
|
|
|
|
2016-09-19 02:58:53 -07:00
|
|
|
use hash::{H264, H520};
|
2016-09-19 06:39:57 -07:00
|
|
|
use network::Network;
|
2019-06-28 14:27:46 -07:00
|
|
|
use secp256k1::key;
|
|
|
|
use std::fmt;
|
|
|
|
use {Address, Error, Private, Public, Secret, Type, SECP256K1};
|
2016-08-16 09:00:26 -07:00
|
|
|
|
|
|
|
pub struct KeyPair {
|
2019-06-28 14:27:46 -07:00
|
|
|
private: Private,
|
|
|
|
public: Public,
|
2016-08-16 09:00:26 -07:00
|
|
|
}
|
|
|
|
|
2016-08-20 09:06:04 -07:00
|
|
|
impl fmt::Debug for KeyPair {
|
2019-06-28 14:27:46 -07:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
try!(self.private.fmt(f));
|
|
|
|
writeln!(f, "public: {:?}", self.public)
|
|
|
|
}
|
2016-08-20 09:06:04 -07:00
|
|
|
}
|
|
|
|
|
2016-08-16 09:00:26 -07:00
|
|
|
impl fmt::Display for KeyPair {
|
2019-06-28 14:27:46 -07:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
try!(writeln!(f, "private: {}", self.private));
|
|
|
|
writeln!(f, "public: {}", self.public)
|
|
|
|
}
|
2016-08-16 09:00:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl KeyPair {
|
2019-06-28 14:27:46 -07:00
|
|
|
pub fn private(&self) -> &Private {
|
|
|
|
&self.private
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn public(&self) -> &Public {
|
|
|
|
&self.public
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_private(private: Private) -> Result<KeyPair, Error> {
|
|
|
|
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(),
|
|
|
|
}
|
|
|
|
}
|
2016-08-17 06:00:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2019-06-28 14:27:46 -07:00
|
|
|
use super::KeyPair;
|
Prefix workspace crates with zebra- (#70)
* Update license and author metadata in workspace crates.
- ensure that the license field is set to GPL-3 for all GPL-3 licensed crates;
- ensure that the author field is set to "Zcash Foundation", responsible for maintenance;
- preserve the original authorship info in AUTHORS.md for human-readable history.
Updating the author field ensures that all of the machine systems that read
crate metadata list the ZF organization, not any single individual, as the
maintainer of the crate.
* Prefix all internal crate names with zebra-.
This does not move the directories containing these crates to also have zebra-
prefixes (for instance, zebra-chain instead of chain). I think that this would
be preferable, but because it's a `git mv`, it will be simple to do later and
leaving it out of this change makes it easier to see the renaming of all of the
internal modules.
* Remove git dependency from eth-secp256k1
* Avoid an error seemingly related to Deref coercions.
This code caused an overflow while evaluating type constraints. As best as I
can determine, the cause of the problem was something like so: the Rust
implementation of the Bitcoin-specific hash function used in the Bloom filter
doesn't operate on byte slices, but only on a `&mut R where R: Read`, so to
hash a byte slice, you need to create a mutable copy of the input slice which
can be consumed as a `Read` implementation by the hash function; the previous
version of this code created a slice copy using a `Deref` coercion instead of
`.clone()`, and when a tokio update added new trait impls, the type inference
for the `Deref` coercion exploded (somehow -- I'm not sure about the last
part?).
This commit avoids the problem by manually cloning the input slice.
2019-07-02 12:07:06 -07:00
|
|
|
use zebra_crypto::dhash256;
|
2019-06-28 14:27:46 -07:00
|
|
|
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));
|
|
|
|
}
|
2016-08-16 09:00:26 -07:00
|
|
|
}
|