1 #![allow(non_snake_case)]
2 
3 extern crate rand;
4 use rand::rngs::OsRng;
5 use rand::thread_rng;
6 
7 #[macro_use]
8 extern crate criterion;
9 
10 use criterion::measurement::Measurement;
11 use criterion::BatchSize;
12 use criterion::Criterion;
13 use criterion::{BenchmarkGroup, BenchmarkId};
14 
15 extern crate curve25519_dalek;
16 
17 use curve25519_dalek::constants;
18 use curve25519_dalek::scalar::Scalar;
19 
20 static BATCH_SIZES: [usize; 5] = [1, 2, 4, 8, 16];
21 static MULTISCALAR_SIZES: [usize; 13] = [1, 2, 4, 8, 16, 32, 64, 128, 256, 384, 512, 768, 1024];
22 
23 mod edwards_benches {
24     use super::*;
25 
26     use curve25519_dalek::edwards::EdwardsPoint;
27 
compress(c: &mut Criterion)28     fn compress(c: &mut Criterion) {
29         let B = &constants::ED25519_BASEPOINT_POINT;
30         c.bench_function("EdwardsPoint compression", move |b| b.iter(|| B.compress()));
31     }
32 
decompress(c: &mut Criterion)33     fn decompress(c: &mut Criterion) {
34         let B_comp = &constants::ED25519_BASEPOINT_COMPRESSED;
35         c.bench_function("EdwardsPoint decompression", move |b| {
36             b.iter(|| B_comp.decompress().unwrap())
37         });
38     }
39 
consttime_fixed_base_scalar_mul(c: &mut Criterion)40     fn consttime_fixed_base_scalar_mul(c: &mut Criterion) {
41         let B = &constants::ED25519_BASEPOINT_TABLE;
42         let s = Scalar::from(897987897u64).invert();
43         c.bench_function("Constant-time fixed-base scalar mul", move |b| {
44             b.iter(|| B * &s)
45         });
46     }
47 
consttime_variable_base_scalar_mul(c: &mut Criterion)48     fn consttime_variable_base_scalar_mul(c: &mut Criterion) {
49         let B = &constants::ED25519_BASEPOINT_POINT;
50         let s = Scalar::from(897987897u64).invert();
51         c.bench_function("Constant-time variable-base scalar mul", move |b| {
52             b.iter(|| B * s)
53         });
54     }
55 
vartime_double_base_scalar_mul(c: &mut Criterion)56     fn vartime_double_base_scalar_mul(c: &mut Criterion) {
57         c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| {
58             let mut rng = thread_rng();
59             let A = &Scalar::random(&mut rng) * &constants::ED25519_BASEPOINT_TABLE;
60             bench.iter_batched(
61                 || (Scalar::random(&mut rng), Scalar::random(&mut rng)),
62                 |(a, b)| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b),
63                 BatchSize::SmallInput,
64             );
65         });
66     }
67 
68     criterion_group! {
69         name = edwards_benches;
70         config = Criterion::default();
71         targets =
72         compress,
73         decompress,
74         consttime_fixed_base_scalar_mul,
75         consttime_variable_base_scalar_mul,
76         vartime_double_base_scalar_mul,
77     }
78 }
79 
80 mod multiscalar_benches {
81     use super::*;
82 
83     use curve25519_dalek::edwards::EdwardsPoint;
84     use curve25519_dalek::edwards::VartimeEdwardsPrecomputation;
85     use curve25519_dalek::traits::MultiscalarMul;
86     use curve25519_dalek::traits::VartimeMultiscalarMul;
87     use curve25519_dalek::traits::VartimePrecomputedMultiscalarMul;
88 
construct_scalars(n: usize) -> Vec<Scalar>89     fn construct_scalars(n: usize) -> Vec<Scalar> {
90         let mut rng = thread_rng();
91         (0..n).map(|_| Scalar::random(&mut rng)).collect()
92     }
93 
construct_points(n: usize) -> Vec<EdwardsPoint>94     fn construct_points(n: usize) -> Vec<EdwardsPoint> {
95         let mut rng = thread_rng();
96         (0..n)
97             .map(|_| &Scalar::random(&mut rng) * &constants::ED25519_BASEPOINT_TABLE)
98             .collect()
99     }
100 
construct(n: usize) -> (Vec<Scalar>, Vec<EdwardsPoint>)101     fn construct(n: usize) -> (Vec<Scalar>, Vec<EdwardsPoint>) {
102         (construct_scalars(n), construct_points(n))
103     }
104 
consttime_multiscalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>)105     fn consttime_multiscalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>) {
106         for multiscalar_size in &MULTISCALAR_SIZES {
107             c.bench_with_input(
108                 BenchmarkId::new(
109                     "Constant-time variable-base multiscalar multiplication",
110                     *multiscalar_size,
111                 ),
112                 &multiscalar_size,
113                 |b, &&size| {
114                     let points = construct_points(size);
115                     // This is supposed to be constant-time, but we might as well
116                     // rerandomize the scalars for every call just in case.
117                     b.iter_batched(
118                         || construct_scalars(size),
119                         |scalars| EdwardsPoint::multiscalar_mul(&scalars, &points),
120                         BatchSize::SmallInput,
121                     );
122                 },
123             );
124         }
125     }
126 
vartime_multiscalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>)127     fn vartime_multiscalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>) {
128         for multiscalar_size in &MULTISCALAR_SIZES {
129             c.bench_with_input(
130                 BenchmarkId::new(
131                     "Variable-time variable-base multiscalar multiplication",
132                     *multiscalar_size,
133                 ),
134                 &multiscalar_size,
135                 |b, &&size| {
136                     let points = construct_points(size);
137                     // Rerandomize the scalars for every call to prevent
138                     // false timings from better caching (e.g., the CPU
139                     // cache lifts exactly the right table entries for the
140                     // benchmark into the highest cache levels).
141                     b.iter_batched(
142                         || construct_scalars(size),
143                         |scalars| EdwardsPoint::vartime_multiscalar_mul(&scalars, &points),
144                         BatchSize::SmallInput,
145                     );
146                 },
147             );
148         }
149     }
150 
vartime_precomputed_pure_static<M: Measurement>(c: &mut BenchmarkGroup<M>)151     fn vartime_precomputed_pure_static<M: Measurement>(c: &mut BenchmarkGroup<M>) {
152         for multiscalar_size in &MULTISCALAR_SIZES {
153             c.bench_with_input(
154                 BenchmarkId::new(
155                     "Variable-time fixed-base multiscalar multiplication",
156                     &multiscalar_size,
157                 ),
158                 &multiscalar_size,
159                 move |b, &&total_size| {
160                     let static_size = total_size;
161 
162                     let static_points = construct_points(static_size);
163                     let precomp = VartimeEdwardsPrecomputation::new(&static_points);
164                     // Rerandomize the scalars for every call to prevent
165                     // false timings from better caching (e.g., the CPU
166                     // cache lifts exactly the right table entries for the
167                     // benchmark into the highest cache levels).
168                     b.iter_batched(
169                         || construct_scalars(static_size),
170                         |scalars| precomp.vartime_multiscalar_mul(&scalars),
171                         BatchSize::SmallInput,
172                     );
173                 },
174             );
175         }
176     }
177 
vartime_precomputed_helper<M: Measurement>( c: &mut BenchmarkGroup<M>, dynamic_fraction: f64, )178     fn vartime_precomputed_helper<M: Measurement>(
179         c: &mut BenchmarkGroup<M>,
180         dynamic_fraction: f64,
181     ) {
182         for multiscalar_size in &MULTISCALAR_SIZES {
183             c.bench_with_input(
184                 BenchmarkId::new(
185                     "Variable-time mixed-base multiscalar multiplication ({:.0}pct dyn)",
186                     format!("({:.0}pct dyn)", 100.0 * dynamic_fraction),
187                 ),
188                 &multiscalar_size,
189                 move |b, &&total_size| {
190                     let dynamic_size = ((total_size as f64) * dynamic_fraction) as usize;
191                     let static_size = total_size - dynamic_size;
192 
193                     let static_points = construct_points(static_size);
194                     let dynamic_points = construct_points(dynamic_size);
195                     let precomp = VartimeEdwardsPrecomputation::new(&static_points);
196                     // Rerandomize the scalars for every call to prevent
197                     // false timings from better caching (e.g., the CPU
198                     // cache lifts exactly the right table entries for the
199                     // benchmark into the highest cache levels).  Timings
200                     // should be independent of points so we don't
201                     // randomize them.
202                     b.iter_batched(
203                         || {
204                             (
205                                 construct_scalars(static_size),
206                                 construct_scalars(dynamic_size),
207                             )
208                         },
209                         |(static_scalars, dynamic_scalars)| {
210                             precomp.vartime_mixed_multiscalar_mul(
211                                 &static_scalars,
212                                 &dynamic_scalars,
213                                 &dynamic_points,
214                             )
215                         },
216                         BatchSize::SmallInput,
217                     );
218                 },
219             );
220         }
221     }
222 
multiscalar_multiplications(c: &mut Criterion)223     fn multiscalar_multiplications(c: &mut Criterion) {
224         let mut group: BenchmarkGroup<_> = c.benchmark_group("Multiscalar muls");
225 
226         consttime_multiscalar_mul(&mut group);
227         vartime_multiscalar_mul(&mut group);
228         vartime_precomputed_pure_static(&mut group);
229 
230         let dynamic_fracs = [0.0, 0.2, 0.5];
231         for frac in dynamic_fracs.iter() {
232             vartime_precomputed_helper(&mut group, *frac);
233         }
234         group.finish();
235     }
236 
237     criterion_group! {
238         name = multiscalar_benches;
239         // Lower the sample size to run the benchmarks faster
240         config = Criterion::default().sample_size(15);
241         targets =
242         multiscalar_multiplications,
243     }
244 }
245 
246 mod ristretto_benches {
247     use super::*;
248     use curve25519_dalek::ristretto::RistrettoPoint;
249 
compress(c: &mut Criterion)250     fn compress(c: &mut Criterion) {
251         c.bench_function("RistrettoPoint compression", |b| {
252             let B = &constants::RISTRETTO_BASEPOINT_POINT;
253             b.iter(|| B.compress())
254         });
255     }
256 
decompress(c: &mut Criterion)257     fn decompress(c: &mut Criterion) {
258         c.bench_function("RistrettoPoint decompression", |b| {
259             let B_comp = &constants::RISTRETTO_BASEPOINT_COMPRESSED;
260             b.iter(|| B_comp.decompress().unwrap())
261         });
262     }
263 
double_and_compress_batch<M: Measurement>(c: &mut BenchmarkGroup<M>)264     fn double_and_compress_batch<M: Measurement>(c: &mut BenchmarkGroup<M>) {
265         for batch_size in &BATCH_SIZES {
266             c.bench_with_input(
267                 BenchmarkId::new("Batch Ristretto double-and-encode", *batch_size),
268                 &batch_size,
269                 |b, &&size| {
270                     let mut rng = OsRng;
271                     let points: Vec<RistrettoPoint> = (0..size)
272                         .map(|_| RistrettoPoint::random(&mut rng))
273                         .collect();
274                     b.iter(|| RistrettoPoint::double_and_compress_batch(&points));
275                 },
276             );
277         }
278     }
279 
double_and_compress_group(c: &mut Criterion)280     fn double_and_compress_group(c: &mut Criterion) {
281         let mut group: BenchmarkGroup<_> = c.benchmark_group("double & compress batched");
282         double_and_compress_batch(&mut group);
283         group.finish();
284     }
285 
286     criterion_group! {
287         name = ristretto_benches;
288         config = Criterion::default();
289         targets =
290         compress,
291         decompress,
292         double_and_compress_group,
293     }
294 }
295 
296 mod montgomery_benches {
297     use super::*;
298 
montgomery_ladder(c: &mut Criterion)299     fn montgomery_ladder(c: &mut Criterion) {
300         c.bench_function("Montgomery pseudomultiplication", |b| {
301             let B = constants::X25519_BASEPOINT;
302             let s = Scalar::from(897987897u64).invert();
303             b.iter(|| B * s);
304         });
305     }
306 
307     criterion_group! {
308         name = montgomery_benches;
309         config = Criterion::default();
310         targets = montgomery_ladder,
311     }
312 }
313 
314 mod scalar_benches {
315     use super::*;
316 
scalar_inversion(c: &mut Criterion)317     fn scalar_inversion(c: &mut Criterion) {
318         c.bench_function("Scalar inversion", |b| {
319             let s = Scalar::from(897987897u64).invert();
320             b.iter(|| s.invert());
321         });
322     }
323 
batch_scalar_inversion<M: Measurement>(c: &mut BenchmarkGroup<M>)324     fn batch_scalar_inversion<M: Measurement>(c: &mut BenchmarkGroup<M>) {
325         for batch_size in &BATCH_SIZES {
326             c.bench_with_input(
327                 BenchmarkId::new("Batch scalar inversion", *batch_size),
328                 &batch_size,
329                 |b, &&size| {
330                     let mut rng = OsRng;
331                     let scalars: Vec<Scalar> =
332                         (0..size).map(|_| Scalar::random(&mut rng)).collect();
333                     b.iter(|| {
334                         let mut s = scalars.clone();
335                         Scalar::batch_invert(&mut s);
336                     });
337                 },
338             );
339         }
340     }
341 
batch_scalar_inversion_group(c: &mut Criterion)342     fn batch_scalar_inversion_group(c: &mut Criterion) {
343         let mut group: BenchmarkGroup<_> = c.benchmark_group("batch scalar inversion");
344         batch_scalar_inversion(&mut group);
345         group.finish();
346     }
347 
348     criterion_group! {
349         name = scalar_benches;
350         config = Criterion::default();
351         targets =
352         scalar_inversion,
353         batch_scalar_inversion_group,
354     }
355 }
356 
357 criterion_main!(
358     scalar_benches::scalar_benches,
359     montgomery_benches::montgomery_benches,
360     ristretto_benches::ristretto_benches,
361     edwards_benches::edwards_benches,
362     multiscalar_benches::multiscalar_benches,
363 );
364