From 59b5a73a553fe4d49d484196aa0bee805a2f030d Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Sat, 23 May 2020 23:20:19 +0100 Subject: [PATCH 1/2] Fix typo and minor formatting in address::Error --- src/util/address.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/util/address.rs b/src/util/address.rs index dcb418d5..d1418ed8 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -73,15 +73,12 @@ impl fmt::Display for Error { Error::Bech32(ref e) => write!(f, "bech32: {}", e), Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"), Error::InvalidWitnessVersion(v) => write!(f, "invalid witness script version: {}", v), - Error::InvalidWitnessProgramLength(l) => write!( - f, - "the witness program must be between 2 and 40 bytes in length: lengh={}", - l + Error::InvalidWitnessProgramLength(l) => write!(f, + "the witness program must be between 2 and 40 bytes in length: length={}", l, + ), + Error::InvalidSegwitV0ProgramLength(l) => write!(f, + "a v0 witness program must be either of length 20 or 32 bytes: length={}", l, ), - Error::InvalidSegwitV0ProgramLength(l) => write!( - f, - "a v0 witness program must be either of length 20 or 32 bytes: length={}", - l ), } } From ed9bf41ff5316517443fe99a706af0fc975f3658 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Sat, 23 May 2020 23:20:46 +0100 Subject: [PATCH 2/2] Don't allow uncompressed pks in witness addresses --- src/util/address.rs | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/util/address.rs b/src/util/address.rs index d1418ed8..97748ac0 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -64,6 +64,8 @@ pub enum Error { InvalidWitnessProgramLength(usize), /// A v0 witness program must be either of length 20 or 32. InvalidSegwitV0ProgramLength(usize), + /// An uncompressed pubkey was used where it is not allowed. + UncompressedPubkey, } impl fmt::Display for Error { @@ -79,6 +81,8 @@ impl fmt::Display for Error { Error::InvalidSegwitV0ProgramLength(l) => write!(f, "a v0 witness program must be either of length 20 or 32 bytes: length={}", l, ), + Error::UncompressedPubkey => write!(f, + "an uncompressed pubkey was used where it is not allowed", ), } } @@ -257,22 +261,34 @@ impl Address { /// Create a witness pay to public key address from a public key /// This is the native segwit address type for an output redeemable with a single signature - pub fn p2wpkh(pk: &key::PublicKey, network: Network) -> Address { + /// + /// Will only return an Error when an uncompressed public key is provided. + pub fn p2wpkh(pk: &key::PublicKey, network: Network) -> Result { + if !pk.compressed { + return Err(Error::UncompressedPubkey); + } + let mut hash_engine = WPubkeyHash::engine(); pk.write_into(&mut hash_engine); - Address { + Ok(Address { network: network, payload: Payload::WitnessProgram { version: bech32::u5::try_from_u8(0).expect("0<32"), program: WPubkeyHash::from_engine(hash_engine)[..].to_vec(), }, - } + }) } /// Create a pay to script address that embeds a witness pay to public key /// This is a segwit address type that looks familiar (as p2sh) to legacy clients - pub fn p2shwpkh(pk: &key::PublicKey, network: Network) -> Address { + /// + /// Will only return an Error when an uncompressed public key is provided. + pub fn p2shwpkh(pk: &key::PublicKey, network: Network) -> Result { + if !pk.compressed { + return Err(Error::UncompressedPubkey); + } + let mut hash_engine = WPubkeyHash::engine(); pk.write_into(&mut hash_engine); @@ -280,10 +296,10 @@ impl Address { .push_int(0) .push_slice(&WPubkeyHash::from_engine(hash_engine)[..]); - Address { + Ok(Address { network: network, payload: Payload::ScriptHash(ScriptHash::hash(builder.into_script().as_bytes())), - } + }) } /// Create a witness pay to script hash address @@ -587,11 +603,15 @@ mod tests { #[test] fn test_p2wpkh() { // stolen from Bitcoin transaction: b3c8c2b6cfc335abbcb2c7823a8453f55d64b2b5125a9a61e8737230cdb8ce20 - let key = hex_key!("033bc8c83c52df5712229a2f72206d90192366c36428cb0c12b6af98324d97bfbc"); - let addr = Address::p2wpkh(&key, Bitcoin); + let mut key = hex_key!("033bc8c83c52df5712229a2f72206d90192366c36428cb0c12b6af98324d97bfbc"); + let addr = Address::p2wpkh(&key, Bitcoin).unwrap(); assert_eq!(&addr.to_string(), "bc1qvzvkjn4q3nszqxrv3nraga2r822xjty3ykvkuw"); assert_eq!(addr.address_type(), Some(AddressType::P2wpkh)); roundtrips(&addr); + + // Test uncompressed pubkey + key.compressed = false; + assert_eq!(Address::p2wpkh(&key, Bitcoin), Err(Error::UncompressedPubkey)); } #[test] @@ -610,11 +630,15 @@ mod tests { #[test] fn test_p2shwpkh() { // stolen from Bitcoin transaction: ad3fd9c6b52e752ba21425435ff3dd361d6ac271531fc1d2144843a9f550ad01 - let key = hex_key!("026c468be64d22761c30cd2f12cbc7de255d592d7904b1bab07236897cc4c2e766"); - let addr = Address::p2shwpkh(&key, Bitcoin); + let mut key = hex_key!("026c468be64d22761c30cd2f12cbc7de255d592d7904b1bab07236897cc4c2e766"); + let addr = Address::p2shwpkh(&key, Bitcoin).unwrap(); assert_eq!(&addr.to_string(), "3QBRmWNqqBGme9er7fMkGqtZtp4gjMFxhE"); assert_eq!(addr.address_type(), Some(AddressType::P2sh)); roundtrips(&addr); + + // Test uncompressed pubkey + key.compressed = false; + assert_eq!(Address::p2wpkh(&key, Bitcoin), Err(Error::UncompressedPubkey)); } #[test]