diff --git a/src/bool/evaluator.rs b/src/bool/evaluator.rs index b19e9cf..8456799 100644 --- a/src/bool/evaluator.rs +++ b/src/bool/evaluator.rs @@ -2309,728 +2309,6 @@ mod tests { use super::*; - #[test] - fn noise_tester() { - let bool_evaluator = BoolEvaluator::< - Vec>, - NttBackendU64, - ModularOpsU64>, - ModularOpsU64>, - ShoupServerKeyEvaluationDomain>>, - >::new(OPTIMISED_SMALL_MP_BOOL_PARAMS); - - // let (_, collective_pk, _, _, server_key_eval, ideal_client_key) = - // _multi_party_all_keygen(&bool_evaluator, 20); - let no_of_parties = 2; - let lwe_q = bool_evaluator.pbs_info.parameters.lwe_q(); - let rlwe_q = bool_evaluator.pbs_info.parameters.rlwe_q(); - let lwe_n = bool_evaluator.pbs_info.parameters.lwe_n().0; - let rlwe_n = bool_evaluator.pbs_info.parameters.rlwe_n().0; - let lwe_modop = &bool_evaluator.pbs_info.lwe_modop; - let rlwe_nttop = &bool_evaluator.pbs_info.rlwe_nttop; - let rlwe_modop = &bool_evaluator.pbs_info.rlwe_modop; - - // let rgsw_rgsw_decomposer = &bool_evaluator - // .pbs_info - // .parameters - // .rgsw_rgsw_decomposer::>(); - // let rgsw_rgsw_gadget_a = rgsw_rgsw_decomposer.0.gadget_vector(); - // let rgsw_rgsw_gadget_b = rgsw_rgsw_decomposer.1.gadget_vector(); - - let rlwe_rgsw_decomposer = &bool_evaluator.pbs_info.rlwe_rgsw_decomposer; - let rlwe_rgsw_gadget_a = rlwe_rgsw_decomposer.0.gadget_vector(); - let rlwe_rgsw_gadget_b = rlwe_rgsw_decomposer.1.gadget_vector(); - - let auto_decomposer = &bool_evaluator.pbs_info.auto_decomposer; - let auto_gadget = auto_decomposer.gadget_vector(); - - let parties = (0..no_of_parties) - .map(|_| bool_evaluator.client_key()) - .collect_vec(); - - let int_mp_seed = MultiPartyCrs::random(); - - let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()]; - parties.iter().for_each(|k| { - izip!( - ideal_rlwe_sk.iter_mut(), - InteractiveMultiPartyClientKey::sk_rlwe(k).iter() - ) - .for_each(|(ideal_i, s_i)| { - *ideal_i = *ideal_i + s_i; - }); - }); - let mut ideal_lwe_sk = vec![0i32; bool_evaluator.pbs_info.lwe_n()]; - parties.iter().for_each(|k| { - izip!( - ideal_lwe_sk.iter_mut(), - InteractiveMultiPartyClientKey::sk_lwe(k).iter() - ) - .for_each(|(ideal_i, s_i)| { - *ideal_i = *ideal_i + s_i; - }); - }); - - let mut rng = DefaultSecureRng::new(); - - // check noise in freshly encrypted RLWE ciphertext (ie var_fresh) - if false { - let mut rng = DefaultSecureRng::new(); - let mut check = Stats { samples: vec![] }; - for _ in 0..10 { - // generate a new collective public key - let mut pk_cr_seed = [0u8; 32]; - rng.fill_bytes(&mut pk_cr_seed); - let public_key_share = parties - .iter() - .map(|k| bool_evaluator.multi_party_public_key_share(&int_mp_seed, k)) - .collect_vec(); - let collective_pk = PublicKey::< - Vec>, - DefaultSecureRng, - ModularOpsU64>, - >::from(public_key_share.as_slice()); - - let mut m = vec![0u64; rlwe_n]; - RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut_slice()); - let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2]; - public_key_encrypt_rlwe::<_, _, _, _, i32, _>( - &mut rlwe_ct, - collective_pk.key(), - &m, - rlwe_modop, - rlwe_nttop, - &mut rng, - ); - - let mut m_back = vec![0u64; rlwe_n]; - decrypt_rlwe( - &rlwe_ct, - &ideal_rlwe_sk, - &mut m_back, - rlwe_nttop, - rlwe_modop, - ); - - rlwe_modop.elwise_sub_mut(m_back.as_mut_slice(), m.as_slice()); - - check.add_more(Vec::::try_convert_from(&m_back, rlwe_q).as_slice()); - } - - println!("Public key Std: {}", check.std_dev().abs().log2()); - } - - if true { - // Generate server key shares - let public_key_share = parties - .iter() - .map(|k| bool_evaluator.multi_party_public_key_share(&int_mp_seed, k)) - .collect_vec(); - let collective_pk = PublicKey::< - Vec>, - DefaultSecureRng, - ModularOpsU64>, - >::from(public_key_share.as_slice()); - - let server_key_shares = parties - .iter() - .enumerate() - .map(|(user_id, k)| { - bool_evaluator.multi_party_server_key_share( - user_id, - no_of_parties, - &int_mp_seed, - collective_pk.key(), - k, - ) - }) - .collect_vec(); - - let seeded_server_key = - bool_evaluator.aggregate_multi_party_server_key_shares(&server_key_shares); - - // Check noise in RGSW ciphertexts of ideal LWE secret elements - if true { - let mut check = Stats { samples: vec![] }; - izip!(ideal_lwe_sk.iter(), seeded_server_key.rgsw_cts().iter()).for_each( - |(s_i, rgsw_ct_i)| { - // X^{s[i]} - let m_si = encode_x_pow_si_with_emebedding_factor::, _>( - *s_i, - bool_evaluator.pbs_info.embedding_factor, - rlwe_n, - rlwe_q, - ); - - // RLWE'(-sm) - let mut neg_s_eval = - Vec::::try_convert_from(ideal_rlwe_sk.as_slice(), rlwe_q); - rlwe_modop.elwise_neg_mut(&mut neg_s_eval); - rlwe_nttop.forward(&mut neg_s_eval); - // let tmp_decomp = bool_evaluator - // .parameters() - // .rgsw_rgsw_decomposer::>(); - // let tmp_gadget = tmp_decomp.a().gadget_vector() - for j in 0..rlwe_rgsw_decomposer.a().decomposition_count() { - // RLWE(B^{j} * -s[X]*X^{s_lwe[i]}) - - // -s[X]*X^{s_lwe[i]}*B_j - let mut m_ideal = m_si.clone(); - rlwe_nttop.forward(m_ideal.as_mut_slice()); - rlwe_modop - .elwise_mul_mut(m_ideal.as_mut_slice(), neg_s_eval.as_slice()); - rlwe_nttop.backward(m_ideal.as_mut_slice()); - rlwe_modop.elwise_scalar_mul_mut( - m_ideal.as_mut_slice(), - &rlwe_rgsw_gadget_a[j], - ); - - // RLWE(-s*X^{s_lwe[i]}*B_j) - let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2]; - rlwe_ct[0].copy_from_slice(&rgsw_ct_i[j]); - rlwe_ct[1].copy_from_slice( - &rgsw_ct_i[j + rlwe_rgsw_decomposer.a().decomposition_count()], - ); - - let mut m_back = vec![0u64; rlwe_n]; - decrypt_rlwe( - &rlwe_ct, - &ideal_rlwe_sk, - &mut m_back, - rlwe_nttop, - rlwe_modop, - ); - - // diff - rlwe_modop.elwise_sub_mut(&mut m_back, &m_ideal); - check.add_more(&Vec::::try_convert_from(&m_back, rlwe_q)); - } - - // RLWE'(m) - for j in 0..rlwe_rgsw_decomposer.b().decomposition_count() { - // RLWE(B^{j} * X^{s_lwe[i]}) - - // X^{s_lwe[i]}*B_j - let mut m_ideal = m_si.clone(); - rlwe_modop.elwise_scalar_mul_mut( - m_ideal.as_mut_slice(), - &rlwe_rgsw_gadget_b[j], - ); - - // RLWE(X^{s_lwe[i]}*B_j) - let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2]; - rlwe_ct[0].copy_from_slice( - &rgsw_ct_i - [j + (2 * rlwe_rgsw_decomposer.a().decomposition_count())], - ); - rlwe_ct[1].copy_from_slice( - &rgsw_ct_i[j - + (2 * rlwe_rgsw_decomposer.a().decomposition_count() - + rlwe_rgsw_decomposer.b().decomposition_count())], - ); - - let mut m_back = vec![0u64; rlwe_n]; - decrypt_rlwe( - &rlwe_ct, - &ideal_rlwe_sk, - &mut m_back, - rlwe_nttop, - rlwe_modop, - ); - - // diff - rlwe_modop.elwise_sub_mut(&mut m_back, &m_ideal); - check.add_more(&Vec::::try_convert_from(&m_back, rlwe_q)); - } - }, - ); - println!( - "RGSW Std: {} {} ;; max={}", - check.mean(), - check.std_dev().abs().log2(), - check.samples.iter().max().unwrap() - ); - } - - // server key in Evaluation domain - let runtime_server_key = - ShoupServerKeyEvaluationDomain::from(ServerKeyEvaluationDomain::< - _, - _, - DefaultSecureRng, - NttBackendU64, - >::from(&seeded_server_key)); - - // check noise in RLWE x RGSW(X^{s_i}) where RGSW is accunulated RGSW ciphertext - if false { - let mut check = Stats { samples: vec![] }; - - ideal_lwe_sk.iter().enumerate().for_each(|(index, s_i)| { - let rgsw_ct_i = runtime_server_key.rgsw_ct_lwe_si(index).as_ref(); - - let mut m = vec![0u64; rlwe_n]; - RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut_slice()); - let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2]; - public_key_encrypt_rlwe::<_, _, _, _, i32, _>( - &mut rlwe_ct, - collective_pk.key(), - &m, - rlwe_modop, - rlwe_nttop, - &mut rng, - ); - - let mut rlwe_after = RlweCiphertext::<_, DefaultSecureRng> { - data: rlwe_ct.clone(), - is_trivial: false, - _phatom: PhantomData, - }; - let mut scratch = vec![ - vec![0u64; rlwe_n]; - std::cmp::max( - rlwe_rgsw_decomposer.0.decomposition_count(), - rlwe_rgsw_decomposer.1.decomposition_count() - ) + 2 - ]; - rlwe_by_rgsw( - &mut rlwe_after, - rgsw_ct_i, - &mut scratch, - rlwe_rgsw_decomposer, - rlwe_nttop, - rlwe_modop, - ); - - // m1 = X^{s[i]} - let mut m1 = vec![0u64; rlwe_n]; - let s_i = *s_i * (bool_evaluator.pbs_info.embedding_factor as i32); - if s_i < 0 { - m1[rlwe_n - (s_i.abs() as usize)] = rlwe_q.neg_one() - } else { - m1[s_i as usize] = 1; - } - - // (m+e) * m1 - let mut m_plus_e_times_m1 = vec![0u64; rlwe_n]; - decrypt_rlwe( - &rlwe_ct, - &ideal_rlwe_sk, - &mut m_plus_e_times_m1, - rlwe_nttop, - rlwe_modop, - ); - rlwe_nttop.forward(m_plus_e_times_m1.as_mut_slice()); - rlwe_nttop.forward(m1.as_mut_slice()); - - rlwe_modop.elwise_mul_mut(m_plus_e_times_m1.as_mut_slice(), m1.as_slice()); - rlwe_nttop.backward(m_plus_e_times_m1.as_mut_slice()); - - // Resulting RLWE ciphertext will equal: (m0m1 + em1) + e_{rlsw x rgsw}. - // Hence, resulting rlwe ciphertext will have error em1 + e_{rlwe x rgsw}. - // Here we're only concerned with e_{rlwe x rgsw}, that is noise added by - // RLWExRGSW. Also note in practice m1 is a monomial, for ex, X^{s_{i}}, for - // some i and var(em1) = var(e). - let mut m_plus_e_times_m1_more_e = vec![0u64; rlwe_n]; - decrypt_rlwe( - &rlwe_after, - &ideal_rlwe_sk, - &mut m_plus_e_times_m1_more_e, - rlwe_nttop, - rlwe_modop, - ); - - // diff - rlwe_modop.elwise_sub_mut( - m_plus_e_times_m1_more_e.as_mut_slice(), - m_plus_e_times_m1.as_slice(), - ); - - // let noise = measure_noise( - // &rlwe_after, - // &m_plus_e_times_m1, - // rlwe_nttop, - // rlwe_modop, - // ideal_client_key.sk_rlwe.values(), - // ); - // print!("NOISE: {}", noise); - - check.add_more(&Vec::::try_convert_from( - &m_plus_e_times_m1_more_e, - rlwe_q, - )); - }); - println!( - "RLWE x RGSW, where RGSW has noise var_brk, std: {} {}", - check.std_dev(), - check.std_dev().abs().log2() - ) - } - - // check noise in Auto key - if false { - let mut check = Stats { samples: vec![] }; - - let mut neg_s_poly = Vec::::try_convert_from(ideal_rlwe_sk.as_slice(), rlwe_q); - rlwe_modop.elwise_neg_mut(neg_s_poly.as_mut_slice()); - - let g = bool_evaluator.pbs_info.g(); - let br_q = bool_evaluator.pbs_info.br_q(); - let auto_element_dlogs = bool_evaluator.pbs_info.parameters.auto_element_dlogs(); - for i in auto_element_dlogs.into_iter() { - let g_pow = if i == 0 { - -g - } else { - (((g as usize).pow(i as u32)) % br_q) as isize - }; - - // -s[X^k] - let (auto_indices, auto_sign) = generate_auto_map(rlwe_n, g_pow); - let mut neg_s_poly_auto_i = vec![0u64; rlwe_n]; - izip!(neg_s_poly.iter(), auto_indices.iter(), auto_sign.iter()).for_each( - |(v, to_i, to_sign)| { - if !to_sign { - neg_s_poly_auto_i[*to_i] = rlwe_modop.neg(v); - } else { - neg_s_poly_auto_i[*to_i] = *v; - } - }, - ); - - let mut auto_key_i = runtime_server_key.galois_key_for_auto(i).as_ref().clone(); //send i^th auto key to coefficient domain - auto_key_i - .iter_mut() - .for_each(|r| rlwe_nttop.backward(r.as_mut_slice())); - auto_gadget.iter().enumerate().for_each(|(i, b_i)| { - // B^i * -s[X^k] - let mut m_ideal = neg_s_poly_auto_i.clone(); - - rlwe_modop.elwise_scalar_mul_mut(m_ideal.as_mut_slice(), b_i); - - let mut m_out = vec![0u64; rlwe_n]; - let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2]; - rlwe_ct[0].copy_from_slice(&auto_key_i[i]); - rlwe_ct[1].copy_from_slice( - &auto_key_i[auto_decomposer.decomposition_count() + i], - ); - decrypt_rlwe(&rlwe_ct, &ideal_rlwe_sk, &mut m_out, rlwe_nttop, rlwe_modop); - - // diff - rlwe_modop.elwise_sub_mut(m_out.as_mut_slice(), m_ideal.as_slice()); - - check.add_more(&Vec::::try_convert_from(&m_out, rlwe_q)); - }); - } - - println!("Auto key noise std dev: {}", check.std_dev().abs().log2()); - } - - // check noise in RLWE(X^k) after sending RLWE(X) -> RLWE(X^k)using collective - // auto key - if false { - let mut check = Stats { samples: vec![] }; - let br_q = bool_evaluator.pbs_info.br_q(); - let g = bool_evaluator.pbs_info.g(); - let auto_element_dlogs = bool_evaluator.pbs_info.parameters.auto_element_dlogs(); - for i in auto_element_dlogs.into_iter() { - for _ in 0..10 { - let mut m = vec![0u64; rlwe_n]; - RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut_slice()); - let mut rlwe_ct = RlweCiphertext::<_, DefaultSecureRng> { - data: vec![vec![0u64; rlwe_n]; 2], - is_trivial: false, - _phatom: PhantomData, - }; - public_key_encrypt_rlwe::<_, _, _, _, i32, _>( - &mut rlwe_ct, - collective_pk.key(), - &m, - rlwe_modop, - rlwe_nttop, - &mut rng, - ); - - // We're only interested in noise increased as a result of automorphism. - // Hence, we take m+e as the bench. - let mut m_plus_e = vec![0u64; rlwe_n]; - decrypt_rlwe( - &rlwe_ct, - &ideal_rlwe_sk, - &mut m_plus_e, - rlwe_nttop, - rlwe_modop, - ); - - let auto_key = runtime_server_key.galois_key_for_auto(i).as_ref(); - let (auto_map_index, auto_map_sign) = - bool_evaluator.pbs_info.rlwe_auto_map(i); - let mut scratch = - vec![vec![0u64; rlwe_n]; auto_decomposer.decomposition_count() + 2]; - galois_auto( - &mut rlwe_ct, - auto_key, - &mut scratch, - &auto_map_index, - &auto_map_sign, - rlwe_modop, - rlwe_nttop, - auto_decomposer, - ); - - // send m+e from X to X^k - let mut m_plus_e_auto = vec![0u64; rlwe_n]; - izip!(m_plus_e.iter(), auto_map_index.iter(), auto_map_sign.iter()) - .for_each(|(v, to_index, to_sign)| { - if !to_sign { - m_plus_e_auto[*to_index] = rlwe_modop.neg(v); - } else { - m_plus_e_auto[*to_index] = *v - } - }); - - let mut m_out = vec![0u64; rlwe_n]; - decrypt_rlwe(&rlwe_ct, &ideal_rlwe_sk, &mut m_out, rlwe_nttop, rlwe_modop); - - // diff - rlwe_modop.elwise_sub_mut(m_out.as_mut_slice(), m_plus_e_auto.as_slice()); - - check.add_more(&Vec::::try_convert_from(m_out.as_slice(), rlwe_q)); - } - } - - println!("Rlwe Auto Noise Std: {}", check.std_dev().abs().log2()); - } - - // Check noise growth in ksk - // TODO check in LWE key switching keys - if false { - // 1. encrypt LWE ciphertext - // 2. Key switching - // 3. - let mut check = Stats { samples: vec![] }; - - for _ in 0..1024 { - // Encrypt m \in Q_{ks} using RLWE sk - let mut lwe_in_ct = vec![0u64; rlwe_n + 1]; - let m = RandomElementInModulus::random(&mut rng, &lwe_q.q().unwrap()); - encrypt_lwe(&mut lwe_in_ct, &m, &ideal_rlwe_sk, lwe_modop, &mut rng); - - // Key switch - let mut lwe_out = vec![0u64; lwe_n + 1]; - lwe_key_switch( - &mut lwe_out, - &lwe_in_ct, - runtime_server_key.lwe_ksk(), - lwe_modop, - bool_evaluator.pbs_info.lwe_decomposer(), - ); - - // We only care about noise added by LWE key switch - // m+e - let m_plus_e = decrypt_lwe(&lwe_in_ct, &ideal_rlwe_sk, lwe_modop); - - let m_plus_e_plus_lwe_ksk_noise = - decrypt_lwe(&lwe_out, &ideal_lwe_sk, lwe_modop); - - let diff = lwe_modop.sub(&m_plus_e_plus_lwe_ksk_noise, &m_plus_e); - - check.add_more(&vec![lwe_q.map_element_to_i64(&diff)]); - } - - println!("Lwe ksk std dev: {}", check.std_dev().abs().log2()); - } - } - - // Check noise in fresh RGSW ciphertexts, ie X^{s_j[i]}, must equalnoise in - // // fresh RLWE ciphertext - if true {} - // test LWE ksk from RLWE -> LWE - // if false { - // let logp = 2; - // let mut rng = DefaultSecureRng::new(); - - // let m = 1; - // let encoded_m = m << (lwe_logq - logp); - - // // Encrypt - // let mut lwe_ct = vec![0u64; rlwe_n + 1]; - // encrypt_lwe( - // &mut lwe_ct, - // &encoded_m, - // ideal_client_key.sk_rlwe.values(), - // lwe_modop, - // &mut rng, - // ); - - // // key switch - // let lwe_decomposer = &bool_evaluator.decomposer_lwe; - // let mut lwe_out = vec![0u64; lwe_n + 1]; - // lwe_key_switch( - // &mut lwe_out, - // &lwe_ct, - // &server_key_eval.lwe_ksk, - // lwe_modop, - // lwe_decomposer, - // ); - - // let encoded_m_back = decrypt_lwe(&lwe_out, - // ideal_client_key.sk_lwe.values(), lwe_modop); let m_back - // = ((encoded_m_back as f64 * (1 << logp) as f64) / - // (lwe_q as f64)).round() as u64; dbg!(m_back, m); - - // let noise = measure_noise_lwe( - // &lwe_out, - // ideal_client_key.sk_lwe.values(), - // lwe_modop, - // &encoded_m, - // ); - - // println!("Noise: {noise}"); - // } - - // Measure noise in RGSW ciphertexts of ideal LWE secrets - // if true { - // let gadget_vec = gadget_vector( - // bool_evaluator.parameters.rlwe_logq, - // bool_evaluator.parameters.logb_rgsw, - // bool_evaluator.parameters.d_rgsw, - // ); - - // for i in 0..20 { - // // measure noise in RGSW(s[i]) - // let si = - // ideal_client_key.sk_lwe.values[i] * - // (bool_evaluator.embedding_factor as i32); let mut - // si_poly = vec![0u64; rlwe_n]; if si < 0 { - // si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1; - // } else { - // si_poly[(si.abs() as usize)] = 1; - // } - - // let mut rgsw_si = server_key_eval.rgsw_cts[i].clone(); - // rgsw_si - // .iter_mut() - // .for_each(|ri| rlwe_nttop.backward(ri.as_mut())); - - // println!("####### Noise in RGSW(X^s_{i}) #######"); - // _measure_noise_rgsw( - // &rgsw_si, - // &si_poly, - // ideal_client_key.sk_rlwe.values(), - // &gadget_vec, - // rlwe_q, - // ); - // println!("####### ##################### #######"); - // } - // } - - // // measure noise grwoth in RLWExRGSW - // if true { - // let mut rng = DefaultSecureRng::new(); - // let mut carry_m = vec![0u64; rlwe_n]; - // RandomUniformDist1::random_fill(&mut rng, &rlwe_q, - // carry_m.as_mut_slice()); - - // // RGSW(carrym) - // let trivial_rlwect = vec![vec![0u64; rlwe_n],carry_m.clone()]; - // let mut rlwe_ct = RlweCiphertext::<_, - // DefaultSecureRng>::from_raw(trivial_rlwect, true); - - // let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n]; - // d_rgsw + 2]; let mul_mod = - // |v0: &u64, v1: &u64| (((*v0 as u128 * *v1 as u128) % (rlwe_q as u128)) as u64); - - // for i in 0..bool_evaluator.parameters.lwe_n { - // rlwe_by_rgsw( - // &mut rlwe_ct, - // server_key_eval.rgsw_ct_lwe_si(i), - // &mut scratch_matrix_dplus2_ring, - // rlwe_decomposer, - // rlwe_nttop, - // rlwe_modop, - // ); - - // // carry_m[X] * s_i[X] - // let si = - // ideal_client_key.sk_lwe.values[i] * - // (bool_evaluator.embedding_factor as i32); let mut - // si_poly = vec![0u64; rlwe_n]; if si < 0 { - // si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1; - // } else { - // si_poly[(si.abs() as usize)] = 1; - // } - // carry_m = negacyclic_mul(&carry_m, &si_poly, mul_mod, - // rlwe_q); - - // let noise = measure_noise( - // &rlwe_ct, - // &carry_m, - // rlwe_nttop, - // rlwe_modop, - // ideal_client_key.sk_rlwe.values(), - // ); - // println!("Noise RLWE(carry_m) accumulating {i}^th secret - // monomial: {noise}"); } - // } - - // // Check galois keys - // if false { - // let g = bool_evaluator.g() as isize; - // let mut rng = DefaultSecureRng::new(); - // let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n]; - // d_rgsw + 2]; for i in [g, -g] { - // let mut m = vec![0u64; rlwe_n]; - // RandomUniformDist1::random_fill(&mut rng, &rlwe_q, - // m.as_mut_slice()); let mut rlwe_ct = { - // let mut data = vec![vec![0u64; rlwe_n]; 2]; - // public_key_encrypt_rlwe( - // &mut data, - // &collective_pk.key, - // &m, - // rlwe_modop, - // rlwe_nttop, - // &mut rng, - // ); - // RlweCiphertext::<_, DefaultSecureRng>::from_raw(data, - // false) }; - - // let auto_key = server_key_eval.galois_key_for_auto(i); - // let (auto_map_index, auto_map_sign) = - // generate_auto_map(rlwe_n, i); galois_auto( - // &mut rlwe_ct, - // auto_key, - // &mut scratch_matrix_dplus2_ring, - // &auto_map_index, - // &auto_map_sign, - // rlwe_modop, - // rlwe_nttop, - // rlwe_decomposer, - // ); - - // // send m(X) -> m(X^i) - // let mut m_k = vec![0u64; rlwe_n]; - // izip!(m.iter(), auto_map_index.iter(), - // auto_map_sign.iter()).for_each( |(mi, to_index,to_sign)| - // // { if !to_sign { - // m_k[*to_index] = rlwe_q - *mi; } else { - // m_k[*to_index] = *mi; - // } - // }, - // ); - - // // measure noise - // let noise = measure_noise( - // &rlwe_ct, - // &m_k, - // rlwe_nttop, - // rlwe_modop, - // ideal_client_key.sk_rlwe.values(), - // ); - - // println!("Noise after auto k={i}: {noise}"); - // } - // } - } - #[test] fn testtest() { let evaluator = BoolEvaluator::< @@ -3155,4 +2433,5 @@ mod tests { } println!("Stats: {}", stats.std_dev().abs().log2()); } + } diff --git a/src/bool/keys.rs b/src/bool/keys.rs index 1d2774a..159dc1e 100644 --- a/src/bool/keys.rs +++ b/src/bool/keys.rs @@ -1270,6 +1270,17 @@ pub(super) mod tests { ideal_rlwe_sk } + pub(crate) fn ideal_sk_lwe(cks: &[ClientKey]) -> Vec { + let mut ideal_rlwe_sk = cks[0].sk_lwe(); + cks.iter().skip(1).for_each(|k| { + let sk_rlwe = k.sk_lwe(); + izip!(ideal_rlwe_sk.iter_mut(), sk_rlwe.iter()).for_each(|(a, b)| { + *a = *a + b; + }); + }); + ideal_rlwe_sk + } + pub(crate) fn measure_noise_lwe< R: Row, S, diff --git a/src/bool/mod.rs b/src/bool/mod.rs index 6f30238..774c48a 100644 --- a/src/bool/mod.rs +++ b/src/bool/mod.rs @@ -3,6 +3,7 @@ mod keys; mod noise; pub(crate) mod parameters; +use keys::tests; pub(crate) use keys::PublicKey; #[cfg(feature = "interactive_mp")] @@ -68,3 +69,6 @@ mod common_mp_enc_dec { } } } + +#[cfg(test)] +mod print_noise; diff --git a/src/bool/print_noise.rs b/src/bool/print_noise.rs new file mode 100644 index 0000000..fb055b5 --- /dev/null +++ b/src/bool/print_noise.rs @@ -0,0 +1,1047 @@ +use std::{fmt::Debug, iter::Sum, thread::panicking}; + +use itertools::izip; +use num_traits::{FromPrimitive, Pow, PrimInt, Zero}; +use rand_distr::uniform::SampleUniform; + +use crate::{ + backend::{GetModulus, Modulus}, + decomposer::RlweDecomposer, + lwe::{decrypt_lwe, lwe_key_switch}, + parameters::{self, BoolParameters, CiphertextModulus}, + pbs::PbsKey, + random::{DefaultSecureRng, RandomFillUniformInModulus}, + rgsw::{decrypt_rlwe, galois_auto, IsTrivial, RlweCiphertext}, + utils::{encode_x_pow_si_with_emebedding_factor, tests::Stats, TryConvertFrom1, WithLocal}, + ArithmeticOps, ClientKey, Decomposer, DefaultDecomposer, MatrixEntity, MatrixMut, ModInit, Ntt, + NttInit, RowEntity, RowMut, VectorOps, +}; + +use super::{ + keys::{ + tests::{ideal_sk_lwe, ideal_sk_rlwe}, + SeededMultiPartyServerKey, ServerKeyEvaluationDomain, + }, + BoolEvaluator, +}; + +struct ServerKeyStats { + brk_rgsw_cts: (Stats, Stats), + post_1_auto: Stats, + post_lwe_key_switch: Stats, +} + +impl ServerKeyStats +where + T: for<'a> Sum<&'a T>, +{ + fn new() -> Self { + ServerKeyStats { + brk_rgsw_cts: (Stats::default(), Stats::default()), + post_1_auto: Stats::default(), + post_lwe_key_switch: Stats::default(), + } + } + + fn add_noise_brk_rgsw_cts_nsm(&mut self, noise: &[T]) { + self.brk_rgsw_cts.0.add_more(noise); + } + + fn add_noise_brk_rgsw_cts_m(&mut self, noise: &[T]) { + self.brk_rgsw_cts.1.add_more(noise); + } + + fn add_noise_post_1_auto(&mut self, noise: &[T]) { + self.post_1_auto.add_more(&noise); + } + + fn add_noise_post_kwe_key_switch(&mut self, noise: &[T]) { + self.post_lwe_key_switch.add_more(&noise); + } +} + +fn collect_server_key_stats< + M: MatrixEntity + MatrixMut, + D: Decomposer, + NttOp: NttInit> + Ntt, + ModOp: VectorOps + + ArithmeticOps + + ModInit> + + GetModulus, Element = M::MatElement>, +>( + parameters: BoolParameters, + client_keys: &[ClientKey], + server_key: &ServerKeyEvaluationDomain, DefaultSecureRng, NttOp>, +) -> ServerKeyStats +where + M::R: RowMut + RowEntity + TryConvertFrom1<[i32], CiphertextModulus> + Clone, + M::MatElement: Copy + PrimInt + FromPrimitive + SampleUniform + Zero + Debug, +{ + let ideal_sk_rlwe = ideal_sk_rlwe(client_keys); + let ideal_sk_lwe = ideal_sk_lwe(client_keys); + + let embedding_factor = (2 * parameters.rlwe_n().0) / parameters.br_q(); + let rlwe_n = parameters.rlwe_n().0; + let rlwe_q = parameters.rlwe_q(); + let lwe_q = parameters.lwe_q(); + let rlwe_modop = ModOp::new(rlwe_q.clone()); + let rlwe_nttop = NttOp::new(rlwe_q, rlwe_n); + let lwe_modop = ModOp::new(*parameters.lwe_q()); + + let rlwe_x_rgsw_decomposer = parameters.rlwe_rgsw_decomposer::(); + let (rlwe_x_rgsw_gadget_a, rlwe_x_rgsw_gadget_b) = ( + rlwe_x_rgsw_decomposer.a().gadget_vector(), + rlwe_x_rgsw_decomposer.b().gadget_vector(), + ); + + let lwe_ks_decomposer = parameters.lwe_decomposer::(); + + let mut server_key_stats = ServerKeyStats::new(); + + let mut rng = DefaultSecureRng::new(); + + // RGSW ciphertext noise + // Check noise in RGSW ciphertexts of ideal LWE secret elements + { + izip!(ideal_sk_lwe.iter(), server_key.rgsw_cts().iter()).for_each(|(s_i, rgsw_ct_i)| { + // X^{s[i]} + let m_si = encode_x_pow_si_with_emebedding_factor::( + *s_i, + embedding_factor, + rlwe_n, + rlwe_q, + ); + + // RLWE'(-sm) + let mut neg_s_eval = M::R::try_convert_from(ideal_sk_rlwe.as_slice(), rlwe_q); + rlwe_modop.elwise_neg_mut(neg_s_eval.as_mut()); + rlwe_nttop.forward(neg_s_eval.as_mut()); + + for j in 0..rlwe_x_rgsw_decomposer.a().decomposition_count() { + // RLWE(B^{j} * -s[X]*X^{s_lwe[i]}) + + // -s[X]*X^{s_lwe[i]}*B_j + let mut m_ideal = m_si.clone(); + rlwe_nttop.forward(m_ideal.as_mut()); + rlwe_modop.elwise_mul_mut(m_ideal.as_mut(), neg_s_eval.as_ref()); + rlwe_nttop.backward(m_ideal.as_mut()); + rlwe_modop.elwise_scalar_mul_mut(m_ideal.as_mut(), &rlwe_x_rgsw_gadget_a[j]); + + // RLWE(-s*X^{s_lwe[i]}*B_j) + let mut rlwe_ct = M::zeros(2, rlwe_n); + rlwe_ct + .get_row_mut(0) + .copy_from_slice(rgsw_ct_i.get_row_slice(j)); + rlwe_ct.get_row_mut(1).copy_from_slice( + rgsw_ct_i.get_row_slice(j + rlwe_x_rgsw_decomposer.a().decomposition_count()), + ); + // RGSW ciphertexts are in eval domain. We put RLWE ciphertexts back in + // coefficient domain + rlwe_ct + .iter_rows_mut() + .for_each(|r| rlwe_nttop.backward(r.as_mut())); + + let mut m_back = M::R::zeros(rlwe_n); + decrypt_rlwe( + &rlwe_ct, + &ideal_sk_rlwe, + &mut m_back, + &rlwe_nttop, + &rlwe_modop, + ); + + // diff + rlwe_modop.elwise_sub_mut(m_back.as_mut(), m_ideal.as_ref()); + server_key_stats.add_noise_brk_rgsw_cts_nsm(&Vec::::try_convert_from( + m_back.as_ref(), + rlwe_q, + )); + } + + // RLWE'(m) + for j in 0..rlwe_x_rgsw_decomposer.b().decomposition_count() { + // RLWE(B^{j} * X^{s_lwe[i]}) + + // X^{s_lwe[i]}*B_j + let mut m_ideal = m_si.clone(); + rlwe_modop.elwise_scalar_mul_mut(m_ideal.as_mut(), &rlwe_x_rgsw_gadget_b[j]); + + // RLWE(X^{s_lwe[i]}*B_j) + let mut rlwe_ct = M::zeros(2, rlwe_n); + rlwe_ct.get_row_mut(0).copy_from_slice( + rgsw_ct_i + .get_row_slice(j + (2 * rlwe_x_rgsw_decomposer.a().decomposition_count())), + ); + rlwe_ct + .get_row_mut(1) + .copy_from_slice(rgsw_ct_i.get_row_slice( + j + (2 * rlwe_x_rgsw_decomposer.a().decomposition_count()) + + rlwe_x_rgsw_decomposer.b().decomposition_count(), + )); + rlwe_ct + .iter_rows_mut() + .for_each(|r| rlwe_nttop.backward(r.as_mut())); + + let mut m_back = M::R::zeros(rlwe_n); + decrypt_rlwe( + &rlwe_ct, + &ideal_sk_rlwe, + &mut m_back, + &rlwe_nttop, + &rlwe_modop, + ); + + // diff + rlwe_modop.elwise_sub_mut(m_back.as_mut(), m_ideal.as_ref()); + server_key_stats.add_noise_brk_rgsw_cts_m(&Vec::::try_convert_from( + m_back.as_ref(), + rlwe_q, + )); + } + }); + } + + // Noise in ciphertext after 1 auto + // For each auto key g^k. Sample random polynomial m(X) and multiply with + // -s(X^{g^k}) using key corresponding to auto g^k. Then check the noise in + // resutling RLWE(m(X) * -s(X^{g^k})) + { + let neg_s = { + let mut s = M::R::try_convert_from(ideal_sk_rlwe.as_slice(), rlwe_q); + rlwe_modop.elwise_neg_mut(s.as_mut()); + s + }; + let g = parameters.g(); + let br_q = parameters.br_q(); + let g_dlogs = parameters.auto_element_dlogs(); + let auto_decomposer = parameters.auto_decomposer::(); + let mut scratch_matrix = M::zeros(auto_decomposer.decomposition_count() + 2, rlwe_n); + + g_dlogs.iter().for_each(|k| { + let g_pow_k = if *k == 0 { + -(g as isize) + } else { + (g.pow(*k as u32) % br_q) as isize + }; + + // Send s(X) -> s(X^{g^k}) + let (auto_index_map, auto_sign_map) = crate::rgsw::generate_auto_map(rlwe_n, g_pow_k); + let mut neg_s_g_k = M::R::zeros(rlwe_n); + izip!( + neg_s.as_ref().iter(), + auto_index_map.iter(), + auto_sign_map.iter() + ) + .for_each(|(el, to_index, to_sign)| { + if !to_sign { + neg_s_g_k.as_mut()[*to_index] = rlwe_modop.neg(el); + } else { + neg_s_g_k.as_mut()[*to_index] = *el; + } + }); + + let mut m = M::R::zeros(rlwe_n); + RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, m.as_mut()); + + // We want -m(X^{g^k})s(X^{g^k}) after key switch + let want_m = { + let mut m_g_k_eval = M::R::zeros(rlwe_n); + // send m(X) -> m(X^{g^k}) + izip!( + m.as_ref().iter(), + auto_index_map.iter(), + auto_sign_map.iter() + ) + .for_each(|(el, to_index, to_sign)| { + if !to_sign { + m_g_k_eval.as_mut()[*to_index] = rlwe_modop.neg(el); + } else { + m_g_k_eval.as_mut()[*to_index] = *el; + } + }); + + rlwe_nttop.forward(m_g_k_eval.as_mut()); + let mut s_g_k = neg_s_g_k.clone(); + rlwe_nttop.forward(s_g_k.as_mut()); + rlwe_modop.elwise_mul_mut(m_g_k_eval.as_mut(), s_g_k.as_ref()); + rlwe_nttop.backward(m_g_k_eval.as_mut()); + m_g_k_eval + }; + + // RLWE auto sends part A, A(X), of RLWE to A(X^{g^k}) and then multiplies it + // with -s(X^{g^k}) using auto key. Deliberately set RLWE = (0, m(X)) + // (ie. m in part A) to get back RLWE(-m(X^{g^k})s(X^{g^k})) + let mut rlwe = RlweCiphertext::<_, DefaultSecureRng>::new_trivial(M::zeros(2, rlwe_n)); + rlwe.data.get_row_mut(0).copy_from_slice(m.as_ref()); + rlwe.set_not_trivial(); + + galois_auto( + &mut rlwe, + server_key.galois_key_for_auto(*k), + &mut scratch_matrix, + &auto_index_map, + &auto_sign_map, + &rlwe_modop, + &rlwe_nttop, + &auto_decomposer, + ); + + // decrypt RLWE(-m(X)s(X^{g^k]})) + let mut back_m = M::R::zeros(rlwe_n); + decrypt_rlwe(&rlwe, &ideal_sk_rlwe, &mut back_m, &rlwe_nttop, &rlwe_modop); + + // check difference + let mut diff = back_m; + rlwe_modop.elwise_sub_mut(diff.as_mut(), want_m.as_ref()); + server_key_stats + .add_noise_post_1_auto(&Vec::::try_convert_from(diff.as_ref(), rlwe_q)); + }); + + // sample random m + + // key switch + } + + // LWE Key switch + // LWE key switches LWE_in = LWE_{Q_ks,N, s}(m) = (b, a_0, ... a_N) -> LWE_out = + // LWE_{Q_{ks}, n, z}(m) = (b', a'_0, ..., a'n) + // If LWE_in = (0, a = {a_0, ..., a_N}), then LWE_out = LWE(-a \cdot s_{rlwe}) + for _ in 0..1000 { + let mut lwe_in = M::R::zeros(rlwe_n + 1); + RandomFillUniformInModulus::random_fill(&mut rng, lwe_q, &mut lwe_in.as_mut()[1..]); + + // Key switch + let mut lwe_out = M::R::zeros(parameters.lwe_n().0 + 1); + lwe_key_switch( + &mut lwe_out, + &lwe_in, + server_key.lwe_ksk(), + &lwe_modop, + &lwe_ks_decomposer, + ); + + // -a \cdot s + let mut want_m = M::MatElement::zero(); + izip!(lwe_in.as_ref().iter().skip(1), ideal_sk_rlwe.iter()).for_each(|(a, b)| { + want_m = lwe_modop.add( + &want_m, + &lwe_modop.mul(a, &lwe_q.map_element_from_i64(*b as i64)), + ); + }); + want_m = lwe_modop.neg(&want_m); + + // decrypt lwe out + let back_m = decrypt_lwe(&lwe_out, &ideal_sk_lwe, &lwe_modop); + + let noise = lwe_modop.sub(&want_m, &back_m); + server_key_stats.add_noise_post_kwe_key_switch(&vec![lwe_q.map_element_to_i64(&noise)]); + } + + server_key_stats + // Auto keys noise + + // Ksk noise +} + +#[cfg(test)] +mod tests { + use itertools::Itertools; + + use crate::{ + aggregate_public_key_shares, aggregate_server_key_shares, + bool::keys::ServerKeyEvaluationDomain, + evaluator::MultiPartyCrs, + gen_client_key, gen_mp_keys_phase1, gen_mp_keys_phase2, + parameters::{BoolParameters, CiphertextModulus}, + random::DefaultSecureRng, + set_mp_seed, set_parameter_set, + utils::WithLocal, + BoolEvaluator, DefaultDecomposer, ModularOpsU64, Ntt, NttBackendU64, + }; + + use super::collect_server_key_stats; + + #[test] + fn qwerty() { + set_parameter_set(crate::ParameterSelector::HighCommunicationButFast2Party); + set_mp_seed(MultiPartyCrs::random().seed); + let parties = 2; + let cks = (0..parties).map(|_| gen_client_key()).collect_vec(); + let pk_shares = cks.iter().map(|k| gen_mp_keys_phase1(k)).collect_vec(); + let pk = aggregate_public_key_shares(&pk_shares); + let server_key_shares = cks + .iter() + .enumerate() + .map(|(index, k)| gen_mp_keys_phase2(k, index, parties, &pk)) + .collect_vec(); + let seeded_server_key = aggregate_server_key_shares(&server_key_shares); + let server_key_eval = + ServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from( + &seeded_server_key, + ); + + let parameters = BoolEvaluator::with_local(|e| e.parameters().clone()); + let server_key_stats = collect_server_key_stats::< + _, + DefaultDecomposer, + NttBackendU64, + ModularOpsU64>, + >(parameters, &cks, &server_key_eval); + + println!( + "Rgsw nsm std log2 {}", + server_key_stats.brk_rgsw_cts.0.std_dev().abs().log2() + ); + println!( + "Rgsw m std log2 {}", + server_key_stats.brk_rgsw_cts.1.std_dev().abs().log2() + ); + println!( + "rlwe post 1 auto std log2 {}", + server_key_stats.post_1_auto.std_dev().abs().log2() + ); + println!( + "key switching noise rlwe secret s to lwe secret z std log2 {}", + server_key_stats.post_lwe_key_switch.std_dev().abs().log2() + ); + } +} + +// #[test] +// fn noise_tester() { +// let bool_evaluator = BoolEvaluator::< +// Vec>, +// NttBackendU64, +// ModularOpsU64>, +// ModularOpsU64>, +// ShoupServerKeyEvaluationDomain>>, +// >::new(OPTIMISED_SMALL_MP_BOOL_PARAMS); + +// // let (_, collective_pk, _, _, server_key_eval, ideal_client_key) = +// // _multi_party_all_keygen(&bool_evaluator, 20); +// let no_of_parties = 2; +// let lwe_q = bool_evaluator.pbs_info.parameters.lwe_q(); +// let rlwe_q = bool_evaluator.pbs_info.parameters.rlwe_q(); +// let lwe_n = bool_evaluator.pbs_info.parameters.lwe_n().0; +// let rlwe_n = bool_evaluator.pbs_info.parameters.rlwe_n().0; +// let lwe_modop = &bool_evaluator.pbs_info.lwe_modop; +// let rlwe_nttop = &bool_evaluator.pbs_info.rlwe_nttop; +// let rlwe_modop = &bool_evaluator.pbs_info.rlwe_modop; + +// // let rgsw_rgsw_decomposer = &bool_evaluator +// // .pbs_info +// // .parameters +// // .rgsw_rgsw_decomposer::>(); +// // let rgsw_rgsw_gadget_a = rgsw_rgsw_decomposer.0.gadget_vector(); +// // let rgsw_rgsw_gadget_b = rgsw_rgsw_decomposer.1.gadget_vector(); + +// let rlwe_rgsw_decomposer = +// &bool_evaluator.pbs_info.rlwe_rgsw_decomposer; let rlwe_rgsw_gadget_a +// = rlwe_rgsw_decomposer.0.gadget_vector(); let rlwe_rgsw_gadget_b = +// rlwe_rgsw_decomposer.1.gadget_vector(); + +// let auto_decomposer = &bool_evaluator.pbs_info.auto_decomposer; +// let auto_gadget = auto_decomposer.gadget_vector(); + +// let parties = (0..no_of_parties) +// .map(|_| bool_evaluator.client_key()) +// .collect_vec(); + +// let int_mp_seed = MultiPartyCrs::random(); + +// let mut ideal_rlwe_sk = vec![0i32; bool_evaluator.pbs_info.rlwe_n()]; +// parties.iter().for_each(|k| { +// izip!( +// ideal_rlwe_sk.iter_mut(), +// InteractiveMultiPartyClientKey::sk_rlwe(k).iter() +// ) +// .for_each(|(ideal_i, s_i)| { +// *ideal_i = *ideal_i + s_i; +// }); +// }); +// let mut ideal_lwe_sk = vec![0i32; bool_evaluator.pbs_info.lwe_n()]; +// parties.iter().for_each(|k| { +// izip!( +// ideal_lwe_sk.iter_mut(), +// InteractiveMultiPartyClientKey::sk_lwe(k).iter() +// ) +// .for_each(|(ideal_i, s_i)| { +// *ideal_i = *ideal_i + s_i; +// }); +// }); + +// let mut rng = DefaultSecureRng::new(); + +// // check noise in freshly encrypted RLWE ciphertext (ie var_fresh) +// if false { +// let mut rng = DefaultSecureRng::new(); +// let mut check = Stats { samples: vec![] }; +// for _ in 0..10 { +// // generate a new collective public key +// let mut pk_cr_seed = [0u8; 32]; +// rng.fill_bytes(&mut pk_cr_seed); +// let public_key_share = parties +// .iter() +// .map(|k| +// bool_evaluator.multi_party_public_key_share(&int_mp_seed, k)) +// .collect_vec(); let collective_pk = PublicKey::< +// Vec>, +// DefaultSecureRng, +// ModularOpsU64>, +// >::from(public_key_share.as_slice()); + +// let mut m = vec![0u64; rlwe_n]; +// RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, +// m.as_mut_slice()); let mut rlwe_ct = vec![vec![0u64; rlwe_n]; +// 2]; public_key_encrypt_rlwe::<_, _, _, _, i32, _>( +// &mut rlwe_ct, +// collective_pk.key(), +// &m, +// rlwe_modop, +// rlwe_nttop, +// &mut rng, +// ); + +// let mut m_back = vec![0u64; rlwe_n]; +// decrypt_rlwe( +// &rlwe_ct, +// &ideal_rlwe_sk, +// &mut m_back, +// rlwe_nttop, +// rlwe_modop, +// ); + +// rlwe_modop.elwise_sub_mut(m_back.as_mut_slice(), +// m.as_slice()); + +// check.add_more(Vec::::try_convert_from(&m_back, +// rlwe_q).as_slice()); } + +// println!("Public key Std: {}", check.std_dev().abs().log2()); +// } + +// if true { +// // Generate server key shares +// let public_key_share = parties +// .iter() +// .map(|k| +// bool_evaluator.multi_party_public_key_share(&int_mp_seed, k)) +// .collect_vec(); let collective_pk = PublicKey::< +// Vec>, +// DefaultSecureRng, +// ModularOpsU64>, +// >::from(public_key_share.as_slice()); + +// let server_key_shares = parties +// .iter() +// .enumerate() +// .map(|(user_id, k)| { +// bool_evaluator.multi_party_server_key_share( +// user_id, +// no_of_parties, +// &int_mp_seed, +// collective_pk.key(), +// k, +// ) +// }) +// .collect_vec(); + +// let seeded_server_key = +// +// bool_evaluator.aggregate_multi_party_server_key_shares(&server_key_shares); +// } + +// // server key in Evaluation domain +// let runtime_server_key = +// ShoupServerKeyEvaluationDomain::from(ServerKeyEvaluationDomain::< +// _, +// _, +// DefaultSecureRng, +// NttBackendU64, +// >::from(&seeded_server_key)); + +// // check noise in RLWE x RGSW(X^{s_i}) where RGSW is accunulated RGSW +// ciphertext if false { +// let mut check = Stats { samples: vec![] }; + +// ideal_lwe_sk.iter().enumerate().for_each(|(index, s_i)| { +// let rgsw_ct_i = +// runtime_server_key.rgsw_ct_lwe_si(index).as_ref(); + +// let mut m = vec![0u64; rlwe_n]; +// RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, +// m.as_mut_slice()); let mut rlwe_ct = vec![vec![0u64; rlwe_n]; +// 2]; public_key_encrypt_rlwe::<_, _, _, _, i32, _>( +// &mut rlwe_ct, +// collective_pk.key(), +// &m, +// rlwe_modop, +// rlwe_nttop, +// &mut rng, +// ); + +// let mut rlwe_after = RlweCiphertext::<_, DefaultSecureRng> { +// data: rlwe_ct.clone(), +// is_trivial: false, +// _phatom: PhantomData, +// }; +// let mut scratch = vec![ +// vec![0u64; rlwe_n]; +// std::cmp::max( +// rlwe_rgsw_decomposer.0.decomposition_count(), +// rlwe_rgsw_decomposer.1.decomposition_count() +// ) + 2 +// ]; +// rlwe_by_rgsw( +// &mut rlwe_after, +// rgsw_ct_i, +// &mut scratch, +// rlwe_rgsw_decomposer, +// rlwe_nttop, +// rlwe_modop, +// ); + +// // m1 = X^{s[i]} +// let mut m1 = vec![0u64; rlwe_n]; +// let s_i = *s_i * (bool_evaluator.pbs_info.embedding_factor as +// i32); if s_i < 0 { +// m1[rlwe_n - (s_i.abs() as usize)] = rlwe_q.neg_one() +// } else { +// m1[s_i as usize] = 1; +// } + +// // (m+e) * m1 +// let mut m_plus_e_times_m1 = vec![0u64; rlwe_n]; +// decrypt_rlwe( +// &rlwe_ct, +// &ideal_rlwe_sk, +// &mut m_plus_e_times_m1, +// rlwe_nttop, +// rlwe_modop, +// ); +// rlwe_nttop.forward(m_plus_e_times_m1.as_mut_slice()); +// rlwe_nttop.forward(m1.as_mut_slice()); + +// rlwe_modop.elwise_mul_mut(m_plus_e_times_m1.as_mut_slice(), +// m1.as_slice()); +// rlwe_nttop.backward(m_plus_e_times_m1.as_mut_slice()); + +// // Resulting RLWE ciphertext will equal: (m0m1 + em1) + +// e_{rlsw x rgsw}. // Hence, resulting rlwe ciphertext will +// have error em1 + e_{rlwe x rgsw}. // Here we're only +// concerned with e_{rlwe x rgsw}, that is noise added by // +// RLWExRGSW. Also note in practice m1 is a monomial, for ex, X^{s_{i}}, for +// // some i and var(em1) = var(e). +// let mut m_plus_e_times_m1_more_e = vec![0u64; rlwe_n]; +// decrypt_rlwe( +// &rlwe_after, +// &ideal_rlwe_sk, +// &mut m_plus_e_times_m1_more_e, +// rlwe_nttop, +// rlwe_modop, +// ); + +// // diff +// rlwe_modop.elwise_sub_mut( +// m_plus_e_times_m1_more_e.as_mut_slice(), +// m_plus_e_times_m1.as_slice(), +// ); + +// // let noise = measure_noise( +// // &rlwe_after, +// // &m_plus_e_times_m1, +// // rlwe_nttop, +// // rlwe_modop, +// // ideal_client_key.sk_rlwe.values(), +// // ); +// // print!("NOISE: {}", noise); + +// check.add_more(&Vec::::try_convert_from( +// &m_plus_e_times_m1_more_e, +// rlwe_q, +// )); +// }); +// println!( +// "RLWE x RGSW, where RGSW has noise var_brk, std: {} {}", +// check.std_dev(), +// check.std_dev().abs().log2() +// ) +// } + +// // check noise in Auto key +// if false { +// let mut check = Stats { samples: vec![] }; + +// let mut neg_s_poly = +// Vec::::try_convert_from(ideal_rlwe_sk.as_slice(), rlwe_q); +// rlwe_modop.elwise_neg_mut(neg_s_poly.as_mut_slice()); + +// let g = bool_evaluator.pbs_info.g(); +// let br_q = bool_evaluator.pbs_info.br_q(); +// let auto_element_dlogs = +// bool_evaluator.pbs_info.parameters.auto_element_dlogs(); for i in +// auto_element_dlogs.into_iter() { let g_pow = if i == 0 { +// -g +// } else { +// (((g as usize).pow(i as u32)) % br_q) as isize +// }; + +// // -s[X^k] +// let (auto_indices, auto_sign) = generate_auto_map(rlwe_n, +// g_pow); let mut neg_s_poly_auto_i = vec![0u64; rlwe_n]; +// izip!(neg_s_poly.iter(), auto_indices.iter(), +// auto_sign.iter()).for_each( |(v, to_i, to_sign)| { +// if !to_sign { +// neg_s_poly_auto_i[*to_i] = rlwe_modop.neg(v); +// } else { +// neg_s_poly_auto_i[*to_i] = *v; +// } +// }, +// ); + +// let mut auto_key_i = +// runtime_server_key.galois_key_for_auto(i).as_ref().clone(); //send i^th auto +// key to coefficient domain auto_key_i +// .iter_mut() +// .for_each(|r| rlwe_nttop.backward(r.as_mut_slice())); +// auto_gadget.iter().enumerate().for_each(|(i, b_i)| { +// // B^i * -s[X^k] +// let mut m_ideal = neg_s_poly_auto_i.clone(); + +// rlwe_modop.elwise_scalar_mul_mut(m_ideal.as_mut_slice(), +// b_i); + +// let mut m_out = vec![0u64; rlwe_n]; +// let mut rlwe_ct = vec![vec![0u64; rlwe_n]; 2]; +// rlwe_ct[0].copy_from_slice(&auto_key_i[i]); +// rlwe_ct[1] +// +// .copy_from_slice(&auto_key_i[auto_decomposer.decomposition_count() + i]); +// decrypt_rlwe(&rlwe_ct, &ideal_rlwe_sk, &mut m_out, +// rlwe_nttop, rlwe_modop); + +// // diff +// rlwe_modop.elwise_sub_mut(m_out.as_mut_slice(), +// m_ideal.as_slice()); + +// check.add_more(&Vec::::try_convert_from(&m_out, +// rlwe_q)); }); +// } + +// println!("Auto key noise std dev: {}", +// check.std_dev().abs().log2()); } + +// // check noise in RLWE(X^k) after sending RLWE(X) -> RLWE(X^k)using +// collective // auto key +// if false { +// let mut check = Stats { samples: vec![] }; +// let br_q = bool_evaluator.pbs_info.br_q(); +// let g = bool_evaluator.pbs_info.g(); +// let auto_element_dlogs = +// bool_evaluator.pbs_info.parameters.auto_element_dlogs(); for i in +// auto_element_dlogs.into_iter() { for _ in 0..10 { +// let mut m = vec![0u64; rlwe_n]; +// RandomFillUniformInModulus::random_fill(&mut rng, rlwe_q, +// m.as_mut_slice()); let mut rlwe_ct = RlweCiphertext::<_, +// DefaultSecureRng> { data: vec![vec![0u64; rlwe_n]; +// 2], is_trivial: false, +// _phatom: PhantomData, +// }; +// public_key_encrypt_rlwe::<_, _, _, _, i32, _>( +// &mut rlwe_ct, +// collective_pk.key(), +// &m, +// rlwe_modop, +// rlwe_nttop, +// &mut rng, +// ); + +// // We're only interested in noise increased as a result +// of automorphism. // Hence, we take m+e as the bench. +// let mut m_plus_e = vec![0u64; rlwe_n]; +// decrypt_rlwe( +// &rlwe_ct, +// &ideal_rlwe_sk, +// &mut m_plus_e, +// rlwe_nttop, +// rlwe_modop, +// ); + +// let auto_key = +// runtime_server_key.galois_key_for_auto(i).as_ref(); let +// (auto_map_index, auto_map_sign) = bool_evaluator.pbs_info.rlwe_auto_map(i); +// let mut scratch = +// vec![vec![0u64; rlwe_n]; +// auto_decomposer.decomposition_count() + 2]; galois_auto( +// &mut rlwe_ct, +// auto_key, +// &mut scratch, +// &auto_map_index, +// &auto_map_sign, +// rlwe_modop, +// rlwe_nttop, +// auto_decomposer, +// ); + +// // send m+e from X to X^k +// let mut m_plus_e_auto = vec![0u64; rlwe_n]; +// izip!(m_plus_e.iter(), auto_map_index.iter(), +// auto_map_sign.iter()).for_each( |(v, to_index, +// to_sign)| { if !to_sign { +// m_plus_e_auto[*to_index] = rlwe_modop.neg(v); +// } else { +// m_plus_e_auto[*to_index] = *v +// } +// }, +// ); + +// let mut m_out = vec![0u64; rlwe_n]; +// decrypt_rlwe(&rlwe_ct, &ideal_rlwe_sk, &mut m_out, +// rlwe_nttop, rlwe_modop); + +// // diff +// rlwe_modop.elwise_sub_mut(m_out.as_mut_slice(), +// m_plus_e_auto.as_slice()); + +// +// check.add_more(&Vec::::try_convert_from(m_out.as_slice(), rlwe_q)); +// } +// } + +// println!("Rlwe Auto Noise Std: {}", +// check.std_dev().abs().log2()); } + +// // Check noise growth in ksk +// // TODO check in LWE key switching keys +// if false { +// // 1. encrypt LWE ciphertext +// // 2. Key switching +// // 3. +// let mut check = Stats { samples: vec![] }; + +// for _ in 0..1024 { +// // Encrypt m \in Q_{ks} using RLWE sk +// let mut lwe_in_ct = vec![0u64; rlwe_n + 1]; +// let m = RandomElementInModulus::random(&mut rng, +// &lwe_q.q().unwrap()); encrypt_lwe(&mut lwe_in_ct, &m, +// &ideal_rlwe_sk, lwe_modop, &mut rng); + +// // Key switch +// let mut lwe_out = vec![0u64; lwe_n + 1]; +// lwe_key_switch( +// &mut lwe_out, +// &lwe_in_ct, +// runtime_server_key.lwe_ksk(), +// lwe_modop, +// bool_evaluator.pbs_info.lwe_decomposer(), +// ); + +// // We only care about noise added by LWE key switch +// // m+e +// let m_plus_e = decrypt_lwe(&lwe_in_ct, &ideal_rlwe_sk, +// lwe_modop); + +// let m_plus_e_plus_lwe_ksk_noise = decrypt_lwe(&lwe_out, +// &ideal_lwe_sk, lwe_modop); + +// let diff = lwe_modop.sub(&m_plus_e_plus_lwe_ksk_noise, +// &m_plus_e); + +// check.add_more(&vec![lwe_q.map_element_to_i64(&diff)]); +// } + +// println!("Lwe ksk std dev: {}", check.std_dev().abs().log2()); +// } +// } + +// // Check noise in fresh RGSW ciphertexts, ie X^{s_j[i]}, must equalnoise +// in // // fresh RLWE ciphertext +// if true {} +// // test LWE ksk from RLWE -> LWE +// // if false { +// // let logp = 2; +// // let mut rng = DefaultSecureRng::new(); + +// // let m = 1; +// // let encoded_m = m << (lwe_logq - logp); + +// // // Encrypt +// // let mut lwe_ct = vec![0u64; rlwe_n + 1]; +// // encrypt_lwe( +// // &mut lwe_ct, +// // &encoded_m, +// // ideal_client_key.sk_rlwe.values(), +// // lwe_modop, +// // &mut rng, +// // ); + +// // // key switch +// // let lwe_decomposer = &bool_evaluator.decomposer_lwe; +// // let mut lwe_out = vec![0u64; lwe_n + 1]; +// // lwe_key_switch( +// // &mut lwe_out, +// // &lwe_ct, +// // &server_key_eval.lwe_ksk, +// // lwe_modop, +// // lwe_decomposer, +// // ); + +// // let encoded_m_back = decrypt_lwe(&lwe_out, +// // ideal_client_key.sk_lwe.values(), lwe_modop); let m_back +// // = ((encoded_m_back as f64 * (1 << logp) as f64) / +// // (lwe_q as f64)).round() as u64; dbg!(m_back, m); + +// // let noise = measure_noise_lwe( +// // &lwe_out, +// // ideal_client_key.sk_lwe.values(), +// // lwe_modop, +// // &encoded_m, +// // ); + +// // println!("Noise: {noise}"); +// // } + +// // Measure noise in RGSW ciphertexts of ideal LWE secrets +// // if true { +// // let gadget_vec = gadget_vector( +// // bool_evaluator.parameters.rlwe_logq, +// // bool_evaluator.parameters.logb_rgsw, +// // bool_evaluator.parameters.d_rgsw, +// // ); + +// // for i in 0..20 { +// // // measure noise in RGSW(s[i]) +// // let si = +// // ideal_client_key.sk_lwe.values[i] * +// // (bool_evaluator.embedding_factor as i32); let mut +// // si_poly = vec![0u64; rlwe_n]; if si < 0 { +// // si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1; +// // } else { +// // si_poly[(si.abs() as usize)] = 1; +// // } + +// // let mut rgsw_si = server_key_eval.rgsw_cts[i].clone(); +// // rgsw_si +// // .iter_mut() +// // .for_each(|ri| rlwe_nttop.backward(ri.as_mut())); + +// // println!("####### Noise in RGSW(X^s_{i}) #######"); +// // _measure_noise_rgsw( +// // &rgsw_si, +// // &si_poly, +// // ideal_client_key.sk_rlwe.values(), +// // &gadget_vec, +// // rlwe_q, +// // ); +// // println!("####### ##################### #######"); +// // } +// // } + +// // // measure noise grwoth in RLWExRGSW +// // if true { +// // let mut rng = DefaultSecureRng::new(); +// // let mut carry_m = vec![0u64; rlwe_n]; +// // RandomUniformDist1::random_fill(&mut rng, &rlwe_q, +// // carry_m.as_mut_slice()); + +// // // RGSW(carrym) +// // let trivial_rlwect = vec![vec![0u64; rlwe_n],carry_m.clone()]; +// // let mut rlwe_ct = RlweCiphertext::<_, +// // DefaultSecureRng>::from_raw(trivial_rlwect, true); + +// // let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n]; +// // d_rgsw + 2]; let mul_mod = +// // |v0: &u64, v1: &u64| (((*v0 as u128 * *v1 as u128) % (rlwe_q +// as u128)) as u64); + +// // for i in 0..bool_evaluator.parameters.lwe_n { +// // rlwe_by_rgsw( +// // &mut rlwe_ct, +// // server_key_eval.rgsw_ct_lwe_si(i), +// // &mut scratch_matrix_dplus2_ring, +// // rlwe_decomposer, +// // rlwe_nttop, +// // rlwe_modop, +// // ); + +// // // carry_m[X] * s_i[X] +// // let si = +// // ideal_client_key.sk_lwe.values[i] * +// // (bool_evaluator.embedding_factor as i32); let mut +// // si_poly = vec![0u64; rlwe_n]; if si < 0 { +// // si_poly[rlwe_n - (si.abs() as usize)] = rlwe_q - 1; +// // } else { +// // si_poly[(si.abs() as usize)] = 1; +// // } +// // carry_m = negacyclic_mul(&carry_m, &si_poly, mul_mod, +// // rlwe_q); + +// // let noise = measure_noise( +// // &rlwe_ct, +// // &carry_m, +// // rlwe_nttop, +// // rlwe_modop, +// // ideal_client_key.sk_rlwe.values(), +// // ); +// // println!("Noise RLWE(carry_m) accumulating {i}^th secret +// // monomial: {noise}"); } +// // } + +// // // Check galois keys +// // if false { +// // let g = bool_evaluator.g() as isize; +// // let mut rng = DefaultSecureRng::new(); +// // let mut scratch_matrix_dplus2_ring = vec![vec![0u64; rlwe_n]; +// // d_rgsw + 2]; for i in [g, -g] { +// // let mut m = vec![0u64; rlwe_n]; +// // RandomUniformDist1::random_fill(&mut rng, &rlwe_q, +// // m.as_mut_slice()); let mut rlwe_ct = { +// // let mut data = vec![vec![0u64; rlwe_n]; 2]; +// // public_key_encrypt_rlwe( +// // &mut data, +// // &collective_pk.key, +// // &m, +// // rlwe_modop, +// // rlwe_nttop, +// // &mut rng, +// // ); +// // RlweCiphertext::<_, DefaultSecureRng>::from_raw(data, +// // false) }; + +// // let auto_key = server_key_eval.galois_key_for_auto(i); +// // let (auto_map_index, auto_map_sign) = +// // generate_auto_map(rlwe_n, i); galois_auto( +// // &mut rlwe_ct, +// // auto_key, +// // &mut scratch_matrix_dplus2_ring, +// // &auto_map_index, +// // &auto_map_sign, +// // rlwe_modop, +// // rlwe_nttop, +// // rlwe_decomposer, +// // ); + +// // // send m(X) -> m(X^i) +// // let mut m_k = vec![0u64; rlwe_n]; +// // izip!(m.iter(), auto_map_index.iter(), +// // auto_map_sign.iter()).for_each( |(mi, to_index,to_sign)| +// // // { if !to_sign { +// // m_k[*to_index] = rlwe_q - *mi; } else { +// // m_k[*to_index] = *mi; +// // } +// // }, +// // ); + +// // // measure noise +// // let noise = measure_noise( +// // &rlwe_ct, +// // &m_k, +// // rlwe_nttop, +// // rlwe_modop, +// // ideal_client_key.sk_rlwe.values(), +// // ); + +// // println!("Noise after auto k={i}: {noise}"); +// // } +// // } +// } diff --git a/src/decomposer.rs b/src/decomposer.rs index 599678a..c4e785c 100644 --- a/src/decomposer.rs +++ b/src/decomposer.rs @@ -52,6 +52,7 @@ pub trait Decomposer { fn decompose_to_vec(&self, v: &Self::Element) -> Vec; fn decompose_iter(&self, v: &Self::Element) -> Self::Iter; fn decomposition_count(&self) -> usize; + fn gadget_vector(&self) -> Vec; } pub struct DefaultDecomposer { @@ -94,17 +95,13 @@ impl DefaultDecomposer { Op: ArithmeticOps, { let mut value = T::zero(); - let gadget_vector = self.gadget_vector(); + let gadget_vector = gadget_vector(self.logq, self.logb, self.d); assert!(limbs.len() == gadget_vector.len()); izip!(limbs.iter(), gadget_vector.iter()) .for_each(|(d_el, beta)| value = modq_op.add(&value, &modq_op.mul(d_el, beta))); value } - - pub(crate) fn gadget_vector(&self) -> Vec { - return gadget_vector(self.logq, self.logb, self.d); - } } impl< @@ -201,6 +198,10 @@ impl< steps_left: self.d, } } + + fn gadget_vector(&self) -> Vec { + return gadget_vector(self.logq, self.logb, self.d); + } } impl DefaultDecomposer {} diff --git a/src/utils.rs b/src/utils.rs index 0b7c137..27cfd24 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -272,6 +272,12 @@ pub(crate) mod tests { pub(crate) samples: Vec, } + impl Default for Stats { + fn default() -> Self { + Stats { samples: vec![] } + } + } + impl Stats where // T: for<'a> Sum<&'a T>,