Skip to main content

ec/
point_weierstrass.rs

1//! Elliptic curve point representation and group law.
2//!
3//! # Representation
4//!
5//! Points are stored in **affine** coordinates $(x, y)$ or as the
6//! distinguished point at infinity $O$ (the group identity).
7//!
8//! # Group law
9//!
10//! The addition formulas implement the general Weierstrass group law:
11//!
12//! | Operation        | Algorithm                                | Cost              |
13//! |------------------|------------------------------------------|-------------------|
14//! | Negate           | $-(x,y) = (x, -y - a_1 x - a_3)$         | $3$ mul + $2$ add |
15//! | Add $(P \ne \pm Q)$ | Chord-and-tangent (general Weierstrass) | $1$ inv + $6$ mul |
16//! | Double           | Tangent (general Weierstrass)            | $1$ inv + $7$ mul |
17//! | Scalar multiply  | Montgomery ladder (constant-time scalar) | $O(n)$ doublings  |
18
19// WARNING: SOME OF THE FUNCTIONS BELOW USE BRANCHES DEPENDING
20// ON WHETHER A POINT IS AT INFINITY OR NOT!!!!
21
22use core::fmt;
23//use std::os::unix::raw::ino_t;
24//use crypto_bigint::modular::ConstMontyForm;
25use crate::curve_weierstrass::WeierstrassCurve;
26use crate::point_ops::PointOps;
27use fp::field_ops::FieldOps;
28use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
29
30/// An affine point on a Weierstrass elliptic curve over `F`.
31///
32/// The point at infinity is represented by `infinity = true`; in that case
33/// the `x` and `y` fields are meaningless (set to zero by convention).
34#[derive(Debug, Clone, Copy)]
35pub struct AffinePoint<F: FieldOps> {
36    /// x coordinate
37    pub x: F,
38    /// y coordinate
39    pub y: F,
40    /// true if and only if the ponit at infinity
41    pub infinity: bool,
42}
43
44// ---------------------------------------------------------------------------
45// Manual trait impls  (avoid over-constraining with #[derive])
46// ---------------------------------------------------------------------------
47
48impl<F: FieldOps> PartialEq for AffinePoint<F>
49where
50    F: FieldOps + ConstantTimeEq,
51{
52    fn eq(&self, other: &Self) -> bool {
53        match (self.infinity, other.infinity) {
54            (true, true) => true,
55            (true, false) => false,
56            (false, true) => false,
57            (false, false) => self.x == other.x && self.y == other.y,
58        }
59    }
60}
61
62impl<F> fmt::Display for AffinePoint<F>
63where
64    F: FieldOps + fmt::Display,
65{
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        if self.infinity {
68            if f.alternate() {
69                write!(f, "AffinePoint {{ O }}")
70            } else {
71                write!(f, "O")
72            }
73        } else if f.alternate() {
74            write!(f, "AffinePoint {{ x = {}, y = {} }}", self.x, self.y)
75        } else {
76            write!(f, "({}, {})", self.x, self.y)
77        }
78    }
79}
80
81
82
83impl<F: FieldOps> Eq for AffinePoint<F>
84where
85F: FieldOps + ConstantTimeEq
86{ }
87
88// ---------------------------------------------------------------------------
89// Constructors
90// ---------------------------------------------------------------------------
91
92impl<F: FieldOps> AffinePoint<F> {
93    /// Construct a finite affine point `(x, y)`.
94    ///
95    /// **No** on-curve check is performed; use
96    /// [`WeierstrassCurve::contains`] if you need validation.
97    pub fn new(x: F, y: F) -> Self {
98        Self {
99            x,
100            y,
101            infinity: false,
102        }
103    }
104
105    /// The point at infinity `O` (the group identity).
106    pub fn identity() -> Self {
107        Self {
108            x: F::zero(),
109            y: F::zero(),
110            infinity: true,
111        }
112    }
113
114    /// Returns `true` if this is the point at infinity.
115    pub fn is_identity(&self) -> bool {
116        self.infinity
117    }
118}
119
120// ---------------------------------------------------------------------------
121// Constant-time functionalities
122// ---------------------------------------------------------------------------
123
124impl<F> ConditionallySelectable for AffinePoint<F>
125where
126    F: FieldOps + Copy,
127{
128    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
129        let ai = a.infinity as u8;
130        let bi = b.infinity as u8;
131        let infinity = u8::conditional_select(&ai, &bi, choice) != 0;
132
133        Self {
134            x: F::conditional_select(&a.x, &b.x, choice),
135            y: F::conditional_select(&a.y, &b.y, choice),
136            infinity,
137        }
138    }
139
140    fn conditional_assign(&mut self, other: &Self, choice: Choice) {
141        F::conditional_assign(&mut self.x, &other.x, choice);
142        F::conditional_assign(&mut self.y, &other.y, choice);
143
144        let mut inf = self.infinity as u8;
145        let other_inf = other.infinity as u8;
146        inf.conditional_assign(&other_inf, choice);
147        self.infinity = inf != 0;
148    }
149
150    fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
151        F::conditional_swap(&mut a.x, &mut b.x, choice);
152        F::conditional_swap(&mut a.y, &mut b.y, choice);
153
154        let mut ai = a.infinity as u8;
155        let mut bi = b.infinity as u8;
156        u8::conditional_swap(&mut ai, &mut bi, choice);
157        a.infinity = ai != 0;
158        b.infinity = bi != 0;
159    }
160}
161
162impl<F> ConstantTimeEq for AffinePoint<F>
163where
164    F: FieldOps + Copy + ConstantTimeEq,
165{
166    fn ct_eq(&self, other: &Self) -> Choice {
167        let self_inf = Choice::from(self.infinity as u8);
168        let other_inf = Choice::from(other.infinity as u8);
169
170        let both_inf = self_inf & other_inf;
171        let both_finite = !self_inf & !other_inf;
172        let coords_eq = self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y);
173
174        both_inf | (both_finite & coords_eq)
175    }
176
177    fn ct_ne(&self, other: &Self) -> Choice {
178        !self.ct_eq(other)
179    }
180}
181
182// ---------------------------------------------------------------------------
183// Group operations
184// ---------------------------------------------------------------------------
185
186impl<F: FieldOps> AffinePoint<F> {
187    /// Negate a point:  `-(x, y) = (x, −y − a₁x − a₃)`.
188    pub fn negate(&self, curve: &WeierstrassCurve<F>) -> Self {
189        if self.infinity {
190            return Self::identity();
191        }
192
193        let neg_y = -self.y - curve.a1 * self.x - curve.a3;
194
195        Self::new(self.x.clone(), neg_y)
196    }
197
198    /// Double a point:  `[2]P`.
199    ///
200    /// Uses the tangent-line formula for the general Weierstrass model:
201    ///
202    /// ```text
203    /// λ = (3x₁² + 2a₂x₁ + a₄ − a₁y₁) / (2y₁ + a₁x₁ + a₃)
204    /// x₃ = λ² + a₁λ − a₂ − 2x₁
205    /// y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃
206    /// ```
207    ///
208    /// Returns `O` when the tangent is vertical (i.e. `2y + a₁x + a₃ = 0`).
209    pub fn double(&self, curve: &WeierstrassCurve<F>) -> Self {
210        if self.infinity {
211            return Self::identity();
212        }
213
214        // denominator = 2y₁ + a₁x₁ + a₃
215        let denom = <F as FieldOps>::double(&self.y) + curve.a1 * self.x + curve.a3;
216
217        // If denominator is zero the tangent is vertical → result is O.
218        let denom_inv = match denom.invert().into_option() {
219            Some(inv) => inv,
220            None => return Self::identity(),
221        };
222
223        // numerator = 3x₁² + 2a₂x₁ + a₄ − a₁y₁
224        let numer = {
225            let x1_sq = <F as FieldOps>::square(&self.x);
226            let three_x1_sq = x1_sq + <F as FieldOps>::double(&x1_sq);
227            let two_a2_x1 = <F as FieldOps>::double(&(curve.a2 * self.x));
228            let a1y1 = curve.a1 * self.y;
229            three_x1_sq + two_a2_x1 + curve.a4 - a1y1
230        };
231
232        let lambda = numer * denom_inv;
233
234        // x₃ = λ² + a₁λ − a₂ − 2x₁
235        let x3 = {
236            let lam_sq = <F as FieldOps>::square(&lambda);
237            let a1_lam = curve.a1 * lambda;
238            let two_x1 = <F as FieldOps>::double(&self.x);
239            lam_sq + a1_lam - curve.a2 - two_x1
240        };
241
242        // y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃
243        let y3 = {
244            let dx = self.x - x3;
245            let lam_dx = lambda * dx;
246            let a1x3 = curve.a1 * x3;
247
248            lam_dx - self.y - a1x3 - curve.a3
249        };
250
251        Self::new(x3, y3)
252    }
253
254    /// Add two points:  `P + Q`.
255    ///
256    /// Handles all cases:
257    ///   - Either operand is `O` → return the other.
258    ///   - `P = Q` → delegate to [`double`](Self::double).
259    ///   - `P = −Q` (same x, opposite y) → return `O`.
260    ///   - General chord:
261    ///
262    /// ```text
263    /// λ  = (y₂ − y₁) / (x₂ − x₁)
264    /// x₃ = λ² + a₁λ − a₂ − x₁ − x₂
265    /// y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃
266    /// ```
267    pub fn add(&self, other: &Self, curve: &WeierstrassCurve<F>) -> Self {
268        // O + Q = Q
269        if self.infinity {
270            return other.clone();
271        }
272        // P + O = P
273        if other.infinity {
274            return self.clone();
275        }
276
277        // Same x-coordinate?
278        if self.x == other.x {
279            if self.y == other.y {
280                // P = Q  → doubling
281                return self.double(curve);
282            }
283            // P = −Q  → identity
284            // (This also covers the char-2 case where y₁ + y₂ + a₁x + a₃ = 0.)
285            return Self::identity();
286        }
287
288        // General chord
289        let dx = other.x - self.x;
290        let dy = other.y - self.y;
291
292        // dx ≠ 0 guaranteed by the x₁ ≠ x₂ check above
293        let dx_inv = dx
294            .invert()
295            .into_option()
296            .expect("dx must be invertible (x₁ ≠ x₂)");
297        let lambda = dy * dx_inv;
298
299        // x₃ = λ² + a₁λ − a₂ − x₁ − x₂
300        let x3 = {
301            let lam_sq = <F as FieldOps>::square(&lambda);
302            let a1_lam = curve.a1 * lambda;
303            lam_sq + a1_lam - curve.a2 - self.x - other.x
304        };
305
306        // y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃
307        let y3 = {
308            let dx3 = self.x - x3;
309            let lam_dx3 = lambda * dx3;
310            let a1x3 = curve.a1 * x3;
311            lam_dx3 - self.y - a1x3 - curve.a3
312        };
313
314        Self::new(x3, y3)
315    }
316
317    /// Multiply `self` by `k`
318    ///
319    /// # Arguments
320    ///
321    /// * `&self` - Point on curve (type: `Self`)
322    /// * `k` - Integer (type: `&[u64]`)
323    /// * `curve` - The curve we're on (type: `&<AffinePoint<F> as PointOps>::Curve`)
324    ///
325    /// # Returns
326    ///
327    /// The point `k * self` (type: `Self`)
328    pub fn scalar_mul(&self, k: &[u64], curve: &<AffinePoint<F> as PointOps>::Curve) -> Self {
329        let mut r0 = Self::identity();
330        let mut r1 = self.clone();
331
332        for &limb in k.iter().rev() {
333            for bit in (0..64).rev() {
334                let choice = Choice::from(((limb >> bit) & 1) as u8);
335
336                Self::conditional_swap(&mut r0, &mut r1, choice);
337
338                let sum = r0.add(&r1, curve);
339                let dbl = r0.double(curve);
340                r1 = sum;
341                r0 = dbl;
342
343                Self::conditional_swap(&mut r0, &mut r1, choice);
344            }
345        }
346
347        r0
348    }
349}
350
351impl<F> PointOps for AffinePoint<F>
352where
353    F: FieldOps,
354{
355    type BaseField = F;
356    type Curve = WeierstrassCurve<F>;
357
358    fn identity(_curve: &Self::Curve) -> Self {
359        AffinePoint::<F>::identity()
360    }
361
362    fn is_identity(&self) -> bool {
363        self.infinity
364    }
365
366    fn negate(&self, curve: &Self::Curve) -> Self {
367        AffinePoint::<F>::negate(self, curve)
368    }
369
370    fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self {
371        AffinePoint::<F>::scalar_mul(self, k, curve)
372    }
373}
374
375impl<F> crate::point_ops::PointAdd for AffinePoint<F>
376where
377    F: FieldOps,
378{
379    fn add(&self, other: &Self, curve: &Self::Curve) -> Self {
380        AffinePoint::<F>::add(self, other, curve)
381    }
382}