1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc(
4 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
5 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
6)]
7#![allow(clippy::int_plus_one, reason = "clearer for our use cases ")]
8#![forbid(unsafe_code)]
9#![doc = include_str!("../README.md")]
10
11#[cfg(feature = "alloc")]
12#[macro_use]
13extern crate alloc;
14
15mod base;
16mod limb_buffer;
17mod scalar;
18mod traits;
19
20#[cfg(feature = "alloc")]
21mod boxed;
22
23pub use crate::{
24 base::WnafBase,
25 scalar::WnafScalar,
26 traits::{WindowSize, WnafGroup, WnafSize},
27};
28pub use array;
29pub use group::Group;
30
31#[cfg(feature = "alloc")]
32pub use crate::boxed::BoxedWnaf;
33
34use crate::limb_buffer::LimbBuffer;
35use ff::PrimeField;
36
37pub type Digit = i8;
42
43pub const W_MAX: usize = 8;
48
49fn wnaf_table<G: Group>(table: &mut [G], base: &G, window: usize) {
56 debug_assert_eq!(table.len(), 1 << (window - 2));
57
58 let dbl = base.double();
59 let mut cur = *base;
60
61 for entry in table {
62 *entry = cur;
63 cur.add_assign(&dbl);
64 }
65}
66
67#[allow(clippy::cast_possible_wrap)]
70fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut [Digit], c: S, bit_len: usize, window: usize) -> usize {
71 fn digit(n: u64) -> Digit {
72 #[cfg(debug_assertions)]
73 {
74 Digit::try_from(n).expect("overflow")
75 }
76 #[cfg(not(debug_assertions))]
77 {
78 n as Digit
79 }
80 }
81
82 debug_assert!(window >= 2);
83 debug_assert!(window <= W_MAX);
84 debug_assert!(bit_len < wnaf.len(), "wnaf storage too small");
85 debug_assert!(c.as_ref().len() <= bit_len.div_ceil(8), "input too large");
86
87 let width = 1u64 << window;
88 let window_mask = width - 1;
89
90 let mut limbs = LimbBuffer::new(c.as_ref());
91 let mut pos = 0;
92 let mut carry = 0;
93 let mut cursor = 0;
94
95 while pos < bit_len {
96 let u64_idx = pos / 64;
98 let bit_idx = pos % 64;
99 let (cur_u64, next_u64) = limbs.get(u64_idx);
100 let bit_buf = if bit_idx + window < 64 {
101 cur_u64 >> bit_idx
103 } else {
104 (cur_u64 >> bit_idx) | (next_u64 << (64 - bit_idx))
106 };
107
108 let window_val = carry + (bit_buf & window_mask);
110
111 if window_val & 1 == 0 {
112 wnaf[cursor] = 0;
117 cursor += 1;
118 pos += 1;
119 } else {
120 wnaf[cursor] = digit(window_val);
121
122 if window_val < width / 2 {
123 carry = 0;
124 } else {
125 carry = 1;
126 wnaf[cursor] -= digit(width);
127 };
128
129 cursor += 1;
130
131 let max_pos = bit_len.saturating_sub(usize::try_from(carry).expect("overflow"));
132 let skip = window.min(max_pos - pos);
133
134 for _ in 1..skip {
135 wnaf[cursor] = 0;
136 cursor += 1;
137 }
138 pos += skip;
139 }
140 }
141
142 if carry != 0 {
145 wnaf[cursor] = digit(carry);
146 cursor += 1;
147 }
148
149 cursor
150}
151
152fn wnaf_multi_exp<'a, G, I>(terms: I) -> G
158where
159 G: Group,
160 I: Clone + IntoIterator<Item = (&'a [G], &'a [Digit], usize)>,
161{
162 let window_size = terms
163 .clone()
164 .into_iter()
165 .map(|(_, _, len)| len)
166 .max()
167 .unwrap_or(0);
168
169 let mut result = G::identity();
170 let mut found_one = false;
171
172 for i in (0..window_size).rev() {
173 if found_one {
174 result = result.double();
175 }
176
177 for (table, wnaf, _) in terms.clone() {
178 let n = wnaf.get(i).copied().unwrap_or(0);
179
180 if n != 0 {
181 found_one = true;
182
183 #[allow(clippy::cast_sign_loss)]
184 if n > 0 {
185 result += table[(n / 2) as usize];
186 } else {
187 result -= table[((-n) / 2) as usize];
188 }
189 }
190 }
191 }
192
193 result
194}
195
196fn le_repr<F: PrimeField>(fe: &F) -> F::Repr {
198 let mut ret = fe.to_repr();
199 ret.as_mut().reverse();
201 ret
202}