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