phantom-zone/src/bool/mp_api.rs
src/bool/mp_api.rsRaw
1use std::{cell::RefCell, 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::InteractiveMultiPartyCrs, keys::*, parameters::*, ClientKey};
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<InteractiveMultiPartyCrs<[u8; 32]>> = OnceLock::new();
27
28pub enum ParameterSelector {
29 InteractiveLTE2Party,
30 InteractiveLTE4Party,
31 InteractiveLTE8Party,
32}
33
34/// Select Interactive multi-party parameter variant
35pub fn set_parameter_set(select: ParameterSelector) {
36 match select {
37 ParameterSelector::InteractiveLTE2Party => {
38 BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_2P_LB_SR)));
39 }
40 ParameterSelector::InteractiveLTE4Party => {
41 BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_4P)));
42 }
43 ParameterSelector::InteractiveLTE8Party => {
44 BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_8P)));
45 }
46 _ => {
47 panic!("Paramerter not supported")
48 }
49 }
50}
51
52/// Set application specific interactive multi-party common reference string
53pub fn set_common_reference_seed(seed: [u8; 32]) {
54 assert!(
55 MULTI_PARTY_CRS
56 .set(InteractiveMultiPartyCrs { seed: seed })
57 .is_ok(),
58 "Attempted to set MP SEED twice."
59 )
60}
61
62/// Generate client key for interactive multi-party protocol
63pub fn gen_client_key() -> ClientKey {
64 BoolEvaluator::with_local(|e| e.client_key())
65}
66
67/// Generate client's share for collective public key, i.e round 1 share, in
68/// round 1 of the 2 round protocol
69pub fn interactive_multi_party_round1_share(
70 ck: &ClientKey,
71) -> CommonReferenceSeededCollectivePublicKeyShare<Vec<u64>, [u8; 32], BoolParameters<u64>> {
72 BoolEvaluator::with_local(|e| {
73 let pk_share = e.multi_party_public_key_share(InteractiveMultiPartyCrs::global(), ck);
74 pk_share
75 })
76}
77
78/// Generate clients share for collective server key, i.e. round 2, of the
79/// protocol
80pub fn gen_mp_keys_phase2<R, ModOp>(
81 ck: &ClientKey,
82 user_id: usize,
83 total_users: usize,
84 pk: &PublicKey<Vec<Vec<u64>>, R, ModOp>,
85) -> CommonReferenceSeededInteractiveMultiPartyServerKeyShare<
86 Vec<Vec<u64>>,
87 BoolParameters<u64>,
88 InteractiveMultiPartyCrs<[u8; 32]>,
89> {
90 BoolEvaluator::with_local_mut(|e| {
91 let server_key_share = e.gen_interactive_multi_party_server_key_share(
92 user_id,
93 total_users,
94 InteractiveMultiPartyCrs::global(),
95 pk.key(),
96 ck,
97 );
98 server_key_share
99 })
100}
101
102/// Aggregate public key shares from all parties.
103///
104/// Public key shares are generated per client in round 1. Aggregation of public
105/// key shares marks the end of round 1.
106pub fn aggregate_public_key_shares(
107 shares: &[CommonReferenceSeededCollectivePublicKeyShare<
108 Vec<u64>,
109 [u8; 32],
110 BoolParameters<u64>,
111 >],
112) -> PublicKey<Vec<Vec<u64>>, DefaultSecureRng, ModularOpsU64<CiphertextModulus<u64>>> {
113 PublicKey::from(shares)
114}
115
116/// Aggregate server key shares
117pub fn aggregate_server_key_shares(
118 shares: &[CommonReferenceSeededInteractiveMultiPartyServerKeyShare<
119 Vec<Vec<u64>>,
120 BoolParameters<u64>,
121 InteractiveMultiPartyCrs<[u8; 32]>,
122 >],
123) -> SeededInteractiveMultiPartyServerKey<
124 Vec<Vec<u64>>,
125 InteractiveMultiPartyCrs<[u8; 32]>,
126 BoolParameters<u64>,
127> {
128 BoolEvaluator::with_local(|e| e.aggregate_interactive_multi_party_server_key_shares(shares))
129}
130
131impl
132 SeededInteractiveMultiPartyServerKey<
133 Vec<Vec<u64>>,
134 InteractiveMultiPartyCrs<<DefaultSecureRng as NewWithSeed>::Seed>,
135 BoolParameters<u64>,
136 >
137{
138 /// Sets the server key as a global reference for circuit evaluation
139 pub fn set_server_key(&self) {
140 assert!(
141 BOOL_SERVER_KEY
142 .set(ShoupServerKeyEvaluationDomain::from(
143 ServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(self),
144 ))
145 .is_ok(),
146 "Attempted to set server key twice."
147 );
148 }
149}
150
151// MULTIPARTY CRS //
152impl Global for InteractiveMultiPartyCrs<[u8; 32]> {
153 fn global() -> &'static Self {
154 MULTI_PARTY_CRS
155 .get()
156 .expect("Multi Party Common Reference String not set")
157 }
158}
159
160// BOOL EVALUATOR //
161impl WithLocal for BoolEvaluator {
162 fn with_local<F, R>(func: F) -> R
163 where
164 F: Fn(&Self) -> R,
165 {
166 BOOL_EVALUATOR.with_borrow(|s| func(s.as_ref().expect("Parameters not set")))
167 }
168
169 fn with_local_mut<F, R>(func: F) -> R
170 where
171 F: Fn(&mut Self) -> R,
172 {
173 BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
174 }
175
176 fn with_local_mut_mut<F, R>(func: &mut F) -> R
177 where
178 F: FnMut(&mut Self) -> R,
179 {
180 BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
181 }
182}
183
184pub(crate) type RuntimeServerKey = ShoupServerKeyEvaluationDomain<Vec<Vec<u64>>>;
185impl Global for RuntimeServerKey {
186 fn global() -> &'static Self {
187 BOOL_SERVER_KEY.get().expect("Server key not set!")
188 }
189}
190
191mod impl_enc_dec {
192 use crate::{
193 bool::evaluator::BoolEncoding,
194 multi_party::{
195 multi_party_aggregate_decryption_shares_and_decrypt, multi_party_decryption_share,
196 },
197 pbs::{sample_extract, PbsInfo},
198 rgsw::public_key_encrypt_rlwe,
199 utils::TryConvertFrom1,
200 Encryptor, Matrix, MatrixEntity, MultiPartyDecryptor, RowEntity,
201 };
202 use itertools::Itertools;
203 use num_traits::{ToPrimitive, Zero};
204
205 use super::*;
206
207 type Mat = Vec<Vec<u64>>;
208
209 impl<Rng, ModOp> Encryptor<[bool], Vec<Mat>> for PublicKey<Mat, Rng, ModOp> {
210 fn encrypt(&self, m: &[bool]) -> Vec<Mat> {
211 BoolEvaluator::with_local(|e| {
212 DefaultSecureRng::with_local_mut(|rng| {
213 let parameters = e.parameters();
214 let ring_size = parameters.rlwe_n().0;
215
216 let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
217 .to_usize()
218 .unwrap();
219
220 // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
221 let rlwes = (0..rlwe_count)
222 .map(|index| {
223 let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
224 m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
225 .iter()
226 .enumerate()
227 .for_each(|(i, v)| {
228 if *v {
229 message[i] = parameters.rlwe_q().true_el()
230 } else {
231 message[i] = parameters.rlwe_q().false_el()
232 }
233 });
234
235 // encrypt message
236 let mut rlwe_out =
237 <Mat as MatrixEntity>::zeros(2, parameters.rlwe_n().0);
238
239 public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
240 &mut rlwe_out,
241 self.key(),
242 &message,
243 e.pbs_info().modop_rlweq(),
244 e.pbs_info().nttop_rlweq(),
245 rng,
246 );
247
248 rlwe_out
249 })
250 .collect_vec();
251 rlwes
252 })
253 })
254 }
255 }
256
257 impl<Rng, ModOp> Encryptor<bool, <Mat as Matrix>::R> for PublicKey<Mat, Rng, ModOp> {
258 fn encrypt(&self, m: &bool) -> <Mat as Matrix>::R {
259 let m = vec![*m];
260 let rlwe = &self.encrypt(m.as_slice())[0];
261 BoolEvaluator::with_local(|e| {
262 let mut lwe = <Mat as Matrix>::R::zeros(e.parameters().rlwe_n().0 + 1);
263 sample_extract(&mut lwe, rlwe, e.pbs_info().modop_rlweq(), 0);
264 lwe
265 })
266 }
267 }
268
269 impl<K> MultiPartyDecryptor<bool, <Mat as Matrix>::R> for K
270 where
271 K: InteractiveMultiPartyClientKey,
272 <Mat as Matrix>::R:
273 TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
274 {
275 type DecryptionShare = <Mat as Matrix>::MatElement;
276
277 fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
278 BoolEvaluator::with_local(|e| {
279 DefaultSecureRng::with_local_mut(|rng| {
280 multi_party_decryption_share(
281 c,
282 self.sk_rlwe().as_slice(),
283 e.pbs_info().modop_rlweq(),
284 rng,
285 )
286 })
287 })
288 }
289
290 fn aggregate_decryption_shares(
291 &self,
292 c: &<Mat as Matrix>::R,
293 shares: &[Self::DecryptionShare],
294 ) -> bool {
295 BoolEvaluator::with_local(|e| {
296 let noisy_m = multi_party_aggregate_decryption_shares_and_decrypt(
297 c,
298 shares,
299 e.pbs_info().modop_rlweq(),
300 );
301
302 e.pbs_info().rlwe_q().decode(noisy_m)
303 })
304 }
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use itertools::Itertools;
311 use rand::{thread_rng, Rng, RngCore};
312
313 use crate::{
314 backend::Modulus,
315 bool::{
316 evaluator::BoolEncoding,
317 keys::tests::{ideal_sk_rlwe, measure_noise_lwe},
318 BooleanGates,
319 },
320 lwe::decrypt_lwe,
321 utils::tests::Stats,
322 Encoder, Encryptor, MultiPartyDecryptor, SampleExtractor,
323 };
324
325 use super::*;
326
327 #[test]
328 fn multi_party_bool_gates() {
329 set_parameter_set(ParameterSelector::InteractiveLTE8Party);
330 let mut seed = [0u8; 32];
331 thread_rng().fill_bytes(&mut seed);
332 set_common_reference_seed(seed);
333
334 let parties = 8;
335 let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
336
337 // round 1
338 let pk_shares = cks
339 .iter()
340 .map(|k| interactive_multi_party_round1_share(k))
341 .collect_vec();
342
343 // collective pk
344 let pk = aggregate_public_key_shares(&pk_shares);
345
346 // round 2
347 let server_key_shares = cks
348 .iter()
349 .enumerate()
350 .map(|(user_id, k)| gen_mp_keys_phase2(k, user_id, parties, &pk))
351 .collect_vec();
352
353 // server key
354 let server_key = aggregate_server_key_shares(&server_key_shares);
355 server_key.set_server_key();
356
357 let mut m0 = false;
358 let mut m1 = true;
359
360 let mut ct0 = pk.encrypt(&m0);
361 let mut ct1 = pk.encrypt(&m1);
362
363 let ideal_sk_rlwe = ideal_sk_rlwe(&cks);
364 let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
365 let rlwe_modop = parameters.default_rlwe_modop();
366
367 let mut stats = Stats::new();
368
369 for _ in 0..1000 {
370 let now = std::time::Instant::now();
371 let ct_out =
372 BoolEvaluator::with_local_mut(|e| e.nand(&ct0, &ct1, RuntimeServerKey::global()));
373 println!("Time: {:?}", now.elapsed());
374
375 let m_expected = !(m0 && m1);
376
377 let decryption_shares = cks
378 .iter()
379 .map(|k| k.gen_decryption_share(&ct_out))
380 .collect_vec();
381 let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
382
383 assert!(m_out == m_expected, "Expected {m_expected}, got {m_out}");
384
385 {
386 let noise = measure_noise_lwe(
387 &ct_out,
388 parameters.rlwe_q().encode(m_expected),
389 &ideal_sk_rlwe,
390 &rlwe_modop,
391 );
392 stats.add_sample(parameters.rlwe_q().map_element_to_i64(&noise));
393 }
394
395 m1 = m0;
396 m0 = m_expected;
397
398 ct1 = ct0;
399 ct0 = ct_out;
400 }
401
402 for _ in 0..1000 {
403 let ct_out =
404 BoolEvaluator::with_local_mut(|e| e.xnor(&ct0, &ct1, RuntimeServerKey::global()));
405
406 let m_expected = !(m0 ^ m1);
407
408 let decryption_shares = cks
409 .iter()
410 .map(|k| k.gen_decryption_share(&ct_out))
411 .collect_vec();
412 let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
413
414 assert!(m_out == m_expected, "Expected {m_expected}, got {m_out}");
415
416 {
417 let noise = measure_noise_lwe(
418 &ct_out,
419 parameters.rlwe_q().encode(m_expected),
420 &ideal_sk_rlwe,
421 &rlwe_modop,
422 );
423 stats.add_sample(parameters.rlwe_q().map_element_to_i64(&noise));
424 }
425
426 m1 = m0;
427 m0 = m_expected;
428
429 ct1 = ct0;
430 ct0 = ct_out;
431 }
432
433 println!("Noise std_dev log2: {}", stats.std_dev().abs().log2());
434 }
435
436 #[test]
437 fn batched_fhe_u8s_extract_works() {
438 set_parameter_set(ParameterSelector::InteractiveLTE2Party);
439 let mut seed = [0u8; 32];
440 thread_rng().fill_bytes(&mut seed);
441 set_common_reference_seed(seed);
442
443 let parties = 2;
444 let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
445
446 // round 1
447 let pk_shares = cks
448 .iter()
449 .map(|k| interactive_multi_party_round1_share(k))
450 .collect_vec();
451
452 // collective pk
453 let pk = aggregate_public_key_shares(&pk_shares);
454
455 let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
456
457 let batch_size = parameters.rlwe_n().0 * 3 + 123;
458 let m = (0..batch_size)
459 .map(|_| thread_rng().gen::<u8>())
460 .collect_vec();
461
462 let seeded_ct = pk.encrypt(m.as_slice());
463
464 let m_back = (0..batch_size)
465 .map(|i| {
466 let ct = seeded_ct.extract_at(i);
467 cks[0].aggregate_decryption_shares(
468 &ct,
469 &cks.iter()
470 .map(|k| k.gen_decryption_share(&ct))
471 .collect_vec(),
472 )
473 })
474 .collect_vec();
475
476 assert_eq!(m, m_back);
477 }
478
479 mod sp_api {
480 use num_traits::ToPrimitive;
481
482 use crate::{
483 bool::impl_bool_frontend::FheBool, pbs::PbsInfo, rgsw::seeded_secret_key_encrypt_rlwe,
484 Decryptor,
485 };
486
487 use super::*;
488
489 pub(crate) fn set_single_party_parameter_sets(parameter: BoolParameters<u64>) {
490 BOOL_EVALUATOR.with_borrow_mut(|e| *e = Some(BoolEvaluator::new(parameter)));
491 }
492
493 // SERVER KEY EVAL (/SHOUP) DOMAIN //
494 impl SeededSinglePartyServerKey<Vec<Vec<u64>>, BoolParameters<u64>, [u8; 32]> {
495 pub fn set_server_key(&self) {
496 assert!(
497 BOOL_SERVER_KEY
498 .set(
499 ShoupServerKeyEvaluationDomain::from(ServerKeyEvaluationDomain::<
500 _,
501 _,
502 DefaultSecureRng,
503 NttBackendU64,
504 >::from(
505 self
506 ),)
507 )
508 .is_ok(),
509 "Attempted to set server key twice."
510 );
511 }
512 }
513
514 pub(crate) fn gen_keys() -> (
515 ClientKey,
516 SeededSinglePartyServerKey<Vec<Vec<u64>>, BoolParameters<u64>, [u8; 32]>,
517 ) {
518 super::BoolEvaluator::with_local_mut(|e| {
519 let ck = e.client_key();
520 let sk = e.single_party_server_key(&ck);
521
522 (ck, sk)
523 })
524 }
525
526 impl<K: SinglePartyClientKey<Element = i32>> Encryptor<bool, Vec<u64>> for K {
527 fn encrypt(&self, m: &bool) -> Vec<u64> {
528 BoolEvaluator::with_local(|e| e.sk_encrypt(*m, self))
529 }
530 }
531
532 impl<K: SinglePartyClientKey<Element = i32>> Decryptor<bool, Vec<u64>> for K {
533 fn decrypt(&self, c: &Vec<u64>) -> bool {
534 BoolEvaluator::with_local(|e| e.sk_decrypt(c, self))
535 }
536 }
537
538 impl<K: SinglePartyClientKey<Element = i32>, C> Encryptor<bool, FheBool<C>> for K
539 where
540 K: Encryptor<bool, C>,
541 {
542 fn encrypt(&self, m: &bool) -> FheBool<C> {
543 FheBool {
544 data: self.encrypt(m),
545 }
546 }
547 }
548
549 impl<K: SinglePartyClientKey<Element = i32>, C> Decryptor<bool, FheBool<C>> for K
550 where
551 K: Decryptor<bool, C>,
552 {
553 fn decrypt(&self, c: &FheBool<C>) -> bool {
554 self.decrypt(c.data())
555 }
556 }
557
558 impl<K> Encryptor<[bool], (Vec<Vec<u64>>, [u8; 32])> for K
559 where
560 K: SinglePartyClientKey<Element = i32>,
561 {
562 fn encrypt(&self, m: &[bool]) -> (Vec<Vec<u64>>, [u8; 32]) {
563 BoolEvaluator::with_local(|e| {
564 DefaultSecureRng::with_local_mut(|rng| {
565 let parameters = e.parameters();
566 let ring_size = parameters.rlwe_n().0;
567
568 let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
569 .to_usize()
570 .unwrap();
571
572 let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
573 rng.fill_bytes(&mut seed);
574 let mut prng = DefaultSecureRng::new_seeded(seed);
575
576 let sk_u = self.sk_rlwe();
577
578 // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
579 let rlwes = (0..rlwe_count)
580 .map(|index| {
581 let mut message = vec![0; ring_size];
582 m[(index * ring_size)
583 ..std::cmp::min(m.len(), (index + 1) * ring_size)]
584 .iter()
585 .enumerate()
586 .for_each(|(i, v)| {
587 if *v {
588 message[i] = parameters.rlwe_q().true_el()
589 } else {
590 message[i] = parameters.rlwe_q().false_el()
591 }
592 });
593
594 // encrypt message
595 let mut rlwe_out = vec![0u64; parameters.rlwe_n().0];
596 seeded_secret_key_encrypt_rlwe(
597 &message,
598 &mut rlwe_out,
599 &sk_u,
600 e.pbs_info().modop_rlweq(),
601 e.pbs_info().nttop_rlweq(),
602 &mut prng,
603 rng,
604 );
605
606 rlwe_out
607 })
608 .collect_vec();
609
610 (rlwes, seed)
611 })
612 })
613 }
614 }
615
616 #[test]
617 #[cfg(feature = "interactive_mp")]
618 fn all_uint8_apis() {
619 use num_traits::Euclid;
620
621 use crate::{div_zero_error_flag, FheBool};
622
623 set_single_party_parameter_sets(SP_TEST_BOOL_PARAMS);
624
625 let (ck, sk) = gen_keys();
626 sk.set_server_key();
627
628 for i in 0..=255 {
629 for j in 0..=255 {
630 let m0 = i;
631 let m1 = j;
632 let c0 = ck.encrypt(&m0);
633 let c1 = ck.encrypt(&m1);
634
635 assert!(ck.decrypt(&c0) == m0);
636 assert!(ck.decrypt(&c1) == m1);
637
638 // Arithmetic
639 {
640 {
641 // Add
642 let c_add = &c0 + &c1;
643 let m0_plus_m1 = ck.decrypt(&c_add);
644 assert_eq!(
645 m0_plus_m1,
646 m0.wrapping_add(m1),
647 "Expected {} but got {m0_plus_m1} for
648 {i}+{j}",
649 m0.wrapping_add(m1)
650 );
651 }
652 {
653 // Sub
654 let c_sub = &c0 - &c1;
655 let m0_sub_m1 = ck.decrypt(&c_sub);
656 assert_eq!(
657 m0_sub_m1,
658 m0.wrapping_sub(m1),
659 "Expected {} but got {m0_sub_m1} for
660 {i}-{j}",
661 m0.wrapping_sub(m1)
662 );
663 }
664
665 {
666 // Mul
667 let c_m0m1 = &c0 * &c1;
668 let m0m1 = ck.decrypt(&c_m0m1);
669 assert_eq!(
670 m0m1,
671 m0.wrapping_mul(m1),
672 "Expected {} but got {m0m1} for {i}x{j}",
673 m0.wrapping_mul(m1)
674 );
675 }
676
677 // Div & Rem
678 {
679 let (c_quotient, c_rem) = c0.div_rem(&c1);
680 let m_quotient = ck.decrypt(&c_quotient);
681 let m_remainder = ck.decrypt(&c_rem);
682 if j != 0 {
683 let (q, r) = i.div_rem_euclid(&j);
684 assert_eq!(
685 m_quotient, q,
686 "Expected {} but got {m_quotient} for
687 {i}/{j}",
688 q
689 );
690 assert_eq!(
691 m_remainder, r,
692 "Expected {} but got {m_remainder} for
693 {i}%{j}",
694 r
695 );
696 } else {
697 assert_eq!(
698 m_quotient, 255,
699 "Expected 255 but got {m_quotient}. Case
700 div by zero"
701 );
702 assert_eq!(
703 m_remainder, i,
704 "Expected {i} but got {m_remainder}. Case
705 div by zero"
706 );
707
708 let div_by_zero = ck.decrypt(&div_zero_error_flag().unwrap());
709 assert_eq!(
710 div_by_zero, true,
711 "Expected true but got {div_by_zero}"
712 );
713 }
714 }
715 }
716
717 // // Comparisons
718 {
719 {
720 let c_eq = c0.eq(&c1);
721 let is_eq = ck.decrypt(&c_eq);
722 assert_eq!(
723 is_eq,
724 i == j,
725 "Expected {} but got {is_eq} for {i}=={j}",
726 i == j
727 );
728 }
729
730 {
731 let c_gt = c0.gt(&c1);
732 let is_gt = ck.decrypt(&c_gt);
733 assert_eq!(
734 is_gt,
735 i > j,
736 "Expected {} but got {is_gt} for {i}>{j}",
737 i > j
738 );
739 }
740
741 {
742 let c_lt = c0.lt(&c1);
743 let is_lt = ck.decrypt(&c_lt);
744 assert_eq!(
745 is_lt,
746 i < j,
747 "Expected {} but got {is_lt} for {i}<{j}",
748 i < j
749 );
750 }
751
752 {
753 let c_ge = c0.ge(&c1);
754 let is_ge = ck.decrypt(&c_ge);
755 assert_eq!(
756 is_ge,
757 i >= j,
758 "Expected {} but got {is_ge} for {i}>={j}",
759 i >= j
760 );
761 }
762
763 {
764 let c_le = c0.le(&c1);
765 let is_le = ck.decrypt(&c_le);
766 assert_eq!(
767 is_le,
768 i <= j,
769 "Expected {} but got {is_le} for {i}<={j}",
770 i <= j
771 );
772 }
773 }
774
775 // mux
776 {
777 let selector = thread_rng().gen_bool(0.5);
778 let selector_enc: FheBool = ck.encrypt(&selector);
779 let mux_out = ck.decrypt(&c0.mux(&c1, &selector_enc));
780 let want_mux_out = if selector { m0 } else { m1 };
781 assert_eq!(mux_out, want_mux_out);
782 }
783 }
784 }
785 }
786
787 #[test]
788 #[cfg(feature = "interactive_mp")]
789 fn all_bool_apis() {
790 use crate::FheBool;
791
792 set_single_party_parameter_sets(SP_TEST_BOOL_PARAMS);
793
794 let (ck, sk) = gen_keys();
795 sk.set_server_key();
796
797 for _ in 0..100 {
798 let a = thread_rng().gen_bool(0.5);
799 let b = thread_rng().gen_bool(0.5);
800
801 let c_a: FheBool = ck.encrypt(&a);
802 let c_b: FheBool = ck.encrypt(&b);
803
804 let c_out = &c_a & &c_b;
805 let out = ck.decrypt(&c_out);
806 assert_eq!(out, a & b, "Expected {} but got {out}", a & b);
807
808 let c_out = &c_a | &c_b;
809 let out = ck.decrypt(&c_out);
810 assert_eq!(out, a | b, "Expected {} but got {out}", a | b);
811
812 let c_out = &c_a ^ &c_b;
813 let out = ck.decrypt(&c_out);
814 assert_eq!(out, a ^ b, "Expected {} but got {out}", a ^ b);
815
816 let c_out = !(&c_a);
817 let out = ck.decrypt(&c_out);
818 assert_eq!(out, !a, "Expected {} but got {out}", !a);
819 }
820 }
821 }
822}