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