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