From 87c62e2248b7fd4e3871c5a22adae8dd6500dbe3 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 16 May 2018 23:30:09 -0600 Subject: [PATCH 1/3] Update to the latest pairing crate version. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a8a7d13..79a5ed7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/zcash-hackworks/sapling" version = "0.0.1" [dependencies.pairing] -version = "0.14" +version = "0.14.2" features = ["expose-arith"] [dependencies] From f491e02b56d5df2c1512b0a7439b3e5dd03e0b52 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 17 May 2018 00:05:32 -0600 Subject: [PATCH 2/3] Correctly interpret BLAKE2s inputs and outputs as little endian. --- src/circuit/blake2s.rs | 6 +++--- src/circuit/uint32.rs | 10 ++-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/circuit/blake2s.rs b/src/circuit/blake2s.rs index a78f3cf..93af806 100644 --- a/src/circuit/blake2s.rs +++ b/src/circuit/blake2s.rs @@ -343,7 +343,7 @@ mod test { let mut out = out.into_iter(); for b in expected.into_iter() { - for i in (0..8).rev() { + for i in 0..8 { let c = out.next().unwrap().get_value().unwrap(); assert_eq!(c, (b >> i) & 1u8 == 1u8); @@ -405,7 +405,7 @@ mod test { let mut input_bits = vec![]; for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in (0..8).rev() { + for bit_i in 0..8 { let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into()); @@ -417,7 +417,7 @@ mod test { assert!(cs.is_satisfied()); let mut s = hash_result.as_ref().iter() - .flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8)); + .flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)); for b in r { match b { diff --git a/src/circuit/uint32.rs b/src/circuit/uint32.rs index e254132..fb0bfa9 100644 --- a/src/circuit/uint32.rs +++ b/src/circuit/uint32.rs @@ -114,10 +114,7 @@ impl UInt32 { /// Turns this `UInt32` into its little-endian byte order representation. pub fn into_bits(&self) -> Vec { - self.bits.chunks(8) - .flat_map(|v| v.iter().rev()) - .cloned() - .collect() + self.bits.clone() } /// Converts a little-endian byte order representation of bits into a @@ -126,10 +123,7 @@ impl UInt32 { { assert_eq!(bits.len(), 32); - let new_bits = bits.chunks(8) - .flat_map(|v| v.iter().rev()) - .cloned() - .collect::>(); + let new_bits = bits.to_vec(); let mut value = Some(0u32); for b in new_bits.iter().rev() { From 2ff318eecba63a235d8de3fb8f6b435e29235818 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 17 May 2018 00:19:57 -0600 Subject: [PATCH 3/3] Use little endian for everything in Sapling. --- src/circuit/multipack.rs | 7 +++++++ src/circuit/sapling/mod.rs | 11 +++-------- src/group_hash.rs | 25 ++++++------------------- src/jubjub/edwards.rs | 25 ++----------------------- src/jubjub/mod.rs | 13 ++++++++++++- src/primitives/mod.rs | 24 +++++++----------------- src/redjubjub.rs | 21 +++------------------ src/util.rs | 28 ---------------------------- 8 files changed, 40 insertions(+), 114 deletions(-) diff --git a/src/circuit/multipack.rs b/src/circuit/multipack.rs index 04f6260..54d4138 100644 --- a/src/circuit/multipack.rs +++ b/src/circuit/multipack.rs @@ -45,6 +45,13 @@ pub fn bytes_to_bits(bytes: &[u8]) -> Vec .collect() } +pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec +{ + bytes.iter() + .flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1)) + .collect() +} + pub fn compute_multipacking( bits: &[bool] ) -> Vec diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index dbfc4ce..31bab6b 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -226,11 +226,6 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { constants::CRH_IVK_PERSONALIZATION )?; - // Swap bit-endianness in each byte - for ivk_byte in ivk.chunks_mut(8) { - ivk_byte.reverse(); - } - // drop_5 to ensure it's in the field ivk.truncate(E::Fs::CAPACITY as usize); @@ -697,7 +692,7 @@ fn test_input_circuit_with_bls12_381() { } let expected_nf = note.nf(&viewing_key, position, params); - let expected_nf = multipack::bytes_to_bits(&expected_nf); + let expected_nf = multipack::bytes_to_bits_le(&expected_nf); let expected_nf = multipack::compute_multipacking::(&expected_nf); assert_eq!(expected_nf.len(), 2); @@ -718,7 +713,7 @@ fn test_input_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 98777); - assert_eq!(cs.hash(), "499305e409599a3e4fe0a885f6adf674e9f49ba4a21e47362356d2a89f15dc1f"); + assert_eq!(cs.hash(), "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"); assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); @@ -795,7 +790,7 @@ fn test_output_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 7827); - assert_eq!(cs.hash(), "d18e83255220328a688134038ba4f82d5ce67ffe9f97b2ae2678042da0efad43"); + assert_eq!(cs.hash(), "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee"); let expected_cm = payment_address.create_note( value_commitment.value, diff --git a/src/group_hash.rs b/src/group_hash.rs index 9e6b2f8..25e65f9 100644 --- a/src/group_hash.rs +++ b/src/group_hash.rs @@ -5,8 +5,7 @@ use jubjub::{ }; use pairing::{ - PrimeField, - PrimeFieldRepr + PrimeField }; use blake2_rfc::blake2s::Blake2s; @@ -29,20 +28,11 @@ pub fn group_hash( let mut h = Blake2s::with_params(32, &[], &[], personalization); h.update(constants::GH_FIRST_BLOCK); h.update(tag); - let mut h = h.finalize().as_ref().to_vec(); + let h = h.finalize().as_ref().to_vec(); assert!(h.len() == 32); - // Take first/unset first bit of hash - let s = h[0] >> 7 == 1; // get s - h[0] &= 0b0111_1111; // unset s from h - - // cast to prime field representation - let mut y0 = ::Repr::default(); - y0.read_be(&h[..]).expect("hash is sufficiently large"); - - if let Ok(y0) = E::Fr::from_repr(y0) { - if let Some(p) = edwards::Point::::get_for_y(y0, s, params) { - // Enter into the prime order subgroup + match edwards::Point::::read(&h[..], params) { + Ok(p) => { let p = p.mul_by_cofactor(params); if p != edwards::Point::zero() { @@ -50,10 +40,7 @@ pub fn group_hash( } else { None } - } else { - None - } - } else { - None + }, + Err(_) => None } } diff --git a/src/jubjub/edwards.rs b/src/jubjub/edwards.rs index d1d2772..2137d99 100644 --- a/src/jubjub/edwards.rs +++ b/src/jubjub/edwards.rs @@ -26,8 +26,6 @@ use std::io::{ Read }; -use util::swap_bits_u64; - // Represents the affine point (X/Z, Y/Z) via the extended // twisted Edwards coordinates. // @@ -97,21 +95,8 @@ impl Point { params: &E::Params ) -> io::Result { - // Jubjub points are encoded least significant bit first. - // The most significant bit (bit 254) encodes the parity - // of the x-coordinate. - let mut y_repr = ::Repr::default(); - - // This reads in big-endian, so we perform a swap of the - // limbs in the representation and swap the bit order. - y_repr.read_be(reader)?; - - y_repr.as_mut().reverse(); - - for b in y_repr.as_mut() { - *b = swap_bits_u64(*b); - } + y_repr.read_le(reader)?; let x_sign = (y_repr.as_ref()[3] >> 63) == 1; y_repr.as_mut()[3] &= 0x7fffffffffffffff; @@ -216,13 +201,7 @@ impl Point { 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) + y_repr.write_le(writer) } /// Convert from a Montgomery point diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs index 1c21192..80a7986 100644 --- a/src/jubjub/mod.rs +++ b/src/jubjub/mod.rs @@ -363,7 +363,7 @@ fn test_jubjub_bls12() { tests::test_suite::(¶ms); - let test_repr = hex!("b9481dd1103b7d1f8578078eb429d3c476472f53e88c0eaefdf51334c7c8b98c"); + let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31"); let p = edwards::Point::::read(&test_repr[..], ¶ms).unwrap(); let q = edwards::Point::::get_for_y( Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(), @@ -372,4 +372,15 @@ fn test_jubjub_bls12() { ).unwrap(); assert!(p == q); + + // Same thing, but sign bit set + let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1"); + let p = edwards::Point::::read(&test_repr[..], ¶ms).unwrap(); + let q = edwards::Point::::get_for_y( + Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(), + true, + ¶ms + ).unwrap(); + + assert!(p == q); } diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index 825aed5..01e042d 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -14,7 +14,7 @@ use pedersen_hash::{ }; use byteorder::{ - BigEndian, + LittleEndian, WriteBytesExt }; @@ -28,8 +28,6 @@ use jubjub::{ use blake2_rfc::blake2s::Blake2s; -use util::swap_bits_u64; - #[derive(Clone)] pub struct ValueCommitment { pub value: u64, @@ -96,14 +94,11 @@ impl ViewingKey { h.update(&preimage); let mut h = h.finalize().as_ref().to_vec(); - // Reverse the bytes to interpret it in little-endian byte order - h.reverse(); - - // Drop the first five bits, so it can be interpreted as a scalar. - h[0] &= 0b0000_0111; + // Drop the most significant five bits, so it can be interpreted as a scalar. + h[31] &= 0b0000_0111; let mut e = ::Repr::default(); - e.read_be(&h[..]).unwrap(); + e.read_le(&h[..]).unwrap(); E::Fs::from_repr(e).expect("should be a valid scalar") } @@ -198,13 +193,8 @@ impl Note { // Calculate the note contents, as bytes let mut note_contents = vec![]; - // Write the value in little-endian bit order - // swapping the bits to ensure the order is - // correct (LittleEndian byte order would - // be incorrect here.) - (&mut note_contents).write_u64::( - swap_bits_u64(self.value) - ).unwrap(); + // Writing the value in little endian + (&mut note_contents).write_u64::(self.value).unwrap(); // Write g_d self.g_d.write(&mut note_contents).unwrap(); @@ -219,7 +209,7 @@ impl Note { Personalization::NoteCommitment, note_contents.into_iter() .flat_map(|byte| { - (0..8).rev().map(move |i| ((byte >> i) & 1) == 1) + (0..8).map(move |i| ((byte >> i) & 1) == 1) }), params ); diff --git a/src/redjubjub.rs b/src/redjubjub.rs index 5b3d8a5..0283508 100644 --- a/src/redjubjub.rs +++ b/src/redjubjub.rs @@ -6,18 +6,11 @@ use rand::Rng; use std::io::{self, Read, Write}; use jubjub::{FixedGenerators, JubjubEngine, JubjubParams, Unknown, edwards::Point}; -use util::{hash_to_scalar, swap_bits_u64}; +use util::{hash_to_scalar}; fn read_scalar(reader: R) -> io::Result { let mut s_repr = ::Repr::default(); - - // This reads in big-endian, so we perform a swap of the - // limbs in the representation and swap the bit order. - s_repr.read_be(reader)?; - s_repr.as_mut().reverse(); - for b in s_repr.as_mut() { - *b = swap_bits_u64(*b); - } + s_repr.read_le(reader)?; match E::Fs::from_repr(s_repr) { Ok(s) => Ok(s), @@ -29,15 +22,7 @@ fn read_scalar(reader: R) -> io::Result { } fn write_scalar(s: &E::Fs, writer: W) -> io::Result<()> { - let mut s_repr = s.into_repr(); - - // This writes in big-endian, so we perform a swap of the - // limbs in the representation and swap the bit order. - s_repr.as_mut().reverse(); - for b in s_repr.as_mut() { - *b = swap_bits_u64(*b); - } - s_repr.write_be(writer) + s.into_repr().write_le(writer) } fn h_star(a: &[u8], b: &[u8]) -> E::Fs { diff --git a/src/util.rs b/src/util.rs index b5cc4e8..e67e660 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,15 +2,6 @@ use blake2_rfc::blake2b::Blake2b; use jubjub::{JubjubEngine, ToUniform}; -pub fn swap_bits_u64(x: u64) -> u64 -{ - let mut tmp = 0; - for i in 0..64 { - tmp |= ((x >> i) & 1) << (63 - i); - } - tmp -} - pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs { let mut hasher = Blake2b::with_params(64, &[], &[], persona); hasher.update(a); @@ -18,22 +9,3 @@ pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> E: let ret = hasher.finalize(); E::Fs::to_uniform(ret.as_ref()) } - -#[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); - } -}