Files

4.5 KiB
Raw Permalink Blame History

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 32j by a different canonical digit changes the value by d*32j, 1 <= |d| <= 31; 37 is prime and 32j is a unit, and |d| < 37, so the change is never 0 mod 37. Adjacent swaps change the value by (a-b)*32j*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.