Skip to content

bip93: Allow other human-readable parts (staging for #2040 successor) - #2

Open
BenWestgate wants to merge 3 commits into
bip93-master-seed-refactorfrom
bip93-generalize-hrp
Open

BenWestgate wants to merge 3 commits into
bip93-master-seed-refactorfrom
bip93-generalize-hrp

Conversation

@BenWestgate

@BenWestgate BenWestgate commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

Staging PR within the fork so this can get reviewed in isolation before going to bitcoin/bips, per BIP-3's recommendation to work in public on a fork before opening against the main repository.

Base is bip93-master-seed-refactor (bitcoin#2285's branch, assumed to merge unchanged). These 3 commits are the HRP-generalization part of the old bitcoin#2040, rewritten from scratch since that PR can no longer be reopened (its branch was rebased onto a sibling PR's head after closing, which GitHub does not allow reopening from). Diff: exactly 3 commits, nothing from bitcoin#2285 itself.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: 0ec3358223

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Specify the human-readable part as in BIP-0173 instead of requiring
"ms", so other applications, such as the registered "cl", can use the
codex32 format. Master seeds and their shares keep "ms".

Pass the human-readable part to the checksum functions and cover its
BIP-0173 expansion, which already counts toward the checksum length
limits. Rename the ms32 functions and constants that now serve every
human-readable part to codex32. Results for "ms" are unchanged.

Require every share in a set to have the same human-readable part, state
that the interpolation helpers do not check the set conditions, and limit
fresh-secret generation to applications that accept every payload, with
rejection sampling allowed for those that do not. Implementations should
not correct the human-readable part unless its application specifies how.

Checked that the new functions match the previous ms32 functions for
"ms" on random data parts of every length from 0 to 1029 symbols
(expanded lengths 5 to 1034), covering both checksums, the 94-95 gap,
and lengths past 1023. Also checked that interpolation is unchanged and
that all 36 valid and 55 invalid vector occurrences decode as before.
Link-format, README table, and whitespace checks pass.

Refs: bitcoin#2040, bitcoin#2258, bitcoin#2285
Add a Core Lightning "cl" secret from that project's test suite, whose
checksum differs from the same data with "ms"; a share set whose
11-character human-readable part requires the long checksum for a
73-character data part; and a secret with an 83-character human-readable
part containing "1".

Add invalid strings for the 94 and 95 expanded-length gap, a checksum
computed over an uppercase human-readable part, and an 84-character
human-readable part. Split the existing prefix examples by reason: two
of them are valid codex32 strings with the human-readable parts "m" and
"s", so they are invalid only as master seeds.

Checked every new vector with the specification's Python code and a
separate implementation, python-codex32. Both agree except that
python-codex32 does not yet limit human-readable parts to 83 characters.
Checked that each invalid group fails for its stated reason and that
the checks detect altered vectors. Link-format, README table, and
whitespace checks pass.

Refs: bitcoin#2040
Record the human-readable part generalization as version 0.3.0. It is a
backward-compatible extension, so BIP 3 calls for a minor version bump:
strings with the human-readable part "ms" are unchanged.

Preamble, README table, link-format, and whitespace checks pass.

Refs: bitcoin#2040

@BenWestgate BenWestgate left a comment •

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This draft is good. My comments about covering low bits vs whole HRP just requires a researched response and argument for the best choice.

However dont update this PR as that would be out of scope to change the checksum limits. Do so in a separate PR if you decide to do so.

Comment thread bip-0093.mediawiki
def ms32_create_regular_checksum(data):
values = data
polymod = ms32_polymod(values + [0] * 13) ^ MS32_CONST
def codex32_create_regular_checksum(hrp, data):

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

I think the primitives should just take combined expanded messages so we dont pass hrp except to the selector.

Comment thread bip-0093.mediawiki
The share generation and secret recovery procedures below are the same for both checksum variants.

The functions in this section represent each codex32 string as a list of integers obtained by converting the data-part characters to their values using the bech32 character table from BIP-0173.
This representation omits the human-readable part, which is the same for the input strings and the result.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This seems kind of arbitrary since we dont omit the threshold and identifier, perhaps we should?

Interpolating the fixed identical characters wastes time and can only cause problems producing invalid outputs especially in the expanded HRP. Although it may be useful for advanced error correction so im fine leaving it as is.

Comment thread bip-0093.mediawiki

In the case that the user wishes to generate a fresh secret, the user generates random initial shares, as follows:
In the case that the user wishes to generate a fresh secret, the user generates random initial shares, as follows.
This requires an application that accepts every payload of the chosen length as a secret, as the master seed format does.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

An application profile.

Comment thread bip-0093.mediawiki
In the case that the user wishes to generate a fresh secret, the user generates random initial shares, as follows:
In the case that the user wishes to generate a fresh secret, the user generates random initial shares, as follows.
This requires an application that accepts every payload of the chosen length as a secret, as the master seed format does.
An application that does not MAY use rejection sampling instead: retry with a fresh set of shares until the resulting secret qualifies.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Technically it can just retry the last initial random share. That gives better UX as they can write down and confirm each share before the next is generated, improving os csrng seeding.

Comment thread bip-0093.mediawiki
Longer strings mean more chances for transcription errors, so shorter strings are better.

If the prefix is damaged and a user is guessing that the data might be using this scheme, then the user can enter the available data explicitly using the suspected <code>MS1</code> prefix.
The checksum covers the expanded human-readable part, as in BIP-0173, and the expansion counts toward the checksum length limits.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

BIP-0173 only covers the HRP low bits, while this standard covers the HRP high bits.

Although now that HRP is limited to 83 characters I am amenable to only require covering the low bits like BIP-0173. Which gives a fixed maximum string length for each checksum and is backwards compatible now.

My pubkey key expression encoding crosses checksums if key origin info is HRP prepended, although I suppose that's unavoidable as there's no maximum length of that info. But usual derivation paths fit in 83-char.

Comment thread bip-0093.mediawiki
If the prefix is damaged and a user is guessing that the data might be using this scheme, then the user can enter the available data explicitly using the suspected <code>MS1</code> prefix.
The checksum covers the expanded human-readable part, as in BIP-0173, and the expansion counts toward the checksum length limits.
Beyond those limits, some errors in the human-readable part cannot be distinguished from errors elsewhere in the string, so counting the expansion keeps the error detection guarantees for the entire string.
It also lets implementations select the checksum variant from the string alone, without knowing the application.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

From the string shape alone.

Comment thread bip-0093.mediawiki
Beyond those limits, some errors in the human-readable part cannot be distinguished from errors elsewhere in the string, so counting the expansion keeps the error detection guarantees for the entire string.
It also lets implementations select the checksum variant from the string alone, without knowing the application.

If the human-readable part is damaged and a user is guessing that the data might be using this scheme, then the user can enter the available data explicitly using the suspected human-readable part.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Evaluate applications that accept multiple HRP in the same place whether this still leaves enough error detection guarantees to prevent crossing network or private/public boundaries.

If not we should suggest error correcting implementations use ?? for the damaged HRP so correction limits are tracked and not silently correcting user assumed errors.

To avoid making this complex we could write

a user is guessing that the data might be master seed data using this scheme, ... suspected MS1 prefix.

Comment thread bip-0093.mediawiki
* checksum: <code>jhsks4laxts8q</code>

The checksum covers the human-readable part.
With the human-readable part <code>ms</code>, the same header and payload form a 256-bit codex32-encoded master seed with a different checksum: <code>ms10peevst6cqh0wu7p5ssjyf4z4ez42ks9jlt3zneju9uuypr2hddak6tlqstxmpzl24l6e0d</code>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

I dont like this explain why we need it.

Comment thread bip-0093.mediawiki
* <code>ms10fauxsXXXXXXXXXXXXXXXXXXXXXXXXXXuqxkk05lyf3x2</code>
* <code>ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxUQXKK05LYF3X2</code>

These examples have a human-readable part other than "ms" and the invalid expanded codeword lengths 94 and 95. They use the regular and long checksums, respectively.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Do we need 4 vectors to exercise both invalid expanded codeword lengths with both checksums?

Comment thread bip-0093.mediawiki
Versions before 0.2.1 are assigned retrospectively to significant revisions.

* '''0.3.0''' (2026-09-21): [https://github.com/bitcoin/bips/pull/2040 #2040]
** Allow human-readable parts other than "ms"; the checksum covers them and counts their expansion toward its length limits.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Nit: This would be simpler if we just covered low bits, there would be no change to string length limits.

@BenWestgate BenWestgate self-assigned this Sep 24, 2026
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.

1 participant