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