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.
320 lines
12 KiB
Java
320 lines
12 KiB
Java
// Standalone (no-Besu-classpath) copy of the BabyBear field + degree-4 extension arithmetic used by
|
|
// the PQ STARK-verify precompile 0x0AE8, plus a self-test. It is a verbatim mirror of the
|
|
// Sp1StarkVerifierPrecompiledContract.BabyBear / BabyBearExt4 logic (same P, same W, same reduction),
|
|
// compilable and runnable without the Besu EVM jars so the field layer can be exercised offline and
|
|
// cross-checked against the Python and Node references.
|
|
//
|
|
// HONEST SCOPE. This is field arithmetic, the FOUNDATION component (a) of the six-component STARK
|
|
// port (docs/AERE-STARK-VERIFIER-PORT-SPEC.md section 2). It verifies NOTHING about a real proof;
|
|
// the top-level 0x0AE8 precompile stays fail-closed. GENERATOR = 31 and W = 11 are proven here to be
|
|
// a real generator and a real non-residue (so F_{p^4} is a field), but that they are Plonky3's exact
|
|
// chosen constants is [VERIFY] against p3-baby-bear at the pinned SP1 v6.1.0 revision.
|
|
//
|
|
// Usage:
|
|
// javac -d out BabyBearFieldSelfTest.java
|
|
// java -cp out BabyBearFieldSelfTest # run the self-test (prints PASS/FAIL counts)
|
|
// java -cp out BabyBearFieldSelfTest --emit # emit the shared cross-language vector set (JSON)
|
|
|
|
import java.math.BigInteger;
|
|
import java.util.Random;
|
|
|
|
public final class BabyBearFieldSelfTest {
|
|
|
|
static final long P = 2013265921L; // 2^31 - 2^27 + 1 = 15 * 2^27 + 1 = 0x78000001
|
|
static final long GENERATOR = 31L; // [VERIFY] Plonky3 generator; order proven = P-1
|
|
static final int TWO_ADICITY = 27;
|
|
static final long W = 11L; // [VERIFY] Plonky3 BabyBear quartic non-residue; irreducibility proven
|
|
static final BigInteger BP = BigInteger.valueOf(P);
|
|
|
|
// ---- base field F_p ----
|
|
static long reduce(final long a) {
|
|
long m = a % P;
|
|
if (m < 0) m += P;
|
|
return m;
|
|
}
|
|
|
|
static long add(final long a, final long b) {
|
|
long s = a + b;
|
|
if (s >= P) s -= P;
|
|
return s;
|
|
}
|
|
|
|
static long sub(final long a, final long b) {
|
|
long s = a - b;
|
|
if (s < 0) s += P;
|
|
return s;
|
|
}
|
|
|
|
static long neg(final long a) {
|
|
final long r = reduce(a);
|
|
return r == 0 ? 0 : P - r;
|
|
}
|
|
|
|
static long mul(final long a, final long b) {
|
|
return (reduce(a) * reduce(b)) % P; // both < 2^31 so product < 2^62, safe in signed 64-bit
|
|
}
|
|
|
|
static long pow(final long base, long e) {
|
|
long b = reduce(base);
|
|
long acc = 1L;
|
|
while (e > 0) {
|
|
if ((e & 1L) == 1L) acc = mul(acc, b);
|
|
b = mul(b, b);
|
|
e >>= 1;
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
static long inv(final long a) {
|
|
final long r = reduce(a);
|
|
if (r == 0) return 0; // inv(0) := 0, callers guard div-by-zero
|
|
return pow(r, P - 2);
|
|
}
|
|
|
|
static long twoAdicGenerator(final int bits) {
|
|
if (bits < 0 || bits > TWO_ADICITY) throw new IllegalArgumentException("bits out of range");
|
|
return pow(GENERATOR, (P - 1) >> bits);
|
|
}
|
|
|
|
/** Exact multiplicative order using the known factorization p-1 = 2^27 * 3 * 5. */
|
|
static long multiplicativeOrder(final long a) {
|
|
final long r = reduce(a);
|
|
if (r == 0) throw new IllegalArgumentException("0 has no order");
|
|
long order = P - 1;
|
|
final int[] primes = {2, 3, 5};
|
|
for (final int q : primes) {
|
|
while (order % q == 0 && pow(r, order / q) == 1) order /= q;
|
|
}
|
|
return order;
|
|
}
|
|
|
|
static boolean isQuadraticNonResidue(final long a) {
|
|
return pow(reduce(a), (P - 1) >> 1) == P - 1;
|
|
}
|
|
|
|
// ---- degree-4 extension F_{p^4} = F_p[x]/(x^4 - W); elements are long[4] = c0 + c1 x + c2 x^2 + c3 x^3 ----
|
|
static long[] extFromBase(final long a) {
|
|
return new long[] {reduce(a), 0, 0, 0};
|
|
}
|
|
|
|
static long[] extAdd(final long[] a, final long[] b) {
|
|
return new long[] {add(a[0], b[0]), add(a[1], b[1]), add(a[2], b[2]), add(a[3], b[3])};
|
|
}
|
|
|
|
static long[] extSub(final long[] a, final long[] b) {
|
|
return new long[] {sub(a[0], b[0]), sub(a[1], b[1]), sub(a[2], b[2]), sub(a[3], b[3])};
|
|
}
|
|
|
|
static long[] extNeg(final long[] a) {
|
|
return new long[] {neg(a[0]), neg(a[1]), neg(a[2]), neg(a[3])};
|
|
}
|
|
|
|
/** Schoolbook convolution reduced by x^4 = W (identical to the precompile BabyBearExt4.mul). */
|
|
static long[] extMul(final long[] a, final long[] b) {
|
|
final long[] t = new long[7];
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < 4; j++) {
|
|
t[i + j] = add(t[i + j], mul(a[i], b[j]));
|
|
}
|
|
}
|
|
return new long[] {
|
|
add(t[0], mul(W, t[4])), add(t[1], mul(W, t[5])), add(t[2], mul(W, t[6])), reduce(t[3])
|
|
};
|
|
}
|
|
|
|
static long[] extPow(final long[] a, final BigInteger e) {
|
|
if (e.signum() < 0) throw new IllegalArgumentException("use extInv for negative exponents");
|
|
long[] base = new long[] {reduce(a[0]), reduce(a[1]), reduce(a[2]), reduce(a[3])};
|
|
long[] acc = new long[] {1, 0, 0, 0};
|
|
BigInteger ee = e;
|
|
while (ee.signum() > 0) {
|
|
if (ee.testBit(0)) acc = extMul(acc, base);
|
|
base = extMul(base, base);
|
|
ee = ee.shiftRight(1);
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
/** Frobenius pi(a) = a^p; Frobenius^4 == id and fixes the base field. */
|
|
static long[] extFrobenius(final long[] a) {
|
|
return extPow(a, BP);
|
|
}
|
|
|
|
static long[] extInv(final long[] a) {
|
|
if (reduce(a[0]) == 0 && reduce(a[1]) == 0 && reduce(a[2]) == 0 && reduce(a[3]) == 0) {
|
|
return new long[] {0, 0, 0, 0};
|
|
}
|
|
return extPow(a, BP.pow(4).subtract(BigInteger.TWO)); // a^(p^4 - 2)
|
|
}
|
|
|
|
static boolean extEq(final long[] a, final long[] b) {
|
|
return reduce(a[0]) == reduce(b[0]) && reduce(a[1]) == reduce(b[1])
|
|
&& reduce(a[2]) == reduce(b[2]) && reduce(a[3]) == reduce(b[3]);
|
|
}
|
|
|
|
// ================================= self-test =================================
|
|
static int pass = 0;
|
|
static int fail = 0;
|
|
|
|
static void check(final String name, final boolean cond) {
|
|
if (cond) pass++;
|
|
else {
|
|
fail++;
|
|
System.out.println("FAIL " + name);
|
|
}
|
|
}
|
|
|
|
static void selfTest() {
|
|
// constants
|
|
check("P.value", P == (1L << 31) - (1L << 27) + 1 && P == 15L * (1L << 27) + 1 && P == 0x78000001L);
|
|
// generator has full order p-1 (real proof, not a Plonky3 claim)
|
|
check("generator.order", multiplicativeOrder(GENERATOR) == P - 1);
|
|
// two-adic generator order is exactly 2^27
|
|
final long tag = twoAdicGenerator(27);
|
|
check("twoadic.order", pow(tag, 1L << 27) == 1 && pow(tag, 1L << 26) != 1);
|
|
for (int bits = 1; bits <= 12; bits++) {
|
|
final long g = twoAdicGenerator(bits);
|
|
check("twoadic.order.b" + bits, pow(g, 1L << bits) == 1 && pow(g, 1L << (bits - 1)) != 1);
|
|
}
|
|
// W = 11 is a QNR and p == 1 mod 4 => x^4 - W irreducible => F_{p^4} is a field
|
|
check("W.qnr", isQuadraticNonResidue(W));
|
|
check("p.mod4", P % 4 == 1);
|
|
// x^4 == W in the extension (defining relation)
|
|
final long[] x = {0, 1, 0, 0};
|
|
check("x4.eq.W", extEq(extPow(x, BigInteger.valueOf(4)), extFromBase(W)));
|
|
|
|
final Random rnd = new Random(0x0AE8);
|
|
for (int it = 0; it < 20000; it++) {
|
|
final long a = Math.floorMod(rnd.nextLong(), P);
|
|
final long b = Math.floorMod(rnd.nextLong(), P);
|
|
final long c = Math.floorMod(rnd.nextLong(), P);
|
|
// base axioms
|
|
check("add.comm", add(a, b) == add(b, a));
|
|
check("mul.comm", mul(a, b) == mul(b, a));
|
|
check("distrib", mul(a, add(b, c)) == add(mul(a, b), mul(a, c)));
|
|
check("add.assoc", add(add(a, b), c) == add(a, add(b, c)));
|
|
check("sub.def", add(sub(a, b), b) == a);
|
|
check("neg.def", add(a, neg(a)) == 0);
|
|
if (a != 0) {
|
|
check("inv.def", mul(a, inv(a)) == 1);
|
|
check("fermat", pow(a, P - 1) == 1); // Fermat little theorem
|
|
}
|
|
check("frobenius.base", pow(a, P) == a); // a^p == a on the base field
|
|
// (p-1) behaves as -1
|
|
check("minus1.sq", mul(P - 1, P - 1) == 1);
|
|
check("minus1.add", add(P - 1, 1) == 0);
|
|
|
|
// extension axioms
|
|
final long[] ea = {a, b, c, add(a, c)};
|
|
final long[] eb = {b, c, a, sub(b, a)};
|
|
check("ext.add.comm", extEq(extAdd(ea, eb), extAdd(eb, ea)));
|
|
check("ext.mul.comm", extEq(extMul(ea, eb), extMul(eb, ea)));
|
|
check("ext.sub.def", extEq(extAdd(extSub(ea, eb), eb), ea));
|
|
check("ext.neg.def", extEq(extAdd(ea, extNeg(ea)), new long[] {0, 0, 0, 0}));
|
|
final boolean nz = a != 0 || b != 0 || c != 0 || add(a, c) != 0;
|
|
if (nz) {
|
|
check("ext.inv.def", extEq(extMul(ea, extInv(ea)), new long[] {1, 0, 0, 0}));
|
|
}
|
|
// Frobenius^4 == id, and Frobenius fixes base-embedded elements
|
|
long[] f = ea;
|
|
for (int k = 0; k < 4; k++) f = extFrobenius(f);
|
|
check("ext.frob4.id", extEq(f, ea));
|
|
check("ext.frob.base.fix", extEq(extFrobenius(extFromBase(a)), extFromBase(a)));
|
|
}
|
|
|
|
System.out.println("BabyBearFieldSelfTest PASS=" + pass + " FAIL=" + fail);
|
|
if (fail != 0) {
|
|
System.out.println("BABYBEAR_FIELD_SELFTEST_FAILED");
|
|
System.exit(1);
|
|
}
|
|
System.out.println("BABYBEAR_FIELD_SELFTEST_OK");
|
|
}
|
|
|
|
// ================================= shared-vector emit (JSON) =================================
|
|
static final long[] BASE_UNARY = {0, 1, 2, 31, 1000000, 123456789, P - 1};
|
|
static final long[][] BASE_BINARY = {{2, 3}, {P - 1, 1}, {1000000, 999}, {123456789, 987654321}, {0, 5}};
|
|
static final long[][] EXT_UNARY = {
|
|
{1, 0, 0, 0}, {0, 1, 0, 0}, {2, 3, 5, 7}, {P - 1, P - 1, P - 1, P - 1}, {11, 0, 0, 0}, {123, 456, 789, 1011}
|
|
};
|
|
static final long[][][] EXT_BINARY = {
|
|
{{1, 2, 3, 4}, {5, 6, 7, 8}}, {{0, 1, 0, 0}, {0, 1, 0, 0}}, {{2, 3, 5, 7}, {11, 0, 0, 0}}
|
|
};
|
|
static final long POW_E = 12345L;
|
|
|
|
static String jArr(final long[] a) {
|
|
final StringBuilder sb = new StringBuilder("[");
|
|
for (int i = 0; i < a.length; i++) {
|
|
if (i > 0) sb.append(',');
|
|
sb.append(a[i]);
|
|
}
|
|
return sb.append(']').toString();
|
|
}
|
|
|
|
static void emit() {
|
|
final StringBuilder sb = new StringBuilder();
|
|
sb.append('{');
|
|
sb.append("\"constants\":{")
|
|
.append("\"P\":").append(P)
|
|
.append(",\"generator\":").append(GENERATOR)
|
|
.append(",\"twoAdicity\":").append(TWO_ADICITY)
|
|
.append(",\"twoAdicGen27\":").append(twoAdicGenerator(27))
|
|
.append(",\"W\":").append(W)
|
|
.append(",\"powE\":").append(POW_E)
|
|
.append('}');
|
|
sb.append(",\"base_unary\":[");
|
|
for (int i = 0; i < BASE_UNARY.length; i++) {
|
|
final long a = BASE_UNARY[i];
|
|
if (i > 0) sb.append(',');
|
|
sb.append("{\"a\":").append(a)
|
|
.append(",\"neg\":").append(neg(a))
|
|
.append(",\"inv\":").append(inv(a))
|
|
.append(",\"pow\":").append(pow(a, POW_E))
|
|
.append('}');
|
|
}
|
|
sb.append(']');
|
|
sb.append(",\"base_binary\":[");
|
|
for (int i = 0; i < BASE_BINARY.length; i++) {
|
|
final long a = BASE_BINARY[i][0], b = BASE_BINARY[i][1];
|
|
if (i > 0) sb.append(',');
|
|
sb.append("{\"a\":").append(a).append(",\"b\":").append(b)
|
|
.append(",\"add\":").append(add(a, b))
|
|
.append(",\"sub\":").append(sub(a, b))
|
|
.append(",\"mul\":").append(mul(a, b))
|
|
.append('}');
|
|
}
|
|
sb.append(']');
|
|
sb.append(",\"ext_unary\":[");
|
|
for (int i = 0; i < EXT_UNARY.length; i++) {
|
|
final long[] a = EXT_UNARY[i];
|
|
if (i > 0) sb.append(',');
|
|
sb.append("{\"a\":").append(jArr(a))
|
|
.append(",\"neg\":").append(jArr(extNeg(a)))
|
|
.append(",\"inv\":").append(jArr(extInv(a)))
|
|
.append(",\"frob\":").append(jArr(extFrobenius(a)))
|
|
.append('}');
|
|
}
|
|
sb.append(']');
|
|
sb.append(",\"ext_binary\":[");
|
|
for (int i = 0; i < EXT_BINARY.length; i++) {
|
|
final long[] a = EXT_BINARY[i][0], b = EXT_BINARY[i][1];
|
|
if (i > 0) sb.append(',');
|
|
sb.append("{\"a\":").append(jArr(a)).append(",\"b\":").append(jArr(b))
|
|
.append(",\"add\":").append(jArr(extAdd(a, b)))
|
|
.append(",\"sub\":").append(jArr(extSub(a, b)))
|
|
.append(",\"mul\":").append(jArr(extMul(a, b)))
|
|
.append('}');
|
|
}
|
|
sb.append(']');
|
|
sb.append('}');
|
|
System.out.print(sb);
|
|
}
|
|
|
|
public static void main(final String[] args) {
|
|
if (args.length > 0 && args[0].equals("--emit")) {
|
|
emit();
|
|
} else {
|
|
selfTest();
|
|
}
|
|
}
|
|
}
|