Add MultiscalarMul and VartimeMultiscalarMul traits.

These traits have the same interface, but with different names, so that it's
not possible to use them interchangeably.  (Constant-time and variable-time
routines should not be used interchangeably).

This commit changes the external API to use these traits, replacing
```
edwards::multiscalar_mul
edwards::vartime::multiscalar_mul
```
with
```
EdwardsPoint::multiscalar_mul (as an impl)
EdwardsPoint::vartime_multiscalar_mul (as an impl)
```
and similarly for Ristretto.

Refactoring the backend is for a later commit.

Multiscalar multiplication with precomputation is for a later commit.

The `edwards::vartime` module is retained since it's used for
`vartime_double_base_scalar_mul`.

It should be subsumed into the precomputation API in a later commit.
This commit is contained in:
Henry de Valence 2018-05-02 14:54:46 -07:00
parent 83280ca181
commit e3bf9b0213
4 changed files with 236 additions and 252 deletions

View file

@ -18,7 +18,10 @@ static MULTISCALAR_SIZES: [usize; 13] = [1, 2, 4, 8, 16, 32, 64, 128, 256, 384,
mod edwards_benches {
use super::*;
use curve25519_dalek::edwards::{self, EdwardsPoint};
use curve25519_dalek::edwards;
use curve25519_dalek::edwards::EdwardsPoint;
use curve25519_dalek::traits::MultiscalarMul;
use curve25519_dalek::traits::VartimeMultiscalarMul;
fn compress(c: &mut Criterion) {
let B = &constants::ED25519_BASEPOINT_POINT;
@ -70,7 +73,7 @@ mod edwards_benches {
.iter()
.map(|s| s * &constants::ED25519_BASEPOINT_TABLE)
.collect();
b.iter(|| edwards::multiscalar_mul(&scalars, &points));
b.iter(|| EdwardsPoint::multiscalar_mul(&scalars, &points));
},
&MULTISCALAR_SIZES,
);
@ -86,7 +89,7 @@ mod edwards_benches {
.iter()
.map(|s| s * &constants::ED25519_BASEPOINT_TABLE)
.collect();
b.iter(|| edwards::vartime::multiscalar_mul(&scalars, &points));
b.iter(|| EdwardsPoint::vartime_multiscalar_mul(&scalars, &points));
},
&MULTISCALAR_SIZES,
);

View file

@ -112,6 +112,7 @@ use field::FieldElement;
use scalar::Scalar;
use montgomery::MontgomeryPoint;
use curve_models::ProjectivePoint;
use curve_models::CompletedPoint;
use curve_models::AffineNielsPoint;
@ -121,6 +122,8 @@ use scalar_mul::window::LookupTable;
use traits::{Identity, IsIdentity};
use traits::ValidityCheck;
use traits::MultiscalarMul;
use traits::VartimeMultiscalarMul;
// ------------------------------------------------------------------------
// Compressed points
@ -516,71 +519,68 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar {
}
}
/// Given an iterator of (possibly secret) scalars and an iterator of
/// (possibly secret) points, compute
/// $$
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
/// $$
///
/// This function has the same behaviour as
/// `vartime::multiscalar_mul` but is constant-time.
///
/// It is an error to call this function with two iterators of different lengths.
///
/// # Examples
///
/// 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<EdwardsPoint>`), to allow
/// iterators returning either `Scalar`s or `&Scalar`s.
///
/// ```
/// use curve25519_dalek::{constants, edwards};
/// use curve25519_dalek::scalar::Scalar;
///
/// // Some scalars
/// let a = Scalar::from_u64(87329482);
/// let b = Scalar::from_u64(37264829);
/// let c = Scalar::from_u64(98098098);
///
/// // Some points
/// let P = constants::ED25519_BASEPOINT_POINT;
/// let Q = P + P;
/// let R = P + Q;
///
/// // A1 = a*P + b*Q + c*R
/// let abc = [a,b,c];
/// let A1 = edwards::multiscalar_mul(&abc, &[P,Q,R]);
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
///
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
/// let minus_abc = abc.iter().map(|x| -x);
/// let A2 = edwards::multiscalar_mul(minus_abc, &[P,Q,R]);
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
///
/// assert_eq!(A1.compress(), (-A2).compress());
/// ```
#[cfg(any(feature = "alloc", feature = "std"))]
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
where I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
{
// XXX later when we do more fancy multiscalar mults, we can
// delegate based on the iter's size hint -- hdevalence
// ------------------------------------------------------------------------
// Multiscalar Multiplication impls
// ------------------------------------------------------------------------
// If we built with AVX2, use the AVX2 backend.
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
// These use the iterator's size hint and the target settings to
// forward to a specific backend implementation.
#[cfg(any(feature = "alloc", feature = "std"))]
impl MultiscalarMul for EdwardsPoint {
type Point = EdwardsPoint;
fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
{
use backend::avx2::scalar_mul::straus::multiscalar_mul;
multiscalar_mul(scalars, points)
// XXX later when we do more fancy multiscalar mults, we can
// delegate based on the iter's size hint -- hdevalence
// If we built with AVX2, use the AVX2 backend.
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
{
use backend::avx2::scalar_mul::straus::multiscalar_mul;
multiscalar_mul(scalars, points)
}
// Otherwise, proceed as normal:
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
{
use scalar_mul::straus::multiscalar_mul;
multiscalar_mul(scalars, points)
}
}
// Otherwise, proceed as normal:
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl VartimeMultiscalarMul for EdwardsPoint {
type Point = EdwardsPoint;
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
{
use scalar_mul::straus::multiscalar_mul;
multiscalar_mul(scalars, points)
// XXX later when we do more fancy multiscalar mults, we can
// delegate based on the iter's size hint -- hdevalence
// If we built with AVX2, use the AVX2 backend.
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
{
use backend::avx2::scalar_mul::vartime_straus::multiscalar_mul;
multiscalar_mul(scalars, points)
}
// Otherwise, proceed as normal:
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
{
use scalar_mul::vartime_straus::multiscalar_mul;
multiscalar_mul(scalars, points)
}
}
}
@ -787,78 +787,17 @@ impl Debug for EdwardsBasepointTable {
// Variable-time functions
// ------------------------------------------------------------------------
/// Variable-time operations on curve points, useful for non-secret data.
///
/// XXX delete this whole module
pub mod vartime {
//! Variable-time operations on curve points, useful for non-secret data.
use super::*;
/// Given an iterator of public scalars and an iterator of public points, compute
/// $$
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
/// $$
///
/// This function has the same behaviour as
/// `edwards::multiscalar_mul` but operates on non-secret data.
///
/// It is an error to call this function with two iterators of different lengths.
///
/// # Examples
///
/// 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<EdwardsPoint>`), to allow
/// iterators returning either `Scalar`s or `&Scalar`s.
///
/// ```
/// use curve25519_dalek::{constants, edwards};
/// use curve25519_dalek::scalar::Scalar;
///
/// // Some scalars
/// let a = Scalar::from_u64(87329482);
/// let b = Scalar::from_u64(37264829);
/// let c = Scalar::from_u64(98098098);
///
/// // Some points
/// let P = constants::ED25519_BASEPOINT_POINT;
/// let Q = P + P;
/// let R = P + Q;
///
/// // A1 = a*P + b*Q + c*R
/// let abc = [a,b,c];
/// let A1 = edwards::vartime::multiscalar_mul(&abc, &[P,Q,R]);
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
///
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
/// let minus_abc = abc.iter().map(|x| -x);
/// let A2 = edwards::vartime::multiscalar_mul(minus_abc, &[P,Q,R]);
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
///
/// assert_eq!(A1.compress(), (-A2).compress());
/// ```
#[cfg(any(feature = "alloc", feature = "std"))]
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
where I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
{
// XXX later when we do more fancy multiscalar mults, we can delegate
// based on the iter's size hint -- hdevalence
// If we built with AVX2, use the AVX2 backend.
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
{
use backend::avx2::scalar_mul::vartime_straus::multiscalar_mul;
multiscalar_mul(scalars, points)
}
// Otherwise, proceed as normal:
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
{
use scalar_mul::vartime_straus::multiscalar_mul;
multiscalar_mul(scalars, points)
}
}
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
#[cfg(feature="stage2_build")]
///
/// XXX eliminate this function when we have the precomputation API
#[cfg(feature = "stage2_build")]
pub fn double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
// If we built with AVX2, use the AVX2 backend.
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
@ -1211,7 +1150,7 @@ mod test {
#[test]
fn multiscalar_mul_vs_ed25519py() {
let A = A_TIMES_BASEPOINT.decompress().unwrap();
let result = vartime::multiscalar_mul(
let result = EdwardsPoint::vartime_multiscalar_mul(
&[A_SCALAR, B_SCALAR],
&[A, constants::ED25519_BASEPOINT_POINT]
);
@ -1221,11 +1160,11 @@ mod test {
#[test]
fn multiscalar_mul_vartime_vs_consttime() {
let A = A_TIMES_BASEPOINT.decompress().unwrap();
let result_vartime = vartime::multiscalar_mul(
let result_vartime = EdwardsPoint::vartime_multiscalar_mul(
&[A_SCALAR, B_SCALAR],
&[A, constants::ED25519_BASEPOINT_POINT]
);
let result_consttime = multiscalar_mul(
let result_consttime = EdwardsPoint::multiscalar_mul(
&[A_SCALAR, B_SCALAR],
&[A, constants::ED25519_BASEPOINT_POINT]
);

View file

@ -186,7 +186,6 @@ use subtle::ConditionallyNegatable;
use subtle::ConstantTimeEq;
use subtle::Choice;
use edwards;
use edwards::EdwardsPoint;
use edwards::EdwardsBasepointTable;
@ -194,7 +193,7 @@ use scalar::Scalar;
use curve_models::CompletedPoint;
use traits::Identity;
use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
// ------------------------------------------------------------------------
// Compressed points
@ -790,60 +789,47 @@ define_mul_assign_variants!(LHS = RistrettoPoint, RHS = Scalar);
define_mul_variants!(LHS = RistrettoPoint, RHS = Scalar, Output = RistrettoPoint);
define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint);
// ------------------------------------------------------------------------
// Multiscalar Multiplication impls
// ------------------------------------------------------------------------
// These use iterator combinators to unwrap the underlying points and
// forward to the EdwardsPoint implementations.
/// Given an iterator of (possibly secret) scalars and an iterator of
/// (possibly secret) points, compute
/// $$
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
/// $$
///
/// This function has the same behaviour as
/// `vartime::multiscalar_mul` but is constant-time.
///
/// It is an error to call this function with two iterators of different lengths.
///
/// # Examples
///
/// 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<RistrettoPoint>`), to allow
/// iterators returning either `Scalar`s or `&Scalar`s.
///
/// ```
/// use curve25519_dalek::{constants, ristretto};
/// use curve25519_dalek::scalar::Scalar;
///
/// // Some scalars
/// let a = Scalar::from_u64(87329482);
/// let b = Scalar::from_u64(37264829);
/// let c = Scalar::from_u64(98098098);
///
/// // Some points
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
/// let Q = P + P;
/// let R = P + Q;
///
/// // A1 = a*P + b*Q + c*R
/// let abc = [a,b,c];
/// let A1 = ristretto::multiscalar_mul(&abc, &[P,Q,R]);
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
///
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
/// let minus_abc = abc.iter().map(|x| -x);
/// let A2 = ristretto::multiscalar_mul(minus_abc, &[P,Q,R]);
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
///
/// assert_eq!(A1.compress(), (-A2).compress());
/// ```
#[cfg(any(feature = "alloc", feature = "std"))]
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
where I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<RistrettoPoint>,
{
let extended_points = points.into_iter().map(|P| P.borrow().0);
RistrettoPoint(edwards::multiscalar_mul(scalars, extended_points))
impl MultiscalarMul for RistrettoPoint {
type Point = RistrettoPoint;
fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<RistrettoPoint>,
{
let extended_points = points.into_iter().map(|P| P.borrow().0);
RistrettoPoint(
EdwardsPoint::multiscalar_mul(scalars, extended_points)
)
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl VartimeMultiscalarMul for RistrettoPoint {
type Point = RistrettoPoint;
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<RistrettoPoint>,
{
let extended_points = points.into_iter().map(|P| P.borrow().0);
RistrettoPoint(
EdwardsPoint::vartime_multiscalar_mul(scalars, extended_points)
)
}
}
/// A precomputed table of multiples of a basepoint, used to accelerate
@ -947,69 +933,6 @@ impl Debug for RistrettoPoint {
}
}
// ------------------------------------------------------------------------
// Variable-time functions
// ------------------------------------------------------------------------
pub mod vartime {
//! Variable-time operations on ristretto points, useful for non-secret data.
use super::*;
/// Given an iterator of public scalars and an iterator of public points, compute
/// $$
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
/// $$
///
/// This function has the same behaviour as
/// `vartime::multiscalar_mul` but is constant-time.
///
/// It is an error to call this function with two iterators of different lengths.
///
/// # Examples
///
/// 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<RistrettoPoint>`), to allow
/// iterators returning either `Scalar`s or `&Scalar`s.
///
/// ```
/// use curve25519_dalek::{constants, ristretto};
/// use curve25519_dalek::scalar::Scalar;
///
/// // Some scalars
/// let a = Scalar::from_u64(87329482);
/// let b = Scalar::from_u64(37264829);
/// let c = Scalar::from_u64(98098098);
///
/// // Some points
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
/// let Q = P + P;
/// let R = P + Q;
///
/// // A1 = a*P + b*Q + c*R
/// let abc = [a,b,c];
/// let A1 = ristretto::vartime::multiscalar_mul(&abc, &[P,Q,R]);
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
///
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
/// let minus_abc = abc.iter().map(|x| -x);
/// let A2 = ristretto::vartime::multiscalar_mul(minus_abc, &[P,Q,R]);
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
///
/// assert_eq!(A1.compress(), (-A2).compress());
/// ```
#[cfg(any(feature = "alloc", feature = "std"))]
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
where I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<RistrettoPoint>,
{
let extended_points = points.into_iter().map(|P| P.borrow().0);
RistrettoPoint(edwards::vartime::multiscalar_mul(scalars, extended_points))
}
}
// ------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------

View file

@ -10,8 +10,12 @@
//! Module for common traits.
use core::borrow::Borrow;
use subtle;
use scalar::Scalar;
// ------------------------------------------------------------------------
// Public Traits
// ------------------------------------------------------------------------
@ -32,12 +36,127 @@ pub trait IsIdentity {
/// Implement generic identity equality testing for a point representations
/// which have constant-time equality testing and a defined identity
/// constructor.
impl<T> IsIdentity for T where T: subtle::ConstantTimeEq + Identity {
impl<T> IsIdentity for T
where
T: subtle::ConstantTimeEq + Identity,
{
fn is_identity(&self) -> bool {
self.ct_eq(&T::identity()).unwrap_u8() == 1u8
}
}
/// A trait for constant-time multiscalar multiplication without precomputation.
pub trait MultiscalarMul {
/// The type of point being multiplied, e.g., `RistrettoPoint`.
type Point;
/// Given an iterator of (possibly secret) scalars and an iterator of
/// public points, compute
/// $$
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
/// $$
///
/// It is an error to call this function with two iterators of different lengths.
///
/// # Examples
///
/// 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.
///
/// ```
/// use curve25519_dalek::constants;
/// use curve25519_dalek::traits::MultiscalarMul;
/// use curve25519_dalek::ristretto::RistrettoPoint;
/// use curve25519_dalek::scalar::Scalar;
///
/// // Some scalars
/// let a = Scalar::from_u64(87329482);
/// let b = Scalar::from_u64(37264829);
/// let c = Scalar::from_u64(98098098);
///
/// // Some points
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
/// let Q = P + P;
/// let R = P + Q;
///
/// // A1 = a*P + b*Q + c*R
/// let abc = [a,b,c];
/// let A1 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]);
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
///
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
/// let minus_abc = abc.iter().map(|x| -x);
/// let A2 = RistrettoPoint::multiscalar_mul(minus_abc, &[P,Q,R]);
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
///
/// assert_eq!(A1.compress(), (-A2).compress());
/// ```
fn multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Self::Point>;
}
/// A trait for variable-time multiscalar multiplication without precomputation.
pub trait VartimeMultiscalarMul {
/// The type of point being multiplied, e.g., `RistrettoPoint`.
type Point;
/// Given an iterator of (possibly secret) scalars and an iterator of
/// public points, compute
/// $$
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
/// $$
///
/// It is an error to call this function with two iterators of different lengths.
///
/// # Examples
///
/// 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.
///
/// ```
/// use curve25519_dalek::constants;
/// use curve25519_dalek::traits::MultiscalarMul;
/// use curve25519_dalek::ristretto::RistrettoPoint;
/// use curve25519_dalek::scalar::Scalar;
///
/// // Some scalars
/// let a = Scalar::from_u64(87329482);
/// let b = Scalar::from_u64(37264829);
/// let c = Scalar::from_u64(98098098);
///
/// // Some points
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
/// let Q = P + P;
/// let R = P + Q;
///
/// // A1 = a*P + b*Q + c*R
/// let abc = [a,b,c];
/// let A1 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]);
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
///
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
/// let minus_abc = abc.iter().map(|x| -x);
/// let A2 = RistrettoPoint::multiscalar_mul(minus_abc, &[P,Q,R]);
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
///
/// assert_eq!(A1.compress(), (-A2).compress());
/// ```
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Self::Point>;
}
// ------------------------------------------------------------------------
// Private Traits
// ------------------------------------------------------------------------