Merge #577: descriptor: refuse non-normalized xpubs

f4d184f6f7fb748bff17f8a846616c589a432342 descriptor: refuse non-normalized xpubs (Antoine Poinsot)

Pull request description:

  We wouldn't be able to derive them. See https://github.com/wizardsardine/liana/issues/576#issuecomment-1642429179.

ACKs for top commit:
  darosior:
    self-ACK f4d184f6f7fb748bff17f8a846616c589a432342

Tree-SHA512: 2da34a8fccbeda1bab69d7bfd6e453173578884d76718ba54f6c505639f4117de9f8d03850b5bddb75bc3e23a57ddfc1ce4b885250c6ff468d591932e947f379
This commit is contained in:
Antoine Poinsot 2023-07-20 09:16:47 +02:00
commit 7ae84a22d2
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
2 changed files with 12 additions and 3 deletions

View File

@ -92,17 +92,20 @@ impl DescKeyChecker {
self.keys_set.insert(xpub.xkey);
// Then perform the contextless checks.
let der_paths = xpub.derivation_paths.paths();
let first_der_path = der_paths.get(0).expect("Cannot be empty");
// Rust-miniscript enforces BIP389 which states that all paths must have the same len.
let len = der_paths.get(0).expect("Cannot be empty").len();
let len = first_der_path.len();
// Technically the xpub could be for the master xpub and not have an origin. But it's
// no unlikely (and easily fixable) while users shooting themselves in the foot by
// unlikely (and easily fixable) while users shooting themselves in the foot by
// forgetting to provide the origin is so likely that it's worth ruling out xpubs
// without origin entirely.
// We also rule out xpubs with hardened derivation steps (non-normalized xpubs).
let valid = xpub.origin.is_some()
&& xpub.wildcard == descriptor::Wildcard::Unhardened
&& der_paths.len() == 2
&& der_paths[0][len - 1] == 0.into()
&& der_paths[1][len - 1] == 1.into();
&& der_paths[1][len - 1] == 1.into()
&& first_der_path.into_iter().all(|step| step.is_normal());
if valid {
return Ok(());
}

View File

@ -577,6 +577,12 @@ mod tests {
let timelock = 52560;
LianaPolicy::new(owner_key, [(timelock, heir_key)].iter().cloned().collect()).unwrap_err();
// One of the xpub isn't normalized.
let owner_key = PathInfo::Single(descriptor::DescriptorPublicKey::from_str("[abcdef01]xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*").unwrap());
let heir_key = PathInfo::Single(descriptor::DescriptorPublicKey::from_str("[aabbccdd]xpub688Hn4wScQAAiYJLPg9yH27hUpfZAUnmJejRQBCiwfP5PEDzjWMNW1wChcninxr5gyavFqbbDjdV1aK5USJz8NDVjUy7FRQaaqqXHh5SbXe/42'/<0;1>/*").unwrap());
let timelock = 52560;
LianaPolicy::new(owner_key, [(timelock, heir_key)].iter().cloned().collect()).unwrap_err();
// A 1-of-N multisig as primary path.
LianaDescriptor::from_str("wsh(or_d(multi(1,[573fb35b/48'/1'/0'/2']tpubDFKp9T7WAYDcENSjoifkrpq1gMDF47KGJcJrpxzX23Qor8wuGbrEVs9utNq1MDS8E2WXJSBk1qoPQLpwyokW7DiUNPwFuxQkL7owNkLAb9W/<0;1>/*,[573fb35b/48'/1'/1'/2']tpubDFGezyzuHJPhdP3jHGW7v7Hwes4Hihqv5W2yyCmRY9VZJCRchETvxrMC8uECeJZdxQ14V4iD4DecoArkUSDwj8ogYE9WEv4MNZr12thNHCs/<0;1>/*),and_v(v:multi(2,[573fb35b/48'/1'/2'/2']tpubDDwxQauiaU964vPzt5Vd7jnDHEUtp2Vc34PaWpEXg5TQ3bRccxnc1MKKh88Hi7xiMeZo9Tm6fBcq4UGXqnDtGUniJLjqAD8SjQ8Eci3aSR7/<0;1>/*,[573fb35b/48'/1'/3'/2']tpubDE37XAVB5CQ1x85md3BQ5uHCoMwT5fgT8X13zzCUQ3x5o2jskYxKjj7Qcxt1Jpj4QB8tqspn2dooPCekRuQDYrDHov7J1ueUNu2wcvgRDxr/<0;1>/*),older(1000))))#qjx6ycpc").unwrap();
}