mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-04 20:24:10 +00:00
Generalize trait bounds on multiscalar multiplication.
This allows iterators returning either &Scalars or Scalars, so that it's possible to use map() and friends to adjust scalars as they're being fed into the multiscalar multiplication.
This commit is contained in:
parent
4a4ec74100
commit
0f185d3e28
3 changed files with 176 additions and 76 deletions
|
|
@ -453,32 +453,19 @@ impl EdwardsBasepointTable {
|
|||
}
|
||||
}
|
||||
|
||||
/// Given a vector of (possibly secret) scalars and a vector of
|
||||
/// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`.
|
||||
///
|
||||
/// This function has the same behaviour as
|
||||
/// `vartime::multiscalar_mult` but is constant-time.
|
||||
///
|
||||
/// # Input
|
||||
///
|
||||
/// A vector of `Scalar`s and a vector of `EdwardsPoints`. It is an
|
||||
/// error to call this function with two vectors of different lengths.
|
||||
///
|
||||
/// XXX this takes `edwards::EdwardsPoints` because we have to alloc scratch space here anyways,
|
||||
/// and we need some space to store the converted points, so we may as well do the conversion here.
|
||||
/// maybe there's a better way to avoid code duplication... however we can't quite just write a
|
||||
/// generic `multiscalar_mult` because the non-vectorized code passes between models and this code
|
||||
/// doesn't.
|
||||
/// Internal multiscalar code.
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::EdwardsPoint
|
||||
where I: IntoIterator<Item = &'a Scalar>,
|
||||
J: IntoIterator<Item = &'b edwards::EdwardsPoint>
|
||||
pub fn multiscalar_mult<I, J>(scalars: I, points: J) -> edwards::EdwardsPoint
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<edwards::EdwardsPoint>,
|
||||
{
|
||||
//assert_eq!(scalars.len(), points.len());
|
||||
|
||||
use clear_on_drop::ClearOnDrop;
|
||||
let lookup_tables_vec: Vec<_> = points.into_iter()
|
||||
.map(|P| LookupTable::from(ExtendedPoint::from(*P)) )
|
||||
.map(|P| LookupTable::from(ExtendedPoint::from(*P.borrow())) )
|
||||
.collect();
|
||||
|
||||
let lookup_tables = ClearOnDrop::new(lookup_tables_vec);
|
||||
|
|
@ -489,7 +476,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::Edwards
|
|||
//
|
||||
// with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`.
|
||||
let scalar_digits_vec: Vec<_> = scalars.into_iter()
|
||||
.map(|c| c.to_radix_16())
|
||||
.map(|c| c.borrow().to_radix_16())
|
||||
.collect();
|
||||
|
||||
// The above puts the scalar digits into a heap-allocated Vec.
|
||||
|
|
@ -606,27 +593,21 @@ pub mod vartime {
|
|||
Q.into()
|
||||
}
|
||||
|
||||
/// Given a vector of public scalars and a vector of public points, compute
|
||||
/// $$
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||
/// $$
|
||||
///
|
||||
/// # Input
|
||||
///
|
||||
/// A vector of `Scalar`s and a vector of `EdwardsPoints`. It is an
|
||||
/// error to call this function with two vectors of different lengths.
|
||||
/// Internal multiscalar function
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::EdwardsPoint
|
||||
where I: IntoIterator<Item = &'a Scalar>,
|
||||
J: IntoIterator<Item = &'b edwards::EdwardsPoint>
|
||||
pub fn multiscalar_mult<I, J>(scalars: I, points: J) -> edwards::EdwardsPoint
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<edwards::EdwardsPoint>,
|
||||
{
|
||||
//assert_eq!(scalars.len(), points.len());
|
||||
|
||||
let nafs: Vec<_> = scalars.into_iter()
|
||||
.map(|c| c.non_adjacent_form()).collect();
|
||||
.map(|c| c.borrow().non_adjacent_form()).collect();
|
||||
|
||||
let odd_multiples: Vec<_> = points.into_iter()
|
||||
.map(|P| OddMultiples::create((*P).into()) ).collect();
|
||||
.map(|P| OddMultiples::create((*P.borrow()).into()) ).collect();
|
||||
|
||||
let mut Q = ExtendedPoint::identity();
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ use core::ops::{Add, Sub, Neg};
|
|||
use core::ops::{AddAssign, SubAssign};
|
||||
use core::ops::{Mul, MulAssign};
|
||||
use core::ops::Index;
|
||||
use core::borrow::Borrow;
|
||||
|
||||
use subtle::slices_equal;
|
||||
use subtle::ConditionallyAssignable;
|
||||
|
|
@ -532,12 +533,15 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar {
|
|||
/// This function has the same behaviour as
|
||||
/// `vartime::multiscalar_mult` but is constant-time.
|
||||
///
|
||||
/// # Input
|
||||
///
|
||||
/// A iterable of `Scalar`s and a iterable of `EdwardsPoints`. It is an
|
||||
/// error to call this function with two iterators of different lengths.
|
||||
/// It is an error to call this function with two iterators of different lengths.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The trait bound aims for maximum flexibility: the inputs must be
|
||||
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
||||
/// must be `Borrow<Scalar>` (or `Borrow<EdwardsPoint>`), to allow
|
||||
/// iterators returning either `Scalar`s or `&Scalar`s.
|
||||
///
|
||||
/// ```
|
||||
/// use curve25519_dalek::{constants, edwards};
|
||||
/// use curve25519_dalek::scalar::Scalar;
|
||||
|
|
@ -553,15 +557,25 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar {
|
|||
/// let R = P + Q;
|
||||
///
|
||||
/// // A1 = a*P + b*Q + c*R
|
||||
/// let A1 = edwards::multiscalar_mult(&[a,b,c], &[P,Q,R]);
|
||||
/// ```
|
||||
/// let abc = [a,b,c];
|
||||
/// let A1 = edwards::multiscalar_mult(&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 = edwards::multiscalar_mult(minus_abc, &[P,Q,R]);
|
||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
// XXX later when we do more fancy multiscalar mults, we can delegate
|
||||
// based on the iter's size hint -- hdevalence
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where I: IntoIterator<Item = &'a Scalar>,
|
||||
J: IntoIterator<Item = &'b EdwardsPoint>
|
||||
pub fn multiscalar_mult<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] {
|
||||
|
|
@ -576,7 +590,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint
|
|||
use clear_on_drop::ClearOnDrop;
|
||||
|
||||
let lookup_tables_vec: Vec<_> = points.into_iter()
|
||||
.map(|P| LookupTable::<ProjectiveNielsPoint>::from(P) )
|
||||
.map(|P| LookupTable::<ProjectiveNielsPoint>::from(P.borrow()) )
|
||||
.collect();
|
||||
|
||||
let lookup_tables = ClearOnDrop::new(lookup_tables_vec);
|
||||
|
|
@ -587,7 +601,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint
|
|||
//
|
||||
// with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`.
|
||||
let scalar_digits_vec: Vec<_> = scalars.into_iter()
|
||||
.map(|c| c.to_radix_16())
|
||||
.map(|c| c.borrow().to_radix_16())
|
||||
.collect();
|
||||
|
||||
// This above puts the scalar digits into a heap-allocated Vec.
|
||||
|
|
@ -886,20 +900,57 @@ pub mod vartime {
|
|||
}
|
||||
}
|
||||
|
||||
/// Given an iterable of public scalars and an iterable of public
|
||||
/// points, compute
|
||||
/// Given an iterator of public scalars and an iterator of public points, compute
|
||||
/// $$
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||
/// $$
|
||||
///
|
||||
/// # Input
|
||||
/// This function has the same behaviour as
|
||||
/// `edwards::multiscalar_mult` but operates on non-secret data.
|
||||
///
|
||||
/// A iterable of `Scalar`s and a iterable of `EdwardsPoints`. It is an
|
||||
/// error to call this function with two iterators of different lengths.
|
||||
/// It is an error to call this function with two iterators of different lengths.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The trait bound aims for maximum flexibility: the inputs must be
|
||||
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
||||
/// must be `Borrow<Scalar>` (or `Borrow<EdwardsPoint>`), to allow
|
||||
/// iterators returning either `Scalar`s or `&Scalar`s.
|
||||
///
|
||||
/// ```
|
||||
/// use curve25519_dalek::{constants, edwards};
|
||||
/// 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);
|
||||
///
|
||||
/// // Some points
|
||||
/// let P = constants::ED25519_BASEPOINT_POINT;
|
||||
/// let Q = P + P;
|
||||
/// let R = P + Q;
|
||||
///
|
||||
/// // A1 = a*P + b*Q + c*R
|
||||
/// let abc = [a,b,c];
|
||||
/// let A1 = edwards::vartime::multiscalar_mult(&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 = edwards::vartime::multiscalar_mult(minus_abc, &[P,Q,R]);
|
||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
// XXX later when we do more fancy multiscalar mults, we can delegate
|
||||
// based on the iter's size hint -- hdevalence
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where I: IntoIterator<Item = &'a Scalar>,
|
||||
J: IntoIterator<Item = &'b EdwardsPoint>
|
||||
pub fn multiscalar_mult<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] {
|
||||
|
|
@ -912,9 +963,9 @@ pub mod vartime {
|
|||
//assert_eq!(scalars.len(), points.len());
|
||||
|
||||
let nafs: Vec<_> = scalars.into_iter()
|
||||
.map(|c| c.non_adjacent_form()).collect();
|
||||
.map(|c| c.borrow().non_adjacent_form()).collect();
|
||||
let odd_multiples: Vec<_> = points.into_iter()
|
||||
.map(|P| OddMultiples::create(P)).collect();
|
||||
.map(|P| OddMultiples::create(P.borrow())).collect();
|
||||
|
||||
let mut r = ProjectivePoint::identity();
|
||||
|
||||
|
|
|
|||
108
src/ristretto.rs
108
src/ristretto.rs
|
|
@ -463,6 +463,10 @@ mod notes {
|
|||
}
|
||||
|
||||
use core::fmt::Debug;
|
||||
use core::ops::{Add, Sub, Neg};
|
||||
use core::ops::{AddAssign, SubAssign};
|
||||
use core::ops::{Mul, MulAssign};
|
||||
use core::borrow::Borrow;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use rand::Rng;
|
||||
|
|
@ -473,10 +477,6 @@ use generic_array::typenum::U32;
|
|||
use constants;
|
||||
use field::FieldElement;
|
||||
|
||||
use core::ops::{Add, Sub, Neg};
|
||||
use core::ops::{AddAssign, SubAssign};
|
||||
use core::ops::{Mul, MulAssign};
|
||||
|
||||
use subtle;
|
||||
use subtle::ConditionallyAssignable;
|
||||
use subtle::ConditionallyNegatable;
|
||||
|
|
@ -1059,16 +1059,49 @@ define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint
|
|||
/// This function has the same behaviour as
|
||||
/// `vartime::multiscalar_mult` but is constant-time.
|
||||
///
|
||||
/// # Input
|
||||
/// It is an error to call this function with two iterators of different lengths.
|
||||
///
|
||||
/// An iterable of `Scalar`s and a iterable of `RistrettoPoints`. It is an
|
||||
/// error to call this function with two iterators of different lengths.
|
||||
/// # Examples
|
||||
///
|
||||
/// The trait bound aims for maximum flexibility: the inputs must be
|
||||
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
||||
/// must be `Borrow<Scalar>` (or `Borrow<RistrettoPoint>`), to allow
|
||||
/// iterators returning either `Scalar`s or `&Scalar`s.
|
||||
///
|
||||
/// ```
|
||||
/// use curve25519_dalek::{constants, ristretto};
|
||||
/// 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);
|
||||
///
|
||||
/// // Some points
|
||||
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
||||
/// let Q = P + P;
|
||||
/// let R = P + Q;
|
||||
///
|
||||
/// // A1 = a*P + b*Q + c*R
|
||||
/// let abc = [a,b,c];
|
||||
/// let A1 = ristretto::multiscalar_mult(&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 = ristretto::multiscalar_mult(minus_abc, &[P,Q,R]);
|
||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint
|
||||
where I: IntoIterator<Item = &'a Scalar>,
|
||||
J: IntoIterator<Item = &'b RistrettoPoint>,
|
||||
pub fn multiscalar_mult<I, J>(scalars: I, points: J) -> RistrettoPoint
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<RistrettoPoint>,
|
||||
{
|
||||
let extended_points = points.into_iter().map(|P| &P.0);
|
||||
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
||||
RistrettoPoint(edwards::multiscalar_mult(scalars, extended_points))
|
||||
}
|
||||
|
||||
|
|
@ -1169,22 +1202,57 @@ pub mod vartime {
|
|||
//! Variable-time operations on ristretto points, useful for non-secret data.
|
||||
use super::*;
|
||||
|
||||
/// Given an iterable of public scalars and an iterable of public
|
||||
/// points, compute
|
||||
/// Given an iterator of public scalars and an iterator of public points, compute
|
||||
/// $$
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||
/// $$
|
||||
///
|
||||
/// # Input
|
||||
/// This function has the same behaviour as
|
||||
/// `vartime::multiscalar_mult` but is constant-time.
|
||||
///
|
||||
/// A iterable of `Scalar`s and a iterable of `RistrettoPoints`. It is an
|
||||
/// error to call this function with two iterators of different lengths.
|
||||
/// It is an error to call this function with two iterators of different lengths.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The trait bound aims for maximum flexibility: the inputs must be
|
||||
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
||||
/// must be `Borrow<Scalar>` (or `Borrow<RistrettoPoint>`), to allow
|
||||
/// iterators returning either `Scalar`s or `&Scalar`s.
|
||||
///
|
||||
/// ```
|
||||
/// use curve25519_dalek::{constants, ristretto};
|
||||
/// 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);
|
||||
///
|
||||
/// // Some points
|
||||
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
||||
/// let Q = P + P;
|
||||
/// let R = P + Q;
|
||||
///
|
||||
/// // A1 = a*P + b*Q + c*R
|
||||
/// let abc = [a,b,c];
|
||||
/// let A1 = ristretto::vartime::multiscalar_mult(&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 = ristretto::vartime::multiscalar_mult(minus_abc, &[P,Q,R]);
|
||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint
|
||||
where I: IntoIterator<Item = &'a Scalar>,
|
||||
J: IntoIterator<Item = &'b RistrettoPoint>
|
||||
pub fn multiscalar_mult<I, J>(scalars: I, points: J) -> RistrettoPoint
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<RistrettoPoint>,
|
||||
{
|
||||
let extended_points = points.into_iter().map(|P| &P.0);
|
||||
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
||||
RistrettoPoint(edwards::vartime::multiscalar_mult(scalars, extended_points))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue