mirror of
https://github.com/saymrwulf/risc0-curve25519-dalek-source.git
synced 2026-09-07 20:30:39 +00:00
Fixup CompressedMontgomeryU::decompress() and ensure no divide-by-zeroes.
Also, match Signal behaviour, and split off recovery of other coordinates into separate functions.
This commit is contained in:
parent
1c9637490c
commit
915f41c643
1 changed files with 64 additions and 20 deletions
84
src/curve.rs
84
src/curve.rs
|
|
@ -171,43 +171,78 @@ impl CompressedMontgomeryU {
|
||||||
|
|
||||||
/// Attempt to decompress to an `ExtendedPoint`.
|
/// Attempt to decompress to an `ExtendedPoint`.
|
||||||
///
|
///
|
||||||
/// Note that since there are two curve points with the same
|
/// # Note
|
||||||
|
///
|
||||||
|
/// Since there are two curve points with the same
|
||||||
/// `u`-coordinate, the `u`-coordinate does not fully specify a
|
/// `u`-coordinate, the `u`-coordinate does not fully specify a
|
||||||
/// point.
|
/// point. That is, roundtripping between an `ExtendedPoint` and
|
||||||
|
/// a `CompressedMontgomeryU` discards its sign bit.
|
||||||
///
|
///
|
||||||
/// XXX match behaviour in Signal specification re: sign choice
|
/// # Warning
|
||||||
/// and rewrite this note
|
|
||||||
///
|
///
|
||||||
/// XXX check for div by zero: when is u = -1 ?
|
/// This function is *not* constant time.
|
||||||
/// XXX exceptional points for the birational map
|
///
|
||||||
|
/// # Return
|
||||||
|
///
|
||||||
|
/// An `Option<ExtendedPoint>`, which will be `None` if either condition holds:
|
||||||
|
///
|
||||||
|
/// * `u = -1`, or
|
||||||
|
/// * `v` is not square.
|
||||||
|
//
|
||||||
|
// XXX any other exceptional points for the birational map?
|
||||||
pub fn decompress(&self) -> Option<ExtendedPoint> {
|
pub fn decompress(&self) -> Option<ExtendedPoint> {
|
||||||
// u = (1 + y) / (1 - y)
|
let u: FieldElement = FieldElement::from_bytes(&self.0);
|
||||||
// v = sqrt(-486664) * u / x
|
|
||||||
//
|
|
||||||
// so
|
|
||||||
//
|
|
||||||
// y = (u - 1) / (u + 1)
|
|
||||||
|
|
||||||
let u = FieldElement::from_bytes(&self.0);
|
|
||||||
|
|
||||||
// If u = -1, then v^2 = u*(u^2+486662*u+1) = 486660.
|
// If u = -1, then v^2 = u*(u^2+486662*u+1) = 486660.
|
||||||
// But 486660 is nonsquare mod p, so this is not a curve point.
|
// But 486660 is nonsquare mod p, so this is not a curve point.
|
||||||
//
|
//
|
||||||
// XXX what does Signal do here?
|
|
||||||
//
|
|
||||||
// Note: currently, without this check, u = -1 will accidentally
|
// Note: currently, without this check, u = -1 will accidentally
|
||||||
// decode to a valid (but incorrect) point, since 0.invert() = 0.
|
// decode to a valid (but incorrect) point, since 0.invert() = 0.
|
||||||
if u == FieldElement::minus_one() {
|
if u == FieldElement::minus_one() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let u_plus_1_inv = (&u + &FieldElement::one()).invert();
|
let y: FieldElement = CompressedMontgomeryU::to_edwards_y(&u); // y = (u-1)/(u+1)
|
||||||
let y = &(&u - &FieldElement::one()) * &u_plus_1_inv;
|
|
||||||
|
|
||||||
// XXX this does two inversions: the above + one in .decompress()
|
|
||||||
// is it possible to do one?
|
|
||||||
CompressedEdwardsY(y.to_bytes()).decompress()
|
CompressedEdwardsY(y.to_bytes()).decompress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a Montgomery `u` coordinate, compute an Edwards `y` via
|
||||||
|
/// `y = (u-1)/(u+1)`.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// Since `u = (1+y)/(1-y)` and `v = √(u(u²+Au+1))`, we can see that
|
||||||
|
/// `y = (u-1)/(u+1)`.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
///
|
||||||
|
/// A `FieldElement` corresponding to this coordinate, but in Edwards form.
|
||||||
|
fn to_edwards_y(u: &FieldElement) -> FieldElement {
|
||||||
|
&(u - &FieldElement::one()) * &(u + &FieldElement::one()).invert()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a Montgomery `u` coordinate, compute the corresponding
|
||||||
|
/// Montgomery `v` coordinate by computing the right-hand side of
|
||||||
|
/// the Montgomery field equation, `v² = u(u² + Au +1)`.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
///
|
||||||
|
/// A tuple of (`u8`, `FieldElement`), where the `u8` is `1` if the v² was
|
||||||
|
/// actually a square and `0` if otherwise, along with a `FieldElement`: the
|
||||||
|
/// Montgomery `v` corresponding to this `u`.
|
||||||
|
fn to_montgomery_v(u: &FieldElement) -> (u8, FieldElement) {
|
||||||
|
let one: FieldElement = FieldElement::one();
|
||||||
|
let v_squared: FieldElement = u * &(&(&u.square() + &(&(&constants::A * u) + &one)));
|
||||||
|
let v_inv: FieldElement;
|
||||||
|
let v: FieldElement;
|
||||||
|
let okay: u8;
|
||||||
|
|
||||||
|
let (okay, v_inv) = v_squared.invsqrt();
|
||||||
|
let v = &v_inv * &v_squared;
|
||||||
|
|
||||||
|
(okay, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
@ -1325,6 +1360,15 @@ mod test {
|
||||||
assert!(ExtendedPoint::identity().is_identity());
|
assert!(ExtendedPoint::identity().is_identity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_montgomery_u_is_neg_one_rejected() {
|
||||||
|
let fe_u: FieldElement = FieldElement::minus_one();
|
||||||
|
let u: CompressedMontgomeryU = CompressedMontgomeryU(fe_u.to_bytes());
|
||||||
|
let result: Option<ExtendedPoint> = u.decompress();
|
||||||
|
|
||||||
|
assert!(result.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_basepoint_mult(b: &mut Bencher) {
|
fn bench_basepoint_mult(b: &mut Bencher) {
|
||||||
b.iter(|| ExtendedPoint::basepoint_mult(&A_SCALAR));
|
b.iter(|| ExtendedPoint::basepoint_mult(&A_SCALAR));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue