From 4f29935c2cfb7825ac177a515030321f8e0b0339 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Dec 2019 02:21:48 +0000 Subject: [PATCH 1/9] Create macro for generating different sized LookupTables. --- src/window.rs | 67 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/src/window.rs b/src/window.rs index 3b01422..4afe63e 100644 --- a/src/window.rs +++ b/src/window.rs @@ -27,6 +27,9 @@ use backend::serial::curve_models::AffineNielsPoint; use zeroize::Zeroize; +macro_rules! impl_lookup_table { + (Name = $name:ident, Size = $size:expr, SizeNeg = $neg:expr, SizeRange = $range:expr, ConversionRange = $conv_range:expr) => { + /// A lookup table of precomputed multiples of a point \\(P\\), used to /// compute \\( xP \\) for \\( -8 \leq x \leq 8 \\). /// @@ -37,19 +40,17 @@ use zeroize::Zeroize; /// only `pub(crate)` so that we can write hardcoded constants, so it's /// still technically possible. It would be nice to prevent direct /// access to the table. -/// -/// XXX make this generic with respect to table size #[derive(Copy, Clone)] -pub struct LookupTable(pub(crate) [T; 8]); +pub struct $name(pub(crate) [T; $size]); -impl LookupTable +impl $name where T: Identity + ConditionallySelectable + 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 >= $neg); + debug_assert!(x as i16 <= $size as i16); // XXX We have to convert to i16s here for the radix-256 case.. this is wrong. // Compute xabs = |x| let xmask = x >> 7; @@ -57,9 +58,9 @@ where // Set t = 0 * P = identity let mut t = T::identity(); - for j in 1..9 { + for j in $range { // Copy `points[j-1] == j*P` onto `t` in constant time if `|x| == j`. - let c = (xabs as u8).ct_eq(&(j as u8)); + let c = (xabs as u16).ct_eq(&(j as u16)); t.conditional_assign(&self.0[j - 1], c); } // Now t == |x| * P. @@ -72,48 +73,68 @@ where } } -impl Default for LookupTable { - fn default() -> LookupTable { - LookupTable([T::default(); 8]) +impl Default for $name { + fn default() -> $name { + $name([T::default(); $size]) } } -impl Debug for LookupTable { +impl Debug for $name { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "LookupTable({:?})", self.0) + write!(f, "{:?}(", stringify!($name))?; + + for x in self.0.iter() { + write!(f, "{:?}", x)?; + } + + write!(f, ")") } } -impl<'a> From<&'a EdwardsPoint> for LookupTable { +impl<'a> From<&'a EdwardsPoint> for $name { fn from(P: &'a EdwardsPoint) -> Self { - let mut points = [P.to_projective_niels(); 8]; - for j in 0..7 { + let mut points = [P.to_projective_niels(); $size]; + for j in $conv_range { points[j + 1] = (P + &points[j]).to_extended().to_projective_niels(); } - LookupTable(points) + $name(points) } } -impl<'a> From<&'a EdwardsPoint> for LookupTable { +impl<'a> From<&'a EdwardsPoint> for $name { fn from(P: &'a EdwardsPoint) -> Self { - let mut points = [P.to_affine_niels(); 8]; + let mut points = [P.to_affine_niels(); $size]; // XXX batch inversion would be good if perf mattered here - for j in 0..7 { + for j in $conv_range { points[j + 1] = (P + &points[j]).to_extended().to_affine_niels() } - LookupTable(points) + $name(points) } } -impl Zeroize for LookupTable +impl Zeroize for $name where T: Copy + Default + Zeroize { fn zeroize(&mut self) { - self.0.zeroize(); + for x in self.0.iter_mut() { + x.zeroize(); + } } } +}} // End macro_rules! impl_lookup_table + +// The first one has to be named "LookupTable" because it's used as a constructor for consts. +impl_lookup_table! {Name = LookupTable, Size = 8, SizeNeg = -8, SizeRange = 1 .. 9, ConversionRange = 0 .. 7} // radix-16 +impl_lookup_table! {Name = LookupTableRadix32, Size = 16, SizeNeg = -16, SizeRange = 1 .. 17, ConversionRange = 0 .. 15} // radix-32 +impl_lookup_table! {Name = LookupTableRadix64, Size = 32, SizeNeg = -32, SizeRange = 1 .. 33, ConversionRange = 0 .. 31} // radix-64 +impl_lookup_table! {Name = LookupTableRadix128, Size = 64, SizeNeg = -64, SizeRange = 1 .. 65, ConversionRange = 0 .. 63} // radix-128 +impl_lookup_table! {Name = LookupTableRadix256, Size = 128, SizeNeg = -128, SizeRange = 1 .. 129, ConversionRange = 0 .. 127} // radix-256 + +// For homogeneity we then alias it to "LookupTableRadix16". +pub type LookupTableRadix16 = LookupTable; + /// Holds odd multiples 1A, 3A, ..., 15A of a point A. #[derive(Copy, Clone)] pub(crate) struct NafLookupTable5(pub(crate) [T; 8]); From 383e65f6bd5df67079d40cd42e154fcb1c67216c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Dec 2019 12:54:52 +0000 Subject: [PATCH 2/9] Add a trait for implementing a basepoint table. --- src/traits.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/traits.rs b/src/traits.rs index b024c88..7fa269c 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -47,6 +47,21 @@ where } } +/// A precomputed table of basepoints, for optimising scalar multiplications. +pub trait BasepointTable { + /// The type of point contained within this table. + type Point; + + /// Generate a new precomputed basepoint table from the given basepoint. + fn create(basepoint: &Self::Point) -> Self; + + /// Retrieve the original basepoint from this table. + fn basepoint(&self) -> Self::Point; + + /// Multiply a `scalar` by this precomputed basepoint table, in constant time. + fn basepoint_mul(&self, scalar: &Scalar) -> Self::Point; +} + /// A trait for constant-time multiscalar multiplication without precomputation. pub trait MultiscalarMul { /// The type of point being multiplied, e.g., `RistrettoPoint`. From 8a9e09ba34943002b64b81970b110e9e75fc5466 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Dec 2019 21:32:30 +0000 Subject: [PATCH 3/9] 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 --- src/edwards.rs | 195 +++++++++++++++++++++++++++++++++-------------- src/ristretto.rs | 1 + src/scalar.rs | 15 ++-- 3 files changed, 149 insertions(+), 62 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index 998af8d..40b2b5e 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -117,11 +117,15 @@ use backend::serial::curve_models::CompletedPoint; use backend::serial::curve_models::ProjectiveNielsPoint; use backend::serial::curve_models::ProjectivePoint; -use window::LookupTable; +use window::LookupTableRadix16; +use window::LookupTableRadix64; +use window::LookupTableRadix128; +use window::LookupTableRadix256; #[allow(unused_imports)] use prelude::*; +use traits::BasepointTable; use traits::ValidityCheck; 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 /// fixed-base scalar multiplication. One table, for the Ed25519 /// basepoint, is provided in the `constants` module. /// -/// The basepoint tables are reasonably large (30KB), so they should -/// probably be boxed. +/// The basepoint tables are reasonably large, so they should 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)] -pub struct EdwardsBasepointTable(pub(crate) [LookupTable; 32]); +pub struct $name(pub(crate) [$table; 32]); -impl EdwardsBasepointTable { - /// The computation uses Pippeneger's algorithm, as described on - /// page 13 of the Ed25519 paper. Write the scalar \\(a\\) in radix \\(16\\) with - /// coefficients in \\([-8,8)\\), i.e., +impl BasepointTable for $name { + type Point = $point; + + /// 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 /// $$ /// \begin{aligned} - /// a B = \quad a\_0 16\^0 B +& a\_2 16\^2 B + \cdots + a\_{62} 16\^{62} B \\\\ - /// + a\_1 16\^1 B +& a\_3 16\^3 B + \cdots + a\_{63} 16\^{63} B \\\\ - /// = \quad(a\_0 16\^0 B +& a\_2 16\^2 B + \cdots + a\_{62} 16\^{62} B) \\\\ - /// + 16(a\_1 16\^0 B +& a\_3 16\^2 B + \cdots + a\_{63} 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 w\^1 B +& a\_3 w\^3 B + \cdots + a\_{x-1} w\^{x-1} B \\\\ + /// = \quad(a\_0 w\^0 B +& a\_2 w\^2 B + \cdots + a\_{x-2} w\^{x-2} B) \\\\ + /// + w(a\_1 w\^0 B +& a\_3 w\^2 B + \cdots + a\_{x-1} w\^{x-2} B). \\\\ /// \end{aligned} /// $$ /// 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. - 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 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.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(); } @@ -800,49 +864,53 @@ impl EdwardsBasepointTable { } } -impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsBasepointTable { - type Output = EdwardsPoint; +impl<'a, 'b> Mul<&'b Scalar> for &'a $name { + type Output = $point; /// Construct an `EdwardsPoint` from a `Scalar` \\(a\\) by /// 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 self.basepoint_mul(scalar) } } -impl<'a, 'b> Mul<&'a EdwardsBasepointTable> for &'b Scalar { - type Output = EdwardsPoint; +impl<'a, 'b> Mul<&'a $name> for &'b Scalar { + type Output = $point; /// Construct an `EdwardsPoint` from a `Scalar` \\(a\\) by /// 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 } } -impl EdwardsBasepointTable { - /// Create a table of precomputed multiples of `basepoint`. - pub fn create(basepoint: &EdwardsPoint) -> EdwardsBasepointTable { - // XXX use init_with - let mut table = EdwardsBasepointTable([LookupTable::default(); 32]); - let mut P = *basepoint; +impl Debug for $name { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "{:?}([\n", stringify!($name))?; for i in 0..32 { - // P = (16^2)^i * B - table.0[i] = LookupTable::from(&P); - P = P.mul_by_pow_2(8); + write!(f, "\t{:?},\n", &self.0[i])?; } - table - } - - /// 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() + write!(f, "])") } } +}} // 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 { /// Multiply by the cofactor: return \\([8]P\\). 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 // ------------------------------------------------------------------------ @@ -1148,6 +1206,29 @@ mod test { 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. #[test] fn basepoint_projective_extended_round_trip() { diff --git a/src/ristretto.rs b/src/ristretto.rs index c4b6170..380df18 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -185,6 +185,7 @@ use prelude::*; use scalar::Scalar; +use traits::BasepointTable; use traits::Identity; #[cfg(any(feature = "alloc", feature = "std"))] use traits::{MultiscalarMul, VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul}; diff --git a/src/scalar.rs b/src/scalar.rs index d365d25..3e70367 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -989,10 +989,11 @@ impl Scalar { /// Returns a size hint indicating how many entries of the return /// value of `to_radix_2w` are nonzero. 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); let digits_count = match w { + 4 => (256 + w - 1)/w as usize, 6 => (256 + w - 1)/w as usize, 7 => (256 + w - 1)/w as usize, // See comment in to_radix_2w on handling the terminal carry. @@ -1000,7 +1001,7 @@ impl Scalar { _ => panic!("invalid radix parameter"), }; - debug_assert!(digits_count <= 43); + debug_assert!(digits_count <= 64); 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\\). /// - pub(crate) fn to_radix_2w(&self, w: usize) -> [i8; 43] { - debug_assert!(w >= 6); + pub(crate) fn to_radix_2w(&self, w: usize) -> [i8; 64] { + debug_assert!(w == 4 || w >= 6); debug_assert!(w <= 8); + if w == 4 { + return self.to_radix_16(); + } + use byteorder::{ByteOrder, LittleEndian}; // 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 mut carry = 0u64; - let mut digits = [0i8; 43]; + let mut digits = [0i8; 64]; let digits_count = (256 + w - 1)/w as usize; for i in 0..digits_count { // Construct a buffer of bits of the scalar, starting at `bit_offset`. From cfbcb6ca6167f2f90ffdd34394050e48715d1dd5 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 30 Dec 2019 22:05:01 +0000 Subject: [PATCH 4/9] Implement conversions between basepoint table sizes. This is useful for programs/protocol which can do some heuristics or learning-based approach towards optimising the table size based on the number of uses of e.g. a public key, the second basepoint in a Pedersen commitment, etc., i.e. the first time a public key is used to verify a signature, the usual variable-time basepoint multiscalar multiplication is used, however after 1000 verifications, the table size is upgraded, and again after 10000 verifications, etc. --- src/edwards.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/edwards.rs b/src/edwards.rs index 40b2b5e..b64e4a3 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -911,6 +911,29 @@ impl_basepoint_table! {Name = EdwardsBasepointTableRadix256, LookupTable = Looku // it. pub type EdwardsBasepointTableRadix16 = EdwardsBasepointTable; +macro_rules! impl_basepoint_table_conversions { + (LHS = $lhs:ty, RHS = $rhs:ty) => { + impl<'a> From<&'a $lhs> for $rhs { + fn from(table: &'a $lhs) -> $rhs { + <$rhs>::create(&table.basepoint()) + } + } + + impl<'a> From<&'a $rhs> for $lhs { + fn from(table: &'a $rhs) -> $lhs { + <$lhs>::create(&table.basepoint()) + } + } + } +} + +impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix64} +impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix128} +impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix256} +impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix64, RHS = EdwardsBasepointTableRadix128} +impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix64, RHS = EdwardsBasepointTableRadix256} +impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix128, RHS = EdwardsBasepointTableRadix256} + impl EdwardsPoint { /// Multiply by the cofactor: return \\([8]P\\). pub fn mul_by_cofactor(&self) -> EdwardsPoint { From c01bd780dca04602f2d3fc18d8cb1aacb1daf1f6 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 31 Dec 2019 00:12:20 +0000 Subject: [PATCH 5/9] Implement radix-32 precomputed scalar multiplication tables. --- src/edwards.rs | 14 +++++++++++++- src/scalar.rs | 8 ++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index b64e4a3..3f21e96 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -118,6 +118,7 @@ use backend::serial::curve_models::ProjectiveNielsPoint; use backend::serial::curve_models::ProjectivePoint; use window::LookupTableRadix16; +use window::LookupTableRadix32; use window::LookupTableRadix64; use window::LookupTableRadix128; use window::LookupTableRadix256; @@ -899,6 +900,7 @@ impl Debug for $name { // 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 = EdwardsBasepointTableRadix32, LookupTable = LookupTableRadix32, Point = EdwardsPoint, Radix = 5, Additions = 52} 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} @@ -927,11 +929,18 @@ macro_rules! impl_basepoint_table_conversions { } } +impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix32} impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix64} impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix128} impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix256} + +impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix32, RHS = EdwardsBasepointTableRadix64} +impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix32, RHS = EdwardsBasepointTableRadix128} +impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix32, RHS = EdwardsBasepointTableRadix256} + impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix64, RHS = EdwardsBasepointTableRadix128} impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix64, RHS = EdwardsBasepointTableRadix256} + impl_basepoint_table_conversions!{LHS = EdwardsBasepointTableRadix128, RHS = EdwardsBasepointTableRadix256} impl EdwardsPoint { @@ -1236,18 +1245,21 @@ mod test { let a = A_SCALAR; let table_radix16 = EdwardsBasepointTableRadix16::create(&P); + let table_radix32 = EdwardsBasepointTableRadix32::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 aP32 = (&table_radix32 * &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!(aP16, aP32); + assert_eq!(aP32, aP64); assert_eq!(aP64, aP128); assert_eq!(aP128, aP256); } diff --git a/src/scalar.rs b/src/scalar.rs index 3e70367..21b801c 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -994,6 +994,7 @@ impl Scalar { let digits_count = match w { 4 => (256 + w - 1)/w as usize, + 5 => (256 + w - 1)/w as usize, 6 => (256 + w - 1)/w as usize, 7 => (256 + w - 1)/w as usize, // See comment in to_radix_2w on handling the terminal carry. @@ -1005,14 +1006,13 @@ impl Scalar { digits_count } - /// Creates a representation of a Scalar in radix 64, 128 or 256 for use with the Pippenger algorithm. + /// Creates a representation of a Scalar in radix 32, 64, 128 or 256 for use with the Pippenger algorithm. /// For lower radix, use `to_radix_16`, which is used by the Straus multi-scalar multiplication. /// Higher radixes are not supported to save cache space. Radix 256 is near-optimal even for very /// large inputs. /// - /// Radix below 64 or above 256 is prohibited. + /// Radix below 32 or above 256 is prohibited. /// This method returns digits in a fixed-sized array, excess digits are zeroes. - /// The second returned value is the number of digits. /// /// ## Scalar representation /// @@ -1024,7 +1024,7 @@ 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\\). /// pub(crate) fn to_radix_2w(&self, w: usize) -> [i8; 64] { - debug_assert!(w == 4 || w >= 6); + debug_assert!(w >= 4); debug_assert!(w <= 8); if w == 4 { From ca1f730790957c2ea938951def365895136d020d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 3 Jan 2020 23:56:30 +0000 Subject: [PATCH 6/9] Fix debug_assert! range in Scalar::to_radix_2w_size_hint(). --- src/scalar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scalar.rs b/src/scalar.rs index 21b801c..13a5ea6 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -989,7 +989,7 @@ impl Scalar { /// Returns a size hint indicating how many entries of the return /// value of `to_radix_2w` are nonzero. pub(crate) fn to_radix_2w_size_hint(w: usize) -> usize { - debug_assert!(w == 4 || w >= 6); + debug_assert!(w >= 4); debug_assert!(w <= 8); let digits_count = match w { From 1f8a19a7539217485613d9396a84dcc755f753b9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 7 Jan 2020 08:04:45 +0000 Subject: [PATCH 7/9] Fix an attempted overflow on absolute value computation for radix-256 tables. Found by the fuzzer in 4f5d2d4. --- src/window.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/window.rs b/src/window.rs index 4afe63e..adedac9 100644 --- a/src/window.rs +++ b/src/window.rs @@ -53,8 +53,8 @@ where debug_assert!(x as i16 <= $size as i16); // XXX We have to convert to i16s here for the radix-256 case.. this is wrong. // Compute xabs = |x| - let xmask = x >> 7; - let xabs = (x + xmask) ^ xmask; + let xmask = x as i16 >> 7; + let xabs = (x as i16 + xmask) ^ xmask; // Set t = 0 * P = identity let mut t = T::identity(); From e82910d215aeb4ba90d689f3253dac4f5a2e2d91 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 7 Jan 2020 08:06:27 +0000 Subject: [PATCH 8/9] Add test for basepoint table multiplication by unreduced scalar. --- src/edwards.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/edwards.rs b/src/edwards.rs index 3f21e96..09eaa56 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -1264,6 +1264,37 @@ mod test { assert_eq!(aP128, aP256); } + // Check a unreduced scalar multiplication by the basepoint tables. + #[test] + fn basepoint_tables_unreduced_scalar() { + let P = &constants::ED25519_BASEPOINT_POINT; + let a = Scalar::from_bits([ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + ]); + + let table_radix16 = EdwardsBasepointTableRadix16::create(&P); + let table_radix32 = EdwardsBasepointTableRadix32::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 aP32 = (&table_radix32 * &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, aP32); + assert_eq!(aP32, aP64); + assert_eq!(aP64, aP128); + assert_eq!(aP128, aP256); + } + /// Check that converting to projective and then back to extended round-trips. #[test] fn basepoint_projective_extended_round_trip() { From 0da8f08d6582cbc35af6ef1da0e7917d4f5ddfed Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 13 Apr 2021 01:24:06 +0000 Subject: [PATCH 9/9] Maintain legacy 3.x support for lookup tables. --- src/edwards.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++--- src/ristretto.rs | 1 - 2 files changed, 124 insertions(+), 9 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index ba82da3..7c97ca4 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -118,6 +118,7 @@ use backend::serial::curve_models::CompletedPoint; use backend::serial::curve_models::ProjectiveNielsPoint; use backend::serial::curve_models::ProjectivePoint; +use window::LookupTable; use window::LookupTableRadix16; use window::LookupTableRadix32; use window::LookupTableRadix64; @@ -900,19 +901,134 @@ impl Debug for $name { }} // 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 = EdwardsBasepointTableRadix16, LookupTable = LookupTableRadix16, Point = EdwardsPoint, Radix = 4, Additions = 64} impl_basepoint_table! {Name = EdwardsBasepointTableRadix32, LookupTable = LookupTableRadix32, Point = EdwardsPoint, Radix = 5, Additions = 52} 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; +// ------------------------------------------------------------------------------------- +// BEGIN legacy 3.x series code for backwards compatibility with BasepointTable trait +// ------------------------------------------------------------------------------------- + +/// A precomputed table of multiples of a basepoint, for accelerating +/// fixed-base scalar multiplication. One table, for the Ed25519 +/// basepoint, is provided in the `constants` module. +/// +/// The basepoint tables are reasonably large, so they should 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)] +pub struct EdwardsBasepointTable(pub(crate) [LookupTable; 32]); + +impl EdwardsBasepointTable { + /// Create a table of precomputed multiples of `basepoint`. + #[allow(warnings)] + pub fn create(basepoint: &EdwardsPoint) -> EdwardsBasepointTable { + Self(EdwardsBasepointTableRadix16::create(basepoint).0) + } + + /// The computation uses Pippenger's algorithm, as described on + /// page 13 of the Ed25519 paper. Write the scalar \\(a\\) in radix \\(16\\) with + /// coefficients in \\([-8,8)\\), i.e., + /// $$ + /// a = a\_0 + a\_1 16\^1 + \cdots + a\_{63} 16\^{63}, + /// $$ + /// with \\(-8 \leq a_i < 8\\), \\(-8 \leq a\_{63} \leq 8\\). Then + /// $$ + /// a B = a\_0 B + a\_1 16\^1 B + \cdots + a\_{63} 16\^{63} B. + /// $$ + /// Grouping even and odd coefficients gives + /// $$ + /// \begin{aligned} + /// a B = \quad a\_0 16\^0 B +& a\_2 16\^2 B + \cdots + a\_{62} 16\^{62} B \\\\ + /// + a\_1 16\^1 B +& a\_3 16\^3 B + \cdots + a\_{63} 16\^{63} B \\\\ + /// = \quad(a\_0 16\^0 B +& a\_2 16\^2 B + \cdots + a\_{62} 16\^{62} B) \\\\ + /// + 16(a\_1 16\^0 B +& a\_3 16\^2 B + \cdots + a\_{63} 16\^{62} B). \\\\ + /// \end{aligned} + /// $$ + /// For each \\(i = 0 \ldots 31\\), we create a lookup table of + /// $$ + /// [16\^{2i} B, \ldots, 8\cdot16\^{2i} B], + /// $$ + /// and use it to select \\( x \cdot 16\^{2i} \cdot B \\) in constant time. + /// + /// The radix-\\(16\\) representation requires that the scalar is bounded + /// by \\(2\^{255}\\), which is always the case. + #[allow(warnings)] + pub fn basepoint_mul(&self, scalar: &Scalar) -> EdwardsPoint { + let a = scalar.to_radix_16(); + + let tables = &self.0; + let mut P = EdwardsPoint::identity(); + + for i in (0..64).filter(|x| x % 2 == 1) { + P = (&P + &tables[i/2].select(a[i])).to_extended(); + } + + P = P.mul_by_pow_2(4); + + for i in (0..64).filter(|x| x % 2 == 0) { + P = (&P + &tables[i/2].select(a[i])).to_extended(); + } + + P + } + + /// Get the basepoint for this table as an `EdwardsPoint`. + #[allow(warnings)] + pub fn basepoint(&self) -> EdwardsPoint { + (&EdwardsPoint::identity() + &self.0[0].select(1)).to_extended() + } +} + +impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsBasepointTable { + type Output = EdwardsPoint; + + /// Construct an `EdwardsPoint` from a `Scalar` \\(a\\) by + /// computing the multiple \\(aB\\) of this basepoint \\(B\\). + fn mul(self, scalar: &'b Scalar) -> EdwardsPoint { + // delegate to a private function so that its documentation appears in internal docs + self.basepoint_mul(scalar) + } +} + +impl<'a, 'b> Mul<&'a EdwardsBasepointTable> for &'b Scalar { + type Output = EdwardsPoint; + + /// Construct an `EdwardsPoint` from a `Scalar` \\(a\\) by + /// computing the multiple \\(aB\\) of this basepoint \\(B\\). + fn mul(self, basepoint_table: &'a EdwardsBasepointTable) -> EdwardsPoint { + basepoint_table * self + } +} + +// ------------------------------------------------------------------------------------- +// END legacy 3.x series code for backwards compatibility with BasepointTable trait +// ------------------------------------------------------------------------------------- macro_rules! impl_basepoint_table_conversions { (LHS = $lhs:ty, RHS = $rhs:ty) => { diff --git a/src/ristretto.rs b/src/ristretto.rs index d5cceb2..d284b5b 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -186,7 +186,6 @@ use prelude::*; use scalar::Scalar; -use traits::BasepointTable; use traits::Identity; #[cfg(any(feature = "alloc", feature = "std"))] use traits::{MultiscalarMul, VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul};