Merge pull request #127 from hdevalence/feature/generalize-naf

Generalize NAF code to wider window sizes.
This commit is contained in:
Henry de Valence 2018-04-08 16:39:45 -07:00 committed by GitHub
commit 6bb2c02a1f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 3589 additions and 140 deletions

View file

@ -41,6 +41,7 @@ harness = false
# match exactly, since the build.rs uses the crate itself as a library.
[dependencies]
byteorder = "1"
digest = "0.7"
generic-array = "0.9"
clear_on_drop = "=0.2.3"
@ -49,6 +50,7 @@ serde = { version = "1.0", optional = true }
rand = { version = "0.4", optional = true }
[build-dependencies]
byteorder = "1"
digest = "0.7"
generic-array = "0.9"
clear_on_drop = "=0.2.3"

View file

@ -5,6 +5,7 @@
#![allow(non_snake_case)]
#![allow(dead_code)]
extern crate byteorder;
extern crate clear_on_drop;
extern crate core;
extern crate digest;
@ -58,7 +59,7 @@ mod scalar_mul;
use edwards::EdwardsBasepointTable;
use curve_models::AffineNielsPoint;
use scalar_mul::window::OddLookupTable;
use scalar_mul::window::NafLookupTable8;
fn main() {
// Enable the "precomputed_tables" feature in the main build stage
@ -85,7 +86,7 @@ use edwards::EdwardsBasepointTable;
use curve_models::AffineNielsPoint;
use scalar_mul::window::LookupTable;
use scalar_mul::window::OddLookupTable;
use scalar_mul::window::NafLookupTable8;
/// Table containing precomputed multiples of the Ed25519 basepoint \\\\(B = (x, 4/5)\\\\).
pub const ED25519_BASEPOINT_TABLE: EdwardsBasepointTable = ED25519_BASEPOINT_TABLE_INNER_DOC_HIDDEN;
@ -100,13 +101,13 @@ pub const ED25519_BASEPOINT_TABLE_INNER_DOC_HIDDEN: EdwardsBasepointTable = {:?}
// Now generate AFFINE_ODD_MULTIPLES_OF_BASEPOINT
let B = &constants::ED25519_BASEPOINT_POINT;
let odd_multiples = OddLookupTable::<AffineNielsPoint>::from(B);
let odd_multiples = NafLookupTable8::<AffineNielsPoint>::from(B);
f.write_all(
format!(
"\n
/// Odd multiples of the basepoint `[B, 3B, 5B, 7B, 9B, 11B, 13B, 15B]`.
pub(crate) const AFFINE_ODD_MULTIPLES_OF_BASEPOINT: OddLookupTable<AffineNielsPoint> = {:?};
/// Odd multiples of the basepoint `[B, 3B, 5B, 7B, 9B, 11B, 13B, 15B, ..., 127B]`.
pub(crate) const AFFINE_ODD_MULTIPLES_OF_BASEPOINT: NafLookupTable8<AffineNielsPoint> = {:?};
\n\n",
&odd_multiples
).as_bytes(),

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,7 @@ use subtle::ConditionallyAssignable;
use subtle::Choice;
use edwards;
use scalar_mul::window::{LookupTable, OddLookupTable};
use scalar_mul::window::{LookupTable, NafLookupTable5, NafLookupTable8};
use traits::Identity;
@ -294,7 +294,7 @@ impl<'a> From<&'a edwards::EdwardsPoint> for LookupTable<CachedPoint> {
}
}
impl<'a> From<&'a edwards::EdwardsPoint> for OddLookupTable<CachedPoint> {
impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable5<CachedPoint> {
fn from(point: &'a edwards::EdwardsPoint) -> Self {
let A = ExtendedPoint::from(*point);
let mut Ai = [CachedPoint::from(A); 8];
@ -303,7 +303,20 @@ impl<'a> From<&'a edwards::EdwardsPoint> for OddLookupTable<CachedPoint> {
Ai[i + 1] = (&A2 + &Ai[i]).into();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A]
OddLookupTable(Ai)
NafLookupTable5(Ai)
}
}
impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable8<CachedPoint> {
fn from(point: &'a edwards::EdwardsPoint) -> Self {
let A = ExtendedPoint::from(*point);
let mut Ai = [CachedPoint::from(A); 64];
let A2 = A.double();
for i in 0..63 {
Ai[i + 1] = (&A2 + &Ai[i]).into();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A, ..., 127A]
NafLookupTable8(Ai)
}
}

View file

@ -12,14 +12,14 @@
use traits::Identity;
use scalar::Scalar;
use edwards::EdwardsPoint;
use scalar_mul::window::OddLookupTable;
use scalar_mul::window::NafLookupTable5;
use backend::avx2::edwards::{CachedPoint, ExtendedPoint};
use backend::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE;
/// 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();
let b_naf = b.non_adjacent_form();
let a_naf = a.non_adjacent_form(5);
let b_naf = b.non_adjacent_form(8);
// Find starting index
let mut i: usize = 255;
@ -30,7 +30,7 @@ pub fn mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
}
}
let table_A = OddLookupTable::<CachedPoint>::from(A);
let table_A = NafLookupTable5::<CachedPoint>::from(A);
let table_B = &BASEPOINT_ODD_LOOKUP_TABLE;
let mut Q = ExtendedPoint::identity();

