diff --git a/src/burnchains/bitcoin/keys.rs b/src/burnchains/bitcoin/keys.rs index 86f4538ec..dd9a56708 100644 --- a/src/burnchains/bitcoin/keys.rs +++ b/src/burnchains/bitcoin/keys.rs @@ -17,219 +17,6 @@ along with Blockstack. If not, see . */ -use secp256k1; -use secp256k1::Secp256k1; -use secp256k1::constants as Secp256k1Constants; -use secp256k1::PublicKey as Secp256k1PublicKey; -use secp256k1::Message as Secp256k1Message; -use secp256k1::Signature as Secp256k1Signature; -use secp256k1::Error as Secp256k1Error; - -use burnchains::PublicKey; -use util::hash::{hex_bytes, to_hex}; - -use serde::de::Deserialize; -use serde::de::Error as de_Error; - -// per-thread Secp256k1 context -thread_local!(static _secp256k1: Secp256k1 = Secp256k1::new()); - -#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] -pub struct BitcoinPublicKey { - // serde is broken for secp256k1, so do it ourselves - #[serde(serialize_with = "serialize_secp256k1_pubkey", deserialize_with = "deserialize_secp256k1_pubkey")] - key: Secp256k1PublicKey, - compressed: bool -} - -impl BitcoinPublicKey { - pub fn from_hex(hex_string: &str) -> Result { - hex_bytes(hex_string) - .and_then(|vec_bytes| BitcoinPublicKey::from_slice(&vec_bytes[..])) - .map_err(|_e| "Invalid hex string") - } - - pub fn from_slice(data: &[u8]) -> Result { - _secp256k1.with(|ctx| { - match Secp256k1PublicKey::from_slice(&ctx, data) { - Ok(pubkey_res) => - Ok(BitcoinPublicKey { - key: pubkey_res, - compressed: data.len() == Secp256k1Constants::PUBLIC_KEY_SIZE - }), - Err(_e) => Err("Invalid public key: failed to load") - } - }) - } -} - -impl PublicKey for BitcoinPublicKey { - fn to_bytes(&self) -> Vec { - if self.compressed { - self.key.serialize().to_vec() - } - else { - self.key.serialize_uncompressed().to_vec() - } - } - - fn verify(&self, data_hash: &[u8], sig_der: &[u8]) -> Result { - _secp256k1.with(|ctx| { - let msg = Secp256k1Message::from_slice(data_hash) - .map_err(|_e| "Invalid message: failed to decode data hash: must be a 32-byte hash")?; - - let sig = Secp256k1Signature::from_der(ctx, sig_der) - .map_err(|_e| "Invalid signature: failed to decode signature: must be DER-encoded")?; - - let v = ctx.verify(&msg, &sig, &self.key); - return match v { - Ok(()) => Ok(true), - Err(e) => - match e { - Secp256k1Error::IncorrectSignature => Ok(false), - _ => Err("Failed to process public key or signature") - } - }; - }) - } -} - -fn serialize_secp256k1_pubkey(pubk: &Secp256k1PublicKey, s: S) -> Result { - let key_hex = to_hex(&pubk.serialize().to_vec()); - s.serialize_str(&key_hex.as_str()) -} - -fn deserialize_secp256k1_pubkey<'de, D: serde::Deserializer<'de>>(d: D) -> Result { - let key_hex = String::deserialize(d)?; - let key_bytes = hex_bytes(&key_hex) - .map_err(de_Error::custom)?; - - _secp256k1.with(|ctx| { - Secp256k1PublicKey::from_slice(&ctx, &key_bytes[..]) - .map_err(de_Error::custom) - }) -} - -#[cfg(test)] -mod tests { - use util::hash::hex_bytes; - - use secp256k1; - use secp256k1::Secp256k1; - use secp256k1::PublicKey as Secp256k1PublicKey; - use burnchains::PublicKey; - use burnchains::bitcoin::keys::BitcoinPublicKey; - - use util::log; - - struct KeyFixture { - input: I, - result: R - } - - struct VerifyFixture { - public_key: &'static str, - data: &'static str, - signature: &'static str, - result: R - } - - #[test] - fn test_parse_serialize() { - let ctx : Secp256k1 = Secp256k1::new(); - let fixtures = vec![ - KeyFixture { - input: "0233d78f74de8ef4a1de815b6d5c5c129c073786305c0826c499b1811c9a12cee5", - result: Some(BitcoinPublicKey { - key: Secp256k1PublicKey::from_slice(&ctx, &hex_bytes("0233d78f74de8ef4a1de815b6d5c5c129c073786305c0826c499b1811c9a12cee5").unwrap()[..]).unwrap(), - compressed: true - }) - }, - KeyFixture { - input: "044a83ad59dbae1e2335f488dbba5f8604d00f612a43ebaae784b5b7124cc38c3aaf509362787e1a8e25131724d57fec81b87889aabb4edf7bd89f5c4daa4f8aa7", - result: Some(BitcoinPublicKey { - key: Secp256k1PublicKey::from_slice(&ctx, &hex_bytes("044a83ad59dbae1e2335f488dbba5f8604d00f612a43ebaae784b5b7124cc38c3aaf509362787e1a8e25131724d57fec81b87889aabb4edf7bd89f5c4daa4f8aa7").unwrap()[..]).unwrap(), - compressed: false - }) - }, - KeyFixture { - input: "0233d78f74de8ef4a1de815b6d5c5c129c073786305c0826c499b1811c9a12ce", - result: None, - }, - KeyFixture { - input: "044a83ad59dbae1e2335f488dbba5f8604d00f612a43ebaae784b5b7124cc38c3aaf509362787e1a8e25131724d57fec81b87889aabb4edf7bd89f5c4daa4f8a", - result: None, - } - ]; - - for fixture in fixtures { - let key_res = BitcoinPublicKey::from_hex(fixture.input); - match (key_res, fixture.result) { - (Ok(key), Some(key_result)) => { - assert_eq!(key, key_result); - - let key_from_slice = BitcoinPublicKey::from_slice(&hex_bytes(fixture.input).unwrap()[..]).unwrap(); - assert_eq!(key_from_slice, key_result); - - let key_bytes = key.to_bytes(); - assert_eq!(key_bytes, hex_bytes(fixture.input).unwrap()); - }, - (Err(_e), None) => {}, - (_, _) => { - // either got a key when we didn't expect one, or didn't get a key when we did - // expect one. - assert!(false); - } - } - } - } - - #[test] - fn test_verify() { - let _ctx : Secp256k1 = Secp256k1::new(); - let fixtures : Vec>> = vec![ - VerifyFixture { - public_key: "034c35b09b758678165d6ed84a50b329900c99986cf8e9a358ceae0d03af91f5b6", - signature: "3045022100853ae0bca72d59aaa335ff967f062952348baf7cc03cd1cb60db21eda6c1fecc0220551afcbcfb81a3f2adba18608d474bf296cccc82d8fca9f2bbd1fea96b4b71dc", - data: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9", // sha256 hash of "hello world" - result: Ok(true) - }, - VerifyFixture { - public_key: "034c35b09b758678165d6ed84a50b329900c99986cf8e9a358ceae0d03af91f5b6", - signature: "3045022100853ae0bca72d59aaa335ff967f062952348baf7cc03cd1cb60db21eda6c1fecc0220551afcbcfb81a3f2adba18608d474bf296cccc82d8fca9f2bbd1fea96b4b71dc", - data: "ca3704aa0b06f5954c79ee837faa152d84d6b2d42838f0637a15eda8337dbdce", // sha256 hash of "nope" - result: Ok(false) - }, - VerifyFixture { - public_key: "034c35b09b758678165d6ed84a50b329900c99986cf8e9a358ceae0d03af91f5b6", // wrong key - signature: "3045022100be57031bf2c095945ba2876e97b3f86ee051643a29b908f22ed45ccf58620103022061e056e5f48c5a51c66604a1ca28e4bfaabab1478424c9bbb396cc6afe5c222e", - data: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9", // sha256 hash of "hello world" - result: Ok(false) - }, - VerifyFixture { - public_key: "02ade4d69dc5f11ab372e10c2fa5ea6a2c6c118dc4ae71cbdf1001292411a05457", - signature: "3045022100853ae0bca72d59aaa335ff967f062952348baf7cc03cd1cb60db21eda6c1fecc0220551afcbcfb81a3f2adba18608d474bf296cccc82d8fca9f2bbd1fea96b4b71dc", // wrong signature - data: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9", // sha256 hash of "hello world" - result: Ok(false) - } - ]; - - for fixture in fixtures { - let key = BitcoinPublicKey::from_hex(fixture.public_key).unwrap(); - let ver_res = key.verify(&hex_bytes(fixture.data).unwrap(), &hex_bytes(fixture.signature).unwrap()); - match (ver_res, fixture.result) { - (Ok(true), Ok(true)) => {}, - (Ok(false), Ok(false)) => {}, - (Err(e1), Err(e2)) => assert_eq!(e1, e2), - (Err(e1), _) => { - test_debug!("Failed to verify signature: {}", e1); - assert!(false); - } - (_, _) => { - assert!(false); - } - } - } - } -} +use util::secp256k1; +pub type BitcoinPublicKey = secp256k1::Secp256k1PublicKey;