diff --git a/src/fields/fp.rs b/src/fields/fp.rs index 8b97bbd..4bb3c62 100644 --- a/src/fields/fp.rs +++ b/src/fields/fp.rs @@ -181,7 +181,12 @@ impl<'a, P: PrimeFieldParams> From<&'a str> for Fp

{ } impl Clone for Fp

{ - fn clone(&self) -> Self { unimplemented!() } + fn clone(&self) -> Self { + Fp { + value: self.value.clone(), + _marker: PhantomData + } + } } forward_ops_to_field_ops!(impl(P: PrimeFieldParams) Fp

); diff --git a/src/macros.rs b/src/fields/macros.rs similarity index 100% rename from src/macros.rs rename to src/fields/macros.rs diff --git a/src/fields/mod.rs b/src/fields/mod.rs index 0d286a0..1bd6a67 100644 --- a/src/fields/mod.rs +++ b/src/fields/mod.rs @@ -1,3 +1,6 @@ +#[macro_use] +mod macros; + pub mod fp; #[cfg(test)] @@ -5,8 +8,9 @@ pub mod tests; use rand::Rng; use self::fp::{Fp, PrimeFieldParams}; +use std::fmt::Debug; -pub trait Field: Sized + Clone { +pub trait Field: Sized + Clone + Debug { fn zero() -> Self; fn one() -> Self; fn random(rng: &mut R) -> Self; diff --git a/src/groups/macros.rs b/src/groups/macros.rs new file mode 100644 index 0000000..cb4dca6 --- /dev/null +++ b/src/groups/macros.rs @@ -0,0 +1,107 @@ +macro_rules! forward_val_val_binop { + (impl($($t:ident: $p:ident),*) $imp:ident for $res:ty, $method:ident, $rhs:ty) => { + impl<$($t: $p),*> $imp<$rhs> for $res { + type Output = $res; + + #[inline] + fn $method(self, other: $rhs) -> $res { + $imp::$method(&self, &other) + } + } + } +} + +macro_rules! forward_ref_val_binop { + (impl($($t:ident: $p:ident),*) $imp:ident for $res:ty, $method:ident, $rhs:ty) => { + impl<'a, $($t: $p),*> $imp<$rhs> for &'a $res { + type Output = $res; + + #[inline] + fn $method(self, other: $rhs) -> $res { + $imp::$method(self, &other) + } + } + } +} + +macro_rules! forward_val_ref_binop { + (impl($($t:ident: $p:ident),*) $imp:ident for $res:ty, $method:ident, $rhs:ty) => { + impl<'a, $($t: $p),*> $imp<&'a $rhs> for $res { + type Output = $res; + + #[inline] + fn $method(self, other: &$rhs) -> $res { + $imp::$method(&self, other) + } + } + } +} + +macro_rules! forward_all_binop_to_ref_ref { + (impl($($t:ident: $p:ident),*) $imp:ident for $res:ty, $method:ident, $rhs:ty) => { + forward_val_val_binop!(impl($($t: $p),*) $imp for $res, $method, $rhs); + forward_ref_val_binop!(impl($($t: $p),*) $imp for $res, $method, $rhs); + forward_val_ref_binop!(impl($($t: $p),*) $imp for $res, $method, $rhs); + }; +} + +macro_rules! forward_ops_to_group_ops { + (impl($($t:ident: $p:ident),*) $res:ty) => { + impl<'a, 'b, $($t: $p),*> Add<&'a $res> for &'b $res { + type Output = $res; + + #[inline] + fn add(self, other: &'a $res) -> $res { + Jacobian::add(self, other) + } + } + + impl<'a, 'b, $($t: $p),*> Mul<&'a Fr> for &'b $res { + type Output = $res; + + #[inline] + fn mul(self, other: &'a Fr) -> $res { + Jacobian::mul(self, other) + } + } + + impl<'a, 'b, $($t: $p),*> Sub<&'a $res> for &'b $res { + type Output = $res; + + #[inline] + fn sub(self, other: &'a $res) -> $res { + Jacobian::sub(self, other) + } + } + + impl<'a, $($t: $p),*> Neg for &'a $res { + type Output = $res; + + #[inline] + fn neg(self) -> $res { + Jacobian::neg(self) + } + } + + impl<$($t: $p),*> Neg for $res { + type Output = $res; + + #[inline] + fn neg(self) -> $res { + Jacobian::neg(&self) + } + } + + impl<$($t: $p),*> PartialEq for $res { + fn eq(&self, other: &Self) -> bool { + Jacobian::eq(self, other) + } + } + + impl<$($t: $p),*> Eq for $res {} + + forward_all_binop_to_ref_ref!(impl($($t: $p),*) Add for $res, add, $res); + forward_all_binop_to_ref_ref!(impl($($t: $p),*) Sub for $res, sub, $res); + forward_all_binop_to_ref_ref!(impl($($t: $p),*) Mul for $res, mul, Fr); + } +} diff --git a/src/groups/mod.rs b/src/groups/mod.rs new file mode 100644 index 0000000..922dfcb --- /dev/null +++ b/src/groups/mod.rs @@ -0,0 +1,255 @@ +use fields::Field; +use fields::fp::{PrimeFieldParams, Fp}; +use super::Fr; +use rand::Rng; +use std::ops::{Add,Mul,Sub,Neg}; +use std::fmt; + +#[cfg(test)] +pub mod tests; + +#[macro_use] +mod macros; + +pub trait GroupParams: Sized { + type Base: Field; + + fn zero() -> Jacobian; + fn one() -> Jacobian; + fn coeff_b() -> Self::Base; + fn name() -> &'static str; +} + +pub struct Jacobian { + x: P::Base, + y: P::Base, + z: P::Base +} + +impl fmt::Debug for Jacobian

