mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-04 20:24:10 +00:00
Connect multiscalar_mult to the AVX2 backend
This commit is contained in:
parent
912fc5d412
commit
b5305b4e30
2 changed files with 112 additions and 86 deletions
|
|
@ -391,20 +391,25 @@ impl EdwardsBasepointTable {
|
|||
/// error to call this function with two vectors of different lengths.
|
||||
///
|
||||
/// XXX need to clear memory
|
||||
///
|
||||
/// XXX this takes `edwards::ExtendedPoints` 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...
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint
|
||||
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::ExtendedPoint
|
||||
where I: IntoIterator<Item = &'a Scalar>,
|
||||
J: IntoIterator<Item = &'b ExtendedPoint>
|
||||
J: IntoIterator<Item = &'b edwards::ExtendedPoint>
|
||||
{
|
||||
use traits::select_precomputed_point;
|
||||
//assert_eq!(scalars.len(), points.len());
|
||||
|
||||
let lookup_tables: Vec<_> = points.into_iter()
|
||||
.map(|P_i| {
|
||||
// Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i]
|
||||
let mut lookup_table: [ExtendedPoint; 8] = [*P_i; 8];
|
||||
.map(|P| {
|
||||
let P = ExtendedPoint::from(*P);
|
||||
// Construct a lookup table of [P,2*P,3*P,4*P,5*P,6*P,7*P]
|
||||
let mut lookup_table: [ExtendedPoint; 8] = [P; 8];
|
||||
for i in 0..7 {
|
||||
lookup_table[i+1] = P_i + &lookup_table[i];
|
||||
lookup_table[i+1] = &P + &lookup_table[i];
|
||||
}
|
||||
lookup_table
|
||||
}).collect();
|
||||
|
|
@ -448,7 +453,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint
|
|||
Q = &Q + &R_i;
|
||||
}
|
||||
}
|
||||
Q
|
||||
Q.into()
|
||||
}
|
||||
|
||||
pub mod vartime {
|
||||
|
|
@ -464,10 +469,12 @@ pub mod vartime {
|
|||
/// error to call this function with two vectors of different lengths.
|
||||
///
|
||||
/// XXX need to clear memory
|
||||
///
|
||||
/// XXX see note on consttime multiscalar mul
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint
|
||||
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::ExtendedPoint
|
||||
where I: IntoIterator<Item = &'a Scalar>,
|
||||
J: IntoIterator<Item = &'b ExtendedPoint>
|
||||
J: IntoIterator<Item = &'b edwards::ExtendedPoint>
|
||||
{
|
||||
//assert_eq!(scalars.len(), points.len());
|
||||
|
||||
|
|
@ -475,9 +482,10 @@ pub mod vartime {
|
|||
.map(|c| c.non_adjacent_form()).collect();
|
||||
let odd_multiples: Vec<_> = points.into_iter()
|
||||
.map(|P| {
|
||||
// Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i]
|
||||
let P = ExtendedPoint::from(*P);
|
||||
// Construct a lookup table of [P,2*P,3*P,4*P,5*P,6*P,7*P]
|
||||
let P2 = P.double();
|
||||
let mut lookup_table: [ExtendedPoint; 8] = [*P; 8];
|
||||
let mut lookup_table: [ExtendedPoint; 8] = [P; 8];
|
||||
for i in 0..7 {
|
||||
lookup_table[i+1] = &P2 + &lookup_table[i];
|
||||
}
|
||||
|
|
@ -498,7 +506,7 @@ pub mod vartime {
|
|||
}
|
||||
}
|
||||
}
|
||||
Q
|
||||
Q.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -734,10 +742,10 @@ mod test {
|
|||
|
||||
let R = &(&P1 * &s1) + &(&P2 * &s2);
|
||||
|
||||
let R_multiscalar = multiscalar_mult(&[s1, s2], &[P1, P2]);
|
||||
let R_multiscalar = multiscalar_mult(&[s1, s2], &[P1.into(), P2.into()]);
|
||||
|
||||
assert_eq!(edwards::ExtendedPoint::from(R).compress(),
|
||||
edwards::ExtendedPoint::from(R_multiscalar).compress());
|
||||
R_multiscalar.compress());
|
||||
}
|
||||
|
||||
mod vartime {
|
||||
|
|
@ -754,10 +762,10 @@ mod test {
|
|||
|
||||
let R = &(&P1 * &s1) + &(&P2 * &s2);
|
||||
|
||||
let R_multiscalar = vartime::multiscalar_mult(&[s1, s2], &[P1, P2]);
|
||||
let R_multiscalar = vartime::multiscalar_mult(&[s1, s2], &[P1.into(), P2.into()]);
|
||||
|
||||
assert_eq!(edwards::ExtendedPoint::from(R).compress(),
|
||||
edwards::ExtendedPoint::from(R_multiscalar).compress());
|
||||
R_multiscalar.compress());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -835,7 +843,7 @@ mod bench {
|
|||
let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect();
|
||||
// Create 10 points (by doing scalar mults)
|
||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
||||
let points: Vec<_> = scalars.iter().map(|s| ExtendedPoint::from(B * &s)).collect();
|
||||
let points: Vec<_> = scalars.iter().map(|s| B * &s).collect();
|
||||
|
||||
b.iter(|| multiscalar_mult(&scalars, &points));
|
||||
}
|
||||
|
|
@ -850,7 +858,7 @@ mod bench {
|
|||
// Create 2 random scalars
|
||||
let s1 = Scalar::random(&mut csprng);
|
||||
let s2 = Scalar::random(&mut csprng);
|
||||
let B: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.into();
|
||||
let B = constants::ED25519_BASEPOINT_POINT;
|
||||
let P = &B * &s1;
|
||||
|
||||
b.iter(|| vartime::multiscalar_mult(&[s1, s2], &[B, P]));
|
||||
|
|
@ -863,7 +871,7 @@ mod bench {
|
|||
let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect();
|
||||
// Create 10 points (by doing scalar mults)
|
||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
||||
let points: Vec<_> = scalars.iter().map(|s| ExtendedPoint::from(B * &s)).collect();
|
||||
let points: Vec<_> = scalars.iter().map(|s| B * &s).collect();
|
||||
|
||||
b.iter(|| vartime::multiscalar_mult(&scalars, &points));
|
||||
}
|
||||
|
|
|
|||
152
src/edwards.rs
152
src/edwards.rs
|
|
@ -510,59 +510,68 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint
|
|||
where I: IntoIterator<Item = &'a Scalar>,
|
||||
J: IntoIterator<Item = &'b ExtendedPoint>
|
||||
{
|
||||
//assert_eq!(scalars.len(), points.len());
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(target_feature = "avx2", feature = "avx2_backend"))] {
|
||||
use backend::avx2::edwards as edwards_avx2;
|
||||
|
||||
let lookup_tables: Vec<_> = points.into_iter()
|
||||
.map(|P_i| {
|
||||
// Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i]
|
||||
let mut lookup_table = [P_i.to_projective_niels(); 8];
|
||||
for j in 0..7 {
|
||||
lookup_table[j+1] = (P_i + &lookup_table[j])
|
||||
.to_extended().to_projective_niels();
|
||||
}
|
||||
lookup_table
|
||||
}).collect();
|
||||
|
||||
// Setting s_i = i-th scalar, compute
|
||||
//
|
||||
// s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63,
|
||||
//
|
||||
// with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`.
|
||||
let scalar_digits_list: Vec<_> = scalars.into_iter()
|
||||
.map(|c| c.to_radix_16()).collect();
|
||||
|
||||
// Compute s_1*P_1 + ... + s_n*P_n: since
|
||||
//
|
||||
// s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63)
|
||||
// s_i*P_i = P_i*s_{i,0} + P_i*s_{i,1}*16^1 + ... + P_i*s_{i,63}*16^63
|
||||
// s_i*P_i = P_i*s_{i,0} + 16*(P_i*s_{i,1} + 16*( ... + 16*P_i*s_{i,63})...)
|
||||
//
|
||||
// we have the two-dimensional sum
|
||||
//
|
||||
// s_1*P_1 = P_1*s_{1,0} + 16*(P_1*s_{1,1} + 16*( ... + 16*P_1*s_{1,63})...)
|
||||
// + s_2*P_2 = + P_2*s_{2,0} + 16*(P_2*s_{2,1} + 16*( ... + 16*P_2*s_{2,63})...)
|
||||
// ...
|
||||
// + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...)
|
||||
//
|
||||
// We sum column-wise top-to-bottom, then right-to-left,
|
||||
// multiplying by 16 only once per column.
|
||||
//
|
||||
// This provides the speedup over doing n independent scalar
|
||||
// mults: we perform 63 multiplications by 16 instead of 63*n
|
||||
// multiplications, saving 252*(n-1) doublings.
|
||||
let mut Q = ExtendedPoint::identity();
|
||||
// XXX this impl makes no effort to be cache-aware; maybe it could be improved?
|
||||
for j in (0..64).rev() {
|
||||
Q = Q.mult_by_pow_2(4);
|
||||
let it = scalar_digits_list.iter().zip(lookup_tables.iter());
|
||||
for (s_i, lookup_table_i) in it {
|
||||
// R_i = s_{i,j} * P_i
|
||||
let R_i = select_precomputed_point(s_i[j], lookup_table_i);
|
||||
// Q = Q + R_i
|
||||
Q = (&Q + &R_i).to_extended();
|
||||
}
|
||||
edwards_avx2::multiscalar_mult(scalars, points)
|
||||
}
|
||||
// Otherwise, proceed as normal:
|
||||
#[cfg(not(all(target_feature = "avx2", feature = "avx2_backend")))] {
|
||||
//assert_eq!(scalars.len(), points.len());
|
||||
|
||||
let lookup_tables: Vec<_> = points.into_iter()
|
||||
.map(|P_i| {
|
||||
// Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i]
|
||||
let mut lookup_table = [P_i.to_projective_niels(); 8];
|
||||
for j in 0..7 {
|
||||
lookup_table[j+1] = (P_i + &lookup_table[j])
|
||||
.to_extended().to_projective_niels();
|
||||
}
|
||||
lookup_table
|
||||
}).collect();
|
||||
|
||||
// Setting s_i = i-th scalar, compute
|
||||
//
|
||||
// s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63,
|
||||
//
|
||||
// with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`.
|
||||
let scalar_digits_list: Vec<_> = scalars.into_iter()
|
||||
.map(|c| c.to_radix_16()).collect();
|
||||
|
||||
// Compute s_1*P_1 + ... + s_n*P_n: since
|
||||
//
|
||||
// s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63)
|
||||
// s_i*P_i = P_i*s_{i,0} + P_i*s_{i,1}*16^1 + ... + P_i*s_{i,63}*16^63
|
||||
// s_i*P_i = P_i*s_{i,0} + 16*(P_i*s_{i,1} + 16*( ... + 16*P_i*s_{i,63})...)
|
||||
//
|
||||
// we have the two-dimensional sum
|
||||
//
|
||||
// s_1*P_1 = P_1*s_{1,0} + 16*(P_1*s_{1,1} + 16*( ... + 16*P_1*s_{1,63})...)
|
||||
// + s_2*P_2 = + P_2*s_{2,0} + 16*(P_2*s_{2,1} + 16*( ... + 16*P_2*s_{2,63})...)
|
||||
// ...
|
||||
// + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...)
|
||||
//
|
||||
// We sum column-wise top-to-bottom, then right-to-left,
|
||||
// multiplying by 16 only once per column.
|
||||
//
|
||||
// This provides the speedup over doing n independent scalar
|
||||
// mults: we perform 63 multiplications by 16 instead of 63*n
|
||||
// multiplications, saving 252*(n-1) doublings.
|
||||
let mut Q = ExtendedPoint::identity();
|
||||
// XXX this impl makes no effort to be cache-aware; maybe it could be improved?
|
||||
for j in (0..64).rev() {
|
||||
Q = Q.mult_by_pow_2(4);
|
||||
let it = scalar_digits_list.iter().zip(lookup_tables.iter());
|
||||
for (s_i, lookup_table_i) in it {
|
||||
// R_i = s_{i,j} * P_i
|
||||
let R_i = select_precomputed_point(s_i[j], lookup_table_i);
|
||||
// Q = Q + R_i
|
||||
Q = (&Q + &R_i).to_extended();
|
||||
}
|
||||
}
|
||||
Q
|
||||
}
|
||||
Q
|
||||
}
|
||||
|
||||
/// A precomputed table of multiples of a basepoint, for accelerating
|
||||
|
|
@ -801,30 +810,39 @@ pub mod vartime {
|
|||
where I: IntoIterator<Item = &'a Scalar>,
|
||||
J: IntoIterator<Item = &'b ExtendedPoint>
|
||||
{
|
||||
//assert_eq!(scalars.len(), points.len());
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(target_feature = "avx2", feature = "avx2_backend"))] {
|
||||
use backend::avx2::edwards as edwards_avx2;
|
||||
|
||||
let nafs: Vec<_> = scalars.into_iter()
|
||||
.map(|c| c.non_adjacent_form()).collect();
|
||||
let odd_multiples: Vec<_> = points.into_iter()
|
||||
.map(|P| OddMultiples::create(P)).collect();
|
||||
edwards_avx2::vartime::multiscalar_mult(scalars, points)
|
||||
}
|
||||
// Otherwise, proceed as normal:
|
||||
#[cfg(not(all(target_feature = "avx2", feature = "avx2_backend")))] {
|
||||
//assert_eq!(scalars.len(), points.len());
|
||||
|
||||
let mut r = ProjectivePoint::identity();
|
||||
let nafs: Vec<_> = scalars.into_iter()
|
||||
.map(|c| c.non_adjacent_form()).collect();
|
||||
let odd_multiples: Vec<_> = points.into_iter()
|
||||
.map(|P| OddMultiples::create(P)).collect();
|
||||
|
||||
for i in (0..255).rev() {
|
||||
let mut t = r.double();
|
||||
let mut r = ProjectivePoint::identity();
|
||||
|
||||
for (naf, odd_multiple) in nafs.iter().zip(odd_multiples.iter()) {
|
||||
if naf[i] > 0 {
|
||||
t = &t.to_extended() + &odd_multiple[( naf[i]/2) as usize];
|
||||
} else if naf[i] < 0 {
|
||||
t = &t.to_extended() - &odd_multiple[(-naf[i]/2) as usize];
|
||||
for i in (0..255).rev() {
|
||||
let mut t = r.double();
|
||||
|
||||
for (naf, odd_multiple) in nafs.iter().zip(odd_multiples.iter()) {
|
||||
if naf[i] > 0 {
|
||||
t = &t.to_extended() + &odd_multiple[( naf[i]/2) as usize];
|
||||
} else if naf[i] < 0 {
|
||||
t = &t.to_extended() - &odd_multiple[(-naf[i]/2) as usize];
|
||||
}
|
||||
}
|
||||
|
||||
r = t.to_projective();
|
||||
}
|
||||
|
||||
r = t.to_projective();
|
||||
r.to_extended()
|
||||
}
|
||||
|
||||
r.to_extended()
|
||||
}
|
||||
|
||||
/// Given a point \\(A\\) and scalars \\(a\\) and \\(b\\), compute the point
|
||||
|
|
|
|||
Loading…
Reference in a new issue