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() + } } // ------------------------------------------------------------------------