diff --git a/Cargo.toml b/Cargo.toml index b9beff6..ae2eed5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ byteorder = "1.1.0" clippy = { version = "0.0.151", optional = true } [features] -unstable-wnaf = [] -unstable-features = ["unstable-wnaf"] +unstable-features = [] u128-support = [] default = ["u128-support"] diff --git a/src/bls12_381/ec.rs b/src/bls12_381/ec.rs index f2aefa2..f441cca 100644 --- a/src/bls12_381/ec.rs +++ b/src/bls12_381/ec.rs @@ -518,7 +518,7 @@ macro_rules! curve_impl { (*self).into() } - fn recommended_wnaf_for_scalar(scalar: ::Repr) -> Option { + fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize { Self::empirical_recommended_wnaf_for_scalar(scalar) } @@ -859,20 +859,17 @@ pub mod g1 { } impl G1 { - fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> Option + fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> 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) - } + if num_bits >= 130 { + 4 + } else if num_bits >= 34 { + 3 + } else { + 2 } - - ret } fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize @@ -1398,20 +1395,17 @@ pub mod g2 { } impl G2 { - fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> Option + fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> 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) - } + if num_bits >= 103 { + 4 + } else if num_bits >= 37 { + 3 + } else { + 2 } - - ret } fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize diff --git a/src/lib.rs b/src/lib.rs index 5790c04..a3c4a2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ #![cfg_attr(feature = "clippy", allow(inline_always))] #![cfg_attr(feature = "clippy", allow(too_many_arguments))] #![cfg_attr(feature = "clippy", allow(unreadable_literal))] +#![cfg_attr(feature = "clippy", allow(new_without_default_derive))] // Force public structures to implement Debug #![deny(missing_debug_implementations)] @@ -24,8 +25,8 @@ pub mod tests; pub mod bls12_381; -#[cfg(feature = "unstable-wnaf")] -pub mod wnaf; +mod wnaf; +pub use self::wnaf::Wnaf; use std::fmt; use std::error::Error; @@ -144,10 +145,9 @@ pub trait CurveProjective: PartialEq + /// Converts this element into its affine representation. fn into_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: ::Repr) -> Option; + /// Recommends a wNAF window table size given a scalar. Always returns a number + /// between 2 and 22, inclusive. + fn recommended_wnaf_for_scalar(scalar: ::Repr) -> 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. diff --git a/src/tests/curve.rs b/src/tests/curve.rs index e3eb0dc..e6deec1 100644 --- a/src/tests/curve.rs +++ b/src/tests/curve.rs @@ -1,4 +1,4 @@ -use rand::{SeedableRng, XorShiftRng, Rand}; +use rand::{SeedableRng, XorShiftRng, Rand, Rng}; use ::{CurveProjective, CurveAffine, Field, EncodedPoint}; @@ -62,31 +62,115 @@ pub fn curve_tests() random_encoding_tests::(); } -#[cfg(not(feature = "unstable-wnaf"))] -fn random_wnaf_tests() { } - -#[cfg(feature = "unstable-wnaf")] fn random_wnaf_tests() { use ::wnaf::*; use ::PrimeField; let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - let mut table = vec![]; - let mut wnaf = vec![]; + { + 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 only_compiles_if_send(_: &S) { } - 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); + let g2 = { + let mut wnaf = Wnaf::new(); + wnaf.base(g, 1).scalar(s) + }; + let g3 = { + let mut wnaf = Wnaf::new(); + wnaf.scalar(s).base(g) + }; + let g4 = { + let mut wnaf = Wnaf::new(); + let mut shared = wnaf.base(g, 1).shared(); + + only_compiles_if_send(&shared); + + shared.scalar(s) + }; + let g5 = { + let mut wnaf = Wnaf::new(); + let mut shared = wnaf.scalar(s).shared(); + + only_compiles_if_send(&shared); + + shared.base(g) + }; + + let g6 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + wnaf.base(g, 1).scalar(s) + }; + let g7 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + wnaf.scalar(s).base(g) + }; + let g8 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + let mut shared = wnaf.base(g, 1).shared(); + + only_compiles_if_send(&shared); + + shared.scalar(s) + }; + let g9 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + let mut shared = wnaf.scalar(s).shared(); + + only_compiles_if_send(&shared); + + shared.base(g) + }; assert_eq!(g1, g2); + assert_eq!(g1, g3); + assert_eq!(g1, g4); + assert_eq!(g1, g5); + assert_eq!(g1, g6); + assert_eq!(g1, g7); + assert_eq!(g1, g8); + assert_eq!(g1, g9); } } } diff --git a/src/wnaf.rs b/src/wnaf.rs index ecc9409..de5021d 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -1,13 +1,8 @@ -use super::{CurveProjective, PrimeFieldRepr}; +use super::{CurveProjective, PrimeFieldRepr, PrimeField}; /// Replaces the contents of `table` with a w-NAF 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(table: &mut Vec, mut base: G, window: usize) +pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { - assert!(window < 23); - assert!(window > 1); - table.truncate(0); table.reserve(1 << (window-1)); @@ -21,13 +16,8 @@ pub fn wnaf_table(table: &mut Vec, mut base: G, window: u } /// Replaces the contents of `wnaf` with the w-NAF representation of a scalar. -/// -/// This function will panic if provided a window size below two, or above 22. -pub fn wnaf_form(wnaf: &mut Vec, mut c: S, window: usize) +pub(crate) fn wnaf_form(wnaf: &mut Vec, mut c: S, window: usize) { - assert!(window < 23); - assert!(window > 1); - wnaf.truncate(0); while !c.is_zero() { @@ -58,7 +48,7 @@ pub fn wnaf_form(wnaf: &mut Vec, mut c: S, window: usize /// /// 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(table: &[G], wnaf: &[i64]) -> G +pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { let mut result = G::zero(); @@ -82,3 +72,118 @@ pub fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G result } + +/// A "w-ary non-adjacent form" exponentiation context. +#[derive(Debug)] +pub struct Wnaf { + base: B, + scalar: S, + window_size: W +} + +impl Wnaf<(), Vec, Vec> { + /// Construct a new wNAF context without allocating. + pub fn new() -> Self { + Wnaf { + base: vec![], + scalar: vec![], + window_size: () + } + } + + /// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that + /// can perform exponentiations with `.scalar(..)`. + pub fn base( + &mut self, + base: G, + num_scalars: usize + ) -> Wnaf> + { + // Compute the appropriate window size based on the number of scalars. + let window_size = G::recommended_wnaf_for_num_scalars(num_scalars); + + // Compute a wNAF table for the provided base and window size. + wnaf_table(&mut self.base, base, window_size); + + // Return a Wnaf object that immutably borrows the computed base storage location, + // but mutably borrows the scalar storage location. + Wnaf { + base: &self.base, + scalar: &mut self.scalar, + window_size: window_size + } + } + + /// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform + /// exponentiations with `.base(..)`. + pub fn scalar( + &mut self, + scalar: <::Scalar as PrimeField>::Repr + ) -> Wnaf, &[i64]> + { + // Compute the appropriate window size for the scalar. + let window_size = G::recommended_wnaf_for_scalar(scalar); + + // Compute the wNAF form of the scalar. + wnaf_form(&mut self.scalar, scalar, window_size); + + // Return a Wnaf object that mutably borrows the base storage location, but + // immutably borrows the computed wNAF form scalar location. + Wnaf { + base: &mut self.base, + scalar: &self.scalar, + window_size: window_size + } + } +} + +impl<'a, G: CurveProjective> Wnaf> { + /// Constructs new space for the scalar representation while borrowing + /// the computed window table, for sending the window table across threads. + pub fn shared(&self) -> Wnaf> { + Wnaf { + base: self.base, + scalar: vec![], + window_size: self.window_size + } + } +} + +impl<'a, G: CurveProjective> Wnaf, &'a [i64]> { + /// Constructs new space for the window table while borrowing + /// the computed scalar representation, for sending the scalar representation + /// across threads. + pub fn shared(&self) -> Wnaf, &'a [i64]> { + Wnaf { + base: vec![], + scalar: self.scalar, + window_size: self.window_size + } + } +} + +impl> Wnaf { + /// Performs exponentiation given a base. + pub fn base( + &mut self, + base: G + ) -> G + where B: AsMut> + { + wnaf_table(self.base.as_mut(), base, self.window_size); + wnaf_exp(self.base.as_mut(), self.scalar.as_ref()) + } +} + +impl>> Wnaf { + /// Performs exponentiation given a scalar. + pub fn scalar( + &mut self, + scalar: <::Scalar as PrimeField>::Repr + ) -> G + where B: AsRef<[G]> + { + wnaf_form(self.scalar.as_mut(), scalar, self.window_size); + wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) + } +}