From 8ed0d5aad9bd7b89c622c5c47c58dbe93363f210 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 5 Jan 2021 09:32:33 -0800 Subject: [PATCH] refactor: use k256 and simplify bip32 --- bip32-old/Cargo.toml | 45 ++ {bip32 => bip32-old}/README.md | 0 {bip32 => bip32-old}/benches/bench.rs | 0 {bip32 => bip32-old}/src/curve/libsecp.rs | 0 {bip32 => bip32-old}/src/curve/mod.rs | 0 {bip32 => bip32-old}/src/curve/model.rs | 0 {bip32 => bip32-old}/src/curve/rust_secp.rs | 0 bip32-old/src/defaults.rs | 235 ++++++ bip32-old/src/derived.rs | 669 ++++++++++++++++++ bip32-old/src/enc.rs | 498 +++++++++++++ {bip32 => bip32-old}/src/keys.rs | 0 bip32-old/src/lib.rs | 218 ++++++ bip32-old/src/macros.rs | 109 +++ {bip32 => bip32-old}/src/model.rs | 0 bip32-old/src/path.rs | 391 ++++++++++ bip32-old/src/prelude.rs | 10 + bip32-old/src/primitives.rs | 102 +++ bip32-old/src/xkeys.rs | 487 +++++++++++++ bip32/Cargo.toml | 18 +- bip32/src/defaults.rs | 192 +---- bip32/src/derived.rs | 747 +++++--------------- bip32/src/enc.rs | 287 +++----- bip32/src/lib.rs | 124 +--- bip32/src/macros.rs | 152 ++-- bip32/src/prelude.rs | 22 +- bip32/src/primitives.rs | 12 - bip32/src/xkeys.rs | 487 +++++++------ bitcoins/src/types/script.rs | 18 +- core/src/hashes/mod.rs | 98 ++- core/src/prelude.rs | 4 +- handshakes/src/types/lockingscript.rs | 15 +- psbt/src/global.rs | 9 +- psbt/src/input.rs | 27 +- psbt/src/lib.rs | 4 +- psbt/src/roles/bip32_signer.rs | 34 +- psbt/src/roles/finalizer.rs | 16 +- psbt/src/schema.rs | 43 +- 37 files changed, 3567 insertions(+), 1506 deletions(-) create mode 100644 bip32-old/Cargo.toml rename {bip32 => bip32-old}/README.md (100%) rename {bip32 => bip32-old}/benches/bench.rs (100%) rename {bip32 => bip32-old}/src/curve/libsecp.rs (100%) rename {bip32 => bip32-old}/src/curve/mod.rs (100%) rename {bip32 => bip32-old}/src/curve/model.rs (100%) rename {bip32 => bip32-old}/src/curve/rust_secp.rs (100%) create mode 100644 bip32-old/src/defaults.rs create mode 100644 bip32-old/src/derived.rs create mode 100644 bip32-old/src/enc.rs rename {bip32 => bip32-old}/src/keys.rs (100%) create mode 100644 bip32-old/src/lib.rs create mode 100644 bip32-old/src/macros.rs rename {bip32 => bip32-old}/src/model.rs (100%) create mode 100644 bip32-old/src/path.rs create mode 100644 bip32-old/src/prelude.rs create mode 100644 bip32-old/src/primitives.rs create mode 100644 bip32-old/src/xkeys.rs diff --git a/bip32-old/Cargo.toml b/bip32-old/Cargo.toml new file mode 100644 index 00000000..023e5242 --- /dev/null +++ b/bip32-old/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "coins-bip32" +version = "0.1.0-beta.0" +authors = ["James Prestwich "] +edition = "2018" +description = "Bip32 (and related BIPs) in Rust" +repository = "https://github.com/summa-tx/bitcoins-rs" +license = "MIT OR Apache-2.0" + +[dependencies] +thiserror = "1.0" +hmac = "0.7.1" +sha2 = "0.8.0" +bs58 = "0.3.0" +lazy_static = "1.4.0" +coins-core = { path = "../core"} +serde = "1.0.105" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.secp256k1] +version = "0.17.2" +features = ["recovery"] + +[target.'cfg(target_arch = "wasm32")'.dependencies.libsecp256k1] +git = "https://github.com/paritytech/libsecp256k1.git" +default-features = false +features = ["std", "hmac"] + +[target.'cfg(target_arch = "wasm32")'.dependencies.libsecp256k1-core] +git = "https://github.com/paritytech/libsecp256k1.git" +default-features = false +features = ["std"] + +[dev-dependencies] +hex = "0.4.2" +criterion = "0.3.1" + +[features] +default = ["mainnet"] +rust-secp-static-context = ["libsecp256k1/static-context"] +mainnet = [] +testnet = [] + +[[bench]] +name = "bench" +harness = false diff --git a/bip32/README.md b/bip32-old/README.md similarity index 100% rename from bip32/README.md rename to bip32-old/README.md diff --git a/bip32/benches/bench.rs b/bip32-old/benches/bench.rs similarity index 100% rename from bip32/benches/bench.rs rename to bip32-old/benches/bench.rs diff --git a/bip32/src/curve/libsecp.rs b/bip32-old/src/curve/libsecp.rs similarity index 100% rename from bip32/src/curve/libsecp.rs rename to bip32-old/src/curve/libsecp.rs diff --git a/bip32/src/curve/mod.rs b/bip32-old/src/curve/mod.rs similarity index 100% rename from bip32/src/curve/mod.rs rename to bip32-old/src/curve/mod.rs diff --git a/bip32/src/curve/model.rs b/bip32-old/src/curve/model.rs similarity index 100% rename from bip32/src/curve/model.rs rename to bip32-old/src/curve/model.rs diff --git a/bip32/src/curve/rust_secp.rs b/bip32-old/src/curve/rust_secp.rs similarity index 100% rename from bip32/src/curve/rust_secp.rs rename to bip32-old/src/curve/rust_secp.rs diff --git a/bip32-old/src/defaults.rs b/bip32-old/src/defaults.rs new file mode 100644 index 00000000..10b5288c --- /dev/null +++ b/bip32-old/src/defaults.rs @@ -0,0 +1,235 @@ +use serde::{de::Visitor, ser::SerializeStruct}; + +use crate::enc::XKeyEncoder; + +/// The default encoder, selected by feature flag +#[cfg(feature = "mainnet")] +pub type Encoder = crate::enc::MainnetEncoder; + +/// The default encoder, selected by feature flag +#[cfg(feature = "testnet")] +pub type Encoder = crate::enc::TestnetEncoder; + +impl std::str::FromStr for crate::XPriv { + type Err = crate::Bip32Error; + + fn from_str(s: &str) -> Result { + Encoder::xpriv_from_base58(s, Some(&crate::curve::BACKEND)) + } +} + +impl std::str::FromStr for crate::XPub { + type Err = crate::Bip32Error; + + fn from_str(s: &str) -> Result { + Encoder::xpub_from_base58(s, Some(&crate::curve::BACKEND)) + } +} + +impl serde::Serialize for crate::XPub { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let encoded = + Encoder::xpub_to_base58(self).map_err(|e| serde::ser::Error::custom(e.to_string()))?; + serializer.serialize_str(&encoded) + } +} + +impl<'de> serde::Deserialize<'de> for crate::XPub { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s: &str = serde::Deserialize::deserialize(deserializer)?; + Encoder::xpub_from_base58(s, Some(crate::Secp256k1::static_ref())) + .map_err(|e| serde::de::Error::custom(e.to_string())) + } +} + +impl serde::Serialize for crate::XPriv { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let encoded = + Encoder::xpriv_to_base58(self).map_err(|e| serde::ser::Error::custom(e.to_string()))?; + serializer.serialize_str(&encoded) + } +} + +impl<'de> serde::Deserialize<'de> for crate::XPriv { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s: &str = serde::Deserialize::deserialize(deserializer)?; + Encoder::xpriv_from_base58(s, Some(crate::Secp256k1::static_ref())) + .map_err(|e| serde::de::Error::custom(e.to_string())) + } +} + +impl serde::Serialize for crate::DerivedXPriv { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("DerivedXPriv", 2)?; + state.serialize_field("derivation", &self.derivation)?; + state.serialize_field("xpriv", &self.xpriv)?; + state.end() + } +} + +struct DerivedXPrivVisitor; + +impl<'de> Visitor<'de> for DerivedXPrivVisitor { + type Value = crate::DerivedXPriv; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct DerivedXPriv") + } + + fn visit_seq(self, mut seq: V) -> Result + where + V: serde::de::SeqAccess<'de>, + { + let xpriv = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + let derivation = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?; + Ok(crate::DerivedXPriv { xpriv, derivation }) + } + + fn visit_map(self, mut map: V) -> Result + where + V: serde::de::MapAccess<'de>, + { + let mut xpriv = None; + let mut derivation = None; + + #[derive(serde::Deserialize)] + #[serde(field_identifier, rename_all = "lowercase")] + enum Field { + XPriv, + Derivation, + } + + while let Some(key) = map.next_key()? { + match key { + Field::XPriv => { + if xpriv.is_some() { + return Err(serde::de::Error::duplicate_field("xpriv")); + } + xpriv = Some(map.next_value()?); + } + Field::Derivation => { + if derivation.is_some() { + return Err(serde::de::Error::duplicate_field("derivation")); + } + derivation = Some(map.next_value()?); + } + } + } + + let xpriv = xpriv.ok_or_else(|| serde::de::Error::missing_field("xpriv"))?; + let derivation = derivation.ok_or_else(|| serde::de::Error::missing_field("derivation"))?; + + Ok(crate::DerivedXPriv { xpriv, derivation }) + } +} + +impl<'de> serde::Deserialize<'de> for crate::DerivedXPriv { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &["xpriv", "derivation"]; + deserializer.deserialize_struct("Duration", FIELDS, DerivedXPrivVisitor) + } +} + +impl serde::Serialize for crate::DerivedXPub { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("DerivedXPub", 2)?; + state.serialize_field("derivation", &self.derivation)?; + state.serialize_field("xpub", &self.xpub)?; + state.end() + } +} + +struct DerivedXPubVisitor; + +impl<'de> Visitor<'de> for DerivedXPubVisitor { + type Value = crate::DerivedXPub; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct DerivedXPub") + } + + fn visit_seq(self, mut seq: V) -> Result + where + V: serde::de::SeqAccess<'de>, + { + let xpub = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; + let derivation = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?; + Ok(crate::DerivedXPub { xpub, derivation }) + } + + fn visit_map(self, mut map: V) -> Result + where + V: serde::de::MapAccess<'de>, + { + let mut xpub = None; + let mut derivation = None; + + #[derive(serde::Deserialize)] + #[serde(field_identifier, rename_all = "lowercase")] + enum Field { + XPub, + Derivation, + } + + while let Some(key) = map.next_key()? { + match key { + Field::XPub => { + if xpub.is_some() { + return Err(serde::de::Error::duplicate_field("xpub")); + } + xpub = Some(map.next_value()?); + } + Field::Derivation => { + if derivation.is_some() { + return Err(serde::de::Error::duplicate_field("derivation")); + } + derivation = Some(map.next_value()?); + } + } + } + + let xpub = xpub.ok_or_else(|| serde::de::Error::missing_field("xpub"))?; + let derivation = derivation.ok_or_else(|| serde::de::Error::missing_field("derivation"))?; + + Ok(crate::DerivedXPub { xpub, derivation }) + } +} + +impl<'de> serde::Deserialize<'de> for crate::DerivedXPub { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &["xpub", "derivation"]; + deserializer.deserialize_struct("Duration", FIELDS, DerivedXPubVisitor) + } +} diff --git a/bip32-old/src/derived.rs b/bip32-old/src/derived.rs new file mode 100644 index 00000000..d1e01aac --- /dev/null +++ b/bip32-old/src/derived.rs @@ -0,0 +1,669 @@ +use crate::{ + curve::model::Secp256k1Backend, + keys::{GenericPrivkey, GenericPubkey}, + model::*, + path::KeyDerivation, + primitives::Hint, + xkeys::{GenericXPriv, GenericXPub, SEED}, + Bip32Error, +}; + +/// A GenericDerivedPrivkey using the compiled-in default backend, coupled with its (purported) +/// derivation path. +/// +/// For interface documentation see the page for +/// [GenericDerivedPrivkey](struct.GenericDerivedPrivkey.html). +pub type DerivedPrivkey = GenericDerivedPrivkey<'static, crate::curve::Secp256k1<'static>>; + +/// A GenericDerivedPubkey using the compiled-in default backend, coupled with its (purported) +/// derivation path. +/// +/// For interface documentation see the page for +/// [GenericDerivedPubkey](struct.GenericDerivedPubkey.html). +pub type DerivedPubkey = GenericDerivedPubkey<'static, crate::curve::Secp256k1<'static>>; + +/// A GenericDerivedXPriv using the compiled-in default backend, coupled with its (purported) +/// derivation path. +/// +/// For interface documentation see the page for +/// [GenericDerivedXPriv](struct.GenericDerivedXPriv.html). +pub type DerivedXPriv = GenericDerivedXPriv<'static, crate::curve::Secp256k1<'static>>; + +/// A GenericDerivedXPub using the compiled-in default backend, coupled with its (purported) +/// derivation path. +/// +/// For interface documentation see the page for +/// [GenericDerivedXPub](struct.GenericDerivedXPub.html). +pub type DerivedXPub = GenericDerivedXPub<'static, crate::curve::Secp256k1<'static>>; + +make_derived_key!( + /// A `Privkey` coupled with its (purported) derivation path. Generally this struct + /// should be used over Privkey wherever possible, in order to preserve information ancestry + /// relationship information. + /// + /// Warning: derivation paths from untrusted sources may be faulty. Make sure to check + /// ancestry using the `DerivedKey` trait methods. + GenericPrivkey, + GenericDerivedPrivkey.privkey +); +inherit_has_privkey!(GenericDerivedPrivkey.privkey); +inherit_backend!(GenericDerivedPrivkey.privkey); + +impl<'a, T: Secp256k1Backend> SigningKey<'a, T> for GenericDerivedPrivkey<'a, T> { + type VerifyingKey = GenericDerivedPubkey<'a, T>; + + fn derive_verifying_key(&self) -> Result { + Ok(GenericDerivedPubkey { + pubkey: self.privkey.derive_verifying_key()?, + derivation: self.derivation.clone(), + }) + } +} + +make_derived_key!( + /// A `GenericPubkey` coupled with its (purported) derivation path. Generally this struct + /// should be used over Pubkey wherever possible, in order to preserve information ancestry + /// relationship information. + /// + /// Warning: derivation paths from untrusted sources may be faulty. Make sure to check + /// ancestry using the `DerivedKey` trait methods. + GenericPubkey, + GenericDerivedPubkey.pubkey +); +inherit_has_pubkey!(GenericDerivedPubkey.pubkey); +inherit_backend!(GenericDerivedPubkey.pubkey); + +impl<'a, T: Secp256k1Backend> VerifyingKey<'a, T> for GenericDerivedPubkey<'a, T> { + type SigningKey = GenericDerivedPrivkey<'a, T>; +} + +make_derived_key!( + /// A `GenericXPriv` coupled with its (purported) derivation path. Generally this struct + /// should be used over XPriv wherever possible, in order to preserve information ancestry + /// relationship information. + /// + /// Warning: derivation paths from untrusted sources may be faulty. Make sure to check + /// ancestry using the `DerivedKey` trait methods. + GenericXPriv, + GenericDerivedXPriv.xpriv +); +inherit_has_privkey!(GenericDerivedXPriv.xpriv); +inherit_backend!(GenericDerivedXPriv.xpriv); +inherit_has_xkeyinfo!(GenericDerivedXPriv.xpriv); + +impl DerivedXPriv { + /// Generate a customized master node using the static backend + pub fn master_node( + hmac_key: &[u8], + data: &[u8], + hint: Option, + ) -> Result { + Self::custom_master_node(hmac_key, data, hint, crate::curve::Secp256k1::static_ref()) + } + + /// Generate a master node from some seed data. Uses the BIP32-standard hmac key. + /// + /// + /// # Important: + /// + /// Use a seed of AT LEAST 128 bits. + pub fn root_from_seed(data: &[u8], hint: Option) -> Result { + Self::custom_root_from_seed(data, hint, crate::curve::Secp256k1::static_ref()) + } +} + +impl<'a, T: Secp256k1Backend> GenericDerivedXPriv<'a, T> { + /// Instantiate a master node using a custom HMAC key. + pub fn custom_master_node( + hmac_key: &[u8], + data: &[u8], + hint: Option, + backend: &'a T, + ) -> Result, Bip32Error> { + let xpriv = GenericXPriv::custom_master_node(hmac_key, data, hint, backend)?; + + let derivation = KeyDerivation { + root: xpriv.derive_fingerprint()?, + path: vec![].into(), + }; + + Ok(GenericDerivedXPriv { xpriv, derivation }) + } + + /// Generate a master node from some seed data. Uses the BIP32-standard hmac key. + /// + /// + /// # Important: + /// + /// Use a seed of AT LEAST 128 bits. + pub fn custom_root_from_seed( + data: &[u8], + hint: Option, + backend: &'a T, + ) -> Result, Bip32Error> { + Self::custom_master_node(SEED, data, hint, backend) + } + + /// Derive the corresponding xpub + pub fn to_derived_xpub(&self) -> Result, Bip32Error> { + Ok(GenericDerivedXPub { + xpub: self.xpriv.derive_verifying_key()?, + derivation: self.derivation.clone(), + }) + } + + /// Check if this XPriv is the private ancestor of some other derived key + pub fn is_private_ancestor_of>( + &self, + other: &D, + ) -> Result { + if let Some(path) = self.path_to_descendant(other) { + let descendant = self.derive_private_path(&path)?; + let descendant_pk_bytes = descendant.derive_pubkey()?; + Ok(&descendant_pk_bytes == other.pubkey()) + } else { + Ok(false) + } + } +} + +impl<'a, T: Secp256k1Backend> SigningKey<'a, T> for GenericDerivedXPriv<'a, T> { + /// The corresponding verifying key + type VerifyingKey = GenericDerivedXPub<'a, T>; + + /// Derive the corresponding pubkey + fn derive_verifying_key(&self) -> Result { + self.to_derived_xpub() + } +} + +impl<'a, T: Secp256k1Backend> DerivePrivateChild<'a, T> for GenericDerivedXPriv<'a, T> { + fn derive_private_child(&self, index: u32) -> Result { + Ok(Self { + xpriv: self.xpriv.derive_private_child(index)?, + derivation: self.derivation.extended(index), + }) + } +} + +make_derived_key!( + /// A `GenericXPub` coupled with its (purported) derivation path. Generally this struct + /// should be used over XPub wherever possible, in order to preserve information ancestry + /// relationship information. + /// + /// Warning: derivation paths from untrusted sources may be faulty. Make sure to check + /// ancestry using the `DerivedKey` trait methods. + GenericXPub, + GenericDerivedXPub.xpub +); +inherit_has_pubkey!(GenericDerivedXPub.xpub); +inherit_backend!(GenericDerivedXPub.xpub); +inherit_has_xkeyinfo!(GenericDerivedXPub.xpub); + +impl<'a, T: Secp256k1Backend> GenericDerivedXPub<'a, T> { + /// Check if this XPriv is the private ancestor of some other derived key + pub fn is_public_ancestor_of>( + &self, + other: &D, + ) -> Result { + if let Some(path) = self.path_to_descendant(other) { + let descendant = self.derive_public_path(&path)?; + Ok(descendant.pubkey() == other.pubkey()) + } else { + Ok(false) + } + } +} + +impl<'a, T: Secp256k1Backend> VerifyingKey<'a, T> for GenericDerivedXPub<'a, T> { + type SigningKey = GenericDerivedXPriv<'a, T>; +} + +impl<'a, T: Secp256k1Backend> DerivePublicChild<'a, T> for GenericDerivedXPub<'a, T> { + fn derive_public_child(&self, index: u32) -> Result { + Ok(Self { + xpub: self.xpub.derive_public_child(index)?, + derivation: self.derivation.extended(index), + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + curve::*, + enc::{MainnetEncoder, XKeyEncoder}, + path::DerivationPath, + primitives::*, + BIP32_HARDEN, + }; + use coins_core::hashes::*; + + use hex; + + struct KeyDeriv<'a> { + pub path: &'a [u32], + } + + fn validate_descendant(d: &KeyDeriv, m: &DerivedXPriv) { + let path: DerivationPath = d.path.into(); + + let m_pub = m.derive_verifying_key().unwrap(); + + let xpriv = m.derive_private_path(&path).unwrap(); + let xpub = xpriv.derive_verifying_key().unwrap(); + assert!(m.same_root(&xpriv)); + assert!(m.same_root(&xpub)); + assert!(m.is_possible_ancestor_of(&xpriv)); + assert!(m.is_possible_ancestor_of(&xpub)); + + let result = m.is_private_ancestor_of(&xpub).expect("should work"); + + if !result { + assert!(false, "failed validate_descendant is_private_ancestor_of"); + } + + let result = m_pub.is_public_ancestor_of(&xpub); + + match result { + Ok(true) => {} + Ok(false) => assert!(false, "failed validate_descendant is_public_ancestor_of"), + Err(_) => { + let path: crate::path::DerivationPath = d.path.into(); + assert!( + path.last_hardened().1.is_some(), + "is_public_ancestor_of failed for unhardened path" + ) + } + } + + let derived_path = m + .path_to_descendant(&xpriv) + .expect("expected a path to descendant"); + assert_eq!(&path, &derived_path, "derived path is not as expected"); + } + + #[test] + fn bip32_vector_1() { + let seed: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + + let xpriv = DerivedXPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); + + let descendants = [ + KeyDeriv { + path: &[0 + BIP32_HARDEN], + }, + KeyDeriv { + path: &[0 + BIP32_HARDEN, 1], + }, + KeyDeriv { + path: &[0 + BIP32_HARDEN, 1, 2 + BIP32_HARDEN], + }, + KeyDeriv { + path: &[0 + BIP32_HARDEN, 1, 2 + BIP32_HARDEN, 2], + }, + KeyDeriv { + path: &[0 + BIP32_HARDEN, 1, 2 + BIP32_HARDEN, 2, 1000000000], + }, + ]; + + for case in descendants.iter() { + validate_descendant(&case, &xpriv); + } + } + + #[test] + fn bip32_vector_2() { + let seed = hex::decode(&"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542").unwrap(); + + let xpriv = DerivedXPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); + + let descendants = [ + KeyDeriv { path: &[0] }, + KeyDeriv { + path: &[0, 2147483647 + BIP32_HARDEN], + }, + KeyDeriv { + path: &[0, 2147483647 + BIP32_HARDEN, 1], + }, + KeyDeriv { + path: &[0, 2147483647 + BIP32_HARDEN, 1, 2147483646 + BIP32_HARDEN], + }, + KeyDeriv { + path: &[ + 0, + 2147483647 + BIP32_HARDEN, + 1, + 2147483646 + BIP32_HARDEN, + 2, + ], + }, + ]; + + for case in descendants.iter() { + validate_descendant(&case, &xpriv); + } + } + + #[test] + fn bip32_vector_3() { + let seed = hex::decode(&"4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be").unwrap(); + + let xpriv = DerivedXPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); + + let descendants = [KeyDeriv { + path: &[0 + BIP32_HARDEN], + }]; + + for case in descendants.iter() { + validate_descendant(&case, &xpriv); + } + } + + #[test] + fn it_can_sign_and_verify() { + let digest: Hash256Digest = [1u8; 32].into(); + let wrong_digest: Hash256Digest = [2u8; 32].into(); + let backend = Secp256k1::static_ref(); + + let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); + let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, Some(backend)).unwrap(); + let fake_deriv = KeyDerivation { + root: [0, 0, 0, 0].into(), + path: (0..0).collect(), + }; + + let mut key = DerivedXPriv::new(xpriv, fake_deriv); + let mut key_pub = DerivedXPub::from_signing_key(&key).unwrap(); + // These had to go somewhere. here is as good as any + key.set_backend(backend); + key_pub.set_backend(backend); + + // sign_digest + verify_digest + let sig = key.sign_digest(digest).unwrap(); + key_pub.verify_digest(digest, &sig).unwrap(); + + let err_bad_sig = key_pub.verify_digest(wrong_digest, &sig); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // sign_digest_recoverable + verify_digest_recoverable + let sig = key.sign_digest_recoverable(digest).unwrap(); + key_pub.verify_digest_recoverable(digest, &sig).unwrap(); + + let err_bad_sig = key_pub.verify_digest_recoverable(wrong_digest, &sig); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // sign_with_hash + verify_with_hash + let sig = key.sign_with_hash::(digest.as_slice()).unwrap(); + key_pub + .verify_with_hash::(digest.as_slice(), &sig) + .unwrap(); + + let err_bad_sig = key_pub.verify_with_hash::(wrong_digest.as_slice(), &sig); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // sign_recoverable_with_hash + verify_recoverable_with_hash + let sig = key + .sign_recoverable_with_hash::(digest.as_slice()) + .unwrap(); + key_pub + .verify_recoverable_with_hash::(digest.as_slice(), &sig) + .unwrap(); + + let err_bad_sig = + key_pub.verify_recoverable_with_hash::(wrong_digest.as_slice(), &sig); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // sign + verify + let sig = key.sign(digest.as_slice()).unwrap(); + key_pub.verify(digest.as_slice(), &sig).unwrap(); + + let err_bad_sig = key_pub.verify(wrong_digest.as_slice(), &sig); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // sign_recoverable + verify_recoverable + let sig = key.sign_recoverable(digest.as_slice()).unwrap(); + key_pub.verify_recoverable(digest.as_slice(), &sig).unwrap(); + + let err_bad_sig = key_pub.verify_recoverable(wrong_digest.as_slice(), &sig); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + } + + #[test] + fn it_can_descendant_sign_and_verify() { + let digest: Hash256Digest = [1u8; 32].into(); + let wrong_digest: Hash256Digest = [2u8; 32].into(); + let backend = Secp256k1::static_ref(); + + let path = vec![0u32, 1, 2]; + + let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); + let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, Some(backend)).unwrap(); + let fake_deriv = KeyDerivation { + root: [0, 0, 0, 0].into(), + path: (0..0).collect(), + }; + + let mut key = DerivedXPriv::new(xpriv, fake_deriv.clone()); + let mut key_pub = DerivedXPub::from_signing_key(&key).unwrap(); + // These had to go somewhere. here is as good as any + assert_eq!(key.derivation(), &fake_deriv); + key.set_backend(backend); + key_pub.set_backend(backend); + + // sign_digest + verify_digest + let sig = key.descendant_sign_digest(&path, digest).unwrap(); + key_pub + .descendant_verify_digest(&path, digest, &sig) + .unwrap(); + + let err_bad_sig = key_pub.descendant_verify_digest(&path, wrong_digest, &sig); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // sign_digest_recoverable + verify_digest_recoverable + let sig = key + .descendant_sign_digest_recoverable(&path, digest) + .unwrap(); + key_pub + .descendant_verify_digest_recoverable(&path, digest, &sig) + .unwrap(); + + let err_bad_sig = key_pub.descendant_verify_digest_recoverable(&path, wrong_digest, &sig); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // sign_with_hash + verify_with_hash + let sig = key + .descendant_sign_with_hash::(&path, digest.as_slice()) + .unwrap(); + key_pub + .descendant_verify_with_hash::(&path, digest.as_slice(), &sig) + .unwrap(); + + let err_bad_sig = key_pub.descendant_verify_with_hash::( + &path, + wrong_digest.as_slice(), + &sig, + ); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // sign_recoverable_with_hash + verify_recoverable_with_hash + let sig = key + .descendant_sign_recoverable_with_hash::(&path, digest.as_slice()) + .unwrap(); + key_pub + .descendant_verify_recoverable_with_hash::( + &path, + digest.as_slice(), + &sig, + ) + .unwrap(); + + let err_bad_sig = key_pub.descendant_verify_recoverable_with_hash::( + &path, + wrong_digest.as_slice(), + &sig, + ); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // sign + verify + let sig = key.descendant_sign(&path, digest.as_slice()).unwrap(); + key_pub + .descendant_verify(&path, digest.as_slice(), &sig) + .unwrap(); + + let err_bad_sig = key_pub.descendant_verify(&path, wrong_digest.as_slice(), &sig); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // sign_recoverable + verify_recoverable + let sig = key + .descendant_sign_recoverable(&path, digest.as_slice()) + .unwrap(); + key_pub + .descendant_verify_recoverable(&path, digest.as_slice(), &sig) + .unwrap(); + + let err_bad_sig = + key_pub.descendant_verify_recoverable(&path, wrong_digest.as_slice(), &sig); + match err_bad_sig { + Err(Bip32Error::BackendError(_)) => {} + _ => assert!(false, "expected signature validation error"), + } + + // Sig serialize/deserialize + let der_sig = hex::decode("3045022100e838d64bb95cdacc1b93f94ad8c2fcc10441e672f66565aca374d5a955d99672022022283ac21bc8c64b7265e71b1972b051b1a818a99ae0db3d563489e55b9826a3").unwrap(); + let vrs = ( + 1, + [ + 232, 56, 214, 75, 185, 92, 218, 204, 27, 147, 249, 74, 216, 194, 252, 193, 4, 65, + 230, 114, 246, 101, 101, 172, 163, 116, 213, 169, 85, 217, 150, 114, + ], + [ + 34, 40, 58, 194, 27, 200, 198, 75, 114, 101, 231, 27, 25, 114, 176, 81, 177, 168, + 24, 169, 154, 224, 219, 61, 86, 52, 137, 229, 91, 152, 38, 163, + ], + ); + assert_eq!(sig.to_der(), der_sig); + assert_eq!(sig.serialize_vrs(), vrs); + assert_eq!( + sig.without_recovery(), + backend::Signature::try_from_der(&der_sig).unwrap() + ); + assert_eq!( + sig, + backend::RecoverableSignature::deserialize_vrs(vrs).unwrap() + ); + + let err_no_rec_id = backend::RecoverableSignature::try_from_der(&der_sig); + match err_no_rec_id { + Err(Bip32Error::NoRecoveryID) => {} + _ => assert!(false, "expected err no rec id"), + }; + } + + #[test] + fn it_derives_verifying_keys() { + let backend = Secp256k1::static_ref(); + let fake_deriv = KeyDerivation { + root: [0, 0, 0, 0].into(), + path: (0..0).collect(), + }; + + let key = crate::curve::Privkey::from_privkey_array([1u8; 32]).unwrap(); + + let privkey = crate::keys::Privkey { + key, + backend: Some(backend), + }; + + let key = DerivedPrivkey::new(privkey, fake_deriv); + + key.derive_verifying_key().unwrap(); + } + + #[test] + fn it_instantiates_derived_xprivs_from_seeds() { + let backend = Secp256k1::static_ref(); + GenericDerivedXPriv::custom_root_from_seed(&[0u8; 32][..], None, backend).unwrap(); + + let err_too_short = + GenericDerivedXPriv::custom_root_from_seed(&[0u8; 2][..], None, backend); + match err_too_short { + Err(Bip32Error::SeedTooShort) => {} + _ => assert!(false, "expected err too short"), + } + + let err_too_short = + GenericDerivedXPriv::custom_root_from_seed(&[0u8; 2][..], None, backend); + match err_too_short { + Err(Bip32Error::SeedTooShort) => {} + _ => assert!(false, "expected err too short"), + } + } + + #[test] + fn it_checks_ancestry() { + let backend = Secp256k1::static_ref(); + let m = GenericDerivedXPriv::custom_root_from_seed(&[0u8; 32][..], None, backend).unwrap(); + let m2 = GenericDerivedXPriv::custom_root_from_seed(&[1u8; 32][..], None, backend).unwrap(); + let m_pub = GenericDerivedXPub::from_signing_key(&m).unwrap(); + let cases = [ + (&m, &m_pub, true), + (&m2, &m_pub, false), + (&m, &m2.derive_verifying_key().unwrap(), false), + ( + &m, + &m.derive_private_child(33) + .unwrap() + .derive_verifying_key() + .unwrap(), + true, + ), + (&m, &m_pub.derive_public_child(33).unwrap(), true), + ( + &m, + &m2.derive_private_child(33) + .unwrap() + .derive_verifying_key() + .unwrap(), + false, + ), + ]; + for case in cases.iter() { + assert_eq!(case.0.is_private_ancestor_of(case.1).unwrap(), case.2); + } + } +} diff --git a/bip32-old/src/enc.rs b/bip32-old/src/enc.rs new file mode 100644 index 00000000..05f7d263 --- /dev/null +++ b/bip32-old/src/enc.rs @@ -0,0 +1,498 @@ +use std::marker::PhantomData; + +use coins_core::hashes::{Digest, Hash256}; + +use crate::{ + curve::model::{PointDeserialize, ScalarDeserialize, Secp256k1Backend}, + keys::{GenericPrivkey, GenericPubkey}, + model::{HasPrivkey, HasPubkey, XKey}, + primitives::{ChainCode, Hint, KeyFingerprint, XKeyInfo}, + xkeys::{GenericXPriv, GenericXPub}, + Bip32Error, +}; + +/// Decode a bytevector from a base58 check string +pub fn decode_b58_check(s: &str) -> Result, Bip32Error> { + let data: Vec = bs58::decode(s).into_vec()?; + let idx = data.len() - 4; + let payload = &data[..idx]; + let checksum = &data[idx..]; + + let digest = &Hash256::digest(payload); + + let mut expected = [0u8; 4]; + expected.copy_from_slice(&digest.as_slice()[..4]); + if expected != checksum { + Err(Bip32Error::BadB58Checksum) + } else { + Ok(payload.to_vec()) + } +} + +/// Encode a vec into a base58 check String +pub fn encode_b58_check(v: &[u8]) -> String { + let digest = &Hash256::digest(v); + + let mut checksum = [0u8; 4]; + checksum.copy_from_slice(&digest.as_slice()[..4]); + + let mut data = v.to_vec(); + data.extend(&checksum); + + bs58::encode(data).into_string() +} + +/// Contains network-specific serialization information +pub trait NetworkParams { + /// The Bip32 privkey version bytes + const PRIV_VERSION: u32; + /// The Bip49 privkey version bytes + const BIP49_PRIV_VERSION: u32; + /// The Bip84 pubkey version bytes + const BIP84_PRIV_VERSION: u32; + /// The Bip32 pubkey version bytes + const PUB_VERSION: u32; + /// The Bip49 pubkey version bytes + const BIP49_PUB_VERSION: u32; + /// The Bip84 pubkey version bytes + const BIP84_PUB_VERSION: u32; +} + +/// Bip32/49/84 encoder +pub trait XKeyEncoder { + #[doc(hidden)] + fn write_key_details(writer: &mut W, key: &K) -> Result + where + K: XKey, + W: std::io::Write, + { + let mut written = writer.write(&[key.depth()])?; + written += writer.write(&key.parent().0)?; + written += writer.write(&key.index().to_be_bytes())?; + written += writer.write(&key.chain_code().0)?; + Ok(written) + } + + /// Serialize the xpub to `std::io::Write` + fn write_xpub<'a, W, T>(writer: &mut W, key: &GenericXPub<'a, T>) -> Result + where + W: std::io::Write, + T: Secp256k1Backend; + + /// Serialize the xpriv to `std::io::Write` + fn write_xpriv<'a, W, T>( + writer: &mut W, + key: &GenericXPriv<'a, T>, + ) -> Result + where + W: std::io::Write, + T: Secp256k1Backend; + + #[doc(hidden)] + fn read_depth(reader: &mut R) -> Result + where + R: std::io::Read, + { + let mut buf = [0u8; 1]; + reader.read_exact(&mut buf)?; + Ok(buf[0]) + } + + #[doc(hidden)] + fn read_parent(reader: &mut R) -> Result + where + R: std::io::Read, + { + let mut buf = [0u8; 4]; + reader.read_exact(&mut buf)?; + Ok(buf.into()) + } + + #[doc(hidden)] + fn read_index(reader: &mut R) -> Result + where + R: std::io::Read, + { + let mut buf = [0u8; 4]; + reader.read_exact(&mut buf)?; + Ok(u32::from_be_bytes(buf)) + } + + #[doc(hidden)] + fn read_chain_code(reader: &mut R) -> Result + where + R: std::io::Read, + { + let mut buf = [0u8; 32]; + reader.read_exact(&mut buf)?; + Ok(buf.into()) + } + + #[doc(hidden)] + fn read_xpriv_body<'a, R, T>( + reader: &mut R, + hint: Hint, + backend: Option<&'a T>, + ) -> Result, Bip32Error> + where + R: std::io::Read, + T: Secp256k1Backend, + { + let depth = Self::read_depth(reader)?; + let parent = Self::read_parent(reader)?; + let index = Self::read_index(reader)?; + let chain_code = Self::read_chain_code(reader)?; + + let mut buf = [0u8]; + reader.read_exact(&mut buf)?; + if buf != [0] { + return Err(Bip32Error::BadPadding(buf[0])); + } + + let mut buf = [0u8; 32]; + reader.read_exact(&mut buf)?; + let key = T::Privkey::from_privkey_array(buf)?; + + Ok(GenericXPriv { + info: XKeyInfo { + depth, + parent, + index, + chain_code, + hint, + }, + privkey: GenericPrivkey { key, backend }, + }) + } + + #[doc(hidden)] + // Can be used for unhealthy but sometimes-desiable behavior. E.g. accepting an xpriv from any + // network. + fn read_xpriv_without_network<'a, R, T>( + reader: &mut R, + backend: Option<&'a T>, + ) -> Result, Bip32Error> + where + R: std::io::Read, + T: Secp256k1Backend, + { + let mut buf = [0u8; 4]; + reader.read_exact(&mut buf)?; + + Self::read_xpriv_body(reader, Hint::Legacy, backend) + } + + /// Attempt to instantiate an `XPriv` from a `std::io::Read` + /// + /// # Note + /// + /// If passing in None, you must hint the return type. This can be the convenience type alias + /// (e.g. XPub) or the full type `GenericXPub` + /// + /// ``` + /// use coins_bip32::{Bip32Error, XPriv, enc::{XKeyEncoder, MainnetEncoder}}; + /// # fn main() -> Result<(), Bip32Error> { + /// let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); + /// + /// // // can't infer type of generic parameter + /// // let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None)?; + /// + /// let xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None)?; + /// # Ok(()) + /// # } + /// ``` + fn read_xpriv<'a, R, T>( + reader: &mut R, + backend: Option<&'a T>, + ) -> Result, Bip32Error> + where + R: std::io::Read, + T: Secp256k1Backend; + + #[doc(hidden)] + fn read_xpub_body<'a, R, T>( + reader: &mut R, + hint: Hint, + backend: Option<&'a T>, + ) -> Result, Bip32Error> + where + R: std::io::Read, + T: Secp256k1Backend, + { + let depth = Self::read_depth(reader)?; + let parent = Self::read_parent(reader)?; + let index = Self::read_index(reader)?; + let chain_code = Self::read_chain_code(reader)?; + + let mut buf = [0u8; 33]; + reader.read_exact(&mut buf)?; + let key = T::Pubkey::from_pubkey_array(buf)?; + + Ok(GenericXPub { + info: XKeyInfo { + depth, + parent, + index, + chain_code, + hint, + }, + pubkey: GenericPubkey { key, backend }, + }) + } + + #[doc(hidden)] + // Can be used for unhealthy but sometimes-desiable behavior. E.g. accepting an xpub from any + // network. + fn read_xpub_without_network<'a, R, T>( + reader: &mut R, + backend: Option<&'a T>, + ) -> Result, Bip32Error> + where + R: std::io::Read, + T: Secp256k1Backend, + { + let mut buf = [0u8; 4]; + reader.read_exact(&mut buf)?; + + Self::read_xpub_body(reader, Hint::Legacy, backend) + } + + /// Attempt to instantiate an `XPriv` from a `std::io::Read` + /// + /// # Note + /// + /// If passing in None, you must hint the return type. This can be the convenience type alias + /// (e.g. XPub) or the full type `GenericXPub` + /// + /// ``` + /// use coins_bip32::{Bip32Error, XPub, enc::{XKeyEncoder, MainnetEncoder}}; + /// # fn main() -> Result<(), Bip32Error> { + /// let xpub_str = "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y".to_owned(); + /// + /// // // can't infer type of generic parameter + /// // let xpub = MainnetEncoder::xpub_from_base58(&xpub_str, None)?; + /// + /// let xpub: XPub = MainnetEncoder::xpub_from_base58(&xpub_str, None)?; + /// # Ok(()) + /// # } + /// ``` + fn read_xpub<'a, R, T>( + reader: &mut R, + backend: Option<&'a T>, + ) -> Result, Bip32Error> + where + R: std::io::Read, + T: Secp256k1Backend; + + /// Serialize an XPriv to base58 + fn xpriv_to_base58(k: &GenericXPriv) -> Result + where + T: Secp256k1Backend, + { + let mut v: Vec = vec![]; + Self::write_xpriv(&mut v, k)?; + Ok(encode_b58_check(&v)) + } + + /// Serialize an XPub to base58 + fn xpub_to_base58(k: &GenericXPub) -> Result + where + T: Secp256k1Backend, + { + let mut v: Vec = vec![]; + Self::write_xpub(&mut v, k)?; + Ok(encode_b58_check(&v)) + } + + /// Attempt to read an XPriv from a b58check string. + /// + /// # Note + /// + /// If passing in None, you must hint the return type. This can be the convenience type alias + /// (e.g. XPub) or the full type `GenericXPub` + /// + /// ``` + /// use coins_bip32::{Bip32Error, XPriv, enc::{XKeyEncoder, MainnetEncoder}}; + /// # fn main() -> Result<(), Bip32Error> { + /// let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); + /// + /// // // can't infer type of generic parameter + /// // let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None)?; + /// + /// let xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None)?; + /// # Ok(()) + /// # } + /// ``` + fn xpriv_from_base58<'a, T>( + s: &str, + backend: Option<&'a T>, + ) -> Result, Bip32Error> + where + T: Secp256k1Backend, + { + let data = decode_b58_check(s)?; + Self::read_xpriv(&mut &data[..], backend) + } + + /// Attempt to read an XPub from a b58check string + /// + /// # Note + /// + /// If passing in None, you must hint the return type. This can be the convenience type alias + /// (e.g. XPub) or the full type `GenericXPub` + /// + /// ``` + /// use coins_bip32::{Bip32Error, XPub, enc::{XKeyEncoder, MainnetEncoder}}; + /// # fn main() -> Result<(), Bip32Error> { + /// let xpub_str = "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y".to_owned(); + /// + /// // // can't infer type of generic parameter + /// // let xpub = MainnetEncoder::xpub_from_base58(&xpub_str, None)?; + /// + /// let xpub: XPub = MainnetEncoder::xpub_from_base58(&xpub_str, None)?; + /// # Ok(()) + /// # } + /// ``` + fn xpub_from_base58<'a, T>( + s: &str, + backend: Option<&'a T>, + ) -> Result, Bip32Error> + where + T: Secp256k1Backend, + { + let data = decode_b58_check(s)?; + Self::read_xpub(&mut &data[..], backend) + } +} + +params!( + /// Mainnet encoding param + Main { + bip32: 0x0488_ADE4, + bip49: 0x049d_7878, + bip84: 0x04b2_430c, + bip32_pub: 0x0488_B21E, + bip49_pub: 0x049d_7cb2, + bip84_pub: 0x04b2_4746 + } +); + +params!( + /// Testnet encoding param + Test { + bip32: 0x0435_8394, + bip49: 0x044a_4e28, + bip84: 0x045f_18bc, + bip32_pub: 0x0435_87CF, + bip49_pub: 0x044a_5262, + bip84_pub: 0x045f_1cf6 + } +); + +/// Parameterizable Bitcoin encoder +#[derive(Debug, Clone)] +pub struct BitcoinEncoder(PhantomData P>); + +impl XKeyEncoder for BitcoinEncoder

