openzeppelin_crypto/curve/mod.rs
1//! This module provides common operations to work with elliptic curves.
2//!
3//! Abstractions and api in this module are similar to Arkworks Algebra [ark-ec
4//! library].
5//!
6//! [ark-ec library]: https://github.com/arkworks-rs/algebra/tree/master/ec
7
8use alloc::vec::Vec;
9use core::{
10 fmt::{Debug, Display},
11 hash::Hash,
12 ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
13};
14
15use num_traits::Zero;
16use zeroize::Zeroize;
17
18use crate::{
19 bits::BitIteratorBE,
20 field::{group::AdditiveGroup, prime::PrimeField, Field},
21};
22
23mod helpers;
24pub mod sw;
25pub mod te;
26
27/// Elliptic curves can be represented via different "models" with varying
28/// efficiency properties.
29///
30/// [`CurveConfig`] bundles together the types that are common
31/// to all models of the given curve, namely the [`Self::BaseField`] over which
32/// the curve is defined, and the [`Self::ScalarField`] defined by the
33/// appropriate prime-order subgroup of the curve.
34pub trait CurveConfig: Send + Sync + Sized + 'static {
35 /// Base field that the curve is defined over.
36 type BaseField: Field;
37 /// Finite prime field corresponding to an appropriate prime-order subgroup
38 /// of the curve group.
39 type ScalarField: PrimeField;
40
41 /// The cofactor of this curve, represented as a sequence of little-endian
42 /// limbs.
43 const COFACTOR: &'static [u64];
44
45 /// The inverse of the cofactor.
46 const COFACTOR_INV: Self::ScalarField;
47
48 /// Returns `true` if the cofactor is one.
49 fn cofactor_is_one() -> bool {
50 let mut iter = Self::COFACTOR.iter();
51 matches!(iter.next(), Some(1)) && iter.all(Zero::is_zero)
52 }
53}
54
55/// Represents (elements of) a group of prime order `r`.
56pub trait PrimeGroup: AdditiveGroup<Scalar = Self::ScalarField> {
57 /// The scalar field `F_r`, where `r` is the order of this group.
58 type ScalarField: PrimeField;
59
60 /// Returns a fixed generator of this group.
61 #[must_use]
62 fn generator() -> Self;
63
64 /// Performs scalar multiplication of this element.
65 #[must_use]
66 fn mul_bigint(&self, other: impl BitIteratorBE) -> Self;
67
68 /// Computes `other * self`, where `other` is a *big-endian*
69 /// bit representation of some integer.
70 #[must_use]
71 fn mul_bits_be(&self, other: impl Iterator<Item = bool>) -> Self {
72 let mut res = Self::zero();
73 for b in other.skip_while(|b| !b) {
74 // skip leading zeros
75 res.double_in_place();
76 if b {
77 res += self;
78 }
79 }
80 res
81 }
82}
83
84/// An opaque representation of an elliptic curve group element that is suitable
85/// for efficient group arithmetic.
86///
87/// The point is guaranteed to be in the correct prime order subgroup.
88pub trait CurveGroup:
89 PrimeGroup
90 + Add<Self::Affine, Output = Self>
91 + AddAssign<Self::Affine>
92 + Sub<Self::Affine, Output = Self>
93 + SubAssign<Self::Affine>
94 + From<Self::Affine>
95 + Into<Self::Affine>
96 + core::iter::Sum<Self::Affine>
97 + for<'a> core::iter::Sum<&'a Self::Affine>
98{
99 /// Associated configuration for this curve.
100 type Config: CurveConfig<
101 ScalarField = Self::ScalarField,
102 BaseField = Self::BaseField,
103 >;
104
105 /// The field over which this curve is defined.
106 type BaseField: Field;
107
108 /// The affine representation of this element.
109 type Affine: AffineRepr<
110 Config = Self::Config,
111 Group = Self,
112 ScalarField = Self::ScalarField,
113 BaseField = Self::BaseField,
114 > + From<Self>
115 + Into<Self>;
116
117 /// Type representing an element of the full elliptic curve group, not just
118 /// the prime order subgroup.
119 type FullGroup;
120
121 /// Normalizes a slice of group elements into affine.
122 #[must_use]
123 fn normalize_batch(v: &[Self]) -> Vec<Self::Affine>;
124
125 /// Converts `self` into the affine representation.
126 fn into_affine(self) -> Self::Affine {
127 self.into()
128 }
129}
130
131/// The canonical representation of an elliptic curve group element.
132/// This should represent the affine coordinates of the point corresponding
133/// to this group element.
134///
135/// The point is guaranteed to be in the correct prime order subgroup.
136pub trait AffineRepr:
137 Eq
138 + 'static
139 + Sized
140 + Copy
141 + Clone
142 + Default
143 + Send
144 + Sync
145 + Hash
146 + Debug
147 + Display
148 + Zeroize
149 + Neg
150 + From<<Self as AffineRepr>::Group>
151 + Into<<Self as AffineRepr>::Group>
152 + Add<Self, Output = Self::Group>
153 + for<'a> Add<&'a Self, Output = Self::Group>
154 + Add<Self::Group, Output = Self::Group>
155 + for<'a> Add<&'a Self::Group, Output = Self::Group>
156 + Sub<Self, Output = Self::Group>
157 + for<'a> Sub<&'a Self, Output = Self::Group>
158 + Sub<Self::Group, Output = Self::Group>
159 + for<'a> Sub<&'a Self::Group, Output = Self::Group>
160 + Mul<Self::ScalarField, Output = Self::Group>
161 + for<'a> Mul<&'a Self::ScalarField, Output = Self::Group>
162{
163 /// Associated configuration for this curve.
164 type Config: CurveConfig<
165 ScalarField = Self::ScalarField,
166 BaseField = Self::BaseField,
167 >;
168
169 /// Finite prime field corresponding to an appropriate prime-order subgroup
170 /// of the curve group.
171 type ScalarField: PrimeField;
172
173 /// Base field that the curve is defined over.
174 type BaseField: Field;
175
176 /// The projective representation of points on this curve.
177 type Group: CurveGroup<
178 Config = Self::Config,
179 Affine = Self,
180 ScalarField = Self::ScalarField,
181 BaseField = Self::BaseField,
182 > + From<Self>
183 + Into<Self>
184 + MulAssign<Self::ScalarField>; // needed due to https://github.com/rust-lang/rust/issues/69640
185
186 /// Returns the x and y coordinates of this affine point.
187 fn xy(&self) -> Option<(Self::BaseField, Self::BaseField)>;
188
189 /// Returns the x coordinate of this affine point.
190 fn x(&self) -> Option<Self::BaseField> {
191 self.xy().map(|(x, _)| x)
192 }
193
194 /// Returns the y coordinate of this affine point.
195 fn y(&self) -> Option<Self::BaseField> {
196 self.xy().map(|(_, y)| y)
197 }
198
199 /// Returns the point at infinity.
200 fn zero() -> Self;
201
202 /// Is `self` the point at infinity?
203 fn is_zero(&self) -> bool {
204 self.xy().is_none()
205 }
206
207 /// Returns a fixed generator of unknown exponent.
208 #[must_use]
209 fn generator() -> Self;
210
211 /// Converts self into the projective representation.
212 fn into_group(self) -> Self::Group {
213 self.into()
214 }
215
216 /// Performs scalar multiplication of this element with mixed addition.
217 #[must_use]
218 fn mul_bigint(&self, by: impl BitIteratorBE) -> Self::Group;
219
220 /// Performs cofactor clearing.
221 /// The default method is simply to multiply by the cofactor.
222 /// For some curve families more efficient methods exist.
223 #[must_use]
224 fn clear_cofactor(&self) -> Self;
225
226 /// Multiplies this element by the cofactor and output the
227 /// resulting projective element.
228 #[must_use]
229 fn mul_by_cofactor_to_group(&self) -> Self::Group;
230
231 /// Multiplies this element by the cofactor.
232 #[must_use]
233 fn mul_by_cofactor(&self) -> Self {
234 self.mul_by_cofactor_to_group().into()
235 }
236
237 /// Multiplies this element by the inverse of the cofactor in
238 /// `Self::ScalarField`.
239 #[must_use]
240 fn mul_by_cofactor_inv(&self) -> Self {
241 self.mul_bigint(Self::Config::COFACTOR_INV.into_bigint()).into()
242 }
243}
244
245/// Efficiently computes inverses of non-zero elements in the slice.
246///
247/// Uses Montgomery's trick to compute multiple inverses with fewer field
248/// operations.
249/// Zero elements remain unchanged.
250///
251/// # Arguments
252///
253/// * `v` - Mutable slice of field elements for in-place inversion.
254pub fn batch_inversion<F: Field>(v: &mut [F]) {
255 batch_inversion_and_mul(v, &F::one());
256}
257
258/// Efficiently computes `coeff * v_i^(-1)` for each non-zero element.
259///
260/// Optimizes batch inversion by multiplying each result by a coefficient.
261/// Implements Montgomery's trick in two passes to minimize field inversions.
262/// Zero elements remain unchanged.
263///
264/// # Arguments
265///
266/// * `v` - Mutable slice for in-place computation.
267/// * `coeff` - Coefficient to multiply each inverse by.
268fn batch_inversion_and_mul<F: Field>(v: &mut [F], coeff: &F) {
269 // Montgomery's Trick and Fast Implementation of Masked AES
270 // Genelle, Prouff and Quisquater
271 // Section 3.2
272 // but with an optimization to multiply every element in the returned vector
273 // by coeff.
274
275 // First pass: compute [a, ab, abc, ...]
276 let mut tmp = F::one();
277 let prod: Vec<_> = v
278 .iter()
279 .filter(|f| !f.is_zero())
280 .map(|f| {
281 tmp *= f;
282 tmp
283 })
284 .collect();
285
286 // Invert `tmp`.
287 tmp = tmp.inverse().expect("should not be zero");
288
289 // Multiply product by coeff, so coeff will scale all inverses.
290 tmp *= coeff;
291
292 // Second pass: iterate backwards to compute inverses
293 for (f, s) in v
294 .iter_mut()
295 // Backwards
296 .rev()
297 // Ignore normalized elements
298 .filter(|f| !f.is_zero())
299 // Backwards, skip the last element, fill in `one()` for the last term.
300 .zip(prod.into_iter().rev().skip(1).chain([F::one()]))
301 {
302 // tmp := tmp * f; f := tmp * s = 1/f
303 let new_tmp = tmp * *f;
304 *f = tmp * s;
305 tmp = new_tmp;
306 }
307}
308
309#[cfg(test)]
310mod test {
311 use alloc::{vec, vec::Vec};
312
313 use crate::{
314 curve::batch_inversion, field::instance::FpBN256, fp_from_num,
315 };
316
317 #[test]
318 fn batch_inversion_works() {
319 let mut v: Vec<FpBN256> = vec![
320 fp_from_num!("3423432434"),
321 fp_from_num!("342"),
322 fp_from_num!("343443534234234"),
323 ];
324
325 let expected_v: Vec<FpBN256> = vec![
326 fp_from_num!("3537284798280370862969965094761407641979943185930201162008302715970127239037"),
327 fp_from_num!("19520216596230932581243139626618330122828219713821317177859509581595384769483"),
328 fp_from_num!("13917121828095828097189447294655626368886333473718821676252546722946587466026"),
329 ];
330
331 batch_inversion(&mut v);
332
333 assert_eq!(v, expected_v);
334 }
335
336 #[test]
337 fn batch_inversion_remains_zeroes() {
338 let mut v: Vec<FpBN256> = vec![
339 fp_from_num!("0"),
340 fp_from_num!("342"),
341 fp_from_num!("343443534234234"),
342 ];
343 batch_inversion(&mut v);
344
345 assert!(v[0].is_zero());
346 }
347}