pasta-pallas-verified/verification/Proofs/Denote.lean
mrwulf 75ae21df06 pasta field: foundation + helper + sub/neg proofs against REAL extraction
- extract.sh: scoped Charon/Aeneas extraction of fields::fp (pow_vartime
  patched upstream to index loops — semantics-preserving, crate tests pass;
  sqrt/cmp/sum/random/... opaque, documented)
- gen/: real transpiled model; subtle/CtOption hand-modeled (Choice := U8,
  CtOption := value × is_some), all other externals are axioms outside
  certificate cones
- Proofs/PPallas: Lucas/Pratt primality certificate (reused — it was the one
  genuine piece of the previous attempt)
- Proofs/Denote: Montgomery denotation ⟪a⟫ = feVal a · R⁻¹, Canon invariant
- Proofs/HelperSpecs: adc/sbb/mac exact ℕ specs (step-registered)
- Proofs/SubNegSpec: sub_spec (general two-case identity covering the
  t<2P reduction shape) and neg_spec, proven, no axioms

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 15:51:57 +02:00

143 lines
5.7 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/- ──────────────────────────────────────────────────────────────────────────────
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