rust_elgamal/
encrypt.rs

1// Encryption logic for rust-elgamal.
2// Copyright 2021 Eleanor McMurtry
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use core::fmt::{Formatter, Debug};
17
18use curve25519_dalek::constants::{RISTRETTO_BASEPOINT_TABLE, RISTRETTO_BASEPOINT_POINT};
19use curve25519_dalek::ristretto::RistrettoPoint;
20use curve25519_dalek::scalar::Scalar;
21use curve25519_dalek::traits::MultiscalarMul;
22use rand_core::{RngCore, CryptoRng};
23
24#[cfg(feature = "enable-serde")]
25use serde::{Serialize, Deserialize};
26
27use crate::{Ciphertext, DecryptionKey};
28
29/// An ElGamal encryption key (also called a public key in other implementations).
30/// To create a new encryption key, see [DecryptionKey](crate::decrypt::DecryptionKey).
31#[derive(Copy, Clone, Eq, PartialEq)]
32#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
33pub struct EncryptionKey(pub(crate) RistrettoPoint);
34
35impl EncryptionKey {
36    /// Encrypt `mG` with a randomly-generated blinding factor, where `G` is the group generator.
37    ///
38    /// This is computationally intensive to decrypt to the original scalar, and not relevant to
39    /// the majority of users. This function takes advantage of a fast implementation for multiple
40    /// multiplications in `curve25519-dalek`.
41    ///
42    /// # Example
43    ///
44    /// ```rust
45    /// use rand::rngs::StdRng;
46    /// use rand::SeedableRng;
47    /// use rust_elgamal::{DecryptionKey, GENERATOR_TABLE, Scalar};
48    ///
49    /// let mut rng = StdRng::from_entropy();
50    /// let dec_key = DecryptionKey::new(&mut rng);
51    /// let enc_key = dec_key.encryption_key();
52    ///
53    /// let m = Scalar::from(5u32);
54    /// let encrypted = enc_key.exp_encrypt(m, &mut rng);
55    /// ```
56    pub fn exp_encrypt<R: RngCore + CryptoRng>(&self, m: Scalar, rng: &mut R) -> Ciphertext {
57        self.exp_encrypt_with(m, Scalar::random(rng))
58    }
59
60    /// Encrypt `mG` with the blinding factor `r`, where `G` is the group generator.
61    ///
62    /// This is computationally intensive to decrypt to the original scalar, and not relevant to
63    /// the majority of users. This function takes advantage of a fast implementation for multiple
64    /// multiplications in `curve25519-dalek`.
65    ///
66    /// # Example
67    ///
68    /// ```rust
69    /// use rand::rngs::StdRng;
70    /// use rand::SeedableRng;
71    /// use rust_elgamal::{DecryptionKey, GENERATOR_TABLE, Scalar};
72    ///
73    /// let mut rng = StdRng::from_entropy();
74    /// let dec_key = DecryptionKey::new(&mut rng);
75    /// let enc_key = dec_key.encryption_key();
76    ///
77    /// let m = Scalar::from(5u32);
78    /// let r = Scalar::from(10u32);
79    /// let encrypted = enc_key.exp_encrypt_with(m, r);
80    /// ```
81    pub fn exp_encrypt_with(&self, m: Scalar, r: Scalar) -> Ciphertext {
82        let c1 = &r * &RISTRETTO_BASEPOINT_TABLE;
83        // mG + rY
84        let c2 = RistrettoPoint::multiscalar_mul(&[m, r], &[RISTRETTO_BASEPOINT_POINT, self.0]);
85        Ciphertext(c1, c2)
86    }
87
88    /// Encrypt the curve point `m` with a randomly-generated blinding factor.
89    ///
90    /// # Example
91    ///
92    /// ```rust
93    /// use rand::rngs::StdRng;
94    /// use rand::SeedableRng;
95    /// use rust_elgamal::{DecryptionKey, GENERATOR_TABLE, Scalar};
96    ///
97    /// let mut rng = StdRng::from_entropy();
98    /// let dec_key = DecryptionKey::new(&mut rng);
99    /// let enc_key = dec_key.encryption_key();
100    ///
101    /// let m = &Scalar::from(5u32) * &GENERATOR_TABLE;
102    /// let encrypted = enc_key.encrypt(m, &mut rng);
103    /// ```
104    pub fn encrypt<R: RngCore + CryptoRng>(&self, m: RistrettoPoint, rng: &mut R) -> Ciphertext {
105        self.encrypt_with(m, Scalar::random(rng))
106    }
107
108    /// Encrypt the curve point `m` with the blinding factor `r`.
109    ///
110    /// # Example
111    ///
112    /// ```rust
113    /// use rand::rngs::StdRng;
114    /// use rand::SeedableRng;
115    /// use rust_elgamal::{DecryptionKey, GENERATOR_TABLE, Scalar};
116    ///
117    /// let mut rng = StdRng::from_entropy();
118    /// let dec_key = DecryptionKey::new(&mut rng);
119    /// let enc_key = dec_key.encryption_key();
120    ///
121    /// let m = &Scalar::from(5u32) * &GENERATOR_TABLE;
122    /// let r = Scalar::from(10u32);
123    /// let encrypted = enc_key.encrypt_with(m, r);
124    /// ```
125    pub fn encrypt_with(&self, m: RistrettoPoint, r: Scalar) -> Ciphertext {
126        let c1 = &r * &RISTRETTO_BASEPOINT_TABLE;
127        let c2 = m + r * &self.0;
128        Ciphertext(c1, c2)
129    }
130
131    /// Re-randomise the ciphertext `ct` with a randomly-generated blinding factor.
132    /// This will generate a new encryption of the same curve point.
133    ///
134    /// # Example
135    ///
136    /// ```rust
137    /// use rand::rngs::StdRng;
138    /// use rand::SeedableRng;
139    /// use rust_elgamal::{DecryptionKey, GENERATOR_TABLE, Scalar};
140    ///
141    /// let mut rng = StdRng::from_entropy();
142    /// let dec_key = DecryptionKey::new(&mut rng);
143    /// let enc_key = dec_key.encryption_key();
144    ///
145    /// let m = &Scalar::from(5u32) * &GENERATOR_TABLE;
146    /// let ct1 = enc_key.encrypt(m, &mut rng);
147    /// let ct2 = enc_key.rerandomise(ct1, &mut rng);
148    /// assert_eq!(dec_key.decrypt(ct1), dec_key.decrypt(ct2));
149    /// ```
150    pub fn rerandomise<R: RngCore + CryptoRng>(&self, ct: Ciphertext, rng: &mut R) -> Ciphertext {
151        self.rerandomise_with(ct, Scalar::random(rng))
152    }
153
154
155    /// Re-randomise the ciphertext `ct` with the provided blinding factor.
156    /// This will generate a new encryption of the same curve point.
157    ///
158    /// # Example
159    ///
160    /// ```rust
161    /// use rand::rngs::StdRng;
162    /// use rand::SeedableRng;
163    /// use rust_elgamal::{DecryptionKey, GENERATOR_TABLE, Scalar};
164    ///
165    /// let mut rng = StdRng::from_entropy();
166    /// let dec_key = DecryptionKey::new(&mut rng);
167    /// let enc_key = dec_key.encryption_key();
168    ///
169    /// let m = &Scalar::from(5u32) * &GENERATOR_TABLE;
170    /// let ct1 = enc_key.encrypt(m, &mut rng);
171    ///
172    /// let r = Scalar::from(10u32);
173    /// let ct2 = enc_key.rerandomise_with(ct1, r);
174    ///
175    /// assert_eq!(dec_key.decrypt(ct1), dec_key.decrypt(ct2));
176    /// ```
177    pub fn rerandomise_with(&self, ct: Ciphertext, r: Scalar) -> Ciphertext {
178        let c1 = ct.0 + &r * &RISTRETTO_BASEPOINT_TABLE;
179        let c2 = ct.1 + &self.0 * r;
180        Ciphertext(c1, c2)
181    }
182}
183
184impl Debug for EncryptionKey {
185    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
186        write!(f, "EncryptionKey({:?})", self.0.compress())
187    }
188}
189
190// Conversion traits
191
192impl From<DecryptionKey> for EncryptionKey {
193    fn from(dk: DecryptionKey) -> Self {
194        dk.ek
195    }
196}
197
198impl From<RistrettoPoint> for EncryptionKey {
199    fn from(y: RistrettoPoint) -> Self {
200        Self(y)
201    }
202}
203
204impl AsRef<RistrettoPoint> for EncryptionKey {
205    fn as_ref(&self) -> &RistrettoPoint {
206        &self.0
207    }
208}