Skip to content

Commit 476eed7

Browse files
committed
Merge rust-bitcoin/rust-bitcoin#590: Taproot: BIP32 extended keys using Scep256k1 keys instead of bitcoin ECDSA
cf0c48c Improve Debug for PrivateKey (Dr Maxim Orlovsky) b65a6ae Test for extended private key keypair generation f5875a (Dr Maxim Orlovsky) e6a3d60 BIP32 extended key `to_ecdsa()` and `to_schnorr()` methods (Dr Maxim Orlovsky) b72f56c BIP32 extended keys are using Scep256k1 keys instead of bitcoin ECDSA (Dr Maxim Orlovsky) Pull request description: This is third step required to introduce Schnorr key support according to #588. This PR starts API-breaking changes and is follow-up to non-API breaking #589, which is already merged. PR rationale: BIP32 does not support uncompressed keys and using type with compression flag was a mistake ACKs for top commit: apoelstra: ACK cf0c48c sanket1729: ACK cf0c48c. #757 might need rework after this Tree-SHA512: 6356a65004e7517256bacbf9aaeb69a22fd8536b341e567c5c4e819288e1105d083fe12ac0641404c407c97acf039bdc525f8e02b1b594a6cdda90106f3b1bdc
2 parents 8e9f99b + cf0c48c commit 476eed7

File tree

4 files changed

+136
-38
lines changed

4 files changed

+136
-38
lines changed

examples/bip32.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{env, process};
44
use std::str::FromStr;
55

66
use bitcoin::secp256k1::Secp256k1;
7-
use bitcoin::util::ecdsa::PrivateKey;
7+
use bitcoin::{PrivateKey, PublicKey};
88
use bitcoin::util::bip32::ExtendedPrivKey;
99
use bitcoin::util::bip32::ExtendedPubKey;
1010
use bitcoin::util::bip32::DerivationPath;
@@ -49,7 +49,7 @@ fn main() {
4949
let path = DerivationPath::from_str("m/84h/0h/0h").unwrap();
5050
let child = root.derive_priv(&secp, &path).unwrap();
5151
println!("Child at {}: {}", path, child);
52-
let xpub = ExtendedPubKey::from_private(&secp, &child);
52+
let xpub = ExtendedPubKey::from_priv(&secp, &child);
5353
println!("Public key at {}: {}", path, xpub);
5454

5555
// generate first receiving address at m/0/0
@@ -58,7 +58,7 @@ fn main() {
5858
let public_key = xpub.derive_pub(&secp, &vec![zero, zero])
5959
.unwrap()
6060
.public_key;
61-
let address = Address::p2wpkh(&public_key, network).unwrap();
61+
let address = Address::p2wpkh(&PublicKey::new(public_key), network).unwrap();
6262
println!("First receiving address: {}", address);
6363

6464
}

src/util/bip32.rs

Lines changed: 111 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ use core::{fmt, str::FromStr, default::Default};
2525

2626
use hash_types::XpubIdentifier;
2727
use hashes::{sha512, Hash, HashEngine, Hmac, HmacEngine};
28-
use secp256k1::{self, Secp256k1};
28+
use secp256k1::{self, Secp256k1, XOnlyPublicKey};
2929

3030
use network::constants::Network;
31-
use util::{base58, endian, key};
32-
use util::ecdsa::{PublicKey, PrivateKey};
31+
use util::{base58, endian};
32+
use util::{key, ecdsa, schnorr};
33+
use io::Write;
3334

3435
/// A chain code
3536
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -44,7 +45,8 @@ impl_array_newtype!(Fingerprint, u8, 4);
4445
impl_bytes_newtype!(Fingerprint, 4);
4546

4647
/// Extended private key
47-
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
48+
#[derive(Copy, Clone, PartialEq, Eq)]
49+
#[cfg_attr(feature = "std", derive(Debug))]
4850
pub struct ExtendedPrivKey {
4951
/// The network this key is to be used on
5052
pub network: Network,
@@ -55,12 +57,26 @@ pub struct ExtendedPrivKey {
5557
/// Child number of the key used to derive from parent (0 for master)
5658
pub child_number: ChildNumber,
5759
/// Private key
58-
pub private_key: PrivateKey,
60+
pub private_key: secp256k1::SecretKey,
5961
/// Chain code
6062
pub chain_code: ChainCode
6163
}
6264
serde_string_impl!(ExtendedPrivKey, "a BIP-32 extended private key");
6365

66+
#[cfg(not(feature = "std"))]
67+
#[cfg_attr(docsrs, doc(cfg(not(feature = "std"))))]
68+
impl fmt::Debug for ExtendedPrivKey {
69+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70+
f.debug_struct("ExtendedPrivKey")
71+
.field("network", &self.network)
72+
.field("depth", &self.depth)
73+
.field("parent_fingerprint", &self.parent_fingerprint)
74+
.field("child_number", &self.child_number)
75+
.field("chain_code", &self.chain_code)
76+
.finish_non_exhaustive()
77+
}
78+
}
79+
6480
/// Extended public key
6581
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
6682
pub struct ExtendedPubKey {
@@ -73,7 +89,7 @@ pub struct ExtendedPubKey {
7389
/// Child number of the key used to derive from parent (0 for master)
7490
pub child_number: ChildNumber,
7591
/// Public key
76-
pub public_key: PublicKey,
92+
pub public_key: secp256k1::PublicKey,
7793
/// Chain code
7894
pub chain_code: ChainCode
7995
}
@@ -506,11 +522,26 @@ impl ExtendedPrivKey {
506522
depth: 0,
507523
parent_fingerprint: Default::default(),
508524
child_number: ChildNumber::from_normal_idx(0)?,
509-
private_key: PrivateKey::from_slice(&hmac_result[..32], network)?,
525+
private_key: secp256k1::SecretKey::from_slice(&hmac_result[..32])?,
510526
chain_code: ChainCode::from(&hmac_result[32..]),
511527
})
512528
}
513529

