Skip to content

Commit

Permalink
Adding wNAF multiplication functionality (#230)
Browse files Browse the repository at this point in the history
Co-authored-by: Pratyush Mishra <pratyushmishra@berkeley.edu>
  • Loading branch information
ggkitsas and Pratyush authored Mar 25, 2021
1 parent 8176e51 commit 3c2c4dd
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 26 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## Pending

### Breaking changes

### Features

- [\#230](https://github.com/arkworks-rs/algebra/pull/230) (ark-ec) Add `wnaf_mul` implementation for `ProjectiveCurve`.

### Improvements


## v0.2.0

The main features of this release are:
Expand Down
2 changes: 2 additions & 0 deletions ec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub mod group;

pub mod msm;

pub mod wnaf;

pub trait PairingEngine: Sized + 'static + Copy + Debug + Sync + Send + Eq + PartialEq {
/// This is the scalar field of the G1/G2 groups.
type Fr: PrimeField + SquareRootField;
Expand Down
77 changes: 77 additions & 0 deletions ec/src/wnaf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use crate::ProjectiveCurve;
use ark_ff::{BigInteger, PrimeField};
use ark_std::vec::Vec;

/// A helper type that contains all the context required for computing
/// a window NAF multiplication of a group element by a scalar.
pub struct WnafContext {
pub window_size: usize,
}

impl WnafContext {
/// Construct a new context for a window of size `window_size`.
pub fn new(window_size: usize) -> Self {
assert!(window_size >= 2);
assert!(window_size < 64);
Self { window_size }
}

pub fn table<G: ProjectiveCurve>(&self, mut base: G) -> Vec<G> {
let mut table = Vec::with_capacity(1 << (self.window_size - 1));
let dbl = base.double();

for _ in 0..(1 << (self.window_size - 1)) {
table.push(base);
base += &dbl;
}
table
}

/// Computes scalar multiplication of a group element `g` by `scalar`.
///
/// This method uses the wNAF algorithm to perform the scalar multiplication;
/// first, it uses `Self::table` to calculate an appropriate table of multiples of `g`,
/// and then uses the wNAF algorithm to compute the scalar multiple.
pub fn mul<G: ProjectiveCurve>(&self, g: G, scalar: &G::ScalarField) -> G {
let table = self.table(g);
self.mul_with_table(&table, scalar).unwrap()
}

/// Computes scalar multiplication of a group element by `scalar`.
/// `base_table` holds precomputed multiples of the group element; it can be generated using `Self::table`.
/// `scalar` is an element of `G::ScalarField`.
///
/// Returns `None` if the table is too small.
pub fn mul_with_table<G: ProjectiveCurve>(
&self,
base_table: &[G],
scalar: &G::ScalarField,
) -> Option<G> {
if 1 << (self.window_size - 1) > base_table.len() {
return None;
}
let scalar_wnaf = scalar.into_repr().find_wnaf(self.window_size).unwrap();

let mut result = G::zero();

let mut found_non_zero = false;

for n in scalar_wnaf.iter().rev() {
if found_non_zero {
result.double_in_place();
}

if *n != 0 {
found_non_zero = true;

if *n > 0 {
result += &base_table[(n / 2) as usize];
} else {
result -= &base_table[((-n) / 2) as usize];
}
}
}

Some(result)
}
}
24 changes: 0 additions & 24 deletions ff/src/biginteger/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,30 +253,6 @@ macro_rules! bigint_impl {
}
res
}

#[inline]
fn find_wnaf(&self) -> Vec<i64> {
let mut res = vec![];

let mut e = self.clone();
while !e.is_zero() {
let z: i64;
if e.is_odd() {
z = 2 - (e.0[0] % 4) as i64;
if z >= 0 {
e.sub_noborrow(&Self::from(z as u64));
} else {
e.add_nocarry(&Self::from((-z) as u64));
}
} else {
z = 0;
}
res.push(z);
e.div2();
}

res
}
}

impl CanonicalSerialize for $name {
Expand Down
40 changes: 38 additions & 2 deletions ff/src/biginteger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ pub mod arithmetic;
#[macro_use]
mod macros;

pub fn signed_mod_reduction(n: u64, modulus: u64) -> i64 {
let t = (n % modulus) as i64;
if t as u64 >= (modulus / 2) {
t - (modulus as i64)
} else {
t
}
}

bigint_impl!(BigInteger64, 1);
bigint_impl!(BigInteger128, 2);
bigint_impl!(BigInteger256, 4);
Expand Down Expand Up @@ -122,8 +131,35 @@ pub trait BigInteger:
/// with trailing zeros.
fn to_bytes_le(&self) -> Vec<u8>;

/// Returns a vector for wnaf.
fn find_wnaf(&self) -> Vec<i64>;
/// Returns the windowed non-adjacent form of `self`, for a window of size `w`.
fn find_wnaf(&self, w: usize) -> Option<Vec<i64>> {
// w > 2 due to definition of wNAF, and w < 64 to make sure that `i64`
// can fit each signed digit
if w >= 2 && w < 64 {
let mut res = vec![];
let mut e = *self;

while !e.is_zero() {
let z: i64;
if e.is_odd() {
z = signed_mod_reduction(e.as_ref()[0], 1 << w);
if z >= 0 {
e.sub_noborrow(&Self::from(z as u64));
} else {
e.add_nocarry(&Self::from((-z) as u64));
}
} else {
z = 0;
}
res.push(z);
e.div2();
}

Some(res)
} else {
None
}
}

/// Writes this `BigInteger` as a big endian integer. Always writes
/// `(num_bits` / 8) bytes.
Expand Down
14 changes: 14 additions & 0 deletions test-templates/src/curves.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![allow(unused)]
use ark_ec::wnaf::WnafContext;
use ark_ec::{
AffineCurve, MontgomeryModelParameters, ProjectiveCurve, SWModelParameters, TEModelParameters,
};
Expand Down Expand Up @@ -103,6 +104,19 @@ fn random_multiplication_test<G: ProjectiveCurve>() {
tmp1.add_assign(&b);
tmp1.mul_assign(s);

// s ( a + b) using wNAF for several window values in [2,5]
for w in 2..=5 {
let mut tmp4 = a + &b;
let context = WnafContext::new(w);
assert_eq!(tmp1, context.mul(tmp4, &s));

if w > 2 {
let bad_context = WnafContext::new(w - 1);
let bad_table = bad_context.table(tmp4);
assert_eq!(context.mul_with_table(&bad_table, &s), None);
}
}

// sa + sb
a.mul_assign(s);
b.mul_assign(s);
Expand Down
1 change: 1 addition & 0 deletions test-templates/src/groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub fn group_test<G: Group>(a: G, mut b: G) {
let fr_zero = G::ScalarField::zero();
let fr_one = G::ScalarField::one();
let fr_two = fr_one + &fr_one;

assert_eq!(zero, zero);
assert_eq!(zero.is_zero(), true);
assert_eq!(a.mul(&fr_one), a);
Expand Down

0 comments on commit 3c2c4dd

Please sign in to comment.