From 9c4046c3d9a95a4ef3a8152a90411a3c9b19c59a Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sun, 30 Jul 2017 21:13:56 -0700 Subject: [PATCH 1/7] Add constant-time k-fold scalar multiplication --- src/curve.rs | 133 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 123 insertions(+), 10 deletions(-) diff --git a/src/curve.rs b/src/curve.rs index deac819..1cf1739 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -884,20 +884,38 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a ExtendedPoint { /// Uses a window of size 4. Note: for scalar multiplication of /// the basepoint, `basepoint_mult` is approximately 4x faster. fn mul(self, scalar: &'b Scalar) -> ExtendedPoint { - let A = self.to_projective_niels(); - let mut As: [ProjectiveNielsPoint; 8] = [A; 8]; + // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] + let P = self.to_projective_niels(); + let mut lookup_table: [ProjectiveNielsPoint; 8] = [P; 8]; for i in 0..7 { - As[i+1] = (self + &As[i]).to_extended().to_projective_niels(); + lookup_table[i+1] = (self + &lookup_table[i]) + .to_extended().to_projective_niels(); } - let e = scalar.to_radix_16(); - let mut h = ExtendedPoint::identity(); - let mut t: CompletedPoint; + + // Setting s = scalar, compute + // + // s = s_0 + s_1*16^1 + ... + s_63*16^63, + // + // with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`. + let scalar_digits = scalar.to_radix_16(); + + // Compute s*P as + // + // s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63) + // s*P = P*s_0 + P*s_1*16^1 + P*s_2*16^2 + ... + P*s_63*16^63 + // s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...)) + // + // We sum right-to-left. + let mut Q = ExtendedPoint::identity(); for i in (0..64).rev() { - h = h.mult_by_pow_2(4); - t = &h + &select_precomputed_point(e[i], &As); - h = t.to_extended(); + // Q = 16*Q + Q = Q.mult_by_pow_2(4); + // R = s_i * Q + let R = select_precomputed_point(scalar_digits[i], &lookup_table); + // Q = Q + R + Q = (&Q + &R).to_extended(); } - h + Q } } @@ -913,6 +931,74 @@ impl<'a, 'b> Mul<&'b ExtendedPoint> for &'a Scalar { } } +/// Given a vector of (possibly secret) scalars and a vector of +/// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`. +/// +/// This function has the same behaviour as +/// `vartime::k_fold_scalar_mult` but is constant-time. +/// +/// # 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. +#[cfg(any(feature = "alloc", feature = "std"))] +pub fn k_fold_scalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint + where I: IntoIterator, + J: IntoIterator +{ + //assert_eq!(scalars.len(), points.len()); + + // Setting s_i = i-th scalar, compute + // + // s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63, + // + // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. + let scalar_digits_list: Vec<_> = scalars.into_iter() + .map(|c| c.to_radix_16()).collect(); + + let lookup_tables: Vec<_> = points.into_iter() + .map(|P_i| { + // Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i] + let mut lookup_table = [P_i.to_projective_niels(); 8]; + for j in 0..7 { + lookup_table[j+1] = (P_i + &lookup_table[j]) + .to_extended().to_projective_niels(); + } + lookup_table + }).collect(); + + // Compute s_1*P_1 + ... + s_n*P_n: since + // + // s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63) + // s_i*P_i = P_i*s_{i,0} + P_i*s_{i,1}*16^1 + ... + P_i*s_{i,63}*16^63 + // s_i*P_i = P_i*s_{i,0} + 16*(P_i*s_{i,1} + 16*( ... + 16*P_i*s_{i,63})...) + // + // we have the two-dimensional sum + // + // s_1*P_1 = P_1*s_{1,0} + 16*(P_1*s_{1,1} + 16*( ... + 16*P_1*s_{1,63})...) + // + s_2*P_2 = + P_2*s_{2,0} + 16*(P_2*s_{2,1} + 16*( ... + 16*P_2*s_{2,63})...) + // ... + // + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...) + // + // We sum column-wise top-to-bottom, then right-to-left, + // multiplying by 16 only once per column. + // + // This provides the speedup over doing n independent scalar + // mults: we perform 63 multiplications by 16 instead of 63*n + // multiplications, saving 252*(n-1) doublings. + let mut Q = ExtendedPoint::identity(); + for j in (0..64).rev() { + Q = Q.mult_by_pow_2(4); + let it = scalar_digits_list.iter().zip(lookup_tables.iter()); + for (s_i, lookup_table_i) in it { + // R_i = s_{i,j} * P_i + let R_i = select_precomputed_point(s_i[j], lookup_table_i); + // Q = Q + R_i + Q = (&Q + &R_i).to_extended(); + } + } + Q +} /// Precomputation #[derive(Clone)] @@ -1635,6 +1721,21 @@ mod test { ); assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); } + + #[test] + fn k_fold_scalar_mult_vartime_vs_consttime() { + let A = A_TIMES_BASEPOINT.decompress().unwrap(); + let result_vartime = vartime::k_fold_scalar_mult( + &[A_SCALAR, B_SCALAR], + &[A, constants::ED25519_BASEPOINT] + ); + let result_consttime = k_fold_scalar_mult( + &[A_SCALAR, B_SCALAR], + &[A, constants::ED25519_BASEPOINT] + ); + + assert_eq!(result_vartime.compress_edwards(), result_consttime.compress_edwards()); + } } #[cfg(feature = "serde")] @@ -1761,6 +1862,18 @@ mod bench { b.iter(|| EdwardsBasepointTable::create(&aB)); } + #[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 B = &constants::ED25519_BASEPOINT_TABLE; + let points: Vec<_> = scalars.iter().map(|s| B * &s).collect(); + + b.iter(|| k_fold_scalar_mult(&scalars, &points)); + } + mod vartime { use super::super::*; use super::super::test::{A_SCALAR, B_SCALAR, A_TIMES_BASEPOINT}; From aaefb90ed3cc3dd4abb87be9439cce4a04da8052 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sun, 30 Jul 2017 22:16:22 -0700 Subject: [PATCH 2/7] Rename `k_fold_scalar_mult` to `multiscalar_mult` --- src/curve.rs | 20 ++++++++++---------- src/decaf.rs | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/curve.rs b/src/curve.rs index 1cf1739..16cc60b 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -935,14 +935,14 @@ impl<'a, 'b> Mul<&'b ExtendedPoint> for &'a Scalar { /// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`. /// /// This function has the same behaviour as -/// `vartime::k_fold_scalar_mult` but is constant-time. +/// `vartime::multiscalar_mult` but is constant-time. /// /// # 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. #[cfg(any(feature = "alloc", feature = "std"))] -pub fn k_fold_scalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint +pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint where I: IntoIterator, J: IntoIterator { @@ -1281,7 +1281,7 @@ pub mod vartime { /// A vector of `Scalar`s and a vector of `ExtendedPoints`. It is an /// error to call this function with two vectors of different lengths. #[cfg(any(feature = "alloc", feature = "std"))] - pub fn k_fold_scalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint + pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint where I: IntoIterator, J: IntoIterator { @@ -1713,9 +1713,9 @@ mod test { } #[test] - fn k_fold_scalar_mult_vs_ed25519py() { + fn multiscalar_mult_vs_ed25519py() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); - let result = vartime::k_fold_scalar_mult( + let result = vartime::multiscalar_mult( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT] ); @@ -1723,13 +1723,13 @@ mod test { } #[test] - fn k_fold_scalar_mult_vartime_vs_consttime() { + fn multiscalar_mult_vartime_vs_consttime() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); - let result_vartime = vartime::k_fold_scalar_mult( + let result_vartime = vartime::multiscalar_mult( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT] ); - let result_consttime = k_fold_scalar_mult( + let result_consttime = multiscalar_mult( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT] ); @@ -1871,7 +1871,7 @@ mod bench { let B = &constants::ED25519_BASEPOINT_TABLE; let points: Vec<_> = scalars.iter().map(|s| B * &s).collect(); - b.iter(|| k_fold_scalar_mult(&scalars, &points)); + b.iter(|| multiscalar_mult(&scalars, &points)); } mod vartime { @@ -1900,7 +1900,7 @@ mod bench { // // 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)); + b.iter(|| vartime::multiscalar_mult(&scalars, &points)); } } } diff --git a/src/decaf.rs b/src/decaf.rs index 2b489b0..15517c7 100644 --- a/src/decaf.rs +++ b/src/decaf.rs @@ -683,12 +683,12 @@ pub mod vartime { /// /// 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<'a, 'b, I, J>(scalars: I, points: J) -> DecafPoint + pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> DecafPoint where I: IntoIterator, J: IntoIterator { let extended_points = points.into_iter().map(|P| &P.0); - DecafPoint(curve::vartime::k_fold_scalar_mult(scalars, extended_points)) + DecafPoint(curve::vartime::multiscalar_mult(scalars, extended_points)) } } From 63ee9d21ae085d7011d82df42ca964a70b0cc361 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sun, 30 Jul 2017 22:24:58 -0700 Subject: [PATCH 3/7] tweak code arrangement to keep comments together --- src/curve.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/curve.rs b/src/curve.rs index 16cc60b..415d4a2 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -948,14 +948,6 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint { //assert_eq!(scalars.len(), points.len()); - // Setting s_i = i-th scalar, compute - // - // s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63, - // - // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. - let scalar_digits_list: Vec<_> = scalars.into_iter() - .map(|c| c.to_radix_16()).collect(); - let lookup_tables: Vec<_> = points.into_iter() .map(|P_i| { // Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i] @@ -967,6 +959,14 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint lookup_table }).collect(); + // Setting s_i = i-th scalar, compute + // + // s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63, + // + // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. + let scalar_digits_list: Vec<_> = scalars.into_iter() + .map(|c| c.to_radix_16()).collect(); + // Compute s_1*P_1 + ... + s_n*P_n: since // // s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63) From 2d01aa1bf78df4b1de1aca1b2f0a6ce2a048a2b8 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sun, 30 Jul 2017 22:26:18 -0700 Subject: [PATCH 4/7] Add fixme note on cache awareness --- src/curve.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/curve.rs b/src/curve.rs index 415d4a2..08e80e5 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -987,6 +987,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint // mults: we perform 63 multiplications by 16 instead of 63*n // multiplications, saving 252*(n-1) doublings. let mut Q = ExtendedPoint::identity(); + // XXX this algorithm makes no effort to be cache-aware; maybe it could be improved? for j in (0..64).rev() { Q = Q.mult_by_pow_2(4); let it = scalar_digits_list.iter().zip(lookup_tables.iter()); From 720da348c0f1689556c949576345abc96a27895e Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sun, 30 Jul 2017 23:54:13 -0700 Subject: [PATCH 5/7] Add size checking to multiscalar multiplication. --- src/curve.rs | 49 ++++++++++++++++++++++++++++++++++++------------- src/decaf.rs | 24 +++++++++++++++++++++++- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/curve.rs b/src/curve.rs index 08e80e5..70e170e 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -944,12 +944,15 @@ impl<'a, 'b> Mul<&'b ExtendedPoint> for &'a Scalar { #[cfg(any(feature = "alloc", feature = "std"))] pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint where I: IntoIterator, - J: IntoIterator + I::IntoIter: ExactSizeIterator, + J: IntoIterator, + J::IntoIter: ExactSizeIterator, { - //assert_eq!(scalars.len(), points.len()); + let scalars_iter = scalars.into_iter(); + let points_iter = points.into_iter(); + assert_eq!(scalars_iter.len(), points_iter.len()); - let lookup_tables: Vec<_> = points.into_iter() - .map(|P_i| { + let lookup_tables: Vec<_> = points_iter.map(|P_i| { // Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i] let mut lookup_table = [P_i.to_projective_niels(); 8]; for j in 0..7 { @@ -964,8 +967,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint // s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63, // // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. - let scalar_digits_list: Vec<_> = scalars.into_iter() - .map(|c| c.to_radix_16()).collect(); + let scalar_digits_list: Vec<_> = scalars_iter.map(|c| c.to_radix_16()).collect(); // Compute s_1*P_1 + ... + s_n*P_n: since // @@ -1283,15 +1285,18 @@ pub mod vartime { /// error to call this function with two vectors of different lengths. #[cfg(any(feature = "alloc", feature = "std"))] pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint - where I: IntoIterator, - J: IntoIterator + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + J: IntoIterator, + J::IntoIter: ExactSizeIterator, { - //assert_eq!(scalars.len(), points.len()); + let scalars_iter = scalars.into_iter(); + let points_iter = points.into_iter(); + assert_eq!(scalars_iter.len(), points_iter.len()); - let nafs: Vec<_> = scalars.into_iter() - .map(|c| c.non_adjacent_form()).collect(); - let odd_multiples: Vec<_> = points.into_iter() - .map(|P| OddMultiples::create(P)).collect(); + 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(); @@ -1701,6 +1706,15 @@ mod test { assert!(P1.compress().as_bytes() == P2.compress().as_bytes()); } + #[test] + #[should_panic] + fn multiscalar_mult_with_wrong_sized_arguments() { + let result = multiscalar_mult( + &[A_SCALAR, B_SCALAR], + &[constants::ED25519_BASEPOINT], + ); + } + mod vartime { use super::super::*; use super::{A_SCALAR, B_SCALAR, A_TIMES_BASEPOINT, DOUBLE_SCALAR_MULT_RESULT}; @@ -1713,6 +1727,15 @@ mod test { assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); } + #[test] + #[should_panic] + fn multiscalar_mult_with_wrong_sized_arguments() { + let result = vartime::multiscalar_mult( + &[A_SCALAR, B_SCALAR], + &[constants::ED25519_BASEPOINT], + ); + } + #[test] fn multiscalar_mult_vs_ed25519py() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); diff --git a/src/decaf.rs b/src/decaf.rs index 15517c7..9abb5bc 100644 --- a/src/decaf.rs +++ b/src/decaf.rs @@ -577,6 +577,26 @@ impl<'a, 'b> Mul<&'b DecafPoint> for &'a Scalar { } } +/// Given a vector of (possibly secret) scalars and a vector of +/// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`. +/// +/// This function has the same behaviour as +/// `vartime::multiscalar_mult` but is constant-time. +/// +/// # Input +/// +/// A vector of `Scalar`s and a vector of `DecafPoints`. It is an +/// error to call this function with two vectors of different lengths. +#[cfg(any(feature = "alloc", feature = "std"))] +pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> DecafPoint + where I: IntoIterator, + I::IntoIter: ExactSizeIterator, + J: IntoIterator, + J::IntoIter: ExactSizeIterator, +{ + let extended_points = points.into_iter().map(|P| &P.0); + DecafPoint(curve::multiscalar_mult(scalars, extended_points)) +} /// Precomputation #[derive(Clone)] @@ -685,7 +705,9 @@ pub mod vartime { /// error to call this function with two vectors of different lengths. pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> DecafPoint where I: IntoIterator, - J: IntoIterator + I::IntoIter: ExactSizeIterator, + J: IntoIterator, + J::IntoIter: ExactSizeIterator, { let extended_points = points.into_iter().map(|P| &P.0); DecafPoint(curve::vartime::multiscalar_mult(scalars, extended_points)) From d2ce1ce5dc7133f8fe7f96ebd03a2242b042621f Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 31 Jul 2017 18:23:26 -0700 Subject: [PATCH 6/7] Revert "Add size checking to multiscalar multiplication." This reverts commit 720da348c0f1689556c949576345abc96a27895e. Unfortunately, iter::chain on two ExactSizeIterators does not produce an ExactSizeIterator, for reasons described here: https://github.com/rust-lang/rust/issues/34433 . --- src/curve.rs | 49 +++++++++++++------------------------------------ src/decaf.rs | 24 +----------------------- 2 files changed, 14 insertions(+), 59 deletions(-) diff --git a/src/curve.rs b/src/curve.rs index 70e170e..08e80e5 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -944,15 +944,12 @@ impl<'a, 'b> Mul<&'b ExtendedPoint> for &'a Scalar { #[cfg(any(feature = "alloc", feature = "std"))] pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint where I: IntoIterator, - I::IntoIter: ExactSizeIterator, - J: IntoIterator, - J::IntoIter: ExactSizeIterator, + J: IntoIterator { - let scalars_iter = scalars.into_iter(); - let points_iter = points.into_iter(); - assert_eq!(scalars_iter.len(), points_iter.len()); + //assert_eq!(scalars.len(), points.len()); - let lookup_tables: Vec<_> = points_iter.map(|P_i| { + let lookup_tables: Vec<_> = points.into_iter() + .map(|P_i| { // Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i] let mut lookup_table = [P_i.to_projective_niels(); 8]; for j in 0..7 { @@ -967,7 +964,8 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint // s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63, // // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. - let scalar_digits_list: Vec<_> = scalars_iter.map(|c| c.to_radix_16()).collect(); + let scalar_digits_list: Vec<_> = scalars.into_iter() + .map(|c| c.to_radix_16()).collect(); // Compute s_1*P_1 + ... + s_n*P_n: since // @@ -1285,18 +1283,15 @@ pub mod vartime { /// error to call this function with two vectors of different lengths. #[cfg(any(feature = "alloc", feature = "std"))] pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint - where - I: IntoIterator, - I::IntoIter: ExactSizeIterator, - J: IntoIterator, - J::IntoIter: ExactSizeIterator, + where I: IntoIterator, + J: IntoIterator { - let scalars_iter = scalars.into_iter(); - let points_iter = points.into_iter(); - assert_eq!(scalars_iter.len(), points_iter.len()); + //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 nafs: Vec<_> = scalars.into_iter() + .map(|c| c.non_adjacent_form()).collect(); + let odd_multiples: Vec<_> = points.into_iter() + .map(|P| OddMultiples::create(P)).collect(); let mut r = ProjectivePoint::identity(); @@ -1706,15 +1701,6 @@ mod test { assert!(P1.compress().as_bytes() == P2.compress().as_bytes()); } - #[test] - #[should_panic] - fn multiscalar_mult_with_wrong_sized_arguments() { - let result = multiscalar_mult( - &[A_SCALAR, B_SCALAR], - &[constants::ED25519_BASEPOINT], - ); - } - mod vartime { use super::super::*; use super::{A_SCALAR, B_SCALAR, A_TIMES_BASEPOINT, DOUBLE_SCALAR_MULT_RESULT}; @@ -1727,15 +1713,6 @@ mod test { assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); } - #[test] - #[should_panic] - fn multiscalar_mult_with_wrong_sized_arguments() { - let result = vartime::multiscalar_mult( - &[A_SCALAR, B_SCALAR], - &[constants::ED25519_BASEPOINT], - ); - } - #[test] fn multiscalar_mult_vs_ed25519py() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); diff --git a/src/decaf.rs b/src/decaf.rs index 9abb5bc..15517c7 100644 --- a/src/decaf.rs +++ b/src/decaf.rs @@ -577,26 +577,6 @@ impl<'a, 'b> Mul<&'b DecafPoint> for &'a Scalar { } } -/// Given a vector of (possibly secret) scalars and a vector of -/// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`. -/// -/// This function has the same behaviour as -/// `vartime::multiscalar_mult` but is constant-time. -/// -/// # Input -/// -/// A vector of `Scalar`s and a vector of `DecafPoints`. It is an -/// error to call this function with two vectors of different lengths. -#[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> DecafPoint - where I: IntoIterator, - I::IntoIter: ExactSizeIterator, - J: IntoIterator, - J::IntoIter: ExactSizeIterator, -{ - let extended_points = points.into_iter().map(|P| &P.0); - DecafPoint(curve::multiscalar_mult(scalars, extended_points)) -} /// Precomputation #[derive(Clone)] @@ -705,9 +685,7 @@ pub mod vartime { /// error to call this function with two vectors of different lengths. pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> DecafPoint where I: IntoIterator, - I::IntoIter: ExactSizeIterator, - J: IntoIterator, - J::IntoIter: ExactSizeIterator, + J: IntoIterator { let extended_points = points.into_iter().map(|P| &P.0); DecafPoint(curve::vartime::multiscalar_mult(scalars, extended_points)) From ddaf602a0956abe60f9bb9ac27251ce9814096e7 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 31 Jul 2017 18:40:11 -0700 Subject: [PATCH 7/7] Add multiscalar_mult to Decaf. --- src/decaf.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/decaf.rs b/src/decaf.rs index 15517c7..a6e809b 100644 --- a/src/decaf.rs +++ b/src/decaf.rs @@ -577,6 +577,24 @@ impl<'a, 'b> Mul<&'b DecafPoint> for &'a Scalar { } } +/// Given a vector of (possibly secret) scalars and a vector of +/// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`. +/// +/// This function has the same behaviour as +/// `vartime::multiscalar_mult` but is constant-time. +/// +/// # Input +/// +/// A vector of `Scalar`s and a vector of `DecafPoints`. It is an +/// error to call this function with two vectors of different lengths. +#[cfg(any(feature = "alloc", feature = "std"))] +pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> DecafPoint + where I: IntoIterator, + J: IntoIterator, +{ + let extended_points = points.into_iter().map(|P| &P.0); + DecafPoint(curve::multiscalar_mult(scalars, extended_points)) +} /// Precomputation #[derive(Clone)]