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
|
|
|
|
# TRUSTED-BASE — what the certificates do NOT cover
|
2026-07-22 19:00:57 +00:00
|
|
|
|
|
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
|
|
|
|
Eleven certificates over the extracted `verify_mono` model are now proven
|
|
|
|
|
|
(`verification/check.sh` green; the apex is
|
|
|
|
|
|
`fips205.slh_verify_128s_accepts_iff`). This file states what those
|
|
|
|
|
|
certificates deliberately do NOT establish; it is maintained as the campaign
|
|
|
|
|
|
proceeds and is part of every claim.
|
2026-07-22 19:00:57 +00:00
|
|
|
|
|
2026-07-22 21:02:40 +00:00
|
|
|
|
1. **The five verify-path hash oracles.** `h_msg, f, h, t_l, t_len`
|
|
|
|
|
|
(SLH-DSA-SHA2-128s instantiations over SHA-256; `prf`/`prf_msg` are
|
|
|
|
|
|
sign-side only and do not appear in the cone) are modeled as opaque
|
2026-07-22 19:00:57 +00:00
|
|
|
|
functions with assumed functional behavior. Their correctness against
|
|
|
|
|
|
FIPS 180-4 is NOT proven here — the same standing boundary as SHA-512
|
|
|
|
|
|
in the ed25519 apex. A collision or misimplementation inside the hash
|
|
|
|
|
|
layer is invisible to these certificates.
|
|
|
|
|
|
2. **Signing and key generation.** Out of extraction scope entirely. A
|
|
|
|
|
|
verified verify path says nothing about the safety of signature or key
|
|
|
|
|
|
production (including randomness).
|
|
|
|
|
|
3. **The transpilation pair.** Charon and Aeneas (pinned versions in the
|
|
|
|
|
|
toolchain) are trusted to preserve semantics from Rust (MIR) to the
|
|
|
|
|
|
Lean model. Divergence between rustc's semantics and the extracted
|
|
|
|
|
|
model is trusted base.
|
docs: say what the button enforces today, and where each check stops
Round-8 estate review (GPT-5.6). Their central complaint across the estate was
that documents promise more than code checks. Here the drift ran the other way
as well: two phases were added this week and the documents described neither,
so the repository was UNDER-claiming while its own header still listed four
phases.
check.sh header now lists Phase 0d and Phase 3b, each with the reason it
exists rather than only what it does
README "binds four things" -> six, and says plainly that four are
checked inside Lean by Proofs/Audit.lean while the last two
deliberately do NOT rely on that file. "four model files"
-> five (the template is pinned now). The Phase 3b bullet
carries its demonstration: axiom planted after the audit
command, Phase 3 passed it, Phase 3b rejected it
TRUSTED-BASE 3 extraction reproducibility split honestly in two. The
committed .llbc means the LLBC -> Lean half re-runs on
demand and did reproduce Types.lean and Funs.lean
byte-identically. The Rust -> LLBC half still needs charon
and still rests on the author alone. "Do not read the first
half as evidence for the second."
TRUSTED-BASE 3b NEW, and it is a limit rather than a capability: Phase 0d
is TEXTUAL. It does not ask Lean how names resolve — the
ed25519 repositories have a semantic phase for that and
this one does not. All eleven externals here happen to be
answered by the model itself, the narrow case where the
textual and semantic answers coincide; that is a property
of today's corpus, not a guarantee of the check.
RECORDED-RUN.md is deliberately untouched again: its "all four model files" and
its `797b4ef` pin describe the state at the run it records. A record edited to
match today is not a corrected record.
Button re-run after every edit: green, 11 externals, 298 declarations across 9
modules, none an axiom.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-03 14:35:12 +00:00
|
|
|
|
|
|
|
|
|
|
**What is now reproducible, and what is not.** Extraction is two steps:
|
|
|
|
|
|
`Rust --charon--> SlhVerify.llbc --aeneas--> gen/SlhVerify/*.lean`. The
|
|
|
|
|
|
`.llbc` is committed, so the SECOND step can be re-run by anyone with the
|
|
|
|
|
|
pinned Aeneas and this repository, and on 2026-08-03 doing so reproduced
|
|
|
|
|
|
`Types.lean` and `Funs.lean` **byte-identically**. The FIRST step still
|
|
|
|
|
|
requires charon, which no reviewer has yet had available. So: the
|
|
|
|
|
|
LLBC → Lean half is reproducible on demand; the Rust → LLBC half rests on
|
|
|
|
|
|
the author's attestation alone, and continues to do so until a third party
|
|
|
|
|
|
runs it. Do not read the first half as evidence for the second.
|
|
|
|
|
|
|
|
|
|
|
|
3b. **The correspondence check is textual, not a Lean query.** Phase 0d parses
|
|
|
|
|
|
`FunsExternal_Template.lean` and the hand-written model as SOURCE TEXT. It
|
|
|
|
|
|
establishes that every external the extraction names is answered by a
|
|
|
|
|
|
declaration of the right name in the pinned model, and that the model
|
|
|
|
|
|
declares no axiom the extraction never asked for. It does **not** ask Lean
|
|
|
|
|
|
how those names resolve at elaboration — the four ed25519 repositories have
|
|
|
|
|
|
a second, semantic phase for that; this one does not. All eleven externals
|
|
|
|
|
|
here are answered by the model itself (none by the proven corpus), which is
|
|
|
|
|
|
the narrow case where the textual answer and the semantic one coincide, but
|
|
|
|
|
|
that is a property of today's corpus and not a guarantee of the check.
|
2026-07-22 19:00:57 +00:00
|
|
|
|
4. **The Lean kernel and its three axioms**
|
|
|
|
|
|
(`propext, Classical.choice, Quot.sound`).
|
|
|
|
|
|
5. **Build correspondence.** No reproducible-builds claim: the proof is
|
|
|
|
|
|
about the pinned source, not about any particular compiled binary
|
|
|
|
|
|
(the estate's R5 gap, stated everywhere it matters).
|
|
|
|
|
|
6. **Parameter-set scope.** Claims will bind SLH-DSA-SHA2-128s only;
|
|
|
|
|
|
other parameter sets are unverified until separately extracted and
|
|
|
|
|
|
proven (R2).
|
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
|
|
|
|
7. **Aeneas-compat + de-plumbing patch surface.** The fn-pointer-to-named-
|
|
|
|
|
|
oracle rewrite in `fips205-source` (phase 1) and the two de-plumbing
|
|
|
|
|
|
rounds (index-loop rewrites of the iterator adapters on the verify path,
|
2026-08-03 13:28:57 +00:00
|
|
|
|
de-plumbing round 2 at `bea1051`; current snapshot head `a3ce8e8`) are
|
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
|
|
|
|
part of the verified surface: the
|
|
|
|
|
|
certificates cover the *patched* verify path, and the patch commits are
|
|
|
|
|
|
the auditable delta from upstream `30bac08`. Each rewrite's equivalence
|
|
|
|
|
|
to upstream is argued in its commit and checked, for SHA2-128s, by the
|
|
|
|
|
|
snapshot differential test — it is not itself machine-checked.
|
|
|
|
|
|
8. **The `base_2b` inner loop.** `helpers.base_2b_loop0_loop0` (which
|
|
|
|
|
|
determines the FORS indices and WOTS digits) is threaded opaquely and
|
|
|
|
|
|
has no certificate; a defect there could change the recomputed root while
|
|
|
|
|
|
all eleven theorems still hold.
|
|
|
|
|
|
9. **The deployed generic 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 — no theorem
|
|
|
|
|
|
here says the two agree; the evidence is empirical and its size is stated so
|
|
|
|
|
|
a reader can judge it (external review, rounds 4–6, correctly objected that
|
|
|
|
|
|
"finite" without a number is not a disclosure):
|
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
|
|
|
|
- **137 evaluated input/verdict cases on the proved path** (was 9 until
|
|
|
|
|
|
2026-07-28: three rounds from one fixed seed, corrupting one fixed byte of a
|
|
|
|
|
|
7856-byte signature). The breakdown, which a reviewer can recount from
|
|
|
|
|
|
`src/verify_mono.rs`:
|
|
|
|
|
|
| source | cases |
|
|
|
|
|
|
|---|---:|
|
|
|
|
|
|
| retained original differential test (3 rounds × valid/corrupt/wrong-message) | 9 |
|
|
|
|
|
|
| randomized differential bridge (12 rounds × valid + 6 corruptions + wrong-key + wrong-context) | 108 |
|
|
|
|
|
|
| NIST ACVP `internal` group — M′ fed straight into `slh_verify_128s` | 10 |
|
|
|
|
|
|
| NIST ACVP `external pure` group — mono vs deployed vs NIST | 10 |
|
|
|
|
|
|
| **total** | **137** |
|
|
|
|
|
|
- **20 of those are NIST known-answer tests against the proved path** (the
|
|
|
|
|
|
two NIST rows above). NIST's negatives sit at structurally distinct sites —
|
|
|
|
|
|
modified R, SIGFORS, SIGHT, modified message — rather than one arbitrary
|
|
|
|
|
|
byte. The external-pure ten carry real contexts, 9 of them non-empty and one
|
|
|
|
|
|
at the FIPS-205 maximum length of 255, which is the only empirical check of
|
|
|
|
|
|
the domain-separator byte and context-length prefix that item 10 declares
|
|
|
|
|
|
outside every proof.
|
|
|
|
|
|
- **127 of the 137 compare mono against the deployed verifier** (all but the
|
|
|
|
|
|
ten `internal` NIST cases, which compare mono against NIST directly).
|
|
|
|
|
|
- Separately and **not** counted in the 137: **3 deployed-only prehash cases**
|
|
|
|
|
|
(NIST `external preHash`), which exercise `hash_verify` rather than the
|
|
|
|
|
|
proved path. Only two SHA2-512 vectors and one SHAKE-256 vector are
|
|
|
|
|
|
executable — NIST's remaining prehash vectors use functions this crate does
|
|
|
|
|
|
not implement — so this is *not* NIST coverage of all four supported prehash
|
|
|
|
|
|
variants.
|
|
|
|
|
|
- Corruption in the randomized bridge covers **72 distinct positions in the
|
|
|
|
|
|
range 11..=7779**, not literally every byte of the signature.
|
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
|
|
|
|
Still **not** covered by any of it: agreement on inputs nobody generated, and
|
|
|
|
|
|
the prehash variant against the mono path (see item 10). A passing
|
|
|
|
|
|
differential test is evidence, not a proof.
|
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
|
|
|
|
10. **Everything above the extraction root.** The root is
|
|
|
|
|
|
`verify_mono::slh_verify_128s = slh_verify_internal_free(M′, sig, pk)`,
|
|
|
|
|
|
which takes the message-digest input **M′ as an argument**. The code in
|
|
|
|
|
|
`slh_verify`/`verify` (`src/lib.rs`) that runs *before* this root is NOT
|
|
|
|
|
|
covered by any certificate: the assembly of M′; the pure-vs-prehash
|
|
|
|
|
|
**domain-separator byte** (`0u8` for `verify` vs `1u8` for `hash_verify`
|
|
|
|
|
|
— the whole cross-variant domain separation); the FIPS-205 `ctx.len() >
|
|
|
|
|
|
255` bound; and signature/public-key deserialization. The certificates
|
|
|
|
|
|
say nothing about this input handling — a defect there (e.g. a wrong
|
|
|
|
|
|
separator byte) would be outside every proof.
|
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
|
|
|
|
**Concretely, so the consequence is not left to the reader:** that byte is
|
|
|
|
|
|
the *only* thing separating the pure and prehash variants. If it were wrong
|
|
|
|
|
|
or dropped, a signature issued over the pure M′ would verify as a prehash
|
|
|
|
|
|
signature and vice versa — cross-variant signature confusion, a forgery
|
|
|
|
|
|
primitive. No certificate in this repository would change.
|
|
|
|
|
|
11. **The verification harness itself.** The certificates are statements
|
|
|
|
|
|
checked by the Lean kernel, but the *button* that reports them is a shell
|
|
|
|
|
|
script. Round-5 review demonstrated that stubbing `verification/lean-guard`
|
|
|
|
|
|
alone — one repo-tracked file, without touching `check.sh`, the manifest, or
|
|
|
|
|
|
the proofs — yields ALL GREEN in 3.6 seconds over deliberately destroyed
|
|
|
|
|
|
proofs. `lean-guard` is therefore **sha256-pinned** by check.sh Phase 0
|
|
|
|
|
|
(`PROVENANCE.json → harness_integrity_sha256`); it is kept rather than
|
|
|
|
|
|
removed because it is the memory cap and machine-wide lock that protect the
|
|
|
|
|
|
build machine (a Lean elaboration once reached 12.2 GB and took the host
|
review round 6: pin the auditor, purge every olean, pin gen/ as a set
Round 6 confirmed the digest redesign closed NEW-1/NEW-2/NEW-5 at the mechanism
("the first time in three rounds I have not been able to gut a certificate"),
then demonstrated two more ways to reach ALL GREEN with the committed digest
BYTE-IDENTICAL over a repository proving False. Both are fixed.
NEW-7 — the digest bound the audit's DATA, never its LOGIC. Flipping the two
fail-closed guards in Proofs/Audit.lean to `unless true` disabled every in-Lean
check; the block's inputs genuinely had not changed, so the digest still
matched. Total attacker diff: 2 files, 6 insertions. Worse, TRUSTED-BASE item 11
listed the trusted-unbound set and did NOT mention Audit.lean, so a reviewer
using it as a map of what to read by hand would have skipped the file that
computes the number it is judged by.
FIX: Proofs/Audit.lean is now sha256-pinned in harness_integrity_sha256,
symmetric with lean-guard, and item 11 says so — including the honest residue:
an author who edits the logic AND rotates its pin is caught only by reading the
diff at the pin.
NEW-8 — Phase 0's purge covered gen/ and Proofs/ while the stray check greped
only *.lean, so an ORPHAN verification/Evil.olean whose source had been DELETED
fell between them, satisfied an import, and was invisible to git status
(*.olean is gitignored).
FIX: purge every .olean under verification/, and forbid stray .lean AND .olean.
NEW-9 (partial) — gen/ was pinned by four NAMES, not as a SET, so a new file
there was neither hashed nor forbidden while LEAN_PATH contains $PWD/gen.
FIX: Phase 0 asserts the gen/*.lean file set equals the pin map exactly. This
found a real gap on its first run: Aeneas emits *_Template.lean scaffolding into
gen/ on every extraction — untracked byproducts (a fresh clone has only the four
pinned files) that nothing imports but that sat on LEAN_PATH unpinned. They are
now purged as byproducts before the set assertion.
Also, from the reviewer's §3 suggestion: the canonical block is now COMMITTED as
verification/AUDIT-MANIFEST.txt, so a digest mismatch prints a real diff instead
of writing an observed file with nothing to compare against; check.sh also fails
if the committed copy drifts from what Lean emits.
check-selftest.sh: 16 attacks, all rejected, plus the coverage check. Attacks 16
and 17 are the reviewer's two demonstrations. Attacks that mutate the audit's
DATA now re-pin Audit.lean first, so they still test the digest/enumeration
rather than being stopped by the byte pin; attack 17 deliberately does not
re-pin, because the pin is what it tests.
Housekeeping: PROVENANCE.json trailing newline restored (NEW-12).
Disclosed rather than buried: two more of my own assertion bugs this round —
attack 16 asserted the hygiene message when the correct rejection is the purge
plus a failed import, and the earlier gen/ set check surfaced the template files
only because it was written strictly. Both are the wrong-diagnostic class.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 07:22:12 +00:00
|
|
|
|
down).
|
|
|
|
|
|
**`verification/Proofs/Audit.lean` is pinned the same way, and for a sharper
|
|
|
|
|
|
reason** (round-6 NEW-7): the digest it emits binds the audit's *data* — the
|
|
|
|
|
|
policy constants, the statements, the specification bodies — but nothing can
|
|
|
|
|
|
make a program hash the correctness of its own logic. Flipping this file's
|
|
|
|
|
|
two fail-closed guards to `unless true` disabled every in-Lean check while
|
|
|
|
|
|
the digest stayed BYTE-IDENTICAL, and a repository proving `False` passed
|
|
|
|
|
|
ALL GREEN. The byte pin converts that from a silent green into a build
|
|
|
|
|
|
failure; a legitimate change to the audit is now a reviewable pin rotation.
|
|
|
|
|
|
Note the residue honestly: an author who edits the logic *and* rotates its
|
|
|
|
|
|
pin in the same commit is not stopped by anything mechanical — that case is
|
|
|
|
|
|
caught only by reading the diff at the pin.
|
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
|
|
|
|
**`PROVENANCE.json` is itself load-bearing and unpinned.** It supplies the
|
|
|
|
|
|
values for every byte pin, and round-7 review demonstrated that deleting one
|
|
|
|
|
|
key from it silently removed both harness pins with no diagnostic, after
|
|
|
|
|
|
which the logic mutation above ran to ALL GREEN. The *policy* — which files
|
|
|
|
|
|
must be pinned — now lives hardcoded in `check.sh` and a missing entry is a
|
|
|
|
|
|
build failure, so the map can no longer be quietly shortened; but the map's
|
|
|
|
|
|
own bytes are still not pinned by anything, and could not be without moving
|
|
|
|
|
|
the root of trust somewhere else.
|
review round 6: pin the auditor, purge every olean, pin gen/ as a set
Round 6 confirmed the digest redesign closed NEW-1/NEW-2/NEW-5 at the mechanism
("the first time in three rounds I have not been able to gut a certificate"),
then demonstrated two more ways to reach ALL GREEN with the committed digest
BYTE-IDENTICAL over a repository proving False. Both are fixed.
NEW-7 — the digest bound the audit's DATA, never its LOGIC. Flipping the two
fail-closed guards in Proofs/Audit.lean to `unless true` disabled every in-Lean
check; the block's inputs genuinely had not changed, so the digest still
matched. Total attacker diff: 2 files, 6 insertions. Worse, TRUSTED-BASE item 11
listed the trusted-unbound set and did NOT mention Audit.lean, so a reviewer
using it as a map of what to read by hand would have skipped the file that
computes the number it is judged by.
FIX: Proofs/Audit.lean is now sha256-pinned in harness_integrity_sha256,
symmetric with lean-guard, and item 11 says so — including the honest residue:
an author who edits the logic AND rotates its pin is caught only by reading the
diff at the pin.
NEW-8 — Phase 0's purge covered gen/ and Proofs/ while the stray check greped
only *.lean, so an ORPHAN verification/Evil.olean whose source had been DELETED
fell between them, satisfied an import, and was invisible to git status
(*.olean is gitignored).
FIX: purge every .olean under verification/, and forbid stray .lean AND .olean.
NEW-9 (partial) — gen/ was pinned by four NAMES, not as a SET, so a new file
there was neither hashed nor forbidden while LEAN_PATH contains $PWD/gen.
FIX: Phase 0 asserts the gen/*.lean file set equals the pin map exactly. This
found a real gap on its first run: Aeneas emits *_Template.lean scaffolding into
gen/ on every extraction — untracked byproducts (a fresh clone has only the four
pinned files) that nothing imports but that sat on LEAN_PATH unpinned. They are
now purged as byproducts before the set assertion.
Also, from the reviewer's §3 suggestion: the canonical block is now COMMITTED as
verification/AUDIT-MANIFEST.txt, so a digest mismatch prints a real diff instead
of writing an observed file with nothing to compare against; check.sh also fails
if the committed copy drifts from what Lean emits.
check-selftest.sh: 16 attacks, all rejected, plus the coverage check. Attacks 16
and 17 are the reviewer's two demonstrations. Attacks that mutate the audit's
DATA now re-pin Audit.lean first, so they still test the digest/enumeration
rather than being stopped by the byte pin; attack 17 deliberately does not
re-pin, because the pin is what it tests.
Housekeeping: PROVENANCE.json trailing newline restored (NEW-12).
Disclosed rather than buried: two more of my own assertion bugs this round —
attack 16 asserted the hygiene message when the correct rejection is the purge
plus a failed import, and the earlier gen/ set check surfaced the template files
only because it was written strictly. Both are the wrong-diagnostic class.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 07:22:12 +00:00
|
|
|
|
Still trusted, and NOT bound by anything the button can check:
|
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
|
|
|
|
`check.sh` itself, `~/aeneas-toolchain/env.sh`, the `$AENEAS_HOME` tree
|
|
|
|
|
|
(i.e. *which* Aeneas/Lean library the proofs are checked against), `python3`,
|
|
|
|
|
|
and the Lean toolchain. An audit executed by a harness cannot defend against
|
|
|
|
|
|
an author who edits that harness; the consumer defense is the pinned commit,
|
|
|
|
|
|
reviewed at the pin.
|
|
|
|
|
|
12. **Composition.** The apex does **not** compose the ten loop-fidelity
|
|
|
|
|
|
theorems — it is a structural factorization of the extracted verifier around
|
|
|
|
|
|
its final equality check and references none of them (it would remain
|
|
|
|
|
|
provable if one were deleted). The ten are independent, individually
|
|
|
|
|
|
human-reviewed lemmas. Round-5 review makes this worth stating here rather
|
|
|
|
|
|
than only in the README: each of the ten is individually meaningful only to
|
|
|
|
|
|
the extent a human has read its reference fold against FIPS 205.
|