elliptic_curve/
ops.rs

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