Skip to content
This repository was archived by the owner on Jun 7, 2026. It is now read-only.

Commit 7e055e9

Browse files
committed
Rollup of commits to support dense wNAF
These are all the commits from the PR opened as zkcrypto#73, squashed into a single commit so I could cherry-pick it onto `rustcrypto-group`, whose history has diverged from upstream.
1 parent 5203ec9 commit 7e055e9

2 files changed

Lines changed: 44 additions & 7 deletions

File tree

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ jobs:
8282
- uses: actions/checkout@v4
8383
- run: cargo fetch
8484
# Requires #![deny(rustdoc::broken_intra_doc_links)] in crates.
85-
- run: sudo apt-get -y install libfontconfig1-dev
8685
- name: Check intra-doc links
8786
run: cargo doc --all-features --document-private-items
8887

src/wnaf.rs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)