phantom-zone/src/bool/evaluator.rs
src/bool/evaluator.rsRaw
1use std::{
2 collections::HashMap,
3 fmt::{Debug, Display},
4 marker::PhantomData,
5 usize,
6};
7
8use itertools::{izip, Itertools};
9use num_traits::{FromPrimitive, One, PrimInt, ToPrimitive, WrappingAdd, WrappingSub, Zero};
10use rand_distr::uniform::SampleUniform;
11
12use crate::{
13 backend::{ArithmeticOps, GetModulus, ModInit, Modulus, ShoupMatrixFMA, VectorOps},
14 bool::parameters::ParameterVariant,
15 decomposer::{Decomposer, DefaultDecomposer, NumInfo, RlweDecomposer},
16 lwe::{decrypt_lwe, encrypt_lwe, seeded_lwe_ksk_keygen},
17 multi_party::{
18 non_interactive_ksk_gen, non_interactive_ksk_zero_encryptions_for_other_party_i,
19 public_key_share,
20 },
21 ntt::{self, Ntt, NttBackendU64, NttInit},
22 pbs::{pbs, sample_extract, PbsInfo, PbsKey, WithShoupRepr},
23 random::{
24 DefaultSecureRng, NewWithSeed, RandomFill, RandomFillGaussianInModulus,
25 RandomFillUniformInModulus, RandomGaussianElementInModulus,
26 },
27 rgsw::{
28 decrypt_rlwe, generate_auto_map, public_key_encrypt_rgsw, rgsw_by_rgsw_inplace,
29 rgsw_x_rgsw_scratch_rows, rlwe_auto, rlwe_auto_scratch_rows, rlwe_x_rgsw_scratch_rows,
30 secret_key_encrypt_rgsw, seeded_auto_key_gen, RgswCiphertext, RgswCiphertextMutRef,
31 RgswCiphertextRef, RuntimeScratchMutRef,
32 },
33 utils::{
34 encode_x_pow_si_with_emebedding_factor, fill_random_ternary_secret_with_hamming_weight,
35 generate_prime, mod_exponent, puncture_p_rng, Global, TryConvertFrom1, WithLocal,
36 },
37 Encoder, Matrix, MatrixEntity, MatrixMut, RowEntity, RowMut,
38};
39
40use super::{
41 keys::{
42 ClientKey, CommonReferenceSeededCollectivePublicKeyShare,
43 CommonReferenceSeededMultiPartyServerKeyShare,
44 CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare,
45 InteractiveMultiPartyClientKey, NonInteractiveMultiPartyClientKey,
46 SeededMultiPartyServerKey, SeededNonInteractiveMultiPartyServerKey,
47 SeededSinglePartyServerKey, SinglePartyClientKey,
48 },
49 parameters::{BoolParameters, CiphertextModulus, DecompositionCount, DoubleDecomposerParams},
50};
51
52/// Common reference seed used for Interactive multi-party,
53///
54/// Seeds for public key shares and differents parts of server key shares are
55/// derived from common reference seed with different puncture rountines.
56///
57/// ## Punctures
58///
59/// Initial Seed:
60/// Puncture 1 -> Public key share seed
61/// Puncture 2 -> Main server key share seed
62/// Puncture 1 -> Auto keys cipertexts seed
63/// Puncture 2 -> LWE ksk seed
64#[derive(Clone, PartialEq)]
65pub struct MultiPartyCrs<S> {
66 pub(super) seed: S,
67}
68
69impl MultiPartyCrs<[u8; 32]> {
70 pub(super) fn random() -> Self {
71 DefaultSecureRng::with_local_mut(|rng| {
72 let mut seed = [0u8; 32];
73 rng.fill_bytes(&mut seed);
74 Self { seed }
75 })
76 }
77}
78impl<S: Default + Copy> MultiPartyCrs<S> {
79 /// Seed to generate public key share
80 fn public_key_share_seed<Rng: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
81 let mut prng = Rng::new_with_seed(self.seed);
82 puncture_p_rng(&mut prng, 1)
83 }
84
85 /// Main server key share seed
86 fn key_seed<Rng: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
87 let mut prng = Rng::new_with_seed(self.seed);
88 puncture_p_rng(&mut prng, 2)
89 }
90
91 pub(super) fn auto_keys_cts_seed<Rng: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
92 let mut key_prng = Rng::new_with_seed(self.key_seed::<Rng>());
93 puncture_p_rng(&mut key_prng, 1)
94 }
95
96 pub(super) fn lwe_ksk_cts_seed_seed<Rng: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
97 let mut key_prng = Rng::new_with_seed(self.key_seed::<Rng>());
98 puncture_p_rng(&mut key_prng, 2)
99 }
100}
101
102/// Common reference seed used for non-interactive multi-party.
103///
104/// Initial Seed
105/// Puncture 1 -> Key Seed
106/// Puncture 1 -> Rgsw ciphertext seed
107/// Puncture l+1 -> Seed for zero encs and non-interactive
108/// multi-party RGSW ciphertexts of
109/// l^th LWE index.
110/// Puncture 2 -> auto keys seed
111/// Puncture 3 -> Lwe key switching key seed
112/// Puncture 2 -> user specific seed for u_j to s ksk
113/// Punture j+1 -> user j's seed
114#[derive(Clone)]
115pub struct NonInteractiveMultiPartyCrs<S> {
116 pub(super) seed: S,
117}
118
119impl NonInteractiveMultiPartyCrs<[u8; 32]> {
120 pub(super) fn random() -> Self {
121 DefaultSecureRng::with_local_mut(|rng| {
122 let mut seed = [0u8; 32];
123 rng.fill_bytes(&mut seed);
124 Self { seed }
125 })
126 }
127}
128
129impl<S: Default + Copy> NonInteractiveMultiPartyCrs<S> {
130 fn key_seed<R: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
131 let mut p_rng = R::new_with_seed(self.seed);
132 puncture_p_rng(&mut p_rng, 1)
133 }
134
135 pub(crate) fn ni_rgsw_cts_main_seed<R: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
136 let mut p_rng = R::new_with_seed(self.key_seed::<R>());
137 puncture_p_rng(&mut p_rng, 1)
138 }
139
140 pub(crate) fn ni_rgsw_ct_seed_for_index<R: NewWithSeed<Seed = S> + RandomFill<S>>(
141 &self,
142 lwe_index: usize,
143 ) -> S {
144 let mut p_rng = R::new_with_seed(self.ni_rgsw_cts_main_seed::<R>());
145 puncture_p_rng(&mut p_rng, lwe_index + 1)
146 }
147
148 pub(crate) fn auto_keys_cts_seed<R: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
149 let mut p_rng = R::new_with_seed(self.key_seed::<R>());
150 puncture_p_rng(&mut p_rng, 2)
151 }
152
153 pub(crate) fn lwe_ksk_cts_seed<R: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
154 let mut p_rng = R::new_with_seed(self.key_seed::<R>());
155 puncture_p_rng(&mut p_rng, 3)
156 }
157
158 fn ui_to_s_ks_seed<R: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
159 let mut p_rng = R::new_with_seed(self.seed);
160 puncture_p_rng(&mut p_rng, 2)
161 }
162
163 pub(crate) fn ui_to_s_ks_seed_for_user_i<R: NewWithSeed<Seed = S> + RandomFill<S>>(
164 &self,
165 user_i: usize,
166 ) -> S {
167 let ks_seed = self.ui_to_s_ks_seed::<R>();
168 let mut p_rng = R::new_with_seed(ks_seed);
169
170 puncture_p_rng(&mut p_rng, user_i + 1)
171 }
172}
173
174pub(crate) trait BooleanGates {
175 type Ciphertext: RowEntity;
176 type Key;
177
178 fn and_inplace(&mut self, c0: &mut Self::Ciphertext, c1: &Self::Ciphertext, key: &Self::Key);
179 fn nand_inplace(&mut self, c0: &mut Self::Ciphertext, c1: &Self::Ciphertext, key: &Self::Key);
180 fn or_inplace(&mut self, c0: &mut Self::Ciphertext, c1: &Self::Ciphertext, key: &Self::Key);
181 fn nor_inplace(&mut self, c0: &mut Self::Ciphertext, c1: &Self::Ciphertext, key: &Self::Key);
182 fn xor_inplace(&mut self, c0: &mut Self::Ciphertext, c1: &Self::Ciphertext, key: &Self::Key);
183 fn xnor_inplace(&mut self, c0: &mut Self::Ciphertext, c1: &Self::Ciphertext, key: &Self::Key);
184 fn not_inplace(&mut self, c: &mut Self::Ciphertext);
185
186 fn and(
187 &mut self,
188 c0: &Self::Ciphertext,
189 c1: &Self::Ciphertext,
190 key: &Self::Key,
191 ) -> Self::Ciphertext;
192 fn nand(
193 &mut self,
194 c0: &Self::Ciphertext,
195 c1: &Self::Ciphertext,
196 key: &Self::Key,
197 ) -> Self::Ciphertext;
198 fn or(
199 &mut self,
200 c0: &Self::Ciphertext,
201 c1: &Self::Ciphertext,
202 key: &Self::Key,
203 ) -> Self::Ciphertext;
204 fn nor(
205 &mut self,
206 c0: &Self::Ciphertext,
207 c1: &Self::Ciphertext,
208 key: &Self::Key,
209 ) -> Self::Ciphertext;
210 fn xor(
211 &mut self,
212 c0: &Self::Ciphertext,
213 c1: &Self::Ciphertext,
214 key: &Self::Key,
215 ) -> Self::Ciphertext;
216 fn xnor(
217 &mut self,
218 c0: &Self::Ciphertext,
219 c1: &Self::Ciphertext,
220 key: &Self::Key,
221 ) -> Self::Ciphertext;
222 fn not(&mut self, c: &Self::Ciphertext) -> Self::Ciphertext;
223}
224
225struct ScratchMemory<M>
226where
227 M: Matrix,
228{
229 lwe_vector: M::R,
230 decomposition_matrix: M,
231}
232
233impl<M: MatrixEntity> ScratchMemory<M>
234where
235 M::R: RowEntity,
236{
237 fn new(parameters: &BoolParameters<M::MatElement>) -> Self {
238 // Vector to store LWE ciphertext with LWE dimesnion n
239 let lwe_vector = M::R::zeros(parameters.lwe_n().0 + 1);
240
241 // PBS perform two operations at runtime: RLWE x RGW and RLWE auto. Since the
242 // operations are performed serially same scratch space can be used for both.
243 // Hence we create scratch space that contains maximum amount of rows that
244 // suffices for RLWE x RGSW and RLWE auto
245 let decomposition_matrix = M::zeros(
246 std::cmp::max(
247 rlwe_x_rgsw_scratch_rows(parameters.rlwe_by_rgsw_decomposition_params()),
248 rlwe_auto_scratch_rows(parameters.auto_decomposition_param()),
249 ),
250 parameters.rlwe_n().0,
251 );
252
253 Self {
254 lwe_vector,
255 decomposition_matrix,
256 }
257 }
258}
259
260pub(super) trait BoolEncoding {
261 type Element;
262 fn true_el(&self) -> Self::Element;
263 fn false_el(&self) -> Self::Element;
264 fn qby4(&self) -> Self::Element;
265 fn decode(&self, m: Self::Element) -> bool;
266}
267
268impl<T> BoolEncoding for CiphertextModulus<T>
269where
270 CiphertextModulus<T>: Modulus<Element = T>,
271 T: PrimInt,
272{
273 type Element = T;
274
275 fn qby4(&self) -> Self::Element {
276 if self.is_native() {
277 T::one() << (CiphertextModulus::<T>::_bits() - 2)
278 } else {
279 self.q().unwrap() >> 2
280 }
281 }
282 /// Q/8
283 fn true_el(&self) -> Self::Element {
284 if self.is_native() {
285 T::one() << (CiphertextModulus::<T>::_bits() - 3)
286 } else {
287 self.q().unwrap() >> 3
288 }
289 }
290 /// -Q/8
291 fn false_el(&self) -> Self::Element {
292 self.largest_unsigned_value() - self.true_el() + T::one()
293 }
294 fn decode(&self, m: Self::Element) -> bool {
295 let qby8 = self.true_el();
296 let m = (((m + qby8).to_f64().unwrap() * 4.0f64) / self.q_as_f64().unwrap()).round()
297 as usize
298 % 4usize;
299
300 if m == 0 {
301 return false;
302 } else if m == 1 {
303 return true;
304 } else {
305 panic!("Incorrect bool decryption. Got m={m} but expected m to be 0 or 1")
306 }
307 }
308}
309
310impl<B> Encoder<bool, B::Element> for B
311where
312 B: BoolEncoding,
313{
314 fn encode(&self, v: bool) -> B::Element {
315 if v {
316 self.true_el()
317 } else {
318 self.false_el()
319 }
320 }
321}
322
323pub(super) struct BoolPbsInfo<M: Matrix, Ntt, RlweModOp, LweModOp> {
324 auto_decomposer: DefaultDecomposer<M::MatElement>,
325 rlwe_rgsw_decomposer: (
326 DefaultDecomposer<M::MatElement>,
327 DefaultDecomposer<M::MatElement>,
328 ),
329 lwe_decomposer: DefaultDecomposer<M::MatElement>,
330 g_k_dlog_map: Vec<usize>,
331 rlwe_nttop: Ntt,
332 rlwe_modop: RlweModOp,
333 lwe_modop: LweModOp,
334 embedding_factor: usize,
335 rlwe_qby4: M::MatElement,
336 rlwe_auto_maps: Vec<(Vec<usize>, Vec<bool>)>,
337 parameters: BoolParameters<M::MatElement>,
338}
339
340impl<M: Matrix, NttOp, RlweModOp, LweModOp> PbsInfo for BoolPbsInfo<M, NttOp, RlweModOp, LweModOp>
341where
342 M::MatElement: PrimInt
343 + WrappingSub
344 + NumInfo
345 + FromPrimitive
346 + From<bool>
347 + Display
348 + WrappingAdd
349 + Debug,
350 RlweModOp: ArithmeticOps<Element = M::MatElement> + ShoupMatrixFMA<M::R>,
351 LweModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
352 NttOp: Ntt<Element = M::MatElement>,
353{
354 type M = M;
355 type Modulus = CiphertextModulus<M::MatElement>;
356 type D = DefaultDecomposer<M::MatElement>;
357 type RlweModOp = RlweModOp;
358 type LweModOp = LweModOp;
359 type NttOp = NttOp;
360 fn rlwe_auto_map(&self, k: usize) -> &(Vec<usize>, Vec<bool>) {
361 &self.rlwe_auto_maps[k]
362 }
363 fn br_q(&self) -> usize {
364 *self.parameters.br_q()
365 }
366 fn lwe_decomposer(&self) -> &Self::D {
367 &self.lwe_decomposer
368 }
369 fn rlwe_rgsw_decomposer(&self) -> &(Self::D, Self::D) {
370 &self.rlwe_rgsw_decomposer
371 }
372 fn auto_decomposer(&self) -> &Self::D {
373 &self.auto_decomposer
374 }
375 fn embedding_factor(&self) -> usize {
376 self.embedding_factor
377 }
378 fn g(&self) -> isize {
379 self.parameters.g() as isize
380 }
381 fn w(&self) -> usize {
382 self.parameters.w()
383 }
384 fn g_k_dlog_map(&self) -> &[usize] {
385 &self.g_k_dlog_map
386 }
387 fn lwe_n(&self) -> usize {
388 self.parameters.lwe_n().0
389 }
390 fn lwe_q(&self) -> &Self::Modulus {
391 self.parameters.lwe_q()
392 }
393 fn rlwe_n(&self) -> usize {
394 self.parameters.rlwe_n().0
395 }
396 fn rlwe_q(&self) -> &Self::Modulus {
397 self.parameters.rlwe_q()
398 }
399 fn modop_lweq(&self) -> &Self::LweModOp {
400 &self.lwe_modop
401 }
402 fn modop_rlweq(&self) -> &Self::RlweModOp {
403 &self.rlwe_modop
404 }
405 fn nttop_rlweq(&self) -> &Self::NttOp {
406 &self.rlwe_nttop
407 }
408}
409
410pub(crate) struct BoolEvaluator<M, Ntt, RlweModOp, LweModOp, SKey>
411where
412 M: Matrix,
413{
414 pbs_info: BoolPbsInfo<M, Ntt, RlweModOp, LweModOp>,
415 scratch_memory: ScratchMemory<M>,
416 nand_test_vec: M::R,
417 and_test_vec: M::R,
418 or_test_vec: M::R,
419 nor_test_vec: M::R,
420 xor_test_vec: M::R,
421 xnor_test_vec: M::R,
422 /// Non-interactive u_i -> s key switch decomposer
423 ni_ui_to_s_ks_decomposer: Option<DefaultDecomposer<M::MatElement>>,
424 _phantom: PhantomData<SKey>,
425}
426
427impl<M: Matrix, NttOp, RlweModOp, LweModOp, Skey>
428 BoolEvaluator<M, NttOp, RlweModOp, LweModOp, Skey>
429{
430 pub(crate) fn parameters(&self) -> &BoolParameters<M::MatElement> {
431 &self.pbs_info.parameters
432 }
433
434 pub(super) fn pbs_info(&self) -> &BoolPbsInfo<M, NttOp, RlweModOp, LweModOp> {
435 &self.pbs_info
436 }
437
438 pub(super) fn ni_ui_to_s_ks_decomposer(&self) -> &Option<DefaultDecomposer<M::MatElement>> {
439 &self.ni_ui_to_s_ks_decomposer
440 }
441}
442
443fn trim_rgsw_ct_matrix_from_rgrg_to_rlrg<
444 M: MatrixMut + MatrixEntity,
445 D: DoubleDecomposerParams<Count = DecompositionCount>,
446>(
447 rgsw_ct_in: M,
448 rgrg_params: D,
449 rlrg_params: D,
450) -> M
451where
452 M::R: RowMut,
453 M::MatElement: Copy,
454{
455 let (rgswrgsw_d_a, rgswrgsw_d_b) = (
456 rgrg_params.decomposition_count_a(),
457 rgrg_params.decomposition_count_b(),
458 );
459 let (rlrg_d_a, rlrg_d_b) = (
460 rlrg_params.decomposition_count_a(),
461 rlrg_params.decomposition_count_b(),
462 );
463 let rgsw_ct_rows_in = rgswrgsw_d_a.0 * 2 + rgswrgsw_d_b.0 * 2;
464 let rgsw_ct_rows_out = rlrg_d_a.0 * 2 + rlrg_d_b.0 * 2;
465 assert!(rgsw_ct_in.dimension().0 == rgsw_ct_rows_in);
466 assert!(rgswrgsw_d_a.0 >= rlrg_d_a.0, "RGSWxRGSW part A decomposition count {} must be >= RLWExRGSW part A decomposition count {}", rgswrgsw_d_a.0 , rlrg_d_a.0);
467 assert!(rgswrgsw_d_b.0 >= rlrg_d_b.0, "RGSWxRGSW part B decomposition count {} must be >= RLWExRGSW part B decomposition count {}", rgswrgsw_d_b.0 , rlrg_d_b.0);
468
469 let mut reduced_ct_i_out = M::zeros(rgsw_ct_rows_out, rgsw_ct_in.dimension().1);
470
471 // RLWE'(-sm) part A
472 izip!(
473 reduced_ct_i_out.iter_rows_mut().take(rlrg_d_a.0),
474 rgsw_ct_in
475 .iter_rows()
476 .skip(rgswrgsw_d_a.0 - rlrg_d_a.0)
477 .take(rlrg_d_a.0)
478 )
479 .for_each(|(to_ri, from_ri)| {
480 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
481 });
482
483 // RLWE'(-sm) part B
484 izip!(
485 reduced_ct_i_out
486 .iter_rows_mut()
487 .skip(rlrg_d_a.0)
488 .take(rlrg_d_a.0),
489 rgsw_ct_in
490 .iter_rows()
491 .skip(rgswrgsw_d_a.0 + (rgswrgsw_d_a.0 - rlrg_d_a.0))
492 .take(rlrg_d_a.0)
493 )
494 .for_each(|(to_ri, from_ri)| {
495 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
496 });
497
498 // RLWE'(m) Part A
499 izip!(
500 reduced_ct_i_out
501 .iter_rows_mut()
502 .skip(rlrg_d_a.0 * 2)
503 .take(rlrg_d_b.0),
504 rgsw_ct_in
505 .iter_rows()
506 .skip(rgswrgsw_d_a.0 * 2 + (rgswrgsw_d_b.0 - rlrg_d_b.0))
507 .take(rlrg_d_b.0)
508 )
509 .for_each(|(to_ri, from_ri)| {
510 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
511 });
512
513 // RLWE'(m) Part B
514 izip!(
515 reduced_ct_i_out
516 .iter_rows_mut()
517 .skip(rlrg_d_a.0 * 2 + rlrg_d_b.0)
518 .take(rlrg_d_b.0),
519 rgsw_ct_in
520 .iter_rows()
521 .skip(rgswrgsw_d_a.0 * 2 + rgswrgsw_d_b.0 + (rgswrgsw_d_b.0 - rlrg_d_b.0))
522 .take(rlrg_d_b.0)
523 )
524 .for_each(|(to_ri, from_ri)| {
525 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
526 });
527
528 reduced_ct_i_out
529}
530
531fn produce_rgsw_ciphertext_from_ni_rgsw<
532 M: MatrixMut + MatrixEntity,
533 D: RlweDecomposer,
534 ModOp: VectorOps<Element = M::MatElement>,
535 NttOp: Ntt<Element = M::MatElement>,
536>(
537 ni_rgsw_ct: &M,
538 aggregated_decomposed_ni_rgsw_zero_encs: &[M],
539 decomposed_neg_ais: &[M],
540 decomposer: &D,
541 parameters: &BoolParameters<M::MatElement>,
542 uj_to_s_ksk: (&M, &M),
543 rlwe_modop: &ModOp,
544 nttop: &NttOp,
545 out_eval: bool,
546) -> M
547where
548 <M as Matrix>::R: RowMut + Clone,
549{
550 let max_decomposer =
551 if decomposer.a().decomposition_count().0 > decomposer.b().decomposition_count().0 {
552 decomposer.a()
553 } else {
554 decomposer.b()
555 };
556
557 assert!(
558 ni_rgsw_ct.dimension()
559 == (
560 max_decomposer.decomposition_count().0,
561 parameters.rlwe_n().0
562 )
563 );
564 assert!(
565 aggregated_decomposed_ni_rgsw_zero_encs.len() == decomposer.a().decomposition_count().0,
566 );
567 assert!(decomposed_neg_ais.len() == decomposer.b().decomposition_count().0);
568
569 let mut rgsw_i = M::zeros(
570 decomposer.a().decomposition_count().0 * 2 + decomposer.b().decomposition_count().0 * 2,
571 parameters.rlwe_n().0,
572 );
573 let (rlwe_dash_nsm, rlwe_dash_m) =
574 rgsw_i.split_at_row_mut(decomposer.a().decomposition_count().0 * 2);
575
576 // RLWE'_{s}(-sm)
577 // Key switch `s * a_{i, l} + e` using ksk(u_j -> s) to produce RLWE(s *
578 // u_{j=user_id} * a_{i, l}).
579 //
580 // Then set RLWE_{s}(-s B^i m) = (0, u_{j=user_id} * a_{i, l} + e + B^i m) +
581 // RLWE(s * u_{j=user_id} * a_{i, l})
582 {
583 let (rlwe_dash_nsm_parta, rlwe_dash_nsm_partb) =
584 rlwe_dash_nsm.split_at_mut(decomposer.a().decomposition_count().0);
585 izip!(
586 rlwe_dash_nsm_parta.iter_mut(),
587 rlwe_dash_nsm_partb.iter_mut(),
588 ni_rgsw_ct.iter_rows().skip(
589 max_decomposer.decomposition_count().0 - decomposer.a().decomposition_count().0
590 ),
591 aggregated_decomposed_ni_rgsw_zero_encs.iter()
592 )
593 .for_each(|(rlwe_a, rlwe_b, ni_rlwe_ct, decomp_zero_enc)| {
594 // KS(s * a_{i, l} + e) = RLWE(s * u_j *
595 // a_{i, l}) using user j's Ksk
596 izip!(
597 decomp_zero_enc.iter_rows(),
598 uj_to_s_ksk.0.iter_rows(),
599 uj_to_s_ksk.1.iter_rows()
600 )
601 .for_each(|(c, pb, pa)| {
602 rlwe_modop.elwise_fma_mut(rlwe_b.as_mut(), pb.as_ref(), c.as_ref());
603 rlwe_modop.elwise_fma_mut(rlwe_a.as_mut(), pa.as_ref(), c.as_ref());
604 });
605
606 // RLWE(-s beta^i m) = (0, u_j * a_{j, l} +
607 // e + beta^i m) + RLWE(s * u_j * a_{i, l})
608 if out_eval {
609 let mut ni_rlwe_ct = ni_rlwe_ct.clone();
610 nttop.forward(ni_rlwe_ct.as_mut());
611 rlwe_modop.elwise_add_mut(rlwe_a.as_mut(), ni_rlwe_ct.as_ref());
612 } else {
613 nttop.backward(rlwe_a.as_mut());
614 nttop.backward(rlwe_b.as_mut());
615 rlwe_modop.elwise_add_mut(rlwe_a.as_mut(), ni_rlwe_ct.as_ref());
616 }
617 });
618 }
619
620 // RLWE'_{s}(m)
621 {
622 let (rlwe_dash_m_parta, rlwe_dash_partb) =
623 rlwe_dash_m.split_at_mut(decomposer.b().decomposition_count().0);
624 izip!(
625 rlwe_dash_m_parta.iter_mut(),
626 rlwe_dash_partb.iter_mut(),
627 ni_rgsw_ct.iter_rows().skip(
628 max_decomposer.decomposition_count().0 - decomposer.b().decomposition_count().0
629 ),
630 decomposed_neg_ais.iter()
631 )
632 .for_each(|(rlwe_a, rlwe_b, ni_rlwe_ct, decomp_neg_ai)| {
633 // KS(-a_{i, l}) = RLWE(u_i * -a_{i,l}) using user j's Ksk
634 izip!(
635 decomp_neg_ai.iter_rows(),
636 uj_to_s_ksk.0.iter_rows(),
637 uj_to_s_ksk.1.iter_rows()
638 )
639 .for_each(|(c, pb, pa)| {
640 rlwe_modop.elwise_fma_mut(rlwe_b.as_mut(), pb.as_ref(), c.as_ref());
641 rlwe_modop.elwise_fma_mut(rlwe_a.as_mut(), pa.as_ref(), c.as_ref());
642 });
643
644 // RLWE_{s}(beta^i m) = (u_j * a_{i, l} + e + beta^i m, 0) -
645 // RLWE(-a_{i, l} u_j)
646 if out_eval {
647 let mut ni_rlwe_ct = ni_rlwe_ct.clone();
648 nttop.forward(ni_rlwe_ct.as_mut());
649 rlwe_modop.elwise_add_mut(rlwe_b.as_mut(), ni_rlwe_ct.as_ref());
650 } else {
651 nttop.backward(rlwe_a.as_mut());
652 nttop.backward(rlwe_b.as_mut());
653 rlwe_modop.elwise_add_mut(rlwe_b.as_mut(), ni_rlwe_ct.as_ref());
654 }
655 });
656 }
657
658 rgsw_i
659}
660
661/// Assigns user with user_id segement of LWE secret indices for which they
662/// generate RGSW(X^{s[i]}) as the leader (i.e. for RLWExRGSW). If returned
663/// tuple is (start, end), user's segment is [start, end)
664pub(super) fn interactive_mult_party_user_id_lwe_segment(
665 user_id: usize,
666 total_users: usize,
667 lwe_n: usize,
668) -> (usize, usize) {
669 let per_user = (lwe_n as f64 / total_users as f64)
670 .ceil()
671 .to_usize()
672 .unwrap();
673 (
674 per_user * user_id,
675 std::cmp::min(per_user * (user_id + 1), lwe_n),
676 )
677}
678
679impl<M: Matrix, NttOp, RlweModOp, LweModOp, SKey> BoolEvaluator<M, NttOp, RlweModOp, LweModOp, SKey>
680where
681 M: MatrixEntity + MatrixMut,
682 M::MatElement: PrimInt
683 + Debug
684 + Display
685 + NumInfo
686 + FromPrimitive
687 + WrappingSub
688 + WrappingAdd
689 + SampleUniform
690 + From<bool>,
691 NttOp: Ntt<Element = M::MatElement>,
692 RlweModOp: ArithmeticOps<Element = M::MatElement>
693 + VectorOps<Element = M::MatElement>
694 + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>
695 + ShoupMatrixFMA<M::R>,
696 LweModOp: ArithmeticOps<Element = M::MatElement>
697 + VectorOps<Element = M::MatElement>
698 + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>,
699 M::R: TryConvertFrom1<[i32], CiphertextModulus<M::MatElement>> + RowEntity + Debug,
700 <M as Matrix>::R: RowMut,
701{
702 pub(super) fn new(parameters: BoolParameters<M::MatElement>) -> Self
703 where
704 RlweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
705 LweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
706 NttOp: NttInit<CiphertextModulus<M::MatElement>>,
707 {
708 //TODO(Jay): Run sanity checks for modulus values in parameters
709
710 // generates dlog map s.t. (+/-)g^{k} % q = a, for all a \in Z*_{q} and k \in
711 // [0, q/4). We store the dlog `k` at index `a`. This makes it easier to
712 // simply look up `k` at runtime as vec[a]. If a = g^{k} then dlog is
713 // stored as k. If a = -g^{k} then dlog is stored as k = q/4. This is done to
714 // differentiate sign.
715 let g = parameters.g();
716 let q = *parameters.br_q();
717 let mut g_k_dlog_map = vec![0usize; q];
718 for i in 0..q / 4 {
719 let v = mod_exponent(g as u64, i as u64, q as u64) as usize;
720 // g^i
721 g_k_dlog_map[v] = i;
722 // -(g^i)
723 g_k_dlog_map[q - v] = i + (q / 4);
724 }
725
726 let embedding_factor = (2 * parameters.rlwe_n().0) / q;
727
728 let rlwe_nttop = NttOp::new(parameters.rlwe_q(), parameters.rlwe_n().0);
729 let rlwe_modop = RlweModOp::new(*parameters.rlwe_q());
730 let lwe_modop = LweModOp::new(*parameters.lwe_q());
731
732 let q = *parameters.br_q();
733 let qby2 = q >> 1;
734 let qby8 = q >> 3;
735 // Q/8 (Q: rlwe_q)
736 let true_m_el = parameters.rlwe_q().true_el();
737 // -Q/8
738 let false_m_el = parameters.rlwe_q().false_el();
739 let (auto_map_index, auto_map_sign) = generate_auto_map(qby2, -(g as isize));
740
741 let init_test_vec = |partition_el: usize,
742 before_partition_el: M::MatElement,
743 after_partition_el: M::MatElement| {
744 let mut test_vec = M::R::zeros(qby2);
745 for i in 0..qby2 {
746 if i < partition_el {
747 test_vec.as_mut()[i] = before_partition_el;
748 } else {
749 test_vec.as_mut()[i] = after_partition_el;
750 }
751 }
752
753 // v(X) -> v(X^{-g})
754 let mut test_vec_autog = M::R::zeros(qby2);
755 izip!(
756 test_vec.as_ref().iter(),
757 auto_map_index.iter(),
758 auto_map_sign.iter()
759 )
760 .for_each(|(v, to_index, to_sign)| {
761 if !to_sign {
762 // negate
763 test_vec_autog.as_mut()[*to_index] = rlwe_modop.neg(v);
764 } else {
765 test_vec_autog.as_mut()[*to_index] = *v;
766 }
767 });
768
769 return test_vec_autog;
770 };
771
772 let nand_test_vec = init_test_vec(3 * qby8, true_m_el, false_m_el);
773 let and_test_vec = init_test_vec(3 * qby8, false_m_el, true_m_el);
774 let or_test_vec = init_test_vec(qby8, false_m_el, true_m_el);
775 let nor_test_vec = init_test_vec(qby8, true_m_el, false_m_el);
776 let xor_test_vec = init_test_vec(qby8, false_m_el, true_m_el);
777 let xnor_test_vec = init_test_vec(qby8, true_m_el, false_m_el);
778
779 // auto map indices and sign
780 // Auto maps are stored as [-g, g^{1}, g^{2}, ..., g^{w}]
781 let mut rlwe_auto_maps = vec![];
782 let ring_size = parameters.rlwe_n().0;
783 let g = parameters.g();
784 let br_q = parameters.br_q();
785 let auto_element_dlogs = parameters.auto_element_dlogs();
786 assert!(auto_element_dlogs[0] == 0);
787 for i in auto_element_dlogs.into_iter() {
788 let el = if i == 0 {
789 -(g as isize)
790 } else {
791 (g.pow(i as u32) % br_q) as isize
792 };
793 rlwe_auto_maps.push(generate_auto_map(ring_size, el))
794 }
795
796 let rlwe_qby4 = parameters.rlwe_q().qby4();
797
798 let scratch_memory = ScratchMemory::new(¶meters);
799
800 let ni_ui_to_s_ks_decomposer = if parameters.variant()
801 == &ParameterVariant::NonInteractiveMultiParty
802 {
803 Some(parameters
804 .non_interactive_ui_to_s_key_switch_decomposer::<DefaultDecomposer<M::MatElement>>())
805 } else {
806 None
807 };
808
809 let pbs_info = BoolPbsInfo {
810 auto_decomposer: parameters.auto_decomposer(),
811 lwe_decomposer: parameters.lwe_decomposer(),
812 rlwe_rgsw_decomposer: parameters.rlwe_rgsw_decomposer(),
813 g_k_dlog_map,
814 embedding_factor,
815 lwe_modop,
816 rlwe_modop,
817 rlwe_nttop,
818 rlwe_qby4,
819 rlwe_auto_maps,
820 parameters: parameters,
821 };
822
823 BoolEvaluator {
824 pbs_info,
825 scratch_memory,
826 nand_test_vec,
827 and_test_vec,
828 or_test_vec,
829 nor_test_vec,
830 xnor_test_vec,
831 xor_test_vec,
832 ni_ui_to_s_ks_decomposer,
833 _phantom: PhantomData,
834 }
835 }
836
837 pub(crate) fn client_key(
838 &self,
839 ) -> ClientKey<<DefaultSecureRng as NewWithSeed>::Seed, M::MatElement> {
840 ClientKey::new(self.parameters().clone())
841 }
842
843 pub(super) fn single_party_server_key<K: SinglePartyClientKey<Element = i32>>(
844 &self,
845 client_key: &K,
846 ) -> SeededSinglePartyServerKey<M, BoolParameters<M::MatElement>, [u8; 32]> {
847 assert_eq!(self.parameters().variant(), &ParameterVariant::SingleParty);
848
849 DefaultSecureRng::with_local_mut(|rng| {
850 let mut main_seed = [0u8; 32];
851 rng.fill_bytes(&mut main_seed);
852
853 let mut main_prng = DefaultSecureRng::new_seeded(main_seed);
854
855 let rlwe_n = self.pbs_info.parameters.rlwe_n().0;
856 let sk_rlwe = client_key.sk_rlwe();
857 let sk_lwe = client_key.sk_lwe();
858
859 // generate auto keys
860 let mut auto_keys = HashMap::new();
861 let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
862 let g = self.pbs_info.parameters.g();
863 let br_q = self.pbs_info.parameters.br_q();
864 let auto_els = self.pbs_info.parameters.auto_element_dlogs();
865 for i in auto_els.into_iter() {
866 let g_pow = if i == 0 {
867 -(g as isize)
868 } else {
869 (g.pow(i as u32) % br_q) as isize
870 };
871 let mut gk = M::zeros(
872 self.pbs_info.auto_decomposer.decomposition_count().0,
873 rlwe_n,
874 );
875 seeded_auto_key_gen(
876 &mut gk,
877 &sk_rlwe,
878 g_pow,
879 &auto_gadget,
880 &self.pbs_info.rlwe_modop,
881 &self.pbs_info.rlwe_nttop,
882 &mut main_prng,
883 rng,
884 );
885 auto_keys.insert(i, gk);
886 }
887
888 // generate rgsw ciphertexts RGSW(si) where si is i^th LWE secret element
889 let ring_size = self.pbs_info.parameters.rlwe_n().0;
890 let rlwe_q = self.pbs_info.parameters.rlwe_q();
891 let (rlrg_d_a, rlrg_d_b) = (
892 self.pbs_info.rlwe_rgsw_decomposer.0.decomposition_count().0,
893 self.pbs_info.rlwe_rgsw_decomposer.1.decomposition_count().0,
894 );
895 let rlrg_gadget_a = self.pbs_info.rlwe_rgsw_decomposer.0.gadget_vector();
896 let rlrg_gadget_b = self.pbs_info.rlwe_rgsw_decomposer.1.gadget_vector();
897 let rgsw_cts = sk_lwe
898 .iter()
899 .map(|si| {
900 // X^{si}; assume |emebedding_factor * si| < N
901 let mut m = M::R::zeros(ring_size);
902 let si = (self.pbs_info.embedding_factor as i32) * si;
903 // dbg!(si);
904 if si < 0 {
905 // X^{-i} = X^{2N - i} = -X^{N-i}
906 m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one();
907 } else {
908 // X^{i}
909 m.as_mut()[si.abs() as usize] = M::MatElement::one();
910 }
911
912 let mut rgsw_si = M::zeros(rlrg_d_a * 2 + rlrg_d_b, ring_size);
913 secret_key_encrypt_rgsw(
914 &mut rgsw_si,
915 m.as_ref(),
916 &rlrg_gadget_a,
917 &rlrg_gadget_b,
918 &sk_rlwe,
919 &self.pbs_info.rlwe_modop,
920 &self.pbs_info.rlwe_nttop,
921 &mut main_prng,
922 rng,
923 );
924
925 rgsw_si
926 })
927 .collect_vec();
928
929 // LWE KSK from RLWE secret s -> LWE secret z
930 let d_lwe_gadget = self.pbs_info.lwe_decomposer.gadget_vector();
931 let lwe_ksk = seeded_lwe_ksk_keygen(
932 &sk_rlwe,
933 &sk_lwe,
934 &d_lwe_gadget,
935 &self.pbs_info.lwe_modop,
936 &mut main_prng,
937 rng,
938 );
939
940 SeededSinglePartyServerKey::from_raw(
941 auto_keys,
942 rgsw_cts,
943 lwe_ksk,
944 self.pbs_info.parameters.clone(),
945 main_seed,
946 )
947 })
948 }
949
950 pub(super) fn multi_party_server_key_share<K: InteractiveMultiPartyClientKey<Element = i32>>(
951 &self,
952 user_id: usize,
953 total_users: usize,
954 cr_seed: &MultiPartyCrs<[u8; 32]>,
955 collective_pk: &M,
956 client_key: &K,
957 ) -> CommonReferenceSeededMultiPartyServerKeyShare<
958 M,
959 BoolParameters<M::MatElement>,
960 MultiPartyCrs<[u8; 32]>,
961 > {
962 assert_eq!(self.parameters().variant(), &ParameterVariant::MultiParty);
963 assert!(user_id < total_users);
964
965 let sk_rlwe = client_key.sk_rlwe();
966 let sk_lwe = client_key.sk_lwe();
967
968 let g = self.pbs_info.parameters.g();
969 let ring_size = self.pbs_info.parameters.rlwe_n().0;
970 let rlwe_q = self.pbs_info.parameters.rlwe_q();
971 let lwe_q = self.pbs_info.parameters.lwe_q();
972
973 let rlweq_modop = &self.pbs_info.rlwe_modop;
974 let rlweq_nttop = &self.pbs_info.rlwe_nttop;
975
976 // sanity check
977 assert!(sk_rlwe.len() == ring_size);
978 assert!(sk_lwe.len() == self.pbs_info.parameters.lwe_n().0);
979
980 // auto keys
981 let auto_keys = self._common_rountine_multi_party_auto_keys_share_gen(
982 cr_seed.auto_keys_cts_seed::<DefaultSecureRng>(),
983 &sk_rlwe,
984 );
985
986 // rgsw ciphertexts of lwe secret elements
987 let (self_leader_rgsws, not_self_leader_rgsws) = DefaultSecureRng::with_local_mut(|rng| {
988 let mut self_leader_rgsw = vec![];
989 let mut not_self_leader_rgsws = vec![];
990
991 let (segment_start, segment_end) = interactive_mult_party_user_id_lwe_segment(
992 user_id,
993 total_users,
994 self.pbs_info().lwe_n(),
995 );
996
997 // self LWE secret indices
998 {
999 // LWE secret indices for which user is the leader they need to send RGSW(m) for
1000 // RLWE x RGSW multiplication
1001 let rlrg_decomposer = self.pbs_info().rlwe_rgsw_decomposer();
1002 let (rlrg_d_a, rlrg_d_b) = (
1003 rlrg_decomposer.a().decomposition_count(),
1004 rlrg_decomposer.b().decomposition_count(),
1005 );
1006 let (gadget_a, gadget_b) = (
1007 rlrg_decomposer.a().gadget_vector(),
1008 rlrg_decomposer.b().gadget_vector(),
1009 );
1010 for s_index in segment_start..segment_end {
1011 let mut out_rgsw = M::zeros(rlrg_d_a.0 * 2 + rlrg_d_b.0 * 2, ring_size);
1012 public_key_encrypt_rgsw(
1013 &mut out_rgsw,
1014 &encode_x_pow_si_with_emebedding_factor::<
1015 M::R,
1016 CiphertextModulus<M::MatElement>,
1017 >(
1018 sk_lwe[s_index],
1019 self.pbs_info().embedding_factor(),
1020 ring_size,
1021 self.pbs_info().rlwe_q(),
1022 )
1023 .as_ref(),
1024 collective_pk,
1025 &gadget_a,
1026 &gadget_b,
1027 rlweq_modop,
1028 rlweq_nttop,
1029 rng,
1030 );
1031 self_leader_rgsw.push(out_rgsw);
1032 }
1033 }
1034
1035 // not self LWE secret indices
1036 {
1037 // LWE secret indices for which user isn't the leader, they need to send RGSW(m)
1038 // for RGSW x RGSW multiplcation
1039 let rgsw_rgsw_decomposer = self
1040 .pbs_info
1041 .parameters
1042 .rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1043 let (rgrg_d_a, rgrg_d_b) = (
1044 rgsw_rgsw_decomposer.a().decomposition_count(),
1045 rgsw_rgsw_decomposer.b().decomposition_count(),
1046 );
1047 let (rgrg_gadget_a, rgrg_gadget_b) = (
1048 rgsw_rgsw_decomposer.a().gadget_vector(),
1049 rgsw_rgsw_decomposer.b().gadget_vector(),
1050 );
1051
1052 for s_index in (0..segment_start).chain(segment_end..self.parameters().lwe_n().0) {
1053 let mut out_rgsw = M::zeros(rgrg_d_a.0 * 2 + rgrg_d_b.0 * 2, ring_size);
1054 public_key_encrypt_rgsw(
1055 &mut out_rgsw,
1056 &encode_x_pow_si_with_emebedding_factor::<
1057 M::R,
1058 CiphertextModulus<M::MatElement>,
1059 >(
1060 sk_lwe[s_index],
1061 self.pbs_info().embedding_factor(),
1062 ring_size,
1063 self.pbs_info().rlwe_q(),
1064 )
1065 .as_ref(),
1066 collective_pk,
1067 &rgrg_gadget_a,
1068 &rgrg_gadget_b,
1069 rlweq_modop,
1070 rlweq_nttop,
1071 rng,
1072 );
1073
1074 not_self_leader_rgsws.push(out_rgsw);
1075 }
1076 }
1077
1078 (self_leader_rgsw, not_self_leader_rgsws)
1079 });
1080
1081 // LWE Ksk
1082 let lwe_ksk = self._common_rountine_multi_party_lwe_ksk_share_gen(
1083 cr_seed.lwe_ksk_cts_seed_seed::<DefaultSecureRng>(),
1084 &sk_rlwe,
1085 &sk_lwe,
1086 );
1087
1088 CommonReferenceSeededMultiPartyServerKeyShare::new(
1089 self_leader_rgsws,
1090 not_self_leader_rgsws,
1091 auto_keys,
1092 lwe_ksk,
1093 cr_seed.clone(),
1094 self.pbs_info.parameters.clone(),
1095 user_id,
1096 )
1097 }
1098
1099 pub(super) fn aggregate_multi_party_server_key_shares<S>(
1100 &self,
1101 shares: &[CommonReferenceSeededMultiPartyServerKeyShare<
1102 M,
1103 BoolParameters<M::MatElement>,
1104 MultiPartyCrs<S>,
1105 >],
1106 ) -> SeededMultiPartyServerKey<M, MultiPartyCrs<S>, BoolParameters<M::MatElement>>
1107 where
1108 S: PartialEq + Clone,
1109 M: Clone,
1110 {
1111 assert_eq!(self.parameters().variant(), &ParameterVariant::MultiParty);
1112 assert!(shares.len() > 0);
1113
1114 let total_users = shares.len();
1115
1116 let parameters = shares[0].parameters().clone();
1117 let cr_seed = shares[0].cr_seed();
1118
1119 let rlwe_n = parameters.rlwe_n().0;
1120 let g = parameters.g() as isize;
1121 let rlwe_q = parameters.rlwe_q();
1122 let lwe_q = parameters.lwe_q();
1123
1124 // sanity checks
1125 shares.iter().skip(1).for_each(|s| {
1126 assert!(s.parameters() == ¶meters);
1127 assert!(s.cr_seed() == cr_seed);
1128 });
1129
1130 let rlweq_modop = &self.pbs_info.rlwe_modop;
1131 let rlweq_nttop = &self.pbs_info.rlwe_nttop;
1132
1133 // auto keys
1134 let mut auto_keys = HashMap::new();
1135 let auto_elements_dlog = parameters.auto_element_dlogs();
1136 for i in auto_elements_dlog.into_iter() {
1137 let mut key = M::zeros(parameters.auto_decomposition_count().0, rlwe_n);
1138
1139 shares.iter().for_each(|s| {
1140 let auto_key_share_i = s.auto_keys().get(&i).expect("Auto key {i} missing");
1141 assert!(
1142 auto_key_share_i.dimension()
1143 == (parameters.auto_decomposition_count().0, rlwe_n)
1144 );
1145 izip!(key.iter_rows_mut(), auto_key_share_i.iter_rows()).for_each(
1146 |(partb_out, partb_share)| {
1147 rlweq_modop.elwise_add_mut(partb_out.as_mut(), partb_share.as_ref());
1148 },
1149 );
1150 });
1151
1152 auto_keys.insert(i, key);
1153 }
1154
1155 // rgsw ciphertext (most expensive part!)
1156 let rgsw_cts = {
1157 let rgsw_x_rgsw_decomposer =
1158 parameters.rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1159 let rlwe_x_rgsw_decomposer = self.pbs_info().rlwe_rgsw_decomposer();
1160 let rgsw_x_rgsw_dimension = (
1161 rgsw_x_rgsw_decomposer.a().decomposition_count().0 * 2
1162 + rgsw_x_rgsw_decomposer.b().decomposition_count().0 * 2,
1163 rlwe_n,
1164 );
1165 let rlwe_x_rgsw_dimension = (
1166 rlwe_x_rgsw_decomposer.a().decomposition_count().0 * 2
1167 + rlwe_x_rgsw_decomposer.b().decomposition_count().0 * 2,
1168 rlwe_n,
1169 );
1170
1171 let mut rgsw_x_rgsw_scratch = M::zeros(
1172 rgsw_x_rgsw_scratch_rows(rlwe_x_rgsw_decomposer, &rgsw_x_rgsw_decomposer),
1173 rlwe_n,
1174 );
1175
1176 let shares_in_correct_order = (0..total_users)
1177 .map(|i| shares.iter().find(|s| s.user_id() == i).unwrap())
1178 .collect_vec();
1179
1180 let lwe_n = self.parameters().lwe_n().0;
1181 let (users_segments, users_segments_sizes): (Vec<(usize, usize)>, Vec<usize>) = (0
1182 ..total_users)
1183 .map(|(user_id)| {
1184 let (start_index, end_index) =
1185 interactive_mult_party_user_id_lwe_segment(user_id, total_users, lwe_n);
1186 ((start_index, end_index), end_index - start_index)
1187 })
1188 .unzip();
1189
1190 let mut rgsw_cts = Vec::with_capacity(lwe_n);
1191 users_segments
1192 .iter()
1193 .enumerate()
1194 .for_each(|(user_id, user_segment)| {
1195 let share = shares_in_correct_order[user_id];
1196 for secret_index in user_segment.0..user_segment.1 {
1197 let mut rgsw_i =
1198 share.self_leader_rgsws()[secret_index - user_segment.0].clone();
1199 // assert already exists in RGSW x RGSW rountine
1200 assert!(rgsw_i.dimension() == rlwe_x_rgsw_dimension);
1201
1202 // multiply leader's RGSW ct at `secret_index` with RGSW cts of other users
1203 // for lwe index `secret_index`
1204 (0..total_users)
1205 .filter(|i| i != &user_id)
1206 .for_each(|other_user_id| {
1207 let mut offset = 0;
1208 if other_user_id < user_id {
1209 offset = users_segments_sizes[other_user_id];
1210 }
1211
1212 let mut other_rgsw_i = shares_in_correct_order[other_user_id]
1213 .not_self_leader_rgsws()
1214 [secret_index.checked_sub(offset).unwrap()]
1215 .clone();
1216 // assert already exists in RGSW x RGSW rountine
1217 assert!(other_rgsw_i.dimension() == rgsw_x_rgsw_dimension);
1218
1219 // send to evaluation domain for RGSwxRGSW mul
1220 other_rgsw_i
1221 .iter_rows_mut()
1222 .for_each(|r| rlweq_nttop.forward(r.as_mut()));
1223
1224 rgsw_by_rgsw_inplace(
1225 &mut RgswCiphertextMutRef::new(
1226 rgsw_i.as_mut(),
1227 rlwe_x_rgsw_decomposer.a().decomposition_count().0,
1228 rlwe_x_rgsw_decomposer.b().decomposition_count().0,
1229 ),
1230 &RgswCiphertextRef::new(
1231 other_rgsw_i.as_ref(),
1232 rgsw_x_rgsw_decomposer.a().decomposition_count().0,
1233 rgsw_x_rgsw_decomposer.b().decomposition_count().0,
1234 ),
1235 rlwe_x_rgsw_decomposer,
1236 &rgsw_x_rgsw_decomposer,
1237 &mut RuntimeScratchMutRef::new(rgsw_x_rgsw_scratch.as_mut()),
1238 rlweq_nttop,
1239 rlweq_modop,
1240 );
1241 });
1242
1243 rgsw_cts.push(rgsw_i);
1244 }
1245 });
1246
1247 rgsw_cts
1248 };
1249
1250 // LWE ksks
1251 let mut lwe_ksk = M::R::zeros(rlwe_n * parameters.lwe_decomposition_count().0);
1252 let lweq_modop = &self.pbs_info.lwe_modop;
1253 shares.iter().for_each(|si| {
1254 assert!(si.lwe_ksk().as_ref().len() == rlwe_n * parameters.lwe_decomposition_count().0);
1255 lweq_modop.elwise_add_mut(lwe_ksk.as_mut(), si.lwe_ksk().as_ref())
1256 });
1257
1258 SeededMultiPartyServerKey::new(rgsw_cts, auto_keys, lwe_ksk, cr_seed.clone(), parameters)
1259 }
1260
1261 pub(super) fn aggregate_non_interactive_multi_party_key_share(
1262 &self,
1263 cr_seed: &NonInteractiveMultiPartyCrs<[u8; 32]>,
1264 total_users: usize,
1265 key_shares: &[CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
1266 M,
1267 NonInteractiveMultiPartyCrs<[u8; 32]>,
1268 >],
1269 ) -> SeededNonInteractiveMultiPartyServerKey<
1270 M,
1271 NonInteractiveMultiPartyCrs<[u8; 32]>,
1272 BoolParameters<M::MatElement>,
1273 >
1274 where
1275 M: Clone + Debug,
1276 <M as Matrix>::R: RowMut + Clone,
1277 {
1278 assert_eq!(
1279 self.parameters().variant(),
1280 &ParameterVariant::NonInteractiveMultiParty
1281 );
1282
1283 let total_users = key_shares.len();
1284 let key_shares = (0..total_users)
1285 .map(|user_id| {
1286 // find share of user_id
1287 key_shares
1288 .iter()
1289 .find(|share| share.user_index() == user_id)
1290 .expect(&format!("Key Share for user_id={user_id} missing"))
1291 })
1292 .collect_vec();
1293
1294 let rlwe_modop = &self.pbs_info().rlwe_modop;
1295 let nttop = &self.pbs_info().rlwe_nttop;
1296 let ring_size = self.parameters().rlwe_n().0;
1297 let rlwe_q = self.parameters().rlwe_q();
1298 let lwe_modop = self.pbs_info().modop_lweq();
1299
1300 // Generate Key switching key from u_j to s, where u_j user j's RLWE secret and
1301 // s is the ideal RLWE secret.
1302 //
1303 // User j gives [s_j * a_{i, j} + e + \beta^i u_j] where a_{i, j} is user j
1304 // specific publicly know polynomial sampled from user j's pseudo random seed
1305 // defined in the protocol.
1306 //
1307 // User k, k != j, gives [s_k * a_{i, j} + e].
1308 //
1309 // We set Ksk(u_j -> s) = [s_j * a_{i, j} + e + \beta^i u_j + \sum_{k \in P, k
1310 // != j} s_k * a_{i, j} + e]
1311 let ni_uj_to_s_decomposer = self
1312 .parameters()
1313 .non_interactive_ui_to_s_key_switch_decomposer::<DefaultDecomposer<M::MatElement>>();
1314 let mut uj_to_s_ksks = key_shares
1315 .iter()
1316 .map(|share| {
1317 let mut useri_ui_to_s_ksk = share.ui_to_s_ksk().clone();
1318 assert!(
1319 useri_ui_to_s_ksk.dimension()
1320 == (ni_uj_to_s_decomposer.decomposition_count().0, ring_size)
1321 );
1322 key_shares
1323 .iter()
1324 .filter(|x| x.user_index() != share.user_index())
1325 .for_each(|other_share| {
1326 let op2 = other_share.ui_to_s_ksk_zero_encs_for_user_i(share.user_index());
1327 assert!(
1328 op2.dimension()
1329 == (ni_uj_to_s_decomposer.decomposition_count().0, ring_size)
1330 );
1331 izip!(useri_ui_to_s_ksk.iter_rows_mut(), op2.iter_rows()).for_each(
1332 |(add_to, add_from)| {
1333 rlwe_modop.elwise_add_mut(add_to.as_mut(), add_from.as_ref())
1334 },
1335 );
1336 });
1337 useri_ui_to_s_ksk
1338 })
1339 .collect_vec();
1340
1341 let rgsw_cts = {
1342 // Send u_j -> s ksk in evaluation domain and sample corresponding a's using
1343 // user j's ksk seed to prepare for upcoming key switches
1344 uj_to_s_ksks.iter_mut().for_each(|ksk_i| {
1345 ksk_i
1346 .iter_rows_mut()
1347 .for_each(|r| nttop.forward(r.as_mut()))
1348 });
1349 let uj_to_s_ksks_part_a_eval = key_shares
1350 .iter()
1351 .map(|share| {
1352 let mut ksk_prng = DefaultSecureRng::new_seeded(
1353 cr_seed.ui_to_s_ks_seed_for_user_i::<DefaultSecureRng>(share.user_index()),
1354 );
1355 let mut ais =
1356 M::zeros(ni_uj_to_s_decomposer.decomposition_count().0, ring_size);
1357
1358 ais.iter_rows_mut().for_each(|r_ai| {
1359 RandomFillUniformInModulus::random_fill(
1360 &mut ksk_prng,
1361 rlwe_q,
1362 r_ai.as_mut(),
1363 );
1364
1365 nttop.forward(r_ai.as_mut())
1366 });
1367 ais
1368 })
1369 .collect_vec();
1370
1371 let rgsw_x_rgsw_decomposer = self
1372 .parameters()
1373 .rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1374 let rlwe_x_rgsw_decomposer = self
1375 .parameters()
1376 .rlwe_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1377
1378 let d_max = if rgsw_x_rgsw_decomposer.a().decomposition_count().0
1379 > rgsw_x_rgsw_decomposer.b().decomposition_count().0
1380 {
1381 rgsw_x_rgsw_decomposer.a().decomposition_count().0
1382 } else {
1383 rgsw_x_rgsw_decomposer.b().decomposition_count().0
1384 };
1385
1386 let mut scratch_rgsw_x_rgsw = M::zeros(
1387 rgsw_x_rgsw_scratch_rows(&rlwe_x_rgsw_decomposer, &rgsw_x_rgsw_decomposer),
1388 self.parameters().rlwe_n().0,
1389 );
1390
1391 // Recall that given u_j * a_{i, l} + e + \beta^i X^{s_j[l]} from user j
1392 //
1393 // We generate:
1394 //
1395 // - RLWE(-s \beta^i X^{s_j[l]}) = KS_{u_j -> s}(a_{i, l} * s + e) + (0 , u_j *
1396 // a_{i, l} + e + \beta^i X^{s_j[l]}), where KS_{u_j -> s}(a_{i, l} * s + e) =
1397 // RLWE_s(a_{i, l} * u_j)
1398 // - RLWE(\beta^i X^{s_j[l]}) = KS_{u_j -> s}(-a_{i,l}) + (u_j * a_{i, l} + e +
1399 // \beta^i X^{s_j[l]}, 0), where KS_{u_j -> s}(-a_{i,l}) = RLWE_s(-a_{i,l} *
1400 // u_j)
1401 //
1402 // a_{i, l} * s + e = \sum_{j \in P} a_{i, l} * s_{j} + e
1403 let user_segments = (0..total_users)
1404 .map(|user_id| {
1405 interactive_mult_party_user_id_lwe_segment(
1406 user_id,
1407 total_users,
1408 self.parameters().lwe_n().0,
1409 )
1410 })
1411 .collect_vec();
1412 // Note: Each user is assigned a contigous LWE segement and the LWE dimension is
1413 // split approximately uniformly across all users. Hence, concatenation of all
1414 // user specific lwe segments will give LWE dimension.
1415 let rgsw_cts = user_segments
1416 .into_iter()
1417 .enumerate()
1418 .flat_map(|(user_id, lwe_segment)| {
1419 (lwe_segment.0..lwe_segment.1)
1420 .into_iter()
1421 .map(|lwe_index| {
1422 // We sample d_b `-a_i`s to key switch and generate RLWE'(m). But before
1423 // we sampling we need to puncture a_prng d_max - d_b times to align
1424 // a_i's. After sampling we decompose `-a_i`s and send them to
1425 // evaluation domain for upcoming key switches.
1426 let mut a_prng = DefaultSecureRng::new_seeded(
1427 cr_seed.ni_rgsw_ct_seed_for_index::<DefaultSecureRng>(lwe_index),
1428 );
1429
1430 let mut scratch = M::R::zeros(self.parameters().rlwe_n().0);
1431 (0..d_max - rgsw_x_rgsw_decomposer.b().decomposition_count().0)
1432 .for_each(|_| {
1433 RandomFillUniformInModulus::random_fill(
1434 &mut a_prng,
1435 rlwe_q,
1436 scratch.as_mut(),
1437 );
1438 });
1439
1440 let decomp_neg_ais = (0..rgsw_x_rgsw_decomposer
1441 .b()
1442 .decomposition_count()
1443 .0)
1444 .map(|_| {
1445 RandomFillUniformInModulus::random_fill(
1446 &mut a_prng,
1447 rlwe_q,
1448 scratch.as_mut(),
1449 );
1450 rlwe_modop.elwise_neg_mut(scratch.as_mut());
1451
1452 let mut decomp_neg_ai = M::zeros(
1453 ni_uj_to_s_decomposer.decomposition_count().0,
1454 self.parameters().rlwe_n().0,
1455 );
1456 scratch.as_ref().iter().enumerate().for_each(|(index, el)| {
1457 ni_uj_to_s_decomposer
1458 .decompose_iter(el)
1459 .enumerate()
1460 .for_each(|(row_j, d_el)| {
1461 (decomp_neg_ai.as_mut()[row_j]).as_mut()[index] =
1462 d_el;
1463 });
1464 });
1465
1466 decomp_neg_ai
1467 .iter_rows_mut()
1468 .for_each(|ri| nttop.forward(ri.as_mut()));
1469
1470 decomp_neg_ai
1471 })
1472 .collect_vec();
1473
1474 // Aggregate zero encryptions to produce a_{i, l} * s + e =
1475 // \sum_{k in P} a_{i, l} * s_{k} + e where s is the ideal RLWE
1476 // secret. Aggregated a_{i, l} * s + e are key switched using
1477 // Ksk(u_j -> s) to produce RLWE_{s}(a_{i, l} * s) which are
1478 // then use to produce RLWE'(-sX^{s_{lwe}[l]}).
1479 // Hence, after aggregation we decompose a_{i, l} * s + e to
1480 // prepare for key switching
1481 let ni_rgsw_zero_encs = (0..rgsw_x_rgsw_decomposer
1482 .a()
1483 .decomposition_count()
1484 .0)
1485 .map(|i| {
1486 let mut sum = M::R::zeros(self.parameters().rlwe_n().0);
1487 key_shares.iter().for_each(|k| {
1488 let to_add_ref = k
1489 .ni_rgsw_zero_enc_for_lwe_index(lwe_index)
1490 .get_row_slice(i);
1491 assert!(to_add_ref.len() == self.parameters().rlwe_n().0);
1492 rlwe_modop.elwise_add_mut(sum.as_mut(), to_add_ref);
1493 });
1494
1495 // decompose
1496 let mut decomp_sum = M::zeros(
1497 ni_uj_to_s_decomposer.decomposition_count().0,
1498 self.parameters().rlwe_n().0,
1499 );
1500 sum.as_ref().iter().enumerate().for_each(|(index, el)| {
1501 ni_uj_to_s_decomposer
1502 .decompose_iter(el)
1503 .enumerate()
1504 .for_each(|(row_j, d_el)| {
1505 (decomp_sum.as_mut()[row_j]).as_mut()[index] = d_el;
1506 });
1507 });
1508
1509 decomp_sum
1510 .iter_rows_mut()
1511 .for_each(|r| nttop.forward(r.as_mut()));
1512
1513 decomp_sum
1514 })
1515 .collect_vec();
1516
1517 // Produce RGSW(X^{s_{j=user_id, lwe}[l]}) for the
1518 // leader, ie user's id = user_id.
1519 // Recall leader's RGSW ciphertext must be constructed
1520 // for RLWE x RGSW multiplication, and is then used
1521 // to accumulate, using RGSW x RGSW multiplication,
1522 // X^{s_{j != user_id, lwe}[l]} from other users.
1523 let mut rgsw_i = produce_rgsw_ciphertext_from_ni_rgsw(
1524 key_shares[user_id]
1525 .ni_rgsw_cts_for_self_leader_lwe_index(lwe_index),
1526 &ni_rgsw_zero_encs[rgsw_x_rgsw_decomposer
1527 .a()
1528 .decomposition_count()
1529 .0
1530 - rlwe_x_rgsw_decomposer.a().decomposition_count().0..],
1531 &decomp_neg_ais[rgsw_x_rgsw_decomposer
1532 .b()
1533 .decomposition_count()
1534 .0
1535 - rlwe_x_rgsw_decomposer.b().decomposition_count().0..],
1536 &rlwe_x_rgsw_decomposer,
1537 self.parameters(),
1538 (&uj_to_s_ksks[user_id], &uj_to_s_ksks_part_a_eval[user_id]),
1539 rlwe_modop,
1540 nttop,
1541 false,
1542 );
1543
1544 // RGSW for lwe_index of users that are not leader.
1545 //
1546 // Recall that for users that are not leader for the
1547 // lwe_index we require to produce RGSW ciphertext for
1548 // RGSW x RGSW product
1549 (0..total_users)
1550 .filter(|i| *i != user_id)
1551 .for_each(|other_user_id| {
1552 let mut other_rgsw_i = produce_rgsw_ciphertext_from_ni_rgsw(
1553 key_shares[other_user_id]
1554 .ni_rgsw_cts_for_self_not_leader_lwe_index(lwe_index),
1555 &ni_rgsw_zero_encs,
1556 &decomp_neg_ais,
1557 &rgsw_x_rgsw_decomposer,
1558 self.parameters(),
1559 (
1560 &uj_to_s_ksks[other_user_id],
1561 &uj_to_s_ksks_part_a_eval[other_user_id],
1562 ),
1563 rlwe_modop,
1564 nttop,
1565 true,
1566 );
1567
1568 rgsw_by_rgsw_inplace(
1569 &mut RgswCiphertextMutRef::new(
1570 rgsw_i.as_mut(),
1571 rlwe_x_rgsw_decomposer.a().decomposition_count().0,
1572 rlwe_x_rgsw_decomposer.b().decomposition_count().0,
1573 ),
1574 &RgswCiphertextRef::new(
1575 other_rgsw_i.as_ref(),
1576 rgsw_x_rgsw_decomposer.a().decomposition_count().0,
1577 rgsw_x_rgsw_decomposer.b().decomposition_count().0,
1578 ),
1579 &rlwe_x_rgsw_decomposer,
1580 &rgsw_x_rgsw_decomposer,
1581 &mut RuntimeScratchMutRef::new(
1582 scratch_rgsw_x_rgsw.as_mut(),
1583 ),
1584 nttop,
1585 rlwe_modop,
1586 )
1587 });
1588
1589 rgsw_i
1590 })
1591 .collect_vec()
1592 })
1593 .collect_vec();
1594
1595 // put u_j to s ksk in coefficient domain
1596 uj_to_s_ksks.iter_mut().for_each(|ksk_i| {
1597 ksk_i
1598 .iter_rows_mut()
1599 .for_each(|r| nttop.backward(r.as_mut()))
1600 });
1601
1602 rgsw_cts
1603 };
1604
1605 // auto keys
1606 let auto_keys = {
1607 let mut auto_keys = HashMap::new();
1608 let auto_elements_dlog = self.parameters().auto_element_dlogs();
1609 for i in auto_elements_dlog.into_iter() {
1610 let mut key = M::zeros(self.parameters().auto_decomposition_count().0, ring_size);
1611
1612 key_shares.iter().for_each(|s| {
1613 let auto_key_share_i =
1614 s.auto_keys_share().get(&i).expect("Auto key {i} missing");
1615 assert!(
1616 auto_key_share_i.dimension()
1617 == (self.parameters().auto_decomposition_count().0, ring_size)
1618 );
1619 izip!(key.iter_rows_mut(), auto_key_share_i.iter_rows()).for_each(
1620 |(partb_out, partb_share)| {
1621 rlwe_modop.elwise_add_mut(partb_out.as_mut(), partb_share.as_ref());
1622 },
1623 );
1624 });
1625
1626 auto_keys.insert(i, key);
1627 }
1628 auto_keys
1629 };
1630
1631 // LWE ksk
1632 let lwe_ksk = {
1633 let mut lwe_ksk =
1634 M::R::zeros(self.parameters().lwe_decomposition_count().0 * ring_size);
1635 key_shares.iter().for_each(|s| {
1636 assert!(
1637 s.lwe_ksk_share().as_ref().len()
1638 == self.parameters().lwe_decomposition_count().0 * ring_size
1639 );
1640 lwe_modop.elwise_add_mut(lwe_ksk.as_mut(), s.lwe_ksk_share().as_ref());
1641 });
1642 lwe_ksk
1643 };
1644
1645 SeededNonInteractiveMultiPartyServerKey::new(
1646 uj_to_s_ksks,
1647 rgsw_cts,
1648 auto_keys,
1649 lwe_ksk,
1650 cr_seed.clone(),
1651 self.parameters().clone(),
1652 )
1653 }
1654
1655 pub(super) fn non_interactive_multi_party_key_share<
1656 K: NonInteractiveMultiPartyClientKey<Element = i32>,
1657 >(
1658 &self,
1659 cr_seed: &NonInteractiveMultiPartyCrs<[u8; 32]>,
1660 self_index: usize,
1661 total_users: usize,
1662 client_key: &K,
1663 ) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
1664 M,
1665 NonInteractiveMultiPartyCrs<[u8; 32]>,
1666 > {
1667 assert_eq!(
1668 self.parameters().variant(),
1669 &ParameterVariant::NonInteractiveMultiParty
1670 );
1671
1672 // TODO: check whether parameters support `total_users`
1673 let nttop = self.pbs_info().nttop_rlweq();
1674 let rlwe_modop = self.pbs_info().modop_rlweq();
1675 let ring_size = self.pbs_info().rlwe_n();
1676 let rlwe_q = self.parameters().rlwe_q();
1677
1678 let sk_rlwe = client_key.sk_rlwe();
1679 let sk_u_rlwe = client_key.sk_u_rlwe();
1680 let sk_lwe = client_key.sk_lwe();
1681
1682 let (ui_to_s_ksk, ksk_zero_encs_for_others) = DefaultSecureRng::with_local_mut(|rng| {
1683 // ui_to_s_ksk
1684 let non_interactive_decomposer = self
1685 .parameters()
1686 .non_interactive_ui_to_s_key_switch_decomposer::<DefaultDecomposer<M::MatElement>>(
1687 );
1688 let non_interactive_gadget_vec = non_interactive_decomposer.gadget_vector();
1689 let ui_to_s_ksk = {
1690 let mut p_rng = DefaultSecureRng::new_seeded(
1691 cr_seed.ui_to_s_ks_seed_for_user_i::<DefaultSecureRng>(self_index),
1692 );
1693
1694 non_interactive_ksk_gen::<M, _, _, _, _, _>(
1695 &sk_rlwe,
1696 &sk_u_rlwe,
1697 &non_interactive_gadget_vec,
1698 &mut p_rng,
1699 rng,
1700 nttop,
1701 rlwe_modop,
1702 )
1703 };
1704
1705 // zero encryptions for others uj_to_s ksk
1706 let all_users_except_self = (0..total_users).filter(|x| *x != self_index);
1707 let zero_encs_for_others = all_users_except_self
1708 .map(|other_user_index| {
1709 let mut p_rng = DefaultSecureRng::new_seeded(
1710 cr_seed.ui_to_s_ks_seed_for_user_i::<DefaultSecureRng>(other_user_index),
1711 );
1712 let zero_encs =
1713 non_interactive_ksk_zero_encryptions_for_other_party_i::<M, _, _, _, _, _>(
1714 &sk_rlwe,
1715 &non_interactive_gadget_vec,
1716 &mut p_rng,
1717 rng,
1718 nttop,
1719 rlwe_modop,
1720 );
1721 zero_encs
1722 })
1723 .collect_vec();
1724
1725 (ui_to_s_ksk, zero_encs_for_others)
1726 });
1727
1728 // Non-interactive RGSW cts
1729 let (ni_rgsw_zero_encs, self_leader_ni_rgsw_cts, not_self_leader_rgsw_cts) = {
1730 let rgsw_x_rgsw_decomposer = self
1731 .parameters()
1732 .rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1733 let rlwe_x_rgsw_decomposer = self
1734 .parameters()
1735 .rlwe_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1736
1737 // We assume that d_{a/b} for RGSW x RGSW are always < d'_{a/b} for RLWE x RGSW
1738 assert!(
1739 rlwe_x_rgsw_decomposer.a().decomposition_count().0
1740 < rgsw_x_rgsw_decomposer.a().decomposition_count().0
1741 );
1742 assert!(
1743 rlwe_x_rgsw_decomposer.b().decomposition_count().0
1744 < rgsw_x_rgsw_decomposer.b().decomposition_count().0
1745 );
1746
1747 let sj_poly_eval = {
1748 let mut s = M::R::try_convert_from(&sk_rlwe, rlwe_q);
1749 nttop.forward(s.as_mut());
1750 s
1751 };
1752
1753 let d_rgsw_a = rgsw_x_rgsw_decomposer.a().decomposition_count().0;
1754 let d_rgsw_b = rgsw_x_rgsw_decomposer.b().decomposition_count().0;
1755 let d_max = std::cmp::max(d_rgsw_a, d_rgsw_b);
1756
1757 // Zero encyptions for each LWE index. We generate d_a zero encryptions for each
1758 // LWE index using a_{i, l} with i \in {d_max - d_a , d_max) and l = lwe_index
1759 let zero_encs = {
1760 (0..self.parameters().lwe_n().0)
1761 .map(|lwe_index| {
1762 let mut p_rng = DefaultSecureRng::new_seeded(
1763 cr_seed.ni_rgsw_ct_seed_for_index::<DefaultSecureRng>(lwe_index),
1764 );
1765
1766 let mut scratch = M::R::zeros(self.parameters().rlwe_n().0);
1767
1768 // puncture seeded prng d_max - d_a times
1769 (0..(d_max - d_rgsw_a)).into_iter().for_each(|_| {
1770 RandomFillUniformInModulus::random_fill(
1771 &mut p_rng,
1772 rlwe_q,
1773 scratch.as_mut(),
1774 );
1775 });
1776
1777 let mut zero_enc = M::zeros(d_rgsw_a, self.parameters().rlwe_n().0);
1778 zero_enc.iter_rows_mut().for_each(|out| {
1779 // sample a_i
1780 RandomFillUniformInModulus::random_fill(
1781 &mut p_rng,
1782 rlwe_q,
1783 out.as_mut(),
1784 );
1785
1786 // a_i * s_j
1787 nttop.forward(out.as_mut());
1788 rlwe_modop.elwise_mul_mut(out.as_mut(), sj_poly_eval.as_ref());
1789 nttop.backward(out.as_mut());
1790
1791 // a_j * s_j + e
1792 DefaultSecureRng::with_local_mut_mut(&mut |rng| {
1793 RandomFillGaussianInModulus::random_fill(
1794 rng,
1795 rlwe_q,
1796 scratch.as_mut(),
1797 );
1798 });
1799
1800 rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
1801 });
1802
1803 zero_enc
1804 })
1805 .collect_vec()
1806 };
1807
1808 let uj_poly_eval = {
1809 let mut u = M::R::try_convert_from(&sk_u_rlwe, rlwe_q);
1810 nttop.forward(u.as_mut());
1811 u
1812 };
1813
1814 // Generate non-interactive RGSW ciphertexts a_{i, l} u_j + e + \beta X^{s_j[l]}
1815 // for i \in (0, d_max]
1816 let (self_start_index, self_end_index) = interactive_mult_party_user_id_lwe_segment(
1817 self_index,
1818 total_users,
1819 self.parameters().lwe_n().0,
1820 );
1821
1822 // For LWE indices [self_start_index, self_end_index) user generates
1823 // non-interactive RGSW cts for RLWE x RGSW product. We refer to
1824 // such indices as where user is the leader. For the rest of
1825 // the indices user generates non-interactive RGWS cts for RGSW x
1826 // RGSW multiplication. We refer to such indices as where user is
1827 // not the leader.
1828 let self_leader_ni_rgsw_cts = {
1829 let max_rlwe_x_rgsw_decomposer =
1830 if rlwe_x_rgsw_decomposer.a().decomposition_count().0
1831 > rlwe_x_rgsw_decomposer.b().decomposition_count().0
1832 {
1833 rlwe_x_rgsw_decomposer.a()
1834 } else {
1835 rlwe_x_rgsw_decomposer.b()
1836 };
1837
1838 let gadget_vec = max_rlwe_x_rgsw_decomposer.gadget_vector();
1839
1840 (self_start_index..self_end_index)
1841 .map(|lwe_index| {
1842 let mut p_rng = DefaultSecureRng::new_seeded(
1843 cr_seed.ni_rgsw_ct_seed_for_index::<DefaultSecureRng>(lwe_index),
1844 );
1845
1846 // puncture p_rng d_max - d'_max time to align with `a_{i, l}`s used to
1847 // produce RGSW cts for RGSW x RGSW
1848 let mut scratch = M::R::zeros(self.parameters().rlwe_n().0);
1849 (0..(d_max - max_rlwe_x_rgsw_decomposer.decomposition_count().0))
1850 .into_iter()
1851 .for_each(|_| {
1852 RandomFillUniformInModulus::random_fill(
1853 &mut p_rng,
1854 rlwe_q,
1855 scratch.as_mut(),
1856 );
1857 });
1858
1859 let mut ni_rgsw_cts = M::zeros(
1860 max_rlwe_x_rgsw_decomposer.decomposition_count().0,
1861 self.parameters().rlwe_n().0,
1862 );
1863
1864 // X^{s_{j, lwe}[l]}
1865 let m_poly = encode_x_pow_si_with_emebedding_factor::<M::R, _>(
1866 sk_lwe[lwe_index],
1867 self.pbs_info().embedding_factor(),
1868 self.parameters().rlwe_n().0,
1869 rlwe_q,
1870 );
1871
1872 izip!(ni_rgsw_cts.iter_rows_mut(), gadget_vec.iter()).for_each(
1873 |(out, beta)| {
1874 // sample a_i
1875 RandomFillUniformInModulus::random_fill(
1876 &mut p_rng,
1877 rlwe_q,
1878 out.as_mut(),
1879 );
1880
1881 // u_j * a_i
1882 nttop.forward(out.as_mut());
1883 rlwe_modop.elwise_mul_mut(out.as_mut(), uj_poly_eval.as_ref());
1884 nttop.backward(out.as_mut());
1885
1886 // u_j + a_i + e
1887 DefaultSecureRng::with_local_mut_mut(&mut |rng| {
1888 RandomFillGaussianInModulus::random_fill(
1889 rng,
1890 rlwe_q,
1891 scratch.as_mut(),
1892 );
1893 });
1894 rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
1895
1896 // u_j + a_i + e + beta m
1897 rlwe_modop.elwise_scalar_mul(
1898 scratch.as_mut(),
1899 m_poly.as_ref(),
1900 beta,
1901 );
1902 rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
1903 },
1904 );
1905
1906 ni_rgsw_cts
1907 })
1908 .collect_vec()
1909 };
1910
1911 let not_self_leader_rgsw_cts = {
1912 let max_rgsw_x_rgsw_decomposer =
1913 if rgsw_x_rgsw_decomposer.a().decomposition_count().0
1914 > rgsw_x_rgsw_decomposer.b().decomposition_count().0
1915 {
1916 rgsw_x_rgsw_decomposer.a()
1917 } else {
1918 rgsw_x_rgsw_decomposer.b()
1919 };
1920 let gadget_vec = max_rgsw_x_rgsw_decomposer.gadget_vector();
1921
1922 ((0..self_start_index).chain(self_end_index..self.parameters().lwe_n().0))
1923 .map(|lwe_index| {
1924 let mut p_rng = DefaultSecureRng::new_seeded(
1925 cr_seed.ni_rgsw_ct_seed_for_index::<DefaultSecureRng>(lwe_index),
1926 );
1927 let mut ni_rgsw_cts = M::zeros(
1928 max_rgsw_x_rgsw_decomposer.decomposition_count().0,
1929 self.parameters().rlwe_n().0,
1930 );
1931 let mut scratch = M::R::zeros(self.parameters().rlwe_n().0);
1932
1933 // X^{s_{j, lwe}[l]}
1934 let m_poly = encode_x_pow_si_with_emebedding_factor::<M::R, _>(
1935 sk_lwe[lwe_index],
1936 self.pbs_info().embedding_factor(),
1937 self.parameters().rlwe_n().0,
1938 rlwe_q,
1939 );
1940
1941 izip!(ni_rgsw_cts.iter_rows_mut(), gadget_vec.iter()).for_each(
1942 |(out, beta)| {
1943 // sample a_i
1944 RandomFillUniformInModulus::random_fill(
1945 &mut p_rng,
1946 rlwe_q,
1947 out.as_mut(),
1948 );
1949
1950 // u_j * a_i
1951 nttop.forward(out.as_mut());
1952 rlwe_modop.elwise_mul_mut(out.as_mut(), uj_poly_eval.as_ref());
1953 nttop.backward(out.as_mut());
1954
1955 // u_j + a_i + e
1956 DefaultSecureRng::with_local_mut_mut(&mut |rng| {
1957 RandomFillGaussianInModulus::random_fill(
1958 rng,
1959 rlwe_q,
1960 scratch.as_mut(),
1961 );
1962 });
1963 rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
1964
1965 // u_j * a_i + e + beta m
1966 rlwe_modop.elwise_scalar_mul(
1967 scratch.as_mut(),
1968 m_poly.as_ref(),
1969 beta,
1970 );
1971 rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
1972 },
1973 );
1974
1975 ni_rgsw_cts
1976 })
1977 .collect_vec()
1978 };
1979
1980 (zero_encs, self_leader_ni_rgsw_cts, not_self_leader_rgsw_cts)
1981 };
1982
1983 // Auto key share
1984 let auto_keys_share = {
1985 let auto_seed = cr_seed.auto_keys_cts_seed::<DefaultSecureRng>();
1986 self._common_rountine_multi_party_auto_keys_share_gen(auto_seed, &sk_rlwe)
1987 };
1988
1989 // Lwe Ksk share
1990 let lwe_ksk_share = {
1991 let lwe_ksk_seed = cr_seed.lwe_ksk_cts_seed::<DefaultSecureRng>();
1992 self._common_rountine_multi_party_lwe_ksk_share_gen(lwe_ksk_seed, &sk_rlwe, &sk_lwe)
1993 };
1994
1995 CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare::new(
1996 self_leader_ni_rgsw_cts,
1997 not_self_leader_rgsw_cts,
1998 ni_rgsw_zero_encs,
1999 ui_to_s_ksk,
2000 ksk_zero_encs_for_others,
2001 auto_keys_share,
2002 lwe_ksk_share,
2003 self_index,
2004 total_users,
2005 self.parameters().lwe_n().0,
2006 cr_seed.clone(),
2007 )
2008 }
2009
2010 fn _common_rountine_multi_party_auto_keys_share_gen(
2011 &self,
2012 auto_seed: <DefaultSecureRng as NewWithSeed>::Seed,
2013 sk_rlwe: &[i32],
2014 ) -> HashMap<usize, M> {
2015 let g = self.pbs_info.parameters.g();
2016 let ring_size = self.pbs_info.parameters.rlwe_n().0;
2017 let br_q = self.pbs_info.parameters.br_q();
2018 let rlweq_modop = &self.pbs_info.rlwe_modop;
2019 let rlweq_nttop = &self.pbs_info.rlwe_nttop;
2020
2021 DefaultSecureRng::with_local_mut(|rng| {
2022 let mut p_rng = DefaultSecureRng::new_seeded(auto_seed);
2023
2024 let mut auto_keys = HashMap::new();
2025 let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
2026 let auto_element_dlogs = self.pbs_info.parameters.auto_element_dlogs();
2027
2028 for i in auto_element_dlogs.into_iter() {
2029 let g_pow = if i == 0 {
2030 -(g as isize)
2031 } else {
2032 (g.pow(i as u32) % br_q) as isize
2033 };
2034
2035 let mut ksk_out = M::zeros(
2036 self.pbs_info.auto_decomposer.decomposition_count().0,
2037 ring_size,
2038 );
2039 seeded_auto_key_gen(
2040 &mut ksk_out,
2041 sk_rlwe,
2042 g_pow,
2043 &auto_gadget,
2044 rlweq_modop,
2045 rlweq_nttop,
2046 &mut p_rng,
2047 rng,
2048 );
2049 auto_keys.insert(i, ksk_out);
2050 }
2051
2052 auto_keys
2053 })
2054 }
2055
2056 fn _common_rountine_multi_party_lwe_ksk_share_gen(
2057 &self,
2058 lwe_ksk_seed: <DefaultSecureRng as NewWithSeed>::Seed,
2059 sk_rlwe: &[i32],
2060 sk_lwe: &[i32],
2061 ) -> M::R {
2062 DefaultSecureRng::with_local_mut(|rng| {
2063 let mut p_rng = DefaultSecureRng::new_seeded(lwe_ksk_seed);
2064 let lwe_modop = &self.pbs_info.lwe_modop;
2065 let d_lwe_gadget_vec = self.pbs_info.lwe_decomposer.gadget_vector();
2066 seeded_lwe_ksk_keygen(
2067 sk_rlwe,
2068 sk_lwe,
2069 &d_lwe_gadget_vec,
2070 lwe_modop,
2071 &mut p_rng,
2072 rng,
2073 )
2074 })
2075 }
2076
2077 pub(super) fn multi_party_public_key_share<K: InteractiveMultiPartyClientKey<Element = i32>>(
2078 &self,
2079 cr_seed: &MultiPartyCrs<[u8; 32]>,
2080 client_key: &K,
2081 ) -> CommonReferenceSeededCollectivePublicKeyShare<
2082 <M as Matrix>::R,
2083 [u8; 32],
2084 BoolParameters<<M as Matrix>::MatElement>,
2085 > {
2086 DefaultSecureRng::with_local_mut(|rng| {
2087 let mut share_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0);
2088 let modop = &self.pbs_info.rlwe_modop;
2089 let nttop = &self.pbs_info.rlwe_nttop;
2090 let pk_seed = cr_seed.public_key_share_seed::<DefaultSecureRng>();
2091 let mut main_prng = DefaultSecureRng::new_seeded(pk_seed);
2092 public_key_share(
2093 &mut share_out,
2094 &client_key.sk_rlwe(),
2095 modop,
2096 nttop,
2097 &mut main_prng,
2098 rng,
2099 );
2100 CommonReferenceSeededCollectivePublicKeyShare::new(
2101 share_out,
2102 pk_seed,
2103 self.pbs_info.parameters.clone(),
2104 )
2105 })
2106 }
2107
2108 pub(super) fn multi_party_decryption_share<K: InteractiveMultiPartyClientKey<Element = i32>>(
2109 &self,
2110 lwe_ct: &M::R,
2111 client_key: &K,
2112 ) -> <M as Matrix>::MatElement {
2113 assert!(lwe_ct.as_ref().len() == self.pbs_info.parameters.rlwe_n().0 + 1);
2114 let modop = &self.pbs_info.rlwe_modop;
2115 let mut neg_s =
2116 M::R::try_convert_from(&client_key.sk_rlwe(), &self.pbs_info.parameters.rlwe_q());
2117 modop.elwise_neg_mut(neg_s.as_mut());
2118
2119 let mut neg_sa = M::MatElement::zero();
2120 izip!(lwe_ct.as_ref().iter().skip(1), neg_s.as_ref().iter()).for_each(|(ai, nsi)| {
2121 neg_sa = modop.add(&neg_sa, &modop.mul(ai, nsi));
2122 });
2123
2124 let e = DefaultSecureRng::with_local_mut(|rng| {
2125 RandomGaussianElementInModulus::random(rng, self.pbs_info.parameters.rlwe_q())
2126 });
2127 let share = modop.add(&neg_sa, &e);
2128
2129 share
2130 }
2131
2132 pub(crate) fn multi_party_decrypt(&self, shares: &[M::MatElement], lwe_ct: &M::R) -> bool {
2133 let modop = &self.pbs_info.rlwe_modop;
2134 let mut sum_a = M::MatElement::zero();
2135 shares
2136 .iter()
2137 .for_each(|share_i| sum_a = modop.add(&sum_a, &share_i));
2138
2139 let encoded_m = modop.add(&lwe_ct.as_ref()[0], &sum_a);
2140 self.pbs_info.parameters.rlwe_q().decode(encoded_m)
2141 }
2142
2143 pub(crate) fn pk_encrypt(&self, pk: &M, m: bool) -> M::R {
2144 self.pk_encrypt_batched(pk, &vec![m]).remove(0)
2145 }
2146
2147 /// Encrypts a batch booleans as multiple LWE ciphertexts.
2148 ///
2149 /// For public key encryption we first encrypt `m` as a RLWE ciphertext and
2150 /// then sample extract LWE samples at required indices.
2151 ///
2152 /// - TODO(Jay:) Communication can be improved by not sample exctracting and
2153 /// instead just truncate degree 0 values (part Bs)
2154 pub(crate) fn pk_encrypt_batched(&self, pk: &M, m: &[bool]) -> Vec<M::R> {
2155 DefaultSecureRng::with_local_mut(|rng| {
2156 let ring_size = self.pbs_info.parameters.rlwe_n().0;
2157 assert!(
2158 m.len() <= ring_size,
2159 "Cannot batch encrypt > ring_size{ring_size} elements at once"
2160 );
2161
2162 let modop = &self.pbs_info.rlwe_modop;
2163 let nttop = &self.pbs_info.rlwe_nttop;
2164
2165 // RLWE(0)
2166 // sample ephemeral key u
2167 let mut u = vec![0i32; ring_size];
2168 fill_random_ternary_secret_with_hamming_weight(u.as_mut(), ring_size >> 1, rng);
2169 let mut u = M::R::try_convert_from(&u, &self.pbs_info.parameters.rlwe_q());
2170 nttop.forward(u.as_mut());
2171
2172 let mut ua = M::R::zeros(ring_size);
2173 ua.as_mut().copy_from_slice(pk.get_row_slice(0));
2174 let mut ub = M::R::zeros(ring_size);
2175 ub.as_mut().copy_from_slice(pk.get_row_slice(1));
2176
2177 // a*u
2178 nttop.forward(ua.as_mut());
2179 modop.elwise_mul_mut(ua.as_mut(), u.as_ref());
2180 nttop.backward(ua.as_mut());
2181
2182 // b*u
2183 nttop.forward(ub.as_mut());
2184 modop.elwise_mul_mut(ub.as_mut(), u.as_ref());
2185 nttop.backward(ub.as_mut());
2186
2187 let mut rlwe = M::zeros(2, ring_size);
2188 // sample error
2189 rlwe.iter_rows_mut().for_each(|ri| {
2190 RandomFillGaussianInModulus::<[M::MatElement], CiphertextModulus<M::MatElement>>::random_fill(
2191 rng,
2192 &self.pbs_info.parameters.rlwe_q(),
2193 ri.as_mut(),
2194 );
2195 });
2196
2197 // a*u + e0
2198 modop.elwise_add_mut(rlwe.get_row_mut(0), ua.as_ref());
2199 // b*u + e1
2200 modop.elwise_add_mut(rlwe.get_row_mut(1), ub.as_ref());
2201
2202 //FIXME(Jay): Figure out a way to get Q/8 form modulus
2203 let mut m_vec = M::R::zeros(ring_size);
2204 izip!(m_vec.as_mut().iter_mut(), m.iter()).for_each(|(m_el, m_bool)| {
2205 if *m_bool {
2206 // Q/8
2207 *m_el = self.pbs_info.rlwe_q().true_el()
2208 } else {
2209 // -Q/8
2210 *m_el = self.pbs_info.rlwe_q().false_el()
2211 }
2212 });
2213
2214 // b*u + e1 + m
2215 modop.elwise_add_mut(rlwe.get_row_mut(1), m_vec.as_ref());
2216 // rlwe.set(1, 0, modop.add(rlwe.get(1, 0), &m));
2217
2218 // sample extract index required indices
2219 let samples = m.len();
2220 (0..samples)
2221 .into_iter()
2222 .map(|i| {
2223 let mut lwe_out = M::R::zeros(ring_size + 1);
2224 sample_extract(&mut lwe_out, &rlwe, modop, i);
2225 lwe_out
2226 })
2227 .collect_vec()
2228 })
2229 }
2230
2231 pub fn sk_encrypt<K: SinglePartyClientKey<Element = i32>>(
2232 &self,
2233 m: bool,
2234 client_key: &K,
2235 ) -> M::R {
2236 //FIXME(Jay): Figure out a way to get Q/8 form modulus
2237 let m = if m {
2238 // Q/8
2239 self.pbs_info.rlwe_q().true_el()
2240 } else {
2241 // -Q/8
2242 self.pbs_info.rlwe_q().false_el()
2243 };
2244
2245 DefaultSecureRng::with_local_mut(|rng| {
2246 encrypt_lwe(&m, &client_key.sk_rlwe(), &self.pbs_info.rlwe_modop, rng)
2247 })
2248 }
2249
2250 pub fn sk_decrypt<K: SinglePartyClientKey<Element = i32>>(
2251 &self,
2252 lwe_ct: &M::R,
2253 client_key: &K,
2254 ) -> bool {
2255 let m = decrypt_lwe(lwe_ct, &client_key.sk_rlwe(), &self.pbs_info.rlwe_modop);
2256 self.pbs_info.rlwe_q().decode(m)
2257 }
2258}
2259
2260impl<M, NttOp, RlweModOp, LweModOp, Skey> BoolEvaluator<M, NttOp, RlweModOp, LweModOp, Skey>
2261where
2262 M: MatrixMut + MatrixEntity,
2263 M::R: RowMut + RowEntity,
2264 M::MatElement: PrimInt + FromPrimitive + One + Copy + Zero + Display + WrappingSub + NumInfo,
2265 RlweModOp: VectorOps<Element = M::MatElement> + ArithmeticOps<Element = M::MatElement>,
2266 LweModOp: VectorOps<Element = M::MatElement> + ArithmeticOps<Element = M::MatElement>,
2267 NttOp: Ntt<Element = M::MatElement>,
2268{
2269 /// Returns c0 + c1 + Q/4
2270 fn _add_and_shift_lwe_cts(&self, c0: &mut M::R, c1: &M::R) {
2271 let modop = &self.pbs_info.rlwe_modop;
2272 modop.elwise_add_mut(c0.as_mut(), c1.as_ref());
2273 // +Q/4
2274 c0.as_mut()[0] = modop.add(&c0.as_ref()[0], &self.pbs_info.rlwe_qby4);
2275 }
2276
2277 /// Returns 2(c0 - c1) + Q/4
2278 fn _subtract_double_lwe_cts(&self, c0: &mut M::R, c1: &M::R) {
2279 let modop = &self.pbs_info.rlwe_modop;
2280 // c0 - c1
2281 modop.elwise_sub_mut(c0.as_mut(), c1.as_ref());
2282
2283 // double
2284 c0.as_mut().iter_mut().for_each(|v| *v = modop.add(v, v));
2285 }
2286}
2287
2288impl<M, NttOp, RlweModOp, LweModOp, Skey> BooleanGates
2289 for BoolEvaluator<M, NttOp, RlweModOp, LweModOp, Skey>
2290where
2291 M: MatrixMut + MatrixEntity,
2292 M::R: RowMut + RowEntity + Clone,
2293 M::MatElement: PrimInt
2294 + FromPrimitive
2295 + One
2296 + Copy
2297 + Zero
2298 + Display
2299 + WrappingSub
2300 + NumInfo
2301 + From<bool>
2302 + WrappingAdd
2303 + Debug,
2304 RlweModOp: VectorOps<Element = M::MatElement>
2305 + ArithmeticOps<Element = M::MatElement>
2306 + ShoupMatrixFMA<M::R>,
2307 LweModOp: VectorOps<Element = M::MatElement> + ArithmeticOps<Element = M::MatElement>,
2308 NttOp: Ntt<Element = M::MatElement>,
2309 Skey: PbsKey<AutoKey = <Skey as PbsKey>::RgswCt, LweKskKey = M>,
2310 <Skey as PbsKey>::RgswCt: WithShoupRepr<M = M>,
2311{
2312 type Ciphertext = M::R;
2313 type Key = Skey;
2314
2315 fn nand_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2316 self._add_and_shift_lwe_cts(c0, c1);
2317
2318 // PBS
2319 pbs(
2320 &self.pbs_info,
2321 &self.nand_test_vec,
2322 c0,
2323 server_key,
2324 &mut self.scratch_memory.lwe_vector,
2325 &mut self.scratch_memory.decomposition_matrix,
2326 );
2327 }
2328
2329 fn and_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2330 self._add_and_shift_lwe_cts(c0, c1);
2331
2332 // PBS
2333 pbs(
2334 &self.pbs_info,
2335 &self.and_test_vec,
2336 c0,
2337 server_key,
2338 &mut self.scratch_memory.lwe_vector,
2339 &mut self.scratch_memory.decomposition_matrix,
2340 );
2341 }
2342
2343 fn or_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2344 self._add_and_shift_lwe_cts(c0, c1);
2345
2346 // PBS
2347 pbs(
2348 &self.pbs_info,
2349 &self.or_test_vec,
2350 c0,
2351 server_key,
2352 &mut self.scratch_memory.lwe_vector,
2353 &mut self.scratch_memory.decomposition_matrix,
2354 );
2355 }
2356
2357 fn nor_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2358 self._add_and_shift_lwe_cts(c0, c1);
2359
2360 // PBS
2361 pbs(
2362 &self.pbs_info,
2363 &self.nor_test_vec,
2364 c0,
2365 server_key,
2366 &mut self.scratch_memory.lwe_vector,
2367 &mut self.scratch_memory.decomposition_matrix,
2368 )
2369 }
2370
2371 fn xor_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2372 self._subtract_double_lwe_cts(c0, c1);
2373
2374 // PBS
2375 pbs(
2376 &self.pbs_info,
2377 &self.xor_test_vec,
2378 c0,
2379 server_key,
2380 &mut self.scratch_memory.lwe_vector,
2381 &mut self.scratch_memory.decomposition_matrix,
2382 );
2383 }
2384
2385 fn xnor_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2386 self._subtract_double_lwe_cts(c0, c1);
2387
2388 // PBS
2389 pbs(
2390 &self.pbs_info,
2391 &self.xnor_test_vec,
2392 c0,
2393 server_key,
2394 &mut self.scratch_memory.lwe_vector,
2395 &mut self.scratch_memory.decomposition_matrix,
2396 );
2397 }
2398
2399 fn not_inplace(&mut self, c0: &mut M::R) {
2400 let modop = &self.pbs_info.rlwe_modop;
2401 c0.as_mut().iter_mut().for_each(|v| *v = modop.neg(v));
2402 }
2403
2404 fn and(
2405 &mut self,
2406 c0: &Self::Ciphertext,
2407 c1: &Self::Ciphertext,
2408 key: &Self::Key,
2409 ) -> Self::Ciphertext {
2410 let mut out = c0.clone();
2411 self.and_inplace(&mut out, c1, key);
2412 out
2413 }
2414
2415 fn nand(
2416 &mut self,
2417 c0: &Self::Ciphertext,
2418 c1: &Self::Ciphertext,
2419 key: &Self::Key,
2420 ) -> Self::Ciphertext {
2421 let mut out = c0.clone();
2422 self.nand_inplace(&mut out, c1, key);
2423 out
2424 }
2425
2426 fn or(
2427 &mut self,
2428 c0: &Self::Ciphertext,
2429 c1: &Self::Ciphertext,
2430 key: &Self::Key,
2431 ) -> Self::Ciphertext {
2432 let mut out = c0.clone();
2433 self.or_inplace(&mut out, c1, key);
2434 out
2435 }
2436
2437 fn nor(
2438 &mut self,
2439 c0: &Self::Ciphertext,
2440 c1: &Self::Ciphertext,
2441 key: &Self::Key,
2442 ) -> Self::Ciphertext {
2443 let mut out = c0.clone();
2444 self.nor_inplace(&mut out, c1, key);
2445 out
2446 }
2447
2448 fn xnor(
2449 &mut self,
2450 c0: &Self::Ciphertext,
2451 c1: &Self::Ciphertext,
2452 key: &Self::Key,
2453 ) -> Self::Ciphertext {
2454 let mut out = c0.clone();
2455 self.xnor_inplace(&mut out, c1, key);
2456 out
2457 }
2458
2459 fn xor(
2460 &mut self,
2461 c0: &Self::Ciphertext,
2462 c1: &Self::Ciphertext,
2463 key: &Self::Key,
2464 ) -> Self::Ciphertext {
2465 let mut out = c0.clone();
2466 self.xor_inplace(&mut out, c1, key);
2467 out
2468 }
2469
2470 fn not(&mut self, c: &Self::Ciphertext) -> Self::Ciphertext {
2471 let mut out = c.clone();
2472 self.not_inplace(&mut out);
2473 out
2474 }
2475}