phantom-zone/src/bool/ni_mp_api.rs
src/bool/ni_mp_api.rsRaw
1use std::{cell::RefCell, sync::OnceLock};
2
3use crate::{
4 backend::ModulusPowerOf2,
5 bool::parameters::ParameterVariant,
6 random::DefaultSecureRng,
7 utils::{Global, WithLocal},
8 ModularOpsU64, NttBackendU64,
9};
10
11use super::{
12 evaluator::NonInteractiveMultiPartyCrs,
13 keys::{
14 CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare,
15 NonInteractiveServerKeyEvaluationDomain, SeededNonInteractiveMultiPartyServerKey,
16 ShoupNonInteractiveServerKeyEvaluationDomain,
17 },
18 parameters::{BoolParameters, CiphertextModulus, NON_INTERACTIVE_SMALL_MP_BOOL_PARAMS},
19 ClientKey, ParameterSelector,
20};
21
22pub(crate) type BoolEvaluator = super::evaluator::BoolEvaluator<
23 Vec<Vec<u64>>,
24 NttBackendU64,
25 ModularOpsU64<CiphertextModulus<u64>>,
26 ModulusPowerOf2<CiphertextModulus<u64>>,
27 ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>,
28>;
29
30thread_local! {
31 static BOOL_EVALUATOR: RefCell<Option<BoolEvaluator>> = RefCell::new(None);
32
33}
34static BOOL_SERVER_KEY: OnceLock<ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>> =
35 OnceLock::new();
36
37static MULTI_PARTY_CRS: OnceLock<NonInteractiveMultiPartyCrs<[u8; 32]>> = OnceLock::new();
38
39pub fn set_parameter_set(select: ParameterSelector) {
40 match select {
41 ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16 => {
42 BOOL_EVALUATOR.with_borrow_mut(|v| {
43 *v = Some(BoolEvaluator::new(NON_INTERACTIVE_SMALL_MP_BOOL_PARAMS))
44 });
45 }
46 _ => {
47 panic!("Paramerters not supported")
48 }
49 }
50}
51
52pub fn set_common_reference_seed(seed: [u8; 32]) {
53 BoolEvaluator::with_local(|e| {
54 assert_eq!(
55 e.parameters().variant(),
56 &ParameterVariant::NonInteractiveMultiParty,
57 "Set parameters do not support Non interactive multi-party"
58 );
59 });
60
61 assert!(
62 MULTI_PARTY_CRS
63 .set(NonInteractiveMultiPartyCrs { seed: seed })
64 .is_ok(),
65 "Attempted to set MP SEED twice."
66 )
67}
68
69pub fn gen_client_key() -> ClientKey {
70 BoolEvaluator::with_local(|e| e.client_key())
71}
72
73pub fn gen_server_key_share(
74 user_id: usize,
75 total_users: usize,
76 client_key: &ClientKey,
77) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
78 Vec<Vec<u64>>,
79 NonInteractiveMultiPartyCrs<[u8; 32]>,
80> {
81 BoolEvaluator::with_local(|e| {
82 let cr_seed = NonInteractiveMultiPartyCrs::global();
83 e.non_interactive_multi_party_key_share(cr_seed, user_id, total_users, client_key)
84 })
85}
86
87pub fn aggregate_server_key_shares(
88 shares: &[CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
89 Vec<Vec<u64>>,
90 NonInteractiveMultiPartyCrs<[u8; 32]>,
91 >],
92) -> SeededNonInteractiveMultiPartyServerKey<
93 Vec<Vec<u64>>,
94 NonInteractiveMultiPartyCrs<[u8; 32]>,
95 BoolParameters<u64>,
96> {
97 BoolEvaluator::with_local(|e| {
98 let cr_seed = NonInteractiveMultiPartyCrs::global();
99 e.aggregate_non_interactive_multi_party_key_share(cr_seed, shares.len(), shares)
100 })
101}
102
103impl
104 SeededNonInteractiveMultiPartyServerKey<
105 Vec<Vec<u64>>,
106 NonInteractiveMultiPartyCrs<[u8; 32]>,
107 BoolParameters<u64>,
108 >
109{
110 pub fn set_server_key(&self) {
111 let eval_key = NonInteractiveServerKeyEvaluationDomain::<
112 _,
113 BoolParameters<u64>,
114 DefaultSecureRng,
115 NttBackendU64,
116 >::from(self);
117 assert!(
118 BOOL_SERVER_KEY
119 .set(ShoupNonInteractiveServerKeyEvaluationDomain::from(eval_key))
120 .is_ok(),
121 "Attempted to set server key twice!"
122 );
123 }
124}
125
126impl Global for NonInteractiveMultiPartyCrs<[u8; 32]> {
127 fn global() -> &'static Self {
128 MULTI_PARTY_CRS
129 .get()
130 .expect("Non-interactive multi-party common reference string not set")
131 }
132}
133
134// BOOL EVALUATOR //
135impl WithLocal for BoolEvaluator {
136 fn with_local<F, R>(func: F) -> R
137 where
138 F: Fn(&Self) -> R,
139 {
140 BOOL_EVALUATOR.with_borrow(|s| func(s.as_ref().expect("Parameters not set")))
141 }
142
143 fn with_local_mut<F, R>(func: F) -> R
144 where
145 F: Fn(&mut Self) -> R,
146 {
147 BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
148 }
149
150 fn with_local_mut_mut<F, R>(func: &mut F) -> R
151 where
152 F: FnMut(&mut Self) -> R,
153 {
154 BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
155 }
156}
157
158pub(crate) type RuntimeServerKey = ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>;
159impl Global for RuntimeServerKey {
160 fn global() -> &'static Self {
161 BOOL_SERVER_KEY.get().expect("Server key not set!")
162 }
163}
164
165/// Non interactive multi-party specfic encryptor decryptor routines
166mod impl_enc_dec {
167 use crate::{
168 bool::{evaluator::BoolEncoding, keys::NonInteractiveMultiPartyClientKey},
169 pbs::{sample_extract, PbsInfo, WithShoupRepr},
170 random::{NewWithSeed, RandomFillUniformInModulus},
171 rgsw::{key_switch, secret_key_encrypt_rlwe},
172 utils::TryConvertFrom1,
173 Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, RowEntity, RowMut,
174 };
175 use itertools::Itertools;
176 use num_traits::{ToPrimitive, Zero};
177
178 use super::*;
179
180 type Mat = Vec<Vec<u64>>;
181
182 pub(super) struct BatchedFheBools<C> {
183 pub(super) data: Vec<C>,
184 }
185
186 impl<C: MatrixMut<MatElement = u64>> BatchedFheBools<C>
187 where
188 C::R: RowEntity + RowMut,
189 {
190 pub(super) fn extract(&self, index: usize) -> C::R {
191 BoolEvaluator::with_local(|e| {
192 let ring_size = e.parameters().rlwe_n().0;
193 let ct_index = index / ring_size;
194 let coeff_index = index % ring_size;
195 let mut lwe_out = C::R::zeros(e.parameters().rlwe_n().0 + 1);
196 sample_extract(
197 &mut lwe_out,
198 &self.data[ct_index],
199 e.pbs_info().modop_rlweq(),
200 coeff_index,
201 );
202 lwe_out
203 })
204 }
205 }
206
207 pub(super) struct NonInteractiveBatchedFheBools<C> {
208 data: Vec<C>,
209 }
210
211 impl<M: MatrixEntity + MatrixMut<MatElement = u64>> From<&(Vec<M::R>, [u8; 32])>
212 for NonInteractiveBatchedFheBools<M>
213 where
214 <M as Matrix>::R: RowMut,
215 {
216 fn from(value: &(Vec<M::R>, [u8; 32])) -> Self {
217 BoolEvaluator::with_local(|e| {
218 let parameters = e.parameters();
219 let ring_size = parameters.rlwe_n().0;
220 let rlwe_q = parameters.rlwe_q();
221
222 let mut prng = DefaultSecureRng::new_seeded(value.1);
223 let rlwes = value
224 .0
225 .iter()
226 .map(|partb| {
227 let mut rlwe = M::zeros(2, ring_size);
228
229 // sample A
230 RandomFillUniformInModulus::random_fill(
231 &mut prng,
232 rlwe_q,
233 rlwe.get_row_mut(0),
234 );
235
236 // Copy over B
237 rlwe.get_row_mut(1).copy_from_slice(partb.as_ref());
238
239 rlwe
240 })
241 .collect_vec();
242 Self { data: rlwes }
243 })
244 }
245 }
246
247 impl<K> Encryptor<[bool], NonInteractiveBatchedFheBools<Mat>> for K
248 where
249 K: Encryptor<[bool], (Mat, [u8; 32])>,
250 {
251 fn encrypt(&self, m: &[bool]) -> NonInteractiveBatchedFheBools<Mat> {
252 NonInteractiveBatchedFheBools::from(&K::encrypt(&self, m))
253 }
254 }
255
256 impl<K> Encryptor<[bool], (Mat, [u8; 32])> for K
257 where
258 K: NonInteractiveMultiPartyClientKey,
259 <Mat as Matrix>::R:
260 TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
261 {
262 fn encrypt(&self, m: &[bool]) -> (Mat, [u8; 32]) {
263 BoolEvaluator::with_local(|e| {
264 DefaultSecureRng::with_local_mut(|rng| {
265 let parameters = e.parameters();
266 let ring_size = parameters.rlwe_n().0;
267
268 let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
269 .to_usize()
270 .unwrap();
271
272 let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
273 rng.fill_bytes(&mut seed);
274 let mut prng = DefaultSecureRng::new_seeded(seed);
275
276 let sk_u = self.sk_u_rlwe();
277
278 // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
279 let rlwes = (0..rlwe_count)
280 .map(|index| {
281 let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
282 m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
283 .iter()
284 .enumerate()
285 .for_each(|(i, v)| {
286 if *v {
287 message[i] = parameters.rlwe_q().true_el()
288 } else {
289 message[i] = parameters.rlwe_q().false_el()
290 }
291 });
292
293 // encrypt message
294 let mut rlwe_out =
295 <<Mat as Matrix>::R as RowEntity>::zeros(parameters.rlwe_n().0);
296
297 secret_key_encrypt_rlwe(
298 &message,
299 &mut rlwe_out,
300 &sk_u,
301 e.pbs_info().modop_rlweq(),
302 e.pbs_info().nttop_rlweq(),
303 &mut prng,
304 rng,
305 );
306
307 rlwe_out
308 })
309 .collect_vec();
310
311 (rlwes, seed)
312 })
313 })
314 }
315 }
316
317 impl KeySwitchWithId<Mat> for Mat {
318 fn key_switch(&self, user_id: usize) -> Mat {
319 BoolEvaluator::with_local(|e| {
320 let server_key = BOOL_SERVER_KEY.get().unwrap();
321 let ksk = server_key.ui_to_s_ksk(user_id);
322 let decomposer = e.ni_ui_to_s_ks_decomposer().as_ref().unwrap();
323
324 // perform key switch
325 key_switch(
326 self,
327 ksk.as_ref(),
328 ksk.shoup_repr(),
329 decomposer,
330 e.pbs_info().nttop_rlweq(),
331 e.pbs_info().modop_rlweq(),
332 )
333 })
334 }
335 }
336
337 impl<C> KeySwitchWithId<BatchedFheBools<C>> for NonInteractiveBatchedFheBools<C>
338 where
339 C: KeySwitchWithId<C>,
340 {
341 fn key_switch(&self, user_id: usize) -> BatchedFheBools<C> {
342 let data = self
343 .data
344 .iter()
345 .map(|c| c.key_switch(user_id))
346 .collect_vec();
347 BatchedFheBools { data }
348 }
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use impl_enc_dec::NonInteractiveBatchedFheBools;
355 use itertools::{izip, Itertools};
356 use num_traits::{FromPrimitive, PrimInt, ToPrimitive, Zero};
357 use rand::{thread_rng, RngCore};
358
359 use crate::{
360 backend::{GetModulus, Modulus},
361 bool::{evaluator::BooleanGates, keys::SinglePartyClientKey},
362 lwe::decrypt_lwe,
363 rgsw::decrypt_rlwe,
364 utils::{tests::Stats, TryConvertFrom1},
365 ArithmeticOps, Encoder, Encryptor, KeySwitchWithId, ModInit, MultiPartyDecryptor, NttInit,
366 Row, VectorOps,
367 };
368
369 use super::*;
370
371
372
373 #[test]
374 fn non_interactive_mp_bool_nand() {
375 set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
376 let mut seed = [0u8; 32];
377 thread_rng().fill_bytes(&mut seed);
378 set_common_reference_seed(seed);
379
380 let parties = 2;
381
382 let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
383
384 let key_shares = cks
385 .iter()
386 .enumerate()
387 .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
388 .collect_vec();
389
390 let seeded_server_key = aggregate_server_key_shares(&key_shares);
391 seeded_server_key.set_server_key();
392
393 let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
394 let rlwe_modop = parameters.default_rlwe_modop();
395
396 let ideal_sk_rlwe = ideal_sk_rlwe(&cks);
397
398 let mut m0 = false;
399 let mut m1 = true;
400
401 let mut ct0 = {
402 let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(vec![m0].as_slice());
403 let ct = ct.key_switch(0);
404 ct.extract(0)
405 };
406 let mut ct1 = {
407 let ct: NonInteractiveBatchedFheBools<_> = cks[1].encrypt(vec![m1].as_slice());
408 let ct = ct.key_switch(1);
409 ct.extract(0)
410 };
411
412 for _ in 0..1000 {
413 // let now = std::time::Instant::now();
414 let ct_out =
415 BoolEvaluator::with_local_mut(|e| e.xor(&ct0, &ct1, RuntimeServerKey::global()));
416 // println!("Time: {:?}", now.elapsed());
417
418 let decryption_shares = cks
419 .iter()
420 .map(|k| k.gen_decryption_share(&ct_out))
421 .collect_vec();
422 let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares);
423
424 let m_expected = m0 ^ m1;
425
426 {
427 let noise = measure_noise_lwe(
428 &ct_out,
429 parameters.rlwe_q().encode(m_expected),
430 &ideal_sk_rlwe,
431 &rlwe_modop,
432 );
433 println!("Noise: {noise}");
434 }
435
436 assert!(m_out == m_expected, "Expected {m_expected} but got {m_out}");
437
438 m1 = m0;
439 m0 = m_out;
440
441 ct1 = ct0;
442 ct0 = ct_out;
443 }
444 }
445
446 #[test]
447 fn trialtest() {
448 set_parameter_set(ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
449 set_common_reference_seed([2; 32]);
450
451 let parties = 2;
452
453 let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
454
455 let key_shares = cks
456 .iter()
457 .enumerate()
458 .map(|(user_index, ck)| gen_server_key_share(user_index, parties, ck))
459 .collect_vec();
460
461 let seeded_server_key = aggregate_server_key_shares(&key_shares);
462 seeded_server_key.set_server_key();
463
464 let m = vec![false, true];
465 let ct: NonInteractiveBatchedFheBools<_> = cks[0].encrypt(m.as_slice());
466 let ct = ct.key_switch(0);
467
468 let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
469 let nttop = NttBackendU64::new(parameters.rlwe_q(), parameters.rlwe_n().0);
470 let rlwe_q_modop = ModularOpsU64::new(*parameters.rlwe_q());
471
472 let mut ideal_rlwe_sk = vec![0i32; parameters.rlwe_n().0];
473 cks.iter().for_each(|k| {
474 let sk_rlwe = k.sk_rlwe();
475 izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
476 *a = *a + b;
477 });
478 });
479
480 let message = m
481 .iter()
482 .map(|b| parameters.rlwe_q().encode(*b))
483 .collect_vec();
484
485 let mut m_out = vec![0u64; parameters.rlwe_n().0];
486 decrypt_rlwe(
487 &ct.data[0],
488 &ideal_rlwe_sk,
489 &mut m_out,
490 &nttop,
491 &rlwe_q_modop,
492 );
493
494 let mut diff = m_out;
495 rlwe_q_modop.elwise_sub_mut(diff.as_mut_slice(), message.as_ref());
496
497 let mut stats = Stats::new();
498 stats.add_more(&Vec::<i64>::try_convert_from(
499 diff.as_slice(),
500 parameters.rlwe_q(),
501 ));
502 println!("Noise: {}", stats.std_dev().abs().log2());
503 }
504}