Merge remote-tracking branch 'hdevalence/feature/montgomery-x-format_r1' into develop

This commit is contained in:
Isis Lovecruft 2017-03-04 02:40:17 +00:00
commit 5edd8d6d26
Failed to extract signature
3 changed files with 164 additions and 0 deletions

View file

@ -72,6 +72,11 @@ pub const SQRT_MINUS_A: FieldElement = FieldElement([ // sqrtMinusA
12222970, 8312128, 11511410, -9067497, 15300785,
241793, -25456130, -14121551, 12187136, -3972024, ]);
/// SQRT_MINUS_APLUS2 is sqrt(-486664)
pub const SQRT_MINUS_APLUS2: FieldElement = FieldElement([
-12222970, -8312128, -11511410, 9067497, -15300785,
-241793, 25456130, 14121551, -12187136, 3972024]);
/// SQRT_MINUS_HALF is sqrt(-1/2)
pub const SQRT_MINUS_HALF: FieldElement = FieldElement([ // sqrtMinusHalf
-17256545, 3971863, 28865457, -1750208, 27359696,

View file

@ -145,6 +145,68 @@ impl CompressedEdwardsY {
}
}
/// In "Montgomery u" format, as used in X25519, a point `(u,v)` on
/// the Montgomery curve
///
/// v^2 = u * (u^2 + 486662*u + 1)
///
/// is represented just by `u`. Note that we use `(u,v)` instead of
/// `(x,y)` for Montgomery coordinates to avoid confusion with Edwards
/// coordinates. For Montgomery curves, it is possible to compute the
/// `u`-coordinate of `n(u,v)` just from `n` and `u`, so it is not
/// necessary to use `v` for a Diffie-Hellman key exchange.
///
/// XXX add note on monty, twist security, edwards impl of x25519, rfc7748
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CompressedMontgomeryU(pub [u8; 32]);
impl CompressedMontgomeryU {
/// View this `CompressedMontgomeryU` as an array of bytes.
pub fn to_bytes(&self) -> [u8;32] {
self.0
}
/// Attempt to decompress to an `ExtendedPoint`.
///
/// Note that since there are two curve points with the same
/// `u`-coordinate, the `u`-coordinate does not fully specify a
/// point.
///
/// XXX match behaviour in Signal specification re: sign choice
/// and rewrite this note
///
/// XXX check for div by zero: when is u = -1 ?
/// XXX exceptional points for the birational map
pub fn decompress(&self) -> Option<ExtendedPoint> {
// u = (1 + y) / (1 - y)
// 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.
// 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
// decode to a valid (but incorrect) point, since 0.invert() = 0.
if u == FieldElement::minus_one() {
return None;
}
let u_plus_1_inv = (&u + &FieldElement::one()).invert();
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()
}
}
// ------------------------------------------------------------------------
// Internal point representations
// ------------------------------------------------------------------------
@ -383,6 +445,35 @@ impl ProjectivePoint {
s[31] ^= (x.is_negative_ed25519() << 7) as u8;
CompressedEdwardsY(s)
}
/// Convert this point to a `CompressedMontgomeryU`.
/// Note that this discards the sign.
///
/// # Return
/// - `None` if `self` is the identity point;
/// - `Some(CompressedMontgomeryU)` otherwise.
///
pub fn compress_montgomery(&self) -> Option<CompressedMontgomeryU> {
// u = (1 + y) / (1 - y)
// v = sqrt(-486664) * u / x
//
// since y = Y/Z, x = X/Z,
//
// u = (1 + Y/Z) / (1 - Y/Z);
// = (Z + Y) / (Z - Y);
//
// exceptional points:
// y = 1 <=> Y/Z = 1 <=> Z - Y = 0
let Z_plus_Y = &self.Z + &self.Y;
let Z_minus_Y = &self.Z - &self.Y;
let u = &Z_plus_Y * &Z_minus_Y.invert();
if Z_minus_Y.is_zero() == 0u8 {
Some(CompressedMontgomeryU(u.to_bytes()))
} else {
None
}
}
}
impl ExtendedPoint {
@ -427,6 +518,17 @@ impl ExtendedPoint {
xy2d: xy2d
}
}
/// Convert this point to a `CompressedMontgomeryU`.
/// Note that this discards the sign.
///
/// # Return
/// - `None` if `self` is the identity point;
/// - `Some(CompressedMontgomeryU)` otherwise.
///
pub fn compress_montgomery(&self) -> Option<CompressedMontgomeryU> {
self.to_projective().compress_montgomery()
}
}
impl CompletedPoint {
@ -919,6 +1021,13 @@ mod test {
use super::*;
use super::select_precomputed_point;
/// The X25519 basepoint, in compressed Montgomery form.
static BASE_CMPRSSD_MONTY: CompressedMontgomeryU =
CompressedMontgomeryU([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
/// X coordinate of the basepoint.
/// = 15112221349535400772501151409588531511454012693041857206046113283949847762202
static BASE_X_COORD_BYTES: [u8; 32] =
@ -965,6 +1074,54 @@ mod test {
0xc0, 0x46, 0x83, 0x43, 0xde, 0x70, 0x4b, 0x85,
0x09, 0x6f, 0xfe, 0x35, 0x4f, 0x13, 0x2b, 0x42]);
#[test]
/// Test that the constant for sqrt(-486664) really is a square
/// root of -486664.
/// XXX this should be a test in constants.rs ??
fn test_sqrt_minus_aplus2() {
let minus_aplus2 = FieldElement([-486664,0,0,0,0,0,0,0,0,0]);
let sqrt = constants::SQRT_MINUS_APLUS2;
let sq = &sqrt * &sqrt;
assert_eq!(sq, minus_aplus2);
}
/// Test Montgomery conversion against the X25519 basepoint.
#[test]
fn test_basepoint_to_montgomery() {
let bp = BASE_CMPRSSD.decompress().unwrap();
let bp_monty = bp.compress_montgomery().unwrap();
assert_eq!(bp_monty, BASE_CMPRSSD_MONTY);
}
/// Test Montgomery conversion against the X25519 basepoint.
#[test]
fn test_basepoint_from_montgomery() {
let bp = BASE_CMPRSSD_MONTY.decompress().unwrap();
let bp_compressed_edwards = bp.compress();
assert_eq!(bp_compressed_edwards, BASE_CMPRSSD);
}
/// If u = -1, then v^2 = u*(u^2+486662*u+1) = 486660.
/// But 486660 is nonsquare mod p, so this should fail.
///
/// XXX what does Signal do here?
#[test]
fn test_u_minus_one_monty() {
let mut m1 = FieldElement::zero();
m1[0] = -1;
let m1_bytes = m1.to_bytes();
let div_by_zero_u = CompressedMontgomeryU(m1_bytes);
assert!(div_by_zero_u.decompress().is_none());
}
/// Montgomery compression of the identity point should
/// fail (it's sent to infinity).
#[test]
fn test_identity_to_monty() {
let id = ExtendedPoint::identity();
assert!(id.compress_montgomery().is_none());
}
/// Test round-trip decompression for the basepoint.
#[test]
fn test_basepoint_decompression_compression() {

View file

@ -798,6 +798,8 @@ impl FieldElement {
/// Given a nonzero field element, compute its inverse.
/// The inverse is computed as self^(p-2), since
/// x^(p-2)x = x^(p-1) = 1 (mod p).
///
/// XXX should we add a debug_assert that self is nonzero?
pub fn invert(&self) -> FieldElement {
// The bits of p-2 = 2^255 -19 -2 are 11010111111...11.
//