Skip to content

Commit 19eb66f

Browse files
committed
Merge #830: Properly forbid multipath keys in definite descriptor keys
b9ccd9d descriptor: add unit tests for constructing multipath keys (Andrew Poelstra) dad3532 DefiniteDescriptorKey: disallow multipath keys (Andrew Poelstra) 258873f key.rs: split "definite keys cannot have wildcards" into own error (Andrew Poelstra) bd5db6a descriptor/key.rs: clean up error matching (Andrew Poelstra) Pull request description: This PR essentially just fixes a logic bug in `DefiniteDescriptorKey::new` but involves several commits that just rearrange error types. I will backport the logic fix without the error refactoring, since for 12.x and earlier we didn't really have error types and were just returning `Error::Unexpected("arbitrary string")` style errors. ACKs for top commit: sanket1729: ACK b9ccd9d Tree-SHA512: 44f5033732eb2b67792cf1f93d909bd255ff5be251c3937450d93265746aedb2944db57b256ee7dea9ae0140c921320cc570a5be54244c4c97970eb3fe09f1ee
2 parents e5f50ab + b9ccd9d commit 19eb66f

File tree

1 file changed

+100
-81
lines changed

1 file changed

+100
-81
lines changed

src/descriptor/key.rs

Lines changed: 100 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,27 @@ impl DescriptorMultiXKey<bip32::Xpriv> {
327327
}
328328
}
329329

330+
/// Kinds of malformed key data
331+
#[derive(Debug, PartialEq, Clone)]
332+
#[non_exhaustive]
333+
#[allow(missing_docs)]
334+
pub enum NonDefiniteKeyError {
335+
Wildcard,
336+
Multipath,
337+
}
338+
339+
impl fmt::Display for NonDefiniteKeyError {
340+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341+
match *self {
342+
Self::Wildcard => f.write_str("key with a wildcard cannot be a DerivedDescriptorKey"),
343+
Self::Multipath => f.write_str("multipath key cannot be a DerivedDescriptorKey"),
344+
}
345+
}
346+
}
347+
348+
#[cfg(feature = "std")]
349+
impl error::Error for NonDefiniteKeyError {}
350+
330351
/// Kinds of malformed key data
331352
#[derive(Debug, PartialEq, Clone)]
332353
#[non_exhaustive]
@@ -346,29 +367,25 @@ pub enum MalformedKeyDataKind {
346367
NoKeyAfterOrigin,
347368
NoMasterFingerprintFound,
348369
UnclosedSquareBracket,
349-
WildcardAsDerivedDescriptorKey,
350370
}
351371

