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/lib.rs b/src/lib.rs index 5790c04..ae67b99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,8 +24,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; 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..45419aa 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -1,4 +1,4 @@ -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. /// @@ -82,3 +82,118 @@ pub fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G result } + +/// A wNAF 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<'a>( + &'a 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<'a>( + &'a mut self, + scalar: <::Scalar as PrimeField>::Repr + ) -> Wnaf, &'a [i64]> + { + // Compute the appropriate window size for the scalar. + let window_size = G::recommended_wnaf_for_scalar(scalar).unwrap_or(2); // TODO + + // 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()) + } +}