group: Make Wnaf generic over Group

Wnaf was originally generic over CurveProjective; in the prior refactor
commit, we renamed this to CofactorCurve. But w-NAF only requires scalar
multiplication, which is provided by the Group trait, so we relax the
bounds on Wnaf to enable it to be used with any group. We move the
generic w-NAF helper methods from the Curve trait to a new WnafGroup
extension trait, to keep the w-NAF API surface self-contained, and not
expose it to users who aren't using it.
This commit is contained in:
Jack Grigg 2020-06-06 11:29:26 +12:00
parent a105ad675a
commit ad96a38750
6 changed files with 40 additions and 25 deletions

View File

@ -3,7 +3,7 @@ use std::ops::{AddAssign, MulAssign};
use std::sync::Arc; use std::sync::Arc;
use ff::{Field, PrimeField}; use ff::{Field, PrimeField};
use group::{cofactor::CofactorCurveAffine, Curve, Group, Wnaf}; use group::{cofactor::CofactorCurveAffine, Curve, Group, Wnaf, WnafGroup};
use pairing::Engine; use pairing::Engine;
use super::{Parameters, VerifyingKey}; use super::{Parameters, VerifyingKey};
@ -22,6 +22,8 @@ pub fn generate_random_parameters<E, C, R>(
) -> Result<Parameters<E>, SynthesisError> ) -> Result<Parameters<E>, SynthesisError>
where where
E: Engine, E: Engine,
E::G1: WnafGroup,
E::G2: WnafGroup,
C: Circuit<E::Fr>, C: Circuit<E::Fr>,
R: RngCore, R: RngCore,
{ {
@ -165,6 +167,8 @@ pub fn generate_parameters<E, C>(
) -> Result<Parameters<E>, SynthesisError> ) -> Result<Parameters<E>, SynthesisError>
where where
E: Engine, E: Engine,
E::G1: WnafGroup,
E::G2: WnafGroup,
C: Circuit<E::Fr>, C: Circuit<E::Fr>,
{ {
let mut assembly = KeypairAssembly { let mut assembly = KeypairAssembly {

View File

@ -2,7 +2,7 @@ use ff::{Field, PrimeField};
use group::{ use group::{
cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup},
prime::PrimeGroup, prime::PrimeGroup,
Curve, Group, GroupEncoding, UncompressedEncoding, Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup,
}; };
use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine};
@ -414,7 +414,9 @@ impl Curve for Fr {
fn to_affine(&self) -> Fr { fn to_affine(&self) -> Fr {
*self *self
} }
}
impl WnafGroup for Fr {
fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize {
3 3
} }

View File

@ -13,7 +13,7 @@ pub mod prime;
pub mod tests; pub mod tests;
mod wnaf; mod wnaf;
pub use self::wnaf::Wnaf; pub use self::wnaf::{Wnaf, WnafGroup};
/// A helper trait for types with a group operation. /// A helper trait for types with a group operation.
pub trait GroupOps<Rhs = Self, Output = Self>: pub trait GroupOps<Rhs = Self, Output = Self>:
@ -98,14 +98,6 @@ pub trait Curve:
/// Converts this element into its affine representation. /// Converts this element into its affine representation.
fn to_affine(&self) -> Self::AffineRepr; fn to_affine(&self) -> Self::AffineRepr;
/// Recommends a wNAF window table size given a scalar. Always returns a number
/// between 2 and 22, inclusive.
fn recommended_wnaf_for_scalar(scalar: &Self::Scalar) -> 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;
} }
pub trait GroupEncoding: Sized { pub trait GroupEncoding: Sized {

View File

@ -5,6 +5,7 @@ use std::ops::{Mul, Neg};
use crate::{ use crate::{
cofactor::{CofactorCurve, CofactorCurveAffine}, cofactor::{CofactorCurve, CofactorCurveAffine},
wnaf::WnafGroup,
GroupEncoding, UncompressedEncoding, GroupEncoding, UncompressedEncoding,
}; };
@ -64,11 +65,10 @@ pub fn curve_tests<G: CofactorCurve>() {
random_doubling_tests::<G>(); random_doubling_tests::<G>();
random_negation_tests::<G>(); random_negation_tests::<G>();
random_transformation_tests::<G>(); random_transformation_tests::<G>();
random_wnaf_tests::<G>();
random_compressed_encoding_tests::<G>(); random_compressed_encoding_tests::<G>();
} }
fn random_wnaf_tests<G: CofactorCurve>() { pub fn random_wnaf_tests<G: WnafGroup>() {
use crate::wnaf::*; use crate::wnaf::*;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([

View File

@ -2,10 +2,21 @@ use byteorder::{ByteOrder, LittleEndian};
use ff::PrimeField; use ff::PrimeField;
use std::iter; use std::iter;
use super::{cofactor::CofactorCurve, Group}; use super::Group;
/// Extension trait on a [`Group`] that provides helpers used by [`Wnaf`].
pub trait WnafGroup: Group {
/// Recommends a wNAF window table size given a scalar. Always returns a number
/// between 2 and 22, inclusive.
fn recommended_wnaf_for_scalar(scalar: &Self::Scalar) -> 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;
}
/// Replaces the contents of `table` with a w-NAF window table for the given window size. /// Replaces the contents of `table` with a w-NAF window table for the given window size.
pub(crate) fn wnaf_table<G: CofactorCurve>(table: &mut Vec<G>, mut base: G, window: usize) { pub(crate) fn wnaf_table<G: Group>(table: &mut Vec<G>, mut base: G, window: usize) {
table.truncate(0); table.truncate(0);
table.reserve(1 << (window - 1)); table.reserve(1 << (window - 1));
@ -78,7 +89,7 @@ pub(crate) fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut Vec<i64>, c: S, window: usize
/// ///
/// This function must be provided a `table` and `wnaf` that were constructed with /// 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. /// the same window size; otherwise, it may panic or produce invalid results.
pub(crate) fn wnaf_exp<G: CofactorCurve>(table: &[G], wnaf: &[i64]) -> G { pub(crate) fn wnaf_exp<G: Group>(table: &[G], wnaf: &[i64]) -> G {
let mut result = G::identity(); let mut result = G::identity();
let mut found_one = false; let mut found_one = false;
@ -110,7 +121,7 @@ pub struct Wnaf<W, B, S> {
window_size: W, window_size: W,
} }
impl<G: CofactorCurve> Wnaf<(), Vec<G>, Vec<i64>> { impl<G: Group> Wnaf<(), Vec<G>, Vec<i64>> {
/// Construct a new wNAF context without allocating. /// Construct a new wNAF context without allocating.
pub fn new() -> Self { pub fn new() -> Self {
Wnaf { Wnaf {
@ -119,7 +130,9 @@ impl<G: CofactorCurve> Wnaf<(), Vec<G>, Vec<i64>> {
window_size: (), window_size: (),
} }
} }
}
impl<G: WnafGroup> Wnaf<(), Vec<G>, Vec<i64>> {
/// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that /// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that
/// can perform exponentiations with `.scalar(..)`. /// can perform exponentiations with `.scalar(..)`.
pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf<usize, &[G], &mut Vec<i64>> { pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf<usize, &[G], &mut Vec<i64>> {
@ -157,7 +170,7 @@ impl<G: CofactorCurve> Wnaf<(), Vec<G>, Vec<i64>> {
} }
} }
impl<'a, G: CofactorCurve> Wnaf<usize, &'a [G], &'a mut Vec<i64>> { impl<'a, G: Group> Wnaf<usize, &'a [G], &'a mut Vec<i64>> {
/// Constructs new space for the scalar representation while borrowing /// Constructs new space for the scalar representation while borrowing
/// the computed window table, for sending the window table across threads. /// the computed window table, for sending the window table across threads.
pub fn shared(&self) -> Wnaf<usize, &'a [G], Vec<i64>> { pub fn shared(&self) -> Wnaf<usize, &'a [G], Vec<i64>> {
@ -169,7 +182,7 @@ impl<'a, G: CofactorCurve> Wnaf<usize, &'a [G], &'a mut Vec<i64>> {
} }
} }
impl<'a, G: CofactorCurve> Wnaf<usize, &'a mut Vec<G>, &'a [i64]> { impl<'a, G: Group> Wnaf<usize, &'a mut Vec<G>, &'a [i64]> {
/// Constructs new space for the window table while borrowing /// Constructs new space for the window table while borrowing
/// the computed scalar representation, for sending the scalar representation /// the computed scalar representation, for sending the scalar representation
/// across threads. /// across threads.
@ -184,7 +197,7 @@ impl<'a, G: CofactorCurve> Wnaf<usize, &'a mut Vec<G>, &'a [i64]> {
impl<B, S: AsRef<[i64]>> Wnaf<usize, B, S> { impl<B, S: AsRef<[i64]>> Wnaf<usize, B, S> {
/// Performs exponentiation given a base. /// Performs exponentiation given a base.
pub fn base<G: CofactorCurve>(&mut self, base: G) -> G pub fn base<G: Group>(&mut self, base: G) -> G
where where
B: AsMut<Vec<G>>, B: AsMut<Vec<G>>,
{ {
@ -195,7 +208,7 @@ impl<B, S: AsRef<[i64]>> Wnaf<usize, B, S> {
impl<B, S: AsMut<Vec<i64>>> Wnaf<usize, B, S> { impl<B, S: AsMut<Vec<i64>>> Wnaf<usize, B, S> {
/// Performs exponentiation given a scalar. /// Performs exponentiation given a scalar.
pub fn scalar<G: CofactorCurve>(&mut self, scalar: &<G as Group>::Scalar) -> G pub fn scalar<G: Group>(&mut self, scalar: &<G as Group>::Scalar) -> G
where where
B: AsRef<[G]>, B: AsRef<[G]>,
{ {

View File

@ -1092,7 +1092,9 @@ macro_rules! curve_impl {
fn to_affine(&self) -> $affine { fn to_affine(&self) -> $affine {
(*self).into() (*self).into()
} }
}
impl WnafGroup for $projective {
fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize {
Self::empirical_recommended_wnaf_for_scalar( Self::empirical_recommended_wnaf_for_scalar(
<Self::Scalar as PrimeField>::NUM_BITS as usize, <Self::Scalar as PrimeField>::NUM_BITS as usize,
@ -1210,7 +1212,7 @@ pub mod g1 {
use group::{ use group::{
cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup},
prime::PrimeGroup, prime::PrimeGroup,
Curve, Group, GroupEncoding, UncompressedEncoding, Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup,
}; };
use rand_core::RngCore; use rand_core::RngCore;
use std::fmt; use std::fmt;
@ -1778,8 +1780,9 @@ pub mod g1 {
#[test] #[test]
fn g1_curve_tests() { fn g1_curve_tests() {
use group::tests::{curve_tests, random_uncompressed_encoding_tests}; use group::tests::{curve_tests, random_uncompressed_encoding_tests, random_wnaf_tests};
curve_tests::<G1>(); curve_tests::<G1>();
random_wnaf_tests::<G1>();
random_uncompressed_encoding_tests::<G1>(); random_uncompressed_encoding_tests::<G1>();
} }
} }
@ -1792,7 +1795,7 @@ pub mod g2 {
use group::{ use group::{
cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup},
prime::PrimeGroup, prime::PrimeGroup,
Curve, Group, GroupEncoding, UncompressedEncoding, Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup,
}; };
use rand_core::RngCore; use rand_core::RngCore;
use std::fmt; use std::fmt;
@ -2484,8 +2487,9 @@ pub mod g2 {
#[test] #[test]
fn g2_curve_tests() { fn g2_curve_tests() {
use group::tests::{curve_tests, random_uncompressed_encoding_tests}; use group::tests::{curve_tests, random_uncompressed_encoding_tests, random_wnaf_tests};
curve_tests::<G2>(); curve_tests::<G2>();
random_wnaf_tests::<G2>();
random_uncompressed_encoding_tests::<G2>(); random_uncompressed_encoding_tests::<G2>();
} }
} }