Skip to content

Commit 6fef10a

Browse files
committed
Fix and optimize scalar multiplication
1 parent 22a5c09 commit 6fef10a

File tree

3 files changed

+75
-45
lines changed

3 files changed

+75
-45
lines changed

ed448-goldilocks/src/montgomery/ops.rs

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use crate::field::FieldElement;
1+
use crate::ProjectiveMontgomeryXpoint;
2+
use crate::field::{ConstMontyType, FieldElement};
23
use core::borrow::Borrow;
34
use core::iter::Sum;
45
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
56
use elliptic_curve::CurveGroup;
6-
use subtle::{Choice, ConditionallySelectable};
7+
use elliptic_curve::bigint::U448;
78

89
use super::{MontgomeryPoint, MontgomeryScalar, ProjectiveMontgomeryPoint};
910

@@ -54,7 +55,7 @@ define_add_variants!(
5455
impl Add<&MontgomeryPoint> for &ProjectiveMontgomeryPoint {
5556
type Output = ProjectiveMontgomeryPoint;
5657

57-
// See Complete Addition Law for Montgomery Curves - Algorithm 1.
58+
// See Complete Addition Law for Montgomery Curves - Algorithm 2.
5859
// With "Trade-Off Technique".
5960
fn add(self, rhs: &MontgomeryPoint) -> ProjectiveMontgomeryPoint {
6061
let (x1, y1, z1) = (self.U, self.V, self.W);
@@ -139,17 +140,9 @@ define_add_assign_variants!(LHS = MontgomeryPoint, RHS = ProjectiveMontgomeryPoi
139140
impl Mul<&MontgomeryScalar> for &ProjectiveMontgomeryPoint {
140141
type Output = ProjectiveMontgomeryPoint;
141142

142-
#[allow(clippy::suspicious_arithmetic_impl)]
143+
#[inline]
143144
fn mul(self, scalar: &MontgomeryScalar) -> ProjectiveMontgomeryPoint {
144-
let mut p = ProjectiveMontgomeryPoint::IDENTITY;
145-
let bits = scalar.bits();
146-
147-
for index in (0..448).rev() {
148-
p = p + p;
149-
p.conditional_assign(&(p + self), Choice::from(bits[index] as u8));
150-
}
151-
152-
p
145+
scalar * self.to_affine()
153146
}
154147
}
155148

@@ -162,9 +155,38 @@ define_mul_variants!(
162155
impl Mul<&MontgomeryPoint> for &MontgomeryScalar {
163156
type Output = ProjectiveMontgomeryPoint;
164157

165-
#[inline]
166-
fn mul(self, point: &MontgomeryPoint) -> ProjectiveMontgomeryPoint {
167-
ProjectiveMontgomeryPoint::from(*point) * self
158+
// Montgomery curves and their arithmetic - Algorithm 6
159+
// https://eprint.iacr.org/2017/212.pdf
160+
fn mul(self, rhs: &MontgomeryPoint) -> ProjectiveMontgomeryPoint {
161+
pub const A2: FieldElement = FieldElement(ConstMontyType::new(&U448::from_u64(312652)));
162+
163+
let MontgomeryPoint { x: xP, y: yP } = rhs;
164+
let (
165+
ProjectiveMontgomeryXpoint { U: xQ, W: zQ },
166+
ProjectiveMontgomeryXpoint { U: xD, W: zD },
167+
) = rhs.to_affine_x().mul_internal(self);
168+
169+
let v1 = xP * zQ;
170+
let v2 = xQ + v1;
171+
let v3 = xQ - v1;
172+
let v3 = v3.square();
173+
let v3 = v3 * xD;
174+
let v1 = A2 * zQ;
175+
let v2 = v2 + v1;
176+
let v4 = xP * xQ;
177+
let v4 = v4 + zQ;
178+
let v2 = v2 * v4;
179+
let v1 = v1 * zQ;
180+
let v2 = v2 - v1;
181+
let v2 = v2 * zD;
182+
let y = v2 - v3;
183+
let v1 = FieldElement::TWO * yP;
184+
let v1 = v1 * zQ;
185+
let v1 = v1 * zD;
186+
let x = v1 * xQ;
187+
let z = v1 * zQ;
188+
189+
ProjectiveMontgomeryPoint { U: x, V: y, W: z }
168190
}
169191
}
170192

ed448-goldilocks/src/montgomery/point.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,11 +424,10 @@ mod tests {
424424
let scalar = MontgomeryScalar::from(200u32);
425425

426426
// Montgomery scalar mul
427-
let montgomery_res = (ProjectiveMontgomeryPoint::GENERATOR * scalar).to_affine();
427+
let montgomery_res = (ProjectiveMontgomeryPoint::GENERATOR * scalar * scalar).to_affine();
428428
// Goldilocks scalar mul
429-
let goldilocks_point = EdwardsPoint::GENERATOR
430-
.scalar_mul(&scalar.to_scalar())
431-
.to_affine();
429+
let goldilocks_point =
430+
(EdwardsPoint::GENERATOR * scalar.to_scalar() * scalar.to_scalar()).to_affine();
432431

433432
assert_eq!(goldilocks_point.to_montgomery(), montgomery_res);
434433
}

ed448-goldilocks/src/montgomery/x.rs

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,16 @@ impl PartialEq for MontgomeryXpoint {
7373
/// A Projective point in Montgomery form
7474
#[derive(Copy, Clone, Debug, Eq)]
7575
pub struct ProjectiveMontgomeryXpoint {
76-
U: FieldElement,
77-
W: FieldElement,
76+
pub(super) U: FieldElement,
77+
pub(super) W: FieldElement,
7878
}
7979

8080
impl Mul<&MontgomeryScalar> for &MontgomeryXpoint {
8181
type Output = MontgomeryXpoint;
8282

8383
#[allow(clippy::suspicious_arithmetic_impl)]
8484
fn mul(self, scalar: &MontgomeryScalar) -> MontgomeryXpoint {
85-
(&self.to_projective() * scalar).to_affine()
85+
self.mul_internal(scalar).0.to_affine()
8686
}
8787
}
8888

@@ -118,6 +118,30 @@ impl MontgomeryXpoint {
118118
self.to_projective().y(sign).to_bytes()
119119
}
120120

121+
pub(super) fn mul_internal(
122+
&self,
123+
scalar: &MontgomeryScalar,
124+
) -> (ProjectiveMontgomeryXpoint, ProjectiveMontgomeryXpoint) {
125+
// Algorithm 8 of Costello-Smith 2017
126+
let mut x0 = ProjectiveMontgomeryXpoint::IDENTITY;
127+
let mut x1 = self.to_projective();
128+
let diff = x1.U;
129+
130+
let bits = scalar.bits();
131+
let mut swap = 0;
132+
for s in (0..448).rev() {
133+
let bit = bits[s] as u8;
134+
let choice: u8 = swap ^ bit;
135+
136+
ProjectiveMontgomeryXpoint::conditional_swap(&mut x0, &mut x1, Choice::from(choice));
137+
differential_add_and_double(&mut x0, &mut x1, &diff);
138+
139+
swap = bit;
140+
}
141+
142+
(x0, x1)
143+
}
144+
121145
/// Convert the point to its form including the y-coordinate
122146
pub fn to_projective(&self) -> ProjectiveMontgomeryXpoint {
123147
ProjectiveMontgomeryXpoint {
@@ -166,25 +190,8 @@ impl PartialEq for ProjectiveMontgomeryXpoint {
166190
impl Mul<&MontgomeryScalar> for &ProjectiveMontgomeryXpoint {
167191
type Output = ProjectiveMontgomeryXpoint;
168192

169-
#[allow(clippy::suspicious_arithmetic_impl)]
170193
fn mul(self, scalar: &MontgomeryScalar) -> ProjectiveMontgomeryXpoint {
171-
// Algorithm 8 of Costello-Smith 2017
172-
let mut x0 = ProjectiveMontgomeryXpoint::IDENTITY;
173-
let mut x1 = *self;
174-
175-
let bits = scalar.bits();
176-
let mut swap = 0;
177-
for s in (0..448).rev() {
178-
let bit = bits[s] as u8;
179-
let choice: u8 = swap ^ bit;
180-
181-
ProjectiveMontgomeryXpoint::conditional_swap(&mut x0, &mut x1, Choice::from(choice));
182-
differential_add_and_double(&mut x0, &mut x1, &self.U);
183-
184-
swap = bit;
185-
}
186-
187-
x0
194+
self.to_affine().mul_internal(scalar).0
188195
}
189196
}
190197

@@ -196,6 +203,8 @@ impl Mul<&ProjectiveMontgomeryXpoint> for &MontgomeryScalar {
196203
}
197204
}
198205

206+
// (1987 Montgomery) Speeding the Pollard and elliptic curve methods of factorization
207+
// fifth and sixth displays, plus common-subexpression elimination, plus assumption Z1=1
199208
fn differential_add_and_double(
200209
P: &mut ProjectiveMontgomeryXpoint,
201210
Q: &mut ProjectiveMontgomeryXpoint,
@@ -371,11 +380,11 @@ mod tests {
371380
let scalar = MontgomeryScalar::from(200u32);
372381

373382
// Montgomery scalar mul
374-
let montgomery_res = (&ProjectiveMontgomeryXpoint::GENERATOR * &scalar).to_affine();
383+
let montgomery_res =
384+
(&(&ProjectiveMontgomeryXpoint::GENERATOR * &scalar) * &scalar).to_affine();
375385
// Goldilocks scalar mul
376-
let goldilocks_point = EdwardsPoint::GENERATOR
377-
.scalar_mul(&scalar.to_scalar())
378-
.to_affine();
386+
let goldilocks_point =
387+
(EdwardsPoint::GENERATOR * scalar.to_scalar() * scalar.to_scalar()).to_affine();
379388

380389
assert_eq!(goldilocks_point.to_montgomery_x(), montgomery_res);
381390
}

0 commit comments

Comments
 (0)