| use alloc::vec::Vec; |
| use core::iter; |
| use core::marker::PhantomData; |
| use core::ops::Mul; |
| |
| use ff::PrimeField; |
| |
| use super::Group; |
| |
| /// Extension trait on a [`Group`] that provides helpers used by [`Wnaf`]. |
| pub trait WnafGroup: Group { |
| /// Recommends a wNAF window size given the number of scalars you intend to multiply |
| /// a base by. Always returns a number between 2 and 22, inclusive. |
| fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; |
| } |
| |
| /// Replaces the contents of `table` with a w-NAF window table for the given window size. |
| pub(crate) fn wnaf_table<G: Group>(table: &mut Vec<G>, mut base: G, window: usize) { |
| table.truncate(0); |
| table.reserve(1 << (window - 1)); |
| |
| let dbl = base.double(); |
| |
| for _ in 0..(1 << (window - 1)) { |
| table.push(base); |
| base.add_assign(&dbl); |
| } |
| } |
| |
| /// This struct represents a view of a sequence of bytes as a sequence of |
| /// `u64` limbs in little-endian byte order. It maintains a current index, and |
| /// allows access to the limb at that index and the one following it. Bytes |
| /// beyond the end of the original buffer are treated as zero. |
| struct LimbBuffer<'a> { |
| buf: &'a [u8], |
| cur_idx: usize, |
| cur_limb: u64, |
| next_limb: u64, |
| } |
| |
| impl<'a> LimbBuffer<'a> { |
| fn new(buf: &'a [u8]) -> Self { |
| let mut ret = Self { |
| buf, |
| cur_idx: 0, |
| cur_limb: 0, |
| next_limb: 0, |
| }; |
| |
| // Initialise the limb buffers. |
| ret.increment_limb(); |
| ret.increment_limb(); |
| ret.cur_idx = 0usize; |
| |
| ret |
| } |
| |
| fn increment_limb(&mut self) { |
| self.cur_idx += 1; |
| self.cur_limb = self.next_limb; |
| match self.buf.len() { |
| // There are no more bytes in the buffer; zero-extend. |
| 0 => self.next_limb = 0, |
| |
| // There are fewer bytes in the buffer than a u64 limb; zero-extend. |
| x @ 1..=7 => { |
| let mut next_limb = [0; 8]; |
| next_limb[..x].copy_from_slice(self.buf); |
| self.next_limb = u64::from_le_bytes(next_limb); |
| self.buf = &[]; |
| } |
| |
| // There are at least eight bytes in the buffer; read the next u64 limb. |
| _ => { |
| let (next_limb, rest) = self.buf.split_at(8); |
| self.next_limb = u64::from_le_bytes(next_limb.try_into().unwrap()); |
| self.buf = rest; |
| } |
| } |
| } |
| |
| fn get(&mut self, idx: usize) -> (u64, u64) { |
| assert!([self.cur_idx, self.cur_idx + 1].contains(&idx)); |
| if idx > self.cur_idx { |
| self.increment_limb(); |
| } |
| (self.cur_limb, self.next_limb) |
| } |
| } |
| |
| /// Replaces the contents of `wnaf` with the w-NAF representation of a little-endian |
| /// scalar. |
| pub(crate) fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut Vec<i64>, c: S, window: usize) { |
| // Required by the NAF definition |
| debug_assert!(window >= 2); |
| // Required so that the NAF digits fit in i64 |
| debug_assert!(window <= 64); |
| |
| let bit_len = c.as_ref().len() * 8; |
| |
| wnaf.truncate(0); |
| wnaf.reserve(bit_len); |
| |
| // Initialise the current and next limb buffers. |
| let mut limbs = LimbBuffer::new(c.as_ref()); |
| |
| let width = 1u64 << window; |
| let window_mask = width - 1; |
| |
| let mut pos = 0; |
| let mut carry = 0; |
| while pos < bit_len { |
| // Construct a buffer of bits of the scalar, starting at bit `pos` |
| let u64_idx = pos / 64; |
| let bit_idx = pos % 64; |
| let (cur_u64, next_u64) = limbs.get(u64_idx); |
| let bit_buf = if bit_idx + window < 64 { |
| // This window's bits are contained in a single u64 |
| cur_u64 >> bit_idx |
| } else { |
| // Combine the current u64's bits with the bits from the next u64 |
| (cur_u64 >> bit_idx) | (next_u64 << (64 - bit_idx)) |
| }; |
| |
| // Add the carry into the current window |
| let window_val = carry + (bit_buf & window_mask); |
| |
| if window_val & 1 == 0 { |
| // If the window value is even, preserve the carry and emit 0. |
| // Why is the carry preserved? |
| // If carry == 0 and window_val & 1 == 0, then the next carry should be 0 |
| // If carry == 1 and window_val & 1 == 0, then bit_buf & 1 == 1 so the next carry should be 1 |
| wnaf.push(0); |
| pos += 1; |
| } else { |
| wnaf.push(if window_val < width / 2 { |
| carry = 0; |
| window_val as i64 |
| } else { |
| carry = 1; |
| (window_val as i64).wrapping_sub(width as i64) |
| }); |
| wnaf.extend(iter::repeat(0).take(window - 1)); |
| pos += window; |
| } |
| } |
| } |
| |
| /// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar. |
| /// |
| /// 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. |
| pub(crate) fn wnaf_exp<G: Group>(table: &[G], wnaf: &[i64]) -> G { |
| let mut result = G::identity(); |
| |
| let mut found_one = false; |
| |
| for n in wnaf.iter().rev() { |
| if found_one { |
| result = result.double(); |
| } |
| |
| if *n != 0 { |
| found_one = true; |
| |
| if *n > 0 { |
| result += &table[(n / 2) as usize]; |
| } else { |
| result -= &table[((-n) / 2) as usize]; |
| } |
| } |
| } |
| |
| result |
| } |
| |
| /// A "w-ary non-adjacent form" scalar multiplication (also known as exponentiation) |
| /// context. |
| /// |
| /// # Examples |
| /// |
| /// This struct can be used to implement several patterns: |
| /// |
| /// ## One base, one scalar |
| /// |
| /// For this pattern, you can use a transient `Wnaf` context: |
| /// |
| /// ```ignore |
| /// use group::Wnaf; |
| /// |
| /// let result = Wnaf::new().scalar(&scalar).base(base); |
| /// ``` |
| /// |
| /// ## Many bases, one scalar |
| /// |
| /// For this pattern, you create a `Wnaf` context, load the scalar into it, and then |
| /// process each base in turn: |
| /// |
| /// ```ignore |
| /// use group::Wnaf; |
| /// |
| /// let mut wnaf = Wnaf::new(); |
| /// let mut wnaf_scalar = wnaf.scalar(&scalar); |
| /// let results: Vec<_> = bases |
| /// .into_iter() |
| /// .map(|base| wnaf_scalar.base(base)) |
| /// .collect(); |
| /// ``` |
| /// |
| /// ## One base, many scalars |
| /// |
| /// For this pattern, you create a `Wnaf` context, load the base into it, and then process |
| /// each scalar in turn: |
| /// |
| /// ```ignore |
| /// use group::Wnaf; |
| /// |
| /// let mut wnaf = Wnaf::new(); |
| /// let mut wnaf_base = wnaf.base(base, scalars.len()); |
| /// let results: Vec<_> = scalars |
| /// .iter() |
| /// .map(|scalar| wnaf_base.scalar(scalar)) |
| /// .collect(); |
| /// ``` |
| /// |
| /// ## Many bases, many scalars |
| /// |
| /// Say you have `n` bases and `m` scalars, and want to produce `n * m` results. For this |
| /// pattern, you need to cache the w-NAF tables for the bases and then compute the w-NAF |
| /// form of the scalars on the fly for every base, or vice versa: |
| /// |
| /// ```ignore |
| /// use group::Wnaf; |
| /// |
| /// let mut wnaf_contexts: Vec<_> = (0..bases.len()).map(|_| Wnaf::new()).collect(); |
| /// let mut wnaf_bases: Vec<_> = wnaf_contexts |
| /// .iter_mut() |
| /// .zip(bases) |
| /// .map(|(wnaf, base)| wnaf.base(base, scalars.len())) |
| /// .collect(); |
| /// let results: Vec<_> = wnaf_bases |
| /// .iter() |
| /// .flat_map(|wnaf_base| scalars.iter().map(|scalar| wnaf_base.scalar(scalar))) |
| /// .collect(); |
| /// ``` |
| /// |
| /// Alternatively, use the [`WnafBase`] and [`WnafScalar`] types, which enable the various |
| /// tables and w-NAF forms to be cached individually per base and scalar. These types can |
| /// then be directly multiplied without any additional runtime work, at the cost of fixing |
| /// a specific window size (rather than choosing the window size dynamically). |
| #[derive(Debug)] |
| pub struct Wnaf<W, B, S> { |
| base: B, |
| scalar: S, |
| window_size: W, |
| } |
| |
| impl<G: Group> Wnaf<(), Vec<G>, Vec<i64>> { |
| /// Construct a new wNAF context without allocating. |
| pub fn new() -> Self { |
| Wnaf { |
| base: vec![], |
| scalar: vec![], |
| window_size: (), |
| } |
| } |
| } |
| |
| #[cfg(feature = "wnaf-memuse")] |
| impl<G: Group + memuse::DynamicUsage> memuse::DynamicUsage for Wnaf<(), Vec<G>, Vec<i64>> { |
| fn dynamic_usage(&self) -> usize { |
| self.base.dynamic_usage() + self.scalar.dynamic_usage() |
| } |
| |
| fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) { |
| let (base_lower, base_upper) = self.base.dynamic_usage_bounds(); |
| let (scalar_lower, scalar_upper) = self.scalar.dynamic_usage_bounds(); |
| |
| ( |
| base_lower + scalar_lower, |
| base_upper.zip(scalar_upper).map(|(a, b)| a + b), |
| ) |
| } |
| } |
| |
| impl<G: WnafGroup> Wnaf<(), Vec<G>, Vec<i64>> { |
| /// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that |
| /// can perform exponentiations with `.scalar(..)`. |
| pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf<usize, &[G], &mut Vec<i64>> { |
| // Compute the appropriate window size based on the number of scalars. |
| let window_size = G::recommended_wnaf_for_num_scalars(num_scalars); |
| |
| // Compute a wNAF table for the provided base and window size. |
| wnaf_table(&mut self.base, base, window_size); |
| |
| // Return a Wnaf object that immutably borrows the computed base storage location, |
| // but mutably borrows the scalar storage location. |
| Wnaf { |
| base: &self.base[..], |
| scalar: &mut self.scalar, |
| window_size, |
| } |
| } |
| |
| /// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform |
| /// exponentiations with `.base(..)`. |
| pub fn scalar(&mut self, scalar: &<G as Group>::Scalar) -> Wnaf<usize, &mut Vec<G>, &[i64]> { |
| // We hard-code a window size of 4. |
| let window_size = 4; |
| |
| // Compute the wNAF form of the scalar. |
| wnaf_form(&mut self.scalar, scalar.to_repr(), window_size); |
| |
| // Return a Wnaf object that mutably borrows the base storage location, but |
| // immutably borrows the computed wNAF form scalar location. |
| Wnaf { |
| base: &mut self.base, |
| scalar: &self.scalar[..], |
| window_size, |
| } |
| } |
| } |
| |
| impl<'a, G: Group> Wnaf<usize, &'a [G], &'a mut Vec<i64>> { |
| /// Constructs new space for the scalar representation while borrowing |
| /// the computed window table, for sending the window table across threads. |
| pub fn shared(&self) -> Wnaf<usize, &'a [G], Vec<i64>> { |
| Wnaf { |
| base: self.base, |
| scalar: vec![], |
| window_size: self.window_size, |
| } |
| } |
| } |
| |
| #[cfg(feature = "wnaf-memuse")] |
| impl<'a, G: Group> memuse::DynamicUsage for Wnaf<usize, &'a [G], Vec<i64>> { |
| fn dynamic_usage(&self) -> usize { |
| // The heap memory for the window table is counted in the parent `Wnaf`. |
| self.scalar.dynamic_usage() |
| } |
| |
| fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) { |
| self.scalar.dynamic_usage_bounds() |
| } |
| } |
| |
| impl<'a, G: Group> Wnaf<usize, &'a mut Vec<G>, &'a [i64]> { |
| /// Constructs new space for the window table while borrowing |
| /// the computed scalar representation, for sending the scalar representation |
| /// across threads. |
| pub fn shared(&self) -> Wnaf<usize, Vec<G>, &'a [i64]> { |
| Wnaf { |
| base: vec![], |
| scalar: self.scalar, |
| window_size: self.window_size, |
| } |
| } |
| } |
| |
| #[cfg(feature = "wnaf-memuse")] |
| impl<'a, G: Group + memuse::DynamicUsage> memuse::DynamicUsage for Wnaf<usize, Vec<G>, &'a [i64]> { |
| fn dynamic_usage(&self) -> usize { |
| // The heap memory for the scalar representation is counted in the parent `Wnaf`. |
| self.base.dynamic_usage() |
| } |
| |
| fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) { |
| self.base.dynamic_usage_bounds() |
| } |
| } |
| |
| impl<B, S: AsRef<[i64]>> Wnaf<usize, B, S> { |
| /// Performs exponentiation given a base. |
| pub fn base<G: Group>(&mut self, base: G) -> G |
| where |
| B: AsMut<Vec<G>>, |
| { |
| wnaf_table(self.base.as_mut(), base, self.window_size); |
| wnaf_exp(self.base.as_mut(), self.scalar.as_ref()) |
| } |
| } |
| |
| impl<B, S: AsMut<Vec<i64>>> Wnaf<usize, B, S> { |
| /// Performs exponentiation given a scalar. |
| pub fn scalar<G: Group>(&mut self, scalar: &<G as Group>::Scalar) -> G |
| where |
| B: AsRef<[G]>, |
| { |
| wnaf_form(self.scalar.as_mut(), scalar.to_repr(), self.window_size); |
| wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) |
| } |
| } |
| |
| /// A "w-ary non-adjacent form" scalar, that uses precomputation to improve the speed of |
| /// scalar multiplication. |
| /// |
| /// # Examples |
| /// |
| /// See [`WnafBase`] for usage examples. |
| #[derive(Clone, Debug)] |
| pub struct WnafScalar<F: PrimeField, const WINDOW_SIZE: usize> { |
| wnaf: Vec<i64>, |
| field: PhantomData<F>, |
| } |
| |
| #[cfg(feature = "wnaf-memuse")] |
| impl<F: PrimeField, const WINDOW_SIZE: usize> memuse::DynamicUsage for WnafScalar<F, WINDOW_SIZE> { |
| fn dynamic_usage(&self) -> usize { |
| self.wnaf.dynamic_usage() |
| } |
| |
| fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) { |
| self.wnaf.dynamic_usage_bounds() |
| } |
| } |
| |
| impl<F: PrimeField, const WINDOW_SIZE: usize> WnafScalar<F, WINDOW_SIZE> { |
| /// Computes the w-NAF representation of the given scalar with the specified |
| /// `WINDOW_SIZE`. |
| pub fn new(scalar: &F) -> Self { |
| let mut wnaf = vec![]; |
| |
| // Compute the w-NAF form of the scalar. |
| wnaf_form(&mut wnaf, scalar.to_repr(), WINDOW_SIZE); |
| |
| WnafScalar { |
| wnaf, |
| field: PhantomData::default(), |
| } |
| } |
| } |
| |
| /// A fixed window table for a group element, precomputed to improve the speed of scalar |
| /// multiplication. |
| /// |
| /// This struct is designed for usage patterns that have long-term cached bases and/or |
| /// scalars, or [Cartesian products] of bases and scalars. The [`Wnaf`] API enables one or |
| /// the other to be cached, but requires either the base window tables or the scalar w-NAF |
| /// forms to be computed repeatedly on the fly, which can become a significant performance |
| /// issue for some use cases. |
| /// |
| /// `WnafBase` and [`WnafScalar`] enable an alternative trade-off: by fixing the window |
| /// size at compile time, the precomputations are guaranteed to only occur once per base |
| /// and once per scalar. Users should select their window size based on how long the bases |
| /// are expected to live; a larger window size will consume more memory and take longer to |
| /// precompute, but result in faster scalar multiplications. |
| /// |
| /// [Cartesian products]: https://en.wikipedia.org/wiki/Cartesian_product |
| /// |
| /// # Examples |
| /// |
| /// ```ignore |
| /// use group::{WnafBase, WnafScalar}; |
| /// |
| /// let wnaf_bases: Vec<_> = bases.into_iter().map(WnafBase::<_, 4>::new).collect(); |
| /// let wnaf_scalars: Vec<_> = scalars.iter().map(WnafScalar::new).collect(); |
| /// let results: Vec<_> = wnaf_bases |
| /// .iter() |
| /// .flat_map(|base| wnaf_scalars.iter().map(|scalar| base * scalar)) |
| /// .collect(); |
| /// ``` |
| /// |
| /// Note that this pattern requires specifying a fixed window size (unlike previous |
| /// patterns that picked a suitable window size internally). This is necessary to ensure |
| /// in the type system that the base and scalar `Wnaf`s were computed with the same window |
| /// size, allowing the result to be computed infallibly. |
| #[derive(Clone, Debug)] |
| pub struct WnafBase<G: Group, const WINDOW_SIZE: usize> { |
| table: Vec<G>, |
| } |
| |
| #[cfg(feature = "wnaf-memuse")] |
| impl<G: Group + memuse::DynamicUsage, const WINDOW_SIZE: usize> memuse::DynamicUsage |
| for WnafBase<G, WINDOW_SIZE> |
| { |
| fn dynamic_usage(&self) -> usize { |
| self.table.dynamic_usage() |
| } |
| |
| fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) { |
| self.table.dynamic_usage_bounds() |
| } |
| } |
| |
| impl<G: Group, const WINDOW_SIZE: usize> WnafBase<G, WINDOW_SIZE> { |
| /// Computes a window table for the given base with the specified `WINDOW_SIZE`. |
| pub fn new(base: G) -> Self { |
| let mut table = vec![]; |
| |
| // Compute a window table for the provided base and window size. |
| wnaf_table(&mut table, base, WINDOW_SIZE); |
| |
| WnafBase { table } |
| } |
| } |
| |
| impl<G: Group, const WINDOW_SIZE: usize> Mul<&WnafScalar<G::Scalar, WINDOW_SIZE>> |
| for &WnafBase<G, WINDOW_SIZE> |
| { |
| type Output = G; |
| |
| fn mul(self, rhs: &WnafScalar<G::Scalar, WINDOW_SIZE>) -> Self::Output { |
| wnaf_exp(&self.table, &rhs.wnaf) |
| } |
| } |