From 5a5a768fe927a36ae6ed2995ff742643fb6e93b3 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Thu, 6 Jun 2024 14:43:32 -0400 Subject: [PATCH] Use a variable time multiplication for the torsion check As the group order is anyway public, there is no reason to prevent it from leaking to a side channel. --- curve25519-dalek/benches/dalek_benchmarks.rs | 8 ++++ curve25519-dalek/src/backend/mod.rs | 15 +++++++ .../serial/scalar_mul/variable_base.rs | 42 ++++++++++++++++++- .../vector/scalar_mul/variable_base.rs | 42 ++++++++++++++++++- curve25519-dalek/src/edwards.rs | 3 +- 5 files changed, 106 insertions(+), 4 deletions(-) diff --git a/curve25519-dalek/benches/dalek_benchmarks.rs b/curve25519-dalek/benches/dalek_benchmarks.rs index 62a6280f4..efffbb8c1 100644 --- a/curve25519-dalek/benches/dalek_benchmarks.rs +++ b/curve25519-dalek/benches/dalek_benchmarks.rs @@ -29,6 +29,13 @@ mod edwards_benches { }); } + fn is_torsion_free(c: &mut BenchmarkGroup) { + let B = &constants::ED25519_BASEPOINT_POINT; + c.bench_function("EdwardsPoint is_torsion_free", move |b| { + b.iter(|| B.is_torsion_free()) + }); + } + 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| { @@ -62,6 +69,7 @@ mod edwards_benches { compress(&mut g); decompress(&mut g); + is_torsion_free(&mut g); consttime_fixed_base_scalar_mul(&mut g); consttime_variable_base_scalar_mul(&mut g); vartime_double_base_scalar_mul(&mut g); diff --git a/curve25519-dalek/src/backend/mod.rs b/curve25519-dalek/src/backend/mod.rs index 9ad1dd3de..84ee15c92 100644 --- a/curve25519-dalek/src/backend/mod.rs +++ b/curve25519-dalek/src/backend/mod.rs @@ -236,6 +236,21 @@ pub fn variable_base_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint } } +/// Perform variable-time, variable-base scalar multiplication. +pub fn vartime_variable_base_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { + match get_selected_backend() { + #[cfg(curve25519_dalek_backend = "simd")] + BackendKind::Avx2 => { + vector::scalar_mul::variable_base::spec_avx2::vartime_mul(point, scalar) + } + #[cfg(all(curve25519_dalek_backend = "simd", nightly))] + BackendKind::Avx512 => { + vector::scalar_mul::variable_base::spec_avx512ifma_avx512vl::vartime_mul(point, scalar) + } + BackendKind::Serial => serial::scalar_mul::variable_base::vartime_mul(point, scalar), + } +} + /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. #[allow(non_snake_case)] pub fn vartime_double_base_mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { diff --git a/curve25519-dalek/src/backend/serial/scalar_mul/variable_base.rs b/curve25519-dalek/src/backend/serial/scalar_mul/variable_base.rs index 1de84bc4d..1da5c298b 100644 --- a/curve25519-dalek/src/backend/serial/scalar_mul/variable_base.rs +++ b/curve25519-dalek/src/backend/serial/scalar_mul/variable_base.rs @@ -1,10 +1,12 @@ #![allow(non_snake_case)] -use crate::backend::serial::curve_models::ProjectiveNielsPoint; +use core::cmp::Ordering; + +use crate::backend::serial::curve_models::{ProjectiveNielsPoint, ProjectivePoint}; use crate::edwards::EdwardsPoint; use crate::scalar::Scalar; use crate::traits::Identity; -use crate::window::LookupTable; +use crate::window::{LookupTable, NafLookupTable5}; /// Perform constant-time, variable-base scalar multiplication. #[rustfmt::skip] // keep alignment of explanatory comments @@ -46,3 +48,39 @@ pub(crate) fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { } tmp1.as_extended() } + +/// Perform variable-time, variable-base scalar multiplication. +pub fn vartime_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { + let naf = scalar.non_adjacent_form(5); + let table = NafLookupTable5::::from(point); + + // Find starting index + let mut i: usize = 255; + for j in (0..256).rev() { + i = j; + if naf[i] != 0 { + break; + } + } + + let mut r = ProjectivePoint::identity(); + + loop { + let mut t = r.double(); + + match naf[i].cmp(&0) { + Ordering::Greater => t = &t.as_extended() + &table.select(naf[i] as usize), + Ordering::Less => t = &t.as_extended() - &table.select(-naf[i] as usize), + Ordering::Equal => {} + } + + r = t.as_projective(); + + if i == 0 { + break; + } + i -= 1; + } + + r.as_extended() +} diff --git a/curve25519-dalek/src/backend/vector/scalar_mul/variable_base.rs b/curve25519-dalek/src/backend/vector/scalar_mul/variable_base.rs index 9f924f286..9be2c01d9 100644 --- a/curve25519-dalek/src/backend/vector/scalar_mul/variable_base.rs +++ b/curve25519-dalek/src/backend/vector/scalar_mul/variable_base.rs @@ -6,6 +6,8 @@ )] pub mod spec { + use core::cmp::Ordering; + #[for_target_feature("avx2")] use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; @@ -15,7 +17,7 @@ pub mod spec { use crate::edwards::EdwardsPoint; use crate::scalar::Scalar; use crate::traits::Identity; - use crate::window::LookupTable; + use crate::window::{LookupTable, NafLookupTable5}; /// Perform constant-time, variable-base scalar multiplication. pub fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { @@ -41,4 +43,42 @@ pub mod spec { } Q.into() } + + /// Perform variable-time, variable-base scalar multiplication. + pub fn vartime_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { + let naf = scalar.non_adjacent_form(5); + let table = NafLookupTable5::::from(point); + + // Find starting index + let mut i: usize = 255; + for j in (0..256).rev() { + i = j; + if naf[i] != 0 { + break; + } + } + + let mut Q = ExtendedPoint::identity(); + + loop { + Q = Q.double(); + + match naf[i].cmp(&0) { + Ordering::Greater => { + Q = &Q + &table.select(naf[i] as usize); + } + Ordering::Less => { + Q = &Q - &table.select(-naf[i] as usize); + } + Ordering::Equal => {} + } + + if i == 0 { + break; + } + i -= 1; + } + + Q.into() + } } diff --git a/curve25519-dalek/src/edwards.rs b/curve25519-dalek/src/edwards.rs index 856fac12f..d46c430ae 100644 --- a/curve25519-dalek/src/edwards.rs +++ b/curve25519-dalek/src/edwards.rs @@ -1255,7 +1255,8 @@ impl EdwardsPoint { /// assert_eq!((P+Q).is_torsion_free(), false); /// ``` pub fn is_torsion_free(&self) -> bool { - (self * constants::BASEPOINT_ORDER_PRIVATE).is_identity() + crate::backend::vartime_variable_base_mul(self, &constants::BASEPOINT_ORDER_PRIVATE) + .is_identity() } }