arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/src/bool/print_noise.rs
src/bool/print_noise.rsRaw
  1use std::{fmt::Debug, iter::Sum};
  2
  3use itertools::izip;
  4use num_traits::{FromPrimitive, PrimInt, Zero};
  5use rand_distr::uniform::SampleUniform;
  6
  7use crate::{
  8    backend::{GetModulus, Modulus},
  9    decomposer::RlweDecomposer,
 10    lwe::{decrypt_lwe, lwe_key_switch},
 11    parameters::{BoolParameters, CiphertextModulus},
 12    random::{DefaultSecureRng, RandomFillUniformInModulus},
 13    rgsw::{decrypt_rlwe, galois_auto, IsTrivial, RlweCiphertext},
 14    utils::{encode_x_pow_si_with_emebedding_factor, tests::Stats, TryConvertFrom1},
 15    ArithmeticOps, ClientKey, Decomposer, MatrixEntity, MatrixMut, ModInit, Ntt, NttInit,
 16    RowEntity, RowMut, VectorOps,
 17};
 18
 19use super::keys::tests::{ideal_sk_lwe, ideal_sk_rlwe};
 20
 21pub(crate) trait CollectRuntimeServerKeyStats {
 22    type M;
 23    /// RGSW ciphertext X^{s[s_index]} in evaluation domain where s the LWE
 24    /// secret
 25    fn rgsw_cts_lwe_si(&self, s_index: usize) -> &Self::M;
 26    /// Auto key in evaluation domain for automorphism g^k. For auto key for
 27    /// automorphism corresponding to -g, set k = 0
 28    fn galois_key_for_auto(&self, k: usize) -> &Self::M;
 29    /// LWE key switching key
 30    fn lwe_ksk(&self) -> &Self::M;
 31}
 32
 33struct ServerKeyStats<T> {
 34    brk_rgsw_cts: (Stats<T>, Stats<T>),
 35    post_1_auto: Stats<T>,
 36    post_lwe_key_switch: Stats<T>,
 37}
 38
 39impl<T: PrimInt + FromPrimitive + Debug + Sum> ServerKeyStats<T>
 40where
 41    T: for<'a> Sum<&'a T>,
 42{
 43    fn new() -> Self {
 44        ServerKeyStats {
 45            brk_rgsw_cts: (Stats::default(), Stats::default()),
 46            post_1_auto: Stats::default(),
 47            post_lwe_key_switch: Stats::default(),
 48        }
 49    }
 50
 51    fn add_noise_brk_rgsw_cts_nsm(&mut self, noise: &[T]) {
 52        self.brk_rgsw_cts.0.add_more(noise);
 53    }
 54
 55    fn add_noise_brk_rgsw_cts_m(&mut self, noise: &[T]) {
 56        self.brk_rgsw_cts.1.add_more(noise);
 57    }
 58
 59    fn add_noise_post_1_auto(&mut self, noise: &[T]) {
 60        self.post_1_auto.add_more(&noise);
 61    }
 62
 63    fn add_noise_post_kwe_key_switch(&mut self, noise: &[T]) {
 64        self.post_lwe_key_switch.add_more(&noise);
 65    }
 66}
 67
 68fn collect_server_key_stats<
 69    M: MatrixEntity + MatrixMut,
 70    D: Decomposer<Element = M::MatElement>,
 71    NttOp: NttInit<CiphertextModulus<M::MatElement>> + Ntt<Element = M::MatElement>,
 72    ModOp: VectorOps<Element = M::MatElement>
 73        + ArithmeticOps<Element = M::MatElement>
 74        + ModInit<M = CiphertextModulus<M::MatElement>>
 75        + GetModulus<M = CiphertextModulus<M::MatElement>, Element = M::MatElement>,
 76    S: CollectRuntimeServerKeyStats<M = M>,
 77>(
 78    parameters: BoolParameters<M::MatElement>,
 79    client_keys: &[ClientKey],
 80    server_key: &S,
 81) -> ServerKeyStats<i64>
 82where
 83    M::R: RowMut + RowEntity + TryConvertFrom1<[i32], CiphertextModulus<M::MatElement>> + Clone,
 84    M::MatElement: Copy + PrimInt + FromPrimitive + SampleUniform + Zero + Debug,
 85{
 86    let ideal_sk_rlwe = ideal_sk_rlwe(client_keys);
 87    let ideal_sk_lwe = ideal_sk_lwe(client_keys);
 88
 89    let embedding_factor = (2 * parameters.rlwe_n().0) / parameters.br_q();
 90    let rlwe_n = parameters.rlwe_n().0;
 91    let rlwe_q = parameters.rlwe_q();
 92    let lwe_q = parameters.lwe_q();
 93    let rlwe_modop = ModOp::new(rlwe_q.clone());
 94    let rlwe_nttop = NttOp::new(rlwe_q, rlwe_n);
 95    let lwe_modop = ModOp::new(*parameters.lwe_q());
 96
 97    let rlwe_x_rgsw_decomposer = parameters.rlwe_rgsw_decomposer::<D>();
 98    let (rlwe_x_rgsw_gadget_a, rlwe_x_rgsw_gadget_b) = (
 99        rlwe_x_rgsw_decomposer.a().gadget_vector(),
100        rlwe_x_rgsw_decomposer.b().gadget_vector(),
101    );
102
103    let lwe_ks_decomposer = parameters.lwe_decomposer::<D>();
104
105    let mut server_key_stats = ServerKeyStats::new();
106
107    let mut rng = DefaultSecureRng::new();
108
109    // RGSW ciphertext noise
110    // Check noise in RGSW ciphertexts of ideal LWE secret elements
111    {
112        ideal_sk_lwe.iter().enumerate().for_each(|(s_index, s_i)| {
113            let rgsw_ct_i = server_key.rgsw_cts_lwe_si(s_index);
114
115            // X^{s[i]}
116            let m_si = encode_x_pow_si_with_emebedding_factor::<M::R, _>(
117                *s_i,
118                embedding_factor,
119                rlwe_n,
120                rlwe_q,
121            );
122
123            // RLWE'(-sm)
124            let mut neg_s_eval = M::R::try_convert_from(ideal_sk_rlwe.as_slice(), rlwe_q);
125            rlwe_modop.elwise_neg_mut(neg_s_eval.as_mut());
126            rlwe_nttop.forward(neg_s_eval.as_mut());
127
128            for j in 0..rlwe_x_rgsw_decomposer.a().decomposition_count() {
129                // RLWE(B^{j} * -s[X]*X^{s_lwe[i]})
130
131                // -s[X]*X^{s_lwe[i]}*B_j
132                let mut m_ideal = m_si.clone();
133                rlwe_nttop.forward(m_ideal.as_mut());
134                rlwe_modop.elwise_mul_mut(m_ideal.as_mut(), neg_s_eval.as_ref());
135                rlwe_nttop.backward(m_ideal.as_mut());
136                rlwe_modop.elwise_scalar_mul_mut(m_ideal.as_mut(), &rlwe_x_rgsw_gadget_a[j]);
137
138                // RLWE(-s*X^{s_lwe[i]}*B_j)
139                let mut rlwe_ct = M::zeros(2, rlwe_n);
140                rlwe_ct
141                    .get_row_mut(0)
142                    .copy_from_slice(rgsw_ct_i.get_row_slice(j));
143                rlwe_ct.get_row_mut(1).copy_from_slice(
144                    rgsw_ct_i.get_row_slice(j + rlwe_x_rgsw_decomposer.a().decomposition_count()),
145                );
146                // RGSW ciphertexts are in eval domain. We put RLWE ciphertexts back in
147                // coefficient domain
148                rlwe_ct
149                    .iter_rows_mut()
150                    .for_each(|r| rlwe_nttop.backward(r.as_mut()));
151
152                let mut m_back = M::R::zeros(rlwe_n);
153                decrypt_rlwe(
154                    &rlwe_ct,
155                    &ideal_sk_rlwe,
156                    &mut m_back,
157                    &rlwe_nttop,
158                    &rlwe_modop,
159                );
160
161                // diff
162                rlwe_modop.elwise_sub_mut(m_back.as_mut(), m_ideal.as_ref());
163                server_key_stats.add_noise_brk_rgsw_cts_nsm(&Vec::<i64>::try_convert_from(
164                    m_back.as_ref(),
165                    rlwe_q,
166                ));
167            }
168
169            // RLWE'(m)
170            for j in 0..rlwe_x_rgsw_decomposer.b().decomposition_count() {
171                // RLWE(B^{j} * X^{s_lwe[i]})
172
173                // X^{s_lwe[i]}*B_j
174                let mut m_ideal = m_si.clone();
175                rlwe_modop.elwise_scalar_mul_mut(m_ideal.as_mut(), &rlwe_x_rgsw_gadget_b[j]);
176
177                // RLWE(X^{s_lwe[i]}*B_j)
178                let mut rlwe_ct = M::zeros(2, rlwe_n);
179                rlwe_ct.get_row_mut(0).copy_from_slice(
180                    rgsw_ct_i
181                        .get_row_slice(j + (2 * rlwe_x_rgsw_decomposer.a().decomposition_count())),
182                );
183                rlwe_ct
184                    .get_row_mut(1)
185                    .copy_from_slice(rgsw_ct_i.get_row_slice(
186                        j + (2 * rlwe_x_rgsw_decomposer.a().decomposition_count())
187                            + rlwe_x_rgsw_decomposer.b().decomposition_count(),
188                    ));
189                rlwe_ct
190                    .iter_rows_mut()
191                    .for_each(|r| rlwe_nttop.backward(r.as_mut()));
192
193                let mut m_back = M::R::zeros(rlwe_n);
194                decrypt_rlwe(
195                    &rlwe_ct,
196                    &ideal_sk_rlwe,
197                    &mut m_back,
198                    &rlwe_nttop,
199                    &rlwe_modop,
200                );
201
202                // diff
203                rlwe_modop.elwise_sub_mut(m_back.as_mut(), m_ideal.as_ref());
204                server_key_stats.add_noise_brk_rgsw_cts_m(&Vec::<i64>::try_convert_from(
205                    m_back.as_ref(),
206                    rlwe_q,
207                ));
208            }
209        });
210    }
211
212    // Noise in ciphertext after 1 auto
213    // For each auto key g^k. Sample random polynomial m(X) and multiply with
214    // -s(X^{g^k}) using key corresponding to auto g^k. Then check the noise in
215    // resutling RLWE(m(X) * -s(X^{g^k}))
216    {
217        let neg_s = {
218            let mut s = M::R::try_convert_from(ideal_sk_rlwe.as_slice(), rlwe_q);
219            rlwe_modop.elwise_neg_mut(s.as_mut());
220            s
221        };
222        let g = parameters.g();
223        let br_q = parameters.br_q();
224        let g_dlogs = parameters.auto_element_dlogs();
225        let auto_decomposer = parameters.auto_decomposer::<D>();
226        let mut scratch_matrix = M::zeros(auto_decomposer.decomposition_count() + 2, rlwe_n);
227
228        g_dlogs.iter().for_each(|k| {
229            let g_pow_k = if *k == 0 {
230                -(g as isize)
231            } else {
232                (g.pow(*k as u32) % br_q) as isize
233            };
234
235            // Send s(X) -> s(X^{g^k})
236            let (auto_index_map, auto_sign_map) = crate::rgsw::generate_auto_map(rlwe_n, g_pow_k);
237            let mut neg_s_g_k = M::R::zeros(rlwe_n);
238            izip!(
239                neg_s.as_ref().iter(),
240                auto_index_map.iter(),
241                auto_sign_map.iter()
242            )
243            .for_each(|(el, to_index, to_sign)| {
244                if !to_sign {
245                    neg_s_g_k.as_mut()[*to_index] = rlwe_modop.neg(el);
246                } else {
247                    neg_s_g_k.as_mut()[*to_index] = *el;
248                }
249            });
250
251            let mut m = M::R::zeros(rlwe_n);
252            RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut());
253
254            // We want -m(X^{g^k})s(X^{g^k}) after key switch
255            let want_m = {
256                let mut m_g_k_eval = M::R::zeros(rlwe_n);
257                // send m(X) -> m(X^{g^k})
258                izip!(
259                    m.as_ref().iter(),
260                    auto_index_map.iter(),
261                    auto_sign_map.iter()
262                )
263                .for_each(|(el, to_index, to_sign)| {
264                    if !to_sign {
265                        m_g_k_eval.as_mut()[*to_index] = rlwe_modop.neg(el);
266                    } else {
267                        m_g_k_eval.as_mut()[*to_index] = *el;
268                    }
269                });
270
271                rlwe_nttop.forward(m_g_k_eval.as_mut());
272                let mut s_g_k = neg_s_g_k.clone();
273                rlwe_nttop.forward(s_g_k.as_mut());
274                rlwe_modop.elwise_mul_mut(m_g_k_eval.as_mut(), s_g_k.as_ref());
275                rlwe_nttop.backward(m_g_k_eval.as_mut());
276                m_g_k_eval
277            };
278
279            // RLWE auto sends part A, A(X), of RLWE to A(X^{g^k}) and then multiplies it
280            // with -s(X^{g^k}) using auto key. Deliberately set RLWE = (0, m(X))
281            // (ie. m in part A) to get back RLWE(-m(X^{g^k})s(X^{g^k}))
282            let mut rlwe = RlweCiphertext::<_, DefaultSecureRng>::new_trivial(M::zeros(2, rlwe_n));
283            rlwe.data.get_row_mut(0).copy_from_slice(m.as_ref());
284            rlwe.set_not_trivial();
285
286            galois_auto(
287                &mut rlwe,
288                server_key.galois_key_for_auto(*k),
289                &mut scratch_matrix,
290                &auto_index_map,
291                &auto_sign_map,
292                &rlwe_modop,
293                &rlwe_nttop,
294                &auto_decomposer,
295            );
296
297            // decrypt RLWE(-m(X)s(X^{g^k]}))
298            let mut back_m = M::R::zeros(rlwe_n);
299            decrypt_rlwe(&rlwe, &ideal_sk_rlwe, &mut back_m, &rlwe_nttop, &rlwe_modop);
300
301            // check difference
302            let mut diff = back_m;
303            rlwe_modop.elwise_sub_mut(diff.as_mut(), want_m.as_ref());
304            server_key_stats
305                .add_noise_post_1_auto(&Vec::<i64>::try_convert_from(diff.as_ref(), rlwe_q));
306        });
307
308        // sample random m
309
310        // key switch
311    }
312
313    // LWE Key switch
314    // LWE key switches LWE_in = LWE_{Q_ks,N, s}(m) = (b, a_0, ... a_N) -> LWE_out =
315    // LWE_{Q_{ks}, n, z}(m) = (b', a'_0, ..., a'n)
316    // If LWE_in = (0, a = {a_0, ..., a_N}), then LWE_out = LWE(-a \cdot s_{rlwe})
317    for _ in 0..10 {
318        let mut lwe_in = M::R::zeros(rlwe_n + 1);
319        RandomFillUniformInModulus::random_fill(&mut rng, lwe_q, &mut lwe_in.as_mut()[1..]);
320
321        // Key switch
322        let mut lwe_out = M::R::zeros(parameters.lwe_n().0 + 1);
323        lwe_key_switch(
324            &mut lwe_out,
325            &lwe_in,
326            server_key.lwe_ksk(),
327            &lwe_modop,
328            &lwe_ks_decomposer,
329        );
330
331        // -a \cdot s
332        let mut want_m = M::MatElement::zero();
333        izip!(lwe_in.as_ref().iter().skip(1), ideal_sk_rlwe.iter()).for_each(|(a, b)| {
334            want_m = lwe_modop.add(
335                &want_m,
336                &lwe_modop.mul(a, &lwe_q.map_element_from_i64(*b as i64)),
337            );
338        });
339        want_m = lwe_modop.neg(&want_m);
340
341        // decrypt lwe out
342        let back_m = decrypt_lwe(&lwe_out, &ideal_sk_lwe, &lwe_modop);
343
344        let noise = lwe_modop.sub(&want_m, &back_m);
345        server_key_stats.add_noise_post_kwe_key_switch(&vec![lwe_q.map_element_to_i64(&noise)]);
346    }
347
348    server_key_stats
349    // Auto keys noise
350
351    // Ksk noise
352}
353
354#[cfg(test)]
355mod tests {
356    use itertools::Itertools;
357
358    use super::collect_server_key_stats;
359
360    #[test]
361    #[cfg(feature = "interactive_mp")]
362    fn qwerty() {
363        use crate::{
364            aggregate_public_key_shares, aggregate_server_key_shares,
365            bool::keys::ServerKeyEvaluationDomain,
366            evaluator::MultiPartyCrs,
367            gen_client_key, gen_mp_keys_phase1, gen_mp_keys_phase2,
368            parameters::{BoolParameters, CiphertextModulus},
369            random::DefaultSecureRng,
370            set_mp_seed, set_parameter_set,
371            utils::WithLocal,
372            BoolEvaluator, DefaultDecomposer, ModularOpsU64, Ntt, NttBackendU64,
373        };
374
375        set_parameter_set(crate::ParameterSelector::HighCommunicationButFast2Party);
376        set_mp_seed(MultiPartyCrs::random().seed);
377        let parties = 2;
378        let cks = (0..parties).map(|_| gen_client_key()).collect_vec();
379        let pk_shares = cks.iter().map(|k| gen_mp_keys_phase1(k)).collect_vec();
380        let pk = aggregate_public_key_shares(&pk_shares);
381        let server_key_shares = cks
382            .iter()
383            .enumerate()
384            .map(|(index, k)| gen_mp_keys_phase2(k, index, parties, &pk))
385            .collect_vec();
386        let seeded_server_key = aggregate_server_key_shares(&server_key_shares);
387        let server_key_eval =
388            ServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(
389                &seeded_server_key,
390            );
391
392        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
393        let server_key_stats = collect_server_key_stats::<
394            _,
395            DefaultDecomposer<u64>,
396            NttBackendU64,
397            ModularOpsU64<CiphertextModulus<u64>>,
398            _,
399        >(parameters, &cks, &server_key_eval);
400
401        println!(
402            "Rgsw nsm std log2 {}",
403            server_key_stats.brk_rgsw_cts.0.std_dev().abs().log2()
404        );
405        println!(
406            "Rgsw m std log2 {}",
407            server_key_stats.brk_rgsw_cts.1.std_dev().abs().log2()
408        );
409        println!(
410            "rlwe post 1 auto std log2 {}",
411            server_key_stats.post_1_auto.std_dev().abs().log2()
412        );
413        println!(
414            "key switching noise rlwe secret s to lwe secret z std log2 {}",
415            server_key_stats.post_lwe_key_switch.std_dev().abs().log2()
416        );
417    }
418
419    #[test]
420    #[cfg(feature = "non_interactive_mp")]
421    fn querty2() {
422        use crate::{
423            aggregate_server_key_shares, bool::keys::NonInteractiveServerKeyEvaluationDomain,
424            evaluator::NonInteractiveMultiPartyCrs, gen_client_key, gen_server_key_share,
425            parameters::CiphertextModulus, random::DefaultSecureRng, set_common_reference_seed,
426            set_parameter_set, utils::WithLocal, BoolEvaluator, DefaultDecomposer, ModularOpsU64,
427            NttBackendU64,
428        };
429
430        set_parameter_set(crate::ParameterSelector::NonInteractiveMultiPartyLessThanOrEqualTo16);
431        set_common_reference_seed(NonInteractiveMultiPartyCrs::random().seed);
432        let parties = 2;
433        let cks = (0..parties).map(|i| gen_client_key()).collect_vec();
434        let server_key_shares = cks
435            .iter()
436            .enumerate()
437            .map(|(user_id, k)| gen_server_key_share(user_id, parties, k))
438            .collect_vec();
439        let server_key = aggregate_server_key_shares(&server_key_shares);
440
441        let server_key_eval =
442            NonInteractiveServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(
443                &server_key,
444            );
445
446        let parameters = BoolEvaluator::with_local(|e| e.parameters().clone());
447        let server_key_stats = collect_server_key_stats::<
448            _,
449            DefaultDecomposer<u64>,
450            NttBackendU64,
451            ModularOpsU64<CiphertextModulus<u64>>,
452            _,
453        >(parameters, &cks, &server_key_eval);
454
455        println!(
456            "Rgsw nsm std log2 {}",
457            server_key_stats.brk_rgsw_cts.0.std_dev().abs().log2()
458        );
459        println!(
460            "Rgsw m std log2 {}",
461            server_key_stats.brk_rgsw_cts.1.std_dev().abs().log2()
462        );
463        println!(
464            "rlwe post 1 auto std log2 {}",
465            server_key_stats.post_1_auto.std_dev().abs().log2()
466        );
467        println!(
468            "key switching noise rlwe secret s to lwe secret z std log2 {}",
469            server_key_stats.post_lwe_key_switch.std_dev().abs().log2()
470        );
471    }
472}