73 lines
4.5 KiB
Markdown
73 lines
4.5 KiB
Markdown
# base32c — Crockford Base32 with a check symbol (stdlib-only)
|
||
|
||
Lane: Codeforge (null). Built 2026-10-23.
|
||
|
||
Spec: https://www.crockford.com/base32.html (Douglas Crockford, 2019-03-04) — the
|
||
authoritative table was fetched during this build; the quoted rules live in the
|
||
`base32c.py` docstring (symbol set, I/L/O/U exclusions, i/l→1 and o→0 decode aliases,
|
||
zero-extension to a multiple of 5 bits, hyphens ignored on decode, check symbol =
|
||
value mod 37 over the 37-symbol alphabet `0-9 A-Z * ~ $ = U`).
|
||
|
||
## Files
|
||
|
||
base32c.py codec + argparse CLI (encode, decode, encode-checked, verify, roundtrip)
|
||
test_base32c.py 16-case evidence suite; exit 0 = all invariants hold, 1 = broken
|
||
mutation_check.py injects 6 faults into a scratch copy; exit 0 = every fault caught
|
||
crosscheck_oracle.py agreement run vs the independent PyPI package base32-crockford 0.3.0
|
||
demo_ids.py real-data demo: SHA-256 -> 128-bit hyphenated public ID + typo sweep
|
||
base32c-selftest.txt captured run of test_base32c.py
|
||
base32c-oracle.txt captured run of crosscheck_oracle.py
|
||
base32c-demo.txt captured run of demo_ids.py
|
||
|
||
## Reproduce (each command's own exit code is the verdict)
|
||
|
||
python3 test_base32c.py
|
||
python3 mutation_check.py
|
||
python3 demo_ids.py
|
||
/root/.hermes/profiles/null/cache/scratch/b32venv/bin/python3.14 crosscheck_oracle.py
|
||
|
||
## Measured results (this build, this machine)
|
||
|
||
test_base32c.py 16/16 cases pass, exit 0 [measured]
|
||
round trips 105,806 values (exhaustive 0..100,000 plus 2**k-1, 2**k, 2**k+1
|
||
for k=0..600 and 4,000 seeded randoms up to 1024 bits), each
|
||
checked by three independent decoders/encoders [measured]
|
||
mutation_check.py 6/6 injected faults killed, control copy passes, exit 0 [measured]
|
||
crosscheck_oracle.py 23,514 values agree with PyPI base32-crockford 0.3.0 on payload,
|
||
checked payload, check symbol, hyphenated decode and alias
|
||
handling; 18/18 junk rejections matched; 10/10 corrupted
|
||
check symbols rejected by both [measured]
|
||
single-symbol typos 9,808/9,808 rejected, of which 9,706/9,706 use a legal payload
|
||
symbol (the interesting case) [measured]
|
||
adjacent transpositions 27/27 rejected (all distinct-symbol adjacent swaps) [measured]
|
||
deletions 816/837 rejected (97.5%) [measured]
|
||
insertions 2,568/2,691 rejected (95.4%) [measured]
|
||
demo_ids.py GPL-3 sha256 -> id 1SEB-E9EH-7P96-FGZ6-SDQX-V6JV-SAE,
|
||
round-trip OK, 842/842 typos rejected, exit 0 [measured]
|
||
|
||
Why substitution and adjacent-transposition detection are 100% (not a coincidence):
|
||
substituting the symbol at weight 32**j by a different canonical digit changes the value
|
||
by d*32**j, 1 <= |d| <= 31; 37 is prime and 32**j is a unit, and |d| < 37, so the change is
|
||
never 0 mod 37. Adjacent swaps change the value by (a-b)*32**j*31 with a != b, |a-b| <= 31 —
|
||
also never 0 mod 37. The suite asserts both exhaustively rather than asserting the algebra.
|
||
|
||
## Limitations — stated, not hidden
|
||
|
||
* The check symbol is value-based, so edits that preserve the value are invisible:
|
||
deleting a leading `0`, or inserting `0` in front, still verifies. The suite asserts
|
||
this blind spot on purpose (`case_leading_zero_blind_spot_is_real`).
|
||
* Single deletions/insertions elsewhere are ~95–98% caught, not 100%; measured above.
|
||
* `min_length` pads with `0`, which is exactly what creates the leading-zero blind spot —
|
||
use it for display widths, not for extra integrity.
|
||
* Behavioural divergence from the PyPI oracle: its `decode(checksum=False)` treats a
|
||
trailing check-only symbol (`* ~ $ = U`) as a payload digit, so `a=` decodes to a number.
|
||
`base32c.decode` refuses check-only symbols in payload positions. In validating mode
|
||
(`decode_checked` / `oracle.decode(..., checksum=True)`) both reject. Not a bug on either
|
||
side — an ambiguity in the lax mode, recorded in `crosscheck_oracle.py`.
|
||
|
||
## Verdict
|
||
|
||
VERDICT: Crockford Base32 codec ships with 16/16 invariant cases, 6/6 mutants killed, and
|
||
23,514-value agreement with an independent PyPI implementation; the one real limitation
|
||
(leading-zero edits are invisible to a mod-37 check) is asserted in the suite, not hidden.
|