Use multiscalar traits for the backend implementations.

This commit is contained in:
Henry de Valence 2018-05-02 17:20:58 -07:00
parent e3bf9b0213
commit 149c5004e8
7 changed files with 262 additions and 217 deletions

View file

@ -13,8 +13,4 @@ pub mod variable_base;
#[cfg(feature = "stage2_build")] #[cfg(feature = "stage2_build")]
pub mod vartime_double_base; pub mod vartime_double_base;
#[cfg(any(feature = "alloc", feature = "std"))]
pub mod straus; pub mod straus;
#[cfg(any(feature = "alloc", feature = "std"))]
pub mod vartime_straus;

View file

@ -7,48 +7,98 @@
// Authors: // Authors:
// - Isis Agora Lovecruft <isis@patternsinthevoid.net> // - Isis Agora Lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca> // - Henry de Valence <hdevalence@hdevalence.ca>
#![allow(non_snake_case)]
use core::borrow::Borrow; use core::borrow::Borrow;
use clear_on_drop::ClearOnDrop; use clear_on_drop::ClearOnDrop;
use traits::Identity;
use scalar::Scalar;
use edwards::EdwardsPoint;
use scalar_mul::window::LookupTable;
use backend::avx2::edwards::{CachedPoint, ExtendedPoint}; use backend::avx2::edwards::{CachedPoint, ExtendedPoint};
use edwards::EdwardsPoint;
use scalar::Scalar;
use scalar_mul::window::{LookupTable, NafLookupTable5};
use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
/// Perform constant-time, variable-base scalar multiplication. /// Multiscalar multiplication using interleaved window / Straus'
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint /// method. See the `Straus` struct in the serial backend for more
where /// details.
I: IntoIterator, ///
I::Item: Borrow<Scalar>, /// This exists as a seperate implementation from that one because the
J: IntoIterator, /// AVX2 code uses different curve models (it does not pass between
J::Item: Borrow<EdwardsPoint>, /// multiple models during scalar mul), and it has to convert the
{ /// point representation on the fly.
// Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] pub struct Straus {}
// for each input point P
let lookup_tables: Vec<_> = points
.into_iter()
.map(|point| LookupTable::<CachedPoint>::from(point.borrow()))
.collect();
let scalar_digits_vec: Vec<_> = scalars #[cfg(any(feature = "alloc", feature = "std"))]
.into_iter() impl MultiscalarMul for Straus {
.map(|s| s.borrow().to_radix_16()) type Point = EdwardsPoint;
.collect();
// Pass ownership to a ClearOnDrop wrapper
let scalar_digits = ClearOnDrop::new(scalar_digits_vec);
let mut Q = ExtendedPoint::identity(); fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
for j in (0..64).rev() { where
Q = Q.mul_by_pow_2(4); I: IntoIterator,
let it = scalar_digits.iter().zip(lookup_tables.iter()); I::Item: Borrow<Scalar>,
for (s_i, lookup_table_i) in it { J: IntoIterator,
// Q = Q + s_{i,j} * P_i J::Item: Borrow<EdwardsPoint>,
Q = &Q + &lookup_table_i.select(s_i[j]); {
// 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::<CachedPoint>::from(point.borrow()))
.collect();
let scalar_digits_vec: Vec<_> = scalars
.into_iter()
.map(|s| s.borrow().to_radix_16())
.collect();
// Pass ownership to a ClearOnDrop wrapper
let scalar_digits = ClearOnDrop::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()
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl VartimeMultiscalarMul for Straus {
type Point = EdwardsPoint;
fn vartime_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(5))
.collect();
let lookup_tables: Vec<_> = points
.into_iter()
.map(|point| NafLookupTable5::<CachedPoint>::from(point.borrow()))
.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()
} }
Q.into()
} }

View file

@ -1,51 +0,0 @@
// -*- 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::NafLookupTable5;
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(5))
.collect();
let lookup_tables: Vec<_> = points
.into_iter()
.map(|point| NafLookupTable5::<CachedPoint>::from(point.borrow()))
.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

@ -543,14 +543,14 @@ impl MultiscalarMul for EdwardsPoint {
// If we built with AVX2, use the AVX2 backend. // If we built with AVX2, use the AVX2 backend.
#[cfg(all(feature="avx2_backend", target_feature="avx2"))] #[cfg(all(feature="avx2_backend", target_feature="avx2"))]
{ {
use backend::avx2::scalar_mul::straus::multiscalar_mul; use backend::avx2::scalar_mul::straus::Straus;
multiscalar_mul(scalars, points) Straus::multiscalar_mul(scalars, points)
} }
// Otherwise, proceed as normal: // Otherwise, proceed as normal:
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
{ {
use scalar_mul::straus::multiscalar_mul; use scalar_mul::straus::Straus;
multiscalar_mul(scalars, points) Straus::multiscalar_mul(scalars, points)
} }
} }
} }
@ -572,14 +572,14 @@ impl VartimeMultiscalarMul for EdwardsPoint {
// If we built with AVX2, use the AVX2 backend. // If we built with AVX2, use the AVX2 backend.
#[cfg(all(feature="avx2_backend", target_feature="avx2"))] #[cfg(all(feature="avx2_backend", target_feature="avx2"))]
{ {
use backend::avx2::scalar_mul::vartime_straus::multiscalar_mul; use backend::avx2::scalar_mul::straus::Straus;
multiscalar_mul(scalars, points) Straus::vartime_multiscalar_mul(scalars, points)
} }
// Otherwise, proceed as normal: // Otherwise, proceed as normal:
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
{ {
use scalar_mul::vartime_straus::multiscalar_mul; use scalar_mul::straus::Straus;
multiscalar_mul(scalars, points) Straus::vartime_multiscalar_mul(scalars, points)
} }
} }
} }

