primeorder/
point_arithmetic.rs

1//! Point arithmetic implementation optimised for different curve equations
2//!
3//! Support for formulas specialized to the short Weierstrass equation's
4//! 𝒂-coefficient.
5
6use elliptic_curve::{subtle::ConditionallySelectable, Field};
7
8use crate::{AffinePoint, PrimeCurveParams, ProjectivePoint};
9
10mod sealed {
11    use crate::{AffinePoint, PrimeCurveParams, ProjectivePoint};
12
13    /// Elliptic point arithmetic implementation
14    ///
15    /// Provides implementation of point arithmetic (point addition, point doubling) which
16    /// might be optimized for the curve.
17    pub trait PointArithmetic<C: PrimeCurveParams> {
18        /// Returns `lhs + rhs`
19        fn add(lhs: &ProjectivePoint<C>, rhs: &ProjectivePoint<C>) -> ProjectivePoint<C>;
20
21        /// Returns `lhs + rhs`
22        fn add_mixed(lhs: &ProjectivePoint<C>, rhs: &AffinePoint<C>) -> ProjectivePoint<C>;
23
24        /// Returns `point + point`
25        fn double(point: &ProjectivePoint<C>) -> ProjectivePoint<C>;
26    }
27}
28
29/// Allow crate-local visibility
30pub(crate) use sealed::PointArithmetic;
31
32/// The 𝒂-coefficient of the short Weierstrass equation does not have specific
33/// properties which allow for an optimized implementation.
34pub struct EquationAIsGeneric {}
35
36impl<C: PrimeCurveParams> PointArithmetic<C> for EquationAIsGeneric {
37    /// Implements complete addition for any curve
38    ///
39    /// Implements the complete addition formula from [Renes-Costello-Batina 2015]
40    /// (Algorithm 1). The comments after each line indicate which algorithm steps
41    /// are being performed.
42    ///
43    /// [Renes-Costello-Batina 2015]: https://eprint.iacr.org/2015/1060
44    fn add(lhs: &ProjectivePoint<C>, rhs: &ProjectivePoint<C>) -> ProjectivePoint<C> {
45        let b3 = C::FieldElement::from(3) * C::EQUATION_B;
46
47        let t0 = lhs.x * rhs.x; // 1
48        let t1 = lhs.y * rhs.y; // 2
49        let t2 = lhs.z * rhs.z; // 3
50        let t3 = lhs.x + lhs.y; // 4
51        let t4 = rhs.x + rhs.y; // 5
52        let t3 = t3 * t4; // 6
53        let t4 = t0 + t1; // 7
54        let t3 = t3 - t4; // 8
55        let t4 = lhs.x + lhs.z; // 9
56        let t5 = rhs.x + rhs.z; // 10
57        let t4 = t4 * t5; // 11
58        let t5 = t0 + t2; // 12
59        let t4 = t4 - t5; // 13
60        let t5 = lhs.y + lhs.z; // 14
61        let x3 = rhs.y + rhs.z; // 15
62        let t5 = t5 * x3; // 16
63        let x3 = t1 + t2; // 17
64        let t5 = t5 - x3; // 18
65        let z3 = C::EQUATION_A * t4; // 19
66        let x3 = b3 * t2; // 20
67        let z3 = x3 + z3; // 21
68        let x3 = t1 - z3; // 22
69        let z3 = t1 + z3; // 23
70        let y3 = x3 * z3; // 24
71        let t1 = t0 + t0; // 25
72        let t1 = t1 + t0; // 26
73        let t2 = C::EQUATION_A * t2; // 27
74        let t4 = b3 * t4; // 28
75        let t1 = t1 + t2; // 29
76        let t2 = t0 - t2; // 30
77        let t2 = C::EQUATION_A * t2; // 31
78        let t4 = t4 + t2; // 32
79        let t0 = t1 * t4; // 33
80        let y3 = y3 + t0; // 34
81        let t0 = t5 * t4; // 35
82        let x3 = t3 * x3; // 36
83        let x3 = x3 - t0; // 37
84        let t0 = t3 * t1; // 38
85        let z3 = t5 * z3; // 39
86        let z3 = z3 + t0; // 40
87
88        ProjectivePoint {
89            x: x3,
90            y: y3,
91            z: z3,
92        }
93    }
94
95    /// Implements complete mixed addition for curves with any `a`
96    ///
97    /// Implements the complete mixed addition formula from [Renes-Costello-Batina 2015]
98    /// (Algorithm 2). The comments after each line indicate which algorithm
99    /// steps are being performed.
100    ///
101    /// [Renes-Costello-Batina 2015]: https://eprint.iacr.org/2015/1060
102    fn add_mixed(lhs: &ProjectivePoint<C>, rhs: &AffinePoint<C>) -> ProjectivePoint<C> {
103        let b3 = C::EQUATION_B * C::FieldElement::from(3);
104
105        let t0 = lhs.x * rhs.x; // 1
106        let t1 = lhs.y * rhs.y; // 2
107        let t3 = rhs.x + rhs.y; // 3
108        let t4 = lhs.x + lhs.y; // 4
109        let t3 = t3 * t4; // 5
110        let t4 = t0 + t1; // 6
111        let t3 = t3 - t4; // 7
112        let t4 = rhs.x * lhs.z; // 8
113        let t4 = t4 + lhs.x; // 9
114        let t5 = rhs.y * lhs.z; // 10
115        let t5 = t5 + lhs.y; // 11
116        let z3 = C::EQUATION_A * t4; // 12
117        let x3 = b3 * lhs.z; // 13
118        let z3 = x3 + z3; // 14
119        let x3 = t1 - z3; // 15
120        let z3 = t1 + z3; // 16
121        let y3 = x3 * z3; // 17
122        let t1 = t0 + t0; // 18
123        let t1 = t1 + t0; // 19
124        let t2 = C::EQUATION_A * lhs.z; // 20
125        let t4 = b3 * t4; // 21
126        let t1 = t1 + t2; // 22
127        let t2 = t0 - t2; // 23
128        let t2 = C::EQUATION_A * t2; // 24
129        let t4 = t4 + t2; // 25
130        let t0 = t1 * t4; // 26
131        let y3 = y3 + t0; // 27
132        let t0 = t5 * t4; // 28
133        let x3 = t3 * x3; // 29
134        let x3 = x3 - t0; // 30
135        let t0 = t3 * t1; // 31
136        let z3 = t5 * z3; // 32
137        let z3 = z3 + t0; // 33
138
139        let mut ret = ProjectivePoint {
140            x: x3,
141            y: y3,
142            z: z3,
143        };
144        ret.conditional_assign(lhs, rhs.is_identity());
145        ret
146    }
147
148    /// Implements point doubling for curves with any `a`
149    ///
150    /// Implements the exception-free point doubling formula from [Renes-Costello-Batina 2015]
151    /// (Algorithm 3). The comments after each line indicate which algorithm
152    /// steps are being performed.
153    ///
154    /// [Renes-Costello-Batina 2015]: https://eprint.iacr.org/2015/1060
155    fn double(point: &ProjectivePoint<C>) -> ProjectivePoint<C> {
156        let b3 = C::EQUATION_B * C::FieldElement::from(3);
157
158        let t0 = point.x * point.x; // 1
159        let t1 = point.y * point.y; // 2
160        let t2 = point.z * point.z; // 3
161        let t3 = point.x * point.y; // 4
162        let t3 = t3 + t3; // 5
163        let z3 = point.x * point.z; // 6
164        let z3 = z3 + z3; // 7
165        let x3 = C::EQUATION_A * z3; // 8
166        let y3 = b3 * t2; // 9
167        let y3 = x3 + y3; // 10
168        let x3 = t1 - y3; // 11
169        let y3 = t1 + y3; // 12
170        let y3 = x3 * y3; // 13
171        let x3 = t3 * x3; // 14
172        let z3 = b3 * z3; // 15
173        let t2 = C::EQUATION_A * t2; // 16
174        let t3 = t0 - t2; // 17
175        let t3 = C::EQUATION_A * t3; // 18
176        let t3 = t3 + z3; // 19
177        let z3 = t0 + t0; // 20
178        let t0 = z3 + t0; // 21
179        let t0 = t0 + t2; // 22
180        let t0 = t0 * t3; // 23
181        let y3 = y3 + t0; // 24
182        let t2 = point.y * point.z; // 25
183        let t2 = t2 + t2; // 26
184        let t0 = t2 * t3; // 27
185        let x3 = x3 - t0; // 28
186        let z3 = t2 * t1; // 29
187        let z3 = z3 + z3; // 30
188        let z3 = z3 + z3; // 31
189
190        ProjectivePoint {
191            x: x3,
192            y: y3,
193            z: z3,
194        }
195    }
196}
197
198/// The 𝒂-coefficient of the short Weierstrass equation is -3.
199pub struct EquationAIsMinusThree {}
200
201impl<C: PrimeCurveParams> PointArithmetic<C> for EquationAIsMinusThree {
202    /// Implements complete addition for curves with `a = -3`
203    ///
204    /// Implements the complete addition formula from [Renes-Costello-Batina 2015]
205    /// (Algorithm 4). The comments after each line indicate which algorithm steps
206    /// are being performed.
207    ///
208    /// [Renes-Costello-Batina 2015]: https://eprint.iacr.org/2015/1060
209    fn add(lhs: &ProjectivePoint<C>, rhs: &ProjectivePoint<C>) -> ProjectivePoint<C> {
210        debug_assert_eq!(
211            C::EQUATION_A,
212            -C::FieldElement::from(3),
213            "this implementation is only valid for C::EQUATION_A = -3"
214        );
215
216        let xx = lhs.x * rhs.x; // 1
217        let yy = lhs.y * rhs.y; // 2
218        let zz = lhs.z * rhs.z; // 3
219        let xy_pairs = ((lhs.x + lhs.y) * (rhs.x + rhs.y)) - (xx + yy); // 4, 5, 6, 7, 8
220        let yz_pairs = ((lhs.y + lhs.z) * (rhs.y + rhs.z)) - (yy + zz); // 9, 10, 11, 12, 13
221        let xz_pairs = ((lhs.x + lhs.z) * (rhs.x + rhs.z)) - (xx + zz); // 14, 15, 16, 17, 18
222
223        let bzz_part = xz_pairs - (C::EQUATION_B * zz); // 19, 20
224        let bzz3_part = bzz_part.double() + bzz_part; // 21, 22
225        let yy_m_bzz3 = yy - bzz3_part; // 23
226        let yy_p_bzz3 = yy + bzz3_part; // 24
227
228        let zz3 = zz.double() + zz; // 26, 27
229        let bxz_part = (C::EQUATION_B * xz_pairs) - (zz3 + xx); // 25, 28, 29
230        let bxz3_part = bxz_part.double() + bxz_part; // 30, 31
231        let xx3_m_zz3 = xx.double() + xx - zz3; // 32, 33, 34
232
233        ProjectivePoint {
234            x: (yy_p_bzz3 * xy_pairs) - (yz_pairs * bxz3_part), // 35, 39, 40
235            y: (yy_p_bzz3 * yy_m_bzz3) + (xx3_m_zz3 * bxz3_part), // 36, 37, 38
236            z: (yy_m_bzz3 * yz_pairs) + (xy_pairs * xx3_m_zz3), // 41, 42, 43
237        }
238    }
239
240    /// Implements complete mixed addition for curves with `a = -3`
241    ///
242    /// Implements the complete mixed addition formula from [Renes-Costello-Batina 2015]
243    /// (Algorithm 5). The comments after each line indicate which algorithm
244    /// steps are being performed.
245    ///
246    /// [Renes-Costello-Batina 2015]: https://eprint.iacr.org/2015/1060
247    fn add_mixed(lhs: &ProjectivePoint<C>, rhs: &AffinePoint<C>) -> ProjectivePoint<C> {
248        debug_assert_eq!(
249            C::EQUATION_A,
250            -C::FieldElement::from(3),
251            "this implementation is only valid for C::EQUATION_A = -3"
252        );
253
254        let xx = lhs.x * rhs.x; // 1
255        let yy = lhs.y * rhs.y; // 2
256        let xy_pairs = ((lhs.x + lhs.y) * (rhs.x + rhs.y)) - (xx + yy); // 3, 4, 5, 6, 7
257        let yz_pairs = (rhs.y * lhs.z) + lhs.y; // 8, 9 (t4)
258        let xz_pairs = (rhs.x * lhs.z) + lhs.x; // 10, 11 (y3)
259
260        let bz_part = xz_pairs - (C::EQUATION_B * lhs.z); // 12, 13
261        let bz3_part = bz_part.double() + bz_part; // 14, 15
262        let yy_m_bzz3 = yy - bz3_part; // 16
263        let yy_p_bzz3 = yy + bz3_part; // 17
264
265        let z3 = lhs.z.double() + lhs.z; // 19, 20
266        let bxz_part = (C::EQUATION_B * xz_pairs) - (z3 + xx); // 18, 21, 22
267        let bxz3_part = bxz_part.double() + bxz_part; // 23, 24
268        let xx3_m_zz3 = xx.double() + xx - z3; // 25, 26, 27
269
270        let mut ret = ProjectivePoint {
271            x: (yy_p_bzz3 * xy_pairs) - (yz_pairs * bxz3_part), // 28, 32, 33
272            y: (yy_p_bzz3 * yy_m_bzz3) + (xx3_m_zz3 * bxz3_part), // 29, 30, 31
273            z: (yy_m_bzz3 * yz_pairs) + (xy_pairs * xx3_m_zz3), // 34, 35, 36
274        };
275        ret.conditional_assign(lhs, rhs.is_identity());
276        ret
277    }
278
279    /// Implements point doubling for curves with `a = -3`
280    ///
281    /// Implements the exception-free point doubling formula from [Renes-Costello-Batina 2015]
282    /// (Algorithm 6). The comments after each line indicate which algorithm
283    /// steps are being performed.
284    ///
285    /// [Renes-Costello-Batina 2015]: https://eprint.iacr.org/2015/1060
286    fn double(point: &ProjectivePoint<C>) -> ProjectivePoint<C> {
287        debug_assert_eq!(
288            C::EQUATION_A,
289            -C::FieldElement::from(3),
290            "this implementation is only valid for C::EQUATION_A = -3"
291        );
292
293        let xx = point.x.square(); // 1
294        let yy = point.y.square(); // 2
295        let zz = point.z.square(); // 3
296        let xy2 = (point.x * point.y).double(); // 4, 5
297        let xz2 = (point.x * point.z).double(); // 6, 7
298
299        let bzz_part = (C::EQUATION_B * zz) - xz2; // 8, 9
300        let bzz3_part = bzz_part.double() + bzz_part; // 10, 11
301        let yy_m_bzz3 = yy - bzz3_part; // 12
302        let yy_p_bzz3 = yy + bzz3_part; // 13
303        let y_frag = yy_p_bzz3 * yy_m_bzz3; // 14
304        let x_frag = yy_m_bzz3 * xy2; // 15
305
306        let zz3 = zz.double() + zz; // 16, 17
307        let bxz2_part = (C::EQUATION_B * xz2) - (zz3 + xx); // 18, 19, 20
308        let bxz6_part = bxz2_part.double() + bxz2_part; // 21, 22
309        let xx3_m_zz3 = xx.double() + xx - zz3; // 23, 24, 25
310
311        let y = y_frag + (xx3_m_zz3 * bxz6_part); // 26, 27
312        let yz2 = (point.y * point.z).double(); // 28, 29
313        let x = x_frag - (bxz6_part * yz2); // 30, 31
314        let z = (yz2 * yy).double().double(); // 32, 33, 34
315
316        ProjectivePoint { x, y, z }
317    }
318}