{ + /// Serialize the xpub to `std::io::Write` + fn write_xpub<'a, W, T>(writer: &mut W, key: &GenericXPub<'a, T>) -> Result + where + W: std::io::Write, + T: Secp256k1Backend, + { + let version = match key.hint() { + Hint::Legacy => P::PUB_VERSION, + Hint::Compatibility => P::BIP49_PUB_VERSION, + Hint::SegWit => P::BIP84_PUB_VERSION, + }; + let mut written = writer.write(&version.to_be_bytes())?; + written += Self::write_key_details(writer, key)?; + written += writer.write(&key.pubkey_bytes())?; + Ok(written) + } + + /// Serialize the xpriv to `std::io::Write` + fn write_xpriv<'a, W, T>(writer: &mut W, key: &GenericXPriv<'a, T>) -> Result + where + W: std::io::Write, + T: Secp256k1Backend, + { + let version = match key.hint() { + Hint::Legacy => P::PRIV_VERSION, + Hint::Compatibility => P::BIP49_PRIV_VERSION, + Hint::SegWit => P::BIP84_PRIV_VERSION, + }; + let mut written = writer.write(&version.to_be_bytes())?; + written += Self::write_key_details(writer, key)?; + written += writer.write(&[0])?; + written += writer.write(&key.privkey_bytes())?; + Ok(written) + } + + fn read_xpriv<'a, R, T>( + reader: &mut R, + backend: Option<&'a T>, + ) -> Result, Bip32Error> + where + R: std::io::Read, + T: Secp256k1Backend, + { + let mut buf = [0u8; 4]; + reader.read_exact(&mut buf)?; + let version_bytes = u32::from_be_bytes(buf); + + // Can't use associated constants in matches :() + let hint = if version_bytes == P::PRIV_VERSION { + Hint::Legacy + } else if version_bytes == P::BIP49_PRIV_VERSION { + Hint::Compatibility + } else if version_bytes == P::BIP84_PRIV_VERSION { + Hint::SegWit + } else { + return Err(Bip32Error::BadXPrivVersionBytes(buf)); + }; + Self::read_xpriv_body(reader, hint, backend) + } + + fn read_xpub<'a, R, T>( + reader: &mut R, + backend: Option<&'a T>, + ) -> Result, Bip32Error> + where + R: std::io::Read, + T: Secp256k1Backend, + { + let mut buf = [0u8; 4]; + reader.read_exact(&mut buf)?; + let version_bytes = u32::from_be_bytes(buf); + + // Can't use associated constants in matches :() + let hint = if version_bytes == P::PUB_VERSION { + Hint::Legacy + } else if version_bytes == P::BIP49_PUB_VERSION { + Hint::Compatibility + } else if version_bytes == P::BIP84_PUB_VERSION { + Hint::SegWit + } else { + return Err(Bip32Error::BadXPrivVersionBytes(buf)); + }; + Self::read_xpub_body(reader, hint, backend) + } +} + +/// XKeyEncoder for Mainnet xkeys +pub type MainnetEncoder = BitcoinEncoder

; +/// XKeyEncoder for Testnet xkeys +pub type TestnetEncoder = BitcoinEncoder; + +#[cfg(test)] +mod test { + use super::*; + use crate::xkeys::XPriv; + + #[test] + fn it_can_read_keys_without_a_backend() { + let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); + let _xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None).unwrap(); + } +} diff --git a/bip32/src/keys.rs b/bip32-old/src/keys.rs similarity index 100% rename from bip32/src/keys.rs rename to bip32-old/src/keys.rs diff --git a/bip32-old/src/lib.rs b/bip32-old/src/lib.rs new file mode 100644 index 00000000..9c329e43 --- /dev/null +++ b/bip32-old/src/lib.rs @@ -0,0 +1,218 @@ +//! This crate provides a basic implementation of BIP32, BIP49, and BIP84 with configurable +//! backends. It can be easily adapted to support other networks, using the paramaterizable +//! encoder. +//! +//! Typically, users will want to use the `MainnetEncoder`, `DerivedXPub`, `DerivedXPriv` types, +//! which are available at the crate root. If key derivations are unknown, use the `XPub` and +//! `XPriv` objects instead. These may be deserialized using a network-specific `Encoder` from the +//! `enc` module. +//! +//! Useful traits will need to be imported from the `enc` or `model` modules. Most users will want +//! `model::SigningKey`, `model::VerifyingKey`, `enc::Encoder`, and `enc::HasPubkey`. +//! +//! # Warnings: +//! +//! - This crate is NOT designed to be used in adversarial environments. +//! - This crate has NOT had a comprehensive security review. +//! - It is not clear whether the rust-secp backend's functions are constant-time. +//! +//! # Usage +//! +//! Most key objects need a backend. They can be instantiated without one, but most operations +//! you want (e.g. signing, verifying, key derivation) will fail at runtime. Simple usage: +//! +//! ``` +//! use coins_bip32::{ +//! Bip32Error, Secp256k1, XPub, XPriv, +//! enc::{XKeyEncoder, MainnetEncoder}, +//! model::*, +//! curve::model::*, +//! }; +//! +//! # fn main() -> Result<(), Bip32Error> { +//! let digest = [1u8; 32]; +//! +//! // `init` sets up a new `lazy_static` backend. Successive calls will re-use that backend +//! // without needing to re-initialize it. +//! let backend = Secp256k1::static_ref(); +//! let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); +//! +//! // Be sure to annotate the type +//! let xpriv: XPriv = xpriv_str.parse().unwrap(); +//! +//! let child_xpriv = xpriv.derive_private_child(33)?; +//! let sig = child_xpriv.sign_digest(digest.into())?; +//! +//! // Signing key types are associated with verifying key types. You can always derive a pubkey +//! // from a privkey using `derive_verifying_key()`. +//! let child_xpub = child_xpriv.derive_verifying_key()?; +//! child_xpub.verify_digest(digest.into(), &sig); +//! +//! sig.to_der(); // serialize to der-encoded byte-array +//! MainnetEncoder::xpub_to_base58(&child_xpub)?; +//! # Ok(()) +//! # } +//! ``` +//! +//! The backend is configurable. By default, it uses bindings to Pieter Wuille's C `libsecp256k1`. +//! Turning off standard features, and compiling with the `rust-secp` feature will use a pure rust +//! backend. Users can provide their own backend by implementing the `Secp256k1Backend` trait. In +//! this case, you will need to use the `Generic` variants of the structs found in the `keys`, +//! `xkeys`, and `derived modules.` These backends are mutually exclusive. So to use `rust-secp` +//! you must disable default features. Compilation will fail otherwise. +//! +//! Additionally, both provided backends allow user-provided context objects via the +//! `Secp256k1Backend::from_context()` method. We also provide access to `lazy_static` on-demand +//! contexts via `Secp256k1Backend::init()`. This has a 1-time cost. The +//! `rust-secp-static-context` allows for compilation-time generation of the context, but must +//! be used with the `rust-secp` backend. + +#![forbid(unsafe_code)] +#![warn(missing_docs)] +#![warn(unused_extern_crates)] + +#[cfg(not(feature = "rust-secp-static-context"))] +#[macro_use] +extern crate lazy_static; + +#[cfg_attr(tarpaulin, skip)] +#[macro_use] +pub(crate) mod macros; + +/// Everything needed for common usage +pub mod prelude; + +/// Low-level types +pub mod primitives; + +/// Keys and related functionality +pub mod keys; + +/// Extended keys and related functionality +pub mod xkeys; + +/// Network-differentiated encoders for extended keys. +pub mod enc; + +/// The Secp256k1 backend and its associated traits. Compiled-in backends may be selected using +/// crate features. Generally, this module's traits shouldn't be used directly, with the notable +/// exception of `SigSerialize` and `RecoverableSigSerialize`. +#[cfg_attr(tarpaulin, skip)] +pub mod curve; + +/// Traits and other high-level model description for Bip32 keys. +pub mod model; + +/// `DerivationPath` type and tooling for parsing it from strings +pub mod path; + +/// Provides keys that are coupled with their derivation path +pub mod derived; + +#[doc(hidden)] +#[cfg(any(feature = "mainnet", feature = "testnet"))] +pub mod defaults; + +#[cfg(any(feature = "mainnet", feature = "testnet"))] +pub use defaults::Encoder; + +pub use enc::MainnetEncoder; +pub use model::*; +pub use primitives::KeyFingerprint; + +pub use crate::{ + curve::{RecoverableSignature, Secp256k1, Signature}, + derived::{DerivedXPriv, DerivedXPub}, + enc::XKeyEncoder, + keys::{Privkey, Pubkey}, + xkeys::{XPriv, XPub}, +}; + +use thiserror::Error; + +/// The hardened derivation flag. Keys at or above this index are hardened. +pub const BIP32_HARDEN: u32 = 0x8000_0000; + +#[doc(hidden)] +pub const CURVE_ORDER: [u8; 32] = [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41, +]; + +/// Errors for this library +#[derive(Debug, Error)] +pub enum Bip32Error { + /// Error bubbled up from the backend + #[error(transparent)] + BackendError(#[from] crate::curve::Error), + + /// General wrapper for errors from custom backends + #[error("Custom backend returned error with info: {0}")] + CustomBackendError(String), + + /// Error bubbled up froom std::io + #[error(transparent)] + IOError(#[from] std::io::Error), + + /// Error bubbled up froom Ser + #[error(transparent)] + SerError(#[from] coins_core::ser::SerError), + + /// Master key seed generation received <16 bytes + #[error("Master key seed generation received <16 bytes")] + SeedTooShort, + + /// HMAC I_l was invalid during key generations. + #[error("HMAC left segment was 0 or greated than the curve order. How?")] + InvalidKey, + + /// pted to derive the hardened child of an xpub + #[error("Attempted to derive the hardened child of an xpub")] + HardenedDerivationFailed, + + /// Attempted to tweak an xpriv or xpub directly + #[error("Attempted to tweak an xpriv or xpub directly")] + BadTweak, + + /// Unrecognized version when deserializing xpriv + #[error("Version bytes 0x{0:x?} don't match any network xpriv version bytes")] + BadXPrivVersionBytes([u8; 4]), + + /// Unrecognized version when deserializing xpub + #[error("Version bytes 0x{0:x?} don't match any network xpub version bytes")] + BadXPubVersionBytes([u8; 4]), + + /// No backed in xtended key + #[error("Attempted to operate on an extended key without supplying a backend")] + NoBackend, + + /// Bad padding byte on serialized xprv + #[error("Expected 0 padding byte. Got {0}")] + BadPadding(u8), + + /// Bad Checks on b58check + #[error("Checksum mismatch on b58 deserialization")] + BadB58Checksum, + + /// Bubbled up error from bs58 library + #[error(transparent)] + B58Error(#[from] bs58::decode::Error), + + /// Parsing an string derivation failed because an index string was malformatted + #[error("Malformatted index during derivation: {0}")] + MalformattedDerivation(String), + + /// Attempted to deserialize a DER signature to a recoverable signature. + #[error("Attempted to deserialize a DER signature to a recoverable signature. Use deserialize_vrs instead")] + NoRecoveryID, + + /// Attempted to deserialize a very long path + #[error("Invalid Bip32 Path.")] + InvalidBip32Path, +} + +impl From for Bip32Error { + fn from(_i: std::convert::Infallible) -> Self { + unimplemented!("unreachable, but required by type system") + } +} diff --git a/bip32-old/src/macros.rs b/bip32-old/src/macros.rs new file mode 100644 index 00000000..34e12a1f --- /dev/null +++ b/bip32-old/src/macros.rs @@ -0,0 +1,109 @@ +macro_rules! params { + ( + $(#[$outer:meta])* + $name:ident{ + bip32: $bip32:expr, + bip49: $bip49:expr, + bip84: $bip84:expr, + bip32_pub: $bip32pub:expr, + bip49_pub: $bip49pub:expr, + bip84_pub: $bip84pub:expr + } + ) => { + $(#[$outer])* + #[derive(Debug, Clone)] + pub struct $name; + + impl crate::enc::NetworkParams for $name { + const PRIV_VERSION: u32 = $bip32; + const BIP49_PRIV_VERSION: u32 = $bip49; + const BIP84_PRIV_VERSION: u32 = $bip84; + const PUB_VERSION: u32 = $bip32pub; + const BIP49_PUB_VERSION: u32 = $bip49pub; + const BIP84_PUB_VERSION: u32 = $bip84pub; + } + } +} + +macro_rules! inherit_backend { + ($struct_name:ident.$attr:ident) => { + impl<'a, T: crate::curve::model::Secp256k1Backend> crate::model::HasBackend<'a, T> + for $struct_name<'a, T> + { + fn set_backend(&mut self, backend: &'a T) { + self.$attr.set_backend(backend) + } + + fn backend(&self) -> Result<&'a T, Bip32Error> { + self.$attr.backend() + } + } + }; +} + +macro_rules! inherit_has_privkey { + ($struct_name:ident.$attr:ident) => { + impl<'a, T: crate::curve::model::Secp256k1Backend> crate::model::HasPrivkey<'a, T> + for $struct_name<'a, T> + { + fn privkey(&self) -> &T::Privkey { + self.$attr.privkey() + } + } + }; +} + +macro_rules! inherit_has_pubkey { + ($struct_name:ident.$attr:ident) => { + impl<'a, T: crate::curve::model::Secp256k1Backend> crate::model::HasPubkey<'a, T> + for $struct_name<'a, T> + { + fn pubkey(&self) -> &T::Pubkey { + self.$attr.pubkey() + } + } + }; +} + +macro_rules! inherit_has_xkeyinfo { + ($struct_name:ident.$attr:ident) => { + impl<'a, T: crate::curve::model::Secp256k1Backend> crate::model::HasXKeyInfo + for $struct_name<'a, T> + { + fn xkey_info(&self) -> &crate::primitives::XKeyInfo { + self.$attr.xkey_info() + } + } + }; +} + +macro_rules! make_derived_key { + ( + $(#[$outer:meta])* + $underlying:ident, $struct_name:ident.$attr:ident + ) => { + $(#[$outer])* + #[derive(Clone, Debug, PartialEq)] + pub struct $struct_name<'a, T: Secp256k1Backend> { + /// The underlying key + pub $attr: $underlying<'a, T>, + /// Its derivation from some master key + pub derivation: crate::path::KeyDerivation, + } + + impl<'a, T: Secp256k1Backend> crate::model::DerivedKey for $struct_name<'a, T> { + type Key = $underlying<'a, T>; + + fn new(k: Self::Key, derivation: KeyDerivation) -> Self { + Self { + $attr: k, + derivation, + } + } + + fn derivation(&self) -> &KeyDerivation { + &self.derivation + } + } + } +} diff --git a/bip32/src/model.rs b/bip32-old/src/model.rs similarity index 100% rename from bip32/src/model.rs rename to bip32-old/src/model.rs diff --git a/bip32-old/src/path.rs b/bip32-old/src/path.rs new file mode 100644 index 00000000..8e8af58f --- /dev/null +++ b/bip32-old/src/path.rs @@ -0,0 +1,391 @@ +use std::{ + io::{Read, Write}, + iter::{FromIterator, IntoIterator}, + slice::Iter, + str::FromStr, +}; + +use coins_core::ser::ByteFormat; + +use crate::{primitives::KeyFingerprint, Bip32Error, BIP32_HARDEN}; + +fn try_parse_index(s: &str) -> Result { + let mut index_str = s.to_owned(); + let harden = if s.ends_with('\'') || s.ends_with('h') { + index_str.pop(); + true + } else { + false + }; + + index_str + .parse::() + .map(|v| if harden { v + BIP32_HARDEN } else { v }) + .map_err(|_| Bip32Error::MalformattedDerivation(s.to_owned())) +} + +fn try_parse_path(path: &str) -> Result, Bip32Error> { + path.to_owned() + .split('/') + .filter(|v| v != &"m") + .map(try_parse_index) + .collect::, Bip32Error>>() + .map_err(|_| Bip32Error::MalformattedDerivation(path.to_owned())) +} + +fn encode_index(idx: u32, harden: char) -> String { + let mut s = (idx % BIP32_HARDEN).to_string(); + if idx >= BIP32_HARDEN { + s.push(harden); + } + s +} + +/// A Bip32 derivation path +#[derive(Default, Debug, Clone, Eq, PartialEq)] +pub struct DerivationPath(Vec); + +impl serde::Serialize for DerivationPath { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.derivation_string()) + } +} + +impl<'de> serde::Deserialize<'de> for DerivationPath { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s: &str = serde::Deserialize::deserialize(deserializer)?; + s.parse::() + .map_err(|e| serde::de::Error::custom(e.to_string())) + } +} + +impl DerivationPath { + #[doc(hidden)] + pub fn custom_string(&self, root: &str, joiner: char, harden: char) -> String { + std::iter::once(root.to_owned()) + .chain(self.0.iter().map(|s| encode_index(*s, harden))) + .collect::>() + .join(&joiner.to_string()) + } + + /// Return the last index in the path. None if the path is the root. + pub fn last(&self) -> Option<&u32> { + self.0.last() + } + + /// Converts the path to a standard bip32 string. e.g `"m/44'/0'/0/32"`. + pub fn derivation_string(&self) -> String { + self.custom_string("m", '/', '\'') + } + + /// Returns `True` if there are no indices in the path + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// The number of derivations in the path + pub fn len(&self) -> usize { + self.0.len() + } + + /// Make an iterator over the path indices + pub fn iter(&self) -> Iter { + self.0.iter() + } + + /// `true` if `other` is a prefix of `self` + pub fn starts_with(&self, other: &Self) -> bool { + self.0.starts_with(&other.0) + } + + /// Remove a prefix from a derivation. Return a new DerivationPath without the prefix. + /// This is useful for determining the path to rech some descendant from some ancestor. + pub fn without_prefix(&self, prefix: &Self) -> Option { + if !self.starts_with(prefix) { + None + } else { + Some(self.0[prefix.len()..].to_vec().into()) + } + } + + /// Convenience function for finding the last hardened derivation in a path. + /// Returns the index and the element. If there is no hardened derivation, it + /// will return (0, None). + pub fn last_hardened(&self) -> (usize, Option) { + match self.iter().rev().position(|v| *v >= BIP32_HARDEN) { + Some(rev_pos) => { + let pos = self.len() - rev_pos - 1; + (pos, Some(self.0[pos])) + } + None => (0, None), + } + } + + /// Return a clone with a resized path. If the new size is shorter, this truncates it. If the + /// new path is longer, we pad with the second argument. + pub fn resized(&self, size: usize, pad_with: u32) -> Self { + let mut child = self.clone(); + child.0.resize(size, pad_with); + child + } + + /// Append an additional derivation to the end, return a clone + pub fn extended(&self, idx: u32) -> Self { + let mut child = self.clone(); + child.0.push(idx); + child + } +} + +impl From<&DerivationPath> for DerivationPath { + fn from(v: &DerivationPath) -> Self { + v.clone() + } +} + +impl From> for DerivationPath { + fn from(v: Vec) -> Self { + Self(v) + } +} + +impl From<&Vec> for DerivationPath { + fn from(v: &Vec) -> Self { + Self(v.clone()) + } +} + +impl From<&[u32]> for DerivationPath { + fn from(v: &[u32]) -> Self { + Self(Vec::from(v)) + } +} + +impl FromIterator for DerivationPath { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + Vec::from_iter(iter).into() + } +} + +impl FromStr for DerivationPath { + type Err = Bip32Error; + + fn from_str(s: &str) -> Result { + try_parse_path(s).map(Into::into) + } +} + +/// A Derivation Path for a bip32 key +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct KeyDerivation { + /// The root key fingerprint + pub root: KeyFingerprint, + /// The derivation path from the root key + pub path: DerivationPath, +} + +impl KeyDerivation { + /// `true` if the keys share a root fingerprint, `false` otherwise. Note that on key + /// fingerprints, which may collide accidentally, or be intentionally collided. + pub fn same_root(&self, other: &Self) -> bool { + self.root == other.root + } + + /// `true` if this key is an ancestor of other, `false` otherwise. Note that on key + /// fingerprints, which may collide accidentally, or be intentionally collided. + pub fn is_possible_ancestor_of(&self, other: &Self) -> bool { + self.same_root(other) && other.path.starts_with(&self.path) + } + + /// Returns the path to the decendant. + pub fn path_to_descendant(&self, descendant: &Self) -> Option { + descendant.path.without_prefix(&self.path) + } + + /// Return a clone with a resized path. If the new size is shorter, this truncates it. If the + /// new path is longer, we pad with the second argument. + pub fn resized(&self, size: usize, pad_with: u32) -> Self { + Self { + root: self.root, + path: self.path.resized(size, pad_with), + } + } + + /// Append an additional derivation to the end, return a clone + pub fn extended(&self, idx: u32) -> Self { + Self { + root: self.root, + path: self.path.extended(idx), + } + } +} + +impl ByteFormat for KeyDerivation { + type Error = Bip32Error; + + fn serialized_length(&self) -> usize { + 4 + 4 * self.path.len() + } + + fn read_from(_reader: &mut T) -> Result + where + T: Read, + Self: std::marker::Sized, + { + unimplemented!() + // if limit == 0 { + // return Err(SerError::RequiresLimit.into()); + // } + + // if limit > 255 { + // return Err(Bip32Error::InvalidBip32Path); + // } + + // let mut finger = [0u8; 4]; + // reader.read_exact(&mut finger)?; + + // let mut path = vec![]; + // for _ in 0..limit { + // let mut buf = [0u8; 4]; + // reader.read_exact(&mut buf)?; + // path.push(u32::from_le_bytes(buf)); + // } + + // Ok(KeyDerivation { + // root: finger.into(), + // path: path.into(), + // }) + } + + fn write_to(&self, writer: &mut T) -> Result + where + T: Write, + { + let mut length = writer.write(&self.root.0)?; + for i in self.path.iter() { + length += writer.write(&i.to_le_bytes())?; + } + Ok(length) + } +} + +#[cfg(test)] +pub mod test { + use super::*; + + #[test] + fn it_parses_index_strings() { + let cases = [("32", 32), ("32h", 32 + BIP32_HARDEN), ("0h", BIP32_HARDEN)]; + for case in cases.iter() { + match try_parse_index(&case.0) { + Ok(v) => assert_eq!(v, case.1), + Err(e) => assert!(false, "unexpected error {}", e), + } + } + } + + #[test] + fn it_handles_malformatted_indices() { + let cases = ["-", "h", "toast", "憂鬱"]; + for case in cases.iter() { + match try_parse_index(&case) { + Ok(_) => assert!(false, "expected an error"), + Err(Bip32Error::MalformattedDerivation(e)) => assert_eq!(&e, case), + Err(e) => assert!(false, "unexpected error {}", e), + } + } + } + + #[test] + fn it_parses_derivation_strings() { + let cases = [ + ("m/32", vec![32]), + ("m/32'", vec![32 + BIP32_HARDEN]), + ("m/0'/32/5/5/5", vec![BIP32_HARDEN, 32, 5, 5, 5]), + ("32", vec![32]), + ("32'", vec![32 + BIP32_HARDEN]), + ("0'/32/5/5/5", vec![BIP32_HARDEN, 32, 5, 5, 5]), + ]; + for case in cases.iter() { + match try_parse_path(&case.0) { + Ok(v) => assert_eq!(v, case.1), + Err(e) => assert!(false, "unexpected error {}", e), + } + } + } + + #[test] + fn it_handles_malformatted_derivations() { + let cases = ["//", "m/", "-", "h", "toast", "憂鬱"]; + for case in cases.iter() { + match try_parse_path(&case) { + Ok(_) => assert!(false, "expected an error"), + Err(Bip32Error::MalformattedDerivation(e)) => assert_eq!(&e, case), + Err(e) => assert!(false, "unexpected error {}", e), + } + } + } + + #[test] + fn it_removes_prefixes_from_derivations() { + // express each row in a separate instantiation syntax :) + let cases = [ + ( + DerivationPath(vec![1, 2, 3]), + DerivationPath(vec![1]), + Some(DerivationPath(vec![2, 3])), + ), + ( + vec![1, 2, 3].into(), + vec![1, 2].into(), + Some(vec![3].into()), + ), + ( + (1u32..=3).collect(), + (1u32..=3).collect(), + Some((0..0).collect()), + ), + (DerivationPath(vec![1, 2, 3]), vec![1, 3].into(), None), + ]; + for case in cases.iter() { + assert_eq!(case.0.without_prefix(&case.1), case.2); + } + } + + #[test] + fn it_proudces_paths_from_strings() { + let cases = ["//", "m/", "-", "h", "toast", "憂鬱"]; + + for case in cases.iter() { + let path: Result = case.parse().map_err(Into::into); + match path { + Ok(_) => assert!(false, "expected an error"), + Err(Bip32Error::MalformattedDerivation(e)) => assert_eq!(&e, case), + Err(e) => assert!(false, "unexpected error {}", e), + } + } + } + + #[test] + fn it_stringifies_derivation_paths() { + let cases = [ + (DerivationPath(vec![1, 2, 3]), "m/1/2/3"), + ( + vec![BIP32_HARDEN, BIP32_HARDEN, BIP32_HARDEN].into(), + "m/0'/0'/0'", + ), + ]; + for case in cases.iter() { + assert_eq!(&case.0.derivation_string(), case.1); + } + } +} diff --git a/bip32-old/src/prelude.rs b/bip32-old/src/prelude.rs new file mode 100644 index 00000000..999ab822 --- /dev/null +++ b/bip32-old/src/prelude.rs @@ -0,0 +1,10 @@ +pub use crate::curve::model; +pub use crate::curve::Secp256k1; +pub use crate::enc::XKeyEncoder; +pub use crate::model::*; + +#[cfg(any(feature = "mainnet", feature = "testnet"))] +pub use crate::defaults::*; + +pub use crate::derived::{DerivedPrivkey, DerivedPubkey}; +pub use crate::xkeys::{XPriv, XPub}; diff --git a/bip32-old/src/primitives.rs b/bip32-old/src/primitives.rs new file mode 100644 index 00000000..02ad0741 --- /dev/null +++ b/bip32-old/src/primitives.rs @@ -0,0 +1,102 @@ +use crate::Bip32Error; +use coins_core::ser::ByteFormat; +use std::io::{Read, Write}; + +/// We treat the bip32 xpub bip49 ypub and bip84 zpub convention as a hint regarding address type. +/// Downstream crates are free to follow or ignore these hints when generating addresses from +/// extended keys. +#[derive(Eq, PartialEq, Debug, Clone, Copy)] +pub enum Hint { + /// Standard Bip32 hint + Legacy, + /// Bip32 + Bip49 hint for Witness-via-P2SH + Compatibility, + /// Bip32 + Bip84 hint for Native SegWit + SegWit, +} + +/// A 4-byte key fingerprint +#[derive(Eq, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)] +pub struct KeyFingerprint(pub [u8; 4]); + +impl From<[u8; 4]> for KeyFingerprint { + fn from(v: [u8; 4]) -> Self { + Self(v) + } +} + +impl ByteFormat for KeyFingerprint { + type Error = Bip32Error; + + fn serialized_length(&self) -> usize { + 4 + } + + fn read_from(reader: &mut R) -> Result + where + R: Read, + Self: std::marker::Sized, + { + let mut buf = [0u8; 4]; + reader.read_exact(&mut buf)?; + Ok(Self(buf)) + } + + fn write_to(&self, writer: &mut W) -> Result + where + W: Write, + { + Ok(writer.write(&self.0)?) + } +} + +impl KeyFingerprint { + /// Determines if the slice represents the same key fingerprint + pub fn eq_slice(self, other: &[u8]) -> bool { + self.0 == other + } +} + +impl std::fmt::Debug for KeyFingerprint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("KeyFingerprint {:x?}", self.0)) + } +} + +/// A 32-byte chain code +#[derive(Eq, PartialEq, Debug, Clone, Copy)] +pub struct ChainCode(pub [u8; 32]); + +impl From<[u8; 32]> for ChainCode { + fn from(v: [u8; 32]) -> Self { + Self(v) + } +} + +/// Info associated with an extended key +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct XKeyInfo { + /// The key depth in the HD tree + pub depth: u8, + /// The 4-byte Fingerprint of the parent + pub parent: KeyFingerprint, + /// The 4-byte derivation index of the key. If the most-significant byte is set, this key is + /// hardened + pub index: u32, + /// The 32-byte chain code used to generate child keys + pub chain_code: ChainCode, + /// The key's stanadard output type preference + pub hint: Hint, +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn fingerprint_slice_equality() { + let print = [0; 4]; + let k: KeyFingerprint = print.into(); + assert!(k.eq_slice(&print[..])); + println!("{:?}", &k); + } +} diff --git a/bip32-old/src/xkeys.rs b/bip32-old/src/xkeys.rs new file mode 100644 index 00000000..e78a8484 --- /dev/null +++ b/bip32-old/src/xkeys.rs @@ -0,0 +1,487 @@ +use hmac::{Hmac, Mac}; +use sha2::Sha512; + +use crate::{ + curve::model::{ScalarDeserialize, Secp256k1Backend}, + keys::{GenericPrivkey, GenericPubkey}, + model::*, + primitives::{ChainCode, Hint, KeyFingerprint, XKeyInfo}, + Bip32Error, BIP32_HARDEN, CURVE_ORDER, +}; + +type HmacSha512 = Hmac; + +/// A BIP32 Extended privkey using the library's compiled-in secp256k1 backend. This defaults to +/// libsecp for native, and parity's rust secp for wasm targets +/// +/// For interface documentation see the page for +/// [GenericXPriv](struct.GenericXPriv.html). +pub type XPriv = GenericXPriv<'static, crate::curve::Secp256k1<'static>>; + +impl XPriv { + /// Generate a customized master node using the static backend + pub fn master_node( + hmac_key: &[u8], + data: &[u8], + hint: Option, + ) -> Result { + Self::custom_master_node(hmac_key, data, hint, crate::curve::Secp256k1::static_ref()) + } + + /// Generate a master node from some seed data. Uses the BIP32-standard hmac key. + /// + /// + /// # Important: + /// + /// Use a seed of AT LEAST 128 bits. + pub fn root_from_seed(data: &[u8], hint: Option) -> Result { + Self::custom_root_from_seed(data, hint, crate::curve::Secp256k1::static_ref()) + } +} + +/// A BIP32 Extended pubkey using the library's compiled-in secp256k1 backend. This defaults to +/// libsecp for native, and parity's rust secp for wasm targets +/// +/// For interface documentation see the page for +/// [GenericXPub](struct.GenericXPub.html). +pub type XPub = GenericXPub<'static, crate::curve::Secp256k1<'static>>; + +/// Default BIP32 +pub const SEED: &[u8; 12] = b"Bitcoin seed"; + +fn hmac_and_split(seed: &[u8], data: &[u8]) -> ([u8; 32], ChainCode) { + let mut mac = HmacSha512::new_varkey(seed).expect("key length is ok"); + mac.input(data); + let result = mac.result().code(); + + let mut left = [0u8; 32]; + left.copy_from_slice(&result[..32]); + + let mut right = [0u8; 32]; + right.copy_from_slice(&result[32..]); + + (left, ChainCode(right)) +} + +/// A BIP32 Extended privkey. This key is genericized to accept any compatibile backend. +#[derive(Clone, Debug, PartialEq)] +pub struct GenericXPriv<'a, T: Secp256k1Backend> { + /// The extended key information + pub info: XKeyInfo, + /// The associated secp256k1 key + pub privkey: GenericPrivkey<'a, T>, +} + +inherit_has_privkey!(GenericXPriv.privkey); +inherit_backend!(GenericXPriv.privkey); + +impl<'a, T: Secp256k1Backend> GenericXPriv<'a, T> { + /// Instantiate a master node using a custom HMAC key. + pub fn custom_master_node( + hmac_key: &[u8], + data: &[u8], + hint: Option, + backend: &'a T, + ) -> Result, Bip32Error> { + if data.len() < 16 { + return Err(Bip32Error::SeedTooShort); + } + let parent = KeyFingerprint([0u8; 4]); + let (key, chain_code) = hmac_and_split(hmac_key, data); + if key == [0u8; 32] || key > CURVE_ORDER { + // This can only be tested by mocking hmac_and_split + return Err(Bip32Error::InvalidKey); + } + let privkey = T::Privkey::from_privkey_array(key)?; + Ok(GenericXPriv { + info: XKeyInfo { + depth: 0, + parent, + index: 0, + chain_code, + hint: hint.unwrap_or(Hint::SegWit), + }, + privkey: GenericPrivkey { + key: privkey, + backend: Some(backend), + }, + }) + } + + /// Generate a master node from some seed data. Uses the BIP32-standard hmac key. + /// + /// + /// # Important: + /// + /// Use a seed of AT LEAST 128 bits. + pub fn custom_root_from_seed( + data: &[u8], + hint: Option, + backend: &'a T, + ) -> Result, Bip32Error> { + Self::custom_master_node(SEED, data, hint, backend) + } + + /// Derive the corresponding xpub + pub fn to_xpub(&self) -> Result, Bip32Error> { + Ok(GenericXPub { + info: self.info, + pubkey: self.privkey.derive_verifying_key()?, + }) + } +} + +impl<'a, T: Secp256k1Backend> HasXKeyInfo for GenericXPriv<'a, T> { + fn xkey_info(&self) -> &XKeyInfo { + &self.info + } +} + +impl<'a, T: Secp256k1Backend> SigningKey<'a, T> for GenericXPriv<'a, T> { + /// The corresponding verifying key + type VerifyingKey = GenericXPub<'a, T>; + + /// Derive the corresponding pubkey + fn derive_verifying_key(&self) -> Result { + self.to_xpub() + } +} + +impl<'a, T: Secp256k1Backend> DerivePrivateChild<'a, T> for GenericXPriv<'a, T> { + fn derive_private_child(&self, index: u32) -> Result, Bip32Error> { + let hardened = index >= BIP32_HARDEN; + + let mut data: Vec = vec![]; + if hardened { + data.push(0); + data.extend(&self.privkey_bytes()); + data.extend(&index.to_be_bytes()); + } else { + data.extend(&self.derive_pubkey_bytes()?.to_vec()); + data.extend(&index.to_be_bytes()); + }; + + let (tweak, chain_code) = hmac_and_split(&self.chain_code().0, &data); + let privkey = self + .backend()? + .tweak_privkey(&self.privkey(), tweak) + .map(|k| GenericPrivkey { + key: k, + backend: self.backend().ok(), + }) + .map_err(Into::into)?; + + Ok(GenericXPriv { + info: XKeyInfo { + depth: self.depth() + 1, + parent: self.derive_fingerprint()?, + index, + chain_code, + hint: self.hint(), + }, + privkey, + }) + } +} + +/// A BIP32 Extended privkey. This key is genericized to accept any compatibile backend. +#[derive(Clone, Debug, PartialEq)] +pub struct GenericXPub<'a, T: Secp256k1Backend> { + /// The extended key information + pub info: XKeyInfo, + /// The associated secp256k1 key + pub pubkey: GenericPubkey<'a, T>, +} + +inherit_has_pubkey!(GenericXPub.pubkey); +inherit_backend!(GenericXPub.pubkey); + +impl<'a, T: Secp256k1Backend> GenericXPub<'a, T> { + /// Derive an XPub from an xpriv + pub fn from_xpriv(xpriv: &GenericXPriv<'a, T>) -> Result, Bip32Error> { + xpriv.to_xpub() + } +} + +impl<'a, T: Secp256k1Backend> HasXKeyInfo for GenericXPub<'a, T> { + fn xkey_info(&self) -> &XKeyInfo { + &self.info + } +} + +impl<'a, T: Secp256k1Backend> VerifyingKey<'a, T> for GenericXPub<'a, T> { + type SigningKey = GenericXPriv<'a, T>; +} + +impl<'a, T: Secp256k1Backend> DerivePublicChild<'a, T> for GenericXPub<'a, T> { + fn derive_public_child(&self, index: u32) -> Result, Bip32Error> { + if index >= BIP32_HARDEN { + return Err(Bip32Error::HardenedDerivationFailed); + } + let mut data: Vec = self.pubkey_bytes().to_vec(); + data.extend(&index.to_be_bytes()); + + let (offset, chain_code) = hmac_and_split(&self.chain_code().0, &data); + // TODO: check for point at infinity + if offset > CURVE_ORDER { + return self.derive_public_child(index + 1); + } + + let pubkey = self + .backend()? + .tweak_pubkey(&self.pubkey(), offset) + .map(|k| GenericPubkey { + key: k, + backend: self.backend().ok(), + }) + .map_err(Into::into)?; + + Ok(Self { + info: XKeyInfo { + depth: self.depth() + 1, + parent: self.fingerprint(), + index, + chain_code, + hint: self.hint(), + }, + pubkey, + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + curve::Secp256k1, + enc::{MainnetEncoder, XKeyEncoder}, + keys::Pubkey, + primitives::*, + }; + use coins_core::hashes::Hash256Digest; + + use hex; + + struct KeyDeriv<'a> { + pub path: &'a [u32], + pub xpub: String, + pub xpriv: String, + } + + fn validate_descendant<'a>(d: &KeyDeriv, m: &XPriv) { + let xpriv = m.derive_private_path(d.path).unwrap(); + let xpub = xpriv.to_xpub().unwrap(); + + let deser_xpriv = + MainnetEncoder::xpriv_from_base58(&d.xpriv, xpriv.backend().ok()).unwrap(); + let deser_xpub = MainnetEncoder::xpub_from_base58(&d.xpub, xpriv.backend().ok()).unwrap(); + + assert_eq!(&xpriv, &deser_xpriv); + assert_eq!(MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(), d.xpriv); + assert_eq!(&xpub, &deser_xpub); + assert_eq!(MainnetEncoder::xpub_to_base58(&xpub).unwrap(), d.xpub); + } + + #[test] + fn bip32_vector_1() { + let seed: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let backend = Secp256k1::static_ref(); + + let xpriv = XPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); + let xpub = xpriv.to_xpub().unwrap(); + + let expected_xpub = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"; + let expected_xpriv = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"; + + let deser_xpub = MainnetEncoder::xpub_from_base58(&expected_xpub, Some(backend)).unwrap(); + let deser_xpriv = + MainnetEncoder::xpriv_from_base58(&expected_xpriv, Some(backend)).unwrap(); + + assert_eq!(&xpriv, &deser_xpriv); + assert_eq!( + MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(), + expected_xpriv + ); + assert_eq!(&xpub, &deser_xpub); + assert_eq!( + MainnetEncoder::xpub_to_base58(&xpub).unwrap(), + expected_xpub + ); + + let descendants = [ + KeyDeriv { + path: &[0 + BIP32_HARDEN], + xpub: "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw".to_owned(), + xpriv: "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7".to_owned(), + }, + KeyDeriv { + path: &[0 + BIP32_HARDEN, 1], + xpub: "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ".to_owned(), + xpriv: "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs".to_owned(), + }, + KeyDeriv { + path: &[0 + BIP32_HARDEN, 1, 2 + BIP32_HARDEN], + xpub: "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5".to_owned(), + xpriv: "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM".to_owned(), + }, + KeyDeriv { + path: &[0 + BIP32_HARDEN, 1, 2 + BIP32_HARDEN, 2], + xpub: "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV".to_owned(), + xpriv: "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334".to_owned(), + }, + KeyDeriv { + path: &[0 + BIP32_HARDEN, 1, 2 + BIP32_HARDEN, 2, 1000000000], + xpub: "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy".to_owned(), + xpriv: "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76".to_owned(), + }, + ]; + + for case in descendants.iter() { + validate_descendant(&case, &xpriv); + } + } + + #[test] + fn bip32_vector_2() { + let seed = hex::decode(&"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542").unwrap(); + let backend = Secp256k1::static_ref(); + + let xpriv = XPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); + let xpub = xpriv.to_xpub().unwrap(); + + let expected_xpub = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"; + let expected_xpriv = "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"; + + let deser_xpub = MainnetEncoder::xpub_from_base58(&expected_xpub, Some(backend)).unwrap(); + let deser_xpriv = + MainnetEncoder::xpriv_from_base58(&expected_xpriv, Some(backend)).unwrap(); + + assert_eq!(&xpriv, &deser_xpriv); + assert_eq!( + MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(), + expected_xpriv + ); + assert_eq!(&xpub, &deser_xpub); + assert_eq!( + MainnetEncoder::xpub_to_base58(&xpub).unwrap(), + expected_xpub + ); + + let descendants = [ + KeyDeriv { + path: &[0], + xpub: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH".to_owned(), + xpriv: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt".to_owned(), + }, + KeyDeriv { + path: &[0, 2147483647 + BIP32_HARDEN], + xpub: "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a".to_owned(), + xpriv: "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9".to_owned(), + }, + KeyDeriv { + path: &[0, 2147483647 + BIP32_HARDEN, 1], + xpub: "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon".to_owned(), + xpriv: "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef".to_owned(), + }, + KeyDeriv { + path: &[0, 2147483647 + BIP32_HARDEN, 1, 2147483646 + BIP32_HARDEN], + xpub: "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL".to_owned(), + xpriv: "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc".to_owned(), + }, + KeyDeriv { + path: &[0, 2147483647 + BIP32_HARDEN, 1, 2147483646 + BIP32_HARDEN, 2], + xpub: "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt".to_owned(), + xpriv: "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j".to_owned(), + }, + ]; + + for case in descendants.iter() { + validate_descendant(&case, &xpriv); + } + } + + #[test] + fn bip32_vector_3() { + let seed = hex::decode(&"4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be").unwrap(); + let backend = Secp256k1::static_ref(); + + let xpriv = XPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); + let xpub = xpriv.to_xpub().unwrap(); + + let expected_xpub = "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13"; + let expected_xpriv = "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6"; + + let deser_xpub = MainnetEncoder::xpub_from_base58(&expected_xpub, Some(backend)).unwrap(); + let deser_xpriv = + MainnetEncoder::xpriv_from_base58(&expected_xpriv, Some(backend)).unwrap(); + + assert_eq!(&xpriv, &deser_xpriv); + assert_eq!( + MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(), + expected_xpriv + ); + assert_eq!(&xpub, &deser_xpub); + assert_eq!( + MainnetEncoder::xpub_to_base58(&xpub).unwrap(), + expected_xpub + ); + + let descendants = [ + KeyDeriv { + path: &[0 + BIP32_HARDEN], + xpub: "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y".to_owned(), + xpriv: "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L".to_owned(), + }, + ]; + + for case in descendants.iter() { + validate_descendant(&case, &xpriv); + } + } + + #[test] + fn it_can_sign_and_verify() { + let digest: Hash256Digest = [1u8; 32].into(); + let backend = Secp256k1::static_ref(); + let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); + let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, Some(backend)).unwrap(); + + let child = xpriv.derive_private_child(33).unwrap(); + let sig = child.sign_digest(digest).unwrap(); + + let child_xpub = child.to_xpub().unwrap(); + child_xpub.verify_digest(digest, &sig).unwrap(); + } + + #[test] + fn it_can_verify_and_recover_from_signatures() { + let digest: Hash256Digest = [1u8; 32].into(); + let backend = Secp256k1::static_ref(); + let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); + let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, Some(backend)).unwrap(); + + let child = xpriv.derive_private_child(33).unwrap(); + + let sig = child.sign_digest_recoverable(digest).unwrap(); + + let child_xpub = child.to_xpub().unwrap(); + child_xpub.verify_digest_recoverable(digest, &sig).unwrap(); + + let recovered = + Pubkey::recover_from_signed_digest(xpriv.backend().unwrap(), digest, &sig).unwrap(); + assert_eq!(&recovered.pubkey(), &child_xpub.pubkey()); + } + + #[test] + fn it_can_read_keys_without_a_backend() { + let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); + let _xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None).unwrap(); + } + + #[test] + fn print_key() { + let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); + let xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None).unwrap(); + println!("{:?}", xpriv); + } +} diff --git a/bip32/Cargo.toml b/bip32/Cargo.toml index 023e5242..ac184458 100644 --- a/bip32/Cargo.toml +++ b/bip32/Cargo.toml @@ -16,19 +16,7 @@ lazy_static = "1.4.0" coins-core = { path = "../core"} serde = "1.0.105" -[target.'cfg(not(target_arch = "wasm32"))'.dependencies.secp256k1] -version = "0.17.2" -features = ["recovery"] - -[target.'cfg(target_arch = "wasm32")'.dependencies.libsecp256k1] -git = "https://github.com/paritytech/libsecp256k1.git" -default-features = false -features = ["std", "hmac"] - -[target.'cfg(target_arch = "wasm32")'.dependencies.libsecp256k1-core] -git = "https://github.com/paritytech/libsecp256k1.git" -default-features = false -features = ["std"] +k256 = { version = "0.7.1", features = ["std", "ecdsa", "arithmetic"] } [dev-dependencies] hex = "0.4.2" @@ -36,10 +24,6 @@ criterion = "0.3.1" [features] default = ["mainnet"] -rust-secp-static-context = ["libsecp256k1/static-context"] mainnet = [] testnet = [] -[[bench]] -name = "bench" -harness = false diff --git a/bip32/src/defaults.rs b/bip32/src/defaults.rs index 10b5288c..1bfc84f5 100644 --- a/bip32/src/defaults.rs +++ b/bip32/src/defaults.rs @@ -1,5 +1,3 @@ -use serde::{de::Visitor, ser::SerializeStruct}; - use crate::enc::XKeyEncoder; /// The default encoder, selected by feature flag @@ -10,23 +8,23 @@ pub type Encoder = crate::enc::MainnetEncoder; #[cfg(feature = "testnet")] pub type Encoder = crate::enc::TestnetEncoder; -impl std::str::FromStr for crate::XPriv { +impl std::str::FromStr for crate::xkeys::XPriv { type Err = crate::Bip32Error; fn from_str(s: &str) -> Result { - Encoder::xpriv_from_base58(s, Some(&crate::curve::BACKEND)) + Encoder::xpriv_from_base58(s) } } -impl std::str::FromStr for crate::XPub { +impl std::str::FromStr for crate::xkeys::XPub { type Err = crate::Bip32Error; fn from_str(s: &str) -> Result { - Encoder::xpub_from_base58(s, Some(&crate::curve::BACKEND)) + Encoder::xpub_from_base58(s) } } -impl serde::Serialize for crate::XPub { +impl serde::Serialize for crate::xkeys::XPub { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -37,18 +35,17 @@ impl serde::Serialize for crate::XPub { } } -impl<'de> serde::Deserialize<'de> for crate::XPub { - fn deserialize(deserializer: D) -> Result +impl<'de> serde::Deserialize<'de> for crate::xkeys::XPub { + fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let s: &str = serde::Deserialize::deserialize(deserializer)?; - Encoder::xpub_from_base58(s, Some(crate::Secp256k1::static_ref())) - .map_err(|e| serde::de::Error::custom(e.to_string())) + Encoder::xpub_from_base58(s).map_err(|e| serde::de::Error::custom(e.to_string())) } } -impl serde::Serialize for crate::XPriv { +impl serde::Serialize for crate::xkeys::XPriv { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -59,177 +56,12 @@ impl serde::Serialize for crate::XPriv { } } -impl<'de> serde::Deserialize<'de> for crate::XPriv { - fn deserialize(deserializer: D) -> Result +impl<'de> serde::Deserialize<'de> for crate::xkeys::XPriv { + fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let s: &str = serde::Deserialize::deserialize(deserializer)?; - Encoder::xpriv_from_base58(s, Some(crate::Secp256k1::static_ref())) - .map_err(|e| serde::de::Error::custom(e.to_string())) - } -} - -impl serde::Serialize for crate::DerivedXPriv { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut state = serializer.serialize_struct("DerivedXPriv", 2)?; - state.serialize_field("derivation", &self.derivation)?; - state.serialize_field("xpriv", &self.xpriv)?; - state.end() - } -} - -struct DerivedXPrivVisitor; - -impl<'de> Visitor<'de> for DerivedXPrivVisitor { - type Value = crate::DerivedXPriv; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("struct DerivedXPriv") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: serde::de::SeqAccess<'de>, - { - let xpriv = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - let derivation = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?; - Ok(crate::DerivedXPriv { xpriv, derivation }) - } - - fn visit_map(self, mut map: V) -> Result - where - V: serde::de::MapAccess<'de>, - { - let mut xpriv = None; - let mut derivation = None; - - #[derive(serde::Deserialize)] - #[serde(field_identifier, rename_all = "lowercase")] - enum Field { - XPriv, - Derivation, - } - - while let Some(key) = map.next_key()? { - match key { - Field::XPriv => { - if xpriv.is_some() { - return Err(serde::de::Error::duplicate_field("xpriv")); - } - xpriv = Some(map.next_value()?); - } - Field::Derivation => { - if derivation.is_some() { - return Err(serde::de::Error::duplicate_field("derivation")); - } - derivation = Some(map.next_value()?); - } - } - } - - let xpriv = xpriv.ok_or_else(|| serde::de::Error::missing_field("xpriv"))?; - let derivation = derivation.ok_or_else(|| serde::de::Error::missing_field("derivation"))?; - - Ok(crate::DerivedXPriv { xpriv, derivation }) - } -} - -impl<'de> serde::Deserialize<'de> for crate::DerivedXPriv { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &["xpriv", "derivation"]; - deserializer.deserialize_struct("Duration", FIELDS, DerivedXPrivVisitor) - } -} - -impl serde::Serialize for crate::DerivedXPub { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut state = serializer.serialize_struct("DerivedXPub", 2)?; - state.serialize_field("derivation", &self.derivation)?; - state.serialize_field("xpub", &self.xpub)?; - state.end() - } -} - -struct DerivedXPubVisitor; - -impl<'de> Visitor<'de> for DerivedXPubVisitor { - type Value = crate::DerivedXPub; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("struct DerivedXPub") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: serde::de::SeqAccess<'de>, - { - let xpub = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - let derivation = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?; - Ok(crate::DerivedXPub { xpub, derivation }) - } - - fn visit_map(self, mut map: V) -> Result - where - V: serde::de::MapAccess<'de>, - { - let mut xpub = None; - let mut derivation = None; - - #[derive(serde::Deserialize)] - #[serde(field_identifier, rename_all = "lowercase")] - enum Field { - XPub, - Derivation, - } - - while let Some(key) = map.next_key()? { - match key { - Field::XPub => { - if xpub.is_some() { - return Err(serde::de::Error::duplicate_field("xpub")); - } - xpub = Some(map.next_value()?); - } - Field::Derivation => { - if derivation.is_some() { - return Err(serde::de::Error::duplicate_field("derivation")); - } - derivation = Some(map.next_value()?); - } - } - } - - let xpub = xpub.ok_or_else(|| serde::de::Error::missing_field("xpub"))?; - let derivation = derivation.ok_or_else(|| serde::de::Error::missing_field("derivation"))?; - - Ok(crate::DerivedXPub { xpub, derivation }) - } -} - -impl<'de> serde::Deserialize<'de> for crate::DerivedXPub { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &["xpub", "derivation"]; - deserializer.deserialize_struct("Duration", FIELDS, DerivedXPubVisitor) + Encoder::xpriv_from_base58(s).map_err(|e| serde::de::Error::custom(e.to_string())) } } diff --git a/bip32/src/derived.rs b/bip32/src/derived.rs index d1e01aac..7bcfc61a 100644 --- a/bip32/src/derived.rs +++ b/bip32/src/derived.rs @@ -1,136 +1,134 @@ +use k256::ecdsa; + +use coins_core::prelude::{Hash160, Hash160Digest, MarkedDigest}; + use crate::{ - curve::model::Secp256k1Backend, - keys::{GenericPrivkey, GenericPubkey}, - model::*, - path::KeyDerivation, - primitives::Hint, - xkeys::{GenericXPriv, GenericXPub, SEED}, + path::{DerivationPath, KeyDerivation}, + primitives::{Hint, XKeyInfo}, + xkeys::{Parent, XPriv, XPub, SEED}, Bip32Error, }; -/// A GenericDerivedPrivkey using the compiled-in default backend, coupled with its (purported) -/// derivation path. -/// -/// For interface documentation see the page for -/// [GenericDerivedPrivkey](struct.GenericDerivedPrivkey.html). -pub type DerivedPrivkey = GenericDerivedPrivkey<'static, crate::curve::Secp256k1<'static>>; - -/// A GenericDerivedPubkey using the compiled-in default backend, coupled with its (purported) -/// derivation path. -/// -/// For interface documentation see the page for -/// [GenericDerivedPubkey](struct.GenericDerivedPubkey.html). -pub type DerivedPubkey = GenericDerivedPubkey<'static, crate::curve::Secp256k1<'static>>; - -/// A GenericDerivedXPriv using the compiled-in default backend, coupled with its (purported) -/// derivation path. -/// -/// For interface documentation see the page for -/// [GenericDerivedXPriv](struct.GenericDerivedXPriv.html). -pub type DerivedXPriv = GenericDerivedXPriv<'static, crate::curve::Secp256k1<'static>>; - -/// A GenericDerivedXPub using the compiled-in default backend, coupled with its (purported) -/// derivation path. -/// -/// For interface documentation see the page for -/// [GenericDerivedXPub](struct.GenericDerivedXPub.html). -pub type DerivedXPub = GenericDerivedXPub<'static, crate::curve::Secp256k1<'static>>; - -make_derived_key!( - /// A `Privkey` coupled with its (purported) derivation path. Generally this struct - /// should be used over Privkey wherever possible, in order to preserve information ancestry - /// relationship information. +/// Derived keys are keys coupled with their derivation. We use this trait to +/// check ancestry relationships between keys. +pub trait DerivedKey { + /// Return this key's derivation + fn derivation(&self) -> &KeyDerivation; + + /// `true` if the keys share a root fingerprint, `false` otherwise. Note that on key + /// fingerprints, which may collide accidentally, or be intentionally collided. + fn same_root(&self, other: &K) -> bool { + self.derivation().same_root(&other.derivation()) + } + + /// `true` if this key is a possible ancestor of the argument, `false` otherwise. /// - /// Warning: derivation paths from untrusted sources may be faulty. Make sure to check - /// ancestry using the `DerivedKey` trait methods. - GenericPrivkey, - GenericDerivedPrivkey.privkey -); -inherit_has_privkey!(GenericDerivedPrivkey.privkey); -inherit_backend!(GenericDerivedPrivkey.privkey); - -impl<'a, T: Secp256k1Backend> SigningKey<'a, T> for GenericDerivedPrivkey<'a, T> { - type VerifyingKey = GenericDerivedPubkey<'a, T>; - - fn derive_verifying_key(&self) -> Result { - Ok(GenericDerivedPubkey { - pubkey: self.privkey.derive_verifying_key()?, - derivation: self.derivation.clone(), - }) + /// Warning: this check is cheap, but imprecise. It simply compares the root fingerprints + /// (which may collide) and checks that `self.path` is a prefix of `other.path`. This may be + /// deliberately foold by an attacker. For a precise check, use + /// `DerivedXPriv::is_private_ancestor_of()` or + /// `DerivedXPub::is_public_ancestor_of()` + fn is_possible_ancestor_of(&self, other: &K) -> bool { + self.derivation() + .is_possible_ancestor_of(&other.derivation()) + } + + /// Returns the path to the descendant, or `None` if the argument is definitely not a + /// descendant. + /// + /// This is useful for determining the path to reach some descendant from some ancestor. + fn path_to_descendant(&self, other: &K) -> Option { + self.derivation().path_to_descendant(&other.derivation()) } } -make_derived_key!( - /// A `GenericPubkey` coupled with its (purported) derivation path. Generally this struct - /// should be used over Pubkey wherever possible, in order to preserve information ancestry - /// relationship information. - /// - /// Warning: derivation paths from untrusted sources may be faulty. Make sure to check - /// ancestry using the `DerivedKey` trait methods. - GenericPubkey, - GenericDerivedPubkey.pubkey -); -inherit_has_pubkey!(GenericDerivedPubkey.pubkey); -inherit_backend!(GenericDerivedPubkey.pubkey); - -impl<'a, T: Secp256k1Backend> VerifyingKey<'a, T> for GenericDerivedPubkey<'a, T> { - type SigningKey = GenericDerivedPrivkey<'a, T>; +/// An XPriv with its derivation. +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct DerivedXPriv { + xpriv: XPriv, + derivation: KeyDerivation, } -make_derived_key!( - /// A `GenericXPriv` coupled with its (purported) derivation path. Generally this struct - /// should be used over XPriv wherever possible, in order to preserve information ancestry - /// relationship information. - /// - /// Warning: derivation paths from untrusted sources may be faulty. Make sure to check - /// ancestry using the `DerivedKey` trait methods. - GenericXPriv, - GenericDerivedXPriv.xpriv -); -inherit_has_privkey!(GenericDerivedXPriv.xpriv); -inherit_backend!(GenericDerivedXPriv.xpriv); -inherit_has_xkeyinfo!(GenericDerivedXPriv.xpriv); +inherit_signer!(DerivedXPriv.xpriv); + +impl AsRef for DerivedXPriv { + fn as_ref(&self) -> &XPriv { + &self.xpriv + } +} + +impl AsRef for DerivedXPriv { + fn as_ref(&self) -> &XKeyInfo { + &self.xpriv.xkey_info + } +} + +impl AsRef for DerivedXPriv { + fn as_ref(&self) -> &ecdsa::SigningKey { + &self.xpriv.key + } +} + +impl DerivedKey for DerivedXPriv { + fn derivation(&self) -> &KeyDerivation { + &self.derivation + } +} impl DerivedXPriv { - /// Generate a customized master node using the static backend - pub fn master_node( + /// Instantiate a derived XPub from the XPub and derivatin. This usually + /// should not be called directly. Prefer deriving keys from parents. + pub fn new(xpriv: XPriv, derivation: KeyDerivation) -> Self { + Self { xpriv, derivation } + } + + /// Check if this XPriv is the private ancestor of some other derived key. + /// To check ancestry of another private key, derive its public key first + pub fn is_private_ancestor_of(&self, other: &DerivedXPub) -> Result { + if let Some(path) = self.path_to_descendant(other) { + let descendant = self.derive_path(&path)?; + Ok(descendant.verify_key() == *other) + } else { + Ok(false) + } + } + + /// Generate a customized root node using the static backend + pub fn root_node( hmac_key: &[u8], data: &[u8], hint: Option, ) -> Result { - Self::custom_master_node(hmac_key, data, hint, crate::curve::Secp256k1::static_ref()) + Self::custom_root_node(hmac_key, data, hint) } - /// Generate a master node from some seed data. Uses the BIP32-standard hmac key. + /// Generate a root node from some seed data. Uses the BIP32-standard hmac key. /// /// /// # Important: /// /// Use a seed of AT LEAST 128 bits. pub fn root_from_seed(data: &[u8], hint: Option) -> Result { - Self::custom_root_from_seed(data, hint, crate::curve::Secp256k1::static_ref()) + Self::custom_root_from_seed(data, hint) } -} -impl<'a, T: Secp256k1Backend> GenericDerivedXPriv<'a, T> { - /// Instantiate a master node using a custom HMAC key. - pub fn custom_master_node( + /// Instantiate a root node using a custom HMAC key. + pub fn custom_root_node( hmac_key: &[u8], data: &[u8], hint: Option, - backend: &'a T, - ) -> Result, Bip32Error> { - let xpriv = GenericXPriv::custom_master_node(hmac_key, data, hint, backend)?; + ) -> Result { + let xpriv = XPriv::custom_root_node(hmac_key, data, hint)?; let derivation = KeyDerivation { - root: xpriv.derive_fingerprint()?, + root: xpriv.fingerprint(), path: vec![].into(), }; - Ok(GenericDerivedXPriv { xpriv, derivation }) + Ok(DerivedXPriv { xpriv, derivation }) } - /// Generate a master node from some seed data. Uses the BIP32-standard hmac key. + /// Generate a root node from some seed data. Uses the BIP32-standard hmac key. /// /// /// # Important: @@ -139,531 +137,116 @@ impl<'a, T: Secp256k1Backend> GenericDerivedXPriv<'a, T> { pub fn custom_root_from_seed( data: &[u8], hint: Option, - backend: &'a T, - ) -> Result, Bip32Error> { - Self::custom_master_node(SEED, data, hint, backend) + ) -> Result { + Self::custom_root_node(SEED, data, hint) } /// Derive the corresponding xpub - pub fn to_derived_xpub(&self) -> Result, Bip32Error> { - Ok(GenericDerivedXPub { - xpub: self.xpriv.derive_verifying_key()?, + pub fn verify_key(&self) -> DerivedXPub { + DerivedXPub { + xpub: self.xpriv.verify_key(), derivation: self.derivation.clone(), - }) - } - - /// Check if this XPriv is the private ancestor of some other derived key - pub fn is_private_ancestor_of>( - &self, - other: &D, - ) -> Result { - if let Some(path) = self.path_to_descendant(other) { - let descendant = self.derive_private_path(&path)?; - let descendant_pk_bytes = descendant.derive_pubkey()?; - Ok(&descendant_pk_bytes == other.pubkey()) - } else { - Ok(false) } } } -impl<'a, T: Secp256k1Backend> SigningKey<'a, T> for GenericDerivedXPriv<'a, T> { - /// The corresponding verifying key - type VerifyingKey = GenericDerivedXPub<'a, T>; - - /// Derive the corresponding pubkey - fn derive_verifying_key(&self) -> Result { - self.to_derived_xpub() - } -} - -impl<'a, T: Secp256k1Backend> DerivePrivateChild<'a, T> for GenericDerivedXPriv<'a, T> { - fn derive_private_child(&self, index: u32) -> Result { +impl Parent for DerivedXPriv { + fn derive_child(&self, index: u32) -> Result { Ok(Self { - xpriv: self.xpriv.derive_private_child(index)?, + xpriv: self.xpriv.derive_child(index)?, derivation: self.derivation.extended(index), }) } } -make_derived_key!( - /// A `GenericXPub` coupled with its (purported) derivation path. Generally this struct - /// should be used over XPub wherever possible, in order to preserve information ancestry - /// relationship information. - /// - /// Warning: derivation paths from untrusted sources may be faulty. Make sure to check - /// ancestry using the `DerivedKey` trait methods. - GenericXPub, - GenericDerivedXPub.xpub -); -inherit_has_pubkey!(GenericDerivedXPub.xpub); -inherit_backend!(GenericDerivedXPub.xpub); -inherit_has_xkeyinfo!(GenericDerivedXPub.xpub); - -impl<'a, T: Secp256k1Backend> GenericDerivedXPub<'a, T> { - /// Check if this XPriv is the private ancestor of some other derived key - pub fn is_public_ancestor_of>( - &self, - other: &D, - ) -> Result { - if let Some(path) = self.path_to_descendant(other) { - let descendant = self.derive_public_path(&path)?; - Ok(descendant.pubkey() == other.pubkey()) - } else { - Ok(false) - } - } +/// An XPub with its derivation. +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)] +pub struct DerivedXPub { + xpub: XPub, + derivation: KeyDerivation, } -impl<'a, T: Secp256k1Backend> VerifyingKey<'a, T> for GenericDerivedXPub<'a, T> { - type SigningKey = GenericDerivedXPriv<'a, T>; -} +inherit_verifier!(DerivedXPub.xpub); -impl<'a, T: Secp256k1Backend> DerivePublicChild<'a, T> for GenericDerivedXPub<'a, T> { - fn derive_public_child(&self, index: u32) -> Result { - Ok(Self { - xpub: self.xpub.derive_public_child(index)?, - derivation: self.derivation.extended(index), - }) +impl AsRef for DerivedXPub { + fn as_ref(&self) -> &XPub { + &self.xpub } } -#[cfg(test)] -mod test { - use super::*; - use crate::{ - curve::*, - enc::{MainnetEncoder, XKeyEncoder}, - path::DerivationPath, - primitives::*, - BIP32_HARDEN, - }; - use coins_core::hashes::*; - - use hex; - - struct KeyDeriv<'a> { - pub path: &'a [u32], +impl AsRef for DerivedXPub { + fn as_ref(&self) -> &XKeyInfo { + &self.xpub.xkey_info } +} - fn validate_descendant(d: &KeyDeriv, m: &DerivedXPriv) { - let path: DerivationPath = d.path.into(); - - let m_pub = m.derive_verifying_key().unwrap(); - - let xpriv = m.derive_private_path(&path).unwrap(); - let xpub = xpriv.derive_verifying_key().unwrap(); - assert!(m.same_root(&xpriv)); - assert!(m.same_root(&xpub)); - assert!(m.is_possible_ancestor_of(&xpriv)); - assert!(m.is_possible_ancestor_of(&xpub)); - - let result = m.is_private_ancestor_of(&xpub).expect("should work"); - - if !result { - assert!(false, "failed validate_descendant is_private_ancestor_of"); - } - - let result = m_pub.is_public_ancestor_of(&xpub); - - match result { - Ok(true) => {} - Ok(false) => assert!(false, "failed validate_descendant is_public_ancestor_of"), - Err(_) => { - let path: crate::path::DerivationPath = d.path.into(); - assert!( - path.last_hardened().1.is_some(), - "is_public_ancestor_of failed for unhardened path" - ) - } - } - - let derived_path = m - .path_to_descendant(&xpriv) - .expect("expected a path to descendant"); - assert_eq!(&path, &derived_path, "derived path is not as expected"); +impl AsRef for DerivedXPub { + fn as_ref(&self) -> &ecdsa::VerifyingKey { + &self.xpub.key } +} - #[test] - fn bip32_vector_1() { - let seed: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - - let xpriv = DerivedXPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); - - let descendants = [ - KeyDeriv { - path: &[0 + BIP32_HARDEN], - }, - KeyDeriv { - path: &[0 + BIP32_HARDEN, 1], - }, - KeyDeriv { - path: &[0 + BIP32_HARDEN, 1, 2 + BIP32_HARDEN], - }, - KeyDeriv { - path: &[0 + BIP32_HARDEN, 1, 2 + BIP32_HARDEN, 2], - }, - KeyDeriv { - path: &[0 + BIP32_HARDEN, 1, 2 + BIP32_HARDEN, 2, 1000000000], - }, - ]; - - for case in descendants.iter() { - validate_descendant(&case, &xpriv); - } +impl Parent for DerivedXPub { + fn derive_child(&self, index: u32) -> Result { + Ok(Self { + xpub: self.xpub.derive_child(index)?, + derivation: self.derivation.extended(index), + }) } +} - #[test] - fn bip32_vector_2() { - let seed = hex::decode(&"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542").unwrap(); - - let xpriv = DerivedXPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); - - let descendants = [ - KeyDeriv { path: &[0] }, - KeyDeriv { - path: &[0, 2147483647 + BIP32_HARDEN], - }, - KeyDeriv { - path: &[0, 2147483647 + BIP32_HARDEN, 1], - }, - KeyDeriv { - path: &[0, 2147483647 + BIP32_HARDEN, 1, 2147483646 + BIP32_HARDEN], - }, - KeyDeriv { - path: &[ - 0, - 2147483647 + BIP32_HARDEN, - 1, - 2147483646 + BIP32_HARDEN, - 2, - ], - }, - ]; - - for case in descendants.iter() { - validate_descendant(&case, &xpriv); - } +impl DerivedKey for DerivedXPub { + fn derivation(&self) -> &KeyDerivation { + &self.derivation } +} - #[test] - fn bip32_vector_3() { - let seed = hex::decode(&"4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be").unwrap(); - - let xpriv = DerivedXPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); - - let descendants = [KeyDeriv { - path: &[0 + BIP32_HARDEN], - }]; - - for case in descendants.iter() { - validate_descendant(&case, &xpriv); - } +impl DerivedXPub { + /// Instantiate a derived XPub from the XPub and derivatin. This usually + /// should not be called directly. Prefer deriving keys from parents. + pub fn new(xpub: XPub, derivation: KeyDerivation) -> Self { + Self { xpub, derivation } } - #[test] - fn it_can_sign_and_verify() { - let digest: Hash256Digest = [1u8; 32].into(); - let wrong_digest: Hash256Digest = [2u8; 32].into(); - let backend = Secp256k1::static_ref(); - - let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); - let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, Some(backend)).unwrap(); - let fake_deriv = KeyDerivation { - root: [0, 0, 0, 0].into(), - path: (0..0).collect(), - }; - - let mut key = DerivedXPriv::new(xpriv, fake_deriv); - let mut key_pub = DerivedXPub::from_signing_key(&key).unwrap(); - // These had to go somewhere. here is as good as any - key.set_backend(backend); - key_pub.set_backend(backend); - - // sign_digest + verify_digest - let sig = key.sign_digest(digest).unwrap(); - key_pub.verify_digest(digest, &sig).unwrap(); - - let err_bad_sig = key_pub.verify_digest(wrong_digest, &sig); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } - - // sign_digest_recoverable + verify_digest_recoverable - let sig = key.sign_digest_recoverable(digest).unwrap(); - key_pub.verify_digest_recoverable(digest, &sig).unwrap(); - - let err_bad_sig = key_pub.verify_digest_recoverable(wrong_digest, &sig); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } - - // sign_with_hash + verify_with_hash - let sig = key.sign_with_hash::(digest.as_slice()).unwrap(); - key_pub - .verify_with_hash::(digest.as_slice(), &sig) - .unwrap(); - - let err_bad_sig = key_pub.verify_with_hash::(wrong_digest.as_slice(), &sig); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } - - // sign_recoverable_with_hash + verify_recoverable_with_hash - let sig = key - .sign_recoverable_with_hash::(digest.as_slice()) - .unwrap(); - key_pub - .verify_recoverable_with_hash::(digest.as_slice(), &sig) - .unwrap(); - - let err_bad_sig = - key_pub.verify_recoverable_with_hash::(wrong_digest.as_slice(), &sig); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } - - // sign + verify - let sig = key.sign(digest.as_slice()).unwrap(); - key_pub.verify(digest.as_slice(), &sig).unwrap(); - - let err_bad_sig = key_pub.verify(wrong_digest.as_slice(), &sig); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } - - // sign_recoverable + verify_recoverable - let sig = key.sign_recoverable(digest.as_slice()).unwrap(); - key_pub.verify_recoverable(digest.as_slice(), &sig).unwrap(); - - let err_bad_sig = key_pub.verify_recoverable(wrong_digest.as_slice(), &sig); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), + /// Check if this XPriv is the private ancestor of some other derived key + pub fn is_public_ancestor_of(&self, other: &DerivedXPub) -> Result { + if let Some(path) = self.path_to_descendant(other) { + let descendant = self.derive_path(&path)?; + Ok(descendant == *other) + } else { + Ok(false) } } +} - #[test] - fn it_can_descendant_sign_and_verify() { - let digest: Hash256Digest = [1u8; 32].into(); - let wrong_digest: Hash256Digest = [2u8; 32].into(); - let backend = Secp256k1::static_ref(); - - let path = vec![0u32, 1, 2]; - - let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); - let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, Some(backend)).unwrap(); - let fake_deriv = KeyDerivation { - root: [0, 0, 0, 0].into(), - path: (0..0).collect(), - }; - - let mut key = DerivedXPriv::new(xpriv, fake_deriv.clone()); - let mut key_pub = DerivedXPub::from_signing_key(&key).unwrap(); - // These had to go somewhere. here is as good as any - assert_eq!(key.derivation(), &fake_deriv); - key.set_backend(backend); - key_pub.set_backend(backend); - - // sign_digest + verify_digest - let sig = key.descendant_sign_digest(&path, digest).unwrap(); - key_pub - .descendant_verify_digest(&path, digest, &sig) - .unwrap(); - - let err_bad_sig = key_pub.descendant_verify_digest(&path, wrong_digest, &sig); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } - - // sign_digest_recoverable + verify_digest_recoverable - let sig = key - .descendant_sign_digest_recoverable(&path, digest) - .unwrap(); - key_pub - .descendant_verify_digest_recoverable(&path, digest, &sig) - .unwrap(); - - let err_bad_sig = key_pub.descendant_verify_digest_recoverable(&path, wrong_digest, &sig); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } - - // sign_with_hash + verify_with_hash - let sig = key - .descendant_sign_with_hash::(&path, digest.as_slice()) - .unwrap(); - key_pub - .descendant_verify_with_hash::(&path, digest.as_slice(), &sig) - .unwrap(); - - let err_bad_sig = key_pub.descendant_verify_with_hash::( - &path, - wrong_digest.as_slice(), - &sig, - ); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } - - // sign_recoverable_with_hash + verify_recoverable_with_hash - let sig = key - .descendant_sign_recoverable_with_hash::(&path, digest.as_slice()) - .unwrap(); - key_pub - .descendant_verify_recoverable_with_hash::( - &path, - digest.as_slice(), - &sig, - ) - .unwrap(); - - let err_bad_sig = key_pub.descendant_verify_recoverable_with_hash::( - &path, - wrong_digest.as_slice(), - &sig, - ); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } - - // sign + verify - let sig = key.descendant_sign(&path, digest.as_slice()).unwrap(); - key_pub - .descendant_verify(&path, digest.as_slice(), &sig) - .unwrap(); - - let err_bad_sig = key_pub.descendant_verify(&path, wrong_digest.as_slice(), &sig); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } +/// A Pubkey with its derivation. Primarily used by PSBT. +pub struct DerivedPubkey { + key: ecdsa::VerifyingKey, + derivation: KeyDerivation, +} - // sign_recoverable + verify_recoverable - let sig = key - .descendant_sign_recoverable(&path, digest.as_slice()) - .unwrap(); - key_pub - .descendant_verify_recoverable(&path, digest.as_slice(), &sig) - .unwrap(); - - let err_bad_sig = - key_pub.descendant_verify_recoverable(&path, wrong_digest.as_slice(), &sig); - match err_bad_sig { - Err(Bip32Error::BackendError(_)) => {} - _ => assert!(false, "expected signature validation error"), - } +inherit_verifier!(DerivedPubkey.key); - // Sig serialize/deserialize - let der_sig = hex::decode("3045022100e838d64bb95cdacc1b93f94ad8c2fcc10441e672f66565aca374d5a955d99672022022283ac21bc8c64b7265e71b1972b051b1a818a99ae0db3d563489e55b9826a3").unwrap(); - let vrs = ( - 1, - [ - 232, 56, 214, 75, 185, 92, 218, 204, 27, 147, 249, 74, 216, 194, 252, 193, 4, 65, - 230, 114, 246, 101, 101, 172, 163, 116, 213, 169, 85, 217, 150, 114, - ], - [ - 34, 40, 58, 194, 27, 200, 198, 75, 114, 101, 231, 27, 25, 114, 176, 81, 177, 168, - 24, 169, 154, 224, 219, 61, 86, 52, 137, 229, 91, 152, 38, 163, - ], - ); - assert_eq!(sig.to_der(), der_sig); - assert_eq!(sig.serialize_vrs(), vrs); - assert_eq!( - sig.without_recovery(), - backend::Signature::try_from_der(&der_sig).unwrap() - ); - assert_eq!( - sig, - backend::RecoverableSignature::deserialize_vrs(vrs).unwrap() - ); - - let err_no_rec_id = backend::RecoverableSignature::try_from_der(&der_sig); - match err_no_rec_id { - Err(Bip32Error::NoRecoveryID) => {} - _ => assert!(false, "expected err no rec id"), - }; +impl DerivedKey for DerivedPubkey { + fn derivation(&self) -> &KeyDerivation { + &self.derivation } +} - #[test] - fn it_derives_verifying_keys() { - let backend = Secp256k1::static_ref(); - let fake_deriv = KeyDerivation { - root: [0, 0, 0, 0].into(), - path: (0..0).collect(), - }; - - let key = crate::curve::Privkey::from_privkey_array([1u8; 32]).unwrap(); - - let privkey = crate::keys::Privkey { - key, - backend: Some(backend), - }; - - let key = DerivedPrivkey::new(privkey, fake_deriv); - - key.derive_verifying_key().unwrap(); +impl AsRef for DerivedPubkey { + fn as_ref(&self) -> &ecdsa::VerifyingKey { + &self.key } +} - #[test] - fn it_instantiates_derived_xprivs_from_seeds() { - let backend = Secp256k1::static_ref(); - GenericDerivedXPriv::custom_root_from_seed(&[0u8; 32][..], None, backend).unwrap(); - - let err_too_short = - GenericDerivedXPriv::custom_root_from_seed(&[0u8; 2][..], None, backend); - match err_too_short { - Err(Bip32Error::SeedTooShort) => {} - _ => assert!(false, "expected err too short"), - } - - let err_too_short = - GenericDerivedXPriv::custom_root_from_seed(&[0u8; 2][..], None, backend); - match err_too_short { - Err(Bip32Error::SeedTooShort) => {} - _ => assert!(false, "expected err too short"), - } +impl DerivedPubkey { + /// Instantiate a new ` + pub fn new(key: ecdsa::VerifyingKey, derivation: KeyDerivation) -> Self { + Self { key, derivation } } - #[test] - fn it_checks_ancestry() { - let backend = Secp256k1::static_ref(); - let m = GenericDerivedXPriv::custom_root_from_seed(&[0u8; 32][..], None, backend).unwrap(); - let m2 = GenericDerivedXPriv::custom_root_from_seed(&[1u8; 32][..], None, backend).unwrap(); - let m_pub = GenericDerivedXPub::from_signing_key(&m).unwrap(); - let cases = [ - (&m, &m_pub, true), - (&m2, &m_pub, false), - (&m, &m2.derive_verifying_key().unwrap(), false), - ( - &m, - &m.derive_private_child(33) - .unwrap() - .derive_verifying_key() - .unwrap(), - true, - ), - (&m, &m_pub.derive_public_child(33).unwrap(), true), - ( - &m, - &m2.derive_private_child(33) - .unwrap() - .derive_verifying_key() - .unwrap(), - false, - ), - ]; - for case in cases.iter() { - assert_eq!(case.0.is_private_ancestor_of(case.1).unwrap(), case.2); - } + /// Return the hash of the compressed (Sec1) pubkey. + pub fn pubkey_hash160(&self) -> Hash160Digest { + Hash160::digest_marked(&self.key.to_bytes()) } } diff --git a/bip32/src/enc.rs b/bip32/src/enc.rs index 05f7d263..9854c571 100644 --- a/bip32/src/enc.rs +++ b/bip32/src/enc.rs @@ -1,13 +1,10 @@ -use std::marker::PhantomData; - use coins_core::hashes::{Digest, Hash256}; +use k256::ecdsa; +use std::marker::PhantomData; use crate::{ - curve::model::{PointDeserialize, ScalarDeserialize, Secp256k1Backend}, - keys::{GenericPrivkey, GenericPubkey}, - model::{HasPrivkey, HasPubkey, XKey}, primitives::{ChainCode, Hint, KeyFingerprint, XKeyInfo}, - xkeys::{GenericXPriv, GenericXPub}, + xkeys::{XPriv, XPub}, Bip32Error, }; @@ -58,35 +55,66 @@ pub trait NetworkParams { const BIP84_PUB_VERSION: u32; } +params!( + /// Mainnet encoding param + Main { + bip32: 0x0488_ADE4, + bip49: 0x049d_7878, + bip84: 0x04b2_430c, + bip32_pub: 0x0488_B21E, + bip49_pub: 0x049d_7cb2, + bip84_pub: 0x04b2_4746 + } +); + +params!( + /// Testnet encoding param + Test { + bip32: 0x0435_8394, + bip49: 0x044a_4e28, + bip84: 0x045f_18bc, + bip32_pub: 0x0435_87CF, + bip49_pub: 0x044a_5262, + bip84_pub: 0x045f_1cf6 + } +); + +/// Parameterizable Bitcoin encoder +#[derive(Debug, Clone)] +pub struct BitcoinEncoder(PhantomData P>); + +/// XKeyEncoder for Mainnet xkeys +pub type MainnetEncoder = BitcoinEncoder
; +/// XKeyEncoder for Testnet xkeys +pub type TestnetEncoder = BitcoinEncoder; + /// Bip32/49/84 encoder pub trait XKeyEncoder { #[doc(hidden)] fn write_key_details(writer: &mut W, key: &K) -> Result where - K: XKey, + K: AsRef, W: std::io::Write, { - let mut written = writer.write(&[key.depth()])?; - written += writer.write(&key.parent().0)?; - written += writer.write(&key.index().to_be_bytes())?; - written += writer.write(&key.chain_code().0)?; + let key = key.as_ref(); + let mut written = writer.write(&[key.depth])?; + written += writer.write(&key.parent.0)?; + written += writer.write(&key.index.to_be_bytes())?; + written += writer.write(&key.chain_code.0)?; Ok(written) } /// Serialize the xpub to `std::io::Write` - fn write_xpub<'a, W, T>(writer: &mut W, key: &GenericXPub<'a, T>) -> Result + fn write_xpub(writer: &mut W, key: &K) -> Result where W: std::io::Write, - T: Secp256k1Backend; + K: AsRef; /// Serialize the xpriv to `std::io::Write` - fn write_xpriv<'a, W, T>( - writer: &mut W, - key: &GenericXPriv<'a, T>, - ) -> Result + fn write_xpriv(writer: &mut W, key: &K) -> Result where W: std::io::Write, - T: Secp256k1Backend; + K: AsRef; #[doc(hidden)] fn read_depth(reader: &mut R) -> Result @@ -129,14 +157,9 @@ pub trait XKeyEncoder { } #[doc(hidden)] - fn read_xpriv_body<'a, R, T>( - reader: &mut R, - hint: Hint, - backend: Option<&'a T>, - ) -> Result, Bip32Error> + fn read_xpriv_body(reader: &mut R, hint: Hint) -> Result where R: std::io::Read, - T: Secp256k1Backend, { let depth = Self::read_depth(reader)?; let parent = Self::read_parent(reader)?; @@ -151,73 +174,52 @@ pub trait XKeyEncoder { let mut buf = [0u8; 32]; reader.read_exact(&mut buf)?; - let key = T::Privkey::from_privkey_array(buf)?; + let key = ecdsa::SigningKey::from_bytes(&buf)?; - Ok(GenericXPriv { - info: XKeyInfo { + Ok(XPriv { + key, + xkey_info: XKeyInfo { depth, parent, index, chain_code, hint, }, - privkey: GenericPrivkey { key, backend }, }) } #[doc(hidden)] // Can be used for unhealthy but sometimes-desiable behavior. E.g. accepting an xpriv from any // network. - fn read_xpriv_without_network<'a, R, T>( - reader: &mut R, - backend: Option<&'a T>, - ) -> Result, Bip32Error> + fn read_xpriv_without_network(reader: &mut R) -> Result where R: std::io::Read, - T: Secp256k1Backend, { let mut buf = [0u8; 4]; reader.read_exact(&mut buf)?; - Self::read_xpriv_body(reader, Hint::Legacy, backend) + Self::read_xpriv_body(reader, Hint::Legacy) } /// Attempt to instantiate an `XPriv` from a `std::io::Read` /// - /// # Note - /// - /// If passing in None, you must hint the return type. This can be the convenience type alias - /// (e.g. XPub) or the full type `GenericXPub` - /// /// ``` - /// use coins_bip32::{Bip32Error, XPriv, enc::{XKeyEncoder, MainnetEncoder}}; + /// use coins_bip32::{Bip32Error, xkeys::XPriv, enc::{XKeyEncoder, MainnetEncoder}}; /// # fn main() -> Result<(), Bip32Error> { /// let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); /// - /// // // can't infer type of generic parameter - /// // let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None)?; - /// - /// let xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None)?; + /// let xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str)?; /// # Ok(()) /// # } /// ``` - fn read_xpriv<'a, R, T>( - reader: &mut R, - backend: Option<&'a T>, - ) -> Result, Bip32Error> + fn read_xpriv(reader: &mut R) -> Result where - R: std::io::Read, - T: Secp256k1Backend; + R: std::io::Read; #[doc(hidden)] - fn read_xpub_body<'a, R, T>( - reader: &mut R, - hint: Hint, - backend: Option<&'a T>, - ) -> Result, Bip32Error> + fn read_xpub_body(reader: &mut R, hint: Hint) -> Result where R: std::io::Read, - T: Secp256k1Backend, { let depth = Self::read_depth(reader)?; let parent = Self::read_parent(reader)?; @@ -226,68 +228,52 @@ pub trait XKeyEncoder { let mut buf = [0u8; 33]; reader.read_exact(&mut buf)?; - let key = T::Pubkey::from_pubkey_array(buf)?; + let key = ecdsa::VerifyingKey::from_sec1_bytes(&buf)?; - Ok(GenericXPub { - info: XKeyInfo { + Ok(XPub { + key, + xkey_info: XKeyInfo { depth, parent, index, chain_code, hint, }, - pubkey: GenericPubkey { key, backend }, }) } #[doc(hidden)] // Can be used for unhealthy but sometimes-desiable behavior. E.g. accepting an xpub from any // network. - fn read_xpub_without_network<'a, R, T>( - reader: &mut R, - backend: Option<&'a T>, - ) -> Result, Bip32Error> + fn read_xpub_without_network(reader: &mut R) -> Result where R: std::io::Read, - T: Secp256k1Backend, { let mut buf = [0u8; 4]; reader.read_exact(&mut buf)?; - Self::read_xpub_body(reader, Hint::Legacy, backend) + Self::read_xpub_body(reader, Hint::Legacy) } - /// Attempt to instantiate an `XPriv` from a `std::io::Read` - /// - /// # Note - /// - /// If passing in None, you must hint the return type. This can be the convenience type alias - /// (e.g. XPub) or the full type `GenericXPub` + /// Attempt to instantiate an `XPub` from a `std::io::Read` /// /// ``` - /// use coins_bip32::{Bip32Error, XPub, enc::{XKeyEncoder, MainnetEncoder}}; + /// use coins_bip32::{Bip32Error, xkeys::XPub, enc::{XKeyEncoder, MainnetEncoder}}; /// # fn main() -> Result<(), Bip32Error> { /// let xpub_str = "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y".to_owned(); /// - /// // // can't infer type of generic parameter - /// // let xpub = MainnetEncoder::xpub_from_base58(&xpub_str, None)?; - /// - /// let xpub: XPub = MainnetEncoder::xpub_from_base58(&xpub_str, None)?; + /// let xpub: XPub = MainnetEncoder::xpub_from_base58(&xpub_str)?; /// # Ok(()) /// # } /// ``` - fn read_xpub<'a, R, T>( - reader: &mut R, - backend: Option<&'a T>, - ) -> Result, Bip32Error> + fn read_xpub(reader: &mut R) -> Result where - R: std::io::Read, - T: Secp256k1Backend; + R: std::io::Read; /// Serialize an XPriv to base58 - fn xpriv_to_base58(k: &GenericXPriv) -> Result + fn xpriv_to_base58(k: &K) -> Result where - T: Secp256k1Backend, + K: AsRef, { let mut v: Vec = vec![]; Self::write_xpriv(&mut v, k)?; @@ -295,9 +281,9 @@ pub trait XKeyEncoder { } /// Serialize an XPub to base58 - fn xpub_to_base58(k: &GenericXPub) -> Result + fn xpub_to_base58(k: &K) -> Result where - T: Secp256k1Backend, + K: AsRef, { let mut v: Vec = vec![]; Self::write_xpub(&mut v, k)?; @@ -306,136 +292,78 @@ pub trait XKeyEncoder { /// Attempt to read an XPriv from a b58check string. /// - /// # Note - /// - /// If passing in None, you must hint the return type. This can be the convenience type alias - /// (e.g. XPub) or the full type `GenericXPub` - /// /// ``` - /// use coins_bip32::{Bip32Error, XPriv, enc::{XKeyEncoder, MainnetEncoder}}; + /// use coins_bip32::{Bip32Error, xkeys::XPriv, enc::{XKeyEncoder, MainnetEncoder}}; /// # fn main() -> Result<(), Bip32Error> { /// let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); /// - /// // // can't infer type of generic parameter - /// // let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None)?; - /// - /// let xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None)?; + /// let xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str)?; /// # Ok(()) /// # } /// ``` - fn xpriv_from_base58<'a, T>( - s: &str, - backend: Option<&'a T>, - ) -> Result, Bip32Error> - where - T: Secp256k1Backend, - { + fn xpriv_from_base58(s: &str) -> Result +where { let data = decode_b58_check(s)?; - Self::read_xpriv(&mut &data[..], backend) + Self::read_xpriv(&mut &data[..]) } /// Attempt to read an XPub from a b58check string /// - /// # Note - /// - /// If passing in None, you must hint the return type. This can be the convenience type alias - /// (e.g. XPub) or the full type `GenericXPub` - /// /// ``` - /// use coins_bip32::{Bip32Error, XPub, enc::{XKeyEncoder, MainnetEncoder}}; + /// use coins_bip32::{Bip32Error, xkeys::XPub, enc::{XKeyEncoder, MainnetEncoder}}; /// # fn main() -> Result<(), Bip32Error> { /// let xpub_str = "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y".to_owned(); /// - /// // // can't infer type of generic parameter - /// // let xpub = MainnetEncoder::xpub_from_base58(&xpub_str, None)?; - /// - /// let xpub: XPub = MainnetEncoder::xpub_from_base58(&xpub_str, None)?; + /// let xpub: XPub = MainnetEncoder::xpub_from_base58(&xpub_str)?; /// # Ok(()) /// # } /// ``` - fn xpub_from_base58<'a, T>( - s: &str, - backend: Option<&'a T>, - ) -> Result, Bip32Error> - where - T: Secp256k1Backend, - { + fn xpub_from_base58(s: &str) -> Result +where { let data = decode_b58_check(s)?; - Self::read_xpub(&mut &data[..], backend) + Self::read_xpub(&mut &data[..]) } } -params!( - /// Mainnet encoding param - Main { - bip32: 0x0488_ADE4, - bip49: 0x049d_7878, - bip84: 0x04b2_430c, - bip32_pub: 0x0488_B21E, - bip49_pub: 0x049d_7cb2, - bip84_pub: 0x04b2_4746 - } -); - -params!( - /// Testnet encoding param - Test { - bip32: 0x0435_8394, - bip49: 0x044a_4e28, - bip84: 0x045f_18bc, - bip32_pub: 0x0435_87CF, - bip49_pub: 0x044a_5262, - bip84_pub: 0x045f_1cf6 - } -); - -/// Parameterizable Bitcoin encoder -#[derive(Debug, Clone)] -pub struct BitcoinEncoder(PhantomData P>); - impl XKeyEncoder for BitcoinEncoder

{ /// Serialize the xpub to `std::io::Write` - fn write_xpub<'a, W, T>(writer: &mut W, key: &GenericXPub<'a, T>) -> Result + fn write_xpub(writer: &mut W, key: &K) -> Result where W: std::io::Write, - T: Secp256k1Backend, + K: AsRef, { - let version = match key.hint() { + let version = match key.as_ref().xkey_info.hint { Hint::Legacy => P::PUB_VERSION, Hint::Compatibility => P::BIP49_PUB_VERSION, Hint::SegWit => P::BIP84_PUB_VERSION, }; let mut written = writer.write(&version.to_be_bytes())?; - written += Self::write_key_details(writer, key)?; - written += writer.write(&key.pubkey_bytes())?; + written += Self::write_key_details(writer, key.as_ref())?; + written += writer.write(&key.as_ref().key.to_bytes())?; Ok(written) } /// Serialize the xpriv to `std::io::Write` - fn write_xpriv<'a, W, T>(writer: &mut W, key: &GenericXPriv<'a, T>) -> Result + fn write_xpriv(writer: &mut W, key: &K) -> Result where W: std::io::Write, - T: Secp256k1Backend, + K: AsRef, { - let version = match key.hint() { + let version = match key.as_ref().xkey_info.hint { Hint::Legacy => P::PRIV_VERSION, Hint::Compatibility => P::BIP49_PRIV_VERSION, Hint::SegWit => P::BIP84_PRIV_VERSION, }; let mut written = writer.write(&version.to_be_bytes())?; - written += Self::write_key_details(writer, key)?; + written += Self::write_key_details(writer, key.as_ref())?; written += writer.write(&[0])?; - written += writer.write(&key.privkey_bytes())?; + written += writer.write(&key.as_ref().key.to_bytes())?; Ok(written) } - fn read_xpriv<'a, R, T>( - reader: &mut R, - backend: Option<&'a T>, - ) -> Result, Bip32Error> + fn read_xpriv(reader: &mut R) -> Result where R: std::io::Read, - T: Secp256k1Backend, { let mut buf = [0u8; 4]; reader.read_exact(&mut buf)?; @@ -451,16 +379,12 @@ impl XKeyEncoder for BitcoinEncoder

{ } else { return Err(Bip32Error::BadXPrivVersionBytes(buf)); }; - Self::read_xpriv_body(reader, hint, backend) + Self::read_xpriv_body(reader, hint) } - fn read_xpub<'a, R, T>( - reader: &mut R, - backend: Option<&'a T>, - ) -> Result, Bip32Error> + fn read_xpub(reader: &mut R) -> Result where R: std::io::Read, - T: Secp256k1Backend, { let mut buf = [0u8; 4]; reader.read_exact(&mut buf)?; @@ -476,23 +400,6 @@ impl XKeyEncoder for BitcoinEncoder

{ } else { return Err(Bip32Error::BadXPrivVersionBytes(buf)); }; - Self::read_xpub_body(reader, hint, backend) - } -} - -/// XKeyEncoder for Mainnet xkeys -pub type MainnetEncoder = BitcoinEncoder

