phantom-zone/src/bool/keys.rs
src/bool/keys.rsRaw
1use std::{collections::HashMap, hash::Hash, marker::PhantomData};
2
3use crate::{
4 backend::{ModInit, VectorOps},
5 lwe::LweSecret,
6 pbs::WithShoupRepr,
7 random::{NewWithSeed, RandomFillUniformInModulus},
8 rgsw::RlweSecret,
9 utils::{ToShoup, WithLocal},
10 Decryptor, Encryptor, Matrix, MatrixEntity, MatrixMut, MultiPartyDecryptor, RowEntity, RowMut,
11};
12
13use super::{
14 evaluator::BoolEvaluator,
15 parameters::{BoolParameters, CiphertextModulus},
16};
17
18pub(crate) trait SinglePartyClientKey {
19 type Element;
20 fn sk_rlwe(&self) -> Vec<Self::Element>;
21 fn sk_lwe(&self) -> Vec<Self::Element>;
22}
23
24pub(crate) trait InteractiveMultiPartyClientKey {
25 type Element;
26 fn sk_rlwe(&self) -> Vec<Self::Element>;
27 fn sk_lwe(&self) -> Vec<Self::Element>;
28}
29
30pub(crate) trait NonInteractiveMultiPartyClientKey {
31 type Element;
32 fn sk_rlwe(&self) -> Vec<Self::Element>;
33 fn sk_u_rlwe(&self) -> Vec<Self::Element>;
34 fn sk_lwe(&self) -> Vec<Self::Element>;
35}
36
37/// Client key
38///
39/// Key is used for all parameter varians - Single party, interactive
40/// multi-party, and non-interactive multi-party. The only stored the main seed
41/// and seeds of the Rlwe/Lwe secrets are derived at puncturing the seed desired
42/// number of times.
43///
44/// ### Punctures required:
45///
46/// Puncture 1 -> Seed of RLWE secret used as main RLWE secret for
47/// single-party, interactive/non-interactive multi-party
48///
49/// Puncture 2 -> Seed of LWE secret used main LWE secret for single-party,
50/// interactive/non-interactive multi-party
51///
52/// Puncture 3 -> Seed of RLWE secret used as `u` in
53/// interactive/non-interactive multi-party.
54#[derive(Clone)]
55pub struct ClientKey<S, E> {
56 seed: S,
57 parameters: BoolParameters<E>,
58}
59
60mod impl_ck {
61 use crate::{
62 random::DefaultSecureRng,
63 utils::{fill_random_ternary_secret_with_hamming_weight, puncture_p_rng},
64 };
65
66 use super::*;
67
68 impl<E> ClientKey<[u8; 32], E> {
69 pub(in super::super) fn new(parameters: BoolParameters<E>) -> ClientKey<[u8; 32], E> {
70 let mut rng = DefaultSecureRng::new();
71 let mut seed = [0u8; 32];
72 rng.fill_bytes(&mut seed);
73 Self { seed, parameters }
74 }
75 }
76
77 impl<E> SinglePartyClientKey for ClientKey<[u8; 32], E> {
78 type Element = i32;
79 fn sk_lwe(&self) -> Vec<Self::Element> {
80 let mut p_rng = DefaultSecureRng::new_seeded(self.seed);
81 let lwe_seed = puncture_p_rng::<[u8; 32], DefaultSecureRng>(&mut p_rng, 2);
82
83 let mut lwe_prng = DefaultSecureRng::new_seeded(lwe_seed);
84 let mut out = vec![0i32; self.parameters.lwe_n().0];
85 fill_random_ternary_secret_with_hamming_weight(
86 &mut out,
87 self.parameters.lwe_n().0 >> 1,
88 &mut lwe_prng,
89 );
90 out
91 }
92 fn sk_rlwe(&self) -> Vec<Self::Element> {
93 let mut p_rng = DefaultSecureRng::new_seeded(self.seed);
94 let rlwe_seed = puncture_p_rng::<[u8; 32], DefaultSecureRng>(&mut p_rng, 1);
95
96 let mut rlwe_prng = DefaultSecureRng::new_seeded(rlwe_seed);
97 let mut out = vec![0i32; self.parameters.rlwe_n().0];
98 fill_random_ternary_secret_with_hamming_weight(
99 &mut out,
100 self.parameters.rlwe_n().0 >> 1,
101 &mut rlwe_prng,
102 );
103 out
104 }
105 }
106
107 impl<E> InteractiveMultiPartyClientKey for ClientKey<[u8; 32], E> {
108 type Element = i32;
109 fn sk_lwe(&self) -> Vec<Self::Element> {
110 <Self as SinglePartyClientKey>::sk_lwe(&self)
111 }
112 fn sk_rlwe(&self) -> Vec<Self::Element> {
113 <Self as SinglePartyClientKey>::sk_rlwe(&self)
114 }
115 }
116
117 impl<E> NonInteractiveMultiPartyClientKey for ClientKey<[u8; 32], E> {
118 type Element = i32;
119 fn sk_lwe(&self) -> Vec<Self::Element> {
120 <Self as SinglePartyClientKey>::sk_lwe(&self)
121 }
122 fn sk_rlwe(&self) -> Vec<Self::Element> {
123 <Self as SinglePartyClientKey>::sk_rlwe(&self)
124 }
125 fn sk_u_rlwe(&self) -> Vec<Self::Element> {
126 let mut p_rng = DefaultSecureRng::new_seeded(self.seed);
127 let rlwe_seed = puncture_p_rng::<[u8; 32], DefaultSecureRng>(&mut p_rng, 3);
128
129 let mut rlwe_prng = DefaultSecureRng::new_seeded(rlwe_seed);
130 let mut out = vec![0i32; self.parameters.rlwe_n().0];
131 fill_random_ternary_secret_with_hamming_weight(
132 &mut out,
133 self.parameters.rlwe_n().0 >> 1,
134 &mut rlwe_prng,
135 );
136 out
137 }
138 }
139}
140
141/// Public key
142pub struct PublicKey<M, Rng, ModOp> {
143 key: M,
144 _phantom: PhantomData<(Rng, ModOp)>,
145}
146
147pub(super) mod impl_pk {
148 use crate::evaluator::MultiPartyCrs;
149
150 use super::*;
151
152 impl<M, R, Mo> PublicKey<M, R, Mo> {
153 pub(in super::super) fn key(&self) -> &M {
154 &self.key
155 }
156 }
157
158 impl<
159 M: MatrixMut + MatrixEntity,
160 Rng: NewWithSeed
161 + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>,
162 ModOp,
163 > From<SeededPublicKey<M::R, Rng::Seed, BoolParameters<M::MatElement>, ModOp>>
164 for PublicKey<M, Rng, ModOp>
165 where
166 <M as Matrix>::R: RowMut,
167 M::MatElement: Copy,
168 {
169 fn from(
170 value: SeededPublicKey<M::R, Rng::Seed, BoolParameters<M::MatElement>, ModOp>,
171 ) -> Self {
172 let mut prng = Rng::new_with_seed(value.seed);
173
174 let mut key = M::zeros(2, value.parameters.rlwe_n().0);
175 // sample A
176 RandomFillUniformInModulus::random_fill(
177 &mut prng,
178 value.parameters.rlwe_q(),
179 key.get_row_mut(0),
180 );
181 // Copy over B
182 key.get_row_mut(1).copy_from_slice(value.part_b.as_ref());
183
184 PublicKey {
185 key,
186 _phantom: PhantomData,
187 }
188 }
189 }
190
191 impl<
192 M: MatrixMut + MatrixEntity,
193 Rng: NewWithSeed
194 + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>,
195 ModOp: VectorOps<Element = M::MatElement> + ModInit<M = CiphertextModulus<M::MatElement>>,
196 >
197 From<
198 &[CommonReferenceSeededCollectivePublicKeyShare<
199 M::R,
200 Rng::Seed,
201 BoolParameters<M::MatElement>,
202 >],
203 > for PublicKey<M, Rng, ModOp>
204 where
205 <M as Matrix>::R: RowMut,
206 Rng::Seed: Copy + PartialEq,
207 M::MatElement: PartialEq + Copy,
208 {
209 fn from(
210 value: &[CommonReferenceSeededCollectivePublicKeyShare<
211 M::R,
212 Rng::Seed,
213 BoolParameters<M::MatElement>,
214 >],
215 ) -> Self {
216 assert!(value.len() > 0);
217
218 let parameters = &value[0].parameters;
219 let mut key = M::zeros(2, parameters.rlwe_n().0);
220
221 // sample A
222 let seed = value[0].cr_seed;
223 let mut main_rng = Rng::new_with_seed(seed);
224 RandomFillUniformInModulus::random_fill(
225 &mut main_rng,
226 parameters.rlwe_q(),
227 key.get_row_mut(0),
228 );
229
230 // Sum all Bs
231 let rlweq_modop = ModOp::new(parameters.rlwe_q().clone());
232 value.iter().for_each(|share_i| {
233 assert!(share_i.cr_seed == seed);
234 assert!(&share_i.parameters == parameters);
235
236 rlweq_modop.elwise_add_mut(key.get_row_mut(1), share_i.share.as_ref());
237 });
238
239 PublicKey {
240 key,
241 _phantom: PhantomData,
242 }
243 }
244 }
245}
246
247/// Seeded public key
248struct SeededPublicKey<Ro, S, P, ModOp> {
249 part_b: Ro,
250 seed: S,
251 parameters: P,
252 _phantom: PhantomData<ModOp>,
253}
254
255mod impl_seeded_pk {
256 use super::*;
257
258 impl<R, S, ModOp>
259 From<&[CommonReferenceSeededCollectivePublicKeyShare<R, S, BoolParameters<R::Element>>]>
260 for SeededPublicKey<R, S, BoolParameters<R::Element>, ModOp>
261 where
262 ModOp: VectorOps<Element = R::Element> + ModInit<M = CiphertextModulus<R::Element>>,
263 S: PartialEq + Clone,
264 R: RowMut + RowEntity + Clone,
265 R::Element: Clone + PartialEq,
266 {
267 fn from(
268 value: &[CommonReferenceSeededCollectivePublicKeyShare<
269 R,
270 S,
271 BoolParameters<R::Element>,
272 >],
273 ) -> Self {
274 assert!(value.len() > 0);
275
276 let parameters = &value[0].parameters;
277 let cr_seed = value[0].cr_seed.clone();
278
279 // Sum all Bs
280 let rlweq_modop = ModOp::new(parameters.rlwe_q().clone());
281 let mut part_b = value[0].share.clone();
282 value.iter().skip(1).for_each(|share_i| {
283 assert!(&share_i.cr_seed == &cr_seed);
284 assert!(&share_i.parameters == parameters);
285
286 rlweq_modop.elwise_add_mut(part_b.as_mut(), share_i.share.as_ref());
287 });
288
289 Self {
290 part_b,
291 seed: cr_seed,
292 parameters: parameters.clone(),
293 _phantom: PhantomData,
294 }
295 }
296 }
297}
298
299/// CRS seeded collective public key share
300pub struct CommonReferenceSeededCollectivePublicKeyShare<Ro, S, P> {
301 share: Ro,
302 cr_seed: S,
303 parameters: P,
304}
305impl<Ro, S, P> CommonReferenceSeededCollectivePublicKeyShare<Ro, S, P> {
306 pub(super) fn new(share: Ro, cr_seed: S, parameters: P) -> Self {
307 CommonReferenceSeededCollectivePublicKeyShare {
308 share,
309 cr_seed,
310 parameters,
311 }
312 }
313}
314
315/// CRS seeded Multi-party server key share
316pub struct CommonReferenceSeededMultiPartyServerKeyShare<M: Matrix, P, S> {
317 self_leader_rgsws: Vec<M>,
318 not_self_leader_rgsws: Vec<M>,
319 /// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding
320 /// to -g is at 0
321 auto_keys: HashMap<usize, M>,
322 lwe_ksk: M::R,
323 /// Common reference seed
324 cr_seed: S,
325 parameters: P,
326 user_id: usize,
327}
328
329impl<M: Matrix, P, S> CommonReferenceSeededMultiPartyServerKeyShare<M, P, S> {
330 pub(super) fn new(
331 self_leader_rgsws: Vec<M>,
332 not_self_leader_rgsws: Vec<M>,
333 auto_keys: HashMap<usize, M>,
334 lwe_ksk: M::R,
335 cr_seed: S,
336 parameters: P,
337 user_id: usize,
338 ) -> Self {
339 CommonReferenceSeededMultiPartyServerKeyShare {
340 self_leader_rgsws,
341 not_self_leader_rgsws,
342 auto_keys,
343 lwe_ksk,
344 cr_seed,
345 parameters,
346 user_id,
347 }
348 }
349
350 pub(super) fn cr_seed(&self) -> &S {
351 &self.cr_seed
352 }
353
354 pub(super) fn parameters(&self) -> &P {
355 &self.parameters
356 }
357
358 pub(super) fn auto_keys(&self) -> &HashMap<usize, M> {
359 &self.auto_keys
360 }
361
362 pub(crate) fn self_leader_rgsws(&self) -> &[M] {
363 &self.self_leader_rgsws
364 }
365
366 pub(super) fn not_self_leader_rgsws(&self) -> &[M] {
367 &self.not_self_leader_rgsws
368 }
369
370 pub(super) fn lwe_ksk(&self) -> &M::R {
371 &self.lwe_ksk
372 }
373
374 pub(super) fn user_id(&self) -> usize {
375 self.user_id
376 }
377}
378
379/// CRS seeded MultiParty server key
380pub struct SeededMultiPartyServerKey<M: Matrix, S, P> {
381 rgsw_cts: Vec<M>,
382 /// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding
383 /// to -g is at 0
384 auto_keys: HashMap<usize, M>,
385 lwe_ksk: M::R,
386 cr_seed: S,
387 parameters: P,
388}
389
390impl<M: Matrix, S, P> SeededMultiPartyServerKey<M, S, P> {
391 pub(super) fn new(
392 rgsw_cts: Vec<M>,
393 auto_keys: HashMap<usize, M>,
394 lwe_ksk: M::R,
395 cr_seed: S,
396 parameters: P,
397 ) -> Self {
398 SeededMultiPartyServerKey {
399 rgsw_cts,
400 auto_keys,
401 lwe_ksk,
402 cr_seed,
403 parameters,
404 }
405 }
406
407 pub(super) fn rgsw_cts(&self) -> &[M] {
408 &self.rgsw_cts
409 }
410}
411
412/// Seeded single party server key
413pub struct SeededSinglePartyServerKey<M: Matrix, P, S> {
414 /// Rgsw cts of LWE secret elements
415 pub(crate) rgsw_cts: Vec<M>,
416 /// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding
417 /// to -g is at 0
418 pub(crate) auto_keys: HashMap<usize, M>,
419 /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
420 pub(crate) lwe_ksk: M::R,
421 /// Parameters
422 pub(crate) parameters: P,
423 /// Main seed
424 pub(crate) seed: S,
425}
426impl<M: Matrix, S> SeededSinglePartyServerKey<M, BoolParameters<M::MatElement>, S> {
427 pub(super) fn from_raw(
428 auto_keys: HashMap<usize, M>,
429 rgsw_cts: Vec<M>,
430 lwe_ksk: M::R,
431 parameters: BoolParameters<M::MatElement>,
432 seed: S,
433 ) -> Self {
434 // sanity checks
435 auto_keys.iter().for_each(|v| {
436 assert!(
437 v.1.dimension()
438 == (
439 parameters.auto_decomposition_count().0,
440 parameters.rlwe_n().0
441 )
442 )
443 });
444
445 let (part_a_d, part_b_d) = parameters.rlwe_rgsw_decomposition_count();
446 rgsw_cts.iter().for_each(|v| {
447 assert!(v.dimension() == (part_a_d.0 * 2 + part_b_d.0, parameters.rlwe_n().0))
448 });
449 assert!(
450 lwe_ksk.as_ref().len()
451 == (parameters.lwe_decomposition_count().0 * parameters.rlwe_n().0)
452 );
453
454 SeededSinglePartyServerKey {
455 rgsw_cts,
456 auto_keys,
457 lwe_ksk,
458 parameters,
459 seed,
460 }
461 }
462}
463
464/// Server key in evaluation domain
465pub(crate) struct ServerKeyEvaluationDomain<M, P, R, N> {
466 /// Rgsw cts of LWE secret elements
467 rgsw_cts: Vec<M>,
468 /// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding
469 /// to -g is at 0
470 galois_keys: HashMap<usize, M>,
471 /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
472 lwe_ksk: M,
473 parameters: P,
474 _phanton: PhantomData<(R, N)>,
475}
476
477pub(super) mod impl_server_key_eval_domain {
478 use itertools::{izip, Itertools};
479
480 use crate::{
481 evaluator::MultiPartyCrs,
482 ntt::{Ntt, NttInit},
483 pbs::PbsKey,
484 random::RandomFill,
485 };
486
487 use super::*;
488
489 impl<M, Mod, R, N> ServerKeyEvaluationDomain<M, Mod, R, N> {
490 pub(in super::super) fn rgsw_cts(&self) -> &[M] {
491 &self.rgsw_cts
492 }
493 }
494
495 impl<
496 M: MatrixMut + MatrixEntity,
497 R: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
498 + NewWithSeed,
499 N: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
500 > From<&SeededSinglePartyServerKey<M, BoolParameters<M::MatElement>, R::Seed>>
501 for ServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>
502 where
503 <M as Matrix>::R: RowMut,
504 M::MatElement: Copy,
505 R::Seed: Clone,
506 {
507 fn from(
508 value: &SeededSinglePartyServerKey<M, BoolParameters<M::MatElement>, R::Seed>,
509 ) -> Self {
510 let mut main_prng = R::new_with_seed(value.seed.clone());
511 let parameters = &value.parameters;
512 let g = parameters.g() as isize;
513 let ring_size = value.parameters.rlwe_n().0;
514 let lwe_n = value.parameters.lwe_n().0;
515 let rlwe_q = value.parameters.rlwe_q();
516 let lwq_q = value.parameters.lwe_q();
517
518 let nttop = N::new(rlwe_q, ring_size);
519
520 // galois keys
521 let mut auto_keys = HashMap::new();
522 let auto_decomp_count = parameters.auto_decomposition_count().0;
523 let auto_element_dlogs = parameters.auto_element_dlogs();
524 for i in auto_element_dlogs.into_iter() {
525 let seeded_auto_key = value.auto_keys.get(&i).unwrap();
526 assert!(seeded_auto_key.dimension() == (auto_decomp_count, ring_size));
527
528 let mut data = M::zeros(auto_decomp_count * 2, ring_size);
529
530 // sample RLWE'_A(-s(X^k))
531 data.iter_rows_mut().take(auto_decomp_count).for_each(|ri| {
532 RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut())
533 });
534
535 // copy over RLWE'B_(-s(X^k))
536 izip!(
537 data.iter_rows_mut().skip(auto_decomp_count),
538 seeded_auto_key.iter_rows()
539 )
540 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
541
542 // Send to Evaluation domain
543 data.iter_rows_mut()
544 .for_each(|ri| nttop.forward(ri.as_mut()));
545
546 auto_keys.insert(i, data);
547 }
548
549 // RGSW ciphertexts
550 let (rlrg_a_decomp, rlrg_b_decomp) = parameters.rlwe_rgsw_decomposition_count();
551 let rgsw_cts = value
552 .rgsw_cts
553 .iter()
554 .map(|seeded_rgsw_si| {
555 assert!(
556 seeded_rgsw_si.dimension()
557 == (rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0, ring_size)
558 );
559
560 let mut data = M::zeros(rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0 * 2, ring_size);
561
562 // copy over RLWE'(-sm)
563 izip!(
564 data.iter_rows_mut().take(rlrg_a_decomp.0 * 2),
565 seeded_rgsw_si.iter_rows().take(rlrg_a_decomp.0 * 2)
566 )
567 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
568
569 // sample RLWE'_A(m)
570 data.iter_rows_mut()
571 .skip(rlrg_a_decomp.0 * 2)
572 .take(rlrg_b_decomp.0)
573 .for_each(|ri| {
574 RandomFillUniformInModulus::random_fill(
575 &mut main_prng,
576 &rlwe_q,
577 ri.as_mut(),
578 )
579 });
580
581 // copy over RLWE'_B(m)
582 izip!(
583 data.iter_rows_mut()
584 .skip(rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0),
585 seeded_rgsw_si.iter_rows().skip(rlrg_a_decomp.0 * 2)
586 )
587 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
588
589 // send polynomials to evaluation domain
590 data.iter_rows_mut()
591 .for_each(|ri| nttop.forward(ri.as_mut()));
592
593 data
594 })
595 .collect_vec();
596
597 // LWE ksk
598 let lwe_ksk = {
599 let d = parameters.lwe_decomposition_count().0;
600 assert!(value.lwe_ksk.as_ref().len() == d * ring_size);
601
602 let mut data = M::zeros(d * ring_size, lwe_n + 1);
603 izip!(data.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(
604 |(lwe_i, bi)| {
605 RandomFillUniformInModulus::random_fill(
606 &mut main_prng,
607 &lwq_q,
608 &mut lwe_i.as_mut()[1..],
609 );
610 lwe_i.as_mut()[0] = *bi;
611 },
612 );
613
614 data
615 };
616
617 ServerKeyEvaluationDomain {
618 rgsw_cts,
619 galois_keys: auto_keys,
620 lwe_ksk,
621 parameters: parameters.clone(),
622 _phanton: PhantomData,
623 }
624 }
625 }
626
627 impl<
628 M: MatrixMut + MatrixEntity,
629 Rng: NewWithSeed,
630 N: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
631 >
632 From<&SeededMultiPartyServerKey<M, MultiPartyCrs<Rng::Seed>, BoolParameters<M::MatElement>>>
633 for ServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, Rng, N>
634 where
635 <M as Matrix>::R: RowMut,
636 Rng::Seed: Copy + Default,
637 Rng: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
638 + RandomFill<Rng::Seed>,
639 M::MatElement: Copy,
640 {
641 fn from(
642 value: &SeededMultiPartyServerKey<
643 M,
644 MultiPartyCrs<Rng::Seed>,
645 BoolParameters<M::MatElement>,
646 >,
647 ) -> Self {
648 let g = value.parameters.g() as isize;
649 let rlwe_n = value.parameters.rlwe_n().0;
650 let lwe_n = value.parameters.lwe_n().0;
651 let rlwe_q = value.parameters.rlwe_q();
652 let lwe_q = value.parameters.lwe_q();
653
654 let rlwe_nttop = N::new(rlwe_q, rlwe_n);
655
656 // auto keys
657 let mut auto_keys = HashMap::new();
658 {
659 let mut auto_prng = Rng::new_with_seed(value.cr_seed.auto_keys_cts_seed::<Rng>());
660 let auto_d_count = value.parameters.auto_decomposition_count().0;
661 let auto_element_dlogs = value.parameters.auto_element_dlogs();
662 for i in auto_element_dlogs.into_iter() {
663 let mut key = M::zeros(auto_d_count * 2, rlwe_n);
664
665 // sample a
666 key.iter_rows_mut().take(auto_d_count).for_each(|ri| {
667 RandomFillUniformInModulus::random_fill(
668 &mut auto_prng,
669 &rlwe_q,
670 ri.as_mut(),
671 )
672 });
673
674 let key_part_b = value.auto_keys.get(&i).unwrap();
675 assert!(key_part_b.dimension() == (auto_d_count, rlwe_n));
676 izip!(
677 key.iter_rows_mut().skip(auto_d_count),
678 key_part_b.iter_rows()
679 )
680 .for_each(|(to_ri, from_ri)| {
681 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
682 });
683
684 // send to evaluation domain
685 key.iter_rows_mut()
686 .for_each(|ri| rlwe_nttop.forward(ri.as_mut()));
687
688 auto_keys.insert(i, key);
689 }
690 }
691
692 // rgsw cts
693 let (rlrg_d_a, rlrg_d_b) = value.parameters.rlwe_rgsw_decomposition_count();
694 let rgsw_ct_out = rlrg_d_a.0 * 2 + rlrg_d_b.0 * 2;
695 let rgsw_cts = value
696 .rgsw_cts
697 .iter()
698 .map(|ct_i_in| {
699 assert!(ct_i_in.dimension() == (rgsw_ct_out, rlwe_n));
700 let mut eval_ct_i_out = M::zeros(rgsw_ct_out, rlwe_n);
701
702 izip!(eval_ct_i_out.iter_rows_mut(), ct_i_in.iter_rows()).for_each(
703 |(to_ri, from_ri)| {
704 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
705 rlwe_nttop.forward(to_ri.as_mut());
706 },
707 );
708
709 eval_ct_i_out
710 })
711 .collect_vec();
712
713 // lwe ksk
714 let mut lwe_ksk_prng = Rng::new_with_seed(value.cr_seed.lwe_ksk_cts_seed_seed::<Rng>());
715 let d_lwe = value.parameters.lwe_decomposition_count().0;
716 let mut lwe_ksk = M::zeros(rlwe_n * d_lwe, lwe_n + 1);
717 izip!(lwe_ksk.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(
718 |(lwe_i, bi)| {
719 RandomFillUniformInModulus::random_fill(
720 &mut lwe_ksk_prng,
721 &lwe_q,
722 &mut lwe_i.as_mut()[1..],
723 );
724 lwe_i.as_mut()[0] = *bi;
725 },
726 );
727
728 ServerKeyEvaluationDomain {
729 rgsw_cts,
730 galois_keys: auto_keys,
731 lwe_ksk,
732 parameters: value.parameters.clone(),
733 _phanton: PhantomData,
734 }
735 }
736 }
737
738 impl<M: Matrix, P, R, N> PbsKey for ServerKeyEvaluationDomain<M, P, R, N> {
739 type AutoKey = M;
740 type LweKskKey = M;
741 type RgswCt = M;
742
743 fn galois_key_for_auto(&self, k: usize) -> &Self::AutoKey {
744 self.galois_keys.get(&k).unwrap()
745 }
746 fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::RgswCt {
747 &self.rgsw_cts[si]
748 }
749
750 fn lwe_ksk(&self) -> &Self::LweKskKey {
751 &self.lwe_ksk
752 }
753 }
754
755 #[cfg(test)]
756 impl<M, P, R, N> super::super::print_noise::CollectRuntimeServerKeyStats
757 for ServerKeyEvaluationDomain<M, P, R, N>
758 {
759 type M = M;
760 fn galois_key_for_auto(&self, k: usize) -> &Self::M {
761 self.galois_keys.get(&k).unwrap()
762 }
763 fn lwe_ksk(&self) -> &Self::M {
764 &self.lwe_ksk
765 }
766 fn rgsw_cts_lwe_si(&self, s_index: usize) -> &Self::M {
767 &self.rgsw_cts[s_index]
768 }
769 }
770}
771
772pub(crate) struct NonInteractiveServerKeyEvaluationDomain<M, P, R, N> {
773 /// RGSW ciphertexts ideal lwe secret key elements under ideal rlwe secret
774 rgsw_cts: Vec<M>,
775 /// Automorphism keys under ideal rlwe secret
776 auto_keys: HashMap<usize, M>,
777 /// LWE key switching key from Q -> Q_{ks}
778 lwe_ksk: M,
779 /// Key switching key from user j to ideal secret key s. User j's ksk is at
780 /// j'th element
781 ui_to_s_ksks: Vec<M>,
782 parameters: P,
783 _phanton: PhantomData<(R, N)>,
784}
785
786pub(super) mod impl_non_interactive_server_key_eval_domain {
787 use itertools::{izip, Itertools};
788
789 use crate::{bool::evaluator::NonInteractiveMultiPartyCrs, random::RandomFill, Ntt, NttInit};
790
791 use super::*;
792
793 impl<M, P, R, N> NonInteractiveServerKeyEvaluationDomain<M, P, R, N> {
794 pub(in super::super) fn rgsw_cts(&self) -> &[M] {
795 &self.rgsw_cts
796 }
797 }
798
799 impl<M, Rng, N>
800 From<
801 &SeededNonInteractiveMultiPartyServerKey<
802 M,
803 NonInteractiveMultiPartyCrs<Rng::Seed>,
804 BoolParameters<M::MatElement>,
805 >,
806 > for NonInteractiveServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, Rng, N>
807 where
808 M: MatrixMut + MatrixEntity + Clone,
809 Rng: NewWithSeed
810 + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
811 + RandomFill<<Rng as NewWithSeed>::Seed>,
812 N: Ntt<Element = M::MatElement> + NttInit<CiphertextModulus<M::MatElement>>,
813 M::R: RowMut,
814 M::MatElement: Copy,
815 Rng::Seed: Clone + Copy + Default,
816 {
817 fn from(
818 value: &SeededNonInteractiveMultiPartyServerKey<
819 M,
820 NonInteractiveMultiPartyCrs<Rng::Seed>,
821 BoolParameters<M::MatElement>,
822 >,
823 ) -> Self {
824 let rlwe_nttop = N::new(value.parameters.rlwe_q(), value.parameters.rlwe_n().0);
825 let ring_size = value.parameters.rlwe_n().0;
826
827 // RGSW cts
828 // copy over rgsw cts and send to evaluation domain
829 let mut rgsw_cts = value.rgsw_cts.clone();
830 rgsw_cts.iter_mut().for_each(|c| {
831 c.iter_rows_mut()
832 .for_each(|ri| rlwe_nttop.forward(ri.as_mut()))
833 });
834
835 // Auto keys
836 // populate pseudo random part of auto keys. Then send auto keys to
837 // evaluation domain
838 let mut auto_keys = HashMap::new();
839 let auto_seed = value.cr_seed.auto_keys_cts_seed::<Rng>();
840 let mut auto_prng = Rng::new_with_seed(auto_seed);
841 let auto_element_dlogs = value.parameters.auto_element_dlogs();
842 let d_auto = value.parameters.auto_decomposition_count().0;
843 auto_element_dlogs.iter().for_each(|el| {
844 let auto_part_b = value
845 .auto_keys
846 .get(el)
847 .expect(&format!("Auto key for element g^{el} not found"));
848
849 assert!(auto_part_b.dimension() == (d_auto, ring_size));
850
851 let mut auto_ct = M::zeros(d_auto * 2, ring_size);
852
853 // sample part A
854 auto_ct.iter_rows_mut().take(d_auto).for_each(|ri| {
855 RandomFillUniformInModulus::random_fill(
856 &mut auto_prng,
857 value.parameters.rlwe_q(),
858 ri.as_mut(),
859 )
860 });
861
862 // Copy over part B
863 izip!(
864 auto_ct.iter_rows_mut().skip(d_auto),
865 auto_part_b.iter_rows()
866 )
867 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
868
869 // send to evaluation domain
870 auto_ct
871 .iter_rows_mut()
872 .for_each(|r| rlwe_nttop.forward(r.as_mut()));
873
874 auto_keys.insert(*el, auto_ct);
875 });
876
877 // LWE ksk
878 // populate pseudo random part of lwe ciphertexts in ksk and copy over part b
879 // elements
880 let lwe_ksk_seed = value.cr_seed.lwe_ksk_cts_seed::<Rng>();
881 let mut lwe_ksk_prng = Rng::new_with_seed(lwe_ksk_seed);
882 let mut lwe_ksk = M::zeros(
883 value.parameters.lwe_decomposition_count().0 * ring_size,
884 value.parameters.lwe_n().0 + 1,
885 );
886 lwe_ksk.iter_rows_mut().for_each(|ri| {
887 // first element is resereved for part b. Only sample a_is in the rest
888 RandomFillUniformInModulus::random_fill(
889 &mut lwe_ksk_prng,
890 value.parameters.lwe_q(),
891 &mut ri.as_mut()[1..],
892 )
893 });
894 // copy over part bs
895 assert!(
896 value.lwe_ksk.as_ref().len()
897 == value.parameters.lwe_decomposition_count().0 * ring_size
898 );
899 izip!(value.lwe_ksk.as_ref().iter(), lwe_ksk.iter_rows_mut()).for_each(
900 |(b_el, lwe_ct)| {
901 lwe_ct.as_mut()[0] = *b_el;
902 },
903 );
904
905 // u_i to s ksk
906 let d_uitos = value
907 .parameters
908 .non_interactive_ui_to_s_key_switch_decomposition_count()
909 .0;
910 let total_users = *value.ui_to_s_ksks_key_order.iter().max().unwrap();
911 let ui_to_s_ksks = (0..total_users + 1)
912 .map(|user_index| {
913 let user_i_seed = value.cr_seed.ui_to_s_ks_seed_for_user_i::<Rng>(user_index);
914 let mut prng = Rng::new_with_seed(user_i_seed);
915
916 let mut ksk_ct = M::zeros(d_uitos * 2, ring_size);
917
918 ksk_ct.iter_rows_mut().take(d_uitos).for_each(|r| {
919 RandomFillUniformInModulus::random_fill(
920 &mut prng,
921 value.parameters.rlwe_q(),
922 r.as_mut(),
923 );
924 });
925
926 let incoming_ksk_partb_ref =
927 &value.ui_to_s_ksks[value.ui_to_s_ksks_key_order[user_index]];
928 assert!(incoming_ksk_partb_ref.dimension() == (d_uitos, ring_size));
929 izip!(
930 ksk_ct.iter_rows_mut().skip(d_uitos),
931 incoming_ksk_partb_ref.iter_rows()
932 )
933 .for_each(|(to_ri, from_ri)| {
934 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
935 });
936
937 ksk_ct
938 .iter_rows_mut()
939 .for_each(|r| rlwe_nttop.forward(r.as_mut()));
940 ksk_ct
941 })
942 .collect_vec();
943
944 NonInteractiveServerKeyEvaluationDomain {
945 rgsw_cts,
946 auto_keys,
947 lwe_ksk,
948 ui_to_s_ksks,
949 parameters: value.parameters.clone(),
950 _phanton: PhantomData,
951 }
952 }
953 }
954
955 #[cfg(test)]
956 impl<M, P, R, N> super::super::print_noise::CollectRuntimeServerKeyStats
957 for NonInteractiveServerKeyEvaluationDomain<M, P, R, N>
958 {
959 type M = M;
960 fn galois_key_for_auto(&self, k: usize) -> &Self::M {
961 self.auto_keys.get(&k).unwrap()
962 }
963 fn lwe_ksk(&self) -> &Self::M {
964 &self.lwe_ksk
965 }
966 fn rgsw_cts_lwe_si(&self, s_index: usize) -> &Self::M {
967 &self.rgsw_cts[s_index]
968 }
969 }
970}
971
972pub struct SeededNonInteractiveMultiPartyServerKey<M: Matrix, S, P> {
973 /// u_i to s key switching keys in random order
974 ui_to_s_ksks: Vec<M>,
975 /// Defines order for u_i to s key switchin keys by storing the index of
976 /// user j's ksk in `ui_to_s_ksks` at index `j`. Find user j's u_i to s ksk
977 /// at `ui_to_s_ksks[ui_to_s_ksks_key_order[j]]`
978 ui_to_s_ksks_key_order: Vec<usize>,
979 /// RGSW ciphertets
980 rgsw_cts: Vec<M>,
981 auto_keys: HashMap<usize, M>,
982 lwe_ksk: M::R,
983 cr_seed: S,
984 parameters: P,
985}
986
987impl<M: Matrix, S, P> SeededNonInteractiveMultiPartyServerKey<M, S, P> {
988 pub(super) fn new(
989 ui_to_s_ksks: Vec<M>,
990 ui_to_s_ksks_key_order: Vec<usize>,
991 rgsw_cts: Vec<M>,
992 auto_keys: HashMap<usize, M>,
993 lwe_ksk: M::R,
994 cr_seed: S,
995 parameters: P,
996 ) -> Self {
997 Self {
998 ui_to_s_ksks,
999 ui_to_s_ksks_key_order,
1000 rgsw_cts,
1001 auto_keys,
1002 lwe_ksk,
1003 cr_seed,
1004 parameters,
1005 }
1006 }
1007}
1008
1009pub(crate) struct ShoupNonInteractiveServerKeyEvaluationDomain<M> {
1010 /// RGSW ciphertexts ideal lwe secret key elements under ideal rlwe secret
1011 rgsw_cts: Vec<NormalAndShoup<M>>,
1012 /// Automorphism keys under ideal rlwe secret
1013 auto_keys: HashMap<usize, NormalAndShoup<M>>,
1014 /// LWE key switching key from Q -> Q_{ks}
1015 lwe_ksk: M,
1016 /// Key switching key from user j to ideal secret key s. User j's ksk is at
1017 /// j'th element
1018 ui_to_s_ksks: Vec<NormalAndShoup<M>>,
1019}
1020
1021mod impl_shoup_non_interactive_server_key_eval_domain {
1022 use itertools::Itertools;
1023 use num_traits::{FromPrimitive, PrimInt, ToPrimitive};
1024
1025 use super::*;
1026 use crate::{backend::Modulus, pbs::PbsKey};
1027
1028 impl<M> ShoupNonInteractiveServerKeyEvaluationDomain<M> {
1029 pub(in super::super) fn ui_to_s_ksk(&self, user_id: usize) -> &NormalAndShoup<M> {
1030 &self.ui_to_s_ksks[user_id]
1031 }
1032 }
1033
1034 impl<M: Matrix + ToShoup<Modulus = M::MatElement>, R, N>
1035 From<NonInteractiveServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>>
1036 for ShoupNonInteractiveServerKeyEvaluationDomain<M>
1037 where
1038 M::MatElement: FromPrimitive + ToPrimitive + PrimInt,
1039 {
1040 fn from(
1041 value: NonInteractiveServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>,
1042 ) -> Self {
1043 let rlwe_q = value.parameters.rlwe_q().q().unwrap();
1044
1045 let rgsw_dim = (
1046 value.parameters.rlwe_rgsw_decomposition_count().0 .0 * 2
1047 + value.parameters.rlwe_rgsw_decomposition_count().1 .0 * 2,
1048 value.parameters.rlwe_n().0,
1049 );
1050 let rgsw_cts = value
1051 .rgsw_cts
1052 .into_iter()
1053 .map(|m| {
1054 assert!(m.dimension() == rgsw_dim);
1055 NormalAndShoup::new_with_modulus(m, rlwe_q)
1056 })
1057 .collect_vec();
1058
1059 let auto_dim = (
1060 value.parameters.auto_decomposition_count().0 * 2,
1061 value.parameters.rlwe_n().0,
1062 );
1063 let mut auto_keys = HashMap::new();
1064 value.auto_keys.into_iter().for_each(|(k, v)| {
1065 assert!(v.dimension() == auto_dim);
1066 auto_keys.insert(k, NormalAndShoup::new_with_modulus(v, rlwe_q));
1067 });
1068
1069 let ui_ks_dim = (
1070 value
1071 .parameters
1072 .non_interactive_ui_to_s_key_switch_decomposition_count()
1073 .0
1074 * 2,
1075 value.parameters.rlwe_n().0,
1076 );
1077 let ui_to_s_ksks = value
1078 .ui_to_s_ksks
1079 .into_iter()
1080 .map(|m| {
1081 assert!(m.dimension() == ui_ks_dim);
1082 NormalAndShoup::new_with_modulus(m, rlwe_q)
1083 })
1084 .collect_vec();
1085
1086 assert!(
1087 value.lwe_ksk.dimension()
1088 == (
1089 value.parameters.rlwe_n().0 * value.parameters.lwe_decomposition_count().0,
1090 value.parameters.lwe_n().0 + 1
1091 )
1092 );
1093
1094 Self {
1095 rgsw_cts,
1096 auto_keys,
1097 lwe_ksk: value.lwe_ksk,
1098 ui_to_s_ksks,
1099 }
1100 }
1101 }
1102
1103 impl<M: Matrix> PbsKey for ShoupNonInteractiveServerKeyEvaluationDomain<M> {
1104 type AutoKey = NormalAndShoup<M>;
1105 type LweKskKey = M;
1106 type RgswCt = NormalAndShoup<M>;
1107
1108 fn galois_key_for_auto(&self, k: usize) -> &Self::AutoKey {
1109 let d = self.auto_keys.get(&k).unwrap();
1110 d
1111 }
1112 fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::RgswCt {
1113 &self.rgsw_cts[si]
1114 }
1115
1116 fn lwe_ksk(&self) -> &Self::LweKskKey {
1117 &self.lwe_ksk
1118 }
1119 }
1120}
1121
1122/// Server key in evaluation domain with Shoup representations
1123pub(crate) struct ShoupServerKeyEvaluationDomain<M> {
1124 /// Rgsw cts of LWE secret elements
1125 rgsw_cts: Vec<NormalAndShoup<M>>,
1126 /// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding
1127 /// to -g is at 0
1128 galois_keys: HashMap<usize, NormalAndShoup<M>>,
1129 /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
1130 lwe_ksk: M,
1131}
1132
1133mod shoup_server_key_eval_domain {
1134 use itertools::{izip, Itertools};
1135 use num_traits::{FromPrimitive, PrimInt};
1136
1137 use crate::{backend::Modulus, pbs::PbsKey};
1138
1139 use super::*;
1140
1141 impl<M: MatrixMut + MatrixEntity + ToShoup<Modulus = M::MatElement>, R, N>
1142 From<ServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>>
1143 for ShoupServerKeyEvaluationDomain<M>
1144 where
1145 <M as Matrix>::R: RowMut,
1146 M::MatElement: PrimInt + FromPrimitive,
1147 {
1148 fn from(value: ServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>) -> Self {
1149 let q = value.parameters.rlwe_q().q().unwrap();
1150 // Rgsw ciphertexts
1151 let rgsw_cts = value
1152 .rgsw_cts
1153 .into_iter()
1154 .map(|ct| NormalAndShoup::new_with_modulus(ct, q))
1155 .collect_vec();
1156
1157 let mut auto_keys = HashMap::new();
1158 value.galois_keys.into_iter().for_each(|(index, key)| {
1159 auto_keys.insert(index, NormalAndShoup::new_with_modulus(key, q));
1160 });
1161
1162 Self {
1163 rgsw_cts,
1164 galois_keys: auto_keys,
1165 lwe_ksk: value.lwe_ksk,
1166 }
1167 }
1168 }
1169
1170 impl<M: Matrix> PbsKey for ShoupServerKeyEvaluationDomain<M> {
1171 type AutoKey = NormalAndShoup<M>;
1172 type LweKskKey = M;
1173 type RgswCt = NormalAndShoup<M>;
1174
1175 fn galois_key_for_auto(&self, k: usize) -> &Self::AutoKey {
1176 self.galois_keys.get(&k).unwrap()
1177 }
1178 fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::RgswCt {
1179 &self.rgsw_cts[si]
1180 }
1181
1182 fn lwe_ksk(&self) -> &Self::LweKskKey {
1183 &self.lwe_ksk
1184 }
1185 }
1186}
1187
1188pub struct CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M: Matrix, S> {
1189 /// (ak*si + e + \beta ui, ak*si + e)
1190 ni_rgsw_cts: (Vec<M>, Vec<M>),
1191 ui_to_s_ksk: M,
1192 others_ksk_zero_encs: Vec<M>,
1193
1194 auto_keys_share: HashMap<usize, M>,
1195 lwe_ksk_share: M::R,
1196
1197 user_index: usize,
1198 cr_seed: S,
1199}
1200
1201mod impl_common_ref_non_interactive_multi_party_server_share {
1202 use super::*;
1203
1204 impl<M: Matrix, S> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M, S> {
1205 pub(in super::super) fn new(
1206 ni_rgsw_cts: (Vec<M>, Vec<M>),
1207 ui_to_s_ksk: M,
1208 others_ksk_zero_encs: Vec<M>,
1209 auto_keys_share: HashMap<usize, M>,
1210 lwe_ksk_share: M::R,
1211 user_index: usize,
1212 cr_seed: S,
1213 ) -> Self {
1214 Self {
1215 ni_rgsw_cts,
1216 ui_to_s_ksk,
1217 others_ksk_zero_encs,
1218 auto_keys_share,
1219 lwe_ksk_share,
1220 user_index,
1221 cr_seed,
1222 }
1223 }
1224
1225 pub(in super::super) fn ni_rgsw_cts(&self) -> &(Vec<M>, Vec<M>) {
1226 &self.ni_rgsw_cts
1227 }
1228
1229 pub(in super::super) fn ui_to_s_ksk(&self) -> &M {
1230 &self.ui_to_s_ksk
1231 }
1232
1233 pub(in super::super) fn user_index(&self) -> usize {
1234 self.user_index
1235 }
1236
1237 pub(in super::super) fn auto_keys_share(&self) -> &HashMap<usize, M> {
1238 &self.auto_keys_share
1239 }
1240
1241 pub(in super::super) fn lwe_ksk_share(&self) -> &M::R {
1242 &self.lwe_ksk_share
1243 }
1244
1245 pub(in super::super) fn ui_to_s_ksk_zero_encs_for_user_i(&self, user_i: usize) -> &M {
1246 assert!(user_i != self.user_index);
1247 if user_i < self.user_index {
1248 &self.others_ksk_zero_encs[user_i]
1249 } else {
1250 &self.others_ksk_zero_encs[user_i - 1]
1251 }
1252 }
1253 }
1254}
1255
1256/// Stores normal and shoup representation of Matrix elements (Normal, Shoup)
1257pub(crate) struct NormalAndShoup<M>(M, M);
1258
1259impl<M: ToShoup> NormalAndShoup<M> {
1260 fn new_with_modulus(value: M, modulus: <M as ToShoup>::Modulus) -> Self {
1261 let value_shoup = M::to_shoup(&value, modulus);
1262 NormalAndShoup(value, value_shoup)
1263 }
1264}
1265
1266impl<M> AsRef<M> for NormalAndShoup<M> {
1267 fn as_ref(&self) -> &M {
1268 &self.0
1269 }
1270}
1271
1272impl<M> WithShoupRepr for NormalAndShoup<M> {
1273 type M = M;
1274 fn shoup_repr(&self) -> &Self::M {
1275 &self.1
1276 }
1277}
1278
1279pub(super) mod tests {
1280 use itertools::izip;
1281 use num_traits::{FromPrimitive, PrimInt, ToPrimitive, Zero};
1282
1283 use crate::{
1284 backend::{GetModulus, Modulus},
1285 bool::ClientKey,
1286 lwe::decrypt_lwe,
1287 parameters::CiphertextModulus,
1288 utils::TryConvertFrom1,
1289 ArithmeticOps, Row,
1290 };
1291
1292 use super::SinglePartyClientKey;
1293
1294 pub(crate) fn ideal_sk_rlwe(cks: &[ClientKey]) -> Vec<i32> {
1295 let mut ideal_rlwe_sk = cks[0].sk_rlwe();
1296 cks.iter().skip(1).for_each(|k| {
1297 let sk_rlwe = k.sk_rlwe();
1298 izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
1299 *a = *a + b;
1300 });
1301 });
1302 ideal_rlwe_sk
1303 }
1304
1305 pub(crate) fn ideal_sk_lwe(cks: &[ClientKey]) -> Vec<i32> {
1306 let mut ideal_rlwe_sk = cks[0].sk_lwe();
1307 cks.iter().skip(1).for_each(|k| {
1308 let sk_rlwe = k.sk_lwe();
1309 izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
1310 *a = *a + b;
1311 });
1312 });
1313 ideal_rlwe_sk
1314 }
1315
1316 pub(crate) fn measure_noise_lwe<
1317 R: Row,
1318 S,
1319 Modop: ArithmeticOps<Element = R::Element>
1320 + GetModulus<M = CiphertextModulus<R::Element>, Element = R::Element>,
1321 >(
1322 lwe_ct: &R,
1323 m_expected: R::Element,
1324 sk: &[S],
1325 modop: &Modop,
1326 ) -> f64
1327 where
1328 R: TryConvertFrom1<[S], CiphertextModulus<R::Element>>,
1329 R::Element: Zero + FromPrimitive + PrimInt,
1330 {
1331 let noisy_m = decrypt_lwe(lwe_ct, &sk, modop);
1332 let noise = modop.sub(&m_expected, &noisy_m);
1333 modop
1334 .modulus()
1335 .map_element_to_i64(&noise)
1336 .abs()
1337 .to_f64()
1338 .unwrap()
1339 .log2()
1340 }
1341}