View file

@ -14,7 +14,7 @@ use core::borrow::Borrow;
use traits::Identity;
use scalar::Scalar;
use edwards::EdwardsPoint;
use scalar_mul::window::OddLookupTable;
use scalar_mul::window::NafLookupTable5;
use backend::avx2::edwards::{CachedPoint, ExtendedPoint};
/// Perform variable-time, variable-base scalar multiplication.
@ -27,11 +27,11 @@ where
{
let nafs: Vec<_> = scalars
.into_iter()
.map(|c| c.borrow().non_adjacent_form())
.map(|c| c.borrow().non_adjacent_form(5))
.collect();
let lookup_tables: Vec<_> = points
.into_iter()
.map(|point| OddLookupTable::<CachedPoint>::from(point.borrow()))
.map(|point| NafLookupTable5::<CachedPoint>::from(point.borrow()))
.collect();
let mut Q = ExtendedPoint::identity();

View file

@ -44,6 +44,8 @@ extern crate alloc;
extern crate clear_on_drop;
extern crate byteorder;
// The `Digest` trait is implemented using `generic_array`, so we need it
// too. Hopefully we can eliminate `generic_array` from `Digest` once const
// generics land.

View file

@ -80,6 +80,7 @@ pub struct Scalar {
///
/// The integer representing this scalar must be bounded above by \\(2\^{255}\\), or equivalently the high bit of `bytes[31]` must be zero.
///
/// This ensures that there is room for a carry bit when computing a NAF representation.
// XXX This is pub(crate) so we can write literal constants. If const fns were stable, we could make the Scalar constructors const fns and use those instead.
pub(crate) bytes: [u8; 32],
}
@ -493,51 +494,130 @@ impl Scalar {
bits
}
/// Compute a width-5 "Non-Adjacent Form" of this scalar.
/// Compute a width-\\(w\\) "Non-Adjacent Form" of this scalar.
///
/// A width-\\(w\\) NAF of a positive integer \\(k\\) is an expression
/// $$
/// k = \sum_{i=0}\^n k\_i 2\^i,
/// k = \sum_{i=0}\^m n\_i 2\^i,
/// $$
/// where each nonzero
/// coefficient \\(k\_i\\) is odd and bounded by \\(|k\_i| < 2\^{w-1}\\),
/// \\(k\_{n-1}\\) is nonzero, and at most one of any \\(w\\) consecutive
/// coefficient \\(n\_i\\) is odd and bounded by \\(|n\_i| < 2\^{w-1}\\),
/// \\(n\_{m-1}\\) is nonzero, and at most one of any \\(w\\) consecutive
/// coefficients is nonzero. (Hankerson, Menezes, Vanstone; def 3.32).
///
/// The length of the NAF is at most one more than the length of
/// the binary representation of \\(k\\). This is why the
/// `Scalar` type maintains an invariant that the top bit is
/// \\(0\\), so that the NAF of a scalar has at most 256 digits.
///
/// Intuitively, this is like a binary expansion, except that we
/// allow some coefficients to grow up to \\(2\^{w-1}\\) so that the
/// nonzero coefficients are as sparse as possible.
pub(crate) fn non_adjacent_form(&self) -> [i8; 256] {
// Step 1: write out bits of the scalar
let mut naf = self.bits();
/// allow some coefficients to grow in magnitude up to
/// \\(2\^{w-1}\\) so that the nonzero coefficients are as sparse
/// as possible.
///
/// When doing scalar multiplication, we can then use a lookup
/// table of precomputed multiples of a point to add the nonzero
/// terms \\( k_i P \\). Using signed digits cuts the table size
/// in half, and using odd digits cuts the table size in half
/// again.
///
/// To compute a \\(w\\)-NAF, we use a modification of Algorithm 3.35 of HMV:
///
/// 1. \\( i \gets 0 \\)
/// 2. While \\( k \ge 1 \\):
/// 1. If \\(k\\) is odd, \\( n_i \gets k \operatorname{mods} 2^w \\), \\( k \gets k - n_i \\).
/// 2. If \\(k\\) is even, \\( n_i \gets 0 \\).
/// 3. \\( k \gets k / 2 \\), \\( i \gets i + 1 \\).
/// 3. Return \\( n_0, n_1, ... , \\)
///
/// Here \\( \bar x = x \operatorname{mods} 2^w \\) means the
/// \\( \bar x \\) with \\( \bar x \equiv x \pmod{2^w} \\) and
/// \\( -2^{w-1} \leq \bar x < 2^w \\).
///
/// We implement this by scanning across the bits of \\(k\\) from
/// least-significant bit to most-significant-bit.
/// Write the bits of \\(k\\) as
/// $$
/// k = \sum\_{i=0}\^m k\_i 2^i,
/// $$
/// and split the sum as
/// $$
/// k = \sum\_{i=0}^{w-1} k\_i 2^i + 2^w \sum\_{i=0} k\_{i+w} 2^i
/// $$
/// where the first part is \\( k \mod 2^w \\).
///
/// If \\( k \mod 2^w\\) is odd, and \\( k \mod 2^w < 2^{w-1} \\), then we emit
/// \\( n_0 = k \mod 2^w \\). Instead of computing
/// \\( k - n_0 \\), we just advance \\(w\\) bits and reindex.
///
/// If \\( k \mod 2^w\\) is odd, and \\( k \mod 2^w \ge 2^{w-1} \\), then
/// \\( n_0 = k \operatorname{mods} 2^w = k \mod 2^w - 2^w \\).
/// The quantity \\( k - n_0 \\) is
/// $$
/// \begin{aligned}
/// k - n_0 &= \sum\_{i=0}^{w-1} k\_i 2^i + 2^w \sum\_{i=0} k\_{i+w} 2^i
/// - \sum\_{i=0}^{w-1} k\_i 2^i + 2^w \\\\
/// &= 2^w + 2^w \sum\_{i=0} k\_{i+w} 2^i
/// \end{aligned}
/// $$
/// so instead of computing the subtraction, we can set a carry
/// bit, advance \\(w\\) bits, and reindex.
///
/// If \\( k \mod 2^w\\) is even, we emit \\(0\\), advance 1 bit
/// and reindex. In fact, by setting all digits to \\(0\\)
/// initially, we don't need to emit anything.
pub(crate) fn non_adjacent_form(&self, w: usize) -> [i8; 256] {
// required by the NAF definition
debug_assert!( w >= 2 );
// required so that the NAF digits fit in i8
debug_assert!( w <= 8 );
// Step 2: zero coefficients by carrying them upwards or downwards
'bits: for i in 0..256 {
if naf[i] == 0 { continue 'bits; }
'window: for b in 1..6 {
if i+b >= 256 { break 'window; }
if naf[i+b] == 0 { continue 'window; }
let potential_carry = naf[i+b] << b;
if naf[i+b] + potential_carry <= 15 {
// Eliminate naf[i+b] by carrying its value onto naf[i]
naf[i] += potential_carry;
naf[i+b] = 0;
} else if naf[i+b] - potential_carry >= -15 {
// Eliminate naf[i+b] by carrying its value upwards.
naf[i] -= potential_carry; // Subtract 2^(i+b)
'carry: for k in i+b..256 {
if naf[k] != 0 {
// Since naf[k] = 0 or 1 for k > i, naf[k] == 1.
naf[k] = 0; // Subtract 2^k
} else {
// By now we have subtracted 2^k =
// 2^(i+b) + 2^(i+b) + 2^(i+b+1) + ... + 2^(k-1).
naf[k] = 1; // Add back 2^k.
break 'carry;
}
}
}
use byteorder::{ByteOrder, LittleEndian};
let mut naf = [0i8; 256];
let mut x_u64 = [0u64; 5];
LittleEndian::read_u64_into(&self.bytes, &mut x_u64[0..4]);
let width = 1 << w;
let window_mask = width - 1;
let mut pos = 0;
let mut carry = 0;
while pos < 256 {
// Construct a buffer of bits of the scalar, starting at bit `pos`
let u64_idx = pos / 64;
let bit_idx = pos % 64;
let bit_buf: u64;
if bit_idx < 64 - w {
// This window's bits are contained in a single u64
bit_buf = x_u64[u64_idx] >> bit_idx;
} else {
// Combine the current u64's bits with the bits from the next u64
bit_buf = (x_u64[u64_idx] >> bit_idx) | (x_u64[1+u64_idx] << (64 - bit_idx));
}
// Add the carry into the current window
let window = carry + (bit_buf & window_mask);
if window & 1 == 0 {
// If the window value is even, preserve the carry and continue.
// Why is the carry preserved?
// If carry == 0 and window & 1 == 0, then the next carry should be 0
// If carry == 1 and window & 1 == 0, then bit_buf & 1 == 1 so the next carry should be 1
pos += 1;
continue;
}
if window < width/2 {
carry = 0;
naf[pos] = window as i8;
} else {
carry = 1;
naf[pos] = (window as i8) - (width as i8);
}
pos += w;
}
naf
@ -548,7 +628,7 @@ impl Scalar {
/// $$
/// a = a\_0 + a\_1 16\^1 + \cdots + a_{63} 16\^{63},
/// $$
/// with \\(-8 \leq a_i < 8\\) for \\(0 \leq i < 63\\) and \\(-8 \leq a_63 \leq 8\\).
/// with \\(-8 \leq a_i < 8\\) for \\(0 \leq i < 63\\) and \\(-8 \leq a_{63} \leq 8\\).
pub(crate) fn to_radix_16(&self) -> [i8; 64] {
debug_assert!(self[31] <= 127);
let mut output = [0i8; 64];
@ -789,7 +869,7 @@ mod test {
#[test]
fn non_adjacent_form() {
let naf = A_SCALAR.non_adjacent_form();
let naf = A_SCALAR.non_adjacent_form(5);
for i in 0..256 {
assert_eq!(naf[i], A_NAF[i]);
}

View file

@ -14,12 +14,12 @@ use traits::Identity;
use scalar::Scalar;
use edwards::EdwardsPoint;
use curve_models::{ProjectiveNielsPoint, ProjectivePoint};
use scalar_mul::window::OddLookupTable;
use scalar_mul::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();
let b_naf = b.non_adjacent_form();
let a_naf = a.non_adjacent_form(5);
let b_naf = b.non_adjacent_form(8);
// Find starting index
let mut i: usize = 255;
@ -30,7 +30,7 @@ pub fn mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
}
}
let table_A = OddLookupTable::<ProjectiveNielsPoint>::from(A);
let table_A = NafLookupTable5::<ProjectiveNielsPoint>::from(A);
let table_B = &constants::AFFINE_ODD_MULTIPLES_OF_BASEPOINT;
let mut r = ProjectivePoint::identity();

View file

@ -15,7 +15,7 @@ use traits::Identity;
use scalar::Scalar;
use edwards::EdwardsPoint;
use curve_models::{CompletedPoint, ProjectivePoint, ProjectiveNielsPoint};
use scalar_mul::window::OddLookupTable;
use scalar_mul::window::NafLookupTable5;
/// Perform variable-time, variable-base scalar multiplication.
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
@ -27,11 +27,11 @@ where
{
let nafs: Vec<_> = scalars
.into_iter()
.map(|c| c.borrow().non_adjacent_form())
.map(|c| c.borrow().non_adjacent_form(5))
.collect();
let lookup_tables: Vec<_> = points
.into_iter()
.map(|P| OddLookupTable::<ProjectiveNielsPoint>::from(P.borrow()))
.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(P.borrow()))
.collect();
let mut r = ProjectivePoint::identity();

View file

@ -122,9 +122,9 @@ impl<'a> From<&'a EdwardsPoint> for LookupTable<AffineNielsPoint> {
/// Holds odd multiples 1A, 3A, ..., 15A of a point A.
#[derive(Copy, Clone)]
pub(crate) struct OddLookupTable<T>(pub(crate) [T; 8]);
pub(crate) struct NafLookupTable5<T>(pub(crate) [T; 8]);
impl<T: Copy> OddLookupTable<T> {
impl<T: Copy> NafLookupTable5<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);
@ -134,13 +134,13 @@ impl<T: Copy> OddLookupTable<T> {
}
}
impl<T: Debug> Debug for OddLookupTable<T> {
impl<T: Debug> Debug for NafLookupTable5<T> {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "OddLookupTable({:?})", self.0)
write!(f, "NafLookupTable5({:?})", self.0)
}
}
impl<'a> From<&'a EdwardsPoint> for OddLookupTable<ProjectiveNielsPoint> {
impl<'a> From<&'a EdwardsPoint> for NafLookupTable5<ProjectiveNielsPoint> {
fn from(A: &'a EdwardsPoint) -> Self {
let mut Ai = [A.to_projective_niels(); 8];
let A2 = A.double();
@ -148,11 +148,11 @@ impl<'a> From<&'a EdwardsPoint> for OddLookupTable<ProjectiveNielsPoint> {
Ai[i + 1] = (&A2 + &Ai[i]).to_extended().to_projective_niels();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A]
OddLookupTable(Ai)
NafLookupTable5(Ai)
}
}
impl<'a> From<&'a EdwardsPoint> for OddLookupTable<AffineNielsPoint> {
impl<'a> From<&'a EdwardsPoint> for NafLookupTable5<AffineNielsPoint> {
fn from(A: &'a EdwardsPoint) -> Self {
let mut Ai = [A.to_affine_niels(); 8];
let A2 = A.double();
@ -160,6 +160,53 @@ impl<'a> From<&'a EdwardsPoint> for OddLookupTable<AffineNielsPoint> {
Ai[i + 1] = (&A2 + &Ai[i]).to_extended().to_affine_niels();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A]
OddLookupTable(Ai)
NafLookupTable5(Ai)
}
}
/// Holds stuff up to 8.
#[derive(Copy, Clone)]
pub(crate) struct NafLookupTable8<T>(pub(crate) [T; 64]);
impl<T: Copy> NafLookupTable8<T> {
pub fn select(&self, x: usize) -> T {
debug_assert_eq!(x & 1, 1);
debug_assert!(x < 256);
self.0[x / 2]
}
}
impl<T: Debug> Debug for NafLookupTable8<T> {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "NafLookupTable8([\n")?;
for i in 0..64 {
write!(f, "\t{:?},\n", &self.0[i])?;
}
write!(f, "])")
}
}
impl<'a> From<&'a EdwardsPoint> for NafLookupTable8<ProjectiveNielsPoint> {
fn from(A: &'a EdwardsPoint) -> Self {
let mut Ai = [A.to_projective_niels(); 64];
let A2 = A.double();
for i in 0..63 {
Ai[i + 1] = (&A2 + &Ai[i]).to_extended().to_projective_niels();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A, ..., 127A]
NafLookupTable8(Ai)
}
}
impl<'a> From<&'a EdwardsPoint> for NafLookupTable8<AffineNielsPoint> {
fn from(A: &'a EdwardsPoint) -> Self {
let mut Ai = [A.to_affine_niels(); 64];
let A2 = A.double();
for i in 0..63 {
Ai[i + 1] = (&A2 + &Ai[i]).to_extended().to_affine_niels();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A, ..., 127A]
NafLookupTable8(Ai)
}
}