\chapter{The Denotation Bridge: What Do Five Numbers \emph{Mean}?} \label{ch:denotation} \section{Two worlds, one bridge} We now hold two very different objects. On one side, mathematics: the field $\Fp$, where elements are abstract clock positions and $+$ means ideal modular addition. On the other side, the extracted model: arrays of five \lean{U64} words, shuffled by loads, adds, and stores. The entire question of verified cryptography is how to say --- precisely --- that the second \emph{implements} the first. The answer is a single function, small enough to write on one line and important enough to carry this whole book. Given limbs $a = (a_0, a_1, a_2, a_3, a_4)$, define the \textbf{denotation}: \[ \denote{a} \;=\; a_0 + 2^{51} a_1 + 2^{102} a_2 + 2^{153} a_3 + 2^{204} a_4 \;\in\; \Fp . \] Read $\denote{a}$ as ``the field element these limbs \emph{mean}.'' It is positional notation, nothing more --- base $2^{51}$ instead of base 10, with the result interpreted on the clock face of $\Fp$. In Lean: \begin{lstlisting}[language=Lean] def denote (a : Array U64 5) : ZMod p := a[0].val + 2^51 * a[1].val + 2^102 * a[2].val + 2^153 * a[3].val + 2^204 * a[4].val \end{lstlisting} With the bridge in hand, correctness of an operation becomes a \emph{commuting square} --- one picture you should internalize until you see it in your sleep: \begin{center} \begin{tikzpicture}[ world/.style={font=\small,align=center}, arr/.style={-{Stealth},thick,ink2}, lbl/.style={font=\small\color{ink2}} ] \node[world] (tl) at (0,2.6) {$(a, b)$\\ \footnotesize limb arrays}; \node[world] (tr) at (7.2,2.6) {$\mathtt{add}(a,b)$\\ \footnotesize limb array}; \node[world] (bl) at (0,0) {$(\denote{a}, \denote{b})$\\ \footnotesize field elements}; \node[world] (br) at (7.2,0) {$\denote{a} + \denote{b}$\\ \footnotesize field element}; \draw[arr] (tl) -- node[lbl,above] {machine code} (tr); \draw[arr] (bl) -- node[lbl,below] {ideal math} (br); \draw[arr] (tl) -- node[lbl,left] {$\denote{\cdot}$} (bl); \draw[arr] (tr) -- node[lbl,right] {$\denote{\cdot}$} (br); \node[font=\small\color{accent},align=center] at (3.6,1.3) {\textbf{the theorem:}\\ both routes agree}; \end{tikzpicture} \end{center} \begin{bigidea} \textbf{The correctness of an implementation is the statement that denotation commutes with every operation:} \[ \denote{\mathtt{add}(a,b)} = \denote{a} + \denote{b}, \qquad \denote{\mathtt{mul}(a,b)} = \denote{a} \cdot \denote{b}, \qquad\dots \] The left-hand side lives in the machine world (with its bounds hypotheses and \lean{Result}s); the right-hand side is pure mathematics. One equation per operation, and the ugly optimized code is pinned, forever, to the textbook meaning. Every verified-crypto project you will ever read is this diagram, instantiated. \end{bigidea} \section{Why redundancy is freedom (and where bugs hide)} A subtlety with consequences: denotation is \textbf{many-to-one}. The limb arrays $(19, 0, 0, 0, 0)$ and $(p + 19 \bmod 2^{\cdots}, \dots)$ --- or more mundanely, unreduced sums whose limbs exceed $2^{51}$ --- can denote the \emph{same} field element. The representation has slack, and the implementation \emph{exploits} it: the fast \code{add} from Chapter~\ref{ch:rust} just adds limbs pairwise, letting values drift above $2^{51}$, and nobody reduces until a cheaper moment. The commuting square still closes because $\denote{\cdot}$ doesn't care how bloated the limbs are --- positional value is positional value. \begin{worked}{denoting the prime itself --- a telescope in radix 51} The canonical example of a collision uses the most important limb array in the codebase: the representation of $p$ itself, \[ P \;=\; (2^{51}-19,\; 2^{51}-1,\; 2^{51}-1,\; 2^{51}-1,\; 2^{51}-1). \] Claim: $\denote{P} = p \equiv 0$, so $P$ collides with $(0,0,0,0,0)$. Verify by hand --- the sum telescopes beautifully. Write out the positional value: \[ \denote{P}_{\Z} = (2^{51}-19) + (2^{51}-1)2^{51} + (2^{51}-1)2^{102} + (2^{51}-1)2^{153} + (2^{51}-1)2^{204}. \] Expand each product: $(2^{51}-1)2^{51k} = 2^{51(k+1)} - 2^{51k}$. So the sum is \[ (2^{51} - 19) + (2^{102} - 2^{51}) + (2^{153} - 2^{102}) + (2^{204} - 2^{153}) + (2^{255} - 2^{204}). \] Every power except the last cancels against its neighbor --- slide a pen along the chain and watch $2^{51}, 2^{102}, 2^{153}, 2^{204}$ annihilate --- leaving \[ \denote{P}_{\Z} = 2^{255} - 19 = p \;\equiv\; 0 \pmod p. \qquad ✓ \] This computation is why the constant array $P$ (and its multiple $16P$, Chapter~\ref{ch:lean}'s worked example) appears throughout the dalek source: adding $P$ limb-wise changes nothing denotationally, and that ``nothing'' is exactly the freedom the subtraction trick spends. When the verified \code{sub} spec says \lean{denote (sub a b) = denote a - denote b}, the proof's central move is this telescope, done once, as a lemma. \end{worked} This is also exactly where the carry bugs of Chapter~\ref{ch:why} live: code that is correct only while the drift stays within headroom, and wrong on the rare inputs where it spills. In the verified development that danger becomes a visible, machine-checked pair of clauses attached to every operation: \begin{lstlisting}[language=Lean] theorem add_spec (ha : Bnd54 a) (hb : Bnd54 b) : ∃ c, add a b = .ok c ∧ Bnd55 c -- (1) bounds: the envelope holds ∧ denote c = denote a + denote b -- (2) value: the meaning is right \end{lstlisting} Clause (2) is the commuting square. Clause (1) feeds the \emph{next} operation's hypothesis --- correctness composes only because every theorem hands the following one the envelope it requires. A chain of such specs is the formal skeleton of ``this sequence of optimized operations computes the formula we claim.'' \begin{aha} The denotation idea is vastly older and bigger than cryptography. Compilers prove ``optimized code means the same as naive code''; databases prove ``this query plan means the same query''; hardware verifies ``this pipelined circuit means this instruction set.'' The pattern --- map both sides into a mathematical meaning-space and prove the square commutes --- is called \emph{denotational semantics}, and you have now used it for real. It is the single most transferable idea in this book. \end{aha} \section{Multiplication: where the bridge earns its keep} Addition's square closes in an afternoon. Multiplication is the boss fight, and seeing \emph{why} teaches you what verified arithmetic is really like. Schoolbook multiplication of two 5-limb numbers produces nine columns of partial products $\sum_{i+j=k} a_i b_j$; each column then owes a \emph{carry} to the next; and columns $k \ge 5$ --- weights $2^{255}$ and up --- must be folded back using the Chapter~\ref{ch:modular} identity $2^{255} \equiv 19$. The implementation interleaves all three concerns for speed. The proof must un-interleave them: \[ \denote{\mathtt{mul}(a,b)} \;\overset{?}{=}\; \Big(\textstyle\sum_{k=0}^{8} 2^{51k} \sum_{i+j=k} a_i b_j \Big) \bmod p \;\overset{?}{=}\; \denote{a} \cdot \denote{b} . \] The right equality is algebra --- \lean{ring} territory. The left is a walk through the extracted code: every intermediate \lean{U128} product bounded (no overflow --- the $2^{54}$ headroom at work), every carry accounted, every $\times 19$ fold placed. In the companion projects this is a long \lean{calc}-and-\lean{have} museum: dozens of small steps, each dispatched by \lean{omega} or a bound lemma, composed into one commuting square. \begin{worked}{the $\times 19$ fold, derived at the real weights} The fold is where students usually first believe the whole enterprise is black magic; five lines of weight arithmetic dispel it, at full scale. Schoolbook multiplication of $a$ and $b$ (limbs $a_0..a_4$, $b_0..b_4$) produces columns $c_k = \sum_{i+j=k} a_i b_j$ at weights $2^{51k}$ for $k = 0, \dots, 8$. Columns $5$--$8$ carry weights $2^{255}, 2^{306}, 2^{357}, 2^{408}$ --- all off the top of the representation. Reduce each weight with the Chapter~\ref{ch:modular} identity $2^{255} \equiv 19$: \[ 2^{51(k+5)} \;=\; 2^{255} \cdot 2^{51k} \;\equiv\; 19 \cdot 2^{51k} \pmod{p}, \qquad k = 0, 1, 2, 3 . \] So column $5$ re-enters at weight $2^{0}$ scaled by $19$, column $6$ at weight $2^{51}$ scaled by $19$, and so on --- each overflow column lands exactly one radix position below where it left, times $19$. The folded five-column result is therefore \[ \begin{array}{lcl} r_0 &=& c_0 + 19\, c_5\\ r_1 &=& c_1 + 19\, c_6\\ r_2 &=& c_2 + 19\, c_7\\ r_3 &=& c_3 + 19\, c_8\\ r_4 &=& c_4 \end{array} \] --- which, written with the products expanded ($r_0 = a_0 b_0 + 19(a_1 b_4 + a_2 b_3 + a_3 b_2 + a_4 b_1)$, etc.), is \emph{character-for-character} the mysterious formula block in \code{curve25519-dalek}'s \code{mul}, the one decorated with the comment ``see the comment above'' that every reader of that file has squinted at. You have now derived it. Two bound checks make it safe, both already yours: each $19 c_k < 2^{5} \cdot 2^{111} = 2^{116}$ (the Chapter~\ref{ch:automation} column bound plus five bits for the $19$), still comfortably inside a \lean{U128}. And the formal proof of \lean{mul}'s commuting square is precisely this box: the four weight identities as \lean{have}s, the bound checks as \lean{omega} goals, and \lean{ring} to shuffle the expanded polynomial. \end{worked} One more representational dialect, because you will meet it in the Pasta repos: \textbf{Montgomery form} stores $x$ as $x \cdot R \bmod p$ (with $R = 2^{256}$) because it makes reduction after multiplication cheap. The bridge absorbs the twist without complaint --- define $\denote{a}_{\mathrm{M}} = (\text{positional value of } a) \cdot R^{-1}$ and the same commuting squares govern everything. Denotation is a \emph{policy about meaning}, and it bends to fit the representation, not the other way around. \begin{worked}{the cast --- moving an equation from $\N$ to the clock face} Every denotation proof ends with the same quiet move --- ``now reduce mod $p$'' --- and in a proof assistant that move must be performed, not gestured at. Here is the by-hand version of what \lean{push_cast} and friends do, on the Interlude's own equation. You hold an \emph{exact} identity of natural numbers: \[ \underbrace{v}_{\text{output value}} + 15\,c_2 = \underbrace{(a_0 + 4a_1)}_{\denote{a}\text{'s integer}} + \underbrace{(b_0 + 4b_1)}_{\denote{b}\text{'s integer}} \qquad\text{in } \N . \] You want its shadow in $\Zmod{15}$. The bridge is the \emph{cast homomorphism} $\iota : \N \to \Zmod{15}$ (send each number to its clock position), and the three facts that make it usable: (i)~$\iota(x + y) = \iota(x) + \iota(y)$ and $\iota(x \cdot y) = \iota(x)\,\iota(y)$ --- casting commutes with arithmetic, so the equation's \emph{shape} survives; (ii)~equal naturals cast to equal clock positions --- so the equation's \emph{truth} survives; (iii)~$\iota(15) = 0$ --- the modulus dies. Apply (ii), then distribute $\iota$ through both sides by (i), then kill the $15$ by (iii): \[ \iota(v) + 0 \cdot \iota(c_2) = \iota(a_0) + 4\,\iota(a_1) + \iota(b_0) + 4\,\iota(b_1) \quad\Longrightarrow\quad \denote{\text{out}} = \denote{a} + \denote{b}. \;\blacksquare \] That is all \lean{push_cast} does: drive $\iota$ inward through $+$ and $\times$ by fact (i), normalizing the statement so facts (ii)/(iii) can fire. Knowing the hand version buys you two things. You can \emph{predict} when the tactic will fail --- fact (i) has no clause for subtraction or division in $\N$ (truncation! Chapter~\ref{ch:lean}), so casts do not push through them, which is precisely why the proof pathway insists on an exact identity with the correction term \emph{added on the left} rather than subtracted on the right. And you can \emph{read} the real proofs' cast steps as what they are: the one-line ceremony where a theorem about machine words becomes a theorem about field elements --- the bridge being crossed, visibly, in both media. \end{worked} \begin{pitfall} When a denotation proof refuses to close, the failure is information --- read it like a detective, in order: (1) Is the \emph{bound} hypothesis strong enough for the intermediate products? (Count bits, on paper.) (2) Is the \emph{denotation} right for this representation --- radix, limb count, Montgomery factor? (3) Only then suspect the code. In the companion projects this checklist ran hundreds of times; its order reflects the actual base rates of what was wrong. \end{pitfall} \begin{tryit} Open \code{exercises/Ch09.lean}. It builds a miniature of the whole story you can hold in your head: a \emph{2-limb, radix-4} representation of $\Zmod{15}$ (limbs are values $0$--$3$, denotation $a_0 + 4a_1$, and $16 \equiv 1$ makes the fold trivial). You will write \lean{denote}, prove the commuting square for the provided \lean{add} with carry, then for \lean{mul} with its fold --- every conceptual ingredient of the dalek proof, at a scale where \lean{decide} can double-check your work. \end{tryit} \section*{Exercises} \exercise{Compute by hand the denotation of the limb arrays $(19,0,0,0,0)$ and $(0,0,0,0,2^{51})$ in the radix-51 system, reducing mod $p = 2^{255}-19$. Conclude that $\denote{\cdot}$ is not injective by exhibiting the collision.} \exercise{In the mini-system of the Try It box, find two distinct limb pairs denoting the same element of $\Zmod{15}$, and check that the provided \lean{add} treats them interchangeably \emph{as far as denotation goes} --- compute both sides.} \exercise{Sketch the multiplication column sums $\sum_{i+j=k} a_i b_j$ for the 2-limb system and carry out the fold $16 \equiv 1$ by hand for $a = (3,2)$, $b = (1,3)$. Check against direct computation in $\Zmod{15}$.} \exercise{(Paper, challenge) For the radix-51 system with limbs bounded by $2^{54}$: bound one column $\sum_{i+j=4} a_i b_j$ of partial products and confirm it fits a \lean{U128}. How much headroom remains? This number --- not elegance --- is why the invariant chose $2^{54}$.} \section*{Solutions and pathways} \solutionsintro \solhead{9.1} \pathway Compute both denotations as integers, then reduce mod $p$. One of them should ring a bell from the reduction identity. \answer $\denote{(19,0,0,0,0)}_{\Z} = 19$. For $(0,0,0,0,2^{51})$: the top limb sits at weight $2^{204}$, so the value is $2^{51} \cdot 2^{204} = 2^{255} \equiv 19 \pmod p$ --- the crown identity of Chapter~\ref{ch:modular} again. Two visibly different arrays, one field element: $\denote{\cdot}$ is not injective, witnessed by the pair $\big((19,0,0,0,0),\, (0,0,0,0,2^{51})\big)$. Note that the second array even violates the ``reduced'' bound ($2^{51} \not< 2^{51}$) --- it is exactly the kind of drifted-but-meaningful value lazy carries produce, and the denotation handles it without complaint. \solhead{9.2} \pathway In the toy system, hunt for a nonzero array whose positional value is a multiple of $15$. The largest representable value is $3 + 4\cdot 3 = 15$ itself --- convenient. \answer $\denote{(3,3)} = 3 + 4 \cdot 3 = 15 \equiv 0 = \denote{(0,0)}$. Interchangeability under \lean{add} with, say, $(1,2)$: the machine runs differ --- $\mathtt{add}\ (3,3)\ (1,2)$ produces carries, $\mathtt{add}\ (0,0)\ (1,2)$ does not --- but both outputs denote $0 + (1 + 4\cdot 2) = 9$. (With the definitions in \code{exercises/Ch09.lean}: $\mathtt{add}\ (3,3)\ (1,2)$: $s_0 = 4$, $s_1 = 3 + 2 + 1 = 6$, output $(0 + 1, 2) = (1,2)$, denotation $9$ ✓; $\mathtt{add}\ (0,0)\ (1,2) = (1,2)$, denotation $9$ ✓.) The spec never promised equal \emph{arrays} --- only equal \emph{meanings}; representation freedom is preserved by every operation, which is the entire content of ``the square commutes.'' \solhead{9.3} \pathway Write the three columns of the $2\times 2$ schoolbook product, fold the top one with $16 \equiv 1$, then cross-check against a direct computation in $\Zmod{15}$ --- at toy scale you can afford both routes, which is the point of having a toy. \answer For $a = (3,2)$, $b = (1,3)$: columns $c_0 = a_0 b_0 = 3$, $c_1 = a_0 b_1 + a_1 b_0 = 9 + 2 = 11$, $c_2 = a_1 b_1 = 6$ at weights $1, 4, 16$. Fold: $16 \equiv 1$, so $c_2$ re-enters at weight $1$: \[ \text{value} \equiv (c_0 + c_2) + 4 c_1 = 9 + 44 = 53 \equiv 53 - 45 = 8 \pmod{15}. \] Direct route: $\denote{a} = 3 + 8 = 11$, $\denote{b} = 1 + 12 = 13$; $11 \cdot 13 = 143 = 9 \cdot 15 + 8 \equiv 8$ ✓. Both routes, one answer --- you have closed a commuting square numerically. The Lean theorem \lean{mulVal_spec} is this computation with the numbers replaced by universally quantified variables: same fold, all inputs at once. \solhead{9.4} \pathway This is the Chapter~\ref{ch:automation} worked example --- reconstruct it from memory before checking back. \answer Each product $a_i b_j < 2^{54} \cdot 2^{54} = 2^{108}$; column $c_4$ has five of them, so $c_4 < 5 \cdot 2^{108} < 2^{111} < 2^{128}$, leaving $17$ bits of headroom --- consumed, in the real code, by the carry-in from the previous column and the $\times 19$ fold factor ($19 < 2^5$: the fold costs five of the seventeen bits, which is why the margin looks generous and is not). Run the same audit at bound $2^{55}$: $5 \cdot 2^{110} < 2^{113}$, fold to $2^{118}$ --- still fits, but the \emph{addition} chain budget halves; at $2^{63}$: $5 \cdot 2^{126} > 2^{128}$ --- broken outright. The invariant $2^{54}$ is where the operations' competing demands settle, and now you have audited the settlement yourself. \begin{checkpoint} You should now be able to: write the radix-51 denotation from memory; draw the commuting square and label which side owns bounds and \lean{Result}s; explain why many-to-one representation is both the performance trick and the bug habitat; and recognize the two-clause shape (bounds propagation + value equation) as the universal skeleton of implementation-correctness theorems. \end{checkpoint}