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/// Batch of bool ciphertexts stored as vector of RLWE ciphertext under user j's
183/// RLWE secret `u_j`
184///
185/// To use the bool ciphertexts in multi-party protocol first key switch the
186/// ciphertexts from u_j to ideal RLWE secret `s` with
187/// `self.key_switch(user_id)` where `user_id` is user j's id. Key switch
188/// returns `BatchedFheBools` that stored key vector of key switched RLWE
189/// ciphertext.
190pub struct NonInteractiveBatchedFheBools<C> {
191 data: Vec<C>,
192}
193
194/// Batch of Bool cipphertexts stored as vector of RLWE ciphertexts under the
195/// ideal RLWE secret key `s` of the protocol
196///
197/// Bool ciphertext at `index` can be extracted from the coefficient at `index %
198/// N` of `index / N`th RLWE ciphertext.
199///
200/// To extract bool ciphertext at `index` as LWE ciphertext use
201/// `self.extract(index)`
202pub struct BatchedFheBools<C> {
203 pub(in super::super) data: Vec<C>,
204}
205
206/// Non interactive multi-party specfic encryptor decryptor routines
207mod impl_enc_dec {
208 use crate::{
209 bool::{evaluator::BoolEncoding, keys::NonInteractiveMultiPartyClientKey},
210 multi_party::{
211 multi_party_aggregate_decryption_shares_and_decrypt, multi_party_decryption_share,
212 },
213 pbs::{sample_extract, 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 // Implement `extract` to extract Bool LWE ciphertext at `index` from
228 // `BatchedFheBools`
229 impl<C: MatrixMut<MatElement = u64>> BatchedFheBools<C>
230 where
231 C::R: RowEntity + RowMut,
232 {
233 pub fn extract(&self, index: usize) -> C::R {
234 BoolEvaluator::with_local(|e| {
235 let ring_size = e.parameters().rlwe_n().0;
236 let ct_index = index / ring_size;
237 let coeff_index = index % ring_size;
238 let mut lwe_out = C::R::zeros(e.parameters().rlwe_n().0 + 1);
239 sample_extract(
240 &mut lwe_out,
241 &self.data[ct_index],
242 e.pbs_info().modop_rlweq(),
243 coeff_index,
244 );
245 lwe_out
246 })
247 }
248 }
249
250 impl<M: MatrixEntity + MatrixMut<MatElement = u64>> From<&(Vec<M::R>, [u8; 32])>
251 for NonInteractiveBatchedFheBools<M>
252 where
253 <M as Matrix>::R: RowMut,
254 {
255 /// Derive `NonInteractiveBatchedFheBools` from a vector seeded RLWE
256 /// ciphertexts (Vec<RLWE>, Seed)
257 ///
258 /// Unseed the RLWE ciphertexts and store them as vector RLWE
259 /// ciphertexts in `NonInteractiveBatchedFheBools`
260 fn from(value: &(Vec<M::R>, [u8; 32])) -> Self {
261 BoolEvaluator::with_local(|e| {
262 let parameters = e.parameters();
263 let ring_size = parameters.rlwe_n().0;
264 let rlwe_q = parameters.rlwe_q();
265
266 let mut prng = DefaultSecureRng::new_seeded(value.1);
267 let rlwes = value
268 .0
269 .iter()
270 .map(|partb| {
271 let mut rlwe = M::zeros(2, ring_size);
272
273 // sample A
274 RandomFillUniformInModulus::random_fill(
275 &mut prng,
276 rlwe_q,
277 rlwe.get_row_mut(0),
278 );
279
280 // Copy over B
281 rlwe.get_row_mut(1).copy_from_slice(partb.as_ref());
282
283 rlwe
284 })
285 .collect_vec();
286 Self { data: rlwes }
287 })
288 }
289 }
290
291 impl<K> Encryptor<[bool], NonInteractiveBatchedFheBools<Mat>> for K
292 where
293 K: Encryptor<[bool], (Mat, [u8; 32])>,
294 {
295 /// Encrypt a vector bool of arbitrary length as vector of unseeded RLWE
296 /// ciphertexts in `NonInteractiveBatchedFheBools`
297 fn encrypt(&self, m: &[bool]) -> NonInteractiveBatchedFheBools<Mat> {
298 NonInteractiveBatchedFheBools::from(&K::encrypt(&self, m))
299 }
300 }
301
302 impl<K> Encryptor<[bool], (Vec<<Mat as Matrix>::R>, [u8; 32])> for K
303 where
304 K: NonInteractiveMultiPartyClientKey,
305 <Mat as Matrix>::R:
306 TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
307 {
308 /// Encrypt a vector of bool of arbitrary length as vector of seeded
309 /// RLWE ciphertexts and returns (Vec<RLWE>, Seed)
310 fn encrypt(&self, m: &[bool]) -> (Mat, [u8; 32]) {
311 BoolEvaluator::with_local(|e| {
312 DefaultSecureRng::with_local_mut(|rng| {
313 let parameters = e.parameters();
314 let ring_size = parameters.rlwe_n().0;
315
316 let rlwe_count = ((m.len() as f64 / ring_size as f64).ceil())
317 .to_usize()
318 .unwrap();
319
320 let mut seed = <DefaultSecureRng as NewWithSeed>::Seed::default();
321 rng.fill_bytes(&mut seed);
322 let mut prng = DefaultSecureRng::new_seeded(seed);
323
324 let sk_u = self.sk_u_rlwe();
325
326 // encrypt `m` into ceil(len(m)/N) RLWE ciphertexts
327 let rlwes = (0..rlwe_count)
328 .map(|index| {
329 let mut message = vec![<Mat as Matrix>::MatElement::zero(); ring_size];
330 m[(index * ring_size)..std::cmp::min(m.len(), (index + 1) * ring_size)]
331 .iter()
332 .enumerate()
333 .for_each(|(i, v)| {
334 if *v {
335 message[i] = parameters.rlwe_q().true_el()
336 } else {
337 message[i] = parameters.rlwe_q().false_el()
338 }
339 });
340
341 // encrypt message
342 let mut rlwe_out =
343 <<Mat as Matrix>::R as RowEntity>::zeros(parameters.rlwe_n().0);
344
345 seeded_secret_key_encrypt_rlwe(
346 &message,
347 &mut rlwe_out,
348 &sk_u,
349 e.pbs_info().modop_rlweq(),
350 e.pbs_info().nttop_rlweq(),
351 &mut prng,
352 rng,
353 );
354
355 rlwe_out
356 })
357 .collect_vec();
358
359 (rlwes, seed)
360 })
361 })
362 }
363 }
364
365 impl<K> MultiPartyDecryptor<bool, <Mat as Matrix>::R> for K
366 where
367 K: NonInteractiveMultiPartyClientKey,
368 <Mat as Matrix>::R:
369 TryConvertFrom1<[K::Element], CiphertextModulus<<Mat as Matrix>::MatElement>>,
370 {
371 type DecryptionShare = <Mat as Matrix>::MatElement;
372
373 fn gen_decryption_share(&self, c: &<Mat as Matrix>::R) -> Self::DecryptionShare {
374 BoolEvaluator::with_local(|e| {
375 DefaultSecureRng::with_local_mut(|rng| {
376 multi_party_decryption_share(
377 c,
378 self.sk_rlwe().as_slice(),
379 e.pbs_info().modop_rlweq(),
380 rng,
381 )
382 })
383 })
384 }
385
386 fn aggregate_decryption_shares(
387 &self,
388 c: &<Mat as Matrix>::R,
389 shares: &[Self::DecryptionShare],
390 ) -> bool {
391 BoolEvaluator::with_local(|e| {
392 let noisy_m = multi_party_aggregate_decryption_shares_and_decrypt(
393 c,
394 shares,
395 e.pbs_info().modop_rlweq(),
396 );
397
398 e.pbs_info().rlwe_q().decode(noisy_m)
399 })
400 }
401 }
402
403 impl KeySwitchWithId<Mat> for Mat {
404 /// Key switch RLWE ciphertext `Self` from user j's RLWE secret u_j
405 /// to ideal RLWE secret `s` of non-interactive multi-party protocol.
406 ///
407 /// - user_id: user j's user_id in the protocol
408 fn key_switch(&self, user_id: usize) -> Mat {
409 BoolEvaluator::with_local(|e| {
410 assert!(self.dimension() == (2, e.parameters().rlwe_n().0));
411 let server_key = BOOL_SERVER_KEY.get().unwrap();
412 let ksk = server_key.ui_to_s_ksk(user_id);
413 let decomposer = e.ni_ui_to_s_ks_decomposer().as_ref().unwrap();
414
415 // perform key switch
416 rlwe_key_switch(
417 self,
418 ksk.as_ref(),
419 ksk.shoup_repr(),
420 decomposer,
421 e.pbs_info().nttop_rlweq(),
422 e.pbs_info().modop_rlweq(),
423 )
424 })
425 }
426 }
427
428 impl<C> KeySwitchWithId<BatchedFheBools<C>> for NonInteractiveBatchedFheBools<C>
429 where
430 C: KeySwitchWithId<C>,
431 {
432 /// Key switch `Self`'s vector of RLWE ciphertexts from user j's RLWE
433 /// secret u_j to ideal RLWE secret `s` of non-interactive
434 /// multi-party protocol.
435 ///
436 /// Returns vector of key switched RLWE ciphertext as `BatchedFheBools`
437 /// which can then be used to extract individual Bool LWE ciphertexts.
438 ///
439 /// - user_id: user j's user_id in the protocol
440 fn key_switch(&self, user_id: usize) -> BatchedFheBools<C> {
441 let data = self
442 .data
443 .iter()
444 .map(|c| c.key_switch(user_id))
445 .collect_vec();
446 BatchedFheBools { data }
447 }
448 }
449}