use alloc::vec::Vec;
use core::iter;
use core::marker::PhantomData;
use core::ops::Mul;
use ff::PrimeField;
use super::Group;
pub trait WnafGroup: Group {
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize;
}
pub(crate) fn wnaf_table<G: Group>(table: &mut Vec<G>, mut base: G, window: usize) {
table.truncate(0);
table.reserve(1 << (window - 1));
let dbl = base.double();
for _ in 0..(1 << (window - 1)) {
table.push(base);
base.add_assign(&dbl);
}
}
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,
};
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() {
0 => self.next_limb = 0,
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 = &[];
}
_ => {
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)
}
}
pub(crate) fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut Vec<i64>, c: S, window: usize) {
debug_assert!(window >= 2);
debug_assert!(window <= 64);
let bit_len = c.as_ref().len() * 8;
wnaf.truncate(0);
wnaf.reserve(bit_len);
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 {
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 {
cur_u64 >> bit_idx
} else {
(cur_u64 >> bit_idx) | (next_u64 << (64 - bit_idx))
};
let window_val = carry + (bit_buf & window_mask);
if window_val & 1 == 0 {
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;
}
}
}
pub(crate) fn wnaf_exp<G: Group>(table: &[G], wnaf: &[i64]) -> G {
let mut result = G::identity();
let mut found_one = false;
for n in wnaf.iter().rev() {
if found_one {
result = result.double();
}
if *n != 0 {
found_one = true;
if *n > 0 {
result += &table[(n / 2) as usize];
} else {
result -= &table[((-n) / 2) as usize];
}
}
}
result
}
#[derive(Debug)]
pub struct Wnaf<W, B, S> {
base: B,
scalar: S,
window_size: W,
}
impl<G: Group> Wnaf<(), Vec<G>, Vec<i64>> {
pub fn new() -> Self {
Wnaf {
base: vec![],
scalar: vec![],
window_size: (),
}
}
}
#[cfg(feature = "wnaf-memuse")]
impl<G: Group + memuse::DynamicUsage> memuse::DynamicUsage for Wnaf<(), Vec<G>, Vec<i64>> {
fn dynamic_usage(&self) -> usize {
self.base.dynamic_usage() + self.scalar.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
let (base_lower, base_upper) = self.base.dynamic_usage_bounds();
let (scalar_lower, scalar_upper) = self.scalar.dynamic_usage_bounds();
(
base_lower + scalar_lower,
base_upper.zip(scalar_upper).map(|(a, b)| a + b),
)
}
}
impl<G: WnafGroup> Wnaf<(), Vec<G>, Vec<i64>> {
pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf<usize, &[G], &mut Vec<i64>> {
let window_size = G::recommended_wnaf_for_num_scalars(num_scalars);
wnaf_table(&mut self.base, base, window_size);
Wnaf {
base: &self.base[..],
scalar: &mut self.scalar,
window_size,
}
}
pub fn scalar(&mut self, scalar: &<G as Group>::Scalar) -> Wnaf<usize, &mut Vec<G>, &[i64]> {
let window_size = 4;
wnaf_form(&mut self.scalar, scalar.to_repr(), window_size);
Wnaf {
base: &mut self.base,
scalar: &self.scalar[..],
window_size,
}
}
}
impl<'a, G: Group> Wnaf<usize, &'a [G], &'a mut Vec<i64>> {
pub fn shared(&self) -> Wnaf<usize, &'a [G], Vec<i64>> {
Wnaf {
base: self.base,
scalar: vec![],
window_size: self.window_size,
}
}
}
#[cfg(feature = "wnaf-memuse")]
impl<'a, G: Group> memuse::DynamicUsage for Wnaf<usize, &'a [G], Vec<i64>> {
fn dynamic_usage(&self) -> usize {
self.scalar.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
self.scalar.dynamic_usage_bounds()
}
}
impl<'a, G: Group> Wnaf<usize, &'a mut Vec<G>, &'a [i64]> {
pub fn shared(&self) -> Wnaf<usize, Vec<G>, &'a [i64]> {
Wnaf {
base: vec![],
scalar: self.scalar,
window_size: self.window_size,
}
}
}
#[cfg(feature = "wnaf-memuse")]
impl<'a, G: Group + memuse::DynamicUsage> memuse::DynamicUsage for Wnaf<usize, Vec<G>, &'a [i64]> {
fn dynamic_usage(&self) -> usize {
self.base.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
self.base.dynamic_usage_bounds()
}
}
impl<B, S: AsRef<[i64]>> Wnaf<usize, B, S> {
pub fn base<G: Group>(&mut self, base: G) -> G
where
B: AsMut<Vec<G>>,
{
wnaf_table(self.base.as_mut(), base, self.window_size);
wnaf_exp(self.base.as_mut(), self.scalar.as_ref())
}
}
impl<B, S: AsMut<Vec<i64>>> Wnaf<usize, B, S> {
pub fn scalar<G: Group>(&mut self, scalar: &<G as Group>::Scalar) -> G
where
B: AsRef<[G]>,
{
wnaf_form(self.scalar.as_mut(), scalar.to_repr(), self.window_size);
wnaf_exp(self.base.as_ref(), self.scalar.as_mut())
}
}
#[derive(Clone, Debug)]
pub struct WnafScalar<F: PrimeField, const WINDOW_SIZE: usize> {
wnaf: Vec<i64>,
field: PhantomData<F>,
}
#[cfg(feature = "wnaf-memuse")]
impl<F: PrimeField, const WINDOW_SIZE: usize> memuse::DynamicUsage for WnafScalar<F, WINDOW_SIZE> {
fn dynamic_usage(&self) -> usize {
self.wnaf.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
self.wnaf.dynamic_usage_bounds()
}
}
impl<F: PrimeField, const WINDOW_SIZE: usize> WnafScalar<F, WINDOW_SIZE> {
pub fn new(scalar: &F) -> Self {
let mut wnaf = vec![];
wnaf_form(&mut wnaf, scalar.to_repr(), WINDOW_SIZE);
WnafScalar {
wnaf,
field: PhantomData::default(),
}
}
}
#[derive(Clone, Debug)]
pub struct WnafBase<G: Group, const WINDOW_SIZE: usize> {
table: Vec<G>,
}
#[cfg(feature = "wnaf-memuse")]
impl<G: Group + memuse::DynamicUsage, const WINDOW_SIZE: usize> memuse::DynamicUsage
for WnafBase<G, WINDOW_SIZE>
{
fn dynamic_usage(&self) -> usize {
self.table.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
self.table.dynamic_usage_bounds()
}
}
impl<G: Group, const WINDOW_SIZE: usize> WnafBase<G, WINDOW_SIZE> {
pub fn new(base: G) -> Self {
let mut table = vec![];
wnaf_table(&mut table, base, WINDOW_SIZE);
WnafBase { table }
}
}
impl<G: Group, const WINDOW_SIZE: usize> Mul<&WnafScalar<G::Scalar, WINDOW_SIZE>>
for &WnafBase<G, WINDOW_SIZE>
{
type Output = G;
fn mul(self, rhs: &WnafScalar<G::Scalar, WINDOW_SIZE>) -> Self::Output {
wnaf_exp(&self.table, &rhs.wnaf)
}
}