Skip to content

Commit 874805c

Browse files
committed
Not every full-coordinate Montgomery point is valid
1 parent 247f5e9 commit 874805c

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
@@ -1199,8 +1199,8 @@ mod tests {
11991199
let conv_p =
12001200
ProjectiveMontgomeryXpoint::encode::<ExpandMsgXof<Shake256>>(&[msg], &[DST])
12011201
.to_affine();
1202-
let conv_p1 = conv_p.to_edwards(Choice::from(0));
1203-
let conv_p2 = conv_p.to_edwards(Choice::from(1));
1202+
let conv_p1 = conv_p.to_edwards(Choice::from(0)).unwrap();
1203+
let conv_p2 = conv_p.to_edwards(Choice::from(1)).unwrap();
12041204
assert!(conv_p1.x == p.x || conv_p2.x == p.x);
12051205
assert!(conv_p1.y == p.y || conv_p2.y == p.y);
12061206

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
@@ -365,20 +365,30 @@ impl ProjectiveMontgomeryXpoint {
365365
}
366366

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

372-
AffineMontgomeryPoint::new(x, y)
372+
y.map(|y| AffineMontgomeryPoint::new(x, y))
373373
}
374374

375375
/// Convert the point to its form including the y-coordinate
376-
pub fn to_extended(&self, sign: Choice) -> ProjectiveMontgomeryPoint {
377-
ProjectiveMontgomeryPoint::conditional_select(
378-
&ProjectiveMontgomeryPoint::new(self.U, self.y(sign), self.W),
379-
&ProjectiveMontgomeryPoint::IDENTITY,
376+
pub fn to_extended(&self, sign: Choice) -> CtOption<ProjectiveMontgomeryPoint> {
377+
CtOption::new(
378+
ProjectiveMontgomeryPoint::IDENTITY,
380379
self.ct_eq(&Self::IDENTITY),
381380
)
381+
.or_else(|| {
382+
let y = self.y(sign);
383+
384+
y.map(|y| {
385+
ProjectiveMontgomeryPoint::conditional_select(
386+
&ProjectiveMontgomeryPoint::new(self.U, y, self.W),
387+
&ProjectiveMontgomeryPoint::IDENTITY,
388+
self.ct_eq(&Self::IDENTITY),
389+
)
390+
})
391+
})
382392
}
383393
}
384394

@@ -411,15 +421,15 @@ mod tests {
411421
let x_identity = ProjectiveMontgomeryXpoint::IDENTITY;
412422
let identity = ProjectiveMontgomeryPoint::IDENTITY;
413423

414-
assert_eq!(x_identity.to_extended(Choice::from(1)), identity);
424+
assert_eq!(x_identity.to_extended(Choice::from(1)).unwrap(), identity);
415425
}
416426

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

422-
assert_eq!(x_identity.to_extended(Choice::from(1)), identity);
432+
assert_eq!(x_identity.to_extended(Choice::from(1)).unwrap(), identity);
423433
}
424434

425435
#[test]
@@ -443,7 +453,7 @@ mod tests {
443453
yy.copy_from_slice(&y[..]);
444454
yy.reverse();
445455
assert_eq!(p.0, xx);
446-
assert!(p.y(Choice::from(0)) == yy || p.y(Choice::from(1)) == yy);
456+
assert!(p.y(Choice::from(0)).unwrap() == yy || p.y(Choice::from(1)).unwrap() == yy);
447457
}
448458
}
449459

@@ -468,7 +478,7 @@ mod tests {
468478
yy.copy_from_slice(&y[..]);
469479
yy.reverse();
470480
assert_eq!(p.0, xx);
471-
assert!(p.y(Choice::from(0)) == yy || p.y(Choice::from(1)) == yy);
481+
assert!(p.y(Choice::from(0)).unwrap() == yy || p.y(Choice::from(1)).unwrap() == yy);
472482
}
473483
}
474484
}

0 commit comments

Comments
 (0)