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(table: &mut Vec, 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>(wnaf: &mut Vec, 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(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 { base: B, scalar: S, window_size: W, } impl Wnaf<(), Vec, Vec> { /// Construct a new wNAF context without allocating. pub fn new() -> Self { Wnaf { base: vec![], scalar: vec![], window_size: (), } } } #[cfg(feature = "wnaf-memuse")] impl memuse::DynamicUsage for Wnaf<(), Vec, Vec> { fn dynamic_usage(&self) -> usize { self.base.dynamic_usage() + self.scalar.dynamic_usage() } fn dynamic_usage_bounds(&self) -> (usize, Option) { 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 Wnaf<(), Vec, Vec> { /// 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> { // 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: &::Scalar) -> Wnaf, &[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> { /// 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> { Wnaf { base: self.base, scalar: vec![], window_size: self.window_size, } } } #[cfg(feature = "wnaf-memuse")] impl<'a, G: Group> memuse::DynamicUsage for Wnaf> { 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) { self.scalar.dynamic_usage_bounds() } } impl<'a, G: Group> Wnaf, &'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, &'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, &'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) { self.base.dynamic_usage_bounds() } } impl> Wnaf { /// Performs exponentiation given a base. pub fn base(&mut self, base: G) -> G where B: AsMut>, { wnaf_table(self.base.as_mut(), base, self.window_size); wnaf_exp(self.base.as_mut(), self.scalar.as_ref()) } } impl>> Wnaf { /// Performs exponentiation given a scalar. pub fn scalar(&mut self, scalar: &::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 { wnaf: Vec, field: PhantomData, } #[cfg(feature = "wnaf-memuse")] impl memuse::DynamicUsage for WnafScalar { fn dynamic_usage(&self) -> usize { self.wnaf.dynamic_usage() } fn dynamic_usage_bounds(&self) -> (usize, Option) { self.wnaf.dynamic_usage_bounds() } } impl WnafScalar { /// 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 { table: Vec, } #[cfg(feature = "wnaf-memuse")] impl memuse::DynamicUsage for WnafBase { fn dynamic_usage(&self) -> usize { self.table.dynamic_usage() } fn dynamic_usage_bounds(&self) -> (usize, Option) { self.table.dynamic_usage_bounds() } } impl WnafBase { /// 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 Mul<&WnafScalar> for &WnafBase { type Output = G; fn mul(self, rhs: &WnafScalar) -> Self::Output { wnaf_exp(&self.table, &rhs.wnaf) } }