diff --git a/benches/dalek_benchmarks.rs b/benches/dalek_benchmarks.rs index 353f4bf..35d1a24 100644 --- a/benches/dalek_benchmarks.rs +++ b/benches/dalek_benchmarks.rs @@ -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, ); diff --git a/src/edwards.rs b/src/edwards.rs index 8a9f89b..27b6154 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -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` (or `Borrow`), 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 -/// -/// // 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 -/// -/// assert_eq!(A1.compress(), (-A2).compress()); -/// ``` -#[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint - where I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, -{ - // 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(scalars: I, points: J) -> EdwardsPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { - 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(scalars: I, points: J) -> EdwardsPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { - 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` (or `Borrow`), 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 - /// - /// // 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 - /// - /// assert_eq!(A1.compress(), (-A2).compress()); - /// ``` - #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint - where I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - { - // 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] ); diff --git a/src/ristretto.rs b/src/ristretto.rs index b293fb8..dca5ed5 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -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` (or `Borrow`), 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 -/// -/// // 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 -/// -/// assert_eq!(A1.compress(), (-A2).compress()); -/// ``` #[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mul(scalars: I, points: J) -> RistrettoPoint - where I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, -{ - 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(scalars: I, points: J) -> RistrettoPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + { + 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(scalars: I, points: J) -> RistrettoPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + { + 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` (or `Borrow`), 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 - /// - /// // 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 - /// - /// assert_eq!(A1.compress(), (-A2).compress()); - /// ``` - #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mul(scalars: I, points: J) -> RistrettoPoint - where I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - { - let extended_points = points.into_iter().map(|P| P.borrow().0); - RistrettoPoint(edwards::vartime::multiscalar_mul(scalars, extended_points)) - } -} - // ------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------ diff --git a/src/traits.rs b/src/traits.rs index d774833..e706348 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -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 IsIdentity for T where T: subtle::ConstantTimeEq + Identity { +impl 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` (or `Borrow`), 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 + /// + /// // 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 + /// + /// assert_eq!(A1.compress(), (-A2).compress()); + /// ``` + fn multiscalar_mul(scalars: I, points: J) -> Self::Point + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow; +} + +/// 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` (or `Borrow`), 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 + /// + /// // 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 + /// + /// assert_eq!(A1.compress(), (-A2).compress()); + /// ``` + fn vartime_multiscalar_mul(scalars: I, points: J) -> Self::Point + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow; +} + // ------------------------------------------------------------------------ // Private Traits // ------------------------------------------------------------------------