diff --git a/src/backend/serial/scalar_mul/precomputed_straus.rs b/src/backend/serial/scalar_mul/precomputed_straus.rs index 989d92a..ceeb8b9 100644 --- a/src/backend/serial/scalar_mul/precomputed_straus.rs +++ b/src/backend/serial/scalar_mul/precomputed_straus.rs @@ -136,19 +136,18 @@ impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { } } - fn vartime_mixed_multiscalar_mul( + fn optional_mixed_multiscalar_mul( &self, static_scalars: I, dynamic_scalars: J, dynamic_points: K, - ) -> Self::Point + ) -> Option where I: IntoIterator, I::Item: Borrow, J: IntoIterator, J::Item: Borrow, - K: IntoIterator, - K::Item: Borrow, + K: IntoIterator>, { let static_nafs = static_scalars .into_iter() @@ -159,10 +158,14 @@ impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { .map(|c| c.borrow().non_adjacent_form(5)) .collect::>(); - let dynamic_lookup_tables = dynamic_points + let dynamic_lookup_tables = match dynamic_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 sp = self.static_lookup_tables.len(); let dp = dynamic_lookup_tables.len(); @@ -197,6 +200,6 @@ impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { S = R.to_projective(); } - S.to_extended() + Some(S.to_extended()) } } diff --git a/src/backend/vector/scalar_mul/precomputed_straus.rs b/src/backend/vector/scalar_mul/precomputed_straus.rs index dc3fc3f..2ddfa24 100644 --- a/src/backend/vector/scalar_mul/precomputed_straus.rs +++ b/src/backend/vector/scalar_mul/precomputed_straus.rs @@ -134,19 +134,18 @@ impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { } } - fn vartime_mixed_multiscalar_mul( + fn optional_mixed_multiscalar_mul( &self, static_scalars: I, dynamic_scalars: J, dynamic_points: K, - ) -> Self::Point + ) -> Option where I: IntoIterator, I::Item: Borrow, J: IntoIterator, J::Item: Borrow, - K: IntoIterator, - K::Item: Borrow, + K: IntoIterator>, { let static_nafs = static_scalars .into_iter() @@ -157,10 +156,14 @@ impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { .map(|c| c.borrow().non_adjacent_form(5)) .collect::>(); - let dynamic_lookup_tables = dynamic_points + let dynamic_lookup_tables = match dynamic_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 sp = self.static_lookup_tables.len(); let dp = dynamic_lookup_tables.len(); @@ -193,6 +196,6 @@ impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { } } - R.into() + Some(R.into()) } } diff --git a/src/edwards.rs b/src/edwards.rs index fa7029e..04c6565 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -732,22 +732,21 @@ impl VartimePrecomputedMultiscalarMul for VartimeEdwardsPrecomputation { Self(scalar_mul::precomputed_straus::VartimePrecomputedStraus::new(static_points)) } - fn vartime_mixed_multiscalar_mul( + fn optional_mixed_multiscalar_mul( &self, static_scalars: I, dynamic_scalars: J, dynamic_points: K, - ) -> Self::Point + ) -> Option where I: IntoIterator, I::Item: Borrow, J: IntoIterator, J::Item: Borrow, - K: IntoIterator, - K::Item: Borrow, + K: IntoIterator>, { self.0 - .vartime_mixed_multiscalar_mul(static_scalars, dynamic_scalars, dynamic_points) + .optional_mixed_multiscalar_mul(static_scalars, dynamic_scalars, dynamic_points) } } diff --git a/src/traits.rs b/src/traits.rs index 630e510..c1166f5 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -219,8 +219,9 @@ pub trait VartimeMultiscalarMul { { Self::optional_multiscalar_mul( scalars, - points.into_iter().map(|P| Some(P.borrow().clone())) - ).unwrap() + points.into_iter().map(|P| Some(P.borrow().clone())), + ) + .unwrap() } } @@ -313,9 +314,26 @@ pub trait PrecomputedMultiscalarMul: Sized { /// where the \\(B_i\\) are *static* points, for which precomputation /// is possible, and the \\(A_j\\) are *dynamic* points, for which /// precomputation is not possible. +/// +/// This trait has three methods for performing this computation: +/// +/// * [`vartime_multiscalar_mul`], which handles the special case +/// where \\(n = 0\\) and there are no dynamic points; +/// +/// * [`vartime_mixed_multiscalar_mul`], which takes the dynamic +/// points as already-validated `Point`s and is infallible; +/// +/// * [`optional_mixed_multiscalar_mul`], which takes the dynamic +/// points as `Option`s and returns an `Option`, +/// allowing decompression to be composed into the input iterators. +/// +/// All methods require that the lengths of the input iterators be +/// known and matching, as if they were `ExactSizeIterator`s. (It +/// does not require `ExactSizeIterator` only because that trait is +/// broken). pub trait VartimePrecomputedMultiscalarMul: Sized { /// The type of point to be multiplied, e.g., `RistrettoPoint`. - type Point; + type Point: Clone; /// Given the static points \\( B_i \\), perform precomputation /// and return the precomputation data. @@ -324,36 +342,6 @@ pub trait VartimePrecomputedMultiscalarMul: Sized { I: IntoIterator, I::Item: Borrow; - /// Given `static_scalars`, an iterator of public scalars - /// \\(b_i\\), `dynamic_scalars`, an iterator of public scalars - /// \\(a_i\\), and `dynamic_points`, an iterator of points - /// \\(A_i\\), compute - /// $$ - /// Q = a_1 A_1 + \cdots + a_n A_n + b_1 B_1 + \cdots + b_m B_m, - /// $$ - /// where the \\(B_j\\) are the points that were supplied to `new`. - /// - /// It is an error to call this function with iterators of - /// inconsistent lengths. - /// - /// 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. - fn vartime_mixed_multiscalar_mul( - &self, - static_scalars: I, - dynamic_scalars: J, - dynamic_points: K, - ) -> Self::Point - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - K: IntoIterator, - K::Item: Borrow; - /// Given `static_scalars`, an iterator of public scalars /// \\(b_i\\), compute /// $$ @@ -382,6 +370,76 @@ pub trait VartimePrecomputedMultiscalarMul: Sized { iter::empty::(), ) } + + /// Given `static_scalars`, an iterator of public scalars + /// \\(b_i\\), `dynamic_scalars`, an iterator of public scalars + /// \\(a_i\\), and `dynamic_points`, an iterator of points + /// \\(A_i\\), compute + /// $$ + /// Q = a_1 A_1 + \cdots + a_n A_n + b_1 B_1 + \cdots + b_m B_m, + /// $$ + /// where the \\(B_j\\) are the points that were supplied to `new`. + /// + /// It is an error to call this function with iterators of + /// inconsistent lengths. + /// + /// 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. + fn vartime_mixed_multiscalar_mul( + &self, + static_scalars: I, + dynamic_scalars: J, + dynamic_points: K, + ) -> Self::Point + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + K: IntoIterator, + K::Item: Borrow, + { + Self::optional_mixed_multiscalar_mul( + self, + static_scalars, + dynamic_scalars, + dynamic_points.into_iter().map(|P| Some(P.borrow().clone())), + ) + .unwrap() + } + + /// Given `static_scalars`, an iterator of public scalars + /// \\(b_i\\), `dynamic_scalars`, an iterator of public scalars + /// \\(a_i\\), and `dynamic_points`, an iterator of points + /// \\(A_i\\), compute + /// $$ + /// Q = a_1 A_1 + \cdots + a_n A_n + b_1 B_1 + \cdots + b_m B_m, + /// $$ + /// where the \\(B_j\\) are the points that were supplied to `new`. + /// + /// If any of the dynamic points were `None`, return `None`. + /// + /// It is an error to call this function with iterators of + /// inconsistent lengths. + /// + /// 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. + fn optional_mixed_multiscalar_mul( + &self, + static_scalars: I, + dynamic_scalars: J, + dynamic_points: K, + ) -> Option + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + K: IntoIterator>; } // ------------------------------------------------------------------------