@@ -99,17 +99,18 @@ pub(crate) fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut Vec<i64>, c: S, window: usize
9999 let bit_len = c. as_ref ( ) . len ( ) * 8 ;
100100
101101 wnaf. truncate ( 0 ) ;
102- wnaf. reserve ( bit_len) ;
102+ wnaf. reserve ( bit_len + 1 ) ;
103103
104104 // Initialise the current and next limb buffers.
105105 let mut limbs = LimbBuffer :: new ( c. as_ref ( ) ) ;
106106
107- let width = 1u64 << window;
108- let window_mask = width - 1 ;
107+ let width_div_2 = 1u64 << ( window - 1 ) ; // window is asserted `2..=64` above
108+ let width = ( width_div_2 as i128 ) * 2 ; // for conditionally subtracting from `window_val`
109+ let window_mask = width_div_2 | ( width_div_2 - 1 ) ; // fill in 1s for all the lower 0 bits
109110
110111 let mut pos = 0 ;
111112 let mut carry = 0 ;
112- while pos < bit_len {
113+ while pos <= bit_len {
113114 // Construct a buffer of bits of the scalar, starting at bit `pos`
114115 let u64_idx = pos / 64 ;
115116 let bit_idx = pos % 64 ;
@@ -133,17 +134,20 @@ pub(crate) fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut Vec<i64>, c: S, window: usize
133134 wnaf. push ( 0 ) ;
134135 pos += 1 ;
135136 } else {
136- wnaf. push ( if window_val < width / 2 {
137+ wnaf. push ( if window_val < width_div_2 {
137138 carry = 0 ;
138139 window_val as i64
139140 } else {
140141 carry = 1 ;
141- ( window_val as i64 ) . wrapping_sub ( width as i64 )
142+ ( ( window_val as i128 ) - width) as i64
142143 } ) ;
143144 wnaf. extend ( iter:: repeat ( 0 ) . take ( window - 1 ) ) ;
144145 pos += window;
145146 }
146147 }
148+
149+ // Remove trailing zeros left by the loop above
150+ wnaf. truncate ( wnaf. len ( ) . saturating_sub ( window - 1 ) ) ;
147151}
148152
149153/// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar.
@@ -510,3 +514,37 @@ impl<G: Group, const WINDOW_SIZE: usize> Mul<&WnafScalar<G::Scalar, WINDOW_SIZE>
510514 wnaf_exp ( & self . table , & rhs. wnaf )
511515 }
512516}
517+
518+ #[ test]
519+ fn test_wnaf_form ( ) {
520+ fn from_wnaf ( wnaf : & Vec < i64 > ) -> u128 {
521+ wnaf. iter ( ) . rev ( ) . fold ( 0 , |acc, next| {
522+ // If one of the least-significant w-NAF limbs is negative, `acc` may be large
523+ // (due to the result being represented as a `u128`). `wrapping_mul` wraps at
524+ // the boundary of the type; in this case, it has the effect of doubling the
525+ // equivalent signed value:
526+ // acc = -x = u128::MAX + 1 - x
527+ // 2 * acc = 2 * (u128::MAX + 1 - x)
528+ // = 2 * (u128::MAX + 1) - 2x
529+ // = -2x as u128
530+ let acc = acc. wrapping_mul ( 2 ) ;
531+ // Rust signed-to-unsigned casts add or subtract `T::MAX + 1` until the value
532+ // fits into the new type. A wrapping addition of the new type is therefore
533+ // equivalent to a wrapping subtraction of the magnitude of the original type.
534+ acc. wrapping_add ( * next as u128 )
535+ } )
536+ }
537+ let mut wnaf = Vec :: with_capacity ( 129 ) ;
538+ for w in 2 ..64 {
539+ for e in 0 ..=u16:: MAX {
540+ wnaf_form ( & mut wnaf, e. to_le_bytes ( ) , w) ;
541+ assert_eq ! ( e as u128 , from_wnaf( & wnaf) ) ;
542+ }
543+ }
544+ for w in 2 ..64 {
545+ for e in u128:: MAX - 10000 ..=u128:: MAX {
546+ wnaf_form ( & mut wnaf, e. to_le_bytes ( ) , w) ;
547+ assert_eq ! ( e, from_wnaf( & wnaf) ) ;
548+ }
549+ }
550+ }
0 commit comments