fips205-slhdsa-verified/verification/check.sh

112 lines
4.9 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# The one-button claim for this repository (rigor invariant R3).
2026-07-23 09:34:59 +00:00
# Green output == the full claim. This script is the ONLY source of the
# word "proven" for this repo.
#
2026-07-23 09:34:59 +00:00
# Phase 1 — compile the extracted Lean model (gen/SlhVerify).
# Phase 2 — compile the proof files (Proofs/).
# Phase 3 — axiom audit: every certificate's #print axioms cone must be a
# subset of {propext, Classical.choice, Quot.sound} plus the five
# SHA-2 hash oracles (the documented boundary) — nothing else.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
source ~/aeneas-toolchain/env.sh
AENEAS_LEAN="$AENEAS_HOME/backends/lean"
2026-07-23 09:34:59 +00:00
TIMEOUT="${LEAN_TIMEOUT:-400}"
MEM="${LEAN_MEM_MB:-4096}"
GEN_MODULES=(
"SlhVerify/TypesExternal"
"SlhVerify/Types"
"SlhVerify/FunsExternal"
"SlhVerify/Funs"
)
2026-07-23 09:34:59 +00:00
# Proof files, in dependency order.
PROOFS=(
"ChainSpec"
"WotsSpec"
phase 2: THIRD CERTIFICATE — XMSS auth-path Merkle loop (Algorithm 10) fips205.xmss_loop_eq (Proofs/XmssSpec.lean): the extracted xmss_pk_from_sig_free_loop equals the explicit Merkle-path fold — at step k set the tree height to k+1, test bit k of the leaf index; even bit: tree_index := i/2 and H(node || auth[k]); odd bit: tree_index := (i-1)/2 and H(auth[k] || node). This pins the sibling hash ORDER, the address schedule, and the auth-path indexing of Merkle verification. Exact cone: [propext, Classical.choice, Quot.sound, verify_mono.oracle.h] — the first certificate where H enters; F does not (the loop runs above the WOTS+ computation). check.sh green over all three certificates. Fidelity review at authorship (three-way): extracted body (gen Funs.lean 761-801) == Rust verify_mono.rs xmss_pk_from_sig_free (verbatim from upstream xmss.rs, hash calls -> oracle) == FIPS 205 Algorithm 10, incl. the per-branch operation order (even: node-slice then auth[k]; odd: auth[k] then node-slice) and the k+1 tree height. Proof: the chain/wots recipe on a u32 range — u32_succ / fwd_succ / hnext / loop_unfold_bind reused VERBATIM from ChainSpec. New layer lesson (the one novel obstruction, on pattern): the loop body BRANCHES on the index bit, so the step lemma splits with by_cases + if_pos/if_neg; and the get_tree_index pair-bind needs its matcher made concrete before the tail normalizes — bind_congr + rintro to fix the scrutinee, then FULL simp (only full simp iota-reduces the pair matcher; simp only will not) with bind_assoc + bind_ok + the loop def closes each branch. The certificate's own induction threads the IH under the opaque binds of BOTH branches with bind_congr, per branch, ending exact ih. check.sh: PROOFS += XmssSpec, CERTS += fips205.xmss_loop_eq, audit imports XmssSpec (self-test structure anchors untouched). README: status three certificates, Algorithm numbering per upstream comments (wots=8, xmss=10). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 14:18:44 +00:00
"XmssSpec"
phase 2: FOURTH CERTIFICATE — hypertree layer walk (Algorithm 12) + de-plumbing fips205.ht_loop_eq (Proofs/HtSpec.lean): the extracted ht_verify_free_loop equals the explicit d-layer fold — at layer j: idx_leaf = idx_tree masked to h' bits (mask+cast), idx_tree >>= h', layer address j, tree address to the shifted index, node recomputed through xmss_pk_from_sig on the j-th XMSS signature. Pins the hypertree layer schedule; the final node == pk_root comparison sits one bind above in ht_verify_free (apex material). Exact cone: [propext, Classical.choice, Quot.sound, verify_mono.oracle.f, verify_mono.oracle.h, verify_mono.oracle.t_l] — kernel-3 plus exactly the three hash primitives the referenced WOTS+/XMSS machinery touches. THE LAYER'S OBSTRUCTION (one per layer, on pattern) was not the proof but the CONE: the first extraction of this loop carried Result-conversion plumbing (try_from/is_err/unwrap; transitively a Take iterator and the &u32 Sub instance) — all axioms, rightly rejected by the Phase-3 audit. Fixed at SOURCE level (fips205-source 6f6a9d6, 8 sites, semantics identical for every FIPS 205 parameter set, differential test re-run green), then re-extracted: the loop body is now straight-line and the proof is the plain chain/wots recipe (no branches; base case via loop.eq_1; step lemma closes by rfl; induction = bind_congr ×12). Also in this commit: - gen/ regenerated from the patched snapshot (loop bodies of the three prior certificates byte-identical modulo source line comments; all three proofs recompiled unchanged and re-audited green). - Dead-stub deletion (axiom-shadowing hygiene rule): the five obsoleted plumbing axioms + vestigial take.default removed from FunsExternal, the orphaned TryFromIntError type axiom removed from TypesExternal. The model's external surface is now: 5 SHA-2 oracles (the boundary), the Take iterator machinery used only by helpers::to_int (apex round's de-plumbing item), 3 zeroize blanket impls (never on the verify path), and the discharged-real u32 Step defs. - check.sh: PROOFS += HtSpec, CERTS += fips205.ht_loop_eq, audit import (self-test structure anchors untouched). README: four certificates + the de-plumbing record. Fidelity review at authorship (three-way): extracted body == Rust ht_verify_free (verbatim from upstream hypertree.rs, calls -> *_free) == FIPS 205 Algorithm 12, incl. mask-then-shift order and layer-then-tree address order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:14:21 +00:00
"HtSpec"
phase 2: FIFTH CERTIFICATE — FORS pk-from-sig (Algorithm 17), inner + outer loops Two theorems, split into two files (METHOD-4 discipline — each proof a clean unit). NB: an early single-file/bare-rfl attempt appeared to "OOM at the clamp", but that memory pressure was a SYMPTOM of the runaway whnf diagnosed below, not a real memory need — the fixed proofs compile in seconds at the default caps. fips205.fors_inner_loop_eq (Proofs/ForsInnerSpec.lean): the extracted inner Merkle auth-path loop for ONE FORS tree (fors_pk_from_sig_free_loop0_loop0) equals the explicit auth-path fold — at level j set tree height j+1, test bit j of THIS tree's leaf index indices[i], hash the current node with auth.tree[j] in the bit order (even: node||auth[j]; odd: auth[j]||node), halving the tree index. Structurally the XMSS auth-path loop, but the bit source is indices[i]>>j and the loop returns the (adrs,node) pair. Cone: kernel-3 + verify_mono.oracle.h. fips205.fors_outer_loop_eq (Proofs/ForsOuterSpec.lean): the extracted outer per-tree loop (fors_pk_from_sig_free_loop0) equals the explicit K-tree fold — for each tree i, compute the leaf with F at tree index (i<<a)+indices[i], run the inner Merkle loop over the A levels, write the result to root[i]. Consumes the inner loop as an opaque sub-call. Cone: kernel-3 + verify_mono.oracle.{f,h} (F per leaf; H transitively through the inner loop). Fidelity review at authorship (three-way, both loops): extracted bodies (gen Funs.lean 893-933 inner, 954-985 outer) == Rust verify_mono.rs fors_pk_from_sig_free (verbatim from upstream fors.rs, hash calls -> oracle) == FIPS 205 Algorithm 17, incl. the even/odd sibling order and the (i<<a)+indices[i] leaf index. Proof: the branched-Merkle recipe (XMSS) for the inner loop (by_cases on the index bit, pair-bind matcher made concrete via bind_congr+rintro then full simp); the HT straight-line recipe for the outer loop, adapted (bind_congr-peeled step lemma + bind_congr x16 induction, both threading the inner-loop sub-call opaquely). loop_unfold_bind / u32_succ / fwd_succ / hnext reused verbatim from ChainSpec. check.sh: PROOFS += ForsInnerSpec, ForsOuterSpec; CERTS += the two fors certs; audit imports both; check.sh settings unchanged (400s/4096MB). ForsOuterSpec compiles in 4.4s / 2.4GB after the fix below. check.sh green over ALL SIX certificates with the axiom audit. README status -> FIVE certificates. DIAGNOSIS NOTE (honesty): ForsOuterSpec's fors_outer_step first closed with a bare `rfl`, which whnf'd the whole 16-bind body INCLUDING the inner-loop `loop` term and hit a DETERMINISTIC 4M-heartbeat timeout (never actually passed — an earlier "green" reading was a misread wrapper exit code; the real error was hidden by check.sh piping per-file output to /dev/null). Fix: peel the 16 binds with bind_congr so the closing rfl only sees the small loop-tail, and close the post-pair-rintro tail with a full simp (the pair `let` won't iota via simp only). This is the HtSpec straight-line recipe adapted for a body that nests a loop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:56:56 +00:00
"ForsInnerSpec"
"ForsOuterSpec"
phase 2: INPUT-PREP layer — to_int, to_byte, WOTS+ checksum (3 kernel-3 certs) Three straight-line range-loop fidelity theorems (Proofs/InputPrepSpec.lean), each #print axioms = EXACTLY [propext, Classical.choice, Quot.sound] — pure byte/bit arithmetic, NO hash oracle enters (the cleanest cones in the campaign): - fips205.to_int_loop_eq (Algorithm 2, toInt): the extracted big-endian byte->u64 loop = the fold total <- (total<<8) + x[i]. - fips205.to_byte_loop_eq (Algorithm 3, toByte): the extracted u32->byte loop = the fold writing s[n-1-i] and shifting total right by 8. - fips205.wots_csum_loop_eq: the WOTS+ checksum loop = the fold csum <- csum + (W-1-msg[i]). All three are the straight-line recipe (hbody -> step lemma closed by rfl -> induction with bind_congr per bind). to_int + checksum use the usize range helpers (WotsSpec), to_byte the u32 range (ChainSpec); loop_unfold_bind reused. Also in this commit — DE-PLUMBING ROUND 2 landed (source bea1051, separate commit in fips205-source): to_int's iter().take() and base_2b's iter_mut() became index loops, so both extract to real definitions. Consequently: - gen/ regenerated (to_int_loop / base_2b_loop0 now clean StepUsize range loops with Slice.index_usize / Slice.update; the six prior certificates recompiled UNCHANGED and re-audited green against the new gen). - The core::iter::adapters::take::Take::next AXIOM — the LAST non-oracle, non-zeroize plumbing axiom on the verify path — is now unreferenced and was DELETED from FunsExternal (dead-stub hygiene rule). The model's external surface is now EXACTLY: the 5 SHA-2 oracles + 3 zeroize blanket impls (never on the verify path) + the discharged-real u32 Step defs. Nothing else. Fidelity review at authorship (three-way): extracted loop bodies (gen Funs.lean) == Rust helpers.rs to_int/to_byte + verify_mono checksum (verbatim FIPS 205 Alg 2/3) == the folds above. check.sh: PROOFS += InputPrepSpec; CERTS += the 3 certs; audit imports it. Green over ALL NINE certificates at default caps (400s/4096MB). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 06:55:34 +00:00
"InputPrepSpec"
2026-07-23 09:34:59 +00:00
)
# 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"
phase 2: THIRD CERTIFICATE — XMSS auth-path Merkle loop (Algorithm 10) fips205.xmss_loop_eq (Proofs/XmssSpec.lean): the extracted xmss_pk_from_sig_free_loop equals the explicit Merkle-path fold — at step k set the tree height to k+1, test bit k of the leaf index; even bit: tree_index := i/2 and H(node || auth[k]); odd bit: tree_index := (i-1)/2 and H(auth[k] || node). This pins the sibling hash ORDER, the address schedule, and the auth-path indexing of Merkle verification. Exact cone: [propext, Classical.choice, Quot.sound, verify_mono.oracle.h] — the first certificate where H enters; F does not (the loop runs above the WOTS+ computation). check.sh green over all three certificates. Fidelity review at authorship (three-way): extracted body (gen Funs.lean 761-801) == Rust verify_mono.rs xmss_pk_from_sig_free (verbatim from upstream xmss.rs, hash calls -> oracle) == FIPS 205 Algorithm 10, incl. the per-branch operation order (even: node-slice then auth[k]; odd: auth[k] then node-slice) and the k+1 tree height. Proof: the chain/wots recipe on a u32 range — u32_succ / fwd_succ / hnext / loop_unfold_bind reused VERBATIM from ChainSpec. New layer lesson (the one novel obstruction, on pattern): the loop body BRANCHES on the index bit, so the step lemma splits with by_cases + if_pos/if_neg; and the get_tree_index pair-bind needs its matcher made concrete before the tail normalizes — bind_congr + rintro to fix the scrutinee, then FULL simp (only full simp iota-reduces the pair matcher; simp only will not) with bind_assoc + bind_ok + the loop def closes each branch. The certificate's own induction threads the IH under the opaque binds of BOTH branches with bind_congr, per branch, ending exact ih. check.sh: PROOFS += XmssSpec, CERTS += fips205.xmss_loop_eq, audit imports XmssSpec (self-test structure anchors untouched). README: status three certificates, Algorithm numbering per upstream comments (wots=8, xmss=10). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 14:18:44 +00:00
"fips205.xmss_loop_eq"
phase 2: FOURTH CERTIFICATE — hypertree layer walk (Algorithm 12) + de-plumbing fips205.ht_loop_eq (Proofs/HtSpec.lean): the extracted ht_verify_free_loop equals the explicit d-layer fold — at layer j: idx_leaf = idx_tree masked to h' bits (mask+cast), idx_tree >>= h', layer address j, tree address to the shifted index, node recomputed through xmss_pk_from_sig on the j-th XMSS signature. Pins the hypertree layer schedule; the final node == pk_root comparison sits one bind above in ht_verify_free (apex material). Exact cone: [propext, Classical.choice, Quot.sound, verify_mono.oracle.f, verify_mono.oracle.h, verify_mono.oracle.t_l] — kernel-3 plus exactly the three hash primitives the referenced WOTS+/XMSS machinery touches. THE LAYER'S OBSTRUCTION (one per layer, on pattern) was not the proof but the CONE: the first extraction of this loop carried Result-conversion plumbing (try_from/is_err/unwrap; transitively a Take iterator and the &u32 Sub instance) — all axioms, rightly rejected by the Phase-3 audit. Fixed at SOURCE level (fips205-source 6f6a9d6, 8 sites, semantics identical for every FIPS 205 parameter set, differential test re-run green), then re-extracted: the loop body is now straight-line and the proof is the plain chain/wots recipe (no branches; base case via loop.eq_1; step lemma closes by rfl; induction = bind_congr ×12). Also in this commit: - gen/ regenerated from the patched snapshot (loop bodies of the three prior certificates byte-identical modulo source line comments; all three proofs recompiled unchanged and re-audited green). - Dead-stub deletion (axiom-shadowing hygiene rule): the five obsoleted plumbing axioms + vestigial take.default removed from FunsExternal, the orphaned TryFromIntError type axiom removed from TypesExternal. The model's external surface is now: 5 SHA-2 oracles (the boundary), the Take iterator machinery used only by helpers::to_int (apex round's de-plumbing item), 3 zeroize blanket impls (never on the verify path), and the discharged-real u32 Step defs. - check.sh: PROOFS += HtSpec, CERTS += fips205.ht_loop_eq, audit import (self-test structure anchors untouched). README: four certificates + the de-plumbing record. Fidelity review at authorship (three-way): extracted body == Rust ht_verify_free (verbatim from upstream hypertree.rs, calls -> *_free) == FIPS 205 Algorithm 12, incl. mask-then-shift order and layer-then-tree address order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:14:21 +00:00
"fips205.ht_loop_eq"
phase 2: FIFTH CERTIFICATE — FORS pk-from-sig (Algorithm 17), inner + outer loops Two theorems, split into two files (METHOD-4 discipline — each proof a clean unit). NB: an early single-file/bare-rfl attempt appeared to "OOM at the clamp", but that memory pressure was a SYMPTOM of the runaway whnf diagnosed below, not a real memory need — the fixed proofs compile in seconds at the default caps. fips205.fors_inner_loop_eq (Proofs/ForsInnerSpec.lean): the extracted inner Merkle auth-path loop for ONE FORS tree (fors_pk_from_sig_free_loop0_loop0) equals the explicit auth-path fold — at level j set tree height j+1, test bit j of THIS tree's leaf index indices[i], hash the current node with auth.tree[j] in the bit order (even: node||auth[j]; odd: auth[j]||node), halving the tree index. Structurally the XMSS auth-path loop, but the bit source is indices[i]>>j and the loop returns the (adrs,node) pair. Cone: kernel-3 + verify_mono.oracle.h. fips205.fors_outer_loop_eq (Proofs/ForsOuterSpec.lean): the extracted outer per-tree loop (fors_pk_from_sig_free_loop0) equals the explicit K-tree fold — for each tree i, compute the leaf with F at tree index (i<<a)+indices[i], run the inner Merkle loop over the A levels, write the result to root[i]. Consumes the inner loop as an opaque sub-call. Cone: kernel-3 + verify_mono.oracle.{f,h} (F per leaf; H transitively through the inner loop). Fidelity review at authorship (three-way, both loops): extracted bodies (gen Funs.lean 893-933 inner, 954-985 outer) == Rust verify_mono.rs fors_pk_from_sig_free (verbatim from upstream fors.rs, hash calls -> oracle) == FIPS 205 Algorithm 17, incl. the even/odd sibling order and the (i<<a)+indices[i] leaf index. Proof: the branched-Merkle recipe (XMSS) for the inner loop (by_cases on the index bit, pair-bind matcher made concrete via bind_congr+rintro then full simp); the HT straight-line recipe for the outer loop, adapted (bind_congr-peeled step lemma + bind_congr x16 induction, both threading the inner-loop sub-call opaquely). loop_unfold_bind / u32_succ / fwd_succ / hnext reused verbatim from ChainSpec. check.sh: PROOFS += ForsInnerSpec, ForsOuterSpec; CERTS += the two fors certs; audit imports both; check.sh settings unchanged (400s/4096MB). ForsOuterSpec compiles in 4.4s / 2.4GB after the fix below. check.sh green over ALL SIX certificates with the axiom audit. README status -> FIVE certificates. DIAGNOSIS NOTE (honesty): ForsOuterSpec's fors_outer_step first closed with a bare `rfl`, which whnf'd the whole 16-bind body INCLUDING the inner-loop `loop` term and hit a DETERMINISTIC 4M-heartbeat timeout (never actually passed — an earlier "green" reading was a misread wrapper exit code; the real error was hidden by check.sh piping per-file output to /dev/null). Fix: peel the 16 binds with bind_congr so the closing rfl only sees the small loop-tail, and close the post-pair-rintro tail with a full simp (the pair `let` won't iota via simp only). This is the HtSpec straight-line recipe adapted for a body that nests a loop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:56:56 +00:00
"fips205.fors_inner_loop_eq"
"fips205.fors_outer_loop_eq"
phase 2: INPUT-PREP layer — to_int, to_byte, WOTS+ checksum (3 kernel-3 certs) Three straight-line range-loop fidelity theorems (Proofs/InputPrepSpec.lean), each #print axioms = EXACTLY [propext, Classical.choice, Quot.sound] — pure byte/bit arithmetic, NO hash oracle enters (the cleanest cones in the campaign): - fips205.to_int_loop_eq (Algorithm 2, toInt): the extracted big-endian byte->u64 loop = the fold total <- (total<<8) + x[i]. - fips205.to_byte_loop_eq (Algorithm 3, toByte): the extracted u32->byte loop = the fold writing s[n-1-i] and shifting total right by 8. - fips205.wots_csum_loop_eq: the WOTS+ checksum loop = the fold csum <- csum + (W-1-msg[i]). All three are the straight-line recipe (hbody -> step lemma closed by rfl -> induction with bind_congr per bind). to_int + checksum use the usize range helpers (WotsSpec), to_byte the u32 range (ChainSpec); loop_unfold_bind reused. Also in this commit — DE-PLUMBING ROUND 2 landed (source bea1051, separate commit in fips205-source): to_int's iter().take() and base_2b's iter_mut() became index loops, so both extract to real definitions. Consequently: - gen/ regenerated (to_int_loop / base_2b_loop0 now clean StepUsize range loops with Slice.index_usize / Slice.update; the six prior certificates recompiled UNCHANGED and re-audited green against the new gen). - The core::iter::adapters::take::Take::next AXIOM — the LAST non-oracle, non-zeroize plumbing axiom on the verify path — is now unreferenced and was DELETED from FunsExternal (dead-stub hygiene rule). The model's external surface is now EXACTLY: the 5 SHA-2 oracles + 3 zeroize blanket impls (never on the verify path) + the discharged-real u32 Step defs. Nothing else. Fidelity review at authorship (three-way): extracted loop bodies (gen Funs.lean) == Rust helpers.rs to_int/to_byte + verify_mono checksum (verbatim FIPS 205 Alg 2/3) == the folds above. check.sh: PROOFS += InputPrepSpec; CERTS += the 3 certs; audit imports it. Green over ALL NINE certificates at default caps (400s/4096MB). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 06:55:34 +00:00
"fips205.to_int_loop_eq"
"fips205.to_byte_loop_eq"
"fips205.wots_csum_loop_eq"
"fips205.base2b_outer_loop_eq"
2026-07-23 09:34:59 +00:00
)
ORACLES="verify_mono.oracle.f, verify_mono.oracle.h, verify_mono.oracle.t_l, verify_mono.oracle.t_len, verify_mono.oracle.h_msg"
ALLOWED="[propext, Classical.choice, Quot.sound, ${ORACLES}]"
2026-07-23 09:34:59 +00:00
echo "fips205-slhdsa-verified — check"
echo "==============================="
2026-07-23 09:34:59 +00:00
# ── Phase 1: model ──────────────────────────────────────────────────────────
echo "=== Phase 1: compile the extracted model ==="
cd "$AENEAS_LEAN"
lake env bash -c "
set -euo pipefail
2026-07-23 09:34:59 +00:00
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
"
2026-07-23 09:34:59 +00:00
# ── 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
for f in Proofs/*.lean; do b=\$(basename \"\$f\" .lean)
case \" ${PROOFS[*]} \" in *\" \$b \"*) ;; *) echo \"DEAD FILE: Proofs/\$b.lean not in manifest\"; exit 1 ;; esac
done
"
# ── Phase 3: axiom audit ────────────────────────────────────────────────────
echo "=== Phase 3: axiom audit (cone ⊆ kernel-3 + 5 oracles) ==="
cd "$AENEAS_LEAN"
AUD="$HERE/Proofs/.audit.lean"
{ echo "import Proofs.ChainSpec"; echo "import Proofs.WotsSpec"
phase 2: FOURTH CERTIFICATE — hypertree layer walk (Algorithm 12) + de-plumbing fips205.ht_loop_eq (Proofs/HtSpec.lean): the extracted ht_verify_free_loop equals the explicit d-layer fold — at layer j: idx_leaf = idx_tree masked to h' bits (mask+cast), idx_tree >>= h', layer address j, tree address to the shifted index, node recomputed through xmss_pk_from_sig on the j-th XMSS signature. Pins the hypertree layer schedule; the final node == pk_root comparison sits one bind above in ht_verify_free (apex material). Exact cone: [propext, Classical.choice, Quot.sound, verify_mono.oracle.f, verify_mono.oracle.h, verify_mono.oracle.t_l] — kernel-3 plus exactly the three hash primitives the referenced WOTS+/XMSS machinery touches. THE LAYER'S OBSTRUCTION (one per layer, on pattern) was not the proof but the CONE: the first extraction of this loop carried Result-conversion plumbing (try_from/is_err/unwrap; transitively a Take iterator and the &u32 Sub instance) — all axioms, rightly rejected by the Phase-3 audit. Fixed at SOURCE level (fips205-source 6f6a9d6, 8 sites, semantics identical for every FIPS 205 parameter set, differential test re-run green), then re-extracted: the loop body is now straight-line and the proof is the plain chain/wots recipe (no branches; base case via loop.eq_1; step lemma closes by rfl; induction = bind_congr ×12). Also in this commit: - gen/ regenerated from the patched snapshot (loop bodies of the three prior certificates byte-identical modulo source line comments; all three proofs recompiled unchanged and re-audited green). - Dead-stub deletion (axiom-shadowing hygiene rule): the five obsoleted plumbing axioms + vestigial take.default removed from FunsExternal, the orphaned TryFromIntError type axiom removed from TypesExternal. The model's external surface is now: 5 SHA-2 oracles (the boundary), the Take iterator machinery used only by helpers::to_int (apex round's de-plumbing item), 3 zeroize blanket impls (never on the verify path), and the discharged-real u32 Step defs. - check.sh: PROOFS += HtSpec, CERTS += fips205.ht_loop_eq, audit import (self-test structure anchors untouched). README: four certificates + the de-plumbing record. Fidelity review at authorship (three-way): extracted body == Rust ht_verify_free (verbatim from upstream hypertree.rs, calls -> *_free) == FIPS 205 Algorithm 12, incl. mask-then-shift order and layer-then-tree address order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:14:21 +00:00
echo "import Proofs.XmssSpec"; echo "import Proofs.HtSpec"
phase 2: INPUT-PREP layer — to_int, to_byte, WOTS+ checksum (3 kernel-3 certs) Three straight-line range-loop fidelity theorems (Proofs/InputPrepSpec.lean), each #print axioms = EXACTLY [propext, Classical.choice, Quot.sound] — pure byte/bit arithmetic, NO hash oracle enters (the cleanest cones in the campaign): - fips205.to_int_loop_eq (Algorithm 2, toInt): the extracted big-endian byte->u64 loop = the fold total <- (total<<8) + x[i]. - fips205.to_byte_loop_eq (Algorithm 3, toByte): the extracted u32->byte loop = the fold writing s[n-1-i] and shifting total right by 8. - fips205.wots_csum_loop_eq: the WOTS+ checksum loop = the fold csum <- csum + (W-1-msg[i]). All three are the straight-line recipe (hbody -> step lemma closed by rfl -> induction with bind_congr per bind). to_int + checksum use the usize range helpers (WotsSpec), to_byte the u32 range (ChainSpec); loop_unfold_bind reused. Also in this commit — DE-PLUMBING ROUND 2 landed (source bea1051, separate commit in fips205-source): to_int's iter().take() and base_2b's iter_mut() became index loops, so both extract to real definitions. Consequently: - gen/ regenerated (to_int_loop / base_2b_loop0 now clean StepUsize range loops with Slice.index_usize / Slice.update; the six prior certificates recompiled UNCHANGED and re-audited green against the new gen). - The core::iter::adapters::take::Take::next AXIOM — the LAST non-oracle, non-zeroize plumbing axiom on the verify path — is now unreferenced and was DELETED from FunsExternal (dead-stub hygiene rule). The model's external surface is now EXACTLY: the 5 SHA-2 oracles + 3 zeroize blanket impls (never on the verify path) + the discharged-real u32 Step defs. Nothing else. Fidelity review at authorship (three-way): extracted loop bodies (gen Funs.lean) == Rust helpers.rs to_int/to_byte + verify_mono checksum (verbatim FIPS 205 Alg 2/3) == the folds above. check.sh: PROOFS += InputPrepSpec; CERTS += the 3 certs; audit imports it. Green over ALL NINE certificates at default caps (400s/4096MB). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 06:55:34 +00:00
echo "import Proofs.ForsInnerSpec"; echo "import Proofs.ForsOuterSpec"; echo "import Proofs.InputPrepSpec"
2026-07-23 09:34:59 +00:00
for c in "${CERTS[@]}"; do echo "#print axioms $c"; done
} > "$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)
rm -f "$AUD"
fail=0
for c in "${CERTS[@]}"; do
line=$(echo "$OUT" | grep -F "'$c' depends on axioms:" || true)
if [ -z "$line" ]; then echo "$c — no axiom report"; fail=1; continue; fi
cone=$(echo "$line" | sed "s/.*depends on axioms: //")
# every axiom in the cone must be in ALLOWED
bad=$(echo "$cone" | tr -d '[]' | tr ',' '\n' | sed 's/^ *//;s/ *$//' | while read -r ax; do
[ -z "$ax" ] && continue
case " propext Classical.choice Quot.sound verify_mono.oracle.f verify_mono.oracle.h verify_mono.oracle.t_l verify_mono.oracle.t_len verify_mono.oracle.h_msg " in
*" $ax "*) ;; *) echo "$ax" ;;
esac
done)
if [ -n "$bad" ]; then echo "$c — DISALLOWED axioms: $bad"; fail=1
else echo "$c cone ⊆ allowed"; fi
done
[ "$fail" = 0 ] || { echo "AXIOM AUDIT FAILED"; exit 1; }
echo
2026-07-23 09:34:59 +00:00
echo "ALL GREEN — model compiles, proofs compile, every certificate cone is"
echo "the three kernel axioms plus (at most) the SHA-2 hash oracles."
echo "Certificates proven: ${CERTS[*]}"