rustfmt src/backend/vector/scalar_mul (no changes besides formatting)

This commit is contained in:
Jan Bujak 2023-04-11 11:13:28 +00:00
parent 0db8783be8
commit 219995dbc9
No known key found for this signature in database
GPG key ID: 3B438F83D43341D4
5 changed files with 402 additions and 406 deletions

View file

@ -15,164 +15,163 @@
)] )]
pub mod spec { pub mod spec {
use alloc::vec::Vec; use alloc::vec::Vec;
use core::borrow::Borrow; use core::borrow::Borrow;
use core::cmp::Ordering; use core::cmp::Ordering;
#[for_target_feature("avx2")] #[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx512ifma")] #[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint};
use crate::edwards::EdwardsPoint; use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar; use crate::scalar::Scalar;
use crate::traits::{Identity, VartimeMultiscalarMul}; use crate::traits::{Identity, VartimeMultiscalarMul};
/// Implements a version of Pippenger's algorithm. /// Implements a version of Pippenger's algorithm.
/// ///
/// See the documentation in the serial `scalar_mul::pippenger` module for details. /// See the documentation in the serial `scalar_mul::pippenger` module for details.
pub struct Pippenger; pub struct Pippenger;
impl VartimeMultiscalarMul for Pippenger { impl VartimeMultiscalarMul for Pippenger {
type Point = EdwardsPoint; type Point = EdwardsPoint;
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint> fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
where where
I: IntoIterator, I: IntoIterator,
I::Item: Borrow<Scalar>, I::Item: Borrow<Scalar>,
J: IntoIterator<Item = Option<EdwardsPoint>>, J: IntoIterator<Item = Option<EdwardsPoint>>,
{ {
let mut scalars = scalars.into_iter(); let mut scalars = scalars.into_iter();
let size = scalars.by_ref().size_hint().0; let size = scalars.by_ref().size_hint().0;
let w = if size < 500 { let w = if size < 500 {
6 6
} else if size < 800 { } else if size < 800 {
7 7
} else { } else {
8 8
}; };
let max_digit: usize = 1 << w; let max_digit: usize = 1 << w;
let digits_count: usize = Scalar::to_radix_2w_size_hint(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 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 // Collect optimized scalars and points in a buffer for repeated access
// (scanning the whole collection per each digit position). // (scanning the whole collection per each digit position).
let scalars = scalars.map(|s| s.borrow().as_radix_2w(w)); let scalars = scalars.map(|s| s.borrow().as_radix_2w(w));
let points = points let points = points
.into_iter() .into_iter()
.map(|p| p.map(|P| CachedPoint::from(ExtendedPoint::from(P)))); .map(|p| p.map(|P| CachedPoint::from(ExtendedPoint::from(P))));
let scalars_points = scalars let scalars_points = scalars
.zip(points) .zip(points)
.map(|(s, maybe_p)| maybe_p.map(|p| (s, p))) .map(|(s, maybe_p)| maybe_p.map(|p| (s, p)))
.collect::<Option<Vec<_>>>()?; .collect::<Option<Vec<_>>>()?;
// Prepare 2^w/2 buckets. // Prepare 2^w/2 buckets.
// buckets[i] corresponds to a multiplication factor (i+1). // buckets[i] corresponds to a multiplication factor (i+1).
let mut buckets: Vec<ExtendedPoint> = (0..buckets_count) let mut buckets: Vec<ExtendedPoint> = (0..buckets_count)
.map(|_| ExtendedPoint::identity()) .map(|_| ExtendedPoint::identity())
.collect(); .collect();
let mut columns = (0..digits_count).rev().map(|digit_index| { let mut columns = (0..digits_count).rev().map(|digit_index| {
// Clear the buckets when processing another digit. // Clear the buckets when processing another digit.
for bucket in &mut buckets { for bucket in &mut buckets {
*bucket = ExtendedPoint::identity(); *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 => {}
} }
}
// Add the buckets applying the multiplication factor to each bucket. // Iterate over pairs of (point, scalar)
// The most efficient way to do that is to have a single sum with two running sums: // and add/sub the point to the corresponding bucket.
// an intermediate sum from last bucket to the first, and a sum of intermediate sums. // Note: if we add support for precomputed lookup tables,
// // we'll be adding/subtractiong point premultiplied by `digits[i]` to buckets[0].
// For example, to add buckets 1*A, 2*B, 3*C we need to add these points: for (digits, pt) in scalars_points.iter() {
// C // Widen digit so that we don't run into edge cases when w=8.
// C B let digit = digits[digit_index] as i16;
// C B A Sum = C + (C+B) + (C+B+A) match digit.cmp(&0) {
let mut buckets_intermediate_sum = buckets[buckets_count - 1]; Ordering::Greater => {
let mut buckets_sum = buckets[buckets_count - 1]; let b = (digit - 1) as usize;
for i in (0..(buckets_count - 1)).rev() { buckets[b] = &buckets[b] + pt;
buckets_intermediate_sum = }
&buckets_intermediate_sum + &CachedPoint::from(buckets[i]); Ordering::Less => {
buckets_sum = &buckets_sum + &CachedPoint::from(buckets_intermediate_sum); 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()`. buckets_sum
// `unwrap()` always succeeds because we know we have more than zero digits. });
let hi_column = columns.next().unwrap();
Some( // Take the high column as an initial value to avoid wasting time doubling the identity element in `fold()`.
columns // `unwrap()` always succeeds because we know we have more than zero digits.
.fold(hi_column, |total, p| { let hi_column = columns.next().unwrap();
&total.mul_by_pow_2(w as u32) + &CachedPoint::from(p)
}) Some(
.into(), columns
) .fold(hi_column, |total, p| {
&total.mul_by_pow_2(w as u32) + &CachedPoint::from(p)
})
.into(),
)
}
} }
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#[test] #[test]
fn test_vartime_pippenger() { fn test_vartime_pippenger() {
use super::*; use super::*;
use crate::constants; use crate::constants;
use crate::scalar::Scalar; use crate::scalar::Scalar;
// Reuse points across different tests // Reuse points across different tests
let mut n = 512; let mut n = 512;
let x = Scalar::from(2128506u64).invert(); let x = Scalar::from(2128506u64).invert();
let y = Scalar::from(4443282u64).invert(); let y = Scalar::from(4443282u64).invert();
let points: Vec<_> = (0..n) let points: Vec<_> = (0..n)
.map(|i| constants::ED25519_BASEPOINT_POINT * Scalar::from(1 + i as u64)) .map(|i| constants::ED25519_BASEPOINT_POINT * Scalar::from(1 + i as u64))
.collect(); .collect();
let scalars: Vec<_> = (0..n) let scalars: Vec<_> = (0..n)
.map(|i| x + (Scalar::from(i as u64) * y)) // fast way to make ~random but deterministic scalars .map(|i| x + (Scalar::from(i as u64) * y)) // fast way to make ~random but deterministic scalars
.collect(); .collect();
let premultiplied: Vec<EdwardsPoint> = scalars let premultiplied: Vec<EdwardsPoint> = scalars
.iter() .iter()
.zip(points.iter()) .zip(points.iter())
.map(|(sc, pt)| sc * pt) .map(|(sc, pt)| sc * pt)
.collect(); .collect();
while n > 0 { while n > 0 {
let scalars = &scalars[0..n].to_vec(); let scalars = &scalars[0..n].to_vec();
let points = &points[0..n].to_vec(); let points = &points[0..n].to_vec();
let control: EdwardsPoint = premultiplied[0..n].iter().sum(); 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;
}
} }
} }
} }
}

View file

@ -17,112 +17,111 @@
)] )]
pub mod spec { pub mod spec {
use alloc::vec::Vec; use alloc::vec::Vec;
use core::borrow::Borrow; use core::borrow::Borrow;
use core::cmp::Ordering; use core::cmp::Ordering;
#[for_target_feature("avx2")] #[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx512ifma")] #[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint};
use crate::edwards::EdwardsPoint; use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar; use crate::scalar::Scalar;
use crate::traits::Identity; use crate::traits::Identity;
use crate::traits::VartimePrecomputedMultiscalarMul; use crate::traits::VartimePrecomputedMultiscalarMul;
use crate::window::{NafLookupTable5, NafLookupTable8}; use crate::window::{NafLookupTable5, NafLookupTable8};
pub struct VartimePrecomputedStraus { pub struct VartimePrecomputedStraus {
static_lookup_tables: Vec<NafLookupTable8<CachedPoint>>, static_lookup_tables: Vec<NafLookupTable8<CachedPoint>>,
} }
impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus {
type Point = EdwardsPoint; type Point = EdwardsPoint;
fn new<I>(static_points: I) -> Self fn new<I>(static_points: I) -> Self
where where
I: IntoIterator, I: IntoIterator,
I::Item: Borrow<EdwardsPoint>, I::Item: Borrow<EdwardsPoint>,
{ {
Self { Self {
static_lookup_tables: static_points static_lookup_tables: static_points
.into_iter()
.map(|P| NafLookupTable8::<CachedPoint>::from(P.borrow()))
.collect(),
}
}
fn optional_mixed_multiscalar_mul<I, J, K>(
&self,
static_scalars: I,
dynamic_scalars: J,
dynamic_points: K,
) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Scalar>,
K: IntoIterator<Item = Option<EdwardsPoint>>,
{
let static_nafs = static_scalars
.into_iter() .into_iter()
.map(|P| NafLookupTable8::<CachedPoint>::from(P.borrow())) .map(|c| c.borrow().non_adjacent_form(5))
.collect(), .collect::<Vec<_>>();
} let dynamic_nafs: Vec<_> = dynamic_scalars
} .into_iter()
.map(|c| c.borrow().non_adjacent_form(5))
.collect::<Vec<_>>();
fn optional_mixed_multiscalar_mul<I, J, K>( let dynamic_lookup_tables = dynamic_points
&self, .into_iter()
static_scalars: I, .map(|P_opt| P_opt.map(|P| NafLookupTable5::<CachedPoint>::from(&P)))
dynamic_scalars: J, .collect::<Option<Vec<_>>>()?;
dynamic_points: K,
) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Scalar>,
K: IntoIterator<Item = Option<EdwardsPoint>>,
{
let static_nafs = static_scalars
.into_iter()
.map(|c| c.borrow().non_adjacent_form(5))
.collect::<Vec<_>>();
let dynamic_nafs: Vec<_> = dynamic_scalars
.into_iter()
.map(|c| c.borrow().non_adjacent_form(5))
.collect::<Vec<_>>();
let dynamic_lookup_tables = dynamic_points let sp = self.static_lookup_tables.len();
.into_iter() let dp = dynamic_lookup_tables.len();
.map(|P_opt| P_opt.map(|P| NafLookupTable5::<CachedPoint>::from(&P))) assert_eq!(sp, static_nafs.len());
.collect::<Option<Vec<_>>>()?; assert_eq!(dp, dynamic_nafs.len());
let sp = self.static_lookup_tables.len(); // We could save some doublings by looking for the highest
let dp = dynamic_lookup_tables.len(); // nonzero NAF coefficient, but since we might have a lot of
assert_eq!(sp, static_nafs.len()); // them to search, it's not clear it's worthwhile to check.
assert_eq!(dp, dynamic_nafs.len()); let mut R = ExtendedPoint::identity();
for j in (0..256).rev() {
R = R.double();
// We could save some doublings by looking for the highest for i in 0..dp {
// nonzero NAF coefficient, but since we might have a lot of let t_ij = dynamic_nafs[i][j];
// them to search, it's not clear it's worthwhile to check. match t_ij.cmp(&0) {
let mut R = ExtendedPoint::identity(); Ordering::Greater => {
for j in (0..256).rev() { R = &R + &dynamic_lookup_tables[i].select(t_ij as usize);
R = R.double(); }
Ordering::Less => {
for i in 0..dp { R = &R - &dynamic_lookup_tables[i].select(-t_ij as usize);
let t_ij = dynamic_nafs[i][j]; }
match t_ij.cmp(&0) { Ordering::Equal => {}
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);
#[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)] Some(R.into())
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())
} }
} }
}

View file

@ -17,109 +17,108 @@
)] )]
pub mod spec { pub mod spec {
use alloc::vec::Vec; use alloc::vec::Vec;
use core::borrow::Borrow; use core::borrow::Borrow;
use core::cmp::Ordering; use core::cmp::Ordering;
use zeroize::Zeroizing; use zeroize::Zeroizing;
#[for_target_feature("avx2")] #[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx512ifma")] #[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint};
use crate::edwards::EdwardsPoint; use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar; use crate::scalar::Scalar;
use crate::traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; use crate::traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
use crate::window::{LookupTable, NafLookupTable5}; use crate::window::{LookupTable, NafLookupTable5};
/// Multiscalar multiplication using interleaved window / Straus' /// Multiscalar multiplication using interleaved window / Straus'
/// method. See the `Straus` struct in the serial backend for more /// method. See the `Straus` struct in the serial backend for more
/// details. /// details.
/// ///
/// This exists as a seperate implementation from that one because the /// This exists as a seperate implementation from that one because the
/// AVX2 code uses different curve models (it does not pass between /// AVX2 code uses different curve models (it does not pass between
/// multiple models during scalar mul), and it has to convert the /// multiple models during scalar mul), and it has to convert the
/// point representation on the fly. /// point representation on the fly.
pub struct Straus {} pub struct Straus {}
impl MultiscalarMul for Straus { impl MultiscalarMul for Straus {
type Point = EdwardsPoint; type Point = EdwardsPoint;
fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
where where
I: IntoIterator, I: IntoIterator,
I::Item: Borrow<Scalar>, I::Item: Borrow<Scalar>,
J: IntoIterator, J: IntoIterator,
J::Item: Borrow<EdwardsPoint>, J::Item: Borrow<EdwardsPoint>,
{ {
// Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P]
// for each input point P // for each input point P
let lookup_tables: Vec<_> = points let lookup_tables: Vec<_> = points
.into_iter() .into_iter()
.map(|point| LookupTable::<CachedPoint>::from(point.borrow())) .map(|point| LookupTable::<CachedPoint>::from(point.borrow()))
.collect(); .collect();
let scalar_digits_vec: Vec<_> = scalars let scalar_digits_vec: Vec<_> = scalars
.into_iter() .into_iter()
.map(|s| s.borrow().as_radix_16()) .map(|s| s.borrow().as_radix_16())
.collect(); .collect();
// Pass ownership to a `Zeroizing` wrapper // Pass ownership to a `Zeroizing` wrapper
let scalar_digits = Zeroizing::new(scalar_digits_vec); let scalar_digits = Zeroizing::new(scalar_digits_vec);
let mut Q = ExtendedPoint::identity(); let mut Q = ExtendedPoint::identity();
for j in (0..64).rev() { for j in (0..64).rev() {
Q = Q.mul_by_pow_2(4); Q = Q.mul_by_pow_2(4);
let it = scalar_digits.iter().zip(lookup_tables.iter()); let it = scalar_digits.iter().zip(lookup_tables.iter());
for (s_i, lookup_table_i) in it { for (s_i, lookup_table_i) in it {
// Q = Q + s_{i,j} * P_i // Q = Q + s_{i,j} * P_i
Q = &Q + &lookup_table_i.select(s_i[j]); Q = &Q + &lookup_table_i.select(s_i[j]);
}
}
Q.into()
}
}
impl VartimeMultiscalarMul for Straus {
type Point = EdwardsPoint;
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator<Item = Option<EdwardsPoint>>,
{
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::<CachedPoint>::from(&P)))
.collect::<Option<Vec<_>>>()?;
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 => {}
} }
} }
Q.into()
} }
}
Some(Q.into()) impl VartimeMultiscalarMul for Straus {
type Point = EdwardsPoint;
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator<Item = Option<EdwardsPoint>>,
{
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::<CachedPoint>::from(&P)))
.collect::<Option<Vec<_>>>()?;
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())
}
} }
} }
}

View file

@ -6,40 +6,39 @@
)] )]
pub mod spec { pub mod spec {
#[for_target_feature("avx2")] #[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx512ifma")] #[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint};
use crate::edwards::EdwardsPoint; use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar; use crate::scalar::Scalar;
use crate::traits::Identity; use crate::traits::Identity;
use crate::window::LookupTable; use crate::window::LookupTable;
/// Perform constant-time, variable-base scalar multiplication. /// Perform constant-time, variable-base scalar multiplication.
pub fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { pub fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
// Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P]
let lookup_table = LookupTable::<CachedPoint>::from(point); let lookup_table = LookupTable::<CachedPoint>::from(point);
// Setting s = scalar, compute // Setting s = scalar, compute
// //
// s = s_0 + s_1*16^1 + ... + s_63*16^63, // 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`. // with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`.
let scalar_digits = scalar.as_radix_16(); let scalar_digits = scalar.as_radix_16();
// Compute s*P as // 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 + 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 + 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)...)) // s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...))
// //
// We sum right-to-left. // We sum right-to-left.
let mut Q = ExtendedPoint::identity(); let mut Q = ExtendedPoint::identity();
for i in (0..64).rev() { for i in (0..64).rev() {
Q = Q.mul_by_pow_2(4); Q = Q.mul_by_pow_2(4);
Q = &Q + &lookup_table.select(scalar_digits[i]); Q = &Q + &lookup_table.select(scalar_digits[i]);
}
Q.into()
} }
Q.into()
}
} }

View file

@ -17,85 +17,85 @@
)] )]
pub mod spec { pub mod spec {
use core::cmp::Ordering; use core::cmp::Ordering;
#[for_target_feature("avx2")] #[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx512ifma")] #[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; 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);
#[cfg(feature = "precomputed-tables")] #[cfg(feature = "precomputed-tables")]
let b_naf = b.non_adjacent_form(8); #[for_target_feature("avx2")]
#[cfg(not(feature = "precomputed-tables"))] use crate::backend::vector::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE;
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::<CachedPoint>::from(A);
#[cfg(feature = "precomputed-tables")] #[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"))] use crate::edwards::EdwardsPoint;
let table_B = &NafLookupTable5::<CachedPoint>::from(&crate::constants::ED25519_BASEPOINT_POINT); 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 { #[cfg(feature = "precomputed-tables")]
Q = Q.double(); 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) { // Find starting index
Ordering::Greater => { let mut i: usize = 255;
Q = &Q + &table_A.select(a_naf[i] as usize); 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) { let table_A = NafLookupTable5::<CachedPoint>::from(A);
Ordering::Greater => {
Q = &Q + &table_B.select(b_naf[i] as usize); #[cfg(feature = "precomputed-tables")]
let table_B = &BASEPOINT_ODD_LOOKUP_TABLE;
#[cfg(not(feature = "precomputed-tables"))]
let table_B =
&NafLookupTable5::<CachedPoint>::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 { Q.into()
break;
}
i -= 1;
} }
Q.into()
}
} }