; -/// XKeyEncoder for Testnet xkeys -pub type TestnetEncoder = BitcoinEncoder; - -#[cfg(test)] -mod test { - use super::*; - use crate::xkeys::XPriv; - - #[test] - fn it_can_read_keys_without_a_backend() { - let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); - let _xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None).unwrap(); + Self::read_xpub_body(reader, hint) } } diff --git a/bip32/src/lib.rs b/bip32/src/lib.rs index 9c329e43..b605cc7b 100644 --- a/bip32/src/lib.rs +++ b/bip32/src/lib.rs @@ -1,111 +1,65 @@ -//! This crate provides a basic implementation of BIP32, BIP49, and BIP84 with configurable -//! backends. It can be easily adapted to support other networks, using the paramaterizable -//! encoder. +//! This crate provides a basic implementation of BIP32, BIP49, and BIP84. +//! It can be easily adapted to support other networks, using the +//! paramaterizable encoder. +//! //! //! Typically, users will want to use the `MainnetEncoder`, `DerivedXPub`, `DerivedXPriv` types, //! which are available at the crate root. If key derivations are unknown, use the `XPub` and //! `XPriv` objects instead. These may be deserialized using a network-specific `Encoder` from the //! `enc` module. //! -//! Useful traits will need to be imported from the `enc` or `model` modules. Most users will want -//! `model::SigningKey`, `model::VerifyingKey`, `enc::Encoder`, and `enc::HasPubkey`. +//! Useful traits will need to be imported from the `enc` or `model` modules. +//! We also provide a `prelude` module with everything you need to get started. //! //! # Warnings: //! //! - This crate is NOT designed to be used in adversarial environments. //! - This crate has NOT had a comprehensive security review. -//! - It is not clear whether the rust-secp backend's functions are constant-time. //! //! # Usage -//! -//! Most key objects need a backend. They can be instantiated without one, but most operations -//! you want (e.g. signing, verifying, key derivation) will fail at runtime. Simple usage: -//! //! ``` -//! use coins_bip32::{ -//! Bip32Error, Secp256k1, XPub, XPriv, -//! enc::{XKeyEncoder, MainnetEncoder}, -//! model::*, -//! curve::model::*, -//! }; +//! use coins_bip32::prelude::*; //! //! # fn main() -> Result<(), Bip32Error> { -//! let digest = [1u8; 32]; +//! let digest = coins_core::Hash256::default(); //! -//! // `init` sets up a new `lazy_static` backend. Successive calls will re-use that backend -//! // without needing to re-initialize it. -//! let backend = Secp256k1::static_ref(); //! let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); //! -//! // Be sure to annotate the type //! let xpriv: XPriv = xpriv_str.parse().unwrap(); //! -//! let child_xpriv = xpriv.derive_private_child(33)?; -//! let sig = child_xpriv.sign_digest(digest.into())?; +//! let child_xpriv = xpriv.derive_child(33)?; +//! let sig: RecoverableSignature = child_xpriv.sign_digest(digest.clone()); //! //! // Signing key types are associated with verifying key types. You can always derive a pubkey -//! // from a privkey using `derive_verifying_key()`. -//! let child_xpub = child_xpriv.derive_verifying_key()?; -//! child_xpub.verify_digest(digest.into(), &sig); +//! let child_xpub = child_xpriv.verify_key(); +//! child_xpub.verify_digest(digest.clone(), &sig); //! -//! sig.to_der(); // serialize to der-encoded byte-array //! MainnetEncoder::xpub_to_base58(&child_xpub)?; //! # Ok(()) //! # } //! ``` -//! -//! The backend is configurable. By default, it uses bindings to Pieter Wuille's C `libsecp256k1`. -//! Turning off standard features, and compiling with the `rust-secp` feature will use a pure rust -//! backend. Users can provide their own backend by implementing the `Secp256k1Backend` trait. In -//! this case, you will need to use the `Generic` variants of the structs found in the `keys`, -//! `xkeys`, and `derived modules.` These backends are mutually exclusive. So to use `rust-secp` -//! you must disable default features. Compilation will fail otherwise. -//! -//! Additionally, both provided backends allow user-provided context objects via the -//! `Secp256k1Backend::from_context()` method. We also provide access to `lazy_static` on-demand -//! contexts via `Secp256k1Backend::init()`. This has a 1-time cost. The -//! `rust-secp-static-context` allows for compilation-time generation of the context, but must -//! be used with the `rust-secp` backend. #![forbid(unsafe_code)] #![warn(missing_docs)] #![warn(unused_extern_crates)] -#[cfg(not(feature = "rust-secp-static-context"))] -#[macro_use] -extern crate lazy_static; +pub use k256::ecdsa; -#[cfg_attr(tarpaulin, skip)] #[macro_use] pub(crate) mod macros; -/// Everything needed for common usage -pub mod prelude; +/// Network-differentiated encoders for extended keys. +pub mod enc; + +/// `DerivationPath` type and tooling for parsing it from strings +pub mod path; /// Low-level types pub mod primitives; -/// Keys and related functionality -pub mod keys; - /// Extended keys and related functionality pub mod xkeys; -/// Network-differentiated encoders for extended keys. -pub mod enc; - -/// The Secp256k1 backend and its associated traits. Compiled-in backends may be selected using -/// crate features. Generally, this module's traits shouldn't be used directly, with the notable -/// exception of `SigSerialize` and `RecoverableSigSerialize`. -#[cfg_attr(tarpaulin, skip)] -pub mod curve; - -/// Traits and other high-level model description for Bip32 keys. -pub mod model; - -/// `DerivationPath` type and tooling for parsing it from strings -pub mod path; - /// Provides keys that are coupled with their derivation path pub mod derived; @@ -113,20 +67,8 @@ pub mod derived; #[cfg(any(feature = "mainnet", feature = "testnet"))] pub mod defaults; -#[cfg(any(feature = "mainnet", feature = "testnet"))] -pub use defaults::Encoder; - -pub use enc::MainnetEncoder; -pub use model::*; -pub use primitives::KeyFingerprint; - -pub use crate::{ - curve::{RecoverableSignature, Secp256k1, Signature}, - derived::{DerivedXPriv, DerivedXPub}, - enc::XKeyEncoder, - keys::{Privkey, Pubkey}, - xkeys::{XPriv, XPub}, -}; +/// Quickstart types and traits +pub mod prelude; use thiserror::Error; @@ -143,12 +85,12 @@ pub const CURVE_ORDER: [u8; 32] = [ #[derive(Debug, Error)] pub enum Bip32Error { /// Error bubbled up from the backend - #[error(transparent)] - BackendError(#[from] crate::curve::Error), + #[error("k256 error")] + BackendError(/*#[from]*/ ecdsa::Error), - /// General wrapper for errors from custom backends - #[error("Custom backend returned error with info: {0}")] - CustomBackendError(String), + /// Error bubbled up from the backend + #[error("elliptic curve error")] + EllipticCurveError(/*#[from]*/ k256::elliptic_curve::Error), /// Error bubbled up froom std::io #[error(transparent)] @@ -182,10 +124,6 @@ pub enum Bip32Error { #[error("Version bytes 0x{0:x?} don't match any network xpub version bytes")] BadXPubVersionBytes([u8; 4]), - /// No backed in xtended key - #[error("Attempted to operate on an extended key without supplying a backend")] - NoBackend, - /// Bad padding byte on serialized xprv #[error("Expected 0 padding byte. Got {0}")] BadPadding(u8), @@ -211,6 +149,18 @@ pub enum Bip32Error { InvalidBip32Path, } +impl From for Bip32Error { + fn from(e: ecdsa::Error) -> Self { + Self::BackendError(e) + } +} + +impl From for Bip32Error { + fn from(e: k256::elliptic_curve::Error) -> Self { + Self::EllipticCurveError(e) + } +} + impl From for Bip32Error { fn from(_i: std::convert::Infallible) -> Self { unimplemented!("unreachable, but required by type system") diff --git a/bip32/src/macros.rs b/bip32/src/macros.rs index 34e12a1f..17a5f316 100644 --- a/bip32/src/macros.rs +++ b/bip32/src/macros.rs @@ -1,109 +1,105 @@ -macro_rules! params { - ( - $(#[$outer:meta])* - $name:ident{ - bip32: $bip32:expr, - bip49: $bip49:expr, - bip84: $bip84:expr, - bip32_pub: $bip32pub:expr, - bip49_pub: $bip49pub:expr, - bip84_pub: $bip84pub:expr - } - ) => { - $(#[$outer])* - #[derive(Debug, Clone)] - pub struct $name; - - impl crate::enc::NetworkParams for $name { - const PRIV_VERSION: u32 = $bip32; - const BIP49_PRIV_VERSION: u32 = $bip49; - const BIP84_PRIV_VERSION: u32 = $bip84; - const PUB_VERSION: u32 = $bip32pub; - const BIP49_PUB_VERSION: u32 = $bip49pub; - const BIP84_PUB_VERSION: u32 = $bip84pub; - } - } -} - -macro_rules! inherit_backend { +macro_rules! inherit_signer { ($struct_name:ident.$attr:ident) => { - impl<'a, T: crate::curve::model::Secp256k1Backend> crate::model::HasBackend<'a, T> - for $struct_name<'a, T> + impl k256::ecdsa::signature::DigestSigner for $struct_name + where + D: k256::elliptic_curve::digest::BlockInput + + k256::elliptic_curve::digest::FixedOutput< + OutputSize = k256::elliptic_curve::consts::U32, + > + Clone + + Default + + k256::elliptic_curve::digest::Reset + + k256::elliptic_curve::digest::Update, { - fn set_backend(&mut self, backend: &'a T) { - self.$attr.set_backend(backend) + fn try_sign_digest( + &self, + digest: D, + ) -> Result { + self.$attr.try_sign_digest(digest) } + } - fn backend(&self) -> Result<&'a T, Bip32Error> { - self.$attr.backend() + impl k256::ecdsa::signature::DigestSigner + for $struct_name + where + D: k256::elliptic_curve::digest::BlockInput + + k256::elliptic_curve::digest::FixedOutput< + OutputSize = k256::elliptic_curve::consts::U32, + > + Clone + + Default + + k256::elliptic_curve::digest::Reset + + k256::elliptic_curve::digest::Update, + { + fn try_sign_digest( + &self, + digest: D, + ) -> Result { + self.$attr.try_sign_digest(digest) } } }; } -macro_rules! inherit_has_privkey { +macro_rules! inherit_verifier { ($struct_name:ident.$attr:ident) => { - impl<'a, T: crate::curve::model::Secp256k1Backend> crate::model::HasPrivkey<'a, T> - for $struct_name<'a, T> - { - fn privkey(&self) -> &T::Privkey { - self.$attr.privkey() + impl $struct_name { + /// Get the sec1 representation of the public key + pub fn to_bytes(&self) -> [u8; 33] { + self.$attr.to_bytes() } } - }; -} -macro_rules! inherit_has_pubkey { - ($struct_name:ident.$attr:ident) => { - impl<'a, T: crate::curve::model::Secp256k1Backend> crate::model::HasPubkey<'a, T> - for $struct_name<'a, T> + impl k256::ecdsa::signature::DigestVerifier for $struct_name + where + D: k256::elliptic_curve::digest::Digest, { - fn pubkey(&self) -> &T::Pubkey { - self.$attr.pubkey() + fn verify_digest( + &self, + digest: D, + signature: &k256::ecdsa::Signature, + ) -> Result<(), k256::ecdsa::Error> { + self.$attr.verify_digest(digest, signature) } } - }; -} -macro_rules! inherit_has_xkeyinfo { - ($struct_name:ident.$attr:ident) => { - impl<'a, T: crate::curve::model::Secp256k1Backend> crate::model::HasXKeyInfo - for $struct_name<'a, T> + impl k256::ecdsa::signature::DigestVerifier + for $struct_name + where + D: k256::elliptic_curve::digest::Digest, { - fn xkey_info(&self) -> &crate::primitives::XKeyInfo { - self.$attr.xkey_info() + fn verify_digest( + &self, + digest: D, + signature: &k256::ecdsa::recoverable::Signature, + ) -> Result<(), k256::ecdsa::Error> { + self.$attr.verify_digest(digest, signature) } } }; } -macro_rules! make_derived_key { +macro_rules! params { ( $(#[$outer:meta])* - $underlying:ident, $struct_name:ident.$attr:ident + $name:ident{ + bip32: $bip32:expr, + bip49: $bip49:expr, + bip84: $bip84:expr, + bip32_pub: $bip32pub:expr, + bip49_pub: $bip49pub:expr, + bip84_pub: $bip84pub:expr + } ) => { $(#[$outer])* - #[derive(Clone, Debug, PartialEq)] - pub struct $struct_name<'a, T: Secp256k1Backend> { - /// The underlying key - pub $attr: $underlying<'a, T>, - /// Its derivation from some master key - pub derivation: crate::path::KeyDerivation, - } - - impl<'a, T: Secp256k1Backend> crate::model::DerivedKey for $struct_name<'a, T> { - type Key = $underlying<'a, T>; - - fn new(k: Self::Key, derivation: KeyDerivation) -> Self { - Self { - $attr: k, - derivation, - } - } + #[derive(Debug, Clone)] + pub struct $name; - fn derivation(&self) -> &KeyDerivation { - &self.derivation - } + impl crate::enc::NetworkParams for $name { + const PRIV_VERSION: u32 = $bip32; + const BIP49_PRIV_VERSION: u32 = $bip49; + const BIP84_PRIV_VERSION: u32 = $bip84; + const PUB_VERSION: u32 = $bip32pub; + const BIP49_PUB_VERSION: u32 = $bip49pub; + const BIP84_PUB_VERSION: u32 = $bip84pub; } } } diff --git a/bip32/src/prelude.rs b/bip32/src/prelude.rs index 999ab822..fcdae385 100644 --- a/bip32/src/prelude.rs +++ b/bip32/src/prelude.rs @@ -1,10 +1,20 @@ -pub use crate::curve::model; -pub use crate::curve::Secp256k1; -pub use crate::enc::XKeyEncoder; -pub use crate::model::*; +pub use crate::derived::{DerivedKey, DerivedPubkey, DerivedXPriv, DerivedXPub}; +pub use crate::enc::{MainnetEncoder, TestnetEncoder, XKeyEncoder}; +pub use crate::path::KeyDerivation; +pub use crate::primitives::*; +pub use crate::xkeys::{Parent, XPriv, XPub}; +pub use crate::Bip32Error; #[cfg(any(feature = "mainnet", feature = "testnet"))] pub use crate::defaults::*; -pub use crate::derived::{DerivedPrivkey, DerivedPubkey}; -pub use crate::xkeys::{XPriv, XPub}; +/// Re-exported signer traits +pub use k256::ecdsa::{ + recoverable::Signature as RecoverableSignature, + signature::{DigestSigner, DigestVerifier}, + Signature, SigningKey, VerifyingKey, +}; + +// TODOS: +// 1. der +// 2. derived key/ diff --git a/bip32/src/primitives.rs b/bip32/src/primitives.rs index 02ad0741..acd80047 100644 --- a/bip32/src/primitives.rs +++ b/bip32/src/primitives.rs @@ -88,15 +88,3 @@ pub struct XKeyInfo { /// The key's stanadard output type preference pub hint: Hint, } - -#[cfg(test)] -mod test { - use super::*; - #[test] - fn fingerprint_slice_equality() { - let print = [0; 4]; - let k: KeyFingerprint = print.into(); - assert!(k.eq_slice(&print[..])); - println!("{:?}", &k); - } -} diff --git a/bip32/src/xkeys.rs b/bip32/src/xkeys.rs index e78a8484..bd6405a5 100644 --- a/bip32/src/xkeys.rs +++ b/bip32/src/xkeys.rs @@ -1,250 +1,337 @@ +use coins_core::hashes::{Hash160, Hash160Digest, MarkedDigest, MarkedDigestOutput}; use hmac::{Hmac, Mac}; +use k256::ecdsa; use sha2::Sha512; +use std::{convert::TryInto, ops::Mul}; use crate::{ - curve::model::{ScalarDeserialize, Secp256k1Backend}, - keys::{GenericPrivkey, GenericPubkey}, - model::*, + path::DerivationPath, primitives::{ChainCode, Hint, KeyFingerprint, XKeyInfo}, - Bip32Error, BIP32_HARDEN, CURVE_ORDER, + Bip32Error, BIP32_HARDEN, }; -type HmacSha512 = Hmac; - -/// A BIP32 Extended privkey using the library's compiled-in secp256k1 backend. This defaults to -/// libsecp for native, and parity's rust secp for wasm targets -/// -/// For interface documentation see the page for -/// [GenericXPriv](struct.GenericXPriv.html). -pub type XPriv = GenericXPriv<'static, crate::curve::Secp256k1<'static>>; - -impl XPriv { - /// Generate a customized master node using the static backend - pub fn master_node( - hmac_key: &[u8], - data: &[u8], - hint: Option, - ) -> Result { - Self::custom_master_node(hmac_key, data, hint, crate::curve::Secp256k1::static_ref()) - } - - /// Generate a master node from some seed data. Uses the BIP32-standard hmac key. - /// - /// - /// # Important: - /// - /// Use a seed of AT LEAST 128 bits. - pub fn root_from_seed(data: &[u8], hint: Option) -> Result { - Self::custom_root_from_seed(data, hint, crate::curve::Secp256k1::static_ref()) - } -} - -/// A BIP32 Extended pubkey using the library's compiled-in secp256k1 backend. This defaults to -/// libsecp for native, and parity's rust secp for wasm targets -/// -/// For interface documentation see the page for -/// [GenericXPub](struct.GenericXPub.html). -pub type XPub = GenericXPub<'static, crate::curve::Secp256k1<'static>>; - -/// Default BIP32 +/// The BIP32-defined seed used for derivation of the root node. pub const SEED: &[u8; 12] = b"Bitcoin seed"; -fn hmac_and_split(seed: &[u8], data: &[u8]) -> ([u8; 32], ChainCode) { - let mut mac = HmacSha512::new_varkey(seed).expect("key length is ok"); +fn hmac_and_split(seed: &[u8], data: &[u8]) -> Result<(k256::SecretKey, ChainCode), Bip32Error> { + let mut mac = Hmac::::new_varkey(seed).expect("key length is ok"); mac.input(data); let result = mac.result().code(); let mut left = [0u8; 32]; left.copy_from_slice(&result[..32]); + let left = k256::SecretKey::from_bytes(&left)?; let mut right = [0u8; 32]; right.copy_from_slice(&result[32..]); - (left, ChainCode(right)) + Ok((left, ChainCode(right))) +} + +/// A Parent key can be used to derive children. +pub trait Parent: Sized + Clone { + /// Derive the child at `index`. Note that this may produce the child at + /// `index+1` in rare circumstances. For public keys this will derive public + /// children. For private keys it will derive private children. + fn derive_child(&self, index: u32) -> Result; + + /// Derive a series of child indices. Allows traversing several levels of the tree at once. + /// Accepts an iterator producing u32, or a string. + fn derive_path(&self, p: P) -> Result + where + E: Into, + P: TryInto, + { + let path: DerivationPath = p.try_into().map_err(Into::into)?; + + if path.is_empty() { + return Ok(self.clone()); + } + + let mut current = self.to_owned(); + for index in path.iter() { + current = current.derive_child(*index)?; + } + Ok(current) + } +} + +/// A BIP32 eXtended Privkey +pub struct XPriv { + pub(crate) key: ecdsa::SigningKey, + pub(crate) xkey_info: XKeyInfo, +} + +impl Clone for XPriv { + fn clone(&self) -> Self { + Self { + key: ecdsa::SigningKey::from_bytes(&self.key.to_bytes()).unwrap(), + xkey_info: self.xkey_info, + } + } +} + +inherit_signer!(XPriv.key); + +impl std::fmt::Debug for XPriv { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("XPriv") + .field("key fingerprint", &self.fingerprint()) + .field("key info", &self.xkey_info) + .finish() + } +} + +impl AsRef for XPriv { + fn as_ref(&self) -> &XPriv { + self + } +} + +impl AsRef for XPriv { + fn as_ref(&self) -> &XKeyInfo { + &self.xkey_info + } } -/// A BIP32 Extended privkey. This key is genericized to accept any compatibile backend. -#[derive(Clone, Debug, PartialEq)] -pub struct GenericXPriv<'a, T: Secp256k1Backend> { - /// The extended key information - pub info: XKeyInfo, - /// The associated secp256k1 key - pub privkey: GenericPrivkey<'a, T>, +impl AsRef for XPriv { + fn as_ref(&self) -> &ecdsa::SigningKey { + &self.key + } } -inherit_has_privkey!(GenericXPriv.privkey); -inherit_backend!(GenericXPriv.privkey); +impl XPriv { + /// Derive the associated XPub + pub fn verify_key(&self) -> XPub { + XPub { + key: self.key.verify_key(), + xkey_info: self.xkey_info, + } + } + + /// The fingerprint is the first 4 bytes of the HASH160 of the public key + pub fn fingerprint(&self) -> KeyFingerprint { + self.verify_key().fingerprint() + } + + /// Generate a customized root node + pub fn root_node( + hmac_key: &[u8], + data: &[u8], + hint: Option, + ) -> Result { + Self::custom_root_node(hmac_key, data, hint) + } + + /// Generate a root node from some seed data. Uses the BIP32-standard hmac + /// key. + /// + /// # Important: + /// + /// Use a seed of AT LEAST 128 bits. + pub fn root_from_seed(data: &[u8], hint: Option) -> Result { + Self::custom_root_from_seed(data, hint) + } -impl<'a, T: Secp256k1Backend> GenericXPriv<'a, T> { - /// Instantiate a master node using a custom HMAC key. - pub fn custom_master_node( + /// Instantiate a root node using a custom HMAC key. + pub fn custom_root_node( hmac_key: &[u8], data: &[u8], hint: Option, - backend: &'a T, - ) -> Result, Bip32Error> { + ) -> Result { if data.len() < 16 { return Err(Bip32Error::SeedTooShort); } let parent = KeyFingerprint([0u8; 4]); - let (key, chain_code) = hmac_and_split(hmac_key, data); - if key == [0u8; 32] || key > CURVE_ORDER { + let (key, chain_code) = hmac_and_split(hmac_key, data)?; + if bool::from(key.secret_scalar().is_zero()) { // This can only be tested by mocking hmac_and_split return Err(Bip32Error::InvalidKey); } - let privkey = T::Privkey::from_privkey_array(key)?; - Ok(GenericXPriv { - info: XKeyInfo { + + let key = ecdsa::SigningKey::from(key); + + Ok(XPriv { + key, + xkey_info: XKeyInfo { depth: 0, parent, index: 0, chain_code, hint: hint.unwrap_or(Hint::SegWit), }, - privkey: GenericPrivkey { - key: privkey, - backend: Some(backend), - }, }) } - /// Generate a master node from some seed data. Uses the BIP32-standard hmac key. + /// Generate a root node from some seed data. Uses the BIP32-standard hmac key. /// /// /// # Important: /// /// Use a seed of AT LEAST 128 bits. - pub fn custom_root_from_seed( - data: &[u8], - hint: Option, - backend: &'a T, - ) -> Result, Bip32Error> { - Self::custom_master_node(SEED, data, hint, backend) + pub fn custom_root_from_seed(data: &[u8], hint: Option) -> Result { + Self::custom_root_node(SEED, data, hint) } - /// Derive the corresponding xpub - pub fn to_xpub(&self) -> Result, Bip32Error> { - Ok(GenericXPub { - info: self.info, - pubkey: self.privkey.derive_verifying_key()?, - }) - } -} - -impl<'a, T: Secp256k1Backend> HasXKeyInfo for GenericXPriv<'a, T> { - fn xkey_info(&self) -> &XKeyInfo { - &self.info - } -} - -impl<'a, T: Secp256k1Backend> SigningKey<'a, T> for GenericXPriv<'a, T> { - /// The corresponding verifying key - type VerifyingKey = GenericXPub<'a, T>; + /// Derive a series of child indices. Allows traversing several levels of the tree at once. + /// Accepts an iterator producing u32, or a string. + pub fn derive_path(&self, p: P) -> Result + where + E: Into, + P: TryInto, + { + let path: DerivationPath = p.try_into().map_err(Into::into)?; + + if path.is_empty() { + return Ok(self.clone()); + } - /// Derive the corresponding pubkey - fn derive_verifying_key(&self) -> Result { - self.to_xpub() + let mut current = self.to_owned(); + for index in path.iter() { + current = current.derive_child(*index)?; + } + Ok(current) } } -impl<'a, T: Secp256k1Backend> DerivePrivateChild<'a, T> for GenericXPriv<'a, T> { - fn derive_private_child(&self, index: u32) -> Result, Bip32Error> { +impl Parent for XPriv { + fn derive_child(&self, index: u32) -> Result { let hardened = index >= BIP32_HARDEN; + let key: &ecdsa::SigningKey = self.as_ref(); + let mut data: Vec = vec![]; if hardened { data.push(0); - data.extend(&self.privkey_bytes()); + data.extend(&key.to_bytes()); data.extend(&index.to_be_bytes()); } else { - data.extend(&self.derive_pubkey_bytes()?.to_vec()); + data.extend(&key.verify_key().to_bytes()); data.extend(&index.to_be_bytes()); }; - let (tweak, chain_code) = hmac_and_split(&self.chain_code().0, &data); - let privkey = self - .backend()? - .tweak_privkey(&self.privkey(), tweak) - .map(|k| GenericPrivkey { - key: k, - backend: self.backend().ok(), - }) - .map_err(Into::into)?; - - Ok(GenericXPriv { - info: XKeyInfo { - depth: self.depth() + 1, - parent: self.derive_fingerprint()?, + let res = hmac_and_split(&self.xkey_info.chain_code.0, &data); + let (tweak, chain_code) = match res { + Ok((tweak, chain_code)) => (tweak, chain_code), + _ => return self.derive_child(index + 1), + }; + + let parent_key = k256::NonZeroScalar::from_repr(key.to_bytes()).unwrap(); + let tweaked = tweak.secret_scalar().clone().add(&parent_key); + + let tweaked = k256::NonZeroScalar::new(tweaked).ok_or(Bip32Error::BadTweak)?; + + Ok(Self { + key: ecdsa::SigningKey::from(tweaked), + xkey_info: XKeyInfo { + depth: self.xkey_info.depth + 1, + parent: self.fingerprint(), index, chain_code, - hint: self.hint(), + hint: self.xkey_info.hint, }, - privkey, }) } } -/// A BIP32 Extended privkey. This key is genericized to accept any compatibile backend. -#[derive(Clone, Debug, PartialEq)] -pub struct GenericXPub<'a, T: Secp256k1Backend> { - /// The extended key information - pub info: XKeyInfo, - /// The associated secp256k1 key - pub pubkey: GenericPubkey<'a, T>, +/// A BIP32 eXtended Public key +pub struct XPub { + pub(crate) key: ecdsa::VerifyingKey, + pub(crate) xkey_info: XKeyInfo, +} + +inherit_verifier!(XPub.key); + +impl Clone for XPub { + fn clone(&self) -> Self { + Self { + key: ecdsa::VerifyingKey::from_sec1_bytes(&self.key.to_bytes()).unwrap(), + xkey_info: self.xkey_info, + } + } +} + +impl std::fmt::Debug for XPub { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("XPub") + .field("key fingerprint", &self.fingerprint()) + .field("key info", &self.xkey_info) + .finish() + } +} + +impl AsRef for XPub { + fn as_ref(&self) -> &XPub { + self + } } -inherit_has_pubkey!(GenericXPub.pubkey); -inherit_backend!(GenericXPub.pubkey); +impl AsRef for XPub { + fn as_ref(&self) -> &XKeyInfo { + &self.xkey_info + } +} -impl<'a, T: Secp256k1Backend> GenericXPub<'a, T> { - /// Derive an XPub from an xpriv - pub fn from_xpriv(xpriv: &GenericXPriv<'a, T>) -> Result, Bip32Error> { - xpriv.to_xpub() +impl AsRef for XPub { + fn as_ref(&self) -> &ecdsa::VerifyingKey { + &self.key } } -impl<'a, T: Secp256k1Backend> HasXKeyInfo for GenericXPub<'a, T> { - fn xkey_info(&self) -> &XKeyInfo { - &self.info +impl XPub { + /// The fingerprint is the first 4 bytes of the HASH160 of the serialized + /// public key. + pub fn fingerprint(&self) -> KeyFingerprint { + let digest = self.pubkey_hash160(); + let mut buf = [0u8; 4]; + buf.copy_from_slice(&digest.as_slice()[..4]); + buf.into() + } + + /// Return the bitcoin HASH160 of the serialized public key + pub fn pubkey_hash160(&self) -> Hash160Digest { + Hash160::digest_marked(&self.key.to_bytes()) } } -impl<'a, T: Secp256k1Backend> VerifyingKey<'a, T> for GenericXPub<'a, T> { - type SigningKey = GenericXPriv<'a, T>; +impl PartialEq for XPub { + fn eq(&self, other: &XPub) -> bool { + self.key.to_bytes() == other.key.to_bytes() + } } -impl<'a, T: Secp256k1Backend> DerivePublicChild<'a, T> for GenericXPub<'a, T> { - fn derive_public_child(&self, index: u32) -> Result, Bip32Error> { +impl Parent for XPub { + fn derive_child(&self, index: u32) -> Result { if index >= BIP32_HARDEN { return Err(Bip32Error::HardenedDerivationFailed); } - let mut data: Vec = self.pubkey_bytes().to_vec(); + let mut data = vec![]; + data.extend(&self.key.to_bytes()); data.extend(&index.to_be_bytes()); - let (offset, chain_code) = hmac_and_split(&self.chain_code().0, &data); - // TODO: check for point at infinity - if offset > CURVE_ORDER { - return self.derive_public_child(index + 1); + let res = hmac_and_split(&self.xkey_info.chain_code.0, &data); + + let (tweak, chain_code) = match res { + Ok((tweak, chain_code)) => (tweak, chain_code), + _ => return self.derive_child(index + 1), + }; + + if bool::from(tweak.secret_scalar().is_zero()) { + return self.derive_child(index + 1); } - let pubkey = self - .backend()? - .tweak_pubkey(&self.pubkey(), offset) - .map(|k| GenericPubkey { - key: k, - backend: self.backend().ok(), - }) - .map_err(Into::into)?; + let parent_key = k256::PublicKey::from_sec1_bytes(&self.key.to_bytes()).unwrap(); + let new_key = parent_key.as_affine().mul(*tweak.secret_scalar()); Ok(Self { - info: XKeyInfo { - depth: self.depth() + 1, + key: ecdsa::VerifyingKey::from(&new_key), + xkey_info: XKeyInfo { + depth: self.xkey_info.depth + 1, parent: self.fingerprint(), index, chain_code, - hint: self.hint(), + hint: self.xkey_info.hint, }, - pubkey, }) } } @@ -253,12 +340,14 @@ impl<'a, T: Secp256k1Backend> DerivePublicChild<'a, T> for GenericXPub<'a, T> { mod test { use super::*; use crate::{ - curve::Secp256k1, enc::{MainnetEncoder, XKeyEncoder}, - keys::Pubkey, primitives::*, }; - use coins_core::hashes::Hash256Digest; + use coins_core::hashes::Hash256; + use k256::ecdsa::{ + recoverable, + signature::{DigestSigner, DigestVerifier}, + }; use hex; @@ -269,40 +358,37 @@ mod test { } fn validate_descendant<'a>(d: &KeyDeriv, m: &XPriv) { - let xpriv = m.derive_private_path(d.path).unwrap(); - let xpub = xpriv.to_xpub().unwrap(); + let xpriv = m.derive_path(d.path).unwrap(); + let xpub = xpriv.verify_key(); - let deser_xpriv = - MainnetEncoder::xpriv_from_base58(&d.xpriv, xpriv.backend().ok()).unwrap(); - let deser_xpub = MainnetEncoder::xpub_from_base58(&d.xpub, xpriv.backend().ok()).unwrap(); + let deser_xpriv = MainnetEncoder::xpriv_from_base58(&d.xpriv).unwrap(); + let deser_xpub = MainnetEncoder::xpub_from_base58(&d.xpub).unwrap(); - assert_eq!(&xpriv, &deser_xpriv); + assert_eq!(&xpriv.key.to_bytes(), &deser_xpriv.key.to_bytes()); assert_eq!(MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(), d.xpriv); - assert_eq!(&xpub, &deser_xpub); + assert_eq!(&xpub.key.to_bytes(), &deser_xpub.key.to_bytes()); assert_eq!(MainnetEncoder::xpub_to_base58(&xpub).unwrap(), d.xpub); } #[test] fn bip32_vector_1() { let seed: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - let backend = Secp256k1::static_ref(); let xpriv = XPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); - let xpub = xpriv.to_xpub().unwrap(); + let xpub = xpriv.verify_key(); let expected_xpub = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"; let expected_xpriv = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"; - let deser_xpub = MainnetEncoder::xpub_from_base58(&expected_xpub, Some(backend)).unwrap(); - let deser_xpriv = - MainnetEncoder::xpriv_from_base58(&expected_xpriv, Some(backend)).unwrap(); + let deser_xpub = MainnetEncoder::xpub_from_base58(&expected_xpub).unwrap(); + let deser_xpriv = MainnetEncoder::xpriv_from_base58(&expected_xpriv).unwrap(); - assert_eq!(&xpriv, &deser_xpriv); + assert_eq!(&xpriv.key.to_bytes(), &deser_xpriv.key.to_bytes()); assert_eq!( MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(), expected_xpriv ); - assert_eq!(&xpub, &deser_xpub); + assert_eq!(&xpub.key.to_bytes(), &deser_xpub.key.to_bytes()); assert_eq!( MainnetEncoder::xpub_to_base58(&xpub).unwrap(), expected_xpub @@ -344,24 +430,22 @@ mod test { #[test] fn bip32_vector_2() { let seed = hex::decode(&"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542").unwrap(); - let backend = Secp256k1::static_ref(); let xpriv = XPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); - let xpub = xpriv.to_xpub().unwrap(); + let xpub = xpriv.verify_key(); let expected_xpub = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"; let expected_xpriv = "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"; - let deser_xpub = MainnetEncoder::xpub_from_base58(&expected_xpub, Some(backend)).unwrap(); - let deser_xpriv = - MainnetEncoder::xpriv_from_base58(&expected_xpriv, Some(backend)).unwrap(); + let deser_xpub = MainnetEncoder::xpub_from_base58(&expected_xpub).unwrap(); + let deser_xpriv = MainnetEncoder::xpriv_from_base58(&expected_xpriv).unwrap(); - assert_eq!(&xpriv, &deser_xpriv); + assert_eq!(&xpriv.key.to_bytes(), &deser_xpriv.key.to_bytes()); assert_eq!( MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(), expected_xpriv ); - assert_eq!(&xpub, &deser_xpub); + assert_eq!(&xpub.key.to_bytes(), &deser_xpub.key.to_bytes()); assert_eq!( MainnetEncoder::xpub_to_base58(&xpub).unwrap(), expected_xpub @@ -403,24 +487,22 @@ mod test { #[test] fn bip32_vector_3() { let seed = hex::decode(&"4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be").unwrap(); - let backend = Secp256k1::static_ref(); let xpriv = XPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap(); - let xpub = xpriv.to_xpub().unwrap(); + let xpub = xpriv.verify_key(); let expected_xpub = "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13"; let expected_xpriv = "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6"; - let deser_xpub = MainnetEncoder::xpub_from_base58(&expected_xpub, Some(backend)).unwrap(); - let deser_xpriv = - MainnetEncoder::xpriv_from_base58(&expected_xpriv, Some(backend)).unwrap(); + let deser_xpub = MainnetEncoder::xpub_from_base58(&expected_xpub).unwrap(); + let deser_xpriv = MainnetEncoder::xpriv_from_base58(&expected_xpriv).unwrap(); - assert_eq!(&xpriv, &deser_xpriv); + assert_eq!(&xpriv.key.to_bytes(), &deser_xpriv.key.to_bytes()); assert_eq!( MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(), expected_xpriv ); - assert_eq!(&xpub, &deser_xpub); + assert_eq!(&xpub.key.to_bytes(), &deser_xpub.key.to_bytes()); assert_eq!( MainnetEncoder::xpub_to_base58(&xpub).unwrap(), expected_xpub @@ -441,47 +523,46 @@ mod test { #[test] fn it_can_sign_and_verify() { - let digest: Hash256Digest = [1u8; 32].into(); - let backend = Secp256k1::static_ref(); + let digest = Hash256::default(); let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); - let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, Some(backend)).unwrap(); + let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str).unwrap(); - let child = xpriv.derive_private_child(33).unwrap(); - let sig = child.sign_digest(digest).unwrap(); + let child = xpriv.derive_child(33).unwrap(); + let sig: recoverable::Signature = child.sign_digest(digest.clone()); - let child_xpub = child.to_xpub().unwrap(); + let child_xpub = child.verify_key(); child_xpub.verify_digest(digest, &sig).unwrap(); } #[test] fn it_can_verify_and_recover_from_signatures() { - let digest: Hash256Digest = [1u8; 32].into(); - let backend = Secp256k1::static_ref(); + let digest = Hash256::default(); + let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); - let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, Some(backend)).unwrap(); + let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str).unwrap(); + + let child = xpriv.derive_child(33).unwrap(); - let child = xpriv.derive_private_child(33).unwrap(); + let sig: recoverable::Signature = child.sign_digest(digest.clone()); - let sig = child.sign_digest_recoverable(digest).unwrap(); + let child_xpub = child.verify_key(); + child_xpub.verify_digest(digest.clone(), &sig).unwrap(); - let child_xpub = child.to_xpub().unwrap(); - child_xpub.verify_digest_recoverable(digest, &sig).unwrap(); + let recovered = sig.recover_verify_key_from_digest(digest).unwrap(); - let recovered = - Pubkey::recover_from_signed_digest(xpriv.backend().unwrap(), digest, &sig).unwrap(); - assert_eq!(&recovered.pubkey(), &child_xpub.pubkey()); + assert_eq!(&recovered.to_bytes(), &child_xpub.key.to_bytes()); } #[test] - fn it_can_read_keys_without_a_backend() { + fn it_can_read_keys() { let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); - let _xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None).unwrap(); + let _xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str).unwrap(); } #[test] fn print_key() { let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned(); - let xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str, None).unwrap(); + let xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str).unwrap(); println!("{:?}", xpriv); } } diff --git a/bitcoins/src/types/script.rs b/bitcoins/src/types/script.rs index ae83e550..8d2a4c89 100644 --- a/bitcoins/src/types/script.rs +++ b/bitcoins/src/types/script.rs @@ -93,25 +93,27 @@ pub type TxWitness = Vec; impl ScriptPubkey { /// Instantiate a standard p2pkh script pubkey from a pubkey. - pub fn p2pkh<'a, T, B>(key: &T) -> Self + pub fn p2pkh(key: &K) -> Self where - B: coins_bip32::curve::Secp256k1Backend, - T: coins_bip32::model::HasPubkey<'a, B>, + K: AsRef, { + let digest = Hash160::digest(&key.as_ref().to_bytes()); + let mut v: Vec = vec![0x76, 0xa9, 0x14]; // DUP, HASH160, PUSH_20 - v.extend(key.pubkey_hash160().as_slice()); + v.extend(&digest); v.extend(&[0x88, 0xac]); // EQUALVERIFY, CHECKSIG v.into() } /// Instantiate a standard p2wpkh script pubkey from a pubkey. - pub fn p2wpkh<'a, T, B>(key: &T) -> Self + pub fn p2wpkh(key: &K) -> Self where - B: coins_bip32::curve::Secp256k1Backend, - T: coins_bip32::model::HasPubkey<'a, B>, + K: AsRef, { + let digest = Hash160::digest(&key.as_ref().to_bytes()); + let mut v: Vec = vec![0x00, 0x14]; // OP_0, PUSH_20 - v.extend(key.pubkey_hash160().as_slice()); + v.extend(&digest); v.into() } diff --git a/core/src/hashes/mod.rs b/core/src/hashes/mod.rs index 0829a367..aaa319ce 100644 --- a/core/src/hashes/mod.rs +++ b/core/src/hashes/mod.rs @@ -84,44 +84,35 @@ impl std::io::Write for Hash256 { } } -impl Digest for Hash256 { - type OutputSize = ::OutputSize; - - fn new() -> Self { - Self::default() - } +impl digest::BlockInput for Hash256 { + type BlockSize = ::BlockSize; +} - fn update(&mut self, data: impl AsRef<[u8]>) { - self.0.update(data) - } +impl digest::FixedOutput for Hash256 { + type OutputSize = ::OutputSize; - fn chain(self, data: impl AsRef<[u8]>) -> Self - where - Self: Sized, - { - Self(self.0.chain(data)) + fn finalize_into(self, out: &mut GenericArray) { + let mut hasher = sha2::Sha256::default(); + hasher.update(self.0.finalize()); + hasher.finalize_into(out); } - fn finalize(self) -> DigestOutput { - sha2::Sha256::digest(self.0.finalize().as_slice()) - } - - fn finalize_reset(&mut self) -> DigestOutput { - let res = self.clone().finalize(); - self.reset(); - res + fn finalize_into_reset(&mut self, out: &mut GenericArray) { + let other = self.clone(); + other.finalize_into(out); + self.0.reset(); } +} +impl digest::Reset for Hash256 { fn reset(&mut self) { - self.0.reset() - } - - fn output_size() -> usize { - sha2::Sha256::output_size() + Digest::reset(&mut self.0); } +} - fn digest(data: &[u8]) -> DigestOutput { - sha2::Sha256::digest(sha2::Sha256::digest(data).as_slice()) +impl digest::Update for Hash256 { + fn update(&mut self, data: impl AsRef<[u8]>) { + Digest::update(&mut self.0, data); } } @@ -140,44 +131,35 @@ impl std::io::Write for Hash160 { } } -impl Digest for Hash160 { - type OutputSize = ::OutputSize; - - fn new() -> Self { - Self::default() - } +impl digest::BlockInput for Hash160 { + type BlockSize = ::BlockSize; +} - fn update(&mut self, data: impl AsRef<[u8]>) { - self.0.update(data) - } +impl digest::FixedOutput for Hash160 { + type OutputSize = ::OutputSize; - fn chain(self, data: impl AsRef<[u8]>) -> Self - where - Self: Sized, - { - Self(self.0.chain(data)) + fn finalize_into(self, out: &mut GenericArray) { + let mut hasher = ripemd160::Ripemd160::default(); + hasher.update(self.0.finalize()); + hasher.finalize_into(out); } - fn finalize(self) -> DigestOutput { - ripemd160::Ripemd160::digest(self.0.finalize().as_slice()) - } - - fn finalize_reset(&mut self) -> DigestOutput { - let res = self.clone().finalize(); - self.reset(); - res + fn finalize_into_reset(&mut self, out: &mut GenericArray) { + let other = self.clone(); + other.finalize_into(out); + self.0.reset(); } +} +impl digest::Reset for Hash160 { fn reset(&mut self) { - self.0.reset() - } - - fn output_size() -> usize { - ripemd160::Ripemd160::output_size() + Digest::reset(&mut self.0); } +} - fn digest(data: &[u8]) -> DigestOutput { - ripemd160::Ripemd160::digest(sha2::Sha256::digest(data).as_slice()) +impl digest::Update for Hash160 { + fn update(&mut self, data: impl AsRef<[u8]>) { + Digest::update(&mut self.0, data); } } diff --git a/core/src/prelude.rs b/core/src/prelude.rs index b8b222fe..9ab4e75d 100644 --- a/core/src/prelude.rs +++ b/core/src/prelude.rs @@ -1,4 +1,5 @@ -//! Re-exports of common traits. +//! Re-exports of common types. + pub use crate::{ builder::TxBuilder, enc::*, @@ -7,3 +8,4 @@ pub use crate::{ ser::{ByteFormat, ReadSeqMode}, types::Transaction, }; +pub use digest::Digest; diff --git a/handshakes/src/types/lockingscript.rs b/handshakes/src/types/lockingscript.rs index ac752e58..70915358 100644 --- a/handshakes/src/types/lockingscript.rs +++ b/handshakes/src/types/lockingscript.rs @@ -202,14 +202,13 @@ pub enum LockingScriptType { impl LockingScript { /// Instantiate a standard p2wpkh script pubkey from a pubkey. - pub fn p2wpkh<'a, T, B>(key: &T) -> Self + pub fn p2wpkh(key: &T) -> Self where - B: coins_bip32::curve::Secp256k1Backend, - T: coins_bip32::model::HasPubkey<'a, B>, + T: AsRef, { Self { version: 0, - witness_program: blake2b160(&key.pubkey_bytes()).into(), + witness_program: blake2b160(&key.as_ref().to_bytes()).into(), } } @@ -276,7 +275,7 @@ impl LockingScript { #[cfg(test)] mod test { use super::*; - use coins_bip32::{curve::model::*, model::*, XPriv}; + use coins_bip32::prelude::XPriv; use coins_core::ser::ByteFormat; #[test] @@ -291,11 +290,11 @@ mod test { fn it_generates_p2wpkh_locking_script() { let xpriv_str = "xprv9s21ZrQH143K24iSk4AuKHKkRzWQBqPHV3bB7n1fFxQxitybVXAixpB72Um9DhrNumiR9YAmmXvPCdqM8s1XMM2inRiCvgND9cy7uHs1FCa"; let xpriv: XPriv = xpriv_str.parse().unwrap(); - let xpub = xpriv.derive_verifying_key().unwrap(); + let xpub = xpriv.verify_key(); - let pubkey = xpriv.derive_pubkey().unwrap(); + let pubkey = xpriv.verify_key(); let mut vec = Vec::new(); - vec.extend(pubkey.pubkey_array().iter()); + vec.extend(&pubkey.to_bytes()); assert_eq!( "026180c26fb38078b5d5c717cd70e4b774f4ef56b8ae994599764a9156909aa437", hex::encode(vec) diff --git a/psbt/src/global.rs b/psbt/src/global.rs index faeaf816..4ee70e36 100644 --- a/psbt/src/global.rs +++ b/psbt/src/global.rs @@ -1,7 +1,10 @@ use std::collections::{btree_map, BTreeMap}; use bitcoins::types::{BitcoinTxIn, LegacyTx}; -use coins_bip32::{enc::XKeyEncoder as Bip32Encoder, model::DerivedKey, DerivedXPub}; +use coins_bip32::{ + derived::{DerivedKey, DerivedXPub}, + enc::XKeyEncoder as Bip32Encoder, +}; use coins_core::{ ser::{self, ByteFormat}, types::tx::Transaction, @@ -102,10 +105,10 @@ impl PSBTGlobal { E: Bip32Encoder, { let mut key = vec![GlobalKey::XPUB as u8]; - E::write_xpub(&mut key, &xpub.xpub).unwrap(); + E::write_xpub(&mut key, &xpub).unwrap(); let mut val = vec![]; - xpub.derivation.write_to(&mut val).unwrap(); + xpub.derivation().write_to(&mut val).unwrap(); self.insert(key.into(), val.into()); } diff --git a/psbt/src/input.rs b/psbt/src/input.rs index f491c4f7..cdf9bf52 100644 --- a/psbt/src/input.rs +++ b/psbt/src/input.rs @@ -1,8 +1,4 @@ -use coins_bip32::{ - curve::{model::Secp256k1Backend, SigSerialize}, - derived::DerivedPubkey, - model::HasPubkey, -}; +use coins_bip32::derived::DerivedPubkey; use coins_core::ser::{self, ByteFormat}; use std::collections::{btree_map, BTreeMap}; @@ -192,23 +188,28 @@ impl PSBTInput { } /// Returns an iterator over Pubkey/Signature pairs - pub fn partial_sigs(&self) -> Vec<(coins_bip32::Pubkey, coins_bip32::Signature, Sighash)> { + pub fn partial_sigs( + &self, + ) -> Vec<( + coins_bip32::ecdsa::VerifyingKey, + coins_bip32::ecdsa::Signature, + Sighash, + )> { self.raw_partial_sigs() .filter_map(|(k, v)| schema::try_kv_pair_as_pubkey_and_sig(k, v).ok()) .collect::>() } /// Inserts a signature into the map - pub fn insert_partial_sig<'a, T: Secp256k1Backend, K: HasPubkey<'a, T>>( - &mut self, - pk: &K, - sig: &T::Signature, - ) { + pub fn insert_partial_sig(&mut self, pk: &K, sig: &coins_bip32::ecdsa::Signature) + where + K: AsRef, + { let mut key = vec![InputKey::PARTIAL_SIG as u8]; - key.extend(pk.pubkey_bytes().iter()); + key.extend(&pk.as_ref().to_bytes()); let mut val = vec![]; - val.extend(sig.to_der()); + val.extend(sig.to_asn1().as_bytes()); val.push(self.sighash_or_default() as u8); self.insert(key.into(), val.into()); diff --git a/psbt/src/lib.rs b/psbt/src/lib.rs index 9b0bc934..328af5d8 100644 --- a/psbt/src/lib.rs +++ b/psbt/src/lib.rs @@ -29,8 +29,8 @@ use std::{ }; use coins_bip32::{ - self as bip32, enc::XKeyEncoder as Bip32Encoder, model::DerivedKey, DerivedXPub, - KeyFingerprint, XPub, + self as bip32, derived::DerivedXPub, enc::XKeyEncoder as Bip32Encoder, + primitives::KeyFingerprint, xkeys::XPub, }; use coins_core::prelude::*; diff --git a/psbt/src/roles/bip32_signer.rs b/psbt/src/roles/bip32_signer.rs index ff697793..0fba67c3 100644 --- a/psbt/src/roles/bip32_signer.rs +++ b/psbt/src/roles/bip32_signer.rs @@ -1,10 +1,7 @@ use thiserror::Error; use bitcoins::prelude::*; -use coins_bip32::{ - self as bip32, - model::{DerivedKey, HasPubkey, SigningKey, XSigning}, -}; +use coins_bip32::prelude::*; use coins_core::types::Transaction; use crate::{input::PSBTInput, roles::PSTSigner, PSBTError, PSBT, PST}; @@ -13,7 +10,7 @@ use crate::{input::PSBTInput, roles::PSTSigner, PSBTError, PSBT, PST}; pub enum Bip32SignerError { /// Bubbled up from the BIP32 library #[error(transparent)] - Bip32Error(#[from] coins_bip32::Bip32Error), + Bip32Error(#[from] Bip32Error), /// PSBTError bubbled up #[error(transparent)] @@ -29,7 +26,7 @@ pub enum Bip32SignerError { /// Implements naive change-checking by simply checking if it owns the pubkey of a PKH or WPKH /// output. pub struct Bip32Signer { - xpriv: bip32::DerivedXPriv, + xpriv: DerivedXPriv, } impl Bip32Signer { @@ -121,10 +118,10 @@ impl Bip32Signer { }; for path in paths.iter() { - // TODO: DRY - let sighash = tx.sighash(&sighash_args)?; - let signature = self.xpriv.descendant_sign_digest(path, sighash.into())?; - input_map.insert_partial_sig(&self.xpriv.derive_verifying_key()?, &signature); + let mut digest = coins_core::hashes::Hash256::default(); + tx.write_sighash_preimage(&mut digest, &sighash_args)?; + let signature = self.xpriv.derive_path(path.clone())?.sign_digest(digest); + input_map.insert_partial_sig(&self.xpriv.verify_key(), &signature); } Ok(()) @@ -153,11 +150,10 @@ impl Bip32Signer { .unwrap(); for path in paths.iter() { - let sighash = tx.sighash(&sighash_args)?; - let signature = self - .xpriv - .descendant_sign_digest(path.clone(), sighash.into())?; - input_map.insert_partial_sig(&self.xpriv.derive_verifying_key()?, &signature); + let mut digest = coins_core::hashes::Hash256::default(); + tx.write_sighash_preimage(&mut digest, &sighash_args)?; + let signature = self.xpriv.derive_path(path.clone())?.sign_digest(digest); + input_map.insert_partial_sig(&self.xpriv.verify_key(), &signature); } Ok(()) @@ -172,13 +168,13 @@ impl Bip32Signer { // for path in paths.iter() { // let sighash = tx.sighash(&sighash_args)?; // let signature = self.xpriv.descendant_sign_digest(path.clone(), sighash)?; - // input_map.insert_partial_sig(&self.xpriv.derive_verifying_key()?, &signature); + // input_map.insert_partial_sig(&self.xpriv.verify_key(), &signature); // } } } -impl From for Bip32Signer { - fn from(xpriv: bip32::DerivedXPriv) -> Self { +impl From for Bip32Signer { + fn from(xpriv: DerivedXPriv) -> Self { Self { xpriv } } } @@ -186,7 +182,7 @@ impl From for Bip32Signer { impl PSTSigner> for Bip32Signer where A: BitcoinEncoderMarker, - E: bip32::enc::XKeyEncoder, + E: XKeyEncoder, { type Error = Bip32SignerError; diff --git a/psbt/src/roles/finalizer.rs b/psbt/src/roles/finalizer.rs index 07ec73d4..73952e71 100644 --- a/psbt/src/roles/finalizer.rs +++ b/psbt/src/roles/finalizer.rs @@ -4,8 +4,8 @@ use bitcoins::{ prelude::Hash160Digest, types::{BitcoinOutpoint, BitcoinTransaction, ScriptType}, }; -use coins_bip32::{self as bip32, curve::SigSerialize, HasPubkey}; -use coins_core::Transaction; +use coins_bip32::{self as bip32}; +use coins_core::prelude::{Digest, MarkedDigestOutput, Transaction}; /// A finalizer that creates WPKH witnesses pub struct PSBTWPKHFinalizer(); @@ -51,17 +51,17 @@ fn finalize_input(outpoint: &BitcoinOutpoint, input_map: &mut PSBTInput) -> Resu }; // If any pubkeys match, build a witness and finalize - if let Some((pubkey, partial_sig, sighash)) = input_map - .partial_sigs() - .iter() - .find(|(pubkey, _, _)| pkh == pubkey.pubkey_hash160()) + if let Some((pubkey, partial_sig, sighash)) = + input_map.partial_sigs().iter().find(|(pubkey, _, _)| { + pkh.as_slice() == coins_core::hashes::Hash160::digest(&pubkey.to_bytes()).as_slice() + }) { let mut witness = bitcoins::types::Witness::default(); let mut sig_bytes = vec![]; - sig_bytes.extend(partial_sig.to_der()); + sig_bytes.extend(partial_sig.to_asn1().as_bytes()); sig_bytes.extend(&[sighash.to_u8()]); - witness.push(pubkey.pubkey_bytes().as_ref().into()); + witness.push(pubkey.to_bytes().as_ref().into()); witness.push(sig_bytes.into()); input_map.insert_witness(&witness); diff --git a/psbt/src/schema.rs b/psbt/src/schema.rs index 0fc0b3ff..6391089d 100644 --- a/psbt/src/schema.rs +++ b/psbt/src/schema.rs @@ -2,16 +2,7 @@ use std::collections::HashMap; use coins_core::ser::{self, ByteFormat, ReadSeqMode}; -use coins_bip32::{ - self as bip32, - curve::{PointDeserialize, Secp256k1Backend, SigSerialize, Signature}, - derived::DerivedPubkey, - keys::Pubkey, - model::DerivedKey, - path::KeyDerivation, - primitives::KeyFingerprint, - Bip32Error, Secp256k1, XPub, -}; +use coins_bip32::prelude::*; use bitcoins::types::{LegacyTx, ScriptType, Sighash, TxError, TxOut, Witness, WitnessStackItem}; @@ -129,7 +120,7 @@ pub fn validate_xpub_depth(key: &PSBTKey, val: &PSBTValue) -> Result<(), PSBTErr } /// Attempt to parse a keyas a Secp256k1 pybkey -pub fn try_key_as_pubkey(key: &PSBTKey) -> Result { +pub fn try_key_as_pubkey(key: &PSBTKey) -> Result { if key.len() != 34 { return Err(PSBTError::WrongKeyLength { expected: 34, @@ -138,10 +129,7 @@ pub fn try_key_as_pubkey(key: &PSBTKey) -> Result { } let mut buf = [0u8; 33]; buf.copy_from_slice(&key.items()[1..]); - Ok(Pubkey { - key: ::Pubkey::from_pubkey_array(buf)?, - backend: Some(Secp256k1::static_ref()), - }) + Ok(VerifyingKey::from_sec1_bytes(&buf).map_err(Bip32Error::from)?) } /// Attempt to deserialize a value as a as transaction @@ -179,7 +167,7 @@ pub fn try_val_as_signature(val: &PSBTValue) -> Result<(Signature, Sighash), PSB expected: 75, })?; - let sig = Signature::try_from_der(sig_bytes)?; + let sig = Signature::from_asn1(sig_bytes).map_err(Bip32Error::from)?; Ok((sig, Sighash::from_u8(*sighash_flag)?)) } @@ -196,17 +184,14 @@ pub fn try_val_as_witness(val: &PSBTValue) -> Result { /// Attempt to parse a key as a valid extended pubkey pub fn try_key_as_xpub(key: &PSBTKey) -> Result where - E: bip32::enc::XKeyEncoder, + E: XKeyEncoder, { if key.len() < 2 { return Err(Bip32Error::BadXPubVersionBytes([0u8; 4]).into()); } // strip off first byte (the type) let mut xpub_bytes = &key.items()[1..]; - Ok(E::read_xpub( - &mut xpub_bytes, - Some(Secp256k1::static_ref()), - )?) + Ok(E::read_xpub(&mut xpub_bytes)?) } /// Attempt to convert a KV pair into a derived pubkey struct @@ -217,11 +202,12 @@ pub fn try_kv_pair_as_derived_pubkey( let pubkey = if key.len() == 34 { let mut pubkey = [0u8; 33]; pubkey.copy_from_slice(&key[1..34]); - bip32::curve::Pubkey::from_pubkey_array(pubkey)? + VerifyingKey::from_sec1_bytes(&pubkey).map_err(Bip32Error::from)? } else if key.len() == 66 { - let mut pubkey = [0u8; 65]; - pubkey.copy_from_slice(&key[1..66]); - bip32::curve::Pubkey::from_pubkey_array_uncompressed(pubkey)? + panic!("unimplemented!") + // let mut pubkey = [0u8; 65]; + // pubkey.copy_from_slice(&key[1..66]); + // bip32::curve::Pubkey::from_pubkey_array_uncompressed(pubkey)? } else { return Err(PSBTError::WrongKeyLength { got: key.len(), @@ -231,18 +217,13 @@ pub fn try_kv_pair_as_derived_pubkey( let deriv = try_val_as_key_derivation(val)?; - let pubkey = bip32::keys::Pubkey { - key: pubkey, - backend: Some(Secp256k1::static_ref()), - }; - Ok(DerivedPubkey::new(pubkey, deriv)) } pub fn try_kv_pair_as_pubkey_and_sig( key: &PSBTKey, val: &PSBTValue, -) -> Result<(Pubkey, Signature, Sighash), PSBTError> { +) -> Result<(VerifyingKey, Signature, Sighash), PSBTError> { let pubkey = try_key_as_pubkey(key)?; let (sig, sighash) = try_val_as_signature(val)?;