/-- **Modular exponentiation by repeated squaring.** To compute `bᵉ mod n`, compute`h = b^⌊e/2⌋ mod n` recursively, square it, and multiply by `b` once more if `e` is odd. Eachcall halves `e`, so about `log₂ e` calls suffice, and every intermediate number is below `n²`(or `n² · b`) — never the astronomically large `bᵉ`. -/def powMod (b e n : ℕ) : ℕ := if e = 0 then 1 % n else let h := powMod b (e / 2) n if e % 2 = 0 then h * h % n else h * h % n * b % ntermination_by edecreasing_by omega
powMod_eqtheoremProved here from basic factspropext, Quot.sound
powMod_eqLean 4Proved here from basic factsFormally verified
/-- **Repeated squaring computes the right number:** `powMod b e n = bᵉ mod n`. Strong inductionon `e`, using `e = 2⌊e/2⌋` or `e = 2⌊e/2⌋ + 1` and the fact that reducing mod `n` beforemultiplying does not change the result mod `n` (`Nat.mul_mod`). -/theorem powMod_eq (b e n : ℕ) : powMod b e n = b ^ e % n := by induction e using Nat.strong_induction_on with | _ e ih => rw [powMod] split_ifs with h0 h2 · simp [h0] · have he : e = e / 2 + e / 2 := by omega have ih' := ih (e / 2) (by omega) simp only [ih'] conv_rhs => rw [he, pow_add, Nat.mul_mod] · have he : e = (e / 2 + e / 2) + 1 := by omega have ih' := ih (e / 2) (by omega) simp only [ih'] conv_rhs => rw [he, pow_succ, pow_add, Nat.mul_mod, Nat.mul_mod (b ^ (e / 2))] simp [Nat.mul_mod]
rsa_malleabletheoremProved here from basic factspropext
rsa_malleableLean 4Proved here from basic factsFormally verified
/-- **Textbook RSA is malleable:** the encryptions of `m₁` and `m₂` multiply to the encryption of`m₁ m₂`, because `m₁ᵉ m₂ᵉ = (m₁ m₂)ᵉ`. An attacker can transform a ciphertext into theciphertext of a related message without knowing either — one reason real systems pad messageswith randomness before encrypting. -/theorem rsa_malleable (m₁ m₂ e n : ℕ) : (m₁ ^ e % n) * (m₂ ^ e % n) % n = (m₁ * m₂) ^ e % n := by rw [← Nat.mul_mod, mul_pow]
dh_agreetheoremProved here from basic factspropext
dh_agreeLean 4Proved here from basic factsFormally verified
/-- **Diffie–Hellman key agreement.** With public `g` and `p`, one party publishes `A = gᵃ mod p`,the other `B = gᵇ mod p`; then `Bᵃ ≡ Aᵇ (mod p)`, since both are `g^(ab) mod p`. Reducing mod `p`in between does not matter (`Nat.pow_mod`), and `(gᵇ)ᵃ = g^(ba) = g^(ab) = (gᵃ)ᵇ`. -/theorem dh_agree (g a b p : ℕ) : (g ^ b % p) ^ a % p = (g ^ a % p) ^ b % p := by rw [← Nat.pow_mod, ← Nat.pow_mod, ← pow_mul, ← pow_mul, mul_comm]
dh_toyLean 4Proved here from basic factsFormally verified
/-- A toy exchange in the group of non-zero residues mod `101`, where `2` is a generator. Ama keeps`a = 37` and publishes `2³⁷ mod 101 = 55`; Bolat keeps `b = 58` and publishes `2⁵⁸ mod 101 = 47`.Both compute the shared secret `82`. -/theorem dh_toy : 2 ^ 37 % 101 = 55 ∧ 2 ^ 58 % 101 = 47 ∧ 47 ^ 37 % 101 = 82 ∧ 55 ^ 58 % 101 = 82 := by decide +kernel
otp_decrypttheoremApplies a library theorempropext, Quot.sound
otp_decryptLean 4Applies a library theoremFormally verified
/-- **The one-time pad decrypts correctly:** encrypting with a key by bitwise XOR (`^^^`) andXOR-ing with the same key again returns the message. Core Lean's `Nat.xor_xor_cancel_right`. -/theorem otp_decrypt (m k : ℕ) : (m ^^^ k) ^^^ k = m := Nat.xor_xor_cancel_right m k
otp_unique_keytheoremProved here from basic factspropext, Quot.sound
otp_unique_keyLean 4Proved here from basic factsFormally verified
/-- **Why the one-time pad hides everything:** for any message `m` and any ciphertext `c` thereis exactly one key `k` with `m ^^^ k = c`, namely `k = m ^^^ c`. So if the key is chosenuniformly at random, every message is equally consistent with the ciphertext — Shannon's perfectsecrecy. -/theorem otp_unique_key (m k c : ℕ) : m ^^^ k = c ↔ k = m ^^^ c := by constructor · rintro rfl rw [← Nat.xor_assoc, Nat.xor_self, Nat.zero_xor] · rintro rfl rw [← Nat.xor_assoc, Nat.xor_self, Nat.zero_xor]
/-- Both Diffie–Hellman parties compute the same key.Hint: `Nat.pow_mod` says `a ^ b % n = (a % n) ^ b % n`; then use `pow_mul` and `mul_comm`. -/theorem dh_agree (g a b p : ℕ) : (g ^ b % p) ^ a % p = (g ^ a % p) ^ b % p := by sorry
Show the compiled solution
dh_agreeLean 4Proved here from basic factsFormally verified
/-- **Diffie–Hellman key agreement.** With public `g` and `p`, one party publishes `A = gᵃ mod p`,the other `B = gᵇ mod p`; then `Bᵃ ≡ Aᵇ (mod p)`, since both are `g^(ab) mod p`. Reducing mod `p`in between does not matter (`Nat.pow_mod`), and `(gᵇ)ᵃ = g^(ba) = g^(ab) = (gᵃ)ᵇ`. -/theorem dh_agree (g a b p : ℕ) : (g ^ b % p) ^ a % p = (g ^ a % p) ^ b % p := by rw [← Nat.pow_mod, ← Nat.pow_mod, ← pow_mul, ← pow_mul, mul_comm]
/-- Textbook RSA is multiplicative.Hint: `Nat.mul_mod` (read right to left) and `mul_pow`. -/theorem rsa_malleable (m₁ m₂ e n : ℕ) : (m₁ ^ e % n) * (m₂ ^ e % n) % n = (m₁ * m₂) ^ e % n := by sorry
Show the compiled solution
rsa_malleableLean 4Proved here from basic factsFormally verified
/-- **Textbook RSA is malleable:** the encryptions of `m₁` and `m₂` multiply to the encryption of`m₁ m₂`, because `m₁ᵉ m₂ᵉ = (m₁ m₂)ᵉ`. An attacker can transform a ciphertext into theciphertext of a related message without knowing either — one reason real systems pad messageswith randomness before encrypting. -/theorem rsa_malleable (m₁ m₂ e n : ℕ) : (m₁ ^ e % n) * (m₂ ^ e % n) % n = (m₁ * m₂) ^ e % n := by rw [← Nat.mul_mod, mul_pow]