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