diff --git a/elliptic-curve/src/arithmetic.rs b/elliptic-curve/src/arithmetic.rs index 7ef7fc53..de9ff6b3 100644 --- a/elliptic-curve/src/arithmetic.rs +++ b/elliptic-curve/src/arithmetic.rs @@ -84,3 +84,14 @@ pub trait PrimeCurveArithmetic: /// Prime order elliptic curve group. type CurveGroup: group::prime::PrimeCurve::AffinePoint>; } + +/// Normalize point(s) in projective representation by converting them to their affine ones. +pub trait BatchNormalize: group::Curve { + /// The output of the batch normalization; a container of affine points. + type Output: AsRef<[Self::AffineRepr]>; + + /// Perform a batched conversion to affine representation on a sequence of projective points + /// at an amortized cost that should be practically as efficient as a single conversion. + /// Internally, implementors should rely upon `InvertBatch`. + fn batch_normalize(points: Points) -> >::Output; +} diff --git a/elliptic-curve/src/lib.rs b/elliptic-curve/src/lib.rs index b0ad188d..389d9713 100644 --- a/elliptic-curve/src/lib.rs +++ b/elliptic-curve/src/lib.rs @@ -127,7 +127,7 @@ pub use zeroize; #[cfg(feature = "arithmetic")] pub use { crate::{ - arithmetic::{CurveArithmetic, PrimeCurveArithmetic}, + arithmetic::{BatchNormalize, CurveArithmetic, PrimeCurveArithmetic}, point::{AffinePoint, ProjectivePoint}, public_key::PublicKey, scalar::{NonZeroScalar, Scalar}, diff --git a/elliptic-curve/src/ops.rs b/elliptic-curve/src/ops.rs index b7e9e3d4..5edddc4e 100644 --- a/elliptic-curve/src/ops.rs +++ b/elliptic-curve/src/ops.rs @@ -4,6 +4,10 @@ pub use core::ops::{Add, AddAssign, Mul, Neg, Shr, ShrAssign, Sub, SubAssign}; use crypto_bigint::Integer; use group::Group; +use subtle::{Choice, ConditionallySelectable, CtOption}; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; /// Perform an inversion on a field element (i.e. base field element or scalar) pub trait Invert { @@ -25,6 +29,122 @@ pub trait Invert { } } +/// Perform a batched inversion on a sequence of field elements (i.e. base field elements or scalars) +/// at an amortized cost that should be practically as efficient as a single inversion. +pub trait BatchInvert: Invert { + /// The output of batch inversion. A container of field elements. + type Output; + + /// Invert a batch of field elements. + fn batch_invert(field_elements: FieldElements) -> >::Output; +} + +impl BatchInvert<&[T; N]> for T +where + T: Invert> + + Mul + + Copy + + Default + + ConditionallySelectable, +{ + type Output = CtOption<[Self; N]>; + + fn batch_invert(field_elements: &[Self; N]) -> >::Output { + let mut field_elements_multiples = [Self::default(); N]; + let mut field_elements_multiples_inverses = [Self::default(); N]; + let mut field_elements_inverses = [Self::default(); N]; + + let inversion_succeeded = invert_batch_internal( + field_elements, + &mut field_elements_multiples, + &mut field_elements_multiples_inverses, + &mut field_elements_inverses, + ); + + CtOption::new(field_elements_inverses, inversion_succeeded) + } +} + +#[cfg(feature = "alloc")] +impl BatchInvert<&[T]> for T +where + T: Invert> + + Mul + + Copy + + Default + + ConditionallySelectable, +{ + type Output = CtOption>; + + fn batch_invert(field_elements: &[Self]) -> >::Output { + let mut field_elements_multiples: Vec = vec![Self::default(); field_elements.len()]; + let mut field_elements_multiples_inverses: Vec = + vec![Self::default(); field_elements.len()]; + let mut field_elements_inverses: Vec = vec![Self::default(); field_elements.len()]; + + let inversion_succeeded = invert_batch_internal( + field_elements, + field_elements_multiples.as_mut(), + field_elements_multiples_inverses.as_mut(), + field_elements_inverses.as_mut(), + ); + + CtOption::new( + field_elements_inverses.into_iter().collect(), + inversion_succeeded, + ) + } +} + +/// Implements "Montgomery's trick", a trick for computing many modular inverses at once. +/// +/// "Montgomery's trick" works by reducing the problem of computing `n` inverses +/// to computing a single inversion, plus some storage and `O(n)` extra multiplications. +/// +/// See: https://iacr.org/archive/pkc2004/29470042/29470042.pdf section 2.2. +fn invert_batch_internal< + T: Invert> + Mul + Default + ConditionallySelectable, +>( + field_elements: &[T], + field_elements_multiples: &mut [T], + field_elements_multiples_inverses: &mut [T], + field_elements_inverses: &mut [T], +) -> Choice { + let batch_size = field_elements.len(); + if batch_size == 0 + || batch_size != field_elements_multiples.len() + || batch_size != field_elements_multiples_inverses.len() + { + return Choice::from(0); + } + + field_elements_multiples[0] = field_elements[0]; + for i in 1..batch_size { + // $ a_n = a_{n-1}*x_n $ + field_elements_multiples[i] = field_elements_multiples[i - 1] * field_elements[i]; + } + + field_elements_multiples[batch_size - 1] + .invert() + .map(|multiple_of_inverses_of_all_field_elements| { + field_elements_multiples_inverses[batch_size - 1] = + multiple_of_inverses_of_all_field_elements; + for i in (1..batch_size).rev() { + // $ a_{n-1} = {a_n}^{-1}*x_n $ + field_elements_multiples_inverses[i - 1] = + field_elements_multiples_inverses[i] * field_elements[i]; + } + + field_elements_inverses[0] = field_elements_multiples_inverses[0]; + for i in 1..batch_size { + // $ {x_n}^{-1} = a_{n}^{-1}*a_{n-1} $ + field_elements_inverses[i] = + field_elements_multiples_inverses[i] * field_elements_multiples[i - 1]; + } + }) + .is_some() +} + /// Linear combination. /// /// This trait enables crates to provide an optimized implementation of