mirror of
https://github.com/saymrwulf/fips205-slhdsa-verified.git
synced 2026-09-03 19:53:49 +00:00
The third reviewer demonstrated that the round-2 in-Lean exact-cone audit, though sound for LISTED certs, left three fail-opens OUTSIDE the cone check — and made check.sh print ALL GREEN over a repo proving False. All closed; no theorem, proof, or fold changed (the 11 cones are unchanged). F1 — the audited SET was unbound. Audit.lean now (a) enumerates EVERY theorem defined in the eight certificate modules and requires each cone ⊆ boundary, so an un-manifested `theorem _ : False := cheat _` fails regardless of naming (this is the exact exploit the reviewer used); and (b) prints a MANIFEST fingerprint over the whole committed manifest, which check.sh binds to — so deleting/swapping a cert row fails outside Lean too. F2 — only cones were bound, not statements. Each cert now also carries the structural fingerprint (Expr.hash) of its elaborated type; a statement gutted to a tautology of the same cone changes the fingerprint and fails. F3 — the gen/ model bytes were unbound. New check.sh Phase 0 sha256-pins all four gen/SlhVerify/*.lean (incl. the two hand-maintained *External files, now hashed in PROVENANCE.json) BEFORE compiling; a hand-edited model fails first. F4/F5 — docs. README cone diagram now roots honestly at slh_verify_internal and states the pure/prehash domain-separator byte, the ctx>255 check, M' assembly, and deserialization are ABOVE the root and uncovered (new TRUSTED-BASE item 10). The false "rules out a wrong ADRS field" claim is corrected in README + ChainSpec (a transliteration makes the field visible, not excluded). check-selftest.sh: eight attacks, all rejected (dead file; extra axiom; dropped oracle; vanished cert; un-manifested False theorem; gutted statement; hand-edited model; deleted manifest row). Full transcript + green check.sh in verification/RECORDED-RUN.md. Standing limit unchanged and disclosed: an audit cannot defend against an author who edits the manifest AND check.sh AND the proofs together; the consumer defense is the pinned commit reviewed at the pin. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
139 lines
6.6 KiB
Bash
Executable file
139 lines
6.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# The one-button claim for this repository (rigor invariant R3).
|
|
# Green output == the full claim. This script is the ONLY source of the
|
|
# word "proven" for this repo.
|
|
#
|
|
# Phase 0 — model-byte integrity: every gen/SlhVerify/*.lean must sha256-match
|
|
# PROVENANCE.json (a hand-edited model fails BEFORE it is compiled;
|
|
# round-4 review F3).
|
|
# Phase 1 — compile the extracted Lean model (gen/SlhVerify).
|
|
# Phase 2 — compile the proof files (Proofs/).
|
|
# Phase 3 — the in-Lean audit (Proofs/Audit.lean): each certificate's cone
|
|
# (via `collectAxioms`) must EQUAL its expected set exactly AND its
|
|
# statement fingerprint must match the committed manifest, EVERY
|
|
# theorem in the eight cert modules must have a cone within the
|
|
# boundary (so an un-manifested `: False := cheat _` cannot pass —
|
|
# round-4 F1), and this script binds to the printed MANIFEST
|
|
# fingerprint (round-4 F2/F1). Any mismatch → non-zero exit →
|
|
# fail-closed. No text parsing of axiom cones.
|
|
set -euo pipefail
|
|
|
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
source ~/aeneas-toolchain/env.sh
|
|
AENEAS_LEAN="$AENEAS_HOME/backends/lean"
|
|
TIMEOUT="${LEAN_TIMEOUT:-400}"
|
|
MEM="${LEAN_MEM_MB:-4096}"
|
|
|
|
GEN_MODULES=(
|
|
"SlhVerify/TypesExternal"
|
|
"SlhVerify/Types"
|
|
"SlhVerify/FunsExternal"
|
|
"SlhVerify/Funs"
|
|
)
|
|
# Proof files, in dependency order.
|
|
PROOFS=(
|
|
"ChainSpec"
|
|
"WotsSpec"
|
|
"XmssSpec"
|
|
"HtSpec"
|
|
"ForsInnerSpec"
|
|
"ForsOuterSpec"
|
|
"InputPrepSpec"
|
|
"ApexSpec"
|
|
)
|
|
# Certificates whose axiom cones are audited, and the allowed extras beyond
|
|
# the three kernel axioms: the five SHA-2 verify-path oracles. A certificate
|
|
# is listed here only once it is genuinely proven.
|
|
CERTS=(
|
|
"fips205.chain_free_loop_eq"
|
|
"fips205.wots_loop1_eq"
|
|
"fips205.xmss_loop_eq"
|
|
"fips205.ht_loop_eq"
|
|
"fips205.fors_inner_loop_eq"
|
|
"fips205.fors_outer_loop_eq"
|
|
"fips205.to_int_loop_eq"
|
|
"fips205.to_byte_loop_eq"
|
|
"fips205.wots_csum_loop_eq"
|
|
"fips205.base2b_outer_loop_eq"
|
|
"fips205.slh_verify_128s_accepts_iff"
|
|
)
|
|
echo "fips205-slhdsa-verified — check"
|
|
echo "==============================="
|
|
|
|
# ── Phase 0: model-byte integrity (gen/ == PROVENANCE.json) ──────────────────
|
|
echo "=== Phase 0: model-byte integrity (gen/ pinned to PROVENANCE.json) ==="
|
|
python3 - "$HERE/PROVENANCE.json" "$HERE" <<'PY' || { echo "MODEL INTEGRITY FAILED (a gen/ file differs from PROVENANCE.json — hand-edited model?)"; exit 1; }
|
|
import json, sys, hashlib, os
|
|
prov = json.load(open(sys.argv[1])); here = sys.argv[2]
|
|
files = {k: v for k, v in prov.get("model_integrity_sha256", {}).items() if k.startswith("gen/")}
|
|
if not files:
|
|
print(" no model_integrity_sha256 in PROVENANCE.json (fail-closed)"); sys.exit(1)
|
|
bad = 0
|
|
for rel, want in sorted(files.items()):
|
|
p = os.path.join(here, rel)
|
|
if not os.path.exists(p):
|
|
print(f" ✗ {rel} MISSING"); bad = 1; continue
|
|
got = hashlib.sha256(open(p, 'rb').read()).hexdigest()
|
|
if got != want:
|
|
print(f" ✗ {rel}: sha256 {got[:12]} ≠ pinned {want[:12]}"); bad = 1
|
|
else:
|
|
print(f" ✓ {rel}")
|
|
sys.exit(1 if bad else 0)
|
|
PY
|
|
|
|
# ── Phase 1: model ──────────────────────────────────────────────────────────
|
|
echo "=== Phase 1: compile the extracted model ==="
|
|
cd "$AENEAS_LEAN"
|
|
lake env bash -c "
|
|
set -euo pipefail
|
|
cd '$HERE' && export LEAN_PATH=\"\$LEAN_PATH:\$PWD/gen:\$PWD\"
|
|
compile() { echo \" · \$1\"; LEAN_TIMEOUT=$TIMEOUT LEAN_MEM_MB=$MEM '$HERE/lean-guard' \"\${1}.lean\" >/dev/null || { echo \"FAIL: \$1\"; exit 1; }; }
|
|
for m in ${GEN_MODULES[*]}; do compile \"gen/\$m\"; done
|
|
"
|
|
|
|
# ── Phase 2: proofs ─────────────────────────────────────────────────────────
|
|
echo "=== Phase 2: compile the proofs ==="
|
|
cd "$AENEAS_LEAN"
|
|
lake env bash -c "
|
|
set -euo pipefail
|
|
cd '$HERE' && export LEAN_PATH=\"\$LEAN_PATH:\$PWD/gen:\$PWD\"
|
|
compile() { echo \" · \$1\"; LEAN_TIMEOUT=$TIMEOUT LEAN_MEM_MB=$MEM '$HERE/lean-guard' \"Proofs/\${1}.lean\" >/dev/null || { echo \"FAIL: Proofs/\$1\"; exit 1; }; }
|
|
for m in ${PROOFS[*]}; do compile \"\$m\"; done
|
|
# no dead proof files: everything under Proofs/ must be in the manifest
|
|
# (PROOFS) or be the audit driver (Audit, compiled in Phase 3).
|
|
for f in Proofs/*.lean; do b=\$(basename \"\$f\" .lean)
|
|
case \" ${PROOFS[*]} Audit \" in *\" \$b \"*) ;; *) echo \"DEAD FILE: Proofs/\$b.lean not in manifest\"; exit 1 ;; esac
|
|
done
|
|
"
|
|
|
|
# ── Phase 3: in-Lean audit (cones + statements + full-module enumeration) ────
|
|
echo "=== Phase 3: in-Lean audit (cones + statement fingerprints + enumeration) ==="
|
|
cd "$AENEAS_LEAN"
|
|
# Proofs/Audit.lean checks: per-cert exact cone + statement fingerprint; every
|
|
# theorem in the eight cert modules has a clean cone (no un-manifested axiom
|
|
# smuggle); and it prints a MANIFEST-FINGERPRINT over the whole committed
|
|
# manifest. We bind to that EXACT fingerprint below, so deleting/swapping a cert
|
|
# row, or editing a cone or a statement fingerprint, changes the printed value
|
|
# and fails HERE even if Lean itself exits 0 (round-4 F1/F2 set+statement bind).
|
|
# To rotate the manifest deliberately, recompute it (compile Audit.lean, read the
|
|
# printed value) and update EXPECTED_MANIFEST_FP in the same reviewable commit.
|
|
EXPECTED_MANIFEST_FP="MANIFEST-FINGERPRINT: 13660980750615609973"
|
|
AUD_OUT=$(lake env bash -c "cd '$HERE' && export LEAN_PATH=\"\$LEAN_PATH:\$PWD/gen:\$PWD\" && LEAN_TIMEOUT=$TIMEOUT LEAN_MEM_MB=$MEM '$HERE/lean-guard' 'Proofs/Audit.lean'" 2>&1) || {
|
|
echo "$AUD_OUT" | sed 's/^/ /'
|
|
echo "AUDIT FAILED (Audit.lean did not compile — cone/statement mismatch, missing cert, sham, or un-audited theorem)"; exit 1; }
|
|
if ! grep -qF "exact-cone audit PASSED" <<<"$AUD_OUT"; then
|
|
echo "$AUD_OUT" | sed 's/^/ /'
|
|
echo "AUDIT FAILED (no PASSED line — fail-closed)"; exit 1
|
|
fi
|
|
if ! grep -qF "$EXPECTED_MANIFEST_FP" <<<"$AUD_OUT"; then
|
|
echo "$AUD_OUT" | sed 's/^/ /'
|
|
echo "AUDIT FAILED — manifest fingerprint mismatch."
|
|
echo " expected: $EXPECTED_MANIFEST_FP"
|
|
echo " the certificate set / a cone / a statement fingerprint changed without a reviewed manifest rotation."; exit 1
|
|
fi
|
|
echo " ✓ $(grep -oF 'exact-cone audit PASSED' <<<"$AUD_OUT" | head -1) ($EXPECTED_MANIFEST_FP)"
|
|
|
|
echo
|
|
echo "ALL GREEN — model compiles, proofs compile, and every certificate cone"
|
|
echo "equals EXACTLY the three kernel axioms plus its documented SHA-2 oracles."
|
|
echo "Certificates proven: ${CERTS[*]}"
|