Pull out variable-time straus implementation

This commit is contained in:
Henry de Valence 2018-03-26 15:54:13 -07:00
parent 2864a422bc
commit 2d99892eab
7 changed files with 156 additions and 156 deletions

View file

@ -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<ExtendedPoint> for LookupTable<CachedPoint> {
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<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 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<CachedPoint> {
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<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.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)]

View file

@ -11,3 +11,5 @@
pub mod variable_base;
pub mod straus;
pub mod vartime_straus;

View file

@ -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 <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
#![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<I, J>(scalars: I, points: J) -> EdwardsPoint
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
{
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::<CachedPoint>::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()
}

View file

@ -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<I, J>(scalars: I, points: J) -> EdwardsPoint
where I: IntoIterator,
@ -854,38 +852,19 @@ pub mod vartime {
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
{
// 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)
}
}

View file

@ -13,3 +13,5 @@ pub mod window;
pub mod variable_base;
pub mod straus;
pub mod vartime_straus;

View file

@ -9,31 +9,21 @@
// - Henry de Valence <hdevalence@hdevalence.ca>
#![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<I, J, ExtendedPoint, CompletedPoint, ProjectivePoint, CachedPoint>(
scalars: I,
points: J,
) -> ExtendedPoint
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<ExtendedPoint>,
for<'a> OddLookupTable<CachedPoint>: 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<CompletedPoint> + From<ProjectivePoint>,
ProjectivePoint: Identity + Doubleable<Output = CompletedPoint> + From<CompletedPoint>,
CachedPoint: Copy,
J::Item: Borrow<EdwardsPoint>,
{
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::<ProjectiveNielsPoint>::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()
}

View file

@ -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<T> ZeroSafe for LookupTable<T> {}
impl<T> LookupTable<T>
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<T: Debug> Debug for LookupTable<T> {
}
}
use edwards::EdwardsPoint;
use curve_models::ProjectiveNielsPoint;
use curve_models::AffineNielsPoint;
impl<'a> From<&'a EdwardsPoint> for LookupTable<ProjectiveNielsPoint> {
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<AffineNielsPoint> {
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<T>(pub(crate) [T; 8]);
impl<T: Copy> OddLookupTable<T> {
/// 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<T: Debug> Debug for OddLookupTable<T> {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "OddLookupTable({:?})", self.0)
}
}
impl<'a> From<&'a EdwardsPoint> for OddLookupTable<ProjectiveNielsPoint> {
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<AffineNielsPoint> {
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)
}
}