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