Allow Options in the VartimeMultiscalarMul trait

This changes the primary function for the `VartimeMultiscalarMul` trait
to an `optional_multiscalar_mul` trait that accepts
`Option<Self::Point>` (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.
This commit is contained in:
Henry de Valence 2018-07-17 08:19:48 -07:00
parent 7bbf7495b0
commit b4db0afe18
4 changed files with 88 additions and 20 deletions

View file

@ -556,12 +556,11 @@ impl MultiscalarMul for EdwardsPoint {
impl VartimeMultiscalarMul for EdwardsPoint {
type Point = EdwardsPoint;
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
J: IntoIterator<Item = Option<EdwardsPoint>>,
{
// 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)
}
}
}

View file

@ -816,17 +816,16 @@ impl MultiscalarMul for RistrettoPoint {
impl VartimeMultiscalarMul for RistrettoPoint {
type Point = RistrettoPoint;
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<RistrettoPoint>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<RistrettoPoint>,
J: IntoIterator<Item = Option<RistrettoPoint>>,
{
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))
}
}

View file

@ -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<I, J>(scalars: I, points: J) -> EdwardsPoint
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
J: IntoIterator<Item = Option<EdwardsPoint>>,
{
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::<ProjectiveNielsPoint>::from(P.borrow()))
.collect();
.map(|P_opt| P_opt.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(&P)))
.collect::<Option<Vec<_>>>()
{
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())
}
}

View file

@ -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<Point>` 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<I, J>(scalars: I, points: J) -> Option<Self::Point>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator<Item = Option<Self::Point>>;
/// 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<I, J>(scalars: I, points: J) -> Self::Point
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Self::Point>;
J::Item: Borrow<Self::Point>,
Self::Point: Clone,
{
Self::optional_multiscalar_mul(
scalars,
points.into_iter().map(|P| Some(P.borrow().clone()))
).unwrap()
}
}
// ------------------------------------------------------------------------