From fcce9a11ff43a1aac0619cd24bee5d9aa159bfd2 Mon Sep 17 00:00:00 2001 From: kilic Date: Wed, 1 Feb 2023 20:57:34 +0300 Subject: [PATCH 1/5] feat: add endo scalar decomposition --- src/arithmetic.rs | 286 ++++++++++++++++++++++++++++++++++++++++++++ src/bn256/curve.rs | 41 ++++++- src/derive/curve.rs | 51 ++++++++ src/pasta/mod.rs | 60 ++++++++++ 4 files changed, 436 insertions(+), 2 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 42b84772..70748ead 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -4,6 +4,19 @@ //! This module is temporary, and the extension traits defined here are expected to be //! upstreamed into the `ff` and `group` crates after some refactoring. +use crate::CurveExt; + +pub(crate) struct EndoParameters { + pub(crate) gamma1: [u64; 4], + pub(crate) gamma2: [u64; 4], + pub(crate) b1: [u64; 4], + pub(crate) b2: [u64; 4], +} + +pub trait CurveEndo: CurveExt { + fn decompose_scalar(e: &Self::ScalarExt) -> (u128, bool, u128, bool); +} + pub trait CurveAffineExt: pasta_curves::arithmetic::CurveAffine { fn batch_add( points: &mut [Self], @@ -42,3 +55,276 @@ pub(crate) const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); (ret as u64, (ret >> 64) as u64) } + +/// Compute a + (b * c), returning the result and the new carry over. +#[inline(always)] +pub(crate) const fn macx(a: u64, b: u64, c: u64) -> (u64, u64) { + let res = (a as u128) + ((b as u128) * (c as u128)); + (res as u64, (res >> 64) as u64) +} + +/// Compute a * b, returning the result. +#[inline(always)] +pub(crate) fn mul_512(a: [u64; 4], b: [u64; 4]) -> [u64; 8] { + let (r0, carry) = macx(0, a[0], b[0]); + let (r1, carry) = macx(carry, a[0], b[1]); + let (r2, carry) = macx(carry, a[0], b[2]); + let (r3, carry_out) = macx(carry, a[0], b[3]); + + let (r1, carry) = macx(r1, a[1], b[0]); + let (r2, carry) = mac(r2, a[1], b[1], carry); + let (r3, carry) = mac(r3, a[1], b[2], carry); + let (r4, carry_out) = mac(carry_out, a[1], b[3], carry); + + let (r2, carry) = macx(r2, a[2], b[0]); + let (r3, carry) = mac(r3, a[2], b[1], carry); + let (r4, carry) = mac(r4, a[2], b[2], carry); + let (r5, carry_out) = mac(carry_out, a[2], b[3], carry); + + let (r3, carry) = macx(r3, a[3], b[0]); + let (r4, carry) = mac(r4, a[3], b[1], carry); + let (r5, carry) = mac(r5, a[3], b[2], carry); + let (r6, carry_out) = mac(carry_out, a[3], b[3], carry); + + [r0, r1, r2, r3, r4, r5, r6, carry_out] +} + +#[cfg(test)] +mod test { + use super::CurveEndo; + use crate::bn256::G1; + use ff::Field; + use pasta_curves::Ep; + use pasta_curves::Eq; + use rand_core::OsRng; + + // naive glv multiplication implementation + fn glv_mul(point: C, scalar: &C::ScalarExt) -> C { + const WINDOW: usize = 3; + // decompose scalar and convert to wnaf representation + let (k1, k1_neg, k2, k2_neg) = C::decompose_scalar(scalar); + + let mut k1_wnaf: Vec = Vec::new(); + let mut k2_wnaf: Vec = Vec::new(); + wnaf::form(&mut k1_wnaf, k1.to_le_bytes(), WINDOW); + wnaf::form(&mut k2_wnaf, k2.to_le_bytes(), WINDOW); + + let n = std::cmp::max(k1_wnaf.len(), k2_wnaf.len()); + k1_wnaf.resize(n, 0); + k2_wnaf.resize(n, 0); + + // prepare tables + let two_p = point.double(); + // T1 = {P, 3P, 5P, ...} + let mut table_k1 = vec![point.clone()]; + // T2 = {λP, 3λP, 5λP, ...} + let mut table_k2 = vec![point.endo()]; + for i in 1..WINDOW - 1 { + table_k1.push(table_k1[i - 1] + two_p); + table_k2.push(table_k1[i].endo()) + } + if !k2_neg { + table_k2.iter_mut().for_each(|p| *p = -p.clone()); + } + if k1_neg { + table_k1.iter_mut().for_each(|p| *p = -p.clone()); + } + // TODO: batch affine tables for mixed add? + + macro_rules! add { + ($acc:expr, $e:expr, $table:expr) => { + let idx = ($e.abs() >> 1) as usize; + $acc += if $e.is_positive() { + $table[idx] + } else if $e.is_negative() { + -$table[idx] + } else { + C::identity() + }; + }; + } + + // apply simultaneus double add + k1_wnaf + .iter() + .rev() + .zip(k2_wnaf.iter().rev()) + .fold(C::identity(), |acc, (e1, e2)| { + let mut acc = acc.double(); + add!(acc, e1, table_k1); + add!(acc, e2, table_k2); + acc + }) + } + + fn run_glv_mul_test() { + for _ in 0..10000 { + let point = C::random(OsRng); + let scalar = C::ScalarExt::random(OsRng); + let r0 = point * scalar; + let r1 = glv_mul(point, &scalar); + assert_eq!(r0, r1); + } + } + + #[test] + fn test_glv_mul() { + run_glv_mul_test::(); + run_glv_mul_test::(); + run_glv_mul_test::(); + } + + #[test] + fn test_wnaf_form() { + use rand::Rng; + fn from_wnaf(wnaf: &Vec) -> u128 { + wnaf.iter().rev().fold(0, |acc, next| { + let mut acc = acc * 2; + acc += *next as u128; + acc + }) + } + for w in 2..64 { + for e in 0..=u16::MAX { + let mut wnaf = vec![]; + wnaf::form(&mut wnaf, e.to_le_bytes(), w); + assert_eq!(e as u128, from_wnaf(&wnaf)); + } + } + let mut wnaf = vec![]; + for w in 2..64 { + for e in u128::MAX - 10000..=u128::MAX { + wnaf::form(&mut wnaf, e.to_le_bytes(), w); + assert_eq!(e, from_wnaf(&wnaf)); + } + } + for w in 2..10 { + for _ in 0..10000 { + let e: u128 = OsRng.gen(); + wnaf::form(&mut wnaf, e.to_le_bytes(), w); + assert_eq!(e as u128, from_wnaf(&wnaf)); + } + } + } + + // taken from zkcrypto/group + mod wnaf { + use std::convert::TryInto; + + #[derive(Debug, Clone)] + struct LimbBuffer<'a> { + buf: &'a [u8], + cur_idx: usize, + cur_limb: u64, + next_limb: u64, + } + + impl<'a> LimbBuffer<'a> { + fn new(buf: &'a [u8]) -> Self { + let mut ret = Self { + buf, + cur_idx: 0, + cur_limb: 0, + next_limb: 0, + }; + + // Initialise the limb buffers. + ret.increment_limb(); + ret.increment_limb(); + ret.cur_idx = 0usize; + ret + } + + fn increment_limb(&mut self) { + self.cur_idx += 1; + self.cur_limb = self.next_limb; + match self.buf.len() { + // There are no more bytes in the buffer; zero-extend. + 0 => self.next_limb = 0, + + // There are fewer bytes in the buffer than a u64 limb; zero-extend. + x @ 1..=7 => { + let mut next_limb = [0; 8]; + next_limb[..x].copy_from_slice(self.buf); + self.next_limb = u64::from_le_bytes(next_limb); + self.buf = &[]; + } + + // There are at least eight bytes in the buffer; read the next u64 limb. + _ => { + let (next_limb, rest) = self.buf.split_at(8); + self.next_limb = u64::from_le_bytes(next_limb.try_into().unwrap()); + self.buf = rest; + } + } + } + + fn get(&mut self, idx: usize) -> (u64, u64) { + assert!([self.cur_idx, self.cur_idx + 1].contains(&idx)); + if idx > self.cur_idx { + self.increment_limb(); + } + (self.cur_limb, self.next_limb) + } + } + + /// Replaces the contents of `wnaf` with the w-NAF representation of a little-endian + /// scalar. + pub(crate) fn 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); + + let bit_len = c.as_ref().len() * 8; + + wnaf.truncate(0); + wnaf.reserve(bit_len + 1); + + // Initialise the current and next limb buffers. + let mut limbs = LimbBuffer::new(c.as_ref()); + + 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 (cur_u64, next_u64) = limbs.get(u64_idx); + let bit_buf = if bit_idx + window < 64 { + // This window's bits are contained in a single u64 + cur_u64 >> bit_idx + } else { + // Combine the current u64's bits with the bits from the next u64 + (cur_u64 >> bit_idx) | (next_u64 << (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(std::iter::repeat(0).take(window - 1)); + pos += window; + } + } + wnaf.truncate(wnaf.len().saturating_sub(window - 1)); + } + } +} diff --git a/src/bn256/curve.rs b/src/bn256/curve.rs index 4d115008..c5f92f6f 100644 --- a/src/bn256/curve.rs +++ b/src/bn256/curve.rs @@ -1,3 +1,7 @@ +use crate::arithmetic::mul_512; +use crate::arithmetic::sbb; +use crate::arithmetic::CurveEndo; +use crate::arithmetic::EndoParameters; use crate::bn256::Fq; use crate::bn256::Fq2; use crate::bn256::Fr; @@ -10,12 +14,14 @@ use crate::{ impl_binops_additive_specify_output, impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, new_curve_impl, }; +use crate::endo; use crate::{Coordinates, CurveAffine, CurveAffineExt, CurveExt}; use core::cmp; use core::fmt::Debug; use core::iter::Sum; use core::ops::{Add, Mul, Neg, Sub}; use rand::RngCore; +use std::convert::TryInto; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; #[cfg(feature = "derive_serde")] @@ -111,6 +117,20 @@ const G2_GENERATOR_Y: Fq2 = Fq2 { ]), }; +const ENDO_PARAMS: EndoParameters = EndoParameters { + gamma1: [ + 0x7a7bd9d4391eb18du64, + 0x4ccef014a773d2cfu64, + 0x0000000000000002u64, + 0u64, + ], + gamma2: [0xd91d232ec7e0b3d7u64, 0x0000000000000002u64, 0u64, 0u64], + b1: [0x8211bbeb7d4f1128u64, 0x6f4d8248eeb859fcu64, 0u64, 0u64], + b2: [0x89d3256894d213e3u64, 0u64, 0u64, 0u64], +}; + +endo!(G1, Fr, ENDO_PARAMS); + impl group::cofactor::CofactorGroup for G1 { type Subgroup = G1; @@ -180,9 +200,14 @@ impl CofactorGroup for G2 { #[cfg(test)] mod tests { + + use crate::arithmetic::CurveEndo; use crate::bn256::{Fr, G1, G2}; use crate::CurveExt; + use ff::Field; + use ff::PrimeField; use ff::WithSmallOrderMulGroup; + use rand_core::OsRng; #[test] fn test_curve() { @@ -191,12 +216,24 @@ mod tests { } #[test] - fn test_endo_consistency() { + fn test_endo() { let g = G1::generator(); assert_eq!(g * Fr::ZETA, g.endo()); - let g = G2::generator(); assert_eq!(g * Fr::ZETA, g.endo()); + for _ in 0..100000 { + let k = Fr::random(OsRng); + let (k1, k1_neg, k2, k2_neg) = G1::decompose_scalar(&k); + if k1_neg & k2_neg { + assert_eq!(k, -Fr::from_u128(k1) + Fr::ZETA * Fr::from_u128(k2)) + } else if k1_neg { + assert_eq!(k, -Fr::from_u128(k1) - Fr::ZETA * Fr::from_u128(k2)) + } else if k2_neg { + assert_eq!(k, Fr::from_u128(k1) + Fr::ZETA * Fr::from_u128(k2)) + } else { + assert_eq!(k, Fr::from_u128(k1) - Fr::ZETA * Fr::from_u128(k2)) + } + } } #[test] diff --git a/src/derive/curve.rs b/src/derive/curve.rs index 39aac496..dc1a4aa4 100644 --- a/src/derive/curve.rs +++ b/src/derive/curve.rs @@ -140,6 +140,57 @@ macro_rules! batch_add { }; } +#[macro_export] +macro_rules! endo { + ($name:ident, $field:ident, $params:expr) => { + impl CurveEndo for $name { + fn decompose_scalar(k: &$field) -> (u128, bool, u128, bool) { + let to_limbs = |e: &$field| { + let repr = e.to_repr(); + let repr = repr.as_ref(); + let tmp0 = u64::from_le_bytes(repr[0..8].try_into().unwrap()); + let tmp1 = u64::from_le_bytes(repr[8..16].try_into().unwrap()); + let tmp2 = u64::from_le_bytes(repr[16..24].try_into().unwrap()); + let tmp3 = u64::from_le_bytes(repr[24..32].try_into().unwrap()); + [tmp0, tmp1, tmp2, tmp3] + }; + + let get_lower_128 = |e: &$field| { + let e = to_limbs(e); + u128::from(e[0]) | (u128::from(e[1]) << 64) + }; + + let is_neg = |e: &$field| { + let e = to_limbs(e); + let (_, borrow) = sbb(0xffffffffffffffff, e[0], 0); + let (_, borrow) = sbb(0xffffffffffffffff, e[1], borrow); + let (_, borrow) = sbb(0xffffffffffffffff, e[2], borrow); + let (_, borrow) = sbb(0x00, e[3], borrow); + borrow & 1 != 0 + }; + + let input = to_limbs(&k); + let c1 = mul_512($params.gamma2, input); + let c2 = mul_512($params.gamma1, input); + let c1 = [c1[4], c1[5], c1[6], c1[7]]; + let c2 = [c2[4], c2[5], c2[6], c2[7]]; + let q1 = mul_512(c1, $params.b1); + let q2 = mul_512(c2, $params.b2); + let q1 = $field::from_raw([q1[0], q1[1], q1[2], q1[3]]); + let q2 = $field::from_raw([q2[0], q2[1], q2[2], q2[3]]); + let k2 = q2 - q1; + let k1 = k + k2 * $field::ZETA; + let k1_neg = is_neg(&k1); + let k2_neg = is_neg(&k2); + let k1 = if k1_neg { -k1 } else { k1 }; + let k2 = if k2_neg { -k2 } else { k2 }; + + (get_lower_128(&k1), k1_neg, get_lower_128(&k2), k2_neg) + } + } + }; +} + #[macro_export] macro_rules! new_curve_impl { (($($privacy:tt)*), diff --git a/src/pasta/mod.rs b/src/pasta/mod.rs index f6aee547..0252b199 100644 --- a/src/pasta/mod.rs +++ b/src/pasta/mod.rs @@ -1,4 +1,13 @@ +use crate::arithmetic::mul_512; +use crate::arithmetic::sbb; +use crate::{ + arithmetic::{CurveEndo, EndoParameters}, + endo, +}; +use ff::PrimeField; +use ff::WithSmallOrderMulGroup; pub use pasta_curves::{pallas, vesta, Ep, EpAffine, Eq, EqAffine, Fp, Fq}; +use std::convert::TryInto; impl crate::CurveAffineExt for EpAffine { fn batch_add( @@ -25,3 +34,54 @@ impl crate::CurveAffineExt for EqAffine { unimplemented!(); } } + +const ENDO_PARAMS_EQ: EndoParameters = EndoParameters { + gamma1: [0x32c49e4c00000003, 0x279a745902a2654e, 0x1, 0x0], + gamma2: [0x31f0256800000002, 0x4f34e8b2066389a4, 0x2, 0x0], + b1: [0x8cb1279300000001, 0x49e69d1640a89953, 0x0, 0x0], + b2: [0x0c7c095a00000001, 0x93cd3a2c8198e269, 0x0, 0x0], +}; + +const ENDO_PARAMS_EP: EndoParameters = EndoParameters { + gamma1: [0x32c49e4bffffffff, 0x279a745902a2654e, 0x1, 0x0], + gamma2: [0x31f0256800000002, 0x4f34e8b2066389a4, 0x2, 0x0], + b1: [0x8cb1279300000000, 0x49e69d1640a89953, 0x0, 0x0], + b2: [0x0c7c095a00000001, 0x93cd3a2c8198e269, 0x0, 0x0], +}; + +endo!(Eq, Fp, ENDO_PARAMS_EQ); +endo!(Ep, Fq, ENDO_PARAMS_EP); + +#[test] +fn test_endo() { + use ff::Field; + use rand_core::OsRng; + + for _ in 0..100000 { + let k = Fp::random(OsRng); + let (k1, k1_neg, k2, k2_neg) = Eq::decompose_scalar(&k); + if k1_neg & k2_neg { + assert_eq!(k, -Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) + } else if k1_neg { + assert_eq!(k, -Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) + } else if k2_neg { + assert_eq!(k, Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) + } else { + assert_eq!(k, Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) + } + } + + for _ in 0..100000 { + let k = Fp::random(OsRng); + let (k1, k1_neg, k2, k2_neg) = Eq::decompose_scalar(&k); + if k1_neg & k2_neg { + assert_eq!(k, -Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) + } else if k1_neg { + assert_eq!(k, -Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) + } else if k2_neg { + assert_eq!(k, Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) + } else { + assert_eq!(k, Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) + } + } +} From 4bcf4eb740fb64888ab38ab287720de8c3182ded Mon Sep 17 00:00:00 2001 From: kilic Date: Wed, 1 Feb 2023 22:48:42 +0300 Subject: [PATCH 2/5] fix: clippy --- src/arithmetic.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 70748ead..277a24ff 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -116,7 +116,7 @@ mod test { // prepare tables let two_p = point.double(); // T1 = {P, 3P, 5P, ...} - let mut table_k1 = vec![point.clone()]; + let mut table_k1 = vec![point]; // T2 = {λP, 3λP, 5λP, ...} let mut table_k2 = vec![point.endo()]; for i in 1..WINDOW - 1 { @@ -124,10 +124,10 @@ mod test { table_k2.push(table_k1[i].endo()) } if !k2_neg { - table_k2.iter_mut().for_each(|p| *p = -p.clone()); + table_k2.iter_mut().for_each(|p| *p = -*p); } if k1_neg { - table_k1.iter_mut().for_each(|p| *p = -p.clone()); + table_k1.iter_mut().for_each(|p| *p = -*p); } // TODO: batch affine tables for mixed add? @@ -177,7 +177,7 @@ mod test { #[test] fn test_wnaf_form() { use rand::Rng; - fn from_wnaf(wnaf: &Vec) -> u128 { + fn from_wnaf(wnaf: &[i64]) -> u128 { wnaf.iter().rev().fold(0, |acc, next| { let mut acc = acc * 2; acc += *next as u128; From 56a31897688865cd218503fad70689cbe4a0c136 Mon Sep 17 00:00:00 2001 From: kilic Date: Wed, 1 Mar 2023 19:51:36 +0300 Subject: [PATCH 3/5] docs: add comments to endo parameters --- src/bn256/curve.rs | 6 ++++++ src/pasta/mod.rs | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/bn256/curve.rs b/src/bn256/curve.rs index c5f92f6f..c10f603e 100644 --- a/src/bn256/curve.rs +++ b/src/bn256/curve.rs @@ -117,13 +117,19 @@ const G2_GENERATOR_Y: Fq2 = Fq2 { ]), }; +// Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go +// with `bn256::Fr::ZETA` +// See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md +// to have more details about the endomorphism. const ENDO_PARAMS: EndoParameters = EndoParameters { + // round(b2/n) gamma1: [ 0x7a7bd9d4391eb18du64, 0x4ccef014a773d2cfu64, 0x0000000000000002u64, 0u64, ], + // round(-b1/n) gamma2: [0xd91d232ec7e0b3d7u64, 0x0000000000000002u64, 0u64, 0u64], b1: [0x8211bbeb7d4f1128u64, 0x6f4d8248eeb859fcu64, 0u64, 0u64], b2: [0x89d3256894d213e3u64, 0u64, 0u64, 0u64], diff --git a/src/pasta/mod.rs b/src/pasta/mod.rs index 0252b199..9cd4592e 100644 --- a/src/pasta/mod.rs +++ b/src/pasta/mod.rs @@ -35,15 +35,27 @@ impl crate::CurveAffineExt for EqAffine { } } +// Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go +// with `pasta_curves::Fp::ZETA` +// See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md +// to have more details about the endomorphism. const ENDO_PARAMS_EQ: EndoParameters = EndoParameters { + // round(b2/n) gamma1: [0x32c49e4c00000003, 0x279a745902a2654e, 0x1, 0x0], + // round(-b1/n) gamma2: [0x31f0256800000002, 0x4f34e8b2066389a4, 0x2, 0x0], b1: [0x8cb1279300000001, 0x49e69d1640a89953, 0x0, 0x0], b2: [0x0c7c095a00000001, 0x93cd3a2c8198e269, 0x0, 0x0], }; +// Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go +// with `pasta_curves::Fq::ZETA` +// See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md +// to have more details about the endomorphism. const ENDO_PARAMS_EP: EndoParameters = EndoParameters { + // round(b2/n) gamma1: [0x32c49e4bffffffff, 0x279a745902a2654e, 0x1, 0x0], + // round(-b1/n) gamma2: [0x31f0256800000002, 0x4f34e8b2066389a4, 0x2, 0x0], b1: [0x8cb1279300000000, 0x49e69d1640a89953, 0x0, 0x0], b2: [0x0c7c095a00000001, 0x93cd3a2c8198e269, 0x0, 0x0], From a1b2d0a5ab073227630ca453c969d954d6e4ad55 Mon Sep 17 00:00:00 2001 From: kilic Date: Mon, 5 Jun 2023 19:32:40 +0300 Subject: [PATCH 4/5] remove example glv mul --- src/arithmetic.rs | 240 ---------------------------------------------- 1 file changed, 240 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 277a24ff..edbefbd6 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -88,243 +88,3 @@ pub(crate) fn mul_512(a: [u64; 4], b: [u64; 4]) -> [u64; 8] { [r0, r1, r2, r3, r4, r5, r6, carry_out] } - -#[cfg(test)] -mod test { - use super::CurveEndo; - use crate::bn256::G1; - use ff::Field; - use pasta_curves::Ep; - use pasta_curves::Eq; - use rand_core::OsRng; - - // naive glv multiplication implementation - fn glv_mul(point: C, scalar: &C::ScalarExt) -> C { - const WINDOW: usize = 3; - // decompose scalar and convert to wnaf representation - let (k1, k1_neg, k2, k2_neg) = C::decompose_scalar(scalar); - - let mut k1_wnaf: Vec = Vec::new(); - let mut k2_wnaf: Vec = Vec::new(); - wnaf::form(&mut k1_wnaf, k1.to_le_bytes(), WINDOW); - wnaf::form(&mut k2_wnaf, k2.to_le_bytes(), WINDOW); - - let n = std::cmp::max(k1_wnaf.len(), k2_wnaf.len()); - k1_wnaf.resize(n, 0); - k2_wnaf.resize(n, 0); - - // prepare tables - let two_p = point.double(); - // T1 = {P, 3P, 5P, ...} - let mut table_k1 = vec![point]; - // T2 = {λP, 3λP, 5λP, ...} - let mut table_k2 = vec![point.endo()]; - for i in 1..WINDOW - 1 { - table_k1.push(table_k1[i - 1] + two_p); - table_k2.push(table_k1[i].endo()) - } - if !k2_neg { - table_k2.iter_mut().for_each(|p| *p = -*p); - } - if k1_neg { - table_k1.iter_mut().for_each(|p| *p = -*p); - } - // TODO: batch affine tables for mixed add? - - macro_rules! add { - ($acc:expr, $e:expr, $table:expr) => { - let idx = ($e.abs() >> 1) as usize; - $acc += if $e.is_positive() { - $table[idx] - } else if $e.is_negative() { - -$table[idx] - } else { - C::identity() - }; - }; - } - - // apply simultaneus double add - k1_wnaf - .iter() - .rev() - .zip(k2_wnaf.iter().rev()) - .fold(C::identity(), |acc, (e1, e2)| { - let mut acc = acc.double(); - add!(acc, e1, table_k1); - add!(acc, e2, table_k2); - acc - }) - } - - fn run_glv_mul_test() { - for _ in 0..10000 { - let point = C::random(OsRng); - let scalar = C::ScalarExt::random(OsRng); - let r0 = point * scalar; - let r1 = glv_mul(point, &scalar); - assert_eq!(r0, r1); - } - } - - #[test] - fn test_glv_mul() { - run_glv_mul_test::(); - run_glv_mul_test::(); - run_glv_mul_test::(); - } - - #[test] - fn test_wnaf_form() { - use rand::Rng; - fn from_wnaf(wnaf: &[i64]) -> u128 { - wnaf.iter().rev().fold(0, |acc, next| { - let mut acc = acc * 2; - acc += *next as u128; - acc - }) - } - for w in 2..64 { - for e in 0..=u16::MAX { - let mut wnaf = vec![]; - wnaf::form(&mut wnaf, e.to_le_bytes(), w); - assert_eq!(e as u128, from_wnaf(&wnaf)); - } - } - let mut wnaf = vec![]; - for w in 2..64 { - for e in u128::MAX - 10000..=u128::MAX { - wnaf::form(&mut wnaf, e.to_le_bytes(), w); - assert_eq!(e, from_wnaf(&wnaf)); - } - } - for w in 2..10 { - for _ in 0..10000 { - let e: u128 = OsRng.gen(); - wnaf::form(&mut wnaf, e.to_le_bytes(), w); - assert_eq!(e as u128, from_wnaf(&wnaf)); - } - } - } - - // taken from zkcrypto/group - mod wnaf { - use std::convert::TryInto; - - #[derive(Debug, Clone)] - struct LimbBuffer<'a> { - buf: &'a [u8], - cur_idx: usize, - cur_limb: u64, - next_limb: u64, - } - - impl<'a> LimbBuffer<'a> { - fn new(buf: &'a [u8]) -> Self { - let mut ret = Self { - buf, - cur_idx: 0, - cur_limb: 0, - next_limb: 0, - }; - - // Initialise the limb buffers. - ret.increment_limb(); - ret.increment_limb(); - ret.cur_idx = 0usize; - ret - } - - fn increment_limb(&mut self) { - self.cur_idx += 1; - self.cur_limb = self.next_limb; - match self.buf.len() { - // There are no more bytes in the buffer; zero-extend. - 0 => self.next_limb = 0, - - // There are fewer bytes in the buffer than a u64 limb; zero-extend. - x @ 1..=7 => { - let mut next_limb = [0; 8]; - next_limb[..x].copy_from_slice(self.buf); - self.next_limb = u64::from_le_bytes(next_limb); - self.buf = &[]; - } - - // There are at least eight bytes in the buffer; read the next u64 limb. - _ => { - let (next_limb, rest) = self.buf.split_at(8); - self.next_limb = u64::from_le_bytes(next_limb.try_into().unwrap()); - self.buf = rest; - } - } - } - - fn get(&mut self, idx: usize) -> (u64, u64) { - assert!([self.cur_idx, self.cur_idx + 1].contains(&idx)); - if idx > self.cur_idx { - self.increment_limb(); - } - (self.cur_limb, self.next_limb) - } - } - - /// Replaces the contents of `wnaf` with the w-NAF representation of a little-endian - /// scalar. - pub(crate) fn 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); - - let bit_len = c.as_ref().len() * 8; - - wnaf.truncate(0); - wnaf.reserve(bit_len + 1); - - // Initialise the current and next limb buffers. - let mut limbs = LimbBuffer::new(c.as_ref()); - - 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 (cur_u64, next_u64) = limbs.get(u64_idx); - let bit_buf = if bit_idx + window < 64 { - // This window's bits are contained in a single u64 - cur_u64 >> bit_idx - } else { - // Combine the current u64's bits with the bits from the next u64 - (cur_u64 >> bit_idx) | (next_u64 << (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(std::iter::repeat(0).take(window - 1)); - pos += window; - } - } - wnaf.truncate(wnaf.len().saturating_sub(window - 1)); - } - } -} From 75fd90b2b07cf3ff2148032f194541deac029bec Mon Sep 17 00:00:00 2001 From: kilic Date: Mon, 5 Jun 2023 19:37:08 +0300 Subject: [PATCH 5/5] cargo fmt --- src/bn256/curve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bn256/curve.rs b/src/bn256/curve.rs index c10f603e..b1d6abe1 100644 --- a/src/bn256/curve.rs +++ b/src/bn256/curve.rs @@ -5,6 +5,7 @@ use crate::arithmetic::EndoParameters; use crate::bn256::Fq; use crate::bn256::Fq2; use crate::bn256::Fr; +use crate::endo; use crate::ff::WithSmallOrderMulGroup; use crate::ff::{Field, PrimeField}; use crate::group::Curve; @@ -14,7 +15,6 @@ use crate::{ impl_binops_additive_specify_output, impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, new_curve_impl, }; -use crate::endo; use crate::{Coordinates, CurveAffine, CurveAffineExt, CurveExt}; use core::cmp; use core::fmt::Debug;