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.
This commit is contained in:
Henry de Valence 2019-02-13 11:10:47 -08:00
parent 27daa5215e
commit 092ff52cb0
6 changed files with 6 additions and 465 deletions

View file

@ -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,

View file

@ -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<LookupTable<AffineNielsPoint>>,
}
impl PrecomputedMultiscalarMul for PrecomputedStraus {
type Point = EdwardsPoint;
fn new<I>(static_points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<Self::Point>,
{
PrecomputedStraus {
static_lookup_tables: static_points
.into_iter()
.map(|point| LookupTable::<AffineNielsPoint>::from(point.borrow()))
.collect(),
}
}
fn mixed_multiscalar_mul<I, J, K>(
&self,
static_scalars: I,
dynamic_scalars: J,
dynamic_points: K,
) -> Self::Point
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Scalar>,
K: IntoIterator,
K::Item: Borrow<Self::Point>,
{
// 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::<ProjectiveNielsPoint>::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<NafLookupTable8<AffineNielsPoint>>,
}

View file

@ -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<LookupTable<CachedPoint>>,
}
impl PrecomputedMultiscalarMul for PrecomputedStraus {
type Point = EdwardsPoint;
fn new<I>(static_points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<Self::Point>,
{
PrecomputedStraus {
static_lookup_tables: static_points
.into_iter()
.map(|point| LookupTable::<CachedPoint>::from(point.borrow()))
.collect(),
}
}
fn mixed_multiscalar_mul<I, J, K>(
&self,
static_scalars: I,
dynamic_scalars: J,
dynamic_points: K,
) -> Self::Point
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Scalar>,
K: IntoIterator,
K::Item: Borrow<Self::Point>,
{
// 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::<CachedPoint>::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<NafLookupTable8<CachedPoint>>,

View file

@ -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<I>(static_points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<Self::Point>,
{
Self(scalar_mul::precomputed_straus::PrecomputedStraus::new(
static_points,
))
}
fn mixed_multiscalar_mul<I, J, K>(
&self,
static_scalars: I,
dynamic_scalars: J,
dynamic_points: K,
) -> Self::Point
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Scalar>,
K: IntoIterator,
K::Item: Borrow<Self::Point>,
{
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::<Vec<_>>();
let dynamic_scalars = (0..128)
.map(|_| Scalar::random(&mut rng))
.collect::<Vec<_>>();
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::<Vec<_>>();
let dynamic_points = dynamic_scalars.iter().map(|s| s * B).collect::<Vec<_>>();
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();

View file

@ -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<I>(static_points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<Self::Point>,
{
Self(scalar_mul::precomputed_straus::PrecomputedStraus::new(
static_points.into_iter().map(|P| P.borrow().0),
))
}
fn mixed_multiscalar_mul<I, J, K>(
&self,
static_scalars: I,
dynamic_scalars: J,
dynamic_points: K,
) -> Self::Point
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Scalar>,
K: IntoIterator,
K::Item: Borrow<Self::Point>,
{
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::<Vec<_>>();
let dynamic_scalars = (0..128)
.map(|_| Scalar::random(&mut rng))
.collect::<Vec<_>>();
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::<Vec<_>>();
let dynamic_points = dynamic_scalars.iter().map(|s| s * B).collect::<Vec<_>>();
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();

View file

@ -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<I>(static_points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<Self::Point>;
/// 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<Scalar>` (or `Borrow<Point>`), to allow
/// iterators returning either `Scalar`s or `&Scalar`s.
fn mixed_multiscalar_mul<I, J, K>(
&self,
static_scalars: I,
dynamic_scalars: J,
dynamic_points: K,
) -> Self::Point
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Scalar>,
K: IntoIterator,
K::Item: Borrow<Self::Point>;
/// 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<Scalar>`, to allow iterators
/// returning either `Scalar`s or `&Scalar`s.
fn multiscalar_mul<I>(&self, static_scalars: I) -> Self::Point
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
{
use core::iter;
Self::mixed_multiscalar_mul(
self,
static_scalars,
iter::empty::<Scalar>(),
iter::empty::<Self::Point>(),
)
}
}
/// A trait for variable-time multiscalar multiplication with precomputation.
///
/// A general multiscalar multiplication with precomputation can be written as