diff --git a/Cargo.toml b/Cargo.toml index 242b13f..0c90b01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,9 +33,12 @@ version = "^0.6" version = "0.4" [features] +nightly = ["basepoint_table_creation"] default = ["std"] std = ["rand"] yolocrypto = [] +# Needs nightly for placement new +basepoint_table_creation = [] bench = [] # The development profile, used for `cargo build`. diff --git a/src/constants.rs b/src/constants.rs index 112d060..bfc103d 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -23,6 +23,7 @@ use field::FieldElement; use curve::ExtendedPoint; use curve::AffineNielsPoint; use curve::CompressedEdwardsY; +use curve::EdwardsBasepointTable; use scalar::Scalar; pub const d: FieldElement = FieldElement([ @@ -100,7 +101,7 @@ pub const BASE_CMPRSSD: CompressedEdwardsY = 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66]); /// Basepoint has y = 4/5. -pub const BASEPOINT: ExtendedPoint = ExtendedPoint{ +pub const ED25519_BASEPOINT: ExtendedPoint = ExtendedPoint{ X: FieldElement([-14297830, -7645148, 16144683, -16471763, 27570974, -2696100, -26142465, 8378389, 20764389, 8758491]), Y: FieldElement([-26843541, -6710886, 13421773, -13421773, 26843546, 6710886, -13421773, 13421773, -26843546, -6710886]), Z: FieldElement([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]), @@ -225,7 +226,7 @@ pub const bi: [AffineNielsPoint; 8] = [ /// /// The table is defined so `constants::base[i][j-1] = j*(16^2i)*B`, /// for `0 ≤ i < 32`, `1 ≤ j < 9`. -pub const base: [[AffineNielsPoint; 8]; 32] = [ +pub const ED25519_BASEPOINT_TABLE: EdwardsBasepointTable = EdwardsBasepointTable([ [ AffineNielsPoint{ y_plus_x: FieldElement([25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605]), @@ -1569,7 +1570,7 @@ pub const base: [[AffineNielsPoint; 8]; 32] = [ y_minus_x: FieldElement([29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684]), xy2d: FieldElement([-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476]), }, -]]; +]]); #[cfg(test)] mod test { @@ -1670,23 +1671,4 @@ mod test { let a_minus_d = &a - &constants::d; assert_eq!(a_minus_d, constants::a_minus_d); } - - /// Test the values in the lookup table of precomputed multiples - /// of the basepoint. - #[test] - fn test_precomputed_basepoint_multiples() { - let bp = constants::BASE_CMPRSSD.decompress().unwrap(); - let mut P = bp; - for i in 0..32 { - // P = (16^2)^i * B - let mut jP = P.to_affine_niels(); - for j in 1..9 { - // constants::base[i][j-1] is supposed to be - // j * (16^2)^i * B - assert_eq!(constants::base[i][j-1], jP); - jP = (&P + &jP).to_extended().to_affine_niels(); - } - P = P.mult_by_pow_2(8); - } - } } diff --git a/src/curve.rs b/src/curve.rs index 93ce31e..2f07fc0 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -90,8 +90,10 @@ use subtle::CTAssignable; use subtle::CTEq; use subtle::CTNegatable; -#[cfg(not(feature = "std"))] +#[cfg(all(not(feature = "std"), feature = "basepoint_table_creation"))] use collections::boxed::Box; +#[cfg(all(feature = "std", feature = "basepoint_table_creation"))] +use std::boxed::Box; // ------------------------------------------------------------------------ // Compressed points @@ -822,17 +824,30 @@ impl ScalarMult for ExtendedPoint { } } -/// Trait for scalar multiplication of a distinguished basepoint. -pub trait BasepointMult { - /// Return the basepoint `B`. - fn basepoint() -> Self; - /// Compute `scalar * B`. - fn basepoint_mult(scalar: &S) -> Self; -} +/// Precomputation +#[derive(Clone)] +pub struct EdwardsBasepointTable(pub [[AffineNielsPoint; 8]; 32]); -impl BasepointMult for ExtendedPoint { - fn basepoint() -> ExtendedPoint { - constants::BASEPOINT +impl EdwardsBasepointTable { + /// Create a table of precomputed multiples of `basepoint`. + #[cfg(feature="basepoint_table_creation")] + pub fn create(basepoint: &ExtendedPoint) -> Box { + // Create the table storage + // XXX can we be assured that this is not allocated on the stack? + // XXX can we skip the initialization without too much unsafety? + let mut table = box EdwardsBasepointTable([[AffineNielsPoint::identity(); 8]; 32]); + let mut P = basepoint.clone(); + for i in 0..32 { + // P = (16^2)^i * B + let mut jP = P.to_affine_niels(); + for j in 1..9 { + // table[i][j-1] is supposed to be j*(16^2)^i*B + table.0[i][j-1] = jP; + jP = (&P + &jP).to_extended().to_affine_niels(); + } + P = P.mult_by_pow_2(8); + } + return table } /// Construct an `ExtendedPoint` from a `Scalar`, `scalar`, by @@ -860,20 +875,20 @@ impl BasepointMult for ExtendedPoint { /// We then use the `select_precomputed_point` function, which /// takes `-8 ≤ x < 8` and `[16^2i * B, ..., 8 * 16^2i * B]`, /// and returns `x * 16^2i * B` in constant time. - fn basepoint_mult(scalar: &Scalar) -> ExtendedPoint { //GeScalarMultBase + pub fn basepoint_mult(&self, scalar: &Scalar) -> ExtendedPoint { let e = scalar.to_radix_16(); let mut h = ExtendedPoint::identity(); let mut t: CompletedPoint; for i in (0..64).filter(|x| x % 2 == 1) { - t = &h + &select_precomputed_point(e[i], &constants::base[i/2]); + t = &h + &select_precomputed_point(e[i], &self.0[i/2]); h = t.to_extended(); } h = h.mult_by_pow_2(4); for i in (0..64).filter(|x| x % 2 == 0) { - t = &h + &select_precomputed_point(e[i], &constants::base[i/2]); + t = &h + &select_precomputed_point(e[i], &self.0[i/2]); h = t.to_extended(); } @@ -881,6 +896,24 @@ impl BasepointMult for ExtendedPoint { } } +/// Trait for scalar multiplication of a distinguished basepoint. +pub trait BasepointMult { + /// Return the basepoint `B`. + fn basepoint() -> Self; + /// Compute `scalar * B`. + fn basepoint_mult(scalar: &S) -> Self; +} + +impl BasepointMult for ExtendedPoint { + fn basepoint() -> ExtendedPoint { + constants::ED25519_BASEPOINT + } + + fn basepoint_mult(scalar: &Scalar) -> ExtendedPoint { + constants::ED25519_BASEPOINT_TABLE.basepoint_mult(scalar) + } +} + impl ExtendedPoint { /// Multiply by the cofactor: compute `8 * self`. /// @@ -1142,7 +1175,7 @@ mod test { /// Test Montgomery conversion against the X25519 basepoint. #[test] fn basepoint_to_montgomery() { - assert_eq!(constants::BASEPOINT.compress_montgomery().unwrap(), + assert_eq!(constants::ED25519_BASEPOINT.compress_montgomery().unwrap(), BASE_CMPRSSD_MONTY); } @@ -1195,10 +1228,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::BASEPOINT.X)); - assert_eq!(minus_basepoint.Y, constants::BASEPOINT.Y); - assert_eq!(minus_basepoint.Z, constants::BASEPOINT.Z); - assert_eq!(minus_basepoint.T, -(&constants::BASEPOINT.T)); + 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)); } /// Test that computing 1*basepoint gives the correct basepoint. @@ -1213,7 +1246,7 @@ mod test { /// using basepoint + basepoint versus the 2*basepoint constant. #[test] fn basepoint_plus_basepoint_vs_basepoint2() { - let bp = constants::BASEPOINT; + let bp = constants::ED25519_BASEPOINT; let bp_added = &bp + &bp; assert_eq!(bp_added.compress_edwards(), BASE2_CMPRSSD); } @@ -1222,7 +1255,7 @@ mod test { /// using the basepoint, basepoint2 constants #[test] fn basepoint_plus_basepoint_projective_niels_vs_basepoint2() { - let bp = constants::BASEPOINT; + let bp = constants::ED25519_BASEPOINT; let bp_added = (&bp + &bp.to_projective_niels()).to_extended(); assert_eq!(bp_added.compress_edwards(), BASE2_CMPRSSD); } @@ -1231,7 +1264,7 @@ mod test { /// using the basepoint, basepoint2 constants #[test] fn basepoint_plus_basepoint_affine_niels_vs_basepoint2() { - let bp = constants::BASEPOINT; + let bp = constants::ED25519_BASEPOINT; 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); @@ -1270,10 +1303,28 @@ mod test { assert_eq!(aB.compress_edwards(), A_TIMES_BASEPOINT); } + /// Test that multiplication by the basepoint order kills the basepoint + #[test] + fn basepoint_mult_by_basepoint_order() { + let should_be_id = ExtendedPoint::basepoint_mult(&constants::l); + assert!(should_be_id.is_identity()); + } + + /// Test precomputed basepoint mult + #[test] + #[cfg(feature="basepoint_table_creation")] + fn test_precomputed_basepoint_mult() { + let table = EdwardsBasepointTable::create(&constants::ED25519_BASEPOINT); + let aB_1 = ExtendedPoint::basepoint_mult(&A_SCALAR); + let aB_2 = table.basepoint_mult(&A_SCALAR); + assert_eq!(aB_1.compress_edwards(), + aB_2.compress_edwards()); + } + /// Test scalar_mult versus a known scalar multiple from ed25519.py #[test] fn scalar_mult_vs_ed25519py() { - let aB = constants::BASEPOINT.scalar_mult(&A_SCALAR); + let aB = constants::ED25519_BASEPOINT.scalar_mult(&A_SCALAR); assert_eq!(aB.compress_edwards(), A_TIMES_BASEPOINT); } @@ -1288,7 +1339,7 @@ mod test { /// Test basepoint.double() versus the 2*basepoint constant. #[test] fn basepoint_double_vs_basepoint2() { - assert_eq!(constants::BASEPOINT.double().compress_edwards(), + assert_eq!(constants::ED25519_BASEPOINT.double().compress_edwards(), BASE2_CMPRSSD); } @@ -1303,14 +1354,15 @@ mod test { /// Check that converting to projective and then back to extended round-trips. #[test] fn basepoint_projective_extended_round_trip() { - assert_eq!(constants::BASEPOINT.to_projective().to_extended().compress_edwards(), + assert_eq!(constants::ED25519_BASEPOINT + .to_projective().to_extended().compress_edwards(), constants::BASE_CMPRSSD); } /// Test computing 16*basepoint vs mult_by_pow_2(4) #[test] fn basepoint16_vs_mult_by_pow_2_4() { - let bp16 = constants::BASEPOINT.mult_by_pow_2(4); + let bp16 = constants::ED25519_BASEPOINT.mult_by_pow_2(4); assert_eq!(bp16.compress_edwards(), BASE16_CMPRSSD); } @@ -1319,7 +1371,7 @@ mod test { fn conditional_assign_for_affine_niels_point() { let id = AffineNielsPoint::identity(); let mut p1 = AffineNielsPoint::identity(); - let bp = constants::BASEPOINT.to_affine_niels(); + let bp = constants::ED25519_BASEPOINT.to_affine_niels(); p1.conditional_assign(&bp, 0); assert_eq!(p1, id); @@ -1330,7 +1382,7 @@ mod test { #[test] fn is_small_order() { // The basepoint has large prime order - assert!(constants::BASEPOINT.is_small_order() == false); + assert!(constants::ED25519_BASEPOINT.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); @@ -1345,8 +1397,8 @@ mod test { #[test] fn is_identity() { - assert!(ExtendedPoint::identity().is_identity() == true); - assert!( constants::BASEPOINT.is_identity() == false); + assert!( ExtendedPoint::identity().is_identity() == true); + assert!(constants::ED25519_BASEPOINT.is_identity() == false); } } @@ -1368,13 +1420,13 @@ mod bench { #[bench] fn scalar_mult(b: &mut Bencher) { - let bp = constants::BASEPOINT; + let bp = constants::ED25519_BASEPOINT; b.iter(|| bp.scalar_mult(&A_SCALAR)); } #[bench] fn bench_select_precomputed_point(b: &mut Bencher) { - b.iter(|| select_precomputed_point(0, &constants::base[12])); + b.iter(|| select_precomputed_point(0, &constants::ED25519_BASEPOINT_TABLE.0[0])); } #[bench] @@ -1385,54 +1437,61 @@ mod bench { #[bench] fn add_extended_and_cached_output_completed(b: &mut Bencher) { - let p1 = constants::BASEPOINT; - let p2 = constants::BASEPOINT.to_projective_niels(); + let p1 = constants::ED25519_BASEPOINT; + let p2 = constants::ED25519_BASEPOINT.to_projective_niels(); b.iter(|| &p1 + &p2); } #[bench] fn add_extended_and_cached_output_extended(b: &mut Bencher) { - let p1 = constants::BASEPOINT; - let p2 = constants::BASEPOINT.to_projective_niels(); + let p1 = constants::ED25519_BASEPOINT; + let p2 = constants::ED25519_BASEPOINT.to_projective_niels(); b.iter(|| (&p1 + &p2).to_extended()); } #[bench] fn add_extended_and_precomputed_output_completed(b: &mut Bencher) { - let p1 = constants::BASEPOINT; - let p2 = select_precomputed_point(6, &constants::base[27]); + let p1 = constants::ED25519_BASEPOINT; + let p2 = constants::ED25519_BASEPOINT.to_affine_niels(); b.iter(|| &p1 + &p2); } #[bench] fn add_extended_and_precomputed_output_extended(b: &mut Bencher) { - let p1 = constants::BASEPOINT; - let p2 = select_precomputed_point(6, &constants::base[27]); + let p1 = constants::ED25519_BASEPOINT; + let p2 = constants::ED25519_BASEPOINT.to_affine_niels(); b.iter(|| (&p1 + &p2).to_extended()); } #[bench] fn projective_double_output_completed(b: &mut Bencher) { - let p1 = constants::BASEPOINT.to_projective(); + let p1 = constants::ED25519_BASEPOINT.to_projective(); b.iter(|| p1.double() ); } #[bench] fn extended_double_output_extended(b: &mut Bencher) { - let p1 = constants::BASEPOINT; + let p1 = constants::ED25519_BASEPOINT; b.iter(|| p1.double() ); } #[bench] fn mult_by_cofactor(b: &mut Bencher) { - let p1 = constants::BASEPOINT; + let p1 = constants::ED25519_BASEPOINT; b.iter(|| p1.mult_by_cofactor() ); } + + #[cfg(feature="basepoint_table_creation")] + #[bench] + fn create_basepoint_table(b: &mut Bencher) { + let aB = ExtendedPoint::basepoint_mult(&A_SCALAR); + b.iter(|| EdwardsBasepointTable::create(&aB)); + } } diff --git a/src/decaf.rs b/src/decaf.rs index 775186a..19d4977 100644 --- a/src/decaf.rs +++ b/src/decaf.rs @@ -31,7 +31,13 @@ use subtle::CTNegatable; use core::ops::{Add, Sub, Neg}; +#[cfg(all(not(feature = "std"), feature = "basepoint_table_creation"))] +use collections::boxed::Box; +#[cfg(all(feature = "std", feature = "basepoint_table_creation"))] +use std::boxed::Box; + use curve::ExtendedPoint; +use curve::EdwardsBasepointTable; use curve::BasepointMult; use curve::ScalarMult; use curve::Identity; @@ -253,7 +259,7 @@ impl BasepointMult for DecafPoint { // XXX is this actually in the image of the isogeny, // or do we need a different basepoint? fn basepoint() -> DecafPoint { - DecafPoint(constants::BASEPOINT) + DecafPoint(ExtendedPoint::basepoint()) } fn basepoint_mult(scalar: &Scalar) -> DecafPoint { @@ -261,6 +267,25 @@ impl BasepointMult for DecafPoint { } } + +/// Precomputation +#[derive(Clone)] +pub struct DecafBasepointTable(EdwardsBasepointTable); + +impl DecafBasepointTable { + /// Create a precomputed table of multiples of the given `basepoint`. + #[cfg(feature = "basepoint_table_creation")] + pub fn create(basepoint: &DecafPoint) -> Box { + let edwards_table = EdwardsBasepointTable::create(&basepoint.0); + box DecafBasepointTable(*edwards_table) + } + + /// Use the precomputed table to quickly compute `scalar * basepoint` + pub fn basepoint_mult(&self, scalar: &Scalar) -> DecafPoint { + DecafPoint(self.0.basepoint_mult(scalar)) + } +} + // ------------------------------------------------------------------------ // Debug traits // ------------------------------------------------------------------------ @@ -322,7 +347,7 @@ mod test { // Check that bp_recaf differs from bp by a point of order 4 let diff = &ExtendedPoint::basepoint() - &bp_recaf; let diff4 = diff.mult_by_pow_2(4); - assert_eq!(diff4.compress_edwards(), ExtendedPoint::identity().compress_edwards()); + assert_eq!(diff4.compress_edwards(), CompressedEdwardsY::identity()); } #[test] @@ -356,6 +381,19 @@ mod test { assert_eq!(P, Q); } } + + /// Test basepoint_mult versus a newly-generated DecafBasepointTable + #[test] + #[cfg(feature = "basepoint_table_creation")] + fn basepoint_mult_vs_decafbasepointtable() { + let table = DecafBasepointTable::create(&DecafPoint::basepoint()); + let mut rng = OsRng::new().unwrap(); + let s = Scalar::random(&mut rng); + let basepoint_mult_s = DecafPoint::basepoint_mult(&s); + let table_basepoint_mult_s = table.basepoint_mult(&s); + + assert_eq!(basepoint_mult_s, table_basepoint_mult_s); + } } #[cfg(all(test, feature = "bench"))] diff --git a/src/lib.rs b/src/lib.rs index 8903710..1d8c1d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(collections))] +#![cfg_attr(feature = "nightly", feature(box_syntax))] #![allow(unused_features)] #![cfg_attr(feature = "bench", feature(test))] #![deny(missing_docs)] // refuse to compile if documentation is missing