Skip to content
This repository was archived by the owner on Jun 7, 2026. It is now read-only.

Commit 2f1ea2e

Browse files
authored
Add back WnafBase::multiscalar_mul (#23)
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. 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. The API and implementation are inspired in part by `curve25519-dalek`, namely the `VartimeMultiscalarMul` trait and corresponding implementation in `straus.rs`. This results in ~28% speedup on `p256` for a 3 scalar/point input: ProjectivePoint operations/point-scalar lincomb (variable-time) time: [149.13 µs 149.80 µs 150.84 µs] change: [−27.999% −27.645% −27.267%] (p = 0.00 < 0.05)
1 parent a40270e commit 2f1ea2e

1 file changed

Lines changed: 44 additions & 8 deletions

File tree

src/wnaf.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,23 +156,45 @@ pub(crate) fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut Vec<i64>, c: S, window: usize
156156
///
157157
/// This function must be provided a `table` and `wnaf` that were constructed with
158158
/// the same window size; otherwise, it may panic or produce invalid results.
159+
#[inline]
159160
pub(crate) fn wnaf_exp<G: Group>(table: &[G], wnaf: &[i64]) -> G {
160-
let mut result = G::identity();
161+
wnaf_multi_exp(&[table], &[wnaf])
162+
}
163+
164+
/// Performs w-NAF multi-exponentiation using the interleaved window method, also known as
165+
/// Straus' method.
166+
///
167+
/// The key insight is that when computing this sum by means of additions and doublings, the
168+
/// doublings can be shared by performing the additions within an inner loop.
169+
///
170+
/// This function must be provided with `tables` and `wnafs` that were constructed with
171+
/// the same window size; otherwise, it may panic or produce invalid results.
172+
pub(crate) fn wnaf_multi_exp<G: Group, T: AsRef<[G]>, W: AsRef<[i64]>>(
173+
tables: &[T],
174+
wnafs: &[W],
175+
) -> G {
176+
debug_assert_eq!(tables.len(), wnafs.len());
177+
let window_size = wnafs.iter().map(|w| w.as_ref().len()).max().unwrap_or(0);
161178

179+
let mut result = G::identity();
162180
let mut found_one = false;
163181

164-
for n in wnaf.iter().rev() {
182+
for i in (0..window_size).rev() {
183+
// Only double once per iteration of the loop
165184
if found_one {
166185
result = result.double();
167186
}
168187

169-
if *n != 0 {
170-
found_one = true;
188+
for (table, wnaf) in tables.iter().zip(wnafs.iter()) {
189+
let n = wnaf.as_ref().get(i).copied().unwrap_or(0);
190+
if n != 0 {
191+
found_one = true;
171192

172-
if *n > 0 {
173-
result += &table[(n / 2) as usize];
174-
} else {
175-
result -= &table[((-n) / 2) as usize];
193+
if n > 0 {
194+
result += table.as_ref()[(n / 2) as usize];
195+
} else {
196+
result -= table.as_ref()[((-n) / 2) as usize];
197+
}
176198
}
177199
}
178200
}
@@ -505,6 +527,20 @@ impl<G: Group, const WINDOW_SIZE: usize> WnafBase<G, WINDOW_SIZE> {
505527

506528
WnafBase { table }
507529
}
530+
531+
/// Perform a multiscalar multiplication.
532+
///
533+
/// Computes a sum-of-products `aA + bB + ...` in variable time with w-NAF multi-exponentiation
534+
/// using the interleaved window method, also known as Straus' method.
535+
pub fn multiscalar_mul<I, J>(scalars: I, bases: J) -> G
536+
where
537+
I: IntoIterator<Item = WnafScalar<G::Scalar, WINDOW_SIZE>>,
538+
J: IntoIterator<Item = Self>,
539+
{
540+
let wnafs = scalars.into_iter().map(|s| s.wnaf).collect::<Vec<_>>();
541+
let tables = bases.into_iter().map(|b| b.table).collect::<Vec<_>>();
542+
wnaf_multi_exp(tables.as_slice(), wnafs.as_slice())
543+
}
508544
}
509545

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

0 commit comments

Comments
 (0)