1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5#[cfg(not(feature = "std"))]
6#[macro_use]
7extern crate alloc;
8use std_shims::vec::Vec;
9
10use zeroize::Zeroize;
11
12use ff::PrimeFieldBits;
13use group::Group;
14
15mod straus;
16use straus::*;
17
18mod pippenger;
19use pippenger::*;
20
21#[cfg(feature = "batch")]
22mod batch;
23#[cfg(feature = "batch")]
24pub use batch::BatchVerifier;
25
26#[cfg(test)]
27mod tests;
28
29#[rustversion::since(1.66)]
31use core::hint::black_box;
32#[rustversion::before(1.66)]
33fn black_box<T>(val: T) -> T {
34 val
35}
36
37fn u8_from_bool(bit_ref: &mut bool) -> u8 {
38 let bit_ref = black_box(bit_ref);
39
40 let mut bit = black_box(*bit_ref);
41 let res = black_box(bit as u8);
42 bit.zeroize();
43 debug_assert!((res | 1) == 1);
44
45 bit_ref.zeroize();
46 res
47}
48
49pub(crate) fn prep_bits<G: Group>(pairs: &[(G::Scalar, G)], window: u8) -> Vec<Vec<u8>>
52where
53 G::Scalar: PrimeFieldBits,
54{
55 let w_usize = usize::from(window);
56
57 let mut groupings = vec![];
58 for pair in pairs {
59 let p = groupings.len();
60 let mut bits = pair.0.to_le_bits();
61 groupings.push(vec![0; (bits.len() + (w_usize - 1)) / w_usize]);
62
63 for (i, mut bit) in bits.iter_mut().enumerate() {
64 let mut bit = u8_from_bool(&mut bit);
65 groupings[p][i / w_usize] |= bit << (i % w_usize);
66 bit.zeroize();
67 }
68 }
69
70 groupings
71}
72
73#[derive(Clone, Copy, PartialEq, Eq, Debug)]
74enum Algorithm {
75 Null,
76 Single,
77 Straus(u8),
78 Pippenger(u8),
79}
80
81fn algorithm(len: usize) -> Algorithm {
127 #[cfg(not(debug_assertions))]
128 if len == 0 {
129 Algorithm::Null
130 } else if len == 1 {
131 Algorithm::Single
132 } else if len < 10 {
133 Algorithm::Straus(3)
135 } else if len < 20 {
136 Algorithm::Straus(4)
137 } else if len < 50 {
138 Algorithm::Straus(5)
139 } else if len < 100 {
140 Algorithm::Pippenger(4)
141 } else if len < 125 {
142 Algorithm::Pippenger(5)
143 } else if len < 275 {
144 Algorithm::Pippenger(6)
145 } else if len < 400 {
146 Algorithm::Pippenger(7)
147 } else {
148 Algorithm::Pippenger(8)
149 }
150
151 #[cfg(debug_assertions)]
152 if len == 0 {
153 Algorithm::Null
154 } else if len == 1 {
155 Algorithm::Single
156 } else if len < 10 {
157 Algorithm::Straus(3)
158 } else if len < 80 {
159 Algorithm::Straus(4)
160 } else if len < 100 {
161 Algorithm::Straus(5)
162 } else if len < 125 {
163 Algorithm::Pippenger(4)
164 } else if len < 275 {
165 Algorithm::Pippenger(5)
166 } else if len < 475 {
167 Algorithm::Pippenger(6)
168 } else if len < 750 {
169 Algorithm::Pippenger(7)
170 } else {
171 Algorithm::Pippenger(8)
172 }
173}
174
175pub fn multiexp<G: Group>(pairs: &[(G::Scalar, G)]) -> G
178where
179 G::Scalar: PrimeFieldBits + Zeroize,
180{
181 match algorithm(pairs.len()) {
182 Algorithm::Null => Group::identity(),
183 Algorithm::Single => pairs[0].1 * pairs[0].0,
184 Algorithm::Straus(window) => straus(pairs, window),
186 Algorithm::Pippenger(window) => pippenger(pairs, window),
187 }
188}
189
190pub fn multiexp_vartime<G: Group>(pairs: &[(G::Scalar, G)]) -> G
193where
194 G::Scalar: PrimeFieldBits,
195{
196 match algorithm(pairs.len()) {
197 Algorithm::Null => Group::identity(),
198 Algorithm::Single => pairs[0].1 * pairs[0].0,
199 Algorithm::Straus(window) => straus_vartime(pairs, window),
200 Algorithm::Pippenger(window) => pippenger_vartime(pairs, window),
201 }
202}