From 57ebc9d7f9066cd7ce33f641764428f8b9d16290 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 13 Mar 2017 18:08:59 -0700 Subject: [PATCH 01/10] Add an OddMultiples helper struct --- src/curve.rs | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/curve.rs b/src/curve.rs index aefecac..3d7007c 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -79,7 +79,7 @@ use core::fmt::Debug; use core::iter::Iterator; -use core::ops::{Add, Sub, Neg}; +use core::ops::{Add, Sub, Neg, Index}; use constants; use field::FieldElement; @@ -957,6 +957,30 @@ impl ExtendedPoint { } } +/// Holds odd multiples 1A, 3A, ..., 15A of a point A. +struct OddMultiples(pub [ProjectiveNielsPoint; 8]); + +impl OddMultiples { + fn create(A: &ExtendedPoint) -> OddMultiples { + let mut Ai = [ProjectiveNielsPoint::identity(); 8]; + let A2 = A.double(); + Ai[0] = A.to_projective_niels(); + for i in 0..7 { + Ai[i+1] = (&A2 + &Ai[i]).to_extended().to_projective_niels(); + } + // Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A] + OddMultiples(Ai) + } +} + +impl Index for OddMultiples { + type Output = ProjectiveNielsPoint; + + fn index<'a>(&'a self, _index: usize) -> &'a ProjectiveNielsPoint { + &(self.0[_index]) + } +} + /// Given a point `A` and scalars `a` and `b`, compute the point /// `aA+bB`, where `B` is the Ed25519 basepoint (i.e., `B = (x,4/5)` /// with x positive). @@ -969,15 +993,6 @@ pub fn double_scalar_mult_vartime(a: &Scalar, A: &ExtendedPoint, b: &Scalar) -> let a_naf = a.non_adjacent_form(); let b_naf = b.non_adjacent_form(); - // Build a lookup table of odd multiples of A - let mut Ai = [ProjectiveNielsPoint::identity(); 8]; - let A2 = A.double(); - Ai[0] = A.to_projective_niels(); - for i in 0..7 { - Ai[i+1] = (&A2 + &Ai[i]).to_extended().to_projective_niels(); - } - // Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A] - // Find starting index let mut i: usize = 255; for j in (0..255).rev() { @@ -987,14 +1002,16 @@ pub fn double_scalar_mult_vartime(a: &Scalar, A: &ExtendedPoint, b: &Scalar) -> } } + let odd_multiples_of_A = OddMultiples::create(A); + let mut r = ProjectivePoint::identity(); loop { let mut t = r.double(); if a_naf[i] > 0 { - t = &t.to_extended() + &Ai[( a_naf[i]/2) as usize]; + t = &t.to_extended() + &odd_multiples_of_A[( a_naf[i]/2) as usize]; } else if a_naf[i] < 0 { - t = &t.to_extended() - &Ai[(-a_naf[i]/2) as usize]; + t = &t.to_extended() - &odd_multiples_of_A[(-a_naf[i]/2) as usize]; } if b_naf[i] > 0 { From abb1b6fef9eaafeecef3fce35bcc455721abd99f Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 13 Mar 2017 20:07:38 -0700 Subject: [PATCH 02/10] Add a variable-time k-fold scalar mult function. --- src/curve.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/curve.rs b/src/curve.rs index 3d7007c..3c313bd 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -958,7 +958,7 @@ impl ExtendedPoint { } /// Holds odd multiples 1A, 3A, ..., 15A of a point A. -struct OddMultiples(pub [ProjectiveNielsPoint; 8]); +struct OddMultiples([ProjectiveNielsPoint; 8]); impl OddMultiples { fn create(A: &ExtendedPoint) -> OddMultiples { @@ -981,6 +981,48 @@ impl Index for OddMultiples { } } + +/// Given a vector of public scalars and a vector of (possibly secret) +/// points, compute +/// +/// c_1 P_1 + ... + c_n P_n. +/// +/// # Warning +/// +/// This function is *not* constant time: its timing depends on the +/// input scalars. +/// +/// # Input +/// +/// A vector of `Scalar`s and a vector of `ExtendedPoints`. It is an +/// error to call this function with two vectors of different lengths. +pub fn k_fold_scalar_mult_vartime(scalars: &Vec, + points: &Vec) + -> ExtendedPoint { + assert_eq!(scalars.len(), points.len()); + + let nafs: Vec<_> = scalars.iter().map(|c| c.non_adjacent_form()).collect(); + let odd_multiples: Vec<_> = points.iter().map(|P| OddMultiples::create(&P)).collect(); + + let mut r = ProjectivePoint::identity(); + + for i in (0..255).rev() { + let mut t = r.double(); + + for (naf, odd_multiple) in nafs.iter().zip(odd_multiples.iter()) { + if naf[i] > 0 { + t = &t.to_extended() + &odd_multiple[( naf[i]/2) as usize]; + } else if naf[i] < 0 { + t = &t.to_extended() - &odd_multiple[(-naf[i]/2) as usize]; + } + } + + r = t.to_projective(); + } + + r.to_extended() +} + /// Given a point `A` and scalars `a` and `b`, compute the point /// `aA+bB`, where `B` is the Ed25519 basepoint (i.e., `B = (x,4/5)` /// with x positive). @@ -1352,6 +1394,15 @@ mod test { assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); } + #[test] + fn k_fold_scalar_mult_vartime_vs_ed25519py() { + let A = A_TIMES_BASEPOINT.decompress().unwrap(); + let points = vec![A,constants::ED25519_BASEPOINT]; + let scalars = vec![A_SCALAR, B_SCALAR]; + let result = k_fold_scalar_mult_vartime(&scalars, &points); + assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); + } + /// Test basepoint.double() versus the 2*basepoint constant. #[test] fn basepoint_double_vs_basepoint2() { @@ -1444,6 +1495,7 @@ mod test { #[cfg(all(test, feature = "bench"))] mod bench { + use rand::OsRng; use test::Bencher; use constants; use super::*; @@ -1471,6 +1523,24 @@ mod bench { b.iter(|| double_scalar_mult_vartime(&A_SCALAR, &A, &B_SCALAR)); } + #[bench] + fn ten_fold_scalar_mult_vartime(b: &mut Bencher) { + let mut csprng: OsRng = OsRng::new().unwrap(); + // Create 10 random scalars + let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect(); + // Create 10 points (by doing scalar mults) + let points: Vec<_> = scalars.iter() + .map(|s| ExtendedPoint::basepoint_mult(s)).collect(); + + // XXX Currently Rust's benchmarking implementation doesn't + // allow you to specify a sequence of random inputs, but only + // many trials of the same input. + // + // Since this is a variable-time function, this means the + // benchmark is only useful as a ballpark measurement. + b.iter(|| k_fold_scalar_mult_vartime(&scalars, &points)); + } + #[bench] fn add_extended_and_projective_niels_output_completed(b: &mut Bencher) { let p1 = constants::ED25519_BASEPOINT; From 057c84abd53cb79d75a0a63fbe08d642f04ad4b8 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 28 Apr 2017 22:51:32 -0700 Subject: [PATCH 03/10] Add a bits() function for scalars --- src/scalar.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 8606913..d97efc1 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -207,6 +207,17 @@ impl Scalar { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]) } + /// Get the bits of the scalar. + pub fn bits(&self) -> [i8;256] { + let mut bits = [0i8; 256]; + for i in 0..256 { + // As i runs from 0..256, the bottom 3 bits index the bit, + // while the upper bits index the byte. + bits[i] = ((self.0[i>>3] >> (i&7)) & 1u8) as i8; + } + bits + } + /// Compute a width-5 "Non-Adjacent Form" of this scalar. /// /// A width-`w` NAF of a positive integer `k` is an expression @@ -220,12 +231,7 @@ impl Scalar { /// nonzero coefficients are as sparse as possible. pub fn non_adjacent_form(&self) -> [i8;256] { // Step 1: write out bits of the scalar - let mut naf = [0i8; 256]; - for i in 0..256 { - // As i runs from 0..256, the bottom 3 bits index the bit, - // while the upper bits index the byte. - naf[i] = ((self.0[i>>3] >> (i&7)) & 1u8) as i8; - } + let mut naf = self.bits(); // Step 2: zero coefficients by carrying them upwards or downwards 'bits: for i in 0..256 { From b5ccb4275927386147468540794ee4b1ccff30f0 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 28 Apr 2017 22:54:00 -0700 Subject: [PATCH 04/10] Rename lminus1 to l_minus_1 --- src/constants.rs | 11 ++++++----- src/scalar.rs | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index c0e6cc2..1821f9a 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -159,12 +159,13 @@ pub const l: Scalar = Scalar([ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 ]); -/// `lminus1` is the order of base point minus one, i.e. 2^252 + +/// `l_minus_1` is the order of base point minus one, i.e. 2^252 + /// 27742317777372353535851937790883648493 - 1, in little-endian form -pub const lminus1: Scalar = Scalar([ 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, - 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 ]); +pub const l_minus_1: Scalar = Scalar([ 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 ]); + /// The 8-torsion subgroup Ɛ[8]. /// /// In the case of Curve25519, it is cyclic; the `i`th element of the diff --git a/src/scalar.rs b/src/scalar.rs index d97efc1..c64b59b 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -106,7 +106,8 @@ impl Neg for Scalar { /// Negate this scalar by computing (l - 1) * self - 0 (mod l). fn neg(self) -> Scalar { - Scalar::multiply_add(&constants::lminus1, &self, &Scalar::zero()) + // XXX this could be more efficient + Scalar::multiply_add(&constants::l_minus_1, &self, &Scalar::zero()) } } From 653f134bc798ad25c4da725404c931eff10ce953 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 28 Apr 2017 22:57:17 -0700 Subject: [PATCH 05/10] Implement Mul, MulAssign, zero(), one() for UnpackedScalar --- src/scalar.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index c64b59b..6468ddd 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -31,6 +31,7 @@ use core::cmp::{Eq, PartialEq}; use core::ops::{Neg, Index, IndexMut}; +use core::ops::{Mul, MulAssign}; use core::fmt::Debug; #[cfg(feature = "std")] @@ -397,6 +398,20 @@ impl IndexMut for UnpackedScalar { } } +impl<'b> MulAssign<&'b UnpackedScalar> for UnpackedScalar { + fn mul_assign(&mut self, _rhs: &'b UnpackedScalar) { + let result = (self as &UnpackedScalar) * _rhs; + self.0 = result.0; + } +} + +impl<'a, 'b> Mul<&'b UnpackedScalar> for &'a UnpackedScalar { + type Output = UnpackedScalar; + fn mul(self, _rhs: &'b UnpackedScalar) -> UnpackedScalar { + UnpackedScalar::multiply_add(self,_rhs, &UnpackedScalar::zero()) + } +} + impl UnpackedScalar { /// Pack the limbs of this `UnpackedScalar` into a `Scalar`. fn pack(&self) -> Scalar { @@ -437,6 +452,16 @@ impl UnpackedScalar { s } + /// Return the zero scalar. + pub fn zero() -> UnpackedScalar { + UnpackedScalar([0,0,0,0,0,0,0,0,0,0,0,0]) + } + + /// Return the one scalar. + pub fn one() -> UnpackedScalar { + UnpackedScalar([1,0,0,0,0,0,0,0,0,0,0,0]) + } + /// Compute `ab+c (mod l)`. pub fn multiply_add(a: &UnpackedScalar, b: &UnpackedScalar, @@ -657,6 +682,14 @@ mod test { } } + #[test] + fn unpacked_mul() { + let x = X.unpack(); + let y = Y.unpack(); + let z = &x * &y; + assert_eq!(z.pack(), X_TIMES_Y); + } + #[test] fn scalar_multiply_only() { let zero = Scalar::zero(); From 6ea1d4dad2bb295513baf6f6c9c23bb9b3e5181f Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 28 Apr 2017 22:59:56 -0700 Subject: [PATCH 06/10] Add an implementation of Scalar inversion --- src/constants.rs | 7 +++++++ src/scalar.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/constants.rs b/src/constants.rs index 1821f9a..50879da 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -166,6 +166,13 @@ pub const l_minus_1: Scalar = Scalar([ 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 ]); +/// `lminus1` is the order of base point minus two, i.e. 2^252 + +/// 27742317777372353535851937790883648493 - 2, in little-endian form +pub const l_minus_2: Scalar = Scalar([ 0xeb, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 ]); + /// The 8-torsion subgroup Ɛ[8]. /// /// In the case of Curve25519, it is cyclic; the `i`th element of the diff --git a/src/scalar.rs b/src/scalar.rs index 6468ddd..d711173 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -209,6 +209,11 @@ impl Scalar { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]) } + /// Compute the multiplicative inverse of this scalar. + pub fn invert(&self) -> Scalar { + self.unpack().invert().pack() + } + /// Get the bits of the scalar. pub fn bits(&self) -> [i8;256] { let mut bits = [0i8; 256]; @@ -462,6 +467,19 @@ impl UnpackedScalar { UnpackedScalar([1,0,0,0,0,0,0,0,0,0,0,0]) } + /// Compute the multiplicative inverse of this scalar. + pub fn invert(&self) -> UnpackedScalar { + let mut y = UnpackedScalar::one(); + // Run through bits of l-2 from highest to least + for bit in constants::l_minus_2.bits().iter().rev() { + y = &y * &y; + if *bit == 1 { + y *= self; + } + } + y + } + /// Compute `ab+c (mod l)`. pub fn multiply_add(a: &UnpackedScalar, b: &UnpackedScalar, @@ -727,6 +745,14 @@ mod test { } } + #[test] + fn invert() { + let x = UnpackedScalar([2,0,0,0,0,0,0,0,0,0,0,0]); + let x_inv = x.invert(); + let should_be_one = &x * &x_inv; + assert_eq!(should_be_one.pack(), Scalar::one()); + } + // Negating a scalar twice should result in the original scalar. #[test] fn scalar_neg() { @@ -757,6 +783,12 @@ mod bench { b.iter(|| Scalar::multiply_add(&X, &Y, &Z) ); } + #[bench] + fn invert(b: &mut Bencher) { + let x = X.unpack(); + b.iter(|| x.invert()); + } + #[bench] fn scalar_unpacked_multiply_add(b: &mut Bencher) { let x = X.unpack(); From 91e11b6318a9144b945140d63a8a00266889265a Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 2 May 2017 21:22:04 -0700 Subject: [PATCH 07/10] Change docstring to match the trait bound --- src/scalar.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index d711173..2ec4959 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -144,21 +144,19 @@ impl CTAssignable for Scalar { } impl Scalar { - /// Return a `Scalar` chosen uniformly at random using a CSPRNG. - /// Panics if the operating system's CSPRNG is unavailable. + /// Return a `Scalar` chosen uniformly at random using a user-provided RNG. /// /// # Inputs /// - /// * `cspring`: any cryptographically secure PRNG which - /// implements the `rand::Rng` interface. + /// * `rng`: any RNG which implements the `rand::Rng` interface. /// /// # Returns /// /// A random scalar within ℤ/lℤ. #[cfg(feature = "std")] - pub fn random(csprng: &mut T) -> Self { + pub fn random(rng: &mut T) -> Self { let mut scalar_bytes = [0u8; 64]; - csprng.fill_bytes(&mut scalar_bytes); + rng.fill_bytes(&mut scalar_bytes); Scalar::reduce(&scalar_bytes) } From b05c8971233fb4c60622820e4b26b41a225b80a4 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 2 May 2017 22:29:18 -0700 Subject: [PATCH 08/10] Implement operators for Scalars using multiply_add --- src/scalar.rs | 121 +++++++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 45 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 2ec4959..03100f6 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -29,10 +29,13 @@ //! between two scalars, the `UnpackedScalar` struct is stored as //! limbs. -use core::cmp::{Eq, PartialEq}; -use core::ops::{Neg, Index, IndexMut}; -use core::ops::{Mul, MulAssign}; use core::fmt::Debug; +use core::ops::Neg; +use core::ops::{Add, AddAssign}; +use core::ops::{Sub, SubAssign}; +use core::ops::{Mul, MulAssign}; +use core::ops::{Index, IndexMut}; +use core::cmp::{Eq, PartialEq}; #[cfg(feature = "std")] use rand::Rng; @@ -102,16 +105,55 @@ impl IndexMut for Scalar { } } -impl Neg for Scalar { - type Output = Scalar; - - /// Negate this scalar by computing (l - 1) * self - 0 (mod l). - fn neg(self) -> Scalar { - // XXX this could be more efficient - Scalar::multiply_add(&constants::l_minus_1, &self, &Scalar::zero()) +impl<'b> MulAssign<&'b Scalar> for Scalar { + fn mul_assign(&mut self, _rhs: &'b Scalar) { + let result = (self as &Scalar) * _rhs; + self.0 = result.0; } } +impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar { + type Output = Scalar; + fn mul(self, _rhs: &'b Scalar) -> Scalar { + Scalar::multiply_add(self, _rhs, &Scalar::zero()) + } +} + +impl<'b> AddAssign<&'b Scalar> for Scalar { + fn add_assign(&mut self, _rhs: &'b Scalar) { + *self = Scalar::multiply_add(&Scalar::one(), self, _rhs); + } +} + +impl<'a, 'b> Add<&'b Scalar> for &'a Scalar { + type Output = Scalar; + fn add(self, _rhs: &'b Scalar) -> Scalar { + Scalar::multiply_add(&Scalar::one(), self, _rhs) + } +} + +impl<'b> SubAssign<&'b Scalar> for Scalar { + fn sub_assign(&mut self, _rhs: &'b Scalar) { + // (l-1)*_rhs + self = self - _rhs + *self = Scalar::multiply_add(&constants::l_minus_1, _rhs, self); + } +} + +impl<'a, 'b> Sub<&'b Scalar> for &'a Scalar { + type Output = Scalar; + fn sub(self, _rhs: &'b Scalar) -> Scalar { + // (l-1)*_rhs + self = self - _rhs + Scalar::multiply_add(&constants::l_minus_1, _rhs, self) + } +} + +impl<'a> Neg for &'a Scalar { + type Output = Scalar; + fn neg(self) -> Scalar { + self * &constants::l_minus_1 + } +} + impl CTAssignable for Scalar { /// Conditionally assign another Scalar to this one. /// @@ -401,20 +443,6 @@ impl IndexMut for UnpackedScalar { } } -impl<'b> MulAssign<&'b UnpackedScalar> for UnpackedScalar { - fn mul_assign(&mut self, _rhs: &'b UnpackedScalar) { - let result = (self as &UnpackedScalar) * _rhs; - self.0 = result.0; - } -} - -impl<'a, 'b> Mul<&'b UnpackedScalar> for &'a UnpackedScalar { - type Output = UnpackedScalar; - fn mul(self, _rhs: &'b UnpackedScalar) -> UnpackedScalar { - UnpackedScalar::multiply_add(self,_rhs, &UnpackedScalar::zero()) - } -} - impl UnpackedScalar { /// Pack the limbs of this `UnpackedScalar` into a `Scalar`. fn pack(&self) -> Scalar { @@ -470,9 +498,9 @@ impl UnpackedScalar { let mut y = UnpackedScalar::one(); // Run through bits of l-2 from highest to least for bit in constants::l_minus_2.bits().iter().rev() { - y = &y * &y; + y = UnpackedScalar::multiply_add(&y, &y, &UnpackedScalar::zero()); if *bit == 1 { - y *= self; + y = UnpackedScalar::multiply_add(&y, self, &UnpackedScalar::zero()); } } y @@ -699,20 +727,24 @@ mod test { } #[test] - fn unpacked_mul() { - let x = X.unpack(); - let y = Y.unpack(); - let z = &x * &y; - assert_eq!(z.pack(), X_TIMES_Y); + fn impl_add() { + let mut two = Scalar::zero(); two[0] = 2; + let two = two; + let one = Scalar::one(); + let should_be_two = &one + &one; + assert_eq!(should_be_two, two); } #[test] - fn scalar_multiply_only() { - let zero = Scalar::zero(); - let test_scalar = Scalar::multiply_add(&X, &Y, &zero); - for i in 0..32 { - assert!(test_scalar[i] == X_TIMES_Y[i]); - } + fn impl_sub() { + let should_be_one = &constants::l - &constants::l_minus_1; + assert_eq!(should_be_one, Scalar::one()); + } + + #[test] + fn impl_mul() { + let should_be_X_TIMES_Y = &X * &Y; + assert_eq!(should_be_X_TIMES_Y, X_TIMES_Y); } #[test] @@ -745,19 +777,18 @@ mod test { #[test] fn invert() { - let x = UnpackedScalar([2,0,0,0,0,0,0,0,0,0,0,0]); - let x_inv = x.invert(); - let should_be_one = &x * &x_inv; - assert_eq!(should_be_one.pack(), Scalar::one()); + let inv_X = X.invert(); + let should_be_one = &inv_X * &X; + assert_eq!(should_be_one, Scalar::one()); } // Negating a scalar twice should result in the original scalar. #[test] - fn scalar_neg() { - let negative_x: Scalar = -X; - let orig: Scalar = -negative_x; + fn neg_twice_is_identity() { + let negative_X = -&X; + let should_be_X = -&negative_X; - assert!(orig == X); + assert_eq!(should_be_X, X); } } From 5100ba4a07a17aeee0a9520c6a9dc17db41346de Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 3 May 2017 17:26:06 -0700 Subject: [PATCH 09/10] Move variable time code into a module --- src/curve.rs | 325 ++++++++++++++++++++++++++------------------------- 1 file changed, 168 insertions(+), 157 deletions(-) diff --git a/src/curve.rs b/src/curve.rs index 732fdf1..26a9ede 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -944,122 +944,6 @@ impl ExtendedPoint { } } -/// Holds odd multiples 1A, 3A, ..., 15A of a point A. -struct OddMultiples([ProjectiveNielsPoint; 8]); - -impl OddMultiples { - fn create(A: &ExtendedPoint) -> OddMultiples { - let mut Ai = [ProjectiveNielsPoint::identity(); 8]; - let A2 = A.double(); - Ai[0] = A.to_projective_niels(); - for i in 0..7 { - Ai[i+1] = (&A2 + &Ai[i]).to_extended().to_projective_niels(); - } - // Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A] - OddMultiples(Ai) - } -} - -impl Index for OddMultiples { - type Output = ProjectiveNielsPoint; - - fn index<'a>(&'a self, _index: usize) -> &'a ProjectiveNielsPoint { - &(self.0[_index]) - } -} - - -/// Given a vector of public scalars and a vector of (possibly secret) -/// points, compute -/// -/// c_1 P_1 + ... + c_n P_n. -/// -/// # Warning -/// -/// This function is *not* constant time: its timing depends on the -/// input scalars. -/// -/// # Input -/// -/// A vector of `Scalar`s and a vector of `ExtendedPoints`. It is an -/// error to call this function with two vectors of different lengths. -pub fn k_fold_scalar_mult_vartime(scalars: &Vec, - points: &Vec) - -> ExtendedPoint { - assert_eq!(scalars.len(), points.len()); - - let nafs: Vec<_> = scalars.iter().map(|c| c.non_adjacent_form()).collect(); - let odd_multiples: Vec<_> = points.iter().map(|P| OddMultiples::create(&P)).collect(); - - let mut r = ProjectivePoint::identity(); - - for i in (0..255).rev() { - let mut t = r.double(); - - for (naf, odd_multiple) in nafs.iter().zip(odd_multiples.iter()) { - if naf[i] > 0 { - t = &t.to_extended() + &odd_multiple[( naf[i]/2) as usize]; - } else if naf[i] < 0 { - t = &t.to_extended() - &odd_multiple[(-naf[i]/2) as usize]; - } - } - - r = t.to_projective(); - } - - r.to_extended() -} - -/// Given a point `A` and scalars `a` and `b`, compute the point -/// `aA+bB`, where `B` is the Ed25519 basepoint (i.e., `B = (x,4/5)` -/// with x positive). -/// -/// # Warning -/// -/// This function is *not* constant time, hence its name. -// XXX should return ExtendedPoint? -pub fn double_scalar_mult_vartime(a: &Scalar, A: &ExtendedPoint, b: &Scalar) -> ProjectivePoint { - let a_naf = a.non_adjacent_form(); - let b_naf = b.non_adjacent_form(); - - // Find starting index - let mut i: usize = 255; - for j in (0..255).rev() { - i = j; - if a_naf[i] != 0 || b_naf[i] != 0 { - break; - } - } - - let odd_multiples_of_A = OddMultiples::create(A); - - let mut r = ProjectivePoint::identity(); - loop { - let mut t = r.double(); - - if a_naf[i] > 0 { - t = &t.to_extended() + &odd_multiples_of_A[( a_naf[i]/2) as usize]; - } else if a_naf[i] < 0 { - t = &t.to_extended() - &odd_multiples_of_A[(-a_naf[i]/2) as usize]; - } - - if b_naf[i] > 0 { - t = &t.to_extended() + &constants::bi[( b_naf[i]/2) as usize]; - } else if b_naf[i] < 0 { - t = &t.to_extended() - &constants::bi[(-b_naf[i]/2) as usize]; - } - - r = t.to_projective(); - - if i == 0 { - break; - } - i -= 1; - } - - r -} - /// Given precomputed points `[P, 2P, 3P, ..., 8P]`, as well as `-8 ≤ /// x ≤ 8`, compute `x * B` in constant time, i.e., without branching /// on x or using it as an array index. @@ -1150,6 +1034,122 @@ impl Debug for ProjectiveNielsPoint { } } +// ------------------------------------------------------------------------ +// Variable-time functions +// ------------------------------------------------------------------------ + +pub mod vartime { + //! Variable-time operations on curve points, useful for non-secret data. + use super::*; + + /// Holds odd multiples 1A, 3A, ..., 15A of a point A. + struct OddMultiples([ProjectiveNielsPoint; 8]); + + impl OddMultiples { + fn create(A: &ExtendedPoint) -> OddMultiples { + let mut Ai = [ProjectiveNielsPoint::identity(); 8]; + let A2 = A.double(); + Ai[0] = A.to_projective_niels(); + for i in 0..7 { + Ai[i+1] = (&A2 + &Ai[i]).to_extended().to_projective_niels(); + } + // Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A] + OddMultiples(Ai) + } + } + + impl Index for OddMultiples { + type Output = ProjectiveNielsPoint; + + fn index<'a>(&'a self, _index: usize) -> &'a ProjectiveNielsPoint { + &(self.0[_index]) + } + } + + /// Given a vector of public scalars and a vector of (possibly secret) + /// points, compute + /// + /// c_1 P_1 + ... + c_n P_n. + /// + /// # Input + /// + /// A vector of `Scalar`s and a vector of `ExtendedPoints`. It is an + /// error to call this function with two vectors of different lengths. + pub fn k_fold_scalar_mult(scalars: &Vec, + points: &Vec) -> ExtendedPoint { + assert_eq!(scalars.len(), points.len()); + + let nafs: Vec<_> = scalars.iter().map(|c| c.non_adjacent_form()).collect(); + let odd_multiples: Vec<_> = points.iter().map(|P| OddMultiples::create(&P)).collect(); + + let mut r = ProjectivePoint::identity(); + + for i in (0..255).rev() { + let mut t = r.double(); + + for (naf, odd_multiple) in nafs.iter().zip(odd_multiples.iter()) { + if naf[i] > 0 { + t = &t.to_extended() + &odd_multiple[( naf[i]/2) as usize]; + } else if naf[i] < 0 { + t = &t.to_extended() - &odd_multiple[(-naf[i]/2) as usize]; + } + } + + r = t.to_projective(); + } + + r.to_extended() + } + + /// Given a point `A` and scalars `a` and `b`, compute the point + /// `aA+bB`, where `B` is the Ed25519 basepoint (i.e., `B = (x,4/5)` + /// with x positive). + pub fn double_scalar_mult_basepoint(a: &Scalar, + A: &ExtendedPoint, + b: &Scalar) -> ProjectivePoint { + let a_naf = a.non_adjacent_form(); + let b_naf = b.non_adjacent_form(); + + // Find starting index + let mut i: usize = 255; + for j in (0..255).rev() { + i = j; + if a_naf[i] != 0 || b_naf[i] != 0 { + break; + } + } + + let odd_multiples_of_A = OddMultiples::create(A); + + let mut r = ProjectivePoint::identity(); + loop { + let mut t = r.double(); + + if a_naf[i] > 0 { + t = &t.to_extended() + &odd_multiples_of_A[( a_naf[i]/2) as usize]; + } else if a_naf[i] < 0 { + t = &t.to_extended() - &odd_multiples_of_A[(-a_naf[i]/2) as usize]; + } + + if b_naf[i] > 0 { + t = &t.to_extended() + &constants::bi[( b_naf[i]/2) as usize]; + } else if b_naf[i] < 0 { + t = &t.to_extended() - &constants::bi[(-b_naf[i]/2) as usize]; + } + + r = t.to_projective(); + + if i == 0 { + break; + } + i -= 1; + } + + r + } + +} + // ------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------ @@ -1373,23 +1373,6 @@ mod test { assert_eq!(aB.compress_edwards(), A_TIMES_BASEPOINT); } - /// Test double_scalar_mult_vartime vs ed25519.py - #[test] - fn double_scalar_mult_vartime_vs_ed25519py() { - let A = A_TIMES_BASEPOINT.decompress().unwrap(); - let result = double_scalar_mult_vartime(&A_SCALAR, &A, &B_SCALAR); - assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); - } - - #[test] - fn k_fold_scalar_mult_vartime_vs_ed25519py() { - let A = A_TIMES_BASEPOINT.decompress().unwrap(); - let points = vec![A,constants::ED25519_BASEPOINT]; - let scalars = vec![A_SCALAR, B_SCALAR]; - let result = k_fold_scalar_mult_vartime(&scalars, &points); - assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); - } - /// Test basepoint.double() versus the 2*basepoint constant. #[test] fn basepoint_double_vs_basepoint2() { @@ -1474,6 +1457,28 @@ mod test { P = P.scalar_mult(&A_SCALAR); } } + + mod vartime { + use super::super::*; + use super::{A_SCALAR, B_SCALAR, A_TIMES_BASEPOINT, DOUBLE_SCALAR_MULT_RESULT}; + + /// Test double_scalar_mult_vartime vs ed25519.py + #[test] + fn double_scalar_mult_basepoint_vs_ed25519py() { + let A = A_TIMES_BASEPOINT.decompress().unwrap(); + let result = vartime::double_scalar_mult_basepoint(&A_SCALAR, &A, &B_SCALAR); + assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); + } + + #[test] + fn k_fold_scalar_mult_vs_ed25519py() { + let A = A_TIMES_BASEPOINT.decompress().unwrap(); + let points = vec![A,constants::ED25519_BASEPOINT]; + let scalars = vec![A_SCALAR, B_SCALAR]; + let result = vartime::k_fold_scalar_mult(&scalars, &points); + assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); + } + } } // ------------------------------------------------------------------------ @@ -1504,30 +1509,6 @@ mod bench { b.iter(|| select_precomputed_point(0, &constants::ED25519_BASEPOINT_TABLE.0[0])); } - #[bench] - fn bench_double_scalar_mult_vartime(b: &mut Bencher) { - let A = A_TIMES_BASEPOINT.decompress().unwrap(); - b.iter(|| double_scalar_mult_vartime(&A_SCALAR, &A, &B_SCALAR)); - } - - #[bench] - fn ten_fold_scalar_mult_vartime(b: &mut Bencher) { - let mut csprng: OsRng = OsRng::new().unwrap(); - // Create 10 random scalars - let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect(); - // Create 10 points (by doing scalar mults) - let points: Vec<_> = scalars.iter() - .map(|s| ExtendedPoint::basepoint_mult(s)).collect(); - - // XXX Currently Rust's benchmarking implementation doesn't - // allow you to specify a sequence of random inputs, but only - // many trials of the same input. - // - // Since this is a variable-time function, this means the - // benchmark is only useful as a ballpark measurement. - b.iter(|| k_fold_scalar_mult_vartime(&scalars, &points)); - } - #[bench] fn add_extended_and_projective_niels_output_completed(b: &mut Bencher) { let p1 = constants::ED25519_BASEPOINT; @@ -1587,4 +1568,34 @@ mod bench { let aB = ExtendedPoint::basepoint_mult(&A_SCALAR); b.iter(|| EdwardsBasepointTable::create(&aB)); } + + mod vartime { + use super::super::*; + use super::super::test::{A_SCALAR, B_SCALAR, A_TIMES_BASEPOINT}; + use super::{Bencher, OsRng}; + + #[bench] + fn bench_double_scalar_mult_basepoint(b: &mut Bencher) { + let A = A_TIMES_BASEPOINT.decompress().unwrap(); + b.iter(|| vartime::double_scalar_mult_basepoint(&A_SCALAR, &A, &B_SCALAR)); + } + + #[bench] + fn ten_fold_scalar_mult(b: &mut Bencher) { + let mut csprng: OsRng = OsRng::new().unwrap(); + // Create 10 random scalars + let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect(); + // Create 10 points (by doing scalar mults) + let points: Vec<_> = scalars.iter() + .map(|s| ExtendedPoint::basepoint_mult(s)).collect(); + + // XXX Currently Rust's benchmarking implementation doesn't + // allow you to specify a sequence of random inputs, but only + // many trials of the same input. + // + // Since this is a variable-time function, this means the + // benchmark is only useful as a ballpark measurement. + b.iter(|| vartime::k_fold_scalar_mult(&scalars, &points)); + } + } } From a479b627a83dca0a4d82e5fb0e876a3b9e8c1d5d Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 3 May 2017 17:39:06 -0700 Subject: [PATCH 10/10] Add Decaf wrapper for k-fold vartime --- src/decaf.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/decaf.rs b/src/decaf.rs index 19d4977..bb73132 100644 --- a/src/decaf.rs +++ b/src/decaf.rs @@ -36,6 +36,7 @@ use collections::boxed::Box; #[cfg(all(feature = "std", feature = "basepoint_table_creation"))] use std::boxed::Box; +use curve; use curve::ExtendedPoint; use curve::EdwardsBasepointTable; use curve::BasepointMult; @@ -304,6 +305,30 @@ impl Debug for DecafPoint { } } +// ------------------------------------------------------------------------ +// Variable-time functions +// ------------------------------------------------------------------------ + +pub mod vartime { + //! Variable-time operations on decaf points, useful for non-secret data. + use super::*; + + /// Given a vector of public scalars and a vector of (possibly secret) + /// points, compute + /// + /// c_1 P_1 + ... + c_n P_n. + /// + /// # Input + /// + /// A vector of `Scalar`s and a vector of `ExtendedPoints`. It is an + /// error to call this function with two vectors of different lengths. + pub fn k_fold_scalar_mult(scalars: &Vec, + points: &Vec) -> DecafPoint { + let extended_points: Vec = points.iter().map(|P| P.0).collect(); + DecafPoint(curve::vartime::k_fold_scalar_mult(scalars, &extended_points)) + } +} + // ------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------