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.
This commit is contained in:
parent
4e685a847d
commit
ae2d2b59b9
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! [Groth16]: https://eprint.iacr.org/2016/260
|
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||||
|
|
||||||
use group::CurveAffine;
|
use group::{CurveAffine, UncompressedEncoding};
|
||||||
use pairing::{Engine, MultiMillerLoop};
|
use pairing::{Engine, MultiMillerLoop};
|
||||||
|
|
||||||
use crate::SynthesisError;
|
use crate::SynthesisError;
|
||||||
|
@ -158,7 +158,7 @@ impl<E: Engine> VerifyingKey<E> {
|
||||||
|
|
||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
|
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
|
||||||
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::default();
|
let mut g1_repr = <E::G1Affine as UncompressedEncoding>::Uncompressed::default();
|
||||||
reader.read_exact(g1_repr.as_mut())?;
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
|
|
||||||
let affine = E::G1Affine::from_uncompressed(&g1_repr);
|
let affine = E::G1Affine::from_uncompressed(&g1_repr);
|
||||||
|
@ -170,7 +170,7 @@ impl<E: Engine> VerifyingKey<E> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
|
let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
|
||||||
let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::default();
|
let mut g2_repr = <E::G2Affine as UncompressedEncoding>::Uncompressed::default();
|
||||||
reader.read_exact(g2_repr.as_mut())?;
|
reader.read_exact(g2_repr.as_mut())?;
|
||||||
|
|
||||||
let affine = E::G2Affine::from_uncompressed(&g2_repr);
|
let affine = E::G2Affine::from_uncompressed(&g2_repr);
|
||||||
|
@ -289,7 +289,7 @@ impl<E: Engine> Parameters<E> {
|
||||||
|
|
||||||
pub fn read<R: Read>(mut reader: R, checked: bool) -> io::Result<Self> {
|
pub fn read<R: Read>(mut reader: R, checked: bool) -> io::Result<Self> {
|
||||||
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
|
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
|
||||||
let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::default();
|
let mut repr = <E::G1Affine as UncompressedEncoding>::Uncompressed::default();
|
||||||
reader.read_exact(repr.as_mut())?;
|
reader.read_exact(repr.as_mut())?;
|
||||||
|
|
||||||
let affine = if checked {
|
let affine = if checked {
|
||||||
|
@ -317,7 +317,7 @@ impl<E: Engine> Parameters<E> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
|
let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
|
||||||
let mut repr = <E::G2Affine as CurveAffine>::Uncompressed::default();
|
let mut repr = <E::G2Affine as UncompressedEncoding>::Uncompressed::default();
|
||||||
reader.read_exact(repr.as_mut())?;
|
reader.read_exact(repr.as_mut())?;
|
||||||
|
|
||||||
let affine = if checked {
|
let affine = if checked {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use ff::{Field, PrimeField};
|
use ff::{Field, PrimeField};
|
||||||
use group::{CurveAffine, CurveProjective, Group, PrimeGroup};
|
use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding};
|
||||||
use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine};
|
use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine};
|
||||||
|
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
|
@ -434,7 +434,6 @@ impl AsRef<[u8]> for FakePoint {
|
||||||
|
|
||||||
impl CurveAffine for Fr {
|
impl CurveAffine for Fr {
|
||||||
type Compressed = FakePoint;
|
type Compressed = FakePoint;
|
||||||
type Uncompressed = FakePoint;
|
|
||||||
type Projective = Fr;
|
type Projective = Fr;
|
||||||
type Scalar = Fr;
|
type Scalar = Fr;
|
||||||
|
|
||||||
|
@ -465,6 +464,10 @@ impl CurveAffine for Fr {
|
||||||
fn to_compressed(&self) -> Self::Compressed {
|
fn to_compressed(&self) -> Self::Compressed {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UncompressedEncoding for Fr {
|
||||||
|
type Uncompressed = FakePoint;
|
||||||
|
|
||||||
fn from_uncompressed(_bytes: &Self::Uncompressed) -> CtOption<Self> {
|
fn from_uncompressed(_bytes: &Self::Uncompressed) -> CtOption<Self> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
|
|
@ -132,7 +132,6 @@ pub trait CurveAffine:
|
||||||
{
|
{
|
||||||
type Scalar: PrimeField;
|
type Scalar: PrimeField;
|
||||||
type Projective: CurveProjective<Affine = Self, Scalar = Self::Scalar>;
|
type Projective: CurveProjective<Affine = Self, Scalar = Self::Scalar>;
|
||||||
type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>;
|
|
||||||
type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>;
|
type Compressed: Default + AsRef<[u8]> + AsMut<[u8]>;
|
||||||
|
|
||||||
/// Returns the additive identity.
|
/// Returns the additive identity.
|
||||||
|
@ -162,6 +161,12 @@ pub trait CurveAffine:
|
||||||
/// Converts this element into its compressed encoding, so long as it's not
|
/// Converts this element into its compressed encoding, so long as it's not
|
||||||
/// the point at infinity.
|
/// the point at infinity.
|
||||||
fn to_compressed(&self) -> Self::Compressed;
|
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.
|
/// Attempts to deserialize an element from its uncompressed encoding.
|
||||||
fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self>;
|
fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self>;
|
||||||
|
@ -171,7 +176,7 @@ pub trait CurveAffine:
|
||||||
///
|
///
|
||||||
/// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
|
/// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
|
||||||
/// API invariants may be broken.** Please consider using
|
/// API invariants may be broken.** Please consider using
|
||||||
/// [`CurveAffine::from_uncompressed`] instead.
|
/// [`UncompressedEncoding::from_uncompressed`] instead.
|
||||||
fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption<Self>;
|
fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption<Self>;
|
||||||
|
|
||||||
/// Converts this element into its uncompressed encoding, so long as it's not
|
/// Converts this element into its uncompressed encoding, so long as it's not
|
||||||
|
|
|
@ -3,7 +3,7 @@ use rand::SeedableRng;
|
||||||
use rand_xorshift::XorShiftRng;
|
use rand_xorshift::XorShiftRng;
|
||||||
use std::ops::{Mul, Neg};
|
use std::ops::{Mul, Neg};
|
||||||
|
|
||||||
use crate::{CurveAffine, CurveProjective};
|
use crate::{CurveAffine, CurveProjective, UncompressedEncoding};
|
||||||
|
|
||||||
pub fn curve_tests<G: CurveProjective>() {
|
pub fn curve_tests<G: CurveProjective>() {
|
||||||
let mut rng = XorShiftRng::from_seed([
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
@ -62,7 +62,7 @@ pub fn curve_tests<G: CurveProjective>() {
|
||||||
random_negation_tests::<G>();
|
random_negation_tests::<G>();
|
||||||
random_transformation_tests::<G>();
|
random_transformation_tests::<G>();
|
||||||
random_wnaf_tests::<G>();
|
random_wnaf_tests::<G>();
|
||||||
random_encoding_tests::<G>();
|
random_compressed_encoding_tests::<G>();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_wnaf_tests<G: CurveProjective>() {
|
fn random_wnaf_tests<G: CurveProjective>() {
|
||||||
|
@ -394,17 +394,12 @@ fn random_transformation_tests<G: CurveProjective>() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_encoding_tests<G: CurveProjective>() {
|
fn random_compressed_encoding_tests<G: CurveProjective>() {
|
||||||
let mut rng = XorShiftRng::from_seed([
|
let mut rng = XorShiftRng::from_seed([
|
||||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
0xe5,
|
0xe5,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
G::Affine::from_uncompressed(&G::Affine::identity().to_uncompressed()).unwrap(),
|
|
||||||
G::Affine::identity()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
G::Affine::from_compressed(&G::Affine::identity().to_compressed()).unwrap(),
|
G::Affine::from_compressed(&G::Affine::identity().to_compressed()).unwrap(),
|
||||||
G::Affine::identity()
|
G::Affine::identity()
|
||||||
|
@ -413,10 +408,6 @@ fn random_encoding_tests<G: CurveProjective>() {
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let mut r = G::random(&mut rng).to_affine();
|
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 compressed = r.to_compressed();
|
||||||
let de_compressed = G::Affine::from_compressed(&compressed).unwrap();
|
let de_compressed = G::Affine::from_compressed(&compressed).unwrap();
|
||||||
assert_eq!(de_compressed, r);
|
assert_eq!(de_compressed, r);
|
||||||
|
@ -428,3 +419,26 @@ fn random_encoding_tests<G: CurveProjective>() {
|
||||||
assert_eq!(de_compressed, r);
|
assert_eq!(de_compressed, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn random_uncompressed_encoding_tests<G: CurveProjective>()
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -200,7 +200,6 @@ macro_rules! curve_impl {
|
||||||
impl CurveAffine for $affine {
|
impl CurveAffine for $affine {
|
||||||
type Scalar = $scalarfield;
|
type Scalar = $scalarfield;
|
||||||
type Projective = $projective;
|
type Projective = $projective;
|
||||||
type Uncompressed = $uncompressed;
|
|
||||||
type Compressed = $compressed;
|
type Compressed = $compressed;
|
||||||
|
|
||||||
fn identity() -> Self {
|
fn identity() -> Self {
|
||||||
|
@ -248,6 +247,10 @@ macro_rules! curve_impl {
|
||||||
fn to_compressed(&self) -> Self::Compressed {
|
fn to_compressed(&self) -> Self::Compressed {
|
||||||
$compressed::from_affine(*self)
|
$compressed::from_affine(*self)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UncompressedEncoding for $affine {
|
||||||
|
type Uncompressed = $uncompressed;
|
||||||
|
|
||||||
fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self> {
|
fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self> {
|
||||||
Self::from_uncompressed_unchecked(bytes).and_then(|affine| {
|
Self::from_uncompressed_unchecked(bytes).and_then(|affine| {
|
||||||
|
@ -901,7 +904,7 @@ pub mod g1 {
|
||||||
use super::{g2::G2Affine, GroupDecodingError};
|
use super::{g2::G2Affine, GroupDecodingError};
|
||||||
use crate::{Engine, PairingCurveAffine};
|
use crate::{Engine, PairingCurveAffine};
|
||||||
use ff::{BitIterator, Field, PrimeField};
|
use ff::{BitIterator, Field, PrimeField};
|
||||||
use group::{CurveAffine, CurveProjective, Group, PrimeGroup};
|
use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding};
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||||
|
@ -1467,8 +1470,9 @@ pub mod g1 {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn g1_curve_tests() {
|
fn g1_curve_tests() {
|
||||||
use group::tests::curve_tests;
|
use group::tests::{curve_tests, random_uncompressed_encoding_tests};
|
||||||
curve_tests::<G1>();
|
curve_tests::<G1>();
|
||||||
|
random_uncompressed_encoding_tests::<G1>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1477,7 +1481,7 @@ pub mod g2 {
|
||||||
use super::{g1::G1Affine, GroupDecodingError};
|
use super::{g1::G1Affine, GroupDecodingError};
|
||||||
use crate::{Engine, PairingCurveAffine};
|
use crate::{Engine, PairingCurveAffine};
|
||||||
use ff::{BitIterator, Field, PrimeField};
|
use ff::{BitIterator, Field, PrimeField};
|
||||||
use group::{CurveAffine, CurveProjective, Group, PrimeGroup};
|
use group::{CurveAffine, CurveProjective, Group, PrimeGroup, UncompressedEncoding};
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||||
|
@ -2167,8 +2171,9 @@ pub mod g2 {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn g2_curve_tests() {
|
fn g2_curve_tests() {
|
||||||
use group::tests::curve_tests;
|
use group::tests::{curve_tests, random_uncompressed_encoding_tests};
|
||||||
curve_tests::<G2>();
|
curve_tests::<G2>();
|
||||||
|
random_uncompressed_encoding_tests::<G2>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
use group::{CurveAffine, CurveProjective};
|
use group::{CurveAffine, CurveProjective, UncompressedEncoding};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
@ -55,9 +55,12 @@ fn test_pairing_result_against_relic() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uncompressed_test_vectors<G: CurveProjective>(expected: &[u8]) {
|
fn uncompressed_test_vectors<G: CurveProjective>(expected: &[u8])
|
||||||
|
where
|
||||||
|
G::Affine: UncompressedEncoding,
|
||||||
|
{
|
||||||
let mut e = G::identity();
|
let mut e = G::identity();
|
||||||
let encoded_len = <G::Affine as CurveAffine>::Uncompressed::default()
|
let encoded_len = <G::Affine as UncompressedEncoding>::Uncompressed::default()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.len();
|
.len();
|
||||||
|
|
||||||
|
@ -69,7 +72,7 @@ fn uncompressed_test_vectors<G: CurveProjective>(expected: &[u8]) {
|
||||||
let encoded = e_affine.to_uncompressed();
|
let encoded = e_affine.to_uncompressed();
|
||||||
v.extend_from_slice(encoded.as_ref());
|
v.extend_from_slice(encoded.as_ref());
|
||||||
|
|
||||||
let mut decoded = <G::Affine as CurveAffine>::Uncompressed::default();
|
let mut decoded = <G::Affine as UncompressedEncoding>::Uncompressed::default();
|
||||||
decoded.as_mut().copy_from_slice(&expected[0..encoded_len]);
|
decoded.as_mut().copy_from_slice(&expected[0..encoded_len]);
|
||||||
expected = &expected[encoded_len..];
|
expected = &expected[encoded_len..];
|
||||||
let decoded = G::Affine::from_uncompressed(&decoded).unwrap();
|
let decoded = G::Affine::from_uncompressed(&decoded).unwrap();
|
||||||
|
|
|
@ -22,7 +22,10 @@ pub mod bls12_381;
|
||||||
|
|
||||||
use core::ops::Mul;
|
use core::ops::Mul;
|
||||||
use ff::{Field, PrimeField};
|
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.)
|
/// An "engine" is a collection of types (fields, elliptic curve groups, etc.)
|
||||||
/// with well-defined relationships. In particular, the G1/G2 curve groups are
|
/// 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
|
/// Affine representation of an elliptic curve point that can be used
|
||||||
/// to perform pairings.
|
/// to perform pairings.
|
||||||
pub trait PairingCurveAffine: CurveAffine {
|
pub trait PairingCurveAffine: CurveAffine + UncompressedEncoding {
|
||||||
type Pair: PairingCurveAffine<Pair = Self>;
|
type Pair: PairingCurveAffine<Pair = Self>;
|
||||||
type PairingResult: Field;
|
type PairingResult: Field;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue