Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions src/wnaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@ pub trait WnafGroup: Group {
}

/// Replaces the contents of `table` with a w-NAF window table for the given window size.
///
/// For a window of size `w`, non-zero wNAF digits are odd and have magnitude at most
/// `2^(w-1) - 1`. The table is indexed by `|digit| / 2`, so the required size is
/// `(2^(w-1) - 1) / 2 + 1 = 2^(w-2)` entries.
pub(crate) fn wnaf_table<G: Group>(table: &mut Vec<G>, mut base: G, window: usize) {
let table_len = 1 << (window - 2);
table.truncate(0);
table.reserve(1 << (window - 1));
table.reserve(table_len);

let dbl = base.double();

for _ in 0..(1 << (window - 1)) {
for _ in 0..table_len {
table.push(base);
base.add_assign(&dbl);
}
Expand Down Expand Up @@ -451,14 +456,29 @@ impl<F: PrimeField, const WINDOW_SIZE: usize> WnafScalar<F, WINDOW_SIZE> {
/// Computes the w-NAF representation of the given scalar with the specified
/// `WINDOW_SIZE`.
pub fn new(scalar: &F) -> Self {
let mut wnaf = vec![];
let repr = scalar.to_le_repr();
let mut wnaf = Vec::with_capacity(repr.as_ref().len() * 8 + WINDOW_SIZE);
wnaf_form(&mut wnaf, repr, WINDOW_SIZE);

WnafScalar {
wnaf,
field: PhantomData,
}
}

// Compute the w-NAF form of the scalar.
wnaf_form(&mut wnaf, scalar.to_le_repr(), WINDOW_SIZE);
/// Computes the w-NAF representation from raw little-endian bytes.
///
/// This is useful when the scalar has been pre-decomposed (e.g. via GLV endomorphism)
/// and is available as a byte slice shorter than the full field representation.
/// Shorter slices produce fewer wNAF digits, reducing the number of doublings in
/// the evaluation loop.
pub fn from_le_bytes(bytes: &[u8]) -> Self {
let mut wnaf = Vec::with_capacity(bytes.len() * 8 + WINDOW_SIZE);
wnaf_form(&mut wnaf, bytes, WINDOW_SIZE);

WnafScalar {
wnaf,
field: PhantomData::default(),
field: PhantomData,
}
}
}
Expand Down Expand Up @@ -518,11 +538,8 @@ impl<G: Group + memuse::DynamicUsage, const WINDOW_SIZE: usize> memuse::DynamicU
impl<G: Group, const WINDOW_SIZE: usize> WnafBase<G, WINDOW_SIZE> {
/// Computes a window table for the given base with the specified `WINDOW_SIZE`.
pub fn new(base: G) -> Self {
let mut table = vec![];

// Compute a window table for the provided base and window size.
let mut table = Vec::with_capacity(1 << (WINDOW_SIZE - 2));
wnaf_table(&mut table, base, WINDOW_SIZE);

WnafBase { table }
}

Expand All @@ -540,6 +557,18 @@ impl<G: Group, const WINDOW_SIZE: usize> WnafBase<G, WINDOW_SIZE> {

wnaf_multi_exp(tables.as_slice(), wnafs.as_slice())
}

/// Perform a multiscalar multiplication over a fixed-size array of bases and scalars,
/// avoiding heap allocation for the slice-of-slices.
pub fn multiscalar_mul_array<const N: usize>(
scalars: &[WnafScalar<G::Scalar, WINDOW_SIZE>; N],
bases: &[Self; N],
) -> G {
let wnafs = scalars.each_ref().map(|s| s.wnaf.as_slice());
let tables = bases.each_ref().map(|b| b.table.as_slice());

wnaf_multi_exp(&tables, &wnafs)
}
}

impl<G: Group, const WINDOW_SIZE: usize> Mul<&WnafScalar<G::Scalar, WINDOW_SIZE>>
Expand Down