{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}({:?}, {:?}, {:?})", P::name(), self.x, self.y, self.z) + } +} + +impl Clone for Jacobian

{ + fn clone(&self) -> Self { + Jacobian { + x: self.x.clone(), + y: self.y.clone(), + z: self.z.clone() + } + } +} +pub enum Affine { + Zero, + Point{x: P::Base, y: P::Base} +} + +impl Clone for Affine

{ + fn clone(&self) -> Self { + match *self { + Affine::Zero => Affine::Zero, + Affine::Point{ref x, ref y} => Affine::Point{x: x.clone(), y: y.clone()} + } + } +} + +impl Affine

{ +/* + fn to_jacobian(self) -> Jacobian

{ + match self { + Affine::Zero => P::zero(), + Affine::Point{x, y} => Jacobian { + x: x, + y: y, + z: P::Base::one() + } + } + } +*/ +} + +impl Jacobian

{ + pub fn new(x: P::Base, y: P::Base, z: P::Base) -> Option { + let tmp = Jacobian { + x: x, + y: y, + z: z + }; + + if tmp.is_well_formed() { + Some(tmp) + } else { + None + } + } + + pub fn random(rng: &mut R) -> Self { + Self::one() * &Fr::random(rng) + } + + pub fn is_well_formed(&self) -> bool { + if self.is_zero() { + true + } else { + let x2 = self.x.squared(); + let y2 = self.y.squared(); + let z2 = self.z.squared(); + + let x3 = self.x.mul(&x2); + let z3 = self.z.mul(&z2); + let z6 = z3.squared(); + + (y2.eq(&z6.mul(&P::coeff_b()).add(&x3))) + } + } + + pub fn to_affine(&self) -> Affine

{ + if self.is_zero() { + Affine::Zero + } else { + let z_inv = self.z.inverse(); + let z2_inv = z_inv.squared(); + let z3_inv = z2_inv.mul(&z_inv); + + Affine::Point { + x: self.x.mul(&z2_inv), + y: self.y.mul(&z3_inv) + } + } + } + + pub fn zero() -> Self { + P::zero() + } + + pub fn one() -> Self { + P::one() + } + + pub fn add(&self, other: &Self) -> Self { + if self.is_zero() { + return other.clone() + } + + if other.is_zero() { + return self.clone() + } + + let z1_squared = self.z.squared(); + let z2_squared = other.z.squared(); + let u1 = self.x.mul(&z2_squared); + let u2 = other.x.mul(&z1_squared); + let z1_cubed = self.z.mul(&z1_squared); + let z2_cubed = other.z.mul(&z2_squared); + let s1 = self.y.mul(&z2_cubed); + let s2 = other.y.mul(&z1_cubed); + + if u1.eq(&u2) && s1.eq(&s2) { + self.double() + } else { + let h = u2.sub(&u1); + let s2_minus_s1 = s2.sub(&s1); + let i = h.add(&h).squared(); + let j = h.mul(&i); + let r = s2_minus_s1.add(&s2_minus_s1); + let v = u1.mul(&i); + let s1_j = s1.mul(&j); + let x3 = r.squared().sub(&j).sub(&v.add(&v)); + let y3 = r.mul(&v.sub(&x3)).sub(&s1_j.add(&s1_j)); + + Jacobian { + x: x3, + y: y3, + z: self.z.add(&other.z).squared().sub(&z1_squared).sub(&z2_squared).mul(&h) + } + } + } + + pub fn double(&self) -> Self { + let a = self.x.squared(); + let b = self.y.squared(); + let c = b.squared(); + let mut d = self.x.add(&b).squared().sub(&a).sub(&c); + d = d.add(&d); + let e = a.add(&a).add(&a); + let f = e.squared(); + let x3 = f.sub(&d.add(&d)); + let mut eight_c = c.add(&c); + eight_c = eight_c.add(&eight_c); + eight_c = eight_c.add(&eight_c); + let y3 = e.mul(&d.sub(&x3)).sub(&eight_c); + let y1z1 = self.y.mul(&self.z); + let z3 = y1z1.add(&y1z1); + + Jacobian { + x: x3, + y: y3, + z: z3 + } + } + + pub fn eq(&self, other: &Self) -> bool { + if self.is_zero() { + return other.is_zero() + } + + if other.is_zero() { + return false; + } + + let z1_squared = self.z.squared(); + let z2_squared = other.z.squared(); + + if self.x.mul(&z2_squared).ne(&other.x.mul(&z1_squared)) { + return false; + } + + let z1_cubed = self.z.mul(&z1_squared); + let z2_cubed = other.z.mul(&z2_squared); + + if self.y.mul(&z2_cubed).ne(&other.y.mul(&z1_cubed)) { + return false; + } + + return true; + } + + pub fn neg(&self) -> Self { + Jacobian { + x: self.x.clone(), + y: self.y.neg(), + z: self.z.clone() + } + } + + #[inline] + pub fn is_zero(&self) -> bool { + self.z.is_zero() + } + + pub fn mul(&self, other: &Fp) -> Jacobian

{ + let mut result = Jacobian::

::zero(); + let mut found_one = false; + for i in (0..S::bits()).rev() { + if found_one { + result = result.double(); + } + + if other.test_bit(i) { + found_one = true; + result = &result + self; + } + } + + result + } + + #[inline] + pub fn sub(&self, other: &Self) -> Jacobian

{ + self.add(&other.neg()) + } +} + +forward_ops_to_group_ops!(impl(P: GroupParams) Jacobian

); diff --git a/src/groups/tests.rs b/src/groups/tests.rs new file mode 100644 index 0000000..1f3d267 --- /dev/null +++ b/src/groups/tests.rs @@ -0,0 +1,76 @@ +use super::*; +use ::Fr; +use fields::Field; + +use rand::Rng; + +pub fn group_trials() { + fn test_doubling() { + type G

= Jacobian

; + + let one = G::

::one(); + let two = one.add(&one); + let three = two.add(&one); + let four = three.add(&one); + + assert_eq!(one.double(), two); + assert_eq!(two.double(), four); + } + + fn random_test_addition(rng: &mut R) { + type G

= Jacobian

; + + for _ in 0..50 { + let r1 = &G::

::random(rng); + let r2 = &G::

::random(rng); + let r3 = &G::

::random(rng); + + let s1 = (r1 + r2) + r3; + let s2 = (r2 + r3) + r1; + + assert_eq!(s1, s2); + } + } + + fn random_test_doubling(rng: &mut R) { + type G

= Jacobian

; + + for _ in 0..50 { + let r1 = &G::

::random(rng); + let r2 = &G::

::random(rng); + + let a = (r1 + r2) + r1; + let b = r1.double() + r2; + + assert!(a.eq(&b)); + } + } + + fn random_test_dh(rng: &mut R) { + type G

= Jacobian

; + + for _ in 0..50 { + let alice_sk = Fr::random(rng); + let bob_sk = Fr::random(rng); + + let alice_pk = G::

::one() * &alice_sk; + let bob_pk = G::

::one() * &bob_sk; + + let alice_shared = &bob_pk * &alice_sk; + let bob_shared = &alice_pk * &bob_sk; + + assert_eq!(alice_shared, bob_shared); + } + } + + test_doubling::

(); + + use rand::{SeedableRng,StdRng}; + let seed: [usize; 4] = [103245, 191922, 1293, 192103]; + let mut rng = StdRng::from_seed(&seed); + + random_test_addition::(&mut rng); + random_test_doubling::(&mut rng); + random_test_dh::(&mut rng); +} + diff --git a/src/lib.rs b/src/lib.rs index 886d1d7..2b273c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,18 @@ extern crate num; extern crate rand; -#[macro_use] -mod macros; mod fields; mod params; + +mod groups; + +pub use fields::fp::Fp; +pub use params::{FrParams,FqParams,G1Params}; +pub use fields::Field; +pub use groups::Jacobian; + +pub type Fr = Fp; +pub type Fq = Fp; + +pub type Scalar = Fr; +pub type G1 = Jacobian; diff --git a/src/params.rs b/src/params.rs index 2bbfdc5..5fdef1f 100644 --- a/src/params.rs +++ b/src/params.rs @@ -1,5 +1,8 @@ use num::{Num,BigUint}; +use fields::Field; use fields::fp::PrimeFieldParams; +use super::{Fr,Fq,G1}; +use groups::*; pub struct FrParams; @@ -25,12 +28,53 @@ impl PrimeFieldParams for FqParams { fn test_fr() { use fields; - fields::tests::field_trials::>(); + fields::tests::field_trials::(); } #[test] fn test_fq() { use fields; - fields::tests::field_trials::>(); + fields::tests::field_trials::(); +} + +pub struct G1Params; + +impl GroupParams for G1Params { + type Base = super::Fq; + + fn name() -> &'static str { + "G1" + } + fn zero() -> Jacobian { + Jacobian::new(Fq::zero(), Fq::one(), Fq::zero()).unwrap() + } + fn one() -> Jacobian { + Jacobian::new(Fq::from("1"), Fq::from("2"), Fq::one()).unwrap() + } + fn coeff_b() -> Self::Base { + Fq::from("3") + } +} + +#[test] +fn test_g1() { + use groups; + + groups::tests::group_trials::(); +} + +#[test] +fn g1_test_vector() { + let a = G1::one() * &Fr::from("19797905000333868150253315089095386158892526856493194078073564469188852136946"); + let b = G1::one() * &Fr::from("2730506433347642574983433139433778984782882168213690554721050571242082865799"); + let e = &a + &b; + + let expect = G1::new( + Fq::from("18450621724990678172567114131642278789161361170999664461184794604011563728206"), + Fq::from("21329688341674583036384007811166435666174342925504675855816423131698588368496"), + Fq::one() + ).unwrap(); + + assert!(expect.eq(&e)); }