Coverage Report

Created: 2026-02-14 06:35

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