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