The public history carried kat/__pycache__/mlkem768_reference.cpython-314.pyc, a compiled Python artifact embedding the operator's absolute local path. Text secret scanners do not read compiled binaries, which is exactly how it slipped through, and removing it from the tip would have left it reachable through the old root commits. So this repository is republished from a single clean root. This root also carries, from the previously unpublished line of work: - corrected LICENSE year, LICENSING.md, VERIFY-POLICY.md, and CITATIONS-UNRESOLVED.md remeasured 2026-08-11 (101 paths, README aligned) - O-018: run_consensus_verification.py ran 19 of 29 models and reported PASS; it now runs all 29, and computemarket_smt.py gains resolveByTimeout / reclaimUnsettled cases plus a negative control - O-006: the word 'audited' removed from next to Bouncy Castle, twice, after a concurrent edit resurrected it - O-014: prior art named and dated - Algorand's native falcon_verify shipped about ten months before AERE's precompiles; the primacy claim is withdrawn where it was implied - bench/ scripts parametrized so they actually run for an outsider (the earlier textual sanitization left $STAGING unexpanded inside Python strings) - AIP-2/AIP-3 errata with measured figures, spec remeasurements at 2026-08-01, and the spec-zk-stack retractions (owner is an operational key, not the Foundation; 'maximally sound' withdrawn; aggregator V1 deprecated) The redacted bench-host environment files from the sanitized line are kept exactly as published; the unredacted local variants are not carried.
288 lines
13 KiB
Python
288 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
# Test harness for the BabyBear field + degree-4 extension sub-component (port spec section 2, the
|
|
# FOUNDATION component (a)). Runs:
|
|
# 1. F_p field axioms over many random cases (comm/assoc/distrib/identities/inverses),
|
|
# 2. inverse cross-check: Fermat a^(p-2) == an INDEPENDENT extended-Euclid bignum inverse
|
|
# (Python pow(a, -1, p)), for many a; and the (p-1) == -1 identities,
|
|
# 3. exponent / Frobenius consistency: a^(p-1) == 1, a^p == a, pow == naive repeated mul,
|
|
# 4. constants (proven offline, NO external fetch): generator has order exactly p-1; the two-adic
|
|
# generator of the order-2^k subgroup has order exactly 2^k; W is a quadratic non-residue and
|
|
# p == 1 mod 4, so x^4 - W is irreducible and F_{p^4} is a real field; x^4 == W in F_{p^4},
|
|
# 5. F_{p^4} axioms: ring laws, base embedding is a homomorphism, a * inv(a) == 1 for many a
|
|
# (including non-base elements), Frobenius^4 == id and Frobenius fixes the base field,
|
|
# 6. cross-language agreement: Python vs Node vs Java produce byte-identical results on a shared
|
|
# vector set (three independent implementations of the same public field definition).
|
|
# Writes ../results/kat-results-babybear-field.json and exits non-zero on any failure.
|
|
#
|
|
# HONEST SCOPE. This validates the field ARITHMETIC against the public BabyBear definition and an
|
|
# independent bignum reference. It is the bottom layer of a six-component STARK port; it does NOT
|
|
# verify any real STARK proof, and the top-level 0x0AE8 precompile stays fail-closed (returns EMPTY
|
|
# for every input). GENERATOR = 31 and W = 11 are proven here to be a real generator and a real
|
|
# non-residue; that they are Plonky3's EXACT chosen constants is [VERIFY]/[MEASURE] against
|
|
# p3-baby-bear at the pinned SP1 v6.1.0 revision (see docs/AERE-STARK-VERIFIER-PORT-SPEC.md sec 2).
|
|
|
|
import json
|
|
import os
|
|
import random
|
|
import subprocess
|
|
import sys
|
|
from shutil import which
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, HERE)
|
|
|
|
import babybear_field_reference as F # noqa: E402
|
|
|
|
RESULTS = os.path.normpath(os.path.join(HERE, "..", "results", "kat-results-babybear-field.json"))
|
|
|
|
_pass = 0
|
|
_fail = 0
|
|
_notes = []
|
|
|
|
|
|
def check(name, cond):
|
|
global _pass, _fail
|
|
if cond:
|
|
_pass += 1
|
|
else:
|
|
_fail += 1
|
|
print(f"FAIL {name}")
|
|
|
|
|
|
# ---- 1. F_p field axioms + 2. inverse cross-check + 3. exponent/Frobenius --------------------------
|
|
def base_field_axioms():
|
|
p = F.P
|
|
rnd = random.Random(0xBEEF)
|
|
for _ in range(20000):
|
|
a = rnd.randrange(p)
|
|
b = rnd.randrange(p)
|
|
c = rnd.randrange(p)
|
|
check("add.comm", F.add(a, b) == F.add(b, a))
|
|
check("mul.comm", F.mul(a, b) == F.mul(b, a))
|
|
check("add.assoc", F.add(F.add(a, b), c) == F.add(a, F.add(b, c)))
|
|
check("mul.assoc", F.mul(F.mul(a, b), c) == F.mul(a, F.mul(b, c)))
|
|
check("distrib", F.mul(a, F.add(b, c)) == F.add(F.mul(a, b), F.mul(a, c)))
|
|
check("add.id", F.add(a, 0) == a)
|
|
check("mul.id", F.mul(a, 1) == a)
|
|
check("sub.def", F.add(F.sub(a, b), b) == a)
|
|
check("neg.def", F.add(a, F.neg(a)) == 0)
|
|
check("mul.vs.bignum", F.mul(a, b) == (a * b) % p) # independent bignum cross-check
|
|
if a != 0:
|
|
# Fermat inverse vs INDEPENDENT extended-Euclid inverse (Python pow(a, -1, p)).
|
|
check("inv.fermat.vs.euclid", F.inv(a) == pow(a, -1, p))
|
|
check("inv.def", F.mul(a, F.inv(a)) == 1)
|
|
check("fermat.little", F.pow_(a, p - 1) == 1)
|
|
check("frobenius.base", F.pow_(a, p) == a) # a^p == a on the base field
|
|
|
|
# (p-1) behaves exactly as -1
|
|
check("minus1.is.neg1", F.P - 1 == F.neg(1))
|
|
check("minus1.sq", F.mul(F.P - 1, F.P - 1) == 1)
|
|
check("minus1.add", F.add(F.P - 1, 1) == 0)
|
|
check("inv.zero.convention", F.inv(0) == 0)
|
|
|
|
# pow vs naive repeated multiplication for small exponents
|
|
for a in (2, 3, 31, 1000, F.P - 1):
|
|
acc = 1
|
|
for e in range(0, 40):
|
|
check(f"pow.naive.a{a}.e{e}", F.pow_(a, e) == acc)
|
|
acc = F.mul(acc, a)
|
|
|
|
|
|
# ---- 4. constants: generator, two-adic subgroup, W / irreducibility -------------------------------
|
|
def constants_offline_proofs():
|
|
p = F.P
|
|
check("p.value", p == 2**31 - 2**27 + 1 == 15 * 2**27 + 1 == 0x78000001)
|
|
check("p.minus1.factor", (p - 1) == (2**27) * 3 * 5)
|
|
check("p.mod4", p % 4 == 1)
|
|
|
|
# generator has order exactly p-1 (real proof via full factorization)
|
|
check("generator.order", F.multiplicative_order(F.GENERATOR) == p - 1)
|
|
_notes.append(f"multiplicative generator {F.GENERATOR} proven order = p-1 = {p-1} [VERIFY it is Plonky3's]")
|
|
|
|
# two-adic generator of order-2^k subgroup has order exactly 2^k, for k = 1..27
|
|
tag = F.two_adic_generator(27)
|
|
check("twoadic27.order", F.pow_(tag, 2**27) == 1 and F.pow_(tag, 2**26) != 1)
|
|
for k in range(1, 28):
|
|
g = F.two_adic_generator(k)
|
|
check(f"twoadic.order.k{k}", F.pow_(g, 2**k) == 1 and (k == 0 or F.pow_(g, 2 ** (k - 1)) != 1))
|
|
_notes.append(f"two_adic_generator(27) = {tag}, order proven 2^27 [VERIFY exact Plonky3 value]")
|
|
|
|
# W = 11: quadratic non-residue + p == 1 mod 4 => x^4 - W irreducible => F_{p^4} is a field
|
|
check("W.qnr", F.is_quadratic_non_residue(F.W))
|
|
_notes.append(
|
|
f"W = {F.W}: 11^((p-1)/2) == p-1 (QNR) and p == 1 mod 4 => x^4 - {F.W} irreducible "
|
|
f"(Lidl-Niederreiter Thm 3.75) => F_(p^4) is a genuine field [VERIFY it is Plonky3's W]"
|
|
)
|
|
# defining relation x^4 == W in the extension
|
|
x = [0, 1, 0, 0]
|
|
check("x4.eq.W", F.ext_eq(F.ext_pow(x, 4), F.ext_from_base(F.W)))
|
|
# order of x = 4 * order(W); must divide 4*(p-1)
|
|
ordW = F.multiplicative_order(F.W)
|
|
ordx = 4 * ordW
|
|
check("ordx.divides", (4 * (p - 1)) % ordx == 0)
|
|
_notes.append(f"ord(W=11) = {ordW} = 2^27 * 5; ord(x) = 4*ord(W) = {ordx} divides 4(p-1)")
|
|
|
|
|
|
# ---- 5. F_{p^4} extension axioms -------------------------------------------------------------------
|
|
def extension_axioms():
|
|
rnd = random.Random(0xF00D)
|
|
one = [1, 0, 0, 0]
|
|
zero = [0, 0, 0, 0]
|
|
for _ in range(10000):
|
|
a = [rnd.randrange(F.P) for _ in range(4)]
|
|
b = [rnd.randrange(F.P) for _ in range(4)]
|
|
c = [rnd.randrange(F.P) for _ in range(4)]
|
|
check("ext.add.comm", F.ext_eq(F.ext_add(a, b), F.ext_add(b, a)))
|
|
check("ext.mul.comm", F.ext_eq(F.ext_mul(a, b), F.ext_mul(b, a)))
|
|
check("ext.add.assoc", F.ext_eq(F.ext_add(F.ext_add(a, b), c), F.ext_add(a, F.ext_add(b, c))))
|
|
check("ext.mul.assoc", F.ext_eq(F.ext_mul(F.ext_mul(a, b), c), F.ext_mul(a, F.ext_mul(b, c))))
|
|
check("ext.distrib", F.ext_eq(F.ext_mul(a, F.ext_add(b, c)),
|
|
F.ext_add(F.ext_mul(a, b), F.ext_mul(a, c))))
|
|
check("ext.mul.id", F.ext_eq(F.ext_mul(a, one), a))
|
|
check("ext.sub.def", F.ext_eq(F.ext_add(F.ext_sub(a, b), b), a))
|
|
check("ext.neg.def", F.ext_eq(F.ext_add(a, F.ext_neg(a)), zero))
|
|
if not F.ext_eq(a, zero):
|
|
check("ext.inv.def", F.ext_is_one(F.ext_mul(a, F.ext_inv(a))))
|
|
# Frobenius^4 == id; Frobenius fixes the base field; Frobenius is a ring homomorphism
|
|
f = a
|
|
for _ in range(4):
|
|
f = F.ext_frobenius(f)
|
|
check("ext.frob4.id", F.ext_eq(f, a))
|
|
check("ext.mul.vs.frob", F.ext_eq(F.ext_frobenius(F.ext_mul(a, b)),
|
|
F.ext_mul(F.ext_frobenius(a), F.ext_frobenius(b))))
|
|
|
|
# base embedding is a ring homomorphism, and is fixed by Frobenius
|
|
for _ in range(2000):
|
|
u = rnd.randrange(F.P)
|
|
v = rnd.randrange(F.P)
|
|
check("embed.add", F.ext_eq(F.ext_add(F.ext_from_base(u), F.ext_from_base(v)),
|
|
F.ext_from_base(F.add(u, v))))
|
|
check("embed.mul", F.ext_eq(F.ext_mul(F.ext_from_base(u), F.ext_from_base(v)),
|
|
F.ext_from_base(F.mul(u, v))))
|
|
check("embed.frob.fix", F.ext_eq(F.ext_frobenius(F.ext_from_base(u)), F.ext_from_base(u)))
|
|
check("ext.inv.zero.convention", F.ext_eq(F.ext_inv([0, 0, 0, 0]), [0, 0, 0, 0]))
|
|
|
|
|
|
# ---- 6. cross-language agreement (Python vs Node vs Java) -----------------------------------------
|
|
def canonicalize(v):
|
|
# Structural comparison ignoring key order / whitespace / int-vs-float JSON encoding.
|
|
def i(x):
|
|
return int(x)
|
|
|
|
def ia(x):
|
|
return [int(e) for e in x]
|
|
|
|
return {
|
|
"constants": {k: i(x) for k, x in v["constants"].items()},
|
|
"base_unary": [(i(r["a"]), i(r["neg"]), i(r["inv"]), i(r["pow"])) for r in v["base_unary"]],
|
|
"base_binary": [(i(r["a"]), i(r["b"]), i(r["add"]), i(r["sub"]), i(r["mul"]))
|
|
for r in v["base_binary"]],
|
|
"ext_unary": [(ia(r["a"]), ia(r["neg"]), ia(r["inv"]), ia(r["frob"])) for r in v["ext_unary"]],
|
|
"ext_binary": [(ia(r["a"]), ia(r["b"]), ia(r["add"]), ia(r["sub"]), ia(r["mul"]))
|
|
for r in v["ext_binary"]],
|
|
}
|
|
|
|
|
|
def cross_language():
|
|
py = canonicalize(F.shared_vectors())
|
|
langs = {"python": py}
|
|
|
|
node = which("node")
|
|
if node:
|
|
try:
|
|
out = subprocess.run(
|
|
[node, os.path.join(HERE, "babybear_field_reference.mjs")],
|
|
capture_output=True, text=True, timeout=120, check=True,
|
|
).stdout.strip()
|
|
langs["node"] = canonicalize(json.loads(out))
|
|
except Exception as e: # noqa: BLE001
|
|
_notes.append(f"node cross-check unavailable: {e}")
|
|
else:
|
|
_notes.append("node not found on PATH; node cross-check skipped")
|
|
|
|
javac = which("javac")
|
|
java = which("java")
|
|
if javac and java:
|
|
try:
|
|
subprocess.run(
|
|
[javac, "-d", os.path.join(HERE, "out"),
|
|
os.path.join(HERE, "BabyBearFieldSelfTest.java")],
|
|
capture_output=True, text=True, timeout=180, check=True,
|
|
)
|
|
out = subprocess.run(
|
|
[java, "-cp", os.path.join(HERE, "out"), "BabyBearFieldSelfTest", "--emit"],
|
|
capture_output=True, text=True, timeout=120, check=True,
|
|
).stdout.strip()
|
|
langs["java"] = canonicalize(json.loads(out))
|
|
except Exception as e: # noqa: BLE001
|
|
_notes.append(f"java cross-check unavailable: {e}")
|
|
else:
|
|
_notes.append("javac/java not found on PATH; java cross-check skipped")
|
|
|
|
for name, data in langs.items():
|
|
if name == "python":
|
|
continue
|
|
check(f"crosslang.{name}.agree", data == py)
|
|
_notes.append("cross-language implementations compared: " + ", ".join(sorted(langs)))
|
|
return sorted(langs)
|
|
|
|
|
|
def main():
|
|
base_field_axioms()
|
|
constants_offline_proofs()
|
|
extension_axioms()
|
|
langs = cross_language()
|
|
|
|
report = {
|
|
"component": "BabyBear field F_p + degree-4 extension F_{p^4} (port spec component (a), FOUNDATION)",
|
|
"precompile": "0x0000000000000000000000000000000000000ae8",
|
|
"scope": "port spec section 2; the field-arithmetic foundation the whole STARK verifier is built on",
|
|
"validates": "F_p and F_{p^4} field axioms; Fermat inverse vs independent extended-Euclid "
|
|
"bignum; generator has order p-1; two-adic generator order 2^k; W is a QNR so "
|
|
"x^4-W is irreducible (real field); across Python/Node/Java",
|
|
"does_not_validate": "any real STARK proof (this is only component (a) of six); and that "
|
|
"GENERATOR=31 / W=11 / two_adic_generator(27) are Plonky3's EXACT "
|
|
"constants ([VERIFY]/[MEASURE] vs p3-baby-bear at pinned SP1 v6.1.0)",
|
|
"top_level_verifier": "unchanged, still fail-closed (returns EMPTY for every input)",
|
|
"constants": {
|
|
"P": F.P,
|
|
"P_expr": "2^31 - 2^27 + 1 = 15*2^27 + 1 = 0x78000001",
|
|
"generator": F.GENERATOR,
|
|
"two_adicity": F.TWO_ADICITY,
|
|
"two_adic_generator_27": F.two_adic_generator(27),
|
|
"W": F.W,
|
|
},
|
|
"cross_language": langs,
|
|
"flags": [
|
|
"[VERIFY] GENERATOR = 31 is Plonky3 p3-baby-bear's chosen multiplicative generator "
|
|
"(order = p-1 is PROVEN here; the identity as Plonky3's is [VERIFY])",
|
|
"[VERIFY] W = 11 is Plonky3's BabyBear BinomialExtensionField<_,4> non-residue "
|
|
"(x^4-11 irreducibility is PROVEN here; the identity as Plonky3's W is [VERIFY]/[MEASURE])",
|
|
"[VERIFY] two_adic_generator(27) exact value vs Plonky3 (order 2^27 is PROVEN here)",
|
|
"[MEASURE] byte-for-byte conformance of Fp/Fp4 outputs vs p3-baby-bear / p3-field unit "
|
|
"vectors at the pinned revision (needs the pinned Plonky3 source)",
|
|
],
|
|
"notes": _notes,
|
|
"passed": _pass,
|
|
"failed": _fail,
|
|
"total": _pass + _fail,
|
|
}
|
|
os.makedirs(os.path.dirname(RESULTS), exist_ok=True)
|
|
with open(RESULTS, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
|
|
print("\n=== BabyBear field + F_{p^4} KAT ===")
|
|
print(f"cross-language: {', '.join(langs)}")
|
|
for nnote in _notes:
|
|
print(f"note: {nnote}")
|
|
print(f"PASS={_pass} FAIL={_fail} TOTAL={_pass + _fail}")
|
|
print(f"results -> {RESULTS}")
|
|
if _fail != 0:
|
|
print("BABYBEAR_FIELD_KAT_FAILED")
|
|
sys.exit(1)
|
|
print("BABYBEAR_FIELD_KAT_OK")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|