From eb7bb6c097fe9fc340ffa7f8b7cf8344b38fa84f Mon Sep 17 00:00:00 2001 From: Ben Westgate Date: Tue, 25 Aug 2026 16:58:50 -0500 Subject: [PATCH 1/6] BIP93: Separate format and seed sections Group the regular and long checksum definitions in the codex32 format section. Move the master-seed application profile to the end of the specification and keep seed-specific checksum motivation in the rationale. This is a behavior-neutral organization change on top of the #2258 profile commit. --- bip-0093.mediawiki | 193 +++++++++++++++++++++++---------------------- 1 file changed, 97 insertions(+), 96 deletions(-) diff --git a/bip-0093.mediawiki b/bip-0093.mediawiki index cde2e95032..d29028b442 100644 --- a/bip-0093.mediawiki +++ b/bip-0093.mediawiki @@ -74,8 +74,8 @@ It reuses the base-32 character set from BIP-0173, and consists of: *** If the threshold parameter is "0" then the share index, defined below, MUST have a value of "s" (or "S"). ** An identifier consisting of 4 bech32 characters. ** A share index, which is any bech32 character. Note that a share index value of "s" (or "S") is special and denotes the unshared secret (see section "Unshared Secret"). -** A payload which is a sequence of up to 69 bech32 characters. (However, see '''Long codex32''' below for an exception to this limit.) -** A checksum which consists of 13 bech32 characters as described below. +** A payload which is a sequence of up to 69 bech32 characters. (However, see '''Long Checksum''' below for an exception to this limit.) +** A checksum which consists of 13 or 15 bech32 characters as described below. String validity may be further restricted by specific applications, see '''Master seed format''' below. @@ -127,7 +127,7 @@ def ms32_verify_regular_checksum(data): def ms32_verify_checksum(data): expanded_length = MS32_HRP_EXPANDED_LENGTH + len(data) - if expanded_length >= 96: # See Long codex32 + if expanded_length >= 96: # See Long Checksum return ms32_verify_long_checksum(data) return ms32_verify_regular_checksum(data) @@ -137,7 +137,7 @@ def ms32_create_regular_checksum(data): return [(polymod >> 5 * (12 - i)) & 31 for i in range(13)] def ms32_create_checksum(data): - if MS32_HRP_EXPANDED_LENGTH + len(data) + 13 > 93: # See Long codex32 + if MS32_HRP_EXPANDED_LENGTH + len(data) + 13 > 93: # See Long Checksum return ms32_create_long_checksum(data) return ms32_create_regular_checksum(data) @@ -146,6 +146,57 @@ guarantees detection of '''any error changing at most 8 symbols''' in expanded c and has less than a 3 in 1020 chance of failing to detect more random errors. +====Long Checksum==== + +The 13 character checksum design only supports expanded codewords of up to 93 values. +After accounting for the expanded ms human-readable part, header, and checksum, this limits the payload of a regular codex32 string to 69 characters. + + +MS32_LONG_CONST = 0x43381e570bf4798ab26 + +def ms32_long_polymod(values): + GEN = [ + 0x3d59d273535ea62d897, + 0x7a9becb6361c6c51507, + 0x543f9b7e6c38d8a2a0e, + 0x0c577eaeccf1990d13c, + 0x1887f74f8dc71b10651, + ] + residue = 0x23181b3 + for v in values: + b = (residue >> 70) + residue = (residue & 0x3fffffffffffffffff) << 5 ^ v + for i in range(5): + residue ^= GEN[i] if ((b >> i) & 1) else 0 + return residue + +def ms32_verify_long_checksum(data): + if MS32_HRP_EXPANDED_LENGTH + len(data) > 1023: + return False + return ms32_long_polymod(data) == MS32_LONG_CONST + +def ms32_create_long_checksum(data): + values = data + polymod = ms32_long_polymod(values + [0] * 15) ^ MS32_LONG_CONST + return [(polymod >> 5 * (14 - i)) & 31 for i in range(15)] + +This implements a [https://en.wikipedia.org/wiki/BCH_code BCH code] that +guarantees detection of '''any error changing at most 8 symbols''' in expanded codewords up to 1023 symbols long +and has less than a 3 in 1023 chance of failing to detect more +random errors. + +A codex32 string using the long checksum follows the same specification as one using the regular checksum, with the following changes. + +* The payload is a sequence of up to 997 bech32 characters. +* The checksum consists of 15 bech32 characters as defined above. +* The expanded codeword length MUST be between 96 and 1023 values, inclusive. + +A codex32 string with an expanded codeword length of 94 or 95 values is never legal. +Generation of long shares and recovery of the long secret from long shares proceeds in exactly the same way as for regular shares with the ms32_interpolate function. + +The long checksum is designed to be an error correcting code that can correct up to 4 character substitutions, up to 8 unreadable characters (called erasures), or up to 15 consecutive erasures. +As with the regular checksum we do not specify how an implementation should implement error correction, and all our recommendations for error correction also apply to the long checksum. + ====Error Correction==== A codex32 string without a valid checksum MUST NOT be used. @@ -200,50 +251,6 @@ def ms32_decode(codex): return data[:-13 if MS32_HRP_EXPANDED_LENGTH + len(data) < 94 else -15] -===Master seed format=== - -When the human-readable part of a valid codex32 secret (converted to lowercase) is the string "ms", we call it a codex32-encoded master seed or secret seed. The payload in this case is a direct encoding of a BIP-0032 HD master seed. - -A secret seed is a codex32 encoding of: - -* The human-readable part "ms" for master seed. -* The data-part values: -** A threshold parameter, which MUST be a single digit between "2" and "9", or the digit "0". -** An identifier consisting of 4 bech32 characters. -*** We do not define how to choose the identifier, beyond noting that it SHOULD be distinct for every master seed and master seed share set the user may need to disambiguate. -** The share index "s". -** A conversion of a 16-, 20-, 24-, 28-, 32-, or 64-byte BIP-0032 HD master seed to bech32: -*** Start with the bits of the master seed, most significant bit per byte first. -*** Re-arrange those bits into groups of 5, and pad with arbitrary bits at the end if needed. -*** Translate those bits to characters using the bech32 character table from BIP-0173. -** A valid checksum in accordance with the Checksum section. - -The payload is decoded to a master seed as follows: - -* Translate the characters to 5-bit values using the bech32 character table from BIP-0173, most significant bit first. -* Re-arrange those bits into groups of 8 bits. Any incomplete group at the end MUST be 4 bits or less, and is discarded. - -Unlike the decoding process in BIP-0173, master-seed decoding does not require that the discarded incomplete group contain only zero bits. -The decoded master seed MUST be exactly 16, 20, 24, 28, 32, or 64 bytes. - -The supported master seed sizes map to codex32 as follows: - -{| class="wikitable" -! Bits !! Bytes !! Payload characters !! Encoded length !! Checksum -|- -| 128 || 16 || 26 || 48 || Regular -|- -| 160 || 20 || 32 || 54 || Regular -|- -| 192 || 24 || 39 || 61 || Regular -|- -| 224 || 28 || 45 || 67 || Regular -|- -| 256 || 32 || 52 || 74 || Regular -|- -| 512 || 64 || 103 || 127 || Long -|} - ===Recovering Secret=== When the share index of a valid codex32 string (converted to lowercase) is not the letter "s", we call the string a codex32 share. @@ -320,7 +327,7 @@ In the case that the user wishes to generate a fresh secret, the user generates #* We do not define how to choose the identifier, beyond noting that it SHOULD be distinct for every secret the user may need to disambiguate # ''k'' many times, generate a random share by: ## Take the next available letter from the bech32 alphabet, in alphabetical order, as a, c, d, ..., to be the share index -## Set the first nine characters to be the prefix ms1, the threshold value ''t'', the 4-character identifier, and then the share index +## Set the first nine characters to be the prefix ms1, the threshold value ''k'', the 4-character identifier, and then the share index ## Choose the next ceil(''bitlength / 5'') characters uniformly at random ## Generate a valid checksum in accordance with the Checksum section, and append this to the resulting shares @@ -345,58 +352,49 @@ These shares should be generated as described in the "fresh secret" section. The codex32 secret and the ''k''-1 codex32 shares form a set of ''k'' valid initial codex32 strings from which additional shares can be derived as described above. -===Long codex32=== - -The 13 character checksum design only supports expanded codewords of up to 93 values. -After accounting for the expanded ms human-readable part, header, and checksum, this limits the payload of a regular codex32 string to 69 characters. -While this is enough to support the 32-byte advised size of BIP-0032 master seeds, BIP-0032 allows seeds to be up to 64 bytes in size. -We define a long codex32 format to support these longer seeds by defining an alternative checksum. +===Master seed format=== - -MS32_LONG_CONST = 0x43381e570bf4798ab26 +When the human-readable part of a valid codex32 secret (converted to lowercase) is the string "ms", we call it a codex32-encoded master seed or secret seed. The payload in this case is a direct encoding of a BIP-0032 HD master seed. -def ms32_long_polymod(values): - GEN = [ - 0x3d59d273535ea62d897, - 0x7a9becb6361c6c51507, - 0x543f9b7e6c38d8a2a0e, - 0x0c577eaeccf1990d13c, - 0x1887f74f8dc71b10651, - ] - residue = 0x23181b3 - for v in values: - b = (residue >> 70) - residue = (residue & 0x3fffffffffffffffff) << 5 ^ v - for i in range(5): - residue ^= GEN[i] if ((b >> i) & 1) else 0 - return residue +A secret seed is a codex32 encoding of: -def ms32_verify_long_checksum(data): - if MS32_HRP_EXPANDED_LENGTH + len(data) > 1023: - return False - return ms32_long_polymod(data) == MS32_LONG_CONST +* The human-readable part "ms" for master seed. +* The data-part values: +** A threshold parameter, which MUST be a single digit between "2" and "9", or the digit "0". +** An identifier consisting of 4 bech32 characters. +*** We do not define how to choose the identifier, beyond noting that it SHOULD be distinct for every master seed and master seed share set the user may need to disambiguate. +** The share index "s". +** A conversion of a 16-, 20-, 24-, 28-, 32-, or 64-byte BIP-0032 HD master seed to bech32: +*** Start with the bits of the master seed, most significant bit per byte first. +*** Re-arrange those bits into groups of 5, and pad with arbitrary bits at the end if needed. +*** Translate those bits to characters using the bech32 character table from BIP-0173. +** A valid checksum in accordance with the Checksum section. -def ms32_create_long_checksum(data): - values = data - polymod = ms32_long_polymod(values + [0] * 15) ^ MS32_LONG_CONST - return [(polymod >> 5 * (14 - i)) & 31 for i in range(15)] - -This implements a [https://en.wikipedia.org/wiki/BCH_code BCH code] that -guarantees detection of '''any error changing at most 8 symbols''' in expanded codewords up to 1023 symbols long -and has less than a 3 in 1023 chance of failing to detect more -random errors. +The payload is decoded to a master seed as follows: -A long codex32 string follows the same specification as a regular codex32 string with the following changes. +* Translate the characters to 5-bit values using the bech32 character table from BIP-0173, most significant bit first. +* Re-arrange those bits into groups of 8 bits. Any incomplete group at the end MUST be 4 bits or less, and is discarded. -* The payload is a sequence of up to 997 bech32 characters. -* The checksum consists of 15 bech32 characters as defined above. -* The expanded codeword length MUST be between 96 and 1023 values, inclusive. +Unlike the decoding process in BIP-0173, master-seed decoding does not require that the discarded incomplete group contain only zero bits. +The decoded master seed MUST be exactly 16, 20, 24, 28, 32, or 64 bytes. -A codex32 string with an expanded codeword length of 94 or 95 values is never legal. -Generation of long shares and recovery of the long secret from long shares proceeds in exactly the same way as for regular shares with the ms32_interpolate function. +The supported master seed sizes map to codex32 as follows: -The long checksum is designed to be an error correcting code that can correct up to 4 character substitutions, up to 8 unreadable characters (called erasures), or up to 15 consecutive erasures. -As with regular checksums we do not specify how an implementation should implement error correction, and all our recommendations for error correction of regular codex32 strings also apply to long codex32 strings. +{| class="wikitable" +! Bits !! Bytes !! Payload characters !! Encoded length !! Checksum +|- +| 128 || 16 || 26 || 48 || Regular +|- +| 160 || 20 || 32 || 54 || Regular +|- +| 192 || 24 || 39 || 61 || Regular +|- +| 224 || 28 || 45 || 67 || Regular +|- +| 256 || 32 || 52 || 74 || Regular +|- +| 512 || 64 || 103 || 127 || Long +|} ==Rationale== @@ -412,13 +410,16 @@ This is a standard size for many common seed storage formats, which has been pop The 13 character checksum is adequate to correct 4 errors in expanded codewords of up to 93 values. We can correct up to 8 erasures (errors with known locations), and up to 13 consecutive errors (burst errors). Beyond that, our code is guaranteed to detect up to 8 errors. -More generally, any number of random errors will be detected with overwhelming (1 - 2^65) probability. However, the checksum does not protect against maliciously constructed errors. +More generally, any number of random errors will be detected with overwhelming (1 - 2-65) probability. However, the checksum does not protect against maliciously constructed errors. These parameters are slightly better than those of the checksum used in SLIP-0039. For 256-bit seeds and shares our strings are 74 characters, which fits into the 96 character format of the 24 four-letter word format of the BIP-0039 mnemonic, with plenty of room to spare. The supported 128-, 160-, 192-, 224-, and 256-bit sizes are the entropy sizes defined by BIP-0039, while 512 bits is the BIP-0032 seed size produced by BIP-0039 recovery. -These encoded lengths have at least six-character gaps, reducing target length ambiguity for optional insert/delection correction workflows. +These encoded lengths have at least six-character gaps, reducing target length ambiguity for optional insertion/deletion correction workflows. + +While the regular checksum is enough to support the 32-byte advised size of BIP-0032 master seeds, BIP-0032 allows seeds to be up to 64 bytes in size. +We define a long checksum to support these longer seeds. A longer checksum is needed to support up to 512-bit seeds, the longest seed length specified in BIP-0032, because their expanded codewords exceed the regular checksum's 93-symbol limit. While we could use the 15 character checksum for both cases, we prefer to keep the strings as short as possible for the more common cases of 128-bit and 256-bit master seeds. @@ -583,7 +584,7 @@ Note that the choice to append four zero bits was arbitrary, and any of the foll This example shows generating a new 512-bit master seed using "random" bech32 characters and appending a checksum. The payload contains 103 bech32 characters, which corresponds to 515 bits. The last three bits are discarded when converting to a 512-bit master seed. -This is an example of a '''Long codex32''' string. +This example uses the '''long checksum'''. k value (bech32): 0 From 9621d3fa066b7c8b2f5c3adefd1aa4c0497c25e3 Mon Sep 17 00:00:00 2001 From: Ben Westgate Date: Sun, 13 Sep 2026 16:55:49 -0500 Subject: [PATCH 2/6] bip93: Deduplicate specification Keep the common format rules and checksum properties in one place, and replace the broad application-validity sentence with a concrete reference to the master seed requirements. Remove the redundant symbol encoder and decoder examples. Preserve their validation rules in the format and master seed text, including the encoded-length restriction for both secrets and shares. Retained checksum and interpolation code is unchanged. Checked retained Python ASTs against eb7bb6c, all 36 valid and 55 invalid vector occurrences, all six size mappings, legacy sizes, 1024 header combinations, checksum boundaries, share recovery, and nonzero padding. Link-format, README table, and whitespace checks pass. Refs: #2258, #2285 --- bip-0093.mediawiki | 79 ++++++++++------------------------------------ 1 file changed, 16 insertions(+), 63 deletions(-) diff --git a/bip-0093.mediawiki b/bip-0093.mediawiki index d29028b442..180d0297d1 100644 --- a/bip-0093.mediawiki +++ b/bip-0093.mediawiki @@ -77,7 +77,7 @@ It reuses the base-32 character set from BIP-0173, and consists of: ** A payload which is a sequence of up to 69 bech32 characters. (However, see '''Long Checksum''' below for an exception to this limit.) ** A checksum which consists of 13 or 15 bech32 characters as described below. -String validity may be further restricted by specific applications, see '''Master seed format''' below. +See [[#Master_seed_format|Master seed format]] for the payload-length and payload-conversion requirements for BIP-0032 master seeds. As with bech32 strings, a codex32 string MUST be entirely uppercase or entirely lowercase. Note that per BIP-0173, the lowercase form is used when determining a character's value for checksum purposes. @@ -148,9 +148,6 @@ random errors. ====Long Checksum==== -The 13 character checksum design only supports expanded codewords of up to 93 values. -After accounting for the expanded ms human-readable part, header, and checksum, this limits the payload of a regular codex32 string to 69 characters. - MS32_LONG_CONST = 0x43381e570bf4798ab26 @@ -189,18 +186,15 @@ A codex32 string using the long checksum follows the same specification as one u * The payload is a sequence of up to 997 bech32 characters. * The checksum consists of 15 bech32 characters as defined above. -* The expanded codeword length MUST be between 96 and 1023 values, inclusive. -A codex32 string with an expanded codeword length of 94 or 95 values is never legal. Generation of long shares and recovery of the long secret from long shares proceeds in exactly the same way as for regular shares with the ms32_interpolate function. -The long checksum is designed to be an error correcting code that can correct up to 4 character substitutions, up to 8 unreadable characters (called erasures), or up to 15 consecutive erasures. -As with the regular checksum we do not specify how an implementation should implement error correction, and all our recommendations for error correction also apply to the long checksum. - ====Error Correction==== A codex32 string without a valid checksum MUST NOT be used. -The checksum is designed to be an error correcting code that can correct up to 4 character substitutions, up to 8 unreadable characters (called erasures), or up to 13 consecutive erasures. +Both checksums are designed to correct up to 4 character substitutions or up to 8 unreadable characters (called erasures). +The regular checksum can also correct up to 13 consecutive erasures, and the long checksum up to 15 consecutive erasures. +The following recommendations apply to both checksums. Implementations SHOULD provide the user with a corrected valid codex32 string if possible. However, implementations SHOULD NOT automatically proceed with a corrected codex32 string without user confirmation of the corrected string, either by prompting the user, or returning a corrected string in an error message and allowing the user to repeat their action. We do not specify how an implementation should implement error correction. However, we recommend that: @@ -221,36 +215,6 @@ For an unshared secret, the threshold parameter (the first character of the data We recommend using the digit "0" for the threshold parameter in this case. The 4 character identifier also has no effect beyond aiding users in distinguishing between multiple different secrets in cases where they have more than one. -The function ms32_encode constructs a codex32 string with the required ms human-readable part when its argument is the converted data-part characters (excluding the checksum). - -To validate an ms master-seed share or secret and determine the data-part (excluding the checksum) as a list of 5-bit values, the ms32_decode function can be used. - - -CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" -MS32_VALID_LENGTHS = (48, 54, 61, 67, 74, 127) - -def ms32_encode(data): - combined = data + ms32_create_checksum(data) - return "ms" + "1" + ''.join([CHARSET[d] for d in combined]) - -def ms32_decode(codex): - if ((any(ord(x) < 33 or ord(x) > 126 for x in codex)) or - (codex.lower() != codex and codex.upper() != codex)): - return None - codex = codex.lower() - pos = codex.rfind("1") - if pos < 2 or len(codex) not in MS32_VALID_LENGTHS: - return None - if not all(x in CHARSET for x in codex[pos+1:]): - return None - if codex[:pos] != "ms" or codex[pos+1].isalpha() or codex[pos+1] == "0" and codex[pos+6] != "s": - return None - data = [CHARSET.index(x) for x in codex[pos+1:]] - if not ms32_verify_checksum(data): - return None - return data[:-13 if MS32_HRP_EXPANDED_LENGTH + len(data) < 94 else -15] - - ===Recovering Secret=== When the share index of a valid codex32 string (converted to lowercase) is not the letter "s", we call the string a codex32 share. @@ -356,19 +320,14 @@ The codex32 secret and the ''k''-1 codex32 shares form a set of ''k'' valid init When the human-readable part of a valid codex32 secret (converted to lowercase) is the string "ms", we call it a codex32-encoded master seed or secret seed. The payload in this case is a direct encoding of a BIP-0032 HD master seed. -A secret seed is a codex32 encoding of: +The header and checksum follow the [[#codex32|codex32 format]] requirements above, with share index "s". +We do not define how to choose the identifier, beyond noting that it SHOULD be distinct for every master seed and master seed share set the user may need to disambiguate. -* The human-readable part "ms" for master seed. -* The data-part values: -** A threshold parameter, which MUST be a single digit between "2" and "9", or the digit "0". -** An identifier consisting of 4 bech32 characters. -*** We do not define how to choose the identifier, beyond noting that it SHOULD be distinct for every master seed and master seed share set the user may need to disambiguate. -** The share index "s". -** A conversion of a 16-, 20-, 24-, 28-, 32-, or 64-byte BIP-0032 HD master seed to bech32: -*** Start with the bits of the master seed, most significant bit per byte first. -*** Re-arrange those bits into groups of 5, and pad with arbitrary bits at the end if needed. -*** Translate those bits to characters using the bech32 character table from BIP-0173. -** A valid checksum in accordance with the Checksum section. +A 16-, 20-, 24-, 28-, 32-, or 64-byte BIP-0032 HD master seed is converted to the payload as follows: + +* Start with the bits of the master seed, most significant bit per byte first. +* Re-arrange those bits into groups of 5, and pad with arbitrary bits at the end if needed. +* Translate those bits to characters using the bech32 character table from BIP-0173. The payload is decoded to a master seed as follows: @@ -378,6 +337,7 @@ The payload is decoded to a master seed as follows: Unlike the decoding process in BIP-0173, master-seed decoding does not require that the discarded incomplete group contain only zero bits. The decoded master seed MUST be exactly 16, 20, 24, 28, 32, or 64 bytes. +A codex32-encoded master seed or a share of one MUST have an encoded length listed in the table below. The supported master seed sizes map to codex32 as follows: {| class="wikitable" @@ -407,11 +367,8 @@ This fact allows the header data to be covered by the checksum. The checksum size and identifier size have been chosen so that the encoding of 128-bit master seeds and shares fit within 48 characters. This is a standard size for many common seed storage formats, which has been popularized by the 12 four-letter word format of the BIP-0039 mnemonic. -The 13 character checksum is adequate to correct 4 errors in expanded codewords of up to 93 values. -We can correct up to 8 erasures (errors with known locations), and up to 13 consecutive errors (burst errors). -Beyond that, our code is guaranteed to detect up to 8 errors. -More generally, any number of random errors will be detected with overwhelming (1 - 2-65) probability. However, the checksum does not protect against maliciously constructed errors. -These parameters are slightly better than those of the checksum used in SLIP-0039. +The error detection and correction properties described above are slightly better than those of the checksum used in SLIP-0039. +However, the checksum does not protect against maliciously constructed errors. For 256-bit seeds and shares our strings are 74 characters, which fits into the 96 character format of the 24 four-letter word format of the BIP-0039 mnemonic, with plenty of room to spare. @@ -419,14 +376,10 @@ The supported 128-, 160-, 192-, 224-, and 256-bit sizes are the entropy sizes de These encoded lengths have at least six-character gaps, reducing target length ambiguity for optional insertion/deletion correction workflows. While the regular checksum is enough to support the 32-byte advised size of BIP-0032 master seeds, BIP-0032 allows seeds to be up to 64 bytes in size. -We define a long checksum to support these longer seeds. - -A longer checksum is needed to support up to 512-bit seeds, the longest seed length specified in BIP-0032, because their expanded codewords exceed the regular checksum's 93-symbol limit. +We define a long checksum to support the maximum seed size because its expanded codewords exceed the regular checksum's limit. While we could use the 15 character checksum for both cases, we prefer to keep the strings as short as possible for the more common cases of 128-bit and 256-bit master seeds. -We only guarantee to correct 4 characters no matter how long the string is. Longer strings mean more chances for transcription errors, so shorter strings are better. -Checksum selection includes the expanded ms human-readable part, so every regular codex32 codeword remains within the 93-value checksum period. 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 MS1 prefix. @@ -484,7 +437,7 @@ Instead, users who wish to switch to codex32 should generate a fresh seed and sw ==Reference Implementation== -The inline code in this BIP text can be used as a Python reference. +The inline code in this BIP text provides Python reference implementations of the checksum and interpolation primitives. A complete Python implementation is available in the [https://github.com/BenWestgate/python-codex32 python-codex32 repository]. The [https://github.com/BlockstreamResearch/codex32 original project repository] contains implementations in Rust and PostScript. From 1589133608ca2ddd56d72f8bec40041d2d6b4550 Mon Sep 17 00:00:00 2001 From: Ben Westgate Date: Sun, 13 Sep 2026 17:01:48 -0500 Subject: [PATCH 3/6] bip93: Reorganize specification Let readers implement unshared master seeds from the format and master seed sections without reading the secret sharing procedures. Keep the common header and unshared-secret rules under codex32, and group share generation and recovery under SSSS-awareness. Give checksum and error correction one TOC entry each. Use bold labels for the individual checksums and generation cases, and preserve both MediaWiki anchors and GitHub permalinks for demoted headings. Put the interpolation helpers with generation and its recovery wrapper afterward. Retained executable code is unchanged. Checked the retained Python ASTs, existing valid/invalid vectors, size mappings, header combinations, checksum boundaries, recovery, and padding. Inspected the rendered TOC and table and checked fragment targets. Link-format, README table, and whitespace checks pass. Refs: #2285 --- bip-0093.mediawiki | 120 ++++++++++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 50 deletions(-) diff --git a/bip-0093.mediawiki b/bip-0093.mediawiki index 180d0297d1..e46bcad586 100644 --- a/bip-0093.mediawiki +++ b/bip-0093.mediawiki @@ -60,7 +60,7 @@ However, BIP-0039 has no error-correcting ability, cannot sensibly be extended t We first describe the general checksummed base32'''Why use base32 at all?''' The lack of mixed case makes it more efficient to read out loud, write, type or to put into QR codes. format called -''codex32'' and then define a BIP-0032 master seed encoding using it. +''codex32'' and then define a secret sharing scheme and BIP-0032 master seed encoding using it. ===codex32=== @@ -70,11 +70,12 @@ It reuses the base-32 character set from BIP-0173, and consists of: * A human-readable part, which is the string "ms" (or "MS"). * A separator, which is always "1". * A data part which is in turn subdivided into: -** A threshold parameter, which MUST be a single digit between "2" and "9", or the digit "0". -*** If the threshold parameter is "0" then the share index, defined below, MUST have a value of "s" (or "S"). -** An identifier consisting of 4 bech32 characters. -** A share index, which is any bech32 character. Note that a share index value of "s" (or "S") is special and denotes the unshared secret (see section "Unshared Secret"). -** A payload which is a sequence of up to 69 bech32 characters. (However, see '''Long Checksum''' below for an exception to this limit.) +** A header consisting of the first six data characters: +*** A threshold parameter, which MUST be a single digit between "2" and "9", or the digit "0". +**** If the threshold parameter is "0" then the share index, defined below, MUST have a value of "s" (or "S"). +*** An identifier consisting of 4 bech32 characters. +*** A share index, which is any bech32 character. Note that a share index value of "s" (or "S") is special and denotes the unshared secret (see [[#Unshared_Secret|Unshared secret]]). +** A payload which is a sequence of up to 69 bech32 characters. (However, see [[#Long_Checksum|Long checksum]] below for an exception to this limit.) ** A checksum which consists of 13 or 15 bech32 characters as described below. See [[#Master_seed_format|Master seed format]] for the payload-length and payload-conversion requirements for BIP-0032 master seeds. @@ -85,6 +86,17 @@ In particular, given an all uppercase codex32 string, we still use lowercase +'''Unshared secret''' + +When the share index of a valid codex32 string (converted to lowercase) is the letter "s", we call the string a codex32 secret. + +The secret's payload is decoded by application-specific rules. + +For an unshared secret, the threshold parameter (the first character of the data part) is ignored (beyond the fact it must be a digit for the codex32 string to be valid). +We recommend using the digit "0" for the threshold parameter in this case. +The 4 character identifier also has no effect beyond aiding users in distinguishing between multiple different secrets in cases where they have more than one. + ====Checksum==== The last thirteen characters of a regular codex32 data part form a checksum and contain no information. @@ -100,6 +112,8 @@ Expanded lengths 94 and 95, and lengths greater than 1023, are invalid. The functions ms32_create_regular_checksum and ms32_create_long_checksum construct the individual checksum variants. To construct the checksum variant required by codex32 given the data-part characters (excluding the checksum), the ms32_create_checksum function can be used. +'''Regular checksum''' + MS32_CONST = 0x10ce0795c2fd1e62a MS32_HRP_EXPANDED_LENGTH = 5 # bech32_hrp_expand("ms") @@ -127,7 +141,7 @@ def ms32_verify_regular_checksum(data): def ms32_verify_checksum(data): expanded_length = MS32_HRP_EXPANDED_LENGTH + len(data) - if expanded_length >= 96: # See Long Checksum + if expanded_length >= 96: # See Long checksum return ms32_verify_long_checksum(data) return ms32_verify_regular_checksum(data) @@ -137,7 +151,7 @@ def ms32_create_regular_checksum(data): return [(polymod >> 5 * (12 - i)) & 31 for i in range(13)] def ms32_create_checksum(data): - if MS32_HRP_EXPANDED_LENGTH + len(data) + 13 > 93: # See Long Checksum + if MS32_HRP_EXPANDED_LENGTH + len(data) + 13 > 93: # See Long checksum return ms32_create_long_checksum(data) return ms32_create_regular_checksum(data) @@ -146,7 +160,8 @@ guarantees detection of '''any error changing at most 8 symbols''' in expanded c and has less than a 3 in 1020 chance of failing to detect more random errors. -====Long Checksum==== + +'''Long checksum''' MS32_LONG_CONST = 0x43381e570bf4798ab26 @@ -187,9 +202,8 @@ A codex32 string using the long checksum follows the same specification as one u * The payload is a sequence of up to 997 bech32 characters. * The checksum consists of 15 bech32 characters as defined above. -Generation of long shares and recovery of the long secret from long shares proceeds in exactly the same way as for regular shares with the ms32_interpolate function. - -====Error Correction==== + +====Error correction==== A codex32 string without a valid checksum MUST NOT be used. Both checksums are designed to correct up to 4 character substitutions or up to 8 unreadable characters (called erasures). @@ -205,28 +219,25 @@ We do not specify how an implementation should implement error correction. Howev * If a string with 8 or fewer erasures can have those erasures filled in to make a valid codex32 string, then the implementation suggests such a string as a correction. * If a string consisting of valid bech32 characters in the proper case can be made valid by substituting 4 or fewer characters, then the implementation suggests such a string as a correction. -===Unshared Secret=== - -When the share index of a valid codex32 string (converted to lowercase) is the letter "s", we call the string a codex32 secret. +===SSSS-awareness=== -The secret's payload is decoded by application-specific rules. +When the share index of a valid codex32 string (converted to lowercase) is not the letter "s", we call the string a codex32 share. +The first character of the data part indicates the threshold of the share, and it is required to be a non-"0" digit. +The corresponding secret has index "s", as described in [[#Unshared_Secret|Unshared secret]]. -For an unshared secret, the threshold parameter (the first character of the data part) is ignored (beyond the fact it must be a digit for the codex32 string to be valid). -We recommend using the digit "0" for the threshold parameter in this case. -The 4 character identifier also has no effect beyond aiding users in distinguishing between multiple different secrets in cases where they have more than one. +Generation of long shares and recovery of the long secret from long shares proceeds in exactly the same way as for regular shares with the ms32_interpolate function. -===Recovering Secret=== + +====Generating shares==== -When the share index of a valid codex32 string (converted to lowercase) is not the letter "s", we call the string a codex32 share. -The first character of the data part indicates the threshold of the share, and it is required to be a non-"0" digit. +If we already have ''k'' valid codex32 strings such that: -In order to recover a secret, one needs a set of valid shares such that: +* All strings have the same threshold value ''k'', the same identifier, and the same length +* All of the share index values are distinct -* All shares have the same threshold value, the same identifier, and the same length. -* All of the share index values are distinct. -* The number of shares is exactly equal to the (common) threshold value. +Then we can derive additional shares with the ms32_interpolate function by passing it a list of exactly ''k'' of these codex32 strings, together with a fresh share index distinct from all of the existing share indexes. +The newly derived share will have the provided share index. -If all the above conditions are satisfied, the ms32_recover function will return a codex32 secret when its argument is the list of codex32 shares with each share represented as a list of integers representing the characters converted using the bech32 character table from BIP-0173. BECH32_INV = [ 0, 1, 20, 24, 10, 8, 12, 29, 5, 11, 4, 9, 6, 28, 26, 31, @@ -261,27 +272,15 @@ def ms32_interpolate(l, x): n ^= bech32_mul(w[j], l[j][i]) res.append(n) return res - -def ms32_recover(shares): - return ms32_interpolate(shares, 16) -===Generating Shares=== - -If we already have ''k'' valid codex32 strings such that: - -* All strings have the same threshold value ''k'', the same identifier, and the same length -* All of the share index values are distinct - -Then we can derive additional shares with the ms32_interpolate function by passing it a list of exactly ''k'' of these codex32 strings, together with a fresh share index distinct from all of the existing share indexes. -The newly derived share will have the provided share index. - Once a user has generated ''n'' shares, they may discard the codex32 secret (if it exists). The ''n'' shares form a ''k'' of ''n'' Shamir's secret sharing scheme of a codex32 secret. There are two ways to create an initial set of ''k'' valid codex32 strings, depending on whether the user already has an existing secret to split. -====For a fresh secret==== + +'''For a fresh secret''' In the case that the user wishes to generate a fresh secret, the user generates random initial shares, as follows: @@ -299,7 +298,8 @@ The result will be ''k'' distinct shares, all with the same initial 8 characters With this set of ''k'' shares, new shares can be derived as discussed above. This process generates a fresh secret, whose value can be retrieved by running the recovery process on any ''k'' of these shares. -====For an existing secret==== + +'''For an existing secret''' Before generating shares for an existing secret, it first must be codex32-encoded. The conversion process consists of: @@ -308,14 +308,29 @@ The conversion process consists of: # Choose a 4 bech32 character identifier #* We do not define how to choose the identifier, beyond noting that it SHOULD be distinct for every set of shares the user may need to disambiguate # Set the share index to s -# Set the payload to a bech32 encoding of the application-specified secret payload bits; for a master seed, follow "Master seed format". +# Set the payload to a bech32 encoding of the application-specified secret payload bits; for a master seed, follow [[#Master_seed_format|Master seed format]]. # Generate a valid checksum in accordance with the Checksum section Along with the codex32 secret, the user must generate ''k''-1 other codex32 shares, each with the same threshold value, the same identifier, and a distinct share index. -These shares should be generated as described in the "fresh secret" section. +These shares should be generated as described in [[#For_a_fresh_secret|For a fresh secret]]. The codex32 secret and the ''k''-1 codex32 shares form a set of ''k'' valid initial codex32 strings from which additional shares can be derived as described above. + +====Recovering secret==== + +In order to recover a secret, one needs a set of valid shares such that: + +* All shares have the same threshold value, the same identifier, and the same length. +* All of the share index values are distinct. +* The number of shares is exactly equal to the (common) threshold value. + +If all the above conditions are satisfied, the ms32_recover function will return a codex32 secret when its argument is the list of codex32 shares with each share represented as a list of integers representing the characters converted using the bech32 character table from BIP-0173. + +def ms32_recover(shares): + return ms32_interpolate(shares, 16) + + ===Master seed format=== When the human-readable part of a valid codex32 secret (converted to lowercase) is the string "ms", we call it a codex32-encoded master seed or secret seed. The payload in this case is a direct encoding of a BIP-0032 HD master seed. @@ -384,7 +399,8 @@ If the prefix is damaged and a user is guessing that the data might be using thi -===Not BIP-0039 Entropy=== + +===Not BIP-0039 entropy=== Instead of encoding a BIP-0032 master seed, an alternative would be to encode BIP-0039 entropy. However this alternative approach is fraught with difficulties. @@ -416,9 +432,10 @@ The main advantage of this alternative approach would be that wallets could give In practice, we do not expect users in switch back and forth between backup formats, and instead just generate a fresh master seed using Codex32. Seeing little value with BIP-0039 compatibility (English-only), all the difficulties with BIP-0039 language choice, not to mention the PBKDF2 overhead of using BIP-0039, we think it is best to abandon BIP-0039 and encode BIP-0032 master seeds directly. -Our approach is semi-convertible with BIP-0039's 512-bit master seeds (in all languages, see Backwards Compatibility) and interconvertible with SLIP-0039 master seeds or any other encoding of BIP-0032 master seeds with a supported length. +Our approach is semi-convertible with BIP-0039's 512-bit master seeds (in all languages, see [[#Backwards_Compatibility|Backwards compatibility]]) and interconvertible with SLIP-0039 master seeds or any other encoding of BIP-0032 master seeds with a supported length. -==Backwards Compatibility== + +==Backwards compatibility== Earlier revisions accepted every master seed length from 16 through 64 bytes using regular checksum up to 93 data characters and long checksum from 96 data characters. This revision retains 16-, 20-, 24-, 28-, 32-, and 64-byte master seeds. @@ -435,13 +452,15 @@ However, SLIP-0039 '''shares''' cannot be converted to codex32 shares because th The authors of this BIP do not recommend interconversion. Instead, users who wish to switch to codex32 should generate a fresh seed and sweep their coins. -==Reference Implementation== + +==Reference implementation== The inline code in this BIP text provides Python reference implementations of the checksum and interpolation primitives. A complete Python implementation is available in the [https://github.com/BenWestgate/python-codex32 python-codex32 repository]. The [https://github.com/BlockstreamResearch/codex32 original project repository] contains implementations in Rust and PostScript. -==Test Vectors== + +==Test vectors== ===Test vector 1=== @@ -644,7 +663,8 @@ These examples all incorrectly mix upper and lower case characters. ==Appendix== -===Mathematical Companion=== + +===Mathematical companion=== Below we use the bech32 character set to denote values in GF[32]. In bech32, the letter Q denotes zero and the letter P denotes one. From 12a61ee567a772d4508875303b1709564b6ca8a5 Mon Sep 17 00:00:00 2001 From: Ben Westgate Date: Sun, 13 Sep 2026 19:59:47 -0500 Subject: [PATCH 4/6] bip93: Clarify seed-size units Describe seed sizes in bits in the encoding instructions, checksum rationale, and retained-size list, matching generation and the vectors. Keep bytes for decoded output and the historical contiguous byte-size range, and retain both units in the size table. The supported sizes and all numeric constraints are unchanged. The existing rationale already explains that BIP39 produces 512-bit seeds. Checked retained Python ASTs, all existing vector occurrences, size mappings, header combinations, checksum boundaries, recovery, padding, and rendered markup. Link-format, README table, and whitespace checks pass. Refs: #2258, #2285 --- bip-0093.mediawiki | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bip-0093.mediawiki b/bip-0093.mediawiki index e46bcad586..b505bb5e08 100644 --- a/bip-0093.mediawiki +++ b/bip-0093.mediawiki @@ -338,7 +338,7 @@ When the human-readable part of a valid codex32 secret (converted to lowercase) The header and checksum follow the [[#codex32|codex32 format]] requirements above, with share index "s". We do not define how to choose the identifier, beyond noting that it SHOULD be distinct for every master seed and master seed share set the user may need to disambiguate. -A 16-, 20-, 24-, 28-, 32-, or 64-byte BIP-0032 HD master seed is converted to the payload as follows: +A 128-, 160-, 192-, 224-, 256-, or 512-bit BIP-0032 HD master seed is converted to the payload as follows: * Start with the bits of the master seed, most significant bit per byte first. * Re-arrange those bits into groups of 5, and pad with arbitrary bits at the end if needed. @@ -390,7 +390,7 @@ For 256-bit seeds and shares our strings are 74 characters, which fits into the The supported 128-, 160-, 192-, 224-, and 256-bit sizes are the entropy sizes defined by BIP-0039, while 512 bits is the BIP-0032 seed size produced by BIP-0039 recovery. These encoded lengths have at least six-character gaps, reducing target length ambiguity for optional insertion/deletion correction workflows. -While the regular checksum is enough to support the 32-byte advised size of BIP-0032 master seeds, BIP-0032 allows seeds to be up to 64 bytes in size. +While the regular checksum is enough to support the 256-bit advised size of BIP-0032 master seeds, BIP-0032 allows seeds to be up to 512 bits in size. We define a long checksum to support the maximum seed size because its expanded codewords exceed the regular checksum's limit. While we could use the 15 character checksum for both cases, we prefer to keep the strings as short as possible for the more common cases of 128-bit and 256-bit master seeds. Longer strings mean more chances for transcription errors, so shorter strings are better. @@ -438,7 +438,7 @@ Our approach is semi-convertible with BIP-0039's 512-bit master seeds (in all la ==Backwards compatibility== Earlier revisions accepted every master seed length from 16 through 64 bytes using regular checksum up to 93 data characters and long checksum from 96 data characters. -This revision retains 16-, 20-, 24-, 28-, 32-, and 64-byte master seeds. +This revision retains 128-, 160-, 192-, 224-, 256-, and 512-bit master seeds. Encodings at retained sizes are unchanged; all other formerly valid 16-to-64-byte strings are now invalid. codex32 is an alternative to BIP-0039 and SLIP-0039. From 54e0233ad7084c8517b588427a2149e90a1f944f Mon Sep 17 00:00:00 2001 From: Ben Westgate Date: Sun, 13 Sep 2026 20:02:17 -0500 Subject: [PATCH 5/6] bip93: Add revision history Add a reverse-chronological draft changelog and matching Version header so readers can distinguish the earlier checksum-boundary and seed-size changes from this behavior-neutral reorganization. Assign retrospective versions to significant revisions and use their upstream integration dates, rather than individual patch author dates. Keep the Draft status and BSD-3-Clause license unchanged. Checked the historical entries against first-parent upstream history, the version and date ordering, and the metadata-only diff. Python ASTs, existing vectors, size and checksum boundaries, header combinations, recovery, padding, rendered markup, link formatting, README table, and whitespace checks pass. Refs: #2258, #2285 --- bip-0093.mediawiki | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/bip-0093.mediawiki b/bip-0093.mediawiki index b505bb5e08..c728491c07 100644 --- a/bip-0093.mediawiki +++ b/bip-0093.mediawiki @@ -9,6 +9,7 @@ Assigned: 2023-02-13 License: BSD-3-Clause Discussion: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-February/021469.html + Version: 0.2.1 ==Introduction== @@ -661,6 +662,26 @@ These examples all incorrectly mix upper and lower case characters. * ms10fauxsXXXXXXXXXXXXXXXXXXXXXXXXXXuqxkk05lyf3x2 * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxUQXKK05LYF3X2 +==Changelog== + +Versions before 0.2.1 are assigned retrospectively to significant revisions. + +* '''0.2.1''' (2026-09-13): [https://github.com/bitcoin/bips/pull/2285 #2285] +** Separate the codex32 format, optional secret sharing, and master seed encoding into distinct sections. +** Remove duplicate explanations and inline encoding and decoding helpers, reorganize headings, and clarify seed-size units without changing behavior. +* '''0.2.0''' (2026-09-11): [https://github.com/bitcoin/bips/pull/2258 #2258] +** Count the expanded human-readable part in checksum length limits. +** Restrict master seed sizes to 128, 160, 192, 224, 256, and 512 bits; encodings at retained sizes are unchanged. +** Update validation, rationale, compatibility notes, and test vectors. +* '''0.1.3''' (2026-01-14): [https://github.com/bitcoin/bips/pull/1820 #1820] +** Update the preamble to the BIP-0003 format. +* '''0.1.2''' (2025-12-15): [https://github.com/bitcoin/bips/pull/2052 #2052] +** Add Python encoding and decoding helpers, clarify header and checksum handling, and standardize terminology and presentation. +* '''0.1.1''' (2023-03-30): [https://github.com/bitcoin/bips/pull/1439 #1439] +** Add invalid test vectors, correct capitalization and typos, fix the reference implementation link, and update the author list. +* '''0.1.0''' (2023-03-17): [https://github.com/bitcoin/bips/pull/1425 #1425] +** Publish the initial draft. + ==Appendix== From 1978ac253d257f8a1087efa2f1f655737d29118e Mon Sep 17 00:00:00 2001 From: Ben Westgate Date: Sun, 13 Sep 2026 22:05:04 -0500 Subject: [PATCH 6/6] bip93: Explain secret-sharing data representation Define the integer-list representation once in the SSSS-awareness introduction, before either procedure needs it. Move the unchanged interpolation helpers there too, so recovery does not depend on code inside Generating shares. Describe interpolation's arguments and result beside its definition and refer to the shared representation from both generation and recovery. State that both checksum variants use the same procedures without an early reference to the interpolation function. Label the existing casing rules and remove the unnecessary word "workflows" from the rationale. No algorithms, validity conditions, version, or changelog entries change. Checked all Python blocks are byte-identical and that vectors, procedure conditions, casing rules, headings, anchors, and metadata are unchanged. Sixteen recovery cases pass using only the shared introduction and recovery snippets, covering regular and long checksums. Existing vector, size, header, checksum-boundary, recovery, and padding checks also pass. Local rendering, link formatting, README table, and whitespace checks pass; the casing label does not add a TOC entry. Refs: #2285 --- bip-0093.mediawiki | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/bip-0093.mediawiki b/bip-0093.mediawiki index c728491c07..d017c16889 100644 --- a/bip-0093.mediawiki +++ b/bip-0093.mediawiki @@ -81,6 +81,8 @@ It reuses the base-32 character set from BIP-0173, and consists of: See [[#Master_seed_format|Master seed format]] for the payload-length and payload-conversion requirements for BIP-0032 master seeds. +'''Uppercase/lowercase''' + As with bech32 strings, a codex32 string MUST be entirely uppercase or entirely lowercase. Note that per BIP-0173, the lowercase form is used when determining a character's value for checksum purposes. In particular, given an all uppercase codex32 string, we still use lowercase ms as the human-readable part during checksum construction. @@ -226,18 +228,13 @@ When the share index of a valid codex32 string (converted to lowercase) is not t The first character of the data part indicates the threshold of the share, and it is required to be a non-"0" digit. The corresponding secret has index "s", as described in [[#Unshared_Secret|Unshared secret]]. -Generation of long shares and recovery of the long secret from long shares proceeds in exactly the same way as for regular shares with the ms32_interpolate function. +The share generation and secret recovery procedures below are the same for both checksum variants. - -====Generating shares==== +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. -If we already have ''k'' valid codex32 strings such that: - -* All strings have the same threshold value ''k'', the same identifier, and the same length -* All of the share index values are distinct - -Then we can derive additional shares with the ms32_interpolate function by passing it a list of exactly ''k'' of these codex32 strings, together with a fresh share index distinct from all of the existing share indexes. -The newly derived share will have the provided share index. +The first argument to the ms32_interpolate function is a list of codex32 strings in this representation. +Its second argument is a target share index converted to its integer value using the same character table, distinct from all input share indexes. +The result represents the codex32 string at that index. BECH32_INV = [ @@ -275,6 +272,17 @@ def ms32_interpolate(l, x): return res + +====Generating shares==== + +If we already have ''k'' valid codex32 strings such that: + +* All strings have the same threshold value ''k'', the same identifier, and the same length +* All of the share index values are distinct + +Then we can derive additional shares with the ms32_interpolate function. +Pass a list of exactly ''k'' of these codex32 strings in the representation defined above, together with the integer value of a fresh share index distinct from all of the existing share indexes. + Once a user has generated ''n'' shares, they may discard the codex32 secret (if it exists). The ''n'' shares form a ''k'' of ''n'' Shamir's secret sharing scheme of a codex32 secret. @@ -326,7 +334,8 @@ In order to recover a secret, one needs a set of valid shares such that: * All of the share index values are distinct. * The number of shares is exactly equal to the (common) threshold value. -If all the above conditions are satisfied, the ms32_recover function will return a codex32 secret when its argument is the list of codex32 shares with each share represented as a list of integers representing the characters converted using the bech32 character table from BIP-0173. +If all the above conditions are satisfied, the ms32_recover function returns the corresponding codex32 secret from a list of the shares. +Both the input shares and the returned secret use the representation defined above. def ms32_recover(shares): return ms32_interpolate(shares, 16) @@ -389,7 +398,7 @@ However, the checksum does not protect against maliciously constructed errors. For 256-bit seeds and shares our strings are 74 characters, which fits into the 96 character format of the 24 four-letter word format of the BIP-0039 mnemonic, with plenty of room to spare. The supported 128-, 160-, 192-, 224-, and 256-bit sizes are the entropy sizes defined by BIP-0039, while 512 bits is the BIP-0032 seed size produced by BIP-0039 recovery. -These encoded lengths have at least six-character gaps, reducing target length ambiguity for optional insertion/deletion correction workflows. +These encoded lengths have at least six-character gaps, reducing target length ambiguity for optional insertion/deletion correction. While the regular checksum is enough to support the 256-bit advised size of BIP-0032 master seeds, BIP-0032 allows seeds to be up to 512 bits in size. We define a long checksum to support the maximum seed size because its expanded codewords exceed the regular checksum's limit.