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
543/// Assigns user with user_id segement of LWE secret indices for which they
544/// generate RGSW(X^{s[i]}) as the leader (i.e. for RLWExRGSW). If returned
545/// tuple is (start, end), user's segment is [start, end)
546pub(super) fn interactive_mult_party_user_id_lwe_segment(
547 user_id: usize,
548 total_users: usize,
549 lwe_n: usize,
550) -> (usize, usize) {
551 let per_user = (lwe_n as f64 / total_users as f64)
552 .ceil()
553 .to_usize()
554 .unwrap();
555 (
556 per_user * user_id,
557 std::cmp::min(per_user * (user_id + 1), lwe_n),
558 )
559}
560
561impl<M: Matrix, NttOp, RlweModOp, LweModOp, SKey> BoolEvaluator<M, NttOp, RlweModOp, LweModOp, SKey>
562where
563 M: MatrixEntity + MatrixMut,
564 M::MatElement: PrimInt
565 + Debug
566 + Display
567 + NumInfo
568 + FromPrimitive
569 + WrappingSub
570 + WrappingAdd
571 + SampleUniform
572 + From<bool>,
573 NttOp: Ntt<Element = M::MatElement>,
574 RlweModOp: ArithmeticOps<Element = M::MatElement>
575 + VectorOps<Element = M::MatElement>
576 + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>
577 + ShoupMatrixFMA<M::R>,
578 LweModOp: ArithmeticOps<Element = M::MatElement>
579 + VectorOps<Element = M::MatElement>
580 + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>,
581 M::R: TryConvertFrom1<[i32], CiphertextModulus<M::MatElement>> + RowEntity + Debug,
582 <M as Matrix>::R: RowMut,
583{
584 pub(super) fn new(parameters: BoolParameters<M::MatElement>) -> Self
585 where
586 RlweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
587 LweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
588 NttOp: NttInit<CiphertextModulus<M::MatElement>>,
589 {
590 //TODO(Jay): Run sanity checks for modulus values in parameters
591
592 // generates dlog map s.t. (+/-)g^{k} % q = a, for all a \in Z*_{q} and k \in
593 // [0, q/4). We store the dlog `k` at index `a`. This makes it easier to
594 // simply look up `k` at runtime as vec[a]. If a = g^{k} then dlog is
595 // stored as k. If a = -g^{k} then dlog is stored as k = q/4. This is done to
596 // differentiate sign.
597 let g = parameters.g();
598 let q = *parameters.br_q();
599 let mut g_k_dlog_map = vec![0usize; q];
600 for i in 0..q / 4 {
601 let v = mod_exponent(g as u64, i as u64, q as u64) as usize;
602 // g^i
603 g_k_dlog_map[v] = i;
604 // -(g^i)
605 g_k_dlog_map[q - v] = i + (q / 4);
606 }
607
608 let embedding_factor = (2 * parameters.rlwe_n().0) / q;
609
610 let rlwe_nttop = NttOp::new(parameters.rlwe_q(), parameters.rlwe_n().0);
611 let rlwe_modop = RlweModOp::new(*parameters.rlwe_q());
612 let lwe_modop = LweModOp::new(*parameters.lwe_q());
613
614 let q = *parameters.br_q();
615 let qby2 = q >> 1;
616 let qby8 = q >> 3;
617 // Q/8 (Q: rlwe_q)
618 let true_m_el = parameters.rlwe_q().true_el();
619 // -Q/8
620 let false_m_el = parameters.rlwe_q().false_el();
621 let (auto_map_index, auto_map_sign) = generate_auto_map(qby2, -(g as isize));
622
623 let init_test_vec = |partition_el: usize,
624 before_partition_el: M::MatElement,
625 after_partition_el: M::MatElement| {
626 let mut test_vec = M::R::zeros(qby2);
627 for i in 0..qby2 {
628 if i < partition_el {
629 test_vec.as_mut()[i] = before_partition_el;
630 } else {
631 test_vec.as_mut()[i] = after_partition_el;
632 }
633 }
634
635 // v(X) -> v(X^{-g})
636 let mut test_vec_autog = M::R::zeros(qby2);
637 izip!(
638 test_vec.as_ref().iter(),
639 auto_map_index.iter(),
640 auto_map_sign.iter()
641 )
642 .for_each(|(v, to_index, to_sign)| {
643 if !to_sign {
644 // negate
645 test_vec_autog.as_mut()[*to_index] = rlwe_modop.neg(v);
646 } else {
647 test_vec_autog.as_mut()[*to_index] = *v;
648 }
649 });
650
651 return test_vec_autog;
652 };
653
654 let nand_test_vec = init_test_vec(3 * qby8, true_m_el, false_m_el);
655 let and_test_vec = init_test_vec(3 * qby8, false_m_el, true_m_el);
656 let or_test_vec = init_test_vec(qby8, false_m_el, true_m_el);
657 let nor_test_vec = init_test_vec(qby8, true_m_el, false_m_el);
658 let xor_test_vec = init_test_vec(qby8, false_m_el, true_m_el);
659 let xnor_test_vec = init_test_vec(qby8, true_m_el, false_m_el);
660
661 // auto map indices and sign
662 // Auto maps are stored as [-g, g^{1}, g^{2}, ..., g^{w}]
663 let mut rlwe_auto_maps = vec![];
664 let ring_size = parameters.rlwe_n().0;
665 let g = parameters.g();
666 let br_q = parameters.br_q();
667 let auto_element_dlogs = parameters.auto_element_dlogs();
668 assert!(auto_element_dlogs[0] == 0);
669 for i in auto_element_dlogs.into_iter() {
670 let el = if i == 0 {
671 -(g as isize)
672 } else {
673 (g.pow(i as u32) % br_q) as isize
674 };
675 rlwe_auto_maps.push(generate_auto_map(ring_size, el))
676 }
677
678 let rlwe_qby4 = parameters.rlwe_q().qby4();
679
680 let scratch_memory = ScratchMemory::new(¶meters);
681
682 let ni_ui_to_s_ks_decomposer = if parameters.variant()
683 == &ParameterVariant::NonInteractiveMultiParty
684 {
685 Some(parameters
686 .non_interactive_ui_to_s_key_switch_decomposer::<DefaultDecomposer<M::MatElement>>())
687 } else {
688 None
689 };
690
691 let pbs_info = BoolPbsInfo {
692 auto_decomposer: parameters.auto_decomposer(),
693 lwe_decomposer: parameters.lwe_decomposer(),
694 rlwe_rgsw_decomposer: parameters.rlwe_rgsw_decomposer(),
695 g_k_dlog_map,
696 embedding_factor,
697 lwe_modop,
698 rlwe_modop,
699 rlwe_nttop,
700 rlwe_qby4,
701 rlwe_auto_maps,
702 parameters: parameters,
703 };
704
705 BoolEvaluator {
706 pbs_info,
707 scratch_memory,
708 nand_test_vec,
709 and_test_vec,
710 or_test_vec,
711 nor_test_vec,
712 xnor_test_vec,
713 xor_test_vec,
714 ni_ui_to_s_ks_decomposer,
715 _phantom: PhantomData,
716 }
717 }
718
719 pub(crate) fn client_key(
720 &self,
721 ) -> ClientKey<<DefaultSecureRng as NewWithSeed>::Seed, M::MatElement> {
722 ClientKey::new(self.parameters().clone())
723 }
724
725 pub(super) fn single_party_server_key<K: SinglePartyClientKey<Element = i32>>(
726 &self,
727 client_key: &K,
728 ) -> SeededSinglePartyServerKey<M, BoolParameters<M::MatElement>, [u8; 32]> {
729 assert_eq!(self.parameters().variant(), &ParameterVariant::SingleParty);
730
731 DefaultSecureRng::with_local_mut(|rng| {
732 let mut main_seed = [0u8; 32];
733 rng.fill_bytes(&mut main_seed);
734
735 let mut main_prng = DefaultSecureRng::new_seeded(main_seed);
736
737 let rlwe_n = self.pbs_info.parameters.rlwe_n().0;
738 let sk_rlwe = client_key.sk_rlwe();
739 let sk_lwe = client_key.sk_lwe();
740
741 // generate auto keys
742 let mut auto_keys = HashMap::new();
743 let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
744 let g = self.pbs_info.parameters.g();
745 let br_q = self.pbs_info.parameters.br_q();
746 let auto_els = self.pbs_info.parameters.auto_element_dlogs();
747 for i in auto_els.into_iter() {
748 let g_pow = if i == 0 {
749 -(g as isize)
750 } else {
751 (g.pow(i as u32) % br_q) as isize
752 };
753 let mut gk = M::zeros(self.pbs_info.auto_decomposer.decomposition_count(), rlwe_n);
754 galois_key_gen(
755 &mut gk,
756 &sk_rlwe,
757 g_pow,
758 &auto_gadget,
759 &self.pbs_info.rlwe_modop,
760 &self.pbs_info.rlwe_nttop,
761 &mut main_prng,
762 rng,
763 );
764 auto_keys.insert(i, gk);
765 }
766
767 // generate rgsw ciphertexts RGSW(si) where si is i^th LWE secret element
768 let ring_size = self.pbs_info.parameters.rlwe_n().0;
769 let rlwe_q = self.pbs_info.parameters.rlwe_q();
770 let (rlrg_d_a, rlrg_d_b) = (
771 self.pbs_info.rlwe_rgsw_decomposer.0.decomposition_count(),
772 self.pbs_info.rlwe_rgsw_decomposer.1.decomposition_count(),
773 );
774 let rlrg_gadget_a = self.pbs_info.rlwe_rgsw_decomposer.0.gadget_vector();
775 let rlrg_gadget_b = self.pbs_info.rlwe_rgsw_decomposer.1.gadget_vector();
776 let rgsw_cts = sk_lwe
777 .iter()
778 .map(|si| {
779 // X^{si}; assume |emebedding_factor * si| < N
780 let mut m = M::R::zeros(ring_size);
781 let si = (self.pbs_info.embedding_factor as i32) * si;
782 // dbg!(si);
783 if si < 0 {
784 // X^{-i} = X^{2N - i} = -X^{N-i}
785 m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one();
786 } else {
787 // X^{i}
788 m.as_mut()[si.abs() as usize] = M::MatElement::one();
789 }
790
791 let mut rgsw_si = M::zeros(rlrg_d_a * 2 + rlrg_d_b, ring_size);
792 secret_key_encrypt_rgsw(
793 &mut rgsw_si,
794 m.as_ref(),
795 &rlrg_gadget_a,
796 &rlrg_gadget_b,
797 &sk_rlwe,
798 &self.pbs_info.rlwe_modop,
799 &self.pbs_info.rlwe_nttop,
800 &mut main_prng,
801 rng,
802 );
803
804 rgsw_si
805 })
806 .collect_vec();
807
808 // LWE KSK from RLWE secret s -> LWE secret z
809 let d_lwe_gadget = self.pbs_info.lwe_decomposer.gadget_vector();
810 let mut lwe_ksk =
811 M::R::zeros(self.pbs_info.lwe_decomposer.decomposition_count() * ring_size);
812 lwe_ksk_keygen(
813 &sk_rlwe,
814 &sk_lwe,
815 &mut lwe_ksk,
816 &d_lwe_gadget,
817 &self.pbs_info.lwe_modop,
818 &mut main_prng,
819 rng,
820 );
821
822 SeededSinglePartyServerKey::from_raw(
823 auto_keys,
824 rgsw_cts,
825 lwe_ksk,
826 self.pbs_info.parameters.clone(),
827 main_seed,
828 )
829 })
830 }
831
832 pub(super) fn multi_party_server_key_share<K: InteractiveMultiPartyClientKey<Element = i32>>(
833 &self,
834 user_id: usize,
835 total_users: usize,
836 cr_seed: &MultiPartyCrs<[u8; 32]>,
837 collective_pk: &M,
838 client_key: &K,
839 ) -> CommonReferenceSeededMultiPartyServerKeyShare<
840 M,
841 BoolParameters<M::MatElement>,
842 MultiPartyCrs<[u8; 32]>,
843 > {
844 assert_eq!(self.parameters().variant(), &ParameterVariant::MultiParty);
845 assert!(user_id < total_users);
846
847 let sk_rlwe = client_key.sk_rlwe();
848 let sk_lwe = client_key.sk_lwe();
849
850 let g = self.pbs_info.parameters.g();
851 let ring_size = self.pbs_info.parameters.rlwe_n().0;
852 let rlwe_q = self.pbs_info.parameters.rlwe_q();
853 let lwe_q = self.pbs_info.parameters.lwe_q();
854
855 let rlweq_modop = &self.pbs_info.rlwe_modop;
856 let rlweq_nttop = &self.pbs_info.rlwe_nttop;
857
858 // sanity check
859 assert!(sk_rlwe.len() == ring_size);
860 assert!(sk_lwe.len() == self.pbs_info.parameters.lwe_n().0);
861
862 // auto keys
863 let auto_keys = self._common_rountine_multi_party_auto_keys_share_gen(
864 cr_seed.auto_keys_cts_seed::<DefaultSecureRng>(),
865 &sk_rlwe,
866 );
867
868 // rgsw ciphertexts of lwe secret elements
869 let (self_leader_rgsws, not_self_leader_rgsws) = DefaultSecureRng::with_local_mut(|rng| {
870 let mut self_leader_rgsw = vec![];
871 let mut not_self_leader_rgsws = vec![];
872
873 let (segment_start, segment_end) = interactive_mult_party_user_id_lwe_segment(
874 user_id,
875 total_users,
876 self.pbs_info().lwe_n(),
877 );
878
879 // self LWE secret indices
880 {
881 // LWE secret indices for which user is the leader they need to send RGSW(m) for
882 // RLWE x RGSW multiplication
883 let rlrg_decomposer = self.pbs_info().rlwe_rgsw_decomposer();
884 let (rlrg_d_a, rlrg_d_b) = (
885 rlrg_decomposer.a().decomposition_count(),
886 rlrg_decomposer.b().decomposition_count(),
887 );
888 let (gadget_a, gadget_b) = (
889 rlrg_decomposer.a().gadget_vector(),
890 rlrg_decomposer.b().gadget_vector(),
891 );
892 for s_index in segment_start..segment_end {
893 let mut out_rgsw = M::zeros(rlrg_d_a * 2 + rlrg_d_b * 2, ring_size);
894 public_key_encrypt_rgsw(
895 &mut out_rgsw,
896 &encode_x_pow_si_with_emebedding_factor::<
897 M::R,
898 CiphertextModulus<M::MatElement>,
899 >(
900 sk_lwe[s_index],
901 self.pbs_info().embedding_factor(),
902 ring_size,
903 self.pbs_info().rlwe_q(),
904 )
905 .as_ref(),
906 collective_pk,
907 &gadget_a,
908 &gadget_b,
909 rlweq_modop,
910 rlweq_nttop,
911 rng,
912 );
913 self_leader_rgsw.push(out_rgsw);
914 }
915 }
916
917 // not self LWE secret indices
918 {
919 // LWE secret indices for which user isn't the leader, they need to send RGSW(m)
920 // for RGSW x RGSW multiplcation
921 let rgsw_rgsw_decomposer = self
922 .pbs_info
923 .parameters
924 .rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
925 let (rgrg_d_a, rgrg_d_b) = (
926 rgsw_rgsw_decomposer.a().decomposition_count(),
927 rgsw_rgsw_decomposer.b().decomposition_count(),
928 );
929 let (rgrg_gadget_a, rgrg_gadget_b) = (
930 rgsw_rgsw_decomposer.a().gadget_vector(),
931 rgsw_rgsw_decomposer.b().gadget_vector(),
932 );
933
934 for s_index in (0..segment_start).chain(segment_end..self.parameters().lwe_n().0) {
935 let mut out_rgsw = M::zeros(rgrg_d_a * 2 + rgrg_d_b * 2, ring_size);
936 public_key_encrypt_rgsw(
937 &mut out_rgsw,
938 &encode_x_pow_si_with_emebedding_factor::<
939 M::R,
940 CiphertextModulus<M::MatElement>,
941 >(
942 sk_lwe[s_index],
943 self.pbs_info().embedding_factor(),
944 ring_size,
945 self.pbs_info().rlwe_q(),
946 )
947 .as_ref(),
948 collective_pk,
949 &rgrg_gadget_a,
950 &rgrg_gadget_b,
951 rlweq_modop,
952 rlweq_nttop,
953 rng,
954 );
955
956 not_self_leader_rgsws.push(out_rgsw);
957 }
958 }
959
960 (self_leader_rgsw, not_self_leader_rgsws)
961 });
962
963 // LWE Ksk
964 let lwe_ksk = self._common_rountine_multi_party_lwe_ksk_share_gen(
965 cr_seed.lwe_ksk_cts_seed_seed::<DefaultSecureRng>(),
966 &sk_rlwe,
967 &sk_lwe,
968 );
969
970 CommonReferenceSeededMultiPartyServerKeyShare::new(
971 self_leader_rgsws,
972 not_self_leader_rgsws,
973 auto_keys,
974 lwe_ksk,
975 cr_seed.clone(),
976 self.pbs_info.parameters.clone(),
977 user_id,
978 )
979 }
980
981 pub(super) fn aggregate_multi_party_server_key_shares<S>(
982 &self,
983 shares: &[CommonReferenceSeededMultiPartyServerKeyShare<
984 M,
985 BoolParameters<M::MatElement>,
986 MultiPartyCrs<S>,
987 >],
988 ) -> SeededMultiPartyServerKey<M, MultiPartyCrs<S>, BoolParameters<M::MatElement>>
989 where
990 S: PartialEq + Clone,
991 M: Clone,
992 {
993 assert_eq!(self.parameters().variant(), &ParameterVariant::MultiParty);
994 assert!(shares.len() > 0);
995
996 let total_users = shares.len();
997
998 let parameters = shares[0].parameters().clone();
999 let cr_seed = shares[0].cr_seed();
1000
1001 let rlwe_n = parameters.rlwe_n().0;
1002 let g = parameters.g() as isize;
1003 let rlwe_q = parameters.rlwe_q();
1004 let lwe_q = parameters.lwe_q();
1005
1006 // sanity checks
1007 shares.iter().skip(1).for_each(|s| {
1008 assert!(s.parameters() == ¶meters);
1009 assert!(s.cr_seed() == cr_seed);
1010 });
1011
1012 let rlweq_modop = &self.pbs_info.rlwe_modop;
1013 let rlweq_nttop = &self.pbs_info.rlwe_nttop;
1014
1015 // auto keys
1016 let mut auto_keys = HashMap::new();
1017 let auto_elements_dlog = parameters.auto_element_dlogs();
1018 for i in auto_elements_dlog.into_iter() {
1019 let mut key = M::zeros(parameters.auto_decomposition_count().0, rlwe_n);
1020
1021 shares.iter().for_each(|s| {
1022 let auto_key_share_i = s.auto_keys().get(&i).expect("Auto key {i} missing");
1023 assert!(
1024 auto_key_share_i.dimension()
1025 == (parameters.auto_decomposition_count().0, rlwe_n)
1026 );
1027 izip!(key.iter_rows_mut(), auto_key_share_i.iter_rows()).for_each(
1028 |(partb_out, partb_share)| {
1029 rlweq_modop.elwise_add_mut(partb_out.as_mut(), partb_share.as_ref());
1030 },
1031 );
1032 });
1033
1034 auto_keys.insert(i, key);
1035 }
1036
1037 // rgsw ciphertext (most expensive part!)
1038 let rgsw_cts = {
1039 let rgsw_by_rgsw_decomposer =
1040 parameters.rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1041 let rlwe_x_rgsw_decomposer = self.pbs_info().rlwe_rgsw_decomposer();
1042 let rgsw_x_rgsw_dimension = (
1043 rgsw_by_rgsw_decomposer.a().decomposition_count() * 2
1044 + rgsw_by_rgsw_decomposer.b().decomposition_count() * 2,
1045 rlwe_n,
1046 );
1047 let rlwe_x_rgsw_dimension = (
1048 rlwe_x_rgsw_decomposer.a().decomposition_count() * 2
1049 + rlwe_x_rgsw_decomposer.b().decomposition_count() * 2,
1050 rlwe_n,
1051 );
1052 let mut rgsw_x_rgsw_scratch_mat = M::zeros(
1053 std::cmp::max(
1054 rgsw_by_rgsw_decomposer.a().decomposition_count(),
1055 rgsw_by_rgsw_decomposer.b().decomposition_count(),
1056 ) + rlwe_x_rgsw_dimension.0,
1057 rlwe_n,
1058 );
1059
1060 let shares_in_correct_order = (0..total_users)
1061 .map(|i| shares.iter().find(|s| s.user_id() == i).unwrap())
1062 .collect_vec();
1063
1064 let lwe_n = self.parameters().lwe_n().0;
1065 let (users_segments, users_segments_sizes): (Vec<(usize, usize)>, Vec<usize>) = (0
1066 ..total_users)
1067 .map(|(user_id)| {
1068 let (start_index, end_index) =
1069 interactive_mult_party_user_id_lwe_segment(user_id, total_users, lwe_n);
1070 ((start_index, end_index), end_index - start_index)
1071 })
1072 .unzip();
1073
1074 let mut rgsw_cts = Vec::with_capacity(lwe_n);
1075 users_segments
1076 .iter()
1077 .enumerate()
1078 .for_each(|(user_id, user_segment)| {
1079 let share = shares_in_correct_order[user_id];
1080 for secret_index in user_segment.0..user_segment.1 {
1081 let mut rgsw_i =
1082 share.self_leader_rgsws()[secret_index - user_segment.0].clone();
1083 // assert already exists in RGSW x RGSW rountine
1084 assert!(rgsw_i.dimension() == rlwe_x_rgsw_dimension);
1085
1086 // multiply leader's RGSW ct at `secret_index` with RGSW cts of other users
1087 // for lwe index `secret_index`
1088 (0..total_users)
1089 .filter(|i| i != &user_id)
1090 .for_each(|other_user_id| {
1091 let mut offset = 0;
1092 if other_user_id < user_id {
1093 offset = users_segments_sizes[other_user_id];
1094 }
1095
1096 let mut other_rgsw_i = shares_in_correct_order[other_user_id]
1097 .not_self_leader_rgsws()
1098 [secret_index.checked_sub(offset).unwrap()]
1099 .clone();
1100 // assert already exists in RGSW x RGSW rountine
1101 assert!(other_rgsw_i.dimension() == rgsw_x_rgsw_dimension);
1102
1103 // send to evaluation domain for RGSwxRGSW mul
1104 other_rgsw_i
1105 .iter_rows_mut()
1106 .for_each(|r| rlweq_nttop.forward(r.as_mut()));
1107
1108 rgsw_by_rgsw_inplace(
1109 &mut rgsw_i,
1110 rlwe_x_rgsw_decomposer.a().decomposition_count(),
1111 rlwe_x_rgsw_decomposer.b().decomposition_count(),
1112 &other_rgsw_i,
1113 &rgsw_by_rgsw_decomposer,
1114 &mut rgsw_x_rgsw_scratch_mat,
1115 rlweq_nttop,
1116 rlweq_modop,
1117 )
1118 });
1119
1120 rgsw_cts.push(rgsw_i);
1121 }
1122 });
1123
1124 rgsw_cts
1125 };
1126
1127 // LWE ksks
1128 let mut lwe_ksk = M::R::zeros(rlwe_n * parameters.lwe_decomposition_count().0);
1129 let lweq_modop = &self.pbs_info.lwe_modop;
1130 shares.iter().for_each(|si| {
1131 assert!(si.lwe_ksk().as_ref().len() == rlwe_n * parameters.lwe_decomposition_count().0);
1132 lweq_modop.elwise_add_mut(lwe_ksk.as_mut(), si.lwe_ksk().as_ref())
1133 });
1134
1135 SeededMultiPartyServerKey::new(rgsw_cts, auto_keys, lwe_ksk, cr_seed.clone(), parameters)
1136 }
1137
1138 pub(super) fn aggregate_non_interactive_multi_party_key_share(
1139 &self,
1140 cr_seed: &NonInteractiveMultiPartyCrs<[u8; 32]>,
1141 total_users: usize,
1142 key_shares: &[CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
1143 M,
1144 NonInteractiveMultiPartyCrs<[u8; 32]>,
1145 >],
1146 ) -> SeededNonInteractiveMultiPartyServerKey<
1147 M,
1148 NonInteractiveMultiPartyCrs<[u8; 32]>,
1149 BoolParameters<M::MatElement>,
1150 >
1151 where
1152 M: Clone + Debug,
1153 {
1154 assert_eq!(
1155 self.parameters().variant(),
1156 &ParameterVariant::NonInteractiveMultiParty
1157 );
1158
1159 // sanity checks
1160 let key_order = {
1161 let existing_key_order = key_shares.iter().map(|s| s.user_index()).collect_vec();
1162
1163 // record the order s.t. key_order[i] stores the position of i^th
1164 // users key share in existing order
1165 let mut key_order = Vec::with_capacity(existing_key_order.len());
1166 (0..total_users).for_each(|i| {
1167 // find i
1168 let index = existing_key_order
1169 .iter()
1170 .position(|x| x == &i)
1171 .expect(&format!("Missing user {i}'s key!"));
1172 key_order.push(index);
1173 });
1174
1175 key_order
1176 };
1177
1178 let rlwe_modop = &self.pbs_info().rlwe_modop;
1179 let nttop = &self.pbs_info().rlwe_nttop;
1180 let ring_size = self.parameters().rlwe_n().0;
1181 let rlwe_q = self.parameters().rlwe_q();
1182 let lwe_modop = self.pbs_info().modop_lweq();
1183
1184 // genrate key switching key from u_i to s
1185 let ui_to_s_ksk_decomposition_count = self
1186 .parameters()
1187 .non_interactive_ui_to_s_key_switch_decomposition_count();
1188 let mut ui_to_s_ksks = key_shares
1189 .iter()
1190 .map(|share| {
1191 let mut useri_ui_to_s_ksk = share.ui_to_s_ksk().clone();
1192 assert!(
1193 useri_ui_to_s_ksk.dimension() == (ui_to_s_ksk_decomposition_count.0, ring_size)
1194 );
1195 key_shares
1196 .iter()
1197 .filter(|x| x.user_index() != share.user_index())
1198 .for_each(|(other_share)| {
1199 let op2 = other_share.ui_to_s_ksk_zero_encs_for_user_i(share.user_index());
1200 assert!(op2.dimension() == (ui_to_s_ksk_decomposition_count.0, ring_size));
1201 izip!(useri_ui_to_s_ksk.iter_rows_mut(), op2.iter_rows()).for_each(
1202 |(add_to, add_from)| {
1203 rlwe_modop.elwise_add_mut(add_to.as_mut(), add_from.as_ref())
1204 },
1205 );
1206 });
1207 useri_ui_to_s_ksk
1208 })
1209 .collect_vec();
1210
1211 let rgsw_cts = {
1212 let mut rgsw_prng =
1213 DefaultSecureRng::new_seeded(cr_seed.ni_rgsw_cts_main_seed::<DefaultSecureRng>());
1214
1215 let rgsw_by_rgsw_decomposer = self
1216 .parameters()
1217 .rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1218
1219 // Generate RGSW Cts
1220 let rgsw_cts_all_users_eval = {
1221 // temporarily put ui_to_s in evaluation domain and sample a_i's for u_i to s
1222 // ksk for upcomong key switches
1223 ui_to_s_ksks.iter_mut().for_each(|ksk_i| {
1224 ksk_i
1225 .iter_rows_mut()
1226 .for_each(|r| nttop.forward(r.as_mut()))
1227 });
1228 let ui_to_s_ksks_part_a_eval = key_shares
1229 .iter()
1230 .map(|share| {
1231 let mut ksk_prng = DefaultSecureRng::new_seeded(
1232 cr_seed
1233 .ui_to_s_ks_seed_for_user_i::<DefaultSecureRng>(share.user_index()),
1234 );
1235 let mut ais = M::zeros(ui_to_s_ksk_decomposition_count.0, ring_size);
1236
1237 ais.iter_rows_mut().for_each(|r_ai| {
1238 RandomFillUniformInModulus::random_fill(
1239 &mut ksk_prng,
1240 rlwe_q,
1241 r_ai.as_mut(),
1242 );
1243
1244 nttop.forward(r_ai.as_mut())
1245 });
1246 ais
1247 })
1248 .collect_vec();
1249
1250 let max_rgrg_deocmposer = if rgsw_by_rgsw_decomposer.a().decomposition_count()
1251 > rgsw_by_rgsw_decomposer.b().decomposition_count()
1252 {
1253 rgsw_by_rgsw_decomposer.a()
1254 } else {
1255 rgsw_by_rgsw_decomposer.b()
1256 };
1257
1258 let ui_to_s_ksk_decomposer = self
1259 .parameters()
1260 .non_interactive_ui_to_s_key_switch_decomposer::<DefaultDecomposer<M::MatElement>>(
1261 );
1262
1263 // Generate a_i*s + E = \sum_{j \in P} a_i*s_j + e for all rlwes in each
1264 // non-interactive rgsws. Then decompose and put decompositions in evaluation
1265 // for u_i -> s key switch.
1266 let decomp_ni_rgsws_part_1_acc = {
1267 let mut tmp_space = M::R::zeros(ring_size);
1268
1269 (0..self.parameters().lwe_n().0)
1270 .into_iter()
1271 .map(|lwe_index| {
1272 (0..max_rgrg_deocmposer.decomposition_count())
1273 .into_iter()
1274 .map(|d_index| {
1275 let mut sum = M::zeros(
1276 ui_to_s_ksk_decomposer.decomposition_count(),
1277 ring_size,
1278 );
1279
1280 // set temp_space to all zeros
1281 tmp_space.as_mut().fill(M::MatElement::zero());
1282
1283 // a_i*s + E
1284 key_shares.iter().for_each(|s| {
1285 rlwe_modop.elwise_add_mut(
1286 tmp_space.as_mut(),
1287 s.ni_rgsw_cts().1[lwe_index].get_row_slice(d_index),
1288 );
1289 });
1290
1291 tmp_space.as_ref().iter().enumerate().for_each(|(ri, el)| {
1292 ui_to_s_ksk_decomposer
1293 .decompose_iter(el)
1294 .enumerate()
1295 .for_each(|(row_j, d_el)| {
1296 (sum.as_mut()[row_j]).as_mut()[ri] = d_el;
1297 });
1298 });
1299
1300 sum.iter_rows_mut().for_each(|r| nttop.forward(r.as_mut()));
1301
1302 sum
1303 })
1304 .collect_vec()
1305 })
1306 .collect_vec()
1307 };
1308
1309 // Sample a_i's are used to generate non-interactive rgsw cts for all lwe
1310 // indices. Since a_i are just needed for key switches , decompose them
1311 // and put them in evaluation domain
1312 // Decomposition count used for RGSW ct in non-interactive key share gen equals
1313 // max of A and B decomposition required in RGSWxRGSW. This is because same
1314 // polynomials are used to generate RLWE'(m) and RLWE'(-sm)
1315 let decomp_ni_rgsw_neg_ais = {
1316 let mut tmp_space = M::R::zeros(ring_size);
1317 (0..self.parameters().lwe_n().0)
1318 .into_iter()
1319 .map(|_| {
1320 // FIXME(Jay): well well, ais are only required for key switching to
1321 // generate RLWE'(m) and RLWE'(m) itself
1322 // requires RLWE(\beta^i m) for i \in RGSWxRGSW
1323 // part B decomposition count. However, we still need to puncture prng
1324 // for ais corresponding to ignored limbs.
1325 // Probably it will be nice idea to
1326 // avoid decomposition after punturing for a_i's corresponding to
1327 // ignored limbs. Moreover, note that, for
1328 // RGSWxRGSW often times decompostion count for part A
1329 // > part B. Hence, it's very likely that we are doing
1330 // unecesary decompositions for ignored limbs all the time.
1331 (0..max_rgrg_deocmposer.decomposition_count())
1332 .map(|_| {
1333 RandomFillUniformInModulus::random_fill(
1334 &mut rgsw_prng,
1335 rlwe_q,
1336 tmp_space.as_mut(),
1337 );
1338
1339 // negate
1340 rlwe_modop.elwise_neg_mut(tmp_space.as_mut());
1341
1342 // decomposer a_i for ui -> s key switch
1343 let mut decomp_neg_ai = M::zeros(
1344 ui_to_s_ksk_decomposer.decomposition_count(),
1345 ring_size,
1346 );
1347 tmp_space.as_ref().iter().enumerate().for_each(
1348 |(index, el)| {
1349 ui_to_s_ksk_decomposer
1350 .decompose_iter(el)
1351 .enumerate()
1352 .for_each(|(row_j, d_el)| {
1353 (decomp_neg_ai.as_mut()[row_j]).as_mut()
1354 [index] = d_el;
1355 });
1356 },
1357 );
1358
1359 // put in evaluation domain
1360 decomp_neg_ai
1361 .iter_rows_mut()
1362 .for_each(|r| nttop.forward(r.as_mut()));
1363
1364 decomp_neg_ai
1365 })
1366 .collect_vec()
1367 })
1368 .collect_vec()
1369 };
1370
1371 // genrate RGSW cts
1372 let rgsw_cts_all_users_eval = izip!(
1373 key_shares.iter(),
1374 ui_to_s_ksks.iter(),
1375 ui_to_s_ksks_part_a_eval.iter()
1376 )
1377 .map(
1378 |(share, user_uitos_ksk_partb_eval, user_uitos_ksk_parta_eval)| {
1379 // RGSW_s(X^{s[i]})
1380 let rgsw_cts_user_i_eval = izip!(
1381 share.ni_rgsw_cts().0.iter(),
1382 decomp_ni_rgsw_neg_ais.iter(),
1383 decomp_ni_rgsws_part_1_acc.iter()
1384 )
1385 .map(
1386 |(
1387 m_encs_under_ui,
1388 decomposed_rgsw_i_neg_ais,
1389 decomposed_acc_rgsw_part1,
1390 )| {
1391 let d_a = rgsw_by_rgsw_decomposer.a().decomposition_count();
1392 let d_b = rgsw_by_rgsw_decomposer.b().decomposition_count();
1393 let max_d = std::cmp::max(d_a, d_b);
1394
1395 assert!(decomposed_rgsw_i_neg_ais.len() == max_d);
1396
1397 // To be RGSW(X^{s[i]}) = [RLWE'(-sm), RLWE'(m)]
1398 let mut rgsw_ct_eval = M::zeros(d_a * 2 + d_b * 2, ring_size);
1399 let (rlwe_dash_nsm, rlwe_dash_m) =
1400 rgsw_ct_eval.split_at_row_mut(d_a * 2);
1401
1402 let mut scratch_row = M::R::zeros(ring_size);
1403
1404 let mut m_encs_under_ui_eval = m_encs_under_ui.clone();
1405 m_encs_under_ui_eval
1406 .iter_rows_mut()
1407 .for_each(|r| nttop.forward(r.as_mut()));
1408
1409 // RLWE(-sm)
1410 {
1411 // Recall that we have RLWE(a_i * s + e). We key
1412 // switch RLWE(a_i * s + e) using ksk(u_i -> s) to
1413 // get RLWE(a_i*s*u_i + e*u_i).
1414 // Given (u_i * a_i + e + \beta m), we obtain
1415 // RLWE(-sm) = RLWE(a_i*s*u_i + e*u_i) + (0, (u_i *
1416 // a_i + e + \beta m))
1417 //
1418 // Again RLWE'(-sm) only cares for RLWE(\beta -sm)
1419 // with scaling factor \beta corresponding to most
1420 // signficant d_a limbs. Hence, we skip (d_max -
1421 // d_a) least signficant limbs
1422
1423 let (rlwe_dash_neg_sm_part_a, rlwe_dash_neg_sm_part_b) =
1424 rlwe_dash_nsm.split_at_mut(d_a);
1425
1426 izip!(
1427 rlwe_dash_neg_sm_part_a.iter_mut(),
1428 rlwe_dash_neg_sm_part_b.iter_mut(),
1429 decomposed_acc_rgsw_part1.iter().skip(max_d - d_a),
1430 m_encs_under_ui_eval.iter_rows().skip(max_d - d_a)
1431 )
1432 .for_each(
1433 |(rlwe_a, rlwe_b, decomp_ai_s, beta_m_enc_ui)| {
1434 // RLWE_s(a_i * s * u_i + u_i * e) = decomp<a_i * s + e>
1435 // * ksk(u_i
1436 // -> s)
1437 izip!(
1438 decomp_ai_s.iter_rows(),
1439 user_uitos_ksk_partb_eval.iter_rows(),
1440 user_uitos_ksk_parta_eval.iter_rows()
1441 )
1442 .for_each(
1443 |(a0, part_b, part_a)| {
1444 // rlwe_b += decomp<a_i * s + e>[i] * ksk
1445 // part_b[i]
1446 rlwe_modop.elwise_mul(
1447 scratch_row.as_mut(),
1448 a0.as_ref(),
1449 part_b.as_ref(),
1450 );
1451 rlwe_modop.elwise_add_mut(
1452 rlwe_b.as_mut(),
1453 scratch_row.as_ref(),
1454 );
1455
1456 // rlwe_a += decomp<a_i * s + e>[i] * ksk
1457 // part_a[i]
1458 rlwe_modop.elwise_mul(
1459 scratch_row.as_mut(),
1460 a0.as_ref(),
1461 part_a.as_ref(),
1462 );
1463 rlwe_modop.elwise_add_mut(
1464 rlwe_a.as_mut(),
1465 scratch_row.as_ref(),
1466 );
1467 },
1468 );
1469
1470 // RLWE_s(-sm) = RLWE_s(a_i * s * u_i + u_i
1471 // * e) + (0, a_i * u + e + m)
1472 rlwe_modop.elwise_add_mut(
1473 rlwe_a.as_mut(),
1474 beta_m_enc_ui.as_ref(),
1475 );
1476 },
1477 );
1478 }
1479
1480 // RLWE(m)
1481 {
1482 // Routine:
1483 // Let RLWE(-a_i * u_i) = (decomp<-a_i> \cdot ksk(u_i -> s)),
1484 // then RLWE(m) = RLWE(a'*s + e + m) = (a_i
1485 // * u_i + e + m, 0) + RLWE(-a_i * u_i)
1486 //
1487 // Since RLWE'(m) only cares for RLWE ciphertexts corresponding
1488 // to higher d_b limbs, we skip routine 1 for lower max(d_a,
1489 // d_b) - d_b limbs
1490 let (rlwe_dash_m_part_a, rlwe_dash_m_part_b) =
1491 rlwe_dash_m.split_at_mut(d_b);
1492
1493 izip!(
1494 rlwe_dash_m_part_a.iter_mut(),
1495 rlwe_dash_m_part_b.iter_mut(),
1496 decomposed_rgsw_i_neg_ais.iter().skip(max_d - d_b),
1497 m_encs_under_ui_eval.iter_rows().skip(max_d - d_b)
1498 )
1499 .for_each(
1500 |(rlwe_a, rlwe_b, decomp_neg_ai, beta_m_enc_ui)| {
1501 // RLWE_s(-a_i * ui) = decomp<-a_i> \cdot ksk(ui -> s)
1502 izip!(
1503 decomp_neg_ai.iter_rows(),
1504 user_uitos_ksk_partb_eval.iter_rows(),
1505 user_uitos_ksk_parta_eval.iter_rows()
1506 )
1507 .for_each(
1508 |(a0, part_b, part_a)| {
1509 // rlwe_b += decomp<a>[i] * ksk part_b[i]
1510 rlwe_modop.elwise_mul(
1511 scratch_row.as_mut(),
1512 a0.as_ref(),
1513 part_b.as_ref(),
1514 );
1515 rlwe_modop.elwise_add_mut(
1516 rlwe_b.as_mut(),
1517 scratch_row.as_ref(),
1518 );
1519
1520 // rlwe_a += decomp<a>[i] * ksk
1521 // part_a[i]
1522 rlwe_modop.elwise_mul(
1523 scratch_row.as_mut(),
1524 a0.as_ref(),
1525 part_a.as_ref(),
1526 );
1527 rlwe_modop.elwise_add_mut(
1528 rlwe_a.as_mut(),
1529 scratch_row.as_ref(),
1530 );
1531 },
1532 );
1533
1534 // RLWE_s(m) = (a_i * ui + e + \beta m, 0)
1535 // +
1536 // RLWE_s(-a_i * ui)
1537 rlwe_modop.elwise_add_mut(
1538 rlwe_b.as_mut(),
1539 beta_m_enc_ui.as_ref(),
1540 );
1541 },
1542 );
1543 }
1544
1545 rgsw_ct_eval
1546 },
1547 )
1548 .collect_vec();
1549 rgsw_cts_user_i_eval
1550 },
1551 )
1552 .collect_vec();
1553
1554 // put u_i -> s ksks back in coefficient domain
1555 ui_to_s_ksks.iter_mut().for_each(|ksk_i| {
1556 ksk_i
1557 .iter_rows_mut()
1558 .for_each(|r| nttop.backward(r.as_mut()))
1559 });
1560
1561 rgsw_cts_all_users_eval
1562 };
1563
1564 // RGSW x RGSW
1565 let lwe_n = self.parameters().lwe_n().0;
1566 let mut scratch_matrix = M::zeros(
1567 std::cmp::max(
1568 rgsw_by_rgsw_decomposer.a().decomposition_count(),
1569 rgsw_by_rgsw_decomposer.b().decomposition_count(),
1570 ) + (rgsw_by_rgsw_decomposer.a().decomposition_count() * 2
1571 + rgsw_by_rgsw_decomposer.b().decomposition_count() * 2),
1572 ring_size,
1573 );
1574 let rgsw_cts_untrimmed = (0..lwe_n).map(|s_index| {
1575 // copy over s_index^th rgsw ct of user 0. Use it to accumulate RGSW products of
1576 // all RGSW ciphertexts at s_index
1577 let mut rgsw_i = rgsw_cts_all_users_eval[0][s_index].clone();
1578 rgsw_i
1579 .iter_rows_mut()
1580 .for_each(|r| nttop.backward(r.as_mut()));
1581
1582 rgsw_cts_all_users_eval
1583 .iter()
1584 .skip(1)
1585 .for_each(|user_i_rgsws| {
1586 rgsw_by_rgsw_inplace(
1587 &mut rgsw_i,
1588 rgsw_by_rgsw_decomposer.a().decomposition_count(),
1589 rgsw_by_rgsw_decomposer.b().decomposition_count(),
1590 &user_i_rgsws[s_index],
1591 &rgsw_by_rgsw_decomposer,
1592 &mut scratch_matrix,
1593 nttop,
1594 rlwe_modop,
1595 );
1596 });
1597
1598 rgsw_i
1599 });
1600
1601 // After this point we don't require RGSW cts for RGSWxRGSW
1602 // multiplicaiton anymore. So we trim them to suit RLWExRGSW require for PBS
1603 let rgsw_cts = rgsw_cts_untrimmed
1604 .map(|rgsw_ct| {
1605 trim_rgsw_ct_matrix_from_rgrg_to_rlrg(
1606 rgsw_ct,
1607 self.parameters().rgsw_by_rgsw_decomposition_params(),
1608 self.parameters().rlwe_by_rgsw_decomposition_params(),
1609 )
1610 })
1611 .collect_vec();
1612 rgsw_cts
1613 };
1614
1615 // auto keys
1616 let auto_keys = {
1617 let mut auto_keys = HashMap::new();
1618 let auto_elements_dlog = self.parameters().auto_element_dlogs();
1619 for i in auto_elements_dlog.into_iter() {
1620 let mut key = M::zeros(self.parameters().auto_decomposition_count().0, ring_size);
1621
1622 key_shares.iter().for_each(|s| {
1623 let auto_key_share_i =
1624 s.auto_keys_share().get(&i).expect("Auto key {i} missing");
1625 assert!(
1626 auto_key_share_i.dimension()
1627 == (self.parameters().auto_decomposition_count().0, ring_size)
1628 );
1629 izip!(key.iter_rows_mut(), auto_key_share_i.iter_rows()).for_each(
1630 |(partb_out, partb_share)| {
1631 rlwe_modop.elwise_add_mut(partb_out.as_mut(), partb_share.as_ref());
1632 },
1633 );
1634 });
1635
1636 auto_keys.insert(i, key);
1637 }
1638 auto_keys
1639 };
1640
1641 // LWE ksk
1642 let lwe_ksk = {
1643 let mut lwe_ksk =
1644 M::R::zeros(self.parameters().lwe_decomposition_count().0 * ring_size);
1645 key_shares.iter().for_each(|s| {
1646 assert!(
1647 s.lwe_ksk_share().as_ref().len()
1648 == self.parameters().lwe_decomposition_count().0 * ring_size
1649 );
1650 lwe_modop.elwise_add_mut(lwe_ksk.as_mut(), s.lwe_ksk_share().as_ref());
1651 });
1652 lwe_ksk
1653 };
1654
1655 SeededNonInteractiveMultiPartyServerKey::new(
1656 ui_to_s_ksks,
1657 key_order,
1658 rgsw_cts,
1659 auto_keys,
1660 lwe_ksk,
1661 cr_seed.clone(),
1662 self.parameters().clone(),
1663 )
1664 }
1665
1666 pub(super) fn non_interactive_multi_party_key_share<
1667 K: NonInteractiveMultiPartyClientKey<Element = i32>,
1668 >(
1669 &self,
1670 cr_seed: &NonInteractiveMultiPartyCrs<[u8; 32]>,
1671 self_index: usize,
1672 total_users: usize,
1673 client_key: &K,
1674 ) -> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<
1675 M,
1676 NonInteractiveMultiPartyCrs<[u8; 32]>,
1677 > {
1678 assert_eq!(
1679 self.parameters().variant(),
1680 &ParameterVariant::NonInteractiveMultiParty
1681 );
1682
1683 // TODO: check whether parameters support `total_users`
1684 let nttop = self.pbs_info().nttop_rlweq();
1685 let rlwe_modop = self.pbs_info().modop_rlweq();
1686 let ring_size = self.pbs_info().rlwe_n();
1687 let rlwe_q = self.parameters().rlwe_q();
1688
1689 let sk_rlwe = client_key.sk_rlwe();
1690 let sk_u_rlwe = client_key.sk_u_rlwe();
1691 let sk_lwe = client_key.sk_lwe();
1692
1693 let (ui_to_s_ksk, ksk_zero_encs_for_others) = DefaultSecureRng::with_local_mut(|rng| {
1694 // ui_to_s_ksk
1695 let non_interactive_decomposer = self
1696 .parameters()
1697 .non_interactive_ui_to_s_key_switch_decomposer::<DefaultDecomposer<M::MatElement>>(
1698 );
1699 let non_interactive_gadget_vec = non_interactive_decomposer.gadget_vector();
1700 let ui_to_s_ksk = {
1701 let mut p_rng = DefaultSecureRng::new_seeded(
1702 cr_seed.ui_to_s_ks_seed_for_user_i::<DefaultSecureRng>(self_index),
1703 );
1704
1705 non_interactive_ksk_gen::<M, _, _, _, _, _>(
1706 &sk_rlwe,
1707 &sk_u_rlwe,
1708 &non_interactive_gadget_vec,
1709 &mut p_rng,
1710 rng,
1711 nttop,
1712 rlwe_modop,
1713 )
1714 };
1715
1716 // zero encryptions for others uj_to_s ksk
1717 let all_users_except_self = (0..total_users).filter(|x| *x != self_index);
1718 let zero_encs_for_others = all_users_except_self
1719 .map(|other_user_index| {
1720 let mut p_rng = DefaultSecureRng::new_seeded(
1721 cr_seed.ui_to_s_ks_seed_for_user_i::<DefaultSecureRng>(other_user_index),
1722 );
1723 let zero_encs =
1724 non_interactive_ksk_zero_encryptions_for_other_party_i::<M, _, _, _, _, _>(
1725 &sk_rlwe,
1726 &non_interactive_gadget_vec,
1727 &mut p_rng,
1728 rng,
1729 nttop,
1730 rlwe_modop,
1731 );
1732 zero_encs
1733 })
1734 .collect_vec();
1735
1736 (ui_to_s_ksk, zero_encs_for_others)
1737 });
1738
1739 // Non-interactive RGSW cts
1740 let (ni_rgsw_zero_encs, self_leader_ni_rgsw_cts, not_self_leader_rgsw_cts) = {
1741 // Warning: Below we assume that max(d_a, d_b) for RLWE x RGSW are always
1742 // smaller than max(d_a, d_b) for RGSW x RGSW. The assumption is reasonable
1743 // since RGSW x RGSW multiplication always precede RLWE x RGSW multiplication,
1744 // hence require greater room.
1745 let rgsw_x_rgsw_decomposer = self
1746 .parameters()
1747 .rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1748 let rlwe_x_rgsw_decomposer = self
1749 .parameters()
1750 .rlwe_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1751
1752 let sj_poly_eval = {
1753 let mut s = M::R::try_convert_from(&sk_rlwe, rlwe_q);
1754 nttop.forward(s.as_mut());
1755 s
1756 };
1757
1758 let d_rgsw_a = rgsw_x_rgsw_decomposer.a().decomposition_count();
1759
1760 // Zero encyptions for each LWE index.
1761 // Zero encryptions are needed to generate RLWE(-sm) from a_i * u_j + e. Hence,
1762 // zero encryptions are only required for first `d_a` a's where d_a
1763 // is decomposition count `a` in RGSW x RGSW.
1764 let zero_encs = {
1765 (0..self.parameters().lwe_n().0)
1766 .map(|lwe_index| {
1767 let mut p_rng = DefaultSecureRng::new_seeded(
1768 cr_seed.ni_rgsw_ct_seed_for_index::<DefaultSecureRng>(lwe_index),
1769 );
1770
1771 let mut scratch = M::R::zeros(self.parameters().rlwe_n().0);
1772
1773 let mut zero_enc = M::zeros(d_rgsw_a, self.parameters().rlwe_n().0);
1774 zero_enc.iter_rows_mut().for_each(|out| {
1775 // sample a_i
1776 RandomFillUniformInModulus::random_fill(
1777 &mut p_rng,
1778 rlwe_q,
1779 out.as_mut(),
1780 );
1781
1782 // a_i * s_j
1783 nttop.forward(out.as_mut());
1784 rlwe_modop.elwise_mul_mut(out.as_mut(), sj_poly_eval.as_ref());
1785 nttop.backward(out.as_mut());
1786
1787 // a_j * s_j + e
1788 DefaultSecureRng::with_local_mut_mut(&mut |rng| {
1789 RandomFillGaussianInModulus::random_fill(
1790 rng,
1791 rlwe_q,
1792 scratch.as_mut(),
1793 );
1794 });
1795
1796 rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
1797 });
1798
1799 zero_enc
1800 })
1801 .collect_vec()
1802 };
1803
1804 let uj_poly_eval = {
1805 let mut u = M::R::try_convert_from(&sk_u_rlwe, rlwe_q);
1806 nttop.forward(u.as_mut());
1807 u
1808 };
1809
1810 // Non-interactive RGSW ciphertexts: a_i u_j + e + \beta X^{s_j[l]}
1811 // Non-interactive RGSW ciphertexts are needed for both RLWE'(-sm)
1812 // and RLWE'(m). Hence, we should generate them a_i's with i \in [0,
1813 // max(d_a, d_b)) where d_{a/b} are either decomposition counts for
1814 // RGSW x RGSW (if self is not leader for l^th) or RLWE x RGSW (if
1815 // self is leader for l^th index)
1816 let (self_start_index, self_end_index) = interactive_mult_party_user_id_lwe_segment(
1817 self_index,
1818 total_users,
1819 self.parameters().lwe_n().0,
1820 );
1821
1822 // For LWE indices [self_start_index, self_end_index) user generates
1823 // non-interactive RGSW cts for RLWE x RGSW product. We refer to
1824 // such indices where user is the leader. For the rest of
1825 // the indices user generates non-interactive RGWS cts for RGSW x
1826 // RGSW multiplication. We refer to such indices as where user is
1827 // not the leader.
1828
1829 let self_leader_ni_rgsw_cts = {
1830 let max_rlwe_x_rgsw_d = std::cmp::max(
1831 rlwe_x_rgsw_decomposer.a().decomposition_count(),
1832 rlwe_x_rgsw_decomposer.b().decomposition_count(),
1833 );
1834 (self_start_index..self_end_index)
1835 .map(|lwe_index| {
1836 let mut p_rng = DefaultSecureRng::new_seeded(
1837 cr_seed.ni_rgsw_ct_seed_for_index::<DefaultSecureRng>(lwe_index),
1838 );
1839 let mut ni_rgsw_cts =
1840 M::zeros(max_rlwe_x_rgsw_d, self.parameters().rlwe_n().0);
1841 let mut scratch = M::R::zeros(self.parameters().rlwe_n().0);
1842 ni_rgsw_cts.iter_rows_mut().for_each(|out| {
1843 // sample a_i
1844 RandomFillUniformInModulus::random_fill(
1845 &mut p_rng,
1846 rlwe_q,
1847 out.as_mut(),
1848 );
1849
1850 // u_j * a_i
1851 nttop.forward(out.as_mut());
1852 rlwe_modop.elwise_mul_mut(out.as_mut(), uj_poly_eval.as_ref());
1853 nttop.backward(out.as_mut());
1854
1855 // u_j + a_i + e
1856 DefaultSecureRng::with_local_mut_mut(&mut |rng| {
1857 RandomFillGaussianInModulus::random_fill(
1858 rng,
1859 rlwe_q,
1860 scratch.as_mut(),
1861 );
1862 });
1863 rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
1864 });
1865
1866 ni_rgsw_cts
1867 })
1868 .collect_vec()
1869 };
1870
1871 let not_self_leader_rgsw_cts = {
1872 let max_rgsw_x_rgsw_d = std::cmp::max(
1873 rgsw_x_rgsw_decomposer.a().decomposition_count(),
1874 rgsw_x_rgsw_decomposer.b().decomposition_count(),
1875 );
1876 ((0..self_start_index).chain(self_end_index..self.parameters().lwe_n().0))
1877 .map(|lwe_index| {
1878 let mut p_rng = DefaultSecureRng::new_seeded(
1879 cr_seed.ni_rgsw_ct_seed_for_index::<DefaultSecureRng>(lwe_index),
1880 );
1881 let mut ni_rgsw_cts =
1882 M::zeros(max_rgsw_x_rgsw_d, self.parameters().rlwe_n().0);
1883 let mut scratch = M::R::zeros(self.parameters().rlwe_n().0);
1884 ni_rgsw_cts.iter_rows_mut().for_each(|out| {
1885 // sample a_i
1886 RandomFillUniformInModulus::random_fill(
1887 &mut p_rng,
1888 rlwe_q,
1889 out.as_mut(),
1890 );
1891
1892 // u_j * a_i
1893 nttop.forward(out.as_mut());
1894 rlwe_modop.elwise_mul_mut(out.as_mut(), uj_poly_eval.as_ref());
1895 nttop.backward(out.as_mut());
1896
1897 // u_j + a_i + e
1898 DefaultSecureRng::with_local_mut_mut(&mut |rng| {
1899 RandomFillGaussianInModulus::random_fill(
1900 rng,
1901 rlwe_q,
1902 scratch.as_mut(),
1903 );
1904 });
1905 rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
1906 });
1907
1908 ni_rgsw_cts
1909 })
1910 .collect_vec()
1911 };
1912
1913 (zero_encs, self_leader_ni_rgsw_cts, not_self_leader_rgsw_cts)
1914 };
1915
1916 // Auto key share
1917 let auto_keys_share = {
1918 let auto_seed = cr_seed.auto_keys_cts_seed::<DefaultSecureRng>();
1919 self._common_rountine_multi_party_auto_keys_share_gen(auto_seed, &sk_rlwe)
1920 };
1921
1922 // Lwe Ksk share
1923 let lwe_ksk_share = {
1924 let lwe_ksk_seed = cr_seed.lwe_ksk_cts_seed::<DefaultSecureRng>();
1925 self._common_rountine_multi_party_lwe_ksk_share_gen(lwe_ksk_seed, &sk_rlwe, &sk_lwe)
1926 };
1927
1928 CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare::new(
1929 self_leader_ni_rgsw_cts,
1930 not_self_leader_rgsw_cts,
1931 ni_rgsw_zero_encs,
1932 ui_to_s_ksk,
1933 ksk_zero_encs_for_others,
1934 auto_keys_share,
1935 lwe_ksk_share,
1936 self_index,
1937 cr_seed.clone(),
1938 )
1939 }
1940
1941 fn _common_rountine_multi_party_auto_keys_share_gen(
1942 &self,
1943 auto_seed: <DefaultSecureRng as NewWithSeed>::Seed,
1944 sk_rlwe: &[i32],
1945 ) -> HashMap<usize, M> {
1946 let g = self.pbs_info.parameters.g();
1947 let ring_size = self.pbs_info.parameters.rlwe_n().0;
1948 let br_q = self.pbs_info.parameters.br_q();
1949 let rlweq_modop = &self.pbs_info.rlwe_modop;
1950 let rlweq_nttop = &self.pbs_info.rlwe_nttop;
1951
1952 DefaultSecureRng::with_local_mut(|rng| {
1953 let mut p_rng = DefaultSecureRng::new_seeded(auto_seed);
1954
1955 let mut auto_keys = HashMap::new();
1956 let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
1957 let auto_element_dlogs = self.pbs_info.parameters.auto_element_dlogs();
1958
1959 for i in auto_element_dlogs.into_iter() {
1960 let g_pow = if i == 0 {
1961 -(g as isize)
1962 } else {
1963 (g.pow(i as u32) % br_q) as isize
1964 };
1965
1966 let mut ksk_out = M::zeros(
1967 self.pbs_info.auto_decomposer.decomposition_count(),
1968 ring_size,
1969 );
1970 galois_key_gen(
1971 &mut ksk_out,
1972 sk_rlwe,
1973 g_pow,
1974 &auto_gadget,
1975 rlweq_modop,
1976 rlweq_nttop,
1977 &mut p_rng,
1978 rng,
1979 );
1980 auto_keys.insert(i, ksk_out);
1981 }
1982
1983 auto_keys
1984 })
1985 }
1986
1987 fn _common_rountine_multi_party_lwe_ksk_share_gen(
1988 &self,
1989 lwe_ksk_seed: <DefaultSecureRng as NewWithSeed>::Seed,
1990 sk_rlwe: &[i32],
1991 sk_lwe: &[i32],
1992 ) -> M::R {
1993 DefaultSecureRng::with_local_mut(|rng| {
1994 let mut p_rng = DefaultSecureRng::new_seeded(lwe_ksk_seed);
1995 let mut lwe_ksk = M::R::zeros(
1996 self.pbs_info.lwe_decomposer.decomposition_count() * self.parameters().rlwe_n().0,
1997 );
1998 let lwe_modop = &self.pbs_info.lwe_modop;
1999 let d_lwe_gadget_vec = self.pbs_info.lwe_decomposer.gadget_vector();
2000 lwe_ksk_keygen(
2001 sk_rlwe,
2002 sk_lwe,
2003 &mut lwe_ksk,
2004 &d_lwe_gadget_vec,
2005 lwe_modop,
2006 &mut p_rng,
2007 rng,
2008 );
2009 lwe_ksk
2010 })
2011 }
2012
2013 pub(super) fn multi_party_public_key_share<K: InteractiveMultiPartyClientKey<Element = i32>>(
2014 &self,
2015 cr_seed: &MultiPartyCrs<[u8; 32]>,
2016 client_key: &K,
2017 ) -> CommonReferenceSeededCollectivePublicKeyShare<
2018 <M as Matrix>::R,
2019 [u8; 32],
2020 BoolParameters<<M as Matrix>::MatElement>,
2021 > {
2022 DefaultSecureRng::with_local_mut(|rng| {
2023 let mut share_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0);
2024 let modop = &self.pbs_info.rlwe_modop;
2025 let nttop = &self.pbs_info.rlwe_nttop;
2026 let pk_seed = cr_seed.public_key_share_seed::<DefaultSecureRng>();
2027 let mut main_prng = DefaultSecureRng::new_seeded(pk_seed);
2028 public_key_share(
2029 &mut share_out,
2030 &client_key.sk_rlwe(),
2031 modop,
2032 nttop,
2033 &mut main_prng,
2034 rng,
2035 );
2036 CommonReferenceSeededCollectivePublicKeyShare::new(
2037 share_out,
2038 pk_seed,
2039 self.pbs_info.parameters.clone(),
2040 )
2041 })
2042 }
2043
2044 pub(super) fn multi_party_decryption_share<K: InteractiveMultiPartyClientKey<Element = i32>>(
2045 &self,
2046 lwe_ct: &M::R,
2047 client_key: &K,
2048 ) -> <M as Matrix>::MatElement {
2049 assert!(lwe_ct.as_ref().len() == self.pbs_info.parameters.rlwe_n().0 + 1);
2050 let modop = &self.pbs_info.rlwe_modop;
2051 let mut neg_s =
2052 M::R::try_convert_from(&client_key.sk_rlwe(), &self.pbs_info.parameters.rlwe_q());
2053 modop.elwise_neg_mut(neg_s.as_mut());
2054
2055 let mut neg_sa = M::MatElement::zero();
2056 izip!(lwe_ct.as_ref().iter().skip(1), neg_s.as_ref().iter()).for_each(|(ai, nsi)| {
2057 neg_sa = modop.add(&neg_sa, &modop.mul(ai, nsi));
2058 });
2059
2060 let e = DefaultSecureRng::with_local_mut(|rng| {
2061 let mut e =
2062 RandomGaussianElementInModulus::random(rng, self.pbs_info.parameters.rlwe_q());
2063 e
2064 });
2065 let share = modop.add(&neg_sa, &e);
2066
2067 share
2068 }
2069
2070 pub(crate) fn multi_party_decrypt(&self, shares: &[M::MatElement], lwe_ct: &M::R) -> bool {
2071 let modop = &self.pbs_info.rlwe_modop;
2072 let mut sum_a = M::MatElement::zero();
2073 shares
2074 .iter()
2075 .for_each(|share_i| sum_a = modop.add(&sum_a, &share_i));
2076
2077 let encoded_m = modop.add(&lwe_ct.as_ref()[0], &sum_a);
2078 self.pbs_info.parameters.rlwe_q().decode(encoded_m)
2079 }
2080
2081 pub(crate) fn pk_encrypt(&self, pk: &M, m: bool) -> M::R {
2082 self.pk_encrypt_batched(pk, &vec![m]).remove(0)
2083 }
2084
2085 /// Encrypts a batch booleans as multiple LWE ciphertexts.
2086 ///
2087 /// For public key encryption we first encrypt `m` as a RLWE ciphertext and
2088 /// then sample extract LWE samples at required indices.
2089 ///
2090 /// - TODO(Jay:) Communication can be improved by not sample exctracting and
2091 /// instead just truncate degree 0 values (part Bs)
2092 pub(crate) fn pk_encrypt_batched(&self, pk: &M, m: &[bool]) -> Vec<M::R> {
2093 DefaultSecureRng::with_local_mut(|rng| {
2094 let ring_size = self.pbs_info.parameters.rlwe_n().0;
2095 assert!(
2096 m.len() <= ring_size,
2097 "Cannot batch encrypt > ring_size{ring_size} elements at once"
2098 );
2099
2100 let modop = &self.pbs_info.rlwe_modop;
2101 let nttop = &self.pbs_info.rlwe_nttop;
2102
2103 // RLWE(0)
2104 // sample ephemeral key u
2105 let mut u = vec![0i32; ring_size];
2106 fill_random_ternary_secret_with_hamming_weight(u.as_mut(), ring_size >> 1, rng);
2107 let mut u = M::R::try_convert_from(&u, &self.pbs_info.parameters.rlwe_q());
2108 nttop.forward(u.as_mut());
2109
2110 let mut ua = M::R::zeros(ring_size);
2111 ua.as_mut().copy_from_slice(pk.get_row_slice(0));
2112 let mut ub = M::R::zeros(ring_size);
2113 ub.as_mut().copy_from_slice(pk.get_row_slice(1));
2114
2115 // a*u
2116 nttop.forward(ua.as_mut());
2117 modop.elwise_mul_mut(ua.as_mut(), u.as_ref());
2118 nttop.backward(ua.as_mut());
2119
2120 // b*u
2121 nttop.forward(ub.as_mut());
2122 modop.elwise_mul_mut(ub.as_mut(), u.as_ref());
2123 nttop.backward(ub.as_mut());
2124
2125 let mut rlwe = M::zeros(2, ring_size);
2126 // sample error
2127 rlwe.iter_rows_mut().for_each(|ri| {
2128 RandomFillGaussianInModulus::<[M::MatElement], CiphertextModulus<M::MatElement>>::random_fill(
2129 rng,
2130 &self.pbs_info.parameters.rlwe_q(),
2131 ri.as_mut(),
2132 );
2133 });
2134
2135 // a*u + e0
2136 modop.elwise_add_mut(rlwe.get_row_mut(0), ua.as_ref());
2137 // b*u + e1
2138 modop.elwise_add_mut(rlwe.get_row_mut(1), ub.as_ref());
2139
2140 //FIXME(Jay): Figure out a way to get Q/8 form modulus
2141 let mut m_vec = M::R::zeros(ring_size);
2142 izip!(m_vec.as_mut().iter_mut(), m.iter()).for_each(|(m_el, m_bool)| {
2143 if *m_bool {
2144 // Q/8
2145 *m_el = self.pbs_info.rlwe_q().true_el()
2146 } else {
2147 // -Q/8
2148 *m_el = self.pbs_info.rlwe_q().false_el()
2149 }
2150 });
2151
2152 // b*u + e1 + m
2153 modop.elwise_add_mut(rlwe.get_row_mut(1), m_vec.as_ref());
2154 // rlwe.set(1, 0, modop.add(rlwe.get(1, 0), &m));
2155
2156 // sample extract index required indices
2157 let samples = m.len();
2158 (0..samples)
2159 .into_iter()
2160 .map(|i| {
2161 let mut lwe_out = M::R::zeros(ring_size + 1);
2162 sample_extract(&mut lwe_out, &rlwe, modop, i);
2163 lwe_out
2164 })
2165 .collect_vec()
2166 })
2167 }
2168
2169 /// TODO(Jay): Fetch client key from thread local
2170 pub fn sk_encrypt<K: SinglePartyClientKey<Element = i32>>(
2171 &self,
2172 m: bool,
2173 client_key: &K,
2174 ) -> M::R {
2175 //FIXME(Jay): Figure out a way to get Q/8 form modulus
2176 let m = if m {
2177 // Q/8
2178 self.pbs_info.rlwe_q().true_el()
2179 } else {
2180 // -Q/8
2181 self.pbs_info.rlwe_q().false_el()
2182 };
2183
2184 DefaultSecureRng::with_local_mut(|rng| {
2185 let mut lwe_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0 + 1);
2186 encrypt_lwe(
2187 &mut lwe_out,
2188 &m,
2189 &client_key.sk_rlwe(),
2190 &self.pbs_info.rlwe_modop,
2191 rng,
2192 );
2193 lwe_out
2194 })
2195 }
2196
2197 pub fn sk_decrypt<K: SinglePartyClientKey<Element = i32>>(
2198 &self,
2199 lwe_ct: &M::R,
2200 client_key: &K,
2201 ) -> bool {
2202 let m = decrypt_lwe(lwe_ct, &client_key.sk_rlwe(), &self.pbs_info.rlwe_modop);
2203 self.pbs_info.rlwe_q().decode(m)
2204 }
2205}
2206
2207impl<M, NttOp, RlweModOp, LweModOp, Skey> BoolEvaluator<M, NttOp, RlweModOp, LweModOp, Skey>
2208where
2209 M: MatrixMut + MatrixEntity,
2210 M::R: RowMut + RowEntity,
2211 M::MatElement: PrimInt + FromPrimitive + One + Copy + Zero + Display + WrappingSub + NumInfo,
2212 RlweModOp: VectorOps<Element = M::MatElement> + ArithmeticOps<Element = M::MatElement>,
2213 LweModOp: VectorOps<Element = M::MatElement> + ArithmeticOps<Element = M::MatElement>,
2214 NttOp: Ntt<Element = M::MatElement>,
2215{
2216 /// Returns c0 + c1 + Q/4
2217 fn _add_and_shift_lwe_cts(&self, c0: &mut M::R, c1: &M::R) {
2218 let modop = &self.pbs_info.rlwe_modop;
2219 modop.elwise_add_mut(c0.as_mut(), c1.as_ref());
2220 // +Q/4
2221 c0.as_mut()[0] = modop.add(&c0.as_ref()[0], &self.pbs_info.rlwe_qby4);
2222 }
2223
2224 /// Returns 2(c0 - c1) + Q/4
2225 fn _subtract_double_lwe_cts(&self, c0: &mut M::R, c1: &M::R) {
2226 let modop = &self.pbs_info.rlwe_modop;
2227 // c0 - c1
2228 modop.elwise_sub_mut(c0.as_mut(), c1.as_ref());
2229
2230 // double
2231 c0.as_mut().iter_mut().for_each(|v| *v = modop.add(v, v));
2232 }
2233}
2234
2235impl<M, NttOp, RlweModOp, LweModOp, Skey> BooleanGates
2236 for BoolEvaluator<M, NttOp, RlweModOp, LweModOp, Skey>
2237where
2238 M: MatrixMut + MatrixEntity,
2239 M::R: RowMut + RowEntity + Clone,
2240 M::MatElement: PrimInt
2241 + FromPrimitive
2242 + One
2243 + Copy
2244 + Zero
2245 + Display
2246 + WrappingSub
2247 + NumInfo
2248 + From<bool>
2249 + WrappingAdd
2250 + Debug,
2251 RlweModOp: VectorOps<Element = M::MatElement>
2252 + ArithmeticOps<Element = M::MatElement>
2253 + ShoupMatrixFMA<M::R>,
2254 LweModOp: VectorOps<Element = M::MatElement> + ArithmeticOps<Element = M::MatElement>,
2255 NttOp: Ntt<Element = M::MatElement>,
2256 Skey: PbsKey<AutoKey = <Skey as PbsKey>::RgswCt, LweKskKey = M>,
2257 <Skey as PbsKey>::RgswCt: WithShoupRepr<M = M>,
2258{
2259 type Ciphertext = M::R;
2260 type Key = Skey;
2261
2262 fn nand_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2263 self._add_and_shift_lwe_cts(c0, c1);
2264
2265 // PBS
2266 pbs(
2267 &self.pbs_info,
2268 &self.nand_test_vec,
2269 c0,
2270 server_key,
2271 &mut self.scratch_memory.lwe_vector,
2272 &mut self.scratch_memory.decomposition_matrix,
2273 );
2274 }
2275
2276 fn and_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2277 self._add_and_shift_lwe_cts(c0, c1);
2278
2279 // PBS
2280 pbs(
2281 &self.pbs_info,
2282 &self.and_test_vec,
2283 c0,
2284 server_key,
2285 &mut self.scratch_memory.lwe_vector,
2286 &mut self.scratch_memory.decomposition_matrix,
2287 );
2288 }
2289
2290 fn or_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2291 self._add_and_shift_lwe_cts(c0, c1);
2292
2293 // PBS
2294 pbs(
2295 &self.pbs_info,
2296 &self.or_test_vec,
2297 c0,
2298 server_key,
2299 &mut self.scratch_memory.lwe_vector,
2300 &mut self.scratch_memory.decomposition_matrix,
2301 );
2302 }
2303
2304 fn nor_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2305 self._add_and_shift_lwe_cts(c0, c1);
2306
2307 // PBS
2308 pbs(
2309 &self.pbs_info,
2310 &self.nor_test_vec,
2311 c0,
2312 server_key,
2313 &mut self.scratch_memory.lwe_vector,
2314 &mut self.scratch_memory.decomposition_matrix,
2315 )
2316 }
2317
2318 fn xor_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2319 self._subtract_double_lwe_cts(c0, c1);
2320
2321 // PBS
2322 pbs(
2323 &self.pbs_info,
2324 &self.xor_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 xnor_inplace(&mut self, c0: &mut M::R, c1: &M::R, server_key: &Self::Key) {
2333 self._subtract_double_lwe_cts(c0, c1);
2334
2335 // PBS
2336 pbs(
2337 &self.pbs_info,
2338 &self.xnor_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 not_inplace(&mut self, c0: &mut M::R) {
2347 let modop = &self.pbs_info.rlwe_modop;
2348 c0.as_mut().iter_mut().for_each(|v| *v = modop.neg(v));
2349 }
2350
2351 fn and(
2352 &mut self,
2353 c0: &Self::Ciphertext,
2354 c1: &Self::Ciphertext,
2355 key: &Self::Key,
2356 ) -> Self::Ciphertext {
2357 let mut out = c0.clone();
2358 self.and_inplace(&mut out, c1, key);
2359 out
2360 }
2361
2362 fn nand(
2363 &mut self,
2364 c0: &Self::Ciphertext,
2365 c1: &Self::Ciphertext,
2366 key: &Self::Key,
2367 ) -> Self::Ciphertext {
2368 let mut out = c0.clone();
2369 self.nand_inplace(&mut out, c1, key);
2370 out
2371 }
2372
2373 fn or(
2374 &mut self,
2375 c0: &Self::Ciphertext,
2376 c1: &Self::Ciphertext,
2377 key: &Self::Key,
2378 ) -> Self::Ciphertext {
2379 let mut out = c0.clone();
2380 self.or_inplace(&mut out, c1, key);
2381 out
2382 }
2383
2384 fn nor(
2385 &mut self,
2386 c0: &Self::Ciphertext,
2387 c1: &Self::Ciphertext,
2388 key: &Self::Key,
2389 ) -> Self::Ciphertext {
2390 let mut out = c0.clone();
2391 self.nor_inplace(&mut out, c1, key);
2392 out
2393 }
2394
2395 fn xnor(
2396 &mut self,
2397 c0: &Self::Ciphertext,
2398 c1: &Self::Ciphertext,
2399 key: &Self::Key,
2400 ) -> Self::Ciphertext {
2401 let mut out = c0.clone();
2402 self.xnor_inplace(&mut out, c1, key);
2403 out
2404 }
2405
2406 fn xor(
2407 &mut self,
2408 c0: &Self::Ciphertext,
2409 c1: &Self::Ciphertext,
2410 key: &Self::Key,
2411 ) -> Self::Ciphertext {
2412 let mut out = c0.clone();
2413 self.xor_inplace(&mut out, c1, key);
2414 out
2415 }
2416
2417 fn not(&mut self, c: &Self::Ciphertext) -> Self::Ciphertext {
2418 let mut out = c.clone();
2419 self.not_inplace(&mut out);
2420 out
2421 }
2422}