mirror of
https://github.com/saymrwulf/risc0-curve25519-dalek-source.git
synced 2026-09-04 20:03:40 +00:00
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:
parent
7bbf7495b0
commit
b4db0afe18
4 changed files with 88 additions and 20 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Reference in a new issue