verifying-crypto-with-lean/exercises/Ch07.lean
saymrwulf 45048d4898 Verifying Cryptography with Lean 4: complete 12-chapter curriculum
- 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>
2026-07-03 09:44:40 +02:00

64 lines
2.7 KiB
Text

/- Chapter 7 — Primality certificates: exercises.
The ladder: certify ever-larger primes, watching what each tool costs.
Requires Mathlib. -/
import Mathlib.Data.ZMod.Basic
import Mathlib.Tactic.NormNum.Prime
namespace Ch07
/- 7.A: rung one — a two-digit prime. `decide` grinds trial division
in the kernel; at this size that is instant. -/
example : Nat.Prime 97 := by
sorry
/- 7.B: rung two — the Fermat prime 65537 = 2^16 + 1 (of RSA fame).
Try `decide` first and FEEL the pause (kernel trial division is
quadratic pain). Then switch to `norm_num`, whose prime extension
builds a certificate instead. Keep whichever is honest AND fast. -/
example : Nat.Prime 65537 := by
sorry
/- 7.C: rung three — the Mersenne prime 2^31 - 1 (ten digits).
Kernel trial division is now out of the question; `norm_num`'s
certificate route answers in seconds. That difference IS the chapter.
(Curious how far certificates scale? 2^61 - 1 already needs minutes —
witness trees are cheap to CHECK but costly to FIND.)
Hint: norm_num's prime extension wants a LITERAL numeral; `show` can
convert the goal to a definitionally equal one first. -/
example : Nat.Prime (2^31 - 1) := by
sorry
/- 7.D: your own hands on a Pratt witness (paper + #eval).
For p = 13: verify that w = 2 satisfies both witness conditions:
w^(p-1) = 1 (mod p) and w^((p-1)/q) ≠ 1 (mod p)
for each prime q dividing p - 1 = 12 (namely q = 2 and q = 3).
Fill in the two missing #evals; check against hand computation. -/
#eval (2:ZMod 13)^12 -- must be 1
-- #eval ... -- w^(12/2): must NOT be 1
-- #eval ... -- w^(12/3): must NOT be 1
/- 7.E (the finale): the top node of the certificate for the real prime,
checked with your own modular exponentiation. First, implement fast
square-and-multiply: at each step, square the base, halve the
exponent, and fold the base into the accumulator when the exponent
bit is 1. (The `fuel` argument just guarantees termination; 300 > the
255 bits we need.) -/
def powModAux : Nat → Nat → Nat → Nat → Nat → Nat
| 0, _, _, _, acc => acc
| fuel+1, b, e, m, acc =>
sorry -- if e = 0 we are done; otherwise recurse as described above
def powMod (b e m : Nat) : Nat :=
if m ≤ 1 then 0 else powModAux 300 (b % m) e m 1
-- sanity checks for your powMod:
-- #eval powMod 2 12 13 -- expected: 1
-- #eval powMod 3 4 7 -- expected: 4 (81 = 11*7 + 4)
/- …then check the first witness condition for w = 2 at 77 digits.
Predict the output before you run it. Note the speed: THAT is why
certificate checking is cheap. -/
def p : Nat := 2^255 - 19
-- #eval powMod 2 (p - 1) p -- prediction: ?
end Ch07