risc0-ed25519-verified/verification/check.sh

202 lines
8 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# check.sh — THE button. Compiles EVERY shipped .lean file and axiom-audits
# EVERY layer certificate. If a file is in this repo, this script checks it;
# if this script doesn't check it, it must not be in the repo.
#
# Phases:
# 0. resource + source-integrity guards
# 1. stub audit: no `by trivial` specs, no True-target theorems, and — the
# anti-axiom-smuggling gate — ZERO `axiom` declarations under Proofs/
# (external models in gen/ are the only sanctioned axiom site)
# 2. compile gen/ + Proofs/ in dependency order (explicit -o, capped cores,
# per-file timeout). Any "declaration uses 'sorry'" warning is a FAILURE
# (this catches sorry robustly — text greps can't, comments mention it).
# 3. axiom audit: #print axioms for every certificate in CERTS; each must
# report exactly [propext, Classical.choice, Quot.sound]
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
source ~/aeneas-toolchain/env.sh
HERE="$(cd "$(dirname "$0")" && pwd)"
AENEAS_LEAN="$AENEAS_HOME/backends/lean"
TIMEOUT="${LEAN_TIMEOUT:-300}"
export LEAN_MEM_MB="${LEAN_MEM_MB:-8192}" # 8192: ReduceSpec exceeds 6144 (coherence pass 2)
CORES="${LEAN_MAX_CORES:-0-3}"
# Layer manifests (extended as the pyramid grows; ORDER = import order).
GEN_MODULES=(
CurveField/TypesExternal
CurveField/Types
CurveField/FunsExternal
CurveField/Funs
CurveSig/TypesExternal
CurveSig/Types
CurveSig/FunsExternal
CurveSig/Funs
)
PROOFS=(
Denote
P25519
ReduceSpec
SubNegSpec
ConstSpecs
AddSpec
MulSpec
SquareSpec
Square2Spec
Field
InvertSpec
FieldMain
FeQ
EdCurve
EdDenote
EdDouble
EdAddProjNiels
EdAddAffNiels
EdConvert
EdMain
Double-scalar-mul proof campaign, bricks 1-3: table, digit step, loop Three new proof files over the CurveField extraction, composing the proven group-law layer (no new axioms, no associativity assumed — computational layering over the abstract `edAdd`): - `Proofs/DsmTableSpec.lean` — `NafLookupTable5::from(&A)`: the 8 entries are valid `ProjectiveNielsPoint` caches of valid on-curve points denoting the odd multiples A, 3A, ..., 15A as the `edOdd` double-and-add recursion. 7 explicit loop peels over edwards_as_projective_niels_spec / add_projniels_law / compl_as_extended_law, seeded by edwards_double_law. `select`: both masserts (x odd, x < 16) DISCHARGED — panic-freedom is proven, not assumed; post enumerates all 8 digit cases. - `Proofs/DsmStepSpec.lean` — `proj_double_law` (the projective doubling denotes `edAdd P P`; same Z^2-scaled linear_combination discipline as the extended-coordinate law), `compl_as_projective_law` ((X:Z),(Y:T) to (XT:YZ:ZT) preserves the point), `naf_select_entry` (digit-indexed lookup returns THE entry: NafEntryOf r A ((x-1)/2)), and `dsm_step_p_law` / `dsm_step_b_law`: the three-way NAF digit step denotes `edDigit` — add the d-th odd multiple, add its negation, or pass through. - `Proofs/DsmLoopSpec.lean` — the 256-iteration Straus loop by GENUINE induction on the counter (one symbolic body walk, no unrolling): `dsm_loop_spec` — from the identity, the loop returns a valid on-curve point denoting `dsmFold ... edId 256`, the abstract double-and-add fold of both digit arrays over the table points. Digit and table hypotheses are exactly what the NAF spec and naf_table_spec provide (layering). check.sh wired: PROOFS + AUDIT_IMPORTS + 7 new CERTS (naf_table_spec, naf_select_spec, proj_double_law, compl_as_projective_law, dsm_step_p_law, dsm_step_b_law, dsm_loop_spec), each `#print axioms`-audited to exactly [propext, Classical.choice, Quot.sound]. Full check.sh green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 13:07:57 +00:00
DsmTableSpec
DsmStepSpec
DsmLoopSpec
NAF encoder proven end-to-end + the phase-1 double-scalar-mul apex The complete non_adjacent_form(5) verification (four stages): - `Proofs/DsmNafLoadSpec.lean` (generated) — the LE byte-to-word load. - `Proofs/DsmNafMath.lean` — the digit loop's arithmetic core: window-read lemmas (single/cross-word), the exact ZZ invariant steps (Nat.mod_mul telescope), the carry-kill argument from V < 2^253, and the exit theorem. - `Proofs/DsmNafLoopSpec.lean` — the w=5 digit loop by induction on the remaining-bits measure: per-step 64-bit window read (4-way word split), digit write via hcast/wrapping_sub (exact value window - 32*carry', oddness, |d| < 16), invariant carried through even/odd steps. - `Proofs/DsmNafSpec.lean` — the public spec: both entry masserts DISCHARGED; the digits satisfy the NAF conditions and sum naf[k]*2^k = V EXACTLY (integers, no modular slack) for any scalar whose LE byte value V is below 2^253. And the campaign's brick 4, `Proofs/DsmMulSpec.lean`: - `run_basepoint` — the transpiled ED25519_BASEPOINT_POINT is the standard base point: valid extended coordinates (X*Y = Z*T) and the curve equation, kernel-checked via denominator-free 121666-scaled witnesses. Includes the generic witness lemmas fp_mul_eq_of_witness / onCurve_of_witness. - `vartime_double_base_mul_spec` — THE PHASE-1 COMPUTATIONAL SPEC of vartime_double_base::mul: for canonical scalars and a valid on-curve A, the result is valid, on-curve, and denotes dsmFold (naf a) (naf b) (edPt A) edBasePt edId 256 with both digit arrays proven exact NAF encodings. Phase 2 (group semantics [a]A + [b]B) requires Edwards associativity — deferred and documented; nothing assumes it. Also: removed a vestigial pre-re-extraction axiom stub (backend.serial.scalar_mul.vartime_double_base.mul) from FunsExternal — a root-level leftover that shadowed the real namespaced definition during name resolution in proof files. Never referenced by any certificate (the #print-axioms audit guards against that); deleted for hygiene. CERTS += naf_load_spec, naf_exit, naf_digit_loop_spec, non_adjacent_form_spec, run_basepoint, vartime_double_base_mul_spec — each audited to exactly [propext, Classical.choice, Quot.sound]. Full check.sh green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 14:52:08 +00:00
DsmNafLoadSpec
DsmNafMath
DsmNafLoopSpec
DsmNafSpec
DsmMulSpec
SigApexSpec
)
# Fully-qualified certificate names; each must be axiom-clean.
CERTS=(
CurveFieldProofs.fieldImplementation
CurveFieldProofs.edwardsImplementation
Double-scalar-mul proof campaign, bricks 1-3: table, digit step, loop Three new proof files over the CurveField extraction, composing the proven group-law layer (no new axioms, no associativity assumed — computational layering over the abstract `edAdd`): - `Proofs/DsmTableSpec.lean` — `NafLookupTable5::from(&A)`: the 8 entries are valid `ProjectiveNielsPoint` caches of valid on-curve points denoting the odd multiples A, 3A, ..., 15A as the `edOdd` double-and-add recursion. 7 explicit loop peels over edwards_as_projective_niels_spec / add_projniels_law / compl_as_extended_law, seeded by edwards_double_law. `select`: both masserts (x odd, x < 16) DISCHARGED — panic-freedom is proven, not assumed; post enumerates all 8 digit cases. - `Proofs/DsmStepSpec.lean` — `proj_double_law` (the projective doubling denotes `edAdd P P`; same Z^2-scaled linear_combination discipline as the extended-coordinate law), `compl_as_projective_law` ((X:Z),(Y:T) to (XT:YZ:ZT) preserves the point), `naf_select_entry` (digit-indexed lookup returns THE entry: NafEntryOf r A ((x-1)/2)), and `dsm_step_p_law` / `dsm_step_b_law`: the three-way NAF digit step denotes `edDigit` — add the d-th odd multiple, add its negation, or pass through. - `Proofs/DsmLoopSpec.lean` — the 256-iteration Straus loop by GENUINE induction on the counter (one symbolic body walk, no unrolling): `dsm_loop_spec` — from the identity, the loop returns a valid on-curve point denoting `dsmFold ... edId 256`, the abstract double-and-add fold of both digit arrays over the table points. Digit and table hypotheses are exactly what the NAF spec and naf_table_spec provide (layering). check.sh wired: PROOFS + AUDIT_IMPORTS + 7 new CERTS (naf_table_spec, naf_select_spec, proj_double_law, compl_as_projective_law, dsm_step_p_law, dsm_step_b_law, dsm_loop_spec), each `#print axioms`-audited to exactly [propext, Classical.choice, Quot.sound]. Full check.sh green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 13:07:57 +00:00
CurveFieldProofs.naf_table_spec
CurveFieldProofs.naf_select_spec
CurveFieldProofs.proj_double_law
CurveFieldProofs.compl_as_projective_law
CurveFieldProofs.dsm_step_p_law
CurveFieldProofs.dsm_step_b_law
CurveFieldProofs.dsm_loop_spec
NAF encoder proven end-to-end + the phase-1 double-scalar-mul apex The complete non_adjacent_form(5) verification (four stages): - `Proofs/DsmNafLoadSpec.lean` (generated) — the LE byte-to-word load. - `Proofs/DsmNafMath.lean` — the digit loop's arithmetic core: window-read lemmas (single/cross-word), the exact ZZ invariant steps (Nat.mod_mul telescope), the carry-kill argument from V < 2^253, and the exit theorem. - `Proofs/DsmNafLoopSpec.lean` — the w=5 digit loop by induction on the remaining-bits measure: per-step 64-bit window read (4-way word split), digit write via hcast/wrapping_sub (exact value window - 32*carry', oddness, |d| < 16), invariant carried through even/odd steps. - `Proofs/DsmNafSpec.lean` — the public spec: both entry masserts DISCHARGED; the digits satisfy the NAF conditions and sum naf[k]*2^k = V EXACTLY (integers, no modular slack) for any scalar whose LE byte value V is below 2^253. And the campaign's brick 4, `Proofs/DsmMulSpec.lean`: - `run_basepoint` — the transpiled ED25519_BASEPOINT_POINT is the standard base point: valid extended coordinates (X*Y = Z*T) and the curve equation, kernel-checked via denominator-free 121666-scaled witnesses. Includes the generic witness lemmas fp_mul_eq_of_witness / onCurve_of_witness. - `vartime_double_base_mul_spec` — THE PHASE-1 COMPUTATIONAL SPEC of vartime_double_base::mul: for canonical scalars and a valid on-curve A, the result is valid, on-curve, and denotes dsmFold (naf a) (naf b) (edPt A) edBasePt edId 256 with both digit arrays proven exact NAF encodings. Phase 2 (group semantics [a]A + [b]B) requires Edwards associativity — deferred and documented; nothing assumes it. Also: removed a vestigial pre-re-extraction axiom stub (backend.serial.scalar_mul.vartime_double_base.mul) from FunsExternal — a root-level leftover that shadowed the real namespaced definition during name resolution in proof files. Never referenced by any certificate (the #print-axioms audit guards against that); deleted for hygiene. CERTS += naf_load_spec, naf_exit, naf_digit_loop_spec, non_adjacent_form_spec, run_basepoint, vartime_double_base_mul_spec — each audited to exactly [propext, Classical.choice, Quot.sound]. Full check.sh green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 14:52:08 +00:00
CurveFieldProofs.naf_load_spec
CurveFieldProofs.naf_exit
CurveFieldProofs.naf_digit_loop_spec
CurveFieldProofs.non_adjacent_form_spec
CurveFieldProofs.run_basepoint
CurveFieldProofs.vartime_double_base_mul_spec
CurveFieldProofs.verify_loop_full
)
# Imports needed so every certificate in CERTS is in scope for the audit.
AUDIT_IMPORTS=(
Proofs.FieldMain
Proofs.EdMain
Double-scalar-mul proof campaign, bricks 1-3: table, digit step, loop Three new proof files over the CurveField extraction, composing the proven group-law layer (no new axioms, no associativity assumed — computational layering over the abstract `edAdd`): - `Proofs/DsmTableSpec.lean` — `NafLookupTable5::from(&A)`: the 8 entries are valid `ProjectiveNielsPoint` caches of valid on-curve points denoting the odd multiples A, 3A, ..., 15A as the `edOdd` double-and-add recursion. 7 explicit loop peels over edwards_as_projective_niels_spec / add_projniels_law / compl_as_extended_law, seeded by edwards_double_law. `select`: both masserts (x odd, x < 16) DISCHARGED — panic-freedom is proven, not assumed; post enumerates all 8 digit cases. - `Proofs/DsmStepSpec.lean` — `proj_double_law` (the projective doubling denotes `edAdd P P`; same Z^2-scaled linear_combination discipline as the extended-coordinate law), `compl_as_projective_law` ((X:Z),(Y:T) to (XT:YZ:ZT) preserves the point), `naf_select_entry` (digit-indexed lookup returns THE entry: NafEntryOf r A ((x-1)/2)), and `dsm_step_p_law` / `dsm_step_b_law`: the three-way NAF digit step denotes `edDigit` — add the d-th odd multiple, add its negation, or pass through. - `Proofs/DsmLoopSpec.lean` — the 256-iteration Straus loop by GENUINE induction on the counter (one symbolic body walk, no unrolling): `dsm_loop_spec` — from the identity, the loop returns a valid on-curve point denoting `dsmFold ... edId 256`, the abstract double-and-add fold of both digit arrays over the table points. Digit and table hypotheses are exactly what the NAF spec and naf_table_spec provide (layering). check.sh wired: PROOFS + AUDIT_IMPORTS + 7 new CERTS (naf_table_spec, naf_select_spec, proj_double_law, compl_as_projective_law, dsm_step_p_law, dsm_step_b_law, dsm_loop_spec), each `#print axioms`-audited to exactly [propext, Classical.choice, Quot.sound]. Full check.sh green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 13:07:57 +00:00
Proofs.DsmTableSpec
Proofs.DsmStepSpec
Proofs.DsmLoopSpec
NAF encoder proven end-to-end + the phase-1 double-scalar-mul apex The complete non_adjacent_form(5) verification (four stages): - `Proofs/DsmNafLoadSpec.lean` (generated) — the LE byte-to-word load. - `Proofs/DsmNafMath.lean` — the digit loop's arithmetic core: window-read lemmas (single/cross-word), the exact ZZ invariant steps (Nat.mod_mul telescope), the carry-kill argument from V < 2^253, and the exit theorem. - `Proofs/DsmNafLoopSpec.lean` — the w=5 digit loop by induction on the remaining-bits measure: per-step 64-bit window read (4-way word split), digit write via hcast/wrapping_sub (exact value window - 32*carry', oddness, |d| < 16), invariant carried through even/odd steps. - `Proofs/DsmNafSpec.lean` — the public spec: both entry masserts DISCHARGED; the digits satisfy the NAF conditions and sum naf[k]*2^k = V EXACTLY (integers, no modular slack) for any scalar whose LE byte value V is below 2^253. And the campaign's brick 4, `Proofs/DsmMulSpec.lean`: - `run_basepoint` — the transpiled ED25519_BASEPOINT_POINT is the standard base point: valid extended coordinates (X*Y = Z*T) and the curve equation, kernel-checked via denominator-free 121666-scaled witnesses. Includes the generic witness lemmas fp_mul_eq_of_witness / onCurve_of_witness. - `vartime_double_base_mul_spec` — THE PHASE-1 COMPUTATIONAL SPEC of vartime_double_base::mul: for canonical scalars and a valid on-curve A, the result is valid, on-curve, and denotes dsmFold (naf a) (naf b) (edPt A) edBasePt edId 256 with both digit arrays proven exact NAF encodings. Phase 2 (group semantics [a]A + [b]B) requires Edwards associativity — deferred and documented; nothing assumes it. Also: removed a vestigial pre-re-extraction axiom stub (backend.serial.scalar_mul.vartime_double_base.mul) from FunsExternal — a root-level leftover that shadowed the real namespaced definition during name resolution in proof files. Never referenced by any certificate (the #print-axioms audit guards against that); deleted for hygiene. CERTS += naf_load_spec, naf_exit, naf_digit_loop_spec, non_adjacent_form_spec, run_basepoint, vartime_double_base_mul_spec — each audited to exactly [propext, Classical.choice, Quot.sound]. Full check.sh green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 14:52:08 +00:00
Proofs.DsmNafSpec
Proofs.DsmMulSpec
Proofs.SigApexSpec
)
# ── Phase 0: resource + integrity guards ────────────────────────────────────
free -m | awk '/Mem:/{if($7<2048){print "FATAL: <2GB RAM available — refusing to compile"; exit 1}}'
echo "=== Phase 0: source integrity ==="
for f in "$HERE"/gen/CurveField/*.lean "$HERE"/Proofs/*.lean; do
[ -f "$f" ] || continue
if ! grep -qE '^(/-|import |namespace |theorem |def |open |set_option |--)' "$f"; then
echo "CORRUPTED: $f is not Lean source (olean clobber?). Restore: git checkout HEAD -- $f"
exit 1
fi
done
echo " all sources valid"
# ── Phase 1: stub + axiom-smuggling audit ───────────────────────────────────
echo "=== Phase 1: stub audit ==="
if grep -rn 'by trivial' "$HERE"/Proofs/*Spec*.lean 2>/dev/null; then
echo "STUB DETECTED: 'by trivial' in spec files"; exit 1; fi
if grep -rn ' : True :=' "$HERE"/Proofs/*.lean 2>/dev/null; then
echo "STUB DETECTED: True-target theorem"; exit 1; fi
if grep -rnE '^(private |protected |noncomputable )*axiom ' "$HERE"/Proofs/*.lean 2>/dev/null; then
echo "AXIOM SMUGGLING DETECTED: axiom declaration under Proofs/ — forbidden."
echo "External models belong in gen/*/FunsExternal.lean and must stay outside"
echo "every certificate's dependency cone (Phase 3 verifies that)."
exit 1
fi
echo " clean: no trivial stubs, no True targets, no axioms outside gen/"
# ── Phase 2: compile everything shipped ─────────────────────────────────────
echo "=== Phase 2: compile ==="
LOG=$(mktemp /tmp/check-compile-XXXX.log)
cd "$AENEAS_LEAN"
lake env bash -c "
set -euo pipefail
cd '$HERE/gen' && export LEAN_PATH=\"\$LEAN_PATH:\$PWD:$HERE\"
compile() {
echo \" · \$1\"
LEAN_TIMEOUT=$TIMEOUT LEAN_MAX_CORES=$CORES '$HERE/lean-guard' \"\${1}.lean\" 2>&1 | tee -a '$LOG' || { echo \"FAIL: \$1\"; exit 1; }
}
for m in ${GEN_MODULES[*]}; do compile \"\$m\"; done
cd '$HERE'
for m in ${PROOFS[*]}; do
[ -f \"Proofs/\$m.lean\" ] || { echo \"MISSING: Proofs/\$m.lean listed in manifest\"; exit 1; }
compile \"Proofs/\$m\"
done
# every shipped proof file must be in the manifest (no dead files)
for f in Proofs/*.lean; do
b=\$(basename \"\$f\" .lean)
[ \"\$b\" = AxiomCheck ] && continue
case \"\$b\" in Scalar*) continue;; esac # scalar layer: checked by check-scalar.sh (coherence pass 2)
case \" ${PROOFS[*]} \" in (*\" \$b \"*) ;; (*) echo \"DEAD FILE: \$f not in check manifest\"; exit 1;; esac
done
"
if grep -q "uses 'sorry'" "$LOG"; then
echo "STUB DETECTED: a compiled declaration uses 'sorry'"; exit 1; fi
rm -f "$LOG"
# ── Phase 3: axiom audit of every certificate ───────────────────────────────
echo "=== Phase 3: axiom audit ==="
EXPECTED="[propext, Classical.choice, Quot.sound]"
cd "$AENEAS_LEAN"
lake env bash -c "
set -euo pipefail
cd '$HERE/gen' && export LEAN_PATH=\"\$LEAN_PATH:\$PWD:$HERE\"
cd '$HERE'
AUD=\$(mktemp '$HERE/.audit-XXXX.lean')
{
for i in ${AUDIT_IMPORTS[*]}; do echo \"import \$i\"; done
for c in ${CERTS[*]}; do echo \"#print axioms \$c\"; done
} > \"\$AUD\"
OUT=\$(LEAN_TIMEOUT=$TIMEOUT LEAN_MEM_MB=4096 '$HERE/lean-guard' \"\$AUD\" 2>&1)
echo \"\$OUT\"
rm -f \"\$AUD\"
N_CLEAN=\$(echo \"\$OUT\" | grep -cF \"depends on axioms: $EXPECTED\" || true)
if [ \"\$N_CLEAN\" -ne ${#CERTS[@]} ]; then
echo \"AXIOM AUDIT FAILED: \$N_CLEAN/${#CERTS[@]} certificates clean\"
exit 1
fi
"
echo "=== Phase 3b: signature-apex audit (SHA-512 + wire-format boundary) ==="
# The verification-equation apex is grounded in the PROVEN curve model; its
# only axioms beyond the standard three are the deliberate, documented
# boundary: the SHA-512 hash oracle and the opaque wire-format types.
# NO curve axioms, NO scalar axioms, NO backend-dispatch axioms.
cd "$AENEAS_LEAN"
lake env bash -c "
set -euo pipefail
cd '$HERE/gen' && export LEAN_PATH=\"\$LEAN_PATH:\$PWD:$HERE\"
cd '$HERE'
ALLOWED='[propext, Classical.choice, Quot.sound, ed25519.Signature, verifying.sha512_hash3, ed25519.Signature.to_bytes, signature.error.Error, signature.error.Error.new]'
AUD=\$(mktemp '$HERE/.apex-XXXX.lean')
{ echo 'import Proofs.SigApexSpec'; echo '#print axioms CurveFieldProofs.verify_accepts_iff'; } > \"\$AUD\"
OUT=\$(LEAN_TIMEOUT=$TIMEOUT LEAN_MEM_MB=4096 '$HERE/lean-guard' \"\$AUD\" 2>&1)
echo \"\$OUT\"
rm -f \"\$AUD\"
FLAT=\$(echo \"\$OUT\" | tr '\\n' ' ' | tr -s ' ')
if echo \"\$FLAT\" | grep -qF \"depends on axioms: \$ALLOWED\"; then
echo ' apex axiom cone = exactly the SHA-512 + wire-format boundary (no curve/scalar/backend axioms)'
else
echo 'APEX AUDIT FAILED: verify_accepts_iff cone is not the documented boundary'; exit 1
fi
"
echo ""
echo "ALL PROOFS PASS. ALL CERTIFICATES AXIOM-CLEAN. NO DEAD FILES."