Skip to content

Commit f62cc79

Browse files
committed
Not every full-coordinate Montgomery point is valid
1 parent 3746ef6 commit f62cc79

File tree

3 files changed

+36
-26
lines changed

3 files changed

+36
-26
lines changed

ed448-goldilocks/src/edwards/extended.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,8 +1025,8 @@ mod tests {
10251025
ProjectiveMontgomeryXpoint::encode::<ExpandMsgXof<Shake256>>(&[msg], &[DST])
10261026
.unwrap()
10271027
.to_affine();
1028-
let conv_p1 = conv_p.to_edwards(Choice::from(0));
1029-
let conv_p2 = conv_p.to_edwards(Choice::from(1));
1028+
let conv_p1 = conv_p.to_edwards(Choice::from(0)).unwrap();
1029+
let conv_p2 = conv_p.to_edwards(Choice::from(1)).unwrap();
10301030
assert!(conv_p1.x == p.x || conv_p2.x == p.x);
10311031
assert!(conv_p1.y == p.y || conv_p2.y == p.y);
10321032

ed448-goldilocks/src/montgomery/point.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ impl AffineCoordinates for AffineMontgomeryPoint {
183183

184184
impl DecompressPoint<Curve448> for AffineMontgomeryPoint {
185185
fn decompress(x: &FieldBytes<Curve448>, y_is_odd: Choice) -> CtOption<Self> {
186-
FieldElement::from_repr(&x.0).map(|_| MontgomeryXpoint(x.0).to_extended(y_is_odd))
186+
FieldElement::from_repr(&x.0).and_then(|_| MontgomeryXpoint(x.0).to_extended(y_is_odd))
187187
}
188188
}
189189

@@ -432,7 +432,7 @@ impl GroupEncoding for ProjectiveMontgomeryPoint {
432432
let sign = bytes[0] & 1;
433433
bytes[0] &= 0xfe;
434434

435-
FieldElement::from_repr(&bytes).map(|U| {
435+
FieldElement::from_repr(&bytes).and_then(|U| {
436436
ProjectiveMontgomeryXpoint {
437437
U,
438438
W: FieldElement::ONE,

ed448-goldilocks/src/montgomery/x.rs

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use elliptic_curve::consts::{U28, U84};
1010
use elliptic_curve::ops::Reduce;
1111
use hash2curve::{ExpandMsg, ExpandMsgXof, ExpandMsgXofError, Expander, MapToCurve};
1212
use sha3::Shake256;
13-
use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq};
13+
use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq, CtOption};
1414

1515
impl MontgomeryXpoint {
1616
/// First low order point on Curve448 and it's twist
@@ -110,19 +110,19 @@ impl MontgomeryXpoint {
110110
}
111111

112112
/// Compute the Y-coordinate
113-
pub fn y(&self, sign: Choice) -> [u8; 56] {
114-
Self::y_internal(&FieldElement::from_bytes(&self.0), sign).to_bytes()
113+
pub fn y(&self, sign: Choice) -> CtOption<[u8; 56]> {
114+
Self::y_internal(&FieldElement::from_bytes(&self.0), sign).map(FieldElement::to_bytes)
115115
}
116116

117117
// See https://www.rfc-editor.org/rfc/rfc7748#section-1.
118-
pub(super) fn y_internal(u: &FieldElement, sign: Choice) -> FieldElement {
118+
pub(super) fn y_internal(u: &FieldElement, sign: Choice) -> CtOption<FieldElement> {
119119
// v^2 = u^3 + A*u^2 + u
120120
let uu = u.square();
121121
let vv = uu * u + FieldElement::J * uu + u;
122122

123123
let mut v = vv.sqrt();
124124
v.conditional_negate(v.is_negative() ^ sign);
125-
v
125+
CtOption::new(v, v.square().ct_eq(&vv))
126126
}
127127

128128
pub(super) fn mul_internal(
@@ -158,21 +158,21 @@ impl MontgomeryXpoint {
158158
}
159159

160160
/// Convert the point to projective form including the y-coordinate
161-
pub fn to_extended_projective(&self, sign: Choice) -> ProjectiveMontgomeryPoint {
161+
pub fn to_extended_projective(&self, sign: Choice) -> CtOption<ProjectiveMontgomeryPoint> {
162162
self.to_projective().to_extended(sign)
163163
}
164164

165165
/// Convert the point to its form including the y-coordinate
166-
pub fn to_extended(&self, sign: Choice) -> AffineMontgomeryPoint {
166+
pub fn to_extended(&self, sign: Choice) -> CtOption<AffineMontgomeryPoint> {
167167
let x = FieldElement::from_bytes(&self.0);
168168
let y = Self::y_internal(&x, sign);
169169

170-
AffineMontgomeryPoint::new(x, y)
170+
y.map(|y| AffineMontgomeryPoint::new(x, y))
171171
}
172172

173173
/// Convert this point to an [`AffinePoint`]
174-
pub fn to_edwards(&self, sign: Choice) -> AffinePoint {
175-
self.to_extended(sign).into()
174+
pub fn to_edwards(&self, sign: Choice) -> CtOption<AffinePoint> {
175+
self.to_extended(sign).map(AffinePoint::from)
176176
}
177177
}
178178

@@ -271,14 +271,14 @@ impl ProjectiveMontgomeryXpoint {
271271
};
272272

273273
// See https://www.rfc-editor.org/rfc/rfc7748#section-1.
274-
fn y(&self, sign: Choice) -> FieldElement {
274+
fn y(&self, sign: Choice) -> CtOption<FieldElement> {
275275
// v^2 = u^3 + A*u^2 + u
276276
let u_sq = self.U.square();
277277
let v_sq = u_sq * self.U + FieldElement::J * u_sq + self.U;
278278

279279
let mut v = v_sq.sqrt();
280280
v.conditional_negate(v.is_negative() ^ sign);
281-
v
281+
CtOption::new(v, v.square().ct_eq(&v_sq))
282282
}
283283

284284
/// Double this point
@@ -367,20 +367,30 @@ impl ProjectiveMontgomeryXpoint {
367367
}
368368

369369
/// Convert the point to affine form including the y-coordinate
370-
pub fn to_extended_affine(&self, sign: Choice) -> AffineMontgomeryPoint {
370+
pub fn to_extended_affine(&self, sign: Choice) -> CtOption<AffineMontgomeryPoint> {
371371
let x = self.U * self.W.invert();
372372
let y = self.y(sign);
373373

374-
AffineMontgomeryPoint::new(x, y)
374+
y.map(|y| AffineMontgomeryPoint::new(x, y))
375375
}
376376

377377
/// Convert the point to its form including the y-coordinate
378-
pub fn to_extended(&self, sign: Choice) -> ProjectiveMontgomeryPoint {
379-
ProjectiveMontgomeryPoint::conditional_select(
380-
&ProjectiveMontgomeryPoint::new(self.U, self.y(sign), self.W),
381-
&ProjectiveMontgomeryPoint::IDENTITY,
378+
pub fn to_extended(&self, sign: Choice) -> CtOption<ProjectiveMontgomeryPoint> {
379+
CtOption::new(
380+
ProjectiveMontgomeryPoint::IDENTITY,
382381
self.ct_eq(&Self::IDENTITY),
383382
)
383+
.or_else(|| {
384+
let y = self.y(sign);
385+
386+
y.map(|y| {
387+
ProjectiveMontgomeryPoint::conditional_select(
388+
&ProjectiveMontgomeryPoint::new(self.U, y, self.W),
389+
&ProjectiveMontgomeryPoint::IDENTITY,
390+
self.ct_eq(&Self::IDENTITY),
391+
)
392+
})
393+
})
384394
}
385395
}
386396

@@ -413,15 +423,15 @@ mod tests {
413423
let x_identity = ProjectiveMontgomeryXpoint::IDENTITY;
414424
let identity = ProjectiveMontgomeryPoint::IDENTITY;
415425

416-
assert_eq!(x_identity.to_extended(Choice::from(1)), identity);
426+
assert_eq!(x_identity.to_extended(Choice::from(1)).unwrap(), identity);
417427
}
418428

419429
#[test]
420430
fn to_extended_affine() {
421431
let x_identity = ProjectiveMontgomeryXpoint::IDENTITY.to_affine();
422432
let identity = ProjectiveMontgomeryPoint::IDENTITY.to_affine();
423433

424-
assert_eq!(x_identity.to_extended(Choice::from(1)), identity);
434+
assert_eq!(x_identity.to_extended(Choice::from(1)).unwrap(), identity);
425435
}
426436

427437
#[test]
@@ -446,7 +456,7 @@ mod tests {
446456
yy.copy_from_slice(&y[..]);
447457
yy.reverse();
448458
assert_eq!(p.0, xx);
449-
assert!(p.y(Choice::from(0)) == yy || p.y(Choice::from(1)) == yy);
459+
assert!(p.y(Choice::from(0)).unwrap() == yy || p.y(Choice::from(1)).unwrap() == yy);
450460
}
451461
}
452462

@@ -472,7 +482,7 @@ mod tests {
472482
yy.copy_from_slice(&y[..]);
473483
yy.reverse();
474484
assert_eq!(p.0, xx);
475-
assert!(p.y(Choice::from(0)) == yy || p.y(Choice::from(1)) == yy);
485+
assert!(p.y(Choice::from(0)).unwrap() == yy || p.y(Choice::from(1)).unwrap() == yy);
476486
}
477487
}
478488
}

0 commit comments

Comments
 (0)