Skip to content

Commit 01485d9

Browse files
committed
[WIP] primeorder: Double and CurveEquationA* marker traits
Extracts a `Double` trait (which should probably go in `elliptic_curve::ops`) which allows for multiple overlapping impls based on specific properties of the curve's a-coefficient. The previous inherent `double()` method is now gated on `CurveEquationAIsMinus3`.
1 parent 2e79f41 commit 01485d9

File tree

4 files changed

+105
-40
lines changed

4 files changed

+105
-40
lines changed

primeorder/src/affine.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
#![allow(clippy::op_ref)]
44

5-
use crate::{PrimeCurveParams, ProjectivePoint};
5+
use crate::{Double, PrimeCurveParams, ProjectivePoint};
66
use core::{
77
borrow::Borrow,
88
ops::{Mul, Neg},
@@ -283,6 +283,7 @@ where
283283
C: PrimeCurveParams,
284284
FieldBytes<C>: Copy,
285285
FieldSize<C>: ModulusSize,
286+
ProjectivePoint<C>: Double,
286287
CompressedPoint<C>: Copy,
287288
<UncompressedPointSize<C> as ArrayLength<u8>>::ArrayType: Copy,
288289
{
@@ -405,6 +406,7 @@ impl<C, S> Mul<S> for AffinePoint<C>
405406
where
406407
C: PrimeCurveParams,
407408
S: Borrow<Scalar<C>>,
409+
ProjectivePoint<C>: Double,
408410
{
409411
type Output = ProjectivePoint<C>;
410412

primeorder/src/equation_a.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//! Support for formulas specialized to the short Weierstrass equation's
2+
//! 𝒂-coefficient.
3+
//!
4+
//! This module is largely a workaround for things which should be possible
5+
//! to implement more elegantly with future Rust features like
6+
//! `generic_const_exprs` and `impl const Trait`.
7+
//!
8+
//! In absence of such features, we define traits that capture properties of
9+
//! the 𝒂-coefficient which could potentially be written as const expressions
10+
//! on `PrimeCurveParams::EQUATION_A` in the future (including ones which
11+
//! could be used as trait bounds).
12+
13+
use super::PrimeCurveParams;
14+
15+
/// The 𝒂-coefficient of the short Weierstrass equation is 0.
16+
pub trait CurveEquationAIsZero: PrimeCurveParams {}
17+
18+
/// The 𝒂-coefficient of the short Weierstrass equation is -3.
19+
pub trait CurveEquationAIsMinusThree: PrimeCurveParams {}
20+
21+
/// The 𝒂-coefficient of the short Weierstrass equation does not have specific
22+
/// properties which allow for an optimized implementation.
23+
pub trait CurveEquationAIsGeneric: PrimeCurveParams {}

primeorder/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@
99
#![doc = include_str!("../README.md")]
1010

1111
mod affine;
12+
mod equation_a;
1213
mod field;
1314
mod projective;
1415

15-
pub use crate::{affine::AffinePoint, projective::ProjectivePoint};
16+
pub use crate::{
17+
affine::AffinePoint,
18+
equation_a::{CurveEquationAIsGeneric, CurveEquationAIsMinusThree, CurveEquationAIsZero},
19+
projective::{Double, ProjectivePoint},
20+
};
1621
pub use elliptic_curve::{self, Field, FieldBytes, PrimeCurve, PrimeField};
1722

1823
use elliptic_curve::{AffineArithmetic, ProjectiveArithmetic, ScalarArithmetic};

primeorder/src/projective.rs

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
#![allow(clippy::needless_range_loop, clippy::op_ref)]
44

5-
use crate::{AffinePoint, Field, PrimeCurveParams};
5+
use crate::{AffinePoint, CurveEquationAIsMinusThree, Field, PrimeCurveParams};
66
use core::{
77
borrow::Borrow,
88
iter::Sum,
@@ -136,38 +136,6 @@ where
136136
ret
137137
}
138138

139-
/// Doubles this point.
140-
pub fn double(&self) -> Self {
141-
// We implement the exception-free point doubling formula from
142-
// Renes-Costello-Batina 2015 (Algorithm 6). The comments after each line
143-
// indicate which algorithm steps are being performed.
144-
145-
let xx = self.x.square(); // 1
146-
let yy = self.y.square(); // 2
147-
let zz = self.z.square(); // 3
148-
let xy2 = (self.x * &self.y).double(); // 4, 5
149-
let xz2 = (self.x * &self.z).double(); // 6, 7
150-
151-
let bzz_part = (C::EQUATION_B * &zz) - &xz2; // 8, 9
152-
let bzz3_part = bzz_part.double() + &bzz_part; // 10, 11
153-
let yy_m_bzz3 = yy - &bzz3_part; // 12
154-
let yy_p_bzz3 = yy + &bzz3_part; // 13
155-
let y_frag = yy_p_bzz3 * &yy_m_bzz3; // 14
156-
let x_frag = yy_m_bzz3 * &xy2; // 15
157-
158-
let zz3 = zz.double() + &zz; // 16, 17
159-
let bxz2_part = (C::EQUATION_B * &xz2) - &(zz3 + &xx); // 18, 19, 20
160-
let bxz6_part = bxz2_part.double() + &bxz2_part; // 21, 22
161-
let xx3_m_zz3 = xx.double() + &xx - &zz3; // 23, 24, 25
162-
163-
let y = y_frag + &(xx3_m_zz3 * &bxz6_part); // 26, 27
164-
let yz2 = (self.y * &self.z).double(); // 28, 29
165-
let x = x_frag - &(bxz6_part * &yz2); // 30, 31
166-
let z = (yz2 * &yy).double().double(); // 32, 33, 34
167-
168-
Self { x, y, z }
169-
}
170-
171139
/// Returns `self - other`.
172140
pub fn sub(&self, other: &Self) -> Self {
173141
self.add(&other.neg())
@@ -179,7 +147,10 @@ where
179147
}
180148

181149
/// Returns `[k] self`.
182-
fn mul(&self, k: &Scalar<C>) -> Self {
150+
fn mul(&self, k: &Scalar<C>) -> Self
151+
where
152+
Self: Double,
153+
{
183154
let k = Into::<C::UInt>::into(*k).to_le_byte_array();
184155

185156
let mut pc = [Self::default(); 16];
@@ -188,7 +159,7 @@ where
188159

189160
for i in 2..16 {
190161
pc[i] = if i % 2 == 0 {
191-
pc[i / 2].double()
162+
Double::double(&pc[i / 2])
192163
} else {
193164
pc[i - 1].add(self)
194165
};
@@ -215,16 +186,68 @@ where
215186
break;
216187
}
217188

218-
q = q.double().double().double().double();
189+
q = Double::double(&Double::double(&Double::double(&Double::double(&q))));
219190
pos -= 4;
220191
}
221192

222193
q
223194
}
224195
}
225196

197+
/// Double a point (i.e. add it to itself)
198+
// TODO(tarcieri): extract this into `elliptic_curve::ops`
199+
pub trait Double {
200+
/// Double this point.
201+
fn double(&self) -> Self;
202+
}
203+
204+
impl<C> Double for ProjectivePoint<C>
205+
where
206+
C: PrimeCurveParams + CurveEquationAIsMinusThree,
207+
{
208+
/// We implement the exception-free point doubling formula from
209+
/// Renes-Costello-Batina 2015 (Algorithm 6), for prime order short
210+
/// Weierstrass curves `y² = x³ + ax + b` where `a = -3`.
211+
///
212+
/// The comments after each lines indicate which algorithm steps
213+
/// are being performed.
214+
fn double(&self) -> Self {
215+
debug_assert_eq!(
216+
C::EQUATION_A,
217+
-C::FieldElement::from(3),
218+
"this implementation is only valid for C::EQUATION_A = -3"
219+
);
220+
221+
let xx = self.x.square(); // 1
222+
let yy = self.y.square(); // 2
223+
let zz = self.z.square(); // 3
224+
let xy2 = (self.x * &self.y).double(); // 4, 5
225+
let xz2 = (self.x * &self.z).double(); // 6, 7
226+
227+
let bzz_part = (C::EQUATION_B * &zz) - &xz2; // 8, 9
228+
let bzz3_part = bzz_part.double() + &bzz_part; // 10, 11
229+
let yy_m_bzz3 = yy - &bzz3_part; // 12
230+
let yy_p_bzz3 = yy + &bzz3_part; // 13
231+
let y_frag = yy_p_bzz3 * &yy_m_bzz3; // 14
232+
let x_frag = yy_m_bzz3 * &xy2; // 15
233+
234+
let zz3 = zz.double() + &zz; // 16, 17
235+
let bxz2_part = (C::EQUATION_B * &xz2) - &(zz3 + &xx); // 18, 19, 20
236+
let bxz6_part = bxz2_part.double() + &bxz2_part; // 21, 22
237+
let xx3_m_zz3 = xx.double() + &xx - &zz3; // 23, 24, 25
238+
239+
let y = y_frag + &(xx3_m_zz3 * &bxz6_part); // 26, 27
240+
let yz2 = (self.y * &self.z).double(); // 28, 29
241+
let x = x_frag - &(bxz6_part * &yz2); // 30, 31
242+
let z = (yz2 * &yy).double().double(); // 32, 33, 34
243+
244+
Self { x, y, z }
245+
}
246+
}
247+
226248
impl<C> CofactorGroup for ProjectivePoint<C>
227249
where
250+
Self: Double,
228251
C: PrimeCurveParams,
229252
FieldBytes<C>: Copy,
230253
FieldSize<C>: ModulusSize,
@@ -336,6 +359,7 @@ where
336359

337360
impl<C> Group for ProjectivePoint<C>
338361
where
362+
Self: Double,
339363
C: PrimeCurveParams,
340364
{
341365
type Scalar = Scalar<C>;
@@ -358,7 +382,7 @@ where
358382

359383
#[must_use]
360384
fn double(&self) -> Self {
361-
ProjectivePoint::double(self)
385+
Double::double(self)
362386
}
363387
}
364388

@@ -388,6 +412,7 @@ where
388412

389413
impl<C> group::Curve for ProjectivePoint<C>
390414
where
415+
Self: Double,
391416
C: PrimeCurveParams,
392417
{
393418
type AffineRepr = AffinePoint<C>;
@@ -397,10 +422,16 @@ where
397422
}
398423
}
399424

400-
impl<C> LinearCombination for ProjectivePoint<C> where C: PrimeCurveParams {}
425+
impl<C> LinearCombination for ProjectivePoint<C>
426+
where
427+
Self: Double,
428+
C: PrimeCurveParams,
429+
{
430+
}
401431

402432
impl<C> PrimeGroup for ProjectivePoint<C>
403433
where
434+
Self: Double,
404435
C: PrimeCurveParams,
405436
FieldBytes<C>: Copy,
406437
FieldSize<C>: ModulusSize,
@@ -411,6 +442,7 @@ where
411442

412443
impl<C> PrimeCurve for ProjectivePoint<C>
413444
where
445+
Self: Double,
414446
C: PrimeCurveParams,
415447
FieldBytes<C>: Copy,
416448
FieldSize<C>: ModulusSize,
@@ -691,6 +723,7 @@ where
691723

692724
impl<C, S> Mul<S> for ProjectivePoint<C>
693725
where
726+
Self: Double,
694727
C: PrimeCurveParams,
695728
S: Borrow<Scalar<C>>,
696729
{
@@ -704,6 +737,7 @@ where
704737
impl<C> Mul<&Scalar<C>> for &ProjectivePoint<C>
705738
where
706739
C: PrimeCurveParams,
740+
ProjectivePoint<C>: Double,
707741
{
708742
type Output = ProjectivePoint<C>;
709743

@@ -714,6 +748,7 @@ where
714748

715749
impl<C, S> MulAssign<S> for ProjectivePoint<C>
716750
where
751+
Self: Double,
717752
C: PrimeCurveParams,
718753
S: Borrow<Scalar<C>>,
719754
{

0 commit comments

Comments
 (0)