diff --git a/Cargo.toml b/Cargo.toml index 4999341..0135682 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ bellman = "0.0.8" byteorder = "1" +[dev-dependencies] +hex-literal = "0.1" + [features] default = ["u128-support"] u128-support = ["pairing/u128-support"] diff --git a/src/jubjub/edwards.rs b/src/jubjub/edwards.rs index 130fd59..eeabe9d 100644 --- a/src/jubjub/edwards.rs +++ b/src/jubjub/edwards.rs @@ -20,6 +20,12 @@ use rand::{ use std::marker::PhantomData; +use std::io::{ + self, + Write, + Read +}; + // Represents the affine point (X/Z, Y/Z) via the extended // twisted Edwards coordinates. pub struct Point { @@ -80,7 +86,67 @@ impl PartialEq for Point { } } +fn swap_bits_u64(x: u64) -> u64 +{ + let mut tmp = 0; + for i in 0..64 { + tmp |= ((x >> i) & 1) << (63 - i); + } + tmp +} + +#[test] +fn test_swap_bits_u64() { + assert_eq!(swap_bits_u64(17182120934178543809), 0b1000001100011011110000011000111000101111111001001100111001110111); + assert_eq!(swap_bits_u64(15135675916470734665), 0b1001001011110010001101010010001110110000100111010011000001001011); + assert_eq!(swap_bits_u64(6724233301461108393), 0b1001010101100000100011100001010111110001011000101000101010111010); + assert_eq!(swap_bits_u64(206708183275952289), 0b1000010100011010001010100011101011111111111110100111101101000000); + assert_eq!(swap_bits_u64(12712751566144824320), 0b0000000000100110010110111000001110001100001000110011011000001101); + + let mut a = 15863238721320035327u64; + for _ in 0..1000 { + a = a.wrapping_mul(a); + + let swapped = swap_bits_u64(a); + let unswapped = swap_bits_u64(swapped); + + assert_eq!(a, unswapped); + } +} + impl Point { + pub fn read( + reader: R, + params: &E::Params + ) -> io::Result + { + let mut y_repr = ::Repr::default(); + y_repr.read_be(reader)?; + + y_repr.as_mut().reverse(); + + for b in y_repr.as_mut() { + *b = swap_bits_u64(*b); + } + + let x_sign = (y_repr.as_ref()[3] >> 63) == 1; + y_repr.as_mut()[3] &= 0x7fffffffffffffff; + + match E::Fr::from_repr(y_repr) { + Ok(y) => { + match Self::get_for_y(y, x_sign, params) { + Some(p) => Ok(p), + None => { + Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")) + } + } + }, + Err(_) => { + Err(io::Error::new(io::ErrorKind::InvalidInput, "y is not in field")) + } + } + } + pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option { // Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1) @@ -151,6 +217,30 @@ impl Point { } impl Point { + pub fn write( + &self, + writer: W + ) -> io::Result<()> + { + let (x, y) = self.into_xy(); + + assert_eq!(E::Fr::NUM_BITS, 255); + + let x_repr = x.into_repr(); + let mut y_repr = y.into_repr(); + if x_repr.is_odd() { + y_repr.as_mut()[3] |= 0x8000000000000000u64; + } + + y_repr.as_mut().reverse(); + + for b in y_repr.as_mut() { + *b = swap_bits_u64(*b); + } + + y_repr.write_be(writer) + } + /// Convert from a Montgomery point pub fn from_montgomery( m: &montgomery::Point, diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs index 4782d90..189d434 100644 --- a/src/jubjub/mod.rs +++ b/src/jubjub/mod.rs @@ -72,7 +72,8 @@ pub enum FixedGenerators { ValueCommitmentValue = 2, ValueCommitmentRandomness = 3, NullifierPosition = 4, - Max = 5 + SpendingKeyGenerator = 5, + Max = 6 } pub struct JubjubBls12 { @@ -236,4 +237,14 @@ fn test_jubjub_bls12() { let params = JubjubBls12::new(); tests::test_suite::(¶ms); + + let test_repr = hex!("b9481dd1103b7d1f8578078eb429d3c476472f53e88c0eaefdf51334c7c8b98c"); + let p = edwards::Point::::read(&test_repr[..], ¶ms).unwrap(); + let q = edwards::Point::::get_for_y( + Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(), + false, + ¶ms + ).unwrap(); + + assert!(p == q); } diff --git a/src/jubjub/tests.rs b/src/jubjub/tests.rs index e034854..dfd44d0 100644 --- a/src/jubjub/tests.rs +++ b/src/jubjub/tests.rs @@ -26,6 +26,7 @@ pub fn test_suite(params: &E::Params) { test_order::(params); test_mul_associativity::(params); test_loworder::(params); + test_read_write::(params); } fn is_on_mont_curve>( @@ -245,6 +246,21 @@ fn test_get_for(params: &E::Params) { } } +fn test_read_write(params: &E::Params) { + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let e = edwards::Point::::rand(rng, params); + + let mut v = vec![]; + e.write(&mut v).unwrap(); + + let e2 = edwards::Point::read(&v[..], params).unwrap(); + + assert!(e == e2); + } +} + fn test_rand(params: &E::Params) { let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); diff --git a/src/lib.rs b/src/lib.rs index 60e5972..48af45c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,10 @@ extern crate rand; extern crate byteorder; +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + pub mod jubjub; pub mod circuit; pub mod group_hash;