Skip to main content

generic_ec_core/
lib.rs

1//! This crate contains core traits for [`generic-ec`](https://docs.rs/generic-ec) crate.
2//! You should only need these traits if you implement your own [`Curve`] instance.
3//! Otherwise, `generic-ec` API should suffice.
4
5#![no_std]
6#![cfg_attr(not(test), forbid(unused_crate_dependencies))]
7#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))]
8#![forbid(missing_docs)]
9
10use core::fmt::Debug;
11use core::hash::Hash;
12
13use generic_array::{ArrayLength, GenericArray};
14use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
15use zeroize::Zeroize;
16
17pub mod coords;
18
19/// Elliptic curve
20///
21/// This trait contains all the low-level curve implementation logic: scalar, point arithmetics,
22/// encoding and etc.
23pub trait Curve: Debug + Copy + Eq + Ord + Hash + Default + Sync + Send + 'static {
24    /// Curve name
25    const CURVE_NAME: &'static str;
26
27    /// Type that represents a curve point
28    type Point: Additive
29        + From<CurveGenerator>
30        + Zero
31        + Zeroize
32        + OnCurve
33        + SmallFactor
34        + Copy
35        + Eq
36        + ConstantTimeEq
37        + ConditionallySelectable
38        + Default
39        + CompressedEncoding<Bytes = Self::CompressedPointArray>
40        + UncompressedEncoding<Bytes = Self::UncompressedPointArray>
41        + Decode
42        + Unpin
43        + Sync
44        + Send;
45    /// Type that represents a curve scalar
46    type Scalar: Additive
47        + Multiplicative<Self::Scalar, Output = Self::Scalar>
48        + Multiplicative<CurveGenerator, Output = Self::Point>
49        + Multiplicative<Self::Point, Output = Self::Point>
50        + Invertible
51        + Zero
52        + One
53        + FromUniformBytes
54        + SamplableVartime
55        + Zeroize
56        + Copy
57        + Eq
58        + ConstantTimeEq
59        + ConditionallySelectable
60        + Default
61        + IntegerEncoding<Bytes = Self::ScalarArray>
62        + Unpin
63        + Sync
64        + Send;
65
66    /// Byte array that fits the whole bytes representation of compressed point
67    type CompressedPointArray: ByteArray;
68    /// Byte array that fits the whole bytes representation of uncompressed point
69    type UncompressedPointArray: ByteArray;
70    /// Byte array that fits the whole bytes representation of a scalar
71    type ScalarArray: ByteArray;
72    /// Byte array that fits the whole bytes representation of a coordinate
73    ///
74    /// If a curve doesn't expose point coordinates, it may be `[u8; 0]`
75    type CoordinateArray: ByteArray;
76}
77
78/// Type for which addition is defined
79pub trait Additive {
80    /// Computes `a + b`
81    fn add(a: &Self, b: &Self) -> Self;
82    /// Computes `a - b`
83    fn sub(a: &Self, b: &Self) -> Self;
84    /// Computes `-a`
85    fn negate(x: &Self) -> Self;
86
87    /// Takes `x`, returns `x + x`
88    ///
89    /// This can be more efficient than calling [`Self::add(x, x)`](Self::add)
90    fn double(x: &Self) -> Self
91    where
92        Self: Sized,
93    {
94        Self::add(x, x)
95    }
96}
97
98/// Type for which multiplication is defined
99pub trait Multiplicative<Rhs> {
100    /// Type of multiplication output
101    type Output;
102    /// Computes `a * b`
103    fn mul(a: &Self, b: &Rhs) -> Self::Output;
104}
105
106/// Type for which invert function is defined
107pub trait Invertible
108where
109    Self: Sized,
110{
111    /// Inverts $x$, returns $x^{-1}$ such that $x \cdot x^{-1} = 1$
112    fn invert(x: &Self) -> CtOption<Self>;
113}
114
115/// Type that has zero value (additive identity)
116pub trait Zero {
117    /// Constructs zero value of `Self`
118    fn zero() -> Self;
119    /// Checks (in constant-time) if `x` is zero
120    fn is_zero(x: &Self) -> Choice;
121}
122
123/// Type that has "one" value (multiplicative identity)
124pub trait One {
125    /// Constructs one value of `Self`
126    fn one() -> Self;
127    /// Checks (in constant-time) if `x` is one
128    fn is_one(x: &Self) -> Choice;
129}
130
131/// Uniform instance of the type can be derived from uniformly distributed byte array
132pub trait FromUniformBytes {
133    /// Byte array that can be converted into instance of `Self` via [`FromUniformBytes::from_uniform_bytes`]
134    type Bytes: ByteArray;
135
136    /// Maps uniformly distributed bytes array to uniformly distributed instance of `Self`.
137    ///
138    /// Instead of taking a source of randomness directly, this implementation takes a byte array, that was
139    /// populated from the source of randomness, and outputs a random element.
140    ///
141    /// ## Guarantees
142    /// When `bytes` are uniformly distributed, output distribution must be indistinguishable from uniform,
143    /// with bias respective to the security level of the curve, meaning that any instance of the type can
144    /// appear with equal probability.
145    ///
146    /// Implementation is reproducible: the same input `bytes` give the same output on all platforms.
147    ///
148    /// Implementation is constant-time: there's no branching on the input.
149    ///
150    /// ## Implementation details
151    /// This trait is required to be implemented for scalars. It's recommended to take approach described in
152    /// "5. Hashing to Finite Field" of [RFC9380]:
153    ///
154    /// 1. Take a uniform bytestring of `L` bytes, where
155    ///    ```text
156    ///    L = ceil((ceil(log2(q)) + k) / 8)
157    ///    ```
158    ///
159    ///    `q` is prime (sub)group order, and `k` is target security level in bits
160    /// 2. Interpret the bytestring as big-endian/little-endian encoding of the integer, and reduce it modulo `q`
161    ///
162    /// The output is then guaranteed to have a bias at most 2^-k.
163    ///
164    /// [RFC9380]: https://www.rfc-editor.org/rfc/rfc9380.html#name-hashing-to-a-finite-field
165    fn from_uniform_bytes(bytes: &Self::Bytes) -> Self;
166}
167
168/// Samples a uniform instance of the type from source of randomness
169pub trait SamplableVartime {
170    /// Samples a uniform instance of the type from source of randomness
171    ///
172    /// ## Guarantees
173    /// If output of provided PRNG is uniform, then the output of this function is uniformly
174    /// distributed (with bias respective to the security level of the curve), meaning that
175    /// any instance of the type can appear with equal probability.
176    ///
177    /// Implementation **is not** reproducible: it may lead to different results on different
178    /// platform and/or instances of the program even if the same `rng` is used with the same
179    /// seed.
180    ///
181    /// Implementation **is not** constant-time: it likely uses reject-sample strategy.
182    fn random_vartime(rng: &mut impl rand_core::RngCore) -> Self;
183}
184
185/// Checks whether the point is on curve
186pub trait OnCurve {
187    /// Checks whether the point is on curve
188    fn is_on_curve(&self) -> Choice;
189}
190
191/// Checks whether a point has small factor
192pub trait SmallFactor {
193    /// Checks whether a point has no small factor
194    fn is_torsion_free(&self) -> Choice;
195}
196
197/// Curve generator
198///
199/// Represents a curve generator. The curve point must implement `From<CurveGenerator>`.
200/// The curve scalar can be multiplied at `CurveGenerator`, implementation may be
201/// more efficient than a generic multiplication.
202pub struct CurveGenerator;
203
204/// Compressed encoding of the point
205pub trait CompressedEncoding
206where
207    Self: Sized,
208{
209    /// Byte array that fits the whole compressed point representation
210    type Bytes: ByteArray;
211
212    /// Encodes the point as bytes in compressed form
213    fn to_bytes_compressed(&self) -> Self::Bytes;
214}
215
216/// Uncompressed encoding of the point
217pub trait UncompressedEncoding
218where
219    Self: Sized,
220{
221    /// Byte array that fits the whole uncompressed point representation
222    type Bytes: ByteArray;
223
224    /// Encodes the point as bytes in uncompressed form
225    ///
226    /// Some curves may not have such thing as compressed and uncompressed forms.
227    /// For these curves, we `CompressedEncoding` and `UncompressedEncoding` should
228    /// return the same encoding.
229    fn to_bytes_uncompressed(&self) -> Self::Bytes;
230}
231
232/// Encodes an integer as bytes
233pub trait IntegerEncoding
234where
235    Self: Sized,
236{
237    /// Byte array that fits the whole encoded integer
238    type Bytes: ByteArray;
239
240    /// Encodes integer as bytes in big-endian byte order
241    fn to_be_bytes(&self) -> Self::Bytes;
242    /// Encodes integer as bytes in little-endian byte order
243    fn to_le_bytes(&self) -> Self::Bytes;
244
245    /// Decodes integer encoded as bytes in big-endian bytes order
246    ///
247    /// Returns `None` if the bytes don't correspond to a valid integer.
248    fn from_be_bytes_exact(bytes: &Self::Bytes) -> Option<Self>;
249    /// Decodes integer encoded as bytes in little-endian bytes order
250    ///
251    /// Returns `None` if the bytes don't correspond to a valid integer.
252    fn from_le_bytes_exact(bytes: &Self::Bytes) -> Option<Self>;
253
254    /// Interprets `bytes` as big-endian encoding of an integer. Returns integer mod curve (prime) order.
255    fn from_be_bytes_mod_order(bytes: &[u8]) -> Self;
256    /// Interprets `bytes` as little-endian encoding of an integer. Returns integer mod curve (prime) order.
257    fn from_le_bytes_mod_order(bytes: &[u8]) -> Self;
258}
259
260/// Decodes a point from its compressed or uncompressed representation
261pub trait Decode: Sized {
262    /// Decodes a point from its compressed or uncompressed representation
263    fn decode(bytes: &[u8]) -> Option<Self>;
264}
265
266/// Error type
267pub struct Error;
268
269/// Byte array
270pub trait ByteArray: AsRef<[u8]> + AsMut<[u8]> + Clone + Send + Sync + 'static {
271    /// New byte array of zeroes
272    ///
273    /// Alternative to [`Default`] that is not implemented for generic `[T; N]`
274    /// (see [tracking issue](https://github.com/rust-lang/rust/issues/61415))
275    fn zeroes() -> Self;
276}
277
278impl<const N: usize> ByteArray for [u8; N] {
279    fn zeroes() -> Self {
280        [0; N]
281    }
282}
283
284impl<N: ArrayLength<u8>> ByteArray for GenericArray<u8, N> {
285    fn zeroes() -> Self {
286        GenericArray::default()
287    }
288}
289
290/// Reduces an integer represented as array of `N` bytes modulo curve (prime) order
291pub trait Reduce<const N: usize> {
292    /// Interprets `bytes` as big-endian encoding of an integer, returns this
293    /// integer modulo curve (prime) order
294    fn from_be_array_mod_order(bytes: &[u8; N]) -> Self;
295    /// Interprets `bytes` as little-endian encoding of an integer, returns this
296    /// integer modulo curve (prime) order
297    fn from_le_array_mod_order(bytes: &[u8; N]) -> Self;
298}
299
300/// Marker trait for curves whose underlying implementation doesn't allow
301/// representing invalid points.
302/// # Safety
303/// Safe to implement when the checks for valid points always return `true`.
304/// Those checks are:
305/// - [`OnCurve::is_on_curve`]
306/// - [`SmallFactor::is_torsion_free`]
307pub unsafe trait NoInvalidPoints {}