Skip to main content

ec/
curve_jacobi_intersection.rs

1//! Elliptic curve definition in Jacobi intersection form.
2//!
3//! # Equation
4//!
5//! $$
6//! s^2 + c^2 = 1,
7//! \quad
8//! a s^2 + d^2 = 1
9//! $$
10//!
11//! over a field $F$ with $\mathrm{char}(F) \ne 2$.
12//!
13//! The EFD records that this model is birationally equivalent to the Weierstrass
14//! curve
15//!
16//! ```text
17//! y² = x³ + (2-a)x² + (1-a)x.
18//! ```
19//!
20use core::fmt;
21use fp::field_ops::{FieldOps, FieldRandom};
22
23use crate::curve_ops::Curve;
24use crate::curve_weierstrass::WeierstrassCurve;
25use crate::point_jacobi_intersection::JacobiIntersectionPoint;
26
27/// A Jacobi-intersection curve.
28///
29/// With the equation
30/// $$
31/// s^2 + c^2 = 1,
32/// \quad
33/// a s^2 + d^2 = 1
34/// $$
35/// or
36/// $$
37/// y^2 = x^3 + (2 - a)x^2 + (1 - a)x.
38/// $$
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct JacobiIntersectionCurve<F: FieldOps> {
41    /// The variable a in the definition of the curve
42    pub a: F,
43}
44
45impl<F> fmt::Display for JacobiIntersectionCurve<F>
46where
47    F: FieldOps + fmt::Display,
48{
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        if f.alternate() {
51            write!(
52                f,
53                "JacobiIntersectionCurve {{\n  s^2 + c^2 = 1\n  a s^2 + d^2 = 1\n  a = {}\n}}",
54                self.a
55            )
56        } else {
57            write!(f, "s^2 + c^2 = 1, {} s^2 + d^2 = 1", self.a)
58        }
59    }
60}
61
62impl<F: FieldOps + FieldRandom> JacobiIntersectionCurve<F> {
63    /// Construct a Jacobi intersection from its parameter `a`.
64    pub fn new(a: F) -> Self {
65        assert!(
66            F::characteristic()[0] != 2,
67            "Jacobi intersections require char(F) != 2"
68        );
69        assert!(Self::is_smooth(&a), "singular Jacobi intersection");
70        Self { a }
71    }
72
73    /// Smoothness requirement: `a != 0` and `a != 1`.
74    pub fn is_smooth(a: &F) -> bool {
75        *a != F::zero() && *a != F::one()
76    }
77
78    /// Check both defining quadrics.
79    pub fn contains(&self, s: &F, c: &F, d: &F) -> bool {
80        let s2 = <F as FieldOps>::square(s);
81        let c2 = <F as FieldOps>::square(c);
82        let d2 = <F as FieldOps>::square(d);
83
84        s2 + c2 == F::one() && self.a * s2 + d2 == F::one()
85    }
86
87    /// Returns the corresponding invariant `a` (not the a-invariants
88    /// of the Jacobian)
89    pub fn a_invariants(&self) -> [F; 1] {
90        [self.a]
91    }
92
93    /// Sample a random affine point on this Jacobi-intersection curve using the
94    /// provided RNG.
95    ///
96    /// The method repeatedly samples `s` and then solves the defining quadrics
97    /// for `c` and `d` by square-root extraction, returning a point
98    /// `(s, c, d)` on the curve.
99    pub fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> JacobiIntersectionPoint<F> {
100        loop {
101            let s = F::random(rng);
102            let s2 = <F as FieldOps>::square(&s);
103
104            let c2 = F::one() - s2;
105            let d2 = F::one() - self.a * s2;
106
107            if let (Some(c), Some(d)) = (c2.sqrt().into_option(), d2.sqrt().into_option()) {
108                let p = JacobiIntersectionPoint::new(s, c, d);
109                debug_assert!(self.is_on_curve(&p));
110                return p;
111            }
112        }
113    }
114
115    /// Birationally equivalent Weierstrass model
116    /// $y^2 = x^3 + (2-a)x^2 + (1-a)x$.
117    pub fn to_weierstrass_curve(&self) -> WeierstrassCurve<F> {
118        let two = <F as FieldOps>::double(&F::one());
119        WeierstrassCurve::new(
120            F::zero(),
121            two - self.a,
122            F::zero(),
123            F::one() - self.a,
124            F::zero(),
125        )
126    }
127}
128
129impl<F: FieldOps + FieldRandom> Curve for JacobiIntersectionCurve<F> {
130    type BaseField = F;
131    type Point = JacobiIntersectionPoint<F>;
132
133    fn is_on_curve(&self, point: &Self::Point) -> bool {
134        self.contains(&point.s, &point.c, &point.d)
135    }
136
137    fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point {
138        JacobiIntersectionCurve::random_point(self, rng)
139    }
140
141    fn j_invariant(&self) -> F {
142        self.to_weierstrass_curve().j_invariant()
143    }
144
145    fn a_invariants(&self) -> Vec<Self::BaseField> {
146        JacobiIntersectionCurve::a_invariants(self).to_vec()
147    }
148}