diff --git a/Cslib.lean b/Cslib.lean index f21b49009..ecb4d0129 100644 --- a/Cslib.lean +++ b/Cslib.lean @@ -54,6 +54,8 @@ public import Cslib.Computability.URM.Defs public import Cslib.Computability.URM.Execution public import Cslib.Computability.URM.StandardForm public import Cslib.Computability.URM.StraightLine +public import Cslib.Crypto.Primitives.ECC.Basic +public import Cslib.Crypto.Primitives.ECC.TwistedEdwardsCurve public import Cslib.Crypto.Protocols.PerfectSecrecy.Basic public import Cslib.Crypto.Protocols.PerfectSecrecy.Defs public import Cslib.Crypto.Protocols.PerfectSecrecy.Encryption @@ -64,6 +66,15 @@ public import Cslib.Crypto.Protocols.SecretSharing.Defs public import Cslib.Crypto.Protocols.SecretSharing.Scheme public import Cslib.Crypto.Protocols.SecretSharing.Shamir public import Cslib.Crypto.Protocols.SecretSharing.Shamir.Polynomial +public import Cslib.Crypto.Systems.Elligator.Basic +public import Cslib.Crypto.Systems.Elligator.Context +public import Cslib.Crypto.Systems.Elligator.Elligator1.AuxiliaryCoordinates +public import Cslib.Crypto.Systems.Elligator.Elligator1.CurveParameters +public import Cslib.Crypto.Systems.Elligator.Elligator1.EdwardsCurve +public import Cslib.Crypto.Systems.Elligator.Elligator1.Map +public import Cslib.Crypto.Systems.Elligator.Elligator1.OutputCoordinates +public import Cslib.Crypto.Systems.Elligator.FiniteFieldBasic +public import Cslib.Crypto.Systems.Elligator.LegendreSymbol public import Cslib.Foundations.Combinatorics.InfiniteGraphRamsey public import Cslib.Foundations.Control.Monad.Free public import Cslib.Foundations.Control.Monad.Free.Effects diff --git a/Cslib/Crypto/Primitives/ECC/Basic.lean b/Cslib/Crypto/Primitives/ECC/Basic.lean new file mode 100644 index 000000000..730199853 --- /dev/null +++ b/Cslib/Crypto/Primitives/ECC/Basic.lean @@ -0,0 +1,9 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl +-/ + +module + +public import Cslib.Init diff --git a/Cslib/Crypto/Primitives/ECC/TwistedEdwardsCurve.lean b/Cslib/Crypto/Primitives/ECC/TwistedEdwardsCurve.lean new file mode 100644 index 000000000..2f5f5dd7c --- /dev/null +++ b/Cslib/Crypto/Primitives/ECC/TwistedEdwardsCurve.lean @@ -0,0 +1,120 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl +-/ + +module + +public import Cslib.Crypto.Primitives.ECC.Basic +public import Mathlib.Algebra.Ring.Commute +public import Mathlib.Data.Set.Defs + +/-! +# Twisted Edwards curves + +This file contains the curve-level definitions that are independent of any specific Elligator. +A twisted Edwards curve with coefficients `a` and `d` has affine equation +`a * x ^ 2 + y ^ 2 = 1 + d * x ^ 2 * y ^ 2`. + +The definitions are made over a commutative ring. Finiteness and the hypotheses used by a +particular cryptographic construction belong in that construction, rather than in the definition +of a curve or its affine points. + +Mathlib's elliptic-curve API is currently centred on Weierstrass models. A twisted Edwards model +is not itself a Weierstrass equation, so using `WeierstrassCurve.Affine.Equation` here would require +a birational coordinate conversion and extra invertibility hypotheses. The API below follows the +same useful separation as that API: coefficients, an affine equation, a set of affine points, and +a bundled point type. + +## Main definitions + +* `TwistedEdwardsCurve`: the coefficients `a`, `d` of a twisted Edwards model, with its equation + `TwistedEdwardsCurve.Equation`, its affine points `TwistedEdwardsCurve.affinePoints` and the + nonsingularity condition `TwistedEdwardsCurve.IsValid`. +* `edwardsCurve d`: the untwisted Edwards curve `x ^ 2 + y ^ 2 = 1 + d * x ^ 2 * y ^ 2`. + +## TODO + +- Move into mathlib next to Mathlib.AlgebraicGeometry.EllipticCurve.Weierstrass + +## References + +* [Bernstein2007a], Section 2. +* [Bernstein2008a], Section 2, Definition 2.1. + +-/ + +@[expose] public section +namespace Cslib.Crypto.Primitives.ECC + +/-- Coefficients of the twisted Edwards equation +`a * x ^ 2 + y ^ 2 = 1 + d * x ^ 2 * y ^ 2`. -/ +@[ext] +structure TwistedEdwardsCurve (R : Type*) where + /-- left hand side coefficient -/ + a : R + /-- right hand side coefficient -/ + d : R + +variable {R : Type*} [CommRing R] + +namespace TwistedEdwardsCurve + +/-- The proposition that `(x, y)` is an affine point of a twisted Edwards curve. -/ +def Equation (E : TwistedEdwardsCurve R) (x y : R) : Prop := + E.a * x ^ 2 + y ^ 2 = 1 + E.d * x ^ 2 * y ^ 2 + +/-- The set of affine coordinate pairs on a twisted Edwards curve. -/ +def affinePoints (E : TwistedEdwardsCurve R) : Set (R × R) := {p | E.Equation p.1 p.2} + +/-- A bundled affine point on a twisted Edwards curve. -/ +abbrev Point (E : TwistedEdwardsCurve R) := {p : R × R // p ∈ E.affinePoints} + +/-- The neutral affine coordinate pair `(0, 1)`. It lies on every twisted Edwards equation. -/ +def zero : R × R := (0, 1) + +lemma zero_mem_affinePoints (E : TwistedEdwardsCurve R) : zero ∈ E.affinePoints := by + change E.a * 0 ^ 2 + 1 ^ 2 = 1 + E.d * 0 ^ 2 * 1 ^ 2 + simp + +/-- The neutral point, bundled as an affine point of `E`. -/ +def zeroPoint (E : TwistedEdwardsCurve R) : E.Point := ⟨zero, E.zero_mem_affinePoints⟩ + +/-- Negation of affine coordinates on a twisted Edwards curve. -/ +def neg (p : R × R) : R × R := (-p.1, p.2) + +lemma neg_mem_affinePoints (E : TwistedEdwardsCurve R) (p : R × R) : + neg p ∈ E.affinePoints ↔ p ∈ E.affinePoints := by + change E.a * (-p.1) ^ 2 + p.2 ^ 2 = 1 + E.d * (-p.1) ^ 2 * p.2 ^ 2 ↔ + E.a * p.1 ^ 2 + p.2 ^ 2 = 1 + E.d * p.1 ^ 2 * p.2 ^ 2 + rw [neg_sq] + +/-- The usual coefficient conditions for a nonsingular twisted Edwards model over a field. +Keeping this predicate separate from `TwistedEdwardsCurve` permits the equation and its points to +be used over more general rings and also permits partially specified curves during developments. +-/ +def IsValid (E : TwistedEdwardsCurve R) : Prop := E.a ≠ 0 ∧ E.d ≠ 0 ∧ E.a ≠ E.d + +end TwistedEdwardsCurve + +/-- The (untwisted) Edwards curve with coefficient `d`, obtained by setting `a = 1`. -/ +def edwardsCurve (d : R) : TwistedEdwardsCurve R where + a := 1 + d := d + +/-- The equation of `edwardsCurve d`, written out. -/ +lemma edwardsCurve_equation_iff (d x y : R) : + (edwardsCurve d).Equation x y ↔ x ^ 2 + y ^ 2 = 1 + d * x ^ 2 * y ^ 2 := by + simp [TwistedEdwardsCurve.Equation, edwardsCurve] + +/-- `edwardsCurve d` is a valid (nonsingular) model exactly when `d ≠ 0` and `d ≠ 1`. -/ +lemma edwardsCurve_isValid_iff [Nontrivial R] (d : R) : + (edwardsCurve d).IsValid ↔ d ≠ 0 ∧ d ≠ 1 := by + constructor + · rintro ⟨_, hd, had⟩ + exact ⟨hd, fun h ↦ had h.symm⟩ + · rintro ⟨hd, hd1⟩ + exact ⟨one_ne_zero, hd, fun h ↦ hd1 h.symm⟩ + +end Cslib.Crypto.Primitives.ECC diff --git a/Cslib/Crypto/Systems/Elligator/Basic.lean b/Cslib/Crypto/Systems/Elligator/Basic.lean new file mode 100644 index 000000000..7c1526e01 --- /dev/null +++ b/Cslib/Crypto/Systems/Elligator/Basic.lean @@ -0,0 +1,10 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl +-/ +module + +public import Cslib.Init +public import Mathlib.Algebra.Field.Defs +public import Mathlib.FieldTheory.Finite.Basic diff --git a/Cslib/Crypto/Systems/Elligator/Context.lean b/Cslib/Crypto/Systems/Elligator/Context.lean new file mode 100644 index 000000000..92d74c109 --- /dev/null +++ b/Cslib/Crypto/Systems/Elligator/Context.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl +-/ +module + +public import Cslib.Crypto.Systems.Elligator.Basic + +/-! +# Bundled data and hypotheses for Elligator + +Almost every statement of the Elligator 1 development repeats the same variables and the same +standing hypotheses: + +* a finite field `F` whose cardinality `q` satisfies `q % 4 = 3`, +* a curve parameter `s`, sometimes with `s ≠ 0`, sometimes with `(s ^ 2 - 2) * (s ^ 2 + 2) ≠ 0`, +* an input `t ∉ {1, -1}` or a point `P` of `E(F)`. + +This file provides two independent mechanisms for getting rid of this repetition: + +1. *the variables are bundled* into a small inheritance hierarchy of `structure`s carrying data + only (`ParamData`, `InputData`, `MapData`, `PointData`), which is what makes dot notation such + as `M.u`, `M.v`, `M.X` available; +2. *the hypotheses are unbundled* into one `class` per hypothesis (`IsCardThreeModFour`, + `IsPrimeCard`, `IsNonzeroParam`, `IsRegularParam`), which a statement lists individually and + which are found by instance resolution instead of being passed by hand. +-/ + +@[expose] public section + +namespace Cslib.Crypto.Systems.Elligator + +variable {F : Type*} [Field F] + +/-- The base field has cardinality `q ≡ 3 (mod 4)`. -/ +class IsCardThreeModFour (F : Type*) [Fintype F] : Prop where + /-- The cardinality of `F` is congruent to `3` modulo `4`. -/ + card_mod_four : Fintype.card F % 4 = 3 + +/-- The base field has prime cardinality; this is the extra assumption of Theorem 4. -/ +class IsPrimeCard (F : Type*) [Fintype F] : Prop where + /-- The cardinality of `F` is prime. -/ + card_prime : Prime (Fintype.card F) + +/-- The curve parameter `s` is nonzero. -/ +class IsNonzeroParam {F : Type*} [Field F] (s : F) : Prop where + /-- The parameter `s` is nonzero. -/ + s_ne_zero : s ≠ 0 + +/-- The curve parameter `s` satisfies `s ^ 2 ≠ ± 2`. -/ +class IsRegularParam {F : Type*} [Field F] (s : F) : Prop where + /-- The parameter `s` satisfies `(s ^ 2 - 2) * (s ^ 2 + 2) ≠ 0`. -/ + s_sq_ne_pm_two : (s ^ 2 - 2) * (s ^ 2 + 2) ≠ 0 + +export IsCardThreeModFour (card_mod_four) +export IsPrimeCard (card_prime) +export IsNonzeroParam (s_ne_zero) +export IsRegularParam (s_sq_ne_pm_two) + +/-- The curve parameter `s` of Theorem 1, bundled. + +No hypotheses: the quantities `c`, `r`, `d` and the curve `E` are defined for every `s`. -/ +structure ParamData (F : Type*) [Field F] where + /-- The Elligator 1 curve parameter. -/ + s : F + +/-- An admissible input `t ∉ {1, -1}` of the Elligator 1 map, bundled. + +The two disequalities are data rather than hypotheses: they are exactly the subtype +`{n : F // n ≠ 1 ∧ n ≠ -1}` on which the unbundled definitions are given, i.e. the domain of `u`. -/ +structure InputData (F : Type*) [Field F] where + /-- The input of the Elligator 1 map. -/ + t : F + /-- The input is not `1`. -/ + t_ne_one : t ≠ 1 + /-- The input is not `-1`. -/ + t_ne_neg_one : t ≠ -1 + +/-- A curve parameter together with an admissible input: the data of Theorem 1. -/ +structure MapData (F : Type*) [Field F] extends ParamData F, InputData F + +/-- A curve parameter together with a point of the plane: the data of Theorem 3. -/ +structure PointData (F : Type*) [Field F] extends ParamData F where + /-- The point. -/ + P : F × F + +namespace InputData + +variable (I : InputData F) + +/-- The input, as an element of the subtype `{n : F // n ≠ 1 ∧ n ≠ -1}` on which the unbundled +definitions are given. -/ +def tSub : {n : F // n ≠ 1 ∧ n ≠ -1} := ⟨I.t, I.t_ne_one, I.t_ne_neg_one⟩ + +end InputData + +end Cslib.Crypto.Systems.Elligator diff --git a/Cslib/Crypto/Systems/Elligator/Elligator1/AuxiliaryCoordinates.lean b/Cslib/Crypto/Systems/Elligator/Elligator1/AuxiliaryCoordinates.lean new file mode 100644 index 000000000..69923b895 --- /dev/null +++ b/Cslib/Crypto/Systems/Elligator/Elligator1/AuxiliaryCoordinates.lean @@ -0,0 +1,247 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl +-/ +module + +public import Cslib.Crypto.Systems.Elligator.Elligator1.CurveParameters + +/-! +# Auxiliary Coordinates + +The auxiliary quantities `u`, `v`, `X`, `Y` built from a field element `t ≠ ±1` on the way to +the Edwards curve coordinates `x`, `y`, together with their nonvanishing facts. + +## Main Results + +* `u`, `v`, `X`, `Y`: the auxiliary quantities of [bernstein2013a], Section 3.2, Theorem 1. +* `u_ne_zero`, `v_ne_zero`, `X_ne_zero`, `Y_ne_zero`: nonvanishing, needed for the later + coordinates `x`, `y` to be well-defined. +* `v_factored`: `v` factors as `u(u² + c²)(u² + 1/c²)`, the key step in `v_ne_zero`. + +## References + +See [bernstein2013a], Section 3.2, Theorem 1. +-/ + +@[expose] public section + +namespace Cslib.Crypto.Systems.Elligator.Elligator1.AuxiliaryCoordinates + +variable {F : Type*} [Field F] + +open Cslib.Crypto.Systems.Elligator.FiniteFieldBasic +open Cslib.Crypto.Systems.Elligator.LegendreSymbol +open Cslib.Crypto.Systems.Elligator.Elligator1.CurveParameters + +section u + +variable (I : InputData F) + +/-- u(t) is a function defined in the paper. + +Original:, Section "3.2 The map": Theorem 1 +-/ +def u (t : {n : F // n ≠ 1 ∧ n ≠ -1}) : F := + let t := t.val + (1 - t) / (1 + t) + +/-- InputData wrapper for u. -/ +def _root_.Cslib.Crypto.Systems.Elligator.InputData.u (I : InputData F) : F := + AuxiliaryCoordinates.u I.tSub + +lemma u_ne_zero : I.u ≠ (0 : F) := + div_ne_zero (one_sub_t_ne_zero I.tSub) (one_add_t_ne_zero I.tSub) + +lemma one_add_u_ne_zero [Fintype F] [IsCardThreeModFour F] : + 1 + I.u ≠ 0 := by + unfold InputData.u u + rw [add_div' _ _ _ (one_add_t_ne_zero I.tSub)] + norm_num + exact ⟨two_ne_zero card_mod_four, one_add_t_ne_zero I.tSub⟩ + +end u + +variable [Fintype F] +variable (M : MapData F) + +section v + +/-- v(t, s) is a function defined in the paper. + +Original:, Section "3.2 The map": Theorem 1 +-/ +def v (t : {n : F // n ≠ 1 ∧ n ≠ -1}) (s : F) : F := + let u := u t + let r := r s + u ^ 5 + (r ^ 2 - 2) * u ^ 3 + u + +/-- MapData wrapper for v. -/ +def _root_.Cslib.Crypto.Systems.Elligator.MapData.v (M : MapData F) : F := + AuxiliaryCoordinates.v M.tSub M.s + +lemma v_factored [IsNonzeroParam M.s] [IsCardThreeModFour F] : + M.v = M.u * (M.u ^ 2 + M.c ^ 2) * (M.u ^ 2 + 1 / M.c ^ 2) := by + change M.u ^ 5 + (M.r ^ 2 - 2) * M.u ^ 3 + M.u + = M.u * (M.u ^ 2 + M.c ^ 2) * (M.u ^ 2 + 1 / M.c ^ 2) + rw [r_sq_sub_two_eq_c_sq_add_inv_c_sq M.toParamData] + ring_nf + rw [inv_pow, mul_assoc, mul_inv_cancel₀ (pow_ne_zero 2 (c_ne_zero M.toParamData))] + ring + +lemma v_factored_second_factor_ne_zero [IsNonzeroParam M.s] [IsCardThreeModFour F] : + M.u ^ 2 + M.c ^ 2 ≠ 0 := by + intro h_contra + have h_neg_one_sq : -1 = (M.u / M.c) ^ 2 := by + have hcpow_ne_zero := (pow_ne_zero 2 (c_ne_zero M.toParamData)) + rw [← mul_left_inj' hcpow_ne_zero] + rw [div_pow, div_mul_comm, div_self hcpow_ne_zero, neg_mul, one_mul, one_mul] + exact neg_eq_of_add_eq_zero_left h_contra + have h_isSquare : IsSquare (-1 : F) := by + rw [h_neg_one_sq, pow_two] + exact IsSquare.mul_self (M.u / M.c) + exact false_of_isSquare_neg_one card_mod_four h_isSquare + +lemma v_factored_third_factor_ne_zero [IsNonzeroParam M.s] [IsCardThreeModFour F] : + M.u ^ 2 + 1 / M.c ^ 2 ≠ 0 := by + intro h_contra + have h_neg_one_sq : -1 = (M.u * M.c) ^ 2 := by + have hcpow_ne_zero := (pow_ne_zero 2 (c_ne_zero M.toParamData)) + rw [← div_left_inj' hcpow_ne_zero] + rw [mul_pow, mul_div_assoc, div_self hcpow_ne_zero] + rw [← add_left_inj (1 / M.c ^ 2), neg_div, neg_add_cancel (1 / M.c ^ 2), mul_one] + symm + exact h_contra + have h_isSquare : IsSquare (-1 : F) := by + rw [h_neg_one_sq, pow_two] + exact IsSquare.mul_self _ + exact false_of_isSquare_neg_one card_mod_four h_isSquare + +lemma v_ne_zero [IsNonzeroParam M.s] [IsCardThreeModFour F] : + M.v ≠ 0 := by + rw [v_factored M] + apply mul_ne_zero + · apply mul_ne_zero + · exact u_ne_zero M.toInputData + · exact (v_factored_second_factor_ne_zero M) + · exact (v_factored_third_factor_ne_zero M) + +lemma χ_of_v_mul_v_of_t_pow_q_add_one_div_four_ne_zero [DecidableEq F] + [IsNonzeroParam M.s] [IsCardThreeModFour F] : + ((χ M.v) * M.v) ^ ((Fintype.card F + 1) / 4) ≠ 0 := by + rw [mul_pow (χ M.v) M.v _] + apply mul_ne_zero + · exact pow_ne_zero ((Fintype.card F + 1) / 4) (χ_a_ne_zero (v_ne_zero M)) + · exact pow_ne_zero ((Fintype.card F + 1) / 4) (v_ne_zero M) + +end v + +variable [DecidableEq F] + +section X + +/-- X(t, s) is a function defined in the paper. + +Original:, Section "3.2 The map": Theorem 1 +-/ +def X (t : {n : F // n ≠ 1 ∧ n ≠ -1}) (s : F) : F := + let u := u t + let v := v t s + (χ v) * u + +/-- MapData wrapper for X. -/ +def _root_.Cslib.Crypto.Systems.Elligator.MapData.X (M : MapData F) : F := + AuxiliaryCoordinates.X M.tSub M.s + +lemma X_pow_two_add_one_div_c_pow_two_ne_zero [IsNonzeroParam M.s] [IsCardThreeModFour F] : + M.X ^ 2 + 1 / M.c ^ 2 ≠ 0 := by + intro h_contra + have h_prod_eq_neg_one : M.X ^ 2 * M.c ^ 2 = -1 := by + have hcpow_ne_zero := (pow_ne_zero 2 (c_ne_zero M.toParamData)) + rw [← div_left_inj' hcpow_ne_zero] + rw [mul_div_assoc, div_self hcpow_ne_zero] + rw [← add_left_inj (1 / M.c ^ 2), neg_div, neg_add_cancel (1 / M.c ^ 2), mul_one] + exact h_contra + have h_isSquare : IsSquare (-1 : F) := by + rw [← h_prod_eq_neg_one, ← mul_pow] + exact IsSquare.sq (M.X * M.c) + exact false_of_isSquare_neg_one card_mod_four h_isSquare + +lemma X_ne_zero [IsNonzeroParam M.s] [IsCardThreeModFour F] : M.X ≠ 0 := by + apply mul_ne_zero + · exact χ_a_ne_zero (v_ne_zero M) + · exact u_ne_zero M.toInputData + +end X + +section Y + +/-- Y(t, s) is a function defined in the paper. + +`q` is still unrelated to the cardinality F here by intention. The theorems using +`Y` will build the necessary context to show useful properties of `Y` by creating +the relation of Field cardinality and `q`. + +Original:, Section "3.2 The map": Theorem 1 +-/ +def Y (t : {n : F // n ≠ 1 ∧ n ≠ -1}) (s : F) (q : ℕ) : F := + let u := u t + let c := c s + let v := v t s + ((χ v) * v) ^ ((q + 1) / 4) * (χ v) * χ (u ^ 2 + 1 / c ^ 2) + +/-- MapData wrapper for Y. -/ +def _root_.Cslib.Crypto.Systems.Elligator.MapData.Y (M : MapData F) : F := + AuxiliaryCoordinates.Y M.tSub M.s (Fintype.card F) + +lemma Y_ne_zero [IsNonzeroParam M.s] [IsCardThreeModFour F] : + M.Y ≠ 0 := by + change ((χ M.v) * M.v) ^ ((Fintype.card F + 1) / 4) * (χ M.v) * χ (M.u ^ 2 + 1 / M.c ^ 2) ≠ 0 + have hv_ne_zero := v_ne_zero M + apply mul_ne_zero + · apply mul_ne_zero + · rw [mul_pow (χ M.v) M.v ((Fintype.card F + 1) / 4)] + apply mul_ne_zero + · exact pow_ne_zero ((Fintype.card F + 1) / 4) (χ_a_ne_zero hv_ne_zero) + · exact pow_ne_zero ((Fintype.card F + 1) / 4) hv_ne_zero + · exact χ_a_ne_zero hv_ne_zero + · exact χ_a_ne_zero (v_factored_third_factor_ne_zero M) + +lemma X_mul_Y_ne_zero [IsNonzeroParam M.s] [IsCardThreeModFour F] : M.X * M.Y ≠ 0 := + mul_ne_zero (X_ne_zero M) (Y_ne_zero M) + +lemma one_add_X_ne_zero [IsNonzeroParam M.s] [IsCardThreeModFour F] : 1 + M.X ≠ 0 := by + change 1 + (χ M.v) * M.u ≠ 0 + intro h_contra + have h_χ_v_mul_u_eq_neg_one : (χ M.v) * M.u = -1 := by linear_combination h_contra + have h_u_eq_neg_χ_v : M.u = -(χ M.v) := by + have hχ_v_ne_zero : χ M.v ≠ 0 := χ_a_ne_zero (v_ne_zero M) + rw [← div_left_inj' hχ_v_ne_zero, neg_eq_neg_one_mul, mul_div_assoc, div_self hχ_v_ne_zero] + rw [one_div_χ_of_a_eq_χ_a, div_div_eq_mul_div, mul_comm] + ring_nf + exact h_χ_v_mul_u_eq_neg_one + have h_v_eq_expand : M.v = -(χ M.v) * (1 + M.r ^ 2 - 2 + 1) := by + change M.u ^ 5 + (M.r ^ 2 - 2) * M.u ^ 3 + M.u = -(χ M.v) * (1 + M.r ^ 2 - 2 + 1) + repeat rw [h_u_eq_neg_χ_v] + rw [← neg_one_mul, mul_pow, mul_pow] + rw [χ_of_a_pow_n_eq_χ_a M.v ⟨3, by trivial⟩, χ_of_a_pow_n_eq_χ_a M.v ⟨5, by trivial⟩] + ring + have h_v_eq_neg_χ_v_mul_r_sq : M.v = -(χ M.v) * M.r ^ 2 := by linear_combination h_v_eq_expand + have h_χ_v_eq_neg_χ_v : (χ M.v) = -(χ M.v) := by + rw [h_u_eq_neg_χ_v] at h_χ_v_mul_u_eq_neg_one + change (χ M.v) * -(χ M.v) = -1 at h_χ_v_mul_u_eq_neg_one + nth_rw 1 [h_v_eq_neg_χ_v_mul_r_sq] at h_χ_v_mul_u_eq_neg_one + rw [χ_mul] at h_χ_v_mul_u_eq_neg_one + nth_rw 1 [← neg_one_mul] at h_χ_v_mul_u_eq_neg_one + rw [χ_mul, χ_neg_one card_mod_four] at h_χ_v_mul_u_eq_neg_one + rw [χ_χ_eq_χ card_mod_four] at h_χ_v_mul_u_eq_neg_one + have hr_sq_ne_zero : M.r ^ 2 ≠ 0 := pow_ne_zero 2 (r_ne_zero M.toParamData) + have hr_sq_isSquare : IsSquare (M.r ^ 2) := IsSquare.sq M.r + grind [χ_a_eq_one] + have h_χ_v_ne_neg_χ_v : (χ M.v) ≠ -(χ M.v) := neg_χ_a_ne_χ_a (v_ne_zero M) card_mod_four + contradiction + +end Y + +end Cslib.Crypto.Systems.Elligator.Elligator1.AuxiliaryCoordinates diff --git a/Cslib/Crypto/Systems/Elligator/Elligator1/CurveParameters.lean b/Cslib/Crypto/Systems/Elligator/Elligator1/CurveParameters.lean new file mode 100644 index 000000000..ba04b4f85 --- /dev/null +++ b/Cslib/Crypto/Systems/Elligator/Elligator1/CurveParameters.lean @@ -0,0 +1,320 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl +-/ +module + +public import Cslib.Crypto.Systems.Elligator.Basic +public import Cslib.Crypto.Systems.Elligator.LegendreSymbol +public import Cslib.Crypto.Systems.Elligator.Context + +/-! +# Curve Parameters + +The parameters `c`, `r`, `d` derived from the Elligator 1 parameter `s`, together with the +nonvanishing and non-square facts about them needed throughout the rest of the development. + +## Main Results + +* `c`, `r`, `d`: the curve parameters of [bernstein2013a], Section 3.2, Theorem 1. +* `c_mul_sub_one_mul_add_one_ne_zero`: `c(c - 1)(c + 1) ≠ 0`. +* `r_ne_zero`, `four_add_r_ne_zero`, `r_sub_two_ne_zero`: `r`'s nonvanishing facts, used in + the well-definedness of the auxiliary and output coordinates. +* `d_nonsquare`, `one_div_d_nonsquare`, `d_ne_zero_and_d_ne_one`: `d` is neither `0`, `1`, nor + a square, the criterion making the resulting curve a valid complete Edwards curve. +* `neg_d_eq_r_add_two_div_r_sub_two`: relates `d` back to `r`, used in Theorem 3. + +## References + +See [bernstein2013a], Section 3.2, Theorem 1. +-/ + +@[expose] public section + +namespace Cslib.Crypto.Systems.Elligator.Elligator1.CurveParameters + +variable {F : Type*} [Field F] + +open Cslib.Crypto.Systems.Elligator +open Cslib.Crypto.Systems.Elligator.FiniteFieldBasic +open Cslib.Crypto.Systems.Elligator.LegendreSymbol + +variable (D : ParamData F) + +section s + +lemma s_pow_two_ne_two [IsRegularParam D.s] : + D.s ^ 2 ≠ 2 := + sub_ne_zero.mp (left_ne_zero_of_mul s_sq_ne_pm_two) + +lemma s_pow_two_ne_neg_two [IsRegularParam D.s] : + D.s ^ 2 ≠ -2 := by + have hright_ne_zero_of_mul : D.s^2 + 2 ≠ 0 := right_ne_zero_of_mul s_sq_ne_pm_two + rwa [ne_eq, add_eq_zero_iff_eq_neg] at hright_ne_zero_of_mul + +end s + +section c + +/-- c(s) is a function defined in the paper. + +Original:, Section "3.2 The map": Theorem 1 +-/ +def c (s : F) : F := 2 / s ^ 2 + +/-- ParamData wrapper for c. -/ +def _root_.Cslib.Crypto.Systems.Elligator.ParamData.c (D : ParamData F) : F := CurveParameters.c D.s + +lemma c_ne_zero [Fintype F] [IsNonzeroParam D.s] [IsCardThreeModFour F] : + D.c ≠ 0 := by + unfold ParamData.c c + exact div_ne_zero (two_ne_zero card_mod_four) (pow_ne_zero 2 s_ne_zero) + +lemma c_ne_one [IsRegularParam D.s] : D.c ≠ 1 := by + unfold ParamData.c c + exact div_ne_one_of_ne (s_pow_two_ne_two D).symm + +lemma c_sub_one_ne_zero [IsRegularParam D.s] : D.c - 1 ≠ 0 := + sub_ne_zero.2 (c_ne_one D) + +lemma c_ne_neg_one [IsRegularParam D.s] : (D.c) ≠ -1 := by + unfold ParamData.c c + intro h_contra + have hs_sq_eq_neg_two : D.s ^ 2 = -2 := by grind + have hs_sq_ne_neg_two := s_pow_two_ne_neg_two D + contradiction + +lemma c_add_one_ne_zero [IsRegularParam D.s] : D.c + 1 ≠ 0 := by + intro h_contra + have hc_ne_neg_one := c_ne_neg_one D + rw [← add_left_inj (-1)] at h_contra + ring_nf at h_contra + contradiction + +lemma c_mul_sub_one_mul_add_one_ne_zero [Fintype F] + [IsNonzeroParam D.s] [IsRegularParam D.s] [IsCardThreeModFour F] : + D.c * (D.c - 1) * (D.c + 1) ≠ 0 := by + unfold ParamData.c c + apply mul_ne_zero + · apply mul_ne_zero + · exact c_ne_zero D + · exact c_sub_one_ne_zero D + · exact c_add_one_ne_zero D + +lemma s_pow_two_eq_two_div_c [Fintype F] [IsCardThreeModFour F] : D.s ^ 2 = 2 / (D.c) := by + unfold ParamData.c c + field_simp [FiniteFieldBasic.two_ne_zero card_mod_four] + +end c + +variable [Fintype F] + +section r + +/-- r(s) is a function defined in the paper. + +Original:, Section "3.2 The map": Theorem 1 +-/ +def r (s : F) : F := + let c := c s + c + 1 / c + +/-- ParamData wrapper for r. -/ +def _root_.Cslib.Crypto.Systems.Elligator.ParamData.r (D : ParamData F) : F := CurveParameters.r D.s + +lemma r_ne_zero [IsNonzeroParam D.s] [IsCardThreeModFour F] : + D.r ≠ 0 := by + intro h_contra + change D.c + 1 / D.c = 0 at h_contra + have hcneg : D.c = -1 / D.c := by + rw [← add_left_inj (1 / D.c)] + rw [neg_div, neg_add_cancel (1 / D.c)] + exact h_contra + have hcpow : D.c ^ 2 = -1 := by + calc + D.c ^ 2 = -1 / D.c * D.c := by + rw [← div_left_inj' (c_ne_zero D)] + rw [pow_two, mul_div_assoc, div_self (c_ne_zero D), mul_one] + rw [mul_div_assoc, div_self (c_ne_zero D), mul_one] + exact hcneg + _ = -1 := by + nth_rw 1 [← neg_one_mul 1] + ring_nf + rw [mul_inv_cancel₀ (c_ne_zero D)] + have hsq : IsSquare (-1 : F) := by + rw [← hcpow, pow_two] + exact IsSquare.mul_self D.c + exact false_of_isSquare_neg_one card_mod_four hsq + +lemma four_add_r_ne_zero [IsNonzeroParam D.s] [IsCardThreeModFour F] : + 4 + D.r ≠ 0 := by + intro h_contra + -- Step 1: clear `1/c` from `r`'s definition. + have h_quad : (D.c) ^ 2 + 4 * (D.c) + 1 = 0 := by + unfold ParamData.r r at h_contra + change 4 + (D.c + 1 / D.c) = 0 at h_contra + rw [← div_left_inj' (c_ne_zero D), zero_div] + rw [← h_contra, add_div, add_div, pow_two, mul_div_assoc, mul_div_assoc] + rw [div_self (c_ne_zero D), mul_one, mul_one] + ring + -- Step 2: substitute `c = 2/s²`, clear denominators - `(s²+4)² = 12`. + let a : F := D.s ^ 2 + 4 + have ha_sq : a ^ 2 = 12 := by + unfold a + rw [← mul_left_inj' (pow_ne_zero 2 (s_ne_zero (s := D.s))), zero_mul] at h_quad + change ((2 / D.s ^ 2) ^ 2 + 4 * (2 / D.s ^ 2) + 1) * D.s ^ 2 = 0 at h_quad + rw [add_mul, add_mul] at h_quad + rw [← mul_div_assoc, div_mul_comm, div_self (pow_ne_zero 2 s_ne_zero)] at h_quad + field_simp [s_ne_zero] at h_quad + linear_combination h_quad + -- Step 3: halving, `u² = 3`. + let u : F := a / 2 + have hu_sq : u ^ 2 = 3 := by + unfold u + rw [div_pow, ha_sq] + rw [← mul_left_inj' (FiniteFieldBasic.four_ne_zero card_mod_four), div_mul] + norm_num + rw [div_self (FiniteFieldBasic.four_ne_zero card_mod_four), div_one] + -- Step 4: `2u = a`, so `u² - 2u + 1 = 3 - a + 1 = -s²`, giving `((u-1)/s)² = -1`. + have hu_eq_a : 2 * u = a := by + unfold u + rw [mul_div_left_comm] + rw [div_self (FiniteFieldBasic.two_ne_zero card_mod_four), mul_one] + have h_neg_one_sq : (-1 : F) = ((u - 1) / D.s) ^ 2 := by + rw [div_pow, eq_div_iff (pow_ne_zero 2 s_ne_zero)] + ring_nf + rw [hu_sq] + unfold u + rw [div_mul, div_self (FiniteFieldBasic.two_ne_zero card_mod_four)] + ring + exact false_of_isSquare_neg_one card_mod_four ⟨_, h_neg_one_sq.trans (sq _)⟩ + +lemma r_sq_sub_two_eq_c_sq_add_inv_c_sq [IsNonzeroParam D.s] [IsCardThreeModFour F] : + (D.r ^ 2 - 2) = D.c ^ 2 + 1 / D.c ^ 2 := by + calc + D.r ^ 2 - 2 = (D.c + 1 / D.c) ^ 2 - 2 := by rfl + _ = D.c ^ 2 + 2 * (D.c * (1 / D.c)) + (1 / D.c) ^ 2 - 2 := by ring + _ = D.c ^ 2 + 2 + 1 / D.c ^ 2 - 2 := by + ring_nf + rw [mul_inv_cancel₀ (c_ne_zero D)] + ring + _ = D.c ^ 2 + 1 / D.c ^ 2 := by ring + +lemma r_sub_two_ne_zero [IsNonzeroParam D.s] [IsCardThreeModFour F] [IsRegularParam D.s] : + D.r - 2 ≠ 0 := by + have hc_ne_zero := c_ne_zero D + change (D.c + 1 / D.c) - 2 ≠ 0 + have hceq : (D.c + 1 / D.c) - 2 = (D.c - 1) ^ 2 / D.c := by + rw [← mul_left_inj' hc_ne_zero] + rw [sub_mul, div_mul, div_self hc_ne_zero] + ring_nf + rw [mul_inv_cancel₀ hc_ne_zero] + ring + rw [hceq] + have hdnez : (D.c - 1) ^ 2 ≠ 0 := pow_ne_zero 2 (c_sub_one_ne_zero D) + exact div_ne_zero hdnez hc_ne_zero + +end r + +section d + +/-- d(s) is a function defined in the paper. + +Original:, Section "3.2 The map": Theorem 1 +-/ +def d (s : F) : F := + let c := c s; + -(c + 1) ^ 2 / (c - 1) ^ 2 + +/-- ParamData wrapper for d. -/ +def _root_.Cslib.Crypto.Systems.Elligator.ParamData.d (D : ParamData F) : F := CurveParameters.d D.s + +lemma d_nonsquare [IsRegularParam D.s] [IsCardThreeModFour F] : ¬IsSquare D.d := by + rw [isSquare_iff_exists_mul_self D.d] + change ¬∃ w, (-((2 / D.s ^ 2) + 1) ^ 2 / ((2 / D.s ^ 2) - 1) ^ 2) = w * w + rintro ⟨w, Pw⟩ + have hdivd : (2 / D.s ^ 2 - 1) ^ 2 ≠ 0 := by grind [s_sq_ne_pm_two] + have hdivs : (2 / D.s ^ 2 + 1) ^ 2 ≠ 0 := by grind [s_sq_ne_pm_two] + have heq : w ^ 2 * ((2 / D.s ^ 2) - 1) ^ 2 / ((2 / D.s ^ 2) + 1) ^ 2 = -1 := by + rw [pow_two, ← Pw] + rw [mul_div_assoc, div_mul_div_comm] + rw [mul_comm, ← div_mul_div_comm] + rw [div_self hdivd, neg_eq_neg_one_mul, mul_div_assoc, div_self hdivs] + ring + have hsq : IsSquare (-1 : F) := by + rw [← heq] + have hw_sq : IsSquare (w ^ 2) := by + rw [pow_two] + exact IsSquare.mul_self w + have hdiv_sq : IsSquare (((2 / D.s ^ 2) - 1) ^ 2 / ((2 / D.s ^ 2) + 1) ^ 2) := by + apply IsSquare.div + · rw [pow_two] + exact IsSquare.mul_self (2 / D.s ^ 2 - 1) + · rw [pow_two] + exact IsSquare.mul_self (2 / D.s ^ 2 + 1) + rw [mul_div_assoc] + exact IsSquare.mul hw_sq hdiv_sq + exact false_of_isSquare_neg_one card_mod_four hsq + +lemma d_ne_zero [IsRegularParam D.s] [IsCardThreeModFour F] : D.d ≠ 0 := by + have hd_nsq := d_nonsquare D + intro hd_eq_zero + have hd_sq : IsSquare D.d := by + unfold IsSquare + use 0 + rwa [mul_zero] + contradiction + +lemma one_div_d_nonsquare [IsRegularParam D.s] [IsCardThreeModFour F] : ¬IsSquare (1 / D.d) := by + rintro ⟨a, ha⟩ + have hd_ne_zero := d_ne_zero D + -- `1/d = a*a ≠ 0` (since `d ≠ 0`), so `a ≠ 0`. + have ha_ne_zero : a ≠ 0 := by + rintro rfl + simp only [one_div, mul_zero, inv_eq_zero] at ha + exact hd_ne_zero (by rw [ha]) + -- Reciprocal of both sides: `d = 1/(a*a) = (1/a)*(1/a)`. + apply d_nonsquare D + unfold IsSquare + use 1 / a + field_simp + rw [pow_two, ← ha, mul_div_left_comm, div_self hd_ne_zero, mul_one] + +lemma d_ne_one [IsRegularParam D.s] [IsCardThreeModFour F] : D.d ≠ 1 := by + have hd_non_sq := d_nonsquare D + intro hd_eq_one + have hd_sq : IsSquare D.d := by + rw [hd_eq_one] + exact IsSquare.one + contradiction + +lemma d_ne_zero_and_d_ne_one [IsRegularParam D.s] [IsCardThreeModFour F] : D.d ≠ 0 ∧ D.d ≠ 1 := by + have hd_ne_zero := d_ne_zero D + have hd_ne_one := d_ne_one D + exact ⟨hd_ne_zero, hd_ne_one⟩ + +lemma neg_d_eq_r_add_two_div_r_sub_two [IsNonzeroParam D.s] [IsCardThreeModFour F] : + -D.d = (D.r + 2) / (D.r - 2) := by + calc + -D.d = (D.c + 2 + 1 / D.c) / (D.c - 2 + 1 / D.c) := by + change -(-(D.c + 1) ^ 2 / (D.c - 1) ^ 2) = (D.c + 2 + 1 / D.c) / (D.c - 2 + 1 / D.c) + rw [← neg_one_mul] + nth_rw 2 [← neg_one_mul] + rw [mul_div_assoc, ← mul_assoc, add_pow_two, sub_pow_two] + rw [mul_neg, mul_one, neg_neg, one_pow, one_mul, one_div, mul_one] + nth_rw 1 [← mul_left_inj' (one_ne_zero' F)] + rw [mul_one] + nth_rw 3 [← div_self (c_ne_zero D)] + rw [div_mul_div_comm, add_mul, add_mul, add_mul, sub_mul] + rw [← pow_two, inv_mul_cancel₀ (c_ne_zero D)] + _ = (D.r + 2) / (D.r - 2) := by + rw [add_assoc, add_comm 2 (1 / D.c), ← add_assoc] + nth_rw 3 [add_comm] + rw [← add_sub_assoc] + nth_rw 3 [add_comm] + rfl + +end d + +end Cslib.Crypto.Systems.Elligator.Elligator1.CurveParameters diff --git a/Cslib/Crypto/Systems/Elligator/Elligator1/EdwardsCurve.lean b/Cslib/Crypto/Systems/Elligator/Elligator1/EdwardsCurve.lean new file mode 100644 index 000000000..f5d94078a --- /dev/null +++ b/Cslib/Crypto/Systems/Elligator/Elligator1/EdwardsCurve.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl +-/ +module + +public import Cslib.Crypto.Primitives.ECC.TwistedEdwardsCurve +public import Cslib.Crypto.Systems.Elligator.Elligator1.CurveParameters + +/-! +# The Edwards curve used by Elligator 1 + +This file specializes the general Edwards curve API of `Elligator.Primitives.ECC.EdwardsCurve` to +the curve and coefficient produced by Cslib.Crypto.Systems.Elligator 1. + +## Main results + +* `curve`: the Edwards curve with the paper's coefficient `d(s)`. +* `curve_isValid`: the Elligator hypotheses imply that `d(s)` is a valid Edwards coefficient. +* `EOverF s`: the set of affine field-valued points satisfying the Elligator 1 curve equation. +* `EOverF s_eq_affinePoints`: `EOverF s` agrees with the general Edwards affine-point set. + +## References + +See [Bernstein2013a], Section 3. +-/ + +@[expose] public section + +namespace Cslib.Crypto.Systems.Elligator.Elligator1 + +open Cslib.Crypto.Primitives.ECC +open Cslib.Crypto.Systems.Elligator.Elligator1.CurveParameters + +variable {F : Type*} [Field F] +variable (D : ParamData F) + +/-- The Edwards curve selected by the Elligator 1 parameter `s`. -/ +def curve (s : F) : TwistedEdwardsCurve F := edwardsCurve (d s) + +/-- ParamData wrapper for curve. -/ +def _root_.Cslib.Crypto.Systems.Elligator.ParamData.curve : TwistedEdwardsCurve F := + Elligator1.curve D.s + +/-- The curve equation of the Elligator 1 curve, in explicit form. -/ +lemma curve_equation_iff (x y : F) : + D.curve.Equation x y ↔ x ^ 2 + y ^ 2 = 1 + D.d * x ^ 2 * y ^ 2 := + edwardsCurve_equation_iff D.d x y + +/-- The Elligator 1 coefficient hypotheses imply that its specialized curve is valid. -/ +lemma curve_isValid [Fintype F] [IsRegularParam D.s] [IsCardThreeModFour F] : + D.curve.IsValid := by + unfold ParamData.curve curve + rw [edwardsCurve_isValid_iff] + exact d_ne_zero_and_d_ne_one D + +/-- `EOverF s` is the set of affine points on the Edwards curve selected by Elligator 1. -/ +def EOverF (s : F) : Set (F × F) := (curve s).affinePoints + +/-- ParamData wrapper for EOverF. -/ +def _root_.Cslib.Crypto.Systems.Elligator.ParamData.EOverF : Set (F × F) := + Elligator1.EOverF D.s + +/-- The compatibility set `EOverF s` is exactly the affine point set of the general curve model. -/ +lemma EOverF_s_eq_affinePoints : D.EOverF = D.curve.affinePoints := by rfl + +/-- Membership in `EOverF s`, written out as the Edwards curve equation. -/ +lemma mem_EOverF_iff (p : F × F) : + p ∈ D.EOverF ↔ p.1 ^ 2 + p.2 ^ 2 = 1 + D.d * p.1 ^ 2 * p.2 ^ 2 := + curve_equation_iff D p.1 p.2 + +/-- The neutral point `(0, 1)` lies in `EOverF s`. -/ +lemma zero_mem_EOverF : ((0 : F), (1 : F)) ∈ D.EOverF := D.curve.zero_mem_affinePoints + +end Cslib.Crypto.Systems.Elligator.Elligator1 diff --git a/Cslib/Crypto/Systems/Elligator/Elligator1/Map.lean b/Cslib/Crypto/Systems/Elligator/Elligator1/Map.lean new file mode 100644 index 000000000..a1fd65c4d --- /dev/null +++ b/Cslib/Crypto/Systems/Elligator/Elligator1/Map.lean @@ -0,0 +1,97 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl +-/ +module + +public import Cslib.Crypto.Systems.Elligator.Elligator1.OutputCoordinates + +/-! +# Map + +This file formalizes the construction and well-definedness results in Theorem 1 of the Elligator +paper. For a field input `t ≠ ±1`, the auxiliary quantities `u`, `v`, `X`, and `Y` determine a +point `(x, y)` on the complete Edwards curve. The exceptional inputs `t = ±1` are incorporated by +`ϕ`, which sends both to `(0, 1)`. + +## Main results + +* `u_defined`, `Y_defined`, `x_defined`, `y_defined`: the denominators in the paper's formulas + are nonzero, so the displayed expressions are defined. +* `map_fulfills_curve_equation`: the resulting `(x, y)` satisfies the Edwards curve equation. +* `ϕ`: Definition 2's total map from field elements to points on the Edwards curve. + +## References + +See [Bernstein2013a], Section 3.2, Theorem 1 and Definition 2. +-/ + +@[expose] public section + +namespace Cslib.Crypto.Systems.Elligator.Elligator1 + +open Cslib.Crypto.Primitives.ECC +open Cslib.Crypto.Systems.Elligator.Elligator1.CurveParameters +open Cslib.Crypto.Systems.Elligator.Elligator1.AuxiliaryCoordinates +open Cslib.Crypto.Systems.Elligator.Elligator1.OutputCoordinates + +variable {F : Type*} [Field F] +variable (D : ParamData F) +variable (I : InputData F) +variable (M : MapData F) + +theorem u_defined : 1 + I.t ≠ 0 := FiniteFieldBasic.one_add_t_ne_zero I.tSub + +variable [Fintype F] + +theorem Y_defined [IsNonzeroParam D.s] [IsCardThreeModFour F] : D.c ^ 2 ≠ 0 := + pow_ne_zero 2 (c_ne_zero D) + +variable [DecidableEq F] + +theorem x_defined [IsNonzeroParam M.s] [IsCardThreeModFour F] : M.Y ≠ 0 := Y_ne_zero M + +theorem y_defined [IsNonzeroParam M.s] [IsRegularParam M.s] [IsCardThreeModFour F] : + (M.r * M.X + (1 + M.X) ^ 2) ≠ 0 := + y_divisor_ne_zero M + +/-- The coordinates produced from a nonexceptional input satisfy the Edwards curve equation +`x² + y² = 1 + d * x² * y²`. This is the final conclusion of Theorem 1. -/ +theorem map_fulfills_curve_equation + [IsNonzeroParam M.s] [IsRegularParam M.s] [IsCardThreeModFour F] : + M.curve.Equation M.x M.y := by + rw [curve_equation_iff] + exact curve_equation M + +/-- The total Elligator map `ϕ : F → E(F)` from Definition 2 of the paper. + +For `t ≠ ±1`, it returns the coordinates `x(t)` and `y(t)` constructed in Theorem 1. The two +exceptional inputs `t = ±1` are both mapped to the neutral point `(0, 1)`. The codomain subtype +records that the result satisfies the Edwards curve equation. -/ +def ϕ (t : F) {s : F} + (hs_ne_zero : s ≠ 0) (sq_ne_pm_two : (s ^ 2 - 2) * (s ^ 2 + 2) ≠ 0) + (hq_mod : Fintype.card F % 4 = 3) : EOverF s := + haveI : IsNonzeroParam s := ⟨hs_ne_zero⟩ + haveI : IsRegularParam s := ⟨sq_ne_pm_two⟩ + haveI : IsCardThreeModFour F := ⟨hq_mod⟩ + let D : ParamData F := ⟨s⟩ + let P := if h : t ≠ 1 ∧ t ≠ -1 then (x ⟨t, h⟩ s (Fintype.card F), y ⟨t, h⟩ s) else (0, 1) + have P_in_EOverF : P ∈ D.EOverF := by + rw [mem_EOverF_iff, ← curve_equation_iff] + unfold P + by_cases ht : t ≠ 1 ∧ t ≠ -1 + · rw [dite_eq_left ht] + exact map_fulfills_curve_equation + {s := s, t := t, t_ne_one := ht.1, t_ne_neg_one := ht.2} + · rw [dite_eq_right ht] + exact (curve s).zero_mem_affinePoints + ⟨P, P_in_EOverF⟩ + +/-- ParamData wrapper for ϕ. -/ +def _root_.Cslib.Crypto.Systems.Elligator.ParamData.ϕ + [IsNonzeroParam D.s] [IsRegularParam D.s] [IsCardThreeModFour F] (t : F) : + {P : F × F // P ∈ D.EOverF} := + Elligator1.ϕ t s_ne_zero s_sq_ne_pm_two card_mod_four + +end Cslib.Crypto.Systems.Elligator.Elligator1 diff --git a/Cslib/Crypto/Systems/Elligator/Elligator1/OutputCoordinates.lean b/Cslib/Crypto/Systems/Elligator/Elligator1/OutputCoordinates.lean new file mode 100644 index 000000000..fcdb4be6f --- /dev/null +++ b/Cslib/Crypto/Systems/Elligator/Elligator1/OutputCoordinates.lean @@ -0,0 +1,323 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl +-/ +module + +public import Cslib.Crypto.Systems.Elligator.Elligator1.AuxiliaryCoordinates +public import Cslib.Crypto.Systems.Elligator.Elligator1.EdwardsCurve + +/-! +# Output Coordinates + +The Edwards curve coordinates `x`, `y` built from the auxiliary quantities of +`AuxiliaryCoordinates.lean`, together with the two conclusions of Theorem 1 — +nonvanishing of `u·v·X·Y·x·(y+1)` and the curve equation `x² + y² = 1 + dx²y²` — and their +behavior under `t ↦ -t`. + +## Main Results + +* `x`, `y`: the curve coordinates of [bernstein2013a], Section 3.2, Theorem 1. +* `x_ne_zero`, `y_add_one_ne_zero`: nonvanishing facts needed for Definition 2's map `ϕ`. +* `map_fulfills_auxiliary_equation`: the auxiliary coordinates satisfy `Y² = X⁵ + (r² - 2)X³ + X`. +* `curve_equation`: `x² + y² = 1 + dx²y²`, the first conclusion of Theorem 1. +* `variable_mul_ne_zero`: `u·v·X·Y·x·(y+1) ≠ 0`, the second conclusion of Theorem 1. + +## References + +See [bernstein2013a], Section 3.2, Theorem 1. +-/ + +@[expose] public section + +namespace Cslib.Crypto.Systems.Elligator.Elligator1.OutputCoordinates + +open Cslib.Crypto.Systems.Elligator.FiniteFieldBasic +open Cslib.Crypto.Systems.Elligator.LegendreSymbol +open Cslib.Crypto.Systems.Elligator.Elligator1.CurveParameters +open Cslib.Crypto.Systems.Elligator.Elligator1.AuxiliaryCoordinates + +variable {F : Type*} [Field F] [Fintype F] [DecidableEq F] +variable (M : MapData F) + +section x + +/-- x(t, s) is a function defined in the paper. It is the x-coordinate of the point on the curve. + +Original:, Section "3.2 The map": Theorem 1 +-/ +def x (t : {n : F // n ≠ 1 ∧ n ≠ -1}) (s : F) (q : ℕ) : F := + let c := c s + let X := X t s + let Y := Y t s q + (c - 1) * s * X * (1 + X) / Y + +/-- MapData wrapper for x. -/ +def _root_.Cslib.Crypto.Systems.Elligator.MapData.x (M : MapData F) : F := + OutputCoordinates.x M.tSub M.s (Fintype.card F) + +lemma x_ne_zero [IsNonzeroParam M.s] [IsRegularParam M.s] [IsCardThreeModFour F] : + M.x ≠ 0 := by + change (M.c - 1) * M.s * M.X * (1 + M.X) / M.Y ≠ 0 + apply div_ne_zero + · apply mul_ne_zero + · apply mul_ne_zero + · apply mul_ne_zero + · intro hc_sub_eq_zero + exact (c_ne_one M.toParamData) (by linear_combination hc_sub_eq_zero) + · exact s_ne_zero + · exact X_ne_zero M + · exact one_add_X_ne_zero M + · exact Y_ne_zero M + +end x + +section y + +/-- y(t, s) is a function defined in the paper. It is the y-coordinate of the point on the curve. + +Original:, Section "3.2 The map": Theorem 1 +-/ +def y (t : {n : F // n ≠ 1 ∧ n ≠ -1}) (s : F) : F := + let r := r s + let X := X t s + (r * X - (1 + X) ^ 2) / (r * X + (1 + X) ^ 2) + +/-- MapData wrapper for y. -/ +def _root_.Cslib.Crypto.Systems.Elligator.MapData.y (M : MapData F) : F := + OutputCoordinates.y M.tSub M.s + +/-- The auxiliary coordinates `X` and `Y` satisfy the hyperelliptic equation used in Theorem 1: +`Y² = X⁵ + (r² - 2)X³ + X`. -/ +theorem auxiliary_coordinates_fulfill_helper_equation + [IsNonzeroParam M.s] [IsCardThreeModFour F] : + M.Y ^ 2 = M.X ^ 5 + (M.r ^ 2 - 2) * M.X ^ 3 + M.X := by + have hv_ne_zero := v_ne_zero M + have h_X_expand_eq_χ_v_mul_v : M.X ^ 5 + (M.r ^ 2 - 2) * M.X ^ 3 + M.X = χ M.v * M.v := by + calc + M.X ^ 5 + (M.r ^ 2 - 2) * M.X ^ 3 + M.X + = χ M.v * (M.u ^ 5 + (M.r ^ 2 - 2) * M.u ^ 3 + M.u) := by + change (χ M.v * M.u) ^ 5 + (M.r ^ 2 - 2) * (χ M.v * M.u) ^ 3 + (χ M.v * M.u) + = χ M.v * (M.u ^ 5 + (M.r ^ 2 -2 ) * M.u ^ 3 + M.u) + rw [mul_pow (χ M.v) (M.u) 5, mul_pow (χ M.v) (M.u) 3] + rw [χ_of_a_pow_n_eq_χ_a M.v ⟨5, by trivial⟩] + rw [χ_of_a_pow_n_eq_χ_a M.v ⟨3, by trivial⟩] + ring + _ = χ M.v * M.v := by rfl + have hχ_a_mul_a_IsSquare := χ_a_mul_a_IsSquare hv_ne_zero card_mod_four + have h_χ_v_mul_v_fixed : (χ M.v * M.v) ^ ((Fintype.card F + 1) / 2) = χ M.v * M.v := + a_pow_q_add_one_div_two_eq_a hχ_a_mul_a_IsSquare card_mod_four + let χ_of_sum := χ (M.u ^ 2 + 1 / M.c ^ 2) + have h_Y_sq_eq_χ_v_mul_v : M.Y ^ 2 = χ M.v * M.v := by + calc + M.Y ^ 2 = (χ M.v * M.v) ^ ((Fintype.card F + 1) / 2) * (χ M.v) ^ 2 * χ_of_sum ^ 2 := by + change ((χ M.v * M.v) ^ ((Fintype.card F + 1) / 4) * χ M.v * χ_of_sum) ^ 2 + = (χ M.v * M.v) ^ ((Fintype.card F + 1) / 2) * (χ M.v) ^ 2 * χ_of_sum ^ 2 + ring_nf + rw [one_add_q_div_four_mul_two_eq_one_add_q_div_two card_mod_four] + _ = (χ M.v * M.v) ^ ((Fintype.card F + 1) / 2) * 1 := by + rw [χ_of_a_even_pow_n_eq_one hv_ne_zero ⟨2, even_two⟩] + rw [χ_of_a_even_pow_n_eq_one + (v_factored_third_factor_ne_zero M) ⟨2, even_two⟩] + rw [mul_one] + _ = χ M.v * M.v := by rw [h_χ_v_mul_v_fixed, mul_one] + rw [h_X_expand_eq_χ_v_mul_v] + exact h_Y_sq_eq_χ_v_mul_v + +lemma y_divisor_ne_zero [IsNonzeroParam M.s] [IsRegularParam M.s] [IsCardThreeModFour F] : + (M.r * M.X + (1 + M.X) ^ 2) ≠ 0 := by + intro h_contra + have hr_mul_X_eq_neg_expand : M.r * M.X = -(1 + M.X) ^ 2 := + Eq.symm (neg_eq_of_add_eq_zero_left h_contra) + have hY_sq_eq_neg_expand : M.Y ^ 2 = -(1 + M.X) ^ 2 * M.X ^ 2 * (M.s + 2 / M.s) ^ 2 := by + calc + M.Y ^ 2 = M.X * (M.X ^ 4 + (M.r ^ 2 - 2) * M.X ^ 2 + 1) := by + rw [mul_add, mul_add] + rw [auxiliary_coordinates_fulfill_helper_equation M] + ring + _ = M.X ^ 3 * (2 * M.r ^ 2 + 4 * M.r) := by grind + _ = M.X ^ 3 * (2 * M.r ^ 2 + 4 * M.r) := by ring + _ = M.r * M.X * M.X ^ 2 * (2 * M.r + 4) := by ring + _ = -(1 + M.X) ^ 2 * M.X ^ 2 * (M.s + 2 / M.s) ^ 2 := by + rw [← hr_mul_X_eq_neg_expand] + change M.r * M.X * M.X ^ 2 * (2 * (2 / M.s ^ 2 + 1 / (2 / M.s ^ 2)) + 4) + = M.r * M.X * M.X ^ 2 * (M.s + 2 / M.s) ^ 2 + have h_algebra_identity : (2 * (2 / M.s ^ 2 + 1 / (2 / M.s ^ 2)) + 4) + = (M.s + 2 / M.s) ^ 2 := by + ring_nf + rw [inv_inv, mul_inv_cancel₀ s_ne_zero, one_mul, mul_assoc] + rw [inv_mul_cancel₀ (FiniteFieldBasic.two_ne_zero card_mod_four)] + ring + rw [h_algebra_identity] + have h_isSquare_neg_one : IsSquare (-1 : F) := by + have h_ratio_eq_neg_one : M.Y ^ 2 / ((1 + M.X) * M.X * (M.s + 2 / M.s)) ^ 2 = -1 := by + rw [← neg_one_mul, mul_assoc (-1) ((1 + M.X) ^ 2) (M.X ^ 2)] at hY_sq_eq_neg_expand + rw [← mul_pow (1 + M.X) (M.X) 2, mul_assoc (-1) _ _] at hY_sq_eq_neg_expand + rw [← mul_pow (((1 + M.X) * M.X))] at hY_sq_eq_neg_expand + have h_denom_ne_zero : ((1 + M.X) * M.X * (M.s + 2 / M.s)) ^ 2 ≠ 0 := by + apply pow_ne_zero 2 + apply mul_ne_zero + · exact mul_ne_zero (one_add_X_ne_zero M) (X_ne_zero M) + · intro h_contra' + have hspow_eq_zero : M.s ^ 2 + 2 = 0 := by + rw [← div_left_inj' (s_ne_zero (s := M.s))] + rw [zero_div, add_div, pow_two, mul_div_assoc, div_self s_ne_zero, mul_one] + exact h_contra' + have hspow_ne_zero : M.s ^ 2 + 2 ≠ 0 := right_ne_zero_of_mul s_sq_ne_pm_two + contradiction + rw [← div_left_inj' h_denom_ne_zero, mul_div_assoc, div_self h_denom_ne_zero, mul_one] + at hY_sq_eq_neg_expand + exact hY_sq_eq_neg_expand + have h_ratio_sq_eq_neg_one : (M.Y / ((1 + M.X) * M.X * (M.s + 2 / M.s))) ^ 2 = -1 := by + rw [← div_pow] at h_ratio_eq_neg_one + exact h_ratio_eq_neg_one + rw [← h_ratio_sq_eq_neg_one, pow_two] + exact IsSquare.mul_self _ + have h_mod_ne_three : Fintype.card F % 4 ≠ 3 := by + rw [FiniteField.isSquare_neg_one_iff] at h_isSquare_neg_one + exact h_isSquare_neg_one + have h_mod_eq_three : Fintype.card F % 4 = 3 := card_mod_four + contradiction + +lemma y_add_one_ne_zero [IsNonzeroParam M.s] [IsCardThreeModFour F] : + M.y + 1 ≠ 0 := by + intro h_contra + have hy_eq_neg_one : M.y = -1 := Eq.symm (neg_eq_of_add_eq_zero_left h_contra) + have hy_unfolded_eq_neg_one : (M.r * M.X - (1 + M.X) ^ 2) / (M.r * M.X + (1 + M.X) ^ 2) = -1 := by + change M.y = -1 + exact hy_eq_neg_one + have h_num_eq_neg_denom : M.r * M.X - (1 + M.X) ^ 2 = -(M.r * M.X + (1 + M.X) ^ 2) := by + rw [neg_eq_neg_one_mul, ← hy_unfolded_eq_neg_one] + have hdiv_ne_zero : M.r * M.X + (1 + M.X) ^ 2 ≠ 0 := by + intro h_contra' + rw [h_contra', div_zero, zero_eq_neg] at hy_unfolded_eq_neg_one + apply one_ne_zero' F at hy_unfolded_eq_neg_one + contradiction + rw [div_mul_comm, div_self hdiv_ne_zero, one_mul] + have hr_mul_X_eq_zero : M.r * M.X = 0 := by + rw [← add_left_inj (M.r * M.X + (1 + M.X) ^ 2)] at h_num_eq_neg_denom + ring_nf at h_num_eq_neg_denom + rw [← div_left_inj' (two_ne_zero card_mod_four), mul_div_assoc] at h_num_eq_neg_denom + rw [div_self (two_ne_zero card_mod_four)] at h_num_eq_neg_denom + ring_nf at h_num_eq_neg_denom + exact h_num_eq_neg_denom + have hr_mul_X_ne_zero : M.r * M.X ≠ 0 := mul_ne_zero (r_ne_zero M.toParamData) (X_ne_zero M) + contradiction + +/-- The quantities constructed for a nonexceptional input are all nonzero as asserted in +Theorem 1: `u * v * X * Y * x * (y + 1) ≠ 0`. -/ +theorem map_variable_mul_ne_zero + [IsNonzeroParam M.s] [IsRegularParam M.s] [IsCardThreeModFour F] : + M.u * M.v * M.X * M.Y * M.x * (M.y + 1) ≠ 0 := by + apply mul_ne_zero + · apply mul_ne_zero + · apply mul_ne_zero + · apply mul_ne_zero + · apply mul_ne_zero (u_ne_zero M.toInputData) (v_ne_zero M) + · exact X_ne_zero M + · exact Y_ne_zero M + · exact x_ne_zero M + · exact y_add_one_ne_zero M + +lemma curve_equation [IsNonzeroParam M.s] [IsRegularParam M.s] [IsCardThreeModFour F] : + M.x ^ 2 + M.y ^ 2 = 1 + M.d * M.x ^ 2 * M.y ^ 2 := by + have h_c_sub_one_sq_mul_s_sq_eq : (M.c - 1) ^ 2 * M.s ^ 2 = 2 * (M.r - 2) := + calc + (M.c - 1) ^ 2 * M.s ^ 2 = (M.c - 1) ^ 2 * (2 / M.c) := by + rw [← s_pow_two_eq_two_div_c M.toParamData] + _ = 2 * (M.r - 2) := by + rw [sub_pow_two, mul_one, one_pow 2, add_mul, sub_mul] + rw [← mul_div_assoc, one_mul, mul_comm, pow_two, ← mul_assoc] + rw [mul_div_assoc, div_self (c_ne_zero M.toParamData), mul_one] + nth_rw 4 [← mul_one 2] + rw [add_comm, ← add_sub_assoc, mul_div_assoc, ← mul_add 2 (1 / M.c) M.c, add_comm] + change 2 * M.r - 2 * M.c * (2 / M.c) = 2 * (M.r - 2) + ring_nf + rw [mul_inv_cancel₀ (c_ne_zero M.toParamData)] + ring + have h_Y_sq_mul_one_sub_x_sq_eq : M.Y ^ 2 * (1 - M.x ^ 2) + = M.X * (M.r * M.X - (1 + M.X) ^ 2) ^ 2 := by + calc + M.Y ^ 2 * (1 - M.x ^ 2) = M.Y ^ 2 - (M.c - 1) ^ 2 * M.s ^ 2 * M.X ^ 2 * (1 + M.X) ^ 2 := by + change M.Y ^ 2 * (1 - (((M.c - 1) * M.s * M.X * (1 + M.X)) / M.Y) ^ 2) + = M.Y ^ 2 - (M.c - 1) ^ 2 * M.s ^ 2 * M.X ^ 2 * (1 + M.X) ^ 2 + have hY_sq_ne_zero : M.Y ^ 2 ≠ 0 := pow_ne_zero 2 (Y_ne_zero M) + rw [mul_sub, mul_one, ← add_right_inj (-(M.Y ^ 2))] + repeat rw [← add_sub_assoc, neg_add_cancel, zero_sub] + nth_rw 2 [← mul_pow, ← mul_pow, ← mul_pow] + rw [neg_inj, ← div_left_inj' hY_sq_ne_zero, mul_comm, mul_div_assoc, div_self hY_sq_ne_zero] + ring + _ = M.X ^ 5 + (M.r ^ 2 - 2) * M.X ^ 3 + M.X - 2 * (M.r - 2) * M.X ^ 2 * (1 + M.X) ^ 2 := by + rw [h_c_sub_one_sq_mul_s_sq_eq] + rw [auxiliary_coordinates_fulfill_helper_equation M] + _ = M.X * (M.r * M.X - (1 + M.X) ^ 2) ^ 2 := by ring + have h_neg_d_mul_c_sub_one_sq_mul_s_sq_eq : -M.d * (M.c - 1) ^ 2 * M.s ^ 2 = 2 * (M.r + 2) := by + rw [neg_d_eq_r_add_two_div_r_sub_two M.toParamData, mul_assoc, h_c_sub_one_sq_mul_s_sq_eq] + rw [mul_comm, ← mul_div_assoc, mul_assoc, mul_comm (M.r - 2) (M.r + 2), ← mul_assoc] + have hr_sub_two_ne_zero : M.r - 2 ≠ 0 := by + intro hr_sub_two_eq_zero + have h_c_sub_one_sq_mul_s_sq_eq_zero : (M.c - 1) ^ 2 * M.s ^ 2 = 0 := by + rw [hr_sub_two_eq_zero, mul_zero] at h_c_sub_one_sq_mul_s_sq_eq + exact h_c_sub_one_sq_mul_s_sq_eq + have h_c_sub_one_sq_mul_s_sq_ne_zero : (M.c - 1) ^ 2 * M.s ^ 2 ≠ 0 := by + apply mul_ne_zero + · exact pow_ne_zero 2 (c_sub_one_ne_zero M.toParamData) + · exact pow_ne_zero 2 s_ne_zero + contradiction + rw [mul_div_assoc, div_self hr_sub_two_ne_zero, mul_one] + have h_Y_sq_mul_one_sub_d_mul_x_sq_eq : M.Y ^ 2 * (1 - M.d * M.x ^ 2) + = M.X * (M.r * M.X + (1 + M.X) ^ 2) ^ 2 := by + calc + M.Y ^ 2 * (1 - M.d * M.x ^ 2) + = M.Y ^ 2 - M.d * (M.c - 1) ^ 2 * M.s ^ 2 * M.X ^ 2 * (1 + M.X) ^ 2 := by + change M.Y ^ 2 * (1 - M.d * (((M.c - 1) * M.s * M.X * (1 + M.X)) / M.Y) ^ 2) + = M.Y ^ 2 - M.d * (M.c - 1) ^ 2 * M.s ^ 2 * M.X ^ 2 * (1 + M.X) ^ 2 + rw [mul_sub, mul_one] + have hY_sq_ne_zero : M.Y ^ 2 ≠ 0 := pow_ne_zero 2 (Y_ne_zero M) + rw [div_pow, mul_comm, ← mul_div_assoc, div_mul_comm, div_self hY_sq_ne_zero, one_mul] + ring + _ = M.X ^ 5 + (M.r ^ 2 - 2) * M.X ^ 3 + M.X + 2 * (M.r + 2) * M.X ^ 2 * (1 + M.X) ^ 2 := by + rw [sub_eq_add_neg, neg_eq_neg_one_mul, ← mul_assoc, ← mul_assoc, ← mul_assoc] + rw [neg_eq_neg_one_mul, mul_assoc (-1)] at h_neg_d_mul_c_sub_one_sq_mul_s_sq_eq + rw [mul_assoc, h_neg_d_mul_c_sub_one_sq_mul_s_sq_eq] + rw [auxiliary_coordinates_fulfill_helper_equation M] + ring + _ = M.X * (M.r * M.X + (1 + M.X) ^ 2) ^ 2 := by ring + have h_one_sub_d_mul_x_sq_ne_zero : (1 - M.d * M.x ^ 2) ≠ 0 := by + intro h_one_sub_d_mul_x_sq_eq_zero + have hd_isSquare : IsSquare M.d := by + rw [← add_right_inj (M.d * M.x ^ 2), add_comm] at h_one_sub_d_mul_x_sq_eq_zero + have h_cancel_identity : 1 - M.d * M.x ^ 2 + M.d * M.x ^ 2 = 1 := by ring + rw [add_zero, h_cancel_identity] at h_one_sub_d_mul_x_sq_eq_zero + have hx_sq_ne_zero : M.x ^ 2 ≠ 0 := pow_ne_zero 2 (x_ne_zero M) + rw [← div_left_inj' hx_sq_ne_zero] at h_one_sub_d_mul_x_sq_eq_zero + rw [mul_div_assoc, div_self hx_sq_ne_zero, mul_one] at h_one_sub_d_mul_x_sq_eq_zero + rw [← mul_one 1, ← pow_two, ← div_pow _ _ 2] at h_one_sub_d_mul_x_sq_eq_zero + rw [← h_one_sub_d_mul_x_sq_eq_zero, pow_two] + exact IsSquare.mul_self _ + have hd_not_isSquare : ¬IsSquare M.d := d_nonsquare M.toParamData + contradiction + have h_Y_sq_mul_one_sub_d_mul_x_sq_ne_zero : M.Y ^ 2 * (1 - M.d * M.x ^ 2) ≠ 0 := by + apply mul_ne_zero + · exact pow_ne_zero 2 (Y_ne_zero M) + · exact h_one_sub_d_mul_x_sq_ne_zero + have h_ratio_eq_y_sq : (1 - M.x ^ 2) / (1 - M.d * M.x ^ 2) = M.y ^ 2 := by + calc + (1 - M.x ^ 2) / (1 - M.d * M.x ^ 2) + = (M.r * M.X - (1 + M.X) ^ 2) ^ 2 / (M.r * M.X + (1 + M.X) ^ 2) ^ 2 := by + have h_Y_sq_div_self_eq_one : M.Y ^ 2 / M.Y ^ 2 = 1 := by + have hY_sq_ne_zero : M.Y ^ 2 ≠ 0 := pow_ne_zero 2 (Y_ne_zero M) + rw [div_self hY_sq_ne_zero] + nth_rw 1 [← one_mul (1 - M.x ^ 2), ← h_Y_sq_div_self_eq_one] + rw [mul_div_assoc, ← mul_div_mul_comm, h_Y_sq_mul_one_sub_x_sq_eq] + rw [h_Y_sq_mul_one_sub_d_mul_x_sq_eq] + rw [mul_div_mul_comm M.X _ M.X _, div_self (X_ne_zero M), one_mul] + _ = M.y ^ 2 := by + rw [← div_pow _ _ 2] + rfl + grind + +end y + +end Cslib.Crypto.Systems.Elligator.Elligator1.OutputCoordinates diff --git a/Cslib/Crypto/Systems/Elligator/FiniteFieldBasic.lean b/Cslib/Crypto/Systems/Elligator/FiniteFieldBasic.lean new file mode 100644 index 000000000..c14a9379c --- /dev/null +++ b/Cslib/Crypto/Systems/Elligator/FiniteFieldBasic.lean @@ -0,0 +1,122 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl, Matthias Güdemann +-/ +module + +public import Cslib.Crypto.Systems.Elligator.Basic +public import Mathlib.NumberTheory.LegendreSymbol.QuadraticChar.Basic +public import Mathlib.FieldTheory.Finite.GaloisField + +/-! +# Finite Field Basic + +In this file we introduce some generally helpful lemmas for the finite field `F` with +`q` fulfilling `IsPrimePow`/`Prime`, `Fintype.card F = q` and `q % 4 = 3`. + +The assumption `IsPrimePow q` of [bernstein2013a] never has to be stated: by +`card_isPrimePow` it is a consequence of `Fintype.card F = q`, so `q` ranges over exactly the +prime powers congruent to `3` modulo `4`. Conversely, `prime_of_natCast_surjective` shows that +representing field elements by the naturals `0, 1, …, q - 1`, as the string encoding of +Section 3.4 does, is possible only when `q` is prime. + +## References + +See [bernstein2013a] for the original account on this specifc finite field. +-/ + +@[expose] public section + +variable {F : Type*} [Field F] [Fintype F] + +namespace Cslib.Crypto.Systems.Elligator.FiniteFieldBasic + +/-- The cardinality of a finite field is always a prime power. + +This is why no statement of this development has to assume `IsPrimePow q`: the hypothesis +`Fintype.card F = q` already forces `q` to be a prime power, so all results proved for a finite +field `F` with `Fintype.card F = q` and `q % 4 = 3` are exactly the results of [Bernstein2013a] +for an arbitrary prime power `q ≡ 3 (mod 4)`. -/ +lemma card_isPrimePow {q : ℕ} (hq_card : Fintype.card F = q) : IsPrimePow q := by + rw [← hq_card] + exact FiniteField.isPrimePow_card F + +lemma two_ne_zero (hq_mod : Fintype.card F % 4 = 3) : (2 : F) ≠ 0 := by + intro h + -- turn `(2 : F) = 0` into a divisibility statement about the characteristic + have hdvd : ringChar F ∣ 2 := (CharP.cast_eq_zero_iff F (ringChar F) 2).mp h + -- ringChar F ∣ 2 and ringChar F ≠ 1 (F is nontrivial) forces ringChar F = 2 + have hp : ringChar F = 2 := by + rcases (Nat.dvd_prime Nat.prime_two).mp hdvd with hchar | hchar + · exact absurd hchar (CharP.char_ne_one F (ringChar F)) + · exact hchar + have hchar : CharP F 2 := by + rw [← hp] + exact ringChar.charP F + -- a finite field of characteristic 2 has cardinality a power of 2 + obtain ⟨n, -, hcard⟩ := FiniteField.card F 2 + have hqeq : Fintype.card F = 2 ^ (n : ℕ) := by rw [hcard] + have hdvd2 : (2 : ℕ) ∣ Fintype.card F := by + rw [hqeq] + exact dvd_pow_self 2 n.pos.ne' + -- q even contradicts q % 4 = 3 + omega + +lemma four_ne_zero (hq_mod : Fintype.card F % 4 = 3) : (4 : F) ≠ 0 := by + have hnum : (4 : F) = 2 * 2 := by norm_num + rw [hnum] + apply mul_ne_zero <;> exact two_ne_zero hq_mod + +lemma ringChar_ne_two (hq_mod : Fintype.card F % 4 = 3) : ringChar F ≠ 2 := by + intro hchar + apply two_ne_zero hq_mod + have hcon : (2 : F) = 0 := (ringChar.spec F 2).mpr (by rw [hchar]) + exact hcon + +lemma neg_one_non_square (hq_mod : Fintype.card F % 4 = 3) : + ¬IsSquare (-1 : F) := by + intro hsq + apply FiniteField.isSquare_neg_one_iff.mp at hsq + contradiction + +/-- If some algebraic identity would force `-1` to be a square, contradiction - `-1` is never +a square when `q % 4 = 3`. A common closing step for the `r`/`d` nonvanishing proofs. -/ +lemma false_of_isSquare_neg_one (hq_mod : Fintype.card F % 4 = 3) + (h : IsSquare (-1 : F)) : False := neg_one_non_square hq_mod h + +-- TODO fix omits +omit [Fintype F] in +lemma one_sub_t_ne_zero (t : {n : F // n ≠ 1 ∧ n ≠ -1}) : (1 : F) - t.val ≠ 0 := + sub_ne_zero.mpr t.prop.1.symm + +omit [Fintype F] in +lemma one_add_t_ne_zero (t : {n : F // n ≠ 1 ∧ n ≠ -1}) : (1 : F) + t.val ≠ 0 := by + intro h + rw [add_comm] at h + exact t.prop.2 (eq_neg_of_add_eq_zero_left h) + +omit [Fintype F] in +lemma neg_t_ne_one_and_neg_t_ne_neg_one (t : { t : F // t ≠ 1 ∧ t ≠ -1}) : + -t.val ≠ 1 ∧ -t.val ≠ -1 := by + constructor + · intro h + apply t.prop.2 + have := congrArg Neg.neg h + simpa using this + · intro h + apply t.prop.1 + have := congrArg Neg.neg h + simpa using this + +omit [Fintype F] in +lemma not_t_ne_one_and_t_ne_neg_one (t : { t : F // t = 1 ∨ t = -1}) : + ¬(t.val ≠ 1 ∧ t.val ≠ -1) := by + rcases t.prop with th | th <;> simp [th] + +omit [Field F] in +lemma one_add_q_div_four_mul_two_eq_one_add_q_div_two (hq_mod : Fintype.card F % 4 = 3) : + ((1 + Fintype.card F) / 4 * 2) = (1 + Fintype.card F) / 2 := by + omega + +end Cslib.Crypto.Systems.Elligator.FiniteFieldBasic diff --git a/Cslib/Crypto/Systems/Elligator/LegendreSymbol.lean b/Cslib/Crypto/Systems/Elligator/LegendreSymbol.lean new file mode 100644 index 000000000..48beabb5e --- /dev/null +++ b/Cslib/Crypto/Systems/Elligator/LegendreSymbol.lean @@ -0,0 +1,188 @@ +/- +Copyright (c) 2026 Chris Anto Fröschl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Anto Fröschl +-/ +module + +public import Cslib.Crypto.Systems.Elligator.FiniteFieldBasic + +/-! +# Legendre Symbol + +In this file we introduce a special case of the traditional Legendre Symbol. + +The quadratic character `χ` used here is Mathlib's `quadraticChar`, whose values are cast from +`ℤ` into the finite field `F` itself; this is the form in which the Elligator 1 paper uses it. +All the facts below are consequences of the Mathlib API for `quadraticChar`, specialised to a +field `F` with `Fintype.card F = q` and `q % 4 = 3`. + +## References + +See [bernstein2013a], Section 3.1. +-/ + +@[expose] public section + +namespace Cslib.Crypto.Systems.Elligator.LegendreSymbol + +open Cslib.Crypto.Systems.Elligator.FiniteFieldBasic + +variable {F : Type*} [Field F] [Fintype F] [DecidableEq F] +variable {q : ℕ} + +/-- χ(a) is the quadratic character of a in the finite field F with q elements, where q is a +prime congruent to 3 modulo 4, viewed as an element of `F`. + +This is Mathlib's `quadraticChar` composed with the cast `ℤ → F`, since +`Mathlib.NumberTheory.LegendreSymbol.Basics` is restricted to `ℤ`. +-/ +def χ (a : F) : F := ((quadraticChar F a : ℤ) : F) + +lemma χ_zero : χ (0 : F) = 0 := by simp [χ] + +lemma χ_one : χ (1 : F) = 1 := by simp [χ] + +/-- Euler's criterion: `χ` is given by the `(q - 1) / 2`-th power. -/ +lemma χ_eq_pow (a : F) (hq_mod : Fintype.card F % 4 = 3) : + χ a = a ^ ((Fintype.card F - 1) / 2) := by + have h : (Fintype.card F - 1) / 2 = Fintype.card F / 2 := by + omega + rw [χ, h] + exact quadraticChar_eq_pow_of_char_ne_two' (ringChar_ne_two hq_mod) a + +lemma χ_values {a : F} : χ a = 0 ∨ χ a = -1 ∨ χ a = 1 := by + rcases eq_or_ne a 0 with ha | ha + · simp [χ_zero, ha] + · rcases quadraticChar_dichotomy ha with h | h <;> simp [χ, h] + +lemma χ_a_ne_zero {a : F} (a_ne_zero : a ≠ 0) : χ a ≠ 0 := by + rcases quadraticChar_dichotomy a_ne_zero with h | h <;> simp [χ, h] + +lemma a_eq_zero_of_χ_of_a_eq_zero {a : F} : χ a = 0 → a = 0 := by + intro h + by_contra ha + apply χ_a_ne_zero ha + exact h + +@[simp] +lemma χ_a_eq_one {a : F} (a_ne_zero : a ≠ 0) (a_square : IsSquare a) : χ a = 1 := by + rw [χ, (quadraticChar_one_iff_isSquare a_ne_zero).mpr a_square] + simp + +lemma χ_eq_one_iff_isSquare {a : F} + (a_ne_zero : a ≠ 0) (hq_mod : Fintype.card F % 4 = 3) : + χ a = 1 ↔ IsSquare a := by + constructor + · intro h + rcases quadraticChar_dichotomy a_ne_zero with h' | h' + · exact (quadraticChar_one_iff_isSquare a_ne_zero).mp h' + · simp_all only [χ] + have heq : (2 : F) = 0 := by grind + have hne : (2 : F) ≠ 0 := by simp_all [FiniteFieldBasic.two_ne_zero] + contradiction + · exact χ_a_eq_one a_ne_zero + +lemma χ_sq {a : F} (a_ne_zero : a ≠ 0) : χ (a ^ 2) = 1 := by + rw [χ, quadraticChar_sq_one' a_ne_zero, Int.cast_one] + +lemma χ_neg_one (hq_mod : Fintype.card F % 4 = 3) : + χ (-1 : F) = -1 := by + rw [χ, quadraticChar_neg_one_iff_not_isSquare.mpr (neg_one_non_square hq_mod)] + simp + +lemma χ_mul {a b : F} : χ (a * b) = (χ a) * (χ b) := by + simp [χ, quadraticCharFun_mul] + +lemma neg_χ_a_ne_χ_a {a : F} + (a_ne_zero : a ≠ 0) (hq_mod : Fintype.card F % 4 = 3) + : χ a ≠ -(χ a) := by + intro h + have heq : (2 : F) * χ a = 0 := by + rw [← add_left_inj (χ a)] at h + ring_nf at h + rwa [mul_comm] + rcases mul_eq_zero.mp heq with hzero | hzero + · exact two_ne_zero hq_mod hzero + · exact χ_a_ne_zero a_ne_zero hzero + +@[simp] +lemma χ_of_a_even_pow_n_eq_one {a : F} (a_ne_zero : a ≠ 0) (n : {n : ℕ | Even n}) : + (χ a) ^ (n.val) = 1 := by + rcases χ_values (a := a) with h | h | h + · exact absurd h (χ_a_ne_zero a_ne_zero) + · rw [h] + exact n.prop.neg_one_pow + · rw [h, one_pow] + +@[simp] +lemma χ_of_a_pow_n_eq_χ_a (a : F) (n : {n : ℕ | Odd n}) : + (χ a) ^ (n.val) = χ a := by + have hn := n.prop + rcases χ_values (a := a) with h | h | h + · rw [h, zero_pow hn.pos.ne'] + · rw [h] + exact hn.neg_one_pow + · rw [h, one_pow] + +lemma χ_χ_eq_χ {a : F} (hq_mod : Fintype.card F % 4 = 3) : + χ (χ a) = χ a := by + rcases χ_values (a := a) with h | h | h + · rw [h, χ_zero] + · rw [h, χ_neg_one hq_mod] + · rw [h, χ_one] + +lemma χ_inv {a : F} : χ a = χ (1 / a) := by + rcases eq_or_ne a 0 with rfl | ha + · simp + · have heq : χ (1 / a) * χ a = 1 := by + rw [← χ_mul, one_div, inv_mul_cancel₀ ha, χ_one] + rcases χ_values (a := a) with h | h | h + · exact absurd h (χ_a_ne_zero ha) + · rw [h] at heq ⊢ + grind + · rw [h] at heq ⊢ + grind + +lemma one_div_χ_of_a_eq_χ_a {a : F} : χ a = 1 / χ a := by + rcases χ_values (a := a) with h | h | h <;> rw [h] <;> norm_num + +/-- Multiplying by a nonzero square does not change the quadratic character. +Introduced in paper theory theorem 3.A proof. -/ +lemma χ_of_a_eq_χ_a_mul_b_pow_two {a b : F} (b_ne_zero : b ≠ 0) : + χ (a * b ^ 2) = χ a := by + rw [χ_mul, χ_sq b_ne_zero, mul_one] + +lemma a_pow_q_add_one_div_two_eq_χ_of_a_mul_a {a : F} + (hq_mod : Fintype.card F % 4 = 3) : + a ^ ((Fintype.card F + 1) / 2) = (χ a) * a := by + rw [χ_eq_pow a hq_mod, ← pow_succ] + congr 1 + omega + +omit [DecidableEq F] in +lemma a_pow_q_add_one_div_two_eq_a {a : F} + (a_square : IsSquare a) (hq_mod : Fintype.card F % 4 = 3) : + a ^ ((Fintype.card F + 1) / 2) = a := by + rcases eq_or_ne a 0 with rfl | ha + · exact zero_pow (by omega) + · classical + rw [a_pow_q_add_one_div_two_eq_χ_of_a_mul_a hq_mod] + rw [χ_a_eq_one ha a_square, one_mul] + +lemma b_pow_q_add_one_div_four_eq_χ_of_a_mul_a {a : F} + (hq_mod : Fintype.card F % 4 = 3) : + (a ^ 2) ^ ((Fintype.card F + 1) / 4) = (χ a) * a := by + rw [← pow_mul] + have h : 2 * ((Fintype.card F + 1) / 4) = (Fintype.card F + 1) / 2 := by omega + rw [h, a_pow_q_add_one_div_two_eq_χ_of_a_mul_a hq_mod] + +lemma χ_a_mul_a_IsSquare {a : F} + (a_ne_zero : a ≠ 0) (hq_mod : Fintype.card F % 4 = 3) + : IsSquare ((χ a) * a) := by + have h : (χ a) * a ≠ 0 := mul_ne_zero (χ_a_ne_zero a_ne_zero) a_ne_zero + apply (χ_eq_one_iff_isSquare h hq_mod).mp + rw [χ_mul, χ_χ_eq_χ hq_mod, ← pow_two] + exact χ_of_a_even_pow_n_eq_one a_ne_zero ⟨2, even_two⟩ + +end Cslib.Crypto.Systems.Elligator.LegendreSymbol diff --git a/references.bib b/references.bib index d2a7dfb13..d7654b213 100644 --- a/references.bib +++ b/references.bib @@ -551,3 +551,22 @@ @book{Papadimitriou94 publisher={Addison-Wesley}, address={Reading, Massachusetts} } + +@inproceedings{bernstein2013a, + location={Munich, Germany}, + author={Bernstein, Daniel J. and Hamburg, Mike and Krasnova, Anna and Lange, Tanja}, + title={Elligator: elliptic-curve points indistinguishable from uniform random strings}, + year={2013}, + isbn={9781450324779}, + publisher={Association for Computing Machinery}, + address={New York, NY, USA}, + url={https://doi.org/10.1145/2508859.2516734}, + doi={10.1145/2508859.2516734}, + abstract={Censorship-circumvention tools are in an arms race against censors. The censors study all traffic passing into and out of their controlled sphere, and try to disable censorship-circumvention tools without completely shutting down the Internet. Tools aim to shape their traffic patterns to match unblocked programs, so that simple traffic profiling cannot identify the tools within a reasonable number of traces; the censors respond by deploying firewalls with increasingly sophisticated deep-packet inspection. Cryptography hides patterns in user data but does not evade censorship if the censor can recognize patterns in the cryptography itself. In particular, elliptic-curve cryptography often transmits points on known elliptic curves, and those points are easily distinguishable from uniform random strings of bits.This paper introduces high-security high-speed elliptic-curve systems in which elliptic-curve points are encoded so as to be indistinguishable from uniform random strings. At a lower level, this paper introduces a new bijection between strings and about half of all curve points; this bijection is applicable to every odd-characteristic elliptic curve with a point of order 2, except for curves of $j$-invariant 1728. This paper also presents guidelines to construct, and two examples of, secure curves suitable for these encodings.}, + booktitle={Proceedings of the 2013 ACM SIGSAC Conference on Computer \& Communications Security}, + pages={967–980}, + numpages={14}, + keywords={injective maps, elliptic curves, censorship circumvention}, + location={Berlin, Germany}, + series={CCS '13} +}