mirror of
https://github.com/saymrwulf/verifying-crypto-with-lean.git
synced 2026-09-04 20:03:41 +00:00
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>
This commit is contained in:
parent
1029f0c251
commit
640b81c1e5
3 changed files with 233 additions and 0 deletions
|
|
@ -11,6 +11,15 @@ every file compiles. Nothing here is hypothetical --- each failure mode
|
|||
appears in the wild, and the discipline below is the one the companion
|
||||
projects hold themselves to.
|
||||
|
||||
\begin{pitfall}
|
||||
This chapter teaches you to interrogate a \emph{certificate}. It does not teach
|
||||
you to interrogate the \emph{script that interrogates certificates for you} ---
|
||||
and that script is software you wrote, verified by nothing.
|
||||
Chapter~\ref{ch:attestation} is about that second problem, which turned out to
|
||||
be far harder than everything in this chapter, and is where every failure in the
|
||||
companion projects has occurred.
|
||||
\end{pitfall}
|
||||
|
||||
\section{The trust ledger}
|
||||
|
||||
When the kernel accepts \lean{fieldImplementation}, what exactly are you
|
||||
|
|
|
|||
223
chapters/ch13-attestation-protocol.tex
Normal file
223
chapters/ch13-attestation-protocol.tex
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
\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.
|
||||
1
main.tex
1
main.tex
|
|
@ -151,6 +151,7 @@ For self-study or a seminar, the book paces naturally as a semester:
|
|||
\input{chapters/ch10-verifying-a-field}
|
||||
\input{chapters/ch11-honesty-and-axioms}
|
||||
\input{chapters/ch12-the-pyramid}
|
||||
\input{chapters/ch13-attestation-protocol}
|
||||
|
||||
\appendix
|
||||
\input{chapters/appendix-toolkit}
|
||||
|
|
|
|||
Loading…
Reference in a new issue