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