mirror of
https://github.com/saymrwulf/verifying-crypto-with-lean.git
synced 2026-09-06 20:20:42 +00:00
- 53-page LaTeX/TikZ book (main.pdf + full sources): from zero background to reading the real Ed25519/Pasta verification projects - runnable exercises with sorry-holes + complete solutions for chapters 2-7, 9, 12; every solution file compiles clean (zero errors, no sorry) against Lean v4.30.0-rc2 + Mathlib 5450b53e - lake project pinned to the same toolchain/Mathlib the solutions were verified with; students fetch the Mathlib cache, never build it - honesty ledger in README: what was machine-checked and how Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
186 lines
8.8 KiB
TeX
186 lines
8.8 KiB
TeX
\chapter{Numbers and Automation: Making the Machine Do the Boring Parts}
|
|
\label{ch:automation}
|
|
|
|
\section{The 90/10 rule of verification}
|
|
|
|
Here is a trade secret: most of a real verification effort is not clever.
|
|
Opening the correctness proof of Ed25519 field multiplication, you will find
|
|
that the overwhelming majority of proof obligations are statements like
|
|
|
|
\[
|
|
a < 2^{51} \;\wedge\; b < 2^{51} \;\Longrightarrow\; a + b < 2^{52},
|
|
\qquad\qquad
|
|
(a + b) \cdot c = a\cdot c + b\cdot c,
|
|
\]
|
|
|
|
--- bookkeeping a patient undergraduate could verify by hand in a minute
|
|
each. There are \emph{thousands} of them. The craft of modern verification is
|
|
to hand exactly this 90\% to decision procedures --- tactics that implement a
|
|
complete algorithm for a well-defined logical fragment --- and save the human
|
|
for the 10\% that needs insight. This chapter is your tour of the arsenal.
|
|
|
|
\section{\texttt{omega}: linear arithmetic, decided}
|
|
|
|
The tactic you will use more than any other in this book's domain is
|
|
\lean{omega}. It completely decides \emph{linear arithmetic} over integers
|
|
and naturals: any goal built from variables, constants, $+$, $-$,
|
|
multiplication \emph{by constants}, $=$, $<$, $\le$, $\lnot$, $\wedge$,
|
|
$\vee$, including the hypotheses in context.
|
|
|
|
\begin{lstlisting}[language=Lean]
|
|
example (a b : Nat) (h1 : a < 2^51) (h2 : b < 2^51) :
|
|
a + b < 2^52 := by omega
|
|
|
|
example (a b : Nat) (h : a ≤ b) : a + (b - a) = b := by omega
|
|
-- note: truncated Nat subtraction handled CORRECTLY -- omega knows
|
|
\end{lstlisting}
|
|
|
|
That second example deserves a salute: \lean{omega} understands \lean{Nat}
|
|
truncation natively, defusing the Chapter~\ref{ch:lean} pitfall by algorithm
|
|
rather than by vigilance. When the goal is false, \lean{omega} \emph{fails} ---
|
|
it is a decision procedure, so failure on a linear goal means the goal (with
|
|
the hypotheses in view) is simply not true. That property turns \lean{omega}
|
|
into a \emph{statement-debugging} tool: if it refuses your ``obviously true''
|
|
bound, go find the counterexample; there is one.
|
|
|
|
\begin{pitfall}
|
|
\lean{omega} does not touch multiplication of two \emph{variables}
|
|
($a \cdot b$ is not linear), division in general, or bit-shifts by variables.
|
|
For a goal mixing $a \cdot b$ with bounds, you often first name the product
|
|
--- \lean{have hab : a * b ≤ 2\textasciicircum{}102 := ...} using a multiplication
|
|
monotonicity lemma --- and then let \lean{omega} finish with \lean{hab} as an
|
|
opaque atom. This two-step, \emph{bound the nonlinear part, then release the
|
|
linear solver}, is the single most-used proof pattern in verified field
|
|
arithmetic. You will write it dozens of times, and by Chapter~\ref{ch:field}
|
|
it will feel like breathing.
|
|
\end{pitfall}
|
|
|
|
\section{\texttt{decide}: when truth is a computation}
|
|
|
|
Some propositions can be checked by running an algorithm to completion:
|
|
``$97$ is prime,'' ``these two sorted lists are equal,'' ``$x^3 = x$ for all
|
|
$x$ in $\Zmod{6}$'' (six cases --- check them all). For any such
|
|
\emph{decidable} proposition, the \lean{decide} tactic runs the decision
|
|
algorithm inside Lean's kernel and turns the answer into a proof:
|
|
|
|
\begin{lstlisting}[language=Lean]
|
|
example : Nat.Prime 97 := by decide
|
|
example : ∀ x : ZMod 6, x^3 = x^3 := by decide -- finite: try all six
|
|
\end{lstlisting}
|
|
|
|
The magic and the limitation are the same fact: the \emph{kernel itself}
|
|
re-executes the computation. That makes \lean{decide} unimpeachable --- and
|
|
completely hopeless for our 77-digit prime $2^{255}-19$, where trial division
|
|
would outlast the universe. There is a variant, \lean{native_decide}, that
|
|
compiles the check to native code first --- fast enough! --- but it makes the
|
|
compiler part of your trusted base, an IOU we will scrutinize hard in
|
|
Chapters~\ref{ch:prime} and~\ref{ch:honesty}. For now, the rule of the house:
|
|
\textbf{\lean{decide} yes, \lean{native_decide} never in a final certificate.}
|
|
|
|
\section{\texttt{ring} and \texttt{norm\_num}: algebra on tap}
|
|
|
|
The five-line commutativity shuffle from Chapter~\ref{ch:tactics}? Here is
|
|
the grown-up version:
|
|
|
|
\begin{lstlisting}[language=Lean]
|
|
example (a b c : Nat) : a + b + c = c + b + a := by ring
|
|
|
|
example (a b : ZMod p) : (a + b)^2 = a^2 + 2*a*b + b^2 := by ring
|
|
|
|
example : (2:Int)^255 - 19 > 2^254 := by norm_num
|
|
\end{lstlisting}
|
|
|
|
\lean{ring} proves any identity that holds in every commutative ring ---
|
|
polynomial rearrangements, binomial expansions, distributivity avalanches ---
|
|
by normalizing both sides to a canonical polynomial form and comparing.
|
|
\lean{norm_num} evaluates concrete numeric facts, comfortable with numbers of
|
|
any size. Between \lean{omega}, \lean{ring}, and \lean{norm_num} you now hold
|
|
the three keys that open most arithmetic doors:
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[
|
|
key/.style={draw=ink2,thick,rounded corners=3pt,fill=white,align=center,
|
|
minimum width=3.55cm,minimum height=1.5cm},
|
|
]
|
|
\node[key,fill=accentsoft] (o) at (0,0)
|
|
{\textbf{\code{omega}}\\[1pt]\small linear $+,-,<,\le$\\\small bounds \& carries};
|
|
\node[key,fill=provensoft] (r) at (4.1,0)
|
|
{\textbf{\code{ring}}\\[1pt]\small polynomial identities\\\small in any comm.\ ring};
|
|
\node[key,fill=warnsoft] (n) at (8.2,0)
|
|
{\textbf{\code{norm\_num}}\\[1pt]\small concrete numerals\\\small any size};
|
|
\node[font=\small\color{ink2},align=center] at (4.1,-1.55)
|
|
{the three keys of verified arithmetic --- learn what each fragment
|
|
\emph{excludes}\\ and you will always know which door you are standing in front of};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
\section{\texttt{simp}: the rewriting engine, and how to hold it}
|
|
|
|
\lean{simp} rewrites the goal to exhaustion using a curated database of
|
|
thousands of ``simplification'' lemmas ($x + 0 \rightsquigarrow x$,
|
|
\lean{List.length (a :: l)} $\rightsquigarrow$ \lean{l.length + 1}, ...). It
|
|
is the most powerful tactic in Lean and the easiest to misuse. Used well, it
|
|
clears brush so the real argument stands out. Used lazily ---
|
|
\lean{simp [*]} with every hypothesis thrown in, in a context of sixty
|
|
accumulated facts --- it becomes a search over an enormous rewrite space:
|
|
slow, fragile under library updates, and occasionally a memory monster.
|
|
|
|
This is not hypothetical. During the development this book accompanies, a
|
|
single over-broad \lean{simp}-style discharge in a fat context consumed
|
|
twelve gigabytes of RAM and took down the machine. The postmortem produced
|
|
house rules worth adopting from day one:
|
|
|
|
\begin{itemize}[leftmargin=1.4em]
|
|
\item Prefer \lean{simp only [lemma1, lemma2]} --- an explicit lemma list ---
|
|
in anything you intend to keep.
|
|
\item Let \lean{simp?} tell you the list: run it once interactively, then
|
|
paste the \lean{simp only [...]} it suggests into the file.
|
|
\item Keep contexts lean (pun intended): a proof with sixty hypotheses in
|
|
scope wants to be five \lean{have}-steps with twelve each.
|
|
\end{itemize}
|
|
|
|
\begin{bigidea}
|
|
Automation is a \emph{contract}, not a slot machine. Each tactic decides a
|
|
known fragment: \lean{omega} linear arithmetic, \lean{ring} ring identities,
|
|
\lean{decide} finite computation, \lean{simp only} a rewrite system you
|
|
chose. The professional habit is to know \emph{which} contract you are
|
|
invoking --- then failure is information (``this goal is not linear''; ``this
|
|
identity needs the modulus''), never mystery.
|
|
\end{bigidea}
|
|
|
|
\begin{tryit}
|
|
Open \code{exercises/Ch05.lean}: ten arithmetic goals, each solvable by
|
|
exactly one of \lean{omega} / \lean{ring} / \lean{norm_num} / \lean{decide}.
|
|
Your task is not just to close them but to close each with the \emph{right}
|
|
tool --- the file rejects overkill by design. Goal number ten is the
|
|
Chapter~\ref{ch:tactics} cliffhanger:
|
|
\lean{a < 2\textasciicircum{}51 → a * 19 < 2\textasciicircum{}56}. (It is not linear --- $19$ is a constant, so
|
|
it is! Think, then fire.)
|
|
\end{tryit}
|
|
|
|
\section*{Exercises}
|
|
|
|
\exercise{For each, name the tactic and predict success before running:
|
|
(a) \lean{(a+b)*(a-b) = a*a - b*b} over \lean{Int};
|
|
(b) \lean{a < 100 → b < 100 → a*b < 10000} over \lean{Nat};
|
|
(c) \lean{Nat.Prime 65537};
|
|
(d) \lean{2\textasciicircum{}51 + 2\textasciicircum{}51 = 2\textasciicircum{}52}.}
|
|
|
|
\exercise{Goal (b) above is nonlinear, yet \lean{omega} alone fails while the
|
|
two-step pattern (bound the product with
|
|
\lean{Nat.mul_lt_mul} machinery, then \lean{omega}) succeeds. Carry it out.
|
|
Time yourself; the pattern should take under five minutes by the second
|
|
attempt.}
|
|
|
|
\exercise{Find a true statement about \lean{Nat} that \emph{no} tactic in
|
|
this chapter proves in one shot, and sketch in prose how you would decompose
|
|
it. (Anything genuinely inductive works --- automation here decides
|
|
arithmetic fragments, not all of mathematics.)}
|
|
|
|
\begin{checkpoint}
|
|
You should now be able to: match a goal to its decision procedure by the
|
|
shape of its operators; execute the bound-then-omega pattern for nonlinear
|
|
bounds; explain why \lean{decide} is trustworthy and where it hits its
|
|
computational wall; and state the \lean{simp} discipline --- and the story of
|
|
why this book is unusually sincere about it.
|
|
\end{checkpoint}
|