Skip to content

Commit 2af0350

Browse files
authored
elliptic_curve: add BatchInvert and BatchNormalize traits (#1376)
1 parent d743482 commit 2af0350

File tree

3 files changed

+132
-1
lines changed

3 files changed

+132
-1
lines changed

elliptic-curve/src/arithmetic.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,14 @@ pub trait PrimeCurveArithmetic:
8484
/// Prime order elliptic curve group.
8585
type CurveGroup: group::prime::PrimeCurve<Affine = <Self as CurveArithmetic>::AffinePoint>;
8686
}
87+
88+
/// Normalize point(s) in projective representation by converting them to their affine ones.
89+
pub trait BatchNormalize<Points>: group::Curve {
90+
/// The output of the batch normalization; a container of affine points.
91+
type Output: AsRef<[Self::AffineRepr]>;
92+
93+
/// Perform a batched conversion to affine representation on a sequence of projective points
94+
/// at an amortized cost that should be practically as efficient as a single conversion.
95+
/// Internally, implementors should rely upon `InvertBatch`.
96+
fn batch_normalize(points: Points) -> <Self as BatchNormalize<Points>>::Output;
97+
}

elliptic-curve/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pub use zeroize;
127127
#[cfg(feature = "arithmetic")]
128128
pub use {
129129
crate::{
130-
arithmetic::{CurveArithmetic, PrimeCurveArithmetic},
130+
arithmetic::{BatchNormalize, CurveArithmetic, PrimeCurveArithmetic},
131131
point::{AffinePoint, ProjectivePoint},
132132
public_key::PublicKey,
133133
scalar::{NonZeroScalar, Scalar},

elliptic-curve/src/ops.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ pub use core::ops::{Add, AddAssign, Mul, Neg, Shr, ShrAssign, Sub, SubAssign};
44

55
use crypto_bigint::Integer;
66
use group::Group;
7+
use subtle::{Choice, ConditionallySelectable, CtOption};
8+
9+
#[cfg(feature = "alloc")]
10+
use alloc::vec::Vec;
711

812
/// Perform an inversion on a field element (i.e. base field element or scalar)
913
pub trait Invert {
@@ -25,6 +29,122 @@ pub trait Invert {
2529
}
2630
}
2731

32+
/// Perform a batched inversion on a sequence of field elements (i.e. base field elements or scalars)
33+
/// at an amortized cost that should be practically as efficient as a single inversion.
34+
pub trait BatchInvert<FieldElements>: Invert {
35+
/// The output of batch inversion. A container of field elements.
36+
type Output;
37+
38+
/// Invert a batch of field elements.
39+
fn batch_invert(field_elements: FieldElements) -> <Self as BatchInvert<FieldElements>>::Output;
40+
}
41+
42+
impl<const N: usize, T> BatchInvert<&[T; N]> for T
43+
where
44+
T: Invert<Output = CtOption<Self>>
45+
+ Mul<Self, Output = Self>
46+
+ Copy
47+
+ Default
48+
+ ConditionallySelectable,
49+
{
50+
type Output = CtOption<[Self; N]>;
51+
52+
fn batch_invert(field_elements: &[Self; N]) -> <Self as BatchInvert<&[T; N]>>::Output {
53+
let mut field_elements_multiples = [Self::default(); N];
54+
let mut field_elements_multiples_inverses = [Self::default(); N];
55+
let mut field_elements_inverses = [Self::default(); N];
56+
57+
let inversion_succeeded = invert_batch_internal(
58+
field_elements,
59+
&mut field_elements_multiples,
60+
&mut field_elements_multiples_inverses,
61+
&mut field_elements_inverses,
62+
);
63+
64+
CtOption::new(field_elements_inverses, inversion_succeeded)
65+
}
66+
}
67+
68+
#[cfg(feature = "alloc")]
69+
impl<T> BatchInvert<&[T]> for T
70+
where
71+
T: Invert<Output = CtOption<Self>>
72+
+ Mul<Self, Output = Self>
73+
+ Copy
74+
+ Default
75+
+ ConditionallySelectable,
76+
{
77+
type Output = CtOption<Vec<Self>>;
78+
79+
fn batch_invert(field_elements: &[Self]) -> <Self as BatchInvert<&[T]>>::Output {
80+
let mut field_elements_multiples: Vec<Self> = vec![Self::default(); field_elements.len()];
81+
let mut field_elements_multiples_inverses: Vec<Self> =
82+
vec![Self::default(); field_elements.len()];
83+
let mut field_elements_inverses: Vec<Self> = vec![Self::default(); field_elements.len()];
84+
85+
let inversion_succeeded = invert_batch_internal(
86+
field_elements,
87+
field_elements_multiples.as_mut(),
88+
field_elements_multiples_inverses.as_mut(),
89+
field_elements_inverses.as_mut(),
90+
);
91+
92+
CtOption::new(
93+
field_elements_inverses.into_iter().collect(),
94+
inversion_succeeded,
95+
)
96+
}
97+
}
98+
99+
/// Implements "Montgomery's trick", a trick for computing many modular inverses at once.
100+
///
101+
/// "Montgomery's trick" works by reducing the problem of computing `n` inverses
102+
/// to computing a single inversion, plus some storage and `O(n)` extra multiplications.
103+
///
104+
/// See: https://iacr.org/archive/pkc2004/29470042/29470042.pdf section 2.2.
105+
fn invert_batch_internal<
106+
T: Invert<Output = CtOption<T>> + Mul<T, Output = T> + Default + ConditionallySelectable,
107+
>(
108+
field_elements: &[T],
109+
field_elements_multiples: &mut [T],
110+
field_elements_multiples_inverses: &mut [T],
111+
field_elements_inverses: &mut [T],
112+
) -> Choice {
113+
let batch_size = field_elements.len();
114+
if batch_size == 0
115+
|| batch_size != field_elements_multiples.len()
116+
|| batch_size != field_elements_multiples_inverses.len()
117+
{
118+
return Choice::from(0);
119+
}
120+
121+
field_elements_multiples[0] = field_elements[0];
122+
for i in 1..batch_size {
123+
// $ a_n = a_{n-1}*x_n $
124+
field_elements_multiples[i] = field_elements_multiples[i - 1] * field_elements[i];
125+
}
126+
127+
field_elements_multiples[batch_size - 1]
128+
.invert()
129+
.map(|multiple_of_inverses_of_all_field_elements| {
130+
field_elements_multiples_inverses[batch_size - 1] =
131+
multiple_of_inverses_of_all_field_elements;
132+
for i in (1..batch_size).rev() {
133+
// $ a_{n-1} = {a_n}^{-1}*x_n $
134+
field_elements_multiples_inverses[i - 1] =
135+
field_elements_multiples_inverses[i] * field_elements[i];
136+
}
137+
138+
field_elements_inverses[0] = field_elements_multiples_inverses[0];
139+
for i in 1..batch_size {
140+
// $ {x_n}^{-1} = a_{n}^{-1}*a_{n-1} $
141+
field_elements_inverses[i] =
142+
field_elements_multiples_inverses[i] * field_elements_multiples[i - 1];
143+
}
144+
})
145+
.is_some()
146+
}
147+
28148
/// Linear combination.
29149
///
30150
/// This trait enables crates to provide an optimized implementation of

0 commit comments

Comments
 (0)