Skip to content

Use a strong recovery commitment, not only the 32-bit fingerprint #27

Description

@BenWestgate

Medium design concern. Restore authentication currently relies on the four-byte BIP32 master fingerprint. That is useful as a human diagnostic, but 32 bits is too weak as the sole active authorization value against a deliberately chosen collision.

Remediation: store and verify an independent public commitment with at least 128 bits of collision resistance before descriptor import. Keep the BIP32 fingerprint as a display aid.

Parent: #20

Activity

  1. BenWestgate commented on Sep 24, 2026

    @BenWestgate
    OwnerAuthor

    Proposal, replacing #29 and #31:

    • Gate: the user types the master fingerprint, and a mismatch is rejected. The check is enforced once, in BitcoinCore.initialize() (gui: Require the recorded fingerprint before import #28). 32 bits catches accidents: wrong cards, a miscorrection, or mixed sets.
    • What 32 bits misses: a colluder who generates the substitute seed can grind a matching fingerprint in about 2^32 trials. A substituted wallet can also carry a fabricated history that an heir can't judge. The no-record path (20-bit identifier) only catches accidents.
    • A 256-bit commitment is rejected: it is too long to write and confirm correctly.
    • Proposed stronger check: also record the descriptor checksum of the BIP84 receive descriptor. It is 8 characters and Bitcoin Core shows it in listdescriptors.
      • The checksum is linear, but its input is the account xpub, which the attacker can only change by trying another seed.
      • Matching both fields takes about 2^72 seed trials, each an HMAC-SHA512 plus an EC multiplication.
      • Needs deciding: pin the exact descriptor and its h/' form, because the checksum depends on the exact string.
  2. self-assigned this
    on Sep 24, 2026
  3. added
    enhancementNew feature or request
    questionFurther information is requested
    wontfixThis will not be worked on
    on Sep 24, 2026
  4. BenWestgate commented on Sep 24, 2026

    @BenWestgate
    OwnerAuthor

    store and verify an independent public commitment with at least 128 bits of collision resistance

    You mean backup the public descriptor, of course we will. The recovery card says to do this.

    it would be better if what we write authenticates both the descriptor and the master seed. Rather than authenticating the master seed further and error checking the descriptor.

    I believe anything we are writing down should have a checksum, that means draw a QR or choose a bech32/m, codex32 or descriptor encoding of it.

    Based on what the reviewer said anything without 128-bits of collision resistance will not really protect this from tampering. So we should keep the authentication record digital rather than write a minimum 74 character codex32 string storing it. We aren't writing the descriptor down so that makes sense to me.

    Bails can BIP85 derive a GPG key from the recovered master seed, decrypt our GPG encrypted descriptor, and check for a good signature, that proves we have recovered the correct descriptor and if our fingerprint is in there and our seed can derive the xpub that strongly confirms the seed recovery against malicious tampering. All without writing or typing more than just the seed

  5. BenWestgate commented on Sep 24, 2026

    @BenWestgate
    OwnerAuthor

    Proposal, replacing #29 and #31:

    * **What 32 bits misses:** a colluder who generates the substitute seed can grind a matching fingerprint in about 2^32 trials. A substituted wallet can also carry a fabricated history that an heir can't judge. The no-record path (20-bit identifier) only catches accidents.
    

    This attacker can already steal your funds if they wanted to. To grind a matching fingerprint they already know the seed they're giving you and have access to a threshold or your secret seed.

    * **A 256-bit commitment is rejected:** it is too long to write and confirm correctly.
    

    cACK this must not be the default, if it's presented as an option at all.

    * **Proposed stronger check:** also record the descriptor checksum of the BIP84 receive descriptor. It is 8 characters and Bitcoin Core shows it in `listdescriptors`.
    

    This isn't cryptographic protection for the descriptor but it is for the seed because the correct descriptor will have a hash of the seed in it, which the descriptor checksum is linear over.

    But if the descriptor itself is unauthenticated, it can't authenticate the seed either so see comments about encryption, HMAC and signatures.

      * The checksum is linear, but its input is the account xpub, which the attacker can only change by trying another seed.
      * Matching both fields takes about 2^72 seed trials, each an HMAC-SHA512 plus an EC multiplication.
      * Needs deciding: pin the exact descriptor and its `h`/`'` form, because the checksum depends on the exact string.
    

    well the H form is smaller in QRs.

    Proposed stronger check: also record the descriptor checksum of the BIP84 receive descriptor. It is 8 characters and Bitcoin Core shows it in listdescriptors.

    This is pretty arbitrary, I dislike that.

    I'd rather use their actual descriptor backup, HMAC'd/Signed/encrypted.

  6. BenWestgate commented on Sep 24, 2026

    @BenWestgate
    OwnerAuthor

    Closing for single-sig. Anyone able to replace a threshold of cards can already read and spend them, so the typed fingerprint (#28, #57) covers mistakes. The seed-keyed encrypted descriptor backup (#55) gives full-strength verification whenever a copy is available. Authenticating descriptors the seed can't re-derive (multisig, policies) is tracked in BenWestgate/Bails#236.

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestgate: adversarial reviewResolve, merge, or explicitly defer before the next full adversarial review.questionFurther information is requestedwontfixThis will not be worked on

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions