Merge pull request #163 from hdevalence/fallible-multiscalar-mul

Allow Options in the VartimeMultiscalarMul trait
This commit is contained in:
Henry de Valence 2018-07-20 11:28:19 -07:00 committed by GitHub
commit bb50700d77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 25 deletions

View file

@ -558,12 +558,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
@ -572,13 +571,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,11 +106,70 @@ 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
/// `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
/// $$
/// 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 +182,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,22 +198,30 @@ 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<Item=&Scalar>
///
/// // 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<Item=Scalar>
///
/// 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()
}
}
// ------------------------------------------------------------------------