verifying-crypto-with-lean/chapters/ch13-attestation-protocol.tex

300 lines
15 KiB
TeX
Raw Normal View History

ch13: The Attestation Protocol — who checks the checker? The book taught act one (getting a kernel to accept a proof) across twelve chapters and never taught act two: the protocol that makes a green light mean something to someone who was not present. ch11 armed the reader to interrogate a CERTIFICATE and left the script that interrogates certificates — software the author wrote, verified by nothing — entirely unexamined. That was the gap eight rounds of external review found in the companion projects, and it is a didactic failure of this book too. ch13 teaches it from the war stories: the two acts and why act one is the easy one (eleven theorems in two days, never disputed; eight review rounds and eighteen defect classes to make the button over them credible); the single shape every failure had (something load-bearing sat outside the binding), with the demonstrated exploits including ALL GREEN over a repository proving False, a certificate reduced to 'the loop equals the loop' with every fingerprint byte-identical, and a stubbed compiler wrapper going green in 3.6 seconds over destroyed proofs; completeness of binding and its four rules (derive the population, fail closed on absence, exact not subset, a stranger must re-derive); and the meta-defect of assertions that pass for the wrong reason, including the tautological assert that appeared twice — the second time inside its own repair. Closes with the habit to carry: ask both questions, and invite someone to attack your button early, because none of the eighteen was found by the author. ch11 now forward-references it at the point where the old blind spot sat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 14:21:40 +00:00
\chapter{The Attestation Protocol: Who Checks the Checker?}
\label{ch:attestation}
\section{The second act nobody warns you about}
Chapter~\ref{ch:honesty} taught you to interrogate a certificate: ask what it
rests on, and refuse to be impressed by a file that merely compiles. That
chapter had a blind spot, and this one exists because a sequence of external
reviewers found it.
The blind spot is this. \lean{\#print axioms} tells \emph{you}, at \emph{your}
terminal, that a theorem's cone is clean. Then you write a script that runs it
across your project, the script prints \texttt{ALL GREEN}, and you publish that
green light as evidence. Between the kernel's verdict and the reader's belief
sits a piece of software you wrote --- and \emph{that software is not verified
by anything}.
\begin{bigidea}
Verification is \textbf{two acts}, not one.
\textbf{Act one --- proof.} The Lean kernel decides whether a term is a valid
derivation of its statement. This is mathematics. It is what everyone pictures
when they hear ``formally verified''.
\textbf{Act two --- attestation.} Establishing that what the kernel accepted is
what you \emph{claim}, resting only on what you \emph{say} it rests on, about
the artifact you \emph{say} it concerns --- and that a stranger who was not
present and does not trust you can check every part of that for themselves.
\medskip
\textbf{Lean decides whether a proof is valid. The Attestation Protocol decides
whether anyone else can know that.} A proof without an attestation protocol is
a private conviction, not public evidence.
\end{bigidea}
The companion projects learned this expensively. In one campaign, eleven
theorems about a hash-based signature verifier were proved in \textbf{two days}
and no reviewer ever disputed one of them. Making the green light over those
same eleven theorems mean something to a determined skeptic took \textbf{eight
rounds of review} and turned up \textbf{eighteen distinct defect classes} ---
none of them in the mathematics, all of them in act two.
\section{The shape of every failure}
Here is the finding that makes this chapter teachable. All eighteen defects had
\emph{one shape}:
\begin{aha}
\textbf{Something load-bearing sat outside the binding.}
The audit was always \emph{sound} about what it examined. The entire attack
surface was what it did \emph{not} examine.
\end{aha}
Read the greatest hits and watch the pattern repeat. Each of these was
demonstrated end-to-end against a button that was printing \texttt{ALL GREEN}
at the time:
\begin{itemize}
\item \textbf{The certificate list was outside.} Delete one row from the list
of audited theorems and that theorem silently leaves the audit. Nothing
noticed.
\item \textbf{Un-listed declarations were outside.} Add
\lean{axiom cheat : ∀ (P : Prop), P} and
\lean{theorem oops : False := cheat _} to a proof file. The audit
checked the theorems on its list; this one was not on the list.
\textbf{The repository proved \lean{False} and the button stayed
green.}
\item \textbf{Non-theorems were outside.} After that was fixed, the same
payload as a \lean{def} rather than a \lean{theorem} passed --- the
enumeration matched only theorems.
\item \textbf{The auditor was outside itself.} After \emph{that} was fixed,
the same payload placed \emph{inside the audit driver} passed, because
the driver was exempt from its own enumeration.
\item \textbf{Statements were outside.} Replace a theorem's statement with a
tautology that happens to have the same axiom cone. Passes.
\item \textbf{Specifications were outside.} This is the subtle one. Each
certificate said ``the extracted loop equals this hand-written
reference fold''. Redefine the fold to \emph{be} the extracted loop.
The certificate now says \emph{the loop equals the loop} --- vacuous ---
and the axiom cone, the statement hash, and the digest are all
\textbf{byte-identical}.
\item \textbf{The policy was outside.} The audit compared each cone against a
list of permitted axioms. That list was not itself covered by the
digest. Add one name to it and every protection re-opens, digest
unchanged.
\item \textbf{The subject was outside.} The proofs were about a generated
model file. Hand-edit the model; every phase still passes.
\item \textbf{The tools were outside.} Stub the compiler-wrapper script and
the button printed \texttt{ALL GREEN} in \textbf{3.6 seconds} over
deliberately destroyed proofs. Flip two characters in the audit
driver's fail-closed guards and every check switched off with the
digest byte-identical.
\end{itemize}
\begin{pitfall}
Notice what is \emph{not} on that list: a wrong proof, a wrong theorem, a bug in
Lean. The kernel did its job perfectly throughout. Every single failure was in
the apparatus built \emph{around} it --- by the same person who was reporting
the results.
\end{pitfall}
\section{Completeness of binding}
Because the failures all have one shape, the property to design for has one
name. It is not soundness --- soundness was never the problem.
\begin{bigidea}
\textbf{Completeness of binding.} Everything load-bearing is inside the binding:
the statements, the definitions those statements are stated \emph{against}, the
policy that decides what is permitted, the bytes of the artifact being reasoned
about, and the tools doing the checking.
\end{bigidea}
Four rules follow, and each one is the generalisation of a defect above.
\subsection{Derive the population; never keep a list}
A hand-maintained list of ``things that must be checked'' is one more thing that
can fall out of sync --- and did, twice. Derive membership instead:
\begin{itemize}
\item from the \emph{environment}: every declaration in these modules, obtained
by walking the environment, not by naming them;
\item from the \emph{filesystem}: every \lean{.lean} file under the generated
directory must appear in the hash map, so a new file fails closed;
\item from the \emph{type system}: the transitive closure of constants a
statement mentions, so a new reference definition cannot appear
unnoticed;
\item from a \emph{file attribute}: every executable file in the verification
directory must be pinned --- so adding a script fails until you pin it.
\end{itemize}
\subsection{Fail closed on absence}
\begin{pitfall}
The most common bug in an audit script: \emph{nothing found} and \emph{nothing
wrong} share a code path.
A missing report, an empty cone, a truncated line, a renamed theorem, an absent
map key --- each of these must be a build failure. In the campaign above,
fail-open-on-absence was both the \emph{first} defect found and the
\emph{last}.
\end{pitfall}
\subsection{Exact, not subset}
Checking that a cone contains nothing forbidden is not enough. A certificate can
lie by resting on \emph{less} than you declared as well as more --- if your
theorem quietly stopped depending on the hash function, something is very wrong,
and a subset check will smile at you. Require set \emph{equality}.
\subsection{A stranger must be able to re-derive it}
The final rule is the one that separates evidence from assertion. Publish a
digest a reader can recompute; commit the input that digest is taken over, so a
mismatch can be \emph{diffed} rather than merely reported; and record who ran
the check, on what machine, at which commit.
\section{The meta-defect: assertions that pass for the wrong reason}
There is a second lesson, about \emph{tests} rather than about products, and it
is humbling enough to state plainly. Across the same eight rounds, \textbf{eight
separate assertions were found to be checking nothing} --- including two inside
fixes written to repair that very problem.
The purest specimen. A script transformed a data file by dropping one field, and
carried this line:
\begin{lstlisting}[language=Python]
kept = {k: v for k, v in record.items() if k not in DROP}
assert set(kept) == set(record) - DROP # can never fail
\end{lstlisting}
\lean{kept} was built by the comprehension on the line above. The assertion is a
tautology. The first attempt to repair it compared \lean{kept[k]} against
\lean{record[k]} --- tautological for exactly the same reason.
\begin{aha}
\textbf{No check inside a transformer can detect a corrupted input, because the
transformer is what defines the output from that input.}
Faithfulness there is a property a \emph{reviewer reads}, not something the code
can test about itself. What can actually fail --- and therefore what must carry
the weight --- is the pin on the input, the presence and count guards, and an
independent re-derivation.
\end{aha}
\begin{tryit}
Take any test you have written that guards an important property. Now
\textbf{break the thing it guards} and run it.
If it does not go red --- or goes red with a message about something else ---
you have a decoration, not a test. Do this for every guard you own. In the
campaign described here, that exercise would have caught eight defects, and the
people who eventually caught them were strangers.
\end{tryit}
\section{What this means for you}
You will not build an eighteen-attack self-test for a homework exercise, and you
should not. What you should take away is a habit of mind and a vocabulary.
\begin{checkpoint}
When you next read the words ``formally verified'', ask two questions instead of
one.
\textbf{Act one:} what statement did a kernel accept, and what does it rest on?
(Chapter~\ref{ch:honesty} taught you this.)
\textbf{Act two:} what binds that statement to the artifact I care about, who
checked, what did the checker \emph{not} look at, and can I re-derive any of it
myself?
If a project cannot answer the second set, it has done act one and called it
finished --- which is exactly the mistake these chapters were rewritten to
prevent.
\end{checkpoint}
And when it is your own project: invite someone to attack the button, early.
Every one of the eighteen defects was found by a reviewer trying to break it.
\emph{None} was found by the author reviewing their own work --- and the author
looked, repeatedly, with the same care they had used to write the proofs.
ch13: send the reader to the live log — the cross-referencing that did not exist Measured before writing: the book made ZERO references to the transparency log, the live site, leaves, receipts, verify.py, or anything post-quantum — two incidental uses of the word "accumulator" were the entire overlap with the estate's flagship artifact. A book that teaches "who checks the checker" never mentioned that a live log practicing every one of its principles is publicly checkable. New closing section of ch13, "Go and touch the real thing": what a leaf is in the chapter's own vocabulary; the fifteen-minute exercise (clone the mirror, verify.py --all, pin both trust anchors two independent ways, read leaf 18 in full); the map from the log's nineteen leaves onto the book's chapters (leaves 13-16 = the pyramid at 44 certificates with ch11's boundary-exact apex cones in production; leaf 17 = ch13 made literal, the log carrying proofs of its own Merkle machinery; leaf 18 = the first post-quantum subject, FIPS 205 verify path); and the two boundaries a reader must hold — verify proven / signing never, for both algorithms, and the frozen paper as an honestly-aged snapshot contained byte-identical inside today's history. Root cause of the staleness, named: the book has no button. Every other doc surface in the estate is gated or audited; the book froze as a July-6 deliverable plus one chapter. Until it grows a gate, estate doc audits are its only clock (last: 2026-08-08). README: ch13 entry expanded accordingly. PDF still awaits a LaTeX host. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-07 21:29:36 +00:00
\section{Go and touch the real thing}
\label{sec:live-log}
Everything in this chapter runs in production, in public, right now. The
companion estate operates a \emph{transparency log} of its own attestations:
a Merkle accumulator whose leaves are signed statements of the form ``this
repository, at this exact commit, was checked by its own button, and these
are the certificates it proved, on exactly these axiom cones.'' The log is
served at \texttt{ltl.zkdefi.org} and mirrored as an ordinary git repository
(\texttt{github.com/saymrwulf/lean-transparency-log}) that you can clone and
interrogate offline. It is act two, industrialized: every failure class this
chapter catalogued has a gate in that pipeline because a reviewer once got
past the spot where the gate now stands.
\begin{checkpoint}
The fifteen-minute exercise, and the best return on time in this book: clone
the mirror and run the verifier.
\begin{itemize}
\item \code{python3 verify.py --all} --- plain Python for the hashing, the
\code{openssl} binary for signatures, and it \emph{fails closed} without
them (Section~\ref{ch:attestation} taught you why ``couldn't check'' must
never print as a pass). It recomputes every leaf hash, every historical
tree head against its recomputed prefix root, every signature, and every
inclusion proof --- your machine, your verdict, nobody's word.
\item Pin the trust anchors \emph{two independent ways}: the keys are served
by the site (\texttt{/log-public-key}, \texttt{/log-slhdsa-public-key})
and shipped in the mirror (\code{provider.ed25519.pub},
\code{provider.slhdsa.pub}). The copies must agree byte-for-byte. If they
ever disagree, you have caught something worth catching.
\item Read one leaf in full --- \code{entries/000018.json} is a good choice
--- and find, inside it, every vocabulary item of this chapter: the pinned
commit, the certificate list, the \emph{observed} axiom cones, the machine
protection, and the stated exclusions.
\end{itemize}
\end{checkpoint}
The log's nineteen leaves map onto this book. Leaves 13--16 attest the four
ed25519 repositories whose pyramid you climbed in
Chapters~\ref{ch:modular}--\ref{ch:pyramid}: forty-four certificates each ---
twenty-seven on the main button, thirteen on the scalar button, and the four
apex-tier theorems whose \emph{documented, boundary-exact} cones are
Chapter~\ref{ch:honesty}'s lesson enforced in production. Leaf 17 is this
chapter made literal: the log carries kernel-checked proofs of \emph{its own
Merkle machinery} as one of its own entries --- ``who checks the checker?''
answered by putting the checker's mathematics inside the thing it checks.
And leaf 18 is the estate's first post-quantum subject: eleven certificates
over the SLH-DSA-SHA2-128s \emph{verification} path (FIPS~205), whose apex
states that the extracted verifier accepts exactly when the recomputed
Merkle root matches --- tree recursions under a hash-oracle boundary, the
same proof shapes you met in the pyramid, on a hash-based signature scheme.
Since tree 14 every head also carries an \emph{additive} deterministic
SLH-DSA signature beside the required Ed25519 one; heads published before
then have none, and the verifier reports them as \code{ABSENT} rather than
failing them --- an append-only log keeps its history, including the history
of its own signature scheme.
Two boundaries, so that you read the log the way this book taught you to
read everything. First: for both signature algorithms the estate has proved
\emph{verification} and nothing about \emph{signing} --- the heads are
signed by unproven code and checkable by proven code, and every leaf names
its trusted base; read a leaf's exclusions before believing anything beyond
them. Second: the estate's paper about this log is frozen under journal
review and describes the thirteen-leaf snapshot of July 2026. Nothing it
describes was altered --- the paper-era leaves and heads sit byte-identical
inside today's history, and \code{verify.py --all} checks both eras in one
run. A document that ages honestly inside a system that keeps moving is not
a defect; it is what append-only means.
That is the whole arc of this book in one artifact: arithmetic became
theorems (act one), theorems became certificates with named cones
(Chapter~\ref{ch:honesty}), certificates became attestations a stranger can
re-derive (this chapter) --- and the attestations went into a structure that
remembers everything and lets anyone catch it lying. When you build your
own, you now know what it costs, and where the bodies are buried.