Skip to content

Commit 33c2bf0

Browse files
committed
Not every full-coordinate Montgomery point is valid
1 parent 42f72bc commit 33c2bf0

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
@@ -1081,8 +1081,8 @@ mod tests {
10811081
ProjectiveMontgomeryXpoint::encode::<ExpandMsgXof<Shake256>>(&[msg], &[DST])
10821082
.unwrap()
10831083
.to_affine();
1084-
let conv_p1 = conv_p.to_edwards(Choice::from(0));
1085-
let conv_p2 = conv_p.to_edwards(Choice::from(1));
1084+
let conv_p1 = conv_p.to_edwards(Choice::from(0)).unwrap();
1085+
let conv_p2 = conv_p.to_edwards(Choice::from(1)).unwrap();
10861086
assert!(conv_p1.x == p.x || conv_p2.x == p.x);
10871087
assert!(conv_p1.y == p.y || conv_p2.y == p.y);
10881088

ed448-goldilocks/src/montgomery/point.rs

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

176176
impl DecompressPoint<Curve448> for AffineMontgomeryPoint {
177177
fn decompress(x: &FieldBytes<Curve448>, y_is_odd: Choice) -> CtOption<Self> {
178-
FieldElement::from_repr(&x.0).map(|_| MontgomeryXpoint(x.0).to_extended(y_is_odd))
178+
FieldElement::from_repr(&x.0).and_then(|_| MontgomeryXpoint(x.0).to_extended(y_is_odd))
179179
}
180180
}
181181

@@ -445,7 +445,7 @@ impl GroupEncoding for ProjectiveMontgomeryPoint {
445445
let sign = bytes[0] & 1;
446446
bytes[0] &= 0xfe;
447447

448-
FieldElement::from_repr(&bytes).map(|U| {
448+
FieldElement::from_repr(&bytes).and_then(|U| {
449449
ProjectiveMontgomeryXpoint {
450450
U,
451451
W: FieldElement::ONE,

ed448-goldilocks/src/montgomery/x.rs

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use elliptic_curve::bigint::U448;
1212
use elliptic_curve::consts::{U28, U84};
1313
use hash2curve::{ExpandMsg, ExpandMsgXof, Expander, FromOkm, MapToCurve};
1414
use sha3::Shake256;
15-
use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq};
15+
use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq, CtOption};
1616

1717
impl MontgomeryXpoint {
1818
/// First low order point on Curve448 and it's twist
@@ -112,19 +112,19 @@ impl MontgomeryXpoint {
112112
}
113113

114114
/// Compute the Y-coordinate
115-
pub fn y(&self, sign: Choice) -> [u8; 56] {
116-
Self::y_internal(&FieldElement::from_bytes(&self.0), sign).to_bytes()
115+
pub fn y(&self, sign: Choice) -> CtOption<[u8; 56]> {
116+
Self::y_internal(&FieldElement::from_bytes(&self.0), sign).map(FieldElement::to_bytes)
117117
}
118118

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

125125
let mut v = vv.sqrt();
126126
v.conditional_negate(v.is_negative() ^ sign);
127-
v
127+
CtOption::new(v, v.square().ct_eq(&vv))
128128
}
129129

130130
pub(super) fn mul_internal(
@@ -160,21 +160,21 @@ impl MontgomeryXpoint {
160160
}
161161

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

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

172-
AffineMontgomeryPoint::new(x, y)
172+
y.map(|y| AffineMontgomeryPoint::new(x, y))
173173
}
174174

175175
/// Convert this point to an [`AffinePoint`]
176-
pub fn to_edwards(&self, sign: Choice) -> AffinePoint {
177-
self.to_extended(sign).into()
176+
pub fn to_edwards(&self, sign: Choice) -> CtOption<AffinePoint> {
177+
self.to_extended(sign).map(AffinePoint::from)
178178
}
179179
}
180180

@@ -273,14 +273,14 @@ impl ProjectiveMontgomeryXpoint {
273273
};
274274

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

281281
let mut v = v_sq.sqrt();
282282
v.conditional_negate(v.is_negative() ^ sign);
283-
v
283+
CtOption::new(v, v.square().ct_eq(&v_sq))
284284
}
285285

286286
/// Double this point
@@ -371,20 +371,30 @@ impl ProjectiveMontgomeryXpoint {
371371
}
372372

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

378-
AffineMontgomeryPoint::new(x, y)
378+
y.map(|y| AffineMontgomeryPoint::new(x, y))
379379
}
380380

381381
/// Convert the point to its form including the y-coordinate
382-
pub fn to_extended(&self, sign: Choice) -> ProjectiveMontgomeryPoint {
383-
ProjectiveMontgomeryPoint::conditional_select(
384-
&ProjectiveMontgomeryPoint::new(self.U, self.y(sign), self.W),
385-
&ProjectiveMontgomeryPoint::IDENTITY,
382+
pub fn to_extended(&self, sign: Choice) -> CtOption<ProjectiveMontgomeryPoint> {
383+
CtOption::new(
384+
ProjectiveMontgomeryPoint::IDENTITY,
386385
self.ct_eq(&Self::IDENTITY),
387386
)
387+
.or_else(|| {
388+
let y = self.y(sign);
389+
390+
y.map(|y| {
391+
ProjectiveMontgomeryPoint::conditional_select(
392+
&ProjectiveMontgomeryPoint::new(self.U, y, self.W),
393+
&ProjectiveMontgomeryPoint::IDENTITY,
394+
self.ct_eq(&Self::IDENTITY),
395+
)
396+
})
397+
})
388398
}
389399
}
390400

@@ -417,15 +427,15 @@ mod tests {
417427
let x_identity = ProjectiveMontgomeryXpoint::IDENTITY;
418428
let identity = ProjectiveMontgomeryPoint::IDENTITY;
419429

420-
assert_eq!(x_identity.to_extended(Choice::from(1)), identity);
430+
assert_eq!(x_identity.to_extended(Choice::from(1)).unwrap(), identity);
421431
}
422432

423433
#[test]
424434
fn to_extended_affine() {
425435
let x_identity = ProjectiveMontgomeryXpoint::IDENTITY.to_affine();
426436
let identity = ProjectiveMontgomeryPoint::IDENTITY.to_affine();
427437

428-
assert_eq!(x_identity.to_extended(Choice::from(1)), identity);
438+
assert_eq!(x_identity.to_extended(Choice::from(1)).unwrap(), identity);
429439
}
430440

431441
#[test]
@@ -450,7 +460,7 @@ mod tests {
450460
yy.copy_from_slice(&y[..]);
451461
yy.reverse();
452462
assert_eq!(p.0, xx);
453-
assert!(p.y(Choice::from(0)) == yy || p.y(Choice::from(1)) == yy);
463+
assert!(p.y(Choice::from(0)).unwrap() == yy || p.y(Choice::from(1)).unwrap() == yy);
454464
}
455465
}
456466

@@ -476,7 +486,7 @@ mod tests {
476486
yy.copy_from_slice(&y[..]);
477487
yy.reverse();
478488
assert_eq!(p.0, xx);
479-
assert!(p.y(Choice::from(0)) == yy || p.y(Choice::from(1)) == yy);
489+
assert!(p.y(Choice::from(0)).unwrap() == yy || p.y(Choice::from(1)).unwrap() == yy);
480490
}
481491
}
482492
}

0 commit comments

Comments
 (0)