Skip to main content

ec/
point_edwards.rs

1//! Point representation and group law for Edwards curves.
2//!
3//! A single `EdwardsPoint<F>` type handles both odd and even characteristic
4//! via runtime dispatch on $F::characteristic()$.
5//!
6//! # Odd characteristic
7//!
8//! The curve is given by
9//!
10//! $$
11//! x^2 + y^2 = 1 + d x^2 y^2
12//! $$
13//!
14//! - Identity: $(0, 1)$
15//! - Negation: $-(x, y) = (-x, y)$
16//! - Addition:
17//!
18//! $$
19//! x_3 = \frac{x_1 y_2 + y_1 x_2}{1 + d x_1 x_2 y_1 y_2},
20//! \quad
21//! y_3 = \frac{y_1 y_2 - x_1 x_2}{1 - d x_1 x_2 y_1 y_2}
22//! $$
23//!
24//! # Characteristic $2$
25//!
26//! The curve is given by
27//!
28//! $$
29//! d_1(x + y) + d_2(x^2 + y^2) = xy + xy(x + y) + x^2 y^2
30//! $$
31//!
32//! - Identity: $(0, 0)$
33//! - Negation: $-(x, y) = (y, x)$
34//! - Addition: Bernstein–Lange–Rezaeian Farashahi §3 formulas
35//!   (strongly unified — works for doubling too)
36
37use core::fmt;
38use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
39
40use crate::curve_edwards::EdwardsCurve;
41use crate::point_ops::PointOps;
42use fp::field_ops::FieldOps;
43
44/// An affine point on an Edwards curve, for any characteristic.
45#[derive(Debug, Clone, Copy)]
46pub struct EdwardsPoint<F: FieldOps> {
47    /// The x coordinate of a point.
48    pub x: F,
49    /// The y coordinate of a point.
50    pub y: F,
51}
52
53impl<F> fmt::Display for EdwardsPoint<F>
54where
55    F: FieldOps + fmt::Display,
56{
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        if self.is_identity() {
59            if f.alternate() {
60                if F::characteristic()[0] != 2 {
61                    write!(f, "EdwardsPoint {{ O = (0,1) }}")
62                } else {
63                    write!(f, "EdwardsPoint {{ O = (0,0) }}")
64                }
65            } else {
66                write!(f, "O")
67            }
68        } else if f.alternate() {
69            write!(f, "EdwardsPoint {{ x = {}, y = {} }}", self.x, self.y)
70        } else {
71            write!(f, "({}, {})", self.x, self.y)
72        }
73    }
74}
75
76// ---------------------------------------------------------------------------
77// PartialEq / Eq
78// ---------------------------------------------------------------------------
79
80impl<F: FieldOps> PartialEq for EdwardsPoint<F> {
81    fn eq(&self, other: &Self) -> bool {
82        self.x == other.x && self.y == other.y
83    }
84}
85
86impl<F: FieldOps> Eq for EdwardsPoint<F> {}
87
88// ---------------------------------------------------------------------------
89// Constructors
90// ---------------------------------------------------------------------------
91
92impl<F: FieldOps> EdwardsPoint<F> {
93    /// Construct an affine Edwards point.  No on-curve check.
94    pub fn new(x: F, y: F) -> Self {
95        Self { x, y }
96    }
97
98    /// The group identity.
99    /// - Odd char: `(0, 1)`
100    /// - Char 2:   `(0, 0)`
101    pub fn identity() -> Self {
102        if F::characteristic()[0] != 2 {
103            Self {
104                x: F::zero(),
105                y: F::one(),
106            }
107        } else {
108            Self {
109                x: F::zero(),
110                y: F::zero(),
111            }
112        }
113    }
114
115    /// Check whether this is the identity element.
116    pub fn is_identity(&self) -> bool {
117        if F::characteristic()[0] != 2 {
118            self.x == F::zero() && self.y == F::one()
119        } else {
120            self.x == F::zero() && self.y == F::zero()
121        }
122    }
123}
124
125// ---------------------------------------------------------------------------
126// Constant-time
127// ---------------------------------------------------------------------------
128
129impl<F> ConditionallySelectable for EdwardsPoint<F>
130where
131    F: FieldOps + Copy,
132{
133    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
134        Self {
135            x: F::conditional_select(&a.x, &b.x, choice),
136            y: F::conditional_select(&a.y, &b.y, choice),
137        }
138    }
139
140    fn conditional_assign(&mut self, other: &Self, choice: Choice) {
141        self.x.conditional_assign(&other.x, choice);
142        self.y.conditional_assign(&other.y, choice);
143    }
144
145    fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
146        F::conditional_swap(&mut a.x, &mut b.x, choice);
147        F::conditional_swap(&mut a.y, &mut b.y, choice);
148    }
149}
150
151impl<F> ConstantTimeEq for EdwardsPoint<F>
152where
153    F: FieldOps + Copy + ConstantTimeEq,
154{
155    fn ct_eq(&self, other: &Self) -> Choice {
156        self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y)
157    }
158
159    fn ct_ne(&self, other: &Self) -> Choice {
160        !self.ct_eq(other)
161    }
162}
163
164// ---------------------------------------------------------------------------
165// Group operations
166// ---------------------------------------------------------------------------
167
168impl<F: FieldOps> EdwardsPoint<F> {
169    /// Negate a point.
170    /// - Odd char: `-(x, y) = (-x, y)`
171    /// - Char 2:   `-(x, y) = (y, x)`
172    pub fn negate(&self, _curve: &EdwardsCurve<F>) -> Self {
173        if F::characteristic()[0] != 2 {
174            Self::new(-self.x, self.y)
175        } else {
176            Self::new(self.y, self.x)
177        }
178    }
179
180    /// Add two points on the Edwards curve.
181    pub fn add(&self, other: &Self, curve: &EdwardsCurve<F>) -> Self {
182        if F::characteristic()[0] != 2 {
183            self.add_odd(other, curve)
184        } else {
185            self.add_binary(other, curve)
186        }
187    }
188
189    /// Double a point.  Both addition laws are strongly unified, so this
190    /// just delegates to `add`.
191    pub fn double(&self, curve: &EdwardsCurve<F>) -> Self {
192        self.add(self, curve)
193    }
194
195    /// Scalar multiplication `[k]P` (constant-time double-and-add).
196    pub fn scalar_mul(&self, k: &[u64], curve: &EdwardsCurve<F>) -> Self {
197        let mut result = Self::identity();
198
199        for &limb in k.iter().rev() {
200            for bit in (0..64).rev() {
201                let doubled = result.double(curve);
202                let added = doubled.add(self, curve);
203                let choice = Choice::from(((limb >> bit) & 1) as u8);
204                result = Self::conditional_select(&doubled, &added, choice);
205            }
206        }
207
208        result
209    }
210
211    // -----------------------------------------------------------------------
212    // Odd-characteristic addition
213    //
214    //   x₃ = (x₁y₂ + y₁x₂) / (1 + d·x₁x₂y₁y₂)
215    //   y₃ = (y₁y₂ − x₁x₂) / (1 − d·x₁x₂y₁y₂)
216    // -----------------------------------------------------------------------
217
218    fn add_odd(&self, other: &Self, curve: &EdwardsCurve<F>) -> Self {
219        let d = curve.d2;
220
221        let x1y2 = self.x * other.y;
222        let y1x2 = self.y * other.x;
223        let x1x2 = self.x * other.x;
224        let y1y2 = self.y * other.y;
225
226        let dxy = d * x1x2 * y1y2;
227
228        let one = F::one();
229        let x_num = x1y2 + y1x2;
230        let x_den = one + dxy;
231        let y_num = y1y2 - x1x2;
232        let y_den = one - dxy;
233
234        let x_den_inv = x_den
235            .invert()
236            .into_option()
237            .expect("Edwards addition: x-denominator must be invertible");
238        let y_den_inv = y_den
239            .invert()
240            .into_option()
241            .expect("Edwards addition: y-denominator must be invertible");
242
243        Self::new(x_num * x_den_inv, y_num * y_den_inv)
244    }
245
246    // -----------------------------------------------------------------------
247    // Characteristic-2 addition  (Bernstein–Lange–Rezaeian Farashahi §3)
248    //
249    //   w₁ = x₁+y₁,  w₂ = x₂+y₂
250    //   A  = x₁²+x₁,  B  = y₁²+y₁
251    //
252    //   x₃ = (d₁(x₁+x₂) + d₂·w₁·w₂ + A·(x₂(y₁+y₂+1)+y₁y₂))
253    //         / (d₁ + A·w₂)
254    //   y₃ = (d₁(y₁+y₂) + d₂·w₁·w₂ + B·(y₂(x₁+x₂+1)+x₁x₂))
255    //         / (d₁ + B·w₂)
256    // -----------------------------------------------------------------------
257
258    fn add_binary(&self, other: &Self, curve: &EdwardsCurve<F>) -> Self {
259        let x1 = self.x;
260        let y1 = self.y;
261        let x2 = other.x;
262        let y2 = other.y;
263        let d1 = curve.d1;
264        let d2 = curve.d2;
265
266        let w1 = x1 + y1;
267        let w2 = x2 + y2;
268
269        let a = <F as FieldOps>::square(&x1) + x1;
270        let b = <F as FieldOps>::square(&y1) + y1;
271
272        let d2_w1w2 = d2 * w1 * w2;
273        let x1x2 = x1 * x2;
274        let y1y2 = y1 * y2;
275
276        let x_num = d1 * (x1 + x2) + d2_w1w2 + a * (x2 * (y1 + y2 + F::one()) + y1y2);
277        let x_den = d1 + a * w2;
278
279        let y_num = d1 * (y1 + y2) + d2_w1w2 + b * (y2 * (x1 + x2 + F::one()) + x1x2);
280        let y_den = d1 + b * w2;
281
282        let x_den_inv = x_den
283            .invert()
284            .into_option()
285            .expect("binary Edwards addition: x-denom must be invertible");
286        let y_den_inv = y_den
287            .invert()
288            .into_option()
289            .expect("binary Edwards addition: y-denom must be invertible");
290
291        Self::new(x_num * x_den_inv, y_num * y_den_inv)
292    }
293}
294
295// ---------------------------------------------------------------------------
296// w-coordinate helpers for char-2 Montgomery ladder
297// ---------------------------------------------------------------------------
298
299impl<F: FieldOps> EdwardsPoint<F> {
300    /// Differential addition on the `w`-line (`w = x + y`, char 2 only).
301    ///
302    /// Given `w₁ = w(Q−P)`, `w₂ = w(P)`, `w₃ = w(Q)`, compute `w₅ = w(P+Q)`.
303    pub fn w_diff_add(w1: &F, w2: &F, w3: &F, curve: &EdwardsCurve<F>) -> F {
304        let r = *w2 * *w3;
305        let s = <F as FieldOps>::square(&r);
306        let one = F::one();
307        let t = r * (one + *w2 + *w3) + s;
308
309        let d2_over_d1 = curve.d2 * curve.d1.invert().into_option().expect("d1 invertible");
310        let coeff = d2_over_d1 + one;
311
312        let den = curve.d1 + t + coeff * s;
313        let den_inv = den
314            .invert()
315            .into_option()
316            .expect("w-diff-add denominator invertible");
317
318        t * den_inv + *w1
319    }
320
321    /// `w`-coordinate doubling (`w = x + y`, char 2 only).
322    ///
323    /// Given `w₂ = w(P)`, compute `w₄ = w(2P)`.
324    pub fn w_double(w2: &F, curve: &EdwardsCurve<F>) -> F {
325        let a = <F as FieldOps>::square(w2);
326        let j = <F as FieldOps>::square(&a);
327
328        let d2_over_d1 = curve.d2 * curve.d1.invert().into_option().expect("d1 invertible");
329
330        let num = a + j;
331        let den = curve.d1 + a + d2_over_d1 * j;
332        let den_inv = den
333            .invert()
334            .into_option()
335            .expect("w-double denominator invertible");
336
337        num * den_inv
338    }
339}
340
341// ---------------------------------------------------------------------------
342// PointOps bridge
343// ---------------------------------------------------------------------------
344
345impl<F: FieldOps> PointOps for EdwardsPoint<F> {
346    type BaseField = F;
347    type Curve = EdwardsCurve<F>;
348
349    fn identity(_curve: &Self::Curve) -> Self {
350        EdwardsPoint::<F>::identity()
351    }
352
353    fn is_identity(&self) -> bool {
354        EdwardsPoint::<F>::is_identity(self)
355    }
356
357    fn negate(&self, curve: &Self::Curve) -> Self {
358        EdwardsPoint::<F>::negate(self, curve)
359    }
360
361    fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self {
362        EdwardsPoint::<F>::scalar_mul(self, k, curve)
363    }
364}