fips205-slhdsa-verified/TRUSTED-BASE.md
mrwulf 1b430dfd68 llbc: commit the artifact the claim depended on, and verify the pin block
Round-9 review (GPT-5.6, R9-F2, BLOCKER). TRUSTED-BASE said:

    "The .llbc is committed, so the SECOND step can be re-run by anyone with
     the pinned Aeneas and this repository"

.gitignore excluded it. `git ls-files` had no LLBC. The file existed only on
the author's disk. I ran `ls`, saw it, and wrote the claim without running
`git ls-files` — so a sentence that reads as an independent-reproducibility
guarantee was true for exactly one person. The experiment itself was real:
re-running Aeneas on that LLBC did reproduce Types.lean and Funs.lean
byte-identically. What was false is that anyone else could repeat it.

CHASING IT FOUND WORSE. `generated_artifacts_sha256` was read by NOTHING —
check.sh had zero references to it. Its Types.lean and Funs.lean entries
matched only because those files are ALSO pinned in model_integrity_sha256,
which is checked. The .llbc entry, the one nothing else covered, had been
stale since review round 2 (522d8b2): the source was re-extracted on
2026-07-28, the model files and their pins were updated, and this pin was not.
It named d8ec0b00…, an artifact that did NOT produce the committed model. The
file that did is 69666ddc… — timestamped nine seconds before Types.lean and
Funs.lean, and demonstrably regenerating them byte-for-byte.

A pin nothing verifies drifts, and nobody notices. That is the finding, and it
is a sharper instance of the pattern than the one the reviewer reported.

  · .gitignore no longer excludes SlhVerify.llbc; it is committed (1.6 MB)
  · its pin corrected to the artifact that actually produced the model
  · check.sh Phase 0 now verifies generated_artifacts_sha256, so the block
    stops being decorative. Negative-tested: one appended byte gives
    `✗ SlhVerify.llbc: sha256 dd5925770bc7 ≠ pinned 69666ddc43a4`, exit 1
  · TRUSTED-BASE item 3 rewritten. It now says what committing the LLBC does
    and does NOT buy: the Lean model is the faithful Aeneas image of THAT
    intermediate, and whether the intermediate is the faithful Charon image of
    fips205-source@a3ce8e8 rests on the author alone. Verifying the committed
    LLBC against itself establishes nothing about Charon.
    "Do not read the second half as evidence for the first."
  · README qualified AT THE CLAIM SITE, not via a later link

Button green after every edit; accounting still closes at 300 with no residual.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-03 20:36:19 +02:00

174 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TRUSTED-BASE — what the certificates do NOT cover
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.
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
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.
**What is now reproducible, and what is not.** Extraction is two steps:
`Rust --charon--> SlhVerify.llbc --aeneas--> gen/SlhVerify/*.lean`.
The `.llbc` is committed **as of 2026-08-03** and byte-pinned in
`generated_artifacts_sha256`, which `check.sh` Phase 0 now verifies. So the
SECOND step can be re-run by anyone with the pinned Aeneas and this
repository, and doing so reproduces `Types.lean` and `Funs.lean`
byte-identically.
THE FIRST STEP CANNOT BE. It requires charon, which no reviewer has yet had
available, and this repository ships no charon output anyone can check
against — the committed `.llbc` IS that output, so verifying it against
itself establishes nothing. What the committed `.llbc` gives a reader is
this and only this: the Lean model in `gen/` is the faithful Aeneas image of
THAT intermediate. Whether that intermediate is the faithful Charon image of
`fips205-source@a3ce8e8` rests on the author's attestation alone, and
continues to do so until a third party runs Charon. **Do not read the second
half as evidence for the first.**
This paragraph previously said the `.llbc` was committed while `.gitignore`
excluded it — round-9 review (GPT-5.6) — so the claim was false for every
reader and true only on the author's disk. Chasing it found the larger
defect: `generated_artifacts_sha256` was read by nothing, and its `.llbc`
entry had been stale since review round 2, naming an artifact that did not
produce the committed model.
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.
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).
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,
de-plumbing round 2 at `bea1051`; current snapshot head `a3ce8e8`) are
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
`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 46, correctly objected that
"finite" without a number is not a disclosure):
- **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.
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.
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.
**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
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.
**`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.
Still trusted, and NOT bound by anything the button can check:
`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.