Encoding of G1/G2 elements in compressed or uncompressed form.

This commit is contained in:
Sean Bowe 2017-07-11 15:01:31 -06:00
parent 021077b56b
commit 3faf8c526a
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
11 changed files with 588 additions and 20 deletions

View File

@ -11,3 +11,4 @@ repository = "https://github.com/ebfull/pairing"
[dependencies]
rand = "0.3"
byteorder = "1.1"

View File

@ -4,7 +4,9 @@ macro_rules! curve_impl {
$affine:ident,
$prepared:ident,
$basefield:ident,
$scalarfield:ident
$scalarfield:ident,
$uncompressed:ident,
$compressed:ident
) => {
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct $affine {
@ -98,6 +100,8 @@ macro_rules! curve_impl {
type Base = $basefield;
type Prepared = $prepared;
type Projective = $projective;
type Uncompressed = $uncompressed;
type Compressed = $compressed;
fn zero() -> Self {
$affine {
@ -563,10 +567,190 @@ macro_rules! curve_impl {
pub mod g1 {
use rand::{Rand, Rng};
use super::super::{Fq, Fr, FrRepr};
use ::{CurveProjective, CurveAffine, PrimeField, PrimeFieldRepr, Field, BitIterator};
use super::super::{Fq, Fr, FrRepr, FqRepr};
use ::{CurveProjective, CurveAffine, PrimeField, SqrtField, PrimeFieldRepr, Field, BitIterator, EncodedPoint};
curve_impl!(G1, G1Affine, G1Prepared, Fq, Fr);
curve_impl!(G1, G1Affine, G1Prepared, Fq, Fr, G1Uncompressed, G1Compressed);
pub struct G1Uncompressed([u8; 96]);
impl AsRef<[u8]> for G1Uncompressed {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for G1Uncompressed {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl EncodedPoint for G1Uncompressed {
type Affine = G1Affine;
fn empty() -> Self { G1Uncompressed([0; 96]) }
fn size() -> usize { 96 }
fn into_affine_unchecked(&self) -> Result<G1Affine, ()> {
use byteorder::{ReadBytesExt, BigEndian};
let mut x = FqRepr([0; 6]);
let mut y = FqRepr([0; 6]);
{
let mut reader = &self.0[..];
for b in x.0.iter_mut().rev() {
*b = reader.read_u64::<BigEndian>().unwrap();
}
for b in y.0.iter_mut().rev() {
*b = reader.read_u64::<BigEndian>().unwrap();
}
}
Ok(G1Affine {
x: Fq::from_repr(x)?,
y: Fq::from_repr(y)?,
infinity: false
})
}
fn from_affine(affine: G1Affine) -> Result<Self, ()> {
use byteorder::{WriteBytesExt, BigEndian};
if affine.is_zero() {
return Err(())
}
let mut res = Self::empty();
{
let mut writer = &mut res.0[..];
for digit in affine.x.into_repr().as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit).unwrap();
}
for digit in affine.y.into_repr().as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit).unwrap();
}
}
Ok(res)
}
}
pub struct G1Compressed([u8; 48]);
impl AsRef<[u8]> for G1Compressed {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for G1Compressed {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl EncodedPoint for G1Compressed {
type Affine = G1Affine;
fn empty() -> Self { G1Compressed([0; 48]) }
fn size() -> usize { 48 }
fn into_affine_unchecked(&self) -> Result<G1Affine, ()> {
use byteorder::{ReadBytesExt, BigEndian};
// Create a copy of this representation.
let mut copy = self.0;
if copy[0] & (1 << 7) == 0 {
// Distinguisher bit isn't set.
return Err(())
}
// Determine if the intended y coordinate must be greater
// lexicographically.
let greatest = copy[0] & (1 << 6) != 0;
// Unset the two most significant bits.
copy[0] &= 0x3f;
let mut x = FqRepr([0; 6]);
{
let mut reader = &copy[..];
for b in x.0.iter_mut().rev() {
*b = reader.read_u64::<BigEndian>().unwrap();
}
}
// Interpret as Fq element.
let x = Fq::from_repr(x)?;
// Compute x^3 + b
let mut x3b = x;
x3b.square();
x3b.mul_assign(&x);
x3b.add_assign(&G1Affine::get_coeff_b());
// Attempt to compute y
match x3b.sqrt() {
Some(y) => {
let mut negy = y;
negy.negate();
// Get the parity of the sqrt we found.
let parity = y.into_repr() > negy.into_repr();
Ok(G1Affine {
x: x,
y: if parity == greatest { y } else { negy },
infinity: false
})
},
None => {
// Point must not be on the curve.
Err(())
}
}
}
fn from_affine(affine: G1Affine) -> Result<Self, ()> {
use byteorder::{WriteBytesExt, BigEndian};
if affine.is_zero() {
return Err(())
}
let mut res = Self::empty();
{
let mut writer = &mut res.0[..];
for digit in affine.x.into_repr().as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit).unwrap();
}
}
// Distinguish this from an uncompressed element.
res.0[0] |= 1 << 7; // Set highest bit.
{
let mut negy = affine.y;
negy.negate();
// If the correct y coordinate is the largest (lexicographically),
// the bit should be set.
if affine.y.into_repr() > negy.into_repr() {
res.0[0] |= 1 << 6; // Set second highest bit.
}
}
Ok(res)
}
}
impl G1Affine {
fn get_generator() -> Self {
@ -629,9 +813,6 @@ pub mod g1 {
}
}
#[cfg(test)]
use super::super::{FqRepr};
#[test]
fn g1_generator() {
use ::SqrtField;
@ -880,10 +1061,226 @@ pub mod g1 {
pub mod g2 {
use rand::{Rand, Rng};
use super::super::{Fq2, Fr, FrRepr};
use ::{CurveProjective, CurveAffine, PrimeField, PrimeFieldRepr, Field, BitIterator};
use super::super::{Fq2, Fr, Fq, FrRepr, FqRepr};
use ::{CurveProjective, CurveAffine, PrimeField, SqrtField, PrimeFieldRepr, Field, BitIterator, EncodedPoint};
curve_impl!(G2, G2Affine, G2Prepared, Fq2, Fr);
curve_impl!(G2, G2Affine, G2Prepared, Fq2, Fr, G2Uncompressed, G2Compressed);
pub struct G2Uncompressed([u8; 192]);
impl AsRef<[u8]> for G2Uncompressed {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for G2Uncompressed {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl EncodedPoint for G2Uncompressed {
type Affine = G2Affine;
fn empty() -> Self { G2Uncompressed([0; 192]) }
fn size() -> usize { 192 }
fn into_affine_unchecked(&self) -> Result<G2Affine, ()> {
use byteorder::{ReadBytesExt, BigEndian};
let mut x_c1 = FqRepr([0; 6]);
let mut x_c0 = FqRepr([0; 6]);
let mut y_c1 = FqRepr([0; 6]);
let mut y_c0 = FqRepr([0; 6]);
{
let mut reader = &self.0[..];
for b in x_c1.0.iter_mut().rev() {
*b = reader.read_u64::<BigEndian>().unwrap();
}
for b in x_c0.0.iter_mut().rev() {
*b = reader.read_u64::<BigEndian>().unwrap();
}
for b in y_c1.0.iter_mut().rev() {
*b = reader.read_u64::<BigEndian>().unwrap();
}
for b in y_c0.0.iter_mut().rev() {
*b = reader.read_u64::<BigEndian>().unwrap();
}
}
Ok(G2Affine {
x: Fq2 {
c0: Fq::from_repr(x_c0)?,
c1: Fq::from_repr(x_c1)?
},
y: Fq2 {
c0: Fq::from_repr(y_c0)?,
c1: Fq::from_repr(y_c1)?
},
infinity: false
})
}
fn from_affine(affine: G2Affine) -> Result<Self, ()> {
use byteorder::{WriteBytesExt, BigEndian};
if affine.is_zero() {
return Err(())
}
let mut res = Self::empty();
{
let mut writer = &mut res.0[..];
for digit in affine.x.c1.into_repr().as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit).unwrap();
}
for digit in affine.x.c0.into_repr().as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit).unwrap();
}
for digit in affine.y.c1.into_repr().as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit).unwrap();
}
for digit in affine.y.c0.into_repr().as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit).unwrap();
}
}
Ok(res)
}
}
pub struct G2Compressed([u8; 96]);
impl AsRef<[u8]> for G2Compressed {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for G2Compressed {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl EncodedPoint for G2Compressed {
type Affine = G2Affine;
fn empty() -> Self { G2Compressed([0; 96]) }
fn size() -> usize { 96 }
fn into_affine_unchecked(&self) -> Result<G2Affine, ()> {
use byteorder::{ReadBytesExt, BigEndian};
// Create a copy of this representation.
let mut copy = self.0;
if copy[0] & (1 << 7) == 0 {
// Distinguisher bit isn't set.
return Err(())
}
// Determine if the intended y coordinate must be greater
// lexicographically.
let greatest = copy[0] & (1 << 6) != 0;
// Unset the two most significant bits.
copy[0] &= 0x3f;
let mut x_c1 = FqRepr([0; 6]);
let mut x_c0 = FqRepr([0; 6]);
{
let mut reader = &copy[..];
for b in x_c1.0.iter_mut().rev() {
*b = reader.read_u64::<BigEndian>().unwrap();
}
for b in x_c0.0.iter_mut().rev() {
*b = reader.read_u64::<BigEndian>().unwrap();
}
}
// Interpret as Fq element.
let x = Fq2 {
c0: Fq::from_repr(x_c0)?,
c1: Fq::from_repr(x_c1)?
};
// Compute x^3 + b
let mut x3b = x;
x3b.square();
x3b.mul_assign(&x);
x3b.add_assign(&G2Affine::get_coeff_b());
// Attempt to compute y
match x3b.sqrt() {
Some(y) => {
let mut negy = y;
negy.negate();
// Get the parity of the sqrt we found.
let parity = y > negy;
Ok(G2Affine {
x: x,
y: if parity == greatest { y } else { negy },
infinity: false
})
},
None => {
// Point must not be on the curve.
Err(())
}
}
}
fn from_affine(affine: G2Affine) -> Result<Self, ()> {
use byteorder::{WriteBytesExt, BigEndian};
if affine.is_zero() {
return Err(())
}
let mut res = Self::empty();
{
let mut writer = &mut res.0[..];
for digit in affine.x.c1.into_repr().as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit).unwrap();
}
for digit in affine.x.c0.into_repr().as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit).unwrap();
}
}
// Distinguish this from an uncompressed element.
res.0[0] |= 1 << 7; // Set highest bit.
{
let mut negy = affine.y;
negy.negate();
// If the correct y coordinate is the largest (lexicographically),
// the bit should be set.
if affine.y > negy {
res.0[0] |= 1 << 6; // Set second highest bit.
}
}
Ok(res)
}
}
impl G2Affine {
fn get_generator() -> Self {
@ -948,9 +1345,6 @@ pub mod g2 {
pub(crate) infinity: bool
}
#[cfg(test)]
use super::super::{Fq, FqRepr};
#[test]
fn g2_generator() {
use ::SqrtField;
@ -965,14 +1359,12 @@ pub mod g2 {
rhs.add_assign(&G2Affine::get_coeff_b());
if let Some(y) = rhs.sqrt() {
let yrepr = y.c1.into_repr();
let mut negy = y;
negy.negate();
let negyrepr = negy.c1.into_repr();
let p = G2Affine {
x: x,
y: if yrepr < negyrepr { y } else { negy },
y: if y < negy { y } else { negy },
infinity: false
};

View File

@ -1,7 +1,9 @@
use rand::{Rng, Rand};
use ::{Field, SqrtField};
use ::{Field, SqrtField, PrimeField};
use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE};
use std::cmp::Ordering;
/// An element of F_{q^2}, represented by c0 + c1 * u.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Fq2 {
@ -9,6 +11,24 @@ pub struct Fq2 {
pub c1: Fq
}
impl Ord for Fq2 {
#[inline(always)]
fn cmp(&self, other: &Fq2) -> Ordering {
match self.c1.into_repr().cmp(&other.c1.into_repr()) {
Ordering::Greater => Ordering::Greater,
Ordering::Less => Ordering::Less,
Ordering::Equal => self.c0.into_repr().cmp(&other.c0.into_repr())
}
}
}
impl PartialOrd for Fq2 {
#[inline(always)]
fn partial_cmp(&self, other: &Fq2) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Fq2 {
/// Multiply this element by the cubic and quadratic nonresidue 1 + u.
pub fn mul_by_nonresidue(&mut self) {
@ -157,6 +177,30 @@ impl SqrtField for Fq2 {
}
}
#[test]
fn test_fq2_ordering() {
let mut a = Fq2 {
c0: Fq::zero(),
c1: Fq::zero()
};
let mut b = a.clone();
assert!(a.cmp(&b) == Ordering::Equal);
b.c0.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Less);
a.c0.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Equal);
b.c1.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Less);
a.c0.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Less);
a.c1.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Greater);
b.c0.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Equal);
}
#[test]
fn test_fq2_basics() {
assert_eq!(Fq2 { c0: Fq::zero(), c1: Fq::zero() }, Fq2::zero());

View File

@ -5,11 +5,14 @@ mod fq6;
mod fq12;
mod ec;
#[cfg(test)]
mod tests;
pub use self::fr::{Fr, FrRepr};
pub use self::fq::{Fq, FqRepr};
pub use self::fq2::Fq2;
pub use self::fq12::Fq12;
pub use self::ec::{G1, G2, G1Affine, G2Affine, G1Prepared, G2Prepared};
pub use self::ec::{G1, G2, G1Affine, G2Affine, G1Prepared, G2Prepared, G1Uncompressed, G2Uncompressed, G1Compressed, G2Compressed};
use super::{Engine, CurveAffine, Field, BitIterator};

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,48 @@
use super::*;
use ::*;
fn test_vectors<G: CurveProjective, E: EncodedPoint<Affine=G::Affine>>(expected: &[u8])
{
let mut e = G::one();
let mut v = vec![];
{
let mut expected = expected;
for _ in 0..1000 {
let e_affine = e.to_affine();
let encoded = E::from_affine(e_affine).unwrap();
v.extend_from_slice(encoded.as_ref());
let mut decoded = E::empty();
decoded.as_mut().copy_from_slice(&expected[0..E::size()]);
expected = &expected[E::size()..];
let decoded = decoded.into_affine().unwrap();
assert_eq!(e_affine, decoded);
e.add_assign(&G::one());
}
}
assert_eq!(&v[..], expected);
}
#[test]
fn test_g1_uncompressed_vectors() {
test_vectors::<G1, G1Uncompressed>(include_bytes!("g1_uncompressed_test_vectors.dat"));
}
#[test]
fn test_g1_compressed_vectors() {
test_vectors::<G1, G1Compressed>(include_bytes!("g1_compressed_test_vectors.dat"));
}
#[test]
fn test_g2_uncompressed_vectors() {
test_vectors::<G2, G2Uncompressed>(include_bytes!("g2_uncompressed_test_vectors.dat"));
}
#[test]
fn test_g2_compressed_vectors() {
test_vectors::<G2, G2Compressed>(include_bytes!("g2_compressed_test_vectors.dat"));
}

View File

@ -5,6 +5,7 @@
extern crate test;
extern crate rand;
extern crate byteorder;
#[cfg(test)]
pub mod tests;
@ -151,6 +152,8 @@ pub trait CurveAffine: Copy +
type Base: SqrtField;
type Projective: CurveProjective<Affine=Self, Scalar=Self::Scalar>;
type Prepared: Clone + Send + Sync + 'static;
type Uncompressed: EncodedPoint<Affine=Self>;
type Compressed: EncodedPoint<Affine=Self>;
/// Returns the additive identity.
fn zero() -> Self;
@ -176,6 +179,56 @@ pub trait CurveAffine: Copy +
/// Converts this element into its affine representation.
fn to_projective(&self) -> Self::Projective;
/// Converts this element into its compressed encoding, so long as it's not
/// the point at infinity.
fn to_compressed(&self) -> Result<Self::Compressed, ()> {
<Self::Compressed as EncodedPoint>::from_affine(*self)
}
/// Converts this element into its uncompressed encoding, so long as it's not
/// the point at infinity.
fn to_uncompressed(&self) -> Result<Self::Uncompressed, ()> {
<Self::Uncompressed as EncodedPoint>::from_affine(*self)
}
}
/// An encoded elliptic curve point, which should essentially wrap a `[u8; N]`.
pub trait EncodedPoint: Sized +
Send +
Sync +
AsRef<[u8]> +
AsMut<[u8]> +
'static
{
type Affine: CurveAffine;
/// Creates an empty representation.
fn empty() -> Self;
/// Returns the number of bytes consumed by this representation.
fn size() -> usize;
/// Converts an `EncodedPoint` into a `CurveAffine` element,
/// if the point is valid.
fn into_affine(&self) -> Result<Self::Affine, ()> {
let affine = self.into_affine_unchecked()?;
if affine.is_valid() {
Ok(affine)
} else {
Err(())
}
}
/// Converts an `EncodedPoint` into a `CurveAffine` element,
/// without checking if it's a valid point. Caller must be careful
/// when using this, as misuse can violate API invariants.
fn into_affine_unchecked(&self) -> Result<Self::Affine, ()>;
/// Creates an `EncodedPoint` from an affine point, as long as the
/// point is not the point at infinity.
fn from_affine(affine: Self::Affine) -> Result<Self, ()>;
}
/// This trait represents an element of a field.

View File

@ -1,6 +1,6 @@
use rand::{SeedableRng, XorShiftRng, Rand};
use ::{CurveProjective, CurveAffine, Field};
use ::{CurveProjective, CurveAffine, Field, EncodedPoint};
pub fn curve_tests<G: CurveProjective>()
{
@ -59,6 +59,7 @@ pub fn curve_tests<G: CurveProjective>()
random_negation_tests::<G>();
random_transformation_tests::<G>();
random_wnaf_tests::<G>();
random_encoding_tests::<G::Affine>();
}
fn random_wnaf_tests<G: CurveProjective>() {
@ -291,3 +292,29 @@ fn random_transformation_tests<G: CurveProjective>() {
assert_eq!(v, expected_v);
}
}
fn random_encoding_tests<G: CurveAffine>()
{
assert!(G::zero().to_compressed().is_err());
assert!(G::zero().to_uncompressed().is_err());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let mut r = G::Projective::rand(&mut rng).to_affine();
let uncompressed = r.to_uncompressed().unwrap();
let de_uncompressed = uncompressed.into_affine().unwrap();
assert_eq!(de_uncompressed, r);
let compressed = r.to_compressed().unwrap();
let de_compressed = compressed.into_affine().unwrap();
assert_eq!(de_compressed, r);
r.negate();
let compressed = r.to_compressed().unwrap();
let de_compressed = compressed.into_affine().unwrap();
assert_eq!(de_compressed, r);
}
}