Implement larger sizes of basepoint tables.

This implements a macro for implementing the BasepointTable trait, and
uses the macro to create basepoint table types. The default table
still uses radix-16 representation and is ~30KB in size.  The new
table types, and their memory usage and additions required per
basepoint multiplication are:

 * `EdwardsBasepointTableRadix64`: ~120KB, 43 additions
 * `EdwardsBasepointTableRadix128`: ~240KB, 37 additions
 * `EdwardsBasepointTableRadix256`: ~480KB, 32 additions
This commit is contained in:
Isis Lovecruft 2019-12-27 21:32:30 +00:00
parent 383e65f6bd
commit 8a9e09ba34
No known key found for this signature in database
GPG key ID: AB41313533E8E812
3 changed files with 149 additions and 62 deletions

View file

@ -117,11 +117,15 @@ use backend::serial::curve_models::CompletedPoint;
use backend::serial::curve_models::ProjectiveNielsPoint; use backend::serial::curve_models::ProjectiveNielsPoint;
use backend::serial::curve_models::ProjectivePoint; use backend::serial::curve_models::ProjectivePoint;
use window::LookupTable; use window::LookupTableRadix16;
use window::LookupTableRadix64;
use window::LookupTableRadix128;
use window::LookupTableRadix256;
#[allow(unused_imports)] #[allow(unused_imports)]
use prelude::*; use prelude::*;
use traits::BasepointTable;
use traits::ValidityCheck; use traits::ValidityCheck;
use traits::{Identity, IsIdentity}; use traits::{Identity, IsIdentity};
@ -743,56 +747,116 @@ impl EdwardsPoint {
} }
} }
macro_rules! impl_basepoint_table {
(Name = $name:ident, LookupTable = $table:ident, Point = $point:ty, Radix = $radix:expr, Additions = $adds:expr) => {
/// A precomputed table of multiples of a basepoint, for accelerating /// A precomputed table of multiples of a basepoint, for accelerating
/// fixed-base scalar multiplication. One table, for the Ed25519 /// fixed-base scalar multiplication. One table, for the Ed25519
/// basepoint, is provided in the `constants` module. /// basepoint, is provided in the `constants` module.
/// ///
/// The basepoint tables are reasonably large (30KB), so they should /// The basepoint tables are reasonably large, so they should probably be boxed.
/// probably be boxed. ///
/// The sizes for the tables and the number of additions required for one scalar
/// multiplication are as follows:
///
/// * [`EdwardsBasepointTableRadix16`]: 30KB, 64A
/// (this is the default size, and is used for [`ED25519_BASEPOINT_TABLE`])
/// * [`EdwardsBasepointTableRadix64`]: 120KB, 43A
/// * [`EdwardsBasepointTableRadix128`]: 240KB, 37A
/// * [`EdwardsBasepointTableRadix256`]: 480KB, 33A
///
/// # Why 33 additions for radix-256?
///
/// Normally, the radix-256 tables would allow for only 32 additions per scalar
/// multiplication. However, due to the fact that standardised definitions of
/// legacy protocols—such as x25519—require allowing unreduced 255-bit scalar
/// invariants, when converting such an unreduced scalar's representation to
/// radix-\\(2^{8}\\), we cannot guarantee the carry bit will fit in the last
/// coefficient (the coefficients are `i8`s). When, \\(w\\), the power-of-2 of
/// the radix, is \\(w < 8\\), we can fold the final carry onto the last
/// coefficient, \\(d\\), because \\(d < 2^{w/2}\\), so
/// $$
/// d + carry \cdot 2^{w} = d + 1 \cdot 2^{w} < 2^{w+1} < 2^{8}
/// $$
/// When \\(w = 8\\), we can't fit \\(carry \cdot 2^{w}\\) into an `i8`, so we
/// add the carry bit onto an additional coefficient.
#[derive(Clone)] #[derive(Clone)]
pub struct EdwardsBasepointTable(pub(crate) [LookupTable<AffineNielsPoint>; 32]); pub struct $name(pub(crate) [$table<AffineNielsPoint>; 32]);
impl EdwardsBasepointTable { impl BasepointTable for $name {
/// The computation uses Pippeneger's algorithm, as described on type Point = $point;
/// page 13 of the Ed25519 paper. Write the scalar \\(a\\) in radix \\(16\\) with
/// coefficients in \\([-8,8)\\), i.e., /// Create a table of precomputed multiples of `basepoint`.
fn create(basepoint: &$point) -> $name {
// XXX use init_with
let mut table = $name([$table::default(); 32]);
let mut P = *basepoint;
for i in 0..32 {
// P = (2w)^i * B
table.0[i] = $table::from(&P);
P = P.mul_by_pow_2($radix + $radix);
}
table
}
/// Get the basepoint for this table as an `EdwardsPoint`.
fn basepoint(&self) -> $point {
// self.0[0].select(1) = 1*(16^2)^0*B
// but as an `AffineNielsPoint`, so add identity to convert to extended.
(&<$point>::identity() + &self.0[0].select(1)).to_extended()
}
/// The computation uses Pippeneger's algorithm, as described for the
/// specific case of radix-16 on page 13 of the Ed25519 paper.
///
/// # Piggenger's Algorithm Generalised
///
/// Write the scalar \\(a\\) in radix-\\(w\\), where \\(w\\) is a power of
/// 2, with coefficients in \\([\frac{-w}{2},\frac{w}{2})\\), i.e.,
/// $$ /// $$
/// a = a\_0 + a\_1 16\^1 + \cdots + a\_{63} 16\^{63}, /// a = a\_0 + a\_1 w\^1 + \cdots + a\_{x} w\^{x},
/// $$ /// $$
/// with \\(-8 \leq a_i < 8\\), \\(-8 \leq a\_{63} \leq 8\\). Then /// with
/// $$ /// $$
/// a B = a\_0 B + a\_1 16\^1 B + \cdots + a\_{63} 16\^{63} B. /// \frac{-w}{2} \leq a_i < \frac{w}{2}, \cdots, \frac{-w}{2} \leq a\_{x} \leq \frac{w}{2}
/// $$
/// and the number of additions, \\(x\\), is given by \\(x = \lceil \frac{256}{w} \rceil\\).
/// Then
/// $$
/// a B = a\_0 B + a\_1 w\^1 B + \cdots + a\_{x-1} w\^{x-1} B.
/// $$ /// $$
/// Grouping even and odd coefficients gives /// Grouping even and odd coefficients gives
/// $$ /// $$
/// \begin{aligned} /// \begin{aligned}
/// a B = \quad a\_0 16\^0 B +& a\_2 16\^2 B + \cdots + a\_{62} 16\^{62} B \\\\ /// a B = \quad a\_0 w\^0 B +& a\_2 w\^2 B + \cdots + a\_{x-2} w\^{x-2} B \\\\
/// + a\_1 16\^1 B +& a\_3 16\^3 B + \cdots + a\_{63} 16\^{63} B \\\\ /// + a\_1 w\^1 B +& a\_3 w\^3 B + \cdots + a\_{x-1} w\^{x-1} B \\\\
/// = \quad(a\_0 16\^0 B +& a\_2 16\^2 B + \cdots + a\_{62} 16\^{62} B) \\\\ /// = \quad(a\_0 w\^0 B +& a\_2 w\^2 B + \cdots + a\_{x-2} w\^{x-2} B) \\\\
/// + 16(a\_1 16\^0 B +& a\_3 16\^2 B + \cdots + a\_{63} 16\^{62} B). \\\\ /// + w(a\_1 w\^0 B +& a\_3 w\^2 B + \cdots + a\_{x-1} w\^{x-2} B). \\\\
/// \end{aligned} /// \end{aligned}
/// $$ /// $$
/// For each \\(i = 0 \ldots 31\\), we create a lookup table of /// For each \\(i = 0 \ldots 31\\), we create a lookup table of
/// $$ /// $$
/// [16\^{2i} B, \ldots, 8\cdot16\^{2i} B], /// [w\^{2i} B, \ldots, \frac{w}{2}\cdotw\^{2i} B],
/// $$ /// $$
/// and use it to select \\( x \cdot 16\^{2i} \cdot B \\) in constant time. /// and use it to select \\( y \cdot w\^{2i} \cdot B \\) in constant time.
/// ///
/// The radix-\\(16\\) representation requires that the scalar is bounded /// The radix-\\(w\\) representation requires that the scalar is bounded
/// by \\(2\^{255}\\), which is always the case. /// by \\(2\^{255}\\), which is always the case.
fn basepoint_mul(&self, scalar: &Scalar) -> EdwardsPoint { ///
let a = scalar.to_radix_16(); /// The above algorithm is trivially generalised to other powers-of-2 radices.
fn basepoint_mul(&self, scalar: &Scalar) -> $point {
let a = scalar.to_radix_2w($radix);
let tables = &self.0; let tables = &self.0;
let mut P = EdwardsPoint::identity(); let mut P = <$point>::identity();
for i in (0..64).filter(|x| x % 2 == 1) { for i in (0..$adds).filter(|x| x % 2 == 1) {
P = (&P + &tables[i/2].select(a[i])).to_extended(); P = (&P + &tables[i/2].select(a[i])).to_extended();
} }
P = P.mul_by_pow_2(4); P = P.mul_by_pow_2($radix);
for i in (0..64).filter(|x| x % 2 == 0) { for i in (0..$adds).filter(|x| x % 2 == 0) {
P = (&P + &tables[i/2].select(a[i])).to_extended(); P = (&P + &tables[i/2].select(a[i])).to_extended();
} }
@ -800,49 +864,53 @@ impl EdwardsBasepointTable {
} }
} }
impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsBasepointTable { impl<'a, 'b> Mul<&'b Scalar> for &'a $name {
type Output = EdwardsPoint; type Output = $point;
/// Construct an `EdwardsPoint` from a `Scalar` \\(a\\) by /// Construct an `EdwardsPoint` from a `Scalar` \\(a\\) by
/// computing the multiple \\(aB\\) of this basepoint \\(B\\). /// computing the multiple \\(aB\\) of this basepoint \\(B\\).
fn mul(self, scalar: &'b Scalar) -> EdwardsPoint { fn mul(self, scalar: &'b Scalar) -> $point {
// delegate to a private function so that its documentation appears in internal docs // delegate to a private function so that its documentation appears in internal docs
self.basepoint_mul(scalar) self.basepoint_mul(scalar)
} }
} }
impl<'a, 'b> Mul<&'a EdwardsBasepointTable> for &'b Scalar { impl<'a, 'b> Mul<&'a $name> for &'b Scalar {
type Output = EdwardsPoint; type Output = $point;
/// Construct an `EdwardsPoint` from a `Scalar` \\(a\\) by /// Construct an `EdwardsPoint` from a `Scalar` \\(a\\) by
/// computing the multiple \\(aB\\) of this basepoint \\(B\\). /// computing the multiple \\(aB\\) of this basepoint \\(B\\).
fn mul(self, basepoint_table: &'a EdwardsBasepointTable) -> EdwardsPoint { fn mul(self, basepoint_table: &'a $name) -> $point {
basepoint_table * self basepoint_table * self
} }
} }
impl EdwardsBasepointTable { impl Debug for $name {
/// Create a table of precomputed multiples of `basepoint`. fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
pub fn create(basepoint: &EdwardsPoint) -> EdwardsBasepointTable { write!(f, "{:?}([\n", stringify!($name))?;
// XXX use init_with
let mut table = EdwardsBasepointTable([LookupTable::default(); 32]);
let mut P = *basepoint;
for i in 0..32 { for i in 0..32 {
// P = (16^2)^i * B write!(f, "\t{:?},\n", &self.0[i])?;
table.0[i] = LookupTable::from(&P);
P = P.mul_by_pow_2(8);
} }
table write!(f, "])")
}
/// Get the basepoint for this table as an `EdwardsPoint`.
pub fn basepoint(&self) -> EdwardsPoint {
// self.0[0].select(1) = 1*(16^2)^0*B
// but as an `AffineNielsPoint`, so add identity to convert to extended.
(&EdwardsPoint::identity() + &self.0[0].select(1)).to_extended()
} }
} }
}} // End macro_rules! impl_basepoint_table
// The number of additions required is ceil(256/w) where w is the radix representation.
impl_basepoint_table! {Name = EdwardsBasepointTable, LookupTable = LookupTableRadix16, Point = EdwardsPoint, Radix = 4, Additions = 64}
impl_basepoint_table! {Name = EdwardsBasepointTableRadix64, LookupTable = LookupTableRadix64, Point = EdwardsPoint, Radix = 6, Additions = 43}
impl_basepoint_table! {Name = EdwardsBasepointTableRadix128, LookupTable = LookupTableRadix128, Point = EdwardsPoint, Radix = 7, Additions = 37}
impl_basepoint_table! {Name = EdwardsBasepointTableRadix256, LookupTable = LookupTableRadix256, Point = EdwardsPoint, Radix = 8, Additions = 33}
/// A type-alias for [`EdwardsBasepointTable`] because the latter is
/// used as a constructor in the `constants` module.
//
// Same as for `LookupTableRadix16`, we have to define `EdwardsBasepointTable`
// first, because it's used as a constructor, and then provide a type alias for
// it.
pub type EdwardsBasepointTableRadix16 = EdwardsBasepointTable;
impl EdwardsPoint { impl EdwardsPoint {
/// Multiply by the cofactor: return \\([8]P\\). /// Multiply by the cofactor: return \\([8]P\\).
pub fn mul_by_cofactor(&self) -> EdwardsPoint { pub fn mul_by_cofactor(&self) -> EdwardsPoint {
@ -930,16 +998,6 @@ impl Debug for EdwardsPoint {
} }
} }
impl Debug for EdwardsBasepointTable {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "EdwardsBasepointTable([\n")?;
for i in 0..32 {
write!(f, "\t{:?},\n", &self.0[i])?;
}
write!(f, "])")
}
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Tests // Tests
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -1148,6 +1206,29 @@ mod test {
assert_eq!(bp2.compress(), BASE2_CMPRSSD); assert_eq!(bp2.compress(), BASE2_CMPRSSD);
} }
/// Test that all the basepoint table types compute the same results.
#[test]
fn basepoint_tables() {
let P = &constants::ED25519_BASEPOINT_POINT;
let a = A_SCALAR;
let table_radix16 = EdwardsBasepointTableRadix16::create(&P);
let table_radix64 = EdwardsBasepointTableRadix64::create(&P);
let table_radix128 = EdwardsBasepointTableRadix128::create(&P);
let table_radix256 = EdwardsBasepointTableRadix256::create(&P);
let aP = (&constants::ED25519_BASEPOINT_TABLE * &a).compress();
let aP16 = (&table_radix16 * &a).compress();
let aP64 = (&table_radix64 * &a).compress();
let aP128 = (&table_radix128 * &a).compress();
let aP256 = (&table_radix256 * &a).compress();
assert_eq!(aP, aP16);
assert_eq!(aP16, aP64);
assert_eq!(aP64, aP128);
assert_eq!(aP128, aP256);
}
/// Check that converting to projective and then back to extended round-trips. /// Check that converting to projective and then back to extended round-trips.
#[test] #[test]
fn basepoint_projective_extended_round_trip() { fn basepoint_projective_extended_round_trip() {

View file

@ -185,6 +185,7 @@ use prelude::*;
use scalar::Scalar; use scalar::Scalar;
use traits::BasepointTable;
use traits::Identity; use traits::Identity;
#[cfg(any(feature = "alloc", feature = "std"))] #[cfg(any(feature = "alloc", feature = "std"))]
use traits::{MultiscalarMul, VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul}; use traits::{MultiscalarMul, VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul};

View file

@ -989,10 +989,11 @@ impl Scalar {
/// Returns a size hint indicating how many entries of the return /// Returns a size hint indicating how many entries of the return
/// value of `to_radix_2w` are nonzero. /// value of `to_radix_2w` are nonzero.
pub(crate) fn to_radix_2w_size_hint(w: usize) -> usize { pub(crate) fn to_radix_2w_size_hint(w: usize) -> usize {
debug_assert!(w >= 6); debug_assert!(w == 4 || w >= 6);
debug_assert!(w <= 8); debug_assert!(w <= 8);
let digits_count = match w { let digits_count = match w {
4 => (256 + w - 1)/w as usize,
6 => (256 + w - 1)/w as usize, 6 => (256 + w - 1)/w as usize,
7 => (256 + w - 1)/w as usize, 7 => (256 + w - 1)/w as usize,
// See comment in to_radix_2w on handling the terminal carry. // See comment in to_radix_2w on handling the terminal carry.
@ -1000,7 +1001,7 @@ impl Scalar {
_ => panic!("invalid radix parameter"), _ => panic!("invalid radix parameter"),
}; };
debug_assert!(digits_count <= 43); debug_assert!(digits_count <= 64);
digits_count digits_count
} }
@ -1022,10 +1023,14 @@ impl Scalar {
/// $$ /// $$
/// with \\(-2\^w/2 \leq a_i < 2\^w/2\\) for \\(0 \leq i < (n-1)\\) and \\(-2\^w/2 \leq a_{n-1} \leq 2\^w/2\\). /// with \\(-2\^w/2 \leq a_i < 2\^w/2\\) for \\(0 \leq i < (n-1)\\) and \\(-2\^w/2 \leq a_{n-1} \leq 2\^w/2\\).
/// ///
pub(crate) fn to_radix_2w(&self, w: usize) -> [i8; 43] { pub(crate) fn to_radix_2w(&self, w: usize) -> [i8; 64] {
debug_assert!(w >= 6); debug_assert!(w == 4 || w >= 6);
debug_assert!(w <= 8); debug_assert!(w <= 8);
if w == 4 {
return self.to_radix_16();
}
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
// Scalar formatted as four `u64`s with carry bit packed into the highest bit. // Scalar formatted as four `u64`s with carry bit packed into the highest bit.
@ -1036,7 +1041,7 @@ impl Scalar {
let window_mask: u64 = radix - 1; let window_mask: u64 = radix - 1;
let mut carry = 0u64; let mut carry = 0u64;
let mut digits = [0i8; 43]; let mut digits = [0i8; 64];
let digits_count = (256 + w - 1)/w as usize; let digits_count = (256 + w - 1)/w as usize;
for i in 0..digits_count { for i in 0..digits_count {
// Construct a buffer of bits of the scalar, starting at `bit_offset`. // Construct a buffer of bits of the scalar, starting at `bit_offset`.