scicrypt_he/threshold_cryptosystems/
paillier.rs1use rug::Integer;
2use scicrypt_bigint::UnsignedInteger;
3use scicrypt_numbertheory::gen_safe_prime;
4use scicrypt_traits::cryptosystems::{Associable, EncryptionKey};
5use scicrypt_traits::homomorphic::HomomorphicAddition;
6use scicrypt_traits::randomness::GeneralRng;
7use scicrypt_traits::randomness::SecureRng;
8use scicrypt_traits::security::BitsOfSecurity;
9use scicrypt_traits::threshold_cryptosystems::{
10 DecryptionShare, PartialDecryptionKey, TOfNCryptosystem,
11};
12use scicrypt_traits::DecryptionError;
13use std::ops::Rem;
14
15use crate::cryptosystems::paillier::PaillierCiphertext;
16
17#[derive(Copy, Clone)]
20pub struct ThresholdPaillier {
21 modulus_size: u32,
22}
23
24#[derive(PartialEq, Eq, Debug)]
26pub struct ThresholdPaillierPK {
27 generator: UnsignedInteger,
28 modulus: UnsignedInteger,
29 theta: UnsignedInteger,
30 delta: UnsignedInteger,
31}
32
33pub struct ThresholdPaillierSK {
35 id: i32,
36 key: UnsignedInteger,
37}
38
39pub struct ThresholdPaillierShare {
41 id: i32,
42 share: UnsignedInteger,
43}
44
45impl TOfNCryptosystem for ThresholdPaillier {
46 type PublicKey = ThresholdPaillierPK;
47 type SecretKey = ThresholdPaillierSK;
48
49 fn setup(security_param: &BitsOfSecurity) -> Self {
50 ThresholdPaillier {
51 modulus_size: security_param.to_public_key_bit_length(),
52 }
53 }
54
55 fn generate_keys<R: SecureRng>(
56 &self,
57 threshold_t: usize,
58 key_count_n: usize,
59 rng: &mut GeneralRng<R>,
60 ) -> (ThresholdPaillierPK, Vec<ThresholdPaillierSK>) {
61 let prime_p = gen_safe_prime(self.modulus_size / 2, rng);
62 let prime_q = gen_safe_prime(self.modulus_size / 2, rng);
63
64 let subprime_p = &prime_p >> 1;
65 let subprime_q = &prime_q >> 1;
66
67 let modulus = &prime_p * &prime_q;
68 let sub_modulus = &subprime_p * &subprime_q;
69
70 let generator = modulus.clone() + 1;
71
72 let beta = UnsignedInteger::random_below(&modulus, rng);
73 let theta = (&sub_modulus * &beta) % &modulus;
74 let delta = UnsignedInteger::factorial_leaky(key_count_n as u64);
75
76 let m_times_n = &sub_modulus * &modulus;
77 let coefficients: Vec<UnsignedInteger> = (0..(threshold_t - 1))
78 .map(|_| UnsignedInteger::random_below(&m_times_n, rng))
79 .collect();
80
81 let partial_keys: Vec<ThresholdPaillierSK> = (1..=key_count_n)
82 .map(|i| {
83 let mut key = &beta * &sub_modulus;
84
85 for j in 0..(threshold_t - 1) {
86 key += &((&coefficients[j as usize]
87 * &UnsignedInteger::from(i.pow((j + 1) as u32) as u64))
88 % &m_times_n);
89 }
90
91 ThresholdPaillierSK {
92 id: i as i32,
93 key: key % &m_times_n,
94 }
95 })
96 .collect();
97
98 (
99 ThresholdPaillierPK {
100 generator,
101 modulus,
102 theta,
103 delta,
104 },
105 partial_keys,
106 )
107 }
108}
109
110impl Associable<ThresholdPaillierPK> for PaillierCiphertext {}
111
112impl EncryptionKey for ThresholdPaillierPK {
113 type Input = UnsignedInteger;
114 type Plaintext = UnsignedInteger;
115 type Ciphertext = PaillierCiphertext;
116 type Randomness = UnsignedInteger;
117
118 fn encrypt_without_randomness(&self, plaintext: &Self::Plaintext) -> Self::Ciphertext {
119 let n_squared = self.modulus.square();
120 PaillierCiphertext {
121 c: self.generator.pow_mod(plaintext, &n_squared),
122 }
123 }
124
125 fn randomize<R: SecureRng>(
126 &self,
127 ciphertext: PaillierCiphertext,
128 rng: &mut GeneralRng<R>,
129 ) -> PaillierCiphertext
130 where
131 Self: Sized,
132 {
133 let n_squared = self.modulus.square();
134 let r = UnsignedInteger::random_below(&n_squared, rng);
135
136 self.randomize_with(ciphertext, &r)
137 }
138
139 fn randomize_with(
140 &self,
141 ciphertext: Self::Ciphertext,
142 randomness: &Self::Randomness,
143 ) -> Self::Ciphertext {
144 let n_squared = self.modulus.square();
145 let randomizer = randomness.pow_mod(&self.modulus, &n_squared);
146
147 PaillierCiphertext {
148 c: (&ciphertext.c * &randomizer) % &n_squared,
149 }
150 }
151}
152
153impl HomomorphicAddition for ThresholdPaillierPK {
154 fn add(
155 &self,
156 ciphertext_a: &Self::Ciphertext,
157 ciphertext_b: &Self::Ciphertext,
158 ) -> Self::Ciphertext {
159 PaillierCiphertext {
160 c: (&ciphertext_a.c * &ciphertext_b.c) % &self.modulus.square(),
161 }
162 }
163
164 fn mul_constant(&self, ciphertext: &Self::Ciphertext, input: &Self::Input) -> Self::Ciphertext {
165 let modulus = self.modulus.square();
166
167 PaillierCiphertext {
168 c: ciphertext.c.pow_mod(input, &modulus),
169 }
170 }
171
172 fn sub(
173 &self,
174 ciphertext_a: &Self::Ciphertext,
175 ciphertext_b: &Self::Ciphertext,
176 ) -> Self::Ciphertext {
177 let modulus = self.modulus.square();
178 PaillierCiphertext {
179 c: (&ciphertext_a.c * &ciphertext_b.c.clone().invert(&modulus).unwrap()) % &modulus,
180 }
181 }
182
183 fn add_constant(
184 &self,
185 ciphertext: &Self::Ciphertext,
186 constant: &Self::Plaintext,
187 ) -> Self::Ciphertext {
188 let modulus = self.modulus.square();
189 PaillierCiphertext {
190 c: (&ciphertext.c * &self.generator.pow_mod(constant, &modulus)) % &modulus,
191 }
192 }
193
194 fn sub_constant(
195 &self,
196 ciphertext: &Self::Ciphertext,
197 constant: &Self::Plaintext,
198 ) -> Self::Ciphertext {
199 let modulus = self.modulus.square();
200 PaillierCiphertext {
201 c: (&ciphertext.c
202 * &self
203 .generator
204 .pow_mod(constant, &modulus)
205 .invert(&modulus)
206 .unwrap())
207 % &modulus,
208 }
209 }
210}
211
212impl PartialDecryptionKey<ThresholdPaillierPK> for ThresholdPaillierSK {
213 type DecryptionShare = ThresholdPaillierShare;
214
215 fn partial_decrypt_raw(
216 &self,
217 public_key: &ThresholdPaillierPK,
218 ciphertext: &PaillierCiphertext,
219 ) -> ThresholdPaillierShare {
220 let n_squared = public_key.modulus.square();
221 ThresholdPaillierShare {
222 id: self.id,
223 share: ciphertext.c.pow_mod(
224 &(&(&UnsignedInteger::new(2, 2) * &public_key.delta) * &self.key),
225 &n_squared,
226 ),
227 }
228 }
229}
230
231impl DecryptionShare<ThresholdPaillierPK> for ThresholdPaillierShare {
232 fn combine(
233 decryption_shares: &[Self],
234 public_key: &ThresholdPaillierPK,
235 ) -> Result<UnsignedInteger, DecryptionError> {
236 let lambdas: Vec<Integer> = (0..decryption_shares.len())
237 .map(|i| {
238 let mut lambda = public_key.delta.clone().to_rug();
239
240 for i_prime in 0..decryption_shares.len() {
241 if i == i_prime {
242 continue;
243 }
244
245 if decryption_shares[i].id == decryption_shares[i_prime].id {
246 continue;
247 }
248
249 lambda *= decryption_shares[i_prime].id;
250 lambda /= decryption_shares[i_prime].id - decryption_shares[i].id;
251 }
252
253 lambda
254 })
255 .collect();
256
257 let n_squared = public_key.modulus.square();
258
259 let mut product = UnsignedInteger::from(1u64);
260
261 for (share, lambda) in decryption_shares.iter().zip(lambdas) {
262 let lambda_is_negative = lambda < 0;
263
264 let mut part = share
265 .share
266 .pow_mod(&(UnsignedInteger::from(lambda.abs() * 2u64)), &n_squared);
267
268 if lambda_is_negative {
269 part = part.invert(&n_squared).unwrap();
270 }
271
272 product = (&product * &part) % &n_squared;
273 }
274
275 let inverse = (&(&UnsignedInteger::from(4u64) * &public_key.delta.square())
276 * &public_key.theta)
277 .rem(&public_key.modulus)
278 .invert(&public_key.modulus)
279 .unwrap();
280
281 product -= 1;
282
283 Result::Ok((&(product / &public_key.modulus) * &inverse) % &public_key.modulus)
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use crate::threshold_cryptosystems::paillier::{ThresholdPaillier, ThresholdPaillierShare};
290 use rand_core::OsRng;
291 use scicrypt_bigint::UnsignedInteger;
292 use scicrypt_traits::cryptosystems::{Associable, EncryptionKey};
293 use scicrypt_traits::randomness::GeneralRng;
294 use scicrypt_traits::security::BitsOfSecurity;
295 use scicrypt_traits::threshold_cryptosystems::{
296 DecryptionShare, PartialDecryptionKey, TOfNCryptosystem,
297 };
298
299 #[test]
300 fn test_encrypt_decrypt_2_of_3() {
301 let mut rng = GeneralRng::new(OsRng);
302
303 let paillier = ThresholdPaillier::setup(&BitsOfSecurity::ToyParameters);
304 let (pk, sks) = paillier.generate_keys(2, 3, &mut rng);
305
306 let ciphertext = pk.encrypt(&UnsignedInteger::from(19u64), &mut rng);
307
308 let share_1 = sks[0].partial_decrypt(&ciphertext);
309 let share_3 = sks[2].partial_decrypt(&ciphertext);
310
311 assert_eq!(
312 UnsignedInteger::from(19u64),
313 ThresholdPaillierShare::combine(&[share_1, share_3], &pk).unwrap()
314 );
315 }
316 #[test]
317 fn test_randomize() {
318 let mut rng = GeneralRng::new(OsRng);
319
320 let paillier = ThresholdPaillier::setup(&BitsOfSecurity::ToyParameters);
321 let (pk, sks) = paillier.generate_keys(2, 3, &mut rng);
322
323 let ciphertext = pk.encrypt_raw(&UnsignedInteger::from(42), &mut rng);
324 let ciphertext_randomized = pk.randomize(ciphertext.clone(), &mut rng);
325
326 assert_ne!(ciphertext, ciphertext_randomized);
327
328 let ciphertext_associated = ciphertext_randomized.associate(&pk);
329 let share_1 = sks[0].partial_decrypt(&ciphertext_associated);
330 let share_3 = sks[2].partial_decrypt(&ciphertext_associated);
331
332 assert_eq!(
333 UnsignedInteger::from(42),
334 ThresholdPaillierShare::combine(&[share_1, share_3], &pk).unwrap()
335 );
336 }
337}