530+
/// Constructs ECDSA compressed private key matching internal secret key representation.
531+
pub fn to_priv(&self) -> ecdsa::PrivateKey {
532+
ecdsa::PrivateKey {
533+
compressed: true,
534+
network: self.network,
535+
key: self.private_key
536+
}
537+
}
538+
539+
/// Constructs BIP340 keypair for Schnorr signatures and Taproot use matching the internal
540+
/// secret key representation.
541+
pub fn to_keypair<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> schnorr::KeyPair {
542+
schnorr::KeyPair::from_seckey_slice(secp, &self.private_key[..]).expect("BIP32 internal private key representation is broken")
543+
}
544+
514545
/// Attempts to derive an extended private key from a path.
515546
///
516547
/// The `path` argument can be both of type `DerivationPath` or `Vec<ChildNumber>`.
@@ -532,7 +563,7 @@ impl ExtendedPrivKey {
532563
match i {
533564
ChildNumber::Normal { .. } => {
534565
// Non-hardened key: compute public data and use that
535-
hmac_engine.input(&PublicKey::from_private_key(secp, &self.private_key).key.serialize()[..]);
566+
hmac_engine.input(&secp256k1::PublicKey::from_secret_key(secp, &self.private_key).serialize()[..]);
536567
}
537568
ChildNumber::Hardened { .. } => {
538569
// Hardened key: use only secret data to prevent public derivation
@@ -543,8 +574,8 @@ impl ExtendedPrivKey {
543574

544575
hmac_engine.input(&endian::u32_to_array_be(u32::from(i)));
545576
let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
546-
let mut sk = PrivateKey::from_slice(&hmac_result[..32], self.network)?;
547-
sk.key.add_assign(&self.private_key[..])?;
577+
let mut sk = secp256k1::SecretKey::from_slice(&hmac_result[..32])?;
578+
sk.add_assign(&self.private_key[..])?;
548579

549580
Ok(ExtendedPrivKey {
550581
network: self.network,
@@ -578,7 +609,7 @@ impl ExtendedPrivKey {
578609
parent_fingerprint: Fingerprint::from(&data[5..9]),
579610
child_number: endian::slice_to_u32_be(&data[9..13]).into(),
580611
chain_code: ChainCode::from(&data[13..45]),
581-
private_key: PrivateKey::from_slice(&data[46..78], network)?,
612+
private_key: secp256k1::SecretKey::from_slice(&data[46..78])?,
582613
})
583614
}
584615

@@ -600,7 +631,7 @@ impl ExtendedPrivKey {
600631

601632
/// Returns the HASH160 of the public key belonging to the xpriv
602633
pub fn identifier<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> XpubIdentifier {
603-
ExtendedPubKey::from_private(secp, self).identifier()
634+
ExtendedPubKey::from_priv(secp, self).identifier()
604635
}
605636

606637
/// Returns the first four bytes of the identifier
@@ -611,17 +642,37 @@ impl ExtendedPrivKey {
611642

612643
impl ExtendedPubKey {
613644
/// Derives a public key from a private key
645+
#[deprecated(since = "0.28.0", note = "use ExtendedPubKey::from_priv")]
614646
pub fn from_private<C: secp256k1::Signing>(secp: &Secp256k1<C>, sk: &ExtendedPrivKey) -> ExtendedPubKey {
647+
ExtendedPubKey::from_priv(secp, sk)
648+
}
649+
650+
/// Derives a public key from a private key
651+
pub fn from_priv<C: secp256k1::Signing>(secp: &Secp256k1<C>, sk: &ExtendedPrivKey) -> ExtendedPubKey {
615652
ExtendedPubKey {
616653
network: sk.network,
617654
depth: sk.depth,
618655
parent_fingerprint: sk.parent_fingerprint,
619656
child_number: sk.child_number,
620-
public_key: PublicKey::from_private_key(secp, &sk.private_key),
657+
public_key: secp256k1::PublicKey::from_secret_key(secp, &sk.private_key),
621658
chain_code: sk.chain_code
622659
}
623660
}
624661

662+
/// Constructs ECDSA compressed public key matching internal public key representation.
663+
pub fn to_pub(&self) -> ecdsa::PublicKey {
664+
ecdsa::PublicKey {
665+
compressed: true,
666+
key: self.public_key
667+
}
668+
}
669+
670+
/// Constructs BIP340 x-only public key for BIP-340 signatures and Taproot use matching
671+
/// the internal public key representation.
672+
pub fn to_x_only_pub(&self) -> XOnlyPublicKey {
673+
XOnlyPublicKey::from(self.public_key)
674+
}
675+
625676
/// Attempts to derive an extended public key from a path.
626677
///
627678
/// The `path` argument can be both of type `DerivationPath` or `Vec<ChildNumber>`.
@@ -638,19 +689,19 @@ impl ExtendedPubKey {
638689
}
639690

640691
/// Compute the scalar tweak added to this key to get a child key
641-
pub fn ckd_pub_tweak(&self, i: ChildNumber) -> Result<(PrivateKey, ChainCode), Error> {
692+
pub fn ckd_pub_tweak(&self, i: ChildNumber) -> Result<(secp256k1::SecretKey, ChainCode), Error> {
642693
match i {
643694
ChildNumber::Hardened { .. } => {
644695
Err(Error::CannotDeriveFromHardenedKey)
645696
}
646697
ChildNumber::Normal { index: n } => {
647698
let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
648-
hmac_engine.input(&self.public_key.key.serialize()[..]);
699+
hmac_engine.input(&self.public_key.serialize()[..]);
649700
hmac_engine.input(&endian::u32_to_array_be(n));
650701

651702
let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
652703

653-
let private_key = PrivateKey::from_slice(&hmac_result[..32], self.network)?;
704+
let private_key = secp256k1::SecretKey::from_slice(&hmac_result[..32])?;
654705
let chain_code = ChainCode::from(&hmac_result[32..]);
655706
Ok((private_key, chain_code))
656707
}
@@ -665,7 +716,7 @@ impl ExtendedPubKey {
665716
) -> Result<ExtendedPubKey, Error> {
666717
let (sk, chain_code) = self.ckd_pub_tweak(i)?;
667718
let mut pk = self.public_key;
668-
pk.key.add_exp_assign(secp, &sk[..])?;
719+
pk.add_exp_assign(secp, &sk[..])?;
669720

670721
Ok(ExtendedPubKey {
671722
network: self.network,
@@ -697,7 +748,7 @@ impl ExtendedPubKey {
697748
parent_fingerprint: Fingerprint::from(&data[5..9]),
698749
child_number: endian::slice_to_u32_be(&data[9..13]).into(),
699750
chain_code: ChainCode::from(&data[13..45]),
700-
public_key: PublicKey::from_slice(&data[45..78])?,
751+
public_key: secp256k1::PublicKey::from_slice(&data[45..78])?,
701752
})
702753
}
703754

@@ -712,14 +763,14 @@ impl ExtendedPubKey {
712763
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
713764
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
714765
ret[13..45].copy_from_slice(&self.chain_code[..]);
715-
ret[45..78].copy_from_slice(&self.public_key.key.serialize()[..]);
766+
ret[45..78].copy_from_slice(&self.public_key.serialize()[..]);
716767
ret
717768
}
718769

719770
/// Returns the HASH160 of the chaincode
720771
pub fn identifier(&self) -> XpubIdentifier {
721772
let mut engine = XpubIdentifier::engine();
722-
self.public_key.write_into(&mut engine).expect("engines don't error");
773+
engine.write(&self.public_key.serialize()).expect("engines don't error");
723774
XpubIdentifier::from_engine(engine)
724775
}
725776

@@ -853,7 +904,7 @@ mod tests {
853904
expected_pk: &str) {
854905

855906
let mut sk = ExtendedPrivKey::new_master(network, seed).unwrap();
856-
let mut pk = ExtendedPubKey::from_private(secp, &sk);
907+
let mut pk = ExtendedPubKey::from_priv(secp, &sk);
857908

858909
// Check derivation convenience method for ExtendedPrivKey
859910
assert_eq!(
@@ -881,15 +932,15 @@ mod tests {
881932
match num {
882933
Normal {..} => {
883934
let pk2 = pk.ckd_pub(secp, num).unwrap();
884-
pk = ExtendedPubKey::from_private(secp, &sk);
935+
pk = ExtendedPubKey::from_priv(secp, &sk);
885936
assert_eq!(pk, pk2);
886937
}
887938
Hardened {..} => {
888939
assert_eq!(
889940
pk.ckd_pub(secp, num),
890941
Err(Error::CannotDeriveFromHardenedKey)
891942
);
892-
pk = ExtendedPubKey::from_private(secp, &sk);
943+
pk = ExtendedPubKey::from_priv(secp, &sk);
893944
}
894945
}
895946
}
@@ -1080,5 +1131,42 @@ mod tests {
10801131
assert_eq!("42", &format!("{}", ChildNumber::from_normal_idx(42).unwrap()));
10811132
assert_eq!("000042", &format!("{:06}", ChildNumber::from_normal_idx(42).unwrap()));
10821133
}
1134+
1135+
#[test]
1136+
#[should_panic(expected = "Secp256k1(InvalidSecretKey)")]
1137+
fn schnorr_broken_privkey_zeros() {
1138+
/* this is how we generate key:
1139+
let mut sk = secp256k1::key::ONE_KEY;
1140+
1141+
let zeros = [0u8; 32];
1142+
unsafe {
1143+
sk.as_mut_ptr().copy_from(zeros.as_ptr(), 32);
1144+
}
1145+
1146+
let xpriv = ExtendedPrivKey {
1147+
network: Network::Bitcoin,
1148+
depth: 0,
1149+
parent_fingerprint: Default::default(),
1150+
child_number: ChildNumber::Normal { index: 0 },
1151+
private_key: sk,
1152+
chain_code: ChainCode::from(&[0u8; 32][..])
1153+
};
1154+
1155+
println!("{}", xpriv);
1156+
*/
1157+
1158+
// Xpriv having secret key set to all zeros
1159+
let xpriv_str = "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx";
1160+
ExtendedPrivKey::from_str(xpriv_str).unwrap();
1161+
}
1162+
1163+
1164+
#[test]
1165+
#[should_panic(expected = "Secp256k1(InvalidSecretKey)")]
1166+
fn schnorr_broken_privkey_ffs() {
1167+
// Xpriv having secret key set to all 0xFF's
1168+
let xpriv_str = "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fENZ3QzxW";
1169+
ExtendedPrivKey::from_str(xpriv_str).unwrap();
1170+
}
10831171
}
10841172

src/util/ecdsa.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ impl FromStr for PublicKey {
174174

175175
/// A Bitcoin ECDSA private key
176176
#[derive(Copy, Clone, PartialEq, Eq)]
177+
#[cfg_attr(feature = "std", derive(Debug))]
177178
pub struct PrivateKey {
178179
/// Whether this private key should be serialized as compressed
179180
pub compressed: bool,
@@ -280,6 +281,7 @@ impl fmt::Display for PrivateKey {
280281
}
281282
}
282283

284+
#[cfg(not(feature = "std"))]
283285
impl fmt::Debug for PrivateKey {
284286
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
285287
write!(f, "[private key data]")

0 commit comments

Comments
 (0)