diff --git a/Cargo.toml b/Cargo.toml index 590e2aa..8c22506 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "curve25519-dalek" -version = "0.10.0" +version = "0.11.0" authors = ["Isis Lovecruft ", "Henry de Valence "] readme = "README.md" @@ -23,7 +23,7 @@ version = "1.0" optional = true [dependencies.arrayref] -version = "0.3.3" +version = "0.3.4" [dependencies.rand] optional = true diff --git a/README.md b/README.md index bf4cecb..3df4040 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Extensive documentation is available [here](https://docs.rs/curve25519-dalek). To install, add the following to the dependencies section of your project's `Cargo.toml`: - curve25519-dalek = "^0.10" + curve25519-dalek = "^0.11" Then, in your library or executable source, add: diff --git a/src/constants.rs b/src/constants.rs index 9935d40..214e128 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -19,7 +19,7 @@ #![allow(missing_docs)] #![allow(non_snake_case)] -use curve::CompressedEdwardsY; +use edwards::CompressedEdwardsY; #[cfg(feature = "yolocrypto")] use decaf::{DecafPoint, DecafBasepointTable}; use scalar::Scalar; @@ -53,9 +53,10 @@ pub const BASE_CMPRSSD: CompressedEdwardsY = 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66]); -/// The Ed25519 basepoint, as a `DecafPoint`. -#[cfg(feature = "yolocrypto")] -pub const DECAF_ED25519_BASEPOINT: DecafPoint = DecafPoint(ED25519_BASEPOINT); +/// The Ed25519 basepoint, as a `DecafPoint`. This is called `_POINT` to distinguish it from +/// `_TABLE`, which provides fast scalar multiplication. +#[cfg(feature = "yolocrypto")] pub const DECAF_ED25519_BASEPOINT_POINT: DecafPoint = +DecafPoint(ED25519_BASEPOINT_POINT); /// `l` is the order of base point, i.e. 2^252 + /// 27742317777372353535851937790883648493, in little-endian form @@ -86,8 +87,8 @@ pub const DECAF_ED25519_BASEPOINT_TABLE: DecafBasepointTable #[cfg(test)] mod test { use field::FieldElement; - use curve::IsIdentity; - use curve::ValidityCheck; + use edwards::IsIdentity; + use edwards::ValidityCheck; use constants; #[test] diff --git a/src/constants_32bit.rs b/src/constants_32bit.rs index 1ad4e1f..ac7c02e 100644 --- a/src/constants_32bit.rs +++ b/src/constants_32bit.rs @@ -20,9 +20,9 @@ #![allow(non_snake_case)] use field_32bit::FieldElement32; -use curve::ExtendedPoint; -use curve::AffineNielsPoint; -use curve::EdwardsBasepointTable; +use edwards::ExtendedPoint; +use edwards::AffineNielsPoint; +use edwards::EdwardsBasepointTable; pub const d: FieldElement32 = FieldElement32([ -10913610, 13857413, -15372611, 6949391, 114729, @@ -94,8 +94,9 @@ pub const SQRT_MINUS_HALF: FieldElement32 = FieldElement32([ // sqrtMinusHalf -17256545, 3971863, 28865457, -1750208, 27359696, -16640980, 12573105, 1002827, -163343, 11073975, ]); -/// Basepoint has y = 4/5. -pub const ED25519_BASEPOINT: ExtendedPoint = ExtendedPoint{ +/// Basepoint has y = 4/5. This is called `_POINT` to distinguish it from `_TABLE`, which should +/// be used for scalar multiplication (it's much faster). +pub const ED25519_BASEPOINT_POINT: ExtendedPoint = ExtendedPoint{ X: FieldElement32([-14297830, -7645148, 16144683, -16471763, 27570974, -2696100, -26142465, 8378389, 20764389, 8758491]), Y: FieldElement32([-26843541, -6710886, 13421773, -13421773, 26843546, 6710886, -13421773, 13421773, -26843546, -6710886]), Z: FieldElement32([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]), diff --git a/src/constants_64bit.rs b/src/constants_64bit.rs index 7825380..90c81ab 100644 --- a/src/constants_64bit.rs +++ b/src/constants_64bit.rs @@ -20,9 +20,9 @@ #![allow(non_snake_case)] use field_64bit::FieldElement64; -use curve::ExtendedPoint; -use curve::AffineNielsPoint; -use curve::EdwardsBasepointTable; +use edwards::ExtendedPoint; +use edwards::AffineNielsPoint; +use edwards::EdwardsBasepointTable; pub const p: FieldElement64 = FieldElement64([2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]); @@ -67,8 +67,9 @@ pub const SQRT_MINUS_APLUS2: FieldElement64 = FieldElement64([1693982333959686, /// `SQRT_MINUS_HALF` is sqrt(-1/2) pub const SQRT_MINUS_HALF: FieldElement64 = FieldElement64([266547196637087, 2134345371906993, 1135042577398223, 67298593331632, 743161882051057]); -/// Basepoint has y = 4/5. -pub const ED25519_BASEPOINT: ExtendedPoint = ExtendedPoint { +/// Basepoint has y = 4/5. This is called `_POINT` to distinguish it from `_TABLE`, which should +/// be used for scalar multiplication (it's much faster). +pub const ED25519_BASEPOINT_POINT: ExtendedPoint = ExtendedPoint{ X: FieldElement64([1738742601995546, 1146398526822698, 2070867633025821, 562264141797630, 587772402128613]), Y: FieldElement64([1801439850948184, 1351079888211148, 450359962737049, 900719925474099, 1801439850948198]), Z: FieldElement64([1, 0, 0, 0, 0]), diff --git a/src/decaf.rs b/src/decaf.rs index c2dad53..d8ea97a 100644 --- a/src/decaf.rs +++ b/src/decaf.rs @@ -37,11 +37,11 @@ use core::ops::{Add, Sub, Neg}; use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; -use curve; -use curve::ExtendedPoint; -use curve::CompletedPoint; -use curve::EdwardsBasepointTable; -use curve::Identity; +use edwards; +use edwards::ExtendedPoint; +use edwards::CompletedPoint; +use edwards::EdwardsBasepointTable; +use edwards::Identity; use scalar::Scalar; use subtle::ConditionallyAssignable; @@ -59,6 +59,11 @@ pub struct CompressedDecaf(pub [u8; 32]); /// The result of compressing a `DecafPoint`. impl CompressedDecaf { + /// Convert this `CompressedDecaf` to an array of bytes. + pub fn to_bytes(&self) -> [u8; 32] { + self.0 + } + /// View this `CompressedDecaf` as an array of bytes. pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] { &self.0 @@ -593,7 +598,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> DecafPoint J: IntoIterator, { let extended_points = points.into_iter().map(|P| &P.0); - DecafPoint(curve::multiscalar_mult(scalars, extended_points)) + DecafPoint(edwards::multiscalar_mult(scalars, extended_points)) } /// Precomputation @@ -643,12 +648,12 @@ impl ConditionallyAssignable for DecafPoint { /// # /// # use subtle::ConditionallyAssignable; /// # - /// # use curve25519_dalek::curve::Identity; + /// # use curve25519_dalek::edwards::Identity; /// # use curve25519_dalek::decaf::DecafPoint; /// # use curve25519_dalek::constants; /// # fn main() { /// let A = DecafPoint::identity(); - /// let B = constants::DECAF_ED25519_BASEPOINT; + /// let B = constants::DECAF_ED25519_BASEPOINT_POINT; /// /// let mut P = A; /// @@ -706,7 +711,7 @@ pub mod vartime { J: IntoIterator { let extended_points = points.into_iter().map(|P| &P.0); - DecafPoint(curve::vartime::multiscalar_mult(scalars, extended_points)) + DecafPoint(edwards::vartime::multiscalar_mult(scalars, extended_points)) } } @@ -720,9 +725,9 @@ mod test { use scalar::Scalar; use constants; - use curve::CompressedEdwardsY; - use curve::Identity; - use curve::ValidityCheck; + use edwards::CompressedEdwardsY; + use edwards::Identity; + use edwards::ValidityCheck; use super::*; #[cfg(feature = "serde")] @@ -731,9 +736,9 @@ mod test { #[test] #[cfg(feature = "serde")] fn serde_cbor_basepoint_roundtrip() { - let output = serde_cbor::to_vec(&constants::DECAF_ED25519_BASEPOINT).unwrap(); + let output = serde_cbor::to_vec(&constants::DECAF_ED25519_BASEPOINT_POINT).unwrap(); let parsed: DecafPoint = serde_cbor::from_slice(&output).unwrap(); - assert_eq!(parsed, constants::DECAF_ED25519_BASEPOINT); + assert_eq!(parsed, constants::DECAF_ED25519_BASEPOINT_POINT); } @@ -759,10 +764,10 @@ mod test { #[test] fn decaf_basepoint_roundtrip() { - let bp_compressed_decaf = constants::DECAF_ED25519_BASEPOINT.compress(); + let bp_compressed_decaf = constants::DECAF_ED25519_BASEPOINT_POINT.compress(); let bp_recaf = bp_compressed_decaf.decompress().unwrap().0; // Check that bp_recaf differs from bp by a point of order 4 - let diff = &constants::ED25519_BASEPOINT - &bp_recaf; + let diff = &constants::ED25519_BASEPOINT_POINT - &bp_recaf; let diff4 = diff.mult_by_pow_2(4); // XXX this is wrong assert_eq!(diff4.compress_edwards(), CompressedEdwardsY::identity()); } @@ -789,16 +794,16 @@ mod test { CompressedDecaf([7, 221, 140, 57, 13, 146, 248, 27, 56, 4, 128, 23, 145, 120, 126, 4, 158, 173, 52, 213, 164, 250, 26, 55, 89, 96, 187, 111, 211, 18, 63, 19]), CompressedDecaf([91, 213, 193, 10, 102, 92, 199, 124, 61, 176, 1, 47, 111, 59, 183, 91, 79, 56, 208, 109, 172, 209, 17, 167, 229, 216, 3, 236, 200, 208, 15, 20]), ]; - let mut bp = constants::DECAF_ED25519_BASEPOINT; + let mut bp = constants::DECAF_ED25519_BASEPOINT_POINT; for i in 0..16 { assert_eq!(bp.compress(), compressed[i]); - bp = &bp + &constants::DECAF_ED25519_BASEPOINT; + bp = &bp + &constants::DECAF_ED25519_BASEPOINT_POINT; } } #[test] fn decaf_four_torsion_basepoint() { - let bp = constants::DECAF_ED25519_BASEPOINT; + let bp = constants::DECAF_ED25519_BASEPOINT_POINT; let bp_coset = bp.coset4(); for i in 0..4 { assert_eq!(bp, DecafPoint(bp_coset[i])); diff --git a/src/curve.rs b/src/edwards.rs similarity index 87% rename from src/curve.rs rename to src/edwards.rs index 95146e7..d0a13bd 100644 --- a/src/curve.rs +++ b/src/edwards.rs @@ -90,6 +90,7 @@ use core::ops::Index; use constants; use field::FieldElement; use scalar::Scalar; +use montgomery::CompressedMontgomeryU; use subtle::slices_equal; use subtle::bytes_equal; @@ -151,133 +152,6 @@ 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 - /// - /// Since there are two curve points with the same - /// `u`-coordinate, the `u`-coordinate does not fully specify a - /// point. That is, roundtripping between an `ExtendedPoint` and - /// a `CompressedMontgomeryU` discards its sign bit. - /// - /// # Warning - /// - /// This function is *not* constant time. - /// - /// # Return - /// - /// An `Option`, 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 { - let u: FieldElement = 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. - // - // 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 y: FieldElement = CompressedMontgomeryU::to_edwards_y(&u); // y = (u-1)/(u+1) - - // XXX this does two inversions: the above + one in .decompress() - // is it possible to do one? - CompressedEdwardsY(y.to_bytes()).decompress() - } - - /// Given a Montgomery `u` coordinate, compute an Edwards `y` via - /// `y = (u-1)/(u+1)`. - /// - /// # Return - /// - /// A `FieldElement` corresponding to this coordinate, but in Edwards form. - pub fn to_edwards_y(u: &FieldElement) -> FieldElement { - // Since `u = (1+y)/(1-y)` and `v = √(u(u²+Au+1))`, so `y = (u-1)/(u+1)`. - &(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`. - pub 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 (okay, v_inv) = v_squared.invsqrt(); - let v = &v_inv * &v_squared; - - (okay, v) - } - - /// Given Montgomery coordinates `(u, v)`, recover the Edwards `x` coordinate. - /// - /// # Inputs - /// - /// * `u` and `v` are both `&FieldElement`s, corresponding the the `(u, v)` - /// coordinates of this `CompressedMontgomeryU`. - /// * `sign` is an &u8. - /// - /// ## Explanation of choice of `sign` - /// - /// ### Original Signal behaviour: - /// - /// - `1u8` will leave `x` negative if it is negative, and will negate - /// `x` if it is positive, and - /// - `0u8` will leave `x` positive if it is positive, and will negate - /// `x` if it is negative. - /// - /// Hence, if `sign` is `1u8`, the returned `x` will be negative. - /// Otherwise, if `sign` is `0u8`, the returned `x` will be positive. - /// - /// # Return - /// - /// A `FieldElement`, the Edwards `x` coordinate, by using `(u, v)` to - /// convert from Montgomery to Edwards form via the right-hand side of the - /// equation: `x=(u/v)*sqrt(-A-2)`. - pub fn to_edwards_x(u: &FieldElement, v: &FieldElement, sign: &u8) -> FieldElement { - let mut x: FieldElement = &(u * &v.invert()) * &constants::SQRT_MINUS_APLUS2; - let neg_x: FieldElement = -(&x); - let current_sign: u8 = x.is_negative_ed25519(); - - // Negate x to match the sign: - x.conditional_assign(&neg_x, current_sign ^ sign); - x - } -} - // ------------------------------------------------------------------------ // Serde support // ------------------------------------------------------------------------ @@ -553,8 +427,7 @@ impl ProjectivePoint { /// Given (X:Y:Z) in Ɛ, passing to Ɛₑ can be performed in 3M+1S by /// computing (XZ,YZ,XY,Z²). (Note that in that paper, points are /// (X:Y:T:Z) so this really does match the code below). - #[allow(dead_code)] // rustc complains this is unused even when it's used - fn to_extended(&self) -> ExtendedPoint { + pub fn to_extended(&self) -> ExtendedPoint { ExtendedPoint{ X: &self.X * &self.Z, Y: &self.Y * &self.Z, @@ -1318,7 +1191,7 @@ pub mod vartime { /// with x positive). pub fn double_scalar_mult_basepoint(a: &Scalar, A: &ExtendedPoint, - b: &Scalar) -> ProjectivePoint { + b: &Scalar) -> ExtendedPoint { let a_naf = a.non_adjacent_form(); let b_naf = b.non_adjacent_form(); @@ -1357,7 +1230,7 @@ pub mod vartime { i -= 1; } - r + r.to_extended() } } @@ -1376,13 +1249,6 @@ mod test { use constants; use super::*; - /// 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] = @@ -1432,40 +1298,6 @@ mod test { 0xc0, 0x46, 0x83, 0x43, 0xde, 0x70, 0x4b, 0x85, 0x09, 0x6f, 0xfe, 0x35, 0x4f, 0x13, 0x2b, 0x42]); - /// Test Montgomery conversion against the X25519 basepoint. - #[test] - fn basepoint_to_montgomery() { - assert_eq!(constants::ED25519_BASEPOINT.compress_montgomery().unwrap(), - BASE_CMPRSSD_MONTY); - } - - /// Test Montgomery conversion against the X25519 basepoint. - #[test] - fn basepoint_from_montgomery() { - assert_eq!(BASE_CMPRSSD_MONTY.decompress().unwrap().compress_edwards(), - constants::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 u_minus_one_monty() { - let minus_one = FieldElement::minus_one(); - let minus_one_bytes = minus_one.to_bytes(); - let div_by_zero_u = CompressedMontgomeryU(minus_one_bytes); - assert!(div_by_zero_u.decompress().is_none()); - } - - /// Montgomery compression of the identity point should - /// fail (it's sent to infinity). - #[test] - fn identity_to_monty() { - let id = ExtendedPoint::identity(); - assert!(id.compress_montgomery().is_none()); - } - /// Test round-trip decompression for the basepoint. #[test] fn basepoint_decompression_compression() { @@ -1487,10 +1319,10 @@ mod test { .decompress().unwrap(); // Test projective coordinates exactly since we know they should // only differ by a flipped sign. - assert_eq!(minus_basepoint.X, -(&constants::ED25519_BASEPOINT.X)); - assert_eq!(minus_basepoint.Y, constants::ED25519_BASEPOINT.Y); - assert_eq!(minus_basepoint.Z, constants::ED25519_BASEPOINT.Z); - assert_eq!(minus_basepoint.T, -(&constants::ED25519_BASEPOINT.T)); + assert_eq!(minus_basepoint.X, -(&constants::ED25519_BASEPOINT_POINT.X)); + assert_eq!(minus_basepoint.Y, constants::ED25519_BASEPOINT_POINT.Y); + assert_eq!(minus_basepoint.Z, constants::ED25519_BASEPOINT_POINT.Z); + assert_eq!(minus_basepoint.T, -(&constants::ED25519_BASEPOINT_POINT.T)); } /// Test that computing 1*basepoint gives the correct basepoint. @@ -1512,7 +1344,7 @@ mod test { /// using basepoint + basepoint versus the 2*basepoint constant. #[test] fn basepoint_plus_basepoint_vs_basepoint2() { - let bp = constants::ED25519_BASEPOINT; + let bp = constants::ED25519_BASEPOINT_POINT; let bp_added = &bp + &bp; assert_eq!(bp_added.compress_edwards(), BASE2_CMPRSSD); } @@ -1521,7 +1353,7 @@ mod test { /// using the basepoint, basepoint2 constants #[test] fn basepoint_plus_basepoint_projective_niels_vs_basepoint2() { - let bp = constants::ED25519_BASEPOINT; + let bp = constants::ED25519_BASEPOINT_POINT; let bp_added = (&bp + &bp.to_projective_niels()).to_extended(); assert_eq!(bp_added.compress_edwards(), BASE2_CMPRSSD); } @@ -1530,7 +1362,7 @@ mod test { /// using the basepoint, basepoint2 constants #[test] fn basepoint_plus_basepoint_affine_niels_vs_basepoint2() { - let bp = constants::ED25519_BASEPOINT; + let bp = constants::ED25519_BASEPOINT_POINT; let bp_affine_niels = bp.to_affine_niels(); let bp_added = (&bp + &bp_affine_niels).to_extended(); assert_eq!(bp_added.compress_edwards(), BASE2_CMPRSSD); @@ -1581,7 +1413,7 @@ mod test { #[test] #[cfg(feature="basepoint_table_creation")] fn test_precomputed_basepoint_mult() { - let table = EdwardsBasepointTable::create(&constants::ED25519_BASEPOINT); + let table = EdwardsBasepointTable::create(&constants::ED25519_BASEPOINT_POINT); let aB_1 = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR; let aB_2 = &table * &A_SCALAR; assert_eq!(aB_1.compress_edwards(), aB_2.compress_edwards()); @@ -1590,14 +1422,14 @@ mod test { /// Test scalar_mult versus a known scalar multiple from ed25519.py #[test] fn scalar_mult_vs_ed25519py() { - let aB = &constants::ED25519_BASEPOINT * &A_SCALAR; + let aB = &constants::ED25519_BASEPOINT_POINT * &A_SCALAR; assert_eq!(aB.compress_edwards(), A_TIMES_BASEPOINT); } /// Test basepoint.double() versus the 2*basepoint constant. #[test] fn basepoint_double_vs_basepoint2() { - assert_eq!(constants::ED25519_BASEPOINT.double().compress_edwards(), + assert_eq!(constants::ED25519_BASEPOINT_POINT.double().compress_edwards(), BASE2_CMPRSSD); } @@ -1612,7 +1444,7 @@ mod test { /// Check that converting to projective and then back to extended round-trips. #[test] fn basepoint_projective_extended_round_trip() { - assert_eq!(constants::ED25519_BASEPOINT + assert_eq!(constants::ED25519_BASEPOINT_POINT .to_projective().to_extended().compress_edwards(), constants::BASE_CMPRSSD); } @@ -1620,7 +1452,7 @@ mod test { /// Test computing 16*basepoint vs mult_by_pow_2(4) #[test] fn basepoint16_vs_mult_by_pow_2_4() { - let bp16 = constants::ED25519_BASEPOINT.mult_by_pow_2(4); + let bp16 = constants::ED25519_BASEPOINT_POINT.mult_by_pow_2(4); assert_eq!(bp16.compress_edwards(), BASE16_CMPRSSD); } @@ -1629,7 +1461,7 @@ mod test { fn conditional_assign_for_affine_niels_point() { let id = AffineNielsPoint::identity(); let mut p1 = AffineNielsPoint::identity(); - let bp = constants::ED25519_BASEPOINT.to_affine_niels(); + let bp = constants::ED25519_BASEPOINT_POINT.to_affine_niels(); p1.conditional_assign(&bp, 0); assert_eq!(p1, id); @@ -1640,7 +1472,7 @@ mod test { #[test] fn is_small_order() { // The basepoint has large prime order - assert!(constants::ED25519_BASEPOINT.is_small_order() == false); + assert!(constants::ED25519_BASEPOINT_POINT.is_small_order() == false); // constants::EIGHT_TORSION has all points of small order. for torsion_point in &constants::EIGHT_TORSION { assert!(torsion_point.is_small_order() == true); @@ -1656,7 +1488,7 @@ mod test { #[test] fn is_identity() { assert!( ExtendedPoint::identity().is_identity() == true); - assert!(constants::ED25519_BASEPOINT.is_identity() == false); + assert!(constants::ED25519_BASEPOINT_POINT.is_identity() == false); } /// Rust's debug builds have overflow and underflow trapping, @@ -1671,7 +1503,7 @@ mod test { /// the type system and prove correctness). #[test] fn monte_carlo_overflow_underflow_debug_assert_test() { - let mut P = constants::ED25519_BASEPOINT; + let mut P = constants::ED25519_BASEPOINT_POINT; // N.B. each scalar_mult does 1407 field mults, 1024 field squarings, // so this does ~ 1M of each operation. for _ in 0..1_000 { @@ -1681,7 +1513,7 @@ mod test { #[test] fn scalarmult_extended_point_works_both_ways() { - let G: ExtendedPoint = constants::ED25519_BASEPOINT; + let G: ExtendedPoint = constants::ED25519_BASEPOINT_POINT; let s: Scalar = A_SCALAR; let P1 = &G * &s; @@ -1693,7 +1525,7 @@ mod test { #[test] #[cfg(feature = "yolocrypto")] fn scalarmult_decafpoint_works_both_ways() { - let P: DecafPoint = DecafPoint(constants::ED25519_BASEPOINT); + let P: DecafPoint = DecafPoint(constants::ED25519_BASEPOINT_POINT); let s: Scalar = A_SCALAR; let P1 = &P * &s; @@ -1719,7 +1551,7 @@ mod test { let A = A_TIMES_BASEPOINT.decompress().unwrap(); let result = vartime::multiscalar_mult( &[A_SCALAR, B_SCALAR], - &[A, constants::ED25519_BASEPOINT] + &[A, constants::ED25519_BASEPOINT_POINT] ); assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); } @@ -1729,11 +1561,11 @@ mod test { let A = A_TIMES_BASEPOINT.decompress().unwrap(); let result_vartime = vartime::multiscalar_mult( &[A_SCALAR, B_SCALAR], - &[A, constants::ED25519_BASEPOINT] + &[A, constants::ED25519_BASEPOINT_POINT] ); let result_consttime = multiscalar_mult( &[A_SCALAR, B_SCALAR], - &[A, constants::ED25519_BASEPOINT] + &[A, constants::ED25519_BASEPOINT_POINT] ); assert_eq!(result_vartime.compress_edwards(), result_consttime.compress_edwards()); @@ -1746,7 +1578,7 @@ mod test { #[test] #[cfg(feature = "serde")] fn serde_cbor_basepoint_roundtrip() { - let output = serde_cbor::to_vec(&constants::ED25519_BASEPOINT).unwrap(); + let output = serde_cbor::to_vec(&constants::ED25519_BASEPOINT_POINT).unwrap(); let parsed: ExtendedPoint = serde_cbor::from_slice(&output).unwrap(); assert_eq!(parsed.compress_edwards(), constants::BASE_CMPRSSD); } @@ -1754,7 +1586,7 @@ mod test { #[test] #[cfg(feature = "serde")] fn serde_cbor_decode_invalid_fails() { - let mut output = serde_cbor::to_vec(&constants::ED25519_BASEPOINT).unwrap(); + let mut output = serde_cbor::to_vec(&constants::ED25519_BASEPOINT_POINT).unwrap(); // CBOR apparently has two bytes of overhead for a 32-byte string. // Set the low byte of the compressed point to 1 to make it invalid. output[2] = 1; @@ -1783,7 +1615,7 @@ mod bench { #[bench] fn edwards_compress(b: &mut Bencher) { - let B = &constants::ED25519_BASEPOINT; + let B = &constants::ED25519_BASEPOINT_POINT; b.iter(|| B.compress_edwards()); } @@ -1795,7 +1627,7 @@ mod bench { #[bench] fn scalar_mult(b: &mut Bencher) { - let B = &constants::ED25519_BASEPOINT; + let B = &constants::ED25519_BASEPOINT_POINT; b.iter(|| B * &A_SCALAR); } @@ -1806,53 +1638,53 @@ mod bench { #[bench] fn add_extended_and_projective_niels_output_completed(b: &mut Bencher) { - let p1 = constants::ED25519_BASEPOINT; - let p2 = constants::ED25519_BASEPOINT.to_projective_niels(); + let p1 = constants::ED25519_BASEPOINT_POINT; + let p2 = constants::ED25519_BASEPOINT_POINT.to_projective_niels(); b.iter(|| &p1 + &p2); } #[bench] fn add_extended_and_projective_niels_output_extended(b: &mut Bencher) { - let p1 = constants::ED25519_BASEPOINT; - let p2 = constants::ED25519_BASEPOINT.to_projective_niels(); + let p1 = constants::ED25519_BASEPOINT_POINT; + let p2 = constants::ED25519_BASEPOINT_POINT.to_projective_niels(); b.iter(|| (&p1 + &p2).to_extended()); } #[bench] fn add_extended_and_affine_niels_output_completed(b: &mut Bencher) { - let p1 = constants::ED25519_BASEPOINT; - let p2 = constants::ED25519_BASEPOINT.to_affine_niels(); + let p1 = constants::ED25519_BASEPOINT_POINT; + let p2 = constants::ED25519_BASEPOINT_POINT.to_affine_niels(); b.iter(|| &p1 + &p2); } #[bench] fn add_extended_and_affine_niels_output_extended(b: &mut Bencher) { - let p1 = constants::ED25519_BASEPOINT; - let p2 = constants::ED25519_BASEPOINT.to_affine_niels(); + let p1 = constants::ED25519_BASEPOINT_POINT; + let p2 = constants::ED25519_BASEPOINT_POINT.to_affine_niels(); b.iter(|| (&p1 + &p2).to_extended()); } #[bench] fn projective_double_output_completed(b: &mut Bencher) { - let p1 = constants::ED25519_BASEPOINT.to_projective(); + let p1 = constants::ED25519_BASEPOINT_POINT.to_projective(); b.iter(|| p1.double()); } #[bench] fn extended_double_output_extended(b: &mut Bencher) { - let p1 = constants::ED25519_BASEPOINT; + let p1 = constants::ED25519_BASEPOINT_POINT; b.iter(|| p1.double()); } #[bench] fn mult_by_cofactor(b: &mut Bencher) { - let p1 = constants::ED25519_BASEPOINT; + let p1 = constants::ED25519_BASEPOINT_POINT; b.iter(|| p1.mult_by_cofactor()); } diff --git a/src/lib.rs b/src/lib.rs index 3afba5a..13da293 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,7 +72,8 @@ mod field_32bit; mod field_64bit; pub mod scalar; -pub mod curve; +pub mod edwards; +pub mod montgomery; // Feature gate decaf while our implementation is unfinished and probably incorrect. #[cfg(feature = "yolocrypto")] diff --git a/src/montgomery.rs b/src/montgomery.rs new file mode 100644 index 0000000..d287eb7 --- /dev/null +++ b/src/montgomery.rs @@ -0,0 +1,203 @@ +// -*- mode: rust; -*- +// +// To the extent possible under law, the authors have waived all copyright and +// related or neighboring rights to curve25519-dalek, using the Creative +// Commons "CC0" public domain dedication. See +// for full details. +// +// Authors: +// - Isis Agora Lovecruft +// - Henry de Valence + +//! Montgomery arithmetic prototype, subject to revision. + +// We allow non snake_case names because coordinates in projective space are +// traditionally denoted by the capitalisation of their respective +// counterparts in affine space. Yeah, you heard me, rustc, I'm gonna have my +// affine and projective cakes and eat both of them too. +#![allow(non_snake_case)] + + +use constants; +use field::FieldElement; +use edwards::{ExtendedPoint, CompressedEdwardsY}; + +use subtle::ConditionallyAssignable; + +/// 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 + /// + /// Since there are two curve points with the same + /// `u`-coordinate, the `u`-coordinate does not fully specify a + /// point. That is, roundtripping between an `ExtendedPoint` and + /// a `CompressedMontgomeryU` discards its sign bit. + /// + /// # Warning + /// + /// This function is *not* constant time. + /// + /// # Return + /// + /// An `Option`, 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 { + let u: FieldElement = 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. + // + // 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 y: FieldElement = CompressedMontgomeryU::to_edwards_y(&u); // y = (u-1)/(u+1) + + // XXX this does two inversions: the above + one in .decompress() + // is it possible to do one? + CompressedEdwardsY(y.to_bytes()).decompress() + } + + /// Given a Montgomery `u` coordinate, compute an Edwards `y` via + /// `y = (u-1)/(u+1)`. + /// + /// # Return + /// + /// A `FieldElement` corresponding to this coordinate, but in Edwards form. + pub fn to_edwards_y(u: &FieldElement) -> FieldElement { + // Since `u = (1+y)/(1-y)` and `v = √(u(u²+Au+1))`, so `y = (u-1)/(u+1)`. + &(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`. + pub 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 (okay, v_inv) = v_squared.invsqrt(); + let v = &v_inv * &v_squared; + + (okay, v) + } + + /// Given Montgomery coordinates `(u, v)`, recover the Edwards `x` coordinate. + /// + /// # Inputs + /// + /// * `u` and `v` are both `&FieldElement`s, corresponding the the `(u, v)` + /// coordinates of this `CompressedMontgomeryU`. + /// * `sign` is an &u8. + /// + /// ## Explanation of choice of `sign` + /// + /// ### Original Signal behaviour: + /// + /// - `1u8` will leave `x` negative if it is negative, and will negate + /// `x` if it is positive, and + /// - `0u8` will leave `x` positive if it is positive, and will negate + /// `x` if it is negative. + /// + /// Hence, if `sign` is `1u8`, the returned `x` will be negative. + /// Otherwise, if `sign` is `0u8`, the returned `x` will be positive. + /// + /// # Return + /// + /// A `FieldElement`, the Edwards `x` coordinate, by using `(u, v)` to + /// convert from Montgomery to Edwards form via the right-hand side of the + /// equation: `x=(u/v)*sqrt(-A-2)`. + pub fn to_edwards_x(u: &FieldElement, v: &FieldElement, sign: &u8) -> FieldElement { + let mut x: FieldElement = &(u * &v.invert()) * &constants::SQRT_MINUS_APLUS2; + let neg_x: FieldElement = -(&x); + let current_sign: u8 = x.is_negative_ed25519(); + + // Negate x to match the sign: + x.conditional_assign(&neg_x, current_sign ^ sign); + x + } +} + +// ------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------ + +#[cfg(test)] +mod test { + use edwards::Identity; + use super::*; + + /// 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]); + + /// Test Montgomery conversion against the X25519 basepoint. + #[test] + fn basepoint_to_montgomery() { + assert_eq!(constants::ED25519_BASEPOINT_POINT.compress_montgomery().unwrap(), + BASE_CMPRSSD_MONTY); + } + + /// Test Montgomery conversion against the X25519 basepoint. + #[test] + fn basepoint_from_montgomery() { + assert_eq!(BASE_CMPRSSD_MONTY.decompress().unwrap().compress_edwards(), + constants::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 u_minus_one_monty() { + let minus_one = FieldElement::minus_one(); + let minus_one_bytes = minus_one.to_bytes(); + let div_by_zero_u = CompressedMontgomeryU(minus_one_bytes); + assert!(div_by_zero_u.decompress().is_none()); + } + + /// Montgomery compression of the identity point should + /// fail (it's sent to infinity). + #[test] + fn identity_to_monty() { + let id = ExtendedPoint::identity(); + assert!(id.compress_montgomery().is_none()); + } +}