From 69c60530d41fc17c2ea52a42135c7b90901c6182 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 26 Mar 2020 19:23:29 +1300 Subject: [PATCH] group: Rewrite wNAF to remove dependency on ff::PrimeFieldRepr Adapted from Scalar::non_adjacent_form in curve25519-dalek. --- bellman/src/groth16/generator.rs | 14 +++--- group/Cargo.toml | 1 + group/src/tests/mod.rs | 30 ++++++------ group/src/wnaf.rs | 81 ++++++++++++++++++++++---------- 4 files changed, 78 insertions(+), 48 deletions(-) diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index d993835..02efc21 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -2,7 +2,7 @@ use rand_core::RngCore; use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; -use ff::{Field, PrimeField}; +use ff::Field; use group::{CurveAffine, CurveProjective, Wnaf}; use pairing::Engine; @@ -273,7 +273,7 @@ where exp.mul_assign(&coeff); // Exponentiate - *h = g1_wnaf.scalar(exp.into_repr()); + *h = g1_wnaf.scalar(&exp); } // Batch normalize @@ -376,14 +376,14 @@ where // Compute A query (in G1) if !at.is_zero() { - *a = g1_wnaf.scalar(at.into_repr()); + *a = g1_wnaf.scalar(&at); } // Compute B query (in G1/G2) if !bt.is_zero() { - let bt_repr = bt.into_repr(); - *b_g1 = g1_wnaf.scalar(bt_repr); - *b_g2 = g2_wnaf.scalar(bt_repr); + (); + *b_g1 = g1_wnaf.scalar(&bt); + *b_g2 = g2_wnaf.scalar(&bt); } at.mul_assign(&beta); @@ -394,7 +394,7 @@ where e.add_assign(&ct); e.mul_assign(inv); - *ext = g1_wnaf.scalar(e.into_repr()); + *ext = g1_wnaf.scalar(&e); } // Batch normalize diff --git a/group/Cargo.toml b/group/Cargo.toml index 9b8fe5f..b68c2fe 100644 --- a/group/Cargo.toml +++ b/group/Cargo.toml @@ -15,6 +15,7 @@ repository = "https://github.com/ebfull/group" edition = "2018" [dependencies] +byteorder = { version = "1", default-features = false } ff = { version = "0.6", path = "../ff" } rand = "0.7" rand_xorshift = "0.2" diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs index c53ba76..66a76c0 100644 --- a/group/src/tests/mod.rs +++ b/group/src/tests/mod.rs @@ -85,12 +85,12 @@ fn random_wnaf_tests() { for w in 2..14 { for _ in 0..100 { let g = G::random(&mut rng); - let s = G::Scalar::random(&mut rng).into_repr(); + let s = G::Scalar::random(&mut rng); let mut g1 = g; g1.mul_assign(s); wnaf_table(&mut table, g, w); - wnaf_form(&mut wnaf, s, w); + wnaf_form(&mut wnaf, s.into_repr(), w); let g2 = wnaf_exp(&table, &wnaf); assert_eq!(g1, g2); @@ -103,17 +103,17 @@ fn random_wnaf_tests() { for _ in 0..100 { let g = G::random(&mut rng); - let s = G::Scalar::random(&mut rng).into_repr(); + let s = G::Scalar::random(&mut rng); let mut g1 = g; g1.mul_assign(s); let g2 = { let mut wnaf = Wnaf::new(); - wnaf.base(g, 1).scalar(s) + wnaf.base(g, 1).scalar(&s) }; let g3 = { let mut wnaf = Wnaf::new(); - wnaf.scalar(s).base(g) + wnaf.scalar(&s).base(g) }; let g4 = { let mut wnaf = Wnaf::new(); @@ -121,11 +121,11 @@ fn random_wnaf_tests() { only_compiles_if_send(&shared); - shared.scalar(s) + shared.scalar(&s) }; let g5 = { let mut wnaf = Wnaf::new(); - let mut shared = wnaf.scalar(s).shared(); + let mut shared = wnaf.scalar(&s).shared(); only_compiles_if_send(&shared); @@ -137,40 +137,40 @@ fn random_wnaf_tests() { { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } - wnaf.base(g, 1).scalar(s) + wnaf.base(g, 1).scalar(&s) }; let g7 = { let mut wnaf = Wnaf::new(); { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } - wnaf.scalar(s).base(g) + wnaf.scalar(&s).base(g) }; let g8 = { let mut wnaf = Wnaf::new(); { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } let mut shared = wnaf.base(g, 1).shared(); only_compiles_if_send(&shared); - shared.scalar(s) + shared.scalar(&s) }; let g9 = { let mut wnaf = Wnaf::new(); { // Populate the vectors. wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); + .scalar(&G::Scalar::random(&mut rng)); } - let mut shared = wnaf.scalar(s).shared(); + let mut shared = wnaf.scalar(&s).shared(); only_compiles_if_send(&shared); diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs index 70f880b..2001101 100644 --- a/group/src/wnaf.rs +++ b/group/src/wnaf.rs @@ -1,4 +1,5 @@ -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; +use std::iter; use super::CurveProjective; @@ -16,31 +17,60 @@ pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, wi } } -/// Replaces the contents of `wnaf` with the w-NAF representation of a scalar. -pub(crate) fn wnaf_form(wnaf: &mut Vec, mut c: S, window: usize) { +/// Replaces the contents of `wnaf` with the w-NAF representation of a little-endian +/// scalar. +pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize) { + // Required by the NAF definition + debug_assert!(window >= 2); + // Required so that the NAF digits fit in i64 + debug_assert!(window <= 64); + wnaf.truncate(0); - while !c.is_zero() { - let mut u; - if c.is_odd() { - u = (c.as_ref()[0] % (1 << (window + 1))) as i64; + let u64_len = c.as_ref().len(); + let bit_len = u64_len * 64; - if u > (1 << window) { - u -= 1 << (window + 1); - } + let mut c_u64 = vec![0u64; u64_len + 1]; + c_u64[0..u64_len].copy_from_slice(c.as_ref()); - if u > 0 { - c.sub_noborrow(&S::from(u as u64)); - } else { - c.add_nocarry(&S::from((-u) as u64)); - } + let width = 1u64 << window; + let window_mask = width - 1; + + let mut pos = 0; + let mut carry = 0; + while pos < bit_len { + // Construct a buffer of bits of the scalar, starting at bit `pos` + let u64_idx = pos / 64; + let bit_idx = pos % 64; + let bit_buf = if bit_idx + window < 64 { + // This window's bits are contained in a single u64 + c_u64[u64_idx] >> bit_idx } else { - u = 0; + // Combine the current u64's bits with the bits from the next u64 + (c_u64[u64_idx] >> bit_idx) | (c_u64[u64_idx + 1] << (64 - bit_idx)) + }; + + // Add the carry into the current window + let window_val = carry + (bit_buf & window_mask); + + if window_val & 1 == 0 { + // If the window value is even, preserve the carry and emit 0. + // Why is the carry preserved? + // If carry == 0 and window_val & 1 == 0, then the next carry should be 0 + // If carry == 1 and window_val & 1 == 0, then bit_buf & 1 == 1 so the next carry should be 1 + wnaf.push(0); + pos += 1; + } else { + wnaf.push(if window_val < width / 2 { + carry = 0; + window_val as i64 + } else { + carry = 1; + (window_val as i64).wrapping_sub(width as i64) + }); + wnaf.extend(iter::repeat(0).take(window - 1)); + pos += window; } - - wnaf.push(u); - - c.div2(); } } @@ -112,8 +142,10 @@ impl Wnaf<(), Vec, Vec> { /// exponentiations with `.base(..)`. pub fn scalar( &mut self, - scalar: <::Scalar as PrimeField>::Repr, + scalar: &::Scalar, ) -> Wnaf, &[i64]> { + let scalar = scalar.into_repr(); + // Compute the appropriate window size for the scalar. let window_size = G::recommended_wnaf_for_scalar(&scalar); @@ -168,14 +200,11 @@ impl> Wnaf { impl>> Wnaf { /// Performs exponentiation given a scalar. - pub fn scalar( - &mut self, - scalar: <::Scalar as PrimeField>::Repr, - ) -> G + pub fn scalar(&mut self, scalar: &::Scalar) -> G where B: AsRef<[G]>, { - wnaf_form(self.scalar.as_mut(), scalar, self.window_size); + wnaf_form(self.scalar.as_mut(), scalar.into_repr(), self.window_size); wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) } }