BIP93: Fix checksum selection boundaries #2258

pull BenWestgate wants to merge 3 commits into bitcoin:master from BenWestgate:bip93-checksum-boundary changing 1 files +117 −26
  1. BenWestgate commented at 4:49 AM on August 17, 2026: contributor

    Motivation

    BIP93 currently admits expanded codewords beyond the checksum’s stated period, so its detection guarantee no longer applies to the complete codeword. This cause problems for a generic codex32_decode function in later PRs. Both regular and long checksums permitted codewords in excess of their period.

    Changes

    • ms32_verify_checksum and ms32_create_checksum now include the expanded "ms" HRP length when selecting regular vs. long checksums.
    • ms32_create_checksum uses if 5 + len(data) > 80: as the switch to ms32_create_long_checksum.
    • added ms32_create_regular_checksum and ms32_verify_regular_checksum.
    • The Python reference code, specification, rationale, and vectors are updated.

    Valid lengths:

    • Regular codewords will now be <= 93 expanded values
    • Long codewords will now be >= 96 and <= 1023 expanded values

    Invalid lengths:

    • Expanded values 94, 95 and > 1023.

    Compatibility

    This is a breaking change for old regular codex32 strings encoding 44-, 45-, and 46-byte seeds. However the authors do not see this is a problem due to the exceedingly rare possibility these have ever been created and used. The underlying seed lengths remain supported so users must migrate by reencoding their data with the Long codex32 checksum for modern decoders to accept them. The 43-byte regular encoding and existing 47-byte long encoding remain valid.

    Testing

    Thoroughly reviewed the complete diff. Checked the new 43–47-byte boundary vectors. Checked that expanded length 1023 verifies and 1024 fails. Checked that the legacy short-checksum vectors should fail.

    Discussion

    Proposal: #2040 (comment) cACK #2040 (comment) Reference impl issue: https://github.com/BlockstreamResearch/codex32/issues/75 rust-codex32 PR: https://github.com/BlockstreamResearch/codex32/pull/76

  2. BIP93: Correct checksum selection boundaries
    The checksum correction guarantees apply to the expanded codeword,
    which includes the five-value expansion of the "ms" human-readable
    part. The reference code instead selected the checksum using only the
    data-part length, allowing regular checksums beyond their 93-value
    correction boundary.
    
    Select regular and long checksums using expanded codeword length.
    Reject expanded lengths 94 and 95, and reject long checksums above
    1023 values. Keep checksum creation permissive while enforcing these
    limits during verification.
    
    This invalidates legacy short-checksum encodings for 44-, 45-, and
    46-byte master seeds. The underlying seed lengths remain supported
    using the long checksum.
    
    Update the specification, Python reference code, compatibility
    discussion, and boundary vectors accordingly.
    
    Refs: https://github.com/BlockstreamResearch/codex32/issues/75
    3531824ed9
  3. vitrixLab commented at 5:05 AM on August 17, 2026: none

    LGTM!

    — specification-consistent correction of the boundary issue discussed in "PR #2040" (https://github.com/bitcoin/bips/pull/2040).

    Checksum selection must use the expanded codeword length, including the fixed "ms" HRP’s five-value expansion.

    · Regular: ≤93 · Invalid: 94–95 · Long: 96–1023

    This matches the 43-byte regular / 44–46-byte long boundary.

    Intentional consequence: legacy regular-checksum encodings of 44–46-byte seeds become invalid and must use the long checksum.

    Remaining gate: reproducible boundary vectors.

  4. murchandamus added the label Proposed BIP modification on Aug 17, 2026
  5. murchandamus added the label Pending acceptance on Aug 17, 2026
  6. murchandamus commented at 9:54 PM on August 17, 2026: member
  7. Update bip-0093.mediawiki
    BIP93: Separate checksum primitives from format selection  Distinguish the regular and long checksum primitives from the codex32 format selector. Each primitive verifier enforces its stated maximum period, while ms32_verify_checksum selects the checksum required by BIP93 and rejects expanded lengths 94 and 95.  Keep ms32_create_checksum as the format-selection helper and add the corresponding regular-checksum creator.  Add reproducible 43- through 47-byte vectors and direct assertions for the 93/94/95/96 and 1023/1024 expanded-length boundaries.
    a9d8246244
  8. BenWestgate commented at 2:03 AM on August 20, 2026: contributor

    Remaining gate: reproducible boundary vectors.

    I added "reproducible boundary vectors."

    Edit: Unsure if I should have done this to satisfy a review comment that was later marked "low quality".

    For vectors, we need at minimum:

    • add regular checksum 44-, 45-, and 46-byte seeds to These examples use the wrong checksum for their given data sizes
    • add Expanded values 94, 95 and > 1023 to These examples have improper lengths
    • remove any vectors that are now accepted

    Willing to revert the "reproducible" vectors added in a9d8246244c06b4143b280844e7b8cf7215fe8f9 if reviews also find it overkill or open to another suggestion.

    What I would keep: Separated checksum verification into regular/long primitives that check expanded length is within the checksum period and residue is valid. While ms32_verify_checksum owns the switch to long primitive at 96 expanded symbol codewords and calls the above.

    Similarly ms32_create_regular_checksum creates the short checksum for data, while ms32_create_checksum selects the required checksum by expanded codewode length and creates it.

    It should be easier to generate test vectors and test them now, as well as to reuse our checksums for non-codex32 data.

  9. BenWestgate referenced this in commit 581a472eea on Aug 20, 2026
  10. Fix newline at end of bip-0093.mediawiki 7c5251d29a

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bips. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-08-24 03:54 UTC