diff --git a/src/wnaf.rs b/src/wnaf.rs index 6afa918..984d94d 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -156,23 +156,45 @@ pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize /// /// This function must be provided a `table` and `wnaf` that were constructed with /// the same window size; otherwise, it may panic or produce invalid results. +#[inline] pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { - let mut result = G::identity(); + wnaf_multi_exp(&[table], &[wnaf]) +} + +/// Performs w-NAF multi-exponentiation using the interleaved window method, also known as +/// Straus' method. +/// +/// The key insight is that when computing this sum by means of additions and doublings, the +/// doublings can be shared by performing the additions within an inner loop. +/// +/// This function must be provided with `tables` and `wnafs` that were constructed with +/// the same window size; otherwise, it may panic or produce invalid results. +pub(crate) fn wnaf_multi_exp, W: AsRef<[i64]>>( + tables: &[T], + wnafs: &[W], +) -> G { + debug_assert_eq!(tables.len(), wnafs.len()); + let window_size = wnafs.iter().map(|w| w.as_ref().len()).max().unwrap_or(0); + let mut result = G::identity(); let mut found_one = false; - for n in wnaf.iter().rev() { + for i in (0..window_size).rev() { + // Only double once per iteration of the loop if found_one { result = result.double(); } - if *n != 0 { - found_one = true; + for (table, wnaf) in tables.iter().zip(wnafs.iter()) { + let n = wnaf.as_ref().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]; + if n > 0 { + result += table.as_ref()[(n / 2) as usize]; + } else { + result -= table.as_ref()[((-n) / 2) as usize]; + } } } } @@ -505,6 +527,20 @@ impl WnafBase { WnafBase { table } } + + /// Perform a multiscalar multiplication. + /// + /// Computes a sum-of-products `aA + bB + ...` in variable time with w-NAF multi-exponentiation + /// using the interleaved window method, also known as Straus' method. + pub fn multiscalar_mul(scalars: I, bases: J) -> G + where + I: IntoIterator>, + J: IntoIterator, + { + let wnafs = scalars.into_iter().map(|s| s.wnaf).collect::>(); + let tables = bases.into_iter().map(|b| b.table).collect::>(); + wnaf_multi_exp(tables.as_slice(), wnafs.as_slice()) + } } impl Mul<&WnafScalar>