bip32: return error when trying to derive a path too deep
This restores the Result return from derive_xpriv which we had removed in a previous PR, but such is life. Fixes #4308
This commit is contained in:
parent
73781e047b
commit
b9a12043b0
|
@ -38,7 +38,7 @@ fn main() {
|
||||||
|
|
||||||
// derive child xpub
|
// derive child xpub
|
||||||
let path = "84h/0h/0h".parse::<DerivationPath>().unwrap();
|
let path = "84h/0h/0h".parse::<DerivationPath>().unwrap();
|
||||||
let child = root.derive_xpriv(&secp, &path);
|
let child = root.derive_xpriv(&secp, &path).expect("only deriving three steps");
|
||||||
println!("Child at {}: {}", path, child);
|
println!("Child at {}: {}", path, child);
|
||||||
let xpub = Xpub::from_xpriv(&secp, &child);
|
let xpub = Xpub::from_xpriv(&secp, &child);
|
||||||
println!("Public key at {}: {}", path, xpub);
|
println!("Public key at {}: {}", path, xpub);
|
||||||
|
|
|
@ -62,11 +62,12 @@ fn get_external_address_xpriv<C: Signing>(
|
||||||
) -> Xpriv {
|
) -> Xpriv {
|
||||||
let derivation_path =
|
let derivation_path =
|
||||||
BIP84_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
|
BIP84_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
|
||||||
let child_xpriv = master_xpriv.derive_xpriv(secp, &derivation_path);
|
let child_xpriv =
|
||||||
|
master_xpriv.derive_xpriv(secp, &derivation_path).expect("only deriving three steps");
|
||||||
let external_index = ChildNumber::ZERO_NORMAL;
|
let external_index = ChildNumber::ZERO_NORMAL;
|
||||||
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
|
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
|
||||||
|
|
||||||
child_xpriv.derive_xpriv(secp, &[external_index, idx])
|
child_xpriv.derive_xpriv(secp, &[external_index, idx]).expect("only deriving two more steps")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive the internal address xpriv.
|
// Derive the internal address xpriv.
|
||||||
|
@ -77,11 +78,12 @@ fn get_internal_address_xpriv<C: Signing>(
|
||||||
) -> Xpriv {
|
) -> Xpriv {
|
||||||
let derivation_path =
|
let derivation_path =
|
||||||
BIP84_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
|
BIP84_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
|
||||||
let child_xpriv = master_xpriv.derive_xpriv(secp, &derivation_path);
|
let child_xpriv =
|
||||||
|
master_xpriv.derive_xpriv(secp, &derivation_path).expect("only deriving three steps");
|
||||||
let internal_index = ChildNumber::ONE_NORMAL;
|
let internal_index = ChildNumber::ONE_NORMAL;
|
||||||
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
|
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
|
||||||
|
|
||||||
child_xpriv.derive_xpriv(secp, &[internal_index, idx])
|
child_xpriv.derive_xpriv(secp, &[internal_index, idx]).expect("only deriving two more steps")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The address to send to.
|
// The address to send to.
|
||||||
|
|
|
@ -118,11 +118,12 @@ impl ColdStorage {
|
||||||
// Hardened children require secret data to derive.
|
// Hardened children require secret data to derive.
|
||||||
|
|
||||||
let path = "84h/0h/0h".into_derivation_path()?;
|
let path = "84h/0h/0h".into_derivation_path()?;
|
||||||
let account_0_xpriv = master_xpriv.derive_xpriv(secp, &path);
|
let account_0_xpriv =
|
||||||
|
master_xpriv.derive_xpriv(secp, &path).expect("derivation path is short");
|
||||||
let account_0_xpub = Xpub::from_xpriv(secp, &account_0_xpriv);
|
let account_0_xpub = Xpub::from_xpriv(secp, &account_0_xpriv);
|
||||||
|
|
||||||
let path = INPUT_UTXO_DERIVATION_PATH.into_derivation_path()?;
|
let path = INPUT_UTXO_DERIVATION_PATH.into_derivation_path()?;
|
||||||
let input_xpriv = master_xpriv.derive_xpriv(secp, &path);
|
let input_xpriv = master_xpriv.derive_xpriv(secp, &path).expect("derivation path is short");
|
||||||
let input_xpub = Xpub::from_xpriv(secp, &input_xpriv);
|
let input_xpub = Xpub::from_xpriv(secp, &input_xpriv);
|
||||||
|
|
||||||
let wallet = ColdStorage { master_xpriv, master_xpub };
|
let wallet = ColdStorage { master_xpriv, master_xpub };
|
||||||
|
|
|
@ -60,11 +60,12 @@ fn get_external_address_xpriv<C: Signing>(
|
||||||
) -> Xpriv {
|
) -> Xpriv {
|
||||||
let derivation_path =
|
let derivation_path =
|
||||||
BIP86_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
|
BIP86_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
|
||||||
let child_xpriv = master_xpriv.derive_xpriv(secp, &derivation_path);
|
let child_xpriv =
|
||||||
|
master_xpriv.derive_xpriv(secp, &derivation_path).expect("only deriving three steps");
|
||||||
let external_index = ChildNumber::ZERO_NORMAL;
|
let external_index = ChildNumber::ZERO_NORMAL;
|
||||||
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
|
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
|
||||||
|
|
||||||
child_xpriv.derive_xpriv(secp, &[external_index, idx])
|
child_xpriv.derive_xpriv(secp, &[external_index, idx]).expect("only deriving two more steps")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive the internal address xpriv.
|
// Derive the internal address xpriv.
|
||||||
|
@ -75,11 +76,12 @@ fn get_internal_address_xpriv<C: Signing>(
|
||||||
) -> Xpriv {
|
) -> Xpriv {
|
||||||
let derivation_path =
|
let derivation_path =
|
||||||
BIP86_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
|
BIP86_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
|
||||||
let child_xpriv = master_xpriv.derive_xpriv(secp, &derivation_path);
|
let child_xpriv =
|
||||||
|
master_xpriv.derive_xpriv(secp, &derivation_path).expect("only deriving three steps");
|
||||||
let internal_index = ChildNumber::ONE_NORMAL;
|
let internal_index = ChildNumber::ONE_NORMAL;
|
||||||
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
|
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
|
||||||
|
|
||||||
child_xpriv.derive_xpriv(secp, &[internal_index, idx])
|
child_xpriv.derive_xpriv(secp, &[internal_index, idx]).expect("only deriving two more steps")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the Taproot Key Origin.
|
// Get the Taproot Key Origin.
|
||||||
|
|
|
@ -299,7 +299,7 @@ fn generate_bip86_key_spend_tx(
|
||||||
.ok_or("missing Taproot key origin")?;
|
.ok_or("missing Taproot key origin")?;
|
||||||
|
|
||||||
let secret_key =
|
let secret_key =
|
||||||
master_xpriv.derive_xpriv(secp, &derivation_path).to_private_key().inner;
|
master_xpriv.derive_xpriv(secp, &derivation_path)?.to_private_key().inner;
|
||||||
sign_psbt_taproot(
|
sign_psbt_taproot(
|
||||||
secret_key,
|
secret_key,
|
||||||
input.tap_internal_key.unwrap(),
|
input.tap_internal_key.unwrap(),
|
||||||
|
@ -393,8 +393,11 @@ impl BenefactorWallet {
|
||||||
// We use some other derivation path in this example for our inheritance protocol. The important thing is to ensure
|
// We use some other derivation path in this example for our inheritance protocol. The important thing is to ensure
|
||||||
// that we use an unhardened path so we can make use of xpubs.
|
// that we use an unhardened path so we can make use of xpubs.
|
||||||
let derivation_path = format!("101/1/0/0/{}", self.next).parse::<DerivationPath>()?;
|
let derivation_path = format!("101/1/0/0/{}", self.next).parse::<DerivationPath>()?;
|
||||||
let internal_keypair =
|
let internal_keypair = self
|
||||||
self.master_xpriv.derive_xpriv(&self.secp, &derivation_path).to_keypair(&self.secp);
|
.master_xpriv
|
||||||
|
.derive_xpriv(&self.secp, &derivation_path)
|
||||||
|
.expect("derivation path is short")
|
||||||
|
.to_keypair(&self.secp);
|
||||||
let beneficiary_key =
|
let beneficiary_key =
|
||||||
self.beneficiary_xpub.derive_xpub(&self.secp, &derivation_path)?.to_x_only_public_key();
|
self.beneficiary_xpub.derive_xpub(&self.secp, &derivation_path)?.to_x_only_public_key();
|
||||||
|
|
||||||
|
@ -486,6 +489,7 @@ impl BenefactorWallet {
|
||||||
let new_internal_keypair = self
|
let new_internal_keypair = self
|
||||||
.master_xpriv
|
.master_xpriv
|
||||||
.derive_xpriv(&self.secp, &new_derivation_path)
|
.derive_xpriv(&self.secp, &new_derivation_path)
|
||||||
|
.expect("derivation path is short")
|
||||||
.to_keypair(&self.secp);
|
.to_keypair(&self.secp);
|
||||||
let beneficiary_key = self
|
let beneficiary_key = self
|
||||||
.beneficiary_xpub
|
.beneficiary_xpub
|
||||||
|
@ -538,6 +542,7 @@ impl BenefactorWallet {
|
||||||
let secret_key = self
|
let secret_key = self
|
||||||
.master_xpriv
|
.master_xpriv
|
||||||
.derive_xpriv(&self.secp, &derivation_path)
|
.derive_xpriv(&self.secp, &derivation_path)
|
||||||
|
.expect("derivation path is short")
|
||||||
.to_private_key()
|
.to_private_key()
|
||||||
.inner;
|
.inner;
|
||||||
sign_psbt_taproot(
|
sign_psbt_taproot(
|
||||||
|
@ -660,8 +665,11 @@ impl BeneficiaryWallet {
|
||||||
for (x_only_pubkey, (leaf_hashes, (_, derivation_path))) in
|
for (x_only_pubkey, (leaf_hashes, (_, derivation_path))) in
|
||||||
&psbt.inputs[0].tap_key_origins.clone()
|
&psbt.inputs[0].tap_key_origins.clone()
|
||||||
{
|
{
|
||||||
let secret_key =
|
let secret_key = self
|
||||||
self.master_xpriv.derive_xpriv(&self.secp, &derivation_path).to_private_key().inner;
|
.master_xpriv
|
||||||
|
.derive_xpriv(&self.secp, &derivation_path)?
|
||||||
|
.to_private_key()
|
||||||
|
.inner;
|
||||||
for lh in leaf_hashes {
|
for lh in leaf_hashes {
|
||||||
let sighash_type = TapSighashType::All;
|
let sighash_type = TapSighashType::All;
|
||||||
let hash = SighashCache::new(&unsigned_tx).taproot_script_spend_signature_hash(
|
let hash = SighashCache::new(&unsigned_tx).taproot_script_spend_signature_hash(
|
||||||
|
|
|
@ -185,6 +185,9 @@ impl ChildNumber {
|
||||||
|
|
||||||
/// Returns the child number that is a single increment from this one.
|
/// Returns the child number that is a single increment from this one.
|
||||||
pub fn increment(self) -> Result<ChildNumber, IndexOutOfRangeError> {
|
pub fn increment(self) -> Result<ChildNumber, IndexOutOfRangeError> {
|
||||||
|
// Bare addition in this function is okay, because we have an invariant that
|
||||||
|
// `index` is always within [0, 2^31 - 1]. FIXME this is not actually an
|
||||||
|
// invariant because the fields are public.
|
||||||
match self {
|
match self {
|
||||||
ChildNumber::Normal { index: idx } => ChildNumber::from_normal_idx(idx + 1),
|
ChildNumber::Normal { index: idx } => ChildNumber::from_normal_idx(idx + 1),
|
||||||
ChildNumber::Hardened { index: idx } => ChildNumber::from_hardened_idx(idx + 1),
|
ChildNumber::Hardened { index: idx } => ChildNumber::from_hardened_idx(idx + 1),
|
||||||
|
@ -706,7 +709,7 @@ impl Xpriv {
|
||||||
&self,
|
&self,
|
||||||
secp: &Secp256k1<C>,
|
secp: &Secp256k1<C>,
|
||||||
path: &P,
|
path: &P,
|
||||||
) -> Xpriv {
|
) -> Result<Xpriv, DerivationError> {
|
||||||
self.derive_xpriv(secp, path)
|
self.derive_xpriv(secp, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -717,16 +720,20 @@ impl Xpriv {
|
||||||
&self,
|
&self,
|
||||||
secp: &Secp256k1<C>,
|
secp: &Secp256k1<C>,
|
||||||
path: &P,
|
path: &P,
|
||||||
) -> Xpriv {
|
) -> Result<Xpriv, DerivationError> {
|
||||||
let mut sk: Xpriv = *self;
|
let mut sk: Xpriv = *self;
|
||||||
for cnum in path.as_ref() {
|
for cnum in path.as_ref() {
|
||||||
sk = sk.ckd_priv(secp, *cnum)
|
sk = sk.ckd_priv(secp, *cnum)?;
|
||||||
}
|
}
|
||||||
sk
|
Ok(sk)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Private->Private child key derivation
|
/// Private->Private child key derivation
|
||||||
fn ckd_priv<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumber) -> Xpriv {
|
fn ckd_priv<C: secp256k1::Signing>(
|
||||||
|
&self,
|
||||||
|
secp: &Secp256k1<C>,
|
||||||
|
i: ChildNumber,
|
||||||
|
) -> Result<Xpriv, DerivationError> {
|
||||||
let mut engine = HmacEngine::<sha512::HashEngine>::new(&self.chain_code[..]);
|
let mut engine = HmacEngine::<sha512::HashEngine>::new(&self.chain_code[..]);
|
||||||
match i {
|
match i {
|
||||||
ChildNumber::Normal { .. } => {
|
ChildNumber::Normal { .. } => {
|
||||||
|
@ -750,14 +757,14 @@ impl Xpriv {
|
||||||
let tweaked =
|
let tweaked =
|
||||||
sk.add_tweak(&self.private_key.into()).expect("statistically impossible to hit");
|
sk.add_tweak(&self.private_key.into()).expect("statistically impossible to hit");
|
||||||
|
|
||||||
Xpriv {
|
Ok(Xpriv {
|
||||||
network: self.network,
|
network: self.network,
|
||||||
depth: self.depth + 1,
|
depth: self.depth.checked_add(1).ok_or(DerivationError::MaximumDepthExceeded)?,
|
||||||
parent_fingerprint: self.fingerprint(secp),
|
parent_fingerprint: self.fingerprint(secp),
|
||||||
child_number: i,
|
child_number: i,
|
||||||
private_key: tweaked,
|
private_key: tweaked,
|
||||||
chain_code: ChainCode::from_hmac(hmac),
|
chain_code: ChainCode::from_hmac(hmac),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decoding extended private key from binary data according to BIP 32
|
/// Decoding extended private key from binary data according to BIP 32
|
||||||
|
@ -910,7 +917,7 @@ impl Xpub {
|
||||||
|
|
||||||
Ok(Xpub {
|
Ok(Xpub {
|
||||||
network: self.network,
|
network: self.network,
|
||||||
depth: self.depth + 1,
|
depth: self.depth.checked_add(1).ok_or(DerivationError::MaximumDepthExceeded)?,
|
||||||
parent_fingerprint: self.fingerprint(),
|
parent_fingerprint: self.fingerprint(),
|
||||||
child_number: i,
|
child_number: i,
|
||||||
public_key: tweaked,
|
public_key: tweaked,
|
||||||
|
@ -1188,7 +1195,7 @@ mod tests {
|
||||||
let mut pk = Xpub::from_xpriv(secp, &sk);
|
let mut pk = Xpub::from_xpriv(secp, &sk);
|
||||||
|
|
||||||
// Check derivation convenience method for Xpriv
|
// Check derivation convenience method for Xpriv
|
||||||
assert_eq!(&sk.derive_xpriv(secp, &path).to_string()[..], expected_sk);
|
assert_eq!(&sk.derive_xpriv(secp, &path).unwrap().to_string()[..], expected_sk);
|
||||||
|
|
||||||
// Check derivation convenience method for Xpub, should error
|
// Check derivation convenience method for Xpub, should error
|
||||||
// appropriately if any ChildNumber is hardened
|
// appropriately if any ChildNumber is hardened
|
||||||
|
@ -1203,7 +1210,7 @@ mod tests {
|
||||||
|
|
||||||
// Derive keys, checking hardened and non-hardened derivation one-by-one
|
// Derive keys, checking hardened and non-hardened derivation one-by-one
|
||||||
for &num in path.0.iter() {
|
for &num in path.0.iter() {
|
||||||
sk = sk.ckd_priv(secp, num);
|
sk = sk.ckd_priv(secp, num).unwrap();
|
||||||
match num {
|
match num {
|
||||||
Normal { .. } => {
|
Normal { .. } => {
|
||||||
let pk2 = pk.ckd_pub(secp, num).unwrap();
|
let pk2 = pk.ckd_pub(secp, num).unwrap();
|
||||||
|
|
|
@ -775,14 +775,14 @@ impl GetKey for Xpriv {
|
||||||
KeyRequest::XOnlyPubkey(_) => Err(GetKeyError::NotSupported),
|
KeyRequest::XOnlyPubkey(_) => Err(GetKeyError::NotSupported),
|
||||||
KeyRequest::Bip32((fingerprint, path)) => {
|
KeyRequest::Bip32((fingerprint, path)) => {
|
||||||
let key = if self.fingerprint(secp) == *fingerprint {
|
let key = if self.fingerprint(secp) == *fingerprint {
|
||||||
let k = self.derive_xpriv(secp, &path);
|
let k = self.derive_xpriv(secp, &path).map_err(GetKeyError::Bip32)?;
|
||||||
Some(k.to_private_key())
|
Some(k.to_private_key())
|
||||||
} else if self.parent_fingerprint == *fingerprint
|
} else if self.parent_fingerprint == *fingerprint
|
||||||
&& !path.is_empty()
|
&& !path.is_empty()
|
||||||
&& path[0] == self.child_number
|
&& path[0] == self.child_number
|
||||||
{
|
{
|
||||||
let path = DerivationPath::from_iter(path.into_iter().skip(1).copied());
|
let path = DerivationPath::from_iter(path.into_iter().skip(1).copied());
|
||||||
let k = self.derive_xpriv(secp, &path);
|
let k = self.derive_xpriv(secp, &path).map_err(GetKeyError::Bip32)?;
|
||||||
Some(k.to_private_key())
|
Some(k.to_private_key())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -915,8 +915,8 @@ impl_get_key_for_xonly_map!(HashMap);
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum GetKeyError {
|
pub enum GetKeyError {
|
||||||
/// A bip32 error.
|
/// A bip32 derivation error.
|
||||||
Bip32(bip32::ParseError),
|
Bip32(bip32::DerivationError),
|
||||||
/// The GetKey operation is not supported for this key request.
|
/// The GetKey operation is not supported for this key request.
|
||||||
NotSupported,
|
NotSupported,
|
||||||
}
|
}
|
||||||
|
@ -930,7 +930,7 @@ impl fmt::Display for GetKeyError {
|
||||||
use GetKeyError::*;
|
use GetKeyError::*;
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Bip32(ref e) => write_err!(f, "a bip23 error"; e),
|
Bip32(ref e) => write_err!(f, "bip32 derivation"; e),
|
||||||
NotSupported =>
|
NotSupported =>
|
||||||
f.write_str("the GetKey operation is not supported for this key request"),
|
f.write_str("the GetKey operation is not supported for this key request"),
|
||||||
}
|
}
|
||||||
|
@ -949,10 +949,6 @@ impl std::error::Error for GetKeyError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bip32::ParseError> for GetKeyError {
|
|
||||||
fn from(e: bip32::ParseError) -> Self { GetKeyError::Bip32(e) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The various output types supported by the Bitcoin network.
|
/// The various output types supported by the Bitcoin network.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
@ -1454,7 +1450,7 @@ mod tests {
|
||||||
ChildNumber::from_normal_idx(31337).unwrap(),
|
ChildNumber::from_normal_idx(31337).unwrap(),
|
||||||
];
|
];
|
||||||
|
|
||||||
sk = sk.derive_xpriv(secp, &dpath);
|
sk = sk.derive_xpriv(secp, &dpath).unwrap();
|
||||||
|
|
||||||
let pk = Xpub::from_xpriv(secp, &sk);
|
let pk = Xpub::from_xpriv(secp, &sk);
|
||||||
|
|
||||||
|
|
|
@ -315,7 +315,8 @@ fn parse_and_verify_keys(
|
||||||
|
|
||||||
let path =
|
let path =
|
||||||
derivation_path.into_derivation_path().expect("failed to convert derivation path");
|
derivation_path.into_derivation_path().expect("failed to convert derivation path");
|
||||||
let derived_priv = ext_priv.derive_xpriv(secp, &path).to_private_key();
|
let derived_priv =
|
||||||
|
ext_priv.derive_xpriv(secp, &path).expect("derivation path too long").to_private_key();
|
||||||
assert_eq!(wif_priv, derived_priv);
|
assert_eq!(wif_priv, derived_priv);
|
||||||
let derived_pub = derived_priv.public_key(secp);
|
let derived_pub = derived_priv.public_key(secp);
|
||||||
key_map.insert(derived_pub, derived_priv);
|
key_map.insert(derived_pub, derived_priv);
|
||||||
|
|
Loading…
Reference in New Issue