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 #[allow(clippy::cast_lossless)]
42 let res = black_box(bit as u8);
43 bit.zeroize();
44 debug_assert!((res | 1) == 1);
45
46 bit_ref.zeroize();
47 res
48}
49
50pub(crate) fn prep_bits<G: Group<Scalar: PrimeFieldBits>>(
53 pairs: &[(G::Scalar, G)],
54 window: u8,
55) -> Vec<Vec<u8>> {
56 let w_usize = usize::from(window);
57
58 let mut groupings = vec![];
59 for pair in pairs {
60 let p = groupings.len();
61 let mut bits = pair.0.to_le_bits();
62 groupings.push(vec![0; (bits.len() + (w_usize - 1)) / w_usize]);
63
64 for (i, mut bit) in bits.iter_mut().enumerate() {
65 let mut bit = u8_from_bool(&mut bit);
66 groupings[p][i / w_usize] |= bit << (i % w_usize);
67 bit.zeroize();
68 }
69 }
70
71 groupings
72}
73
74#[derive(Clone, Copy, PartialEq, Eq, Debug)]
75enum Algorithm {
76 Null,
77 Single,
78 Straus(u8),
79 Pippenger(u8),
80}
81
82fn algorithm(len: usize) -> Algorithm {
128 #[cfg(not(debug_assertions))]
129 if len == 0 {
130 Algorithm::Null
131 } else if len == 1 {
132 Algorithm::Single
133 } else if len < 10 {
134 Algorithm::Straus(3)
136 } else if len < 20 {
137 Algorithm::Straus(4)
138 } else if len < 50 {
139 Algorithm::Straus(5)
140 } else if len < 100 {
141 Algorithm::Pippenger(4)
142 } else if len < 125 {
143 Algorithm::Pippenger(5)
144 } else if len < 275 {
145 Algorithm::Pippenger(6)
146 } else if len < 400 {
147 Algorithm::Pippenger(7)
148 } else {
149 Algorithm::Pippenger(8)
150 }
151
152 #[cfg(debug_assertions)]
153 if len == 0 {
154 Algorithm::Null
155 } else if len == 1 {
156 Algorithm::Single
157 } else if len < 10 {
158 Algorithm::Straus(3)
159 } else if len < 80 {
160 Algorithm::Straus(4)
161 } else if len < 100 {
162 Algorithm::Straus(5)
163 } else if len < 125 {
164 Algorithm::Pippenger(4)
165 } else if len < 275 {
166 Algorithm::Pippenger(5)
167 } else if len < 475 {
168 Algorithm::Pippenger(6)
169 } else if len < 750 {
170 Algorithm::Pippenger(7)
171 } else {
172 Algorithm::Pippenger(8)
173 }
174}
175
176pub fn multiexp<G: Group<Scalar: PrimeFieldBits + Zeroize>>(pairs: &[(G::Scalar, G)]) -> G {
179 match algorithm(pairs.len()) {
180 Algorithm::Null => Group::identity(),
181 Algorithm::Single => pairs[0].1 * pairs[0].0,
182 Algorithm::Straus(window) => straus(pairs, window),
184 Algorithm::Pippenger(window) => pippenger(pairs, window),
185 }
186}
187
188pub fn multiexp_vartime<G: Group<Scalar: PrimeFieldBits>>(pairs: &[(G::Scalar, G)]) -> G {
191 match algorithm(pairs.len()) {
192 Algorithm::Null => Group::identity(),
193 Algorithm::Single => pairs[0].1 * pairs[0].0,
194 Algorithm::Straus(window) => straus_vartime(pairs, window),
195 Algorithm::Pippenger(window) => pippenger_vartime(pairs, window),
196 }
197}