diff --git a/src/wnaf.rs b/src/wnaf.rs index 6de3d71..1849c9e 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -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(table: &mut Vec, 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); } @@ -451,14 +456,29 @@ impl WnafScalar { /// 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, } } } @@ -518,11 +538,8 @@ impl memuse::DynamicU impl WnafBase { /// 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 } } @@ -540,6 +557,18 @@ impl WnafBase { 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( + scalars: &[WnafScalar; 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 Mul<&WnafScalar>