Added G1/G2/Fq2 serialization and test vectors.

This commit is contained in:
Sean Bowe 2016-09-09 01:12:25 -06:00
parent cb2ff5c1ad
commit b05ecd3bb0
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
4 changed files with 30287 additions and 35 deletions

View File

@ -2,6 +2,8 @@ use fields::{FieldElement, const_fp, Fq};
use std::ops::{Add, Sub, Mul, Neg};
use rand::Rng;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
#[inline]
fn non_residue() -> Fq {
// (q - 1) is a quadratic nonresidue in Fq
@ -15,6 +17,26 @@ pub struct Fq2 {
c1: Fq
}
impl Encodable for Fq2 {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
// TODO: multiply c0 and c1 during encoding
try!(self.c0.encode(s));
try!(self.c1.encode(s));
Ok(())
}
}
impl Decodable for Fq2 {
fn decode<S: Decoder>(s: &mut S) -> Result<Fq2, S::Error> {
// TODO: divrem to get c0 and c1
let c0 = try!(Fq::decode(s));
let c1 = try!(Fq::decode(s));
Ok(Fq2::new(c0, c1))
}
}
impl Fq2 {
pub fn new(c0: Fq, c1: Fq) -> Self {
Fq2 {
@ -22,6 +44,13 @@ impl Fq2 {
c1: c1
}
}
pub fn scale(&self, by: Fq) -> Self {
Fq2 {
c0: self.c0 * by,
c1: self.c1 * by
}
}
}
impl FieldElement for Fq2 {

View File

@ -4,6 +4,8 @@ use arith::U256;
use std::fmt;
use rand::Rng;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
pub trait GroupElement: Sized +
Copy +
Clone +
@ -23,36 +25,42 @@ pub trait GroupElement: Sized +
}
pub trait GroupParams: Sized {
type Base: FieldElement;
type Base: FieldElement + Decodable + Encodable;
fn name() -> &'static str;
fn one() -> Jacobian<Self>;
fn one() -> G<Self>;
fn coeff_b() -> Self::Base;
}
pub struct Jacobian<P: GroupParams> {
pub struct G<P: GroupParams> {
x: P::Base,
y: P::Base,
z: P::Base
}
impl<P: GroupParams> fmt::Debug for Jacobian<P> {
pub struct AffineG<P: GroupParams> {
x: P::Base,
y: P::Base
}
impl<P: GroupParams> fmt::Debug for G<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}({:?}, {:?}, {:?})", P::name(), self.x, self.y, self.z)
}
}
impl<P: GroupParams> Clone for Jacobian<P> {
impl<P: GroupParams> Clone for G<P> {
fn clone(&self) -> Self {
Jacobian {
G {
x: self.x,
y: self.y,
z: self.z
}
}
}
impl<P: GroupParams> Copy for Jacobian<P> {}
impl<P: GroupParams> Copy for G<P> {}
impl<P: GroupParams> PartialEq for Jacobian<P> {
impl<P: GroupParams> PartialEq for G<P> {
fn eq(&self, other: &Self) -> bool {
if self.is_zero() {
return other.is_zero()
@ -79,11 +87,81 @@ impl<P: GroupParams> PartialEq for Jacobian<P> {
return true;
}
}
impl<P: GroupParams> Eq for Jacobian<P> { }
impl<P: GroupParams> Eq for G<P> { }
impl<P: GroupParams> GroupElement for Jacobian<P> {
impl<P: GroupParams> G<P> {
fn to_affine(&self) -> Option<AffineG<P>> {
if self.z.is_zero() {
None
} else {
let zinv = self.z.inverse().unwrap();
let zinv_squared = zinv.squared();
Some(AffineG {
x: self.x * zinv_squared,
y: self.y * (zinv_squared * zinv)
})
}
}
}
impl<P: GroupParams> AffineG<P> {
fn to_jacobian(&self) -> G<P> {
G {
x: self.x,
y: self.y,
z: P::Base::one()
}
}
}
impl<P: GroupParams> Encodable for G<P> {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
match self.to_affine() {
None => {
let l: u8 = 0;
try!(l.encode(s));
},
Some(p) => {
let l: u8 = 4;
try!(l.encode(s));
try!(p.x.encode(s));
try!(p.y.encode(s));
}
}
Ok(())
}
}
impl<P: GroupParams> Decodable for G<P> {
fn decode<S: Decoder>(s: &mut S) -> Result<G<P>, S::Error> {
let l = try!(u8::decode(s));
if l == 0 {
Ok(G::zero())
} else if l == 4 {
let x = try!(P::Base::decode(s));
let y = try!(P::Base::decode(s));
// y^2 = x^3 + b
if y.squared() == (x.squared() * x) + P::coeff_b() {
Ok(G {
x: x,
y: y,
z: P::Base::one()
})
} else {
Err(s.error("point is not on the curve"))
}
} else {
Err(s.error("invalid leading byte for uncompressed group element"))
}
}
}
impl<P: GroupParams> GroupElement for G<P> {
fn zero() -> Self {
Jacobian {
G {
x: P::Base::zero(),
y: P::Base::one(),
z: P::Base::zero()
@ -116,7 +194,7 @@ impl<P: GroupParams> GroupElement for Jacobian<P> {
eight_c = eight_c + eight_c;
let y1z1 = self.y * self.z;
Jacobian {
G {
x: x3,
y: e * (d - x3) - eight_c,
z: y1z1 + y1z1
@ -124,11 +202,11 @@ impl<P: GroupParams> GroupElement for Jacobian<P> {
}
}
impl<P: GroupParams> Mul<Fr> for Jacobian<P> {
type Output = Jacobian<P>;
impl<P: GroupParams> Mul<Fr> for G<P> {
type Output = G<P>;
fn mul(self, other: Fr) -> Jacobian<P> {
let mut res = Jacobian::zero();
fn mul(self, other: Fr) -> G<P> {
let mut res = G::zero();
let mut found_one = false;
for i in U256::from(other).bits() {
@ -146,10 +224,10 @@ impl<P: GroupParams> Mul<Fr> for Jacobian<P> {
}
}
impl<P: GroupParams> Add<Jacobian<P>> for Jacobian<P> {
type Output = Jacobian<P>;
impl<P: GroupParams> Add<G<P>> for G<P> {
type Output = G<P>;
fn add(self, other: Jacobian<P>) -> Jacobian<P> {
fn add(self, other: G<P>) -> G<P> {
if self.is_zero() {
return other;
}
@ -179,7 +257,7 @@ impl<P: GroupParams> Add<Jacobian<P>> for Jacobian<P> {
let s1_j = s1 * j;
let x3 = r.squared() - j - (v + v);
Jacobian {
G {
x: x3,
y: r * (v - x3) - (s1_j + s1_j),
z: ((self.z + other.z).squared() - z1_squared - z2_squared) * h
@ -188,11 +266,11 @@ impl<P: GroupParams> Add<Jacobian<P>> for Jacobian<P> {
}
}
impl<P: GroupParams> Neg for Jacobian<P> {
type Output = Jacobian<P>;
impl<P: GroupParams> Neg for G<P> {
type Output = G<P>;
fn neg(self) -> Jacobian<P> {
Jacobian {
fn neg(self) -> G<P> {
G {
x: self.x,
y: -self.y,
z: self.z
@ -200,10 +278,10 @@ impl<P: GroupParams> Neg for Jacobian<P> {
}
}
impl<P: GroupParams> Sub<Jacobian<P>> for Jacobian<P> {
type Output = Jacobian<P>;
impl<P: GroupParams> Sub<G<P>> for G<P> {
type Output = G<P>;
fn sub(self, other: Jacobian<P>) -> Jacobian<P> {
fn sub(self, other: G<P>) -> G<P> {
self + (-other)
}
}
@ -215,16 +293,20 @@ impl GroupParams for G1Params {
fn name() -> &'static str { "G1" }
fn one() -> Jacobian<Self> {
Jacobian {
fn one() -> G<Self> {
G {
x: Fq::one(),
y: const_fp([0xa6ba871b8b1e1b3a, 0x14f1d651eb8e167b, 0xccdd46def0f28c58, 0x1c14ef83340fbe5e]),
z: Fq::one()
}
}
fn coeff_b() -> Fq {
const_fp([0x7a17caa950ad28d7, 0x1f6ac17ae15521b9, 0x334bea4e696bd284, 0x2a1f6744ce179d8e])
}
}
pub type G1 = Jacobian<G1Params>;
pub type G1 = G<G1Params>;
pub struct G2Params;
@ -233,16 +315,29 @@ impl GroupParams for G2Params {
fn name() -> &'static str { "G2" }
fn one() -> Jacobian<Self> {
Jacobian {
x: Fq2::new(const_fp([0x8e83b5d102bc2026, 0xdceb1935497b0172, 0xfbb8264797811adf, 0x19573841af96503b]), const_fp([0xafb4737da84c6140, 0x6043dd5a5802d8c4, 0x09e950fc52a02f86, 0x14fef0833aea7b6b])),
y: Fq2::new(const_fp([0x619dfa9d886be9f6, 0xfe7fd297f59e9b78, 0xff9e1a62231b7dfe, 0x28fd7eebae9e4206]), const_fp([0x64095b56c71856ee, 0xdc57f922327d3cbb, 0x55f935be33351076, 0x0da4a0e693fd6482])),
fn one() -> G<Self> {
G {
x: Fq2::new(
const_fp([0x8e83b5d102bc2026, 0xdceb1935497b0172, 0xfbb8264797811adf, 0x19573841af96503b]),
const_fp([0xafb4737da84c6140, 0x6043dd5a5802d8c4, 0x09e950fc52a02f86, 0x14fef0833aea7b6b])
),
y: Fq2::new(
const_fp([0x619dfa9d886be9f6, 0xfe7fd297f59e9b78, 0xff9e1a62231b7dfe, 0x28fd7eebae9e4206]),
const_fp([0x64095b56c71856ee, 0xdc57f922327d3cbb, 0x55f935be33351076, 0x0da4a0e693fd6482])
),
z: Fq2::one()
}
}
fn coeff_b() -> Fq2 {
Fq2::new(
const_fp([0x3bf938e377b802a8, 0x020b1b273633535d, 0x26b7edf049755260, 0x2514c6324384a86d]),
const_fp([0x38e7ecccd1dcff67, 0x65f0b37d93ce0d3e, 0xd749d0dd22ac00aa, 0x0141b9ce4a688d4d])
)
}
}
pub type G2 = Jacobian<G2Params>;
pub type G2 = G<G2Params>;
#[cfg(test)]
mod tests;
@ -256,3 +351,27 @@ fn test_g1() {
fn test_g2() {
tests::group_trials::<G2>();
}
#[test]
fn test_affine_jacobian_conversion() {
let rng = &mut ::rand::thread_rng();
assert!(G1::zero().to_affine().is_none());
assert!(G2::zero().to_affine().is_none());
for _ in 0..1000 {
let a = G1::one() * Fr::random(rng);
let b = a.to_affine().unwrap();
let c = b.to_jacobian();
assert_eq!(a, c);
}
for _ in 0..1000 {
let a = G2::one() * Fr::random(rng);
let b = a.to_affine().unwrap();
let c = b.to_jacobian();
assert_eq!(a, c);
}
}

10035
tests/fp2_vectors.rs Normal file

File diff suppressed because it is too large Load Diff

20069
tests/g1_g2_vectors.rs Normal file

File diff suppressed because it is too large Load Diff