rustcrypto_group/wnaf.rs
1use alloc::vec::Vec;
2use core::iter;
3use core::marker::PhantomData;
4use core::ops::Mul;
5
6use ff::PrimeField;
7
8use super::Group;
9
10/// Extension trait on a [`Group`] that provides helpers used by [`Wnaf`].
11pub trait WnafGroup: Group {
12 /// Recommends a wNAF window size given the number of scalars you intend to multiply
13 /// a base by. Always returns a number between 2 and 22, inclusive.
14 fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize;
15}
16
17/// Replaces the contents of `table` with a w-NAF window table for the given window size.
18pub(crate) fn wnaf_table<G: Group>(table: &mut Vec<G>, mut base: G, window: usize) {
19 table.truncate(0);
20 table.reserve(1 << (window - 1));
21
22 let dbl = base.double();
23
24 for _ in 0..(1 << (window - 1)) {
25 table.push(base);
26 base.add_assign(&dbl);
27 }
28}
29
30/// This struct represents a view of a sequence of bytes as a sequence of
31/// `u64` limbs in little-endian byte order. It maintains a current index, and
32/// allows access to the limb at that index and the one following it. Bytes
33/// beyond the end of the original buffer are treated as zero.
34struct LimbBuffer<'a> {
35 buf: &'a [u8],
36 cur_idx: usize,
37 cur_limb: u64,
38 next_limb: u64,
39}
40
41impl<'a> LimbBuffer<'a> {
42 fn new(buf: &'a [u8]) -> Self {
43 let mut ret = Self {
44 buf,
45 cur_idx: 0,
46 cur_limb: 0,
47 next_limb: 0,
48 };
49
50 // Initialise the limb buffers.
51 ret.increment_limb();
52 ret.increment_limb();
53 ret.cur_idx = 0usize;
54
55 ret
56 }
57
58 fn increment_limb(&mut self) {
59 self.cur_idx += 1;
60 self.cur_limb = self.next_limb;
61 match self.buf.len() {
62 // There are no more bytes in the buffer; zero-extend.
63 0 => self.next_limb = 0,
64
65 // There are fewer bytes in the buffer than a u64 limb; zero-extend.
66 x @ 1..=7 => {
67 let mut next_limb = [0; 8];
68 next_limb[..x].copy_from_slice(self.buf);
69 self.next_limb = u64::from_le_bytes(next_limb);
70 self.buf = &[];
71 }
72
73 // There are at least eight bytes in the buffer; read the next u64 limb.
74 _ => {
75 let (next_limb, rest) = self.buf.split_at(8);
76 self.next_limb = u64::from_le_bytes(next_limb.try_into().unwrap());
77 self.buf = rest;
78 }
79 }
80 }
81
82 fn get(&mut self, idx: usize) -> (u64, u64) {
83 assert!([self.cur_idx, self.cur_idx + 1].contains(&idx));
84 if idx > self.cur_idx {
85 self.increment_limb();
86 }
87 (self.cur_limb, self.next_limb)
88 }
89}
90
91/// Replaces the contents of `wnaf` with the w-NAF representation of a little-endian
92/// scalar.
93pub(crate) fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut Vec<i64>, c: S, window: usize) {
94 // Required by the NAF definition
95 debug_assert!(window >= 2);
96 // Required so that the NAF digits fit in i64
97 debug_assert!(window <= 64);
98
99 let bit_len = c.as_ref().len() * 8;
100
101 wnaf.truncate(0);
102 wnaf.reserve(bit_len);
103
104 // Initialise the current and next limb buffers.
105 let mut limbs = LimbBuffer::new(c.as_ref());
106
107 let width = 1u64 << window;
108 let window_mask = width - 1;
109
110 let mut pos = 0;
111 let mut carry = 0;
112 while pos < bit_len {
113 // Construct a buffer of bits of the scalar, starting at bit `pos`
114 let u64_idx = pos / 64;
115 let bit_idx = pos % 64;
116 let (cur_u64, next_u64) = limbs.get(u64_idx);
117 let bit_buf = if bit_idx + window < 64 {
118 // This window's bits are contained in a single u64
119 cur_u64 >> bit_idx
120 } else {
121 // Combine the current u64's bits with the bits from the next u64
122 (cur_u64 >> bit_idx) | (next_u64 << (64 - bit_idx))
123 };
124
125 // Add the carry into the current window
126 let window_val = carry + (bit_buf & window_mask);
127
128 if window_val & 1 == 0 {
129 // If the window value is even, preserve the carry and emit 0.
130 // Why is the carry preserved?
131 // If carry == 0 and window_val & 1 == 0, then the next carry should be 0
132 // If carry == 1 and window_val & 1 == 0, then bit_buf & 1 == 1 so the next carry should be 1
133 wnaf.push(0);
134 pos += 1;
135 } else {
136 wnaf.push(if window_val < width / 2 {
137 carry = 0;
138 window_val as i64
139 } else {
140 carry = 1;
141 (window_val as i64).wrapping_sub(width as i64)
142 });
143 wnaf.extend(iter::repeat(0).take(window - 1));
144 pos += window;
145 }
146 }
147}
148
149/// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar.
150///
151/// This function must be provided a `table` and `wnaf` that were constructed with
152/// the same window size; otherwise, it may panic or produce invalid results.
153pub(crate) fn wnaf_exp<G: Group>(table: &[G], wnaf: &[i64]) -> G {
154 let mut result = G::identity();
155
156 let mut found_one = false;
157
158 for n in wnaf.iter().rev() {
159 if found_one {
160 result = result.double();
161 }
162
163 if *n != 0 {
164 found_one = true;
165
166 if *n > 0 {
167 result += &table[(n / 2) as usize];
168 } else {
169 result -= &table[((-n) / 2) as usize];
170 }
171 }
172 }
173
174 result
175}
176
177/// A "w-ary non-adjacent form" scalar multiplication (also known as exponentiation)
178/// context.
179///
180/// # Examples
181///
182/// This struct can be used to implement several patterns:
183///
184/// ## One base, one scalar
185///
186/// For this pattern, you can use a transient `Wnaf` context:
187///
188/// ```ignore
189/// use group::Wnaf;
190///
191/// let result = Wnaf::new().scalar(&scalar).base(base);
192/// ```
193///
194/// ## Many bases, one scalar
195///
196/// For this pattern, you create a `Wnaf` context, load the scalar into it, and then
197/// process each base in turn:
198///
199/// ```ignore
200/// use group::Wnaf;
201///
202/// let mut wnaf = Wnaf::new();
203/// let mut wnaf_scalar = wnaf.scalar(&scalar);
204/// let results: Vec<_> = bases
205/// .into_iter()
206/// .map(|base| wnaf_scalar.base(base))
207/// .collect();
208/// ```
209///
210/// ## One base, many scalars
211///
212/// For this pattern, you create a `Wnaf` context, load the base into it, and then process
213/// each scalar in turn:
214///
215/// ```ignore
216/// use group::Wnaf;
217///
218/// let mut wnaf = Wnaf::new();
219/// let mut wnaf_base = wnaf.base(base, scalars.len());
220/// let results: Vec<_> = scalars
221/// .iter()
222/// .map(|scalar| wnaf_base.scalar(scalar))
223/// .collect();
224/// ```
225///
226/// ## Many bases, many scalars
227///
228/// Say you have `n` bases and `m` scalars, and want to produce `n * m` results. For this
229/// pattern, you need to cache the w-NAF tables for the bases and then compute the w-NAF
230/// form of the scalars on the fly for every base, or vice versa:
231///
232/// ```ignore
233/// use group::Wnaf;
234///
235/// let mut wnaf_contexts: Vec<_> = (0..bases.len()).map(|_| Wnaf::new()).collect();
236/// let mut wnaf_bases: Vec<_> = wnaf_contexts
237/// .iter_mut()
238/// .zip(bases)
239/// .map(|(wnaf, base)| wnaf.base(base, scalars.len()))
240/// .collect();
241/// let results: Vec<_> = wnaf_bases
242/// .iter()
243/// .flat_map(|wnaf_base| scalars.iter().map(|scalar| wnaf_base.scalar(scalar)))
244/// .collect();
245/// ```
246///
247/// Alternatively, use the [`WnafBase`] and [`WnafScalar`] types, which enable the various
248/// tables and w-NAF forms to be cached individually per base and scalar. These types can
249/// then be directly multiplied without any additional runtime work, at the cost of fixing
250/// a specific window size (rather than choosing the window size dynamically).
251#[derive(Debug)]
252pub struct Wnaf<W, B, S> {
253 base: B,
254 scalar: S,
255 window_size: W,
256}
257
258impl<G: Group> Default for Wnaf<(), Vec<G>, Vec<i64>> {
259 fn default() -> Self {
260 Self::new()
261 }
262}
263
264impl<G: Group> Wnaf<(), Vec<G>, Vec<i64>> {
265 /// Construct a new wNAF context without allocating.
266 pub fn new() -> Self {
267 Wnaf {
268 base: vec![],
269 scalar: vec![],
270 window_size: (),
271 }
272 }
273}
274
275#[cfg(feature = "wnaf-memuse")]
276impl<G: Group + memuse::DynamicUsage> memuse::DynamicUsage for Wnaf<(), Vec<G>, Vec<i64>> {
277 fn dynamic_usage(&self) -> usize {
278 self.base.dynamic_usage() + self.scalar.dynamic_usage()
279 }
280
281 fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
282 let (base_lower, base_upper) = self.base.dynamic_usage_bounds();
283 let (scalar_lower, scalar_upper) = self.scalar.dynamic_usage_bounds();
284
285 (
286 base_lower + scalar_lower,
287 base_upper.zip(scalar_upper).map(|(a, b)| a + b),
288 )
289 }
290}
291
292impl<G: WnafGroup> Wnaf<(), Vec<G>, Vec<i64>> {
293 /// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that
294 /// can perform exponentiations with `.scalar(..)`.
295 pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf<usize, &[G], &mut Vec<i64>> {
296 // Compute the appropriate window size based on the number of scalars.
297 let window_size = G::recommended_wnaf_for_num_scalars(num_scalars);
298
299 // Compute a wNAF table for the provided base and window size.
300 wnaf_table(&mut self.base, base, window_size);
301
302 // Return a Wnaf object that immutably borrows the computed base storage location,
303 // but mutably borrows the scalar storage location.
304 Wnaf {
305 base: &self.base[..],
306 scalar: &mut self.scalar,
307 window_size,
308 }
309 }
310
311 /// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform
312 /// exponentiations with `.base(..)`.
313 pub fn scalar(&mut self, scalar: &<G as Group>::Scalar) -> Wnaf<usize, &mut Vec<G>, &[i64]> {
314 // We hard-code a window size of 4.
315 let window_size = 4;
316
317 // Compute the wNAF form of the scalar.
318 wnaf_form(&mut self.scalar, scalar.to_repr(), window_size);
319
320 // Return a Wnaf object that mutably borrows the base storage location, but
321 // immutably borrows the computed wNAF form scalar location.
322 Wnaf {
323 base: &mut self.base,
324 scalar: &self.scalar[..],
325 window_size,
326 }
327 }
328}
329
330impl<'a, G: Group> Wnaf<usize, &'a [G], &'a mut Vec<i64>> {
331 /// Constructs new space for the scalar representation while borrowing
332 /// the computed window table, for sending the window table across threads.
333 pub fn shared(&self) -> Wnaf<usize, &'a [G], Vec<i64>> {
334 Wnaf {
335 base: self.base,
336 scalar: vec![],
337 window_size: self.window_size,
338 }
339 }
340}
341
342#[cfg(feature = "wnaf-memuse")]
343impl<'a, G: Group> memuse::DynamicUsage for Wnaf<usize, &'a [G], Vec<i64>> {
344 fn dynamic_usage(&self) -> usize {
345 // The heap memory for the window table is counted in the parent `Wnaf`.
346 self.scalar.dynamic_usage()
347 }
348
349 fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
350 self.scalar.dynamic_usage_bounds()
351 }
352}
353
354impl<'a, G: Group> Wnaf<usize, &'a mut Vec<G>, &'a [i64]> {
355 /// Constructs new space for the window table while borrowing
356 /// the computed scalar representation, for sending the scalar representation
357 /// across threads.
358 pub fn shared(&self) -> Wnaf<usize, Vec<G>, &'a [i64]> {
359 Wnaf {
360 base: vec![],
361 scalar: self.scalar,
362 window_size: self.window_size,
363 }
364 }
365}
366
367#[cfg(feature = "wnaf-memuse")]
368impl<'a, G: Group + memuse::DynamicUsage> memuse::DynamicUsage for Wnaf<usize, Vec<G>, &'a [i64]> {
369 fn dynamic_usage(&self) -> usize {
370 // The heap memory for the scalar representation is counted in the parent `Wnaf`.
371 self.base.dynamic_usage()
372 }
373
374 fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
375 self.base.dynamic_usage_bounds()
376 }
377}
378
379impl<B, S: AsRef<[i64]>> Wnaf<usize, B, S> {
380 /// Performs exponentiation given a base.
381 pub fn base<G: Group>(&mut self, base: G) -> G
382 where
383 B: AsMut<Vec<G>>,
384 {
385 wnaf_table(self.base.as_mut(), base, self.window_size);
386 wnaf_exp(self.base.as_mut(), self.scalar.as_ref())
387 }
388}
389
390impl<B, S: AsMut<Vec<i64>>> Wnaf<usize, B, S> {
391 /// Performs exponentiation given a scalar.
392 pub fn scalar<G: Group>(&mut self, scalar: &<G as Group>::Scalar) -> G
393 where
394 B: AsRef<[G]>,
395 {
396 wnaf_form(self.scalar.as_mut(), scalar.to_repr(), self.window_size);
397 wnaf_exp(self.base.as_ref(), self.scalar.as_mut())
398 }
399}
400
401/// A "w-ary non-adjacent form" scalar, that uses precomputation to improve the speed of
402/// scalar multiplication.
403///
404/// # Examples
405///
406/// See [`WnafBase`] for usage examples.
407#[derive(Clone, Debug)]
408pub struct WnafScalar<F: PrimeField, const WINDOW_SIZE: usize> {
409 wnaf: Vec<i64>,
410 field: PhantomData<F>,
411}
412
413#[cfg(feature = "wnaf-memuse")]
414impl<F: PrimeField, const WINDOW_SIZE: usize> memuse::DynamicUsage for WnafScalar<F, WINDOW_SIZE> {
415 fn dynamic_usage(&self) -> usize {
416 self.wnaf.dynamic_usage()
417 }
418
419 fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
420 self.wnaf.dynamic_usage_bounds()
421 }
422}
423
424impl<F: PrimeField, const WINDOW_SIZE: usize> WnafScalar<F, WINDOW_SIZE> {
425 /// Computes the w-NAF representation of the given scalar with the specified
426 /// `WINDOW_SIZE`.
427 pub fn new(scalar: &F) -> Self {
428 let mut wnaf = vec![];
429
430 // Compute the w-NAF form of the scalar.
431 wnaf_form(&mut wnaf, scalar.to_repr(), WINDOW_SIZE);
432
433 WnafScalar {
434 wnaf,
435 field: PhantomData::default(),
436 }
437 }
438}
439
440/// A fixed window table for a group element, precomputed to improve the speed of scalar
441/// multiplication.
442///
443/// This struct is designed for usage patterns that have long-term cached bases and/or
444/// scalars, or [Cartesian products] of bases and scalars. The [`Wnaf`] API enables one or
445/// the other to be cached, but requires either the base window tables or the scalar w-NAF
446/// forms to be computed repeatedly on the fly, which can become a significant performance
447/// issue for some use cases.
448///
449/// `WnafBase` and [`WnafScalar`] enable an alternative trade-off: by fixing the window
450/// size at compile time, the precomputations are guaranteed to only occur once per base
451/// and once per scalar. Users should select their window size based on how long the bases
452/// are expected to live; a larger window size will consume more memory and take longer to
453/// precompute, but result in faster scalar multiplications.
454///
455/// [Cartesian products]: https://en.wikipedia.org/wiki/Cartesian_product
456///
457/// # Examples
458///
459/// ```ignore
460/// use group::{WnafBase, WnafScalar};
461///
462/// let wnaf_bases: Vec<_> = bases.into_iter().map(WnafBase::<_, 4>::new).collect();
463/// let wnaf_scalars: Vec<_> = scalars.iter().map(WnafScalar::new).collect();
464/// let results: Vec<_> = wnaf_bases
465/// .iter()
466/// .flat_map(|base| wnaf_scalars.iter().map(|scalar| base * scalar))
467/// .collect();
468/// ```
469///
470/// Note that this pattern requires specifying a fixed window size (unlike previous
471/// patterns that picked a suitable window size internally). This is necessary to ensure
472/// in the type system that the base and scalar `Wnaf`s were computed with the same window
473/// size, allowing the result to be computed infallibly.
474#[derive(Clone, Debug)]
475pub struct WnafBase<G: Group, const WINDOW_SIZE: usize> {
476 table: Vec<G>,
477}
478
479#[cfg(feature = "wnaf-memuse")]
480impl<G: Group + memuse::DynamicUsage, const WINDOW_SIZE: usize> memuse::DynamicUsage
481 for WnafBase<G, WINDOW_SIZE>
482{
483 fn dynamic_usage(&self) -> usize {
484 self.table.dynamic_usage()
485 }
486
487 fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
488 self.table.dynamic_usage_bounds()
489 }
490}
491
492impl<G: Group, const WINDOW_SIZE: usize> WnafBase<G, WINDOW_SIZE> {
493 /// Computes a window table for the given base with the specified `WINDOW_SIZE`.
494 pub fn new(base: G) -> Self {
495 let mut table = vec![];
496
497 // Compute a window table for the provided base and window size.
498 wnaf_table(&mut table, base, WINDOW_SIZE);
499
500 WnafBase { table }
501 }
502}
503
504impl<G: Group, const WINDOW_SIZE: usize> Mul<&WnafScalar<G::Scalar, WINDOW_SIZE>>
505 for &WnafBase<G, WINDOW_SIZE>
506{
507 type Output = G;
508
509 fn mul(self, rhs: &WnafScalar<G::Scalar, WINDOW_SIZE>) -> Self::Output {
510 wnaf_exp(&self.table, &rhs.wnaf)
511 }
512}