phantom-zone/src/bool/ni_mp_api.rs
src/bool/ni_mp_api.rsRaw
1use std::{borrow::Borrow, 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, NI_2P, NI_4P_HB_FR, NI_8P},
19 ClientKey,
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 static ACTIVE_PARAMETER_SET: RefCell<Option<ParameterSelector>> = RefCell::new(None);
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
39#[derive(Copy, Clone)]
40pub enum ParameterSelector {
41 NonInteractiveLTE2Party,
42 NonInteractiveLTE4Party,
43 NonInteractiveLTE8Party,
44}
45
46pub fn set_parameter_set(select: ParameterSelector) {
47 match select {
48 ParameterSelector::NonInteractiveLTE2Party => {
49 BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_2P)));
50 }
51 ParameterSelector::NonInteractiveLTE4Party => {
52 BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_4P_HB_FR)));
53 }
54 ParameterSelector::NonInteractiveLTE8Party => {
55 BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_8P)));
56 }
57 }
58 ACTIVE_PARAMETER_SET.with_borrow_mut(|v| *v = Some(select));
59}
60
61pub fn get_active_parameter_set() -> ParameterSelector {
62 ACTIVE_PARAMETER_SET
63 .borrow()
64 .take()
65 .expect("Parameters not set")
66}
67
68pub fn set_common_reference_seed(seed: [u8; 32]) {
69 BoolEvaluator::with_local(|e| {
70 assert_eq!(
71 e.parameters().variant(),
72 &ParameterVariant::NonInteractiveMultiParty,
73 "Set parameters do not support Non interactive multi-party"
74 );
75 });
76
77 assert!(
78 MULTI_PARTY_CRS
79 .set(NonInteractiveMultiPartyCrs { seed: seed })
80 .is_ok(),
81 "Attempted to set MP SEED twice."
82 )
83}
84
85pub fn gen_client_key() -> ClientKey {
86 BoolEvaluator::with_local(|e| e.client_key())
87}
88
89pub fn gen_server_key_share(
90 user_id: usize,
91 total_users: usize,
92 client_key: &ClientKey,
93) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
94 Vec<Vec<u64>>,
95 BoolParameters<u64>,
96 NonInteractiveMultiPartyCrs<[u8; 32]>,
97> {
98 BoolEvaluator::with_local(|e| {
99 let cr_seed = NonInteractiveMultiPartyCrs::global();
100 e.gen_non_interactive_multi_party_key_share(cr_seed, user_id, total_users, client_key)
101 })
102}
103
104pub fn aggregate_server_key_shares(
105 shares: &[CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
106 Vec<Vec<u64>>,
107 BoolParameters<u64>,
108 NonInteractiveMultiPartyCrs<[u8; 32]>,
109 >],
110) -> SeededNonInteractiveMultiPartyServerKey<
111 Vec<Vec<u64>>,
112 NonInteractiveMultiPartyCrs<[u8; 32]>,
113 BoolParameters<u64>,
114> {
115 BoolEvaluator::with_local(|e| {
116 let cr_seed = NonInteractiveMultiPartyCrs::global();
117 e.aggregate_non_interactive_multi_party_server_key_shares(cr_seed, shares)
118 })
119}
120
121impl
122 SeededNonInteractiveMultiPartyServerKey<
123 Vec<Vec<u64>>,
124 NonInteractiveMultiPartyCrs<[u8; 32]>,
125 BoolParameters<u64>,
126 >
127{
128 pub fn set_server_key(&self) {
129 let eval_key = NonInteractiveServerKeyEvaluationDomain::<
130 _,
131 BoolParameters<u64>,
132 DefaultSecureRng,
133 NttBackendU64,
134 >::from(self);
135 assert!(
136 BOOL_SERVER_KEY
137 .set(ShoupNonInteractiveServerKeyEvaluationDomain::from(eval_key))
138 .is_ok(),
139 "Attempted to set server key twice!"
140 );
141 }
142}
143
144impl Global for NonInteractiveMultiPartyCrs<[u8; 32]> {
145 fn global() -> &'static Self {
146 MULTI_PARTY_CRS
147 .get()
148 .expect("Non-interactive multi-party common reference string not set")
149 }
150}
151
152// BOOL EVALUATOR //
153impl WithLocal for BoolEvaluator {
154 fn with_local<F, R>(func: F) -> R
155 where
156 F: Fn(&Self) -> R,
157 {
158 BOOL_EVALUATOR.with_borrow(|s| func(s.as_ref().expect("Parameters not set")))
159 }
160
161 fn with_local_mut<F, R>(func: F) -> R
162 where
163 F: Fn(&mut Self) -> R,
164 {
165 BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
166 }
167
168 fn with_local_mut_mut<F, R>(func: &mut F) -> R
169 where
170 F: FnMut(&mut Self) -> R,
171 {
172 BOOL_EVALUATOR.with_borrow_mut(|s| func(s.as_mut().expect("Parameters not set")))
173 }
174}
175
176pub(crate) type RuntimeServerKey = ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>;
177impl Global for RuntimeServerKey {
178 fn global() -> &'static Self {
179 BOOL_SERVER_KEY.get().expect("Server key not set!")
180 }
181}
182
183/// `Self::data` stores collection of seeded RLWE ciphertexts encrypted unser user j's RLWE secret `u_j`.
184pub struct NonInteractiveSeededFheBools<C, S> {
185 data: Vec<C>,
186 seed: S,
187 count: usize,
188}
189
190/// Batch of bool ciphertexts stored as vector of RLWE ciphertext under user j's
191/// RLWE secret `u_j`
192///
193/// To use the bool ciphertexts in multi-party protocol first key switch the
194/// ciphertexts from u_j to ideal RLWE secret `s` with
195/// `self.key_switch(user_id)` where `user_id` is user j's id. Key switch
196/// returns `BatchedFheBools` which stores vector of key switched RLWE
197/// ciphertext.
198pub struct NonInteractiveBatchedFheBools<C> {
199 data: Vec<C>,
200 count: usize,
201}
202
203/// Non interactive multi-party specfic encryptor decryptor routines
204mod impl_enc_dec {
205 use crate::{
206 bool::{
207 common_mp_enc_dec::BatchedFheBools, evaluator::BoolEncoding,
208 keys::NonInteractiveMultiPartyClientKey,
209 },
210 multi_party::{
211 multi_party_aggregate_decryption_shares_and_decrypt, multi_party_decryption_share,
212 },
213 pbs::{PbsInfo, WithShoupRepr},
214 random::{NewWithSeed, RandomFillUniformInModulus},
215 rgsw::{rlwe_key_switch, seeded_secret_key_encrypt_rlwe},
216 utils::TryConvertFrom1,
217 Encryptor, KeySwitchWithId, Matrix, MatrixEntity, MatrixMut, MultiPartyDecryptor,
218 RowEntity, RowMut,
219 };
220 use itertools::Itertools;
221 use num_traits::{ToPrimitive, Zero};
222
223 use super::*;
224
225 type Mat = Vec<Vec<u64>>;
226
227 impl<C, S> NonInteractiveSeededFheBools<C, S> {
228 /// Unseed `Self`'s collection of RLWE ciphertexts into `NonInteractiveBatchedFheBools`
229 pub fn unseed<M>(&self) -> NonInteractiveBatchedFheBools<M>
230 where
231 NonInteractiveBatchedFheBools<M>: for<'a> From<&'a Self>,
232 {
233 NonInteractiveBatchedFheBools::from(self)
234 }
235 }
236
237 impl<C, S> From<NonInteractiveSeededFheBools<C, S>> for (Vec<C>, S) {
238 fn from(value: NonInteractiveSeededFheBools<C, S>) -> Self {
239 (value.data, value.seed)
240 }
241 }
242
243 impl<K> Encryptor<[bool], NonInteractiveSeededFheBools<<Mat as Matrix>::R, [u8; 32]>> for K
244 where
245 K: NonInteractiveMultiPartyClientKey,
246 <Mat as Matrix>::R:
247 TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
248 {
249 /// Encrypt a vector of bool of arbitrary length as vector of seeded
250 /// RLWE ciphertexts and returns (Vec<RLWE>, Seed)
251 fn encrypt(
252 &self,
253 m: &[bool],
254 ) -> NonInteractiveSeededFheBools<<Mat as Matrix>::R, [u8; 32]> {
255 BoolEvaluator::with_local(|e| {
256 DefaultSecureRng::with_local_mut(|rng| {
257 let parameters = e.parameters();
258 let ring_size = parameters.rlwe_n().0;
259
260 let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
261 .to_usize()
262 .unwrap();
263
264 let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
265 rng.fill_bytes(&mut seed);
266 let mut prng = DefaultSecureRng::new_seeded(seed);
267
268 let sk_u = self.sk_u_rlwe();
269
270 // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
271 let rlwes = (0..rlwe_count)
272 .map(|index| {
273 let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
274 m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
275 .iter()
276 .enumerate()
277 .for_each(|(i, v)| {
278 if *v {
279 message[i] = parameters.rlwe_q().true_el()
280 } else {
281 message[i] = parameters.rlwe_q().false_el()
282 }
283 });
284
285 // encrypt message
286 let mut rlwe_out =
287 <<Mat as Matrix>::R as RowEntity>::zeros(parameters.rlwe_n().0);
288
289 seeded_secret_key_encrypt_rlwe(
290 &message,
291 &mut rlwe_out,
292 &sk_u,
293 e.pbs_info().modop_rlweq(),
294 e.pbs_info().nttop_rlweq(),
295 &mut prng,
296 rng,
297 );
298
299 rlwe_out
300 })
301 .collect_vec();
302
303 NonInteractiveSeededFheBools {
304 data: rlwes,
305 seed,
306 count: m.len(),
307 }
308 })
309 })
310 }
311 }
312
313 impl<M: MatrixEntity + MatrixMut<MatElement = u64>>
314 From<&NonInteractiveSeededFheBools<<Mat as Matrix>::R, [u8; 32]>>
315 for NonInteractiveBatchedFheBools<M>
316 where
317 <M as Matrix>::R: RowMut,
318 {
319 /// Derive `NonInteractiveBatchedFheBools` from a vector seeded RLWE
320 /// ciphertexts (Vec<RLWE>, Seed)
321 ///
322 /// Unseed the RLWE ciphertexts and store them as vector RLWE
323 /// ciphertexts in `NonInteractiveBatchedFheBools`
324 fn from(value: &NonInteractiveSeededFheBools<<Mat as Matrix>::R, [u8; 32]>) -> Self {
325 BoolEvaluator::with_local(|e| {
326 let parameters = e.parameters();
327 let ring_size = parameters.rlwe_n().0;
328 let rlwe_q = parameters.rlwe_q();
329
330 let mut prng = DefaultSecureRng::new_seeded(value.seed);
331 let rlwes = value
332 .data
333 .iter()
334 .map(|partb| {
335 let mut rlwe = M::zeros(2, ring_size);
336
337 // sample A
338 RandomFillUniformInModulus::random_fill(
339 &mut prng,
340 rlwe_q,
341 rlwe.get_row_mut(0),
342 );
343
344 // Copy over B
345 rlwe.get_row_mut(1).copy_from_slice(partb.as_ref());
346
347 rlwe
348 })
349 .collect_vec();
350 Self {
351 data: rlwes,
352 count: value.count,
353 }
354 })
355 }
356 }
357
358 impl KeySwitchWithId<Mat> for Mat {
359 /// Key switch RLWE ciphertext `Self` from user j's RLWE secret u_j
360 /// to ideal RLWE secret `s` of non-interactive multi-party protocol.
361 ///
362 /// - user_id: user j's user_id in the protocol
363 fn key_switch(&self, user_id: usize) -> Mat {
364 BoolEvaluator::with_local(|e| {
365 assert!(self.dimension() == (2, e.parameters().rlwe_n().0));
366 let server_key = BOOL_SERVER_KEY.get().unwrap();
367 let ksk = server_key.ui_to_s_ksk(user_id);
368 let decomposer = e.ni_ui_to_s_ks_decomposer().as_ref().unwrap();
369
370 // perform key switch
371 rlwe_key_switch(
372 self,
373 ksk.as_ref(),
374 ksk.shoup_repr(),
375 decomposer,
376 e.pbs_info().nttop_rlweq(),
377 e.pbs_info().modop_rlweq(),
378 )
379 })
380 }
381 }
382
383 impl<C> KeySwitchWithId<BatchedFheBools<C>> for NonInteractiveBatchedFheBools<C>
384 where
385 C: KeySwitchWithId<C>,
386 {
387 /// Key switch `Self`'s vector of RLWE ciphertexts from user j's RLWE
388 /// secret u_j to ideal RLWE secret `s` of non-interactive
389 /// multi-party protocol.
390 ///
391 /// Returns vector of key switched RLWE ciphertext as `BatchedFheBools`
392 /// which can then be used to extract individual Bool LWE ciphertexts.
393 ///
394 /// - user_id: user j's user_id in the protocol
395 fn key_switch(&self, user_id: usize) -> BatchedFheBools<C> {
396 let data = self
397 .data
398 .iter()
399 .map(|c| c.key_switch(user_id))
400 .collect_vec();
401 BatchedFheBools::new(data, self.count)
402 }
403 }
404
405 impl<K> MultiPartyDecryptor<bool, <Mat as Matrix>::R> for K
406 where
407 K: NonInteractiveMultiPartyClientKey,
408 <Mat as Matrix>::R:
409 TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
410 {
411 type DecryptionShare = <Mat as Matrix>::MatElement;
412
413 fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
414 BoolEvaluator::with_local(|e| {
415 DefaultSecureRng::with_local_mut(|rng| {
416 multi_party_decryption_share(
417 c,
418 self.sk_rlwe().as_slice(),
419 e.pbs_info().modop_rlweq(),
420 rng,
421 )
422 })
423 })
424 }
425
426 fn aggregate_decryption_shares(
427 &self,
428 c: &<Mat as Matrix>::R,
429 shares: &[Self::DecryptionShare],
430 ) -> bool {
431 BoolEvaluator::with_local(|e| {
432 let noisy_m = multi_party_aggregate_decryption_shares_and_decrypt(
433 c,
434 shares,
435 e.pbs_info().modop_rlweq(),
436 );
437
438 e.pbs_info().rlwe_q().decode(noisy_m)
439 })
440 }
441 }
442}