diff --git a/Cargo.toml b/Cargo.toml index f02410681..aefc6023e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,4 @@ repository = "https://github.com/ebfull/pairing" [dependencies] rand = "0.3" +byteorder = "1.1" diff --git a/src/bls12_381/ec.rs b/src/bls12_381/ec.rs index 5570adef5..5d20d4c79 100644 --- a/src/bls12_381/ec.rs +++ b/src/bls12_381/ec.rs @@ -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 { + 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::().unwrap(); + } + + for b in y.0.iter_mut().rev() { + *b = reader.read_u64::().unwrap(); + } + } + + Ok(G1Affine { + x: Fq::from_repr(x)?, + y: Fq::from_repr(y)?, + infinity: false + }) + } + fn from_affine(affine: G1Affine) -> Result { + 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::(*digit).unwrap(); + } + + for digit in affine.y.into_repr().as_ref().iter().rev() { + writer.write_u64::(*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 { + 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 = ©[..]; + + for b in x.0.iter_mut().rev() { + *b = reader.read_u64::().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 { + 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::(*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 { + 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::().unwrap(); + } + + for b in x_c0.0.iter_mut().rev() { + *b = reader.read_u64::().unwrap(); + } + + for b in y_c1.0.iter_mut().rev() { + *b = reader.read_u64::().unwrap(); + } + + for b in y_c0.0.iter_mut().rev() { + *b = reader.read_u64::().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 { + 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::(*digit).unwrap(); + } + + for digit in affine.x.c0.into_repr().as_ref().iter().rev() { + writer.write_u64::(*digit).unwrap(); + } + + for digit in affine.y.c1.into_repr().as_ref().iter().rev() { + writer.write_u64::(*digit).unwrap(); + } + + for digit in affine.y.c0.into_repr().as_ref().iter().rev() { + writer.write_u64::(*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 { + 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 = ©[..]; + + for b in x_c1.0.iter_mut().rev() { + *b = reader.read_u64::().unwrap(); + } + + for b in x_c0.0.iter_mut().rev() { + *b = reader.read_u64::().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 { + 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::(*digit).unwrap(); + } + + for digit in affine.x.c0.into_repr().as_ref().iter().rev() { + writer.write_u64::(*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 }; diff --git a/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs index 55da9405b..a7a66d102 100644 --- a/src/bls12_381/fq2.rs +++ b/src/bls12_381/fq2.rs @@ -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 { + 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()); diff --git a/src/bls12_381/mod.rs b/src/bls12_381/mod.rs index 43290fd69..f0ef0b93d 100644 --- a/src/bls12_381/mod.rs +++ b/src/bls12_381/mod.rs @@ -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}; diff --git a/src/bls12_381/tests/g1_compressed_test_vectors.dat b/src/bls12_381/tests/g1_compressed_test_vectors.dat new file mode 100644 index 000000000..36bf17d9d Binary files /dev/null and b/src/bls12_381/tests/g1_compressed_test_vectors.dat differ diff --git a/src/bls12_381/tests/g1_uncompressed_test_vectors.dat b/src/bls12_381/tests/g1_uncompressed_test_vectors.dat new file mode 100644 index 000000000..5194b62b6 Binary files /dev/null and b/src/bls12_381/tests/g1_uncompressed_test_vectors.dat differ diff --git a/src/bls12_381/tests/g2_compressed_test_vectors.dat b/src/bls12_381/tests/g2_compressed_test_vectors.dat new file mode 100644 index 000000000..42b6ee9c0 Binary files /dev/null and b/src/bls12_381/tests/g2_compressed_test_vectors.dat differ diff --git a/src/bls12_381/tests/g2_uncompressed_test_vectors.dat b/src/bls12_381/tests/g2_uncompressed_test_vectors.dat new file mode 100644 index 000000000..7fcbcc964 Binary files /dev/null and b/src/bls12_381/tests/g2_uncompressed_test_vectors.dat differ diff --git a/src/bls12_381/tests/mod.rs b/src/bls12_381/tests/mod.rs new file mode 100644 index 000000000..f499d9601 --- /dev/null +++ b/src/bls12_381/tests/mod.rs @@ -0,0 +1,48 @@ +use super::*; +use ::*; + +fn test_vectors>(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::(include_bytes!("g1_uncompressed_test_vectors.dat")); +} + +#[test] +fn test_g1_compressed_vectors() { + test_vectors::(include_bytes!("g1_compressed_test_vectors.dat")); +} + +#[test] +fn test_g2_uncompressed_vectors() { + test_vectors::(include_bytes!("g2_uncompressed_test_vectors.dat")); +} + +#[test] +fn test_g2_compressed_vectors() { + test_vectors::(include_bytes!("g2_compressed_test_vectors.dat")); +} + diff --git a/src/lib.rs b/src/lib.rs index 3b9c47a7b..4e27e9403 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; type Prepared: Clone + Send + Sync + 'static; + type Uncompressed: EncodedPoint; + type Compressed: EncodedPoint; /// 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 { + ::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 { + ::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 { + 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; + + /// 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; } /// This trait represents an element of a field. diff --git a/src/tests/curve.rs b/src/tests/curve.rs index 0356b64c6..61ea6c5c0 100644 --- a/src/tests/curve.rs +++ b/src/tests/curve.rs @@ -1,6 +1,6 @@ use rand::{SeedableRng, XorShiftRng, Rand}; -use ::{CurveProjective, CurveAffine, Field}; +use ::{CurveProjective, CurveAffine, Field, EncodedPoint}; pub fn curve_tests() { @@ -59,6 +59,7 @@ pub fn curve_tests() random_negation_tests::(); random_transformation_tests::(); random_wnaf_tests::(); + random_encoding_tests::(); } fn random_wnaf_tests() { @@ -291,3 +292,29 @@ fn random_transformation_tests() { assert_eq!(v, expected_v); } } + +fn random_encoding_tests() +{ + 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); + } +}