From 0f185d3e289bbd540672062c287a7811edd62af1 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 20 Feb 2018 18:10:40 -0800 Subject: [PATCH] Generalize trait bounds on multiscalar multiplication. This allows iterators returning either &Scalars or Scalars, so that it's possible to use map() and friends to adjust scalars as they're being fed into the multiscalar multiplication. --- src/backend/avx2/edwards.rs | 51 ++++++----------- src/edwards.rs | 93 ++++++++++++++++++++++++------- src/ristretto.rs | 108 +++++++++++++++++++++++++++++------- 3 files changed, 176 insertions(+), 76 deletions(-) diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index 1a58133..b5f3245 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -453,32 +453,19 @@ impl EdwardsBasepointTable { } } -/// 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 `EdwardsPoints`. It is an -/// error to call this function with two vectors of different lengths. -/// -/// XXX this takes `edwards::EdwardsPoints` because we have to alloc scratch space here anyways, -/// and we need some space to store the converted points, so we may as well do the conversion here. -/// maybe there's a better way to avoid code duplication... however we can't quite just write a -/// generic `multiscalar_mult` because the non-vectorized code passes between models and this code -/// doesn't. +/// Internal multiscalar code. #[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::EdwardsPoint - where I: IntoIterator, - J: IntoIterator +pub fn multiscalar_mult(scalars: I, points: J) -> edwards::EdwardsPoint + where I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { //assert_eq!(scalars.len(), points.len()); use clear_on_drop::ClearOnDrop; let lookup_tables_vec: Vec<_> = points.into_iter() - .map(|P| LookupTable::from(ExtendedPoint::from(*P)) ) + .map(|P| LookupTable::from(ExtendedPoint::from(*P.borrow())) ) .collect(); let lookup_tables = ClearOnDrop::new(lookup_tables_vec); @@ -489,7 +476,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::Edwards // // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. let scalar_digits_vec: Vec<_> = scalars.into_iter() - .map(|c| c.to_radix_16()) + .map(|c| c.borrow().to_radix_16()) .collect(); // The above puts the scalar digits into a heap-allocated Vec. @@ -606,27 +593,21 @@ pub mod vartime { Q.into() } - /// Given a vector of public scalars and a vector of public points, compute - /// $$ - /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. - /// $$ - /// - /// # Input - /// - /// A vector of `Scalar`s and a vector of `EdwardsPoints`. It is an - /// error to call this function with two vectors of different lengths. + /// Internal multiscalar function #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::EdwardsPoint - where I: IntoIterator, - J: IntoIterator + pub fn multiscalar_mult(scalars: I, points: J) -> edwards::EdwardsPoint + where I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { //assert_eq!(scalars.len(), points.len()); let nafs: Vec<_> = scalars.into_iter() - .map(|c| c.non_adjacent_form()).collect(); + .map(|c| c.borrow().non_adjacent_form()).collect(); let odd_multiples: Vec<_> = points.into_iter() - .map(|P| OddMultiples::create((*P).into()) ).collect(); + .map(|P| OddMultiples::create((*P.borrow()).into()) ).collect(); let mut Q = ExtendedPoint::identity(); diff --git a/src/edwards.rs b/src/edwards.rs index 8d242ab..2fd7c1e 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -96,6 +96,7 @@ use core::ops::{Add, Sub, Neg}; use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; use core::ops::Index; +use core::borrow::Borrow; use subtle::slices_equal; use subtle::ConditionallyAssignable; @@ -532,12 +533,15 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { /// This function has the same behaviour as /// `vartime::multiscalar_mult` but is constant-time. /// -/// # Input -/// -/// A iterable of `Scalar`s and a iterable of `EdwardsPoints`. It is an -/// error to call this function with two iterators of different lengths. +/// 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; @@ -553,15 +557,25 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { /// let R = P + Q; /// /// // A1 = a*P + b*Q + c*R -/// let A1 = edwards::multiscalar_mult(&[a,b,c], &[P,Q,R]); -/// ``` +/// let abc = [a,b,c]; +/// let A1 = edwards::multiscalar_mult(&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_mult(minus_abc, &[P,Q,R]); +/// // Note: minus_abc.into_iter(): Iterator +/// +/// assert_eq!(A1.compress(), (-A2).compress()); +/// ``` // XXX later when we do more fancy multiscalar mults, we can delegate // based on the iter's size hint -- hdevalence #[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint - where I: IntoIterator, - J: IntoIterator +pub fn multiscalar_mult(scalars: I, points: J) -> EdwardsPoint + where I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] { @@ -576,7 +590,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint use clear_on_drop::ClearOnDrop; let lookup_tables_vec: Vec<_> = points.into_iter() - .map(|P| LookupTable::::from(P) ) + .map(|P| LookupTable::::from(P.borrow()) ) .collect(); let lookup_tables = ClearOnDrop::new(lookup_tables_vec); @@ -587,7 +601,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint // // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. let scalar_digits_vec: Vec<_> = scalars.into_iter() - .map(|c| c.to_radix_16()) + .map(|c| c.borrow().to_radix_16()) .collect(); // This above puts the scalar digits into a heap-allocated Vec. @@ -886,20 +900,57 @@ pub mod vartime { } } - /// Given an iterable of public scalars and an iterable of public - /// points, compute + /// Given an iterator of public scalars and an iterator of public points, compute /// $$ /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. /// $$ /// - /// # Input + /// This function has the same behaviour as + /// `edwards::multiscalar_mult` but operates on non-secret data. /// - /// A iterable of `Scalar`s and a iterable of `EdwardsPoints`. It is an - /// error to call this function with two iterators of different lengths. + /// 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_mult(&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_mult(minus_abc, &[P,Q,R]); + /// // Note: minus_abc.into_iter(): Iterator + /// + /// assert_eq!(A1.compress(), (-A2).compress()); + /// ``` + // XXX later when we do more fancy multiscalar mults, we can delegate + // based on the iter's size hint -- hdevalence #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint - where I: IntoIterator, - J: IntoIterator + pub fn multiscalar_mult(scalars: I, points: J) -> EdwardsPoint + where I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] { @@ -912,9 +963,9 @@ pub mod vartime { //assert_eq!(scalars.len(), points.len()); let nafs: Vec<_> = scalars.into_iter() - .map(|c| c.non_adjacent_form()).collect(); + .map(|c| c.borrow().non_adjacent_form()).collect(); let odd_multiples: Vec<_> = points.into_iter() - .map(|P| OddMultiples::create(P)).collect(); + .map(|P| OddMultiples::create(P.borrow())).collect(); let mut r = ProjectivePoint::identity(); diff --git a/src/ristretto.rs b/src/ristretto.rs index 6c30121..c809dbf 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -463,6 +463,10 @@ mod notes { } use core::fmt::Debug; +use core::ops::{Add, Sub, Neg}; +use core::ops::{AddAssign, SubAssign}; +use core::ops::{Mul, MulAssign}; +use core::borrow::Borrow; #[cfg(feature = "std")] use rand::Rng; @@ -473,10 +477,6 @@ use generic_array::typenum::U32; use constants; use field::FieldElement; -use core::ops::{Add, Sub, Neg}; -use core::ops::{AddAssign, SubAssign}; -use core::ops::{Mul, MulAssign}; - use subtle; use subtle::ConditionallyAssignable; use subtle::ConditionallyNegatable; @@ -1059,16 +1059,49 @@ define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint /// This function has the same behaviour as /// `vartime::multiscalar_mult` but is constant-time. /// -/// # Input +/// It is an error to call this function with two iterators of different lengths. /// -/// An iterable of `Scalar`s and a iterable of `RistrettoPoints`. 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_mult(&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_mult(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_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint - where I: IntoIterator, - J: IntoIterator, +pub fn multiscalar_mult(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.0); + let extended_points = points.into_iter().map(|P| P.borrow().0); RistrettoPoint(edwards::multiscalar_mult(scalars, extended_points)) } @@ -1169,22 +1202,57 @@ pub mod vartime { //! Variable-time operations on ristretto points, useful for non-secret data. use super::*; - /// Given an iterable of public scalars and an iterable of public - /// points, compute + /// Given an iterator of public scalars and an iterator of public points, compute /// $$ /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. /// $$ /// - /// # Input + /// This function has the same behaviour as + /// `vartime::multiscalar_mult` but is constant-time. /// - /// A iterable of `Scalar`s and a iterable of `RistrettoPoints`. It is an - /// error to call this function with two iterators of different lengths. + /// 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_mult(&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_mult(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_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint - where I: IntoIterator, - J: IntoIterator + pub fn multiscalar_mult(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.0); + let extended_points = points.into_iter().map(|P| P.borrow().0); RistrettoPoint(edwards::vartime::multiscalar_mult(scalars, extended_points)) } }