138 lines
5.4 KiB
Python
138 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""crosscheck_oracle.py -- my Crockford Base32 vs an independent third-party one.
|
|
|
|
Oracle: PyPI package `base32-crockford` 0.3.0 (site-packages/base32_crockford.py),
|
|
installed in a scratch venv, not written by me. If my alphabet, positional weight or
|
|
mod-37 check-symbol ordering were wrong, the two implementations would disagree.
|
|
|
|
The main suite (test_base32c.py) deliberately stays dependency-free; this script is the
|
|
one place that reaches for an outside implementation, so run it with the venv python:
|
|
|
|
/root/.hermes/profiles/null/cache/scratch/b32venv/bin/python3.14 crosscheck_oracle.py
|
|
|
|
Exit 0 = full agreement, 1 = disagreement (or oracle missing).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import random
|
|
import sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
if HERE not in sys.path:
|
|
sys.path.insert(0, HERE)
|
|
|
|
import base32c # noqa: E402
|
|
|
|
try:
|
|
import base32_crockford as oracle # type: ignore
|
|
except ImportError:
|
|
print("ORACLE MISSING: base32_crockford not importable (run with the scratch venv python)")
|
|
raise SystemExit(1)
|
|
|
|
|
|
def main() -> int:
|
|
rng = random.Random(4242)
|
|
values = list(range(0, 20_001))
|
|
values += [2 ** k - 1 for k in range(1, 257)]
|
|
values += [2 ** k for k in range(0, 257)]
|
|
values += [rng.getrandbits(rng.choice([8, 32, 64, 128, 256, 512])) for _ in range(3000)]
|
|
|
|
payload_checks = checked_checks = check_symbol_checks = hyphen_checks = alias_checks = 0
|
|
for value in values:
|
|
mine = base32c.encode(value)
|
|
theirs = oracle.encode(value)
|
|
assert mine == theirs, f"payload mismatch for {value}: {mine!r} != {theirs!r}"
|
|
payload_checks += 1
|
|
|
|
mine_checked = base32c.encode_checked(value)
|
|
theirs_checked = oracle.encode(value, checksum=True)
|
|
assert mine_checked == theirs_checked, f"checked mismatch for {value}: {mine_checked!r} != {theirs_checked!r}"
|
|
checked_checks += 1
|
|
|
|
assert base32c.check_symbol(value) == theirs_checked[-1], value
|
|
check_symbol_checks += 1
|
|
|
|
assert oracle.decode(mine) == value, value
|
|
assert oracle.decode(mine_checked, checksum=True) == value, value
|
|
assert base32c.decode(theirs) == value, value
|
|
assert base32c.decode_checked(theirs_checked) == value, value
|
|
|
|
hyphenated = base32c.encode(value, hyphenate=4)
|
|
assert oracle.decode(hyphenated) == value, value
|
|
hyphen_checks += 1
|
|
|
|
lowered = mine.lower().replace("l", "1").replace("i", "1").replace("o", "0")
|
|
assert base32c.decode(lowered) == value == oracle.decode(lowered), value
|
|
alias_checks += 1
|
|
|
|
# Both implementations must reject the same kinds of junk.
|
|
never_legal = ("", "-", "U", "u", "A U", "a b", "a!", "\n", "Z Q")
|
|
rejections = 0
|
|
for junk in never_legal:
|
|
for module in (base32c, oracle):
|
|
try:
|
|
module.decode(junk)
|
|
except (ValueError, TypeError):
|
|
rejections += 1
|
|
else:
|
|
raise AssertionError(f"{module.__name__} accepted junk {junk!r}")
|
|
assert rejections == 2 * len(never_legal), rejections
|
|
|
|
# Documented divergence: the oracle's lax decode(checksum=False) treats a trailing
|
|
# check-only symbol as a payload digit, so 'a=' decodes to a number. Mine refuses
|
|
# '*~$=U' anywhere in a payload. In the validating mode both agree and reject.
|
|
divergences = 0
|
|
for tail in "*~$=U":
|
|
lax = f"a{tail}"
|
|
assert oracle.decode(lax) >= 0 # oracle: accepted, lax
|
|
try:
|
|
base32c.decode(lax)
|
|
except base32c.InvalidSymbolError:
|
|
divergences += 1
|
|
else:
|
|
raise AssertionError(f"base32c accepted {lax!r} as a payload")
|
|
for name, call in (
|
|
("base32c.decode_checked", lambda: base32c.decode_checked(lax)),
|
|
("oracle.decode(checksum=True)", lambda: oracle.decode(lax, checksum=True)),
|
|
):
|
|
try:
|
|
call()
|
|
except ValueError:
|
|
pass
|
|
else:
|
|
raise AssertionError(f"{name} accepted {lax!r}")
|
|
assert divergences == 5, divergences
|
|
|
|
# A corrupted check symbol must be rejected by both.
|
|
corrupt_rejections = 0
|
|
for value in (0, 1, 37, 1234, 2 ** 64 - 1):
|
|
good = base32c.encode_checked(value)
|
|
bad = good[:-1] + ("Z" if good[-1] != "Z" else "Y")
|
|
try:
|
|
base32c.decode_checked(bad)
|
|
except base32c.Base32Error:
|
|
corrupt_rejections += 1
|
|
try:
|
|
oracle.decode(bad, checksum=True)
|
|
except ValueError:
|
|
corrupt_rejections += 1
|
|
|
|
print(f"values compared: {len(values)}")
|
|
print(f"payload encodings equal: {payload_checks}")
|
|
print(f"checked encodings equal: {checked_checks}")
|
|
print(f"check symbols equal: {check_symbol_checks}")
|
|
print(f"hyphenated decode equal: {hyphen_checks}")
|
|
print(f"alias/lowercase equal: {alias_checks}")
|
|
print(f"junk rejections matched: {rejections}/{2 * len(never_legal)} (never-legal strings x 2 implementations)")
|
|
print(f"documented divergence: {divergences}/5 (oracle lax-accepts payload+check-only symbol; base32c is strict, both agree in validating mode)")
|
|
print(f"corrupt check rejected: {corrupt_rejections}/10 (5 values x 2 implementations)")
|
|
assert corrupt_rejections == 10, corrupt_rejections
|
|
print("ORACLE AGREEMENT: full (on every non-divergent behaviour)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|