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