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_common_reference_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 share, in
61/// round 1 of the 2 round protocol
62pub fn interactive_multi_party_round1_share(
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_common_reference_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
329 .iter()
330 .map(|k| interactive_multi_party_round1_share(k))
331 .collect_vec();
332
333 // collective pk
334 let pk = aggregate_public_key_shares(&pk_shares);
335
336 // round 2
337 let server_key_shares = cks
338 .iter()
339 .enumerate()
340 .map(|(user_id, k)| gen_mp_keys_phase2(k, user_id, parties, &pk))
341 .collect_vec();
342
343 // server key
344 let server_key = aggregate_server_key_shares(&server_key_shares);
345 server_key.set_server_key();
346
347 let mut m0 = false;
348 let mut m1 = true;
349
350 let mut ct0 = pk.encrypt(&m0);
351 let mut ct1 = pk.encrypt(&m1);
352
353 let ideal_sk_rlwe = ideal_sk_rlwe(&cks);
354 let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
355 let rlwe_modop = parameters.default_rlwe_modop();
356
357 for _ in 0..500 {
358 let ct_out =
359 BoolEvaluator::with_local_mut(|e| e.nand(&ct0, &ct1, RuntimeServerKey::global()));
360
361 let m_expected = !(m0 && m1);
362
363 let decryption_shares = cks
364 .iter()
365 .map(|k| k.gen_decryption_share(&ct_out))
366 .collect_vec();
367 let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
368
369 assert!(m_out == m_expected, "Expected {m_expected}, got {m_out}");
370 {
371 let m_expected_el = if m_expected == true {
372 parameters.rlwe_q().true_el()
373 } else {
374 parameters.rlwe_q().false_el()
375 };
376 let noise = measure_noise_lwe(&ct_out, m_expected_el, &ideal_sk_rlwe, &rlwe_modop);
377 println!("NAND Noise: {noise}");
378 }
379
380 m1 = m0;
381 m0 = m_expected;
382
383 ct1 = ct0;
384 ct0 = ct_out;
385 }
386
387 for _ in 0..500 {
388 let ct_out =
389 BoolEvaluator::with_local_mut(|e| e.xnor(&ct0, &ct1, RuntimeServerKey::global()));
390
391 let m_expected = !(m0 ^ m1);
392
393 let decryption_shares = cks
394 .iter()
395 .map(|k| k.gen_decryption_share(&ct_out))
396 .collect_vec();
397 let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
398
399 assert!(m_out == m_expected, "Expected {m_expected}, got {m_out}");
400
401 m1 = m0;
402 m0 = m_expected;
403
404 ct1 = ct0;
405 ct0 = ct_out;
406 }
407 }
408
409 #[test]
410 fn batched_fhe_u8s_extract_works() {
411 set_parameter_set(ParameterSelector::InteractiveLTE2Party);
412 let mut seed = [0u8; 32];
413 thread_rng().fill_bytes(&mut seed);
414 set_common_reference_seed(seed);
415
416 let parties = 2;
417 let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
418
419 // round 1
420 let pk_shares = cks
421 .iter()
422 .map(|k| interactive_multi_party_round1_share(k))
423 .collect_vec();
424
425 // collective pk
426 let pk = aggregate_public_key_shares(&pk_shares);
427
428 let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
429
430 let batch_size = parameters.rlwe_n().0 * 3 + 123;
431 let m = (0..batch_size)
432 .map(|_| thread_rng().gen::<u8>())
433 .collect_vec();
434
435 let seeded_ct = pk.encrypt(m.as_slice());
436
437 let m_back = (0..batch_size)
438 .map(|i| {
439 let ct = seeded_ct.extract_at(i);
440 cks[0].aggregate_decryption_shares(
441 &ct,
442 &cks.iter()
443 .map(|k| k.gen_decryption_share(&ct))
444 .collect_vec(),
445 )
446 })
447 .collect_vec();
448
449 assert_eq!(m, m_back);
450 }
451
452 mod sp_api {
453 use num_traits::ToPrimitive;
454
455 use crate::{
456 bool::impl_bool_frontend::FheBool, pbs::PbsInfo, rgsw::seeded_secret_key_encrypt_rlwe,
457 Decryptor,
458 };
459
460 use super::*;
461
462 pub(crate) fn set_single_party_parameter_sets(parameter: BoolParameters<u64>) {
463 BOOL_EVALUATOR.with_borrow_mut(|e| *e = Some(BoolEvaluator::new(parameter)));
464 }
465
466 // SERVER KEY EVAL (/SHOUP) DOMAIN //
467 impl SeededSinglePartyServerKey<Vec<Vec<u64>>, BoolParameters<u64>, [u8; 32]> {
468 pub fn set_server_key(&self) {
469 assert!(
470 BOOL_SERVER_KEY
471 .set(
472 ShoupServerKeyEvaluationDomain::from(ServerKeyEvaluationDomain::<
473 _,
474 _,
475 DefaultSecureRng,
476 NttBackendU64,
477 >::from(
478 self
479 ),)
480 )
481 .is_ok(),
482 "Attempted to set server key twice."
483 );
484 }
485 }
486
487 pub(crate) fn gen_keys() -> (
488 ClientKey,
489 SeededSinglePartyServerKey<Vec<Vec<u64>>, BoolParameters<u64>, [u8; 32]>,
490 ) {
491 super::BoolEvaluator::with_local_mut(|e| {
492 let ck = e.client_key();
493 let sk = e.single_party_server_key(&ck);
494
495 (ck, sk)
496 })
497 }
498
499 impl<K: SinglePartyClientKey<Element = i32>> Encryptor<bool, Vec<u64>> for K {
500 fn encrypt(&self, m: &bool) -> Vec<u64> {
501 BoolEvaluator::with_local(|e| e.sk_encrypt(*m, self))
502 }
503 }
504
505 impl<K: SinglePartyClientKey<Element = i32>> Decryptor<bool, Vec<u64>> for K {
506 fn decrypt(&self, c: &Vec<u64>) -> bool {
507 BoolEvaluator::with_local(|e| e.sk_decrypt(c, self))
508 }
509 }
510
511 impl<K: SinglePartyClientKey<Element = i32>, C> Encryptor<bool, FheBool<C>> for K
512 where
513 K: Encryptor<bool, C>,
514 {
515 fn encrypt(&self, m: &bool) -> FheBool<C> {
516 FheBool {
517 data: self.encrypt(m),
518 }
519 }
520 }
521
522 impl<K: SinglePartyClientKey<Element = i32>, C> Decryptor<bool, FheBool<C>> for K
523 where
524 K: Decryptor<bool, C>,
525 {
526 fn decrypt(&self, c: &FheBool<C>) -> bool {
527 self.decrypt(c.data())
528 }
529 }
530
531 impl<K> Encryptor<[bool], (Vec<Vec<u64>>, [u8; 32])> for K
532 where
533 K: SinglePartyClientKey<Element = i32>,
534 {
535 fn encrypt(&self, m: &[bool]) -> (Vec<Vec<u64>>, [u8; 32]) {
536 BoolEvaluator::with_local(|e| {
537 DefaultSecureRng::with_local_mut(|rng| {
538 let parameters = e.parameters();
539 let ring_size = parameters.rlwe_n().0;
540
541 let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
542 .to_usize()
543 .unwrap();
544
545 let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
546 rng.fill_bytes(&mut seed);
547 let mut prng = DefaultSecureRng::new_seeded(seed);
548
549 let sk_u = self.sk_rlwe();
550
551 // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
552 let rlwes = (0..rlwe_count)
553 .map(|index| {
554 let mut message = vec![0; ring_size];
555 m[(index * ring_size)
556 ..std::cmp::min(m.len(), (index + 1) * ring_size)]
557 .iter()
558 .enumerate()
559 .for_each(|(i, v)| {
560 if *v {
561 message[i] = parameters.rlwe_q().true_el()
562 } else {
563 message[i] = parameters.rlwe_q().false_el()
564 }
565 });
566
567 // encrypt message
568 let mut rlwe_out = vec![0u64; parameters.rlwe_n().0];
569 seeded_secret_key_encrypt_rlwe(
570 &message,
571 &mut rlwe_out,
572 &sk_u,
573 e.pbs_info().modop_rlweq(),
574 e.pbs_info().nttop_rlweq(),
575 &mut prng,
576 rng,
577 );
578
579 rlwe_out
580 })
581 .collect_vec();
582
583 (rlwes, seed)
584 })
585 })
586 }
587 }
588
589 #[test]
590 #[cfg(feature = "interactive_mp")]
591 fn all_uint8_apis() {
592 use num_traits::Euclid;
593
594 use crate::{div_zero_error_flag, FheBool};
595
596 set_single_party_parameter_sets(SP_TEST_BOOL_PARAMS);
597
598 let (ck, sk) = gen_keys();
599 sk.set_server_key();
600
601 for i in 0..=255 {
602 for j in 0..=255 {
603 let m0 = i;
604 let m1 = j;
605 let c0 = ck.encrypt(&m0);
606 let c1 = ck.encrypt(&m1);
607
608 assert!(ck.decrypt(&c0) == m0);
609 assert!(ck.decrypt(&c1) == m1);
610
611 // Arithmetic
612 {
613 {
614 // Add
615 let c_add = &c0 + &c1;
616 let m0_plus_m1 = ck.decrypt(&c_add);
617 assert_eq!(
618 m0_plus_m1,
619 m0.wrapping_add(m1),
620 "Expected {} but got {m0_plus_m1} for
621 {i}+{j}",
622 m0.wrapping_add(m1)
623 );
624 }
625 {
626 // Sub
627 let c_sub = &c0 - &c1;
628 let m0_sub_m1 = ck.decrypt(&c_sub);
629 assert_eq!(
630 m0_sub_m1,
631 m0.wrapping_sub(m1),
632 "Expected {} but got {m0_sub_m1} for
633 {i}-{j}",
634 m0.wrapping_sub(m1)
635 );
636 }
637
638 {
639 // Mul
640 let c_m0m1 = &c0 * &c1;
641 let m0m1 = ck.decrypt(&c_m0m1);
642 assert_eq!(
643 m0m1,
644 m0.wrapping_mul(m1),
645 "Expected {} but got {m0m1} for {i}x{j}",
646 m0.wrapping_mul(m1)
647 );
648 }
649
650 // Div & Rem
651 {
652 let (c_quotient, c_rem) = c0.div_rem(&c1);
653 let m_quotient = ck.decrypt(&c_quotient);
654 let m_remainder = ck.decrypt(&c_rem);
655 if j != 0 {
656 let (q, r) = i.div_rem_euclid(&j);
657 assert_eq!(
658 m_quotient, q,
659 "Expected {} but got {m_quotient} for
660 {i}/{j}",
661 q
662 );
663 assert_eq!(
664 m_remainder, r,
665 "Expected {} but got {m_remainder} for
666 {i}%{j}",
667 r
668 );
669 } else {
670 assert_eq!(
671 m_quotient, 255,
672 "Expected 255 but got {m_quotient}. Case
673 div by zero"
674 );
675 assert_eq!(
676 m_remainder, i,
677 "Expected {i} but got {m_remainder}. Case
678 div by zero"
679 );
680
681 let div_by_zero = ck.decrypt(&div_zero_error_flag().unwrap());
682 assert_eq!(
683 div_by_zero, true,
684 "Expected true but got {div_by_zero}"
685 );
686 }
687 }
688 }
689
690 // // Comparisons
691 {
692 {
693 let c_eq = c0.eq(&c1);
694 let is_eq = ck.decrypt(&c_eq);
695 assert_eq!(
696 is_eq,
697 i == j,
698 "Expected {} but got {is_eq} for {i}=={j}",
699 i == j
700 );
701 }
702
703 {
704 let c_gt = c0.gt(&c1);
705 let is_gt = ck.decrypt(&c_gt);
706 assert_eq!(
707 is_gt,
708 i > j,
709 "Expected {} but got {is_gt} for {i}>{j}",
710 i > j
711 );
712 }
713
714 {
715 let c_lt = c0.lt(&c1);
716 let is_lt = ck.decrypt(&c_lt);
717 assert_eq!(
718 is_lt,
719 i < j,
720 "Expected {} but got {is_lt} for {i}<{j}",
721 i < j
722 );
723 }
724
725 {
726 let c_ge = c0.ge(&c1);
727 let is_ge = ck.decrypt(&c_ge);
728 assert_eq!(
729 is_ge,
730 i >= j,
731 "Expected {} but got {is_ge} for {i}>={j}",
732 i >= j
733 );
734 }
735
736 {
737 let c_le = c0.le(&c1);
738 let is_le = ck.decrypt(&c_le);
739 assert_eq!(
740 is_le,
741 i <= j,
742 "Expected {} but got {is_le} for {i}<={j}",
743 i <= j
744 );
745 }
746 }
747
748 // mux
749 {
750 let selector = thread_rng().gen_bool(0.5);
751 let selector_enc: FheBool = ck.encrypt(&selector);
752 let mux_out = ck.decrypt(&c0.mux(&c1, &selector_enc));
753 let want_mux_out = if selector { m0 } else { m1 };
754 assert_eq!(mux_out, want_mux_out);
755 }
756 }
757 }
758 }
759
760 #[test]
761 #[cfg(feature = "interactive_mp")]
762 fn all_bool_apis() {
763 use crate::FheBool;
764
765 set_single_party_parameter_sets(SP_TEST_BOOL_PARAMS);
766
767 let (ck, sk) = gen_keys();
768 sk.set_server_key();
769
770 for _ in 0..100 {
771 let a = thread_rng().gen_bool(0.5);
772 let b = thread_rng().gen_bool(0.5);
773
774 let c_a: FheBool = ck.encrypt(&a);
775 let c_b: FheBool = ck.encrypt(&b);
776
777 let c_out = &c_a & &c_b;
778 let out = ck.decrypt(&c_out);
779 assert_eq!(out, a & b, "Expected {} but got {out}", a & b);
780
781 let c_out = &c_a | &c_b;
782 let out = ck.decrypt(&c_out);
783 assert_eq!(out, a | b, "Expected {} but got {out}", a | b);
784
785 let c_out = &c_a ^ &c_b;
786 let out = ck.decrypt(&c_out);
787 assert_eq!(out, a ^ b, "Expected {} but got {out}", a ^ b);
788
789 let c_out = !(&c_a);
790 let out = ck.decrypt(&c_out);
791 assert_eq!(out, !a, "Expected {} but got {out}", !a);
792 }
793 }
794 }
795}