diff --git a/src/backend/vector/scalar_mul/pippenger.rs b/src/backend/vector/scalar_mul/pippenger.rs index 6d4b5aa..b00cb87 100644 --- a/src/backend/vector/scalar_mul/pippenger.rs +++ b/src/backend/vector/scalar_mul/pippenger.rs @@ -15,164 +15,163 @@ )] pub mod spec { -use alloc::vec::Vec; + use alloc::vec::Vec; -use core::borrow::Borrow; -use core::cmp::Ordering; + use core::borrow::Borrow; + use core::cmp::Ordering; -#[for_target_feature("avx2")] -use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; + #[for_target_feature("avx2")] + use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; -#[for_target_feature("avx512ifma")] -use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; + #[for_target_feature("avx512ifma")] + use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; -use crate::edwards::EdwardsPoint; -use crate::scalar::Scalar; -use crate::traits::{Identity, VartimeMultiscalarMul}; + use crate::edwards::EdwardsPoint; + use crate::scalar::Scalar; + use crate::traits::{Identity, VartimeMultiscalarMul}; -/// Implements a version of Pippenger's algorithm. -/// -/// See the documentation in the serial `scalar_mul::pippenger` module for details. -pub struct Pippenger; + /// Implements a version of Pippenger's algorithm. + /// + /// See the documentation in the serial `scalar_mul::pippenger` module for details. + pub struct Pippenger; -impl VartimeMultiscalarMul for Pippenger { - type Point = EdwardsPoint; + impl VartimeMultiscalarMul for Pippenger { + type Point = EdwardsPoint; - fn optional_multiscalar_mul(scalars: I, points: J) -> Option - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator>, - { - let mut scalars = scalars.into_iter(); - let size = scalars.by_ref().size_hint().0; - let w = if size < 500 { - 6 - } else if size < 800 { - 7 - } else { - 8 - }; + fn optional_multiscalar_mul(scalars: I, points: J) -> Option + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator>, + { + let mut scalars = scalars.into_iter(); + let size = scalars.by_ref().size_hint().0; + let w = if size < 500 { + 6 + } else if size < 800 { + 7 + } else { + 8 + }; - let max_digit: usize = 1 << w; - let digits_count: usize = Scalar::to_radix_2w_size_hint(w); - let buckets_count: usize = max_digit / 2; // digits are signed+centered hence 2^w/2, excluding 0-th bucket + let max_digit: usize = 1 << w; + let digits_count: usize = Scalar::to_radix_2w_size_hint(w); + let buckets_count: usize = max_digit / 2; // digits are signed+centered hence 2^w/2, excluding 0-th bucket - // Collect optimized scalars and points in a buffer for repeated access - // (scanning the whole collection per each digit position). - let scalars = scalars.map(|s| s.borrow().as_radix_2w(w)); + // Collect optimized scalars and points in a buffer for repeated access + // (scanning the whole collection per each digit position). + let scalars = scalars.map(|s| s.borrow().as_radix_2w(w)); - let points = points - .into_iter() - .map(|p| p.map(|P| CachedPoint::from(ExtendedPoint::from(P)))); + let points = points + .into_iter() + .map(|p| p.map(|P| CachedPoint::from(ExtendedPoint::from(P)))); - let scalars_points = scalars - .zip(points) - .map(|(s, maybe_p)| maybe_p.map(|p| (s, p))) - .collect::>>()?; + let scalars_points = scalars + .zip(points) + .map(|(s, maybe_p)| maybe_p.map(|p| (s, p))) + .collect::>>()?; - // Prepare 2^w/2 buckets. - // buckets[i] corresponds to a multiplication factor (i+1). - let mut buckets: Vec = (0..buckets_count) - .map(|_| ExtendedPoint::identity()) - .collect(); + // Prepare 2^w/2 buckets. + // buckets[i] corresponds to a multiplication factor (i+1). + let mut buckets: Vec = (0..buckets_count) + .map(|_| ExtendedPoint::identity()) + .collect(); - let mut columns = (0..digits_count).rev().map(|digit_index| { - // Clear the buckets when processing another digit. - for bucket in &mut buckets { - *bucket = ExtendedPoint::identity(); - } - - // Iterate over pairs of (point, scalar) - // and add/sub the point to the corresponding bucket. - // Note: if we add support for precomputed lookup tables, - // we'll be adding/subtractiong point premultiplied by `digits[i]` to buckets[0]. - for (digits, pt) in scalars_points.iter() { - // Widen digit so that we don't run into edge cases when w=8. - let digit = digits[digit_index] as i16; - match digit.cmp(&0) { - Ordering::Greater => { - let b = (digit - 1) as usize; - buckets[b] = &buckets[b] + pt; - } - Ordering::Less => { - let b = (-digit - 1) as usize; - buckets[b] = &buckets[b] - pt; - } - Ordering::Equal => {} + let mut columns = (0..digits_count).rev().map(|digit_index| { + // Clear the buckets when processing another digit. + for bucket in &mut buckets { + *bucket = ExtendedPoint::identity(); } - } - // Add the buckets applying the multiplication factor to each bucket. - // The most efficient way to do that is to have a single sum with two running sums: - // an intermediate sum from last bucket to the first, and a sum of intermediate sums. - // - // For example, to add buckets 1*A, 2*B, 3*C we need to add these points: - // C - // C B - // C B A Sum = C + (C+B) + (C+B+A) - let mut buckets_intermediate_sum = buckets[buckets_count - 1]; - let mut buckets_sum = buckets[buckets_count - 1]; - for i in (0..(buckets_count - 1)).rev() { - buckets_intermediate_sum = - &buckets_intermediate_sum + &CachedPoint::from(buckets[i]); - buckets_sum = &buckets_sum + &CachedPoint::from(buckets_intermediate_sum); - } + // Iterate over pairs of (point, scalar) + // and add/sub the point to the corresponding bucket. + // Note: if we add support for precomputed lookup tables, + // we'll be adding/subtractiong point premultiplied by `digits[i]` to buckets[0]. + for (digits, pt) in scalars_points.iter() { + // Widen digit so that we don't run into edge cases when w=8. + let digit = digits[digit_index] as i16; + match digit.cmp(&0) { + Ordering::Greater => { + let b = (digit - 1) as usize; + buckets[b] = &buckets[b] + pt; + } + Ordering::Less => { + let b = (-digit - 1) as usize; + buckets[b] = &buckets[b] - pt; + } + Ordering::Equal => {} + } + } - buckets_sum - }); + // Add the buckets applying the multiplication factor to each bucket. + // The most efficient way to do that is to have a single sum with two running sums: + // an intermediate sum from last bucket to the first, and a sum of intermediate sums. + // + // For example, to add buckets 1*A, 2*B, 3*C we need to add these points: + // C + // C B + // C B A Sum = C + (C+B) + (C+B+A) + let mut buckets_intermediate_sum = buckets[buckets_count - 1]; + let mut buckets_sum = buckets[buckets_count - 1]; + for i in (0..(buckets_count - 1)).rev() { + buckets_intermediate_sum = + &buckets_intermediate_sum + &CachedPoint::from(buckets[i]); + buckets_sum = &buckets_sum + &CachedPoint::from(buckets_intermediate_sum); + } - // Take the high column as an initial value to avoid wasting time doubling the identity element in `fold()`. - // `unwrap()` always succeeds because we know we have more than zero digits. - let hi_column = columns.next().unwrap(); + buckets_sum + }); - Some( - columns - .fold(hi_column, |total, p| { - &total.mul_by_pow_2(w as u32) + &CachedPoint::from(p) - }) - .into(), - ) + // Take the high column as an initial value to avoid wasting time doubling the identity element in `fold()`. + // `unwrap()` always succeeds because we know we have more than zero digits. + let hi_column = columns.next().unwrap(); + + Some( + columns + .fold(hi_column, |total, p| { + &total.mul_by_pow_2(w as u32) + &CachedPoint::from(p) + }) + .into(), + ) + } } -} -#[cfg(test)] -mod test { - #[test] - fn test_vartime_pippenger() { - use super::*; - use crate::constants; - use crate::scalar::Scalar; + #[cfg(test)] + mod test { + #[test] + fn test_vartime_pippenger() { + use super::*; + use crate::constants; + use crate::scalar::Scalar; - // Reuse points across different tests - let mut n = 512; - let x = Scalar::from(2128506u64).invert(); - let y = Scalar::from(4443282u64).invert(); - let points: Vec<_> = (0..n) - .map(|i| constants::ED25519_BASEPOINT_POINT * Scalar::from(1 + i as u64)) - .collect(); - let scalars: Vec<_> = (0..n) - .map(|i| x + (Scalar::from(i as u64) * y)) // fast way to make ~random but deterministic scalars - .collect(); + // Reuse points across different tests + let mut n = 512; + let x = Scalar::from(2128506u64).invert(); + let y = Scalar::from(4443282u64).invert(); + let points: Vec<_> = (0..n) + .map(|i| constants::ED25519_BASEPOINT_POINT * Scalar::from(1 + i as u64)) + .collect(); + let scalars: Vec<_> = (0..n) + .map(|i| x + (Scalar::from(i as u64) * y)) // fast way to make ~random but deterministic scalars + .collect(); - let premultiplied: Vec = scalars - .iter() - .zip(points.iter()) - .map(|(sc, pt)| sc * pt) - .collect(); + let premultiplied: Vec = scalars + .iter() + .zip(points.iter()) + .map(|(sc, pt)| sc * pt) + .collect(); - while n > 0 { - let scalars = &scalars[0..n].to_vec(); - let points = &points[0..n].to_vec(); - let control: EdwardsPoint = premultiplied[0..n].iter().sum(); + while n > 0 { + let scalars = &scalars[0..n].to_vec(); + let points = &points[0..n].to_vec(); + let control: EdwardsPoint = premultiplied[0..n].iter().sum(); - let subject = Pippenger::vartime_multiscalar_mul(scalars.clone(), points.clone()); + let subject = Pippenger::vartime_multiscalar_mul(scalars.clone(), points.clone()); - assert_eq!(subject.compress(), control.compress()); + assert_eq!(subject.compress(), control.compress()); - n = n / 2; + n = n / 2; + } } } } - -} diff --git a/src/backend/vector/scalar_mul/precomputed_straus.rs b/src/backend/vector/scalar_mul/precomputed_straus.rs index 8c7d725..8c45c29 100644 --- a/src/backend/vector/scalar_mul/precomputed_straus.rs +++ b/src/backend/vector/scalar_mul/precomputed_straus.rs @@ -17,112 +17,111 @@ )] pub mod spec { -use alloc::vec::Vec; + use alloc::vec::Vec; -use core::borrow::Borrow; -use core::cmp::Ordering; + use core::borrow::Borrow; + use core::cmp::Ordering; -#[for_target_feature("avx2")] -use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; + #[for_target_feature("avx2")] + use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; -#[for_target_feature("avx512ifma")] -use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; + #[for_target_feature("avx512ifma")] + use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; -use crate::edwards::EdwardsPoint; -use crate::scalar::Scalar; -use crate::traits::Identity; -use crate::traits::VartimePrecomputedMultiscalarMul; -use crate::window::{NafLookupTable5, NafLookupTable8}; + use crate::edwards::EdwardsPoint; + use crate::scalar::Scalar; + use crate::traits::Identity; + use crate::traits::VartimePrecomputedMultiscalarMul; + use crate::window::{NafLookupTable5, NafLookupTable8}; -pub struct VartimePrecomputedStraus { - static_lookup_tables: Vec>, -} + pub struct VartimePrecomputedStraus { + static_lookup_tables: Vec>, + } -impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { - type Point = EdwardsPoint; + impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { + type Point = EdwardsPoint; - fn new(static_points: I) -> Self - where - I: IntoIterator, - I::Item: Borrow, - { - Self { - static_lookup_tables: static_points + fn new(static_points: I) -> Self + where + I: IntoIterator, + I::Item: Borrow, + { + Self { + static_lookup_tables: static_points + .into_iter() + .map(|P| NafLookupTable8::::from(P.borrow())) + .collect(), + } + } + + fn optional_mixed_multiscalar_mul( + &self, + static_scalars: I, + dynamic_scalars: J, + dynamic_points: K, + ) -> Option + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + K: IntoIterator>, + { + let static_nafs = static_scalars .into_iter() - .map(|P| NafLookupTable8::::from(P.borrow())) - .collect(), - } - } + .map(|c| c.borrow().non_adjacent_form(5)) + .collect::>(); + let dynamic_nafs: Vec<_> = dynamic_scalars + .into_iter() + .map(|c| c.borrow().non_adjacent_form(5)) + .collect::>(); - fn optional_mixed_multiscalar_mul( - &self, - static_scalars: I, - dynamic_scalars: J, - dynamic_points: K, - ) -> Option - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - K: IntoIterator>, - { - let static_nafs = static_scalars - .into_iter() - .map(|c| c.borrow().non_adjacent_form(5)) - .collect::>(); - let dynamic_nafs: Vec<_> = dynamic_scalars - .into_iter() - .map(|c| c.borrow().non_adjacent_form(5)) - .collect::>(); + let dynamic_lookup_tables = dynamic_points + .into_iter() + .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) + .collect::>>()?; - let dynamic_lookup_tables = dynamic_points - .into_iter() - .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) - .collect::>>()?; + let sp = self.static_lookup_tables.len(); + let dp = dynamic_lookup_tables.len(); + assert_eq!(sp, static_nafs.len()); + assert_eq!(dp, dynamic_nafs.len()); - let sp = self.static_lookup_tables.len(); - let dp = dynamic_lookup_tables.len(); - assert_eq!(sp, static_nafs.len()); - assert_eq!(dp, dynamic_nafs.len()); + // We could save some doublings by looking for the highest + // nonzero NAF coefficient, but since we might have a lot of + // them to search, it's not clear it's worthwhile to check. + let mut R = ExtendedPoint::identity(); + for j in (0..256).rev() { + R = R.double(); - // We could save some doublings by looking for the highest - // nonzero NAF coefficient, but since we might have a lot of - // them to search, it's not clear it's worthwhile to check. - let mut R = ExtendedPoint::identity(); - for j in (0..256).rev() { - R = R.double(); - - for i in 0..dp { - let t_ij = dynamic_nafs[i][j]; - match t_ij.cmp(&0) { - Ordering::Greater => { - R = &R + &dynamic_lookup_tables[i].select(t_ij as usize); + for i in 0..dp { + let t_ij = dynamic_nafs[i][j]; + match t_ij.cmp(&0) { + Ordering::Greater => { + R = &R + &dynamic_lookup_tables[i].select(t_ij as usize); + } + Ordering::Less => { + R = &R - &dynamic_lookup_tables[i].select(-t_ij as usize); + } + Ordering::Equal => {} } - Ordering::Less => { - R = &R - &dynamic_lookup_tables[i].select(-t_ij as usize); + } + + #[allow(clippy::needless_range_loop)] + for i in 0..sp { + let t_ij = static_nafs[i][j]; + match t_ij.cmp(&0) { + Ordering::Greater => { + R = &R + &self.static_lookup_tables[i].select(t_ij as usize); + } + Ordering::Less => { + R = &R - &self.static_lookup_tables[i].select(-t_ij as usize); + } + Ordering::Equal => {} } - Ordering::Equal => {} } } - #[allow(clippy::needless_range_loop)] - for i in 0..sp { - let t_ij = static_nafs[i][j]; - match t_ij.cmp(&0) { - Ordering::Greater => { - R = &R + &self.static_lookup_tables[i].select(t_ij as usize); - } - Ordering::Less => { - R = &R - &self.static_lookup_tables[i].select(-t_ij as usize); - } - Ordering::Equal => {} - } - } + Some(R.into()) } - - Some(R.into()) } } - -} diff --git a/src/backend/vector/scalar_mul/straus.rs b/src/backend/vector/scalar_mul/straus.rs index 1f3e784..046bcd1 100644 --- a/src/backend/vector/scalar_mul/straus.rs +++ b/src/backend/vector/scalar_mul/straus.rs @@ -17,109 +17,108 @@ )] pub mod spec { -use alloc::vec::Vec; + use alloc::vec::Vec; -use core::borrow::Borrow; -use core::cmp::Ordering; + use core::borrow::Borrow; + use core::cmp::Ordering; -use zeroize::Zeroizing; + use zeroize::Zeroizing; -#[for_target_feature("avx2")] -use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; + #[for_target_feature("avx2")] + use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; -#[for_target_feature("avx512ifma")] -use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; + #[for_target_feature("avx512ifma")] + use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; -use crate::edwards::EdwardsPoint; -use crate::scalar::Scalar; -use crate::traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; -use crate::window::{LookupTable, NafLookupTable5}; + use crate::edwards::EdwardsPoint; + use crate::scalar::Scalar; + use crate::traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; + use crate::window::{LookupTable, NafLookupTable5}; -/// Multiscalar multiplication using interleaved window / Straus' -/// method. See the `Straus` struct in the serial backend for more -/// details. -/// -/// This exists as a seperate implementation from that one because the -/// AVX2 code uses different curve models (it does not pass between -/// multiple models during scalar mul), and it has to convert the -/// point representation on the fly. -pub struct Straus {} + /// Multiscalar multiplication using interleaved window / Straus' + /// method. See the `Straus` struct in the serial backend for more + /// details. + /// + /// This exists as a seperate implementation from that one because the + /// AVX2 code uses different curve models (it does not pass between + /// multiple models during scalar mul), and it has to convert the + /// point representation on the fly. + pub struct Straus {} -impl MultiscalarMul for Straus { - type Point = EdwardsPoint; + impl MultiscalarMul for Straus { + type Point = EdwardsPoint; - fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - { - // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] - // for each input point P - let lookup_tables: Vec<_> = points - .into_iter() - .map(|point| LookupTable::::from(point.borrow())) - .collect(); + fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + { + // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] + // for each input point P + let lookup_tables: Vec<_> = points + .into_iter() + .map(|point| LookupTable::::from(point.borrow())) + .collect(); - let scalar_digits_vec: Vec<_> = scalars - .into_iter() - .map(|s| s.borrow().as_radix_16()) - .collect(); - // Pass ownership to a `Zeroizing` wrapper - let scalar_digits = Zeroizing::new(scalar_digits_vec); + let scalar_digits_vec: Vec<_> = scalars + .into_iter() + .map(|s| s.borrow().as_radix_16()) + .collect(); + // Pass ownership to a `Zeroizing` wrapper + let scalar_digits = Zeroizing::new(scalar_digits_vec); - let mut Q = ExtendedPoint::identity(); - 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]); - } - } - Q.into() - } -} - -impl VartimeMultiscalarMul for Straus { - type Point = EdwardsPoint; - - fn optional_multiscalar_mul(scalars: I, points: J) -> Option - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator>, - { - let nafs: Vec<_> = scalars - .into_iter() - .map(|c| c.borrow().non_adjacent_form(5)) - .collect(); - let lookup_tables: Vec<_> = points - .into_iter() - .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) - .collect::>>()?; - - let mut Q = ExtendedPoint::identity(); - - for i in (0..256).rev() { - Q = Q.double(); - - for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { - match naf[i].cmp(&0) { - Ordering::Greater => { - Q = &Q + &lookup_table.select(naf[i] as usize); - } - Ordering::Less => { - Q = &Q - &lookup_table.select(-naf[i] as usize); - } - Ordering::Equal => {} + let mut Q = ExtendedPoint::identity(); + 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]); } } + Q.into() } + } - Some(Q.into()) + impl VartimeMultiscalarMul for Straus { + type Point = EdwardsPoint; + + fn optional_multiscalar_mul(scalars: I, points: J) -> Option + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator>, + { + let nafs: Vec<_> = scalars + .into_iter() + .map(|c| c.borrow().non_adjacent_form(5)) + .collect(); + let lookup_tables: Vec<_> = points + .into_iter() + .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) + .collect::>>()?; + + let mut Q = ExtendedPoint::identity(); + + for i in (0..256).rev() { + Q = Q.double(); + + for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { + match naf[i].cmp(&0) { + Ordering::Greater => { + Q = &Q + &lookup_table.select(naf[i] as usize); + } + Ordering::Less => { + Q = &Q - &lookup_table.select(-naf[i] as usize); + } + Ordering::Equal => {} + } + } + } + + Some(Q.into()) + } } } - -} diff --git a/src/backend/vector/scalar_mul/variable_base.rs b/src/backend/vector/scalar_mul/variable_base.rs index 0653d27..2da4799 100644 --- a/src/backend/vector/scalar_mul/variable_base.rs +++ b/src/backend/vector/scalar_mul/variable_base.rs @@ -6,40 +6,39 @@ )] pub mod spec { -#[for_target_feature("avx2")] -use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; + #[for_target_feature("avx2")] + use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; -#[for_target_feature("avx512ifma")] -use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; + #[for_target_feature("avx512ifma")] + use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; -use crate::edwards::EdwardsPoint; -use crate::scalar::Scalar; -use crate::traits::Identity; -use crate::window::LookupTable; + use crate::edwards::EdwardsPoint; + use crate::scalar::Scalar; + use crate::traits::Identity; + use crate::window::LookupTable; -/// Perform constant-time, variable-base scalar multiplication. -pub fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { - // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] - let lookup_table = LookupTable::::from(point); - // Setting s = scalar, compute - // - // s = s_0 + s_1*16^1 + ... + s_63*16^63, - // - // with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`. - let scalar_digits = scalar.as_radix_16(); - // Compute s*P as - // - // s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63) - // s*P = P*s_0 + P*s_1*16^1 + P*s_2*16^2 + ... + P*s_63*16^63 - // s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...)) - // - // We sum right-to-left. - let mut Q = ExtendedPoint::identity(); - for i in (0..64).rev() { - Q = Q.mul_by_pow_2(4); - Q = &Q + &lookup_table.select(scalar_digits[i]); + /// Perform constant-time, variable-base scalar multiplication. + pub fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { + // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] + let lookup_table = LookupTable::::from(point); + // Setting s = scalar, compute + // + // s = s_0 + s_1*16^1 + ... + s_63*16^63, + // + // with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`. + let scalar_digits = scalar.as_radix_16(); + // Compute s*P as + // + // s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63) + // s*P = P*s_0 + P*s_1*16^1 + P*s_2*16^2 + ... + P*s_63*16^63 + // s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...)) + // + // We sum right-to-left. + let mut Q = ExtendedPoint::identity(); + for i in (0..64).rev() { + Q = Q.mul_by_pow_2(4); + Q = &Q + &lookup_table.select(scalar_digits[i]); + } + Q.into() } - Q.into() -} - } diff --git a/src/backend/vector/scalar_mul/vartime_double_base.rs b/src/backend/vector/scalar_mul/vartime_double_base.rs index 842a729..191572b 100644 --- a/src/backend/vector/scalar_mul/vartime_double_base.rs +++ b/src/backend/vector/scalar_mul/vartime_double_base.rs @@ -17,85 +17,85 @@ )] pub mod spec { -use core::cmp::Ordering; + use core::cmp::Ordering; -#[for_target_feature("avx2")] -use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; + #[for_target_feature("avx2")] + use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; -#[for_target_feature("avx512ifma")] -use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; - -#[cfg(feature = "precomputed-tables")] -#[for_target_feature("avx2")] -use crate::backend::vector::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE; - -#[cfg(feature = "precomputed-tables")] -#[for_target_feature("avx512ifma")] -use crate::backend::vector::ifma::constants::BASEPOINT_ODD_LOOKUP_TABLE; - -use crate::edwards::EdwardsPoint; -use crate::scalar::Scalar; -use crate::traits::Identity; -use crate::window::NafLookupTable5; - -/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. -pub fn mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { - let a_naf = a.non_adjacent_form(5); + #[for_target_feature("avx512ifma")] + use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; #[cfg(feature = "precomputed-tables")] - let b_naf = b.non_adjacent_form(8); - #[cfg(not(feature = "precomputed-tables"))] - let b_naf = b.non_adjacent_form(5); - - // Find starting index - let mut i: usize = 255; - for j in (0..256).rev() { - i = j; - if a_naf[i] != 0 || b_naf[i] != 0 { - break; - } - } - - let table_A = NafLookupTable5::::from(A); + #[for_target_feature("avx2")] + use crate::backend::vector::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE; #[cfg(feature = "precomputed-tables")] - let table_B = &BASEPOINT_ODD_LOOKUP_TABLE; + #[for_target_feature("avx512ifma")] + use crate::backend::vector::ifma::constants::BASEPOINT_ODD_LOOKUP_TABLE; - #[cfg(not(feature = "precomputed-tables"))] - let table_B = &NafLookupTable5::::from(&crate::constants::ED25519_BASEPOINT_POINT); + use crate::edwards::EdwardsPoint; + use crate::scalar::Scalar; + use crate::traits::Identity; + use crate::window::NafLookupTable5; - let mut Q = ExtendedPoint::identity(); + /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. + pub fn mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { + let a_naf = a.non_adjacent_form(5); - loop { - Q = Q.double(); + #[cfg(feature = "precomputed-tables")] + let b_naf = b.non_adjacent_form(8); + #[cfg(not(feature = "precomputed-tables"))] + let b_naf = b.non_adjacent_form(5); - match a_naf[i].cmp(&0) { - Ordering::Greater => { - Q = &Q + &table_A.select(a_naf[i] as usize); + // Find starting index + let mut i: usize = 255; + for j in (0..256).rev() { + i = j; + if a_naf[i] != 0 || b_naf[i] != 0 { + break; } - Ordering::Less => { - Q = &Q - &table_A.select(-a_naf[i] as usize); - } - Ordering::Equal => {} } - match b_naf[i].cmp(&0) { - Ordering::Greater => { - Q = &Q + &table_B.select(b_naf[i] as usize); + let table_A = NafLookupTable5::::from(A); + + #[cfg(feature = "precomputed-tables")] + let table_B = &BASEPOINT_ODD_LOOKUP_TABLE; + + #[cfg(not(feature = "precomputed-tables"))] + let table_B = + &NafLookupTable5::::from(&crate::constants::ED25519_BASEPOINT_POINT); + + let mut Q = ExtendedPoint::identity(); + + loop { + Q = Q.double(); + + match a_naf[i].cmp(&0) { + Ordering::Greater => { + Q = &Q + &table_A.select(a_naf[i] as usize); + } + Ordering::Less => { + Q = &Q - &table_A.select(-a_naf[i] as usize); + } + Ordering::Equal => {} } - Ordering::Less => { - Q = &Q - &table_B.select(-b_naf[i] as usize); + + match b_naf[i].cmp(&0) { + Ordering::Greater => { + Q = &Q + &table_B.select(b_naf[i] as usize); + } + Ordering::Less => { + Q = &Q - &table_B.select(-b_naf[i] as usize); + } + Ordering::Equal => {} } - Ordering::Equal => {} + + if i == 0 { + break; + } + i -= 1; } - if i == 0 { - break; - } - i -= 1; + Q.into() } - - Q.into() -} - }