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.
259 lines
12 KiB
Python
259 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
# ASSEMBLED END-TO-END BabyBear + FRI STARK verifier for the PQ STARK-verify precompile 0x0AE8 reference.
|
|
#
|
|
# ============================ WHAT THIS IS (read first) ============================
|
|
# The six generic components of a BabyBear+FRI STARK verifier were each conformance-confirmed INDIVIDUALLY
|
|
# against the pinned Plonky3 0.4.3-succinct crates (field, Poseidon2, MMCS, FRI verify_query, duplex
|
|
# challenger, generic AIR quotient check). THIS module wires all six together into ONE full p3-uni-stark
|
|
# verify pipeline and runs it, end to end, against a REAL proof emitted by the pinned p3-uni-stark PROVER
|
|
# (e2e-extractor -> e2e_ground_truth.json), which the pinned p3-uni-stark VERIFIER accepts (the ground
|
|
# truth). It asserts the assembled pipeline ACCEPTS the genuine proof and REJECTS tampered ones (a trace
|
|
# opening, a FRI layer, an input-batch opening, the public values, the final poly).
|
|
#
|
|
# The pipeline follows p3-uni-stark verifier.rs + p3-fri two_adic_pcs.rs::verify + p3-fri verifier.rs
|
|
# VERBATIM (pinned 0.4.3-succinct):
|
|
# 1. observe(trace commit); alpha_c = sample_ext (constraint challenge)
|
|
# 2. observe(quotient commit); zeta = sample_ext; zeta_next = zeta * g_trace
|
|
# 3. PCS.verify:
|
|
# alpha_fri = sample_ext (FRI batch-combining challenge)
|
|
# verify_shape_and_sample_challenges: per commit-phase commit observe+sample beta; observe final
|
|
# poly; check grinding PoW; sample num_queries query indices
|
|
# per query: reduced-opening combination sum_i alpha_fri^i (p(x)-p(z))/(x-z) over the input-batch
|
|
# MMCS openings (input-batch root recomputed by the CONFIRMED MMCS verify_batch), then
|
|
# verify_query (the CONFIRMED FRI fold + commit-phase MMCS opening) and folded == final_poly
|
|
# 4. AIR quotient consistency: folded_constraints(zeta) * inv_zeroifier == quotient(zeta)
|
|
#
|
|
# HONEST SCOPE. The end-to-end proof verified here is a p3-uni-stark EXAMPLE AIR (Fibonacci / degree-3
|
|
# multiply) under an EXAMPLE FRI config (log_blowup=2, num_queries=28, pow_bits=8), NOT an Aere production
|
|
# circuit and NOT an SP1 6.1.0 proof. Aere's own zk-circuits are SP1 guest programs, and SP1 6.1.0 is
|
|
# Hypercube (KoalaBear multilinear: BaseFold/Jagged + sumcheck-zerocheck + LogUp-GKR), which this
|
|
# BabyBear+FRI pipeline does not target. This module does NOT run in the precompile and does NOT make
|
|
# 0x0AE8 accept anything: the top level stays FAIL-CLOSED (SP1_RECURSION_AIR_PORTED=false). See the port
|
|
# spec and README for exactly what an Aere-own / SP1 end-to-end KAT still needs.
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, HERE)
|
|
|
|
import babybear_field_reference as F # (a) F_p and F_{p^4} CONFIRMED
|
|
import poseidon2_babybear_reference as P2 # (b) Poseidon2 permutation CONFIRMED
|
|
import mmcs_babybear_reference as M # (c) FieldMerkleTreeMmcs verify_batch CONFIRMED
|
|
import challenger_reference as CH # (f) DuplexChallenger + grinding CONFIRMED
|
|
import fri_verify_reference as FRI # (d) FRI verify_query (fold + opening) CONFIRMED
|
|
import air_quotient_reference as AQ # (e) generic AIR quotient-consistency CONFIRMED
|
|
|
|
GROUND_TRUTH = os.path.join(HERE, "e2e_ground_truth.json")
|
|
P = F.P
|
|
|
|
|
|
def _log2_exact(n):
|
|
b = n.bit_length() - 1
|
|
assert (1 << b) == n, f"{n} is not a power of two"
|
|
return b
|
|
|
|
|
|
def verify_stark(case, tamper=None):
|
|
"""Full assembled BabyBear+FRI STARK verify of a real p3-uni-stark proof. Returns True iff every
|
|
stage passes. `tamper` in {None, 'trace_open', 'fri_layer', 'input_batch', 'public_values',
|
|
'final_poly'} injects a single fault to demonstrate rejection."""
|
|
c = json.loads(json.dumps(case)) # deep copy so tampers do not mutate the shared ground truth
|
|
|
|
log_blowup = c["log_blowup"]
|
|
num_queries = c["num_queries"]
|
|
pow_bits = c["pow_bits"]
|
|
degree_bits = c["degree_bits"]
|
|
air = c["air"]
|
|
pis = list(c["public_values"])
|
|
|
|
trace_commit = list(c["commitments"]["trace"])
|
|
quotient_commit = list(c["commitments"]["quotient_chunks"])
|
|
trace_local = [list(v) for v in c["opened_values"]["trace_local"]]
|
|
trace_next = [list(v) for v in c["opened_values"]["trace_next"]]
|
|
quotient_chunks = [[list(v) for v in ch] for ch in c["opened_values"]["quotient_chunks"]]
|
|
|
|
fp = c["opening_proof"]["fri_proof"]
|
|
commit_phase_commits = [list(x) for x in fp["commit_phase_commits"]]
|
|
final_poly = list(fp["final_poly"])
|
|
pow_witness = fp["pow_witness"]
|
|
query_proofs = fp["query_proofs"]
|
|
query_openings = c["opening_proof"]["query_openings"]
|
|
|
|
# ---- tampers that live in the claimed openings / public inputs ----
|
|
if tamper == "public_values":
|
|
pis[-1] = (pis[-1] + 1) % P
|
|
if tamper == "trace_open":
|
|
trace_local[0][0] = (trace_local[0][0] + 1) % P
|
|
if tamper == "final_poly":
|
|
final_poly[0] = (final_poly[0] + 1) % P
|
|
|
|
quotient_degree = len(quotient_chunks)
|
|
log_max_height = len(commit_phase_commits) + log_blowup # = log_global_max_height
|
|
|
|
# ---- 1) transcript: observe trace, sample constraint alpha ----
|
|
ch = CH.DuplexChallenger()
|
|
ch.observe_slice(trace_commit)
|
|
alpha_c = ch.sample_ext()
|
|
|
|
# ---- 2) observe quotient, sample DEEP point zeta ----
|
|
ch.observe_slice(quotient_commit)
|
|
zeta = ch.sample_ext()
|
|
g_trace = F.two_adic_generator(degree_bits)
|
|
zeta_next = F.ext_mul(zeta, F.ext_from_base(g_trace))
|
|
|
|
# ---- 3) PCS.verify ----
|
|
alpha_fri = ch.sample_ext() # batch-combining challenge (distinct from the constraint alpha)
|
|
|
|
# verify_shape_and_sample_challenges (p3-fri verifier.rs)
|
|
betas = []
|
|
for comm in commit_phase_commits:
|
|
ch.observe_slice(comm)
|
|
betas.append(ch.sample_ext())
|
|
ch.observe_ext(final_poly)
|
|
if len(query_proofs) != num_queries:
|
|
return False
|
|
if not ch.check_witness(pow_bits, pow_witness):
|
|
return False
|
|
query_indices = [ch.sample_bits(log_max_height) for _ in range(num_queries)]
|
|
|
|
# rounds structure (p3-uni-stark verifier.rs pcs.verify call):
|
|
# batch 0 = trace : 1 matrix of size 2^degree_bits, opened at [(zeta, trace_local), (zeta_next, trace_next)]
|
|
# batch 1 = quotient : quotient_degree matrices of size 2^degree_bits, opened at [(zeta, chunk_i)]
|
|
trace_size = 1 << degree_bits
|
|
rounds = [
|
|
(trace_commit, [(trace_size, [(zeta, trace_local), (zeta_next, trace_next)])]),
|
|
(quotient_commit, [(trace_size, [(zeta, quotient_chunks[i])]) for i in range(quotient_degree)]),
|
|
]
|
|
|
|
# verify_challenges: for each query, build the reduced openings, then FRI verify_query.
|
|
for qi in range(num_queries):
|
|
index = query_indices[qi]
|
|
per_query_batches = query_openings[qi]
|
|
if len(per_query_batches) != len(rounds):
|
|
return False
|
|
|
|
# ro / alpha_pow indexed by log_height (p3-fri two_adic_pcs.rs::verify)
|
|
ro = [[0, 0, 0, 0] for _ in range(log_max_height + 2)]
|
|
alpha_pow = [F.ext_from_base(1) for _ in range(log_max_height + 2)]
|
|
|
|
for bi, (batch_commit, mats) in enumerate(rounds):
|
|
batch = per_query_batches[bi]
|
|
opened_values = [list(r) for r in batch["opened_values"]] # per matrix: base LDE row
|
|
opening_proof = [list(d) for d in batch["opening_proof"]] # sibling digest path
|
|
|
|
if tamper == "input_batch" and qi == 0 and bi == 0:
|
|
opened_values[0][0] = (opened_values[0][0] + 1) % P
|
|
|
|
batch_heights = [ms << log_blowup for (ms, _) in mats]
|
|
batch_dims = [(0, h) for h in batch_heights] # MMCS ignores width; heights drive the tree
|
|
log_batch_max_height = _log2_exact(max(batch_heights))
|
|
bits_reduced = log_max_height - log_batch_max_height
|
|
reduced_index = index >> bits_reduced
|
|
|
|
# input-batch MMCS opening (component (c)): recompute the batch root, compare to batch_commit.
|
|
if not M.verify_batch(batch_commit, batch_dims, reduced_index, opened_values, opening_proof):
|
|
return False
|
|
|
|
for mat_opening, (ms, points) in zip(opened_values, mats):
|
|
log_height = _log2_exact(ms) + log_blowup
|
|
br2 = log_max_height - log_height
|
|
rev = FRI.reverse_bits_len(index >> br2, log_height)
|
|
x = F.mul(F.GENERATOR, F.pow_(F.two_adic_generator(log_height), rev)) # LDE coset point (base)
|
|
x_ext = F.ext_from_base(x)
|
|
for z, ps_at_z in points:
|
|
if len(mat_opening) != len(ps_at_z):
|
|
return False
|
|
for p_at_x, p_at_z in zip(mat_opening, ps_at_z):
|
|
# quotient = (p_at_x - p_at_z) / (x - z), in F_{p^4}
|
|
num = F.ext_sub(F.ext_from_base(p_at_x), list(p_at_z))
|
|
den = F.ext_sub(x_ext, z)
|
|
q = F.ext_mul(num, F.ext_inv(den))
|
|
ro[log_height] = F.ext_add(ro[log_height], F.ext_mul(alpha_pow[log_height], q))
|
|
alpha_pow[log_height] = F.ext_mul(alpha_pow[log_height], alpha_fri)
|
|
|
|
# FRI verify_query over the CONFIRMED fold + commit-phase MMCS opening (components (d),(c)).
|
|
layers = []
|
|
for st in query_proofs[qi]["commit_phase_openings"]:
|
|
layers.append({
|
|
"sibling_value": list(st["sibling_value"]),
|
|
"opening_proof": [list(d) for d in st["opening_proof"]],
|
|
})
|
|
if tamper == "fri_layer" and qi == 0:
|
|
layers[0]["sibling_value"][0] = (layers[0]["sibling_value"][0] + 1) % P
|
|
|
|
folded = FRI.verify_query(log_blowup, log_max_height, commit_phase_commits, betas,
|
|
index, ro, layers)
|
|
if folded is None:
|
|
return False
|
|
if folded != final_poly:
|
|
return False
|
|
|
|
# ---- 4) AIR quotient consistency (component (e)) with the constraint alpha + zeta ----
|
|
is_first, is_last, is_transition, inv_zeroifier = AQ.selectors_at_point(degree_bits, zeta)
|
|
quotient = AQ.reconstruct_quotient(degree_bits, quotient_chunks, zeta)
|
|
folder = AQ.AIR_FOLDERS[air]
|
|
folded_constraints = folder(trace_local, trace_next, pis,
|
|
is_first, is_last, is_transition, alpha_c)
|
|
if not F.ext_eq(F.ext_mul(folded_constraints, inv_zeroifier), quotient):
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
TAMPERS = ("trace_open", "fri_layer", "input_batch", "public_values", "final_poly")
|
|
|
|
|
|
def conformance_kats():
|
|
"""(passed, total, details). (0) tie ground truth to the CONFIRMED Poseidon2 permutation + Val
|
|
generator; (1) ACCEPT each genuine library-accepted proof end to end; (2) REJECT each tamper."""
|
|
with open(GROUND_TRUTH) as fp:
|
|
gt = json.load(fp)
|
|
|
|
passed = 0
|
|
total = 0
|
|
details = []
|
|
|
|
def check(name, cond):
|
|
nonlocal passed, total
|
|
total += 1
|
|
if cond:
|
|
passed += 1
|
|
details.append({"check": name, "pass": bool(cond)})
|
|
|
|
# (0) sanity: same permutation + generator the CONFIRMED components use.
|
|
check("perm_zeros_matches_poseidon2", P2.permute([0] * 16) == gt["perm_zeros"])
|
|
check("val_generator_is_31", gt["val_generator"] == F.GENERATOR == 31)
|
|
|
|
for case in gt["cases"]:
|
|
name = case["name"]
|
|
check(f"{name}.library_accept", case["library_accept"] is True)
|
|
# (1) genuine proof accepts end to end.
|
|
check(f"{name}.e2e_accept", verify_stark(case) is True)
|
|
# (2) each tamper is rejected.
|
|
for t in TAMPERS:
|
|
check(f"{name}.reject[{t}]", verify_stark(case, t) is False)
|
|
|
|
return passed, total, details
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) > 1 and sys.argv[1] == "kat":
|
|
p, t, det = conformance_kats()
|
|
for d in det:
|
|
print(("PASS" if d["pass"] else "FAIL"), d["check"])
|
|
print(f"conformance: PASS={p} FAIL={t - p} TOTAL={t}")
|
|
sys.exit(0 if p == t else 1)
|
|
# default: emit the accept/reject matrix as JSON
|
|
with open(GROUND_TRUTH) as fp:
|
|
gt = json.load(fp)
|
|
out = {"cases": []}
|
|
for case in gt["cases"]:
|
|
out["cases"].append({
|
|
"name": case["name"],
|
|
"accept": verify_stark(case),
|
|
"rejects": {t: (not verify_stark(case, t)) for t in TAMPERS},
|
|
})
|
|
json.dump(out, sys.stdout, indent=2)
|