elliptic_curve/
ops.rs

1//! Traits for arithmetic operations on elliptic curve field elements.
2
3pub use bigint::{Invert, Reduce};
4pub use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Shr, ShrAssign, Sub, SubAssign};
5
6use crate::CurveGroup;
7use core::iter;
8use ff::Field;
9use subtle::{Choice, CtOption};
10
11#[cfg(feature = "alloc")]
12use alloc::{borrow::ToOwned, vec::Vec};
13
14/// Perform a batched inversion on a sequence of field elements (i.e. base field elements or scalars)
15/// at an amortized cost that should be practically as efficient as a single inversion.
16pub trait BatchInvert<FieldElements: ?Sized> {
17    /// The output of batch inversion. A container of field elements.
18    type Output;
19
20    /// Invert a batch of field elements.
21    fn batch_invert(field_elements: FieldElements) -> <Self as BatchInvert<FieldElements>>::Output;
22}
23
24impl<const N: usize, T> BatchInvert<[T; N]> for T
25where
26    T: Field,
27{
28    type Output = CtOption<[Self; N]>;
29
30    fn batch_invert(mut field_elements: [Self; N]) -> CtOption<[Self; N]> {
31        let mut field_elements_pad = [Self::default(); N];
32        let inversion_succeeded =
33            invert_batch_internal(&mut field_elements, &mut field_elements_pad, invert);
34
35        CtOption::new(field_elements, inversion_succeeded)
36    }
37}
38
39#[cfg(feature = "alloc")]
40impl<'this, T> BatchInvert<&'this mut [Self]> for T
41where
42    T: Field,
43{
44    type Output = CtOption<&'this mut [Self]>;
45
46    fn batch_invert(field_elements: &'this mut [Self]) -> CtOption<&'this mut [Self]> {
47        let mut field_elements_pad: Vec<Self> = vec![Self::default(); field_elements.len()];
48        let inversion_succeeded =
49            invert_batch_internal(field_elements, &mut field_elements_pad, invert);
50
51        CtOption::new(field_elements, inversion_succeeded)
52    }
53}
54
55#[cfg(feature = "alloc")]
56impl<T> BatchInvert<&[Self]> for T
57where
58    T: Field,
59{
60    type Output = CtOption<Vec<Self>>;
61
62    fn batch_invert(field_elements: &[Self]) -> CtOption<Vec<Self>> {
63        let mut field_elements: Vec<Self> = field_elements.to_owned();
64        let mut field_elements_pad: Vec<Self> = vec![Self::default(); field_elements.len()];
65        let inversion_succeeded =
66            invert_batch_internal(&mut field_elements, &mut field_elements_pad, invert);
67
68        CtOption::new(field_elements, inversion_succeeded)
69    }
70}
71
72#[cfg(feature = "alloc")]
73impl<T> BatchInvert<Vec<Self>> for T
74where
75    T: Field,
76{
77    type Output = CtOption<Vec<Self>>;
78
79    fn batch_invert(mut field_elements: Vec<Self>) -> CtOption<Vec<Self>> {
80        let mut field_elements_pad: Vec<Self> = vec![Self::default(); field_elements.len()];
81        let inversion_succeeded =
82            invert_batch_internal(&mut field_elements, &mut field_elements_pad, invert);
83
84        CtOption::new(field_elements, inversion_succeeded)
85    }
86}
87
88fn invert<T: Field>(scalar: T) -> (T, Choice) {
89    let scalar = scalar.invert();
90    let choice = scalar.is_some();
91    let scalar = scalar.unwrap_or(T::default());
92
93    (scalar, choice)
94}
95
96/// Implements "Montgomery's trick", a trick for computing many modular inverses at once.
97///
98/// "Montgomery's trick" works by reducing the problem of computing `n` inverses
99/// to computing a single inversion, plus some storage and `O(n)` extra multiplications.
100///
101/// See: https://iacr.org/archive/pkc2004/29470042/29470042.pdf section 2.2.
102pub(crate) fn invert_batch_internal<T: Copy + Mul<Output = T> + MulAssign>(
103    field_elements: &mut [T],
104    field_elements_pad: &mut [T],
105    invert: fn(T) -> (T, Choice),
106) -> Choice {
107    let batch_size = field_elements.len();
108    if batch_size != field_elements_pad.len() {
109        return Choice::from(0);
110    }
111    if batch_size == 0 {
112        return Choice::from(1);
113    }
114
115    let mut acc = field_elements[0];
116    field_elements_pad[0] = acc;
117
118    for (field_element, field_element_pad) in field_elements
119        .iter_mut()
120        .zip(field_elements_pad.iter_mut())
121        .skip(1)
122    {
123        // $ a_n = a_{n-1}*x_n $
124        acc *= *field_element;
125        *field_element_pad = acc;
126    }
127
128    let (mut acc, choice) = invert(acc);
129
130    // Shift the iterator by one element back. The one we are skipping is served in `acc`.
131    let field_elements_pad = field_elements_pad
132        .iter()
133        .rev()
134        .skip(1)
135        .map(Some)
136        .chain(iter::once(None));
137
138    for (field_element, field_element_pad) in
139        field_elements.iter_mut().rev().zip(field_elements_pad)
140    {
141        if let Some(field_element_pad) = field_element_pad {
142            // Store in a temporary so we can overwrite `field_element`.
143            // $ a_{n-1} = {a_n}^{-1}*x_n $
144            let tmp = acc * *field_element;
145            // $ {x_n}^{-1} = a_{n}^{-1}*a_{n-1} $
146            *field_element = acc * *field_element_pad;
147            acc = tmp;
148        } else {
149            *field_element = acc;
150        }
151    }
152
153    choice
154}
155
156/// Linear combination.
157///
158/// This trait enables optimized implementations of linear combinations (e.g. Shamir's Trick).
159///
160/// It's generic around `PointsAndScalars` to allow overlapping impls. For example, const generic
161/// impls can use the input size to determine the size needed to store temporary variables.
162pub trait LinearCombination<PointsAndScalars>: CurveGroup
163where
164    PointsAndScalars: AsRef<[(Self, Self::Scalar)]> + ?Sized,
165{
166    /// Calculates `x1 * k1 + ... + xn * kn`.
167    fn lincomb(points_and_scalars: &PointsAndScalars) -> Self {
168        points_and_scalars
169            .as_ref()
170            .iter()
171            .copied()
172            .map(|(point, scalar)| point * scalar)
173            .sum()
174    }
175}
176
177/// Modular reduction to a non-zero output.
178///
179/// This trait is primarily intended for use by curve implementations such
180/// as the `k256` and `p256` crates.
181///
182/// End users should use the [`Reduce`] impl on
183/// [`NonZeroScalar`][`crate::NonZeroScalar`] instead.
184pub trait ReduceNonZero<T>: Reduce<T> {
185    /// Perform a modular reduction, returning a field element.
186    fn reduce_nonzero(n: &T) -> Self;
187}