phantom-zone/src/bool/evaluator.rs
src/bool/evaluator.rsRaw
1use std::{
2 cell::{OnceCell, RefCell},
3 collections::HashMap,
4 fmt::{Debug, Display},
5 marker::PhantomData,
6};
7
8use itertools::{izip, partition, Itertools};
9use num_traits::{FromPrimitive, Num, One, PrimInt, ToPrimitive, WrappingSub, Zero};
10
11use crate::{
12 backend::{ArithmeticOps, GetModulus, ModInit, ModularOpsU64, Modulus, VectorOps},
13 bool::parameters::{MP_BOOL_PARAMS, SP_BOOL_PARAMS},
14 decomposer::{Decomposer, DefaultDecomposer, NumInfo, RlweDecomposer},
15 lwe::{decrypt_lwe, encrypt_lwe, lwe_key_switch, lwe_ksk_keygen, measure_noise_lwe, LweSecret},
16 multi_party::public_key_share,
17 ntt::{self, Ntt, NttBackendU64, NttInit},
18 random::{
19 DefaultSecureRng, NewWithSeed, RandomFillGaussianInModulus, RandomFillUniformInModulus,
20 RandomGaussianElementInModulus,
21 },
22 rgsw::{
23 decrypt_rlwe, galois_auto, galois_key_gen, generate_auto_map, public_key_encrypt_rgsw,
24 rgsw_by_rgsw_inplace, rlwe_by_rgsw, secret_key_encrypt_rgsw, IsTrivial, RgswCiphertext,
25 RlweCiphertext, RlweSecret,
26 },
27 utils::{
28 fill_random_ternary_secret_with_hamming_weight, generate_prime, mod_exponent,
29 TryConvertFrom1, WithLocal,
30 },
31 Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret,
32};
33
34use super::parameters::{BoolParameters, CiphertextModulus};
35
36thread_local! {
37 static BOOL_EVALUATOR: RefCell<BoolEvaluator<Vec<Vec<u64>>, NttBackendU64, ModularOpsU64<CiphertextModulus<u64>>, ModularOpsU64<CiphertextModulus<u64>>>> = RefCell::new(BoolEvaluator::new(MP_BOOL_PARAMS));
38}
39
40pub fn set_parameter_set(parameter: &BoolParameters<u64>) {
41 BoolEvaluator::with_local_mut(|e| *e = BoolEvaluator::new(parameter.clone()))
42}
43
44impl WithLocal
45 for BoolEvaluator<
46 Vec<Vec<u64>>,
47 NttBackendU64,
48 ModularOpsU64<CiphertextModulus<u64>>,
49 ModularOpsU64<CiphertextModulus<u64>>,
50 >
51{
52 fn with_local<F, R>(func: F) -> R
53 where
54 F: Fn(&Self) -> R,
55 {
56 BOOL_EVALUATOR.with_borrow(|s| func(s))
57 }
58
59 fn with_local_mut<F, R>(func: F) -> R
60 where
61 F: Fn(&mut Self) -> R,
62 {
63 BOOL_EVALUATOR.with_borrow_mut(|s| func(s))
64 }
65}
66
67struct ScratchMemory<M>
68where
69 M: Matrix,
70{
71 lwe_vector: M::R,
72 decomposition_matrix: M,
73}
74
75impl<M: MatrixEntity> ScratchMemory<M>
76where
77 M::R: RowEntity,
78{
79 fn new(parameters: &BoolParameters<M::MatElement>) -> Self {
80 // Vector to store LWE ciphertext with LWE dimesnion n
81 let lwe_vector = M::R::zeros(parameters.lwe_n().0 + 1);
82
83 // Matrix to store decomposed polynomials
84 // Max decompistion count + space for temporary RLWE
85 let d = std::cmp::max(
86 parameters.auto_decomposition_count().0,
87 std::cmp::max(
88 parameters.rlwe_rgsw_decomposition_count().0 .0,
89 parameters.rlwe_rgsw_decomposition_count().1 .0,
90 ),
91 ) + 2;
92 let decomposition_matrix = M::zeros(d, parameters.rlwe_n().0);
93
94 Self {
95 lwe_vector,
96 decomposition_matrix,
97 }
98 }
99}
100
101// thread_local! {
102// pub(crate) static CLIENT_KEY: RefCell<ClientKey> =
103// RefCell::new(ClientKey::random()); }
104
105trait BoolEncoding {
106 type Element;
107 fn true_el(&self) -> Self::Element;
108 fn false_el(&self) -> Self::Element;
109 fn qby4(&self) -> Self::Element;
110 fn decode(&self, m: Self::Element) -> bool;
111}
112
113impl<T> BoolEncoding for CiphertextModulus<T> {
114 type Element = T;
115 fn false_el(&self) -> Self::Element {
116 todo!()
117 }
118 fn qby4(&self) -> Self::Element {
119 todo!()
120 }
121 fn true_el(&self) -> Self::Element {
122 todo!()
123 }
124 fn decode(&self, m: Self::Element) -> bool {
125 // let m = (((encoded_m + self.pbs_info.rlweq_by8).to_f64().unwrap() * 4f64)
126 // / self.pbs_info.parameters.rlwe_q().0.to_f64().unwrap())
127 // .round() as usize
128 // % 4usize;
129 // panic!("Incorrect bool decryption. Got m={m} but expected m to be
130 // 0 or 1")
131
132 todo!()
133 }
134}
135
136trait PbsKey {
137 type M: Matrix;
138
139 /// RGSW ciphertext of LWE secret elements
140 fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::M;
141 /// Key for automorphism
142 fn galois_key_for_auto(&self, k: isize) -> &Self::M;
143 /// LWE ksk to key switch from RLWE secret to LWE secret
144 fn lwe_ksk(&self) -> &Self::M;
145}
146
147trait PbsInfo {
148 type Element;
149 type Modulus: Modulus<Element = Self::Element>;
150 type NttOp: Ntt<Element = Self::Element>;
151 type D: Decomposer<Element = Self::Element>;
152
153 // Although both types have same bounds, they can be different types. For ex,
154 // type RlweModOp may only support native modulus, where LweModOp may only
155 // support prime modulus, etc.
156 type RlweModOp: VectorOps<Element = Self::Element> + ArithmeticOps<Element = Self::Element>;
157 type LweModOp: VectorOps<Element = Self::Element> + ArithmeticOps<Element = Self::Element>;
158
159 fn rlwe_q(&self) -> &Self::Modulus;
160 fn lwe_q(&self) -> &Self::Modulus;
161 fn br_q(&self) -> usize;
162 fn rlwe_n(&self) -> usize;
163 fn lwe_n(&self) -> usize;
164 /// Embedding fator for ring X^{q}+1 inside
165 fn embedding_factor(&self) -> usize;
166 /// generator g
167 fn g(&self) -> isize;
168 /// Decomposers
169 fn lwe_decomposer(&self) -> &Self::D;
170 fn rlwe_rgsw_decomposer(&self) -> &(Self::D, Self::D);
171 fn auto_decomposer(&self) -> &Self::D;
172
173 /// Modulus operators
174 fn modop_lweq(&self) -> &Self::LweModOp;
175 fn modop_rlweq(&self) -> &Self::RlweModOp;
176
177 /// Ntt operators
178 fn nttop_rlweq(&self) -> &Self::NttOp;
179
180 /// Maps a \in Z^*_{q} to discrete log k, with generator g (i.e. g^k =
181 /// a). Returned vector is of size q that stores dlog of a at `vec[a]`.
182 /// For any a, if k is s.t. a = g^{k}, then k is expressed as k. If k is s.t
183 /// a = -g^{k}, then k is expressed as k=k+q/2
184 fn g_k_dlog_map(&self) -> &[usize];
185 fn rlwe_auto_map(&self, k: isize) -> &(Vec<usize>, Vec<bool>);
186}
187
188#[derive(Clone)]
189struct ClientKey {
190 sk_rlwe: RlweSecret,
191 sk_lwe: LweSecret,
192}
193
194impl ClientKey {
195 fn random() -> Self {
196 let sk_rlwe = RlweSecret::random(0, 0);
197 let sk_lwe = LweSecret::random(0, 0);
198 Self { sk_rlwe, sk_lwe }
199 }
200}
201
202// impl WithLocal for ClientKey {
203// fn with_local<F, R>(func: F) -> R
204// where
205// F: Fn(&Self) -> R,
206// {
207// CLIENT_KEY.with_borrow(|client_key| func(client_key))
208// }
209
210// fn with_local_mut<F, R>(func: F) -> R
211// where
212// F: Fn(&mut Self) -> R,
213// {
214// CLIENT_KEY.with_borrow_mut(|client_key| func(client_key))
215// }
216// }
217
218// fn set_client_key(key: &ClientKey) {
219// ClientKey::with_local_mut(|k| *k = key.clone())
220// }
221
222struct MultiPartyDecryptionShare<E> {
223 share: E,
224}
225
226struct CommonReferenceSeededCollectivePublicKeyShare<R, S, P> {
227 share: R,
228 cr_seed: S,
229 parameters: P,
230}
231
232struct PublicKey<M, R, O> {
233 key: M,
234 _phantom: PhantomData<(R, O)>,
235}
236
237impl<
238 M: MatrixMut + MatrixEntity,
239 Rng: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>,
240 ModOp: VectorOps<Element = M::MatElement> + ModInit<M = CiphertextModulus<M::MatElement>>,
241 >
242 From<
243 &[CommonReferenceSeededCollectivePublicKeyShare<
244 M::R,
245 Rng::Seed,
246 BoolParameters<M::MatElement>,
247 >],
248 > for PublicKey<M, Rng, ModOp>
249where
250 <M as Matrix>::R: RowMut,
251 Rng::Seed: Copy + PartialEq,
252 M::MatElement: PartialEq + Copy,
253{
254 fn from(
255 value: &[CommonReferenceSeededCollectivePublicKeyShare<
256 M::R,
257 Rng::Seed,
258 BoolParameters<M::MatElement>,
259 >],
260 ) -> Self {
261 assert!(value.len() > 0);
262
263 let parameters = &value[0].parameters;
264 let mut key = M::zeros(2, parameters.rlwe_n().0);
265
266 // sample A
267 let seed = value[0].cr_seed;
268 let mut main_rng = Rng::new_with_seed(seed);
269 RandomFillUniformInModulus::random_fill(
270 &mut main_rng,
271 parameters.rlwe_q(),
272 key.get_row_mut(0),
273 );
274
275 // Sum all Bs
276 let rlweq_modop = ModOp::new(parameters.rlwe_q().clone());
277 value.iter().for_each(|share_i| {
278 assert!(share_i.cr_seed == seed);
279 assert!(&share_i.parameters == parameters);
280
281 rlweq_modop.elwise_add_mut(key.get_row_mut(1), share_i.share.as_ref());
282 });
283
284 PublicKey {
285 key,
286 _phantom: PhantomData,
287 }
288 }
289}
290
291struct CommonReferenceSeededMultiPartyServerKeyShare<M: Matrix, P, S> {
292 rgsw_cts: Vec<M>,
293 auto_keys: HashMap<isize, M>,
294 lwe_ksk: M::R,
295 /// Common reference seed
296 cr_seed: S,
297 parameters: P,
298}
299struct SeededMultiPartyServerKey<M: Matrix, S, P> {
300 rgsw_cts: Vec<M>,
301 auto_keys: HashMap<isize, M>,
302 lwe_ksk: M::R,
303 cr_seed: S,
304 parameters: P,
305}
306
307fn aggregate_multi_party_server_key_shares<
308 M: MatrixMut + MatrixEntity,
309 S: Copy + PartialEq,
310 D: RlweDecomposer<Element = M::MatElement>,
311 ModOp: VectorOps<Element = M::MatElement> + ModInit<M = CiphertextModulus<M::MatElement>>,
312 NttOp: Ntt<Element = M::MatElement> + NttInit<CiphertextModulus<M::MatElement>>,
313>(
314 shares: &[CommonReferenceSeededMultiPartyServerKeyShare<M, BoolParameters<M::MatElement>, S>],
315) -> SeededMultiPartyServerKey<M, S, BoolParameters<M::MatElement>>
316where
317 <M as Matrix>::R: RowMut + RowEntity,
318 M::MatElement: PrimInt + PartialEq + Zero,
319 M: Clone,
320{
321 assert!(shares.len() > 0);
322 let parameters = shares[0].parameters.clone();
323 let cr_seed = shares[0].cr_seed;
324
325 let rlwe_n = parameters.rlwe_n().0;
326 let g = parameters.g() as isize;
327 let rlwe_q = parameters.rlwe_q();
328 let lwe_q = parameters.lwe_q();
329
330 // sanity checks
331 shares.iter().skip(1).for_each(|s| {
332 assert!(s.parameters == parameters);
333 assert!(s.cr_seed == cr_seed);
334 });
335
336 let rlweq_modop = ModOp::new(*rlwe_q);
337 let rlweq_nttop = NttOp::new(rlwe_q, rlwe_n);
338
339 // auto keys
340 let mut auto_keys = HashMap::new();
341 for i in [g, -g] {
342 let mut key = M::zeros(parameters.auto_decomposition_count().0, rlwe_n);
343
344 shares.iter().for_each(|s| {
345 let auto_key_share_i = s.auto_keys.get(&i).expect("Auto key {i} missing");
346 assert!(
347 auto_key_share_i.dimension() == (parameters.auto_decomposition_count().0, rlwe_n)
348 );
349 izip!(key.iter_rows_mut(), auto_key_share_i.iter_rows()).for_each(
350 |(partb_out, partb_share)| {
351 rlweq_modop.elwise_add_mut(partb_out.as_mut(), partb_share.as_ref());
352 },
353 );
354 });
355
356 auto_keys.insert(i, key);
357 }
358
359 // rgsw ciphertext (most expensive part!)
360 let lwe_n = parameters.lwe_n().0;
361 let rgsw_by_rgsw_decomposer = parameters.rgsw_rgsw_decomposer::<D::D>();
362 let mut scratch_matrix = M::zeros(
363 std::cmp::max(
364 rgsw_by_rgsw_decomposer.a().decomposition_count(),
365 rgsw_by_rgsw_decomposer.b().decomposition_count(),
366 ) + (rgsw_by_rgsw_decomposer.a().decomposition_count() * 2
367 + rgsw_by_rgsw_decomposer.b().decomposition_count() * 2),
368 rlwe_n,
369 );
370
371 let mut tmp_rgsw =
372 RgswCiphertext::<M, _>::empty(rlwe_n, &rgsw_by_rgsw_decomposer, rlwe_q.clone()).data;
373 let rgsw_cts = (0..lwe_n)
374 .into_iter()
375 .map(|index| {
376 // copy over rgsw ciphertext for index^th secret element from first share and
377 // treat it as accumulating rgsw ciphertext
378 let mut rgsw_i = shares[0].rgsw_cts[index].clone();
379
380 shares.iter().skip(1).for_each(|si| {
381 // copy over si's RGSW[index] ciphertext and send to evaluation domain
382 izip!(tmp_rgsw.iter_rows_mut(), si.rgsw_cts[index].iter_rows()).for_each(
383 |(to_ri, from_ri)| {
384 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
385 rlweq_nttop.forward(to_ri.as_mut())
386 },
387 );
388
389 rgsw_by_rgsw_inplace(
390 &mut rgsw_i,
391 &tmp_rgsw,
392 &rgsw_by_rgsw_decomposer,
393 &mut scratch_matrix,
394 &rlweq_nttop,
395 &rlweq_modop,
396 );
397 });
398
399 rgsw_i
400 })
401 .collect_vec();
402
403 // LWE ksks
404 let mut lwe_ksk = M::R::zeros(rlwe_n * parameters.lwe_decomposition_count().0);
405 let lweq_modop = ModOp::new(*lwe_q);
406 shares.iter().for_each(|si| {
407 assert!(si.lwe_ksk.as_ref().len() == rlwe_n * parameters.lwe_decomposition_count().0);
408 lweq_modop.elwise_add_mut(lwe_ksk.as_mut(), si.lwe_ksk.as_ref())
409 });
410
411 SeededMultiPartyServerKey {
412 rgsw_cts,
413 auto_keys,
414 lwe_ksk,
415 cr_seed,
416 parameters: parameters,
417 }
418}
419
420/// Seeded single party server key
421struct SeededServerKey<M: Matrix, P, S> {
422 /// Rgsw cts of LWE secret elements
423 pub(crate) rgsw_cts: Vec<M>,
424 /// Auto keys
425 pub(crate) auto_keys: HashMap<isize, M>,
426 /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
427 pub(crate) lwe_ksk: M::R,
428 /// Parameters
429 pub(crate) parameters: P,
430 /// Main seed
431 pub(crate) seed: S,
432}
433
434impl<M: Matrix, S> SeededServerKey<M, BoolParameters<M::MatElement>, S> {
435 pub(crate) fn from_raw(
436 auto_keys: HashMap<isize, M>,
437 rgsw_cts: Vec<M>,
438 lwe_ksk: M::R,
439 parameters: BoolParameters<M::MatElement>,
440 seed: S,
441 ) -> Self {
442 // sanity checks
443 auto_keys.iter().for_each(|v| {
444 assert!(
445 v.1.dimension()
446 == (
447 parameters.auto_decomposition_count().0,
448 parameters.rlwe_n().0
449 )
450 )
451 });
452
453 let (part_a_d, part_b_d) = parameters.rlwe_rgsw_decomposition_count();
454 rgsw_cts.iter().for_each(|v| {
455 assert!(v.dimension() == (part_a_d.0 * 2 + part_b_d.0, parameters.rlwe_n().0))
456 });
457 assert!(
458 lwe_ksk.as_ref().len()
459 == (parameters.lwe_decomposition_count().0 * parameters.rlwe_n().0)
460 );
461
462 SeededServerKey {
463 rgsw_cts,
464 auto_keys,
465 lwe_ksk,
466 parameters,
467 seed,
468 }
469 }
470}
471
472/// Server key in evaluation domain
473struct ServerKeyEvaluationDomain<M, R, N> {
474 /// Rgsw cts of LWE secret elements
475 rgsw_cts: Vec<M>,
476 /// Galois keys
477 galois_keys: HashMap<isize, M>,
478 /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
479 lwe_ksk: M,
480 _phanton: PhantomData<(R, N)>,
481}
482
483impl<
484 M: MatrixMut + MatrixEntity,
485 R: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>> + NewWithSeed,
486 N: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
487 > From<&SeededServerKey<M, BoolParameters<M::MatElement>, R::Seed>>
488 for ServerKeyEvaluationDomain<M, R, N>
489where
490 <M as Matrix>::R: RowMut,
491 M::MatElement: Copy,
492 R::Seed: Clone,
493{
494 fn from(value: &SeededServerKey<M, BoolParameters<M::MatElement>, R::Seed>) -> Self {
495 let mut main_prng = R::new_with_seed(value.seed.clone());
496 let parameters = &value.parameters;
497 let g = parameters.g() as isize;
498 let ring_size = value.parameters.rlwe_n().0;
499 let lwe_n = value.parameters.lwe_n().0;
500 let rlwe_q = value.parameters.rlwe_q();
501 let lwq_q = value.parameters.lwe_q();
502
503 let nttop = N::new(rlwe_q, ring_size);
504
505 // galois keys
506 let mut auto_keys = HashMap::new();
507 let auto_decomp_count = parameters.auto_decomposition_count().0;
508 for i in [g, -g] {
509 let seeded_auto_key = value.auto_keys.get(&i).unwrap();
510 assert!(seeded_auto_key.dimension() == (auto_decomp_count, ring_size));
511
512 let mut data = M::zeros(auto_decomp_count * 2, ring_size);
513
514 // sample RLWE'_A(-s(X^k))
515 data.iter_rows_mut().take(auto_decomp_count).for_each(|ri| {
516 RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut())
517 });
518
519 // copy over RLWE'B_(-s(X^k))
520 izip!(
521 data.iter_rows_mut().skip(auto_decomp_count),
522 seeded_auto_key.iter_rows()
523 )
524 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
525
526 // Send to Evaluation domain
527 data.iter_rows_mut()
528 .for_each(|ri| nttop.forward(ri.as_mut()));
529
530 auto_keys.insert(i, data);
531 }
532
533 // RGSW ciphertexts
534 let (rlrg_a_decomp, rlrg_b_decomp) = parameters.rlwe_rgsw_decomposition_count();
535 let rgsw_cts = value
536 .rgsw_cts
537 .iter()
538 .map(|seeded_rgsw_si| {
539 assert!(
540 seeded_rgsw_si.dimension()
541 == (rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0, ring_size)
542 );
543
544 let mut data = M::zeros(rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0 * 2, ring_size);
545
546 // copy over RLWE'(-sm)
547 izip!(
548 data.iter_rows_mut().take(rlrg_a_decomp.0 * 2),
549 seeded_rgsw_si.iter_rows().take(rlrg_a_decomp.0 * 2)
550 )
551 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
552
553 // sample RLWE'_A(m)
554 data.iter_rows_mut()
555 .skip(rlrg_a_decomp.0 * 2)
556 .take(rlrg_b_decomp.0)
557 .for_each(|ri| {
558 RandomFillUniformInModulus::random_fill(
559 &mut main_prng,
560 &rlwe_q,
561 ri.as_mut(),
562 )
563 });
564
565 // copy over RLWE'_B(m)
566 izip!(
567 data.iter_rows_mut()
568 .skip(rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0),
569 seeded_rgsw_si.iter_rows().skip(rlrg_a_decomp.0 * 2)
570 )
571 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
572
573 // send polynomials to evaluation domain
574 data.iter_rows_mut()
575 .for_each(|ri| nttop.forward(ri.as_mut()));
576
577 data
578 })
579 .collect_vec();
580
581 // LWE ksk
582 let lwe_ksk = {
583 let d = parameters.lwe_decomposition_count().0;
584 assert!(value.lwe_ksk.as_ref().len() == d * ring_size);
585
586 let mut data = M::zeros(d * ring_size, lwe_n + 1);
587 izip!(data.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(|(lwe_i, bi)| {
588 RandomFillUniformInModulus::random_fill(
589 &mut main_prng,
590 &lwq_q,
591 &mut lwe_i.as_mut()[1..],
592 );
593 lwe_i.as_mut()[0] = *bi;
594 });
595
596 data
597 };
598
599 ServerKeyEvaluationDomain {
600 rgsw_cts,
601 galois_keys: auto_keys,
602 lwe_ksk,
603 _phanton: PhantomData,
604 }
605 }
606}
607
608impl<
609 M: MatrixMut + MatrixEntity,
610 Rng: NewWithSeed,
611 N: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
612 > From<&SeededMultiPartyServerKey<M, Rng::Seed, BoolParameters<M::MatElement>>>
613 for ServerKeyEvaluationDomain<M, Rng, N>
614where
615 <M as Matrix>::R: RowMut,
616 Rng::Seed: Copy,
617 Rng: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>,
618 M::MatElement: Copy,
619{
620 fn from(
621 value: &SeededMultiPartyServerKey<M, Rng::Seed, BoolParameters<M::MatElement>>,
622 ) -> Self {
623 let g = value.parameters.g() as isize;
624 let rlwe_n = value.parameters.rlwe_n().0;
625 let lwe_n = value.parameters.lwe_n().0;
626 let rlwe_q = value.parameters.rlwe_q();
627 let lwe_q = value.parameters.lwe_q();
628
629 let mut main_prng = Rng::new_with_seed(value.cr_seed);
630
631 let rlwe_nttop = N::new(rlwe_q, rlwe_n);
632
633 // auto keys
634 let mut auto_keys = HashMap::new();
635 let auto_d_count = value.parameters.auto_decomposition_count().0;
636 for i in [g, -g] {
637 let mut key = M::zeros(auto_d_count * 2, rlwe_n);
638
639 // sample a
640 key.iter_rows_mut().take(auto_d_count).for_each(|ri| {
641 RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut())
642 });
643
644 let key_part_b = value.auto_keys.get(&i).unwrap();
645 assert!(key_part_b.dimension() == (auto_d_count, rlwe_n));
646 izip!(
647 key.iter_rows_mut().skip(auto_d_count),
648 key_part_b.iter_rows()
649 )
650 .for_each(|(to_ri, from_ri)| {
651 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
652 });
653
654 // send to evaluation domain
655 key.iter_rows_mut()
656 .for_each(|ri| rlwe_nttop.forward(ri.as_mut()));
657
658 auto_keys.insert(i, key);
659 }
660
661 // rgsw cts
662 let (rgswrgsw_d_a, rgswrgsw_d_b) = value.parameters.rgsw_rgsw_decomposition_count();
663 let (rlrg_d_a, rlrg_d_b) = value.parameters.rlwe_rgsw_decomposition_count();
664 // d_a and d_b may differ for RGSWxRGSW multiplication and RLWExRGSW
665 // multiplication. After this point RGSW ciphertexts will only be used for
666 // RLWExRGSW multiplication (in blind rotation). Thus we drop any additional
667 // RLWE ciphertexts in incoming collective RGSW ciphertexts, which are
668 // result of RGSWxRGSW multiplications.
669 let rgsw_ct_rows_in = rgswrgsw_d_a.0 * 2 + rgswrgsw_d_b.0 * 2;
670 let rgsw_ct_rows_out = rlrg_d_a.0 * 2 + rlrg_d_b.0 * 2;
671 let rgsw_cts = value
672 .rgsw_cts
673 .iter()
674 .map(|ct_i_in| {
675 assert!(ct_i_in.dimension() == (rgsw_ct_rows_in, rlwe_n));
676 let mut eval_ct_i_out = M::zeros(rgsw_ct_rows_out, rlwe_n);
677
678 // RLWE'(-sm) part A
679 izip!(
680 eval_ct_i_out.iter_rows_mut().take(rlrg_d_a.0),
681 ct_i_in.iter_rows().take(rlrg_d_a.0)
682 )
683 .for_each(|(to_ri, from_ri)| {
684 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
685 rlwe_nttop.forward(to_ri.as_mut());
686 });
687
688 // RLWE'(-sm) part B
689 izip!(
690 eval_ct_i_out
691 .iter_rows_mut()
692 .skip(rlrg_d_a.0)
693 .take(rlrg_d_a.0),
694 ct_i_in.iter_rows().skip(rgswrgsw_d_a.0).take(rlrg_d_a.0)
695 )
696 .for_each(|(to_ri, from_ri)| {
697 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
698 rlwe_nttop.forward(to_ri.as_mut());
699 });
700
701 // RLWE'(m) Part A
702 izip!(
703 eval_ct_i_out
704 .iter_rows_mut()
705 .skip(rlrg_d_a.0 * 2)
706 .take(rlrg_d_b.0),
707 ct_i_in
708 .iter_rows()
709 .skip(rgswrgsw_d_a.0 * 2)
710 .take(rlrg_d_b.0)
711 )
712 .for_each(|(to_ri, from_ri)| {
713 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
714 rlwe_nttop.forward(to_ri.as_mut());
715 });
716
717 // RLWE'(m) Part B
718 izip!(
719 eval_ct_i_out
720 .iter_rows_mut()
721 .skip(rlrg_d_a.0 * 2 + rlrg_d_b.0)
722 .take(rlrg_d_b.0),
723 ct_i_in
724 .iter_rows()
725 .skip(rgswrgsw_d_a.0 * 2 + rgswrgsw_d_b.0)
726 .take(rlrg_d_b.0)
727 )
728 .for_each(|(to_ri, from_ri)| {
729 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
730 rlwe_nttop.forward(to_ri.as_mut());
731 });
732
733 eval_ct_i_out
734 })
735 .collect_vec();
736
737 // lwe ksk
738 let d_lwe = value.parameters.lwe_decomposition_count().0;
739 let mut lwe_ksk = M::zeros(rlwe_n * d_lwe, lwe_n + 1);
740 izip!(lwe_ksk.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(|(lwe_i, bi)| {
741 RandomFillUniformInModulus::random_fill(
742 &mut main_prng,
743 &lwe_q,
744 &mut lwe_i.as_mut()[1..],
745 );
746 lwe_i.as_mut()[0] = *bi;
747 });
748
749 ServerKeyEvaluationDomain {
750 rgsw_cts,
751 galois_keys: auto_keys,
752 lwe_ksk,
753 _phanton: PhantomData,
754 }
755 }
756}
757
758//FIXME(Jay): Figure out a way for BoolEvaluator to have access to ServerKey
759// via a pointer and implement PbsKey for BoolEvaluator instead of ServerKey
760// directly
761impl<M: Matrix, R, N> PbsKey for ServerKeyEvaluationDomain<M, R, N> {
762 type M = M;
763 fn galois_key_for_auto(&self, k: isize) -> &Self::M {
764 self.galois_keys.get(&k).unwrap()
765 }
766 fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::M {
767 &self.rgsw_cts[si]
768 }
769
770 fn lwe_ksk(&self) -> &Self::M {
771 &self.lwe_ksk
772 }
773}
774
775struct BoolPbsInfo<M: Matrix, Ntt, RlweModOp, LweModOp> {
776 auto_decomposer: DefaultDecomposer<M::MatElement>,
777 rlwe_rgsw_decomposer: (
778 DefaultDecomposer<M::MatElement>,
779 DefaultDecomposer<M::MatElement>,
780 ),
781 lwe_decomposer: DefaultDecomposer<M::MatElement>,
782 g_k_dlog_map: Vec<usize>,
783 rlwe_nttop: Ntt,
784 rlwe_modop: RlweModOp,
785 lwe_modop: LweModOp,
786 embedding_factor: usize,
787 nand_test_vec: M::R,
788 rlwe_qby4: M::MatElement,
789 rlwe_auto_maps: Vec<(Vec<usize>, Vec<bool>)>,
790 parameters: BoolParameters<M::MatElement>,
791}
792
793impl<M: Matrix, NttOp, RlweModOp, LweModOp> PbsInfo for BoolPbsInfo<M, NttOp, RlweModOp, LweModOp>
794where
795 M::MatElement: PrimInt + WrappingSub + NumInfo + Debug,
796 RlweModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
797 LweModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
798 NttOp: Ntt<Element = M::MatElement>,
799{
800 type Modulus = CiphertextModulus<M::MatElement>;
801 type Element = M::MatElement;
802 type D = DefaultDecomposer<M::MatElement>;
803 type RlweModOp = RlweModOp;
804 type LweModOp = LweModOp;
805 type NttOp = NttOp;
806 fn rlwe_auto_map(&self, k: isize) -> &(Vec<usize>, Vec<bool>) {
807 let g = self.parameters.g() as isize;
808 if k == g {
809 &self.rlwe_auto_maps[0]
810 } else if k == -g {
811 &self.rlwe_auto_maps[1]
812 } else {
813 panic!("RLWE auto map only supports k in [-g, g], but got k={k}");
814 }
815 }
816 fn br_q(&self) -> usize {
817 *self.parameters.br_q()
818 }
819 fn lwe_decomposer(&self) -> &Self::D {
820 &self.lwe_decomposer
821 }
822 fn rlwe_rgsw_decomposer(&self) -> &(Self::D, Self::D) {
823 &self.rlwe_rgsw_decomposer
824 }
825 fn auto_decomposer(&self) -> &Self::D {
826 &self.auto_decomposer
827 }
828 fn embedding_factor(&self) -> usize {
829 self.embedding_factor
830 }
831 fn g(&self) -> isize {
832 self.parameters.g() as isize
833 }
834 fn g_k_dlog_map(&self) -> &[usize] {
835 &self.g_k_dlog_map
836 }
837 fn lwe_n(&self) -> usize {
838 self.parameters.lwe_n().0
839 }
840 fn lwe_q(&self) -> &Self::Modulus {
841 self.parameters.lwe_q()
842 }
843 fn rlwe_n(&self) -> usize {
844 self.parameters.rlwe_n().0
845 }
846 fn rlwe_q(&self) -> &Self::Modulus {
847 self.parameters.rlwe_q()
848 }
849 fn modop_lweq(&self) -> &Self::LweModOp {
850 &self.lwe_modop
851 }
852 fn modop_rlweq(&self) -> &Self::RlweModOp {
853 &self.rlwe_modop
854 }
855 fn nttop_rlweq(&self) -> &Self::NttOp {
856 &self.rlwe_nttop
857 }
858}
859
860struct BoolEvaluator<M, Ntt, RlweModOp, LweModOp>
861where
862 M: Matrix,
863{
864 pbs_info: BoolPbsInfo<M, Ntt, RlweModOp, LweModOp>,
865 scratch_memory: ScratchMemory<M>,
866 _phantom: PhantomData<M>,
867}
868
869impl<M: Matrix, NttOp, RlweModOp, LweModOp> BoolEvaluator<M, NttOp, RlweModOp, LweModOp> {}
870
871impl<M: Matrix, NttOp, RlweModOp, LweModOp> BoolEvaluator<M, NttOp, RlweModOp, LweModOp>
872where
873 M: MatrixEntity + MatrixMut,
874 M::MatElement: PrimInt + Debug + Display + NumInfo + FromPrimitive + WrappingSub,
875 NttOp: Ntt<Element = M::MatElement>,
876 RlweModOp: ArithmeticOps<Element = M::MatElement>
877 + VectorOps<Element = M::MatElement>
878 + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>,
879 LweModOp: ArithmeticOps<Element = M::MatElement>
880 + VectorOps<Element = M::MatElement>
881 + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>,
882 M::R: TryConvertFrom1<[i32], CiphertextModulus<M::MatElement>> + RowEntity + Debug,
883 <M as Matrix>::R: RowMut,
884 DefaultSecureRng: RandomFillGaussianInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
885 + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
886 + RandomGaussianElementInModulus<M::MatElement, CiphertextModulus<M::MatElement>>
887 + NewWithSeed,
888{
889 fn new(parameters: BoolParameters<M::MatElement>) -> Self
890 where
891 RlweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
892 LweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
893 NttOp: NttInit<CiphertextModulus<M::MatElement>>,
894 {
895 //TODO(Jay): Run sanity checks for modulus values in parameters
896
897 // generatr dlog map s.t. g^{k} % q = a, for all a \in Z*_{q}
898 let g = parameters.g();
899 let q = *parameters.br_q();
900 let mut g_k_dlog_map = vec![0usize; q];
901 for i in 0..q / 2 {
902 let v = mod_exponent(g as u64, i as u64, q as u64) as usize;
903 // g^i
904 g_k_dlog_map[v] = i;
905 // -(g^i)
906 g_k_dlog_map[q - v] = i + (q / 2);
907 }
908
909 let embedding_factor = (2 * parameters.rlwe_n().0) / q;
910
911 let rlwe_nttop = NttOp::new(parameters.rlwe_q(), parameters.rlwe_n().0);
912 let rlwe_modop = RlweModOp::new(*parameters.rlwe_q());
913 let lwe_modop = LweModOp::new(*parameters.lwe_q());
914
915 // set test vectors
916 let q = *parameters.br_q();
917 let qby2 = q >> 1;
918 let qby8 = q >> 3;
919 let mut nand_test_vec = M::R::zeros(qby2);
920 // Q/8 (Q: rlwe_q)
921 let true_m_el = parameters.rlwe_q().true_el();
922 // -Q/8
923 let false_m_el = parameters.rlwe_q().false_el();
924 for i in 0..qby2 {
925 if i < (3 * qby8) {
926 nand_test_vec.as_mut()[i] = true_m_el;
927 } else {
928 nand_test_vec.as_mut()[i] = false_m_el;
929 }
930 }
931
932 // v(X) -> v(X^{-g})
933 let (auto_map_index, auto_map_sign) = generate_auto_map(qby2, -(g as isize));
934 let mut nand_test_vec_autog = M::R::zeros(qby2);
935 izip!(
936 nand_test_vec.as_ref().iter(),
937 auto_map_index.iter(),
938 auto_map_sign.iter()
939 )
940 .for_each(|(v, to_index, to_sign)| {
941 if !to_sign {
942 // negate
943 nand_test_vec_autog.as_mut()[*to_index] = rlwe_modop.neg(v);
944 } else {
945 nand_test_vec_autog.as_mut()[*to_index] = *v;
946 }
947 });
948
949 // auto map indices and sign
950 let mut rlwe_auto_maps = vec![];
951 let ring_size = parameters.rlwe_n().0;
952 let g = parameters.g() as isize;
953 for i in [g, -g] {
954 rlwe_auto_maps.push(generate_auto_map(ring_size, i))
955 }
956
957 let rlwe_qby4 = parameters.rlwe_q().qby4();
958
959 let scratch_memory = ScratchMemory::new(¶meters);
960
961 let pbs_info = BoolPbsInfo {
962 auto_decomposer: parameters.auto_decomposer(),
963 lwe_decomposer: parameters.lwe_decomposer(),
964 rlwe_rgsw_decomposer: parameters.rlwe_rgsw_decomposer(),
965 g_k_dlog_map,
966 embedding_factor,
967 lwe_modop,
968 rlwe_modop,
969 rlwe_nttop,
970 nand_test_vec: nand_test_vec_autog,
971 rlwe_qby4,
972 rlwe_auto_maps,
973 parameters: parameters,
974 };
975
976 BoolEvaluator {
977 pbs_info,
978 scratch_memory,
979 _phantom: PhantomData,
980 }
981 }
982
983 fn client_key(&self) -> ClientKey {
984 let sk_lwe = LweSecret::random(
985 self.pbs_info.parameters.lwe_n().0 >> 1,
986 self.pbs_info.parameters.lwe_n().0,
987 );
988 let sk_rlwe = RlweSecret::random(
989 self.pbs_info.parameters.rlwe_n().0 >> 1,
990 self.pbs_info.parameters.rlwe_n().0,
991 );
992 ClientKey { sk_rlwe, sk_lwe }
993 }
994
995 fn server_key(
996 &self,
997 client_key: &ClientKey,
998 ) -> SeededServerKey<M, BoolParameters<M::MatElement>, [u8; 32]> {
999 DefaultSecureRng::with_local_mut(|rng| {
1000 let mut main_seed = [0u8; 32];
1001 rng.fill_bytes(&mut main_seed);
1002
1003 let mut main_prng = DefaultSecureRng::new_seeded(main_seed);
1004
1005 let rlwe_n = self.pbs_info.parameters.rlwe_n().0;
1006 let sk_rlwe = &client_key.sk_rlwe;
1007 let sk_lwe = &client_key.sk_lwe;
1008
1009 // generate auto keys -g, g
1010 let mut auto_keys = HashMap::new();
1011 let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
1012 let g = self.pbs_info.parameters.g() as isize;
1013 for i in [g, -g] {
1014 let mut gk = M::zeros(self.pbs_info.auto_decomposer.decomposition_count(), rlwe_n);
1015 galois_key_gen(
1016 &mut gk,
1017 sk_rlwe.values(),
1018 i,
1019 &auto_gadget,
1020 &self.pbs_info.rlwe_modop,
1021 &self.pbs_info.rlwe_nttop,
1022 &mut main_prng,
1023 rng,
1024 );
1025 auto_keys.insert(i, gk);
1026 }
1027
1028 // generate rgsw ciphertexts RGSW(si) where si is i^th LWE secret element
1029 let ring_size = self.pbs_info.parameters.rlwe_n().0;
1030 let rlwe_q = self.pbs_info.parameters.rlwe_q();
1031 let (rlrg_d_a, rlrg_d_b) = (
1032 self.pbs_info.rlwe_rgsw_decomposer.0.decomposition_count(),
1033 self.pbs_info.rlwe_rgsw_decomposer.1.decomposition_count(),
1034 );
1035 let rlrg_gadget_a = self.pbs_info.rlwe_rgsw_decomposer.0.gadget_vector();
1036 let rlrg_gadget_b = self.pbs_info.rlwe_rgsw_decomposer.1.gadget_vector();
1037 let rgsw_cts = sk_lwe
1038 .values()
1039 .iter()
1040 .map(|si| {
1041 // X^{si}; assume |emebedding_factor * si| < N
1042 let mut m = M::R::zeros(ring_size);
1043 let si = (self.pbs_info.embedding_factor as i32) * si;
1044 // dbg!(si);
1045 if si < 0 {
1046 // X^{-i} = X^{2N - i} = -X^{N-i}
1047 m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one();
1048 } else {
1049 // X^{i}
1050 m.as_mut()[si.abs() as usize] = M::MatElement::one();
1051 }
1052
1053 let mut rgsw_si = M::zeros(rlrg_d_a * 2 + rlrg_d_b, ring_size);
1054 secret_key_encrypt_rgsw(
1055 &mut rgsw_si,
1056 m.as_ref(),
1057 &rlrg_gadget_a,
1058 &rlrg_gadget_b,
1059 sk_rlwe.values(),
1060 &self.pbs_info.rlwe_modop,
1061 &self.pbs_info.rlwe_nttop,
1062 &mut main_prng,
1063 rng,
1064 );
1065
1066 rgsw_si
1067 })
1068 .collect_vec();
1069
1070 // LWE KSK from RLWE secret s -> LWE secret z
1071 let d_lwe_gadget = self.pbs_info.lwe_decomposer.gadget_vector();
1072 let mut lwe_ksk =
1073 M::R::zeros(self.pbs_info.lwe_decomposer.decomposition_count() * ring_size);
1074 lwe_ksk_keygen(
1075 &sk_rlwe.values(),
1076 &sk_lwe.values(),
1077 &mut lwe_ksk,
1078 &d_lwe_gadget,
1079 &self.pbs_info.lwe_modop,
1080 &mut main_prng,
1081 rng,
1082 );
1083
1084 SeededServerKey::from_raw(
1085 auto_keys,
1086 rgsw_cts,
1087 lwe_ksk,
1088 self.pbs_info.parameters.clone(),
1089 main_seed,
1090 )
1091 })
1092 }
1093
1094 fn multi_party_server_key_share(
1095 &self,
1096 cr_seed: [u8; 32],
1097 collective_pk: &M,
1098 client_key: &ClientKey,
1099 ) -> CommonReferenceSeededMultiPartyServerKeyShare<M, BoolParameters<M::MatElement>, [u8; 32]>
1100 {
1101 DefaultSecureRng::with_local_mut(|rng| {
1102 let mut main_prng = DefaultSecureRng::new_seeded(cr_seed);
1103
1104 let sk_rlwe = &client_key.sk_rlwe;
1105 let sk_lwe = &client_key.sk_lwe;
1106
1107 let g = self.pbs_info.parameters.g() as isize;
1108 let ring_size = self.pbs_info.parameters.rlwe_n().0;
1109 let rlwe_q = self.pbs_info.parameters.rlwe_q();
1110 let lwe_q = self.pbs_info.parameters.lwe_q();
1111
1112 let rlweq_modop = &self.pbs_info.rlwe_modop;
1113 let rlweq_nttop = &self.pbs_info.rlwe_nttop;
1114
1115 // sanity check
1116 assert!(sk_rlwe.values().len() == ring_size);
1117 assert!(sk_lwe.values().len() == self.pbs_info.parameters.lwe_n().0);
1118
1119 // auto keys
1120 let mut auto_keys = HashMap::new();
1121 let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
1122 for i in [g, -g] {
1123 let mut ksk_out = M::zeros(
1124 self.pbs_info.auto_decomposer.decomposition_count(),
1125 ring_size,
1126 );
1127 galois_key_gen(
1128 &mut ksk_out,
1129 sk_rlwe.values(),
1130 i,
1131 &auto_gadget,
1132 rlweq_modop,
1133 rlweq_nttop,
1134 &mut main_prng,
1135 rng,
1136 );
1137 auto_keys.insert(i, ksk_out);
1138 }
1139
1140 // rgsw ciphertexts of lwe secret elements
1141 let rgsw_rgsw_decomposer = self
1142 .pbs_info
1143 .parameters
1144 .rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1145 let (rgrg_d_a, rgrg_d_b) = (
1146 rgsw_rgsw_decomposer.0.decomposition_count(),
1147 rgsw_rgsw_decomposer.1.decomposition_count(),
1148 );
1149 let (rgrg_gadget_a, rgrg_gadget_b) = (
1150 rgsw_rgsw_decomposer.0.gadget_vector(),
1151 rgsw_rgsw_decomposer.1.gadget_vector(),
1152 );
1153 let rgsw_cts = sk_lwe
1154 .values()
1155 .iter()
1156 .map(|si| {
1157 let mut m = M::R::zeros(ring_size);
1158 //TODO(Jay): It will be nice to have a function that returns polynomial
1159 // (monomial infact!) corresponding to secret element embedded in ring X^{2N+1}.
1160 // Save lots of mistakes where one forgest to emebed si in bigger ring.
1161 let si = *si * (self.pbs_info.embedding_factor as i32);
1162 if si < 0 {
1163 // X^{-si} = X^{2N-si} = -X^{N-si}, assuming abs(si) < N
1164 // (which it is given si is secret element)
1165 m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one();
1166 } else {
1167 m.as_mut()[si as usize] = M::MatElement::one();
1168 }
1169
1170 // public key RGSW encryption has no part that can be seeded, unlike secret key
1171 // RGSW encryption where RLWE'_A(m) is seeded
1172 let mut out_rgsw = M::zeros(rgrg_d_a * 2 + rgrg_d_b * 2, ring_size);
1173 public_key_encrypt_rgsw(
1174 &mut out_rgsw,
1175 &m.as_ref(),
1176 collective_pk,
1177 &rgrg_gadget_a,
1178 &rgrg_gadget_b,
1179 rlweq_modop,
1180 rlweq_nttop,
1181 rng,
1182 );
1183
1184 out_rgsw
1185 })
1186 .collect_vec();
1187
1188 // LWE ksk
1189 let mut lwe_ksk =
1190 M::R::zeros(self.pbs_info.lwe_decomposer.decomposition_count() * ring_size);
1191 let lwe_modop = &self.pbs_info.lwe_modop;
1192 let d_lwe_gadget_vec = self.pbs_info.lwe_decomposer.gadget_vector();
1193 lwe_ksk_keygen(
1194 sk_rlwe.values(),
1195 sk_lwe.values(),
1196 &mut lwe_ksk,
1197 &d_lwe_gadget_vec,
1198 lwe_modop,
1199 &mut main_prng,
1200 rng,
1201 );
1202
1203 CommonReferenceSeededMultiPartyServerKeyShare {
1204 auto_keys,
1205 rgsw_cts,
1206 lwe_ksk,
1207 cr_seed,
1208 parameters: self.pbs_info.parameters.clone(),
1209 }
1210 })
1211 }
1212
1213 fn multi_party_public_key_share(
1214 &self,
1215 cr_seed: [u8; 32],
1216 client_key: &ClientKey,
1217 ) -> CommonReferenceSeededCollectivePublicKeyShare<
1218 <M as Matrix>::R,
1219 [u8; 32],
1220 BoolParameters<<M as Matrix>::MatElement>,
1221 > {
1222 DefaultSecureRng::with_local_mut(|rng| {
1223 let mut share_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0);
1224 let modop = &self.pbs_info.rlwe_modop;
1225 let nttop = &self.pbs_info.rlwe_nttop;
1226 let mut main_prng = DefaultSecureRng::new_seeded(cr_seed);
1227 public_key_share(
1228 &mut share_out,
1229 client_key.sk_rlwe.values(),
1230 modop,
1231 nttop,
1232 &mut main_prng,
1233 rng,
1234 );
1235
1236 CommonReferenceSeededCollectivePublicKeyShare {
1237 share: share_out,
1238 cr_seed: cr_seed,
1239 parameters: self.pbs_info.parameters.clone(),
1240 }
1241 })
1242 }
1243
1244 fn multi_party_decryption_share(
1245 &self,
1246 lwe_ct: &M::R,
1247 client_key: &ClientKey,
1248 ) -> MultiPartyDecryptionShare<<M as Matrix>::MatElement> {
1249 assert!(lwe_ct.as_ref().len() == self.pbs_info.parameters.rlwe_n().0 + 1);
1250 let modop = &self.pbs_info.rlwe_modop;
1251 let mut neg_s = M::R::try_convert_from(
1252 client_key.sk_rlwe.values(),
1253 &self.pbs_info.parameters.rlwe_q(),
1254 );
1255 modop.elwise_neg_mut(neg_s.as_mut());
1256
1257 let mut neg_sa = M::MatElement::zero();
1258 izip!(lwe_ct.as_ref().iter().skip(1), neg_s.as_ref().iter()).for_each(|(ai, nsi)| {
1259 neg_sa = modop.add(&neg_sa, &modop.mul(ai, nsi));
1260 });
1261
1262 let e = DefaultSecureRng::with_local_mut(|rng| {
1263 let mut e =
1264 RandomGaussianElementInModulus::random(rng, self.pbs_info.parameters.rlwe_q());
1265 e
1266 });
1267 let share = modop.add(&neg_sa, &e);
1268
1269 MultiPartyDecryptionShare { share }
1270 }
1271
1272 pub(crate) fn multi_party_decrypt(
1273 &self,
1274 shares: &[MultiPartyDecryptionShare<M::MatElement>],
1275 lwe_ct: &M::R,
1276 ) -> bool {
1277 let modop = &self.pbs_info.rlwe_modop;
1278 let mut sum_a = M::MatElement::zero();
1279 shares
1280 .iter()
1281 .for_each(|share_i| sum_a = modop.add(&sum_a, &share_i.share));
1282
1283 let encoded_m = modop.add(&lwe_ct.as_ref()[0], &sum_a);
1284 self.pbs_info.parameters.rlwe_q().decode(encoded_m)
1285 }
1286
1287 /// First encrypt as RLWE(m) with m as constant polynomial and extract it as
1288 /// LWE ciphertext
1289 pub(crate) fn pk_encrypt(&self, pk: &M, m: bool) -> M::R {
1290 DefaultSecureRng::with_local_mut(|rng| {
1291 let modop = &self.pbs_info.rlwe_modop;
1292 let nttop = &self.pbs_info.rlwe_nttop;
1293
1294 // RLWE(0)
1295 // sample ephemeral key u
1296 let ring_size = self.pbs_info.parameters.rlwe_n().0;
1297 let mut u = vec![0i32; ring_size];
1298 fill_random_ternary_secret_with_hamming_weight(u.as_mut(), ring_size >> 1, rng);
1299 let mut u = M::R::try_convert_from(&u, &self.pbs_info.parameters.rlwe_q());
1300 nttop.forward(u.as_mut());
1301
1302 let mut ua = M::R::zeros(ring_size);
1303 ua.as_mut().copy_from_slice(pk.get_row_slice(0));
1304 let mut ub = M::R::zeros(ring_size);
1305 ub.as_mut().copy_from_slice(pk.get_row_slice(1));
1306
1307 // a*u
1308 nttop.forward(ua.as_mut());
1309 modop.elwise_mul_mut(ua.as_mut(), u.as_ref());
1310 nttop.backward(ua.as_mut());
1311
1312 // b*u
1313 nttop.forward(ub.as_mut());
1314 modop.elwise_mul_mut(ub.as_mut(), u.as_ref());
1315 nttop.backward(ub.as_mut());
1316
1317 let mut rlwe = M::zeros(2, ring_size);
1318 // sample error
1319 rlwe.iter_rows_mut().for_each(|ri| {
1320 RandomFillGaussianInModulus::random_fill(
1321 rng,
1322 &self.pbs_info.parameters.rlwe_q(),
1323 ri.as_mut(),
1324 );
1325 });
1326
1327 // a*u + e0
1328 modop.elwise_add_mut(rlwe.get_row_mut(0), ua.as_ref());
1329 // b*u + e1
1330 modop.elwise_add_mut(rlwe.get_row_mut(1), ub.as_ref());
1331
1332 //FIXME(Jay): Figure out a way to get Q/8 form modulus
1333 let m = if m {
1334 // Q/8
1335 self.pbs_info.rlwe_q().true_el()
1336 } else {
1337 // -Q/8
1338 self.pbs_info.rlwe_q().false_el()
1339 };
1340
1341 // b*u + e1 + m, where m is constant polynomial
1342 rlwe.set(1, 0, modop.add(rlwe.get(1, 0), &m));
1343
1344 // sample extract index 0
1345 let mut lwe_out = M::R::zeros(ring_size + 1);
1346 sample_extract(&mut lwe_out, &rlwe, modop, 0);
1347
1348 lwe_out
1349 })
1350 }
1351
1352 /// TODO(Jay): Fetch client key from thread local
1353 pub fn sk_encrypt(&self, m: bool, client_key: &ClientKey) -> M::R {
1354 //FIXME(Jay): Figure out a way to get Q/8 form modulus
1355 let m = if m {
1356 // Q/8
1357 self.pbs_info.rlwe_q().true_el()
1358 } else {
1359 // -Q/8
1360 self.pbs_info.rlwe_q().false_el()
1361 };
1362
1363 DefaultSecureRng::with_local_mut(|rng| {
1364 let mut lwe_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0 + 1);
1365 encrypt_lwe(
1366 &mut lwe_out,
1367 &m,
1368 client_key.sk_rlwe.values(),
1369 &self.pbs_info.rlwe_modop,
1370 rng,
1371 );
1372 lwe_out
1373 })
1374 }
1375
1376 pub fn sk_decrypt(&self, lwe_ct: &M::R, client_key: &ClientKey) -> bool {
1377 let m = decrypt_lwe(
1378 lwe_ct,
1379 client_key.sk_rlwe.values(),
1380 &self.pbs_info.rlwe_modop,
1381 );
1382 self.pbs_info.rlwe_q().decode(m)
1383 }
1384
1385 // TODO(Jay): scratch spaces must be thread local. Don't pass them as arguments
1386 pub fn nand(
1387 &mut self,
1388 c0: &M::R,
1389 c1: &M::R,
1390 server_key: &ServerKeyEvaluationDomain<M, DefaultSecureRng, NttOp>,
1391 ) -> M::R {
1392 let mut c_out = M::R::zeros(c0.as_ref().len());
1393 let modop = &self.pbs_info.rlwe_modop;
1394 izip!(
1395 c_out.as_mut().iter_mut(),
1396 c0.as_ref().iter(),
1397 c1.as_ref().iter()
1398 )
1399 .for_each(|(o, i0, i1)| {
1400 *o = modop.add(i0, i1);
1401 });
1402 // +Q/4
1403 c_out.as_mut()[0] = modop.add(&c_out.as_ref()[0], &self.pbs_info.rlwe_qby4);
1404
1405 // PBS
1406 pbs(
1407 &self.pbs_info,
1408 &self.pbs_info.nand_test_vec,
1409 &mut c_out,
1410 server_key,
1411 &mut self.scratch_memory.lwe_vector,
1412 &mut self.scratch_memory.decomposition_matrix,
1413 );
1414
1415 c_out
1416 }
1417}
1418
1419/// LMKCY+ Blind rotation
1420///
1421/// gk_to_si: [g^0, ..., g^{q/2-1}, -g^0, -g^1, .., -g^{q/2-1}]
1422fn blind_rotation<
1423 MT: IsTrivial + MatrixMut,
1424 Mmut: MatrixMut<MatElement = MT::MatElement> + Matrix,
1425 D: Decomposer<Element = MT::MatElement>,
1426 NttOp: Ntt<Element = MT::MatElement>,
1427 ModOp: ArithmeticOps<Element = MT::MatElement> + VectorOps<Element = MT::MatElement>,
1428 K: PbsKey<M = Mmut>,
1429 P: PbsInfo<Element = MT::MatElement>,
1430>(
1431 trivial_rlwe_test_poly: &mut MT,
1432 scratch_matrix: &mut Mmut,
1433 g: isize,
1434 w: usize,
1435 q: usize,
1436 gk_to_si: &[Vec<usize>],
1437 rlwe_rgsw_decomposer: &(D, D),
1438 auto_decomposer: &D,
1439 ntt_op: &NttOp,
1440 mod_op: &ModOp,
1441 parameters: &P,
1442 pbs_key: &K,
1443) where
1444 <Mmut as Matrix>::R: RowMut,
1445 Mmut::MatElement: Copy + Zero,
1446 <MT as Matrix>::R: RowMut,
1447{
1448 let q_by_2 = q / 2;
1449
1450 // -(g^k)
1451 for i in (1..q_by_2).rev() {
1452 gk_to_si[q_by_2 + i].iter().for_each(|s_index| {
1453 rlwe_by_rgsw(
1454 trivial_rlwe_test_poly,
1455 pbs_key.rgsw_ct_lwe_si(*s_index),
1456 scratch_matrix,
1457 rlwe_rgsw_decomposer,
1458 ntt_op,
1459 mod_op,
1460 );
1461 });
1462
1463 let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(g);
1464 galois_auto(
1465 trivial_rlwe_test_poly,
1466 pbs_key.galois_key_for_auto(g),
1467 scratch_matrix,
1468 &auto_map_index,
1469 &auto_map_sign,
1470 mod_op,
1471 ntt_op,
1472 auto_decomposer,
1473 );
1474 }
1475
1476 // -(g^0)
1477 gk_to_si[q_by_2].iter().for_each(|s_index| {
1478 rlwe_by_rgsw(
1479 trivial_rlwe_test_poly,
1480 pbs_key.rgsw_ct_lwe_si(*s_index),
1481 scratch_matrix,
1482 rlwe_rgsw_decomposer,
1483 ntt_op,
1484 mod_op,
1485 );
1486 });
1487 let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(-g);
1488 galois_auto(
1489 trivial_rlwe_test_poly,
1490 pbs_key.galois_key_for_auto(-g),
1491 scratch_matrix,
1492 &auto_map_index,
1493 &auto_map_sign,
1494 mod_op,
1495 ntt_op,
1496 auto_decomposer,
1497 );
1498
1499 // +(g^k)
1500 for i in (1..q_by_2).rev() {
1501 gk_to_si[i].iter().for_each(|s_index| {
1502 rlwe_by_rgsw(
1503 trivial_rlwe_test_poly,
1504 pbs_key.rgsw_ct_lwe_si(*s_index),
1505 scratch_matrix,
1506 rlwe_rgsw_decomposer,
1507 ntt_op,
1508 mod_op,
1509 );
1510 });
1511
1512 let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(g);
1513 galois_auto(
1514 trivial_rlwe_test_poly,
1515 pbs_key.galois_key_for_auto(g),
1516 scratch_matrix,
1517 &auto_map_index,
1518 &auto_map_sign,
1519 mod_op,
1520 ntt_op,
1521 auto_decomposer,
1522 );
1523 }
1524
1525 // +(g^0)
1526 gk_to_si[0].iter().for_each(|s_index| {
1527 rlwe_by_rgsw(
1528 trivial_rlwe_test_poly,
1529 pbs_key.rgsw_ct_lwe_si(gk_to_si[q_by_2][*s_index]),
1530 scratch_matrix,
1531 rlwe_rgsw_decomposer,
1532 ntt_op,
1533 mod_op,
1534 );
1535 });
1536}
1537
1538/// - Mod down
1539/// - key switching
1540/// - mod down
1541/// - blind rotate
1542fn pbs<
1543 M: Matrix + MatrixMut + MatrixEntity,
1544 P: PbsInfo<Element = M::MatElement>,
1545 // NttOp: Ntt<Element = M::MatElement>,
1546 // ModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
1547 K: PbsKey<M = M>,
1548>(
1549 pbs_info: &P,
1550 test_vec: &M::R,
1551 lwe_in: &mut M::R,
1552 pbs_key: &K,
1553 scratch_lwe_vec: &mut M::R,
1554 scratch_blind_rotate_matrix: &mut M,
1555) where
1556 <M as Matrix>::R: RowMut,
1557 M::MatElement: PrimInt + ToPrimitive + FromPrimitive + One + Copy + Zero + Display,
1558{
1559 let rlwe_q = pbs_info.rlwe_q();
1560 let lwe_q = pbs_info.lwe_q();
1561 let br_q = pbs_info.br_q();
1562 let rlwe_qf64 = rlwe_q.q().unwrap().to_f64().unwrap();
1563 let lwe_qf64 = lwe_q.q().unwrap().to_f64().unwrap();
1564 let br_qf64 = br_q.to_f64().unwrap();
1565 let rlwe_n = pbs_info.rlwe_n();
1566
1567 // PBSTracer::with_local_mut(|t| {
1568 // let out = lwe_in
1569 // .as_ref()
1570 // .iter()
1571 // .map(|v| v.to_u64().unwrap())
1572 // .collect_vec();
1573 // t.ct_rlwe_q_mod = out;
1574 // });
1575
1576 // moddown Q -> Q_ks
1577 lwe_in.as_mut().iter_mut().for_each(|v| {
1578 *v =
1579 M::MatElement::from_f64(((v.to_f64().unwrap() * lwe_qf64) / rlwe_qf64).round()).unwrap()
1580 });
1581
1582 // PBSTracer::with_local_mut(|t| {
1583 // let out = lwe_in
1584 // .as_ref()
1585 // .iter()
1586 // .map(|v| v.to_u64().unwrap())
1587 // .collect_vec();
1588 // t.ct_lwe_q_mod = out;
1589 // });
1590
1591 // key switch RLWE secret to LWE secret
1592 scratch_lwe_vec.as_mut().fill(M::MatElement::zero());
1593 lwe_key_switch(
1594 scratch_lwe_vec,
1595 lwe_in,
1596 pbs_key.lwe_ksk(),
1597 pbs_info.modop_lweq(),
1598 pbs_info.lwe_decomposer(),
1599 );
1600
1601 // PBSTracer::with_local_mut(|t| {
1602 // let out = scratch_lwe_vec
1603 // .as_ref()
1604 // .iter()
1605 // .map(|v| v.to_u64().unwrap())
1606 // .collect_vec();
1607 // t.ct_lwe_q_mod_after_ksk = out;
1608 // });
1609
1610 // odd mowdown Q_ks -> q
1611 let g_k_dlog_map = pbs_info.g_k_dlog_map();
1612 let mut g_k_si = vec![vec![]; br_q];
1613 scratch_lwe_vec
1614 .as_ref()
1615 .iter()
1616 .skip(1)
1617 .enumerate()
1618 .for_each(|(index, v)| {
1619 let odd_v = mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64);
1620 let k = g_k_dlog_map[odd_v];
1621 g_k_si[k].push(index);
1622 });
1623
1624 // PBSTracer::with_local_mut(|t| {
1625 // let out = scratch_lwe_vec
1626 // .as_ref()
1627 // .iter()
1628 // .map(|v| mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64) as
1629 // u64) .collect_vec();
1630 // t.ct_br_q_mod = out;
1631 // });
1632
1633 // handle b and set trivial test RLWE
1634 let g = pbs_info.g() as usize;
1635 let g_times_b = (g * mod_switch_odd(
1636 scratch_lwe_vec.as_ref()[0].to_f64().unwrap(),
1637 lwe_qf64,
1638 br_qf64,
1639 )) % (br_q);
1640 // v = (v(X) * X^{g*b}) mod X^{q/2}+1
1641 let br_qby2 = br_q / 2;
1642 let mut gb_monomial_sign = true;
1643 let mut gb_monomial_exp = g_times_b;
1644 // X^{g*b} mod X^{q/2}+1
1645 if gb_monomial_exp > br_qby2 {
1646 gb_monomial_exp -= br_qby2;
1647 gb_monomial_sign = false
1648 }
1649 // monomial mul
1650 let mut trivial_rlwe_test_poly = RlweCiphertext::<_, DefaultSecureRng> {
1651 data: M::zeros(2, rlwe_n),
1652 is_trivial: true,
1653 _phatom: PhantomData,
1654 };
1655 if pbs_info.embedding_factor() == 1 {
1656 monomial_mul(
1657 test_vec.as_ref(),
1658 trivial_rlwe_test_poly.get_row_mut(1).as_mut(),
1659 gb_monomial_exp,
1660 gb_monomial_sign,
1661 br_qby2,
1662 pbs_info.modop_rlweq(),
1663 );
1664 } else {
1665 // use lwe_in to store the `t = v(X) * X^{g*2} mod X^{q/2}+1` temporarily. This
1666 // works because q/2 <= N (where N is lwe_in LWE dimension) always.
1667 monomial_mul(
1668 test_vec.as_ref(),
1669 &mut lwe_in.as_mut()[..br_qby2],
1670 gb_monomial_exp,
1671 gb_monomial_sign,
1672 br_qby2,
1673 pbs_info.modop_rlweq(),
1674 );
1675
1676 // emebed poly `t` in ring X^{q/2}+1 inside the bigger ring X^{N}+1
1677 let embed_factor = pbs_info.embedding_factor();
1678 let partb_trivial_rlwe = trivial_rlwe_test_poly.get_row_mut(1);
1679 lwe_in.as_ref()[..br_qby2]
1680 .iter()
1681 .enumerate()
1682 .for_each(|(index, v)| {
1683 partb_trivial_rlwe[embed_factor * index] = *v;
1684 });
1685 }
1686
1687 // blind rotate
1688 blind_rotation(
1689 &mut trivial_rlwe_test_poly,
1690 scratch_blind_rotate_matrix,
1691 pbs_info.g(),
1692 1,
1693 br_q,
1694 &g_k_si,
1695 pbs_info.rlwe_rgsw_decomposer(),
1696 pbs_info.auto_decomposer(),
1697 pbs_info.nttop_rlweq(),
1698 pbs_info.modop_rlweq(),
1699 pbs_info,
1700 pbs_key,
1701 );
1702
1703 // ClientKey::with_local(|ck| {
1704 // let ring_size = parameters.rlwe_n();
1705 // let mut rlwe_ct = vec![vec![0u64; ring_size]; 2];
1706 // izip!(
1707 // rlwe_ct[0].iter_mut(),
1708 // trivial_rlwe_test_poly.0.get_row_slice(0)
1709 // )
1710 // .for_each(|(t, f)| {
1711 // *t = f.to_u64().unwrap();
1712 // });
1713 // izip!(
1714 // rlwe_ct[1].iter_mut(),
1715 // trivial_rlwe_test_poly.0.get_row_slice(1)
1716 // )
1717 // .for_each(|(t, f)| {
1718 // *t = f.to_u64().unwrap();
1719 // });
1720 // let mut m_out = vec![vec![0u64; ring_size]];
1721 // let modop = ModularOpsU64::new(rlwe_q.to_u64().unwrap());
1722 // let nttop = NttBackendU64::new(rlwe_q.to_u64().unwrap(), ring_size);
1723 // decrypt_rlwe(&rlwe_ct, ck.sk_rlwe.values(), &mut m_out, &nttop, &modop);
1724
1725 // println!("RLWE post PBS message: {:?}", m_out[0]);
1726 // });
1727
1728 // sample extract
1729 sample_extract(lwe_in, &trivial_rlwe_test_poly, pbs_info.modop_rlweq(), 0);
1730}
1731
1732fn mod_switch_odd(v: f64, from_q: f64, to_q: f64) -> usize {
1733 let odd_v = (((v * to_q) / (from_q)).floor()).to_usize().unwrap();
1734 //TODO(Jay): check correctness of this
1735 odd_v + ((odd_v & 1) ^ 1)
1736}
1737
1738// TODO(Jay): Add tests for sample extract
1739fn sample_extract<M: Matrix + MatrixMut, ModOp: ArithmeticOps<Element = M::MatElement>>(
1740 lwe_out: &mut M::R,
1741 rlwe_in: &M,
1742 mod_op: &ModOp,
1743 index: usize,
1744) where
1745 <M as Matrix>::R: RowMut,
1746 M::MatElement: Copy,
1747{
1748 let ring_size = rlwe_in.dimension().1;
1749
1750 // index..=0
1751 let to = &mut lwe_out.as_mut()[1..];
1752 let from = rlwe_in.get_row_slice(0);
1753 for i in 0..index + 1 {
1754 to[i] = from[index - i];
1755 }
1756
1757 // -(N..index)
1758 for i in index + 1..ring_size {
1759 to[i] = mod_op.neg(&from[ring_size + index - i]);
1760 }
1761
1762 // set b
1763 lwe_out.as_mut()[0] = *rlwe_in.get(1, index);
1764}
1765
1766/// TODO(Jay): Write tests for monomial mul
1767fn monomial_mul<El, ModOp: ArithmeticOps<Element = El>>(
1768 p_in: &[El],
1769 p_out: &mut [El],
1770 mon_exp: usize,
1771 mon_sign: bool,
1772 ring_size: usize,
1773 mod_op: &ModOp,
1774) where
1775 El: Copy,
1776{
1777 debug_assert!(p_in.as_ref().len() == ring_size);
1778 debug_assert!(p_in.as_ref().len() == p_out.as_ref().len());
1779 debug_assert!(mon_exp < ring_size);
1780
1781 p_in.as_ref().iter().enumerate().for_each(|(index, v)| {
1782 let mut to_index = index + mon_exp;
1783 let mut to_sign = mon_sign;
1784 if to_index >= ring_size {
1785 to_index = to_index - ring_size;
1786 to_sign = !to_sign;
1787 }
1788
1789 if !to_sign {
1790 p_out.as_mut()[to_index] = mod_op.neg(v);
1791 } else {
1792 p_out.as_mut()[to_index] = *v;
1793 }
1794 });
1795}
1796
1797// thread_local! {
1798// static PBS_TRACER: RefCell<PBSTracer<Vec<Vec<u64>>>> =
1799// RefCell::new(PBSTracer::default()); }
1800
1801// #[derive(Default)]
1802// struct PBSTracer<M>
1803// where
1804// M: Matrix + Default,
1805// {
1806// pub(crate) ct_rlwe_q_mod: M::R,
1807// pub(crate) ct_lwe_q_mod: M::R,
1808// pub(crate) ct_lwe_q_mod_after_ksk: M::R,
1809// pub(crate) ct_br_q_mod: Vec<u64>,
1810// }
1811
1812// impl PBSTracer<Vec<Vec<u64>>> {
1813// fn trace(&self, parameters: &BoolParameters<u64>, sk_lwe: &[i32],
1814// sk_rlwe: &[i32]) { assert!(parameters.rlwe_n().0 == sk_rlwe.len());
1815// assert!(parameters.lwe_n().0 == sk_lwe.len());
1816
1817// let modop_rlweq = ModularOpsU64::new(*parameters.rlwe_q());
1818// // noise after mod down Q -> Q_ks
1819// let m_back0 = decrypt_lwe(&self.ct_rlwe_q_mod, sk_rlwe,
1820// &modop_rlweq);
1821
1822// let modop_lweq = ModularOpsU64::new(parameters.lwe_q().0);
1823// // noise after mod down Q -> Q_ks
1824// let m_back1 = decrypt_lwe(&self.ct_lwe_q_mod, sk_rlwe, &modop_lweq);
1825// // noise after key switch from RLWE -> LWE
1826// let m_back2 = decrypt_lwe(&self.ct_lwe_q_mod_after_ksk, sk_lwe,
1827// &modop_lweq);
1828
1829// // noise after mod down odd from Q_ks -> q
1830// let modop_br_q = ModularOpsU64::new(parameters.br_q().0);
1831// let m_back3 = decrypt_lwe(&self.ct_br_q_mod, sk_lwe, &modop_br_q);
1832
1833// println!(
1834// "
1835// M initial mod Q: {m_back0},
1836// M after mod down Q -> Q_ks: {m_back1},
1837// M after key switch from RLWE -> LWE: {m_back2},
1838// M after mod dwon Q_ks -> q: {m_back3}
1839// "
1840// );
1841// }
1842// }
1843
1844// impl WithLocal for PBSTracer<Vec<Vec<u64>>> {
1845// fn with_local<F, R>(func: F) -> R
1846// where
1847// F: Fn(&Self) -> R,
1848// {
1849// PBS_TRACER.with_borrow(|t| func(t))
1850// }
1851
1852// fn with_local_mut<F, R>(func: F) -> R
1853// where
1854// F: Fn(&mut Self) -> R,
1855// {
1856// PBS_TRACER.with_borrow_mut(|t| func(t))
1857// }
1858// }