phantom-zone/src/bool/mp_api.rs
src/bool/mp_api.rsRaw
1use std::{cell::RefCell, ops::Mul, sync::OnceLock};
2
3use crate::{
4 backend::{ModularOpsU64, ModulusPowerOf2},
5 ntt::NttBackendU64,
6 random::{DefaultSecureRng, NewWithSeed},
7 utils::{Global, WithLocal},
8};
9
10use super::{evaluator::MultiPartyCrs, keys::*, parameters::*, ClientKey, ParameterSelector};
11
12pub(crate) type BoolEvaluator = super::evaluator::BoolEvaluator<
13 Vec<Vec<u64>>,
14 NttBackendU64,
15 ModularOpsU64<CiphertextModulus<u64>>,
16 ModulusPowerOf2<CiphertextModulus<u64>>,
17 ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>,
18>;
19
20thread_local! {
21 static BOOL_EVALUATOR: RefCell<Option<BoolEvaluator>> = RefCell::new(None);
22
23}
24static BOOL_SERVER_KEY: OnceLock<ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>> = OnceLock::new();
25
26static MULTI_PARTY_CRS: OnceLock<MultiPartyCrs<[u8; 32]>> = OnceLock::new();
27
28pub fn set_parameter_set(select: ParameterSelector) {
29 match select {
30 ParameterSelector::MultiPartyLessThanOrEqualTo16 => {
31 BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(SMALL_MP_BOOL_PARAMS)));
32 }
33 _ => {
34 panic!("Paramerters not supported")
35 }
36 }
37}
38
39pub fn set_mp_seed(seed: [u8; 32]) {
40 assert!(
41 MULTI_PARTY_CRS.set(MultiPartyCrs { seed: seed }).is_ok(),
42 "Attempted to set MP SEED twice."
43 )
44}
45
46pub fn gen_client_key() -> ClientKey {
47 BoolEvaluator::with_local(|e| e.client_key())
48}
49
50pub fn gen_mp_keys_phase1(
51 ck: &ClientKey,
52) -> CommonReferenceSeededCollectivePublicKeyShare<Vec<u64>, [u8; 32], BoolParameters<u64>> {
53 BoolEvaluator::with_local(|e| {
54 let pk_share = e.multi_party_public_key_share(MultiPartyCrs::global(), ck);
55 pk_share
56 })
57}
58
59pub fn gen_mp_keys_phase2<R, ModOp>(
60 ck: &ClientKey,
61 pk: &PublicKey<Vec<Vec<u64>>, R, ModOp>,
62) -> CommonReferenceSeededMultiPartyServerKeyShare<
63 Vec<Vec<u64>>,
64 BoolParameters<u64>,
65 MultiPartyCrs<[u8; 32]>,
66> {
67 BoolEvaluator::with_local_mut(|e| {
68 let server_key_share =
69 e.multi_party_server_key_share(MultiPartyCrs::global(), pk.key(), ck);
70 server_key_share
71 })
72}
73
74pub fn aggregate_public_key_shares(
75 shares: &[CommonReferenceSeededCollectivePublicKeyShare<
76 Vec<u64>,
77 [u8; 32],
78 BoolParameters<u64>,
79 >],
80) -> PublicKey<Vec<Vec<u64>>, DefaultSecureRng, ModularOpsU64<CiphertextModulus<u64>>> {
81 PublicKey::from(shares)
82}
83
84pub fn aggregate_server_key_shares(
85 shares: &[CommonReferenceSeededMultiPartyServerKeyShare<
86 Vec<Vec<u64>>,
87 BoolParameters<u64>,
88 MultiPartyCrs<[u8; 32]>,
89 >],
90) -> SeededMultiPartyServerKey<Vec<Vec<u64>>, MultiPartyCrs<[u8; 32]>, BoolParameters<u64>> {
91 BoolEvaluator::with_local(|e| e.aggregate_multi_party_server_key_shares(shares))
92}
93
94impl
95 SeededMultiPartyServerKey<
96 Vec<Vec<u64>>,
97 MultiPartyCrs<<DefaultSecureRng as NewWithSeed>::Seed>,
98 BoolParameters<u64>,
99 >
100{
101 pub fn set_server_key(&self) {
102 assert!(
103 BOOL_SERVER_KEY
104 .set(ShoupServerKeyEvaluationDomain::from(
105 ServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(self),
106 ))
107 .is_ok(),
108 "Attempted to set server key twice."
109 );
110 }
111}
112
113// MULTIPARTY CRS //
114impl Global for MultiPartyCrs<[u8; 32]> {
115 fn global() -> &'static Self {
116 MULTI_PARTY_CRS
117 .get()
118 .expect("Multi Party Common Reference String not set")
119 }
120}
121
122// BOOL EVALUATOR //
123impl WithLocal for BoolEvaluator {
124 fn with_local<F, R>(func: F) -> R
125 where
126 F: Fn(&Self) -> R,
127 {
128 BOOL_EVALUATOR.with_borrow(|s| func(s.as_ref().expect("Parameters not set")))
129 }
130
131 fn with_local_mut<F, R>(func: F) -> R
132 where
133 F: Fn(&mut Self) -> R,
134 {
135 BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
136 }
137
138 fn with_local_mut_mut<F, R>(func: &mut F) -> R
139 where
140 F: FnMut(&mut Self) -> R,
141 {
142 BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
143 }
144}
145
146pub(crate) type RuntimeServerKey = ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>;
147impl Global for RuntimeServerKey {
148 fn global() -> &'static Self {
149 BOOL_SERVER_KEY.get().expect("Server key not set!")
150 }
151}
152
153mod impl_enc_dec {
154 use crate::{
155 bool::evaluator::BoolEncoding,
156 pbs::{sample_extract, PbsInfo},
157 rgsw::public_key_encrypt_rlwe,
158 Encryptor, Matrix, MatrixEntity, MultiPartyDecryptor, RowEntity,
159 };
160 use itertools::Itertools;
161 use num_traits::{ToPrimitive, Zero};
162
163 use super::*;
164
165 type Mat = Vec<Vec<u64>>;
166
167 impl<Rng, ModOp> Encryptor<[bool], Vec<Mat>> for PublicKey<Mat, Rng, ModOp> {
168 fn encrypt(&self, m: &[bool]) -> Vec<Mat> {
169 BoolEvaluator::with_local(|e| {
170 DefaultSecureRng::with_local_mut(|rng| {
171 let parameters = e.parameters();
172 let ring_size = parameters.rlwe_n().0;
173
174 let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
175 .to_usize()
176 .unwrap();
177
178 // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
179 let rlwes = (0..rlwe_count)
180 .map(|index| {
181 let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
182 m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
183 .iter()
184 .enumerate()
185 .for_each(|(i, v)| {
186 if *v {
187 message[i] = parameters.rlwe_q().true_el()
188 } else {
189 message[i] = parameters.rlwe_q().false_el()
190 }
191 });
192
193 // encrypt message
194 let mut rlwe_out =
195 <Mat as MatrixEntity>::zeros(2, parameters.rlwe_n().0);
196
197 public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
198 &mut rlwe_out,
199 self.key(),
200 &message,
201 e.pbs_info().modop_rlweq(),
202 e.pbs_info().nttop_rlweq(),
203 rng,
204 );
205
206 rlwe_out
207 })
208 .collect_vec();
209 rlwes
210 })
211 })
212 }
213 }
214
215 impl<Rng, ModOp> Encryptor<bool, <Mat as Matrix>::R> for PublicKey<Mat, Rng, ModOp> {
216 fn encrypt(&self, m: &bool) -> <Mat as Matrix>::R {
217 let m = vec![*m];
218 let rlwe = &self.encrypt(m.as_slice())[0];
219 BoolEvaluator::with_local(|e| {
220 let mut lwe = <Mat as Matrix>::R::zeros(e.parameters().rlwe_n().0 + 1);
221 sample_extract(&mut lwe, rlwe, e.pbs_info().modop_rlweq(), 0);
222 lwe
223 })
224 }
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use itertools::Itertools;
231 use rand::{thread_rng, RngCore};
232
233 use crate::{bool::evaluator::BooleanGates, Encryptor, MultiPartyDecryptor};
234
235 use super::*;
236
237 #[test]
238 fn multi_party_bool_gates() {
239 set_parameter_set(ParameterSelector::MultiPartyLessThanOrEqualTo16);
240 let mut seed = [0u8; 32];
241 thread_rng().fill_bytes(&mut seed);
242 set_mp_seed(seed);
243
244 let parties = 2;
245 let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
246
247 // round 1
248 let pk_shares = cks.iter().map(|k| gen_mp_keys_phase1(k)).collect_vec();
249
250 // collective pk
251 let pk = aggregate_public_key_shares(&pk_shares);
252
253 // round 2
254 let server_key_shares = cks.iter().map(|k| gen_mp_keys_phase2(k, &pk)).collect_vec();
255
256 // server key
257 let server_key = aggregate_server_key_shares(&server_key_shares);
258 server_key.set_server_key();
259
260 let mut m0 = false;
261 let mut m1 = true;
262
263 let mut ct0 = pk.encrypt(&m0);
264 let mut ct1 = pk.encrypt(&m1);
265
266 for _ in 0..500 {
267 let ct_out =
268 BoolEvaluator::with_local_mut(|e| e.nand(&ct0, &ct1, RuntimeServerKey::global()));
269
270 let m_expected = !(m0 && m1);
271
272 let decryption_shares = cks
273 .iter()
274 .map(|k| k.gen_decryption_share(&ct_out))
275 .collect_vec();
276 let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
277
278 assert!(m_out == m_expected, "Expected {m_expected}, got {m_out}");
279
280 m1 = m0;
281 m0 = m_expected;
282
283 ct1 = ct0;
284 ct0 = ct_out;
285 }
286
287 for _ in 0..500 {
288 let ct_out =
289 BoolEvaluator::with_local_mut(|e| e.xnor(&ct0, &ct1, RuntimeServerKey::global()));
290
291 let m_expected = !(m0 ^ m1);
292
293 let decryption_shares = cks
294 .iter()
295 .map(|k| k.gen_decryption_share(&ct_out))
296 .collect_vec();
297 let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
298
299 assert!(m_out == m_expected, "Expected {m_expected}, got {m_out}");
300
301 m1 = m0;
302 m0 = m_expected;
303
304 ct1 = ct0;
305 ct0 = ct_out;
306 }
307 }
308
309 mod sp_api {
310 use num_traits::ToPrimitive;
311 use rand::Rng;
312
313 use crate::{
314 backend::ModulusPowerOf2, evaluator::BoolEncoding, pbs::PbsInfo,
315 rgsw::secret_key_encrypt_rlwe, utils::WithLocal, Decryptor, ModularOpsU64,
316 NttBackendU64, SampleExtractor,
317 };
318
319 use super::*;
320
321 pub(crate) fn set_single_party_parameter_sets(parameter: BoolParameters<u64>) {
322 BOOL_EVALUATOR.with_borrow_mut(|e| *e = Some(BoolEvaluator::new(parameter)));
323 }
324
325 // SERVER KEY EVAL (/SHOUP) DOMAIN //
326 impl SeededSinglePartyServerKey<Vec<Vec<u64>>, BoolParameters<u64>, [u8; 32]> {
327 pub fn set_server_key(&self) {
328 assert!(
329 BOOL_SERVER_KEY
330 .set(
331 ShoupServerKeyEvaluationDomain::from(ServerKeyEvaluationDomain::<
332 _,
333 _,
334 DefaultSecureRng,
335 NttBackendU64,
336 >::from(
337 self
338 ),)
339 )
340 .is_ok(),
341 "Attempted to set server key twice."
342 );
343 }
344 }
345
346 pub(crate) fn gen_keys() -> (
347 ClientKey,
348 SeededSinglePartyServerKey<Vec<Vec<u64>>, BoolParameters<u64>, [u8; 32]>,
349 ) {
350 super::BoolEvaluator::with_local_mut(|e| {
351 let ck = e.client_key();
352 let sk = e.single_party_server_key(&ck);
353
354 (ck, sk)
355 })
356 }
357
358 impl<K: SinglePartyClientKey<Element = i32>> Encryptor<bool, Vec<u64>> for K {
359 fn encrypt(&self, m: &bool) -> Vec<u64> {
360 BoolEvaluator::with_local(|e| e.sk_encrypt(*m, self))
361 }
362 }
363
364 impl<K: SinglePartyClientKey<Element = i32>> Decryptor<bool, Vec<u64>> for K {
365 fn decrypt(&self, c: &Vec<u64>) -> bool {
366 BoolEvaluator::with_local(|e| e.sk_decrypt(c, self))
367 }
368 }
369 impl<K> Encryptor<[bool], (Vec<Vec<u64>>, [u8; 32])> for K
370 where
371 K: SinglePartyClientKey<Element = i32>,
372 {
373 fn encrypt(&self, m: &[bool]) -> (Vec<Vec<u64>>, [u8; 32]) {
374 BoolEvaluator::with_local(|e| {
375 DefaultSecureRng::with_local_mut(|rng| {
376 let parameters = e.parameters();
377 let ring_size = parameters.rlwe_n().0;
378
379 let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
380 .to_usize()
381 .unwrap();
382
383 let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
384 rng.fill_bytes(&mut seed);
385 let mut prng = DefaultSecureRng::new_seeded(seed);
386
387 let sk_u = self.sk_rlwe();
388
389 // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
390 let rlwes = (0..rlwe_count)
391 .map(|index| {
392 let mut message = vec![0; ring_size];
393 m[(index * ring_size)
394 ..std::cmp::min(m.len(), (index + 1) * ring_size)]
395 .iter()
396 .enumerate()
397 .for_each(|(i, v)| {
398 if *v {
399 message[i] = parameters.rlwe_q().true_el()
400 } else {
401 message[i] = parameters.rlwe_q().false_el()
402 }
403 });
404
405 // encrypt message
406 let mut rlwe_out = vec![0u64; parameters.rlwe_n().0];
407
408 secret_key_encrypt_rlwe(
409 &message,
410 &mut rlwe_out,
411 &sk_u,
412 e.pbs_info().modop_rlweq(),
413 e.pbs_info().nttop_rlweq(),
414 &mut prng,
415 rng,
416 );
417
418 rlwe_out
419 })
420 .collect_vec();
421
422 (rlwes, seed)
423 })
424 })
425 }
426 }
427
428 #[test]
429 fn batch_extract_works() {
430 set_single_party_parameter_sets(SP_TEST_BOOL_PARAMS);
431
432 let (ck, sk) = gen_keys();
433 sk.set_server_key();
434
435 let batch_size = (SP_TEST_BOOL_PARAMS.rlwe_n().0 * 3 + 123);
436 let m = (0..batch_size)
437 .map(|_| thread_rng().gen::<u8>())
438 .collect_vec();
439
440 let seeded_ct = ck.encrypt(m.as_slice());
441 let ct = seeded_ct.unseed::<Vec<Vec<u64>>>();
442
443 let m_back = (0..batch_size)
444 .map(|i| ck.decrypt(&ct.extract(i)))
445 .collect_vec();
446
447 assert_eq!(m, m_back);
448 }
449
450 #[test]
451 #[cfg(feature = "interactive_mp")]
452 fn all_uint8_apis() {
453 use num_traits::Euclid;
454
455 set_single_party_parameter_sets(SP_TEST_BOOL_PARAMS);
456
457 let (ck, sk) = gen_keys();
458 sk.set_server_key();
459
460 for i in 144..=255 {
461 for j in 100..=255 {
462 let m0 = i;
463 let m1 = j;
464 let c0 = ck.encrypt(&m0);
465 let c1 = ck.encrypt(&m1);
466
467 assert!(ck.decrypt(&c0) == m0);
468 assert!(ck.decrypt(&c1) == m1);
469
470 // Arithmetic
471 {
472 {
473 // Add
474 let c_add = &c0 + &c1;
475 let m0_plus_m1 = ck.decrypt(&c_add);
476 assert_eq!(
477 m0_plus_m1,
478 m0.wrapping_add(m1),
479 "Expected {} but got {m0_plus_m1} for {i}+{j}",
480 m0.wrapping_add(m1)
481 );
482 }
483 {
484 // Sub
485 let c_sub = &c0 - &c1;
486 let m0_sub_m1 = ck.decrypt(&c_sub);
487 assert_eq!(
488 m0_sub_m1,
489 m0.wrapping_sub(m1),
490 "Expected {} but got {m0_sub_m1} for {i}-{j}",
491 m0.wrapping_sub(m1)
492 );
493 }
494
495 {
496 // Mul
497 let c_m0m1 = &c0 * &c1;
498 let m0m1 = ck.decrypt(&c_m0m1);
499 assert_eq!(
500 m0m1,
501 m0.wrapping_mul(m1),
502 "Expected {} but got {m0m1} for {i}x{j}",
503 m0.wrapping_mul(m1)
504 );
505 }
506
507 // Div & Rem
508 {
509 let (c_quotient, c_rem) = c0.div_rem(&c1);
510 let m_quotient = ck.decrypt(&c_quotient);
511 let m_remainder = ck.decrypt(&c_rem);
512 if j != 0 {
513 let (q, r) = i.div_rem_euclid(&j);
514 assert_eq!(
515 m_quotient, q,
516 "Expected {} but got {m_quotient} for {i}/{j}",
517 q
518 );
519 assert_eq!(
520 m_remainder, r,
521 "Expected {} but got {m_quotient} for {i}%{j}",
522 r
523 );
524 } else {
525 assert_eq!(
526 m_quotient, 255,
527 "Expected 255 but got {m_quotient}. Case div by zero"
528 );
529 assert_eq!(
530 m_remainder, i,
531 "Expected {i} but got {m_quotient}. Case div by zero"
532 )
533 }
534 }
535 }
536
537 // Comparisons
538 {
539 {
540 let c_eq = c0.eq(&c1);
541 let is_eq = ck.decrypt(&c_eq);
542 assert_eq!(
543 is_eq,
544 i == j,
545 "Expected {} but got {is_eq} for {i}=={j}",
546 i == j
547 );
548 }
549
550 {
551 let c_gt = c0.gt(&c1);
552 let is_gt = ck.decrypt(&c_gt);
553 assert_eq!(
554 is_gt,
555 i > j,
556 "Expected {} but got {is_gt} for {i}>{j}",
557 i > j
558 );
559 }
560
561 {
562 let c_lt = c0.lt(&c1);
563 let is_lt = ck.decrypt(&c_lt);
564 assert_eq!(
565 is_lt,
566 i < j,
567 "Expected {} but got {is_lt} for {i}<{j}",
568 i < j
569 );
570 }
571
572 {
573 let c_ge = c0.ge(&c1);
574 let is_ge = ck.decrypt(&c_ge);
575 assert_eq!(
576 is_ge,
577 i >= j,
578 "Expected {} but got {is_ge} for {i}>={j}",
579 i >= j
580 );
581 }
582
583 {
584 let c_le = c0.le(&c1);
585 let is_le = ck.decrypt(&c_le);
586 assert_eq!(
587 is_le,
588 i <= j,
589 "Expected {} but got {is_le} for {i}<={j}",
590 i <= j
591 );
592 }
593 }
594 }
595 }
596 }
597 }
598}