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