diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index 5aad787..0741652 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -144,7 +144,7 @@ impl<'a> Neg for &'a ExtendedPoint { } impl ExtendedPoint { - fn double(&self) -> ExtendedPoint { + pub fn double(&self) -> ExtendedPoint { unsafe { use stdsimd::vendor::_mm256_permute2x128_si256; use stdsimd::vendor::_mm256_permutevar8x32_epi32; @@ -361,6 +361,18 @@ impl<'a, 'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint { } } +impl<'a, 'b> Sub<&'b CachedPoint> for &'a ExtendedPoint { + type Output = ExtendedPoint; + + /// Implement subtraction by negating the point and adding. + /// + /// Empirically, this seems about the same cost as a custom subtraction impl (maybe because the + /// benefit is cancelled by increased code size?) + fn sub(self, other: &'b CachedPoint) -> ExtendedPoint { + self + &(-other) + } +} + impl From for LookupTable { fn from(P: ExtendedPoint) -> Self { let mut points = [CachedPoint::from(P); 8]; @@ -421,67 +433,18 @@ impl EdwardsBasepointTable { } } -/// Internal multiscalar code. -#[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mul(scalars: I, points: J) -> edwards::EdwardsPoint - where I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, -{ - //assert_eq!(scalars.len(), points.len()); +use scalar_mul::window::OddLookupTable; - use clear_on_drop::ClearOnDrop; - let lookup_tables_vec: Vec<_> = points.into_iter() - .map(|P| LookupTable::from(ExtendedPoint::from(*P.borrow())) ) - .collect(); - - let lookup_tables = ClearOnDrop::new(lookup_tables_vec); - - // 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_vec: Vec<_> = scalars.into_iter() - .map(|c| c.borrow().to_radix_16()) - .collect(); - - // The above puts the scalar digits into a heap-allocated Vec. - // To ensure that these are erased, pass ownership of the Vec into a - // ClearOnDrop wrapper. - let scalar_digits = ClearOnDrop::new(scalar_digits_vec); - - // 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 algorithm makes no effort to be cache-aware; maybe it could be improved? - for j in (0..64).rev() { - Q = Q.mul_by_pow_2(4); - let it = scalar_digits.iter().zip(lookup_tables.iter()); - for (s_i, lookup_table_i) in it { - // Q = Q + s_{i,j} * P_i - Q = &Q + &lookup_table_i.select(s_i[j]); +impl<'a> From<&'a ExtendedPoint> for OddLookupTable { + fn from(A: &'a ExtendedPoint) -> Self { + let mut Ai = [CachedPoint::from(*A); 8]; + let A2 = A.double(); + for i in 0..7 { + Ai[i + 1] = (&A2 + &Ai[i]).into(); } + // Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A] + OddLookupTable(Ai) } - Q.into() } pub mod vartime { @@ -560,38 +523,6 @@ pub mod vartime { Q.into() } - - /// Internal multiscalar function - #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mul(scalars: I, points: J) -> edwards::EdwardsPoint - where I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - { - //assert_eq!(scalars.len(), points.len()); - - let nafs: Vec<_> = scalars.into_iter() - .map(|c| c.borrow().non_adjacent_form()).collect(); - - let odd_multiples: Vec<_> = points.into_iter() - .map(|P| OddMultiples::create((*P.borrow()).into()) ).collect(); - - let mut Q = ExtendedPoint::identity(); - - for i in (0..255).rev() { - Q = Q.double(); - - for (naf, odd_multiple) in nafs.iter().zip(odd_multiples.iter()) { - if naf[i] > 0 { - Q = &Q + &odd_multiple[( naf[i]/2) as usize]; - } else if naf[i] < 0 { - Q = &Q - &odd_multiple[(-naf[i]/2) as usize]; - } - } - } - Q.into() - } } #[cfg(test)] diff --git a/src/backend/avx2/scalar_mul/mod.rs b/src/backend/avx2/scalar_mul/mod.rs index f529730..3c62538 100644 --- a/src/backend/avx2/scalar_mul/mod.rs +++ b/src/backend/avx2/scalar_mul/mod.rs @@ -11,3 +11,5 @@ pub mod variable_base; pub mod straus; + +pub mod vartime_straus; diff --git a/src/backend/avx2/scalar_mul/vartime_straus.rs b/src/backend/avx2/scalar_mul/vartime_straus.rs new file mode 100644 index 0000000..c53e7d4 --- /dev/null +++ b/src/backend/avx2/scalar_mul/vartime_straus.rs @@ -0,0 +1,54 @@ +// -*- mode: rust; -*- +// +// This file is part of curve25519-dalek. +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft +// - Henry de Valence +#![allow(non_snake_case)] + +use core::borrow::Borrow; + +use traits::Identity; +use scalar::Scalar; +use edwards::EdwardsPoint; +use scalar_mul::window::OddLookupTable; +use backend::avx2::edwards::{CachedPoint, ExtendedPoint}; + +/// Perform variable-time, variable-base scalar multiplication. +pub(crate) fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint +where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, +{ + let nafs: Vec<_> = scalars + .into_iter() + .map(|c| c.borrow().non_adjacent_form()) + .collect(); + let lookup_tables: Vec<_> = points + .into_iter() + .map(|point| { + let avx2_point = ExtendedPoint::from(*point.borrow()); + OddLookupTable::::from(&avx2_point) + }) + .collect(); + + let mut Q = ExtendedPoint::identity(); + + for i in (0..255).rev() { + Q = Q.double(); + + for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { + if naf[i] > 0 { + Q = &Q + &lookup_table.select(naf[i] as usize); + } else if naf[i] < 0 { + Q = &Q - &lookup_table.select(-naf[i] as usize); + } + } + } + Q.into() +} diff --git a/src/edwards.rs b/src/edwards.rs index 42802cb..0fc0855 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -845,8 +845,6 @@ pub mod vartime { /// /// 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_mul(scalars: I, points: J) -> EdwardsPoint where I: IntoIterator, @@ -854,38 +852,19 @@ pub mod vartime { J: IntoIterator, J::Item: Borrow, { + // XXX later when we do more fancy multiscalar mults, we can delegate + // based on the iter's size hint -- hdevalence // If we built with AVX2, use the AVX2 backend. - #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] { - use backend::avx2::edwards as edwards_avx2; - - edwards_avx2::vartime::multiscalar_mul(scalars, points) + #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] + { + use backend::avx2::scalar_mul::vartime_straus::multiscalar_mul; + multiscalar_mul(scalars, points) } // Otherwise, proceed as normal: - #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] { - //assert_eq!(scalars.len(), points.len()); - - let nafs: Vec<_> = scalars.into_iter() - .map(|c| c.borrow().non_adjacent_form()).collect(); - let odd_multiples: Vec<_> = points.into_iter() - .map(|P| OddMultiples::create(P.borrow())).collect(); - - let mut r = ProjectivePoint::identity(); - - 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.to_extended() + #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] + { + use scalar_mul::vartime_straus::multiscalar_mul; + multiscalar_mul(scalars, points) } } diff --git a/src/scalar_mul/mod.rs b/src/scalar_mul/mod.rs index b69535d..2f295d7 100644 --- a/src/scalar_mul/mod.rs +++ b/src/scalar_mul/mod.rs @@ -13,3 +13,5 @@ pub mod window; pub mod variable_base; pub mod straus; + +pub mod vartime_straus; diff --git a/src/scalar_mul/vartime_straus.rs b/src/scalar_mul/vartime_straus.rs index 890677b..c55ce21 100644 --- a/src/scalar_mul/vartime_straus.rs +++ b/src/scalar_mul/vartime_straus.rs @@ -9,31 +9,21 @@ // - Henry de Valence #![allow(non_snake_case)] -use core::ops::{Add, Sub}; use core::borrow::Borrow; +use traits::Identity; use scalar::Scalar; - -//use super::window::OddLookupTable; +use edwards::EdwardsPoint; +use curve_models::{CompletedPoint, ProjectivePoint, ProjectiveNielsPoint}; use scalar_mul::window::OddLookupTable; -use traits::{Doubleable, Identity}; /// Perform variable-time, variable-base scalar multiplication. -pub(crate) fn multiscalar_mul( - scalars: I, - points: J, -) -> ExtendedPoint +pub(crate) fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint where I: IntoIterator, I::Item: Borrow, J: IntoIterator, - J::Item: Borrow, - for<'a> OddLookupTable: From<&'a ExtendedPoint>, - for<'a, 'b> &'a ExtendedPoint: Add<&'b CachedPoint, Output = CompletedPoint>, - for<'a, 'b> &'a ExtendedPoint: Sub<&'b CachedPoint, Output = CompletedPoint>, - ExtendedPoint: Identity + From + From, - ProjectivePoint: Identity + Doubleable + From, - CachedPoint: Copy, + J::Item: Borrow, { let nafs: Vec<_> = scalars .into_iter() @@ -41,7 +31,7 @@ where .collect(); let lookup_tables: Vec<_> = points .into_iter() - .map(|P| OddLookupTable::from(P.borrow())) + .map(|P| OddLookupTable::::from(P.borrow())) .collect(); let mut r = ProjectivePoint::identity(); @@ -51,14 +41,14 @@ where for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { if naf[i] > 0 { - t = &ExtendedPoint::from(t) + &lookup_table.select(naf[i] as usize); + t = &t.to_extended() + &lookup_table.select(naf[i] as usize); } else if naf[i] < 0 { - t = &ExtendedPoint::from(t) - &lookup_table.select(-naf[i] as usize); + t = &t.to_extended() - &lookup_table.select(-naf[i] as usize); } } - r = ProjectivePoint::from(t); + r = t.to_projective(); } - ExtendedPoint::from(r) + r.to_extended() } diff --git a/src/scalar_mul/window.rs b/src/scalar_mul/window.rs index ada3706..9b25917 100644 --- a/src/scalar_mul/window.rs +++ b/src/scalar_mul/window.rs @@ -21,6 +21,10 @@ use subtle::Choice; use traits::Identity; +use edwards::EdwardsPoint; +use curve_models::ProjectiveNielsPoint; +use curve_models::AffineNielsPoint; + /// A lookup table of precomputed multiples of a point \\(P\\), used to /// compute \\( xP \\) for \\( -8 \leq x \leq 8 \\). /// @@ -54,22 +58,24 @@ use clear_on_drop::clear::ZeroSafe; unsafe impl ZeroSafe for LookupTable {} impl LookupTable -where T: Identity + ConditionallyAssignable + ConditionallyNegatable +where + T: Identity + ConditionallyAssignable + ConditionallyNegatable, { /// Given \\(-8 \leq x \leq 8\\), return \\(xP\\) in constant time. pub fn select(&self, x: i8) -> T { - debug_assert!(x >= -8); debug_assert!(x <= 8); + debug_assert!(x >= -8); + debug_assert!(x <= 8); // Compute xabs = |x| let xmask = x >> 7; - let xabs = (x + xmask) ^ xmask; + let xabs = (x + xmask) ^ xmask; // Set t = 0 * P = identity let mut t = T::identity(); for j in 1..9 { // Copy `points[j-1] == j*P` onto `t` in constant time if `|x| == j`. let c = (xabs as u8).ct_eq(&(j as u8)); - t.conditional_assign(&self.0[j-1], c); + t.conditional_assign(&self.0[j - 1], c); } // Now t == |x| * P. @@ -93,17 +99,11 @@ impl Debug for LookupTable { } } -use edwards::EdwardsPoint; -use curve_models::ProjectiveNielsPoint; -use curve_models::AffineNielsPoint; - impl<'a> From<&'a EdwardsPoint> for LookupTable { fn from(P: &'a EdwardsPoint) -> Self { let mut points = [P.to_projective_niels(); 8]; for j in 0..7 { - points[j+1] = (P + &points[j]) - .to_extended() - .to_projective_niels(); + points[j + 1] = (P + &points[j]).to_extended().to_projective_niels(); } LookupTable(points) } @@ -114,10 +114,52 @@ impl<'a> From<&'a EdwardsPoint> for LookupTable { let mut points = [P.to_affine_niels(); 8]; // XXX batch inversion would be good if perf mattered here for j in 0..7 { - points[j+1] = (P + &points[j]) - .to_extended() - .to_affine_niels() + points[j + 1] = (P + &points[j]).to_extended().to_affine_niels() } LookupTable(points) } } + +/// Holds odd multiples 1A, 3A, ..., 15A of a point A. +#[derive(Copy, Clone)] +pub(crate) struct OddLookupTable(pub(crate) [T; 8]); + +impl OddLookupTable { + /// Given public, odd \\( x \\) with \\( 0 < x < 2^4 \\), return \\(xA\\). + pub fn select(&self, x: usize) -> T { + debug_assert_eq!(x & 1, 1); + debug_assert!(x < 16); + + self.0[x / 2] + } +} + +impl Debug for OddLookupTable { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "OddLookupTable({:?})", self.0) + } +} + +impl<'a> From<&'a EdwardsPoint> for OddLookupTable { + fn from(A: &'a EdwardsPoint) -> Self { + let mut Ai = [A.to_projective_niels(); 8]; + let A2 = A.double(); + for i in 0..7 { + Ai[i + 1] = (&A2 + &Ai[i]).to_extended().to_projective_niels(); + } + // Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A] + OddLookupTable(Ai) + } +} + +impl<'a> From<&'a EdwardsPoint> for OddLookupTable { + fn from(A: &'a EdwardsPoint) -> Self { + let mut Ai = [A.to_affine_niels(); 8]; + let A2 = A.double(); + for i in 0..7 { + Ai[i + 1] = (&A2 + &Ai[i]).to_extended().to_affine_niels(); + } + // Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A] + OddLookupTable(Ai) + } +}