#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
)]
#![allow(clippy::int_plus_one, reason = "clearer for our use cases ")]
#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
#[cfg(feature = "alloc")]
#[macro_use]
extern crate alloc;
mod base;
mod limb_buffer;
mod scalar;
mod traits;
#[cfg(feature = "alloc")]
mod boxed;
pub use crate::{
base::WnafBase,
scalar::WnafScalar,
traits::{WindowSize, WnafGroup, WnafSize},
};
pub use array;
pub use group::Group;
#[cfg(feature = "alloc")]
pub use crate::boxed::BoxedWnaf;
use crate::limb_buffer::LimbBuffer;
use ff::PrimeField;
pub type Digit = i8;
pub const W_MAX: usize = 8;
fn wnaf_table<G: Group>(table: &mut [G], base: &G, window: usize) {
debug_assert_eq!(table.len(), 1 << (window - 2));
let dbl = base.double();
let mut cur = *base;
for entry in table {
*entry = cur;
cur.add_assign(&dbl);
}
}
#[allow(clippy::cast_possible_wrap)]
fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut [Digit], c: S, bit_len: usize, window: usize) -> usize {
debug_assert!(window >= 2);
debug_assert!(window <= W_MAX);
debug_assert!(bit_len < wnaf.len(), "wnaf storage too small");
debug_assert!(c.as_ref().len() <= bit_len.div_ceil(8), "input too large");
let width = 1u64 << window;
let window_mask = width - 1;
let mut limbs = LimbBuffer::new(c.as_ref());
let mut pos = 0;
let mut carry = 0;
let mut cursor = 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[cursor] = 0;
cursor += 1;
pos += 1;
} else {
wnaf[cursor] = if window_val < width / 2 {
carry = 0;
window_val as Digit
} else {
carry = 1;
(window_val as Digit).wrapping_sub(width as Digit)
};
cursor += 1;
let max_pos = bit_len.saturating_sub(carry as usize);
let skip = window.min(max_pos - pos);
for _ in 1..skip {
wnaf[cursor] = 0;
cursor += 1;
}
pos += skip;
}
}
if carry != 0 {
wnaf[cursor] = carry as Digit;
cursor += 1;
}
cursor
}
#[allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss
)]
fn wnaf_multi_exp<'a, G, I>(terms: I) -> G
where
G: Group,
I: Clone + IntoIterator<Item = (&'a [G], &'a [Digit], usize)>,
{
let window_size = terms
.clone()
.into_iter()
.map(|(_, _, len)| len)
.max()
.unwrap_or(0);
let mut result = G::identity();
let mut found_one = false;
for i in (0..window_size).rev() {
if found_one {
result = result.double();
}
for (table, wnaf, _) in terms.clone() {
let n = wnaf.get(i).copied().unwrap_or(0);
if n != 0 {
found_one = true;
if n > 0 {
result += table[(n / 2) as usize];
} else {
result -= table[((-n) / 2) as usize];
}
}
}
}
result
}
fn le_repr<F: PrimeField>(fe: &F) -> F::Repr {
let mut ret = fe.to_repr();
ret.as_mut().reverse();
ret
}