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