From 092ff52cb0927696ccda4100203446c2374e1d3a Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 13 Feb 2019 11:10:47 -0800 Subject: [PATCH] Remove constant-time multiscalar precomputation. This doesn't (yet) give any speedup over the non-precomputed multiscalar multiplication, and it's not clear that it's a good idea to commit to supporting it in the future. Removing it means that it's not committed-to as part of the public API, but the source is still there in the tree if we want to revisit it later. --- benches/dalek_benchmarks.rs | 46 ---------- .../serial/scalar_mul/precomputed_straus.rs | 85 +---------------- .../vector/scalar_mul/precomputed_straus.rs | 84 +---------------- src/edwards.rs | 85 +---------------- src/ristretto.rs | 91 +------------------ src/traits.rs | 80 ---------------- 6 files changed, 6 insertions(+), 465 deletions(-) diff --git a/benches/dalek_benchmarks.rs b/benches/dalek_benchmarks.rs index 118a809..e2e3f5b 100644 --- a/benches/dalek_benchmarks.rs +++ b/benches/dalek_benchmarks.rs @@ -110,37 +110,6 @@ mod multiscalar_benches { ); } - fn precomputed_ct_straus_helper(c: &mut Criterion, dynamic_fraction: f64) { - let label = format!( - "Constant-time mixed-base Straus ({:.2}pct dyn)", - 100.0*dynamic_fraction, - ); - c.bench_function_over_inputs( - &label, - move |b, &&total_size| { - let dynamic_size = ((total_size as f64) * dynamic_fraction) as usize; - let static_size = total_size - dynamic_size; - - let (static_scalars, static_points) = construct(static_size); - let (dynamic_scalars, dynamic_points) = construct(dynamic_size); - - use curve25519_dalek::edwards::EdwardsPrecomputation; - use curve25519_dalek::traits::PrecomputedMultiscalarMul; - - let precomp = EdwardsPrecomputation::new(&static_points); - - b.iter(|| { - precomp.mixed_multiscalar_mul( - &static_scalars, - &dynamic_scalars, - &dynamic_points, - ) - }); - }, - &MULTISCALAR_SIZES, - ); - } - fn precomputed_vt_straus_helper(c: &mut Criterion, dynamic_fraction: f64) { let label = format!( "Variable-time mixed-base Straus ({:.2}pct dyn)", @@ -172,18 +141,6 @@ mod multiscalar_benches { ); } - fn precomputed_ct_straus_00_pct_dynamic(c: &mut Criterion) { - precomputed_ct_straus_helper(c, 0.0); - } - - fn precomputed_ct_straus_20_pct_dynamic(c: &mut Criterion) { - precomputed_ct_straus_helper(c, 0.2); - } - - fn precomputed_ct_straus_50_pct_dynamic(c: &mut Criterion) { - precomputed_ct_straus_helper(c, 0.5); - } - fn precomputed_vt_straus_00_pct_dynamic(c: &mut Criterion) { precomputed_vt_straus_helper(c, 0.0); } @@ -203,9 +160,6 @@ mod multiscalar_benches { targets = consttime_multiscalar_mul, vartime_multiscalar_mul, - precomputed_ct_straus_00_pct_dynamic, - precomputed_ct_straus_20_pct_dynamic, - precomputed_ct_straus_50_pct_dynamic, precomputed_vt_straus_00_pct_dynamic, precomputed_vt_straus_20_pct_dynamic, precomputed_vt_straus_50_pct_dynamic, diff --git a/src/backend/serial/scalar_mul/precomputed_straus.rs b/src/backend/serial/scalar_mul/precomputed_straus.rs index 905b168..4019b14 100644 --- a/src/backend/serial/scalar_mul/precomputed_straus.rs +++ b/src/backend/serial/scalar_mul/precomputed_straus.rs @@ -13,99 +13,18 @@ use core::borrow::Borrow; -use clear_on_drop::ClearOnDrop; - use backend::serial::curve_models::{ AffineNielsPoint, CompletedPoint, ProjectiveNielsPoint, ProjectivePoint, }; use edwards::EdwardsPoint; use scalar::Scalar; use traits::Identity; -use traits::{PrecomputedMultiscalarMul, VartimePrecomputedMultiscalarMul}; -use window::{LookupTable, NafLookupTable5, NafLookupTable8}; +use traits::VartimePrecomputedMultiscalarMul; +use window::{NafLookupTable5, NafLookupTable8}; #[allow(unused_imports)] use prelude::*; -pub struct PrecomputedStraus { - static_lookup_tables: Vec>, -} - -impl PrecomputedMultiscalarMul for PrecomputedStraus { - type Point = EdwardsPoint; - - fn new(static_points: I) -> Self - where - I: IntoIterator, - I::Item: Borrow, - { - PrecomputedStraus { - static_lookup_tables: static_points - .into_iter() - .map(|point| LookupTable::::from(point.borrow())) - .collect(), - } - } - - fn mixed_multiscalar_mul( - &self, - static_scalars: I, - dynamic_scalars: J, - dynamic_points: K, - ) -> Self::Point - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - K: IntoIterator, - K::Item: Borrow, - { - // This does two allocs for the scalar digits instead of - // putting them in a contiguous array, which makes handling - // the two kinds of lookup tables slightly easier. - // Use a ClearOnDrop wrapper. - - let static_scalar_digits_vec: Vec<_> = static_scalars - .into_iter() - .map(|s| s.borrow().to_radix_16()) - .collect(); - let static_scalar_digits = ClearOnDrop::new(static_scalar_digits_vec); - - let dynamic_scalar_digits_vec: Vec<_> = dynamic_scalars - .into_iter() - .map(|s| s.borrow().to_radix_16()) - .collect(); - let dynamic_scalar_digits = ClearOnDrop::new(dynamic_scalar_digits_vec); - - // Build lookup tables for dynamic points - let dynamic_lookup_tables: Vec<_> = dynamic_points - .into_iter() - .map(|point| LookupTable::::from(point.borrow())) - .collect(); - - let sp = self.static_lookup_tables.len(); - let dp = dynamic_lookup_tables.len(); - assert_eq!(sp, static_scalar_digits.len()); - assert_eq!(dp, dynamic_scalar_digits.len()); - - let mut R = EdwardsPoint::identity(); - for j in (0..64).rev() { - R = R.mul_by_pow_2(4); - for i in 0..dp { - let t_ij = dynamic_scalar_digits[i][j]; - R = (&R + &dynamic_lookup_tables[i].select(t_ij)).to_extended(); - } - for i in 0..sp { - let s_ij = static_scalar_digits[i][j]; - R = (&R + &self.static_lookup_tables[i].select(s_ij)).to_extended(); - } - } - - R - } -} - pub struct VartimePrecomputedStraus { static_lookup_tables: Vec>, } diff --git a/src/backend/vector/scalar_mul/precomputed_straus.rs b/src/backend/vector/scalar_mul/precomputed_straus.rs index 9ca767f..49d1be4 100644 --- a/src/backend/vector/scalar_mul/precomputed_straus.rs +++ b/src/backend/vector/scalar_mul/precomputed_straus.rs @@ -13,96 +13,16 @@ use core::borrow::Borrow; -use clear_on_drop::ClearOnDrop; - use backend::vector::{CachedPoint, ExtendedPoint}; use edwards::EdwardsPoint; use scalar::Scalar; use traits::Identity; -use traits::{PrecomputedMultiscalarMul, VartimePrecomputedMultiscalarMul}; -use window::{LookupTable, NafLookupTable5, NafLookupTable8}; +use traits::VartimePrecomputedMultiscalarMul; +use window::{NafLookupTable5, NafLookupTable8}; #[allow(unused_imports)] use prelude::*; -pub struct PrecomputedStraus { - static_lookup_tables: Vec>, -} - -impl PrecomputedMultiscalarMul for PrecomputedStraus { - type Point = EdwardsPoint; - - fn new(static_points: I) -> Self - where - I: IntoIterator, - I::Item: Borrow, - { - PrecomputedStraus { - static_lookup_tables: static_points - .into_iter() - .map(|point| LookupTable::::from(point.borrow())) - .collect(), - } - } - - fn mixed_multiscalar_mul( - &self, - static_scalars: I, - dynamic_scalars: J, - dynamic_points: K, - ) -> Self::Point - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - K: IntoIterator, - K::Item: Borrow, - { - // This does two allocs for the scalar digits instead of - // putting them in a contiguous array, which makes handling - // the two kinds of lookup tables slightly easier. - // Use a ClearOnDrop wrapper. - - let static_scalar_digits_vec: Vec<_> = static_scalars - .into_iter() - .map(|s| s.borrow().to_radix_16()) - .collect(); - let static_scalar_digits = ClearOnDrop::new(static_scalar_digits_vec); - - let dynamic_scalar_digits_vec: Vec<_> = dynamic_scalars - .into_iter() - .map(|s| s.borrow().to_radix_16()) - .collect(); - let dynamic_scalar_digits = ClearOnDrop::new(dynamic_scalar_digits_vec); - - // Build lookup tables for dynamic points - let dynamic_lookup_tables: Vec<_> = dynamic_points - .into_iter() - .map(|point| LookupTable::::from(point.borrow())) - .collect(); - - let sp = self.static_lookup_tables.len(); - let dp = dynamic_lookup_tables.len(); - assert_eq!(sp, static_scalar_digits.len()); - assert_eq!(dp, dynamic_scalar_digits.len()); - - let mut R = ExtendedPoint::identity(); - for j in (0..64).rev() { - R = R.mul_by_pow_2(4); - for i in 0..dp { - let t_ij = dynamic_scalar_digits[i][j]; - R = &R + &dynamic_lookup_tables[i].select(t_ij); - } - for i in 0..sp { - let s_ij = static_scalar_digits[i][j]; - R = &R + &self.static_lookup_tables[i].select(s_ij); - } - } - - R.into() - } -} pub struct VartimePrecomputedStraus { static_lookup_tables: Vec>, diff --git a/src/edwards.rs b/src/edwards.rs index 3ab3e60..06f53ea 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -126,7 +126,7 @@ use traits::ValidityCheck; use traits::{Identity, IsIdentity}; #[cfg(any(feature = "alloc", feature = "std"))] -use traits::{MultiscalarMul, PrecomputedMultiscalarMul}; +use traits::MultiscalarMul; #[cfg(any(feature = "alloc", feature = "std"))] use traits::{VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul}; @@ -673,13 +673,6 @@ impl VartimeMultiscalarMul for EdwardsPoint { } } -/// Precomputation for multiscalar multiplication with `EdwardsPoint`s. -// This wraps the inner implementation in a facade type so that we can -// decouple stability of the inner type from the stability of the -// outer type. -#[cfg(feature = "alloc")] -pub struct EdwardsPrecomputation(scalar_mul::precomputed_straus::PrecomputedStraus); - /// Precomputation for variable-time multiscalar multiplication with `EdwardsPoint`s. // This wraps the inner implementation in a facade type so that we can // decouple stability of the inner type from the stability of the @@ -687,39 +680,6 @@ pub struct EdwardsPrecomputation(scalar_mul::precomputed_straus::PrecomputedStra #[cfg(feature = "alloc")] pub struct VartimeEdwardsPrecomputation(scalar_mul::precomputed_straus::VartimePrecomputedStraus); -#[cfg(feature = "alloc")] -impl PrecomputedMultiscalarMul for EdwardsPrecomputation { - type Point = EdwardsPoint; - - fn new(static_points: I) -> Self - where - I: IntoIterator, - I::Item: Borrow, - { - Self(scalar_mul::precomputed_straus::PrecomputedStraus::new( - static_points, - )) - } - - fn mixed_multiscalar_mul( - &self, - static_scalars: I, - dynamic_scalars: J, - dynamic_points: K, - ) -> Self::Point - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - K: IntoIterator, - K::Item: Borrow, - { - self.0 - .mixed_multiscalar_mul(static_scalars, dynamic_scalars, dynamic_points) - } -} - #[cfg(feature = "alloc")] impl VartimePrecomputedMultiscalarMul for VartimeEdwardsPrecomputation { type Point = EdwardsPoint; @@ -1280,49 +1240,6 @@ mod test { assert!(P1.compress().to_bytes() == P2.compress().to_bytes()); } - #[test] - fn precomputed_vs_nonprecomputed_multiscalar() { - let mut rng = rand::thread_rng(); - - let B = &::constants::ED25519_BASEPOINT_TABLE; - - let static_scalars = (0..128) - .map(|_| Scalar::random(&mut rng)) - .collect::>(); - - let dynamic_scalars = (0..128) - .map(|_| Scalar::random(&mut rng)) - .collect::>(); - - let check_scalar: Scalar = static_scalars - .iter() - .chain(dynamic_scalars.iter()) - .map(|s| s * s) - .sum(); - - let static_points = static_scalars.iter().map(|s| s * B).collect::>(); - let dynamic_points = dynamic_scalars.iter().map(|s| s * B).collect::>(); - - let precomputation = EdwardsPrecomputation::new(static_points.iter()); - - let P = precomputation.mixed_multiscalar_mul( - &static_scalars, - &dynamic_scalars, - &dynamic_points, - ); - - use traits::MultiscalarMul; - let Q = EdwardsPoint::multiscalar_mul( - static_scalars.iter().chain(dynamic_scalars.iter()), - static_points.iter().chain(dynamic_points.iter()), - ); - - let R = &check_scalar * B; - - assert_eq!(P.compress(), R.compress()); - assert_eq!(Q.compress(), R.compress()); - } - #[test] fn vartime_precomputed_vs_nonprecomputed_multiscalar() { let mut rng = rand::thread_rng(); diff --git a/src/ristretto.rs b/src/ristretto.rs index 34e3c33..3851949 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -187,10 +187,7 @@ use scalar::Scalar; use traits::Identity; #[cfg(any(feature = "alloc", feature = "std"))] -use traits::{ - MultiscalarMul, PrecomputedMultiscalarMul, VartimeMultiscalarMul, - VartimePrecomputedMultiscalarMul, -}; +use traits::{MultiscalarMul, VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul}; #[cfg(not(all( feature = "simd_backend", @@ -909,13 +906,6 @@ impl VartimeMultiscalarMul for RistrettoPoint { } } -/// Precomputation for multiscalar multiplication with `RistrettoPoint`s. -// This wraps the inner implementation in a facade type so that we can -// decouple stability of the inner type from the stability of the -// outer type. -#[cfg(feature = "alloc")] -pub struct RistrettoPrecomputation(scalar_mul::precomputed_straus::PrecomputedStraus); - /// Precomputation for variable-time multiscalar multiplication with `RistrettoPoint`s. // This wraps the inner implementation in a facade type so that we can // decouple stability of the inner type from the stability of the @@ -923,42 +913,6 @@ pub struct RistrettoPrecomputation(scalar_mul::precomputed_straus::PrecomputedSt #[cfg(feature = "alloc")] pub struct VartimeRistrettoPrecomputation(scalar_mul::precomputed_straus::VartimePrecomputedStraus); -#[cfg(feature = "alloc")] -impl PrecomputedMultiscalarMul for RistrettoPrecomputation { - type Point = RistrettoPoint; - - fn new(static_points: I) -> Self - where - I: IntoIterator, - I::Item: Borrow, - { - Self(scalar_mul::precomputed_straus::PrecomputedStraus::new( - static_points.into_iter().map(|P| P.borrow().0), - )) - } - - fn mixed_multiscalar_mul( - &self, - static_scalars: I, - dynamic_scalars: J, - dynamic_points: K, - ) -> Self::Point - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - K: IntoIterator, - K::Item: Borrow, - { - RistrettoPoint(self.0.mixed_multiscalar_mul( - static_scalars, - dynamic_scalars, - dynamic_points.into_iter().map(|P| P.borrow().0), - )) - } -} - #[cfg(feature = "alloc")] impl VartimePrecomputedMultiscalarMul for VartimeRistrettoPrecomputation { type Point = RistrettoPoint; @@ -1366,49 +1320,6 @@ mod test { } } - #[test] - fn precomputed_vs_nonprecomputed_multiscalar() { - let mut rng = rand::thread_rng(); - - let B = &::constants::RISTRETTO_BASEPOINT_TABLE; - - let static_scalars = (0..128) - .map(|_| Scalar::random(&mut rng)) - .collect::>(); - - let dynamic_scalars = (0..128) - .map(|_| Scalar::random(&mut rng)) - .collect::>(); - - let check_scalar: Scalar = static_scalars - .iter() - .chain(dynamic_scalars.iter()) - .map(|s| s * s) - .sum(); - - let static_points = static_scalars.iter().map(|s| s * B).collect::>(); - let dynamic_points = dynamic_scalars.iter().map(|s| s * B).collect::>(); - - let precomputation = RistrettoPrecomputation::new(static_points.iter()); - - let P = precomputation.mixed_multiscalar_mul( - &static_scalars, - &dynamic_scalars, - &dynamic_points, - ); - - use traits::MultiscalarMul; - let Q = RistrettoPoint::multiscalar_mul( - static_scalars.iter().chain(dynamic_scalars.iter()), - static_points.iter().chain(dynamic_points.iter()), - ); - - let R = &check_scalar * B; - - assert_eq!(P.compress(), R.compress()); - assert_eq!(Q.compress(), R.compress()); - } - #[test] fn vartime_precomputed_vs_nonprecomputed_multiscalar() { let mut rng = rand::thread_rng(); diff --git a/src/traits.rs b/src/traits.rs index c1166f5..5297aa4 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -225,86 +225,6 @@ pub trait VartimeMultiscalarMul { } } -/// A trait for constant-time multiscalar multiplication with precomputation. -/// -/// A general multiscalar multiplication with precomputation can be written as -/// $$ -/// Q = a_1 A_1 + \cdots + a_n A_n + b_1 B_1 + \cdots + b_m B_m, -/// $$ -/// where the \\(B_i\\) are *static* points, for which precomputation -/// is possible, and the \\(A_j\\) are *dynamic* points, for which -/// precomputation is not possible. -pub trait PrecomputedMultiscalarMul: Sized { - /// The type of point to be multiplied, e.g., `RistrettoPoint`. - type Point; - - /// Given the static points \\( B_i \\), perform precomputation - /// and return the precomputation data. - fn new(static_points: I) -> Self - where - I: IntoIterator, - I::Item: Borrow; - - /// Given `static_scalars`, an iterator of (possibly secret) - /// scalars \\(b_i\\), `dynamic_scalars`, an iterator of (possibly - /// secret) scalars \\(a_i\\), and `dynamic_points`, an iterator - /// of points \\(A_i\\), compute - /// $$ - /// Q = a_1 A_1 + \cdots + a_n A_n + b_1 B_1 + \cdots + b_m B_m, - /// $$ - /// where the \\(B_j\\) are the points that were supplied to `new`. - /// - /// It is an error to call this function with iterators of - /// inconsistent lengths. - /// - /// The trait bound aims for maximum flexibility: the inputs must be - /// convertable to iterators (`I: IntoIter`), and the iterator's items - /// must be `Borrow` (or `Borrow`), to allow - /// iterators returning either `Scalar`s or `&Scalar`s. - fn mixed_multiscalar_mul( - &self, - static_scalars: I, - dynamic_scalars: J, - dynamic_points: K, - ) -> Self::Point - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - K: IntoIterator, - K::Item: Borrow; - - /// Given `static_scalars`, an iterator of (possibly secret) - /// scalars \\(b_i\\), compute - /// $$ - /// Q = b_1 B_1 + \cdots + b_m B_m, - /// $$ - /// where the \\(B_j\\) are the points that were supplied to `new`. - /// - /// It is an error to call this function with iterators of - /// inconsistent lengths. - /// - /// The trait bound aims for maximum flexibility: the input must - /// be convertable to iterators (`I: IntoIter`), and the - /// iterator's items must be `Borrow`, to allow iterators - /// returning either `Scalar`s or `&Scalar`s. - fn multiscalar_mul(&self, static_scalars: I) -> Self::Point - where - I: IntoIterator, - I::Item: Borrow, - { - use core::iter; - - Self::mixed_multiscalar_mul( - self, - static_scalars, - iter::empty::(), - iter::empty::(), - ) - } -} - /// A trait for variable-time multiscalar multiplication with precomputation. /// /// A general multiscalar multiplication with precomputation can be written as