Skip to content

descriptor: reject keys deeper than BIP32 allows - #1065

Open
starius wants to merge 2 commits into
rust-bitcoin:masterfrom
starius:descriptor-key-depth
Open

starius wants to merge 2 commits into
rust-bitcoin:masterfrom
starius:descriptor-key-depth

Conversation

@starius

@starius starius commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

DefiniteDescriptorKey::new checks for wildcards, hardened steps and multipath derivation, but not for BIP32's maximum depth of 255. A key that passes these checks but derives past that depth makes rust-bitcoin return MaximumDepthExceeded, and derive_public_key turns that error into a panic labeled "cryptographically unreachable". A descriptor string is enough to trigger it: tr(xpub/0/0/.../0) with 256 steps below a master xpub parses, passes into_definite(), and panics in script_pubkey().

This is missing validation in miniscript. Deriving that deep used to overflow rust-bitcoin's depth counter (rust-bitcoin/rust-bitcoin#4308). rust-bitcoin 0.32.6 made it a recoverable error (rust-bitcoin/rust-bitcoin#4434), which miniscript doesn't handle.

The first commit makes DefiniteDescriptorKey::new return the new NonDefiniteKeyError::MaximumDepthExceeded. into_definite() and derivation at an index go through it, so they report the error too. Only the steps after the extended key count, since its depth already includes the steps of its origin.

The second commit rejects such key expressions when parsing, with the new MalformedKeyDataKind::MaximumDepthExceeded. It counts a wildcard as one more step and covers xprvs and multipath keys. It's a separate commit because some strings that parse today will stop parsing, even though no key can be derived from them. It can be dropped if parsing should stay unchanged.

Together, they remove the deterministic depth panic. Derivation can still fail in BIP32's invalid-child case, which has a probability below 2^-127 and stays unreachable!.

Tests cover depths 255 and 256, a nonzero starting depth, wildcard derivation, multipath keys, xprvs and origin paths.

This pre-existing bug was found while working on #954.

🤖 AI disclosure: The patch, tests and this description were generated with Claude Opus 5.5. Codex reviewed the approach.

DefiniteDescriptorKey::new checks for wildcards, hardened steps and
multipath derivation, but not for BIP32's maximum depth of 255. A key
that passes these checks but derives past that depth makes rust-bitcoin
return MaximumDepthExceeded, and derive_public_key turns this error
into a panic labeled "cryptographically unreachable". A descriptor
string can trigger it: an xpub with a long enough path parses, becomes
definite, and panics when its script pubkey is computed.

Return the new NonDefiniteKeyError::MaximumDepthExceeded from
DefiniteDescriptorKey::new instead. Derivation at an index and
into_definite go through it, so they return the error too. Only the
steps after the extended key count, since its depth already includes
its origin steps.

Assisted-by: Claude Opus 5.5
A key expression that derives past BIP32's maximum depth of 255 can
never produce a key. The previous commit rejects such a key when it
becomes definite. Reject it when parsing too, so that the error points
at the string that caused it.

Count the steps after the extended key, plus one for a wildcard, for
xpubs and xprvs, including multipath keys. Origin steps don't count.

Assisted-by: Claude Opus 5.5
@apoelstra

apoelstra commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Can you backport to the release-13.x branch? Should apply cleanly.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants