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