mirror of
https://github.com/saymrwulf/verifying-crypto-with-lean.git
synced 2026-09-04 20:03:41 +00:00
book overhaul moves 1+2: the minute-one artifact, and cold-open surgery on ch1
From the 7-reader didactic audit (control/BOOK-OVERHAUL-PLAN.md). The two
highest-leverage moves per the cold-open reader and the panel's most-repeated
finding.
MOVE 1 — the reader touches the real thing in minute one. The panel's single
loudest note: a book that teaches "demand verification" gave the reader
nothing to verify for twelve chapters. Now: the title page names
ltl.zkdefi.org (19 entries, one post-quantum, "verify every entry yourself by
the last chapter"); the preface opens on that page instead of a generic power
claim; ch1 gains a try-it box after the pyramid pointing at entries 13-16 and
18. And ch7's broken promise is honored — it invited "check one leaf" while
never printing the 71-digit Q; the exact Q from P25519.lean is now on the
page, no hidden digits (render-verified against the repo).
MOVE 2 — ch1 cold open. Per the cold reader who hooked at line 13 and wobbled
by line 31: the first worked box (a log10 re-derivation of a punch the prose
already landed) collapses to four lines, the mechanics pushed to Exercise 1.1
— getting the reader ~30 lines sooner to the headroom box, the genuinely novel
bit. The vague "In 2014, researchers…" opener becomes the actual bug: a
two-line diff captioned as the entire defect, anchored to the real Fiat-Crypto
S&P 2019 lineage (no fabricated CVE). Competitor roll-call → footnote.
Syllabus roadmap → a second-person promise ("by Chapter 7 you will have handed
a kernel a certificate…"). "Why Lean" vendor bullets → reader-inheritance
voice ("you start on a million lines of proved mathematics").
Builds here: tectonic, 116 pages, zero errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f0088a317e
commit
60936028a6
4 changed files with 97 additions and 77 deletions
|
|
@ -3,12 +3,23 @@
|
||||||
|
|
||||||
\section{A story about one carry bit}
|
\section{A story about one carry bit}
|
||||||
|
|
||||||
In 2014, researchers examining widely deployed elliptic-curve code found
|
Here is the entire bug that this book exists because of:
|
||||||
arithmetic bugs of a very particular species: the code was correct on
|
|
||||||
\emph{almost every} input. Not most inputs --- almost all of them, in a
|
\begin{lstlisting}
|
||||||
precise sense. One famous example, a carry-propagation flaw in an
|
h[4] += carry; // propagate the top limb's overflow
|
||||||
implementation of curve25519 arithmetic, produced a wrong answer with
|
- // (missing: one more conditional subtraction of p)
|
||||||
probability on the order of $2^{-64}$ per random input.
|
+ if (h[4] >= LIMB_CAP) h[4] -= LIMB_CAP, h[0] += 19;
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\noindent One missing line. A field-arithmetic routine that forgets a single
|
||||||
|
final carry is correct on \emph{almost every} input --- not most, almost
|
||||||
|
\emph{all} of them, in a precise sense --- and silently wrong on the rare
|
||||||
|
ones where that last carry would have fired. Bugs of exactly this species
|
||||||
|
were found in deployed elliptic-curve code and became the motivating
|
||||||
|
disaster behind a whole line of verification research (the Fiat-Cryptography
|
||||||
|
project, Erbsen et al., IEEE S\&P 2019 --- a direct ancestor of the work this
|
||||||
|
book teaches). A representative one produced a wrong answer with probability
|
||||||
|
on the order of $2^{-64}$ per random input.
|
||||||
|
|
||||||
Pause on that number. If you tested this function a billion times per second,
|
Pause on that number. If you tested this function a billion times per second,
|
||||||
around the clock, you should expect to wait \emph{centuries} before a random
|
around the clock, you should expect to wait \emph{centuries} before a random
|
||||||
|
|
@ -24,41 +35,18 @@ than the square of the number of atoms in the observable universe. Testing
|
||||||
samples a raindrop from that ocean.
|
samples a raindrop from that ocean.
|
||||||
\end{pitfall}
|
\end{pitfall}
|
||||||
|
|
||||||
\begin{worked}{feel what $2^{-64}$ means, with the real numbers}
|
\begin{worked}{feel what $2^{-64}$ means}
|
||||||
Claims about astronomical improbability deserve to be checked by hand, so
|
One hit per $2^{64}$ trials, and $2^{64} \approx 1.8 \times 10^{19}$. At a
|
||||||
check this one. A failure probability of $2^{-64}$ per random input means
|
billion tests a second that is $1.8 \times 10^{10}$ seconds to expect a
|
||||||
you expect one hit per $2^{64}$ trials. First, get $2^{64}$ into scientific
|
single failure --- about \textbf{580 years}. So a test farm hammering this
|
||||||
notation the way you always can: $\log_{10} 2 \approx 0.30103$, so
|
function a \emph{billion} times per second, started when Copernicus
|
||||||
\[
|
published, would be expected to see the bug for the first time about now.
|
||||||
\log_{10} 2^{64} = 64 \times 0.30103 \approx 19.27
|
And that is the \emph{optimistic} case: carry bugs cluster in exactly the
|
||||||
\qquad\Longrightarrow\qquad
|
corners uniform sampling underweights, so in practice you wait longer than
|
||||||
2^{64} \approx 1.8 \times 10^{19}.
|
the calendar of the universe. (The one-line $\log_{10}$ derivation behind
|
||||||
\]
|
``580 years,'' and the far more hopeless arithmetic for the full 510-bit
|
||||||
At $10^9$ tests per second, the expected waiting time is
|
space of input \emph{pairs}, are Exercise~1.1 --- worth doing, because the
|
||||||
\[
|
number that falls out has more digits than the universe has atoms.)
|
||||||
\frac{1.8 \times 10^{19}}{10^{9}} = 1.8 \times 10^{10} \text{ seconds}.
|
|
||||||
\]
|
|
||||||
A year is $\approx 3.15 \times 10^{7}$ seconds (a number worth memorizing:
|
|
||||||
``$\pi \times 10^7$ seconds per year'' is accidentally almost exact), so
|
|
||||||
\[
|
|
||||||
\frac{1.8 \times 10^{10}}{3.15 \times 10^{7}} \approx 580 \text{ years}.
|
|
||||||
\]
|
|
||||||
So: a test farm hammering this function a \emph{billion} times per second,
|
|
||||||
started when Copernicus published, would be expected to see the bug for the
|
|
||||||
first time about now. And this is the \emph{optimistic} case where failing
|
|
||||||
inputs are hit by uniform sampling --- for carry bugs they are typically
|
|
||||||
\emph{correlated}, clustered in corners uniform sampling underweights.
|
|
||||||
|
|
||||||
Now the input space itself. A single \lean{FieldElement} is 255 bits; a
|
|
||||||
pair is 510 bits, and
|
|
||||||
\[
|
|
||||||
\log_{10} 2^{510} = 510 \times 0.30103 \approx 153.5
|
|
||||||
\qquad\Longrightarrow\qquad
|
|
||||||
2^{510} \approx 10^{153}.
|
|
||||||
\]
|
|
||||||
For comparison, the number of atoms in the observable universe is around
|
|
||||||
$10^{80}$. Testing all pairs is not ``hard''; it is not a thing that
|
|
||||||
happens in this universe.
|
|
||||||
\end{worked}
|
\end{worked}
|
||||||
|
|
||||||
Why does cryptographic code have bugs of exactly this shape? Because of how it
|
Why does cryptographic code have bugs of exactly this shape? Because of how it
|
||||||
|
|
@ -102,10 +90,12 @@ headroom count, and the headroom count is where the bodies were buried.
|
||||||
\end{worked}
|
\end{worked}
|
||||||
|
|
||||||
And in cryptography, ``rare wrong answer'' does not mean ``rare small
|
And in cryptography, ``rare wrong answer'' does not mean ``rare small
|
||||||
glitch.'' Wrong field arithmetic can leak private keys: several published
|
glitch.'' Wrong field arithmetic can leak the private key itself: published
|
||||||
attacks turn a single faulty group operation into full key recovery. The
|
attacks in the ``invalid-curve'' and fault-injection families turn a
|
||||||
stakes are not a corrupted pixel; they are every signature your machine has
|
\emph{single} faulty group operation into full key recovery --- the attacker
|
||||||
ever made.
|
feeds inputs engineered to land in the buggy corner, and reads the secret
|
||||||
|
off the wrong answers. The stakes are not a corrupted pixel; they are every
|
||||||
|
signature your machine has ever made, and every one it ever will.
|
||||||
|
|
||||||
\section{There is another way}
|
\section{There is another way}
|
||||||
|
|
||||||
|
|
@ -122,8 +112,10 @@ Every input, forever, or the proof does not check.
|
||||||
|
|
||||||
The tool that checks such arguments is called a \emph{proof assistant}. This
|
The tool that checks such arguments is called a \emph{proof assistant}. This
|
||||||
book uses \textbf{Lean~4}, a modern proof assistant that is also a
|
book uses \textbf{Lean~4}, a modern proof assistant that is also a
|
||||||
full-fledged programming language. Others you may have heard of: Rocq
|
full-fledged programming language.\footnote{You may have heard of Rocq
|
||||||
(formerly Coq), Isabelle/HOL, Agda. The ideas transfer; the syntax differs.
|
(formerly Coq), Isabelle/HOL, or Agda. The ideas in this book transfer to
|
||||||
|
all of them; only the syntax differs. We pick Lean~4 for reasons that will
|
||||||
|
be concrete by the end of this chapter.}
|
||||||
|
|
||||||
A proof assistant is built around a small, paranoid core called the
|
A proof assistant is built around a small, paranoid core called the
|
||||||
\emph{kernel}. Everything you will learn in this book --- clever tactics,
|
\emph{kernel}. Everything you will learn in this book --- clever tactics,
|
||||||
|
|
@ -171,21 +163,30 @@ one abstraction level and rests on the layer beneath it:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
By the end of this book you will be able to read --- and extend --- the real
|
\begin{tryit}
|
||||||
proofs at every layer of this pyramid. The journey looks like this:
|
Before you read another word, go and touch the thing this book is about. Open
|
||||||
|
\textbf{\code{ltl.zkdefi.org}} on any device. You are looking at a public,
|
||||||
|
append-only log of machine-checked proofs --- 19 entries, each one a claim of
|
||||||
|
the form ``this exact version of this software was verified, resting on
|
||||||
|
exactly these assumptions.'' Entries 13--16 are the four Ed25519 libraries
|
||||||
|
whose pyramid you see above. Entry~18 is the first \emph{post-quantum} entry
|
||||||
|
in the log. You cannot read the proofs yet --- that is what the next twelve
|
||||||
|
chapters are for --- but you can already see that they are real, public, and
|
||||||
|
checkable by a stranger with a stock laptop. That stranger is who you are
|
||||||
|
becoming.
|
||||||
|
\end{tryit}
|
||||||
|
|
||||||
\begin{itemize}[leftmargin=1.4em]
|
By the end of this book you will be able to read --- and extend --- the real
|
||||||
\item \textbf{Chapters 2--5} teach Lean itself, from \code{\#eval 1+1} to
|
proofs at every layer of this pyramid. To make that concrete, here is what
|
||||||
proofs by induction and the automation that dispatches arithmetic goals.
|
you will personally be able to \emph{do}, and roughly when: by Chapter~7 you
|
||||||
\item \textbf{Chapters 6--7} build the mathematics: modular arithmetic, finite
|
will have handed a paranoid kernel a certificate that a 77-digit number is
|
||||||
fields, and how to convince a paranoid kernel that a 77-digit number is
|
prime, and watched it agree; by Chapter~9 you will read real Rust translated
|
||||||
prime.
|
into Lean and understand the one idea (the \emph{denotation function}) that
|
||||||
\item \textbf{Chapters 8--9} cross the bridge from Rust to Lean: how real
|
makes the translation trustworthy; by Chapter~12 you will stand on the apex
|
||||||
code is translated into a form we can reason about, and the single most
|
and read the signature-verification theorem for the actual code in your SSH
|
||||||
important idea in the whole enterprise --- the \emph{denotation function}.
|
client. The chapters between here and there earn each of those, in order ---
|
||||||
\item \textbf{Chapters 10--12} assemble the pyramid: field correctness, the
|
Lean itself first, then the mathematics, then the bridge from real code, then
|
||||||
ethics of axioms and honest boundaries, and the layers above.
|
the climb.
|
||||||
\end{itemize}
|
|
||||||
|
|
||||||
\begin{bigidea}
|
\begin{bigidea}
|
||||||
\textbf{The ratchet rule of this book.} Every load-bearing idea is worked
|
\textbf{The ratchet rule of this book.} Every load-bearing idea is worked
|
||||||
|
|
@ -239,20 +240,24 @@ Learning to smell those is as important as learning to write proofs at all.
|
||||||
\section{Why Lean, and why now}
|
\section{Why Lean, and why now}
|
||||||
|
|
||||||
Twenty years ago, verifying real cryptographic C or Rust code was a heroic,
|
Twenty years ago, verifying real cryptographic C or Rust code was a heroic,
|
||||||
multi-year effort. Three things changed:
|
multi-year effort. Three things changed --- and each one is an advantage
|
||||||
|
\emph{you} inherit the moment you start:
|
||||||
|
|
||||||
\begin{enumerate}[leftmargin=1.6em]
|
\begin{enumerate}[leftmargin=1.6em]
|
||||||
\item \textbf{Proof assistants matured.} Lean~4 is fast, pleasant, and comes
|
\item \textbf{You start on a million lines of proved mathematics.} Lean~4
|
||||||
with \emph{Mathlib}, a library of over a million lines of formalized
|
ships with \emph{Mathlib} --- finite fields, elliptic curves, number theory,
|
||||||
mathematics --- finite fields and elliptic-curve ingredients included, so we
|
already formalized and checked. You do not build the tower from bare axioms;
|
||||||
do not start from bare axioms.
|
you walk onto a finished floor and add one room.
|
||||||
\item \textbf{Translation pipelines appeared.} Tools like \emph{Charon} and
|
\item \textbf{You verify the code that ships, not a story about it.} Tools
|
||||||
\emph{Aeneas} mechanically translate real Rust code into Lean definitions,
|
called \emph{Charon} and \emph{Aeneas} mechanically translate real Rust into
|
||||||
so the thing we verify is derived from the code that ships, not a
|
Lean, so what you reason about is \emph{derived} from the deployed source
|
||||||
hand-transcribed approximation (Chapter~\ref{ch:rust}).
|
rather than hand-copied by someone who might have copied it wrong
|
||||||
\item \textbf{Automation got serious.} Decision procedures like \lean{omega}
|
(Chapter~\ref{ch:rust}). This is the difference between verifying software
|
||||||
(linear integer arithmetic) and \lean{decide} discharge the boring 90\% of
|
and verifying an essay about software.
|
||||||
goals, leaving humans the interesting 10\%.
|
\item \textbf{The machine does the boring 90\%.} Decision procedures like
|
||||||
|
\lean{omega} and \lean{decide} dispatch the routine arithmetic goals on
|
||||||
|
their own, so your attention goes to the 10\% that is actually interesting
|
||||||
|
--- the part where the real idea lives.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
None of this made verification \emph{easy}. It made verification
|
None of this made verification \emph{easy}. It made verification
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,13 @@ Concretely for our hero: $p - 1 = 2^{255} - 20$ factors as
|
||||||
\[
|
\[
|
||||||
p - 1 \;=\; 2^{2} \cdot 3 \cdot 65147 \cdot Q,
|
p - 1 \;=\; 2^{2} \cdot 3 \cdot 65147 \cdot Q,
|
||||||
\]
|
\]
|
||||||
where $Q$ is a 71-digit prime with its own (short) certificate, and
|
where $Q$ is the 71-digit prime
|
||||||
$65147$ recurses one more level: $65146 = 2 \cdot 32573$ with $32573$
|
\[
|
||||||
|
Q = 740582127325613583022312264370627886761\allowbreak 66966415465897661863160754340907,
|
||||||
|
\]
|
||||||
|
printed here in full --- no hidden digits, this is the exact value in the
|
||||||
|
repository's \code{P25519.lean} --- carrying its own (short) certificate,
|
||||||
|
and $65147$ recurses one more level: $65146 = 2 \cdot 32573$ with $32573$
|
||||||
prime. The full certificate for $p$ is a small tree of witnesses and
|
prime. The full certificate for $p$ is a small tree of witnesses and
|
||||||
factorizations --- a few hundred bytes of data standing behind a 77-digit
|
factorizations --- a few hundred bytes of data standing behind a 77-digit
|
||||||
claim:
|
claim:
|
||||||
|
|
|
||||||
BIN
main.pdf
BIN
main.pdf
Binary file not shown.
14
main.tex
14
main.tex
|
|
@ -28,8 +28,11 @@ no prior formal-verification or Lean experience assumed.\par}
|
||||||
\vspace{0.8cm}
|
\vspace{0.8cm}
|
||||||
{\color{ink2}\rule{\linewidth}{0.6pt}}
|
{\color{ink2}\rule{\linewidth}{0.6pt}}
|
||||||
\vspace{0.3cm}
|
\vspace{0.3cm}
|
||||||
{\small\color{paper} Companion to the \code{*-ed25519-verified} and \code{pasta-pallas-verified}
|
{\small\color{paper} Companion to a public transparency log of machine-checked
|
||||||
proof projects. \\ Every code snippet in this book runs. Every claim it makes about a proof, a proof assistant has checked.\par}
|
proofs --- \textbf{\code{ltl.zkdefi.org}}, 19 entries and counting, one of them
|
||||||
|
post-quantum. Open it on your phone now; by the last chapter you will be able
|
||||||
|
to verify every entry yourself. \\ Every code snippet in this book runs. Every
|
||||||
|
claim it makes about a proof, a proof assistant has checked.\par}
|
||||||
\end{titlepage}
|
\end{titlepage}
|
||||||
\restoregeometry
|
\restoregeometry
|
||||||
\pagecolor{paper}\color{ink}
|
\pagecolor{paper}\color{ink}
|
||||||
|
|
@ -39,6 +42,13 @@ proof projects. \\ Every code snippet in this book runs. Every claim it makes ab
|
||||||
\markboth{How to read this book}{}
|
\markboth{How to read this book}{}
|
||||||
\addcontentsline{toc}{chapter}{How to read this book}
|
\addcontentsline{toc}{chapter}{How to read this book}
|
||||||
|
|
||||||
|
There is a public web page --- \code{ltl.zkdefi.org} --- that lists nineteen
|
||||||
|
pieces of software, each stamped with a machine-checked proof that it does what
|
||||||
|
it claims. One of those stamps was earned two days before the writer of that
|
||||||
|
proof could make anyone else believe it; another survived quantum-resistant
|
||||||
|
cryptography. This book is the road from not understanding a single word on that
|
||||||
|
page to being able to verify every entry on it yourself, and to add your own.
|
||||||
|
|
||||||
You are about to learn one of the most powerful ideas in computer science: how
|
You are about to learn one of the most powerful ideas in computer science: how
|
||||||
to make a computer \emph{prove} that a program is correct --- not test it on a
|
to make a computer \emph{prove} that a program is correct --- not test it on a
|
||||||
few inputs and hope, but establish, with the certainty of mathematics, that it
|
few inputs and hope, but establish, with the certainty of mathematics, that it
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue