From d230603190174d3f9ac278faddf2de86b3fcfb3b Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 27 Sep 2017 19:09:59 -0600 Subject: [PATCH 1/5] Introduce a more typesafe wNAF API, and remove the unstable-wnaf feature. --- Cargo.toml | 3 +- src/lib.rs | 4 +- src/tests/curve.rs | 106 +++++++++++++++++++++++++++++++++++----- src/wnaf.rs | 117 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 214 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b9beff684..ae2eed506 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 5790c04fc..ae67b9921 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 e3eb0dc67..e6deec14c 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 ecc940959..45419aadc 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()) + } +} From 06f6334679fc09e4bad207955722decb2111757e Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 27 Sep 2017 20:06:51 -0600 Subject: [PATCH 2/5] Change to docs for satisfying clippy. --- src/wnaf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wnaf.rs b/src/wnaf.rs index 45419aadc..d4061aaec 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -83,7 +83,7 @@ pub fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G result } -/// A wNAF exponentiation context. +/// A "w-ary non-adjacent form" exponentiation context. #[derive(Debug)] pub struct Wnaf { base: B, From 894b44d0347253bdf3235a17a5dff64eb328e619 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 28 Sep 2017 08:12:37 -0600 Subject: [PATCH 3/5] These structures are no longer exported outside the crate, and these assertions are unnecessary now that the external API can enforce them. --- src/lib.rs | 1 + src/wnaf.rs | 16 +++------------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ae67b9921..8b6c4b06d 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)] diff --git a/src/wnaf.rs b/src/wnaf.rs index d4061aaec..9b009898a 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -1,13 +1,8 @@ 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(); From bda22db9d5907d64782f30fa4caebf5d511fddfa Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 28 Sep 2017 08:38:13 -0600 Subject: [PATCH 4/5] Always recommend a window table size. --- src/bls12_381/ec.rs | 36 +++++++++++++++--------------------- src/lib.rs | 7 +++---- src/wnaf.rs | 2 +- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/bls12_381/ec.rs b/src/bls12_381/ec.rs index f2aefa2bc..f441ccab6 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 8b6c4b06d..a3c4a2e1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,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/wnaf.rs b/src/wnaf.rs index 9b009898a..03c027459 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -122,7 +122,7 @@ impl Wnaf<(), Vec, Vec> { ) -> Wnaf, &'a [i64]> { // Compute the appropriate window size for the scalar. - let window_size = G::recommended_wnaf_for_scalar(scalar).unwrap_or(2); // TODO + let window_size = G::recommended_wnaf_for_scalar(scalar); // Compute the wNAF form of the scalar. wnaf_form(&mut self.scalar, scalar, window_size); From 6708878f4cdebbd424d9dc2f3fc5506288cd8b53 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 28 Sep 2017 11:39:53 -0600 Subject: [PATCH 5/5] Elide these lifetimes. --- src/wnaf.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/wnaf.rs b/src/wnaf.rs index 03c027459..de5021d1a 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -93,11 +93,11 @@ impl Wnaf<(), Vec, Vec> { /// 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, + pub fn base( + &mut self, base: G, num_scalars: usize - ) -> Wnaf> + ) -> Wnaf> { // Compute the appropriate window size based on the number of scalars. let window_size = G::recommended_wnaf_for_num_scalars(num_scalars); @@ -116,10 +116,10 @@ impl Wnaf<(), Vec, Vec> { /// 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, + pub fn scalar( + &mut self, scalar: <::Scalar as PrimeField>::Repr - ) -> Wnaf, &'a [i64]> + ) -> Wnaf, &[i64]> { // Compute the appropriate window size for the scalar. let window_size = G::recommended_wnaf_for_scalar(scalar);