Skip to main content

a/math/ec/
edwards.rs

1//! Twisted Edwards curves, `a x^2 + y^2 = 1 + d x^2 y^2`.
2
3use super::curve::{Arithmetic, Curve, CurveError, choose_root};
4use super::field::{Base, Field, invert_all, reduced, sqrt_ratio, with_prime};
5use super::group::{Coordinates, Group};
6use super::montgomery::Montgomery;
7use super::point::Point;
8use crate::math::numbers::{ByteOrder, Number};
9
10/// A twisted Edwards curve `a x^2 + y^2 = 1 + d x^2 y^2` over the field of a prime `p > 3`, with
11/// `a` a square and `d` not, which makes its addition law complete: one formula for every pair of
12/// points, doublings and the neutral element `(0, 1)` included.
13///
14/// Its points are held in extended coordinates, `x = X / Z`, `y = Y / Z`, `x y = T / Z`, the
15/// fastest known form of the group, with a formula of its own for `a = -1`, as on Ed25519.
16///
17/// # Examples
18///
19/// ```
20/// use a::math::ec::{Curve, Edwards};
21/// use a::math::numbers::{ByteOrder, Number};
22///
23/// let n = |x| Number::new_from_u32(x, ByteOrder::BigEndian);
24/// // x^2 + y^2 = 1 + 2 x^2 y^2 over F_13, where 1 is a square and 2 is not.
25/// let curve = Edwards::new(&n(1), &n(2), &n(13)).unwrap();
26/// let o = curve.identity();
27/// assert_eq!(o.coordinates(), Some((n(0), n(1))));
28/// ```
29pub struct Edwards {
30    /// The field.
31    base: Base,
32    /// `a` below `p`.
33    a: Number,
34    /// `d` below `p`.
35    d: Number,
36    /// `a` in the field's form.
37    a_limbs: Vec<u64>,
38    /// `d` in the field's form.
39    d_limbs: Vec<u64>,
40    /// Whether `a = -1`.
41    minus_one: bool,
42}
43
44impl Edwards {
45    /// The curve `a x^2 + y^2 = 1 + d x^2 y^2` modulo `p`, in `p`'s byte order. `a` and `d` are
46    /// reduced modulo `p`.
47    ///
48    /// # Errors
49    ///
50    /// [`CurveError::NotPrime`] when `p` is not a prime above 3, by
51    /// [`is_probable_prime`](Number::is_probable_prime); [`CurveError::Singular`] when `a`, `d` or
52    /// `a - d` is zero modulo `p`; [`CurveError::NotComplete`] when `a` is not a square or `d` is.
53    pub fn new(a: &Number, d: &Number, p: &Number) -> Result<Edwards, CurveError> {
54        let three = Number::new_from_u8(3, p.byte_order);
55        if p.is_negative() || *p <= three || !p.is_probable_prime() {
56            return Err(CurveError::NotPrime);
57        }
58        let a = reduced(a, p);
59        let d = reduced(d, p);
60        if a.is_zero() || d.is_zero() || a == d {
61            return Err(CurveError::Singular);
62        }
63        if a.jacobi(p) != 1 || d.jacobi(p) != -1 {
64            return Err(CurveError::NotComplete);
65        }
66        let base = Base::new(p);
67        let n = with_prime!(&base.prime, f => f.limbs());
68        let (mut a_limbs, mut d_limbs) = (vec![0; n], vec![0; n]);
69        with_prime!(&base.prime, f => {
70            f.store(&f.enter(&a), &mut a_limbs);
71            f.store(&f.enter(&d), &mut d_limbs);
72        });
73        let minus_one = &a + 1u8 == *p;
74        Ok(Edwards {
75            base,
76            a,
77            d,
78            a_limbs,
79            d_limbs,
80            minus_one,
81        })
82    }
83
84    /// The coefficient `a`, below `p`.
85    pub fn a(&self) -> &Number {
86        &self.a
87    }
88
89    /// The coefficient `d`, below `p`.
90    pub fn d(&self) -> &Number {
91        &self.d
92    }
93
94    /// The Montgomery curve this one is birationally equivalent to, with
95    /// `A = 2 (a + d) / (a - d)` and `B = 4 / (a - d)`.
96    pub fn to_montgomery(&self) -> Montgomery {
97        let (ma, mb) = self.montgomery_coefficients();
98        // The curve is not singular, and so neither is this one; `p` was checked.
99        Montgomery::new(&ma, &mb, &self.base.p).unwrap_or_else(|_| unreachable!())
100    }
101
102    /// The point of `target`, from [`to_montgomery`](Edwards::to_montgomery), that `point` of this
103    /// curve corresponds to: `((1 + y) / (1 - y), (1 + y) / ((1 - y) x))`, with the neutral element
104    /// going to the point at infinity and `(0, -1)` to `(0, 0)`.
105    ///
106    /// # Panics
107    ///
108    /// Panics if `point` is not on this curve or `target` is not the curve from
109    /// [`to_montgomery`](Edwards::to_montgomery).
110    pub fn map_to_montgomery<'t>(
111        &self,
112        target: &'t Montgomery,
113        point: &Point<'_, Edwards>,
114    ) -> Point<'t, Montgomery> {
115        point.check_curve(self);
116        let p = &self.base.p;
117        let (ma, mb) = self.montgomery_coefficients();
118        assert!(
119            target.modulus() == p && (target.a(), target.b()) == (&ma, &mb),
120            "the target is not this curve's Montgomery form"
121        );
122        let Some((x, y)) = point.coordinates() else {
123            return target.identity();
124        };
125        if x.is_zero() {
126            if y == Number::new_from_u8(1, p.byte_order) {
127                return target.identity();
128            }
129            let zero = Number::new_from_u8(0, p.byte_order);
130            return target
131                .point(&zero, &zero)
132                .unwrap_or_else(|_| unreachable!());
133        }
134        let inv = (1u8 - &y).rem_euclid(p).mod_inverse(p).unwrap_or_default();
135        let u = ((&y + 1u8) * inv).rem_euclid(p);
136        let v = (&u * x.mod_inverse(p).unwrap_or_default()).rem_euclid(p);
137        // `y = 1` only on the neutral element, so this is a point of the target.
138        target.point(&u, &v).unwrap_or_else(|_| unreachable!())
139    }
140
141    /// The coefficients `A` and `B` of [`to_montgomery`](Edwards::to_montgomery)'s curve.
142    fn montgomery_coefficients(&self) -> (Number, Number) {
143        let (a, d, p) = (&self.a, &self.d, &self.base.p);
144        let i = (a - d).rem_euclid(p).mod_inverse(p).unwrap_or_default();
145        let ma = ((a + d) * 2u8 * &i).rem_euclid(p);
146        let mb = (&i * 4u8).rem_euclid(p);
147        (ma, mb)
148    }
149
150    /// The bytes of RFC 8032's encoding of an element: enough for the prime and one bit more.
151    fn encoded_len(&self) -> usize {
152        self.base.p.bits() as usize / 8 + 1
153    }
154}
155
156/// A point in extended coordinates.
157#[derive(Clone)]
158pub(super) struct Extended<E> {
159    x: E,
160    y: E,
161    z: E,
162    t: E,
163}
164
165/// A point prepared to be added: for `a = -1`, `(Y + X, Y - X, 2 Z, 2 d T)`; otherwise
166/// `(X, Y, Z, d T)`. An affine one has no `Z`, as it is 1, which saves its addition a product.
167#[derive(Clone)]
168pub(super) struct Cached<E> {
169    u: E,
170    v: E,
171    z: Option<E>,
172    t: E,
173}
174
175/// A doubling without its `T`, and the two factors of `T`.
176struct Doubled<E> {
177    x: E,
178    y: E,
179    z: E,
180    e: E,
181    h: E,
182}
183
184/// The arithmetic of a curve over the field `F`.
185pub(super) struct Edw<'a, F: Field> {
186    f: &'a F,
187    a: F::E,
188    d: F::E,
189    minus_one: bool,
190}
191
192/// Runs `$body` with `$g` bound to the group of the curve `$c`, compiled for each field.
193macro_rules! with_group {
194    ($c:expr, $g:ident => $body:expr) => {
195        with_prime!(&$c.base.prime, f => {
196            let $g = Edw {
197                f,
198                a: f.load(&$c.a_limbs),
199                d: f.load(&$c.d_limbs),
200                minus_one: $c.minus_one,
201            };
202            $body
203        })
204    };
205}
206
207impl<F: Field> Edw<'_, F> {
208    /// The `X`, `Y`, `Z` of `2 p`, and the two factors of `T`: dbl-2008-hwcd without its `T`, 3M +
209    /// 4S, which is all a run of doublings needs but for the last.
210    #[inline]
211    fn double_parts(&self, x: &F::E, y: &F::E, z: &F::E) -> Doubled<F::E> {
212        let f = self.f;
213        let a = f.sqr(x);
214        let b = f.sqr(y);
215        let c = f.twice(&f.sqr(z));
216        let d = if self.minus_one {
217            f.neg(&a)
218        } else {
219            f.mul(&self.a, &a)
220        };
221        let e = f.sub(&f.sub(&f.sqr(&f.add(x, y)), &a), &b);
222        let g = f.add(&d, &b);
223        let ff = f.sub(&g, &c);
224        let h = f.sub(&d, &b);
225        Doubled {
226            x: f.mul(&e, &ff),
227            y: f.mul(&g, &h),
228            z: f.mul(&ff, &g),
229            e,
230            h,
231        }
232    }
233
234    /// The point from its cached form's four products: `E`, `F`, `G`, `H`.
235    #[inline(always)]
236    fn combine(&self, e: &F::E, ff: &F::E, g: &F::E, h: &F::E) -> Extended<F::E> {
237        let f = self.f;
238        Extended {
239            x: f.mul(e, ff),
240            y: f.mul(g, h),
241            z: f.mul(ff, g),
242            t: f.mul(e, h),
243        }
244    }
245}
246
247impl<F: Field> Group for Edw<'_, F> {
248    type F = F;
249    type Proj = Extended<F::E>;
250    type Cached = Cached<F::E>;
251    // A table holds affine points, without their `Z`.
252    const CACHED: usize = 3;
253
254    #[inline(always)]
255    fn field(&self) -> &F {
256        self.f
257    }
258
259    fn identity(&self) -> Extended<F::E> {
260        let f = self.f;
261        Extended {
262            x: f.zero(),
263            y: f.one(),
264            z: f.one(),
265            t: f.zero(),
266        }
267    }
268
269    fn is_identity(&self, p: &Extended<F::E>) -> bool {
270        let f = self.f;
271        f.is_zero(&p.x) && f.equal(&p.y, &p.z)
272    }
273
274    fn load(&self, x: &[u64]) -> Extended<F::E> {
275        let (f, n) = (self.f, self.f.limbs());
276        Extended {
277            x: f.load(&x[..n]),
278            y: f.load(&x[n..2 * n]),
279            z: f.load(&x[2 * n..3 * n]),
280            t: f.load(&x[3 * n..4 * n]),
281        }
282    }
283
284    fn store(&self, p: &Extended<F::E>, r: &mut [u64]) {
285        let (f, n) = (self.f, self.f.limbs());
286        f.store(&p.x, &mut r[..n]);
287        f.store(&p.y, &mut r[n..2 * n]);
288        f.store(&p.z, &mut r[2 * n..3 * n]);
289        f.store(&p.t, &mut r[3 * n..4 * n]);
290    }
291
292    fn neg(&self, p: &Extended<F::E>) -> Extended<F::E> {
293        let f = self.f;
294        Extended {
295            x: f.neg(&p.x),
296            y: p.y.clone(),
297            z: p.z.clone(),
298            t: f.neg(&p.t),
299        }
300    }
301
302    fn double(&self, p: &Extended<F::E>) -> Extended<F::E> {
303        let d = self.double_parts(&p.x, &p.y, &p.z);
304        Extended {
305            t: self.f.mul(&d.e, &d.h),
306            x: d.x,
307            y: d.y,
308            z: d.z,
309        }
310    }
311
312    fn double_n(&self, p: &mut Extended<F::E>, n: usize) {
313        if n == 0 {
314            return;
315        }
316        let (mut x, mut y, mut z) = (p.x.clone(), p.y.clone(), p.z.clone());
317        for _ in 1..n {
318            let d = self.double_parts(&x, &y, &z);
319            (x, y, z) = (d.x, d.y, d.z);
320        }
321        let d = self.double_parts(&x, &y, &z);
322        *p = Extended {
323            t: self.f.mul(&d.e, &d.h),
324            x: d.x,
325            y: d.y,
326            z: d.z,
327        };
328    }
329
330    fn add(&self, p: &Extended<F::E>, q: &Extended<F::E>) -> Extended<F::E> {
331        self.add_cached(p, &self.cache_one(q))
332    }
333
334    fn equal(&self, p: &Extended<F::E>, q: &Extended<F::E>) -> bool {
335        let f = self.f;
336        f.equal(&f.mul(&p.x, &q.z), &f.mul(&q.x, &p.z))
337            && f.equal(&f.mul(&p.y, &q.z), &f.mul(&q.y, &p.z))
338    }
339
340    fn cache(&self, p: &[Extended<F::E>]) -> Vec<Cached<F::E>> {
341        p.iter().map(|q| self.cache_one(q)).collect()
342    }
343
344    // The points made affine with one inversion, at about seven products each.
345    fn cache_affine(&self, p: &[Extended<F::E>]) -> Vec<Cached<F::E>> {
346        let f = self.f;
347        let mut z: Vec<F::E> = p.iter().map(|q| q.z.clone()).collect();
348        invert_all(f, &mut z);
349        p.iter()
350            .zip(z.iter())
351            .map(|(q, i)| {
352                let (x, y) = (f.mul(&q.x, i), f.mul(&q.y, i));
353                let t = f.mul(&f.mul(&x, &y), &self.d);
354                if self.minus_one {
355                    Cached {
356                        u: f.add(&y, &x),
357                        v: f.sub(&y, &x),
358                        z: None,
359                        t: f.twice(&t),
360                    }
361                } else {
362                    Cached {
363                        u: x,
364                        v: y,
365                        z: None,
366                        t,
367                    }
368                }
369            })
370            .collect()
371    }
372
373    // add-2008-hwcd-3 for `a = -1`, 8M; add-2008-hwcd otherwise, 9M and a product by `a`; a
374    // product less for an affine point.
375    fn add_cached(&self, p: &Extended<F::E>, c: &Cached<F::E>) -> Extended<F::E> {
376        let f = self.f;
377        if self.minus_one {
378            let a = f.mul(&f.sub(&p.y, &p.x), &c.v);
379            let b = f.mul(&f.add(&p.y, &p.x), &c.u);
380            let cc = f.mul(&p.t, &c.t);
381            let d = match &c.z {
382                Some(z) => f.mul(&p.z, z),
383                None => f.twice(&p.z),
384            };
385            self.combine(
386                &f.sub(&b, &a),
387                &f.sub(&d, &cc),
388                &f.add(&d, &cc),
389                &f.add(&b, &a),
390            )
391        } else {
392            let a = f.mul(&p.x, &c.u);
393            let b = f.mul(&p.y, &c.v);
394            let cc = f.mul(&p.t, &c.t);
395            let d = match &c.z {
396                Some(z) => f.mul(&p.z, z),
397                None => p.z.clone(),
398            };
399            let e = f.sub(
400                &f.sub(&f.mul(&f.add(&p.x, &p.y), &f.add(&c.u, &c.v)), &a),
401                &b,
402            );
403            self.combine(
404                &e,
405                &f.sub(&d, &cc),
406                &f.add(&d, &cc),
407                &f.sub(&b, &f.mul(&self.a, &a)),
408            )
409        }
410    }
411
412    fn neg_cached(&self, c: &Cached<F::E>) -> Cached<F::E> {
413        let f = self.f;
414        if self.minus_one {
415            Cached {
416                u: c.v.clone(),
417                v: c.u.clone(),
418                z: c.z.clone(),
419                t: f.neg(&c.t),
420            }
421        } else {
422            Cached {
423                u: f.neg(&c.u),
424                v: c.v.clone(),
425                z: c.z.clone(),
426                t: f.neg(&c.t),
427            }
428        }
429    }
430
431    // Of an affine point, which is all that `cache_affine` gives.
432    fn store_cached(&self, c: &Cached<F::E>, r: &mut [u64]) {
433        let (f, n) = (self.f, self.f.limbs());
434        f.store(&c.u, &mut r[..n]);
435        f.store(&c.v, &mut r[n..2 * n]);
436        f.store(&c.t, &mut r[2 * n..3 * n]);
437    }
438
439    fn load_cached(&self, x: &[u64]) -> Cached<F::E> {
440        let (f, n) = (self.f, self.f.limbs());
441        Cached {
442            u: f.load(&x[..n]),
443            v: f.load(&x[n..2 * n]),
444            z: None,
445            t: f.load(&x[2 * n..3 * n]),
446        }
447    }
448
449    fn of_affine(&self, x: &F::E, y: &F::E) -> Extended<F::E> {
450        let f = self.f;
451        Extended {
452            x: x.clone(),
453            y: y.clone(),
454            z: f.one(),
455            t: f.mul(x, y),
456        }
457    }
458
459    fn affine_all(&self, p: &[Extended<F::E>]) -> Vec<Option<Coordinates<F>>> {
460        let f = self.f;
461        let mut z: Vec<F::E> = p.iter().map(|q| q.z.clone()).collect();
462        invert_all(f, &mut z);
463        p.iter()
464            .zip(z.iter())
465            .map(|(q, i)| Some((f.mul(&q.x, i), f.mul(&q.y, i))))
466            .collect()
467    }
468}
469
470impl<F: Field> Edw<'_, F> {
471    /// `q` prepared to be added.
472    fn cache_one(&self, q: &Extended<F::E>) -> Cached<F::E> {
473        let f = self.f;
474        if self.minus_one {
475            Cached {
476                u: f.add(&q.y, &q.x),
477                v: f.sub(&q.y, &q.x),
478                z: Some(f.twice(&q.z)),
479                t: f.twice(&f.mul(&q.t, &self.d)),
480            }
481        } else {
482            Cached {
483                u: q.x.clone(),
484                v: q.y.clone(),
485                z: Some(q.z.clone()),
486                t: f.mul(&q.t, &self.d),
487            }
488        }
489    }
490
491    /// Whether `(x, y)` satisfies the equation.
492    fn on_curve(&self, x: &F::E, y: &F::E) -> bool {
493        let f = self.f;
494        let (xx, yy) = (f.sqr(x), f.sqr(y));
495        f.equal(
496            &f.add(&f.mul(&self.a, &xx), &yy),
497            &f.add(&f.one(), &f.mul(&self.d, &f.mul(&xx, &yy))),
498        )
499    }
500
501    /// An `x` for the given `y`: its square is `(y^2 - 1) / (d y^2 - a)`, whose denominator isn't
502    /// zero, as `a` is a square and `d` is not.
503    fn x_of(&self, y: &F::E, base: &Base) -> Option<F::E> {
504        let f = self.f;
505        let yy = f.sqr(y);
506        let u = f.sub(&yy, &f.one());
507        let v = f.sub(&f.mul(&self.d, &yy), &self.a);
508        sqrt_ratio(f, base.sqrt_plan(), &u, &v)
509    }
510}
511
512impl Arithmetic for Edwards {
513    const COORDINATES: usize = 4;
514
515    fn base(&self) -> &Base {
516        &self.base
517    }
518
519    fn same_curve(&self, other: &Self) -> bool {
520        self.base.p == other.base.p && self.a == other.a && self.d == other.d
521    }
522
523    fn satisfied(&self, x: &Number, y: &Number) -> bool {
524        with_group!(self, g => g.on_curve(&g.f.enter(x), &g.f.enter(y)))
525    }
526
527    fn lift_limbs(&self, y: &Number, odd: bool) -> Option<Vec<u64>> {
528        with_group!(self, g => {
529            let f = g.f;
530            let ey = f.enter(y);
531            let x = choose_root(f, g.x_of(&ey, &self.base)?, odd)?;
532            let mut r = vec![0; 4 * f.limbs()];
533            g.store(&g.of_affine(&x, &ey), &mut r);
534            Some(r)
535        })
536    }
537
538    fn encode(&self, xy: Option<(&Number, &Number)>, compressed: bool) -> Vec<u8> {
539        let len = self.encoded_len();
540        let le = |n: &Number| {
541            let mut b = super::curve::fixed_be(n, len);
542            b.reverse();
543            b
544        };
545        let Some((x, y)) = xy else {
546            return Vec::new();
547        };
548        if compressed {
549            let mut out = le(y);
550            out[len - 1] |= u8::from(x.bit(0)) << 7;
551            out
552        } else {
553            let mut out = le(x);
554            out.extend_from_slice(&le(y));
555            out
556        }
557    }
558
559    fn decode(&self, bytes: &[u8]) -> Option<Vec<u64>> {
560        let len = self.encoded_len();
561        let order = self.base.p.byte_order;
562        let number = |b: &[u8]| {
563            let mut n = Number::new(b.to_vec(), ByteOrder::LittleEndian);
564            if order == ByteOrder::BigEndian {
565                n.value.reverse();
566                n.byte_order = order;
567            }
568            (n < self.base.p).then_some(n)
569        };
570        if bytes.len() == len {
571            let odd = bytes[len - 1] >> 7 != 0;
572            let mut b = bytes.to_vec();
573            b[len - 1] &= 0x7f;
574            self.lift_limbs(&number(&b)?, odd)
575        } else if bytes.len() == 2 * len {
576            let (x, y) = (number(&bytes[..len])?, number(&bytes[len..])?);
577            self.satisfied(&x, &y).then(|| self.affine_limbs(&x, &y))
578        } else {
579            None
580        }
581    }
582
583    super::curve::arithmetic!(with_group);
584}
585
586impl Curve for Edwards {
587    /// `a d (a - d)^4 / 16` modulo `p`, the discriminant of the curve's Montgomery form.
588    fn discriminant(&self) -> Number {
589        let (a, d, p) = (&self.a, &self.d, &self.base.p);
590        let m = (a - d).rem_euclid(p);
591        let m2 = (&m * &m).rem_euclid(p);
592        let sixteen = Number::new_from_u8(16, p.byte_order);
593        (a * d * &m2 * &m2 * sixteen.mod_inverse(p).unwrap_or_default()).rem_euclid(p)
594    }
595
596    /// `16 (a^2 + 14 a d + d^2)^3 / (a d (a - d)^4)` modulo `p`.
597    fn j_invariant(&self) -> Number {
598        let (a, d, p) = (&self.a, &self.d, &self.base.p);
599        let t = (a * a + a * d * 14u8 + d * d).rem_euclid(p);
600        let m = (a - d).rem_euclid(p);
601        let m2 = (&m * &m).rem_euclid(p);
602        let den = (a * d * &m2 * &m2).rem_euclid(p);
603        (&t * &t * &t * 16u8 * den.mod_inverse(p).unwrap_or_default()).rem_euclid(p)
604    }
605}