Skip to content

fix(signer): validate wildcard derivation paths - #569

Open
busayo-OD wants to merge 2 commits into
bitcoindevkit:masterfrom
busayo-OD:fix/xprv-wildcard-signing
Open

busayo-OD wants to merge 2 commits into
bitcoindevkit:masterfrom
busayo-OD:fix/xprv-wildcard-signing

Conversation

@busayo-OD

@busayo-OD busayo-OD commented Sep 17, 2026 •

Copy link
Copy Markdown

Fixes #563

Description

SignerWrapper<DescriptorXKey<Xpriv>>::sign_input uses DescriptorXKey::matches() to determine whether a PSBT key origin
matches the signer. Since matches() ignores the final derivation step for wildcard descriptors, a hardened child could be accepted for an unhardened wildcard, and vice versa.

Notes to the reviewers

DescriptorXKey::matches() intentionally ignores the wildcard's final path step, so the additional check is required to validate the wildcard-specific derivation constraint.

Added regression tests covering both hardened and unhardened wildcard descriptors and their valid and invalid derivation paths, through both the bip32_derivation and tap_key_origins key-origin paths.

Changelog notice

  • Validate wildcard derivation hardness when signing with an xprv signer

Before submitting

@j-kon j-kon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 6b15185

Reviewed and verified:

  • Confirmed reproduction of #563 on master: 0/* signed for m/0/1h and 0/*h signed for m/0/1.
  • Confirmed PR #569 rejects both invalid combinations and accepts legitimate ones across both debug and release builds.
  • Verified that DescriptorXKey::matches() guarantees prefix equality and length (compare_path.len() + 1), so keysource.1.into_iter().last() is guaranteed to be the wildcard child step.
  • Verified path length edge cases (missing child, extra steps, empty paths) and Wildcard::None exact path matching.
  • Verified that tap_key_origins (Taproot key-path and script-path) enforces the same wildcard hardness checks.
  • Verified that multiple candidate origins in find_map skip invalid entries without error and sign valid ones.

@Jennycj Jennycj left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this fix. I was able to run the tests without the fix and reproduced the bug, which this commit fixes.

I have an observation though: I think that tests should be added to cover the tap_key_origins path as the tests currently cover the bip32_derivation path. Even though the check covers both, any change that is made to taproot that makes taproot entries skip the check would slip through because they are not being asserted here.

`DescriptorXKey::matches()` ignores the final derivation step for
wildcard descriptors, so the xprv signer must validate its hardenedness
separately before signing.
Add regression tests for the wildcard-hardness check in the Xprv
signer, through both the bip32_derivation and tap_key_origins paths:
`*` accepts only unhardened children and `*h` only hardened ones.
@busayo-OD
busayo-OD force-pushed the fix/xprv-wildcard-signing branch from 6b15185 to 2ad3042 Compare September 28, 2026 09:53
@busayo-OD

Copy link
Copy Markdown
Author

I have an observation though: I think that tests should be added to cover the tap_key_origins path as the tests currently cover the bip32_derivation path. Even though the check covers both, any change that is made to taproot that makes taproot entries skip the check would slip through because they are not being asserted here.

Thanks for pointing this out! I’ve added tests for tap_key_origins as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Xprv signer signs for hardened child under an unhardened wildcard descriptor

3 participants