Skip to main content

ec/
point_jacobi_intersection.rs

1//! Affine points on a Jacobi-intersection curve.
2//!
3//! The EFD gives the affine group law for
4//!
5//! $$
6//! s^2 + c^2 = 1 \quad
7//! a s^2 + d^2 = 1.
8//! $$
9//!
10//! We use those formulas directly, with neutral element `(0, 1, 1)` and
11//! negation `-(s, c, d) = (-s, c, d)`.
12use core::fmt;
13use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
14
15use crate::curve_jacobi_intersection::JacobiIntersectionCurve;
16use crate::point_ops::{PointAdd, PointOps};
17use fp::field_ops::FieldOps;
18
19/// An affine point $(s, c, d)$ on a Jacobi intersection.
20#[derive(Debug, Clone, Copy)]
21pub struct JacobiIntersectionPoint<F: FieldOps> {
22    /// The coordinate `s` of the point
23    pub s: F,
24    /// The coordinate `c` of the point
25    pub c: F,
26    /// The coordinate `d` of the point
27    pub d: F,
28}
29
30
31impl<F> fmt::Display for JacobiIntersectionPoint<F>
32where
33    F: FieldOps + fmt::Display,
34{
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        if self.is_identity() {
37            if f.alternate() {
38                write!(f, "JacobiIntersectionPoint {{ O = (0,1,1) }}")
39            } else {
40                write!(f, "O")
41            }
42        } else if f.alternate() {
43            write!(
44                f,
45                "JacobiIntersectionPoint {{ s = {}, c = {}, d = {} }}",
46                self.s, self.c, self.d
47            )
48        } else {
49            write!(f, "({}, {}, {})", self.s, self.c, self.d)
50        }
51    }
52}
53
54impl<F: FieldOps> PartialEq for JacobiIntersectionPoint<F> {
55    fn eq(&self, other: &Self) -> bool {
56        self.s == other.s && self.c == other.c && self.d == other.d
57    }
58}
59
60impl<F: FieldOps> Eq for JacobiIntersectionPoint<F> {}
61
62impl<F: FieldOps> JacobiIntersectionPoint<F> {
63    /// Creates a new point on a Jacobi intersection
64    ///
65    /// The point should satisfy
66    /// $$
67    /// s^2 + c^2 = 1 \quad
68    /// a s^2 + d^2 = 1.
69    /// $$
70    ///
71    /// # Arguments
72    ///
73    /// * `s` - Element of `F` (type: F)
74    /// * `c` - Element of `F` (type: F)
75    /// * `d` - Element of `F` (type: F)
76    ///
77    /// # Returns
78    ///
79    /// Returns the point on the Jacobi intersection (type: Self)
80    pub fn new(s: F, c: F, d: F) -> Self {
81        Self { s, c, d }
82    }
83
84    /// The neutral element $(0, 1, 1)$.
85    pub fn identity() -> Self {
86        Self {
87            s: F::zero(),
88            c: F::one(),
89            d: F::one(),
90        }
91    }
92
93    /// Checks if the point is the indentity point
94    ///
95    /// # Arguments
96    ///
97    /// * `&self` - A point on the curve (type: self)
98    ///
99    /// # Returns
100    ///
101    /// True if and only if it is $(0,1,1)$ (type: bool)
102    pub fn is_identity(&self) -> bool {
103        self.s == F::zero() && self.c == F::one() && self.d == F::one()
104    }
105}
106
107impl<F> ConditionallySelectable for JacobiIntersectionPoint<F>
108where
109    F: FieldOps + Copy,
110{
111    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
112        Self {
113            s: F::conditional_select(&a.s, &b.s, choice),
114            c: F::conditional_select(&a.c, &b.c, choice),
115            d: F::conditional_select(&a.d, &b.d, choice),
116        }
117    }
118
119    fn conditional_assign(&mut self, other: &Self, choice: Choice) {
120        self.s.conditional_assign(&other.s, choice);
121        self.c.conditional_assign(&other.c, choice);
122        self.d.conditional_assign(&other.d, choice);
123    }
124
125    fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
126        F::conditional_swap(&mut a.s, &mut b.s, choice);
127        F::conditional_swap(&mut a.c, &mut b.c, choice);
128        F::conditional_swap(&mut a.d, &mut b.d, choice);
129    }
130}
131
132impl<F> ConstantTimeEq for JacobiIntersectionPoint<F>
133where
134    F: FieldOps + Copy + ConstantTimeEq,
135{
136    fn ct_eq(&self, other: &Self) -> Choice {
137        self.s.ct_eq(&other.s) & self.c.ct_eq(&other.c) & self.d.ct_eq(&other.d)
138    }
139
140    fn ct_ne(&self, other: &Self) -> Choice {
141        !self.ct_eq(other)
142    }
143}
144
145impl<F: FieldOps> JacobiIntersectionPoint<F> {
146    /// Negation: `-(s, c, d) = (-s, c, d)`.
147    pub fn negate(&self, _curve: &JacobiIntersectionCurve<F>) -> Self {
148        Self::new(-self.s, self.c, self.d)
149    }
150
151    /// Affine addition formulas from the EFD:
152    ///
153    /// ```text
154    /// s₃ = (c₂ s₁ d₂ + d₁ s₂ c₁)/(c₂² + (d₁ s₂)²)
155    /// c₃ = (c₂ c₁ - d₁ s₂ s₁ d₂)/(c₂² + (d₁ s₂)²)
156    /// d₃ = (d₁ d₂ - a s₁ c₁ s₂ c₂)/(c₂² + (d₁ s₂)²).
157    /// ```
158    ///
159    pub fn add(&self, other: &Self, curve: &JacobiIntersectionCurve<F>) -> Self {
160        let d1s2 = self.d * other.s;
161        let denom = <F as FieldOps>::square(&other.c) + <F as FieldOps>::square(&d1s2);
162        let denom_inv = denom
163            .invert()
164            .into_option()
165            .expect("Jacobi-intersection addition denominator vanished");
166
167        let s3 = (other.c * self.s * other.d + self.d * other.s * self.c) * denom_inv;
168        let c3 = (other.c * self.c - self.d * other.s * self.s * other.d) * denom_inv;
169        let d3 = (self.d * other.d - curve.a * self.s * self.c * other.s * other.c) * denom_inv;
170
171        Self::new(s3, c3, d3)
172    }
173
174    /// Affine doubling formulas from the EFD:
175    ///
176    /// ```text
177    /// s₃ = 2c s d / (c² + (d s)²)
178    /// c₃ = (c² - d² s²) / (c² + (d s)²)
179    /// d₃ = (d² - a s² c²) / (c² + (d s)²).
180    /// ```
181    ///
182    pub fn double(&self, curve: &JacobiIntersectionCurve<F>) -> Self {
183        let ds = self.d * self.s;
184        let denom = <F as FieldOps>::square(&self.c) + <F as FieldOps>::square(&ds);
185        let denom_inv = denom
186            .invert()
187            .into_option()
188            .expect("Jacobi-intersection doubling denominator vanished");
189
190        let two = <F as FieldOps>::double(&F::one());
191        let s_sq = <F as FieldOps>::square(&self.s);
192        let c_sq = <F as FieldOps>::square(&self.c);
193        let d_sq = <F as FieldOps>::square(&self.d);
194
195        let s3 = (two * self.c * self.s * self.d) * denom_inv;
196        let c3 = (c_sq - d_sq * s_sq) * denom_inv;
197        let d3 = (d_sq - curve.a * s_sq * c_sq) * denom_inv;
198
199        Self::new(s3, c3, d3)
200    }
201
202    /// Scalar multiplication on the curve
203    ///
204    /// Multiplies a point by an integers
205    ///
206    /// # Arguments
207    ///
208    /// * `&self` - Point on the curve (type: `Self`)
209    /// * `k` - An integer represented as a vec of `u64` (type: `&[u64]`)
210    /// * `curve` - The curve on which `self` lies (type: `&JacobiIntersectionCurve<F>`)
211    ///
212    /// # Returns
213    ///
214    /// The point `k * self` (type: Self)
215    pub fn scalar_mul(&self, k: &[u64], curve: &JacobiIntersectionCurve<F>) -> Self {
216        let mut result = Self::identity();
217
218        for &limb in k.iter().rev() {
219            for bit in (0..64).rev() {
220                let doubled = result.double(curve);
221                let added = doubled.add(self, curve);
222                let choice = Choice::from(((limb >> bit) & 1) as u8);
223                result = Self::conditional_select(&doubled, &added, choice);
224            }
225        }
226
227        result
228    }
229}
230
231impl<F: FieldOps> PointOps for JacobiIntersectionPoint<F> {
232    type BaseField = F;
233    type Curve = JacobiIntersectionCurve<F>;
234
235    fn identity(_curve: &Self::Curve) -> Self {
236        JacobiIntersectionPoint::<F>::identity()
237    }
238
239    fn is_identity(&self) -> bool {
240        JacobiIntersectionPoint::<F>::is_identity(self)
241    }
242
243    fn negate(&self, curve: &Self::Curve) -> Self {
244        JacobiIntersectionPoint::<F>::negate(self, curve)
245    }
246
247    fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self {
248        JacobiIntersectionPoint::<F>::scalar_mul(self, k, curve)
249    }
250}
251
252impl<F: FieldOps> PointAdd for JacobiIntersectionPoint<F> {
253    fn add(&self, other: &Self, curve: &Self::Curve) -> Self {
254        JacobiIntersectionPoint::<F>::add(self, other, curve)
255    }
256}