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
756pub(crate) struct NonInteractiveServerKeyEvaluationDomain<M, P, R, N> {
757 /// RGSW ciphertexts ideal lwe secret key elements under ideal rlwe secret
758 rgsw_cts: Vec<M>,
759 /// Automorphism keys under ideal rlwe secret
760 auto_keys: HashMap<usize, M>,
761 /// LWE key switching key from Q -> Q_{ks}
762 lwe_ksk: M,
763 /// Key switching key from user j to ideal secret key s. User j's ksk is at
764 /// j'th element
765 ui_to_s_ksks: Vec<M>,
766 parameters: P,
767 _phanton: PhantomData<(R, N)>,
768}
769
770pub(super) mod impl_non_interactive_server_key_eval_domain {
771 use itertools::{izip, Itertools};
772
773 use crate::{bool::evaluator::NonInteractiveMultiPartyCrs, random::RandomFill, Ntt, NttInit};
774
775 use super::*;
776
777 impl<M, P, R, N> NonInteractiveServerKeyEvaluationDomain<M, P, R, N> {
778 pub(in super::super) fn rgsw_cts(&self) -> &[M] {
779 &self.rgsw_cts
780 }
781 }
782
783 impl<M, Rng, N>
784 From<
785 &SeededNonInteractiveMultiPartyServerKey<
786 M,
787 NonInteractiveMultiPartyCrs<Rng::Seed>,
788 BoolParameters<M::MatElement>,
789 >,
790 > for NonInteractiveServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, Rng, N>
791 where
792 M: MatrixMut + MatrixEntity + Clone,
793 Rng: NewWithSeed
794 + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
795 + RandomFill<<Rng as NewWithSeed>::Seed>,
796 N: Ntt<Element = M::MatElement> + NttInit<CiphertextModulus<M::MatElement>>,
797 M::R: RowMut,
798 M::MatElement: Copy,
799 Rng::Seed: Clone + Copy + Default,
800 {
801 fn from(
802 value: &SeededNonInteractiveMultiPartyServerKey<
803 M,
804 NonInteractiveMultiPartyCrs<Rng::Seed>,
805 BoolParameters<M::MatElement>,
806 >,
807 ) -> Self {
808 let rlwe_nttop = N::new(value.parameters.rlwe_q(), value.parameters.rlwe_n().0);
809 let ring_size = value.parameters.rlwe_n().0;
810
811 // RGSW cts
812 // copy over rgsw cts and send to evaluation domain
813 let mut rgsw_cts = value.rgsw_cts.clone();
814 rgsw_cts.iter_mut().for_each(|c| {
815 c.iter_rows_mut()
816 .for_each(|ri| rlwe_nttop.forward(ri.as_mut()))
817 });
818
819 // Auto keys
820 // populate pseudo random part of auto keys. Then send auto keys to
821 // evaluation domain
822 let mut auto_keys = HashMap::new();
823 let auto_seed = value.cr_seed.auto_keys_cts_seed::<Rng>();
824 let mut auto_prng = Rng::new_with_seed(auto_seed);
825 let auto_element_dlogs = value.parameters.auto_element_dlogs();
826 let d_auto = value.parameters.auto_decomposition_count().0;
827 auto_element_dlogs.iter().for_each(|el| {
828 let auto_part_b = value
829 .auto_keys
830 .get(el)
831 .expect(&format!("Auto key for element g^{el} not found"));
832
833 assert!(auto_part_b.dimension() == (d_auto, ring_size));
834
835 let mut auto_ct = M::zeros(d_auto * 2, ring_size);
836
837 // sample part A
838 auto_ct.iter_rows_mut().take(d_auto).for_each(|ri| {
839 RandomFillUniformInModulus::random_fill(
840 &mut auto_prng,
841 value.parameters.rlwe_q(),
842 ri.as_mut(),
843 )
844 });
845
846 // Copy over part B
847 izip!(
848 auto_ct.iter_rows_mut().skip(d_auto),
849 auto_part_b.iter_rows()
850 )
851 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
852
853 // send to evaluation domain
854 auto_ct
855 .iter_rows_mut()
856 .for_each(|r| rlwe_nttop.forward(r.as_mut()));
857
858 auto_keys.insert(*el, auto_ct);
859 });
860
861 // LWE ksk
862 // populate pseudo random part of lwe ciphertexts in ksk and copy over part b
863 // elements
864 let lwe_ksk_seed = value.cr_seed.lwe_ksk_cts_seed::<Rng>();
865 let mut lwe_ksk_prng = Rng::new_with_seed(lwe_ksk_seed);
866 let mut lwe_ksk = M::zeros(
867 value.parameters.lwe_decomposition_count().0 * ring_size,
868 value.parameters.lwe_n().0 + 1,
869 );
870 lwe_ksk.iter_rows_mut().for_each(|ri| {
871 // first element is resereved for part b. Only sample a_is in the rest
872 RandomFillUniformInModulus::random_fill(
873 &mut lwe_ksk_prng,
874 value.parameters.lwe_q(),
875 &mut ri.as_mut()[1..],
876 )
877 });
878 // copy over part bs
879 assert!(
880 value.lwe_ksk.as_ref().len()
881 == value.parameters.lwe_decomposition_count().0 * ring_size
882 );
883 izip!(value.lwe_ksk.as_ref().iter(), lwe_ksk.iter_rows_mut()).for_each(
884 |(b_el, lwe_ct)| {
885 lwe_ct.as_mut()[0] = *b_el;
886 },
887 );
888
889 // u_i to s ksk
890 let d_uitos = value
891 .parameters
892 .non_interactive_ui_to_s_key_switch_decomposition_count()
893 .0;
894 let total_users = *value.ui_to_s_ksks_key_order.iter().max().unwrap();
895 let ui_to_s_ksks = (0..total_users + 1)
896 .map(|user_index| {
897 let user_i_seed = value.cr_seed.ui_to_s_ks_seed_for_user_i::<Rng>(user_index);
898 let mut prng = Rng::new_with_seed(user_i_seed);
899
900 let mut ksk_ct = M::zeros(d_uitos * 2, ring_size);
901
902 ksk_ct.iter_rows_mut().take(d_uitos).for_each(|r| {
903 RandomFillUniformInModulus::random_fill(
904 &mut prng,
905 value.parameters.rlwe_q(),
906 r.as_mut(),
907 );
908 });
909
910 let incoming_ksk_partb_ref =
911 &value.ui_to_s_ksks[value.ui_to_s_ksks_key_order[user_index]];
912 assert!(incoming_ksk_partb_ref.dimension() == (d_uitos, ring_size));
913 izip!(
914 ksk_ct.iter_rows_mut().skip(d_uitos),
915 incoming_ksk_partb_ref.iter_rows()
916 )
917 .for_each(|(to_ri, from_ri)| {
918 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
919 });
920
921 ksk_ct
922 .iter_rows_mut()
923 .for_each(|r| rlwe_nttop.forward(r.as_mut()));
924 ksk_ct
925 })
926 .collect_vec();
927
928 NonInteractiveServerKeyEvaluationDomain {
929 rgsw_cts,
930 auto_keys,
931 lwe_ksk,
932 ui_to_s_ksks,
933 parameters: value.parameters.clone(),
934 _phanton: PhantomData,
935 }
936 }
937 }
938}
939
940pub struct SeededNonInteractiveMultiPartyServerKey<M: Matrix, S, P> {
941 /// u_i to s key switching keys in random order
942 ui_to_s_ksks: Vec<M>,
943 /// Defines order for u_i to s key switchin keys by storing the index of
944 /// user j's ksk in `ui_to_s_ksks` at index `j`. Find user j's u_i to s ksk
945 /// at `ui_to_s_ksks[ui_to_s_ksks_key_order[j]]`
946 ui_to_s_ksks_key_order: Vec<usize>,
947 /// RGSW ciphertets
948 rgsw_cts: Vec<M>,
949 auto_keys: HashMap<usize, M>,
950 lwe_ksk: M::R,
951 cr_seed: S,
952 parameters: P,
953}
954
955impl<M: Matrix, S, P> SeededNonInteractiveMultiPartyServerKey<M, S, P> {
956 pub(super) fn new(
957 ui_to_s_ksks: Vec<M>,
958 ui_to_s_ksks_key_order: Vec<usize>,
959 rgsw_cts: Vec<M>,
960 auto_keys: HashMap<usize, M>,
961 lwe_ksk: M::R,
962 cr_seed: S,
963 parameters: P,
964 ) -> Self {
965 Self {
966 ui_to_s_ksks,
967 ui_to_s_ksks_key_order,
968 rgsw_cts,
969 auto_keys,
970 lwe_ksk,
971 cr_seed,
972 parameters,
973 }
974 }
975}
976
977pub(crate) struct ShoupNonInteractiveServerKeyEvaluationDomain<M> {
978 /// RGSW ciphertexts ideal lwe secret key elements under ideal rlwe secret
979 rgsw_cts: Vec<NormalAndShoup<M>>,
980 /// Automorphism keys under ideal rlwe secret
981 auto_keys: HashMap<usize, NormalAndShoup<M>>,
982 /// LWE key switching key from Q -> Q_{ks}
983 lwe_ksk: M,
984 /// Key switching key from user j to ideal secret key s. User j's ksk is at
985 /// j'th element
986 ui_to_s_ksks: Vec<NormalAndShoup<M>>,
987}
988
989mod impl_shoup_non_interactive_server_key_eval_domain {
990 use itertools::Itertools;
991 use num_traits::{FromPrimitive, PrimInt, ToPrimitive};
992
993 use super::*;
994 use crate::{backend::Modulus, pbs::PbsKey};
995
996 impl<M> ShoupNonInteractiveServerKeyEvaluationDomain<M> {
997 pub(in super::super) fn ui_to_s_ksk(&self, user_id: usize) -> &NormalAndShoup<M> {
998 &self.ui_to_s_ksks[user_id]
999 }
1000 }
1001
1002 impl<M: Matrix + ToShoup<Modulus = M::MatElement>, R, N>
1003 From<NonInteractiveServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>>
1004 for ShoupNonInteractiveServerKeyEvaluationDomain<M>
1005 where
1006 M::MatElement: FromPrimitive + ToPrimitive + PrimInt,
1007 {
1008 fn from(
1009 value: NonInteractiveServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>,
1010 ) -> Self {
1011 let rlwe_q = value.parameters.rlwe_q().q().unwrap();
1012
1013 let rgsw_dim = (
1014 value.parameters.rlwe_rgsw_decomposition_count().0 .0 * 2
1015 + value.parameters.rlwe_rgsw_decomposition_count().1 .0 * 2,
1016 value.parameters.rlwe_n().0,
1017 );
1018 let rgsw_cts = value
1019 .rgsw_cts
1020 .into_iter()
1021 .map(|m| {
1022 assert!(m.dimension() == rgsw_dim);
1023 NormalAndShoup::new_with_modulus(m, rlwe_q)
1024 })
1025 .collect_vec();
1026
1027 let auto_dim = (
1028 value.parameters.auto_decomposition_count().0 * 2,
1029 value.parameters.rlwe_n().0,
1030 );
1031 let mut auto_keys = HashMap::new();
1032 value.auto_keys.into_iter().for_each(|(k, v)| {
1033 assert!(v.dimension() == auto_dim);
1034 auto_keys.insert(k, NormalAndShoup::new_with_modulus(v, rlwe_q));
1035 });
1036
1037 let ui_ks_dim = (
1038 value
1039 .parameters
1040 .non_interactive_ui_to_s_key_switch_decomposition_count()
1041 .0
1042 * 2,
1043 value.parameters.rlwe_n().0,
1044 );
1045 let ui_to_s_ksks = value
1046 .ui_to_s_ksks
1047 .into_iter()
1048 .map(|m| {
1049 assert!(m.dimension() == ui_ks_dim);
1050 NormalAndShoup::new_with_modulus(m, rlwe_q)
1051 })
1052 .collect_vec();
1053
1054 assert!(
1055 value.lwe_ksk.dimension()
1056 == (
1057 value.parameters.rlwe_n().0 * value.parameters.lwe_decomposition_count().0,
1058 value.parameters.lwe_n().0 + 1
1059 )
1060 );
1061
1062 Self {
1063 rgsw_cts,
1064 auto_keys,
1065 lwe_ksk: value.lwe_ksk,
1066 ui_to_s_ksks,
1067 }
1068 }
1069 }
1070
1071 impl<M: Matrix> PbsKey for ShoupNonInteractiveServerKeyEvaluationDomain<M> {
1072 type AutoKey = NormalAndShoup<M>;
1073 type LweKskKey = M;
1074 type RgswCt = NormalAndShoup<M>;
1075
1076 fn galois_key_for_auto(&self, k: usize) -> &Self::AutoKey {
1077 let d = self.auto_keys.get(&k).unwrap();
1078 d
1079 }
1080 fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::RgswCt {
1081 &self.rgsw_cts[si]
1082 }
1083
1084 fn lwe_ksk(&self) -> &Self::LweKskKey {
1085 &self.lwe_ksk
1086 }
1087 }
1088}
1089
1090/// Server key in evaluation domain with Shoup representations
1091pub(crate) struct ShoupServerKeyEvaluationDomain<M> {
1092 /// Rgsw cts of LWE secret elements
1093 rgsw_cts: Vec<NormalAndShoup<M>>,
1094 /// Auto keys. Key corresponding to g^{k} is at index `k`. Key corresponding
1095 /// to -g is at 0
1096 galois_keys: HashMap<usize, NormalAndShoup<M>>,
1097 /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
1098 lwe_ksk: M,
1099}
1100
1101mod shoup_server_key_eval_domain {
1102 use itertools::{izip, Itertools};
1103 use num_traits::{FromPrimitive, PrimInt};
1104
1105 use crate::{backend::Modulus, pbs::PbsKey};
1106
1107 use super::*;
1108
1109 impl<M: MatrixMut + MatrixEntity + ToShoup<Modulus = M::MatElement>, R, N>
1110 From<ServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>>
1111 for ShoupServerKeyEvaluationDomain<M>
1112 where
1113 <M as Matrix>::R: RowMut,
1114 M::MatElement: PrimInt + FromPrimitive,
1115 {
1116 fn from(value: ServerKeyEvaluationDomain<M, BoolParameters<M::MatElement>, R, N>) -> Self {
1117 let q = value.parameters.rlwe_q().q().unwrap();
1118 // Rgsw ciphertexts
1119 let rgsw_cts = value
1120 .rgsw_cts
1121 .into_iter()
1122 .map(|ct| NormalAndShoup::new_with_modulus(ct, q))
1123 .collect_vec();
1124
1125 let mut auto_keys = HashMap::new();
1126 value.galois_keys.into_iter().for_each(|(index, key)| {
1127 auto_keys.insert(index, NormalAndShoup::new_with_modulus(key, q));
1128 });
1129
1130 Self {
1131 rgsw_cts,
1132 galois_keys: auto_keys,
1133 lwe_ksk: value.lwe_ksk,
1134 }
1135 }
1136 }
1137
1138 impl<M: Matrix> PbsKey for ShoupServerKeyEvaluationDomain<M> {
1139 type AutoKey = NormalAndShoup<M>;
1140 type LweKskKey = M;
1141 type RgswCt = NormalAndShoup<M>;
1142
1143 fn galois_key_for_auto(&self, k: usize) -> &Self::AutoKey {
1144 self.galois_keys.get(&k).unwrap()
1145 }
1146 fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::RgswCt {
1147 &self.rgsw_cts[si]
1148 }
1149
1150 fn lwe_ksk(&self) -> &Self::LweKskKey {
1151 &self.lwe_ksk
1152 }
1153 }
1154}
1155
1156pub struct CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M: Matrix, S> {
1157 /// (ak*si + e + \beta ui, ak*si + e)
1158 ni_rgsw_cts: (Vec<M>, Vec<M>),
1159 ui_to_s_ksk: M,
1160 others_ksk_zero_encs: Vec<M>,
1161
1162 auto_keys_share: HashMap<usize, M>,
1163 lwe_ksk_share: M::R,
1164
1165 user_index: usize,
1166 cr_seed: S,
1167}
1168
1169mod impl_common_ref_non_interactive_multi_party_server_share {
1170 use super::*;
1171
1172 impl<M: Matrix, S> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M, S> {
1173 pub(in super::super) fn new(
1174 ni_rgsw_cts: (Vec<M>, Vec<M>),
1175 ui_to_s_ksk: M,
1176 others_ksk_zero_encs: Vec<M>,
1177 auto_keys_share: HashMap<usize, M>,
1178 lwe_ksk_share: M::R,
1179 user_index: usize,
1180 cr_seed: S,
1181 ) -> Self {
1182 Self {
1183 ni_rgsw_cts,
1184 ui_to_s_ksk,
1185 others_ksk_zero_encs,
1186 auto_keys_share,
1187 lwe_ksk_share,
1188 user_index,
1189 cr_seed,
1190 }
1191 }
1192
1193 pub(in super::super) fn ni_rgsw_cts(&self) -> &(Vec<M>, Vec<M>) {
1194 &self.ni_rgsw_cts
1195 }
1196
1197 pub(in super::super) fn ui_to_s_ksk(&self) -> &M {
1198 &self.ui_to_s_ksk
1199 }
1200
1201 pub(in super::super) fn user_index(&self) -> usize {
1202 self.user_index
1203 }
1204
1205 pub(in super::super) fn auto_keys_share(&self) -> &HashMap<usize, M> {
1206 &self.auto_keys_share
1207 }
1208
1209 pub(in super::super) fn lwe_ksk_share(&self) -> &M::R {
1210 &self.lwe_ksk_share
1211 }
1212
1213 pub(in super::super) fn ui_to_s_ksk_zero_encs_for_user_i(&self, user_i: usize) -> &M {
1214 assert!(user_i != self.user_index);
1215 if user_i < self.user_index {
1216 &self.others_ksk_zero_encs[user_i]
1217 } else {
1218 &self.others_ksk_zero_encs[user_i - 1]
1219 }
1220 }
1221 }
1222}
1223
1224/// Stores normal and shoup representation of Matrix elements (Normal, Shoup)
1225pub(crate) struct NormalAndShoup<M>(M, M);
1226
1227impl<M: ToShoup> NormalAndShoup<M> {
1228 fn new_with_modulus(value: M, modulus: <M as ToShoup>::Modulus) -> Self {
1229 let value_shoup = M::to_shoup(&value, modulus);
1230 NormalAndShoup(value, value_shoup)
1231 }
1232}
1233
1234impl<M> AsRef<M> for NormalAndShoup<M> {
1235 fn as_ref(&self) -> &M {
1236 &self.0
1237 }
1238}
1239
1240impl<M> WithShoupRepr for NormalAndShoup<M> {
1241 type M = M;
1242 fn shoup_repr(&self) -> &Self::M {
1243 &self.1
1244 }
1245}
1246
1247pub(super) mod tests {
1248 use itertools::izip;
1249 use num_traits::{FromPrimitive, PrimInt, ToPrimitive, Zero};
1250
1251 use crate::{
1252 backend::{GetModulus, Modulus},
1253 bool::ClientKey,
1254 lwe::decrypt_lwe,
1255 parameters::CiphertextModulus,
1256 utils::TryConvertFrom1,
1257 ArithmeticOps, Row,
1258 };
1259
1260 use super::SinglePartyClientKey;
1261
1262 pub(crate) fn ideal_sk_rlwe(cks: &[ClientKey]) -> Vec<i32> {
1263 let mut ideal_rlwe_sk = cks[0].sk_rlwe();
1264 cks.iter().skip(1).for_each(|k| {
1265 let sk_rlwe = k.sk_rlwe();
1266 izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
1267 *a = *a + b;
1268 });
1269 });
1270 ideal_rlwe_sk
1271 }
1272
1273 pub(crate) fn ideal_sk_lwe(cks: &[ClientKey]) -> Vec<i32> {
1274 let mut ideal_rlwe_sk = cks[0].sk_lwe();
1275 cks.iter().skip(1).for_each(|k| {
1276 let sk_rlwe = k.sk_lwe();
1277 izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| {
1278 *a = *a + b;
1279 });
1280 });
1281 ideal_rlwe_sk
1282 }
1283
1284 pub(crate) fn measure_noise_lwe<
1285 R: Row,
1286 S,
1287 Modop: ArithmeticOps<Element = R::Element>
1288 + GetModulus<M = CiphertextModulus<R::Element>, Element = R::Element>,
1289 >(
1290 lwe_ct: &R,
1291 m_expected: R::Element,
1292 sk: &[S],
1293 modop: &Modop,
1294 ) -> f64
1295 where
1296 R: TryConvertFrom1<[S], CiphertextModulus<R::Element>>,
1297 R::Element: Zero + FromPrimitive + PrimInt,
1298 {
1299 let noisy_m = decrypt_lwe(lwe_ct, &sk, modop);
1300 let noise = modop.sub(&m_expected, &noisy_m);
1301 modop
1302 .modulus()
1303 .map_element_to_i64(&noise)
1304 .abs()
1305 .to_f64()
1306 .unwrap()
1307 .log2()
1308 }
1309}