fips205-slhdsa-verified/README.md

336 lines
20 KiB
Markdown
Raw Normal View History

# fips205-slhdsa-verified
Machine-checked verification campaign for the **SLH-DSA (FIPS 205) verify
path**, extracted from a pure-Rust implementation into Lean 4 via
Charon/Aeneas — the same pipeline, discipline, and honesty rules as the
four ed25519 campaigns (`dalek/anza/risc0/betrusted-ed25519-verified`).
bridge: re-pin to the NIST-ACVP source commit; state the real coverage numbers The companion fips205-source commit adds NIST ACVP SHA2-128s verification vectors and a real differential bridge. This repo re-pins to it and replaces the word "finite" with numbers, per external review rounds 4-6. - extract.sh + PROVENANCE re-pinned 797b4ef -> 3153988. The provenance guard did its job first: it REFUSED the moved source until the pin was rotated deliberately. - VERIFIED that the test/vector commit does not perturb the proved model: after re-extraction all four pinned model files are byte-identical (Types db720b4a…, Funs 7b7de55f…, TypesExternal 37958beb…, FunsExternal 5efe551c…), check.sh is ALL GREEN, and the audit digest is unchanged (d83e297a…). The only regenerated difference is the untracked Aeneas *_Template.lean byproduct, which Phase 0 purges. - TRUSTED-BASE item 9 and the README now state the bridge's actual size: 131 assertion points (was 9), of which 20 are NIST ACVP SHA2-128s known-answer tests run against the proved path — 10 from the `internal` group (whose message IS M', exactly what slh_verify_128s consumes) and 10 from `external pure` where mono, the deployed verifier and NIST must all agree, 9 of those with a NON-EMPTY context, which is the first empirical check of the domain-separator byte and context prefix that item 10 declares outside every proof. Both documents keep saying plainly that a passing differential test is evidence, not a proof. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 07:46:29 +00:00
## STATUS: eleven certificates over the extracted verify model (external review rounds 16 applied)
2026-07-23 09:34:59 +00:00
review round 5: bind the policy, the specification bodies, and the harness The third reviewer demonstrated that the round-4 gate was closed at the EXPLOITS, not the MECHANISMS: it bound the manifest rows while the things he moved sat outside the binding. Three fail-opens, each executed end-to-end with the button printing ALL GREEN. No theorem, proof, fold, or model body changed. NEW-1 — the policy constant was unbound. `manifestFingerprint` covered `manifest` but never `allowedBoundary`, the sole predicate the enumeration tests against; adding one name re-opened the False-proof with the committed fingerprint BYTE-IDENTICAL. NEW-2 — the specification bodies were unbound. The statement fingerprint pinned each certificate's TYPE, which names its reference fold; redefining that fold to *be* the extracted loop left cone and type-hash identical while the certificate degenerated to `loop = loop`. 100% of the fidelity content lives in those defs. NEW-3 — the harness was unbound. Stubbing verification/lean-guard alone, without touching check.sh or the proofs, gave ALL GREEN in 3.6s over destroyed proofs. FIX — one mechanism rather than three patches. Audit.lean now emits a canonical AUDIT-MANIFEST block and check.sh binds to its SHA-256. The block covers the POLICY constants, every certificate's fully-elaborated statement, and every specification constant transitively reachable from those statements with its fully-elaborated BODY (41 constants; the closure is computed, so a new fold cannot appear without moving the digest; Prop-valued constants contribute their statement, by proof irrelevance). This also retires the 32-bit Expr.hash as the binding (NEW-5) — it survives only as a per-certificate diagnostic. Enumeration now covers EVERY declaration kind (a `def : False` passed before) in the eight certificate modules AND in Audit.lean itself — the auditor is no longer exempt (round-5 R1). A bare `axiom` in audited scope is now an error. Phase 0 purges stale .olean (the verdict must depend on committed bytes, not .gitignored build state — NEW-4), forbids any .lean outside gen/ and Proofs/, and sha256-pins the four model files AND lean-guard. lean-guard is KEPT rather than removed (the reviewer's portability advice is declined by operator decision): it is the memory cap and machine-wide lock that protect the build machine after a 12.2GB OOM took the host down. That trade-off is documented. check.sh's "Certificates proven:" line now comes from the audited manifest; the hand-kept CERTS array — the one authoritative claim string nothing bound — is deleted. check-selftest.sh: 14 attacks, all rejected, plus a check that the hashed block literally carries the twelve fold bodies. Attacks 9-14 are the reviewers' and an independent drill's own exploits, turned into regression tests. DOCS. TRUSTED-BASE gains item 11 (the REAL trusted computing base — lean-guard pinned; check.sh, the toolchain env, $AENEAS_HOME, python3 and Lean still trusted) and item 12 (the apex does not compose the ten). README: the audit description rewritten; the XMSS sibling-order claim downgraded from "pins" to "makes visible", with a new blanket non-claim covering all ten loop certificates; the de-plumbing file claim corrected (round 1 touched only verify_mono.rs, round 2 only helpers.rs — which is ON the deployed verify AND sign paths, now disclosed; wots.rs was never patched). RECORDED-RUN: three lines that stood inside a fence were a hand-written summary, not console output — fabricated evidence in the file whose purpose is machine evidence. They are removed and the fabrication is named in place, together with the correction that the "INDEPENDENT RUN" block predates this gate. New rule: nothing goes in a fence unless captured with tee/cat, and every block states its date, pin, and who ran it. The transcripts added here follow it. Also disclosed rather than buried: three bugs in my own test harness this round (an olean-purge build-order break, an attack rejected by the wrong rule, and a coverage assertion looking on the wrong line) — each would have let an attack pass or fail for an unrelated reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 20:57:46 +00:00
`verification/check.sh` is **green** (exit 0): the model compiles, the proofs
compile, and the audit passes. The audit runs **inside Lean**
(`verification/Proofs/Audit.lean`) and binds four things, each added because an
external reviewer *demonstrated* the button going green without it:
- **axiom cones** — each certificate's cone is read from the kernel via
`collectAxioms` and must equal its expected set EXACTLY, so an added axiom
and a silently dropped oracle both fail (round 2 retired a text parser that
could fail open on an empty or truncated report);
- **coverage** — EVERY declaration in the eight certificate modules, of every
kind, and in `Audit.lean` itself, must stay inside the axiom boundary (rounds
45: an un-manifested `theorem : False`, then a `def : False`, then one inside
the auditor, each passed a gate that checked only the listed certificates);
- **statements and specifications** — `check.sh` binds to the **SHA-256** of a
canonical block containing the policy constants, every certificate's
fully-elaborated statement, and every reference fold's fully-elaborated
*body*. Round 5 showed why the last part is essential: redefining a fold to
*be* the extracted loop left every earlier fingerprint bit-identical while the
certificate degenerated to "the loop equals the loop";
- **bytes** — Phase 0 sha256-pins the four model files and the compiler harness
`lean-guard`, purges stale `.olean`s, and forbids stray `.lean` files, so the
verdict depends on committed bytes rather than build-directory state.
What the button still does **not** bind is stated plainly in
[TRUSTED-BASE.md](TRUSTED-BASE.md) item 11 — `check.sh` itself, the toolchain
env, and `$AENEAS_HOME`. `verification/check-selftest.sh` runs the reviewers'
own exploits back against the gate; all are rejected.
review round 1: fix the fail-open audit gate + remove the overclaimed framing External review (both standing reviewers, 2026-07-24) returned DO NOT ATTEST. The eleven Lean theorems compile with genuinely clean cones (both reviewers independently reconstructed them), but two real defects were found and are fixed here. FIX 1 — the axiom audit was FAIL-OPEN (the critical blocker). check.sh Phase 3 grepped a single physical line of each `#print axioms` report; Lean WRAPS long cones across lines, so for ht/fors_outer/APEX the audit checked only `[propext,` and silently ignored the continuation lines — a disallowed axiom on line 2+ passed (the GPT reviewer demonstrated `review_evil_ax` passing). Since check.sh is the sole source of the word "proven", this is unacceptable. - New parser: FLATTEN the whole report (join newlines) BEFORE parsing, then extract each certificate's complete bracketed cone with a literal-string (regex-safe) scan and subset-check every axiom. Missing/empty report => FAIL CLOSED. The audit now prints the count of axioms actually audited per cert (apex: 8, previously 1). - check-selftest.sh gains ATTACK 3: a smuggled axiom bundled with the apex so its cone WRAPS with the evil axiom on a continuation line — the exact exploit. Verified: all three attacks now rejected, attack 3 via the axiom gate naming the continuation-line axiom. (Also fixed attack 2's leftover EvilSpec.lean tripping attack 3's dead-file gate.) FIX 2 — remove the overclaimed framing (refuted by both reviewers). Corrected in README, the ApexSpec header + apex docstring, and (separately) the control MANIFEST: - "composes all ten loop-fidelity certificates" — FALSE. The apex proof is a STRUCTURAL FACTORIZATION; it references NONE of the ten (grep: 0) and would remain provable if one were deleted. They are independent local-fidelity lemmas, not links in the apex proof. - "every loop is individually fidelity-certified" — FALSE. base_2b's inner accumulation loop is threaded opaquely and uncertified — and it determines the FORS indices / WOTS digits, so a defect there could change the recomputed root while all eleven theorems still hold. - "the deployed verifier" — the proved subject is verify_mono, a private #![allow(dead_code)] monomorphic facade NOT called by the public API; the bridge to the deployed generic verifier is the finite differential test, not a machine-checked refinement. - "verify-path pyramid complete" — replaced with "intermediate verification layer"; the apex is an ACCEPTANCE CHARACTERIZATION, not closed-form FIPS-205 correctness. Also: FunsExternal header noted the Take axiom "remains" (stale — deleted in de-plumbing round 2); corrected. check.sh green over all eleven certificates under the fixed fail-closed parser (exit 0, 8 axioms audited for the apex). Nothing about the theorems changed — they were and are sound; only the audit tool and the claims about them are fixed. NOT DONE (remaining reviewer blockers, tracked): reproducible extract tuple (pin commits, de-hard-code extract.sh) + Cargo.lock / toolchain pin. Attestation remains gated behind review round 2 + the operator halt + the appeal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:55:10 +00:00
**What is actually established** — eleven Lean theorems about the
Aeneas-generated model of the **monomorphic `verify_mono` compatibility
verify path** (an additive, `#![allow(dead_code)]` re-expression of the
deployed generic verifier, using named hash oracles because Charon/Aeneas
cannot translate the deployed `Hashers` function-pointer struct):
- **Ten loop-fidelity theorems** (chain 5, WOTS+ 8, XMSS 10, hypertree 12,
FORS-inner/outer 17, and the input-prep helpers to_int/to_byte/checksum/
base_2b-outer, Alg 2/3/4). Each equates one *generated* Aeneas loop with an
explicit hand-written recursive fold — a local control-flow correspondence,
not an Algorithm-level mathematical specification.
- **The apex, `fips205.slh_verify_128s_accepts_iff`** — the extracted
`verify_mono::slh_verify_128s` returns `ok true` **iff** the recomputed
hypertree root byte-equals `pk.pk_root`. This is an *acceptance
characterization*: there is no acceptance path other than root equality
over the extracted recomputation. Its `#print axioms` cone is exactly
`[propext, Classical.choice, Quot.sound]` + the five SHA-2 oracles.
**What is NOT (yet) established — do not overclaim:**
- **The apex proof does not compose the ten loop theorems.** It is a
*structural factorization* of the extracted verifier around its final
equality check; it references none of the ten (it would remain provable if
one were deleted). The ten are independent local-fidelity lemmas, not links
in the apex's proof chain.
- **Not "every loop":** `base_2b`'s inner accumulation loop
(`helpers.base_2b_loop0_loop0`) is threaded *opaquely* and has no
certificate — and it determines the FORS indices / WOTS digits, so a defect
there could change the recomputed root while all eleven theorems still hold.
- **Not the deployed public verifier:** the proved subject is the private
bridge: re-pin to the NIST-ACVP source commit; state the real coverage numbers The companion fips205-source commit adds NIST ACVP SHA2-128s verification vectors and a real differential bridge. This repo re-pins to it and replaces the word "finite" with numbers, per external review rounds 4-6. - extract.sh + PROVENANCE re-pinned 797b4ef -> 3153988. The provenance guard did its job first: it REFUSED the moved source until the pin was rotated deliberately. - VERIFIED that the test/vector commit does not perturb the proved model: after re-extraction all four pinned model files are byte-identical (Types db720b4a…, Funs 7b7de55f…, TypesExternal 37958beb…, FunsExternal 5efe551c…), check.sh is ALL GREEN, and the audit digest is unchanged (d83e297a…). The only regenerated difference is the untracked Aeneas *_Template.lean byproduct, which Phase 0 purges. - TRUSTED-BASE item 9 and the README now state the bridge's actual size: 131 assertion points (was 9), of which 20 are NIST ACVP SHA2-128s known-answer tests run against the proved path — 10 from the `internal` group (whose message IS M', exactly what slh_verify_128s consumes) and 10 from `external pure` where mono, the deployed verifier and NIST must all agree, 9 of those with a NON-EMPTY context, which is the first empirical check of the domain-separator byte and context prefix that item 10 declares outside every proof. Both documents keep saying plainly that a passing differential test is evidence, not a proof. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 07:46:29 +00:00
`verify_mono` facade; the bridge to upstream's generic `pk.verify()` is a
finite **differential test**, not a machine-checked refinement. Its size is
round 7: assert pin-map completeness (NEW-13), correct the count to 137, fix the regeneration-scope contradiction The third reviewer demonstrated NEW-13: PROVENANCE.json is a tracked file that nothing pins, and Phase 0's only completeness test was 'is the map non-empty'. Deleting the harness_integrity_sha256 key therefore silently un-pinned BOTH lean-guard and Proofs/Audit.lean with no diagnostic, after which the round-6 logic mutation ran to ALL GREEN over a repository proving False with the digest byte-identical. Reproduced here before fixing. The required pin NAMES now live hardcoded in check.sh — policy in the root of trust, values in the map — so a shortened map is a build failure naming the missing entries. Self-test attack 18 performs the deletion. GPT reviewer, independently: the documented '131 assertion points' was wrong. Recounted from the code, the defensible figure is 137 mono-path evaluated cases (9 retained original + 108 randomized + 10 NIST internal + 10 NIST external-pure); 131 had folded in 3 deployed-only prehash cases while omitting the retained test, and TRUSTED-BASE then decomposed it as 20 + 108 = 128, contradicting itself. Item 9 now carries the full table, states that 127 of the 137 compare mono against deployed, keeps the 3 prehash cases explicitly outside the total, and records that only two SHA2-512 and one SHAKE-256 vector are executable there — so this is not NIST coverage of all four supported prehash variants. Also from GPT: PROVENANCE.json contradicted itself, saying extraction 'reproduces all four model files byte-identically' while its own _comment correctly said the two *External files are hand-maintained. Extraction regenerates two files; the other two are byte-pinned. Corrected. TRUSTED-BASE item 11 now discloses that PROVENANCE.json is itself load-bearing and unpinned, and item 7's stale snapshot head is fixed. README states the lean-guard graceful fallback and that the empirical bridge runs on stable Rust without any Lean toolchain (round-7 NEW-16), which is the first load-bearing part of this work a third party can reproduce with cargo alone. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 11:03:35 +00:00
now stated rather than left to the word "finite": **137 evaluated
input/verdict cases on the proved path**, of which **20 are NIST ACVP
SHA2-128s known-answer tests** (9 retained original + 108 randomized + 10 NIST
internal + 10 NIST external-pure; see TRUSTED-BASE.md item 9 for the table and
for the 3 deployed-only prehash cases counted separately). Until 2026-07-28 it
was nine cases from a single seed, and this parameter set had *no* NIST
verification coverage at all — the vectors vendored upstream contain no
SHA2-128s sigVer group, so the 128s groups were extracted from the official
NIST ACVP-Server set by a committed, re-runnable script
(`tests/nist_acvp_vectors/extract_sha2_128s.py` in the snapshot repo) that
pins the upstream hash and fails closed on any drift.
review round 1: fix the fail-open audit gate + remove the overclaimed framing External review (both standing reviewers, 2026-07-24) returned DO NOT ATTEST. The eleven Lean theorems compile with genuinely clean cones (both reviewers independently reconstructed them), but two real defects were found and are fixed here. FIX 1 — the axiom audit was FAIL-OPEN (the critical blocker). check.sh Phase 3 grepped a single physical line of each `#print axioms` report; Lean WRAPS long cones across lines, so for ht/fors_outer/APEX the audit checked only `[propext,` and silently ignored the continuation lines — a disallowed axiom on line 2+ passed (the GPT reviewer demonstrated `review_evil_ax` passing). Since check.sh is the sole source of the word "proven", this is unacceptable. - New parser: FLATTEN the whole report (join newlines) BEFORE parsing, then extract each certificate's complete bracketed cone with a literal-string (regex-safe) scan and subset-check every axiom. Missing/empty report => FAIL CLOSED. The audit now prints the count of axioms actually audited per cert (apex: 8, previously 1). - check-selftest.sh gains ATTACK 3: a smuggled axiom bundled with the apex so its cone WRAPS with the evil axiom on a continuation line — the exact exploit. Verified: all three attacks now rejected, attack 3 via the axiom gate naming the continuation-line axiom. (Also fixed attack 2's leftover EvilSpec.lean tripping attack 3's dead-file gate.) FIX 2 — remove the overclaimed framing (refuted by both reviewers). Corrected in README, the ApexSpec header + apex docstring, and (separately) the control MANIFEST: - "composes all ten loop-fidelity certificates" — FALSE. The apex proof is a STRUCTURAL FACTORIZATION; it references NONE of the ten (grep: 0) and would remain provable if one were deleted. They are independent local-fidelity lemmas, not links in the apex proof. - "every loop is individually fidelity-certified" — FALSE. base_2b's inner accumulation loop is threaded opaquely and uncertified — and it determines the FORS indices / WOTS digits, so a defect there could change the recomputed root while all eleven theorems still hold. - "the deployed verifier" — the proved subject is verify_mono, a private #![allow(dead_code)] monomorphic facade NOT called by the public API; the bridge to the deployed generic verifier is the finite differential test, not a machine-checked refinement. - "verify-path pyramid complete" — replaced with "intermediate verification layer"; the apex is an ACCEPTANCE CHARACTERIZATION, not closed-form FIPS-205 correctness. Also: FunsExternal header noted the Take axiom "remains" (stale — deleted in de-plumbing round 2); corrected. check.sh green over all eleven certificates under the fixed fail-closed parser (exit 0, 8 axioms audited for the apex). Nothing about the theorems changed — they were and are sound; only the audit tool and the claims about them are fixed. NOT DONE (remaining reviewer blockers, tracked): reproducible extract tuple (pin commits, de-hard-code extract.sh) + Cargo.lock / toolchain pin. Attestation remains gated behind review round 2 + the operator halt + the appeal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:55:10 +00:00
- **Not closed-form FIPS 205 correctness:** the folds are transliterations of
the extracted loops (the hash primitives stay opaque); nothing here relates
the recomputed root to a mathematical SLH-DSA specification.
review round 5: bind the policy, the specification bodies, and the harness The third reviewer demonstrated that the round-4 gate was closed at the EXPLOITS, not the MECHANISMS: it bound the manifest rows while the things he moved sat outside the binding. Three fail-opens, each executed end-to-end with the button printing ALL GREEN. No theorem, proof, fold, or model body changed. NEW-1 — the policy constant was unbound. `manifestFingerprint` covered `manifest` but never `allowedBoundary`, the sole predicate the enumeration tests against; adding one name re-opened the False-proof with the committed fingerprint BYTE-IDENTICAL. NEW-2 — the specification bodies were unbound. The statement fingerprint pinned each certificate's TYPE, which names its reference fold; redefining that fold to *be* the extracted loop left cone and type-hash identical while the certificate degenerated to `loop = loop`. 100% of the fidelity content lives in those defs. NEW-3 — the harness was unbound. Stubbing verification/lean-guard alone, without touching check.sh or the proofs, gave ALL GREEN in 3.6s over destroyed proofs. FIX — one mechanism rather than three patches. Audit.lean now emits a canonical AUDIT-MANIFEST block and check.sh binds to its SHA-256. The block covers the POLICY constants, every certificate's fully-elaborated statement, and every specification constant transitively reachable from those statements with its fully-elaborated BODY (41 constants; the closure is computed, so a new fold cannot appear without moving the digest; Prop-valued constants contribute their statement, by proof irrelevance). This also retires the 32-bit Expr.hash as the binding (NEW-5) — it survives only as a per-certificate diagnostic. Enumeration now covers EVERY declaration kind (a `def : False` passed before) in the eight certificate modules AND in Audit.lean itself — the auditor is no longer exempt (round-5 R1). A bare `axiom` in audited scope is now an error. Phase 0 purges stale .olean (the verdict must depend on committed bytes, not .gitignored build state — NEW-4), forbids any .lean outside gen/ and Proofs/, and sha256-pins the four model files AND lean-guard. lean-guard is KEPT rather than removed (the reviewer's portability advice is declined by operator decision): it is the memory cap and machine-wide lock that protect the build machine after a 12.2GB OOM took the host down. That trade-off is documented. check.sh's "Certificates proven:" line now comes from the audited manifest; the hand-kept CERTS array — the one authoritative claim string nothing bound — is deleted. check-selftest.sh: 14 attacks, all rejected, plus a check that the hashed block literally carries the twelve fold bodies. Attacks 9-14 are the reviewers' and an independent drill's own exploits, turned into regression tests. DOCS. TRUSTED-BASE gains item 11 (the REAL trusted computing base — lean-guard pinned; check.sh, the toolchain env, $AENEAS_HOME, python3 and Lean still trusted) and item 12 (the apex does not compose the ten). README: the audit description rewritten; the XMSS sibling-order claim downgraded from "pins" to "makes visible", with a new blanket non-claim covering all ten loop certificates; the de-plumbing file claim corrected (round 1 touched only verify_mono.rs, round 2 only helpers.rs — which is ON the deployed verify AND sign paths, now disclosed; wots.rs was never patched). RECORDED-RUN: three lines that stood inside a fence were a hand-written summary, not console output — fabricated evidence in the file whose purpose is machine evidence. They are removed and the fabrication is named in place, together with the correction that the "INDEPENDENT RUN" block predates this gate. New rule: nothing goes in a fence unless captured with tee/cat, and every block states its date, pin, and who ran it. The transcripts added here follow it. Also disclosed rather than buried: three bugs in my own test harness this round (an olean-purge build-order break, an attack rejected by the wrong rule, and a coverage assertion looking on the wrong line) — each would have let an attack pass or fail for an unrelated reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 20:57:46 +00:00
- **Read every loop certificate as "visible", not "correct".** This follows
from the previous point but is worth stating on its own, because the per-
certificate descriptions below are easy to over-read. Each reference fold is
built from the *same* extracted primitives the loop calls, so any defect in
the extracted code is faithfully copied into the fold and the theorem still
holds. What is machine-checked is the loop's *scaffolding* — trip count,
index arithmetic, state threading, argument order, branch structure. Whether
the address schedule, the Merkle sibling order, or the FORS leaf index match
FIPS 205 is a **human reading step**, not a proved one. (Round-5 review makes
this sharper: the certificates are individually meaningful only to the extent
someone has read each fold against the standard — see TRUSTED-BASE item 12.)
review round 1: fix the fail-open audit gate + remove the overclaimed framing External review (both standing reviewers, 2026-07-24) returned DO NOT ATTEST. The eleven Lean theorems compile with genuinely clean cones (both reviewers independently reconstructed them), but two real defects were found and are fixed here. FIX 1 — the axiom audit was FAIL-OPEN (the critical blocker). check.sh Phase 3 grepped a single physical line of each `#print axioms` report; Lean WRAPS long cones across lines, so for ht/fors_outer/APEX the audit checked only `[propext,` and silently ignored the continuation lines — a disallowed axiom on line 2+ passed (the GPT reviewer demonstrated `review_evil_ax` passing). Since check.sh is the sole source of the word "proven", this is unacceptable. - New parser: FLATTEN the whole report (join newlines) BEFORE parsing, then extract each certificate's complete bracketed cone with a literal-string (regex-safe) scan and subset-check every axiom. Missing/empty report => FAIL CLOSED. The audit now prints the count of axioms actually audited per cert (apex: 8, previously 1). - check-selftest.sh gains ATTACK 3: a smuggled axiom bundled with the apex so its cone WRAPS with the evil axiom on a continuation line — the exact exploit. Verified: all three attacks now rejected, attack 3 via the axiom gate naming the continuation-line axiom. (Also fixed attack 2's leftover EvilSpec.lean tripping attack 3's dead-file gate.) FIX 2 — remove the overclaimed framing (refuted by both reviewers). Corrected in README, the ApexSpec header + apex docstring, and (separately) the control MANIFEST: - "composes all ten loop-fidelity certificates" — FALSE. The apex proof is a STRUCTURAL FACTORIZATION; it references NONE of the ten (grep: 0) and would remain provable if one were deleted. They are independent local-fidelity lemmas, not links in the apex proof. - "every loop is individually fidelity-certified" — FALSE. base_2b's inner accumulation loop is threaded opaquely and uncertified — and it determines the FORS indices / WOTS digits, so a defect there could change the recomputed root while all eleven theorems still hold. - "the deployed verifier" — the proved subject is verify_mono, a private #![allow(dead_code)] monomorphic facade NOT called by the public API; the bridge to the deployed generic verifier is the finite differential test, not a machine-checked refinement. - "verify-path pyramid complete" — replaced with "intermediate verification layer"; the apex is an ACCEPTANCE CHARACTERIZATION, not closed-form FIPS-205 correctness. Also: FunsExternal header noted the Take axiom "remains" (stale — deleted in de-plumbing round 2); corrected. check.sh green over all eleven certificates under the fixed fail-closed parser (exit 0, 8 axioms audited for the apex). Nothing about the theorems changed — they were and are sound; only the audit tool and the claims about them are fixed. NOT DONE (remaining reviewer blockers, tracked): reproducible extract tuple (pin commits, de-hard-code extract.sh) + Cargo.lock / toolchain pin. Attestation remains gated behind review round 2 + the operator halt + the appeal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:55:10 +00:00
This is a real **intermediate** verification layer, not an end-to-end
formal verification of the deployed verifier. After de-plumbing rounds 1+2
the model carries no plumbing axioms on the verify path — its external
surface is exactly the five SHA-2 oracles (plus off-path zeroize impls).
The trust base and residual assumptions are stated in
[TRUSTED-BASE.md](TRUSTED-BASE.md).
2026-07-23 09:34:59 +00:00
- **`fips205.chain_free_loop_eq`** (Algorithm 5, WOTS+ chaining): the
extracted `chain_free` loop equals the explicit s-fold hash chain, with
the hash address set to i, i+1, …, i+s1 in turn. This rules out —
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
machine-checked, for the monomorphic SHA2-128s `verify_mono` path — an
review round 4: bind the cert set, statements, and model bytes (F1/F2/F3) 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>
2026-07-27 17:47:39 +00:00
off-by-one loop bound and wrong state threading. (It does **not** rule out
a wrong ADRS field: the reference fold is built from the same extracted
`set_hash_address` primitive the loop calls, so a wrong field would be
faithfully copied into the fold and the theorem would still hold. What the
certificate pins is what the extracted code does at each index, so a wrong
field is *visible* in the certificate, not *excluded* by it — the mapping
onto FIPS 205 Alg 5 is a human reading step, consistent with "the folds are
transliterations of the extracted loops" above.) Its
2026-07-23 09:34:59 +00:00
`#print axioms` cone is **exactly** `[propext, Classical.choice,
Quot.sound, verify_mono.oracle.f]` — the three kernel axioms plus the one
hash oracle it touches, and nothing else (no transpiler plumbing; the u32
range machinery was discharged with real definitions). check.sh Phase 3
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
(the in-Lean exact-cone audit) fails the build if any certificate's cone
differs from its expected set — an extra axiom or a dropped oracle both
break it.
2026-07-23 09:34:59 +00:00
- **`fips205.wots_loop1_eq`** (Algorithm 8, WOTS+ pk recomputation — the
chain loop): the extracted `wots_pk_from_sig_free_loop1` equals the fold
that, at each index i in [0, LEN), sets the chain address to i and runs
`chain_free` on sig[i] starting at digit msg[i] for W1msg[i] steps,
writing tmp[i]. This is the layer above chain: it consumes `chain_free`
and pins that the LEN chains run with the right start indices, step
counts, and slots. Cone: kernel three + `verify_mono.oracle.f`.
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`** (Algorithm 10, XMSS pk-from-sig — the
authentication-path Merkle loop): the extracted
`xmss_pk_from_sig_free_loop` equals the fold that, at step k, sets the
tree height to k+1, tests bit k of the leaf index, and on an even bit
halves the tree index and hashes H(node ∥ auth[k]), on an odd bit sets
review round 5: bind the policy, the specification bodies, and the harness The third reviewer demonstrated that the round-4 gate was closed at the EXPLOITS, not the MECHANISMS: it bound the manifest rows while the things he moved sat outside the binding. Three fail-opens, each executed end-to-end with the button printing ALL GREEN. No theorem, proof, fold, or model body changed. NEW-1 — the policy constant was unbound. `manifestFingerprint` covered `manifest` but never `allowedBoundary`, the sole predicate the enumeration tests against; adding one name re-opened the False-proof with the committed fingerprint BYTE-IDENTICAL. NEW-2 — the specification bodies were unbound. The statement fingerprint pinned each certificate's TYPE, which names its reference fold; redefining that fold to *be* the extracted loop left cone and type-hash identical while the certificate degenerated to `loop = loop`. 100% of the fidelity content lives in those defs. NEW-3 — the harness was unbound. Stubbing verification/lean-guard alone, without touching check.sh or the proofs, gave ALL GREEN in 3.6s over destroyed proofs. FIX — one mechanism rather than three patches. Audit.lean now emits a canonical AUDIT-MANIFEST block and check.sh binds to its SHA-256. The block covers the POLICY constants, every certificate's fully-elaborated statement, and every specification constant transitively reachable from those statements with its fully-elaborated BODY (41 constants; the closure is computed, so a new fold cannot appear without moving the digest; Prop-valued constants contribute their statement, by proof irrelevance). This also retires the 32-bit Expr.hash as the binding (NEW-5) — it survives only as a per-certificate diagnostic. Enumeration now covers EVERY declaration kind (a `def : False` passed before) in the eight certificate modules AND in Audit.lean itself — the auditor is no longer exempt (round-5 R1). A bare `axiom` in audited scope is now an error. Phase 0 purges stale .olean (the verdict must depend on committed bytes, not .gitignored build state — NEW-4), forbids any .lean outside gen/ and Proofs/, and sha256-pins the four model files AND lean-guard. lean-guard is KEPT rather than removed (the reviewer's portability advice is declined by operator decision): it is the memory cap and machine-wide lock that protect the build machine after a 12.2GB OOM took the host down. That trade-off is documented. check.sh's "Certificates proven:" line now comes from the audited manifest; the hand-kept CERTS array — the one authoritative claim string nothing bound — is deleted. check-selftest.sh: 14 attacks, all rejected, plus a check that the hashed block literally carries the twelve fold bodies. Attacks 9-14 are the reviewers' and an independent drill's own exploits, turned into regression tests. DOCS. TRUSTED-BASE gains item 11 (the REAL trusted computing base — lean-guard pinned; check.sh, the toolchain env, $AENEAS_HOME, python3 and Lean still trusted) and item 12 (the apex does not compose the ten). README: the audit description rewritten; the XMSS sibling-order claim downgraded from "pins" to "makes visible", with a new blanket non-claim covering all ten loop certificates; the de-plumbing file claim corrected (round 1 touched only verify_mono.rs, round 2 only helpers.rs — which is ON the deployed verify AND sign paths, now disclosed; wots.rs was never patched). RECORDED-RUN: three lines that stood inside a fence were a hand-written summary, not console output — fabricated evidence in the file whose purpose is machine evidence. They are removed and the fabrication is named in place, together with the correction that the "INDEPENDENT RUN" block predates this gate. New rule: nothing goes in a fence unless captured with tee/cat, and every block states its date, pin, and who ran it. The transcripts added here follow it. Also disclosed rather than buried: three bugs in my own test harness this round (an olean-purge build-order break, an attack rejected by the wrong rule, and a coverage assertion looking on the wrong line) — each would have let an attack pass or fail for an unrelated reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 20:57:46 +00:00
the tree index to (i1)/2 and hashes H(auth[k] ∥ node). This makes the
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
Merkle sibling ORDER (the even/odd rule), the tree-height/tree-index
review round 5: bind the policy, the specification bodies, and the harness The third reviewer demonstrated that the round-4 gate was closed at the EXPLOITS, not the MECHANISMS: it bound the manifest rows while the things he moved sat outside the binding. Three fail-opens, each executed end-to-end with the button printing ALL GREEN. No theorem, proof, fold, or model body changed. NEW-1 — the policy constant was unbound. `manifestFingerprint` covered `manifest` but never `allowedBoundary`, the sole predicate the enumeration tests against; adding one name re-opened the False-proof with the committed fingerprint BYTE-IDENTICAL. NEW-2 — the specification bodies were unbound. The statement fingerprint pinned each certificate's TYPE, which names its reference fold; redefining that fold to *be* the extracted loop left cone and type-hash identical while the certificate degenerated to `loop = loop`. 100% of the fidelity content lives in those defs. NEW-3 — the harness was unbound. Stubbing verification/lean-guard alone, without touching check.sh or the proofs, gave ALL GREEN in 3.6s over destroyed proofs. FIX — one mechanism rather than three patches. Audit.lean now emits a canonical AUDIT-MANIFEST block and check.sh binds to its SHA-256. The block covers the POLICY constants, every certificate's fully-elaborated statement, and every specification constant transitively reachable from those statements with its fully-elaborated BODY (41 constants; the closure is computed, so a new fold cannot appear without moving the digest; Prop-valued constants contribute their statement, by proof irrelevance). This also retires the 32-bit Expr.hash as the binding (NEW-5) — it survives only as a per-certificate diagnostic. Enumeration now covers EVERY declaration kind (a `def : False` passed before) in the eight certificate modules AND in Audit.lean itself — the auditor is no longer exempt (round-5 R1). A bare `axiom` in audited scope is now an error. Phase 0 purges stale .olean (the verdict must depend on committed bytes, not .gitignored build state — NEW-4), forbids any .lean outside gen/ and Proofs/, and sha256-pins the four model files AND lean-guard. lean-guard is KEPT rather than removed (the reviewer's portability advice is declined by operator decision): it is the memory cap and machine-wide lock that protect the build machine after a 12.2GB OOM took the host down. That trade-off is documented. check.sh's "Certificates proven:" line now comes from the audited manifest; the hand-kept CERTS array — the one authoritative claim string nothing bound — is deleted. check-selftest.sh: 14 attacks, all rejected, plus a check that the hashed block literally carries the twelve fold bodies. Attacks 9-14 are the reviewers' and an independent drill's own exploits, turned into regression tests. DOCS. TRUSTED-BASE gains item 11 (the REAL trusted computing base — lean-guard pinned; check.sh, the toolchain env, $AENEAS_HOME, python3 and Lean still trusted) and item 12 (the apex does not compose the ten). README: the audit description rewritten; the XMSS sibling-order claim downgraded from "pins" to "makes visible", with a new blanket non-claim covering all ten loop certificates; the de-plumbing file claim corrected (round 1 touched only verify_mono.rs, round 2 only helpers.rs — which is ON the deployed verify AND sign paths, now disclosed; wots.rs was never patched). RECORDED-RUN: three lines that stood inside a fence were a hand-written summary, not console output — fabricated evidence in the file whose purpose is machine evidence. They are removed and the fabrication is named in place, together with the correction that the "INDEPENDENT RUN" block predates this gate. New rule: nothing goes in a fence unless captured with tee/cat, and every block states its date, pin, and who ran it. The transcripts added here follow it. Also disclosed rather than buried: three bugs in my own test harness this round (an olean-purge build-order break, an attack rejected by the wrong rule, and a coverage assertion looking on the wrong line) — each would have let an attack pass or fail for an unrelated reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 20:57:46 +00:00
address schedule, and the auth-path indexing VISIBLE in the certificate —
**it does not establish them as correct.** `xmssFoldN` calls the same
extracted `set_tree_height`/`get_tree_index`/`oracle.h` that the loop body
calls, so a swapped sibling order would be copied into the fold and the
theorem would still hold. Read that as: the certificate pins what the
extracted code *does* at each step; whether that matches FIPS 205
Algorithm 10 is a human reading step. Cone: kernel three +
`verify_mono.oracle.h` (the first
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
certificate where H enters; F does not — the loop runs above the WOTS+
computation).
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`** (Algorithm 12, hypertree verification — the
layer walk): the extracted `ht_verify_free_loop` equals the fold that,
at layer j, splits the tree index (idx_leaf = idx_tree mod 2^h' by
mask+cast, then idx_tree >>= h'), sets the layer address to j and the
tree address to the shifted index, and recomputes the node through
review round 5: bind the policy, the specification bodies, and the harness The third reviewer demonstrated that the round-4 gate was closed at the EXPLOITS, not the MECHANISMS: it bound the manifest rows while the things he moved sat outside the binding. Three fail-opens, each executed end-to-end with the button printing ALL GREEN. No theorem, proof, fold, or model body changed. NEW-1 — the policy constant was unbound. `manifestFingerprint` covered `manifest` but never `allowedBoundary`, the sole predicate the enumeration tests against; adding one name re-opened the False-proof with the committed fingerprint BYTE-IDENTICAL. NEW-2 — the specification bodies were unbound. The statement fingerprint pinned each certificate's TYPE, which names its reference fold; redefining that fold to *be* the extracted loop left cone and type-hash identical while the certificate degenerated to `loop = loop`. 100% of the fidelity content lives in those defs. NEW-3 — the harness was unbound. Stubbing verification/lean-guard alone, without touching check.sh or the proofs, gave ALL GREEN in 3.6s over destroyed proofs. FIX — one mechanism rather than three patches. Audit.lean now emits a canonical AUDIT-MANIFEST block and check.sh binds to its SHA-256. The block covers the POLICY constants, every certificate's fully-elaborated statement, and every specification constant transitively reachable from those statements with its fully-elaborated BODY (41 constants; the closure is computed, so a new fold cannot appear without moving the digest; Prop-valued constants contribute their statement, by proof irrelevance). This also retires the 32-bit Expr.hash as the binding (NEW-5) — it survives only as a per-certificate diagnostic. Enumeration now covers EVERY declaration kind (a `def : False` passed before) in the eight certificate modules AND in Audit.lean itself — the auditor is no longer exempt (round-5 R1). A bare `axiom` in audited scope is now an error. Phase 0 purges stale .olean (the verdict must depend on committed bytes, not .gitignored build state — NEW-4), forbids any .lean outside gen/ and Proofs/, and sha256-pins the four model files AND lean-guard. lean-guard is KEPT rather than removed (the reviewer's portability advice is declined by operator decision): it is the memory cap and machine-wide lock that protect the build machine after a 12.2GB OOM took the host down. That trade-off is documented. check.sh's "Certificates proven:" line now comes from the audited manifest; the hand-kept CERTS array — the one authoritative claim string nothing bound — is deleted. check-selftest.sh: 14 attacks, all rejected, plus a check that the hashed block literally carries the twelve fold bodies. Attacks 9-14 are the reviewers' and an independent drill's own exploits, turned into regression tests. DOCS. TRUSTED-BASE gains item 11 (the REAL trusted computing base — lean-guard pinned; check.sh, the toolchain env, $AENEAS_HOME, python3 and Lean still trusted) and item 12 (the apex does not compose the ten). README: the audit description rewritten; the XMSS sibling-order claim downgraded from "pins" to "makes visible", with a new blanket non-claim covering all ten loop certificates; the de-plumbing file claim corrected (round 1 touched only verify_mono.rs, round 2 only helpers.rs — which is ON the deployed verify AND sign paths, now disclosed; wots.rs was never patched). RECORDED-RUN: three lines that stood inside a fence were a hand-written summary, not console output — fabricated evidence in the file whose purpose is machine evidence. They are removed and the fabrication is named in place, together with the correction that the "INDEPENDENT RUN" block predates this gate. New rule: nothing goes in a fence unless captured with tee/cat, and every block states its date, pin, and who ran it. The transcripts added here follow it. Also disclosed rather than buried: three bugs in my own test harness this round (an olean-purge build-order break, an attack rejected by the wrong rule, and a coverage assertion looking on the wrong line) — each would have let an attack pass or fail for an unrelated reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 20:57:46 +00:00
`xmss_pk_from_sig` on the j-th XMSS signature. This makes the layer
schedule of hypertree verification visible in the certificate (same
transliteration caveat as above); the final node = pk_root comparison
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
sits one bind above, in `ht_verify_free`, and belongs to the apex
composition. Cone: kernel three + `verify_mono.oracle.{f, h, t_l}`
the full WOTS+/XMSS machinery referenced through the fold, and nothing
else.
2026-07-23 09:34:59 +00:00
Foundations behind this (2026-07-22/23): the Aeneas-compat patch (additive
monomorphic verify module through a named oracle boundary; charon + aeneas
exit 0); the u32 range-loop de-plumbing (faithful `Step` defs vs pinned
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
rustc, axiom-clean); the 8-site source de-plumbing (snapshot commit
`6f6a9d6`: `try_from`/`is_err`/`unwrap` on pre-masked values → plain
casts, the WOTS+ checksum `iter().take()` + `&u32` Sub → an index loop —
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
each site a local rewrite whose equivalence is argued in the commit and
checked, for SHA2-128s, by the differential test; the obsoleted transpiler
axioms were deleted from the external files); fidelity pinned by that
bridge: re-pin to the NIST-ACVP source commit; state the real coverage numbers The companion fips205-source commit adds NIST ACVP SHA2-128s verification vectors and a real differential bridge. This repo re-pins to it and replaces the word "finite" with numbers, per external review rounds 4-6. - extract.sh + PROVENANCE re-pinned 797b4ef -> 3153988. The provenance guard did its job first: it REFUSED the moved source until the pin was rotated deliberately. - VERIFIED that the test/vector commit does not perturb the proved model: after re-extraction all four pinned model files are byte-identical (Types db720b4a…, Funs 7b7de55f…, TypesExternal 37958beb…, FunsExternal 5efe551c…), check.sh is ALL GREEN, and the audit digest is unchanged (d83e297a…). The only regenerated difference is the untracked Aeneas *_Template.lean byproduct, which Phase 0 purges. - TRUSTED-BASE item 9 and the README now state the bridge's actual size: 131 assertion points (was 9), of which 20 are NIST ACVP SHA2-128s known-answer tests run against the proved path — 10 from the `internal` group (whose message IS M', exactly what slh_verify_128s consumes) and 10 from `external pure` where mono, the deployed verifier and NIST must all agree, 9 of those with a NON-EMPTY context, which is the first empirical check of the domain-separator byte and context prefix that item 10 declares outside every proof. Both documents keep saying plainly that a passing differential test is evidence, not a proof. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 07:46:29 +00:00
differential test in the snapshot — since 2026-07-28 a randomized bridge
(12 rounds, corruption across the whole signature, wrong-key and wrong-context
cases) plus NIST ACVP 128s known-answer tests — re-run green after every
source patch.
2026-07-23 09:34:59 +00:00
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`**
(Algorithm 17, FORS pk-from-sig): a nested loop, split into two theorems.
review round 5: bind the policy, the specification bodies, and the harness The third reviewer demonstrated that the round-4 gate was closed at the EXPLOITS, not the MECHANISMS: it bound the manifest rows while the things he moved sat outside the binding. Three fail-opens, each executed end-to-end with the button printing ALL GREEN. No theorem, proof, fold, or model body changed. NEW-1 — the policy constant was unbound. `manifestFingerprint` covered `manifest` but never `allowedBoundary`, the sole predicate the enumeration tests against; adding one name re-opened the False-proof with the committed fingerprint BYTE-IDENTICAL. NEW-2 — the specification bodies were unbound. The statement fingerprint pinned each certificate's TYPE, which names its reference fold; redefining that fold to *be* the extracted loop left cone and type-hash identical while the certificate degenerated to `loop = loop`. 100% of the fidelity content lives in those defs. NEW-3 — the harness was unbound. Stubbing verification/lean-guard alone, without touching check.sh or the proofs, gave ALL GREEN in 3.6s over destroyed proofs. FIX — one mechanism rather than three patches. Audit.lean now emits a canonical AUDIT-MANIFEST block and check.sh binds to its SHA-256. The block covers the POLICY constants, every certificate's fully-elaborated statement, and every specification constant transitively reachable from those statements with its fully-elaborated BODY (41 constants; the closure is computed, so a new fold cannot appear without moving the digest; Prop-valued constants contribute their statement, by proof irrelevance). This also retires the 32-bit Expr.hash as the binding (NEW-5) — it survives only as a per-certificate diagnostic. Enumeration now covers EVERY declaration kind (a `def : False` passed before) in the eight certificate modules AND in Audit.lean itself — the auditor is no longer exempt (round-5 R1). A bare `axiom` in audited scope is now an error. Phase 0 purges stale .olean (the verdict must depend on committed bytes, not .gitignored build state — NEW-4), forbids any .lean outside gen/ and Proofs/, and sha256-pins the four model files AND lean-guard. lean-guard is KEPT rather than removed (the reviewer's portability advice is declined by operator decision): it is the memory cap and machine-wide lock that protect the build machine after a 12.2GB OOM took the host down. That trade-off is documented. check.sh's "Certificates proven:" line now comes from the audited manifest; the hand-kept CERTS array — the one authoritative claim string nothing bound — is deleted. check-selftest.sh: 14 attacks, all rejected, plus a check that the hashed block literally carries the twelve fold bodies. Attacks 9-14 are the reviewers' and an independent drill's own exploits, turned into regression tests. DOCS. TRUSTED-BASE gains item 11 (the REAL trusted computing base — lean-guard pinned; check.sh, the toolchain env, $AENEAS_HOME, python3 and Lean still trusted) and item 12 (the apex does not compose the ten). README: the audit description rewritten; the XMSS sibling-order claim downgraded from "pins" to "makes visible", with a new blanket non-claim covering all ten loop certificates; the de-plumbing file claim corrected (round 1 touched only verify_mono.rs, round 2 only helpers.rs — which is ON the deployed verify AND sign paths, now disclosed; wots.rs was never patched). RECORDED-RUN: three lines that stood inside a fence were a hand-written summary, not console output — fabricated evidence in the file whose purpose is machine evidence. They are removed and the fabrication is named in place, together with the correction that the "INDEPENDENT RUN" block predates this gate. New rule: nothing goes in a fence unless captured with tee/cat, and every block states its date, pin, and who ran it. The transcripts added here follow it. Also disclosed rather than buried: three bugs in my own test harness this round (an olean-purge build-order break, an attack rejected by the wrong rule, and a coverage assertion looking on the wrong line) — each would have let an attack pass or fail for an unrelated reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 20:57:46 +00:00
The inner one equates the auth-path Merkle fold for a single FORS tree (bit
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
source `indices[i] >> j`, `H` in the even/odd sibling order) — cone
review round 5: bind the policy, the specification bodies, and the harness The third reviewer demonstrated that the round-4 gate was closed at the EXPLOITS, not the MECHANISMS: it bound the manifest rows while the things he moved sat outside the binding. Three fail-opens, each executed end-to-end with the button printing ALL GREEN. No theorem, proof, fold, or model body changed. NEW-1 — the policy constant was unbound. `manifestFingerprint` covered `manifest` but never `allowedBoundary`, the sole predicate the enumeration tests against; adding one name re-opened the False-proof with the committed fingerprint BYTE-IDENTICAL. NEW-2 — the specification bodies were unbound. The statement fingerprint pinned each certificate's TYPE, which names its reference fold; redefining that fold to *be* the extracted loop left cone and type-hash identical while the certificate degenerated to `loop = loop`. 100% of the fidelity content lives in those defs. NEW-3 — the harness was unbound. Stubbing verification/lean-guard alone, without touching check.sh or the proofs, gave ALL GREEN in 3.6s over destroyed proofs. FIX — one mechanism rather than three patches. Audit.lean now emits a canonical AUDIT-MANIFEST block and check.sh binds to its SHA-256. The block covers the POLICY constants, every certificate's fully-elaborated statement, and every specification constant transitively reachable from those statements with its fully-elaborated BODY (41 constants; the closure is computed, so a new fold cannot appear without moving the digest; Prop-valued constants contribute their statement, by proof irrelevance). This also retires the 32-bit Expr.hash as the binding (NEW-5) — it survives only as a per-certificate diagnostic. Enumeration now covers EVERY declaration kind (a `def : False` passed before) in the eight certificate modules AND in Audit.lean itself — the auditor is no longer exempt (round-5 R1). A bare `axiom` in audited scope is now an error. Phase 0 purges stale .olean (the verdict must depend on committed bytes, not .gitignored build state — NEW-4), forbids any .lean outside gen/ and Proofs/, and sha256-pins the four model files AND lean-guard. lean-guard is KEPT rather than removed (the reviewer's portability advice is declined by operator decision): it is the memory cap and machine-wide lock that protect the build machine after a 12.2GB OOM took the host down. That trade-off is documented. check.sh's "Certificates proven:" line now comes from the audited manifest; the hand-kept CERTS array — the one authoritative claim string nothing bound — is deleted. check-selftest.sh: 14 attacks, all rejected, plus a check that the hashed block literally carries the twelve fold bodies. Attacks 9-14 are the reviewers' and an independent drill's own exploits, turned into regression tests. DOCS. TRUSTED-BASE gains item 11 (the REAL trusted computing base — lean-guard pinned; check.sh, the toolchain env, $AENEAS_HOME, python3 and Lean still trusted) and item 12 (the apex does not compose the ten). README: the audit description rewritten; the XMSS sibling-order claim downgraded from "pins" to "makes visible", with a new blanket non-claim covering all ten loop certificates; the de-plumbing file claim corrected (round 1 touched only verify_mono.rs, round 2 only helpers.rs — which is ON the deployed verify AND sign paths, now disclosed; wots.rs was never patched). RECORDED-RUN: three lines that stood inside a fence were a hand-written summary, not console output — fabricated evidence in the file whose purpose is machine evidence. They are removed and the fabrication is named in place, together with the correction that the "INDEPENDENT RUN" block predates this gate. New rule: nothing goes in a fence unless captured with tee/cat, and every block states its date, pin, and who ran it. The transcripts added here follow it. Also disclosed rather than buried: three bugs in my own test harness this round (an olean-purge build-order break, an attack rejected by the wrong rule, and a coverage assertion looking on the wrong line) — each would have let an attack pass or fail for an unrelated reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 20:57:46 +00:00
kernel-3 + `oracle.h`. The outer one equates the K-tree fold: for each tree
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
compute the leaf with `F` at index `(i<<a)+indices[i]`, run the inner
Merkle loop, write `root[i]` — cone kernel-3 + `oracle.{f, h}`. Split into
two files under the memory discipline; the outer step lemma closes by
peeling its 16-bind body with `bind_congr` (a bare `rfl` there whnf-times-
out over the nested inner `loop`).
- **input-prep** (`fips205.to_int_loop_eq`, `to_byte_loop_eq`,
`wots_csum_loop_eq`, `base2b_outer_loop_eq` — Algorithms 2/3/4 + the WOTS+
checksum): the byte→integer, integer→byte, checksum, and digit-decomposition
loops that prepare the verifier's inputs. All four cones are **exactly**
`[propext, Classical.choice, Quot.sound]` — pure kernel-3, no hash oracle
(byte/bit arithmetic touches no hash). `base_2b`'s inner `while` loop is
threaded opaquely, as every layer treats its sub-loops. These proofs became
possible after **de-plumbing round 2** (snapshot `bea1051`) rewrote
`to_int`'s `iter().take()` and `base_2b`'s `iter_mut()` as index loops,
removing the last `Take`/`IterMut` iterator adapters; the obsoleted `Take`
axiom was then deleted.
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
The apex (`slh_verify_128s_accepts_iff`, above) sits at the top of this layer:
the extracted `verify_mono::slh_verify_128s` accepts iff the recomputed
hypertree root byte-equals the pinned public-key root. What remains genuinely
unproven is stated in "What is NOT (yet) established" above — most sharply the
opaque `base_2b` inner loop (no certificate) and the bridge from this private
`verify_mono` facade to the deployed generic verifier (a finite differential
test, not a machine-checked refinement). Each certificate is audited to the
same boundary.
## Subject
- Upstream: `integritychain/fips205` — pure-Rust FIPS 205 (final standard,
2024-08-13), zero `unsafe`, `no_std`, const-generic parameterization,
modules mirroring the FIPS 205 algorithm structure.
- Pinned at upstream commit `30bac08580aa61f653e5436d1bbacb5ffac446c4`
(2025-09-01), snapshotted with full history at
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
`saymrwulf/fips205-source`. The verbatim-import base commit's only
deviation from upstream is the removal of CI workflows (documented in
that commit); the Aeneas-compat and de-plumbing patches then landed as
transparent, individually-justified commits on top — never upstream.
The current snapshot head is **`797b4ef`** (the round-2 reproducibility
commit — committed `Cargo.lock` + pinned `rust-toolchain.toml` — on top of
de-plumbing round 2, `bea1051`); the model in this repo is extracted from
it, and `verification/extract.sh` refuses any other commit. **No
affiliation with, and no changes proposed to, the upstream project.**
- Parameter set: **SLH-DSA-SHA2-128s** first (the small-signature profile
deployed in the firmware/code-signing lane). The architecture
generalizes; each further parameter set is a separate claim (rigor
invariant R2).
## Scope
review round 4: bind the cert set, statements, and model bytes (F1/F2/F3) 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>
2026-07-27 17:47:39 +00:00
**Verify path only, rooted at `slh_verify_internal`.** The extraction root
is `verify_mono::slh_verify_128s`, which is `slh_verify_internal_free(M, sig,
pk)` — it takes the already-assembled message digest input **M as an
argument**. So the covered cone is:
```
review round 4: bind the cert set, statements, and model bytes (F1/F2/F3) 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>
2026-07-27 17:47:39 +00:00
slh_verify_internal(M, …) ← the extraction ROOT (M is an input)
-> fors_pk_from_sig
-> ht_verify -> xmss_pk_from_sig -> wots_pk_from_sig -> chain
```
review round 4: bind the cert set, statements, and model bytes (F1/F2/F3) 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>
2026-07-27 17:47:39 +00:00
Everything **above** this root, in `slh_verify`/`verify` (`src/lib.rs`), is
OUT of scope and is stated as such in [TRUSTED-BASE.md](TRUSTED-BASE.md): M
assembly, the pure-vs-prehash **domain-separator byte** (`0u8` for `verify`,
`1u8` for `hash_verify` — the entire cross-variant separation), the
`ctx.len() > 255` check, and signature/public-key deserialization. A reader
must NOT read `slh_verify -> slh_verify_internal` as "the top of the verify
path is covered" — it is not; the top-of-path input handling is trusted base.
Key generation and signing are out of scope (trusted base), exactly as
ed25519 signing was. The five verify-path hash oracles (`h_msg, f, h,
t_l, t_len` — SHA-2 instantiations; `prf`/`prf_msg` are sign-side only
and never enter the cone) are opaque external models with written
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
justifications. They are the *only* things beyond Lean's three kernel
axioms that any certificate cone contains: each cone is exactly the
kernel three plus the specific oracles that certificate's computation
reaches (e.g. `chain` reaches `F`, so `oracle.f` is inside its cone; the
input-prep helpers reach no hash, so their cones are kernel-3 alone).
That the cones contain *nothing else* — no transpiler plumbing, no
hidden axiom — is what the audit enforces (honesty invariant H4); their
semantics are the standing SHA-2 oracle boundary documented in
[TRUSTED-BASE.md](TRUSTED-BASE.md).
## Gate-0 record (2026-07-22)
Per TARGETS.md ("re-verify before use"), the subject was probed before
this repository was created:
- **Charon**: clean (`charon cargo --preset=aeneas`, roots at the verify
cone, `sha2/sha3/zeroize/rand_core` opaque, features
`slh_dsa_sha2_128s`) — LLBC produced, exit 0.
- **Aeneas**: translated the entire const-generic verify cone to Lean
definitions (`wots.chain` … `slh.slh_verify_internal` all generated),
with exactly **one obstruction class** (3 unique errors): the
`crate::hashers::Hashers` struct of plain **function pointers** cannot
be translated.
- **Phase 1 — DONE (2026-07-22)**: the Aeneas-compat patch landed in
`fips205-source` (snapshot `2d89ee3`): an additive monomorphic SHA2-128s
verify module (`src/verify_mono.rs`) whose hash suite is reached through
named free functions in `verify_mono::oracle` (marked opaque at the
Charon boundary) — the `sha512_*`-shim pattern. Two further compat
refinements: the message-digest input M' passes as a single `&[u8]`
(nested `&[&[u8]]` is untranslatable), and one `let-else` became the
`is_err`/`unwrap` idiom. `verification/extract.sh` now re-derives the
model from the mono root; charon + aeneas both exit 0, and
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
`verification/check.sh` compiles the result. At this compat-patch commit
the only change to pre-existing code was two lines wiring the new module;
review round 5: bind the policy, the specification bodies, and the harness The third reviewer demonstrated that the round-4 gate was closed at the EXPLOITS, not the MECHANISMS: it bound the manifest rows while the things he moved sat outside the binding. Three fail-opens, each executed end-to-end with the button printing ALL GREEN. No theorem, proof, fold, or model body changed. NEW-1 — the policy constant was unbound. `manifestFingerprint` covered `manifest` but never `allowedBoundary`, the sole predicate the enumeration tests against; adding one name re-opened the False-proof with the committed fingerprint BYTE-IDENTICAL. NEW-2 — the specification bodies were unbound. The statement fingerprint pinned each certificate's TYPE, which names its reference fold; redefining that fold to *be* the extracted loop left cone and type-hash identical while the certificate degenerated to `loop = loop`. 100% of the fidelity content lives in those defs. NEW-3 — the harness was unbound. Stubbing verification/lean-guard alone, without touching check.sh or the proofs, gave ALL GREEN in 3.6s over destroyed proofs. FIX — one mechanism rather than three patches. Audit.lean now emits a canonical AUDIT-MANIFEST block and check.sh binds to its SHA-256. The block covers the POLICY constants, every certificate's fully-elaborated statement, and every specification constant transitively reachable from those statements with its fully-elaborated BODY (41 constants; the closure is computed, so a new fold cannot appear without moving the digest; Prop-valued constants contribute their statement, by proof irrelevance). This also retires the 32-bit Expr.hash as the binding (NEW-5) — it survives only as a per-certificate diagnostic. Enumeration now covers EVERY declaration kind (a `def : False` passed before) in the eight certificate modules AND in Audit.lean itself — the auditor is no longer exempt (round-5 R1). A bare `axiom` in audited scope is now an error. Phase 0 purges stale .olean (the verdict must depend on committed bytes, not .gitignored build state — NEW-4), forbids any .lean outside gen/ and Proofs/, and sha256-pins the four model files AND lean-guard. lean-guard is KEPT rather than removed (the reviewer's portability advice is declined by operator decision): it is the memory cap and machine-wide lock that protect the build machine after a 12.2GB OOM took the host down. That trade-off is documented. check.sh's "Certificates proven:" line now comes from the audited manifest; the hand-kept CERTS array — the one authoritative claim string nothing bound — is deleted. check-selftest.sh: 14 attacks, all rejected, plus a check that the hashed block literally carries the twelve fold bodies. Attacks 9-14 are the reviewers' and an independent drill's own exploits, turned into regression tests. DOCS. TRUSTED-BASE gains item 11 (the REAL trusted computing base — lean-guard pinned; check.sh, the toolchain env, $AENEAS_HOME, python3 and Lean still trusted) and item 12 (the apex does not compose the ten). README: the audit description rewritten; the XMSS sibling-order claim downgraded from "pins" to "makes visible", with a new blanket non-claim covering all ten loop certificates; the de-plumbing file claim corrected (round 1 touched only verify_mono.rs, round 2 only helpers.rs — which is ON the deployed verify AND sign paths, now disclosed; wots.rs was never patched). RECORDED-RUN: three lines that stood inside a fence were a hand-written summary, not console output — fabricated evidence in the file whose purpose is machine evidence. They are removed and the fabrication is named in place, together with the correction that the "INDEPENDENT RUN" block predates this gate. New rule: nothing goes in a fence unless captured with tee/cat, and every block states its date, pin, and who ran it. The transcripts added here follow it. Also disclosed rather than buried: three bugs in my own test harness this round (an olean-purge build-order break, an attack rejected by the wrong rule, and a coverage assertion looking on the wrong line) — each would have let an attack pass or fail for an unrelated reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 20:57:46 +00:00
the generic paths and all twelve parameter sets stayed untouched. **That
scoping does not extend to the later de-plumbing commits, and the difference
matters:** round 1 (`6f6a9d6`) touched only the private `verify_mono.rs`, but
round 2 (`bea1051`) rewrote `to_int` and `base_2b` in **`src/helpers.rs`,
which the DEPLOYED generic verify path and the SIGNING path also call**
(`slh.rs`, `wots.rs`, `fors.rs`). So the snapshot's deployed verifier — the
one the differential test compares against — is itself patched relative to
upstream `30bac08`. `src/wots.rs` was never modified by any patch commit (an
earlier revision of this README wrongly named it). See the snapshot history
at head `797b4ef` and TRUSTED-BASE.md item 7.
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
## What is claimed (the button is green)
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
Each certificate is a statement about the **extracted** functions (H3),
compiled by `verification/check.sh` with the in-Lean exact-cone audit (H1):
chain semantics, WOTS+ pk recomputation, XMSS path recomputation, hypertree
acceptance, FORS pk recomputation, the input-prep helpers, and the apex —
`verify_mono::slh_verify_128s` accepts iff the recomputed hypertree root
equals the pinned public-key root. The precise scope and non-claims are in
the STATUS section above.
**The allowed axiom set, stated precisely:** unlike the ed25519 field and
scalar layers (whose cones are exactly `[propext, Classical.choice,
Quot.sound]`), the hash oracles permeate *every* SLH-DSA layer — `chain`
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
already calls `F`. Each certificate's cone is therefore the three kernel
axioms **plus exactly the named oracles its computation reaches** (and
nothing else). The transpiler-plumbing axioms that once sat in
`FunsExternal.lean` were discharged (de-plumbing rounds 1+2) before any
certificate shipped; the audit fails the button if anything outside a
certificate's expected boundary — plumbing, an extra oracle, or a dropped
one — appears in its cone.
## Discipline
Every Lean compile in this repository runs under `verification/lean-guard`
round 7: assert pin-map completeness (NEW-13), correct the count to 137, fix the regeneration-scope contradiction The third reviewer demonstrated NEW-13: PROVENANCE.json is a tracked file that nothing pins, and Phase 0's only completeness test was 'is the map non-empty'. Deleting the harness_integrity_sha256 key therefore silently un-pinned BOTH lean-guard and Proofs/Audit.lean with no diagnostic, after which the round-6 logic mutation ran to ALL GREEN over a repository proving False with the digest byte-identical. Reproduced here before fixing. The required pin NAMES now live hardcoded in check.sh — policy in the root of trust, values in the map — so a shortened map is a build failure naming the missing entries. Self-test attack 18 performs the deletion. GPT reviewer, independently: the documented '131 assertion points' was wrong. Recounted from the code, the defensible figure is 137 mono-path evaluated cases (9 retained original + 108 randomized + 10 NIST internal + 10 NIST external-pure); 131 had folded in 3 deployed-only prehash cases while omitting the retained test, and TRUSTED-BASE then decomposed it as 20 + 108 = 128, contradicting itself. Item 9 now carries the full table, states that 127 of the 137 compare mono against deployed, keeps the 3 prehash cases explicitly outside the total, and records that only two SHA2-512 and one SHAKE-256 vector are executable there — so this is not NIST coverage of all four supported prehash variants. Also from GPT: PROVENANCE.json contradicted itself, saying extraction 'reproduces all four model files byte-identically' while its own _comment correctly said the two *External files are hand-maintained. Extraction regenerates two files; the other two are byte-pinned. Corrected. TRUSTED-BASE item 11 now discloses that PROVENANCE.json is itself load-bearing and unpinned, and item 7's stale snapshot head is fixed. README states the lean-guard graceful fallback and that the empirical bridge runs on stable Rust without any Lean toolchain (round-7 NEW-16), which is the first load-bearing part of this work a third party can reproduce with cargo alone. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 11:03:35 +00:00
(memory-capped, machine-wide serialized). It is Linux-oriented but **degrades
gracefully**: when `systemd-run` is unavailable it falls back to Lean's own
`-M` cap, so the button runs on a stock Linux box without cgroup support — an
external reviewer has run it green that way. Note also that the *empirical
bridge* (`cargo test` in the snapshot repo) needs no Lean toolchain at all and
runs on stable Rust. Extraction is reproducible: the
review round 2: in-Lean exact-cone audit + reproducibility + doc honesty Addresses the round-2 reviewer punch-list. No theorem statement, proof term, or fold definition changed; the eleven cones are unchanged (independent collectAxioms dump in verification/RECORDED-RUN.md). AUDIT GATE (both reviewers, the critical one) - Retire the bash #print-axioms text parser (fail-open on empty/truncated reports, and only a SUBSET check). Replace with verification/Proofs/Audit.lean: reads each certificate's cone from the kernel via collectAxioms and asserts EXACT set equality against its expected boundary. Extra axiom, dropped oracle, renamed/deleted cert, or an axiom/opaque sham each throw -> non-zero Lean exit. No text to misparse; nothing fails open. check.sh Phase 3 now just compiles it (and still requires the explicit PASSED line). - check-selftest.sh rewritten to attack the new gate: dead-file, smuggled extra axiom (named), dropped-oracle (subset would pass, exact must not), and a vanished certificate (the collectAxioms-returns-[] trap). All four rejected. REPRODUCIBILITY (GPT B1.4 / B1.5) - extract.sh refuses a wrong-commit or dirty source tree (fail-closed), takes an optional source-path arg, and pins the source commit. - verification/PROVENANCE.json: single machine-readable pin set (source + charon + aeneas commits/channel + lean + ocaml) with generated-file sha256. - Re-running extract.sh reproduces gen/SlhVerify/{Types,Funs}.lean byte-identically (companion fips205-source commit adds Cargo.lock + rust-toolchain.toml; verified not to perturb the model). DOC HONESTY (both reviewers) - README: fix the self-contradiction (apex "not yet proven" trailer vs the proven apex), the false "oracles kept OUTSIDE every cone" (they are INSIDE, by design), "deployed monomorphic path" and "semantics-identical for every parameter set" overclaims, "only two lines changed", stale snapshot head; retitle the stale future-tense "what will be claimed" section. - TRUSTED-BASE: drop "nothing proven yet"; add base_2b-inner and deployment- bridge non-claims explicitly; current pin. - ChainSpec header: "deployed monomorphic path" -> private verify_mono facade (comment only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:13:55 +00:00
full pin set (source commit, Charon/Aeneas commits + toolchain channel, Lean
and OCaml versions) is in [verification/PROVENANCE.json](verification/PROVENANCE.json);
`verification/extract.sh` refuses to run against a wrong-commit or dirty
source tree, and re-running it reproduces the aeneas-generated model
byte-identically (verified 2026-07-24). The axiom audit runs inside Lean
([verification/Proofs/Audit.lean](verification/Proofs/Audit.lean)): exact
per-certificate cone equality, fail-closed, adversarially exercised by
`verification/check-selftest.sh`. What cannot be proven is named in
[TRUSTED-BASE.md](TRUSTED-BASE.md), not hidden (H5).