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