View file

@ -15,8 +15,4 @@ pub mod variable_base;
#[cfg(feature = "stage2_build")] #[cfg(feature = "stage2_build")]
pub mod vartime_double_base; pub mod vartime_double_base;
#[cfg(any(feature = "alloc", feature = "std"))]
pub mod straus; pub mod straus;
#[cfg(any(feature = "alloc", feature = "std"))]
pub mod vartime_straus;

View file

@ -7,77 +7,185 @@
// Authors: // Authors:
// - Isis Agora Lovecruft <isis@patternsinthevoid.net> // - Isis Agora Lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca> // - Henry de Valence <hdevalence@hdevalence.ca>
#![allow(non_snake_case)] #![allow(non_snake_case)]
use core::borrow::Borrow; use core::borrow::Borrow;
use clear_on_drop::ClearOnDrop;
use traits::Identity;
use scalar::Scalar;
use edwards::EdwardsPoint; use edwards::EdwardsPoint;
use curve_models::ProjectiveNielsPoint; use scalar::Scalar;
use scalar_mul::window::LookupTable; use traits::MultiscalarMul;
use traits::VartimeMultiscalarMul;
/// Perform constant-time, variable-base scalar multiplication. /// Perform multiscalar multiplication by the interleaved window
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint /// method, also known as Straus' method (since it was apparently
where /// [first published][solution] by Straus in 1964, as a solution to [a
I: IntoIterator, /// problem][problem] posted in the American Mathematical Monthly in
I::Item: Borrow<Scalar>, /// 1963).
J: IntoIterator, ///
J::Item: Borrow<EdwardsPoint>, /// It is easy enough to reinvent, and has been repeatedly. The basic
{ /// idea is that when computing
// Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] /// \\[
// for each input point P /// Q = s_1 P_1 + \cdots + s_n P_n
let lookup_tables: Vec<_> = points /// \\]
.into_iter() /// by means of additions and doublings, the doublings can be shared
.map(|point| LookupTable::<ProjectiveNielsPoint>::from(point.borrow())) /// across the \\( P_i \\\).
.collect(); ///
/// We implement two versions, a constant-time algorithm using fixed
/// windows and a variable-time algorithm using sliding windows. They
/// are slight variations on the same idea, and are described in more
/// detail in the respective implementations.
///
/// [solution]: https://www.jstor.org/stable/2310929
/// [problem]: https://www.jstor.org/stable/2312273
pub struct Straus {}
// Setting s_i = i-th scalar, compute #[cfg(any(feature = "alloc", feature = "std"))]
// impl MultiscalarMul for Straus {
// s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63, type Point = EdwardsPoint;
//
// with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`.
//
// This 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_vec: Vec<_> = scalars
.into_iter()
.map(|s| s.borrow().to_radix_16())
.collect();
let scalar_digits = ClearOnDrop::new(scalar_digits_vec);
// Compute s_1*P_1 + ... + s_n*P_n: since /// Constant-time Straus using a fixed window of size \\(4\\).
// ///
// s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63) /// Our goal is to compute
// 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})...) /// Q = s_1 P_1 + \cdots + s_n P_n.
// /// \\]
// we have the two-dimensional sum ///
// /// For each point \\( P_i \\), precompute a lookup table of
// 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})...) /// P_i, 2P_i, 3P_i, 4P_i, 5P_i, 6P_i, 7P_i, 8P_i.
// ... /// \\]
// + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...) ///
// /// For each scalar \\( s_i \\), compute its radix-\\(2^4\\)
// We sum column-wise top-to-bottom, then right-to-left, /// signed digits \\( s_{i,j} \\), i.e.,
// multiplying by 16 only once per column. /// \\[
// /// s_i = s_{i,0} + s_{i,1} 16^1 + ... + s_{i,63} 16^{63},
// This provides the speedup over doing n independent scalar /// \\]
// mults: we perform 63 multiplications by 16 instead of 63*n /// with \\( -8 \leq s_{i,j} < 8 \\). Since \\( 0 \leq |s_{i,j}|
// multiplications, saving 252*(n-1) doublings. /// \leq 8 \\), we can retrieve \\( s_{i,j} P_i \\) from the
let mut Q = EdwardsPoint::identity(); /// lookup table with a conditional negation: using signed
for j in (0..64).rev() { /// digits halves the required table size.
Q = Q.mul_by_pow_2(4); ///
let it = scalar_digits.iter().zip(lookup_tables.iter()); /// Then as in the single-base fixed window case, we have
for (s_i, lookup_table_i) in it { /// \\[
// R_i = s_{i,j} * P_i /// \begin{aligned}
let R_i = lookup_table_i.select(s_i[j]); /// s_i P_i &= P_i (s_{i,0} + s_{i,1} 16^1 + \cdots + s_{i,63} 16^{63}) \\\\
// Q = Q + R_i /// s_i P_i &= P_i s_{i,0} + P_i s_{i,1} 16^1 + \cdots + P_i s_{i,63} 16^{63} \\\\
Q = (&Q + &R_i).to_extended(); /// s_i P_i &= P_i s_{i,0} + 16(P_i s_{i,1} + 16( \cdots +16P_i s_{i,63})\cdots )
/// \end{aligned}
/// \\]
/// so each \\( s_i P_i \\) can be computed by alternately adding
/// a precomputed multiple \\( P_i s_{i,j} \\) of \\( P_i \\) and
/// repeatedly doubling.
///
/// Now consider the two-dimensional sum
/// \\[
/// \begin{aligned}
/// s\_1 P\_1 &=& P\_1 s\_{1,0} &+& 16 (P\_1 s\_{1,1} &+& 16 ( \cdots &+& 16 P\_1 s\_{1,63}&) \cdots ) \\\\
/// + & & + & & + & & & & + & \\\\
/// s\_2 P\_2 &=& P\_2 s\_{2,0} &+& 16 (P\_2 s\_{2,1} &+& 16 ( \cdots &+& 16 P\_2 s\_{2,63}&) \cdots ) \\\\
/// + & & + & & + & & & & + & \\\\
/// \vdots & & \vdots & & \vdots & & & & \vdots & \\\\
/// + & & + & & + & & & & + & \\\\
/// s\_n P\_n &=& P\_n s\_{n,0} &+& 16 (P\_n s\_{n,1} &+& 16 ( \cdots &+& 16 P\_n s\_{n,63}&) \cdots )
/// \end{aligned}
/// \\]
/// The sum of the left-hand column is the result \\( Q \\); by
/// computing the two-dimensional sum on the right column-wise,
/// top-to-bottom, then right-to-left, we need to multiply by \\(
/// 16\\) only once per column, sharing the doublings across all
/// of the input points.
fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
{
use clear_on_drop::ClearOnDrop;
use curve_models::ProjectiveNielsPoint;
use scalar_mul::window::LookupTable;
use traits::Identity;
let lookup_tables: Vec<_> = points
.into_iter()
.map(|point| LookupTable::<ProjectiveNielsPoint>::from(point.borrow()))
.collect();
// This 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_vec: Vec<_> = scalars
.into_iter()
.map(|s| s.borrow().to_radix_16())
.collect();
let scalar_digits = ClearOnDrop::new(scalar_digits_vec);
let mut Q = EdwardsPoint::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 {
// R_i = s_{i,j} * P_i
let R_i = lookup_table_i.select(s_i[j]);
// Q = Q + R_i
Q = (&Q + &R_i).to_extended();
}
} }
Q
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl VartimeMultiscalarMul for Straus {
type Point = EdwardsPoint;
/// Variable-time Straus using a non-adjacent form of width \\(5\\).
///
/// This is completely similar to the constant-time code, but we
/// use a non-adjacent form for the scalar, and do not do table
/// lookups in constant time.
///
/// The non-adjacent form has signed, odd digits. Using only odd
/// digits halves the table size (since we only need odd
/// multiples), or gives fewer additions for the same table size.
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
{
use curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint};
use scalar_mul::window::NafLookupTable5;
use traits::Identity;
let nafs: Vec<_> = scalars
.into_iter()
.map(|c| c.borrow().non_adjacent_form(5))
.collect();
let lookup_tables: Vec<_> = points
.into_iter()
.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(P.borrow()))
.collect();
let mut r = ProjectivePoint::identity();
for i in (0..255).rev() {
let mut t: CompletedPoint = r.double();
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
if naf[i] > 0 {
t = &t.to_extended() + &lookup_table.select(naf[i] as usize);
} else if naf[i] < 0 {
t = &t.to_extended() - &lookup_table.select(-naf[i] as usize);
}
}
r = t.to_projective();
}
r.to_extended()
} }
Q
} }

View file

@ -1,54 +0,0 @@
// -*- 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 curve_models::{CompletedPoint, ProjectivePoint, ProjectiveNielsPoint};
use scalar_mul::window::NafLookupTable5;
/// 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(5))
.collect();
let lookup_tables: Vec<_> = points
.into_iter()
.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(P.borrow()))
.collect();
let mut r = ProjectivePoint::identity();
for i in (0..255).rev() {
let mut t: CompletedPoint = r.double();
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
if naf[i] > 0 {
t = &t.to_extended() + &lookup_table.select(naf[i] as usize);
} else if naf[i] < 0 {
t = &t.to_extended() - &lookup_table.select(-naf[i] as usize);
}
}
r = t.to_projective();
}
r.to_extended()
}