From ae2d2b59b90e0bfd82ae0abe2433b82a884e36c8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 21 May 2020 11:48:52 +1200 Subject: [PATCH 1/7] group: Move uncompressed encodings to an UncompressedEncoding trait Specifications of deployed elliptic curves fall into one of two categories: - They specify both compressed and uncompressed encodings, allowing implementations to use either depending on performance vs data size considerations. - They specify a single point encoding format using point compression. I am unaware of any elliptic curve specification that explicitly forbids compressed encodings. To support both categories of elliptic curves, we provide the CurveAffine::Compressed associated type which all curves must define, and then curves that additionally specify an uncompressed encoding may implement the UncompressedEncoding trait and its Uncompressed associated type. pairing::PairingCurveAffine continues to require that its groups provide uncompressed encodings, because this is relied upon by bellman::groth16. We can revisit this restriction when that module is refactored as a separate crate. --- bellman/src/groth16/mod.rs | 10 +++--- bellman/src/groth16/tests/dummy_engine.rs | 7 +++-- group/src/lib.rs | 9 ++++-- group/src/tests/mod.rs | 38 ++++++++++++++++------- pairing/src/bls12_381/ec.rs | 15 ++++++--- pairing/src/bls12_381/tests/mod.rs | 11 ++++--- pairing/src/lib.rs | 7 +++-- 7 files changed, 65 insertions(+), 32 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index e86b4f597..e4285ce77 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -2,7 +2,7 @@ //! //! [Groth16]: https://eprint.iacr.org/2016/260 -use group::CurveAffine; +use group::{CurveAffine, UncompressedEncoding}; use pairing::{Engine, MultiMillerLoop}; use crate::SynthesisError; @@ -158,7 +158,7 @@ impl VerifyingKey { pub fn read(mut reader: R) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::Uncompressed::default(); + let mut g1_repr = ::Uncompressed::default(); reader.read_exact(g1_repr.as_mut())?; let affine = E::G1Affine::from_uncompressed(&g1_repr); @@ -170,7 +170,7 @@ impl VerifyingKey { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut g2_repr = ::Uncompressed::default(); + let mut g2_repr = ::Uncompressed::default(); reader.read_exact(g2_repr.as_mut())?; let affine = E::G2Affine::from_uncompressed(&g2_repr); @@ -289,7 +289,7 @@ impl Parameters { pub fn read(mut reader: R, checked: bool) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut repr = ::Uncompressed::default(); + let mut repr = ::Uncompressed::default(); reader.read_exact(repr.as_mut())?; let affine = if checked { @@ -317,7 +317,7 @@ impl Parameters { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut repr = ::Uncompressed::default(); + let mut repr = ::Uncompressed::default(); reader.read_exact(repr.as_mut())?; let affine = if checked { diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 2b1c25857..e9dcb3a8f 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,5 @@ use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; +use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use rand_core::RngCore; @@ -434,7 +434,6 @@ impl AsRef<[u8]> for FakePoint { impl CurveAffine for Fr { type Compressed = FakePoint; - type Uncompressed = FakePoint; type Projective = Fr; type Scalar = Fr; @@ -465,6 +464,10 @@ impl CurveAffine for Fr { fn to_compressed(&self) -> Self::Compressed { unimplemented!() } +} + +impl UncompressedEncoding for Fr { + type Uncompressed = FakePoint; fn from_uncompressed(_bytes: &Self::Uncompressed) -> CtOption { unimplemented!() diff --git a/group/src/lib.rs b/group/src/lib.rs index d88e320c6..e9b396bab 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -132,7 +132,6 @@ pub trait CurveAffine: { type Scalar: PrimeField; type Projective: CurveProjective; - type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>; type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Returns the additive identity. @@ -162,6 +161,12 @@ pub trait CurveAffine: /// Converts this element into its compressed encoding, so long as it's not /// the point at infinity. fn to_compressed(&self) -> Self::Compressed; +} + +/// Affine representation of a point on an elliptic curve that has a defined uncompressed +/// encoding. +pub trait UncompressedEncoding: CurveAffine { + type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Attempts to deserialize an element from its uncompressed encoding. fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption; @@ -171,7 +176,7 @@ pub trait CurveAffine: /// /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, /// API invariants may be broken.** Please consider using - /// [`CurveAffine::from_uncompressed`] instead. + /// [`UncompressedEncoding::from_uncompressed`] instead. fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption; /// Converts this element into its uncompressed encoding, so long as it's not diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 406ae6e3b..1862df8fa 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -3,7 +3,7 @@ use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Mul, Neg}; -use crate::{CurveAffine, CurveProjective}; +use crate::{CurveAffine, CurveProjective, UncompressedEncoding}; pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ @@ -62,7 +62,7 @@ pub fn curve_tests() { random_negation_tests::(); random_transformation_tests::(); random_wnaf_tests::(); - random_encoding_tests::(); + random_compressed_encoding_tests::(); } fn random_wnaf_tests() { @@ -394,17 +394,12 @@ fn random_transformation_tests() { } } -fn random_encoding_tests() { +fn random_compressed_encoding_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - assert_eq!( - G::Affine::from_uncompressed(&G::Affine::identity().to_uncompressed()).unwrap(), - G::Affine::identity() - ); - assert_eq!( G::Affine::from_compressed(&G::Affine::identity().to_compressed()).unwrap(), G::Affine::identity() @@ -413,10 +408,6 @@ fn random_encoding_tests() { for _ in 0..1000 { let mut r = G::random(&mut rng).to_affine(); - let uncompressed = r.to_uncompressed(); - let de_uncompressed = G::Affine::from_uncompressed(&uncompressed).unwrap(); - assert_eq!(de_uncompressed, r); - let compressed = r.to_compressed(); let de_compressed = G::Affine::from_compressed(&compressed).unwrap(); assert_eq!(de_compressed, r); @@ -428,3 +419,26 @@ fn random_encoding_tests() { assert_eq!(de_compressed, r); } } + +pub fn random_uncompressed_encoding_tests() +where + G::Affine: UncompressedEncoding, +{ + let mut rng = XorShiftRng::from_seed([ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, + 0xe5, + ]); + + assert_eq!( + G::Affine::from_uncompressed(&G::Affine::identity().to_uncompressed()).unwrap(), + G::Affine::identity() + ); + + for _ in 0..1000 { + let r = G::random(&mut rng).to_affine(); + + let uncompressed = r.to_uncompressed(); + let de_uncompressed = G::Affine::from_uncompressed(&uncompressed).unwrap(); + assert_eq!(de_uncompressed, r); + } +} diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 2654afcf6..e0d59d183 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -200,7 +200,6 @@ macro_rules! curve_impl { impl CurveAffine for $affine { type Scalar = $scalarfield; type Projective = $projective; - type Uncompressed = $uncompressed; type Compressed = $compressed; fn identity() -> Self { @@ -248,6 +247,10 @@ macro_rules! curve_impl { fn to_compressed(&self) -> Self::Compressed { $compressed::from_affine(*self) } + } + + impl UncompressedEncoding for $affine { + type Uncompressed = $uncompressed; fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { Self::from_uncompressed_unchecked(bytes).and_then(|affine| { @@ -901,7 +904,7 @@ pub mod g1 { use super::{g2::G2Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; + use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -1467,8 +1470,9 @@ pub mod g1 { #[test] fn g1_curve_tests() { - use group::tests::curve_tests; + use group::tests::{curve_tests, random_uncompressed_encoding_tests}; curve_tests::(); + random_uncompressed_encoding_tests::(); } } @@ -1477,7 +1481,7 @@ pub mod g2 { use super::{g1::G1Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, Group, PrimeGroup}; + use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -2167,8 +2171,9 @@ pub mod g2 { #[test] fn g2_curve_tests() { - use group::tests::curve_tests; + use group::tests::{curve_tests, random_uncompressed_encoding_tests}; curve_tests::(); + random_uncompressed_encoding_tests::(); } } diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index dd7a21bb6..30be7edbb 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,5 @@ use ff::PrimeField; -use group::{CurveAffine, CurveProjective}; +use group::{CurveAffine, CurveProjective, UncompressedEncoding}; use super::*; use crate::*; @@ -55,9 +55,12 @@ fn test_pairing_result_against_relic() { }); } -fn uncompressed_test_vectors(expected: &[u8]) { +fn uncompressed_test_vectors(expected: &[u8]) +where + G::Affine: UncompressedEncoding, +{ let mut e = G::identity(); - let encoded_len = ::Uncompressed::default() + let encoded_len = ::Uncompressed::default() .as_ref() .len(); @@ -69,7 +72,7 @@ fn uncompressed_test_vectors(expected: &[u8]) { let encoded = e_affine.to_uncompressed(); v.extend_from_slice(encoded.as_ref()); - let mut decoded = ::Uncompressed::default(); + let mut decoded = ::Uncompressed::default(); decoded.as_mut().copy_from_slice(&expected[0..encoded_len]); expected = &expected[encoded_len..]; let decoded = G::Affine::from_uncompressed(&decoded).unwrap(); diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 62af20dc1..6c3e2b47f 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -22,7 +22,10 @@ pub mod bls12_381; use core::ops::Mul; use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned}; +use group::{ + CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, + UncompressedEncoding, +}; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) /// with well-defined relationships. In particular, the G1/G2 curve groups are @@ -77,7 +80,7 @@ pub trait Engine: Sized + 'static + Clone { /// Affine representation of an elliptic curve point that can be used /// to perform pairings. -pub trait PairingCurveAffine: CurveAffine { +pub trait PairingCurveAffine: CurveAffine + UncompressedEncoding { type Pair: PairingCurveAffine; type PairingResult: Field; From 3759fc8aabed815edc985f6d0e67894275e0324c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 21 May 2020 11:58:57 +1200 Subject: [PATCH 2/7] group: Default implementation of CurveProjective::batch_normalize For convenience. Implementations will usually override this to take advantage of implementation-specific batching optimisations. --- bellman/src/groth16/tests/dummy_engine.rs | 8 -------- group/src/lib.rs | 8 +++++++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index e9dcb3a8f..22361d6b5 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -396,14 +396,6 @@ impl PrimeGroup for Fr {} impl CurveProjective for Fr { type Affine = Fr; - fn batch_normalize(p: &[Self], q: &mut [Self::Affine]) { - assert_eq!(p.len(), q.len()); - - for (p, q) in p.iter().zip(q.iter_mut()) { - *q = p.to_affine(); - } - } - fn to_affine(&self) -> Fr { *self } diff --git a/group/src/lib.rs b/group/src/lib.rs index e9b396bab..8837345f2 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -99,7 +99,13 @@ pub trait CurveProjective: /// Converts a batch of projective elements into affine elements. This function will /// panic if `p.len() != q.len()`. - fn batch_normalize(p: &[Self], q: &mut [Self::Affine]); + fn batch_normalize(p: &[Self], q: &mut [Self::Affine]) { + assert_eq!(p.len(), q.len()); + + for (p, q) in p.iter().zip(q.iter_mut()) { + *q = p.to_affine(); + } + } /// Converts this element into its affine representation. fn to_affine(&self) -> Self::Affine; From 71586914d471f798250d45c674a54e0fc785e63d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 29 May 2020 20:22:53 +1200 Subject: [PATCH 3/7] group: Extract compressed encodings from CurveAffine trait --- bellman/src/groth16/mod.rs | 6 +++--- bellman/src/groth16/tests/dummy_engine.rs | 7 +++++-- group/src/lib.rs | 7 ++++++- group/src/tests/mod.rs | 2 +- pairing/src/bls12_381/ec.rs | 13 ++++++++++--- pairing/src/bls12_381/tests/mod.rs | 6 +++--- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index e4285ce77..0d85c0ffb 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -2,7 +2,7 @@ //! //! [Groth16]: https://eprint.iacr.org/2016/260 -use group::{CurveAffine, UncompressedEncoding}; +use group::{CurveAffine, GroupEncoding, UncompressedEncoding}; use pairing::{Engine, MultiMillerLoop}; use crate::SynthesisError; @@ -47,7 +47,7 @@ impl Proof { pub fn read(mut reader: R) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::Compressed::default(); + let mut g1_repr = ::Compressed::default(); reader.read_exact(g1_repr.as_mut())?; let affine = E::G1Affine::from_compressed(&g1_repr); @@ -70,7 +70,7 @@ impl Proof { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut g2_repr = ::Compressed::default(); + let mut g2_repr = ::Compressed::default(); reader.read_exact(g2_repr.as_mut())?; let affine = E::G2Affine::from_compressed(&g2_repr); diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 22361d6b5..5fb661bf4 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,5 @@ use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; +use group::{CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding}; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use rand_core::RngCore; @@ -425,7 +425,6 @@ impl AsRef<[u8]> for FakePoint { } impl CurveAffine for Fr { - type Compressed = FakePoint; type Projective = Fr; type Scalar = Fr; @@ -444,6 +443,10 @@ impl CurveAffine for Fr { fn to_projective(&self) -> Self::Projective { *self } +} + +impl GroupEncoding for Fr { + type Compressed = FakePoint; fn from_compressed(_bytes: &Self::Compressed) -> CtOption { unimplemented!() diff --git a/group/src/lib.rs b/group/src/lib.rs index 8837345f2..59e25aa76 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -132,13 +132,13 @@ pub trait CurveAffine: + PartialEq + Eq + 'static + + GroupEncoding + Neg + Mul<::Scalar, Output = ::Projective> + for<'r> Mul<::Scalar, Output = ::Projective> { type Scalar: PrimeField; type Projective: CurveProjective; - type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Returns the additive identity. fn identity() -> Self; @@ -152,6 +152,11 @@ pub trait CurveAffine: /// Converts this element into its affine representation. fn to_projective(&self) -> Self::Projective; +} + +pub trait GroupEncoding: Sized { + /// The encoding of group elements. + type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Attempts to deserialize an element from its compressed encoding. fn from_compressed(bytes: &Self::Compressed) -> CtOption; diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 1862df8fa..cb1f12f8a 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -3,7 +3,7 @@ use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Mul, Neg}; -use crate::{CurveAffine, CurveProjective, UncompressedEncoding}; +use crate::{CurveAffine, CurveProjective, GroupEncoding, UncompressedEncoding}; pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index e0d59d183..29c591cf2 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -200,7 +200,6 @@ macro_rules! curve_impl { impl CurveAffine for $affine { type Scalar = $scalarfield; type Projective = $projective; - type Compressed = $compressed; fn identity() -> Self { $affine { @@ -221,6 +220,10 @@ macro_rules! curve_impl { fn to_projective(&self) -> $projective { (*self).into() } + } + + impl GroupEncoding for $affine { + type Compressed = $compressed; fn from_compressed(bytes: &Self::Compressed) -> CtOption { Self::from_compressed_unchecked(bytes).and_then(|affine| { @@ -904,7 +907,9 @@ pub mod g1 { use super::{g2::G2Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; + use group::{ + CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + }; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; @@ -1481,7 +1486,9 @@ pub mod g2 { use super::{g1::G1Affine, GroupDecodingError}; use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; - use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding}; + use group::{ + CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + }; use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 30be7edbb..cac563caf 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,5 @@ use ff::PrimeField; -use group::{CurveAffine, CurveProjective, UncompressedEncoding}; +use group::{CurveAffine, CurveProjective, GroupEncoding, UncompressedEncoding}; use super::*; use crate::*; @@ -87,7 +87,7 @@ where fn compressed_test_vectors(expected: &[u8]) { let mut e = G::identity(); - let encoded_len = ::Compressed::default() + let encoded_len = ::Compressed::default() .as_ref() .len(); @@ -99,7 +99,7 @@ fn compressed_test_vectors(expected: &[u8]) { let encoded = e_affine.to_compressed(); v.extend_from_slice(encoded.as_ref()); - let mut decoded = ::Compressed::default(); + let mut decoded = ::Compressed::default(); decoded.as_mut().copy_from_slice(&expected[0..encoded_len]); expected = &expected[encoded_len..]; let decoded = G::Affine::from_compressed(&decoded).unwrap(); From df13cd7480df294aca1f2e69276581774fa353cb Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 29 May 2020 20:58:54 +1200 Subject: [PATCH 4/7] group: Remove "compressed" notion from GroupEncoding A generic group has a single encoding; for elliptic curves, this happens to be the compressed encoding. --- bellman/src/groth16/mod.rs | 14 +++---- bellman/src/groth16/tests/dummy_engine.rs | 8 ++-- group/src/lib.rs | 20 +++++----- group/src/tests/mod.rs | 10 ++--- pairing/src/bls12_381/ec.rs | 10 ++--- pairing/src/bls12_381/tests/mod.rs | 48 +++++++++++------------ 6 files changed, 54 insertions(+), 56 deletions(-) diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 0d85c0ffb..2602bc716 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -38,19 +38,19 @@ impl PartialEq for Proof { impl Proof { pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_all(self.a.to_compressed().as_ref())?; - writer.write_all(self.b.to_compressed().as_ref())?; - writer.write_all(self.c.to_compressed().as_ref())?; + writer.write_all(self.a.to_bytes().as_ref())?; + writer.write_all(self.b.to_bytes().as_ref())?; + writer.write_all(self.c.to_bytes().as_ref())?; Ok(()) } pub fn read(mut reader: R) -> io::Result { let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::Compressed::default(); + let mut g1_repr = ::Repr::default(); reader.read_exact(g1_repr.as_mut())?; - let affine = E::G1Affine::from_compressed(&g1_repr); + let affine = E::G1Affine::from_bytes(&g1_repr); let affine = if affine.is_some().into() { Ok(affine.unwrap()) } else { @@ -70,10 +70,10 @@ impl Proof { }; let read_g2 = |reader: &mut R| -> io::Result { - let mut g2_repr = ::Compressed::default(); + let mut g2_repr = ::Repr::default(); reader.read_exact(g2_repr.as_mut())?; - let affine = E::G2Affine::from_compressed(&g2_repr); + let affine = E::G2Affine::from_bytes(&g2_repr); let affine = if affine.is_some().into() { Ok(affine.unwrap()) } else { diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 5fb661bf4..3ddb98c90 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -446,17 +446,17 @@ impl CurveAffine for Fr { } impl GroupEncoding for Fr { - type Compressed = FakePoint; + type Repr = FakePoint; - fn from_compressed(_bytes: &Self::Compressed) -> CtOption { + fn from_bytes(_bytes: &Self::Repr) -> CtOption { unimplemented!() } - fn from_compressed_unchecked(_bytes: &Self::Compressed) -> CtOption { + fn from_bytes_unchecked(_bytes: &Self::Repr) -> CtOption { unimplemented!() } - fn to_compressed(&self) -> Self::Compressed { + fn to_bytes(&self) -> Self::Repr { unimplemented!() } } diff --git a/group/src/lib.rs b/group/src/lib.rs index 59e25aa76..0b105c051 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -156,22 +156,22 @@ pub trait CurveAffine: pub trait GroupEncoding: Sized { /// The encoding of group elements. - type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>; + type Repr: Default + AsRef<[u8]> + AsMut<[u8]>; - /// Attempts to deserialize an element from its compressed encoding. - fn from_compressed(bytes: &Self::Compressed) -> CtOption; + /// Attempts to deserialize a group element from its encoding. + fn from_bytes(bytes: &Self::Repr) -> CtOption; - /// Attempts to deserialize a compressed element, not checking if the element is in - /// the correct subgroup. + /// Attempts to deserialize a group element, not checking if the element is valid. /// /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, /// API invariants may be broken.** Please consider using - /// [`CurveAffine::from_compressed`] instead. - fn from_compressed_unchecked(bytes: &Self::Compressed) -> CtOption; + /// [`GroupEncoding::from_bytes`] instead. + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption; - /// Converts this element into its compressed encoding, so long as it's not - /// the point at infinity. - fn to_compressed(&self) -> Self::Compressed; + /// Converts this element into its byte encoding. This may or may not support + /// encoding the identity. + // TODO: Figure out how to handle identity encoding generically. + fn to_bytes(&self) -> Self::Repr; } /// Affine representation of a point on an elliptic curve that has a defined uncompressed diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index cb1f12f8a..763cee28e 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -401,21 +401,21 @@ fn random_compressed_encoding_tests() { ]); assert_eq!( - G::Affine::from_compressed(&G::Affine::identity().to_compressed()).unwrap(), + G::Affine::from_bytes(&G::Affine::identity().to_bytes()).unwrap(), G::Affine::identity() ); for _ in 0..1000 { let mut r = G::random(&mut rng).to_affine(); - let compressed = r.to_compressed(); - let de_compressed = G::Affine::from_compressed(&compressed).unwrap(); + let compressed = r.to_bytes(); + let de_compressed = G::Affine::from_bytes(&compressed).unwrap(); assert_eq!(de_compressed, r); r = r.neg(); - let compressed = r.to_compressed(); - let de_compressed = G::Affine::from_compressed(&compressed).unwrap(); + let compressed = r.to_bytes(); + let de_compressed = G::Affine::from_bytes(&compressed).unwrap(); assert_eq!(de_compressed, r); } } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 29c591cf2..8d2a3f18c 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -223,10 +223,10 @@ macro_rules! curve_impl { } impl GroupEncoding for $affine { - type Compressed = $compressed; + type Repr = $compressed; - fn from_compressed(bytes: &Self::Compressed) -> CtOption { - Self::from_compressed_unchecked(bytes).and_then(|affine| { + fn from_bytes(bytes: &Self::Repr) -> CtOption { + Self::from_bytes_unchecked(bytes).and_then(|affine| { // NB: Decompression guarantees that it is on the curve already. CtOption::new( affine, @@ -239,7 +239,7 @@ macro_rules! curve_impl { }) } - fn from_compressed_unchecked(bytes: &Self::Compressed) -> CtOption { + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { if let Ok(p) = bytes.into_affine_unchecked() { CtOption::new(p, Choice::from(1)) } else { @@ -247,7 +247,7 @@ macro_rules! curve_impl { } } - fn to_compressed(&self) -> Self::Compressed { + fn to_bytes(&self) -> Self::Repr { $compressed::from_affine(*self) } } diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index cac563caf..e905a486a 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -87,22 +87,20 @@ where fn compressed_test_vectors(expected: &[u8]) { let mut e = G::identity(); - let encoded_len = ::Compressed::default() - .as_ref() - .len(); + let encoded_len = ::Repr::default().as_ref().len(); let mut v = vec![]; { let mut expected = expected; for _ in 0..1000 { let e_affine = e.to_affine(); - let encoded = e_affine.to_compressed(); + let encoded = e_affine.to_bytes(); v.extend_from_slice(encoded.as_ref()); - let mut decoded = ::Compressed::default(); + let mut decoded = ::Repr::default(); decoded.as_mut().copy_from_slice(&expected[0..encoded_len]); expected = &expected[encoded_len..]; - let decoded = G::Affine::from_compressed(&decoded).unwrap(); + let decoded = G::Affine::from_bytes(&decoded).unwrap(); assert_eq!(e_affine, decoded); e.add_assign(&G::generator()); @@ -395,12 +393,12 @@ fn test_g2_uncompressed_invalid_vectors() { #[test] fn test_g1_compressed_invalid_vectors() { { - let z = G1Affine::identity().to_compressed(); + let z = G1Affine::identity().to_bytes(); { let mut z = z; z.as_mut()[0] &= 0b0111_1111; - if G1Affine::from_compressed(&z).is_none().into() { + if G1Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -410,7 +408,7 @@ fn test_g1_compressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] |= 0b0010_0000; - if G1Affine::from_compressed(&z).is_none().into() { + if G1Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); @@ -420,7 +418,7 @@ fn test_g1_compressed_invalid_vectors() { for i in 0..G1Compressed::size() { let mut z = z; z.as_mut()[i] |= 0b0000_0001; - if G1Affine::from_compressed(&z).is_none().into() { + if G1Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); @@ -428,12 +426,12 @@ fn test_g1_compressed_invalid_vectors() { } } - let o = G1Affine::generator().to_compressed(); + let o = G1Affine::generator().to_bytes(); { let mut o = o; o.as_mut()[0] &= 0b0111_1111; - if G1Affine::from_compressed(&o).is_none().into() { + if G1Affine::from_bytes(&o).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -447,7 +445,7 @@ fn test_g1_compressed_invalid_vectors() { o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G1Affine::from_compressed(&o).is_none().into() { + if G1Affine::from_bytes(&o).is_none().into() { // x coordinate } else { panic!("should have rejected the point") @@ -469,7 +467,7 @@ fn test_g1_compressed_invalid_vectors() { o.as_mut().copy_from_slice(x.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G1Affine::from_compressed(&o).is_none().into() { + if G1Affine::from_bytes(&o).is_none().into() { break; } else { panic!("should have rejected the point because it isn't on the curve") @@ -492,7 +490,7 @@ fn test_g1_compressed_invalid_vectors() { o.as_mut().copy_from_slice(x.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G1Affine::from_compressed(&o).is_none().into() { + if G1Affine::from_bytes(&o).is_none().into() { break; } else { panic!( @@ -509,12 +507,12 @@ fn test_g1_compressed_invalid_vectors() { #[test] fn test_g2_compressed_invalid_vectors() { { - let z = G2Affine::identity().to_compressed(); + let z = G2Affine::identity().to_bytes(); { let mut z = z; z.as_mut()[0] &= 0b0111_1111; - if G2Affine::from_compressed(&z).is_none().into() { + if G2Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -524,7 +522,7 @@ fn test_g2_compressed_invalid_vectors() { { let mut z = z; z.as_mut()[0] |= 0b0010_0000; - if G2Affine::from_compressed(&z).is_none().into() { + if G2Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); @@ -534,7 +532,7 @@ fn test_g2_compressed_invalid_vectors() { for i in 0..G2Compressed::size() { let mut z = z; z.as_mut()[i] |= 0b0000_0001; - if G2Affine::from_compressed(&z).is_none().into() { + if G2Affine::from_bytes(&z).is_none().into() { // :) } else { panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); @@ -542,12 +540,12 @@ fn test_g2_compressed_invalid_vectors() { } } - let o = G2Affine::generator().to_compressed(); + let o = G2Affine::generator().to_bytes(); { let mut o = o; o.as_mut()[0] &= 0b0111_1111; - if G2Affine::from_compressed(&o).is_none().into() { + if G2Affine::from_bytes(&o).is_none().into() { // :) } else { panic!("should have rejected the point because we expected a compressed point"); @@ -561,7 +559,7 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[..48].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G2Affine::from_compressed(&o).is_none().into() { + if G2Affine::from_bytes(&o).is_none().into() { // x coordinate (c1) } else { panic!("should have rejected the point") @@ -573,7 +571,7 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[48..96].copy_from_slice(m.as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G2Affine::from_compressed(&o).is_none().into() { + if G2Affine::from_bytes(&o).is_none().into() { // x coordinate (c0) } else { panic!("should have rejected the point") @@ -602,7 +600,7 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G2Affine::from_compressed(&o).is_none().into() { + if G2Affine::from_bytes(&o).is_none().into() { break; } else { panic!("should have rejected the point because it isn't on the curve") @@ -632,7 +630,7 @@ fn test_g2_compressed_invalid_vectors() { o.as_mut()[48..].copy_from_slice(x.c0.to_repr().as_ref()); o.as_mut()[0] |= 0b1000_0000; - if G2Affine::from_compressed(&o).is_none().into() { + if G2Affine::from_bytes(&o).is_none().into() { break; } else { panic!( From d52053d877fd051ca62eb11797a340fe67195ebe Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 29 May 2020 21:52:39 +1200 Subject: [PATCH 5/7] group: Renaming prior to trait refactor This will reduce the size of the subsequent refactor diff. --- bellman/src/domain.rs | 12 ++--- bellman/src/groth16/generator.rs | 2 +- bellman/src/groth16/prover.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 8 ++-- bellman/src/groth16/verifier.rs | 4 +- bellman/src/multiexp.rs | 22 ++++----- group/src/lib.rs | 18 ++++---- group/src/tests/mod.rs | 36 +++++++-------- group/src/wnaf.rs | 16 +++---- pairing/src/bls12_381/ec.rs | 56 +++++++++++------------ pairing/src/bls12_381/tests/mod.rs | 6 +-- pairing/src/lib.rs | 10 ++-- pairing/src/tests/engine.rs | 2 +- 13 files changed, 94 insertions(+), 100 deletions(-) diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index 47f8ff083..e1dc22970 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -12,7 +12,7 @@ //! [Groth16]: https://eprint.iacr.org/2016/260 use ff::PrimeField; -use group::CurveProjective; +use group::CofactorCurve; use super::SynthesisError; @@ -196,23 +196,23 @@ pub trait Group: Sized + Copy + Clone + Send + Sync { fn group_sub_assign(&mut self, other: &Self); } -pub struct Point(pub G); +pub struct Point(pub G); -impl PartialEq for Point { +impl PartialEq for Point { fn eq(&self, other: &Point) -> bool { self.0 == other.0 } } -impl Copy for Point {} +impl Copy for Point {} -impl Clone for Point { +impl Clone for Point { fn clone(&self) -> Point { *self } } -impl Group for Point { +impl Group for Point { fn group_zero() -> Self { Point(G::identity()) } diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 060ff1bec..e84fc12a9 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -3,7 +3,7 @@ use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Group, Wnaf}; +use group::{CurveAffine, CofactorCurve, Group, Wnaf}; use pairing::Engine; use super::{Parameters, VerifyingKey}; diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index ff91c2675..293cad2b7 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use futures::Future; use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective}; +use group::{CurveAffine, CofactorCurve}; use pairing::Engine; use super::{ParameterSource, Proof}; diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 3ddb98c90..01e8b82d1 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,5 @@ use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding}; +use group::{CurveAffine, CofactorCurve, Group, GroupEncoding, PrimeGroup, UncompressedEncoding}; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use rand_core::RngCore; @@ -393,7 +393,7 @@ impl Group for Fr { impl PrimeGroup for Fr {} -impl CurveProjective for Fr { +impl CofactorCurve for Fr { type Affine = Fr; fn to_affine(&self) -> Fr { @@ -425,7 +425,7 @@ impl AsRef<[u8]> for FakePoint { } impl CurveAffine for Fr { - type Projective = Fr; + type Curve = Fr; type Scalar = Fr; fn identity() -> Self { @@ -440,7 +440,7 @@ impl CurveAffine for Fr { Choice::from(if ::is_zero(self) { 1 } else { 0 }) } - fn to_projective(&self) -> Self::Projective { + fn to_curve(&self) -> Self::Curve { *self } } diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index e86b15aa5..5758d5d83 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,4 +1,4 @@ -use group::{CurveAffine, CurveProjective}; +use group::{CurveAffine, CofactorCurve}; use pairing::{MillerLoopResult, MultiMillerLoop}; use std::ops::{AddAssign, Neg}; @@ -27,7 +27,7 @@ pub fn verify_proof<'a, E: MultiMillerLoop>( return Err(SynthesisError::MalformedVerifyingKey); } - let mut acc = pvk.ic[0].to_projective(); + let mut acc = pvk.ic[0].to_curve(); for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) { AddAssign::<&E::G1>::add_assign(&mut acc, &(*b * i)); diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index c9e00d6ba..fea10c667 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -2,7 +2,7 @@ use super::multicore::Worker; use bit_vec::{self, BitVec}; use ff::{Endianness, Field, PrimeField}; use futures::Future; -use group::{CurveAffine, CurveProjective}; +use group::{CofactorCurve, CurveAffine}; use std::io; use std::iter; use std::ops::AddAssign; @@ -25,17 +25,17 @@ pub trait Source { fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; } -pub trait AddAssignFromSource: CurveProjective { +pub trait AddAssignFromSource: CofactorCurve { /// Parses the element from the source. Fails if the point is at infinity. - fn add_assign_from_source::Affine>>( + fn add_assign_from_source::Affine>>( &mut self, source: &mut S, ) -> Result<(), SynthesisError> { - AddAssign::<&::Affine>::add_assign(self, source.next()?); + AddAssign::<&::Affine>::add_assign(self, source.next()?); Ok(()) } } -impl AddAssignFromSource for G where G: CurveProjective {} +impl AddAssignFromSource for G where G: CofactorCurve {} impl SourceBuilder for (Arc>, usize) { type Source = (Arc>, usize); @@ -162,8 +162,8 @@ fn multiexp_inner( where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, - G: CurveProjective, - S: SourceBuilder<::Affine>, + G: CofactorCurve, + S: SourceBuilder<::Affine>, { // Perform this region of the multiexp let this = { @@ -274,8 +274,8 @@ pub fn multiexp( where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, - G: CurveProjective, - S: SourceBuilder<::Affine>, + G: CofactorCurve, + S: SourceBuilder<::Affine>, { let c = if exponents.len() < 32 { 3u32 @@ -296,8 +296,8 @@ where #[cfg(feature = "pairing")] #[test] fn test_with_bls12() { - fn naive_multiexp( - bases: Arc::Affine>>, + fn naive_multiexp( + bases: Arc::Affine>>, exponents: Arc>, ) -> G { assert_eq!(bases.len(), exponents.len()); diff --git a/group/src/lib.rs b/group/src/lib.rs index 0b105c051..839e31a3b 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -88,12 +88,10 @@ pub trait PrimeGroup: Group {} /// Projective representation of an elliptic curve point guaranteed to be /// in the correct prime order subgroup. -pub trait CurveProjective: - Group - + GroupOps<::Affine> - + GroupOpsOwned<::Affine> +pub trait CofactorCurve: + Group + GroupOps<::Affine> + GroupOpsOwned<::Affine> { - type Affine: CurveAffine + type Affine: CurveAffine + Mul + for<'r> Mul; @@ -134,11 +132,11 @@ pub trait CurveAffine: + 'static + GroupEncoding + Neg - + Mul<::Scalar, Output = ::Projective> - + for<'r> Mul<::Scalar, Output = ::Projective> + + Mul<::Scalar, Output = ::Curve> + + for<'r> Mul<::Scalar, Output = ::Curve> { type Scalar: PrimeField; - type Projective: CurveProjective; + type Curve: CofactorCurve; /// Returns the additive identity. fn identity() -> Self; @@ -150,8 +148,8 @@ pub trait CurveAffine: /// additive identity. fn is_identity(&self) -> Choice; - /// Converts this element into its affine representation. - fn to_projective(&self) -> Self::Projective; + /// Converts this element into its efficient representation. + fn to_curve(&self) -> Self::Curve; } pub trait GroupEncoding: Sized { diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 763cee28e..920b44c91 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -3,9 +3,9 @@ use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Mul, Neg}; -use crate::{CurveAffine, CurveProjective, GroupEncoding, UncompressedEncoding}; +use crate::{CofactorCurve, CurveAffine, GroupEncoding, UncompressedEncoding}; -pub fn curve_tests() { +pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -50,8 +50,8 @@ pub fn curve_tests() { // Transformations { let a = G::random(&mut rng); - let b = a.to_affine().to_projective(); - let c = a.to_affine().to_projective().to_affine().to_projective(); + let b = a.to_affine().to_curve(); + let c = a.to_affine().to_curve().to_affine().to_curve(); assert_eq!(a, b); assert_eq!(b, c); } @@ -65,7 +65,7 @@ pub fn curve_tests() { random_compressed_encoding_tests::(); } -fn random_wnaf_tests() { +fn random_wnaf_tests() { use crate::wnaf::*; let mut rng = XorShiftRng::from_seed([ @@ -184,7 +184,7 @@ fn random_wnaf_tests() { } } -fn random_negation_tests() { +fn random_negation_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -214,7 +214,7 @@ fn random_negation_tests() { } } -fn random_doubling_tests() { +fn random_doubling_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -242,7 +242,7 @@ fn random_doubling_tests() { } } -fn random_multiplication_tests() { +fn random_multiplication_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -277,7 +277,7 @@ fn random_multiplication_tests() { } } -fn random_addition_tests() { +fn random_addition_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -325,17 +325,17 @@ fn random_addition_tests() { // Mixed addition // (a + b) + c - tmp[3] = a_affine.to_projective(); + tmp[3] = a_affine.to_curve(); tmp[3].add_assign(&b_affine); tmp[3].add_assign(&c_affine); // a + (b + c) - tmp[4] = b_affine.to_projective(); + tmp[4] = b_affine.to_curve(); tmp[4].add_assign(&c_affine); tmp[4].add_assign(&a_affine); // (a + c) + b - tmp[5] = a_affine.to_projective(); + tmp[5] = a_affine.to_curve(); tmp[5].add_assign(&c_affine); tmp[5].add_assign(&b_affine); @@ -357,7 +357,7 @@ fn random_addition_tests() { } } -fn random_transformation_tests() { +fn random_transformation_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -366,7 +366,7 @@ fn random_transformation_tests() { for _ in 0..1000 { let g = G::random(&mut rng); let g_affine = g.to_affine(); - let g_projective = g_affine.to_projective(); + let g_projective = g_affine.to_curve(); assert_eq!(g, g_projective); } @@ -382,7 +382,7 @@ fn random_transformation_tests() { } for _ in 0..5 { let s = between.sample(&mut rng); - v[s] = v[s].to_affine().to_projective(); + v[s] = v[s].to_affine().to_curve(); } let expected_v = v.iter().map(|v| v.to_affine()).collect::>(); @@ -394,7 +394,7 @@ fn random_transformation_tests() { } } -fn random_compressed_encoding_tests() { +fn random_compressed_encoding_tests() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -420,9 +420,9 @@ fn random_compressed_encoding_tests() { } } -pub fn random_uncompressed_encoding_tests() +pub fn random_uncompressed_encoding_tests() where - G::Affine: UncompressedEncoding, + ::Affine: UncompressedEncoding, { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 0ee46cafc..64a2de1b5 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -2,10 +2,10 @@ use byteorder::{ByteOrder, LittleEndian}; use ff::PrimeField; use std::iter; -use super::{CurveProjective, Group}; +use super::{CofactorCurve, Group}; /// Replaces the contents of `table` with a w-NAF window table for the given window size. -pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { +pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { table.truncate(0); table.reserve(1 << (window - 1)); @@ -78,7 +78,7 @@ pub(crate) fn wnaf_form>(wnaf: &mut Vec, 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(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { +pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { let mut result = G::identity(); let mut found_one = false; @@ -110,7 +110,7 @@ pub struct Wnaf { window_size: W, } -impl Wnaf<(), Vec, Vec> { +impl Wnaf<(), Vec, Vec> { /// Construct a new wNAF context without allocating. pub fn new() -> Self { Wnaf { @@ -157,7 +157,7 @@ impl Wnaf<(), Vec, Vec> { } } -impl<'a, G: CurveProjective> Wnaf> { +impl<'a, G: CofactorCurve> 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> { @@ -169,7 +169,7 @@ impl<'a, G: CurveProjective> Wnaf> { } } -impl<'a, G: CurveProjective> Wnaf, &'a [i64]> { +impl<'a, G: CofactorCurve> Wnaf, &'a [i64]> { /// Constructs new space for the window table while borrowing /// the computed scalar representation, for sending the scalar representation /// across threads. @@ -184,7 +184,7 @@ impl<'a, G: CurveProjective> Wnaf, &'a [i64]> { impl> Wnaf { /// Performs exponentiation given a base. - pub fn base(&mut self, base: G) -> G + pub fn base(&mut self, base: G) -> G where B: AsMut>, { @@ -195,7 +195,7 @@ impl> Wnaf { impl>> Wnaf { /// Performs exponentiation given a scalar. - pub fn scalar(&mut self, scalar: &::Scalar) -> G + pub fn scalar(&mut self, scalar: &::Scalar) -> G where B: AsRef<[G]>, { diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index 8d2a3f18c..e480700f8 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -199,7 +199,7 @@ macro_rules! curve_impl { impl CurveAffine for $affine { type Scalar = $scalarfield; - type Projective = $projective; + type Curve = $projective; fn identity() -> Self { $affine { @@ -217,7 +217,7 @@ macro_rules! curve_impl { Choice::from(if self.infinity { 1 } else { 0 }) } - fn to_projective(&self) -> $projective { + fn to_curve(&self) -> $projective { (*self).into() } } @@ -466,30 +466,28 @@ macro_rules! curve_impl { } } - impl<'r> ::std::ops::Add<&'r <$projective as CurveProjective>::Affine> for $projective { + impl<'r> ::std::ops::Add<&'r $affine> for $projective { type Output = Self; #[inline] - fn add(self, other: &<$projective as CurveProjective>::Affine) -> Self { + fn add(self, other: &$affine) -> Self { let mut ret = self; ret.add_assign(other); ret } } - impl ::std::ops::Add<<$projective as CurveProjective>::Affine> for $projective { + impl ::std::ops::Add<$affine> for $projective { type Output = Self; #[inline] - fn add(self, other: <$projective as CurveProjective>::Affine) -> Self { + fn add(self, other: $affine) -> Self { self + &other } } - impl<'r> ::std::ops::AddAssign<&'r <$projective as CurveProjective>::Affine> - for $projective - { - fn add_assign(&mut self, other: &<$projective as CurveProjective>::Affine) { + impl<'r> ::std::ops::AddAssign<&'r $affine> for $projective { + fn add_assign(&mut self, other: &$affine) { if other.is_identity().into() { return; } @@ -567,44 +565,42 @@ macro_rules! curve_impl { } } - impl ::std::ops::AddAssign<<$projective as CurveProjective>::Affine> for $projective { + impl ::std::ops::AddAssign<$affine> for $projective { #[inline] - fn add_assign(&mut self, other: <$projective as CurveProjective>::Affine) { + fn add_assign(&mut self, other: $affine) { self.add_assign(&other); } } - impl<'r> ::std::ops::Sub<&'r <$projective as CurveProjective>::Affine> for $projective { + impl<'r> ::std::ops::Sub<&'r $affine> for $projective { type Output = Self; #[inline] - fn sub(self, other: &<$projective as CurveProjective>::Affine) -> Self { + fn sub(self, other: &$affine) -> Self { let mut ret = self; ret.sub_assign(other); ret } } - impl ::std::ops::Sub<<$projective as CurveProjective>::Affine> for $projective { + impl ::std::ops::Sub<$affine> for $projective { type Output = Self; #[inline] - fn sub(self, other: <$projective as CurveProjective>::Affine) -> Self { + fn sub(self, other: $affine) -> Self { self - &other } } - impl<'r> ::std::ops::SubAssign<&'r <$projective as CurveProjective>::Affine> - for $projective - { - fn sub_assign(&mut self, other: &<$projective as CurveProjective>::Affine) { + impl<'r> ::std::ops::SubAssign<&'r $affine> for $projective { + fn sub_assign(&mut self, other: &$affine) { self.add_assign(&other.neg()); } } - impl ::std::ops::SubAssign<<$projective as CurveProjective>::Affine> for $projective { + impl ::std::ops::SubAssign<$affine> for $projective { #[inline] - fn sub_assign(&mut self, other: <$projective as CurveProjective>::Affine) { + fn sub_assign(&mut self, other: $affine) { self.sub_assign(&other); } } @@ -746,7 +742,7 @@ macro_rules! curve_impl { impl PrimeGroup for $projective {} - impl CurveProjective for $projective { + impl CofactorCurve for $projective { type Affine = $affine; fn batch_normalize(p: &[Self], q: &mut [$affine]) { @@ -908,7 +904,7 @@ pub mod g1 { use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; use group::{ - CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + CofactorCurve, CurveAffine, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, }; use rand_core::RngCore; use std::fmt; @@ -1462,15 +1458,15 @@ pub mod g1 { assert!(b.is_on_curve() && b.is_in_correct_subgroup_assuming_on_curve()); assert!(c.is_on_curve() && c.is_in_correct_subgroup_assuming_on_curve()); - let mut tmp1 = a.to_projective(); - tmp1.add_assign(&b.to_projective()); + let mut tmp1 = a.to_curve(); + tmp1.add_assign(&b.to_curve()); assert_eq!(tmp1.to_affine(), c); - assert_eq!(tmp1, c.to_projective()); + assert_eq!(tmp1, c.to_curve()); - let mut tmp2 = a.to_projective(); + let mut tmp2 = a.to_curve(); tmp2.add_assign(&b); assert_eq!(tmp2.to_affine(), c); - assert_eq!(tmp2, c.to_projective()); + assert_eq!(tmp2, c.to_curve()); } #[test] @@ -1487,7 +1483,7 @@ pub mod g2 { use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; use group::{ - CurveAffine, CurveProjective, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + CofactorCurve, CurveAffine, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, }; use rand_core::RngCore; use std::fmt; diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index e905a486a..1d9f41fc9 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,5 @@ use ff::PrimeField; -use group::{CurveAffine, CurveProjective, GroupEncoding, UncompressedEncoding}; +use group::{CofactorCurve, CurveAffine, GroupEncoding, UncompressedEncoding}; use super::*; use crate::*; @@ -55,7 +55,7 @@ fn test_pairing_result_against_relic() { }); } -fn uncompressed_test_vectors(expected: &[u8]) +fn uncompressed_test_vectors(expected: &[u8]) where G::Affine: UncompressedEncoding, { @@ -85,7 +85,7 @@ where assert_eq!(&v[..], expected); } -fn compressed_test_vectors(expected: &[u8]) { +fn compressed_test_vectors(expected: &[u8]) { let mut e = G::identity(); let encoded_len = ::Repr::default().as_ref().len(); diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 6c3e2b47f..6898bd553 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -23,7 +23,7 @@ pub mod bls12_381; use core::ops::Mul; use ff::{Field, PrimeField}; use group::{ - CurveAffine, CurveProjective, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, + CofactorCurve, CurveAffine, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, UncompressedEncoding, }; @@ -35,7 +35,7 @@ pub trait Engine: Sized + 'static + Clone { type Fr: PrimeField; /// The projective representation of an element in G1. - type G1: CurveProjective + type G1: CofactorCurve + From + GroupOps + GroupOpsOwned @@ -45,7 +45,7 @@ pub trait Engine: Sized + 'static + Clone { /// The affine representation of an element in G1. type G1Affine: PairingCurveAffine< Scalar = Self::Fr, - Projective = Self::G1, + Curve = Self::G1, Pair = Self::G2Affine, PairingResult = Self::Gt, > + From @@ -53,7 +53,7 @@ pub trait Engine: Sized + 'static + Clone { + for<'a> Mul<&'a Self::Fr, Output = Self::G1>; /// The projective representation of an element in G2. - type G2: CurveProjective + type G2: CofactorCurve + From + GroupOps + GroupOpsOwned @@ -63,7 +63,7 @@ pub trait Engine: Sized + 'static + Clone { /// The affine representation of an element in G2. type G2Affine: PairingCurveAffine< Scalar = Self::Fr, - Projective = Self::G2, + Curve = Self::G2, Pair = Self::G1Affine, PairingResult = Self::Gt, > + From diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index bf0f8849d..2cfc473b2 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -1,5 +1,5 @@ use ff::{Endianness, Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Group}; +use group::{CofactorCurve, CurveAffine, Group}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::MulAssign; From a105ad675aa2ff9e64b61e35c76b161d655398fe Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 30 May 2020 00:49:44 +1200 Subject: [PATCH 6/7] group: Separate prime and cofactor traits into modules Instead of having the Group crate hold a Subgroup associated type (and thus needing to define the subgroup of a prime-order group as itself), we specify two separate sets of traits for prime-order groups and ones with a cofactor. Protocol implementors can either restrict their implementations to only work with PrimeGroup, or can explicitly choose to support CofactorGroup and then explicitly handle the subgroup edge cases with e.g. CofactorGroup::mul_by_cofactor (which would be a no-op for PrimeGroup). Protocol implementors can also choose to specialise to elliptic curves if they want to leverage an affine representation and mixed addition in their protocol for efficiency, or they can ignore those traits and stick with the simpler group-focused traits. --- bellman/src/domain.rs | 2 +- bellman/src/groth16/generator.rs | 2 +- bellman/src/groth16/mod.rs | 2 +- bellman/src/groth16/prover.rs | 2 +- bellman/src/groth16/tests/dummy_engine.rs | 29 +- bellman/src/groth16/verifier.rs | 2 +- bellman/src/multiexp.rs | 12 +- ff/src/lib.rs | 2 + group/src/cofactor.rs | 113 ++++++++ group/src/lib.rs | 68 +---- group/src/prime.rs | 52 ++++ group/src/tests/mod.rs | 5 +- group/src/wnaf.rs | 6 +- pairing/src/bls12_381/ec.rs | 324 +++++++++++++++++++++- pairing/src/bls12_381/mod.rs | 2 +- pairing/src/bls12_381/tests/mod.rs | 5 +- pairing/src/lib.rs | 6 +- pairing/src/tests/engine.rs | 2 +- 18 files changed, 547 insertions(+), 89 deletions(-) create mode 100644 group/src/cofactor.rs create mode 100644 group/src/prime.rs diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index e1dc22970..098b0edda 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -12,7 +12,7 @@ //! [Groth16]: https://eprint.iacr.org/2016/260 use ff::PrimeField; -use group::CofactorCurve; +use group::cofactor::CofactorCurve; use super::SynthesisError; diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index e84fc12a9..084e7a376 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -3,7 +3,7 @@ use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; use ff::{Field, PrimeField}; -use group::{CurveAffine, CofactorCurve, Group, Wnaf}; +use group::{cofactor::CofactorCurveAffine, Curve, Group, Wnaf}; use pairing::Engine; use super::{Parameters, VerifyingKey}; diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs index 2602bc716..7c97197aa 100644 --- a/bellman/src/groth16/mod.rs +++ b/bellman/src/groth16/mod.rs @@ -2,7 +2,7 @@ //! //! [Groth16]: https://eprint.iacr.org/2016/260 -use group::{CurveAffine, GroupEncoding, UncompressedEncoding}; +use group::{cofactor::CofactorCurveAffine, GroupEncoding, UncompressedEncoding}; use pairing::{Engine, MultiMillerLoop}; use crate::SynthesisError; diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs index 293cad2b7..cbe883aa7 100644 --- a/bellman/src/groth16/prover.rs +++ b/bellman/src/groth16/prover.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use futures::Future; use ff::{Field, PrimeField}; -use group::{CurveAffine, CofactorCurve}; +use group::{cofactor::CofactorCurveAffine, Curve}; use pairing::Engine; use super::{ParameterSource, Proof}; diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 01e8b82d1..17e0043a1 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,5 +1,9 @@ use ff::{Field, PrimeField}; -use group::{CurveAffine, CofactorCurve, Group, GroupEncoding, PrimeGroup, UncompressedEncoding}; +use group::{ + cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, + prime::PrimeGroup, + Curve, Group, GroupEncoding, UncompressedEncoding, +}; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; use rand_core::RngCore; @@ -367,7 +371,6 @@ impl MillerLoopResult for Fr { } impl Group for Fr { - type Subgroup = Fr; type Scalar = Fr; fn random(rng: &mut R) -> Self { @@ -393,8 +396,20 @@ impl Group for Fr { impl PrimeGroup for Fr {} -impl CofactorCurve for Fr { - type Affine = Fr; +impl CofactorGroup for Fr { + type Subgroup = Fr; + + fn mul_by_cofactor(&self) -> Self::Subgroup { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, Choice::from(1)) + } +} + +impl Curve for Fr { + type AffineRepr = Fr; fn to_affine(&self) -> Fr { *self @@ -409,6 +424,10 @@ impl CofactorCurve for Fr { } } +impl CofactorCurve for Fr { + type Affine = Fr; +} + #[derive(Copy, Clone, Default)] pub struct FakePoint; @@ -424,7 +443,7 @@ impl AsRef<[u8]> for FakePoint { } } -impl CurveAffine for Fr { +impl CofactorCurveAffine for Fr { type Curve = Fr; type Scalar = Fr; diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs index 5758d5d83..0fe8c9460 100644 --- a/bellman/src/groth16/verifier.rs +++ b/bellman/src/groth16/verifier.rs @@ -1,4 +1,4 @@ -use group::{CurveAffine, CofactorCurve}; +use group::{cofactor::CofactorCurveAffine, Curve}; use pairing::{MillerLoopResult, MultiMillerLoop}; use std::ops::{AddAssign, Neg}; diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs index fea10c667..8fdbc7003 100644 --- a/bellman/src/multiexp.rs +++ b/bellman/src/multiexp.rs @@ -2,7 +2,7 @@ use super::multicore::Worker; use bit_vec::{self, BitVec}; use ff::{Endianness, Field, PrimeField}; use futures::Future; -use group::{CofactorCurve, CurveAffine}; +use group::cofactor::{CofactorCurve, CofactorCurveAffine}; use std::io; use std::iter; use std::ops::AddAssign; @@ -11,14 +11,14 @@ use std::sync::Arc; use super::SynthesisError; /// An object that builds a source of bases. -pub trait SourceBuilder: Send + Sync + 'static + Clone { +pub trait SourceBuilder: Send + Sync + 'static + Clone { type Source: Source; fn new(self) -> Self::Source; } /// A source of bases, like an iterator. -pub trait Source { +pub trait Source { fn next(&mut self) -> Result<&G, SynthesisError>; /// Skips `amt` elements from the source, avoiding deserialization. @@ -37,7 +37,7 @@ pub trait AddAssignFromSource: CofactorCurve { } impl AddAssignFromSource for G where G: CofactorCurve {} -impl SourceBuilder for (Arc>, usize) { +impl SourceBuilder for (Arc>, usize) { type Source = (Arc>, usize); fn new(self) -> (Arc>, usize) { @@ -45,7 +45,7 @@ impl SourceBuilder for (Arc>, usize) { } } -impl Source for (Arc>, usize) { +impl Source for (Arc>, usize) { fn next(&mut self) -> Result<&G, SynthesisError> { if self.0.len() <= self.1 { return Err(io::Error::new( @@ -311,7 +311,7 @@ fn test_with_bls12() { acc } - use group::Group; + use group::{Curve, Group}; use pairing::{ bls12_381::{Bls12, Fr}, Engine, diff --git a/ff/src/lib.rs b/ff/src/lib.rs index e975d9136..9d96f5ede 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -213,6 +213,8 @@ pub trait PrimeField: Field + From { fn root_of_unity() -> Self; } +/// Takes a little-endian representation of some value, and returns its bits in big-endian +/// order. #[derive(Debug)] pub struct BitIterator> { t: E, diff --git a/group/src/cofactor.rs b/group/src/cofactor.rs new file mode 100644 index 000000000..9ccc98394 --- /dev/null +++ b/group/src/cofactor.rs @@ -0,0 +1,113 @@ +use core::fmt; +use core::ops::{Mul, Neg}; +use ff::{BitIterator, Endianness, PrimeField}; +use subtle::{Choice, CtOption}; + +use crate::{prime::PrimeGroup, Curve, Group, GroupEncoding, GroupOps, GroupOpsOwned}; + +/// This trait represents an element of a cryptographic group with a large prime-order +/// subgroup and a comparatively-small cofactor. +pub trait CofactorGroup: + Group + + GroupEncoding + + GroupOps<::Subgroup> + + GroupOpsOwned<::Subgroup> +{ + /// The large prime-order subgroup in which cryptographic operations are performed. + /// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`. + type Subgroup: PrimeGroup + Into; + + /// Returns `[h] self`, where `h` is the cofactor of the group. + /// + /// If `Self` implements [`PrimeGroup`], this returns `self`. + fn mul_by_cofactor(&self) -> Self::Subgroup; + + /// Returns `self` if it is contained in the prime-order subgroup. + /// + /// If `Self` implements [`PrimeGroup`], this returns `Some(self)`. + fn into_subgroup(self) -> CtOption; + + /// Determines if this element is of small order. + /// + /// Returns: + /// - `true` if `self` is in the torsion subgroup. + /// - `false` if `self` is not in the torsion subgroup. + fn is_small_order(&self) -> Choice { + self.mul_by_cofactor().is_identity() + } + + /// Determines if this element is "torsion free", i.e., is contained in the + /// prime-order subgroup. + /// + /// Returns: + /// - `true` if `self` has zero torsion component and is in the prime-order subgroup. + /// - `false` if `self` has non-zero torsion component and is not in the prime-order + /// subgroup. + fn is_torsion_free(&self) -> Choice { + // Obtain the scalar field characteristic in little endian. + let mut char = Self::Scalar::char(); + ::ReprEndianness::toggle_little_endian(&mut char); + + // Multiply self by the characteristic to eliminate any prime-order subgroup + // component. + let bits = BitIterator::::new(char); + let mut res = Self::identity(); + for i in bits { + res = res.double(); + if i { + res.add_assign(self) + } + } + + // If the result is the identity, there was zero torsion component! + res.is_identity() + } +} + +/// Efficient representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait CofactorCurve: + Curve::Affine> + CofactorGroup +{ + type Affine: CofactorCurveAffine + + Mul + + for<'r> Mul; +} + +/// Affine representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait CofactorCurveAffine: + GroupEncoding + + Copy + + Clone + + Sized + + Send + + Sync + + fmt::Debug + + fmt::Display + + PartialEq + + Eq + + 'static + + Neg + + Mul<::Scalar, Output = ::Curve> + + for<'r> Mul< + ::Scalar, + Output = ::Curve, + > +{ + type Scalar: PrimeField; + type Curve: CofactorCurve; + + /// Returns the additive identity. + fn identity() -> Self; + + /// Returns a fixed generator of unknown exponent. + fn generator() -> Self; + + /// Determines if this point represents the point at infinity; the + /// additive identity. + fn is_identity(&self) -> Choice; + + /// Converts this element to its curve representation. + fn to_curve(&self) -> Self::Curve; +} diff --git a/group/src/lib.rs b/group/src/lib.rs index 839e31a3b..9696df42d 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -8,6 +8,8 @@ use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, CtOption}; +pub mod cofactor; +pub mod prime; pub mod tests; mod wnaf; @@ -54,16 +56,10 @@ pub trait Group: + Neg + GroupOps + GroupOpsOwned - + GroupOps<::Subgroup> - + GroupOpsOwned<::Subgroup> + ScalarMul<::Scalar> + ScalarMulOwned<::Scalar> { - /// The large prime-order subgroup in which cryptographic operations are performed. - /// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`. - type Subgroup: PrimeGroup; - - /// Scalars modulo the order of [`Group::Subgroup`]. + /// Scalars modulo the order of this group's scalar field. type Scalar: PrimeField; /// Returns an element chosen uniformly at random using a user-provided RNG. @@ -73,7 +69,7 @@ pub trait Group: fn identity() -> Self; /// Returns a fixed generator of the prime-order subgroup. - fn generator() -> Self::Subgroup; + fn generator() -> Self; /// Determines if this point is the identity. fn is_identity(&self) -> Choice; @@ -83,21 +79,16 @@ pub trait Group: fn double(&self) -> Self; } -/// This trait represents an element of a prime-order cryptographic group. -pub trait PrimeGroup: Group {} - -/// Projective representation of an elliptic curve point guaranteed to be -/// in the correct prime order subgroup. -pub trait CofactorCurve: - Group + GroupOps<::Affine> + GroupOpsOwned<::Affine> +/// Efficient representation of an elliptic curve point guaranteed. +pub trait Curve: + Group + GroupOps<::AffineRepr> + GroupOpsOwned<::AffineRepr> { - type Affine: CurveAffine - + Mul - + for<'r> Mul; + /// The affine representation for this elliptic curve. + type AffineRepr; /// Converts a batch of projective elements into affine elements. This function will /// panic if `p.len() != q.len()`. - fn batch_normalize(p: &[Self], q: &mut [Self::Affine]) { + fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { assert_eq!(p.len(), q.len()); for (p, q) in p.iter().zip(q.iter_mut()) { @@ -106,7 +97,7 @@ pub trait CofactorCurve: } /// Converts this element into its affine representation. - fn to_affine(&self) -> Self::Affine; + fn to_affine(&self) -> Self::AffineRepr; /// Recommends a wNAF window table size given a scalar. Always returns a number /// between 2 and 22, inclusive. @@ -117,41 +108,6 @@ pub trait CofactorCurve: fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; } -/// Affine representation of an elliptic curve point guaranteed to be -/// in the correct prime order subgroup. -pub trait CurveAffine: - Copy - + Clone - + Sized - + Send - + Sync - + fmt::Debug - + fmt::Display - + PartialEq - + Eq - + 'static - + GroupEncoding - + Neg - + Mul<::Scalar, Output = ::Curve> - + for<'r> Mul<::Scalar, Output = ::Curve> -{ - type Scalar: PrimeField; - type Curve: CofactorCurve; - - /// Returns the additive identity. - fn identity() -> Self; - - /// Returns a fixed generator of unknown exponent. - fn generator() -> Self; - - /// Determines if this point represents the point at infinity; the - /// additive identity. - fn is_identity(&self) -> Choice; - - /// Converts this element into its efficient representation. - fn to_curve(&self) -> Self::Curve; -} - pub trait GroupEncoding: Sized { /// The encoding of group elements. type Repr: Default + AsRef<[u8]> + AsMut<[u8]>; @@ -174,7 +130,7 @@ pub trait GroupEncoding: Sized { /// Affine representation of a point on an elliptic curve that has a defined uncompressed /// encoding. -pub trait UncompressedEncoding: CurveAffine { +pub trait UncompressedEncoding: Sized { type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>; /// Attempts to deserialize an element from its uncompressed encoding. diff --git a/group/src/prime.rs b/group/src/prime.rs new file mode 100644 index 000000000..d02105d79 --- /dev/null +++ b/group/src/prime.rs @@ -0,0 +1,52 @@ +use core::fmt; +use core::ops::{Mul, Neg}; +use ff::PrimeField; +use subtle::Choice; + +use crate::{Curve, Group, GroupEncoding}; + +/// This trait represents an element of a prime-order cryptographic group. +pub trait PrimeGroup: Group + GroupEncoding {} + +/// Efficient representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait PrimeCurve: Curve::Affine> + PrimeGroup { + type Affine: PrimeCurveAffine + + Mul + + for<'r> Mul; +} + +/// Affine representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait PrimeCurveAffine: + GroupEncoding + + Copy + + Clone + + Sized + + Send + + Sync + + fmt::Debug + + fmt::Display + + PartialEq + + Eq + + 'static + + Neg + + Mul<::Scalar, Output = ::Curve> + + for<'r> Mul<::Scalar, Output = ::Curve> +{ + type Scalar: PrimeField; + type Curve: PrimeCurve; + + /// Returns the additive identity. + fn identity() -> Self; + + /// Returns a fixed generator of unknown exponent. + fn generator() -> Self; + + /// Determines if this point represents the point at infinity; the + /// additive identity. + fn is_identity(&self) -> Choice; + + /// Converts this element to its curve representation. + fn to_curve(&self) -> Self::Curve; +} diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 920b44c91..50ba58135 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -3,7 +3,10 @@ use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::{Mul, Neg}; -use crate::{CofactorCurve, CurveAffine, GroupEncoding, UncompressedEncoding}; +use crate::{ + cofactor::{CofactorCurve, CofactorCurveAffine}, + GroupEncoding, UncompressedEncoding, +}; pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 64a2de1b5..98573b4ad 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -2,7 +2,7 @@ use byteorder::{ByteOrder, LittleEndian}; use ff::PrimeField; use std::iter; -use super::{CofactorCurve, Group}; +use super::{cofactor::CofactorCurve, Group}; /// Replaces the contents of `table` with a w-NAF window table for the given window size. pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { @@ -92,9 +92,9 @@ pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { found_one = true; if *n > 0 { - result.add_assign(&table[(n / 2) as usize]); + result += &table[(n / 2) as usize]; } else { - result.sub_assign(&table[((-n) / 2) as usize]); + result -= &table[((-n) / 2) as usize]; } } } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index e480700f8..bf4d96240 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -2,6 +2,7 @@ macro_rules! curve_impl { ( $name:expr, $projective:ident, + $subgroup:ident, $affine:ident, $basefield:ident, $scalarfield:ident, @@ -100,6 +101,21 @@ macro_rules! curve_impl { } } + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub struct $subgroup($projective); + + impl ::std::fmt::Display for $subgroup { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{}", self.0) + } + } + + impl From<$subgroup> for $projective { + fn from(val: $subgroup) -> $projective { + val.0 + } + } + impl $affine { fn mul_bits_u64>(&self, bits: BitIterator) -> $projective { let mut res = $projective::identity(); @@ -197,7 +213,7 @@ macro_rules! curve_impl { } } - impl CurveAffine for $affine { + impl CofactorCurveAffine for $affine { type Scalar = $scalarfield; type Curve = $projective; @@ -222,6 +238,70 @@ macro_rules! curve_impl { } } + impl GroupEncoding for $projective { + type Repr = $compressed; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + if let Ok(affine) = bytes.into_affine_unchecked() { + // NB: Decompression guarantees that it is on the curve already. + CtOption::new( + affine.into(), + Choice::from(if affine.is_in_correct_subgroup_assuming_on_curve() { + 1 + } else { + 0 + }), + ) + } else { + CtOption::new(Self::identity(), Choice::from(0)) + } + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + if let Ok(p) = bytes.into_affine_unchecked() { + CtOption::new(p.into(), Choice::from(1)) + } else { + CtOption::new(Self::identity(), Choice::from(0)) + } + } + + fn to_bytes(&self) -> Self::Repr { + self.to_affine().to_bytes() + } + } + + impl GroupEncoding for $subgroup { + type Repr = $compressed; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + if let Ok(affine) = bytes.into_affine_unchecked() { + // NB: Decompression guarantees that it is on the curve already. + CtOption::new( + $subgroup(affine.into()), + Choice::from(if affine.is_in_correct_subgroup_assuming_on_curve() { + 1 + } else { + 0 + }), + ) + } else { + CtOption::new(Self::identity(), Choice::from(0)) + } + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + if let Ok(p) = bytes.into_affine_unchecked() { + CtOption::new($subgroup(p.into()), Choice::from(1)) + } else { + CtOption::new(Self::identity(), Choice::from(0)) + } + } + + fn to_bytes(&self) -> Self::Repr { + self.0.to_bytes() + } + } + impl GroupEncoding for $affine { type Repr = $compressed; @@ -651,8 +731,182 @@ macro_rules! curve_impl { } } + impl ::std::iter::Sum for $subgroup { + fn sum>(iter: I) -> Self { + iter.fold(Self::identity(), ::std::ops::Add::add) + } + } + + impl<'r> ::std::iter::Sum<&'r $subgroup> for $subgroup { + fn sum>(iter: I) -> Self { + iter.fold(Self::identity(), ::std::ops::Add::add) + } + } + + impl ::std::ops::Neg for $subgroup { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + $subgroup(self.0.neg()) + } + } + + impl<'r> ::std::ops::Add<&'r $subgroup> for $projective { + type Output = Self; + + #[inline] + fn add(self, other: &$subgroup) -> Self { + self + &other.0 + } + } + + impl ::std::ops::Add<$subgroup> for $projective { + type Output = Self; + + #[inline] + fn add(self, other: $subgroup) -> Self { + self + &other.0 + } + } + + impl<'r> ::std::ops::AddAssign<&'r $subgroup> for $projective { + fn add_assign(&mut self, other: &$subgroup) { + self.add_assign(&other.0) + } + } + + impl ::std::ops::AddAssign<$subgroup> for $projective { + #[inline] + fn add_assign(&mut self, other: $subgroup) { + self.add_assign(&other.0); + } + } + + impl<'r> ::std::ops::Sub<&'r $subgroup> for $projective { + type Output = Self; + + #[inline] + fn sub(self, other: &$subgroup) -> Self { + self - &other.0 + } + } + + impl ::std::ops::Sub<$subgroup> for $projective { + type Output = Self; + + #[inline] + fn sub(self, other: $subgroup) -> Self { + self - &other.0 + } + } + + impl<'r> ::std::ops::SubAssign<&'r $subgroup> for $projective { + fn sub_assign(&mut self, other: &$subgroup) { + self.sub_assign(&other.0); + } + } + + impl ::std::ops::SubAssign<$subgroup> for $projective { + #[inline] + fn sub_assign(&mut self, other: $subgroup) { + self.sub_assign(&other.0); + } + } + + impl<'r> ::std::ops::Add<&'r $subgroup> for $subgroup { + type Output = Self; + + #[inline] + fn add(self, other: &$subgroup) -> Self { + $subgroup(self.0 + &other.0) + } + } + + impl ::std::ops::Add<$subgroup> for $subgroup { + type Output = Self; + + #[inline] + fn add(self, other: $subgroup) -> Self { + $subgroup(self.0 + &other.0) + } + } + + impl<'r> ::std::ops::AddAssign<&'r $subgroup> for $subgroup { + fn add_assign(&mut self, other: &$subgroup) { + self.0.add_assign(&other.0) + } + } + + impl ::std::ops::AddAssign<$subgroup> for $subgroup { + #[inline] + fn add_assign(&mut self, other: $subgroup) { + self.0.add_assign(&other.0); + } + } + + impl<'r> ::std::ops::Sub<&'r $subgroup> for $subgroup { + type Output = Self; + + #[inline] + fn sub(self, other: &$subgroup) -> Self { + $subgroup(self.0 - &other.0) + } + } + + impl ::std::ops::Sub<$subgroup> for $subgroup { + type Output = Self; + + #[inline] + fn sub(self, other: $subgroup) -> Self { + $subgroup(self.0 - &other.0) + } + } + + impl<'r> ::std::ops::SubAssign<&'r $subgroup> for $subgroup { + fn sub_assign(&mut self, other: &$subgroup) { + self.0.sub_assign(&other.0); + } + } + + impl ::std::ops::SubAssign<$subgroup> for $subgroup { + #[inline] + fn sub_assign(&mut self, other: $subgroup) { + self.0.sub_assign(&other.0); + } + } + + impl ::std::ops::Mul<<$projective as Group>::Scalar> for $subgroup { + type Output = Self; + + fn mul(mut self, other: <$projective as Group>::Scalar) -> Self { + self.0.mul_assign(&other); + self + } + } + + impl<'r> ::std::ops::Mul<&'r <$projective as Group>::Scalar> for $subgroup { + type Output = Self; + + fn mul(mut self, other: &'r <$projective as Group>::Scalar) -> Self { + self.0.mul_assign(other); + self + } + } + + impl ::std::ops::MulAssign<<$projective as Group>::Scalar> for $subgroup { + fn mul_assign(&mut self, other: <$projective as Group>::Scalar) { + self.0.mul_assign(&other); + } + } + + impl<'r> ::std::ops::MulAssign<&'r <$projective as Group>::Scalar> for $subgroup { + fn mul_assign(&mut self, other: &'r <$projective as Group>::Scalar) { + self.0.mul_assign(other) + } + } + impl Group for $projective { - type Subgroup = Self; type Scalar = $scalarfield; fn random(rng: &mut R) -> Self { @@ -740,10 +994,56 @@ macro_rules! curve_impl { } } - impl PrimeGroup for $projective {} + impl Group for $subgroup { + type Scalar = $scalarfield; - impl CofactorCurve for $projective { - type Affine = $affine; + fn random(rng: &mut R) -> Self { + $subgroup($projective::random(rng)) + } + + fn identity() -> Self { + $subgroup($projective::identity()) + } + + fn generator() -> Self { + $subgroup($projective::generator()) + } + + fn is_identity(&self) -> Choice { + self.0.is_identity() + } + + #[must_use] + fn double(&self) -> Self { + $subgroup(self.0.double()) + } + } + + impl PrimeGroup for $subgroup {} + + impl CofactorGroup for $projective { + type Subgroup = $subgroup; + + fn mul_by_cofactor(&self) -> Self::Subgroup { + $subgroup($affine::from(*self).scale_by_cofactor().into()) + } + + fn into_subgroup(self) -> CtOption { + CtOption::new( + $subgroup(self), + Choice::from( + if $affine::from(self).is_in_correct_subgroup_assuming_on_curve() { + 1 + } else { + 0 + }, + ), + ) + } + } + + impl Curve for $projective { + type AffineRepr = $affine; fn batch_normalize(p: &[Self], q: &mut [$affine]) { assert_eq!(p.len(), q.len()); @@ -804,6 +1104,10 @@ macro_rules! curve_impl { } } + impl CofactorCurve for $projective { + type Affine = $affine; + } + // The affine point X, Y is represented in the jacobian // coordinates with Z = 1. impl From<$affine> for $projective { @@ -904,7 +1208,9 @@ pub mod g1 { use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; use group::{ - CofactorCurve, CurveAffine, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, + prime::PrimeGroup, + Curve, Group, GroupEncoding, UncompressedEncoding, }; use rand_core::RngCore; use std::fmt; @@ -914,6 +1220,7 @@ pub mod g1 { curve_impl!( "G1", G1, + G1Subgroup, G1Affine, Fq, Fr, @@ -1483,7 +1790,9 @@ pub mod g2 { use crate::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, PrimeField}; use group::{ - CofactorCurve, CurveAffine, Group, GroupEncoding, PrimeGroup, UncompressedEncoding, + cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, + prime::PrimeGroup, + Curve, Group, GroupEncoding, UncompressedEncoding, }; use rand_core::RngCore; use std::fmt; @@ -1493,6 +1802,7 @@ pub mod g2 { curve_impl!( "G2", G2, + G2Subgroup, G2Affine, Fq2, Fr, diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index c0fb39356..0c3de349e 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -24,7 +24,7 @@ pub use self::fr::{Fr, FrRepr}; use super::{Engine, MillerLoopResult, MultiMillerLoop}; use ff::{BitIterator, Field}; -use group::CurveAffine; +use group::cofactor::CofactorCurveAffine; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; // The BLS parameter x for BLS12-381 is -0xd201000000010000 diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 1d9f41fc9..b698c0a0f 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -1,5 +1,8 @@ use ff::PrimeField; -use group::{CofactorCurve, CurveAffine, GroupEncoding, UncompressedEncoding}; +use group::{ + cofactor::{CofactorCurve, CofactorCurveAffine}, + GroupEncoding, UncompressedEncoding, +}; use super::*; use crate::*; diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 6898bd553..e25d364e5 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -23,8 +23,8 @@ pub mod bls12_381; use core::ops::Mul; use ff::{Field, PrimeField}; use group::{ - CofactorCurve, CurveAffine, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, - UncompressedEncoding, + cofactor::{CofactorCurve, CofactorCurveAffine}, + GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, UncompressedEncoding, }; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) @@ -80,7 +80,7 @@ pub trait Engine: Sized + 'static + Clone { /// Affine representation of an elliptic curve point that can be used /// to perform pairings. -pub trait PairingCurveAffine: CurveAffine + UncompressedEncoding { +pub trait PairingCurveAffine: CofactorCurveAffine + UncompressedEncoding { type Pair: PairingCurveAffine; type PairingResult: Field; diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs index 2cfc473b2..6e971622f 100644 --- a/pairing/src/tests/engine.rs +++ b/pairing/src/tests/engine.rs @@ -1,5 +1,5 @@ use ff::{Endianness, Field, PrimeField}; -use group::{CofactorCurve, CurveAffine, Group}; +use group::{cofactor::CofactorCurveAffine, Curve, Group}; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use std::ops::MulAssign; From ad96a38750b1f23c18765403f3b3faa26bd23d31 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 6 Jun 2020 11:29:26 +1200 Subject: [PATCH 7/7] 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. --- bellman/src/groth16/generator.rs | 6 ++++- bellman/src/groth16/tests/dummy_engine.rs | 4 +++- group/src/lib.rs | 10 +------- group/src/tests/mod.rs | 4 ++-- group/src/wnaf.rs | 29 ++++++++++++++++------- pairing/src/bls12_381/ec.rs | 12 ++++++---- 6 files changed, 40 insertions(+), 25 deletions(-) diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 084e7a376..d04ed6c0a 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -3,7 +3,7 @@ use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; use ff::{Field, PrimeField}; -use group::{cofactor::CofactorCurveAffine, Curve, Group, Wnaf}; +use group::{cofactor::CofactorCurveAffine, Curve, Group, Wnaf, WnafGroup}; use pairing::Engine; use super::{Parameters, VerifyingKey}; @@ -22,6 +22,8 @@ pub fn generate_random_parameters( ) -> Result, SynthesisError> where E: Engine, + E::G1: WnafGroup, + E::G2: WnafGroup, C: Circuit, R: RngCore, { @@ -165,6 +167,8 @@ pub fn generate_parameters( ) -> Result, SynthesisError> where E: Engine, + E::G1: WnafGroup, + E::G2: WnafGroup, C: Circuit, { let mut assembly = KeypairAssembly { diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 17e0043a1..8f11fe154 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -2,7 +2,7 @@ use ff::{Field, PrimeField}; use group::{ cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, prime::PrimeGroup, - Curve, Group, GroupEncoding, UncompressedEncoding, + Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, }; use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; @@ -414,7 +414,9 @@ impl Curve for Fr { fn to_affine(&self) -> Fr { *self } +} +impl WnafGroup for Fr { fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { 3 } diff --git a/group/src/lib.rs b/group/src/lib.rs index 9696df42d..57914f820 100644 --- a/group/src/lib.rs +++ b/group/src/lib.rs @@ -13,7 +13,7 @@ pub mod prime; pub mod tests; mod wnaf; -pub use self::wnaf::Wnaf; +pub use self::wnaf::{Wnaf, WnafGroup}; /// A helper trait for types with a group operation. pub trait GroupOps: @@ -98,14 +98,6 @@ pub trait Curve: /// Converts this element into its affine representation. 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 { diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index 50ba58135..be383b167 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -5,6 +5,7 @@ use std::ops::{Mul, Neg}; use crate::{ cofactor::{CofactorCurve, CofactorCurveAffine}, + wnaf::WnafGroup, GroupEncoding, UncompressedEncoding, }; @@ -64,11 +65,10 @@ pub fn curve_tests() { random_doubling_tests::(); random_negation_tests::(); random_transformation_tests::(); - random_wnaf_tests::(); random_compressed_encoding_tests::(); } -fn random_wnaf_tests() { +pub fn random_wnaf_tests() { use crate::wnaf::*; let mut rng = XorShiftRng::from_seed([ diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 98573b4ad..20651d437 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -2,10 +2,21 @@ use byteorder::{ByteOrder, LittleEndian}; use ff::PrimeField; 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. -pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { +pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { table.truncate(0); table.reserve(1 << (window - 1)); @@ -78,7 +89,7 @@ pub(crate) fn wnaf_form>(wnaf: &mut Vec, 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(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { +pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { let mut result = G::identity(); let mut found_one = false; @@ -110,7 +121,7 @@ pub struct Wnaf { window_size: W, } -impl Wnaf<(), Vec, Vec> { +impl Wnaf<(), Vec, Vec> { /// Construct a new wNAF context without allocating. pub fn new() -> Self { Wnaf { @@ -119,7 +130,9 @@ impl Wnaf<(), Vec, Vec> { window_size: (), } } +} +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(&mut self, base: G, num_scalars: usize) -> Wnaf> { @@ -157,7 +170,7 @@ impl Wnaf<(), Vec, Vec> { } } -impl<'a, G: CofactorCurve> Wnaf> { +impl<'a, G: Group> 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> { @@ -169,7 +182,7 @@ impl<'a, G: CofactorCurve> Wnaf> { } } -impl<'a, G: CofactorCurve> Wnaf, &'a [i64]> { +impl<'a, G: Group> Wnaf, &'a [i64]> { /// Constructs new space for the window table while borrowing /// the computed scalar representation, for sending the scalar representation /// across threads. @@ -184,7 +197,7 @@ impl<'a, G: CofactorCurve> Wnaf, &'a [i64]> { impl> Wnaf { /// Performs exponentiation given a base. - pub fn base(&mut self, base: G) -> G + pub fn base(&mut self, base: G) -> G where B: AsMut>, { @@ -195,7 +208,7 @@ impl> Wnaf { impl>> Wnaf { /// Performs exponentiation given a scalar. - pub fn scalar(&mut self, scalar: &::Scalar) -> G + pub fn scalar(&mut self, scalar: &::Scalar) -> G where B: AsRef<[G]>, { diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index bf4d96240..487455844 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -1092,7 +1092,9 @@ macro_rules! curve_impl { fn to_affine(&self) -> $affine { (*self).into() } + } + impl WnafGroup for $projective { fn recommended_wnaf_for_scalar(_: &Self::Scalar) -> usize { Self::empirical_recommended_wnaf_for_scalar( ::NUM_BITS as usize, @@ -1210,7 +1212,7 @@ pub mod g1 { use group::{ cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, prime::PrimeGroup, - Curve, Group, GroupEncoding, UncompressedEncoding, + Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, }; use rand_core::RngCore; use std::fmt; @@ -1778,8 +1780,9 @@ pub mod g1 { #[test] 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::(); + random_wnaf_tests::(); random_uncompressed_encoding_tests::(); } } @@ -1792,7 +1795,7 @@ pub mod g2 { use group::{ cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, prime::PrimeGroup, - Curve, Group, GroupEncoding, UncompressedEncoding, + Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, }; use rand_core::RngCore; use std::fmt; @@ -2484,8 +2487,9 @@ pub mod g2 { #[test] 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::(); + random_wnaf_tests::(); random_uncompressed_encoding_tests::(); } }