From 7bbf7495b0440446fec212a3cbd9144d60cb56f1 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 16 Jul 2018 22:54:45 -0700 Subject: [PATCH 1/2] Change VartimeMultiscalarMul docs to use vartime_ --- src/traits.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/traits.rs b/src/traits.rs index aac84d0..42530b2 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -106,11 +106,12 @@ 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 + /// Given an iterator of public scalars and an iterator of /// public points, compute /// $$ - /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. + /// Q = c\_1 P\_1 + \cdots + c\_n P\_n, /// $$ + /// using variable-time operations. /// /// It is an error to call this function with two iterators of different lengths. /// @@ -123,7 +124,7 @@ pub trait VartimeMultiscalarMul { /// /// ``` /// use curve25519_dalek::constants; - /// use curve25519_dalek::traits::MultiscalarMul; + /// use curve25519_dalek::traits::VartimeMultiscalarMul; /// use curve25519_dalek::ristretto::RistrettoPoint; /// use curve25519_dalek::scalar::Scalar; /// @@ -139,12 +140,12 @@ pub trait VartimeMultiscalarMul { /// /// // A1 = a*P + b*Q + c*R /// let abc = [a,b,c]; - /// let A1 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]); + /// let A1 = RistrettoPoint::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 = RistrettoPoint::multiscalar_mul(minus_abc, &[P,Q,R]); + /// let A2 = RistrettoPoint::vartime_multiscalar_mul(minus_abc, &[P,Q,R]); /// // Note: minus_abc.into_iter(): Iterator /// /// assert_eq!(A1.compress(), (-A2).compress()); From b4db0afe18d59523c962a915b03e365e379bf9e7 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 17 Jul 2018 08:19:48 -0700 Subject: [PATCH 2/2] Allow Options in the VartimeMultiscalarMul trait This changes the primary function for the `VartimeMultiscalarMul` trait to an `optional_multiscalar_mul` trait that accepts `Option` (and returns `None` if any input points are `None`). The existing `vartime_multiscalar_mul` is changed to be a wrapper around this function to avoid code duplication. This may result in an extra copy of each input point, but that cost is probably not significant compared to the cost of the multiscalar multiplication. The motivation is to allow performing multiscalar multiplications with inline decompression. Currently, API consumers have to allocate temporary buffers for all of their points, decompress into those buffers, then pass (iterators over) those buffers into the multiscalar multiplication code, which then creates new buffers for lookup tables. --- src/edwards.rs | 9 +++--- src/ristretto.rs | 13 ++++---- src/scalar_mul/straus.rs | 18 ++++++----- src/traits.rs | 68 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 20 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index 7e15fce..7353da1 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -556,12 +556,11 @@ impl MultiscalarMul for EdwardsPoint { impl VartimeMultiscalarMul for EdwardsPoint { type Point = EdwardsPoint; - fn vartime_multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { // XXX later when we do more fancy multiscalar mults, we can // delegate based on the iter's size hint -- hdevalence @@ -570,13 +569,13 @@ impl VartimeMultiscalarMul for EdwardsPoint { #[cfg(all(feature="avx2_backend", target_feature="avx2"))] { use backend::avx2::scalar_mul::straus::Straus; - Straus::vartime_multiscalar_mul(scalars, points) + Straus::optional_multiscalar_mul(scalars, points) } // Otherwise, proceed as normal: #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] { use scalar_mul::straus::Straus; - Straus::vartime_multiscalar_mul(scalars, points) + Straus::optional_multiscalar_mul(scalars, points) } } } diff --git a/src/ristretto.rs b/src/ristretto.rs index 5581a19..06d977f 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -816,17 +816,16 @@ impl MultiscalarMul for RistrettoPoint { impl VartimeMultiscalarMul for RistrettoPoint { type Point = RistrettoPoint; - fn vartime_multiscalar_mul(scalars: I, points: J) -> RistrettoPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { - let extended_points = points.into_iter().map(|P| P.borrow().0); - RistrettoPoint( - EdwardsPoint::vartime_multiscalar_mul(scalars, extended_points) - ) + let extended_points = points.into_iter().map(|opt_P| opt_P.map(|P| P.borrow().0)); + + EdwardsPoint::optional_multiscalar_mul(scalars, extended_points) + .map(|P| RistrettoPoint(P)) } } diff --git a/src/scalar_mul/straus.rs b/src/scalar_mul/straus.rs index 21bf29e..cfa563f 100644 --- a/src/scalar_mul/straus.rs +++ b/src/scalar_mul/straus.rs @@ -152,12 +152,11 @@ impl VartimeMultiscalarMul for Straus { /// The non-adjacent form has signed, odd digits. Using only odd /// digits halves the table size (since we only need odd /// multiples), or gives fewer additions for the same table size. - fn vartime_multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { use curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint}; use scalar_mul::window::NafLookupTable5; @@ -167,10 +166,15 @@ impl VartimeMultiscalarMul for Straus { .into_iter() .map(|c| c.borrow().non_adjacent_form(5)) .collect(); - let lookup_tables: Vec<_> = points + + let lookup_tables = match points .into_iter() - .map(|P| NafLookupTable5::::from(P.borrow())) - .collect(); + .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) + .collect::>>() + { + Some(x) => x, + None => return None, + }; let mut r = ProjectivePoint::identity(); @@ -188,6 +192,6 @@ impl VartimeMultiscalarMul for Straus { r = t.to_projective(); } - r.to_extended() + Some(r.to_extended()) } } diff --git a/src/traits.rs b/src/traits.rs index 42530b2..2ed295a 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -106,6 +106,64 @@ pub trait VartimeMultiscalarMul { /// The type of point being multiplied, e.g., `RistrettoPoint`. type Point; + /// Given an iterator of public scalars and an iterator of + /// `Option`s of points, compute either `Some(Q)`, where + /// $$ + /// Q = c\_1 P\_1 + \cdots + c\_n P\_n, + /// $$ + /// if all points were `Some(P_i)`, or else return `None`. + /// + /// This function is particularly useful when verifying statements + /// involving compressed points. Accepting `Option` allows + /// inlining point decompression into the multiscalar call, + /// avoiding the need for temporary buffers. + /// ``` + /// use curve25519_dalek::constants; + /// use curve25519_dalek::traits::VartimeMultiscalarMul; + /// 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); + /// let abc = [a,b,c]; + /// + /// // Some points + /// let P = constants::RISTRETTO_BASEPOINT_POINT; + /// let Q = P + P; + /// let R = P + Q; + /// let PQR = [P, Q, R]; + /// + /// let compressed = [P.compress(), Q.compress(), R.compress()]; + /// + /// // Now we can compute A1 = a*P + b*Q + c*R using P, Q, R: + /// let A1 = RistrettoPoint::vartime_multiscalar_mul(&abc, &PQR); + /// + /// // Or using the compressed points: + /// let A2 = RistrettoPoint::optional_multiscalar_mul( + /// &abc, + /// compressed.iter().map(|pt| pt.decompress()), + /// ); + /// + /// assert_eq!(A2, Some(A1)); + /// + /// // It's also possible to mix compressed and uncompressed points: + /// let A3 = RistrettoPoint::optional_multiscalar_mul( + /// abc.iter() + /// .chain(abc.iter()), + /// compressed.iter().map(|pt| pt.decompress()) + /// .chain(PQR.iter().map(|&pt| Some(pt))), + /// ); + /// + /// assert_eq!(A3, Some(A1+A1)); + /// ``` + fn optional_multiscalar_mul(scalars: I, points: J) -> Option + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator>; + /// Given an iterator of public scalars and an iterator of /// public points, compute /// $$ @@ -150,12 +208,20 @@ pub trait VartimeMultiscalarMul { /// /// assert_eq!(A1.compress(), (-A2).compress()); /// ``` + #[allow(non_snake_case)] fn vartime_multiscalar_mul(scalars: I, points: J) -> Self::Point where I: IntoIterator, I::Item: Borrow, J: IntoIterator, - J::Item: Borrow; + J::Item: Borrow, + Self::Point: Clone, + { + Self::optional_multiscalar_mul( + scalars, + points.into_iter().map(|P| Some(P.borrow().clone())) + ).unwrap() + } } // ------------------------------------------------------------------------