elastic_elgamal/group/
mod.rs

1//! Traits and implementations for prime-order groups in which
2//! the [decisional Diffie–Hellman][DDH] (DDH), [computational Diffie–Hellman][CDH] (CDH)
3//! and [discrete log][DLP] (DL) problems are believed to be hard.
4//!
5//! (Decisional Diffie–Hellman assumption is considered stronger than both CDH and DL,
6//! so if DDH is believed to hold for a certain group, it should be good to go.)
7//!
8//! Such groups can be applied for ElGamal encryption and other cryptographic protocols
9//! from this crate.
10//!
11//! [DDH]: https://en.wikipedia.org/wiki/Decisional_Diffie%E2%80%93Hellman_assumption
12//! [CDH]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_problem
13//! [DLP]: https://en.wikipedia.org/wiki/Discrete_logarithm
14
15use core::{fmt, ops, str};
16
17use elliptic_curve::{
18    rand_core::{CryptoRng, RngCore, SeedableRng},
19    zeroize::Zeroize,
20};
21use merlin::Transcript;
22use rand_chacha::ChaChaRng;
23
24#[cfg(any(feature = "curve25519-dalek", feature = "curve25519-dalek-ng"))]
25mod curve25519;
26mod generic;
27#[cfg(any(feature = "curve25519-dalek", feature = "curve25519-dalek-ng"))]
28mod ristretto;
29
30pub use self::generic::Generic;
31#[cfg(any(feature = "curve25519-dalek", feature = "curve25519-dalek-ng"))]
32pub use self::{curve25519::Curve25519Subgroup, ristretto::Ristretto};
33
34/// Provides an arbitrary number of random bytes.
35///
36/// Unlike [`RngCore::fill_bytes()`], a single provider can only be used once.
37pub struct RandomBytesProvider<'a> {
38    transcript: &'a mut Transcript,
39    label: &'static [u8],
40}
41
42impl fmt::Debug for RandomBytesProvider<'_> {
43    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44        let label = str::from_utf8(self.label).unwrap_or("(non-utf8 label)");
45        formatter
46            .debug_struct("RandomBytesProvider")
47            .field("label", &label)
48            .finish()
49    }
50}
51
52impl<'a> RandomBytesProvider<'a> {
53    pub(crate) fn new(transcript: &'a mut Transcript, label: &'static [u8]) -> Self {
54        Self { transcript, label }
55    }
56
57    /// Writes random bytes into the specified buffer. As follows from the signature, this method
58    /// can only be called once for a provider instance.
59    pub fn fill_bytes(self, dest: &mut [u8]) {
60        self.transcript.challenge_bytes(self.label, dest);
61    }
62}
63
64/// Helper trait for [`Group`] that describes operations on group scalars.
65pub trait ScalarOps {
66    /// Scalar type. As per [`Group`] contract, scalars must form a prime field.
67    /// Arithmetic operations on scalars requested here must be constant-time.
68    type Scalar: Copy
69        + Default
70        + From<u64>
71        + From<Self::Scalar> // `PublicKey::encrypt()` doesn't work without this
72        + ops::Neg<Output = Self::Scalar>
73        + ops::Add<Output = Self::Scalar>
74        + for<'a> ops::Add<&'a Self::Scalar, Output = Self::Scalar>
75        + ops::Sub<Output = Self::Scalar>
76        + ops::Mul<Output = Self::Scalar>
77        + for<'a> ops::Mul<&'a Self::Scalar, Output = Self::Scalar>
78        + PartialEq
79        + Zeroize
80        + fmt::Debug;
81
82    /// Byte size of a serialized [`Self::Scalar`].
83    const SCALAR_SIZE: usize;
84
85    /// Generates a random scalar based on the provided CSPRNG. This operation
86    /// must be constant-time.
87    fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar;
88
89    /// Generates a scalar from a `source` of random bytes. This operation must be constant-time.
90    /// The `source` is guaranteed to return any necessary number of bytes.
91    ///
92    /// # Default implementation
93    ///
94    /// 1. Create a [ChaCha RNG] using 32 bytes read from `source` as the seed.
95    /// 2. Call [`Self::generate_scalar()`] with the created RNG.
96    ///
97    /// [ChaCha RNG]: https://docs.rs/rand_chacha/
98    fn scalar_from_random_bytes(source: RandomBytesProvider<'_>) -> Self::Scalar {
99        let mut rng_seed = <ChaChaRng as SeedableRng>::Seed::default();
100        source.fill_bytes(&mut rng_seed);
101        let mut rng = ChaChaRng::from_seed(rng_seed);
102        Self::generate_scalar(&mut rng)
103    }
104
105    /// Inverts the `scalar`, which is guaranteed to be non-zero. This operation does not
106    /// need to be constant-time.
107    fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
108
109    /// Inverts scalars in a batch. This operation does not need to be constant-time.
110    ///
111    /// # Default implementation
112    ///
113    /// Inverts every scalar successively.
114    fn invert_scalars(scalars: &mut [Self::Scalar]) {
115        for scalar in scalars {
116            *scalar = Self::invert_scalar(*scalar);
117        }
118    }
119
120    /// Serializes the scalar into the provided `buffer`, which is guaranteed to have length
121    /// [`Self::SCALAR_SIZE`].
122    fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8]);
123
124    /// Deserializes the scalar from `buffer`, which is guaranteed to have length
125    /// [`Self::SCALAR_SIZE`]. This method returns `None` if the buffer
126    /// does not correspond to a representation of a valid scalar.
127    fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar>;
128}
129
130/// Helper trait for [`Group`] that describes operations on group elements (i.e., EC points
131/// for elliptic curve groups).
132pub trait ElementOps: ScalarOps {
133    /// Element of the group. Arithmetic operations requested here (addition among
134    /// elements and multiplication by a `Scalar`) must be constant-time.
135    type Element: Copy
136        + ops::Add<Output = Self::Element>
137        + ops::Sub<Output = Self::Element>
138        + ops::Neg<Output = Self::Element>
139        + for<'a> ops::Mul<&'a Self::Scalar, Output = Self::Element>
140        + PartialEq
141        + fmt::Debug;
142
143    /// Byte size of a serialized [`Self::Element`].
144    const ELEMENT_SIZE: usize;
145
146    /// Returns the identity of the group (aka point at infinity for EC groups).
147    fn identity() -> Self::Element;
148
149    /// Checks if the specified element is the identity.
150    fn is_identity(element: &Self::Element) -> bool;
151
152    /// Returns the agreed-upon generator of the group.
153    fn generator() -> Self::Element;
154
155    /// Serializes `element` into the provided `buffer`, which is guaranteed to have length
156    /// [`Self::ELEMENT_SIZE`].
157    fn serialize_element(element: &Self::Element, output: &mut [u8]);
158
159    /// Deserializes an element from `buffer`, which is guaranteed to have length
160    /// [`Self::ELEMENT_SIZE`]. This method returns `None` if the buffer
161    /// does not correspond to a representation of a valid scalar.
162    fn deserialize_element(buffer: &[u8]) -> Option<Self::Element>;
163}
164
165/// Prime-order group in which the discrete log problem and decisional / computational
166/// Diffie–Hellman problems are believed to be hard.
167///
168/// Groups conforming to this trait can be used for ElGamal encryption and other
169/// cryptographic protocols defined in this crate.
170///
171/// This crate provides the following implementations of this trait:
172///
173/// - [`Curve25519Subgroup`], representation of a prime-order subgroup of Curve25519
174///   with the conventionally chosen generator.
175/// - [`Ristretto`], a transform of Curve25519 which eliminates its co-factor 8 with the help
176///   of the [eponymous technique][ristretto].
177/// - [`Generic`] implementation defined in terms of traits from the [`elliptic-curve`] crate.
178///   (For example, this means secp256k1 support via the [`k256`] crate.)
179///
180/// [ristretto]: https://ristretto.group/
181/// [`elliptic-curve`]: https://docs.rs/elliptic-curve/
182/// [`k256`]: https://docs.rs/k256/
183pub trait Group: Copy + ScalarOps + ElementOps + 'static {
184    /// Multiplies the provided scalar by [`ElementOps::generator()`]. This operation must be
185    /// constant-time.
186    ///
187    /// # Default implementation
188    ///
189    /// Implemented using [`Mul`](ops::Mul) (which is constant-time as per the [`ElementOps`]
190    /// contract).
191    fn mul_generator(k: &Self::Scalar) -> Self::Element {
192        Self::generator() * k
193    }
194
195    /// Multiplies the provided scalar by [`ElementOps::generator()`].
196    /// Unlike [`Self::mul_generator()`], this operation does not need to be constant-time;
197    /// thus, it may employ additional optimizations.
198    ///
199    /// # Default implementation
200    ///
201    /// Implemented by calling [`Self::mul_generator()`].
202    #[inline]
203    fn vartime_mul_generator(k: &Self::Scalar) -> Self::Element {
204        Self::mul_generator(k)
205    }
206
207    /// Multiplies provided `scalars` by `elements`. This operation must be constant-time
208    /// w.r.t. the given length of elements.
209    ///
210    /// # Default implementation
211    ///
212    /// Implemented by straightforward computations, which are constant-time as per
213    /// the [`ElementOps`] contract.
214    fn multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
215    where
216        I: IntoIterator<Item = &'a Self::Scalar>,
217        J: IntoIterator<Item = Self::Element>,
218    {
219        let mut output = Self::identity();
220        for (scalar, element) in scalars.into_iter().zip(elements) {
221            output = output + element * scalar;
222        }
223        output
224    }
225
226    /// Calculates `k * k_element + r * G`, where `G` is the group generator. This operation
227    /// does not need to be constant-time.
228    ///
229    /// # Default implementation
230    ///
231    /// Implemented by straightforward arithmetic.
232    fn vartime_double_mul_generator(
233        k: &Self::Scalar,
234        k_element: Self::Element,
235        r: &Self::Scalar,
236    ) -> Self::Element {
237        k_element * k + Self::generator() * r
238    }
239
240    /// Multiplies provided `scalars` by `elements`. Unlike [`Self::multi_mul()`],
241    /// this operation does not need to be constant-time; thus, it may employ
242    /// additional optimizations.
243    ///
244    /// # Default implementation
245    ///
246    /// Implemented by calling [`Self::multi_mul()`].
247    #[inline]
248    fn vartime_multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
249    where
250        I: IntoIterator<Item = &'a Self::Scalar>,
251        J: IntoIterator<Item = Self::Element>,
252    {
253        Self::multi_mul(scalars, elements)
254    }
255}