Added wNAF scalar multiplication.

This commit is contained in:
Sean Bowe 2017-07-10 00:39:38 -06:00
parent b965c58ac1
commit 021077b56b
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
4 changed files with 200 additions and 4 deletions

View File

@ -496,6 +496,14 @@ macro_rules! curve_impl {
fn to_affine(&self) -> $affine {
(*self).into()
}
fn recommended_wnaf_for_scalar(scalar: <Self::Scalar as PrimeField>::Repr) -> Option<usize> {
Self::empirical_recommended_wnaf_for_scalar(scalar)
}
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
Self::empirical_recommended_wnaf_for_num_scalars(num_scalars)
}
}
// The affine point X, Y is represented in the jacobian
@ -555,8 +563,8 @@ macro_rules! curve_impl {
pub mod g1 {
use rand::{Rand, Rng};
use super::super::{Fq, Fr};
use ::{CurveProjective, CurveAffine, PrimeField, Field, BitIterator};
use super::super::{Fq, Fr, FrRepr};
use ::{CurveProjective, CurveAffine, PrimeField, PrimeFieldRepr, Field, BitIterator};
curve_impl!(G1, G1Affine, G1Prepared, Fq, Fr);
@ -574,6 +582,40 @@ pub mod g1 {
}
}
impl G1 {
fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> Option<usize>
{
const RECOMMENDATIONS: [usize; 3] = [12, 34, 130];
let mut ret = None;
let num_bits = scalar.num_bits() as usize;
for (i, r) in RECOMMENDATIONS.iter().enumerate() {
if *r >= num_bits {
ret = Some(i + 2)
}
}
ret
}
fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize
{
const RECOMMENDATIONS: [usize; 12] = [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569];
let mut ret = 4;
for r in RECOMMENDATIONS.iter() {
if num_scalars > *r {
ret += 1;
} else {
break
}
}
ret
}
}
#[derive(Clone)]
pub struct G1Prepared(pub(crate) G1Affine);
@ -838,8 +880,8 @@ pub mod g1 {
pub mod g2 {
use rand::{Rand, Rng};
use super::super::{Fq2, Fr};
use ::{CurveProjective, CurveAffine, PrimeField, Field, BitIterator};
use super::super::{Fq2, Fr, FrRepr};
use ::{CurveProjective, CurveAffine, PrimeField, PrimeFieldRepr, Field, BitIterator};
curve_impl!(G2, G2Affine, G2Prepared, Fq2, Fr);
@ -866,6 +908,40 @@ pub mod g2 {
}
}
impl G2 {
fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> Option<usize>
{
const RECOMMENDATIONS: [usize; 3] = [13, 37, 103];
let mut ret = None;
let num_bits = scalar.num_bits() as usize;
for (i, r) in RECOMMENDATIONS.iter().enumerate() {
if *r >= num_bits {
ret = Some(i + 2)
}
}
ret
}
fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize
{
const RECOMMENDATIONS: [usize; 11] = [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071];
let mut ret = 4;
for r in RECOMMENDATIONS.iter() {
if num_scalars > *r {
ret += 1;
} else {
break
}
}
ret
}
}
#[derive(Clone)]
pub struct G2Prepared {
pub(crate) coeffs: Vec<(Fq2, Fq2, Fq2)>,

View File

@ -10,6 +10,7 @@ extern crate rand;
pub mod tests;
pub mod bls12_381;
pub mod wnaf;
use std::fmt;
@ -123,6 +124,15 @@ pub trait CurveProjective: PartialEq +
/// Converts this element into its affine representation.
fn to_affine(&self) -> Self::Affine;
/// Recommends a wNAF window table size given a scalar. Returns `None` if normal
/// scalar multiplication is encouraged. If `Some` is returned, it will be between
/// 2 and 22, inclusive.
fn recommended_wnaf_for_scalar(scalar: <Self::Scalar as PrimeField>::Repr) -> Option<usize>;
/// Recommends a wNAF window size given the number of scalars you intend to multiply
/// a base by. Always returns a number between 2 and 22, inclusive.
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize;
}
/// Affine representation of an elliptic curve point guaranteed to be

View File

@ -58,6 +58,32 @@ pub fn curve_tests<G: CurveProjective>()
random_doubling_tests::<G>();
random_negation_tests::<G>();
random_transformation_tests::<G>();
random_wnaf_tests::<G>();
}
fn random_wnaf_tests<G: CurveProjective>() {
use ::wnaf::*;
use ::PrimeField;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut table = vec![];
let mut wnaf = vec![];
for w in 2..14 {
for _ in 0..100 {
let g = G::rand(&mut rng);
let s = G::Scalar::rand(&mut rng).into_repr();
let mut g1 = g;
g1.mul_assign(s);
wnaf_table(&mut table, g, w);
wnaf_form(&mut wnaf, s, w);
let g2 = wnaf_exp(&table, &wnaf);
assert_eq!(g1, g2);
}
}
}
fn random_negation_tests<G: CurveProjective>() {

84
src/wnaf.rs Normal file
View File

@ -0,0 +1,84 @@
use super::{CurveProjective, PrimeFieldRepr};
/// Replaces the contents of `table` with a wNAF window table for the given window size.
///
/// This function will panic if provided a window size below two, or above 22.
pub fn wnaf_table<G: CurveProjective>(table: &mut Vec<G>, mut base: G, window: usize)
{
assert!(window < 23);
assert!(window > 1);
table.truncate(0);
table.reserve(1 << (window-1));
let mut dbl = base;
dbl.double();
for _ in 0..(1 << (window-1)) {
table.push(base);
base.add_assign(&dbl);
}
}
/// Replaces the contents of `wnaf` with the wNAF representation of a scalar.
///
/// This function will panic if provided a window size below two, or above 22.
pub fn wnaf_form<S: PrimeFieldRepr>(wnaf: &mut Vec<i64>, mut c: S, window: usize)
{
assert!(window < 23);
assert!(window > 1);
wnaf.truncate(0);
while !c.is_zero() {
let mut u;
if c.is_odd() {
u = (c.as_ref()[0] % (1 << (window+1))) as i64;
if u > (1 << window) {
u -= 1 << (window+1);
}
if u > 0 {
c.sub_noborrow(&S::from(u as u64));
} else {
c.add_nocarry(&S::from((-u) as u64));
}
} else {
u = 0;
}
wnaf.push(u);
c.div2();
}
}
/// Performs wNAF exponentiation with the provided window table and wNAF-form scalar.
///
/// This function must be provided a `table` and `wnaf` that were constructed with
/// the same window size; otherwise, it may panic or produce invalid results.
pub fn wnaf_exp<G: CurveProjective>(table: &[G], wnaf: &[i64]) -> G
{
let mut result = G::zero();
let mut found_one = false;
for n in wnaf.iter().rev() {
if found_one {
result.double();
}
if *n != 0 {
found_one = true;
if *n > 0 {
result.add_assign(&table[(n/2) as usize]);
} else {
result.sub_assign(&table[((-n)/2) as usize]);
}
}
}
result
}