mirror of
https://github.com/saymrwulf/pasta-pallas-verified.git
synced 2026-09-05 20:10:31 +00:00
144 lines
5.7 KiB
Text
144 lines
5.7 KiB
Text
|
|
/- ──────────────────────────────────────────────────────────────────────────────
|
|||
|
|
Proofs/Denote.lean — the SEMANTIC FOUNDATION: from machine limbs to 𝔽_p
|
|||
|
|
(Pallas base field), MONTGOMERY FORM.
|
|||
|
|
|
|||
|
|
WHAT THIS FILE PROVIDES
|
|||
|
|
* `P`, `Fp := ZMod P` — the Pallas modulus (primality from Proofs/PPallas.lean)
|
|||
|
|
* `Fe := fields.fp.Fp` — the transpiled element type: 4 little-endian u64
|
|||
|
|
limbs (`Array Std.U64 4`)
|
|||
|
|
* `limbsVal`/`feVal` — the EXACT natural-number value Σ lᵢ·2^(64i)
|
|||
|
|
* `R`, `Rinv` — the Montgomery factor 2²⁵⁶ and its inverse in 𝔽_p
|
|||
|
|
* `denote` (⟪·⟫) — THE DENOTATION: ⟪a⟫ = feVal a · R⁻¹ ∈ 𝔽_p.
|
|||
|
|
pasta_curves stores a·R mod p; dividing by R in the denotation makes
|
|||
|
|
every proof statement live in plain 𝔽_p (⟪mul a b⟫ = ⟪a⟫·⟪b⟫ with NO
|
|||
|
|
R-factor bookkeeping at the spec level).
|
|||
|
|
* `Canon` — the representation invariant: feVal a < P. Unlike dalek's
|
|||
|
|
radix-51 code (loose 2⁵²/2⁵⁴ bounds), pasta_curves keeps every element
|
|||
|
|
STRICTLY REDUCED: each op ends with a conditional subtraction of p.
|
|||
|
|
* `Fe.exists_limbs` — the destructuring device every proof starts with.
|
|||
|
|
|
|||
|
|
RUST ANALOG: src/fields/fp.rs — `pub struct Fp(pub(crate) [u64; 4])`,
|
|||
|
|
invariant documented at the type: "little-endian bit order; values are
|
|||
|
|
always in Montgomery form aR mod p, with the reduced representative".
|
|||
|
|
|
|||
|
|
Imports: gen/PallasFp (the transpiled code), Proofs/PPallas (primality).
|
|||
|
|
Imported by: every other proof file.
|
|||
|
|
────────────────────────────────────────────────────────────────────────────── -/
|
|||
|
|
import PallasFp.Funs
|
|||
|
|
import Proofs.PPallas
|
|||
|
|
open Aeneas Aeneas.Std Result
|
|||
|
|
open pasta_curves
|
|||
|
|
|
|||
|
|
namespace PastaProofs
|
|||
|
|
|
|||
|
|
/-- The Pallas base-field modulus
|
|||
|
|
p = 2²⁵⁴ + 45560315531419706090280762371685220353. -/
|
|||
|
|
def P : ℕ := 28948022309329048855892746252171976963363056481941560715954676764349967630337
|
|||
|
|
|
|||
|
|
theorem P_prime : Nat.Prime P := pallas_prime
|
|||
|
|
|
|||
|
|
instance : Fact (Nat.Prime P) := ⟨P_prime⟩
|
|||
|
|
|
|||
|
|
/-- 𝔽_p as mathlib's `ZMod P` — a `Field` because `P` is prime. -/
|
|||
|
|
abbrev Fp := ZMod P
|
|||
|
|
|
|||
|
|
/-- The transpiled element type: 4 little-endian u64 limbs. -/
|
|||
|
|
abbrev Fe := fields.fp.Fp
|
|||
|
|
|
|||
|
|
/-- Exact ℕ value of 4 little-endian u64 limbs. -/
|
|||
|
|
def limbsVal (a0 a1 a2 a3 : U64) : ℕ :=
|
|||
|
|
a0.val + 2^64 * a1.val + 2^128 * a2.val + 2^192 * a3.val
|
|||
|
|
|
|||
|
|
/-- Exact ℕ value of an `Fe`. -/
|
|||
|
|
def feVal (a : Fe) : ℕ :=
|
|||
|
|
match (↑a : List U64) with
|
|||
|
|
| [a0, a1, a2, a3] => limbsVal a0 a1 a2 a3
|
|||
|
|
| _ => 0
|
|||
|
|
|
|||
|
|
/-- Every `Fe` IS four named u64 limbs. -/
|
|||
|
|
theorem Fe.exists_limbs (a : Fe) :
|
|||
|
|
∃ a0 a1 a2 a3 : U64, (↑a : List U64) = [a0, a1, a2, a3] := by
|
|||
|
|
obtain ⟨l, hl⟩ := a
|
|||
|
|
match l, hl with
|
|||
|
|
| [a0, a1, a2, a3], _ => exact ⟨a0, a1, a2, a3, rfl⟩
|
|||
|
|
|
|||
|
|
/-- Once limbs are named, `feVal` unfolds to the polynomial. -/
|
|||
|
|
@[simp]
|
|||
|
|
theorem feVal_eq (a : Fe) (a0 a1 a2 a3 : U64)
|
|||
|
|
(h : (↑a : List U64) = [a0, a1, a2, a3]) :
|
|||
|
|
feVal a = limbsVal a0 a1 a2 a3 := by
|
|||
|
|
unfold feVal; rw [h]
|
|||
|
|
|
|||
|
|
/-- feVal of a literal `Array.make` — the form the generated code produces
|
|||
|
|
(the length side condition `h` is quantified so simp matches any proof). -/
|
|||
|
|
@[simp]
|
|||
|
|
theorem feVal_make (a0 a1 a2 a3 : U64) (h) :
|
|||
|
|
feVal (Array.make 4#usize [a0, a1, a2, a3] h) = limbsVal a0 a1 a2 a3 := rfl
|
|||
|
|
|
|||
|
|
/-- Any `Fe` value is < 2²⁵⁶ (four u64 limbs). -/
|
|||
|
|
theorem feVal_lt (a : Fe) : feVal a < 2^256 := by
|
|||
|
|
obtain ⟨a0, a1, a2, a3, hl⟩ := Fe.exists_limbs a
|
|||
|
|
rw [feVal_eq a a0 a1 a2 a3 hl]
|
|||
|
|
unfold limbsVal
|
|||
|
|
scalar_tac
|
|||
|
|
|
|||
|
|
/-- The representation invariant: strictly reduced (value below the modulus).
|
|||
|
|
Every constructor/operation of the crate maintains this. -/
|
|||
|
|
def Canon (a : Fe) : Prop := feVal a < P
|
|||
|
|
|
|||
|
|
/-- The Montgomery factor. -/
|
|||
|
|
def R : ℕ := 2^256
|
|||
|
|
|
|||
|
|
/-- P is odd (in particular ≠ 2), so 2 — hence R = 2²⁵⁶ — is a unit mod P. -/
|
|||
|
|
theorem two_ne_zero_fp : (2 : Fp) ≠ 0 := by
|
|||
|
|
intro h
|
|||
|
|
have h2 : ((2 : ℕ) : Fp).val = 2 :=
|
|||
|
|
ZMod.val_cast_of_lt (by norm_num [P])
|
|||
|
|
rw [show ((2:ℕ):Fp) = (2:Fp) by push_cast; ring, h, ZMod.val_zero] at h2
|
|||
|
|
norm_num at h2
|
|||
|
|
|
|||
|
|
theorem R_ne_zero : (R : Fp) ≠ 0 := by
|
|||
|
|
have hR : (R : Fp) = (2 : Fp)^256 := by unfold R; push_cast; ring
|
|||
|
|
rw [hR]
|
|||
|
|
exact pow_ne_zero 256 two_ne_zero_fp
|
|||
|
|
|
|||
|
|
/-- R⁻¹ in 𝔽_p (field inverse; noncomputable, spec-level only). -/
|
|||
|
|
noncomputable def Rinv : Fp := (R : Fp)⁻¹
|
|||
|
|
|
|||
|
|
theorem R_mul_Rinv : (R : Fp) * Rinv = 1 :=
|
|||
|
|
mul_inv_cancel₀ R_ne_zero
|
|||
|
|
|
|||
|
|
theorem Rinv_mul_R : Rinv * (R : Fp) = 1 := by
|
|||
|
|
rw [mul_comm]; exact R_mul_Rinv
|
|||
|
|
|
|||
|
|
theorem Rinv_ne_zero : Rinv ≠ 0 := by
|
|||
|
|
intro h
|
|||
|
|
have := R_mul_Rinv
|
|||
|
|
rw [h, mul_zero] at this
|
|||
|
|
exact one_ne_zero this.symm
|
|||
|
|
|
|||
|
|
/-- THE DENOTATION: machine limbs ↦ 𝔽_p, absorbing the Montgomery factor. -/
|
|||
|
|
noncomputable def denote (a : Fe) : Fp := (feVal a : Fp) * Rinv
|
|||
|
|
|
|||
|
|
notation "⟪" a "⟫" => denote a
|
|||
|
|
|
|||
|
|
/-- Two canonical representatives with equal denotation are limb-identical
|
|||
|
|
in value: ⟪·⟫ is injective on `Canon`. -/
|
|||
|
|
theorem denote_inj (a b : Fe) (ha : Canon a) (hb : Canon b)
|
|||
|
|
(h : ⟪a⟫ = ⟪b⟫) : feVal a = feVal b := by
|
|||
|
|
unfold denote at h
|
|||
|
|
have h' : (feVal a : Fp) = (feVal b : Fp) :=
|
|||
|
|
mul_right_cancel₀ Rinv_ne_zero h
|
|||
|
|
have := (ZMod.natCast_eq_natCast_iff' (feVal a) (feVal b) P).mp h'
|
|||
|
|
unfold Canon at ha hb
|
|||
|
|
rwa [Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt hb] at this
|
|||
|
|
|
|||
|
|
/-- Congruence mod P transfers to equal denotations. -/
|
|||
|
|
theorem denote_eq_of_feVal_congr (a b : Fe)
|
|||
|
|
(h : feVal a % P = feVal b % P) : ⟪a⟫ = ⟪b⟫ := by
|
|||
|
|
unfold denote
|
|||
|
|
congr 1
|
|||
|
|
exact (ZMod.natCast_eq_natCast_iff' _ _ _).mpr h
|
|||
|
|
|
|||
|
|
end PastaProofs
|