From b375b46d37b7350d2cb2e2af63e438675640ea24 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 31 Jan 2023 01:37:03 -0700 Subject: [PATCH] Fixed-based Montgomery scalar multiplication (#503) * Fixed-based Montgomery scalar multiplication Adds `MontgomeryPoint::mul_base` as an API for fixed-base scalar multiplication which allows for potential future optimizations. As a baseline implementation, it uses the variable base scalar multiplication implementation. This follows the existing `EdwardsPoint::mul_base` and `RistrettoPoint::mul_base` APIs. * Added Montgomery mul_base bench * Switched MontgomeryPoint::mul_base to use EdwardsPoint::mul_base --------- Co-authored-by: Michael Rosenberg --- benches/dalek_benchmarks.rs | 121 ++++++++++++++++-------------------- src/montgomery.rs | 5 ++ 2 files changed, 59 insertions(+), 67 deletions(-) diff --git a/benches/dalek_benchmarks.rs b/benches/dalek_benchmarks.rs index 5f8fee4..aa16d0f 100644 --- a/benches/dalek_benchmarks.rs +++ b/benches/dalek_benchmarks.rs @@ -1,12 +1,10 @@ #![allow(non_snake_case)] -use rand::rngs::OsRng; -use rand::thread_rng; +use rand::{rngs::OsRng, thread_rng}; -use criterion::measurement::Measurement; -use criterion::BatchSize; -use criterion::Criterion; -use criterion::{criterion_group, criterion_main, BenchmarkGroup, BenchmarkId}; +use criterion::{ + criterion_main, measurement::Measurement, BatchSize, BenchmarkGroup, BenchmarkId, Criterion, +}; use curve25519_dalek::constants; use curve25519_dalek::scalar::Scalar; @@ -19,26 +17,26 @@ mod edwards_benches { use curve25519_dalek::edwards::EdwardsPoint; - fn compress(c: &mut Criterion) { + fn compress(c: &mut BenchmarkGroup) { let B = &constants::ED25519_BASEPOINT_POINT; c.bench_function("EdwardsPoint compression", move |b| b.iter(|| B.compress())); } - fn decompress(c: &mut Criterion) { + fn decompress(c: &mut BenchmarkGroup) { let B_comp = &constants::ED25519_BASEPOINT_COMPRESSED; c.bench_function("EdwardsPoint decompression", move |b| { b.iter(|| B_comp.decompress().unwrap()) }); } - fn consttime_fixed_base_scalar_mul(c: &mut Criterion) { + fn consttime_fixed_base_scalar_mul(c: &mut BenchmarkGroup) { let s = Scalar::from(897987897u64).invert(); c.bench_function("Constant-time fixed-base scalar mul", move |b| { b.iter(|| EdwardsPoint::mul_base(&s)) }); } - fn consttime_variable_base_scalar_mul(c: &mut Criterion) { + fn consttime_variable_base_scalar_mul(c: &mut BenchmarkGroup) { let B = &constants::ED25519_BASEPOINT_POINT; let s = Scalar::from(897987897u64).invert(); c.bench_function("Constant-time variable-base scalar mul", move |b| { @@ -46,7 +44,7 @@ mod edwards_benches { }); } - fn vartime_double_base_scalar_mul(c: &mut Criterion) { + fn vartime_double_base_scalar_mul(c: &mut BenchmarkGroup) { c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| { let mut rng = thread_rng(); let A = EdwardsPoint::mul_base(&Scalar::random(&mut rng)); @@ -58,15 +56,15 @@ mod edwards_benches { }); } - criterion_group! { - name = edwards_benches; - config = Criterion::default(); - targets = - compress, - decompress, - consttime_fixed_base_scalar_mul, - consttime_variable_base_scalar_mul, - vartime_double_base_scalar_mul, + pub(crate) fn edwards_benches() { + let mut c = Criterion::default(); + let mut g = c.benchmark_group("edwards benches"); + + compress(&mut g); + decompress(&mut g); + consttime_fixed_base_scalar_mul(&mut g); + consttime_variable_base_scalar_mul(&mut g); + vartime_double_base_scalar_mul(&mut g); } } @@ -211,28 +209,19 @@ mod multiscalar_benches { } } - fn multiscalar_multiplications(c: &mut Criterion) { - let mut group: BenchmarkGroup<_> = c.benchmark_group("Multiscalar multiplications"); + pub(crate) fn multiscalar_benches() { + let mut c = Criterion::default(); + let mut g = c.benchmark_group("multiscalar benches"); - consttime_multiscalar_mul(&mut group); - vartime_multiscalar_mul(&mut group); - vartime_precomputed_pure_static(&mut group); + consttime_multiscalar_mul(&mut g); + vartime_multiscalar_mul(&mut g); + vartime_precomputed_pure_static(&mut g); let dynamic_fracs = [0.0, 0.2, 0.5]; for frac in dynamic_fracs.iter() { - vartime_precomputed_helper(&mut group, *frac); + vartime_precomputed_helper(&mut g, *frac); } - - group.finish(); - } - - criterion_group! { - name = multiscalar_benches; - // Lower the sample size to run the benchmarks faster - config = Criterion::default().sample_size(15); - targets = - multiscalar_multiplications, } } @@ -240,14 +229,14 @@ mod ristretto_benches { use super::*; use curve25519_dalek::ristretto::RistrettoPoint; - fn compress(c: &mut Criterion) { + fn compress(c: &mut BenchmarkGroup) { c.bench_function("RistrettoPoint compression", |b| { let B = &constants::RISTRETTO_BASEPOINT_POINT; b.iter(|| B.compress()) }); } - fn decompress(c: &mut Criterion) { + fn decompress(c: &mut BenchmarkGroup) { c.bench_function("RistrettoPoint decompression", |b| { let B_comp = &constants::RISTRETTO_BASEPOINT_COMPRESSED; b.iter(|| B_comp.decompress().unwrap()) @@ -270,26 +259,21 @@ mod ristretto_benches { } } - fn double_and_compress_group(c: &mut Criterion) { - let mut group: BenchmarkGroup<_> = c.benchmark_group("double & compress batched"); - double_and_compress_batch(&mut group); - group.finish(); - } + pub(crate) fn ristretto_benches() { + let mut c = Criterion::default(); + let mut g = c.benchmark_group("ristretto benches"); - criterion_group! { - name = ristretto_benches; - config = Criterion::default(); - targets = - compress, - decompress, - double_and_compress_group, + compress(&mut g); + decompress(&mut g); + double_and_compress_batch(&mut g); } } mod montgomery_benches { use super::*; + use curve25519_dalek::montgomery::MontgomeryPoint; - fn montgomery_ladder(c: &mut Criterion) { + fn montgomery_ladder(c: &mut BenchmarkGroup) { c.bench_function("Montgomery pseudomultiplication", |b| { let B = constants::X25519_BASEPOINT; let s = Scalar::from(897987897u64).invert(); @@ -297,17 +281,26 @@ mod montgomery_benches { }); } - criterion_group! { - name = montgomery_benches; - config = Criterion::default(); - targets = montgomery_ladder, + fn consttime_fixed_base_scalar_mul(c: &mut BenchmarkGroup) { + let s = Scalar::from(897987897u64).invert(); + c.bench_function("Constant-time fixed-base scalar mul", move |b| { + b.iter(|| MontgomeryPoint::mul_base(&s)) + }); + } + + pub(crate) fn montgomery_benches() { + let mut c = Criterion::default(); + let mut g = c.benchmark_group("montgomery benches"); + + montgomery_ladder(&mut g); + consttime_fixed_base_scalar_mul(&mut g); } } mod scalar_benches { use super::*; - fn scalar_inversion(c: &mut Criterion) { + fn scalar_inversion(c: &mut BenchmarkGroup) { c.bench_function("Scalar inversion", |b| { let s = Scalar::from(897987897u64).invert(); b.iter(|| s.invert()); @@ -332,18 +325,12 @@ mod scalar_benches { } } - fn batch_scalar_inversion_group(c: &mut Criterion) { - let mut group: BenchmarkGroup<_> = c.benchmark_group("batch scalar inversion"); - batch_scalar_inversion(&mut group); - group.finish(); - } + pub(crate) fn scalar_benches() { + let mut c = Criterion::default(); + let mut g = c.benchmark_group("scalar benches"); - criterion_group! { - name = scalar_benches; - config = Criterion::default(); - targets = - scalar_inversion, - batch_scalar_inversion_group, + scalar_inversion(&mut g); + batch_scalar_inversion(&mut g); } } diff --git a/src/montgomery.rs b/src/montgomery.rs index 9aab60e..7bb4a29 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -118,6 +118,11 @@ impl Zeroize for MontgomeryPoint { } impl MontgomeryPoint { + /// Fixed-base scalar multiplication (i.e. multiplication by the base point). + pub fn mul_base(scalar: &Scalar) -> Self { + EdwardsPoint::mul_base(scalar).to_montgomery() + } + /// View this `MontgomeryPoint` as an array of bytes. pub const fn as_bytes(&self) -> &[u8; 32] { &self.0