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 ops::Shr,
7};
8
9use itertools::{izip, partition, Itertools};
10use num_traits::{FromPrimitive, Num, One, PrimInt, ToPrimitive, WrappingSub, Zero};
11
12use crate::{
13 backend::{ArithmeticOps, GetModulus, ModInit, ModularOpsU64, Modulus, VectorOps},
14 bool::parameters::{MP_BOOL_PARAMS, SP_BOOL_PARAMS},
15 decomposer::{Decomposer, DefaultDecomposer, NumInfo, RlweDecomposer},
16 lwe::{decrypt_lwe, encrypt_lwe, lwe_key_switch, lwe_ksk_keygen, measure_noise_lwe, LweSecret},
17 multi_party::public_key_share,
18 ntt::{self, Ntt, NttBackendU64, NttInit},
19 random::{
20 DefaultSecureRng, NewWithSeed, RandomFillGaussianInModulus, RandomFillUniformInModulus,
21 RandomGaussianElementInModulus,
22 },
23 rgsw::{
24 decrypt_rlwe, galois_auto, galois_key_gen, generate_auto_map, public_key_encrypt_rgsw,
25 rgsw_by_rgsw_inplace, rlwe_by_rgsw, secret_key_encrypt_rgsw, IsTrivial, RgswCiphertext,
26 RlweCiphertext, RlweSecret,
27 },
28 utils::{
29 fill_random_ternary_secret_with_hamming_weight, generate_prime, mod_exponent,
30 TryConvertFrom1, WithLocal,
31 },
32 Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret,
33};
34
35use super::parameters::{BoolParameters, CiphertextModulus};
36
37thread_local! {
38 static BOOL_EVALUATOR: RefCell<BoolEvaluator<Vec<Vec<u64>>, NttBackendU64, ModularOpsU64<CiphertextModulus<u64>>, ModularOpsU64<CiphertextModulus<u64>>>> = RefCell::new(BoolEvaluator::new(MP_BOOL_PARAMS));
39}
40
41pub fn set_parameter_set(parameter: &BoolParameters<u64>) {
42 BoolEvaluator::with_local_mut(|e| *e = BoolEvaluator::new(parameter.clone()))
43}
44
45impl WithLocal
46 for BoolEvaluator<
47 Vec<Vec<u64>>,
48 NttBackendU64,
49 ModularOpsU64<CiphertextModulus<u64>>,
50 ModularOpsU64<CiphertextModulus<u64>>,
51 >
52{
53 fn with_local<F, R>(func: F) -> R
54 where
55 F: Fn(&Self) -> R,
56 {
57 BOOL_EVALUATOR.with_borrow(|s| func(s))
58 }
59
60 fn with_local_mut<F, R>(func: F) -> R
61 where
62 F: Fn(&mut Self) -> R,
63 {
64 BOOL_EVALUATOR.with_borrow_mut(|s| func(s))
65 }
66}
67
68struct ScratchMemory<M>
69where
70 M: Matrix,
71{
72 lwe_vector: M::R,
73 decomposition_matrix: M,
74}
75
76impl<M: MatrixEntity> ScratchMemory<M>
77where
78 M::R: RowEntity,
79{
80 fn new(parameters: &BoolParameters<M::MatElement>) -> Self {
81 // Vector to store LWE ciphertext with LWE dimesnion n
82 let lwe_vector = M::R::zeros(parameters.lwe_n().0 + 1);
83
84 // Matrix to store decomposed polynomials
85 // Max decompistion count + space for temporary RLWE
86 let d = std::cmp::max(
87 parameters.auto_decomposition_count().0,
88 std::cmp::max(
89 parameters.rlwe_rgsw_decomposition_count().0 .0,
90 parameters.rlwe_rgsw_decomposition_count().1 .0,
91 ),
92 ) + 2;
93 let decomposition_matrix = M::zeros(d, parameters.rlwe_n().0);
94
95 Self {
96 lwe_vector,
97 decomposition_matrix,
98 }
99 }
100}
101
102// thread_local! {
103// pub(crate) static CLIENT_KEY: RefCell<ClientKey> =
104// RefCell::new(ClientKey::random()); }
105
106trait BoolEncoding {
107 type Element;
108 fn true_el(&self) -> Self::Element;
109 fn false_el(&self) -> Self::Element;
110 fn qby4(&self) -> Self::Element;
111 fn decode(&self, m: Self::Element) -> bool;
112}
113
114impl<T> BoolEncoding for CiphertextModulus<T>
115where
116 CiphertextModulus<T>: Modulus<Element = T>,
117 T: PrimInt,
118{
119 type Element = T;
120
121 fn qby4(&self) -> Self::Element {
122 if self.is_native() {
123 T::one() << (CiphertextModulus::<T>::_bits() - 2)
124 } else {
125 self.q().unwrap() >> 2
126 }
127 }
128 /// Q/8
129 fn true_el(&self) -> Self::Element {
130 if self.is_native() {
131 T::one() << (CiphertextModulus::<T>::_bits() - 3)
132 } else {
133 self.q().unwrap() >> 3
134 }
135 }
136 /// -Q/8
137 fn false_el(&self) -> Self::Element {
138 self.largest_unsigned_value() - self.true_el() + T::one()
139 }
140 fn decode(&self, m: Self::Element) -> bool {
141 let qby8 = self.true_el();
142 let m = (((m + qby8).to_f64().unwrap() * 4.0f64) / self.q_as_f64().unwrap()).round()
143 as usize
144 % 4usize;
145
146 if m == 0 {
147 return false;
148 } else if m == 1 {
149 return true;
150 } else {
151 panic!("Incorrect bool decryption. Got m={m} but expected m to be 0 or 1")
152 }
153 }
154}
155
156trait PbsKey {
157 type M: Matrix;
158
159 /// RGSW ciphertext of LWE secret elements
160 fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::M;
161 /// Key for automorphism
162 fn galois_key_for_auto(&self, k: isize) -> &Self::M;
163 /// LWE ksk to key switch from RLWE secret to LWE secret
164 fn lwe_ksk(&self) -> &Self::M;
165}
166
167trait PbsInfo {
168 type Element;
169 type Modulus: Modulus<Element = Self::Element>;
170 type NttOp: Ntt<Element = Self::Element>;
171 type D: Decomposer<Element = Self::Element>;
172
173 // Although both types have same bounds, they can be different types. For ex,
174 // type RlweModOp may only support native modulus, where LweModOp may only
175 // support prime modulus, etc.
176 type RlweModOp: VectorOps<Element = Self::Element> + ArithmeticOps<Element = Self::Element>;
177 type LweModOp: VectorOps<Element = Self::Element> + ArithmeticOps<Element = Self::Element>;
178
179 fn rlwe_q(&self) -> &Self::Modulus;
180 fn lwe_q(&self) -> &Self::Modulus;
181 fn br_q(&self) -> usize;
182 fn rlwe_n(&self) -> usize;
183 fn lwe_n(&self) -> usize;
184 /// Embedding fator for ring X^{q}+1 inside
185 fn embedding_factor(&self) -> usize;
186 /// generator g
187 fn g(&self) -> isize;
188 /// Decomposers
189 fn lwe_decomposer(&self) -> &Self::D;
190 fn rlwe_rgsw_decomposer(&self) -> &(Self::D, Self::D);
191 fn auto_decomposer(&self) -> &Self::D;
192
193 /// Modulus operators
194 fn modop_lweq(&self) -> &Self::LweModOp;
195 fn modop_rlweq(&self) -> &Self::RlweModOp;
196
197 /// Ntt operators
198 fn nttop_rlweq(&self) -> &Self::NttOp;
199
200 /// Maps a \in Z^*_{q} to discrete log k, with generator g (i.e. g^k =
201 /// a). Returned vector is of size q that stores dlog of a at `vec[a]`.
202 /// For any a, if k is s.t. a = g^{k}, then k is expressed as k. If k is s.t
203 /// a = -g^{k}, then k is expressed as k=k+q/2
204 fn g_k_dlog_map(&self) -> &[usize];
205 fn rlwe_auto_map(&self, k: isize) -> &(Vec<usize>, Vec<bool>);
206}
207
208#[derive(Clone)]
209struct ClientKey {
210 sk_rlwe: RlweSecret,
211 sk_lwe: LweSecret,
212}
213
214impl ClientKey {
215 fn random() -> Self {
216 let sk_rlwe = RlweSecret::random(0, 0);
217 let sk_lwe = LweSecret::random(0, 0);
218 Self { sk_rlwe, sk_lwe }
219 }
220}
221
222// impl WithLocal for ClientKey {
223// fn with_local<F, R>(func: F) -> R
224// where
225// F: Fn(&Self) -> R,
226// {
227// CLIENT_KEY.with_borrow(|client_key| func(client_key))
228// }
229
230// fn with_local_mut<F, R>(func: F) -> R
231// where
232// F: Fn(&mut Self) -> R,
233// {
234// CLIENT_KEY.with_borrow_mut(|client_key| func(client_key))
235// }
236// }
237
238// fn set_client_key(key: &ClientKey) {
239// ClientKey::with_local_mut(|k| *k = key.clone())
240// }
241
242struct MultiPartyDecryptionShare<E> {
243 share: E,
244}
245
246struct CommonReferenceSeededCollectivePublicKeyShare<R, S, P> {
247 share: R,
248 cr_seed: S,
249 parameters: P,
250}
251
252struct PublicKey<M, R, O> {
253 key: M,
254 _phantom: PhantomData<(R, O)>,
255}
256
257impl<
258 M: MatrixMut + MatrixEntity,
259 Rng: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>,
260 ModOp: VectorOps<Element = M::MatElement> + ModInit<M = CiphertextModulus<M::MatElement>>,
261 >
262 From<
263 &[CommonReferenceSeededCollectivePublicKeyShare<
264 M::R,
265 Rng::Seed,
266 BoolParameters<M::MatElement>,
267 >],
268 > for PublicKey<M, Rng, ModOp>
269where
270 <M as Matrix>::R: RowMut,
271 Rng::Seed: Copy + PartialEq,
272 M::MatElement: PartialEq + Copy,
273{
274 fn from(
275 value: &[CommonReferenceSeededCollectivePublicKeyShare<
276 M::R,
277 Rng::Seed,
278 BoolParameters<M::MatElement>,
279 >],
280 ) -> Self {
281 assert!(value.len() > 0);
282
283 let parameters = &value[0].parameters;
284 let mut key = M::zeros(2, parameters.rlwe_n().0);
285
286 // sample A
287 let seed = value[0].cr_seed;
288 let mut main_rng = Rng::new_with_seed(seed);
289 RandomFillUniformInModulus::random_fill(
290 &mut main_rng,
291 parameters.rlwe_q(),
292 key.get_row_mut(0),
293 );
294
295 // Sum all Bs
296 let rlweq_modop = ModOp::new(parameters.rlwe_q().clone());
297 value.iter().for_each(|share_i| {
298 assert!(share_i.cr_seed == seed);
299 assert!(&share_i.parameters == parameters);
300
301 rlweq_modop.elwise_add_mut(key.get_row_mut(1), share_i.share.as_ref());
302 });
303
304 PublicKey {
305 key,
306 _phantom: PhantomData,
307 }
308 }
309}
310
311struct CommonReferenceSeededMultiPartyServerKeyShare<M: Matrix, P, S> {
312 rgsw_cts: Vec<M>,
313 auto_keys: HashMap<isize, M>,
314 lwe_ksk: M::R,
315 /// Common reference seed
316 cr_seed: S,
317 parameters: P,
318}
319struct SeededMultiPartyServerKey<M: Matrix, S, P> {
320 rgsw_cts: Vec<M>,
321 auto_keys: HashMap<isize, M>,
322 lwe_ksk: M::R,
323 cr_seed: S,
324 parameters: P,
325}
326
327/// Seeded single party server key
328struct SeededServerKey<M: Matrix, P, S> {
329 /// Rgsw cts of LWE secret elements
330 pub(crate) rgsw_cts: Vec<M>,
331 /// Auto keys
332 pub(crate) auto_keys: HashMap<isize, M>,
333 /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
334 pub(crate) lwe_ksk: M::R,
335 /// Parameters
336 pub(crate) parameters: P,
337 /// Main seed
338 pub(crate) seed: S,
339}
340
341impl<M: Matrix, S> SeededServerKey<M, BoolParameters<M::MatElement>, S> {
342 pub(crate) fn from_raw(
343 auto_keys: HashMap<isize, M>,
344 rgsw_cts: Vec<M>,
345 lwe_ksk: M::R,
346 parameters: BoolParameters<M::MatElement>,
347 seed: S,
348 ) -> Self {
349 // sanity checks
350 auto_keys.iter().for_each(|v| {
351 assert!(
352 v.1.dimension()
353 == (
354 parameters.auto_decomposition_count().0,
355 parameters.rlwe_n().0
356 )
357 )
358 });
359
360 let (part_a_d, part_b_d) = parameters.rlwe_rgsw_decomposition_count();
361 rgsw_cts.iter().for_each(|v| {
362 assert!(v.dimension() == (part_a_d.0 * 2 + part_b_d.0, parameters.rlwe_n().0))
363 });
364 assert!(
365 lwe_ksk.as_ref().len()
366 == (parameters.lwe_decomposition_count().0 * parameters.rlwe_n().0)
367 );
368
369 SeededServerKey {
370 rgsw_cts,
371 auto_keys,
372 lwe_ksk,
373 parameters,
374 seed,
375 }
376 }
377}
378
379/// Server key in evaluation domain
380struct ServerKeyEvaluationDomain<M, R, N> {
381 /// Rgsw cts of LWE secret elements
382 rgsw_cts: Vec<M>,
383 /// Galois keys
384 galois_keys: HashMap<isize, M>,
385 /// LWE ksk to key switching LWE ciphertext from RLWE secret to LWE secret
386 lwe_ksk: M,
387 _phanton: PhantomData<(R, N)>,
388}
389
390impl<
391 M: MatrixMut + MatrixEntity,
392 R: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>> + NewWithSeed,
393 N: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
394 > From<&SeededServerKey<M, BoolParameters<M::MatElement>, R::Seed>>
395 for ServerKeyEvaluationDomain<M, R, N>
396where
397 <M as Matrix>::R: RowMut,
398 M::MatElement: Copy,
399 R::Seed: Clone,
400{
401 fn from(value: &SeededServerKey<M, BoolParameters<M::MatElement>, R::Seed>) -> Self {
402 let mut main_prng = R::new_with_seed(value.seed.clone());
403 let parameters = &value.parameters;
404 let g = parameters.g() as isize;
405 let ring_size = value.parameters.rlwe_n().0;
406 let lwe_n = value.parameters.lwe_n().0;
407 let rlwe_q = value.parameters.rlwe_q();
408 let lwq_q = value.parameters.lwe_q();
409
410 let nttop = N::new(rlwe_q, ring_size);
411
412 // galois keys
413 let mut auto_keys = HashMap::new();
414 let auto_decomp_count = parameters.auto_decomposition_count().0;
415 for i in [g, -g] {
416 let seeded_auto_key = value.auto_keys.get(&i).unwrap();
417 assert!(seeded_auto_key.dimension() == (auto_decomp_count, ring_size));
418
419 let mut data = M::zeros(auto_decomp_count * 2, ring_size);
420
421 // sample RLWE'_A(-s(X^k))
422 data.iter_rows_mut().take(auto_decomp_count).for_each(|ri| {
423 RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut())
424 });
425
426 // copy over RLWE'B_(-s(X^k))
427 izip!(
428 data.iter_rows_mut().skip(auto_decomp_count),
429 seeded_auto_key.iter_rows()
430 )
431 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
432
433 // Send to Evaluation domain
434 data.iter_rows_mut()
435 .for_each(|ri| nttop.forward(ri.as_mut()));
436
437 auto_keys.insert(i, data);
438 }
439
440 // RGSW ciphertexts
441 let (rlrg_a_decomp, rlrg_b_decomp) = parameters.rlwe_rgsw_decomposition_count();
442 let rgsw_cts = value
443 .rgsw_cts
444 .iter()
445 .map(|seeded_rgsw_si| {
446 assert!(
447 seeded_rgsw_si.dimension()
448 == (rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0, ring_size)
449 );
450
451 let mut data = M::zeros(rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0 * 2, ring_size);
452
453 // copy over RLWE'(-sm)
454 izip!(
455 data.iter_rows_mut().take(rlrg_a_decomp.0 * 2),
456 seeded_rgsw_si.iter_rows().take(rlrg_a_decomp.0 * 2)
457 )
458 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
459
460 // sample RLWE'_A(m)
461 data.iter_rows_mut()
462 .skip(rlrg_a_decomp.0 * 2)
463 .take(rlrg_b_decomp.0)
464 .for_each(|ri| {
465 RandomFillUniformInModulus::random_fill(
466 &mut main_prng,
467 &rlwe_q,
468 ri.as_mut(),
469 )
470 });
471
472 // copy over RLWE'_B(m)
473 izip!(
474 data.iter_rows_mut()
475 .skip(rlrg_a_decomp.0 * 2 + rlrg_b_decomp.0),
476 seeded_rgsw_si.iter_rows().skip(rlrg_a_decomp.0 * 2)
477 )
478 .for_each(|(to_ri, from_ri)| to_ri.as_mut().copy_from_slice(from_ri.as_ref()));
479
480 // send polynomials to evaluation domain
481 data.iter_rows_mut()
482 .for_each(|ri| nttop.forward(ri.as_mut()));
483
484 data
485 })
486 .collect_vec();
487
488 // LWE ksk
489 let lwe_ksk = {
490 let d = parameters.lwe_decomposition_count().0;
491 assert!(value.lwe_ksk.as_ref().len() == d * ring_size);
492
493 let mut data = M::zeros(d * ring_size, lwe_n + 1);
494 izip!(data.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(|(lwe_i, bi)| {
495 RandomFillUniformInModulus::random_fill(
496 &mut main_prng,
497 &lwq_q,
498 &mut lwe_i.as_mut()[1..],
499 );
500 lwe_i.as_mut()[0] = *bi;
501 });
502
503 data
504 };
505
506 ServerKeyEvaluationDomain {
507 rgsw_cts,
508 galois_keys: auto_keys,
509 lwe_ksk,
510 _phanton: PhantomData,
511 }
512 }
513}
514
515impl<
516 M: MatrixMut + MatrixEntity,
517 Rng: NewWithSeed,
518 N: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
519 > From<&SeededMultiPartyServerKey<M, Rng::Seed, BoolParameters<M::MatElement>>>
520 for ServerKeyEvaluationDomain<M, Rng, N>
521where
522 <M as Matrix>::R: RowMut,
523 Rng::Seed: Copy,
524 Rng: RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>,
525 M::MatElement: Copy,
526{
527 fn from(
528 value: &SeededMultiPartyServerKey<M, Rng::Seed, BoolParameters<M::MatElement>>,
529 ) -> Self {
530 let g = value.parameters.g() as isize;
531 let rlwe_n = value.parameters.rlwe_n().0;
532 let lwe_n = value.parameters.lwe_n().0;
533 let rlwe_q = value.parameters.rlwe_q();
534 let lwe_q = value.parameters.lwe_q();
535
536 let mut main_prng = Rng::new_with_seed(value.cr_seed);
537
538 let rlwe_nttop = N::new(rlwe_q, rlwe_n);
539
540 // auto keys
541 let mut auto_keys = HashMap::new();
542 let auto_d_count = value.parameters.auto_decomposition_count().0;
543 for i in [g, -g] {
544 let mut key = M::zeros(auto_d_count * 2, rlwe_n);
545
546 // sample a
547 key.iter_rows_mut().take(auto_d_count).for_each(|ri| {
548 RandomFillUniformInModulus::random_fill(&mut main_prng, &rlwe_q, ri.as_mut())
549 });
550
551 let key_part_b = value.auto_keys.get(&i).unwrap();
552 assert!(key_part_b.dimension() == (auto_d_count, rlwe_n));
553 izip!(
554 key.iter_rows_mut().skip(auto_d_count),
555 key_part_b.iter_rows()
556 )
557 .for_each(|(to_ri, from_ri)| {
558 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
559 });
560
561 // send to evaluation domain
562 key.iter_rows_mut()
563 .for_each(|ri| rlwe_nttop.forward(ri.as_mut()));
564
565 auto_keys.insert(i, key);
566 }
567
568 // rgsw cts
569 let (rlrg_d_a, rlrg_d_b) = value.parameters.rlwe_rgsw_decomposition_count();
570 let rgsw_ct_out = rlrg_d_a.0 * 2 + rlrg_d_b.0 * 2;
571 let rgsw_cts = value
572 .rgsw_cts
573 .iter()
574 .map(|ct_i_in| {
575 assert!(ct_i_in.dimension() == (rgsw_ct_out, rlwe_n));
576 let mut eval_ct_i_out = M::zeros(rgsw_ct_out, rlwe_n);
577
578 izip!(eval_ct_i_out.iter_rows_mut(), ct_i_in.iter_rows()).for_each(
579 |(to_ri, from_ri)| {
580 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
581 rlwe_nttop.forward(to_ri.as_mut());
582 },
583 );
584
585 eval_ct_i_out
586 })
587 .collect_vec();
588
589 // lwe ksk
590 let d_lwe = value.parameters.lwe_decomposition_count().0;
591 let mut lwe_ksk = M::zeros(rlwe_n * d_lwe, lwe_n + 1);
592 izip!(lwe_ksk.iter_rows_mut(), value.lwe_ksk.as_ref().iter()).for_each(|(lwe_i, bi)| {
593 RandomFillUniformInModulus::random_fill(
594 &mut main_prng,
595 &lwe_q,
596 &mut lwe_i.as_mut()[1..],
597 );
598 lwe_i.as_mut()[0] = *bi;
599 });
600
601 ServerKeyEvaluationDomain {
602 rgsw_cts,
603 galois_keys: auto_keys,
604 lwe_ksk,
605 _phanton: PhantomData,
606 }
607 }
608}
609
610//FIXME(Jay): Figure out a way for BoolEvaluator to have access to ServerKey
611// via a pointer and implement PbsKey for BoolEvaluator instead of ServerKey
612// directly
613impl<M: Matrix, R, N> PbsKey for ServerKeyEvaluationDomain<M, R, N> {
614 type M = M;
615 fn galois_key_for_auto(&self, k: isize) -> &Self::M {
616 self.galois_keys.get(&k).unwrap()
617 }
618 fn rgsw_ct_lwe_si(&self, si: usize) -> &Self::M {
619 &self.rgsw_cts[si]
620 }
621
622 fn lwe_ksk(&self) -> &Self::M {
623 &self.lwe_ksk
624 }
625}
626
627struct BoolPbsInfo<M: Matrix, Ntt, RlweModOp, LweModOp> {
628 auto_decomposer: DefaultDecomposer<M::MatElement>,
629 rlwe_rgsw_decomposer: (
630 DefaultDecomposer<M::MatElement>,
631 DefaultDecomposer<M::MatElement>,
632 ),
633 lwe_decomposer: DefaultDecomposer<M::MatElement>,
634 g_k_dlog_map: Vec<usize>,
635 rlwe_nttop: Ntt,
636 rlwe_modop: RlweModOp,
637 lwe_modop: LweModOp,
638 embedding_factor: usize,
639 nand_test_vec: M::R,
640 rlwe_qby4: M::MatElement,
641 rlwe_auto_maps: Vec<(Vec<usize>, Vec<bool>)>,
642 parameters: BoolParameters<M::MatElement>,
643}
644
645impl<M: Matrix, NttOp, RlweModOp, LweModOp> PbsInfo for BoolPbsInfo<M, NttOp, RlweModOp, LweModOp>
646where
647 M::MatElement: PrimInt + WrappingSub + NumInfo + Debug + FromPrimitive,
648 RlweModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
649 LweModOp: ArithmeticOps<Element = M::MatElement> + VectorOps<Element = M::MatElement>,
650 NttOp: Ntt<Element = M::MatElement>,
651{
652 type Modulus = CiphertextModulus<M::MatElement>;
653 type Element = M::MatElement;
654 type D = DefaultDecomposer<M::MatElement>;
655 type RlweModOp = RlweModOp;
656 type LweModOp = LweModOp;
657 type NttOp = NttOp;
658 fn rlwe_auto_map(&self, k: isize) -> &(Vec<usize>, Vec<bool>) {
659 let g = self.parameters.g() as isize;
660 if k == g {
661 &self.rlwe_auto_maps[0]
662 } else if k == -g {
663 &self.rlwe_auto_maps[1]
664 } else {
665 panic!("RLWE auto map only supports k in [-g, g], but got k={k}");
666 }
667 }
668 fn br_q(&self) -> usize {
669 *self.parameters.br_q()
670 }
671 fn lwe_decomposer(&self) -> &Self::D {
672 &self.lwe_decomposer
673 }
674 fn rlwe_rgsw_decomposer(&self) -> &(Self::D, Self::D) {
675 &self.rlwe_rgsw_decomposer
676 }
677 fn auto_decomposer(&self) -> &Self::D {
678 &self.auto_decomposer
679 }
680 fn embedding_factor(&self) -> usize {
681 self.embedding_factor
682 }
683 fn g(&self) -> isize {
684 self.parameters.g() as isize
685 }
686 fn g_k_dlog_map(&self) -> &[usize] {
687 &self.g_k_dlog_map
688 }
689 fn lwe_n(&self) -> usize {
690 self.parameters.lwe_n().0
691 }
692 fn lwe_q(&self) -> &Self::Modulus {
693 self.parameters.lwe_q()
694 }
695 fn rlwe_n(&self) -> usize {
696 self.parameters.rlwe_n().0
697 }
698 fn rlwe_q(&self) -> &Self::Modulus {
699 self.parameters.rlwe_q()
700 }
701 fn modop_lweq(&self) -> &Self::LweModOp {
702 &self.lwe_modop
703 }
704 fn modop_rlweq(&self) -> &Self::RlweModOp {
705 &self.rlwe_modop
706 }
707 fn nttop_rlweq(&self) -> &Self::NttOp {
708 &self.rlwe_nttop
709 }
710}
711
712struct BoolEvaluator<M, Ntt, RlweModOp, LweModOp>
713where
714 M: Matrix,
715{
716 pbs_info: BoolPbsInfo<M, Ntt, RlweModOp, LweModOp>,
717 scratch_memory: ScratchMemory<M>,
718 _phantom: PhantomData<M>,
719}
720
721impl<M: Matrix, NttOp, RlweModOp, LweModOp> BoolEvaluator<M, NttOp, RlweModOp, LweModOp> {}
722
723impl<M: Matrix, NttOp, RlweModOp, LweModOp> BoolEvaluator<M, NttOp, RlweModOp, LweModOp>
724where
725 M: MatrixEntity + MatrixMut,
726 M::MatElement: PrimInt + Debug + Display + NumInfo + FromPrimitive + WrappingSub,
727 NttOp: Ntt<Element = M::MatElement>,
728 RlweModOp: ArithmeticOps<Element = M::MatElement>
729 + VectorOps<Element = M::MatElement>
730 + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>,
731 LweModOp: ArithmeticOps<Element = M::MatElement>
732 + VectorOps<Element = M::MatElement>
733 + GetModulus<Element = M::MatElement, M = CiphertextModulus<M::MatElement>>,
734 M::R: TryConvertFrom1<[i32], CiphertextModulus<M::MatElement>> + RowEntity + Debug,
735 <M as Matrix>::R: RowMut,
736 DefaultSecureRng: RandomFillGaussianInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
737 + RandomFillUniformInModulus<[M::MatElement], CiphertextModulus<M::MatElement>>
738 + RandomGaussianElementInModulus<M::MatElement, CiphertextModulus<M::MatElement>>
739 + NewWithSeed,
740{
741 fn new(parameters: BoolParameters<M::MatElement>) -> Self
742 where
743 RlweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
744 LweModOp: ModInit<M = CiphertextModulus<M::MatElement>>,
745 NttOp: NttInit<CiphertextModulus<M::MatElement>>,
746 {
747 //TODO(Jay): Run sanity checks for modulus values in parameters
748
749 // generatr dlog map s.t. g^{k} % q = a, for all a \in Z*_{q}
750 let g = parameters.g();
751 let q = *parameters.br_q();
752 let mut g_k_dlog_map = vec![0usize; q];
753 for i in 0..q / 2 {
754 let v = mod_exponent(g as u64, i as u64, q as u64) as usize;
755 // g^i
756 g_k_dlog_map[v] = i;
757 // -(g^i)
758 g_k_dlog_map[q - v] = i + (q / 2);
759 }
760
761 let embedding_factor = (2 * parameters.rlwe_n().0) / q;
762
763 let rlwe_nttop = NttOp::new(parameters.rlwe_q(), parameters.rlwe_n().0);
764 let rlwe_modop = RlweModOp::new(*parameters.rlwe_q());
765 let lwe_modop = LweModOp::new(*parameters.lwe_q());
766
767 // set test vectors
768 let q = *parameters.br_q();
769 let qby2 = q >> 1;
770 let qby8 = q >> 3;
771 let mut nand_test_vec = M::R::zeros(qby2);
772 // Q/8 (Q: rlwe_q)
773 let true_m_el = parameters.rlwe_q().true_el();
774 // -Q/8
775 let false_m_el = parameters.rlwe_q().false_el();
776 for i in 0..qby2 {
777 if i < (3 * qby8) {
778 nand_test_vec.as_mut()[i] = true_m_el;
779 } else {
780 nand_test_vec.as_mut()[i] = false_m_el;
781 }
782 }
783
784 // v(X) -> v(X^{-g})
785 let (auto_map_index, auto_map_sign) = generate_auto_map(qby2, -(g as isize));
786 let mut nand_test_vec_autog = M::R::zeros(qby2);
787 izip!(
788 nand_test_vec.as_ref().iter(),
789 auto_map_index.iter(),
790 auto_map_sign.iter()
791 )
792 .for_each(|(v, to_index, to_sign)| {
793 if !to_sign {
794 // negate
795 nand_test_vec_autog.as_mut()[*to_index] = rlwe_modop.neg(v);
796 } else {
797 nand_test_vec_autog.as_mut()[*to_index] = *v;
798 }
799 });
800
801 // auto map indices and sign
802 let mut rlwe_auto_maps = vec![];
803 let ring_size = parameters.rlwe_n().0;
804 let g = parameters.g() as isize;
805 for i in [g, -g] {
806 rlwe_auto_maps.push(generate_auto_map(ring_size, i))
807 }
808
809 let rlwe_qby4 = parameters.rlwe_q().qby4();
810
811 let scratch_memory = ScratchMemory::new(¶meters);
812
813 let pbs_info = BoolPbsInfo {
814 auto_decomposer: parameters.auto_decomposer(),
815 lwe_decomposer: parameters.lwe_decomposer(),
816 rlwe_rgsw_decomposer: parameters.rlwe_rgsw_decomposer(),
817 g_k_dlog_map,
818 embedding_factor,
819 lwe_modop,
820 rlwe_modop,
821 rlwe_nttop,
822 nand_test_vec: nand_test_vec_autog,
823 rlwe_qby4,
824 rlwe_auto_maps,
825 parameters: parameters,
826 };
827
828 BoolEvaluator {
829 pbs_info,
830 scratch_memory,
831 _phantom: PhantomData,
832 }
833 }
834
835 fn client_key(&self) -> ClientKey {
836 let sk_lwe = LweSecret::random(
837 self.pbs_info.parameters.lwe_n().0 >> 1,
838 self.pbs_info.parameters.lwe_n().0,
839 );
840 let sk_rlwe = RlweSecret::random(
841 self.pbs_info.parameters.rlwe_n().0 >> 1,
842 self.pbs_info.parameters.rlwe_n().0,
843 );
844 ClientKey { sk_rlwe, sk_lwe }
845 }
846
847 fn server_key(
848 &self,
849 client_key: &ClientKey,
850 ) -> SeededServerKey<M, BoolParameters<M::MatElement>, [u8; 32]> {
851 DefaultSecureRng::with_local_mut(|rng| {
852 let mut main_seed = [0u8; 32];
853 rng.fill_bytes(&mut main_seed);
854
855 let mut main_prng = DefaultSecureRng::new_seeded(main_seed);
856
857 let rlwe_n = self.pbs_info.parameters.rlwe_n().0;
858 let sk_rlwe = &client_key.sk_rlwe;
859 let sk_lwe = &client_key.sk_lwe;
860
861 // generate auto keys -g, g
862 let mut auto_keys = HashMap::new();
863 let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
864 let g = self.pbs_info.parameters.g() as isize;
865 for i in [g, -g] {
866 let mut gk = M::zeros(self.pbs_info.auto_decomposer.decomposition_count(), rlwe_n);
867 galois_key_gen(
868 &mut gk,
869 sk_rlwe.values(),
870 i,
871 &auto_gadget,
872 &self.pbs_info.rlwe_modop,
873 &self.pbs_info.rlwe_nttop,
874 &mut main_prng,
875 rng,
876 );
877 auto_keys.insert(i, gk);
878 }
879
880 // generate rgsw ciphertexts RGSW(si) where si is i^th LWE secret element
881 let ring_size = self.pbs_info.parameters.rlwe_n().0;
882 let rlwe_q = self.pbs_info.parameters.rlwe_q();
883 let (rlrg_d_a, rlrg_d_b) = (
884 self.pbs_info.rlwe_rgsw_decomposer.0.decomposition_count(),
885 self.pbs_info.rlwe_rgsw_decomposer.1.decomposition_count(),
886 );
887 let rlrg_gadget_a = self.pbs_info.rlwe_rgsw_decomposer.0.gadget_vector();
888 let rlrg_gadget_b = self.pbs_info.rlwe_rgsw_decomposer.1.gadget_vector();
889 let rgsw_cts = sk_lwe
890 .values()
891 .iter()
892 .map(|si| {
893 // X^{si}; assume |emebedding_factor * si| < N
894 let mut m = M::R::zeros(ring_size);
895 let si = (self.pbs_info.embedding_factor as i32) * si;
896 // dbg!(si);
897 if si < 0 {
898 // X^{-i} = X^{2N - i} = -X^{N-i}
899 m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one();
900 } else {
901 // X^{i}
902 m.as_mut()[si.abs() as usize] = M::MatElement::one();
903 }
904
905 let mut rgsw_si = M::zeros(rlrg_d_a * 2 + rlrg_d_b, ring_size);
906 secret_key_encrypt_rgsw(
907 &mut rgsw_si,
908 m.as_ref(),
909 &rlrg_gadget_a,
910 &rlrg_gadget_b,
911 sk_rlwe.values(),
912 &self.pbs_info.rlwe_modop,
913 &self.pbs_info.rlwe_nttop,
914 &mut main_prng,
915 rng,
916 );
917
918 rgsw_si
919 })
920 .collect_vec();
921
922 // LWE KSK from RLWE secret s -> LWE secret z
923 let d_lwe_gadget = self.pbs_info.lwe_decomposer.gadget_vector();
924 let mut lwe_ksk =
925 M::R::zeros(self.pbs_info.lwe_decomposer.decomposition_count() * ring_size);
926 lwe_ksk_keygen(
927 &sk_rlwe.values(),
928 &sk_lwe.values(),
929 &mut lwe_ksk,
930 &d_lwe_gadget,
931 &self.pbs_info.lwe_modop,
932 &mut main_prng,
933 rng,
934 );
935
936 SeededServerKey::from_raw(
937 auto_keys,
938 rgsw_cts,
939 lwe_ksk,
940 self.pbs_info.parameters.clone(),
941 main_seed,
942 )
943 })
944 }
945
946 fn multi_party_server_key_share(
947 &self,
948 cr_seed: [u8; 32],
949 collective_pk: &M,
950 client_key: &ClientKey,
951 ) -> CommonReferenceSeededMultiPartyServerKeyShare<M, BoolParameters<M::MatElement>, [u8; 32]>
952 {
953 DefaultSecureRng::with_local_mut(|rng| {
954 let mut main_prng = DefaultSecureRng::new_seeded(cr_seed);
955
956 let sk_rlwe = &client_key.sk_rlwe;
957 let sk_lwe = &client_key.sk_lwe;
958
959 let g = self.pbs_info.parameters.g() as isize;
960 let ring_size = self.pbs_info.parameters.rlwe_n().0;
961 let rlwe_q = self.pbs_info.parameters.rlwe_q();
962 let lwe_q = self.pbs_info.parameters.lwe_q();
963
964 let rlweq_modop = &self.pbs_info.rlwe_modop;
965 let rlweq_nttop = &self.pbs_info.rlwe_nttop;
966
967 // sanity check
968 assert!(sk_rlwe.values().len() == ring_size);
969 assert!(sk_lwe.values().len() == self.pbs_info.parameters.lwe_n().0);
970
971 // auto keys
972 let mut auto_keys = HashMap::new();
973 let auto_gadget = self.pbs_info.auto_decomposer.gadget_vector();
974 for i in [g, -g] {
975 let mut ksk_out = M::zeros(
976 self.pbs_info.auto_decomposer.decomposition_count(),
977 ring_size,
978 );
979 galois_key_gen(
980 &mut ksk_out,
981 sk_rlwe.values(),
982 i,
983 &auto_gadget,
984 rlweq_modop,
985 rlweq_nttop,
986 &mut main_prng,
987 rng,
988 );
989 auto_keys.insert(i, ksk_out);
990 }
991
992 // rgsw ciphertexts of lwe secret elements
993 let rgsw_rgsw_decomposer = self
994 .pbs_info
995 .parameters
996 .rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
997 let (rgrg_d_a, rgrg_d_b) = (
998 rgsw_rgsw_decomposer.0.decomposition_count(),
999 rgsw_rgsw_decomposer.1.decomposition_count(),
1000 );
1001 let (rgrg_gadget_a, rgrg_gadget_b) = (
1002 rgsw_rgsw_decomposer.0.gadget_vector(),
1003 rgsw_rgsw_decomposer.1.gadget_vector(),
1004 );
1005 let rgsw_cts = sk_lwe
1006 .values()
1007 .iter()
1008 .map(|si| {
1009 let mut m = M::R::zeros(ring_size);
1010 //TODO(Jay): It will be nice to have a function that returns polynomial
1011 // (monomial infact!) corresponding to secret element embedded in ring X^{2N+1}.
1012 // Save lots of mistakes where one forgest to emebed si in bigger ring.
1013 let si = *si * (self.pbs_info.embedding_factor as i32);
1014 if si < 0 {
1015 // X^{-si} = X^{2N-si} = -X^{N-si}, assuming abs(si) < N
1016 // (which it is given si is secret element)
1017 m.as_mut()[ring_size - (si.abs() as usize)] = rlwe_q.neg_one();
1018 } else {
1019 m.as_mut()[si as usize] = M::MatElement::one();
1020 }
1021
1022 // public key RGSW encryption has no part that can be seeded, unlike secret key
1023 // RGSW encryption where RLWE'_A(m) is seeded
1024 let mut out_rgsw = M::zeros(rgrg_d_a * 2 + rgrg_d_b * 2, ring_size);
1025 public_key_encrypt_rgsw(
1026 &mut out_rgsw,
1027 &m.as_ref(),
1028 collective_pk,
1029 &rgrg_gadget_a,
1030 &rgrg_gadget_b,
1031 rlweq_modop,
1032 rlweq_nttop,
1033 rng,
1034 );
1035
1036 out_rgsw
1037 })
1038 .collect_vec();
1039
1040 // LWE ksk
1041 let mut lwe_ksk =
1042 M::R::zeros(self.pbs_info.lwe_decomposer.decomposition_count() * ring_size);
1043 let lwe_modop = &self.pbs_info.lwe_modop;
1044 let d_lwe_gadget_vec = self.pbs_info.lwe_decomposer.gadget_vector();
1045 lwe_ksk_keygen(
1046 sk_rlwe.values(),
1047 sk_lwe.values(),
1048 &mut lwe_ksk,
1049 &d_lwe_gadget_vec,
1050 lwe_modop,
1051 &mut main_prng,
1052 rng,
1053 );
1054
1055 CommonReferenceSeededMultiPartyServerKeyShare {
1056 auto_keys,
1057 rgsw_cts,
1058 lwe_ksk,
1059 cr_seed,
1060 parameters: self.pbs_info.parameters.clone(),
1061 }
1062 })
1063 }
1064
1065 fn multi_party_public_key_share(
1066 &self,
1067 cr_seed: [u8; 32],
1068 client_key: &ClientKey,
1069 ) -> CommonReferenceSeededCollectivePublicKeyShare<
1070 <M as Matrix>::R,
1071 [u8; 32],
1072 BoolParameters<<M as Matrix>::MatElement>,
1073 > {
1074 DefaultSecureRng::with_local_mut(|rng| {
1075 let mut share_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0);
1076 let modop = &self.pbs_info.rlwe_modop;
1077 let nttop = &self.pbs_info.rlwe_nttop;
1078 let mut main_prng = DefaultSecureRng::new_seeded(cr_seed);
1079 public_key_share(
1080 &mut share_out,
1081 client_key.sk_rlwe.values(),
1082 modop,
1083 nttop,
1084 &mut main_prng,
1085 rng,
1086 );
1087
1088 CommonReferenceSeededCollectivePublicKeyShare {
1089 share: share_out,
1090 cr_seed: cr_seed,
1091 parameters: self.pbs_info.parameters.clone(),
1092 }
1093 })
1094 }
1095
1096 fn multi_party_decryption_share(
1097 &self,
1098 lwe_ct: &M::R,
1099 client_key: &ClientKey,
1100 ) -> MultiPartyDecryptionShare<<M as Matrix>::MatElement> {
1101 assert!(lwe_ct.as_ref().len() == self.pbs_info.parameters.rlwe_n().0 + 1);
1102 let modop = &self.pbs_info.rlwe_modop;
1103 let mut neg_s = M::R::try_convert_from(
1104 client_key.sk_rlwe.values(),
1105 &self.pbs_info.parameters.rlwe_q(),
1106 );
1107 modop.elwise_neg_mut(neg_s.as_mut());
1108
1109 let mut neg_sa = M::MatElement::zero();
1110 izip!(lwe_ct.as_ref().iter().skip(1), neg_s.as_ref().iter()).for_each(|(ai, nsi)| {
1111 neg_sa = modop.add(&neg_sa, &modop.mul(ai, nsi));
1112 });
1113
1114 let e = DefaultSecureRng::with_local_mut(|rng| {
1115 let mut e =
1116 RandomGaussianElementInModulus::random(rng, self.pbs_info.parameters.rlwe_q());
1117 e
1118 });
1119 let share = modop.add(&neg_sa, &e);
1120
1121 MultiPartyDecryptionShare { share }
1122 }
1123
1124 pub(crate) fn multi_party_decrypt(
1125 &self,
1126 shares: &[MultiPartyDecryptionShare<M::MatElement>],
1127 lwe_ct: &M::R,
1128 ) -> bool {
1129 let modop = &self.pbs_info.rlwe_modop;
1130 let mut sum_a = M::MatElement::zero();
1131 shares
1132 .iter()
1133 .for_each(|share_i| sum_a = modop.add(&sum_a, &share_i.share));
1134
1135 let encoded_m = modop.add(&lwe_ct.as_ref()[0], &sum_a);
1136 self.pbs_info.parameters.rlwe_q().decode(encoded_m)
1137 }
1138
1139 /// First encrypt as RLWE(m) with m as constant polynomial and extract it as
1140 /// LWE ciphertext
1141 pub(crate) fn pk_encrypt(&self, pk: &M, m: bool) -> M::R {
1142 DefaultSecureRng::with_local_mut(|rng| {
1143 let modop = &self.pbs_info.rlwe_modop;
1144 let nttop = &self.pbs_info.rlwe_nttop;
1145
1146 // RLWE(0)
1147 // sample ephemeral key u
1148 let ring_size = self.pbs_info.parameters.rlwe_n().0;
1149 let mut u = vec![0i32; ring_size];
1150 fill_random_ternary_secret_with_hamming_weight(u.as_mut(), ring_size >> 1, rng);
1151 let mut u = M::R::try_convert_from(&u, &self.pbs_info.parameters.rlwe_q());
1152 nttop.forward(u.as_mut());
1153
1154 let mut ua = M::R::zeros(ring_size);
1155 ua.as_mut().copy_from_slice(pk.get_row_slice(0));
1156 let mut ub = M::R::zeros(ring_size);
1157 ub.as_mut().copy_from_slice(pk.get_row_slice(1));
1158
1159 // a*u
1160 nttop.forward(ua.as_mut());
1161 modop.elwise_mul_mut(ua.as_mut(), u.as_ref());
1162 nttop.backward(ua.as_mut());
1163
1164 // b*u
1165 nttop.forward(ub.as_mut());
1166 modop.elwise_mul_mut(ub.as_mut(), u.as_ref());
1167 nttop.backward(ub.as_mut());
1168
1169 let mut rlwe = M::zeros(2, ring_size);
1170 // sample error
1171 rlwe.iter_rows_mut().for_each(|ri| {
1172 RandomFillGaussianInModulus::random_fill(
1173 rng,
1174 &self.pbs_info.parameters.rlwe_q(),
1175 ri.as_mut(),
1176 );
1177 });
1178
1179 // a*u + e0
1180 modop.elwise_add_mut(rlwe.get_row_mut(0), ua.as_ref());
1181 // b*u + e1
1182 modop.elwise_add_mut(rlwe.get_row_mut(1), ub.as_ref());
1183
1184 //FIXME(Jay): Figure out a way to get Q/8 form modulus
1185 let m = if m {
1186 // Q/8
1187 self.pbs_info.rlwe_q().true_el()
1188 } else {
1189 // -Q/8
1190 self.pbs_info.rlwe_q().false_el()
1191 };
1192
1193 // b*u + e1 + m, where m is constant polynomial
1194 rlwe.set(1, 0, modop.add(rlwe.get(1, 0), &m));
1195
1196 // sample extract index 0
1197 let mut lwe_out = M::R::zeros(ring_size + 1);
1198 sample_extract(&mut lwe_out, &rlwe, modop, 0);
1199
1200 lwe_out
1201 })
1202 }
1203
1204 /// TODO(Jay): Fetch client key from thread local
1205 pub fn sk_encrypt(&self, m: bool, client_key: &ClientKey) -> M::R {
1206 //FIXME(Jay): Figure out a way to get Q/8 form modulus
1207 let m = if m {
1208 // Q/8
1209 self.pbs_info.rlwe_q().true_el()
1210 } else {
1211 // -Q/8
1212 self.pbs_info.rlwe_q().false_el()
1213 };
1214
1215 DefaultSecureRng::with_local_mut(|rng| {
1216 let mut lwe_out = M::R::zeros(self.pbs_info.parameters.rlwe_n().0 + 1);
1217 encrypt_lwe(
1218 &mut lwe_out,
1219 &m,
1220 client_key.sk_rlwe.values(),
1221 &self.pbs_info.rlwe_modop,
1222 rng,
1223 );
1224 lwe_out
1225 })
1226 }
1227
1228 pub fn sk_decrypt(&self, lwe_ct: &M::R, client_key: &ClientKey) -> bool {
1229 let m = decrypt_lwe(
1230 lwe_ct,
1231 client_key.sk_rlwe.values(),
1232 &self.pbs_info.rlwe_modop,
1233 );
1234 self.pbs_info.rlwe_q().decode(m)
1235 }
1236
1237 fn aggregate_multi_party_server_key_shares<S>(
1238 &self,
1239 shares: &[CommonReferenceSeededMultiPartyServerKeyShare<
1240 M,
1241 BoolParameters<M::MatElement>,
1242 S,
1243 >],
1244 ) -> SeededMultiPartyServerKey<M, S, BoolParameters<M::MatElement>>
1245 where
1246 S: PartialEq + Clone,
1247 M: Clone,
1248 {
1249 assert!(shares.len() > 0);
1250 let parameters = shares[0].parameters.clone();
1251 let cr_seed = &shares[0].cr_seed;
1252
1253 let rlwe_n = parameters.rlwe_n().0;
1254 let g = parameters.g() as isize;
1255 let rlwe_q = parameters.rlwe_q();
1256 let lwe_q = parameters.lwe_q();
1257
1258 // sanity checks
1259 shares.iter().skip(1).for_each(|s| {
1260 assert!(s.parameters == parameters);
1261 assert!(&s.cr_seed == cr_seed);
1262 });
1263
1264 let rlweq_modop = &self.pbs_info.rlwe_modop;
1265 let rlweq_nttop = &self.pbs_info.rlwe_nttop;
1266
1267 // auto keys
1268 let mut auto_keys = HashMap::new();
1269 for i in [g, -g] {
1270 let mut key = M::zeros(parameters.auto_decomposition_count().0, rlwe_n);
1271
1272 shares.iter().for_each(|s| {
1273 let auto_key_share_i = s.auto_keys.get(&i).expect("Auto key {i} missing");
1274 assert!(
1275 auto_key_share_i.dimension()
1276 == (parameters.auto_decomposition_count().0, rlwe_n)
1277 );
1278 izip!(key.iter_rows_mut(), auto_key_share_i.iter_rows()).for_each(
1279 |(partb_out, partb_share)| {
1280 rlweq_modop.elwise_add_mut(partb_out.as_mut(), partb_share.as_ref());
1281 },
1282 );
1283 });
1284
1285 auto_keys.insert(i, key);
1286 }
1287
1288 // rgsw ciphertext (most expensive part!)
1289 let lwe_n = parameters.lwe_n().0;
1290 let rgsw_by_rgsw_decomposer =
1291 parameters.rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
1292 let mut scratch_matrix = M::zeros(
1293 std::cmp::max(
1294 rgsw_by_rgsw_decomposer.a().decomposition_count(),
1295 rgsw_by_rgsw_decomposer.b().decomposition_count(),
1296 ) + (rgsw_by_rgsw_decomposer.a().decomposition_count() * 2
1297 + rgsw_by_rgsw_decomposer.b().decomposition_count() * 2),
1298 rlwe_n,
1299 );
1300
1301 let mut tmp_rgsw =
1302 RgswCiphertext::<M, _>::empty(rlwe_n, &rgsw_by_rgsw_decomposer, rlwe_q.clone()).data;
1303 let rgsw_cts = (0..lwe_n).into_iter().map(|index| {
1304 // copy over rgsw ciphertext for index^th secret element from first share and
1305 // treat it as accumulating rgsw ciphertext
1306 let mut rgsw_i = shares[0].rgsw_cts[index].clone();
1307
1308 shares.iter().skip(1).for_each(|si| {
1309 // copy over si's RGSW[index] ciphertext and send to evaluation domain
1310 izip!(tmp_rgsw.iter_rows_mut(), si.rgsw_cts[index].iter_rows()).for_each(
1311 |(to_ri, from_ri)| {
1312 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
1313 rlweq_nttop.forward(to_ri.as_mut())
1314 },
1315 );
1316
1317 rgsw_by_rgsw_inplace(
1318 &mut rgsw_i,
1319 &tmp_rgsw,
1320 &rgsw_by_rgsw_decomposer,
1321 &mut scratch_matrix,
1322 rlweq_nttop,
1323 rlweq_modop,
1324 );
1325 });
1326
1327 rgsw_i
1328 });
1329 // d_a and d_b may differ for RGSWxRGSW multiplication and RLWExRGSW
1330 // multiplication. After this point RGSW ciphertexts will only be used for
1331 // RLWExRGSW multiplication (in blind rotation). Thus we drop any additional
1332 // RLWE ciphertexts in RGSW ciphertexts after RGSw x RGSW multiplication
1333 let (rgswrgsw_d_a, rgswrgsw_d_b) = self.pbs_info.parameters.rgsw_rgsw_decomposition_count();
1334 let (rlrg_d_a, rlrg_d_b) = self.pbs_info.parameters.rlwe_rgsw_decomposition_count();
1335 let rgsw_ct_rows_in = rgswrgsw_d_a.0 * 2 + rgswrgsw_d_b.0 * 2;
1336 let rgsw_ct_rows_out = rlrg_d_a.0 * 2 + rlrg_d_b.0 * 2;
1337 assert!(rgswrgsw_d_a.0 >= rlrg_d_a.0, "RGSWxRGSW part A decomposition count {} must be >= RLWExRGSW part A decomposition count {}", rgswrgsw_d_a.0 , rlrg_d_a.0);
1338 assert!(rgswrgsw_d_b.0 >= rlrg_d_b.0, "RGSWxRGSW part B decomposition count {} must be >= RLWExRGSW part B decomposition count {}", rgswrgsw_d_b.0 , rlrg_d_b.0);
1339 let rgsw_cts = rgsw_cts
1340 .map(|ct_i_in| {
1341 assert!(ct_i_in.dimension() == (rgsw_ct_rows_in, rlwe_n));
1342 let mut reduced_ct_i_out = M::zeros(rgsw_ct_rows_out, rlwe_n);
1343
1344 // RLWE'(-sm) part A
1345 izip!(
1346 reduced_ct_i_out.iter_rows_mut().take(rlrg_d_a.0),
1347 ct_i_in
1348 .iter_rows()
1349 .skip(rgswrgsw_d_a.0 - rlrg_d_a.0)
1350 .take(rlrg_d_a.0)
1351 )
1352 .for_each(|(to_ri, from_ri)| {
1353 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
1354 });
1355
1356 // RLWE'(-sm) part B
1357 izip!(
1358 reduced_ct_i_out
1359 .iter_rows_mut()
1360 .skip(rlrg_d_a.0)
1361 .take(rlrg_d_a.0),
1362 ct_i_in
1363 .iter_rows()
1364 .skip(rgswrgsw_d_a.0 + (rgswrgsw_d_a.0 - rlrg_d_a.0))
1365 .take(rlrg_d_a.0)
1366 )
1367 .for_each(|(to_ri, from_ri)| {
1368 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
1369 });
1370
1371 // RLWE'(m) Part A
1372 izip!(
1373 reduced_ct_i_out
1374 .iter_rows_mut()
1375 .skip(rlrg_d_a.0 * 2)
1376 .take(rlrg_d_b.0),
1377 ct_i_in
1378 .iter_rows()
1379 .skip(rgswrgsw_d_a.0 * 2 + (rgswrgsw_d_b.0 - rlrg_d_b.0))
1380 .take(rlrg_d_b.0)
1381 )
1382 .for_each(|(to_ri, from_ri)| {
1383 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
1384 });
1385
1386 // RLWE'(m) Part B
1387 izip!(
1388 reduced_ct_i_out
1389 .iter_rows_mut()
1390 .skip(rlrg_d_a.0 * 2 + rlrg_d_b.0)
1391 .take(rlrg_d_b.0),
1392 ct_i_in
1393 .iter_rows()
1394 .skip(rgswrgsw_d_a.0 * 2 + rgswrgsw_d_b.0 + (rgswrgsw_d_b.0 - rlrg_d_b.0))
1395 .take(rlrg_d_b.0)
1396 )
1397 .for_each(|(to_ri, from_ri)| {
1398 to_ri.as_mut().copy_from_slice(from_ri.as_ref());
1399 });
1400
1401 reduced_ct_i_out
1402 })
1403 .collect_vec();
1404
1405 // LWE ksks
1406 let mut lwe_ksk = M::R::zeros(rlwe_n * parameters.lwe_decomposition_count().0);
1407 let lweq_modop = &self.pbs_info.lwe_modop;
1408 shares.iter().for_each(|si| {
1409 assert!(si.lwe_ksk.as_ref().len() == rlwe_n * parameters.lwe_decomposition_count().0);
1410 lweq_modop.elwise_add_mut(lwe_ksk.as_mut(), si.lwe_ksk.as_ref())
1411 });
1412
1413 SeededMultiPartyServerKey {
1414 rgsw_cts,
1415 auto_keys,
1416 lwe_ksk,
1417 cr_seed: cr_seed.clone(),
1418 parameters: parameters,
1419 }
1420 }
1421
1422 // TODO(Jay): scratch spaces must be thread local. Don't pass them as arguments
1423 pub fn nand(
1424 &mut self,
1425 c0: &M::R,
1426 c1: &M::R,
1427 server_key: &ServerKeyEvaluationDomain<M, DefaultSecureRng, NttOp>,
1428 ) -> M::R {
1429 let mut c_out = M::R::zeros(c0.as_ref().len());
1430 let modop = &self.pbs_info.rlwe_modop;
1431 izip!(
1432 c_out.as_mut().iter_mut(),
1433 c0.as_ref().iter(),
1434 c1.as_ref().iter()
1435 )
1436 .for_each(|(o, i0, i1)| {
1437 *o = modop.add(i0, i1);
1438 });
1439 // +Q/4
1440 c_out.as_mut()[0] = modop.add(&c_out.as_ref()[0], &self.pbs_info.rlwe_qby4);
1441
1442 // PBS
1443 pbs(
1444 &self.pbs_info,
1445 &self.pbs_info.nand_test_vec,
1446 &mut c_out,
1447 server_key,
1448 &mut self.scratch_memory.lwe_vector,
1449 &mut self.scratch_memory.decomposition_matrix,
1450 );
1451
1452 c_out
1453 }
1454}
1455
1456/// LMKCY+ Blind rotation
1457///
1458/// gk_to_si: [g^0, ..., g^{q/2-1}, -g^0, -g^1, .., -g^{q/2-1}]
1459fn blind_rotation<
1460 MT: IsTrivial + MatrixMut,
1461 Mmut: MatrixMut<MatElement = MT::MatElement> + Matrix,
1462 D: Decomposer<Element = MT::MatElement>,
1463 NttOp: Ntt<Element = MT::MatElement>,
1464 ModOp: ArithmeticOps<Element = MT::MatElement> + VectorOps<Element = MT::MatElement>,
1465 K: PbsKey<M = Mmut>,
1466 P: PbsInfo<Element = MT::MatElement>,
1467>(
1468 trivial_rlwe_test_poly: &mut MT,
1469 scratch_matrix: &mut Mmut,
1470 g: isize,
1471 w: usize,
1472 q: usize,
1473 gk_to_si: &[Vec<usize>],
1474 rlwe_rgsw_decomposer: &(D, D),
1475 auto_decomposer: &D,
1476 ntt_op: &NttOp,
1477 mod_op: &ModOp,
1478 parameters: &P,
1479 pbs_key: &K,
1480) where
1481 <Mmut as Matrix>::R: RowMut,
1482 Mmut::MatElement: Copy + Zero,
1483 <MT as Matrix>::R: RowMut,
1484{
1485 let q_by_2 = q / 2;
1486
1487 // -(g^k)
1488 for i in (1..q_by_2).rev() {
1489 gk_to_si[q_by_2 + i].iter().for_each(|s_index| {
1490 rlwe_by_rgsw(
1491 trivial_rlwe_test_poly,
1492 pbs_key.rgsw_ct_lwe_si(*s_index),
1493 scratch_matrix,
1494 rlwe_rgsw_decomposer,
1495 ntt_op,
1496 mod_op,
1497 );
1498 });
1499
1500 let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(g);
1501 galois_auto(
1502 trivial_rlwe_test_poly,
1503 pbs_key.galois_key_for_auto(g),
1504 scratch_matrix,
1505 &auto_map_index,
1506 &auto_map_sign,
1507 mod_op,
1508 ntt_op,
1509 auto_decomposer,
1510 );
1511 }
1512
1513 // -(g^0)
1514 gk_to_si[q_by_2].iter().for_each(|s_index| {
1515 rlwe_by_rgsw(
1516 trivial_rlwe_test_poly,
1517 pbs_key.rgsw_ct_lwe_si(*s_index),
1518 scratch_matrix,
1519 rlwe_rgsw_decomposer,
1520 ntt_op,
1521 mod_op,
1522 );
1523 });
1524 let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(-g);
1525 galois_auto(
1526 trivial_rlwe_test_poly,
1527 pbs_key.galois_key_for_auto(-g),
1528 scratch_matrix,
1529 &auto_map_index,
1530 &auto_map_sign,
1531 mod_op,
1532 ntt_op,
1533 auto_decomposer,
1534 );
1535
1536 // +(g^k)
1537 for i in (1..q_by_2).rev() {
1538 gk_to_si[i].iter().for_each(|s_index| {
1539 rlwe_by_rgsw(
1540 trivial_rlwe_test_poly,
1541 pbs_key.rgsw_ct_lwe_si(*s_index),
1542 scratch_matrix,
1543 rlwe_rgsw_decomposer,
1544 ntt_op,
1545 mod_op,
1546 );
1547 });
1548
1549 let (auto_map_index, auto_map_sign) = parameters.rlwe_auto_map(g);
1550 galois_auto(
1551 trivial_rlwe_test_poly,
1552 pbs_key.galois_key_for_auto(g),
1553 scratch_matrix,
1554 &auto_map_index,
1555 &auto_map_sign,
1556 mod_op,
1557 ntt_op,
1558 auto_decomposer,
1559 );
1560 }
1561
1562 // +(g^0)
1563 gk_to_si[0].iter().for_each(|s_index| {
1564 rlwe_by_rgsw(
1565 trivial_rlwe_test_poly,
1566 pbs_key.rgsw_ct_lwe_si(gk_to_si[q_by_2][*s_index]),
1567 scratch_matrix,
1568 rlwe_rgsw_decomposer,
1569 ntt_op,
1570 mod_op,
1571 );
1572 });
1573}
1574
1575/// - Mod down
1576/// - key switching
1577/// - mod down
1578/// - blind rotate
1579fn pbs<
1580 M: Matrix + MatrixMut + MatrixEntity,
1581 P: PbsInfo<Element = M::MatElement>,
1582 K: PbsKey<M = M>,
1583>(
1584 pbs_info: &P,
1585 test_vec: &M::R,
1586 lwe_in: &mut M::R,
1587 pbs_key: &K,
1588 scratch_lwe_vec: &mut M::R,
1589 scratch_blind_rotate_matrix: &mut M,
1590) where
1591 <M as Matrix>::R: RowMut,
1592 M::MatElement: PrimInt + ToPrimitive + FromPrimitive + One + Copy + Zero + Display,
1593{
1594 let rlwe_q = pbs_info.rlwe_q();
1595 let lwe_q = pbs_info.lwe_q();
1596 let br_q = pbs_info.br_q();
1597 let rlwe_qf64 = rlwe_q.q_as_f64().unwrap();
1598 let lwe_qf64 = lwe_q.q_as_f64().unwrap();
1599 let br_qf64 = br_q.to_f64().unwrap();
1600 let rlwe_n = pbs_info.rlwe_n();
1601
1602 // PBSTracer::with_local_mut(|t| {
1603 // let out = lwe_in
1604 // .as_ref()
1605 // .iter()
1606 // .map(|v| v.to_u64().unwrap())
1607 // .collect_vec();
1608 // t.ct_rlwe_q_mod = out;
1609 // });
1610
1611 // moddown Q -> Q_ks
1612 lwe_in.as_mut().iter_mut().for_each(|v| {
1613 *v =
1614 M::MatElement::from_f64(((v.to_f64().unwrap() * lwe_qf64) / rlwe_qf64).round()).unwrap()
1615 });
1616
1617 // PBSTracer::with_local_mut(|t| {
1618 // let out = lwe_in
1619 // .as_ref()
1620 // .iter()
1621 // .map(|v| v.to_u64().unwrap())
1622 // .collect_vec();
1623 // t.ct_lwe_q_mod = out;
1624 // });
1625
1626 // key switch RLWE secret to LWE secret
1627 scratch_lwe_vec.as_mut().fill(M::MatElement::zero());
1628 lwe_key_switch(
1629 scratch_lwe_vec,
1630 lwe_in,
1631 pbs_key.lwe_ksk(),
1632 pbs_info.modop_lweq(),
1633 pbs_info.lwe_decomposer(),
1634 );
1635
1636 // PBSTracer::with_local_mut(|t| {
1637 // let out = scratch_lwe_vec
1638 // .as_ref()
1639 // .iter()
1640 // .map(|v| v.to_u64().unwrap())
1641 // .collect_vec();
1642 // t.ct_lwe_q_mod_after_ksk = out;
1643 // });
1644
1645 // odd mowdown Q_ks -> q
1646 let g_k_dlog_map = pbs_info.g_k_dlog_map();
1647 let mut g_k_si = vec![vec![]; br_q];
1648 scratch_lwe_vec
1649 .as_ref()
1650 .iter()
1651 .skip(1)
1652 .enumerate()
1653 .for_each(|(index, v)| {
1654 let odd_v = mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64);
1655 let k = g_k_dlog_map[odd_v];
1656 g_k_si[k].push(index);
1657 });
1658
1659 // PBSTracer::with_local_mut(|t| {
1660 // let out = scratch_lwe_vec
1661 // .as_ref()
1662 // .iter()
1663 // .map(|v| mod_switch_odd(v.to_f64().unwrap(), lwe_qf64, br_qf64) as
1664 // u64) .collect_vec();
1665 // t.ct_br_q_mod = out;
1666 // });
1667
1668 // handle b and set trivial test RLWE
1669 let g = pbs_info.g() as usize;
1670 let g_times_b = (g * mod_switch_odd(
1671 scratch_lwe_vec.as_ref()[0].to_f64().unwrap(),
1672 lwe_qf64,
1673 br_qf64,
1674 )) % (br_q);
1675 // v = (v(X) * X^{g*b}) mod X^{q/2}+1
1676 let br_qby2 = br_q / 2;
1677 let mut gb_monomial_sign = true;
1678 let mut gb_monomial_exp = g_times_b;
1679 // X^{g*b} mod X^{q/2}+1
1680 if gb_monomial_exp > br_qby2 {
1681 gb_monomial_exp -= br_qby2;
1682 gb_monomial_sign = false
1683 }
1684 // monomial mul
1685 let mut trivial_rlwe_test_poly = RlweCiphertext::<_, DefaultSecureRng> {
1686 data: M::zeros(2, rlwe_n),
1687 is_trivial: true,
1688 _phatom: PhantomData,
1689 };
1690 if pbs_info.embedding_factor() == 1 {
1691 monomial_mul(
1692 test_vec.as_ref(),
1693 trivial_rlwe_test_poly.get_row_mut(1).as_mut(),
1694 gb_monomial_exp,
1695 gb_monomial_sign,
1696 br_qby2,
1697 pbs_info.modop_rlweq(),
1698 );
1699 } else {
1700 // use lwe_in to store the `t = v(X) * X^{g*2} mod X^{q/2}+1` temporarily. This
1701 // works because q/2 <= N (where N is lwe_in LWE dimension) always.
1702 monomial_mul(
1703 test_vec.as_ref(),
1704 &mut lwe_in.as_mut()[..br_qby2],
1705 gb_monomial_exp,
1706 gb_monomial_sign,
1707 br_qby2,
1708 pbs_info.modop_rlweq(),
1709 );
1710
1711 // emebed poly `t` in ring X^{q/2}+1 inside the bigger ring X^{N}+1
1712 let embed_factor = pbs_info.embedding_factor();
1713 let partb_trivial_rlwe = trivial_rlwe_test_poly.get_row_mut(1);
1714 lwe_in.as_ref()[..br_qby2]
1715 .iter()
1716 .enumerate()
1717 .for_each(|(index, v)| {
1718 partb_trivial_rlwe[embed_factor * index] = *v;
1719 });
1720 }
1721
1722 // blind rotate
1723 blind_rotation(
1724 &mut trivial_rlwe_test_poly,
1725 scratch_blind_rotate_matrix,
1726 pbs_info.g(),
1727 1,
1728 br_q,
1729 &g_k_si,
1730 pbs_info.rlwe_rgsw_decomposer(),
1731 pbs_info.auto_decomposer(),
1732 pbs_info.nttop_rlweq(),
1733 pbs_info.modop_rlweq(),
1734 pbs_info,
1735 pbs_key,
1736 );
1737
1738 // ClientKey::with_local(|ck| {
1739 // let ring_size = parameters.rlwe_n();
1740 // let mut rlwe_ct = vec![vec![0u64; ring_size]; 2];
1741 // izip!(
1742 // rlwe_ct[0].iter_mut(),
1743 // trivial_rlwe_test_poly.0.get_row_slice(0)
1744 // )
1745 // .for_each(|(t, f)| {
1746 // *t = f.to_u64().unwrap();
1747 // });
1748 // izip!(
1749 // rlwe_ct[1].iter_mut(),
1750 // trivial_rlwe_test_poly.0.get_row_slice(1)
1751 // )
1752 // .for_each(|(t, f)| {
1753 // *t = f.to_u64().unwrap();
1754 // });
1755 // let mut m_out = vec![vec![0u64; ring_size]];
1756 // let modop = ModularOpsU64::new(rlwe_q.to_u64().unwrap());
1757 // let nttop = NttBackendU64::new(rlwe_q.to_u64().unwrap(), ring_size);
1758 // decrypt_rlwe(&rlwe_ct, ck.sk_rlwe.values(), &mut m_out, &nttop, &modop);
1759
1760 // println!("RLWE post PBS message: {:?}", m_out[0]);
1761 // });
1762
1763 // sample extract
1764 sample_extract(lwe_in, &trivial_rlwe_test_poly, pbs_info.modop_rlweq(), 0);
1765}
1766
1767fn mod_switch_odd(v: f64, from_q: f64, to_q: f64) -> usize {
1768 let odd_v = (((v * to_q) / (from_q)).floor()).to_usize().unwrap();
1769 //TODO(Jay): check correctness of this
1770 odd_v + ((odd_v & 1) ^ 1)
1771}
1772
1773// TODO(Jay): Add tests for sample extract
1774fn sample_extract<M: Matrix + MatrixMut, ModOp: ArithmeticOps<Element = M::MatElement>>(
1775 lwe_out: &mut M::R,
1776 rlwe_in: &M,
1777 mod_op: &ModOp,
1778 index: usize,
1779) where
1780 <M as Matrix>::R: RowMut,
1781 M::MatElement: Copy,
1782{
1783 let ring_size = rlwe_in.dimension().1;
1784
1785 // index..=0
1786 let to = &mut lwe_out.as_mut()[1..];
1787 let from = rlwe_in.get_row_slice(0);
1788 for i in 0..index + 1 {
1789 to[i] = from[index - i];
1790 }
1791
1792 // -(N..index)
1793 for i in index + 1..ring_size {
1794 to[i] = mod_op.neg(&from[ring_size + index - i]);
1795 }
1796
1797 // set b
1798 lwe_out.as_mut()[0] = *rlwe_in.get(1, index);
1799}
1800
1801/// TODO(Jay): Write tests for monomial mul
1802fn monomial_mul<El, ModOp: ArithmeticOps<Element = El>>(
1803 p_in: &[El],
1804 p_out: &mut [El],
1805 mon_exp: usize,
1806 mon_sign: bool,
1807 ring_size: usize,
1808 mod_op: &ModOp,
1809) where
1810 El: Copy,
1811{
1812 debug_assert!(p_in.as_ref().len() == ring_size);
1813 debug_assert!(p_in.as_ref().len() == p_out.as_ref().len());
1814 debug_assert!(mon_exp < ring_size);
1815
1816 p_in.as_ref().iter().enumerate().for_each(|(index, v)| {
1817 let mut to_index = index + mon_exp;
1818 let mut to_sign = mon_sign;
1819 if to_index >= ring_size {
1820 to_index = to_index - ring_size;
1821 to_sign = !to_sign;
1822 }
1823
1824 if !to_sign {
1825 p_out.as_mut()[to_index] = mod_op.neg(v);
1826 } else {
1827 p_out.as_mut()[to_index] = *v;
1828 }
1829 });
1830}
1831
1832thread_local! {
1833 static PBS_TRACER: RefCell<PBSTracer<Vec<Vec<u64>>>> =
1834RefCell::new(PBSTracer::default()); }
1835
1836#[derive(Default)]
1837struct PBSTracer<M>
1838where
1839 M: Matrix + Default,
1840{
1841 pub(crate) ct_rlwe_q_mod: M::R,
1842 pub(crate) ct_lwe_q_mod: M::R,
1843 pub(crate) ct_lwe_q_mod_after_ksk: M::R,
1844 pub(crate) ct_br_q_mod: Vec<u64>,
1845}
1846
1847impl PBSTracer<Vec<Vec<u64>>> {
1848 fn trace(&self, parameters: &BoolParameters<u64>, sk_lwe: &[i32], sk_rlwe: &[i32]) {
1849 assert!(parameters.rlwe_n().0 == sk_rlwe.len());
1850 assert!(parameters.lwe_n().0 == sk_lwe.len());
1851
1852 let modop_rlweq = ModularOpsU64::new(*parameters.rlwe_q());
1853 // noise after mod down Q -> Q_ks
1854 let m_back0 = decrypt_lwe(&self.ct_rlwe_q_mod, sk_rlwe, &modop_rlweq);
1855
1856 let modop_lweq = ModularOpsU64::<CiphertextModulus<u64>>::new(*parameters.lwe_q());
1857 // noise after mod down Q -> Q_ks
1858 let m_back1 = decrypt_lwe(&self.ct_lwe_q_mod, sk_rlwe, &modop_lweq);
1859 // noise after key switch from RLWE -> LWE
1860 let m_back2 = decrypt_lwe(&self.ct_lwe_q_mod_after_ksk, sk_lwe, &modop_lweq);
1861
1862 // noise after mod down odd from Q_ks -> q
1863 let modop_br_q = ModularOpsU64::<u64>::new(*parameters.br_q() as u64);
1864 let m_back3 = decrypt_lwe(&self.ct_br_q_mod, sk_lwe, &modop_br_q);
1865
1866 println!(
1867 "
1868 M initial mod Q: {m_back0},
1869 M after mod down Q -> Q_ks: {m_back1},
1870 M after key switch from RLWE -> LWE: {m_back2},
1871 M after mod dwon Q_ks -> q: {m_back3}
1872 "
1873 );
1874 }
1875}
1876
1877impl WithLocal for PBSTracer<Vec<Vec<u64>>> {
1878 fn with_local<F, R>(func: F) -> R
1879 where
1880 F: Fn(&Self) -> R,
1881 {
1882 PBS_TRACER.with_borrow(|t| func(t))
1883 }
1884
1885 fn with_local_mut<F, R>(func: F) -> R
1886 where
1887 F: Fn(&mut Self) -> R,
1888 {
1889 PBS_TRACER.with_borrow_mut(|t| func(t))
1890 }
1891}
1892
1893#[cfg(test)]
1894mod tests {
1895 use rand::{thread_rng, Rng};
1896 use rand_distr::Uniform;
1897
1898 use crate::{
1899 backend::{GetModulus, ModInit, ModularOpsU64, WordSizeModulus},
1900 bool,
1901 ntt::NttBackendU64,
1902 random::{RandomElementInModulus, DEFAULT_RNG},
1903 rgsw::{
1904 self, measure_noise, public_key_encrypt_rlwe, secret_key_encrypt_rlwe,
1905 tests::{_measure_noise_rgsw, _sk_encrypt_rlwe},
1906 RgswCiphertext, RgswCiphertextEvaluationDomain, SeededRgswCiphertext,
1907 SeededRlweCiphertext,
1908 },
1909 utils::{negacyclic_mul, Stats},
1910 };
1911
1912 use super::*;
1913
1914 #[test]
1915 fn bool_encrypt_decrypt_works() {
1916 let bool_evaluator = BoolEvaluator::<
1917 Vec<Vec<u64>>,
1918 NttBackendU64,
1919 ModularOpsU64<CiphertextModulus<u64>>,
1920 ModularOpsU64<CiphertextModulus<u64>>,
1921 >::new(SP_BOOL_PARAMS);
1922 let client_key = bool_evaluator.client_key();
1923
1924 let mut m = true;
1925 for _ in 0..1000 {
1926 let lwe_ct = bool_evaluator.sk_encrypt(m, &client_key);
1927 let m_back = bool_evaluator.sk_decrypt(&lwe_ct, &client_key);
1928 assert_eq!(m, m_back);
1929 m = !m;
1930 }
1931 }
1932
1933 #[test]
1934 fn bool_nand() {
1935 DefaultSecureRng::with_local_mut(|r| {
1936 let rng = DefaultSecureRng::new_seeded([19u8; 32]);
1937 *r = rng;
1938 });
1939
1940 // let mog = WordSizeModulus::<CiphertextModulus<u64>>::new(12u64);
1941
1942 let mut bool_evaluator = BoolEvaluator::<
1943 Vec<Vec<u64>>,
1944 NttBackendU64,
1945 ModularOpsU64<CiphertextModulus<u64>>,
1946 ModularOpsU64<CiphertextModulus<u64>>,
1947 >::new(SP_BOOL_PARAMS);
1948
1949 // println!("{:?}", bool_evaluator.nand_test_vec);
1950 let client_key = bool_evaluator.client_key();
1951 let seeded_server_key = bool_evaluator.server_key(&client_key);
1952 let server_key_eval_domain =
1953 ServerKeyEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
1954 &seeded_server_key,
1955 );
1956
1957 let mut m0 = false;
1958 let mut m1 = true;
1959 let mut ct0 = bool_evaluator.sk_encrypt(m0, &client_key);
1960 let mut ct1 = bool_evaluator.sk_encrypt(m1, &client_key);
1961
1962 for _ in 0..1000 {
1963 let ct_back = bool_evaluator.nand(&ct0, &ct1, &server_key_eval_domain);
1964
1965 let m_out = !(m0 && m1);
1966
1967 // Trace and measure PBS noise
1968 {
1969 let noise0 = {
1970 let ideal = if m0 {
1971 bool_evaluator.pbs_info.parameters.rlwe_q().true_el()
1972 } else {
1973 bool_evaluator.pbs_info.parameters.rlwe_q().false_el()
1974 };
1975 let n = measure_noise_lwe(
1976 &ct0,
1977 client_key.sk_rlwe.values(),
1978 &bool_evaluator.pbs_info.rlwe_modop,
1979 &ideal,
1980 );
1981 let v = decrypt_lwe(
1982 &ct0,
1983 client_key.sk_rlwe.values(),
1984 &bool_evaluator.pbs_info.rlwe_modop,
1985 );
1986 (n, v)
1987 };
1988 let noise1 = {
1989 let ideal = if m1 {
1990 bool_evaluator.pbs_info.parameters.rlwe_q().true_el()
1991 } else {
1992 bool_evaluator.pbs_info.parameters.rlwe_q().false_el()
1993 };
1994 let n = measure_noise_lwe(
1995 &ct1,
1996 client_key.sk_rlwe.values(),
1997 &bool_evaluator.pbs_info.rlwe_modop,
1998 &ideal,
1999 );
2000 let v = decrypt_lwe(
2001 &ct1,
2002 client_key.sk_rlwe.values(),
2003 &bool_evaluator.pbs_info.rlwe_modop,
2004 );
2005 (n, v)
2006 };
2007
2008 // // Trace PBS
2009 // PBSTracer::with_local(|t| {
2010 // t.trace(
2011 // &SP_BOOL_PARAMS,
2012 // &client_key.sk_lwe.values(),
2013 // client_key.sk_rlwe.values(),
2014 // )
2015 // });
2016
2017 // Calculate noise in ciphertext post PBS
2018 let noise_out = {
2019 let ideal = if m_out {
2020 bool_evaluator.pbs_info.parameters.rlwe_q().true_el()
2021 } else {
2022 bool_evaluator.pbs_info.parameters.rlwe_q().false_el()
2023 };
2024 let n = measure_noise_lwe(
2025 &ct_back,
2026 client_key.sk_rlwe.values(),
2027 &bool_evaluator.pbs_info.rlwe_modop,
2028 &ideal,
2029 );
2030 let v = decrypt_lwe(
2031 &ct_back,
2032 client_key.sk_rlwe.values(),
2033 &bool_evaluator.pbs_info.rlwe_modop,
2034 );
2035 (n, v)
2036 };
2037 dbg!(m0, m1, m_out);
2038 println!(
2039 "ct0 (noise, message): {:?} \n ct1 (noise, message): {:?} \n PBS (noise, message): {:?}", noise0, noise1, noise_out
2040 );
2041 }
2042 let m_back = bool_evaluator.sk_decrypt(&ct_back, &client_key);
2043 assert!(m_out == m_back, "Expected {m_out}, got {m_back}");
2044 println!("----------");
2045
2046 m1 = m0;
2047 m0 = m_out;
2048
2049 ct1 = ct0;
2050 ct0 = ct_back;
2051 }
2052 }
2053
2054 #[test]
2055 fn multi_party_encryption_decryption() {
2056 let bool_evaluator = BoolEvaluator::<
2057 Vec<Vec<u64>>,
2058 NttBackendU64,
2059 ModularOpsU64<CiphertextModulus<u64>>,
2060 ModularOpsU64<CiphertextModulus<u64>>,
2061 >::new(MP_BOOL_PARAMS);
2062
2063 let no_of_parties = 500;
2064 let parties = (0..no_of_parties)
2065 .map(|_| bool_evaluator.client_key())
2066 .collect_vec();
2067
2068 let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()];
2069 parties.iter().for_each(|k| {
2070 izip!(ideal_rlwe_sk.iter_mut(), k.sk_rlwe.values()).for_each(|(ideal_i, s_i)| {
2071 *ideal_i = *ideal_i + s_i;
2072 });
2073 });
2074
2075 println!("{:?}", &ideal_rlwe_sk);
2076
2077 let mut m = true;
2078 for i in 0..100 {
2079 let pk_cr_seed = [0u8; 32];
2080
2081 let public_key_share = parties
2082 .iter()
2083 .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k))
2084 .collect_vec();
2085
2086 let collective_pk = PublicKey::<
2087 Vec<Vec<u64>>,
2088 DefaultSecureRng,
2089 ModularOpsU64<CiphertextModulus<u64>>,
2090 >::from(public_key_share.as_slice());
2091 let lwe_ct = bool_evaluator.pk_encrypt(&collective_pk.key, m);
2092
2093 let decryption_shares = parties
2094 .iter()
2095 .map(|k| bool_evaluator.multi_party_decryption_share(&lwe_ct, k))
2096 .collect_vec();
2097
2098 let m_back = bool_evaluator.multi_party_decrypt(&decryption_shares, &lwe_ct);
2099
2100 {
2101 let ideal_m = if m {
2102 bool_evaluator.pbs_info.parameters.rlwe_q().true_el()
2103 } else {
2104 bool_evaluator.pbs_info.parameters.rlwe_q().false_el()
2105 };
2106 let noise = measure_noise_lwe(
2107 &lwe_ct,
2108 &ideal_rlwe_sk,
2109 &bool_evaluator.pbs_info.rlwe_modop,
2110 &ideal_m,
2111 );
2112 println!("Noise: {noise}");
2113 }
2114
2115 assert_eq!(m_back, m);
2116 m = !m;
2117 }
2118 }
2119
2120 fn _collecitve_public_key_gen(rlwe_q: u64, parties_rlwe_sk: &[RlweSecret]) -> Vec<Vec<u64>> {
2121 let ring_size = parties_rlwe_sk[0].values.len();
2122 assert!(ring_size.is_power_of_two());
2123 let mut rng = DefaultSecureRng::new();
2124 let nttop = NttBackendU64::new(&rlwe_q, ring_size);
2125 let modop = ModularOpsU64::new(rlwe_q);
2126
2127 // Generate Pk shares
2128 let pk_seed = [0u8; 32];
2129 let pk_shares = parties_rlwe_sk.iter().map(|sk| {
2130 let mut p_rng = DefaultSecureRng::new_seeded(pk_seed);
2131 let mut share_out = vec![0u64; ring_size];
2132 public_key_share(
2133 &mut share_out,
2134 sk.values(),
2135 &modop,
2136 &nttop,
2137 &mut p_rng,
2138 &mut rng,
2139 );
2140 share_out
2141 });
2142
2143 let mut pk_part_b = vec![0u64; ring_size];
2144 pk_shares.for_each(|share| modop.elwise_add_mut(&mut pk_part_b, &share));
2145 let mut pk_part_a = vec![0u64; ring_size];
2146 let mut p_rng = DefaultSecureRng::new_seeded(pk_seed);
2147 RandomFillUniformInModulus::random_fill(&mut p_rng, &rlwe_q, pk_part_a.as_mut_slice());
2148
2149 vec![pk_part_a, pk_part_b]
2150 }
2151
2152 fn _multi_party_all_keygen(
2153 bool_evaluator: &BoolEvaluator<
2154 Vec<Vec<u64>>,
2155 NttBackendU64,
2156 ModularOpsU64<CiphertextModulus<u64>>,
2157 ModularOpsU64<CiphertextModulus<u64>>,
2158 >,
2159 no_of_parties: usize,
2160 ) -> (
2161 Vec<ClientKey>,
2162 PublicKey<Vec<Vec<u64>>, DefaultSecureRng, ModularOpsU64<CiphertextModulus<u64>>>,
2163 Vec<
2164 CommonReferenceSeededMultiPartyServerKeyShare<
2165 Vec<Vec<u64>>,
2166 BoolParameters<u64>,
2167 [u8; 32],
2168 >,
2169 >,
2170 SeededMultiPartyServerKey<Vec<Vec<u64>>, [u8; 32], BoolParameters<u64>>,
2171 ServerKeyEvaluationDomain<Vec<Vec<u64>>, DefaultSecureRng, NttBackendU64>,
2172 ClientKey,
2173 ) {
2174 let parties = (0..no_of_parties)
2175 .map(|_| bool_evaluator.client_key())
2176 .collect_vec();
2177
2178 let mut rng = DefaultSecureRng::new();
2179
2180 // Collective public key
2181 let mut pk_cr_seed = [0u8; 32];
2182 rng.fill_bytes(&mut pk_cr_seed);
2183 let public_key_share = parties
2184 .iter()
2185 .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k))
2186 .collect_vec();
2187 let collective_pk =
2188 PublicKey::<Vec<Vec<u64>>, DefaultSecureRng, _>::from(public_key_share.as_slice());
2189
2190 // Server key
2191 let mut pbs_cr_seed = [0u8; 32];
2192 rng.fill_bytes(&mut pbs_cr_seed);
2193 let server_key_shares = parties
2194 .iter()
2195 .map(|k| {
2196 bool_evaluator.multi_party_server_key_share(pbs_cr_seed, &collective_pk.key, k)
2197 })
2198 .collect_vec();
2199 let seeded_server_key =
2200 bool_evaluator.aggregate_multi_party_server_key_shares(&server_key_shares);
2201 let server_key_eval = ServerKeyEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
2202 &seeded_server_key,
2203 );
2204
2205 // construct ideal rlwe sk for meauring noise
2206 let ideal_client_key = {
2207 let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()];
2208 parties.iter().for_each(|k| {
2209 izip!(ideal_rlwe_sk.iter_mut(), k.sk_rlwe.values()).for_each(|(ideal_i, s_i)| {
2210 *ideal_i = *ideal_i + s_i;
2211 });
2212 });
2213 let mut ideal_lwe_sk = vec![0i32; bool_evaluator.pbs_info.lwe_n()];
2214 parties.iter().for_each(|k| {
2215 izip!(ideal_lwe_sk.iter_mut(), k.sk_lwe.values()).for_each(|(ideal_i, s_i)| {
2216 *ideal_i = *ideal_i + s_i;
2217 });
2218 });
2219
2220 ClientKey {
2221 sk_lwe: LweSecret {
2222 values: ideal_lwe_sk,
2223 },
2224 sk_rlwe: RlweSecret {
2225 values: ideal_rlwe_sk,
2226 },
2227 }
2228 };
2229
2230 (
2231 parties,
2232 collective_pk,
2233 server_key_shares,
2234 seeded_server_key,
2235 server_key_eval,
2236 ideal_client_key,
2237 )
2238 }
2239
2240 #[test]
2241 fn multi_party_nand() {
2242 let mut bool_evaluator = BoolEvaluator::<
2243 Vec<Vec<u64>>,
2244 NttBackendU64,
2245 ModularOpsU64<CiphertextModulus<u64>>,
2246 ModularOpsU64<CiphertextModulus<u64>>,
2247 >::new(MP_BOOL_PARAMS);
2248
2249 let (parties, collective_pk, _, _, server_key_eval, ideal_client_key) =
2250 _multi_party_all_keygen(&bool_evaluator, 8);
2251
2252 let mut m0 = true;
2253 let mut m1 = false;
2254
2255 let mut lwe0 = bool_evaluator.pk_encrypt(&collective_pk.key, m0);
2256 let mut lwe1 = bool_evaluator.pk_encrypt(&collective_pk.key, m1);
2257
2258 for _ in 0..2000 {
2259 let lwe_out = bool_evaluator.nand(&lwe0, &lwe1, &server_key_eval);
2260
2261 let m_expected = !(m0 & m1);
2262
2263 // measure noise
2264 {
2265 let noise0 = {
2266 let ideal = if m0 {
2267 bool_evaluator.pbs_info.rlwe_q().true_el()
2268 } else {
2269 bool_evaluator.pbs_info.rlwe_q().false_el()
2270 };
2271 let n = measure_noise_lwe(
2272 &lwe0,
2273 ideal_client_key.sk_rlwe.values(),
2274 &bool_evaluator.pbs_info.rlwe_modop,
2275 &ideal,
2276 );
2277 let v = decrypt_lwe(
2278 &lwe0,
2279 ideal_client_key.sk_rlwe.values(),
2280 &bool_evaluator.pbs_info.rlwe_modop,
2281 );
2282 (n, v)
2283 };
2284 let noise1 = {
2285 let ideal = if m1 {
2286 bool_evaluator.pbs_info.rlwe_q().true_el()
2287 } else {
2288 bool_evaluator.pbs_info.rlwe_q().false_el()
2289 };
2290 let n = measure_noise_lwe(
2291 &lwe1,
2292 ideal_client_key.sk_rlwe.values(),
2293 &bool_evaluator.pbs_info.rlwe_modop,
2294 &ideal,
2295 );
2296 let v = decrypt_lwe(
2297 &lwe1,
2298 ideal_client_key.sk_rlwe.values(),
2299 &bool_evaluator.pbs_info.rlwe_modop,
2300 );
2301 (n, v)
2302 };
2303
2304 // // Trace PBS
2305 // PBSTracer::with_local(|t| {
2306 // t.trace(
2307 // &MP_BOOL_PARAMS,
2308 // &ideal_client_key.sk_lwe.values(),
2309 // &ideal_client_key.sk_rlwe.values(),
2310 // )
2311 // });
2312
2313 let noise_out = {
2314 let ideal_m = if m_expected {
2315 bool_evaluator.pbs_info.rlwe_q().true_el()
2316 } else {
2317 bool_evaluator.pbs_info.rlwe_q().false_el()
2318 };
2319 let n = measure_noise_lwe(
2320 &lwe_out,
2321 ideal_client_key.sk_rlwe.values(),
2322 &bool_evaluator.pbs_info.rlwe_modop,
2323 &ideal_m,
2324 );
2325 let v = decrypt_lwe(
2326 &lwe_out,
2327 ideal_client_key.sk_rlwe.values(),
2328 &bool_evaluator.pbs_info.rlwe_modop,
2329 );
2330 (n, v)
2331 };
2332 dbg!(m0, m1, m_expected);
2333 println!(
2334 "ct0 (noise, message): {:?} \n ct1 (noise, message): {:?} \n PBS (noise, message): {:?}", noise0, noise1, noise_out
2335 );
2336 }
2337
2338 // multi-party decrypt
2339 let decryption_shares = parties
2340 .iter()
2341 .map(|k| bool_evaluator.multi_party_decryption_share(&lwe_out, k))
2342 .collect_vec();
2343 let m_back = bool_evaluator.multi_party_decrypt(&decryption_shares, &lwe_out);
2344
2345 // let m_back = bool_evaluator.sk_decrypt(&lwe_out, &ideal_client_key);
2346
2347 assert!(m_expected == m_back, "Expected {m_expected}, got {m_back}");
2348 m1 = m0;
2349 m0 = m_expected;
2350
2351 lwe1 = lwe0;
2352 lwe0 = lwe_out;
2353 }
2354 }
2355
2356 #[test]
2357 fn noise_tester() {
2358 let bool_evaluator = BoolEvaluator::<
2359 Vec<Vec<u64>>,
2360 NttBackendU64,
2361 ModularOpsU64<CiphertextModulus<u64>>,
2362 ModularOpsU64<CiphertextModulus<u64>>,
2363 >::new(MP_BOOL_PARAMS);
2364
2365 // let (_, collective_pk, _, _, server_key_eval, ideal_client_key) =
2366 // _multi_party_all_keygen(&bool_evaluator, 20);
2367 let no_of_parties = 32;
2368 let lwe_q = bool_evaluator.pbs_info.parameters.lwe_q();
2369 let rlwe_q = bool_evaluator.pbs_info.parameters.rlwe_q();
2370 let lwe_n = bool_evaluator.pbs_info.parameters.lwe_n().0;
2371 let rlwe_n = bool_evaluator.pbs_info.parameters.rlwe_n().0;
2372 let lwe_modop = &bool_evaluator.pbs_info.lwe_modop;
2373 let rlwe_nttop = &bool_evaluator.pbs_info.rlwe_nttop;
2374 let rlwe_modop = &bool_evaluator.pbs_info.rlwe_modop;
2375
2376 // let rgsw_rgsw_decomposer = &bool_evaluator
2377 // .pbs_info
2378 // .parameters
2379 // .rgsw_rgsw_decomposer::<DefaultDecomposer<u64>>();
2380 // let rgsw_rgsw_gadget_a = rgsw_rgsw_decomposer.0.gadget_vector();
2381 // let rgsw_rgsw_gadget_b = rgsw_rgsw_decomposer.1.gadget_vector();
2382
2383 let rlwe_rgsw_decomposer = &bool_evaluator.pbs_info.rlwe_rgsw_decomposer;
2384 let rlwe_rgsw_gadget_a = rlwe_rgsw_decomposer.0.gadget_vector();
2385 let rlwe_rgsw_gadget_b = rlwe_rgsw_decomposer.1.gadget_vector();
2386
2387 let auto_decomposer = &bool_evaluator.pbs_info.auto_decomposer;
2388 let auto_gadget = auto_decomposer.gadget_vector();
2389
2390 let parties = (0..no_of_parties)
2391 .map(|_| bool_evaluator.client_key())
2392 .collect_vec();
2393
2394 let ideal_client_key = {
2395 let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()];
2396 parties.iter().for_each(|k| {
2397 izip!(ideal_rlwe_sk.iter_mut(), k.sk_rlwe.values()).for_each(|(ideal_i, s_i)| {
2398 *ideal_i = *ideal_i + s_i;
2399 });
2400 });
2401 let mut ideal_lwe_sk = vec![0i32; bool_evaluator.pbs_info.lwe_n()];
2402 parties.iter().for_each(|k| {
2403 izip!(ideal_lwe_sk.iter_mut(), k.sk_lwe.values()).for_each(|(ideal_i, s_i)| {
2404 *ideal_i = *ideal_i + s_i;
2405 });
2406 });
2407
2408 ClientKey {
2409 sk_lwe: LweSecret {
2410 values: ideal_lwe_sk,
2411 },
2412 sk_rlwe: RlweSecret {
2413 values: ideal_rlwe_sk,
2414 },
2415 }
2416 };
2417
2418 // check noise in freshly encrypted RLWE ciphertext (ie var_fresh)
2419 if true {
2420 let mut rng = DefaultSecureRng::new();
2421 let mut check = Stats { samples: vec![] };
2422 for _ in 0..10 {
2423 // generate a new collective public key
2424 let mut pk_cr_seed = [0u8; 32];
2425 rng.fill_bytes(&mut pk_cr_seed);
2426 let public_key_share = parties
2427 .iter()
2428 .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k))
2429 .collect_vec();
2430 let collective_pk = PublicKey::<
2431 Vec<Vec<u64>>,
2432 DefaultSecureRng,
2433 ModularOpsU64<CiphertextModulus<u64>>,
2434 >::from(public_key_share.as_slice());
2435
2436 let mut m = vec![0u64; rlwe_n];
2437 RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut_slice());
2438 let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2];
2439 public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
2440 &mut rlwe_ct,
2441 &collective_pk.key,
2442 &m,
2443 rlwe_modop,
2444 rlwe_nttop,
2445 &mut rng,
2446 );
2447
2448 let mut m_back = vec![0u64; rlwe_n];
2449 decrypt_rlwe(
2450 &rlwe_ct,
2451 ideal_client_key.sk_rlwe.values(),
2452 &mut m_back,
2453 rlwe_nttop,
2454 rlwe_modop,
2455 );
2456
2457 rlwe_modop.elwise_sub_mut(m_back.as_mut_slice(), m.as_slice());
2458
2459 check.add_more(Vec::<i64>::try_convert_from(&m_back, rlwe_q).as_slice());
2460 }
2461
2462 println!("Public key Std: {}", check.std_dev().abs().log2());
2463 }
2464
2465 if true {
2466 // Generate server key shares
2467 let mut rng = DefaultSecureRng::new();
2468 let mut pk_cr_seed = [0u8; 32];
2469 rng.fill_bytes(&mut pk_cr_seed);
2470 let public_key_share = parties
2471 .iter()
2472 .map(|k| bool_evaluator.multi_party_public_key_share(pk_cr_seed, k))
2473 .collect_vec();
2474 let collective_pk = PublicKey::<
2475 Vec<Vec<u64>>,
2476 DefaultSecureRng,
2477 ModularOpsU64<CiphertextModulus<u64>>,
2478 >::from(public_key_share.as_slice());
2479
2480 let pbs_cr_seed = [0u8; 32];
2481 rng.fill_bytes(&mut pk_cr_seed);
2482 let server_key_shares = parties
2483 .iter()
2484 .map(|k| {
2485 bool_evaluator.multi_party_server_key_share(pbs_cr_seed, &collective_pk.key, k)
2486 })
2487 .collect_vec();
2488
2489 let seeded_server_key =
2490 bool_evaluator.aggregate_multi_party_server_key_shares(&server_key_shares);
2491
2492 // Check noise in RGSW ciphertexts of ideal LWE secret elements
2493 if true {
2494 let mut check = Stats { samples: vec![] };
2495 izip!(
2496 ideal_client_key.sk_lwe.values.iter(),
2497 seeded_server_key.rgsw_cts.iter()
2498 )
2499 .for_each(|(s_i, rgsw_ct_i)| {
2500 // X^{s[i]}
2501 let mut m_si = vec![0u64; rlwe_n];
2502 let s_i = *s_i * (bool_evaluator.pbs_info.embedding_factor as i32);
2503 if s_i < 0 {
2504 m_si[rlwe_n - (s_i.abs() as usize)] = rlwe_q.neg_one();
2505 } else {
2506 m_si[s_i as usize] = 1;
2507 }
2508
2509 // RLWE'(-sm)
2510 let mut neg_s_eval =
2511 Vec::<u64>::try_convert_from(ideal_client_key.sk_rlwe.values(), rlwe_q);
2512 rlwe_modop.elwise_neg_mut(&mut neg_s_eval);
2513 rlwe_nttop.forward(&mut neg_s_eval);
2514 for j in 0..rlwe_rgsw_decomposer.a().decomposition_count() {
2515 // RLWE(B^{j} * -s[X]*X^{s_lwe[i]})
2516
2517 // -s[X]*X^{s_lwe[i]}*B_j
2518 let mut m_ideal = m_si.clone();
2519 rlwe_nttop.forward(m_ideal.as_mut_slice());
2520 rlwe_modop.elwise_mul_mut(m_ideal.as_mut_slice(), neg_s_eval.as_slice());
2521 rlwe_nttop.backward(m_ideal.as_mut_slice());
2522 rlwe_modop
2523 .elwise_scalar_mul_mut(m_ideal.as_mut_slice(), &rlwe_rgsw_gadget_a[j]);
2524
2525 // RLWE(-s*X^{s_lwe[i]}*B_j)
2526 let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2];
2527 rlwe_ct[0].copy_from_slice(&rgsw_ct_i[j]);
2528 rlwe_ct[1].copy_from_slice(
2529 &rgsw_ct_i[j + rlwe_rgsw_decomposer.a().decomposition_count()],
2530 );
2531
2532 let mut m_back = vec![0u64; rlwe_n];
2533 decrypt_rlwe(
2534 &rlwe_ct,
2535 ideal_client_key.sk_rlwe.values(),
2536 &mut m_back,
2537 rlwe_nttop,
2538 rlwe_modop,
2539 );
2540
2541 // diff
2542 rlwe_modop.elwise_sub_mut(&mut m_back, &m_ideal);
2543 check.add_more(&Vec::<i64>::try_convert_from(&m_back, rlwe_q));
2544 }
2545
2546 // RLWE'(m)
2547 for j in 0..rlwe_rgsw_decomposer.b().decomposition_count() {
2548 // RLWE(B^{j} * X^{s_lwe[i]})
2549
2550 // X^{s_lwe[i]}*B_j
2551 let mut m_ideal = m_si.clone();
2552 rlwe_modop
2553 .elwise_scalar_mul_mut(m_ideal.as_mut_slice(), &rlwe_rgsw_gadget_b[j]);
2554
2555 // RLWE(X^{s_lwe[i]}*B_j)
2556 let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2];
2557 rlwe_ct[0].copy_from_slice(
2558 &rgsw_ct_i[j + (2 * rlwe_rgsw_decomposer.a().decomposition_count())],
2559 );
2560 rlwe_ct[1].copy_from_slice(
2561 &rgsw_ct_i[j
2562 + (2 * rlwe_rgsw_decomposer.a().decomposition_count()
2563 + rlwe_rgsw_decomposer.b().decomposition_count())],
2564 );
2565
2566 let mut m_back = vec![0u64; rlwe_n];
2567 decrypt_rlwe(
2568 &rlwe_ct,
2569 ideal_client_key.sk_rlwe.values(),
2570 &mut m_back,
2571 rlwe_nttop,
2572 rlwe_modop,
2573 );
2574
2575 // diff
2576 rlwe_modop.elwise_sub_mut(&mut m_back, &m_ideal);
2577 check.add_more(&Vec::<i64>::try_convert_from(&m_back, rlwe_q));
2578 }
2579 });
2580 println!(
2581 "RGSW Std: {} {} ;; max={}",
2582 check.mean(),
2583 check.std_dev().abs().log2(),
2584 check.samples.iter().max().unwrap()
2585 );
2586 }
2587
2588 // server key in Evaluation domain
2589 let server_key_eval_domain =
2590 ServerKeyEvaluationDomain::<_, DefaultSecureRng, NttBackendU64>::from(
2591 &seeded_server_key,
2592 );
2593
2594 // check noise in RLWE x RGSW(X^{s_i}) where RGSW is accunulated RGSW ciphertext
2595 if true {
2596 let mut check = Stats { samples: vec![] };
2597
2598 izip!(
2599 ideal_client_key.sk_lwe.values(),
2600 server_key_eval_domain.rgsw_cts.iter()
2601 )
2602 .for_each(|(s_i, rgsw_ct_i)| {
2603 let mut m = vec![0u64; rlwe_n];
2604 RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut_slice());
2605 let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2];
2606 public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
2607 &mut rlwe_ct,
2608 &collective_pk.key,
2609 &m,
2610 rlwe_modop,
2611 rlwe_nttop,
2612 &mut rng,
2613 );
2614
2615 // RLWE(m*X^{s[i]}) = RLWE(m) x RGSW(X^{s[i]})
2616 // let mut rlwe_after = RlweCiphertext::<_, DefaultSecureRng>::new_trivial(vec![
2617 // vec![0u64; rlwe_n],
2618 // m.clone(),
2619 // ]);
2620 let mut rlwe_after = RlweCiphertext::<_, DefaultSecureRng> {
2621 data: rlwe_ct.clone(),
2622 is_trivial: false,
2623 _phatom: PhantomData,
2624 };
2625 let mut scratch = vec![
2626 vec![0u64; rlwe_n];
2627 std::cmp::max(
2628 rlwe_rgsw_decomposer.0.decomposition_count(),
2629 rlwe_rgsw_decomposer.1.decomposition_count()
2630 ) + 2
2631 ];
2632 rlwe_by_rgsw(
2633 &mut rlwe_after,
2634 rgsw_ct_i,
2635 &mut scratch,
2636 rlwe_rgsw_decomposer,
2637 rlwe_nttop,
2638 rlwe_modop,
2639 );
2640
2641 // m1 = X^{s[i]}
2642 let mut m1 = vec![0u64; rlwe_n];
2643 let s_i = *s_i * (bool_evaluator.pbs_info.embedding_factor as i32);
2644 if s_i < 0 {
2645 m1[rlwe_n - (s_i.abs() as usize)] = rlwe_q.neg_one()
2646 } else {
2647 m1[s_i as usize] = 1;
2648 }
2649
2650 // (m+e) * m1
2651 let mut m_plus_e_times_m1 = vec![0u64; rlwe_n];
2652 decrypt_rlwe(
2653 &rlwe_ct,
2654 ideal_client_key.sk_rlwe.values(),
2655 &mut m_plus_e_times_m1,
2656 rlwe_nttop,
2657 rlwe_modop,
2658 );
2659 rlwe_nttop.forward(m_plus_e_times_m1.as_mut_slice());
2660 rlwe_nttop.forward(m1.as_mut_slice());
2661 rlwe_modop.elwise_mul_mut(m_plus_e_times_m1.as_mut_slice(), m1.as_slice());
2662 rlwe_nttop.backward(m_plus_e_times_m1.as_mut_slice());
2663
2664 // Resulting RLWE ciphertext will equal: (m0m1 + em1) + e_{rlsw x rgsw}.
2665 // Hence, resulting rlwe ciphertext will have error em1 + e_{rlwe x rgsw}.
2666 // Here we're only concerned with e_{rlwe x rgsw}, that is noise added by
2667 // RLWExRGSW. Also note in practice m1 is a monomial, for ex, X^{s_{i}}, for
2668 // some i and var(em1) = var(e).
2669 let mut m_plus_e_times_m1_more_e = vec![0u64; rlwe_n];
2670 decrypt_rlwe(
2671 &rlwe_after,
2672 ideal_client_key.sk_rlwe.values(),
2673 &mut m_plus_e_times_m1_more_e,
2674 rlwe_nttop,
2675 rlwe_modop,
2676 );
2677
2678 // diff
2679 rlwe_modop.elwise_sub_mut(
2680 m_plus_e_times_m1_more_e.as_mut_slice(),
2681 m_plus_e_times_m1.as_slice(),
2682 );
2683
2684 // let noise = measure_noise(
2685 // &rlwe_after,
2686 // &m_plus_e_times_m1,
2687 // rlwe_nttop,
2688 // rlwe_modop,
2689 // ideal_client_key.sk_rlwe.values(),
2690 // );
2691 // print!("NOISE: {}", noise);
2692
2693 check.add_more(&Vec::<i64>::try_convert_from(
2694 &m_plus_e_times_m1_more_e,
2695 rlwe_q,
2696 ));
2697 });
2698 println!(
2699 "RLWE x RGSW, where RGSW has noise var_brk, std: {} {}",
2700 check.std_dev(),
2701 check.std_dev().abs().log2()
2702 )
2703 }
2704
2705 // check noise in Auto key
2706 if true {
2707 let mut check = Stats { samples: vec![] };
2708
2709 let mut neg_s_poly =
2710 Vec::<u64>::try_convert_from(ideal_client_key.sk_rlwe.values(), rlwe_q);
2711 rlwe_modop.elwise_neg_mut(neg_s_poly.as_mut_slice());
2712
2713 let g = bool_evaluator.pbs_info.g();
2714 for i in [-g, g] {
2715 // -s[X^k]
2716 let (auto_indices, auto_sign) = generate_auto_map(rlwe_n, i);
2717 let mut neg_s_poly_auto_i = vec![0u64; rlwe_n];
2718 izip!(neg_s_poly.iter(), auto_indices.iter(), auto_sign.iter()).for_each(
2719 |(v, to_i, to_sign)| {
2720 if !to_sign {
2721 neg_s_poly_auto_i[*to_i] = rlwe_modop.neg(v);
2722 } else {
2723 neg_s_poly_auto_i[*to_i] = *v;
2724 }
2725 },
2726 );
2727
2728 let mut auto_key_i = server_key_eval_domain.galois_key_for_auto(i).clone();
2729 // send i^th auto key to coefficient domain
2730 auto_key_i
2731 .iter_mut()
2732 .for_each(|r| rlwe_nttop.backward(r.as_mut_slice()));
2733 auto_gadget.iter().enumerate().for_each(|(i, b_i)| {
2734 // B^i * -s[X^k]
2735 let mut m_ideal = neg_s_poly_auto_i.clone();
2736 rlwe_modop.elwise_scalar_mul_mut(m_ideal.as_mut_slice(), b_i);
2737
2738 let mut m_out = vec![0u64; rlwe_n];
2739 let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2];
2740 rlwe_ct[0].copy_from_slice(&auto_key_i[i]);
2741 rlwe_ct[1].copy_from_slice(
2742 &auto_key_i[auto_decomposer.decomposition_count() + i],
2743 );
2744 decrypt_rlwe(
2745 &rlwe_ct,
2746 ideal_client_key.sk_rlwe.values(),
2747 &mut m_out,
2748 rlwe_nttop,
2749 rlwe_modop,
2750 );
2751
2752 // diff
2753 rlwe_modop.elwise_sub_mut(m_out.as_mut_slice(), m_ideal.as_slice());
2754
2755 check.add_more(&Vec::<i64>::try_convert_from(&m_out, rlwe_q));
2756 });
2757 }
2758
2759 println!("Auto key noise std dev: {}", check.std_dev().abs().log2());
2760 }
2761
2762 // check noise in RLWE(X^k) after sending RLWE(X) -> RLWE(X^k) using collective
2763 // auto key
2764 if true {
2765 let mut check = Stats { samples: vec![] };
2766
2767 let g = bool_evaluator.pbs_info.g();
2768 for i in [-g, g] {
2769 for _ in 0..10 {
2770 let mut m = vec![0u64; rlwe_n];
2771 RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut_slice());
2772 let mut rlwe_ct = RlweCiphertext::<_, DefaultSecureRng> {
2773 data: vec![vec![0u64; rlwe_n]; 2],
2774 is_trivial: false,
2775 _phatom: PhantomData,
2776 };
2777 public_key_encrypt_rlwe::<_, _, _, _, i32, _>(
2778 &mut rlwe_ct,
2779 &collective_pk.key,
2780 &m,
2781 rlwe_modop,
2782 rlwe_nttop,
2783 &mut rng,
2784 );
2785
2786 // We're only interested in noise increased as a result of automorphism.
2787 // Hence, we take m+e as the bench.
2788 let mut m_plus_e = vec![0u64; rlwe_n];
2789 decrypt_rlwe(
2790 &rlwe_ct,
2791 ideal_client_key.sk_rlwe.values(),
2792 &mut m_plus_e,
2793 rlwe_nttop,
2794 rlwe_modop,
2795 );
2796
2797 let auto_key = server_key_eval_domain.galois_key_for_auto(i);
2798 let (auto_map_index, auto_map_sign) = generate_auto_map(rlwe_n, i);
2799 let mut scratch =
2800 vec![vec![0u64; rlwe_n]; auto_decomposer.decomposition_count() + 2];
2801 galois_auto(
2802 &mut rlwe_ct,
2803 auto_key,
2804 &mut scratch,
2805 &auto_map_index,
2806 &auto_map_sign,
2807 rlwe_modop,
2808 rlwe_nttop,
2809 auto_decomposer,
2810 );
2811
2812 // send m+e from X to X^k
2813 let mut m_plus_e_auto = vec![0u64; rlwe_n];
2814 izip!(m_plus_e.iter(), auto_map_index.iter(), auto_map_sign.iter())
2815 .for_each(|(v, to_index, to_sign)| {
2816 if !to_sign {
2817 m_plus_e_auto[*to_index] = rlwe_modop.neg(v);
2818 } else {
2819 m_plus_e_auto[*to_index] = *v
2820 }
2821 });
2822
2823 let mut m_out = vec![0u64; rlwe_n];
2824 decrypt_rlwe(
2825 &rlwe_ct,
2826 ideal_client_key.sk_rlwe.values(),
2827 &mut m_out,
2828 rlwe_nttop,
2829 rlwe_modop,
2830 );
2831
2832 // diff
2833 rlwe_modop.elwise_sub_mut(m_out.as_mut_slice(), m_plus_e_auto.as_slice());
2834
2835 check.add_more(&Vec::<i64>::try_convert_from(m_out.as_slice(), rlwe_q));
2836 }
2837 }
2838
2839 println!("Rlwe Auto Noise Std: {}", check.std_dev().abs().log2());
2840 }
2841
2842 // Check noise growth in ksk
2843 // TODO check in LWE key switching keys
2844 if true {
2845 // 1. encrypt LWE ciphertext
2846 // 2. Key switching
2847 // 3.
2848 let mut check = Stats { samples: vec![] };
2849
2850 for _ in 0..1024 {
2851 // Encrypt m \in Q_{ks} using RLWE sk
2852 let mut lwe_in_ct = vec![0u64; rlwe_n + 1];
2853 let m = RandomElementInModulus::random(&mut rng, &lwe_q.q().unwrap());
2854 encrypt_lwe(
2855 &mut lwe_in_ct,
2856 &m,
2857 ideal_client_key.sk_rlwe.values(),
2858 lwe_modop,
2859 &mut rng,
2860 );
2861
2862 // Key switch
2863 let mut lwe_out = vec![0u64; lwe_n + 1];
2864 lwe_key_switch(
2865 &mut lwe_out,
2866 &lwe_in_ct,
2867 server_key_eval_domain.lwe_ksk(),
2868 lwe_modop,
2869 bool_evaluator.pbs_info.lwe_decomposer(),
2870 );
2871
2872 // We only care about noise added by LWE key switch
2873 // m+e
2874 let m_plus_e =
2875 decrypt_lwe(&lwe_in_ct, ideal_client_key.sk_rlwe.values(), lwe_modop);
2876
2877 let m_plus_e_plus_lwe_ksk_noise =
2878 decrypt_lwe(&lwe_out, ideal_client_key.sk_lwe.values(), lwe_modop);
2879
2880 let diff = lwe_modop.sub(&m_plus_e_plus_lwe_ksk_noise, &m_plus_e);
2881
2882 check.add_more(&vec![lwe_q.map_element_to_i64(&diff)]);
2883 }
2884
2885 println!("Lwe ksk std dev: {}", check.std_dev().abs().log2());
2886 }
2887 }
2888
2889 // Check noise in fresh RGSW ciphertexts, ie X^{s_j[i]}, must equal noise in
2890 // fresh RLWE ciphertext
2891 if true {}
2892 // test LWE ksk from RLWE -> LWE
2893 // if false {
2894 // let logp = 2;
2895 // let mut rng = DefaultSecureRng::new();
2896
2897 // let m = 1;
2898 // let encoded_m = m << (lwe_logq - logp);
2899
2900 // // Encrypt
2901 // let mut lwe_ct = vec![0u64; rlwe_n + 1];
2902 // encrypt_lwe(
2903 // &mut lwe_ct,
2904 // &encoded_m,
2905 // ideal_client_key.sk_rlwe.values(),
2906 // lwe_modop,
2907 // &mut rng,
2908 // );
2909
2910 // // key switch
2911 // let lwe_decomposer = &bool_evaluator.decomposer_lwe;
2912 // let mut lwe_out = vec![0u64; lwe_n + 1];
2913 // lwe_key_switch(
2914 // &mut lwe_out,
2915 // &lwe_ct,
2916 // &server_key_eval.lwe_ksk,
2917 // lwe_modop,
2918 // lwe_decomposer,
2919 // );
2920
2921 // let encoded_m_back = decrypt_lwe(&lwe_out,
2922 // ideal_client_key.sk_lwe.values(), lwe_modop); let m_back
2923 // = ((encoded_m_back as f64 * (1 << logp) as f64) /
2924 // (lwe_q as f64)).round() as u64; dbg!(m_back, m);
2925
2926 // let noise = measure_noise_lwe(
2927 // &lwe_out,
2928 // ideal_client_key.sk_lwe.values(),
2929 // lwe_modop,
2930 // &encoded_m,
2931 // );
2932
2933 // println!("Noise: {noise}");
2934 // }
2935
2936 // Measure noise in RGSW ciphertexts of ideal LWE secrets
2937 // if true {
2938 // let gadget_vec = gadget_vector(
2939 // bool_evaluator.parameters.rlwe_logq,
2940 // bool_evaluator.parameters.logb_rgsw,
2941 // bool_evaluator.parameters.d_rgsw,
2942 // );
2943
2944 // for i in 0..20 {
2945 // // measure noise in RGSW(s[i])
2946 // let si =
2947 // ideal_client_key.sk_lwe.values[i] *
2948 // (bool_evaluator.embedding_factor as i32); let mut
2949 // si_poly = vec![0u64; rlwe_n]; if si < 0 {
2950 // si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1;
2951 // } else {
2952 // si_poly[(si.abs() as usize)] = 1;
2953 // }
2954
2955 // let mut rgsw_si = server_key_eval.rgsw_cts[i].clone();
2956 // rgsw_si
2957 // .iter_mut()
2958 // .for_each(|ri| rlwe_nttop.backward(ri.as_mut()));
2959
2960 // println!("####### Noise in RGSW(X^s_{i}) #######");
2961 // _measure_noise_rgsw(
2962 // &rgsw_si,
2963 // &si_poly,
2964 // ideal_client_key.sk_rlwe.values(),
2965 // &gadget_vec,
2966 // rlwe_q,
2967 // );
2968 // println!("####### ##################### #######");
2969 // }
2970 // }
2971
2972 // // measure noise grwoth in RLWExRGSW
2973 // if true {
2974 // let mut rng = DefaultSecureRng::new();
2975 // let mut carry_m = vec![0u64; rlwe_n];
2976 // RandomUniformDist1::random_fill(&mut rng, &rlwe_q,
2977 // carry_m.as_mut_slice());
2978
2979 // // RGSW(carrym)
2980 // let trivial_rlwect = vec![vec![0u64; rlwe_n], carry_m.clone()];
2981 // let mut rlwe_ct = RlweCiphertext::<_,
2982 // DefaultSecureRng>::from_raw(trivial_rlwect, true);
2983
2984 // let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n];
2985 // d_rgsw + 2]; let mul_mod =
2986 // |v0: &u64, v1: &u64| (((*v0 as u128 * *v1 as u128) % (rlwe_q as u128)) as u64);
2987
2988 // for i in 0..bool_evaluator.parameters.lwe_n {
2989 // rlwe_by_rgsw(
2990 // &mut rlwe_ct,
2991 // server_key_eval.rgsw_ct_lwe_si(i),
2992 // &mut scratch_matrix_dplus2_ring,
2993 // rlwe_decomposer,
2994 // rlwe_nttop,
2995 // rlwe_modop,
2996 // );
2997
2998 // // carry_m[X] * s_i[X]
2999 // let si =
3000 // ideal_client_key.sk_lwe.values[i] *
3001 // (bool_evaluator.embedding_factor as i32); let mut
3002 // si_poly = vec![0u64; rlwe_n]; if si < 0 {
3003 // si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1;
3004 // } else {
3005 // si_poly[(si.abs() as usize)] = 1;
3006 // }
3007 // carry_m = negacyclic_mul(&carry_m, &si_poly, mul_mod,
3008 // rlwe_q);
3009
3010 // let noise = measure_noise(
3011 // &rlwe_ct,
3012 // &carry_m,
3013 // rlwe_nttop,
3014 // rlwe_modop,
3015 // ideal_client_key.sk_rlwe.values(),
3016 // );
3017 // println!("Noise RLWE(carry_m) accumulating {i}^th secret
3018 // monomial: {noise}"); }
3019 // }
3020
3021 // // Check galois keys
3022 // if false {
3023 // let g = bool_evaluator.g() as isize;
3024 // let mut rng = DefaultSecureRng::new();
3025 // let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n];
3026 // d_rgsw + 2]; for i in [g, -g] {
3027 // let mut m = vec![0u64; rlwe_n];
3028 // RandomUniformDist1::random_fill(&mut rng, &rlwe_q,
3029 // m.as_mut_slice()); let mut rlwe_ct = {
3030 // let mut data = vec![vec![0u64; rlwe_n]; 2];
3031 // public_key_encrypt_rlwe(
3032 // &mut data,
3033 // &collective_pk.key,
3034 // &m,
3035 // rlwe_modop,
3036 // rlwe_nttop,
3037 // &mut rng,
3038 // );
3039 // RlweCiphertext::<_, DefaultSecureRng>::from_raw(data,
3040 // false) };
3041
3042 // let auto_key = server_key_eval.galois_key_for_auto(i);
3043 // let (auto_map_index, auto_map_sign) =
3044 // generate_auto_map(rlwe_n, i); galois_auto(
3045 // &mut rlwe_ct,
3046 // auto_key,
3047 // &mut scratch_matrix_dplus2_ring,
3048 // &auto_map_index,
3049 // &auto_map_sign,
3050 // rlwe_modop,
3051 // rlwe_nttop,
3052 // rlwe_decomposer,
3053 // );
3054
3055 // // send m(X) -> m(X^i)
3056 // let mut m_k = vec![0u64; rlwe_n];
3057 // izip!(m.iter(), auto_map_index.iter(),
3058 // auto_map_sign.iter()).for_each( |(mi, to_index, to_sign)|
3059 // { if !to_sign {
3060 // m_k[*to_index] = rlwe_q - *mi;
3061 // } else {
3062 // m_k[*to_index] = *mi;
3063 // }
3064 // },
3065 // );
3066
3067 // // measure noise
3068 // let noise = measure_noise(
3069 // &rlwe_ct,
3070 // &m_k,
3071 // rlwe_nttop,
3072 // rlwe_modop,
3073 // ideal_client_key.sk_rlwe.values(),
3074 // );
3075
3076 // println!("Noise after auto k={i}: {noise}");
3077 // }
3078 // }
3079 }
3080}