Skip to main content

class_groups/crypto_bigint/
element.rs

1use core::{ops::Neg, fmt::Debug};
2
3use zeroize::Zeroize;
4
5use crypto_bigint::{
6  Choice, CtOption, CtEq, CtGt as _, CtSelect, CtAssign as _, BitOps, NonZero, One as _, Limb,
7  UintRef,
8};
9
10use super::I;
11
12use crate::Element;
13#[cfg(feature = "alloc")]
14use crate::Table;
15
16pub(super) trait Limbs:
17  Send
18  + Sync
19  + Debug
20  + Zeroize
21  + CtEq
22  + CtSelect
23  + BitOps
24  + super::composition::Limbs<
25    Wide: Send
26            + Sync
27            + Debug
28            + Zeroize
29            + CtSelect
30            + BitOps
31            + super::c::Limbs
32            + super::reduction::Limbs,
33  > + super::encoding::Limbs
34{
35  type Bytes: AsRef<[u8]> + AsMut<[u8]>;
36
37  /// The maximum amount of bits this value can support.
38  ///
39  /// `None` signifies this value is unbounded.
40  fn max_bits() -> Option<u32>;
41
42  /// Truncate `wide` to a member of `Self`.
43  ///
44  /// `bits`, a bound on the size of the result, is provided. The value in `wide` MUST be less than
45  /// $2^{bits}$. `bits` MUST be less than or equal to `Self::max_bits()` when
46  /// `Self::max_bits().is_some()`.
47  fn truncate(wide: Self::Wide, bits: u32) -> Self;
48
49  /// Widen `thin` to a member of `Self::Wide`.
50  ///
51  /// `wide_bits` is a bound on how big the widened type should be able to support. The value in
52  /// `thin` MUST be less than $2^{wide_bits}$. `wide_bits` MUST be less than or equal to
53  /// `2 * Self::max_bits()` when `Self::max_bits().is_some()`.
54  fn widen(thin: Self, wide_bits: u32) -> Self::Wide;
55
56  /// Convert this number to a sequence of little-endian bytes.
57  ///
58  /// This function MUST run in constant time and yield a result of length constant to the relevant
59  /// bound.
60  fn to_le_bytes(self) -> Self::Bytes;
61
62  /// Convert this wide number to a sequence of little-endian bytes.
63  ///
64  /// This function MUST run in constant time and yield a result of length constant to the relevant
65  /// bound.
66  fn wide_to_le_bytes(wide: Self::Wide) -> impl AsRef<[u8]>;
67
68  /// Load this number from a sequence of little-endian bytes.
69  ///
70  /// The slice MAY be of arbitrary length so long as the encoded value has bits less than or
71  /// equal to `max_bits`. `max_bits` MUST be less than or equal to `Self::max_bits()` when
72  /// `Self::max_bits().is_some()`.
73  fn from_le_slice(bytes: &[u8], max_bits: u32) -> Self;
74
75  /// Load a wide number from a sequence of little-endian bytes.
76  ///
77  /// The slice MAY be of arbitrary length so long as the encoded value has bits less than or
78  /// equal to `max_bits`.`max_bits` MUST be less than or equal to `2 * Self::max_bits()` when
79  /// `Self::max_bits().is_some()`.
80  fn wide_from_le_slice(bytes: &[u8], max_bits: u32) -> Self::Wide;
81
82  /// Stich together two byte sequences into a single container of length `2 * bytes_per_element`.
83  fn stitch(first: Self::Bytes, second: Self::Bytes, bytes_per_element: usize) -> impl AsRef<[u8]>;
84}
85
86/// A constant-time primitive element of a class group, implemented via `crypto-bigint`.
87///
88/// This only supports discriminants `delta` such that $delta < 0, |delta| \cong 1 \mod 2$.
89///
90/// This is implemented in time variable to the discriminant yet constant to the `a, b, c`
91/// coefficients. This prevents timing analysis from leaking the elements being composed. It is
92/// only recommended for provers as it has a significant performance overhead compared to other
93/// backends, which verifiers should take advantage of.
94///
95/// This implementation does not itself allocate and can be used with [`crypto_bigint::Uint`] to
96/// achieve elements which are fixed size and live on the stack, albeit only with support for a
97/// bounded subset of discriminants. Alternatively, [`crypto_bigint::BoxedUint`] may be used to
98/// support all discriminants, at the cost of using the heap.
99#[expect(private_bounds)]
100#[derive(Clone, Debug)]
101pub struct CryptoBigintElement<U: Limbs> {
102  /// The `a` coefficient.
103  ///
104  /// This may not be reduced but is bounded to less than the square root of the discriminant.
105  a: U,
106
107  /// The `b` coefficient.
108  ///
109  /// This may not be reduced but is bounded to have an absolute value less than the square root of
110  /// the discriminant.
111  b: I<U>,
112
113  /// The `c` coefficient.
114  ///
115  /// This may not be reduced but is bounded to be less than the discriminant.
116  /*
117    This `c` coefficient satisfies `b^2 - 4ac = delta`. `b^2` is at most the discriminant itself.
118    This means `4ac` can be at most `2 |delta|` (for a negative discriminant, as we bound). This
119    means `ac` can be at most `2 |delta| / 4`, and therefore `c` itself is within that bound.
120  */
121  c: U::Wide,
122
123  /// The absolute value of the negative discriminant for this form.
124  ///
125  /// This is used to recalculate the `c` coefficient after composition.
126  discriminant_abs: U::Wide,
127}
128
129impl<U: Limbs> CtEq for CryptoBigintElement<U> {
130  /// This MAY return an incorrect result for forms of different discriminants.
131  fn ct_eq(&self, other: &Self) -> Choice {
132    let a = self.clone().reduce();
133    let other = other.clone().reduce();
134
135    /*
136      We do not check the `c` coefficient is equal, as if the `a, b` coefficients are equal, the
137      `c` coefficient will be so long as these forms are of the same discriminant (and if not,
138      we're allowed to return an incorrect result, which is why we don't check the discriminant).
139    */
140    a.a.ct_eq(&other.a) & a.b.0.ct_eq(&other.b.0) & a.b.1.ct_eq(&other.b.1)
141  }
142}
143
144impl<U: Limbs> PartialEq for CryptoBigintElement<U> {
145  fn eq(&self, other: &Self) -> bool {
146    bool::from(self.ct_eq(other))
147  }
148}
149impl<U: Limbs> Eq for CryptoBigintElement<U> {}
150
151#[expect(private_bounds)]
152impl<U: Limbs> CryptoBigintElement<U> {
153  /// This is only valid for forms of negative odd discriminant.
154  fn identity_from_discriminant(discriminant_abs: U::Wide) -> Self {
155    /*
156      The identity element has `a = 1`. As composition sets $a_3 = (a_1 * a_2) / gcd(a_1, a_2)^2$,
157      it's clear how having one $a_1 = 1$ causes $a_3 = a_2$ (or $a_2 = 1$ causes $a_3 = a_1$). As
158      for the `b` coefficient, the only `b` coefficient less than or equal to an `a` coefficient of
159      `1` (as required for a reduced form) is itself `1`. As `|b| == a`, then `b` must be positive.
160      Finally, for the `c` coefficient, we are able to calculate it from the `a, b` coefficients
161      and the discriminant.
162
163      The identity element is primitive as `gcd(a, b, c) = gcd(1, 1, c) = 1`.
164    */
165    let a = &[1];
166    let b = (Choice::TRUE, &[1]);
167
168    /*
169      `b^2 - 4ac = delta`
170      `b^2 - delta = 4ac`
171      `(b^2 - delta) / 4a = c`
172
173      where `b^2 = 1, delta < 0, a = 1` so
174
175      $(1 + |delta|) / 4 = c$
176    */
177    let mut c = discriminant_abs.clone();
178    {
179      let c = AsMut::<[Limb]>::as_mut(&mut c);
180      let mut i = 0;
181      let mut carry = Limb::ONE;
182      while i < c.len() {
183        // `|delta| >> 2`
184        if let Some(j) = i.checked_sub(1) {
185          c[j] |= c[i] << (Limb::BITS - 2);
186        }
187        c[i] >>= 2;
188        /*
189          `+ 1`, as `carry` was initialized to one.
190
191          This technically doesn't calculate $(1 + |delta|) / 4$ but $floor(|delta| / 4) + 1$.
192          These two values are equivalent when $|delta| \cong 3 \mod 4$.
193
194          We only explicitly bound $delta < 0, delta \cong 1 \mod 2$. By $b^2 - 4 a c = delta$, we
195          have $b^2 \cong delta \mod 4$. $3$ is not a square modulo $4$, so if
196          $delta \cong 1 \mod 2$ (as we bound), we MUST have $delta \cong 1 \mod 4$ (and therefore
197          $|delta| \cong 3 \mod 4$).
198        */
199        (c[i], carry) = c[i].carrying_add(Limb::ZERO, carry);
200        i += 1;
201      }
202    }
203
204    // SAFETY: This is well-defined, reduced, and primitive
205    unsafe {
206      <Self as Element>::from_coefficients(
207        a,
208        b,
209        U::wide_to_le_bytes(c),
210        U::wide_to_le_bytes(discriminant_abs),
211      )
212    }
213  }
214}
215
216impl<U: Limbs> Zeroize for CryptoBigintElement<U> {
217  /// This is only valid for forms of negative odd discriminant.
218  ///
219  /// This does not zeroize the discriminant, solely the `a, b, c` coefficients, and will set the
220  /// result to the identity element of the same discriminant.
221  fn zeroize(&mut self) {
222    // Zeroize
223    self.a.zeroize();
224    // Best effort, as `Choice: !Zeroize`
225    self.b.0.ct_assign(&Choice::TRUE, Choice::TRUE);
226    self.b.1.zeroize();
227    self.c.zeroize();
228
229    *self = Self::identity_from_discriminant(self.discriminant_abs.clone());
230  }
231}
232
233impl<U: Limbs> CtSelect for CryptoBigintElement<U> {
234  /// This MAY return an incorrect result for forms of different discriminants.
235  fn ct_select(&self, b: &Self, choice: Choice) -> Self {
236    Self {
237      a: U::ct_select(&self.a, &b.a, choice),
238      b: (<_>::ct_select(&self.b.0, &b.b.0, choice), <_>::ct_select(&self.b.1, &b.b.1, choice)),
239      c: U::Wide::ct_select(&self.c, &b.c, choice),
240      // This is the same when the forms are of the same discriminant, allowing us to simply use
241      // `self`'s without invoking `ct_select`
242      discriminant_abs: self.discriminant_abs.clone(),
243    }
244  }
245}
246
247impl<U: Limbs> Neg for CryptoBigintElement<U> {
248  type Output = Self;
249  /// This is only correct for primitives forms where $delta \cong 1 \mod 2$.
250  fn neg(mut self) -> Self {
251    /*
252      Per Proposition 5.2.5 of A Course in Computational Algebraic Number Theory, a binary
253      quadratic form is only invertible if `(a, b, c)` is primitive, hence why we bound the input
254      to be primitive.
255
256      Proposition 5.2.5 also includes a very academic description of the inverse, which here is
257      implemented as negating the `b` coefficient.
258
259      While `-0` is of unclear validity in this context, we know that this result will _NOT_ be
260      `-0` as $b \cong 1 \mod 2$ for odd discriminants (as we bound).
261    */
262    self.b.0 = !self.b.0;
263    self
264  }
265}
266
267#[expect(private_bounds)]
268impl<U: Limbs> CryptoBigintElement<U> {
269  /// Partially reduce an element.
270  ///
271  /// This assumes the coefficients are the result from composition (either addition or doubling)
272  /// of two well-defined instances of this type.
273  fn partial_reduce(a: U::Wide, b: I<U::Wide>, discriminant_abs: U::Wide) -> Self {
274    /*
275      `partial_reduce` yields `a, b` coefficients which are bounded by the square root of delta, so
276      the `a` from composition will be bounded by delta. The `b` coefficient is bound to be
277      within one bit of twice the resulting `a` coefficient or within one bit of the `b2`
278      coefficient used for composition, whichever is greater. As `b2` was also bounded by the
279      square root of delta, we know the bound from `a` to be the greater (and therefore the
280      relevant) bound.
281    */
282    let (a, b, c) =
283      super::partial_reduce(2 + discriminant_abs.bits_vartime(), a, b, &discriminant_abs);
284
285    /*
286      `partial_reduce` is guaranteed to return `a, b` which are each less then the square root of
287      the discriminant. We need one additional bit for these to be usable with our methods for
288      composition however.
289    */
290    let sqrt_discriminant_bits = discriminant_abs.bits_vartime().div_ceil(2);
291    let a = U::truncate(a, 1 + sqrt_discriminant_bits);
292    let b = (b.0, U::truncate(b.1, 1 + sqrt_discriminant_bits));
293    // These `a, b` coefficients are bounded by the square root of delta, as this type requires
294    // `c`'s bound, that it's bounded by the discriminant, is inherently satisfied from there
295    Self { a, b, c, discriminant_abs }
296  }
297
298  /// Reduce an element.
299  fn reduce(self) -> Self {
300    let discriminant_bits = self.discriminant_abs.bits_vartime();
301    let sqrt_discriminant_bits = discriminant_bits.div_ceil(2);
302    let (a, b, c) = super::reduce(
303      // The documentation on our `struct` require `a, b` satisfy this bound
304      sqrt_discriminant_bits,
305      // Widen these, as `reduce` requires all its arguments have the same capacity
306      U::widen(self.a, self.c.bits_precision()),
307      (self.b.0, U::widen(self.b.1, self.c.bits_precision())),
308      self.c,
309    );
310
311    /*
312      A reduced element has `|b| <= a < sqrt(|delta|)` per
313      Definition 5.3.2 and Lemma 5.3.4 of A Course in Computational Algebraic Number Theory.
314
315      We again keep one extra bit for these to be usable with our methods for composition.
316    */
317    let a = U::truncate(a, 1 + sqrt_discriminant_bits);
318    let b = (b.0, U::truncate(b.1, 1 + sqrt_discriminant_bits));
319    Self { a, b, c, discriminant_abs: self.discriminant_abs.clone() }
320  }
321}
322
323// SAFETY: This reduces the form before yielding it and does return a well-defined form as
324// required.
325unsafe impl<U: Limbs> crate::Coefficients for CryptoBigintElement<U> {
326  /// This function runs in constant time.
327  fn a_b_c_discriminant(
328    self,
329  ) -> (impl AsRef<[u8]>, (Choice, impl AsRef<[u8]>), impl AsRef<[u8]>, impl AsRef<[u8]>) {
330    let reduced = self.reduce();
331    (
332      reduced.a.to_le_bytes(),
333      (reduced.b.0, reduced.b.1.to_le_bytes()),
334      U::wide_to_le_bytes(reduced.c),
335      U::wide_to_le_bytes(reduced.discriminant_abs),
336    )
337  }
338}
339
340impl<U: Limbs> Element for CryptoBigintElement<U> {
341  /// This is only valid for forms of negative odd discriminant.
342  fn identity(discriminant_abs: impl AsRef<[u8]>) -> Self {
343    let discriminant_abs = discriminant_abs.as_ref();
344    assert_eq!(discriminant_abs[0] & 0b11, 0b11, "discriminant was not a valid odd discriminant");
345    Self::identity_from_discriminant(U::wide_from_le_slice(
346      discriminant_abs,
347      8 * u32::try_from(discriminant_abs.len()).expect("4 GB discriminant?"),
348    ))
349  }
350
351  /// This MAY return an incorrect result when the form doesn't have an odd, negative discriminant.
352  fn is_identity(&self) -> Choice {
353    let reduced = self.clone().reduce();
354
355    let a = AsRef::<[Limb]>::as_ref(&reduced.a);
356    let b = AsRef::<[Limb]>::as_ref(&reduced.b.1);
357
358    /*
359      We know the `a, b` coefficients are non-zero due to having a negative odd discriminant. We
360      check both have only the least-significant bit set.
361    */
362    let is_one = a[0] | b[0];
363    let mut is_zero = Limb::ZERO;
364    for limb in &a[1 ..] {
365      is_zero |= *limb;
366    }
367    for limb in &b[1 ..] {
368      is_zero |= *limb;
369    }
370    is_one.is_one() & is_zero.is_zero()
371  }
372
373  /// This is only correct for forms of the same discriminant where at least one form is primitive.
374  fn add(&self, other: &Self) -> Self {
375    let (a3, b3) =
376      super::add(self.a.clone(), self.b.clone(), other.a.clone(), other.b.clone(), other.c.clone());
377    Self::partial_reduce(a3, b3, self.discriminant_abs.clone())
378  }
379
380  /// This is only correct when the form is primitive.
381  fn double(&self) -> Self {
382    let (a3, b3) = super::double(self.a.clone(), self.b.clone(), self.c.clone());
383    Self::partial_reduce(a3, b3, self.discriminant_abs.clone())
384  }
385
386  /// This is only correct for forms of the same discriminant where at least one form is primitive.
387  fn sub(&self, other: Self) -> Self {
388    let (a3, b3) =
389      super::add(self.a.clone(), self.b.clone(), other.a, (!other.b.0, other.b.1), other.c);
390    Self::partial_reduce(a3, b3, self.discriminant_abs.clone())
391  }
392
393  /// This function is only valid for primitive reduced positive definite binary quadratic forms of
394  /// negative odd discriminant where the discriminant fits within `(2 * max_bits) - 2` bits.
395  ///
396  /// This function MAY run in time variable to:
397  /// - the byte-length of the inputs
398  /// - the validity of the inputs
399  /// - the discriminant
400  ///
401  /// This function MAY panic if asked to handle coefficients which exceed the capacity of the
402  /// underlying container(s).
403  unsafe fn from_coefficients(
404    a: impl AsRef<[u8]>,
405    (b_positive, b_abs): (Choice, impl AsRef<[u8]>),
406    c: impl AsRef<[u8]>,
407    discriminant_abs: impl AsRef<[u8]>,
408  ) -> Self {
409    let a = a.as_ref();
410    let b_abs = b_abs.as_ref();
411    let c = c.as_ref();
412    let discriminant_abs = discriminant_abs.as_ref();
413
414    let bit_len = |slice: &[u8]| {
415      if let Some(last) = slice.last() {
416        u32::try_from(8 * (slice.len() - 1))
417          .ok()
418          .and_then(|value| value.checked_add(Limb::from(*last).bits()))
419          .expect("slice exceeded 4 GB")
420      } else {
421        0
422      }
423    };
424
425    // Determine how many bits are actually in the absolute value of the discriminant
426    let discriminant_bits =
427      U::wide_from_le_slice(discriminant_abs, bit_len(discriminant_abs)).bits_vartime();
428    // Ensure our bound on the discriminant is respected
429    if let Some(max_bits) = U::max_bits() {
430      assert!(discriminant_bits <= ((2 * max_bits) - 2));
431    }
432
433    // Load the absolute value of the discriminant with the exact precision required
434    let discriminant_abs = U::wide_from_le_slice(discriminant_abs, discriminant_bits);
435
436    let sqrt_discriminant_bits = discriminant_bits.div_ceil(2);
437    let a = U::from_le_slice(a, 1 + sqrt_discriminant_bits);
438    assert!(bool::from(!a.bits().ct_gt(&sqrt_discriminant_bits)));
439    let b = (b_positive, U::from_le_slice(b_abs, 1 + sqrt_discriminant_bits));
440    assert!(bool::from(!b.1.bits().ct_gt(&sqrt_discriminant_bits)));
441    let c = U::wide_from_le_slice(c, discriminant_bits);
442
443    Self { a, b, c, discriminant_abs }
444  }
445
446  /// This runs in time variable to the size of the discriminant and the size of the underlying
447  /// container.
448  fn uncompressed_encode(self) -> impl AsRef<[u8]> {
449    let bits_per_element = ((self.discriminant_abs.bits() - 1) / 2) + 1;
450    let bytes_per_element = usize::try_from(bits_per_element.div_ceil(8)).unwrap();
451
452    let reduced = self.reduce();
453    let a = reduced.a.to_le_bytes();
454    let mut b = reduced.b.1.to_le_bytes();
455    b.as_mut()[0] ^= u8::from(!reduced.b.0);
456
457    U::stitch(a, b, bytes_per_element)
458  }
459
460  /// This runs in time variable to the size of the discriminant and the size of the underlying
461  /// container. This MAY return `None` for discriminants which fit within the bounds but have
462  /// trailing zero bytes which cause the amount of encoded bits to exceed the bounds.
463  fn uncompressed_decode(
464    buf: impl AsRef<[u8]>,
465    discriminant_abs: impl AsRef<[u8]>,
466  ) -> CtOption<Self> {
467    let discriminant_abs = discriminant_abs.as_ref();
468
469    let invalid_size = CtOption::new(
470      Self {
471        a: U::from_le_slice(&[], 1),
472        b: (Choice::TRUE, U::from_le_slice(&[], 1)),
473        c: U::wide_from_le_slice(&[], 1),
474        discriminant_abs: U::wide_from_le_slice(&[], 1),
475      },
476      Choice::FALSE,
477    );
478    let discriminant_bits = {
479      let Ok(discriminant_bytes) = u32::try_from(discriminant_abs.len()) else {
480        return invalid_size;
481      };
482      let Some(msb) = discriminant_abs.last().copied() else { return invalid_size };
483      /*
484        This is lossy in that it _may_ believe a discriminant is larger than it actually is, but
485        only if the discriminant has trailing zero bytes, which we're allowed to not support.
486      */
487      let Some(discriminant_bits) = 8u32.checked_mul(discriminant_bytes) else {
488        return invalid_size;
489      };
490      let discriminant_bits = discriminant_bits - (8 - Limb::from(msb).bits());
491
492      if let Some(max_bits) = U::max_bits() {
493        if discriminant_bits > (2u32.checked_mul(max_bits).unwrap_or(0).saturating_sub(2)) {
494          return invalid_size;
495        }
496      }
497
498      discriminant_bits
499    };
500
501    let discriminant_bits =
502      UintRef::new(U::wide_from_le_slice(discriminant_abs, discriminant_bits).as_ref()).bits();
503    let discriminant_abs = U::wide_from_le_slice(discriminant_abs, discriminant_bits);
504
505    let bits_per_element = (discriminant_abs.bits() / 2) + 1;
506    let bytes_per_element = usize::try_from(bits_per_element.div_ceil(8)).unwrap();
507
508    let buf = buf.as_ref();
509    if buf.len() != (2 * bytes_per_element) {
510      return invalid_size;
511    }
512
513    let sqrt_discriminant_bits = discriminant_bits.div_ceil(2);
514    let a = U::from_le_slice(&buf[.. bytes_per_element], 1 + sqrt_discriminant_bits);
515    let mut b_abs = U::from_le_slice(&buf[bytes_per_element ..], 1 + sqrt_discriminant_bits);
516
517    let b_positive =
518      (b_abs.as_ref()[0] & Limb::ONE).ct_eq(&(discriminant_abs.as_ref()[0] & Limb::ONE));
519    b_abs.as_mut()[0] ^= Limb::from(u8::from(!b_positive));
520
521    NonZero::new(a).and_then(|a| {
522      super::encoding::validate_binary_quadratic_form(a, (b_positive, b_abs), &discriminant_abs)
523        .map(|(a, b, c)| Self { a: a.get(), b, c, discriminant_abs })
524    })
525  }
526}
527
528// TODO
529#[cfg(feature = "alloc")]
530impl<U: Limbs> crate::ElementExt for CryptoBigintElement<U> {
531  const MAX_TABLE_BITS: u32 = 12;
532
533  /// This is only correct when `identity` is in fact the identity element for the class group the
534  /// elements in the table belong to.
535  fn multiexp(identity: &Self, pairs: &[(&Table<Self>, &[u8])]) -> Self {
536    let mut longest_scalar_bits = 0;
537    for (_table, scalar) in pairs {
538      longest_scalar_bits = longest_scalar_bits.max(scalar.len() * 8);
539    }
540
541    let mut res: Option<Self> = None;
542    for i in 0 .. longest_scalar_bits {
543      // Shift over the existing result by a bit
544      if let Some(res) = res.as_mut() {
545        *res = res.double();
546      }
547
548      for (table, scalar) in pairs {
549        let scalar_bits = scalar.len() * 8;
550        // Transform the index of the bit in our longest scalar to the index of the bit in this one
551        let Some(i) = i.checked_sub(longest_scalar_bits - scalar_bits) else {
552          // If we're indexing a bit which doesn't exist in this scalar, continue
553          continue;
554        };
555
556        // If it's time to add this entry, do so
557        let table_bits = table.bits();
558        if ((i + 1) % table_bits) == 0 {
559          let mut accum = 0usize;
560          debug_assert_eq!(i - (i + 1 - table_bits) + 1, table_bits);
561          for i in (i + 1 - table_bits) ..= i {
562            accum <<= 1;
563            accum |= (usize::from(scalar[i / 8] >> (7 - (i % 8)))) & 1;
564          }
565
566          let mut to_add = Self::ct_select(&table[0], &table[1], 1.ct_eq(&accum));
567          for i in 2 .. table.as_ref().len() {
568            to_add = Self::ct_select(&to_add, &table[i], i.ct_eq(&accum));
569          }
570          res = Some(res.as_ref().map(|res| res.add(&to_add)).unwrap_or_else(|| to_add.clone()));
571        }
572      }
573    }
574
575    // Perform the final step of the accumulator
576    for (table, scalar) in pairs {
577      let scalar_bits = scalar.len() * 8;
578
579      let table_bits = table.bits();
580      let mut accum = 0usize;
581      for i in ((scalar_bits / table_bits) * table_bits) .. scalar_bits {
582        accum <<= 1;
583        accum |= (usize::from(scalar[i / 8] >> (7 - (i % 8)))) & 1;
584      }
585
586      let mut to_add = Self::ct_select(&table[0], &table[1], 1.ct_eq(&accum));
587      for i in 2 .. table.as_ref().len() {
588        to_add = Self::ct_select(&to_add, &table[i], i.ct_eq(&accum));
589      }
590      res = Some(res.as_ref().map(|res| res.add(&to_add)).unwrap_or_else(|| to_add.clone()));
591    }
592
593    res.unwrap_or_else(|| identity.clone())
594  }
595}