scicrypt_he/cryptosystems/
integer_el_gamal.rs1use crate::constants::{SAFE_PRIME_1024, SAFE_PRIME_2048, SAFE_PRIME_3072};
17use scicrypt_bigint::UnsignedInteger;
18use scicrypt_traits::cryptosystems::{
19 Associable, AsymmetricCryptosystem, DecryptionKey, EncryptionKey,
20};
21use scicrypt_traits::homomorphic::HomomorphicMultiplication;
22use scicrypt_traits::randomness::GeneralRng;
23use scicrypt_traits::randomness::SecureRng;
24use scicrypt_traits::security::BitsOfSecurity;
25use serde::{Deserialize, Serialize};
26
27#[derive(Clone)]
48pub struct IntegerElGamal {
49 modulus: UnsignedInteger,
50}
51
52#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
54pub struct IntegerElGamalPK {
55 pub h: UnsignedInteger,
57 pub modulus: UnsignedInteger,
59}
60
61#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
63pub struct IntegerElGamalCiphertext {
64 pub c1: UnsignedInteger,
66 pub c2: UnsignedInteger,
68}
69
70impl Associable<IntegerElGamalPK> for IntegerElGamalCiphertext {}
71
72pub struct IntegerElGamalSK {
74 pub(crate) key: UnsignedInteger,
75}
76
77impl AsymmetricCryptosystem for IntegerElGamal {
78 type PublicKey = IntegerElGamalPK;
79 type SecretKey = IntegerElGamalSK;
80
81 fn setup(security_param: &BitsOfSecurity) -> Self {
83 let public_key_len = security_param.to_public_key_bit_length();
84 IntegerElGamal {
85 modulus: UnsignedInteger::from_string_leaky(
86 match public_key_len {
87 1024 => SAFE_PRIME_1024.to_string(),
88 2048 => SAFE_PRIME_2048.to_string(),
89 3072 => SAFE_PRIME_3072.to_string(),
90 _ => panic!("No parameters available for this security parameter"),
91 },
92 16,
93 public_key_len,
94 ),
95 }
96 }
97
98 fn generate_keys<R: SecureRng>(
110 &self,
111 rng: &mut GeneralRng<R>,
112 ) -> (IntegerElGamalPK, IntegerElGamalSK) {
113 let q = &self.modulus >> 1;
114 let secret_key = UnsignedInteger::random_below(&q, rng);
115 let public_key = UnsignedInteger::from(4u64).pow_mod(&secret_key, &self.modulus);
116
117 (
118 IntegerElGamalPK {
119 h: public_key,
120 modulus: self.modulus.clone(),
121 },
122 IntegerElGamalSK { key: secret_key },
123 )
124 }
125}
126
127impl EncryptionKey for IntegerElGamalPK {
128 type Input = UnsignedInteger;
129 type Plaintext = UnsignedInteger;
130 type Ciphertext = IntegerElGamalCiphertext;
131 type Randomness = UnsignedInteger;
132
133 fn encrypt_without_randomness(&self, plaintext: &Self::Plaintext) -> Self::Ciphertext {
134 IntegerElGamalCiphertext {
135 c1: UnsignedInteger::new(1, 1),
136 c2: plaintext.clone() % &self.modulus,
137 }
138 }
139
140 fn randomize<R: SecureRng>(
141 &self,
142 ciphertext: Self::Ciphertext,
143 rng: &mut GeneralRng<R>,
144 ) -> Self::Ciphertext {
145 let q = &self.modulus >> 1;
146 let y = UnsignedInteger::random_below(&q, rng);
147
148 self.randomize_with(ciphertext, &y)
149 }
150
151 fn randomize_with(
152 &self,
153 ciphertext: Self::Ciphertext,
154 randomness: &Self::Randomness,
155 ) -> Self::Ciphertext {
156 IntegerElGamalCiphertext {
157 c1: &ciphertext.c1 * &UnsignedInteger::from(4u64).pow_mod(randomness, &self.modulus),
158 c2: (&ciphertext.c2 * &self.h.pow_mod(randomness, &self.modulus)) % &self.modulus,
159 }
160 }
161}
162
163impl DecryptionKey<IntegerElGamalPK> for IntegerElGamalSK {
164 fn decrypt_raw(
180 &self,
181 public_key: &IntegerElGamalPK,
182 ciphertext: &IntegerElGamalCiphertext,
183 ) -> UnsignedInteger {
184 (&ciphertext.c2
185 * &ciphertext
186 .c1
187 .pow_mod(&self.key, &public_key.modulus)
188 .invert(&public_key.modulus)
189 .unwrap())
190 % &public_key.modulus
191 }
192
193 fn decrypt_identity_raw(
194 &self,
195 public_key: &IntegerElGamalPK,
196 ciphertext: &<IntegerElGamalPK as EncryptionKey>::Ciphertext,
197 ) -> bool {
198 ciphertext.c2 == ciphertext.c1.pow_mod(&self.key, &public_key.modulus)
199 }
200}
201
202impl HomomorphicMultiplication for IntegerElGamalPK {
203 fn mul(
204 &self,
205 ciphertext_a: &Self::Ciphertext,
206 ciphertext_b: &Self::Ciphertext,
207 ) -> Self::Ciphertext {
208 IntegerElGamalCiphertext {
209 c1: (&ciphertext_a.c1 * &ciphertext_b.c1) % &self.modulus,
210 c2: (&ciphertext_a.c2 * &ciphertext_b.c2) % &self.modulus,
211 }
212 }
213
214 fn pow(&self, ciphertext: &Self::Ciphertext, input: &Self::Input) -> Self::Ciphertext {
215 IntegerElGamalCiphertext {
216 c1: ciphertext.c1.pow_mod(input, &self.modulus),
217 c2: ciphertext.c2.pow_mod(input, &self.modulus),
218 }
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use crate::cryptosystems::integer_el_gamal::IntegerElGamal;
225 use rand_core::OsRng;
226 use scicrypt_bigint::UnsignedInteger;
227 use scicrypt_traits::cryptosystems::{
228 Associable, AsymmetricCryptosystem, DecryptionKey, EncryptionKey,
229 };
230 use scicrypt_traits::randomness::GeneralRng;
231
232 #[test]
233 fn test_encrypt_decrypt_generator() {
234 let mut rng = GeneralRng::new(OsRng);
235
236 let el_gamal = IntegerElGamal::setup(&Default::default());
237 let (pk, sk) = el_gamal.generate_keys(&mut rng);
238
239 let ciphertext = pk.encrypt(&UnsignedInteger::from(19u64), &mut rng);
240
241 assert_eq!(UnsignedInteger::from(19u64), sk.decrypt(&ciphertext));
242 }
243
244 #[test]
245 fn test_encrypt_decrypt_identity() {
246 let mut rng = GeneralRng::new(OsRng);
247
248 let el_gamal = IntegerElGamal::setup(&Default::default());
249 let (pk, sk) = el_gamal.generate_keys(&mut rng);
250
251 let ciphertext = pk.encrypt(&UnsignedInteger::from(1), &mut rng);
252
253 assert!(sk.decrypt_identity(&ciphertext));
254 }
255
256 #[test]
257 fn test_homomorphic_mul() {
258 let mut rng = GeneralRng::new(OsRng);
259
260 let el_gamal = IntegerElGamal::setup(&Default::default());
261 let (pk, sk) = el_gamal.generate_keys(&mut rng);
262
263 let ciphertext_a = pk.encrypt(&UnsignedInteger::from(7u64), &mut rng);
264 let ciphertext_b = pk.encrypt(&UnsignedInteger::from(7u64), &mut rng);
265 let ciphertext_twice = &ciphertext_a * &ciphertext_b;
266
267 assert_eq!(UnsignedInteger::from(49u64), sk.decrypt(&ciphertext_twice));
268 }
269
270 #[test]
271 fn test_homomorphic_scalar_pow() {
272 let mut rng = GeneralRng::new(OsRng);
273
274 let el_gamal = IntegerElGamal::setup(&Default::default());
275 let (pk, sk) = el_gamal.generate_keys(&mut rng);
276
277 let ciphertext = pk.encrypt(&UnsignedInteger::from(9u64), &mut rng);
278 let ciphertext_twice = ciphertext.pow(&UnsignedInteger::from(4u64));
279
280 assert_eq!(
281 UnsignedInteger::from(6561u64),
282 sk.decrypt(&ciphertext_twice)
283 );
284 }
285
286 #[test]
287 fn randomize() {
288 let mut rng = GeneralRng::new(OsRng);
289
290 let el_gamal = IntegerElGamal::setup(&Default::default());
291 let (pk, sk) = el_gamal.generate_keys(&mut rng);
292
293 let ciphertext = pk.encrypt_raw(&UnsignedInteger::from(15u64), &mut rng);
294 let ciphertext_randomized = pk.randomize(ciphertext.clone(), &mut rng);
295
296 assert_ne!(ciphertext, ciphertext_randomized);
297
298 assert_eq!(
299 UnsignedInteger::from(15u64),
300 sk.decrypt(&ciphertext_randomized.associate(&pk))
301 );
302 }
303}