352372
impl fmt::Display for MalformedKeyDataKind {
353373
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354-
use MalformedKeyDataKind::*;
355-
356374
let err = match self {
357-
EmptyKey => "empty key",
358-
EncounteredUnprintableCharacter => "encountered an unprintable character",
359-
InvalidFullPublicKeyPrefix => "only full public keys with prefixes '02', '03' or '04' are allowed",
360-
InvalidMasterFingerprintLength => "master fingerprint should be 8 characters long",
361-
InvalidMultiIndexStep => "invalid multi index step in multipath descriptor",
362-
InvalidMultiXKeyDerivation => "can't make a multi-xpriv with hardened derivation steps that are not shared among all paths into a public key",
363-
InvalidPublicKeyLength => "public keys must be 64, 66 or 130 characters in size",
364-
InvalidWildcardInDerivationPath => "'*' may only appear as last element in a derivation path",
365-
KeyTooShort => "key too short",
366-
MultipleFingerprintsInPublicKey => "multiple ']' in Descriptor Public Key",
367-
MultipleDerivationPathIndexSteps => "'<' may only appear once in a derivation path",
368-
NoKeyAfterOrigin => "no key after origin",
369-
NoMasterFingerprintFound => "no master fingerprint found after '['",
370-
UnclosedSquareBracket => "unclosed '['",
371-
WildcardAsDerivedDescriptorKey => "cannot parse key with a wilcard as a DerivedDescriptorKey",
375+
Self::EmptyKey => "empty key",
376+
Self::EncounteredUnprintableCharacter => "encountered an unprintable character",
377+
Self::InvalidFullPublicKeyPrefix => "only full public keys with prefixes '02', '03' or '04' are allowed",
378+
Self::InvalidMasterFingerprintLength => "master fingerprint should be 8 characters long",
379+
Self::InvalidMultiIndexStep => "invalid multi index step in multipath descriptor",
380+
Self::InvalidMultiXKeyDerivation => "can't make a multi-xpriv with hardened derivation steps that are not shared among all paths into a public key",
381+
Self::InvalidPublicKeyLength => "public keys must be 64, 66 or 130 characters in size",
382+
Self::InvalidWildcardInDerivationPath => "'*' may only appear as last element in a derivation path",
383+
Self::KeyTooShort => "key too short",
384+
Self::MultipleFingerprintsInPublicKey => "multiple ']' in Descriptor Public Key",
385+
Self::MultipleDerivationPathIndexSteps => "'<' may only appear once in a derivation path",
386+
Self::NoKeyAfterOrigin => "no key after origin",
387+
Self::NoMasterFingerprintFound => "no master fingerprint found after '['",
388+
Self::UnclosedSquareBracket => "unclosed '['",
372389
};
373390

374391
f.write_str(err)
@@ -403,6 +420,8 @@ pub enum DescriptorKeyParseError {
403420
/// The underlying parse error
404421
err: bitcoin::hex::HexToArrayError,
405422
},
423+
/// Attempt to construct a [`DefiniteDescriptorKey`] from an ambiguous key.
424+
NonDefiniteKey(NonDefiniteKeyError),
406425
/// Error while parsing a simple public key.
407426
FullPublicKey(bitcoin::key::ParsePublicKeyError),
408427
/// Error while parsing a WIF private key.
@@ -414,72 +433,56 @@ pub enum DescriptorKeyParseError {
414433
impl fmt::Display for DescriptorKeyParseError {
415434
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
416435
match self {
417-
DescriptorKeyParseError::Bip32Xpriv(err) => {
418-
write!(f, "error while parsing BIP32 Xpriv: {err}")
419-
}
420-
DescriptorKeyParseError::Bip32Xpub(err) => {
421-
write!(f, "error while parsing BIP32 Xpub: {err}")
422-
}
423-
DescriptorKeyParseError::DerivationIndexError { index, err } => {
424-
write!(f, "error while parsing derivation index '{index}': {err}")
425-
}
426-
DescriptorKeyParseError::DeriveHardenedKey(err) => {
427-
write!(f, "unable to derive the hardened steps: {err}")
428-
}
429-
DescriptorKeyParseError::MalformedKeyData(err) => {
430-
write!(f, "{err}")
436+
Self::Bip32Xpriv(err) => err.fmt(f),
437+
Self::Bip32Xpub(err) => err.fmt(f),
438+
Self::DerivationIndexError { index, err } => {
439+
write!(f, "at derivation index '{index}': {err}")
431440
}
432-
DescriptorKeyParseError::MasterDerivationPath(err) => {
433-
write!(f, "error while parsing master derivation path: {err}")
434-
}
435-
DescriptorKeyParseError::MasterFingerprint { fingerprint, err } => {
436-
write!(f, "error while parsing master fingerprint '{fingerprint}': {err}")
437-
}
438-
DescriptorKeyParseError::FullPublicKey(err) => {
439-
write!(f, "error while parsing full public key: {err}")
440-
}
441-
DescriptorKeyParseError::WifPrivateKey(err) => {
442-
write!(f, "error while parsing WIF private key: {err}")
443-
}
444-
DescriptorKeyParseError::XonlyPublicKey(err) => {
445-
write!(f, "error while parsing xonly public key: {err}")
441+
Self::DeriveHardenedKey(err) => err.fmt(f),
442+
Self::MalformedKeyData(err) => err.fmt(f),
443+
Self::MasterDerivationPath(err) => err.fmt(f),
444+
Self::MasterFingerprint { fingerprint, err } => {
445+
write!(f, "on master fingerprint '{fingerprint}': {err}")
446446
}
447+
Self::NonDefiniteKey(err) => err.fmt(f),
448+
Self::FullPublicKey(err) => err.fmt(f),
449+
Self::WifPrivateKey(err) => err.fmt(f),
450+
Self::XonlyPublicKey(err) => err.fmt(f),
447451
}
448452
}
449453
}
450454

451455
#[cfg(feature = "std")]
452456
impl error::Error for DescriptorKeyParseError {
453457
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
454-
use DescriptorKeyParseError::*;
455-
456458
match self {
457-
Bip32Xpriv(err)
458-
| Bip32Xpub(err)
459-
| DerivationIndexError { err, .. }
460-
| DeriveHardenedKey(err)
461-
| MasterDerivationPath(err) => Some(err),
462-
MasterFingerprint { err, .. } => Some(err),
463-
FullPublicKey(err) => Some(err),
464-
WifPrivateKey(err) => Some(err),
465-
XonlyPublicKey(err) => Some(err),
466-
MalformedKeyData(_) => None,
459+
Self::Bip32Xpriv(err)
460+
| Self::Bip32Xpub(err)
461+
| Self::DerivationIndexError { err, .. }
462+
| Self::DeriveHardenedKey(err)
463+
| Self::MasterDerivationPath(err) => Some(err),
464+
Self::MasterFingerprint { err, .. } => Some(err),
465+
Self::NonDefiniteKey(err) => Some(err),
466+
Self::FullPublicKey(err) => Some(err),
467+
Self::WifPrivateKey(err) => Some(err),
468+
Self::XonlyPublicKey(err) => Some(err),
469+
Self::MalformedKeyData(_) => None,
467470
}
468471
}
469472
}
470473

471474
impl fmt::Display for DescriptorPublicKey {
472475
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
473476
match *self {
474-
DescriptorPublicKey::Single(ref pk) => {
477+
Self::Single(ref pk) => {
475478
maybe_fmt_master_id(f, &pk.origin)?;
476479
match pk.key {
477480
SinglePubKey::FullKey(full_key) => full_key.fmt(f),
478481
SinglePubKey::XOnly(x_only_key) => x_only_key.fmt(f),
479482
}?;
480483
Ok(())
481484
}
482-
DescriptorPublicKey::XPub(ref xpub) => {
485+
Self::XPub(ref xpub) => {
483486
maybe_fmt_master_id(f, &xpub.origin)?;
484487
xpub.xkey.fmt(f)?;
485488
fmt_derivation_path(f, &xpub.derivation_path)?;
@@ -490,7 +493,7 @@ impl fmt::Display for DescriptorPublicKey {
490493
}
491494
Ok(())
492495
}
493-
DescriptorPublicKey::MultiXPub(ref xpub) => {
496+
Self::MultiXPub(ref xpub) => {
494497
maybe_fmt_master_id(f, &xpub.origin)?;
495498
xpub.xkey.fmt(f)?;
496499
fmt_derivation_paths(f, xpub.derivation_paths.paths())?;
@@ -694,19 +697,17 @@ pub enum ConversionError {
694697
impl fmt::Display for ConversionError {
695698
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
696699
f.write_str(match *self {
697-
ConversionError::HardenedChild => "hardened child step in bip32 path",
698-
ConversionError::MultiKey => "multiple existing keys",
700+
Self::HardenedChild => "hardened child step in bip32 path",
701+
Self::MultiKey => "multiple existing keys",
699702
})
700703
}
701704
}
702705

703706
#[cfg(feature = "std")]
704707
impl error::Error for ConversionError {
705708
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
706-
use self::ConversionError::*;
707-
708709
match self {
709-
HardenedChild | MultiKey => None,
710+
Self::HardenedChild | Self::MultiKey => None,
710711
}
711712
}
712713
}
@@ -1272,11 +1273,13 @@ impl DefiniteDescriptorKey {
12721273
/// Construct an instance from a descriptor key and a derivation index
12731274
///
12741275
/// Returns `None` if the key contains a wildcard
1275-
pub fn new(key: DescriptorPublicKey) -> Option<Self> {
1276+
pub fn new(key: DescriptorPublicKey) -> Result<Self, NonDefiniteKeyError> {
12761277
if key.has_wildcard() {
1277-
None
1278+
Err(NonDefiniteKeyError::Wildcard)
1279+
} else if key.is_multipath() {
1280+
Err(NonDefiniteKeyError::Multipath)
12781281
} else {
1279-
Some(Self(key))
1282+
Ok(Self(key))
12801283
}
12811284
}
12821285

@@ -1305,10 +1308,8 @@ impl FromStr for DefiniteDescriptorKey {
13051308
type Err = DescriptorKeyParseError;
13061309

13071310
fn from_str(s: &str) -> Result<Self, Self::Err> {
1308-
let inner = DescriptorPublicKey::from_str(s)?;
1309-
DefiniteDescriptorKey::new(inner).ok_or(DescriptorKeyParseError::MalformedKeyData(
1310-
MalformedKeyDataKind::WildcardAsDerivedDescriptorKey,
1311-
))
1311+
let d = DescriptorPublicKey::from_str(s)?;
1312+
DefiniteDescriptorKey::new(d).map_err(DescriptorKeyParseError::NonDefiniteKey)
13121313
}
13131314
}
13141315

@@ -1384,7 +1385,9 @@ mod test {
13841385
use super::{
13851386
DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, MiniscriptKey, Wildcard,
13861387
};
1388+
use crate::descriptor::key::NonDefiniteKeyError;
13871389
use crate::prelude::*;
1390+
use crate::DefiniteDescriptorKey;
13881391

13891392
#[test]
13901393
fn parse_descriptor_key_errors() {
@@ -1399,14 +1402,14 @@ mod test {
13991402
let desc = "[NonHexor]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*";
14001403
assert_eq!(
14011404
DescriptorPublicKey::from_str(desc).unwrap_err().to_string(),
1402-
"error while parsing master fingerprint 'NonHexor': failed to parse hex digit"
1405+
"on master fingerprint 'NonHexor': failed to parse hex digit"
14031406
);
14041407

14051408
// And ones with invalid xpubs..
14061409
let desc = "[78412e3a]xpub1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaLcgJvLJuZZvRcEL/1/*";
14071410
assert_eq!(
14081411
DescriptorPublicKey::from_str(desc).unwrap_err().to_string(),
1409-
"error while parsing BIP32 Xpub: base58 encoding error"
1412+
"base58 encoding error"
14101413
);
14111414

14121415
// ..or invalid raw keys
@@ -1446,22 +1449,19 @@ mod test {
14461449
DescriptorSecretKey::from_str(secret_key)
14471450
.unwrap_err()
14481451
.to_string(),
1449-
"error while parsing BIP32 Xpriv: unknown version magic bytes: [4, 136, 178, 30]"
1452+
"unknown version magic bytes: [4, 136, 178, 30]"
14501453
);
14511454

14521455
// And ones with invalid fingerprints
14531456
let desc = "[NonHexor]tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/1/*";
14541457
assert_eq!(
14551458
DescriptorSecretKey::from_str(desc).unwrap_err().to_string(),
1456-
"error while parsing master fingerprint 'NonHexor': failed to parse hex digit"
1459+
"on master fingerprint 'NonHexor': failed to parse hex digit"
14571460
);
14581461

14591462
// ..or invalid raw keys
14601463
let desc = "[78412e3a]L32jTfVLei6BYTPUpwpJSkrHx8iL9GZzeErVS8y4Y/1/*";
1461-
assert_eq!(
1462-
DescriptorSecretKey::from_str(desc).unwrap_err().to_string(),
1463-
"error while parsing WIF private key: invalid base58"
1464-
);
1464+
assert_eq!(DescriptorSecretKey::from_str(desc).unwrap_err().to_string(), "invalid base58");
14651465
}
14661466

14671467
#[test]
@@ -1767,4 +1767,23 @@ mod test {
17671767
let public_key = DescriptorPublicKey::from_str(desc).unwrap();
17681768
assert_tokens(&public_key, &[Token::String(desc)]);
17691769
}
1770+
1771+
#[test]
1772+
fn definite_keys() {
1773+
// basic xpub
1774+
let desc = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
1775+
.parse::<DescriptorPublicKey>()
1776+
.unwrap();
1777+
assert!(matches!(DefiniteDescriptorKey::new(desc), Ok(..)));
1778+
// xpub with wildcard
1779+
let desc = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/*"
1780+
.parse::<DescriptorPublicKey>()
1781+
.unwrap();
1782+
assert!(matches!(DefiniteDescriptorKey::new(desc), Err(NonDefiniteKeyError::Wildcard)));
1783+
// multipath xpub
1784+
let desc = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/<0;1>"
1785+
.parse::<DescriptorPublicKey>()
1786+
.unwrap();
1787+
assert!(matches!(DefiniteDescriptorKey::new(desc), Err(NonDefiniteKeyError::Multipath)));
1788+
}
17701789
}

0 commit comments

Comments
 (0)