From 56c4276cbab34731d9f4a36615cff6eff6584275 Mon Sep 17 00:00:00 2001 From: Janmajaya Mall Date: Thu, 4 Jul 2024 19:01:57 +0530 Subject: [PATCH] add more MP params --- src/bool/keys.rs | 21 +-- src/bool/mp_api.rs | 46 ++++-- src/bool/ni_mp_api.rs | 37 ++--- src/bool/parameters.rs | 144 ++++++++---------- src/bool/print_noise.rs | 318 +++++++++++++++++++++++++++++++--------- src/lib.rs | 1 + src/rgsw/mod.rs | 4 +- src/utils.rs | 22 ++- 8 files changed, 386 insertions(+), 207 deletions(-) diff --git a/src/bool/keys.rs b/src/bool/keys.rs index a927747..16dfdf1 100644 --- a/src/bool/keys.rs +++ b/src/bool/keys.rs @@ -1499,16 +1499,11 @@ pub(crate) mod key_size { pub(super) mod tests { use itertools::izip; - use num_traits::{FromPrimitive, PrimInt, ToPrimitive, Zero}; + use num_traits::{FromPrimitive, PrimInt, Zero}; use crate::{ - backend::{GetModulus, Modulus}, - bool::ClientKey, - decomposer::NumInfo, - lwe::decrypt_lwe, - parameters::{CiphertextModulus, I_2P}, - utils::TryConvertFrom1, - ArithmeticOps, Row, + backend::GetModulus, bool::ClientKey, decomposer::NumInfo, lwe::decrypt_lwe, + parameters::CiphertextModulus, utils::TryConvertFrom1, ArithmeticOps, Row, }; use super::SinglePartyClientKey; @@ -1545,20 +1540,14 @@ pub(super) mod tests { m_expected: R::Element, sk: &[S], modop: &Modop, - ) -> f64 + ) -> R::Element where R: TryConvertFrom1<[S], CiphertextModulus>, R::Element: Zero + FromPrimitive + PrimInt + NumInfo, { let noisy_m = decrypt_lwe(lwe_ct, &sk, modop); let noise = modop.sub(&m_expected, &noisy_m); - modop - .modulus() - .map_element_to_i64(&noise) - .abs() - .to_f64() - .unwrap() - .log2() + noise } // #[test] // fn trial() { diff --git a/src/bool/mp_api.rs b/src/bool/mp_api.rs index d640b89..4791033 100644 --- a/src/bool/mp_api.rs +++ b/src/bool/mp_api.rs @@ -35,13 +35,13 @@ pub enum ParameterSelector { pub fn set_parameter_set(select: ParameterSelector) { match select { ParameterSelector::InteractiveLTE2Party => { - BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_2P))); + BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_2P_LB_SR))); } ParameterSelector::InteractiveLTE4Party => { BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_4P))); } ParameterSelector::InteractiveLTE8Party => { - BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_8P_LB_SR))); + BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(I_8P))); } _ => { panic!("Paramerter not supported") @@ -311,24 +311,27 @@ mod tests { use rand::{thread_rng, Rng, RngCore}; use crate::{ + backend::Modulus, bool::{ evaluator::BoolEncoding, keys::tests::{ideal_sk_rlwe, measure_noise_lwe}, BooleanGates, }, - Encryptor, MultiPartyDecryptor, SampleExtractor, + lwe::decrypt_lwe, + utils::tests::Stats, + Encoder, Encryptor, MultiPartyDecryptor, SampleExtractor, }; use super::*; #[test] fn multi_party_bool_gates() { - set_parameter_set(ParameterSelector::InteractiveLTE2Party); + set_parameter_set(ParameterSelector::InteractiveLTE8Party); let mut seed = [0u8; 32]; thread_rng().fill_bytes(&mut seed); set_common_reference_seed(seed); - let parties = 2; + let parties = 8; let cks = (0..parties).map(|_| gen_client_key()).collect_vec(); // round 1 @@ -361,7 +364,9 @@ mod tests { let parameters = BoolEvaluator::with_local(|e| e.parameters().clone()); let rlwe_modop = parameters.default_rlwe_modop(); - for _ in 0..500 { + let mut stats = Stats::new(); + + for _ in 0..1000 { let now = std::time::Instant::now(); let ct_out = BoolEvaluator::with_local_mut(|e| e.nand(&ct0, &ct1, RuntimeServerKey::global())); @@ -376,14 +381,15 @@ mod tests { let m_out = cks[0].aggregate_decryption_shares(&ct_out, &decryption_shares); assert!(m_out == m_expected, "Expected {m_expected}, got {m_out}"); + { - let m_expected_el = if m_expected == true { - parameters.rlwe_q().true_el() - } else { - parameters.rlwe_q().false_el() - }; - let noise = measure_noise_lwe(&ct_out, m_expected_el, &ideal_sk_rlwe, &rlwe_modop); - println!("NAND Noise: {noise}"); + let noise = measure_noise_lwe( + &ct_out, + parameters.rlwe_q().encode(m_expected), + &ideal_sk_rlwe, + &rlwe_modop, + ); + stats.add_sample(parameters.rlwe_q().map_element_to_i64(&noise)); } m1 = m0; @@ -393,7 +399,7 @@ mod tests { ct0 = ct_out; } - for _ in 0..500 { + for _ in 0..1000 { let ct_out = BoolEvaluator::with_local_mut(|e| e.xnor(&ct0, &ct1, RuntimeServerKey::global())); @@ -407,12 +413,24 @@ mod tests { assert!(m_out == m_expected, "Expected {m_expected}, got {m_out}"); + { + let noise = measure_noise_lwe( + &ct_out, + parameters.rlwe_q().encode(m_expected), + &ideal_sk_rlwe, + &rlwe_modop, + ); + stats.add_sample(parameters.rlwe_q().map_element_to_i64(&noise)); + } + m1 = m0; m0 = m_expected; ct1 = ct0; ct0 = ct_out; } + + println!("Noise std_dev log2: {}", stats.std_dev().abs().log2()); } #[test] diff --git a/src/bool/ni_mp_api.rs b/src/bool/ni_mp_api.rs index 8328e9a..5a20e73 100644 --- a/src/bool/ni_mp_api.rs +++ b/src/bool/ni_mp_api.rs @@ -3,7 +3,6 @@ use std::{cell::RefCell, sync::OnceLock}; use crate::{ backend::ModulusPowerOf2, bool::parameters::ParameterVariant, - parameters::NI_4P, random::DefaultSecureRng, utils::{Global, WithLocal}, ModularOpsU64, NttBackendU64, @@ -16,7 +15,7 @@ use super::{ NonInteractiveServerKeyEvaluationDomain, SeededNonInteractiveMultiPartyServerKey, ShoupNonInteractiveServerKeyEvaluationDomain, }, - parameters::{BoolParameters, CiphertextModulus, NI_2P}, + parameters::{BoolParameters, CiphertextModulus, NI_2P, NI_4P_HB_FR, NI_8P}, ClientKey, }; @@ -40,6 +39,7 @@ static MULTI_PARTY_CRS: OnceLock> = OnceLo pub enum ParameterSelector { NonInteractiveLTE2Party, NonInteractiveLTE4Party, + NonInteractiveLTE8Party, } pub fn set_parameter_set(select: ParameterSelector) { @@ -48,11 +48,10 @@ pub fn set_parameter_set(select: ParameterSelector) { BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_2P))); } ParameterSelector::NonInteractiveLTE4Party => { - BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_4P))); + BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_4P_HB_FR))); } - - _ => { - panic!("Paramerters not supported") + ParameterSelector::NonInteractiveLTE8Party => { + BOOL_EVALUATOR.with_borrow_mut(|v| *v = Some(BoolEvaluator::new(NI_8P))); } } } @@ -461,12 +460,12 @@ mod tests { #[test] fn non_interactive_mp_bool_nand() { - set_parameter_set(ParameterSelector::NonInteractiveLTE2Party); + set_parameter_set(ParameterSelector::NonInteractiveLTE8Party); let mut seed = [0u8; 32]; thread_rng().fill_bytes(&mut seed); set_common_reference_seed(seed); - let parties = 2; + let parties = 8; let cks = (0..parties).map(|_| gen_client_key()).collect_vec(); @@ -501,10 +500,10 @@ mod tests { let mut stats = Stats::new(); for _ in 0..1000 { - // let now = std::time::Instant::now(); + let now = std::time::Instant::now(); let ct_out = BoolEvaluator::with_local_mut(|e| e.xor(&ct0, &ct1, RuntimeServerKey::global())); - // println!("Time: {:?}", now.elapsed()); + println!("Time: {:?}", now.elapsed()); let decryption_shares = cks .iter() @@ -515,17 +514,13 @@ mod tests { let m_expected = m0 ^ m1; { - // let noise = measure_noise_lwe( - // &ct_out, - // parameters.rlwe_q().encode(m_expected), - // &ideal_sk_rlwe, - // &rlwe_modop, - // ); - // println!("Noise: {noise}"); - - let noisy_m = decrypt_lwe(&ct_out, &ideal_sk_rlwe, &rlwe_modop); - let noise = rlwe_modop.sub(¶meters.rlwe_q().encode(m_expected), &noisy_m); - stats.add_more(&vec![parameters.rlwe_q().map_element_to_i64(&noise)]); + let noise = measure_noise_lwe( + &ct_out, + parameters.rlwe_q().encode(m_expected), + &ideal_sk_rlwe, + &rlwe_modop, + ); + stats.add_sample(parameters.rlwe_q().map_element_to_i64(&noise)); } assert!(m_out == m_expected, "Expected {m_expected} but got {m_out}"); diff --git a/src/bool/parameters.rs b/src/bool/parameters.rs index d928de7..c6373e7 100644 --- a/src/bool/parameters.rs +++ b/src/bool/parameters.rs @@ -523,70 +523,46 @@ where } } -pub(crate) const MP_BOOL_PARAMS: BoolParameters = BoolParameters:: { +pub(crate) const I_2P_LB_SR: BoolParameters = BoolParameters:: { rlwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, - lwe_secret_key_dist: SecretKeyDistribution::ErrorDistribution, - rlwe_q: CiphertextModulus::new_non_native(1152921504606830593), - lwe_q: CiphertextModulus::new_non_native(1 << 20), - br_q: 1 << 11, - rlwe_n: PolynomialSize(1 << 11), - lwe_n: LweDimension(500), - lwe_decomposer_params: (DecompostionLogBase(4), DecompositionCount(5)), - rlrg_decomposer_params: ( - DecompostionLogBase(12), - (DecompositionCount(5), DecompositionCount(5)), - ), - rgrg_decomposer_params: Some(( - DecompostionLogBase(12), - (DecompositionCount(5), DecompositionCount(5)), - )), - auto_decomposer_params: (DecompostionLogBase(12), DecompositionCount(5)), - non_interactive_ui_to_s_key_switch_decomposer: None, - g: 5, - w: 10, - variant: ParameterVariant::InteractiveMultiParty, -}; - -pub(crate) const SMALL_MP_BOOL_PARAMS: BoolParameters = BoolParameters:: { - rlwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, - lwe_secret_key_dist: SecretKeyDistribution::ErrorDistribution, - rlwe_q: CiphertextModulus::new_non_native(36028797018820609), - lwe_q: CiphertextModulus::new_non_native(1 << 20), + lwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, + rlwe_q: CiphertextModulus::new_non_native(18014398509404161), + lwe_q: CiphertextModulus::new_non_native(1 << 15), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), - lwe_n: LweDimension(600), - lwe_decomposer_params: (DecompostionLogBase(4), DecompositionCount(5)), + lwe_n: LweDimension(580), + lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(12)), rlrg_decomposer_params: ( - DecompostionLogBase(11), - (DecompositionCount(2), DecompositionCount(1)), + DecompostionLogBase(17), + (DecompositionCount(1), DecompositionCount(1)), ), rgrg_decomposer_params: Some(( - DecompostionLogBase(11), - (DecompositionCount(5), DecompositionCount(4)), + DecompostionLogBase(7), + (DecompositionCount(6), DecompositionCount(5)), )), - auto_decomposer_params: (DecompostionLogBase(11), DecompositionCount(2)), + auto_decomposer_params: (DecompostionLogBase(24), DecompositionCount(1)), non_interactive_ui_to_s_key_switch_decomposer: None, g: 5, w: 10, variant: ParameterVariant::InteractiveMultiParty, }; -pub(crate) const I_2P: BoolParameters = BoolParameters:: { +pub(crate) const I_4P: BoolParameters = BoolParameters:: { rlwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, - lwe_secret_key_dist: SecretKeyDistribution::ErrorDistribution, + lwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, rlwe_q: CiphertextModulus::new_non_native(18014398509404161), - lwe_q: CiphertextModulus::new_non_native(1 << 15), + lwe_q: CiphertextModulus::new_non_native(1 << 16), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), - lwe_n: LweDimension(480), - lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(12)), + lwe_n: LweDimension(620), + lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(13)), rlrg_decomposer_params: ( DecompostionLogBase(17), (DecompositionCount(1), DecompositionCount(1)), ), rgrg_decomposer_params: Some(( - DecompostionLogBase(7), - (DecompositionCount(6), DecompositionCount(5)), + DecompostionLogBase(6), + (DecompositionCount(7), DecompositionCount(6)), )), auto_decomposer_params: (DecompostionLogBase(24), DecompositionCount(1)), non_interactive_ui_to_s_key_switch_decomposer: None, @@ -595,22 +571,22 @@ pub(crate) const I_2P: BoolParameters = BoolParameters:: { variant: ParameterVariant::InteractiveMultiParty, }; -pub(crate) const I_4P: BoolParameters = BoolParameters:: { +pub(crate) const I_8P: BoolParameters = BoolParameters:: { rlwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, - lwe_secret_key_dist: SecretKeyDistribution::ErrorDistribution, + lwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, rlwe_q: CiphertextModulus::new_non_native(18014398509404161), - lwe_q: CiphertextModulus::new_non_native(1 << 16), - br_q: 1 << 11, + lwe_q: CiphertextModulus::new_non_native(1 << 17), + br_q: 1 << 12, rlwe_n: PolynomialSize(1 << 11), - lwe_n: LweDimension(520), - lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(13)), + lwe_n: LweDimension(660), + lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(14)), rlrg_decomposer_params: ( DecompostionLogBase(17), (DecompositionCount(1), DecompositionCount(1)), ), rgrg_decomposer_params: Some(( - DecompostionLogBase(6), - (DecompositionCount(7), DecompositionCount(6)), + DecompostionLogBase(5), + (DecompositionCount(9), DecompositionCount(8)), )), auto_decomposer_params: (DecompostionLogBase(24), DecompositionCount(1)), non_interactive_ui_to_s_key_switch_decomposer: None, @@ -619,12 +595,12 @@ pub(crate) const I_4P: BoolParameters = BoolParameters:: { variant: ParameterVariant::InteractiveMultiParty, }; -pub(crate) const I_8P_HB_FR: BoolParameters = BoolParameters:: { +pub(crate) const NI_2P: BoolParameters = BoolParameters:: { rlwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, lwe_secret_key_dist: SecretKeyDistribution::ErrorDistribution, rlwe_q: CiphertextModulus::new_non_native(18014398509404161), lwe_q: CiphertextModulus::new_non_native(1 << 16), - br_q: 1 << 11, + br_q: 1 << 12, rlwe_n: PolynomialSize(1 << 11), lwe_n: LweDimension(520), lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(13)), @@ -634,55 +610,61 @@ pub(crate) const I_8P_HB_FR: BoolParameters = BoolParameters:: { ), rgrg_decomposer_params: Some(( DecompostionLogBase(4), - (DecompositionCount(12), DecompositionCount(11)), + (DecompositionCount(10), DecompositionCount(9)), )), auto_decomposer_params: (DecompostionLogBase(24), DecompositionCount(1)), - non_interactive_ui_to_s_key_switch_decomposer: None, + non_interactive_ui_to_s_key_switch_decomposer: Some(( + DecompostionLogBase(1), + DecompositionCount(50), + )), g: 5, w: 10, - variant: ParameterVariant::InteractiveMultiParty, + variant: ParameterVariant::NonInteractiveMultiParty, }; -pub(crate) const I_8P_LB_SR: BoolParameters = BoolParameters:: { +pub(crate) const NI_4P_HB_FR: BoolParameters = BoolParameters:: { rlwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, - lwe_secret_key_dist: SecretKeyDistribution::ErrorDistribution, + lwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, rlwe_q: CiphertextModulus::new_non_native(18014398509404161), - lwe_q: CiphertextModulus::new_non_native(1 << 17), + lwe_q: CiphertextModulus::new_non_native(1 << 16), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), - lwe_n: LweDimension(560), - lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(14)), + lwe_n: LweDimension(620), + lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(13)), rlrg_decomposer_params: ( DecompostionLogBase(17), (DecompositionCount(1), DecompositionCount(1)), ), rgrg_decomposer_params: Some(( - DecompostionLogBase(5), - (DecompositionCount(8), DecompositionCount(7)), + DecompostionLogBase(3), + (DecompositionCount(13), DecompositionCount(12)), )), auto_decomposer_params: (DecompostionLogBase(24), DecompositionCount(1)), - non_interactive_ui_to_s_key_switch_decomposer: None, + non_interactive_ui_to_s_key_switch_decomposer: Some(( + DecompostionLogBase(1), + DecompositionCount(50), + )), g: 5, w: 10, - variant: ParameterVariant::InteractiveMultiParty, + variant: ParameterVariant::NonInteractiveMultiParty, }; -pub(crate) const NI_2P: BoolParameters = BoolParameters:: { +pub(crate) const NI_4P_LB_SR: BoolParameters = BoolParameters:: { rlwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, - lwe_secret_key_dist: SecretKeyDistribution::ErrorDistribution, + lwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, rlwe_q: CiphertextModulus::new_non_native(18014398509404161), - lwe_q: CiphertextModulus::new_non_native(1 << 15), - br_q: 1 << 11, + lwe_q: CiphertextModulus::new_non_native(1 << 16), + br_q: 1 << 12, rlwe_n: PolynomialSize(1 << 11), - lwe_n: LweDimension(480), - lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(12)), + lwe_n: LweDimension(620), + lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(13)), rlrg_decomposer_params: ( - DecompostionLogBase(16), + DecompostionLogBase(17), (DecompositionCount(1), DecompositionCount(1)), ), rgrg_decomposer_params: Some(( - DecompostionLogBase(6), - (DecompositionCount(7), DecompositionCount(7)), + DecompostionLogBase(4), + (DecompositionCount(10), DecompositionCount(9)), )), auto_decomposer_params: (DecompostionLogBase(24), DecompositionCount(1)), non_interactive_ui_to_s_key_switch_decomposer: Some(( @@ -694,22 +676,22 @@ pub(crate) const NI_2P: BoolParameters = BoolParameters:: { variant: ParameterVariant::NonInteractiveMultiParty, }; -pub(crate) const NI_4P: BoolParameters = BoolParameters:: { +pub(crate) const NI_8P: BoolParameters = BoolParameters:: { rlwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, - lwe_secret_key_dist: SecretKeyDistribution::ErrorDistribution, + lwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, rlwe_q: CiphertextModulus::new_non_native(18014398509404161), - lwe_q: CiphertextModulus::new_non_native(1 << 16), - br_q: 1 << 11, + lwe_q: CiphertextModulus::new_non_native(1 << 17), + br_q: 1 << 12, rlwe_n: PolynomialSize(1 << 11), - lwe_n: LweDimension(510), - lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(12)), + lwe_n: LweDimension(660), + lwe_decomposer_params: (DecompostionLogBase(1), DecompositionCount(14)), rlrg_decomposer_params: ( DecompostionLogBase(17), (DecompositionCount(1), DecompositionCount(1)), ), rgrg_decomposer_params: Some(( - DecompostionLogBase(4), - (DecompositionCount(10), DecompositionCount(9)), + DecompostionLogBase(2), + (DecompositionCount(20), DecompositionCount(18)), )), auto_decomposer_params: (DecompostionLogBase(24), DecompositionCount(1)), non_interactive_ui_to_s_key_switch_decomposer: Some(( diff --git a/src/bool/print_noise.rs b/src/bool/print_noise.rs index c4b4260..eef5ee4 100644 --- a/src/bool/print_noise.rs +++ b/src/bool/print_noise.rs @@ -23,7 +23,7 @@ use super::keys::tests::{ideal_sk_lwe, ideal_sk_rlwe}; pub(crate) trait CollectRuntimeServerKeyStats { type M; - /// RGSW ciphertext X^{s[s_index]} in evaluation domain where s the LWE + /// RGSW ciphertext X^{s[s_index]} in evaluation domain where `s` the LWE /// secret fn rgsw_cts_lwe_si(&self, s_index: usize) -> &Self::M; /// Auto key in evaluation domain for automorphism g^k. For auto key for @@ -33,9 +33,20 @@ pub(crate) trait CollectRuntimeServerKeyStats { fn lwe_ksk(&self) -> &Self::M; } +#[derive(Default)] struct ServerKeyStats { + /// Distribution of noise in RGSW ciphertexts + /// + /// We collect statistics for RLWE'(-sm) separately from RLWE'(m) because + /// non-interactive protocol differents between the two. Although we expect + /// the distribution of noise in both to be the same. brk_rgsw_cts: (Stats, Stats), + /// Distribtion of noise added to RLWE ciphertext after automorphism using + /// Server auto keys. post_1_auto: Stats, + /// Distribution of noise added in LWE key switching from LWE_{q, s} to + /// LWE_{q, z} where `z` is ideal LWE secret and `s` is ideal RLWE secret + /// using Server's LWE key switching key. post_lwe_key_switch: Stats, } @@ -52,19 +63,28 @@ where } fn add_noise_brk_rgsw_cts_nsm(&mut self, noise: &[T]) { - self.brk_rgsw_cts.0.add_more(noise); + self.brk_rgsw_cts.0.add_many_samples(noise); } fn add_noise_brk_rgsw_cts_m(&mut self, noise: &[T]) { - self.brk_rgsw_cts.1.add_more(noise); + self.brk_rgsw_cts.1.add_many_samples(noise); } fn add_noise_post_1_auto(&mut self, noise: &[T]) { - self.post_1_auto.add_more(&noise); + self.post_1_auto.add_many_samples(&noise); } fn add_noise_post_kwe_key_switch(&mut self, noise: &[T]) { - self.post_lwe_key_switch.add_more(&noise); + self.post_lwe_key_switch.add_many_samples(&noise); + } + + fn merge_in(&mut self, other: &Self) { + self.brk_rgsw_cts.0.merge_in(&other.brk_rgsw_cts.0); + self.brk_rgsw_cts.1.merge_in(&other.brk_rgsw_cts.1); + + self.post_1_auto.merge_in(&other.post_1_auto); + self.post_lwe_key_switch + .merge_in(&other.post_lwe_key_switch); } } @@ -363,8 +383,6 @@ where mod tests { use itertools::Itertools; - use super::collect_server_key_stats; - #[test] #[cfg(feature = "interactive_mp")] fn interactive_key_noise() { @@ -382,64 +400,82 @@ mod tests { BoolEvaluator, DefaultDecomposer, ModularOpsU64, NttBackendU64, }; - set_parameter_set(crate::ParameterSelector::InteractiveLTE2Party); - set_common_reference_seed(InteractiveMultiPartyCrs::random().seed); - let parties = 2; - let cks = (0..parties).map(|_| gen_client_key()).collect_vec(); - let pk_shares = cks - .iter() - .map(|k| interactive_multi_party_round1_share(k)) - .collect_vec(); + use super::*; - 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(); + set_parameter_set(crate::ParameterSelector::InteractiveLTE8Party); + set_common_reference_seed(InteractiveMultiPartyCrs::random().seed); + let parties = 8; + + let mut server_key_stats = ServerKeyStats::default(); + let mut server_key_share_size = 0usize; + + for i in 0..2 { + let cks = (0..parties).map(|_| gen_client_key()).collect_vec(); + let pk_shares = cks + .iter() + .map(|k| interactive_multi_party_round1_share(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(); + + // In 0th iteration measure server key size + if i == 0 { + // Server key share size of user with last id may not equal server key share + // sizes of other users if LWE dimension does not divides number of parties. + server_key_share_size = std::cmp::max( + server_key_shares.first().unwrap().size(), + server_key_shares.last().unwrap().size(), + ); + } - // println!("Size: {}", server_key_shares[0].size()); - let seeded_server_key = aggregate_server_key_shares(&server_key_shares); - let server_key_eval = - ServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from( - &seeded_server_key, - ); + // println!("Size: {}", server_key_shares[0].size()); + 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); + let parameters = BoolEvaluator::with_local(|e| e.parameters().clone()); + server_key_stats.merge_in(&collect_server_key_stats::< + _, + DefaultDecomposer, + NttBackendU64, + ModularOpsU64>, + _, + >(parameters, &cks, &server_key_eval)); + } println!( - "Common reference seeded server key share key size size: {} Bits", - server_key_shares[0].size() + "Common reference seeded server key share key size: {} Bits", + server_key_share_size ); println!( "Rgsw nsm std log2 {}", - server_key_stats.brk_rgsw_cts.0.std_dev().abs().log2() + server_key_stats.brk_rgsw_cts.0.std_dev().log2() ); println!( "Rgsw m std log2 {}", - server_key_stats.brk_rgsw_cts.1.std_dev().abs().log2() + server_key_stats.brk_rgsw_cts.1.std_dev().log2() ); println!( "rlwe post 1 auto std log2 {}", - server_key_stats.post_1_auto.std_dev().abs().log2() + server_key_stats.post_1_auto.std_dev().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() + server_key_stats.post_lwe_key_switch.std_dev().log2() ); } #[test] #[cfg(feature = "non_interactive_mp")] - fn querty2() { + fn non_interactive_key_noise() { use crate::{ aggregate_server_key_shares, bool::{ @@ -455,35 +491,55 @@ mod tests { BoolEvaluator, ModularOpsU64, NttBackendU64, }; - set_parameter_set(crate::ParameterSelector::NonInteractiveLTE2Party); - set_common_reference_seed(NonInteractiveMultiPartyCrs::random().seed); - let parties = 2; - let cks = (0..parties).map(|_| gen_client_key()).collect_vec(); - let server_key_shares = cks - .iter() - .enumerate() - .map(|(user_id, k)| gen_server_key_share(user_id, parties, k)) - .collect_vec(); - - let server_key = aggregate_server_key_shares(&server_key_shares); + use super::*; - let server_key_eval = - NonInteractiveServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from( - &server_key, - ); + set_parameter_set(crate::ParameterSelector::NonInteractiveLTE8Party); + set_common_reference_seed(NonInteractiveMultiPartyCrs::random().seed); + let parties = 8; + + let mut server_key_stats = ServerKeyStats::default(); + let mut server_key_share_size = 0; + for i in 0..2 { + let cks = (0..parties).map(|_| gen_client_key()).collect_vec(); + let server_key_shares = cks + .iter() + .enumerate() + .map(|(user_id, k)| gen_server_key_share(user_id, parties, k)) + .collect_vec(); + + // Collect server key size in the 0th iteration + if i == 0 { + // Server key share size may differ for user with last id from + // the share size of other users if the LWE dimension `n` is not + // divisible by no. of parties. + server_key_share_size = std::cmp::max( + server_key_shares.first().unwrap().size(), + server_key_shares.last().unwrap().size(), + ); + } - 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); + let server_key = aggregate_server_key_shares(&server_key_shares); + + let server_key_eval = NonInteractiveServerKeyEvaluationDomain::< + _, + _, + DefaultSecureRng, + NttBackendU64, + >::from(&server_key); + + let parameters = BoolEvaluator::with_local(|e| e.parameters().clone()); + server_key_stats.merge_in(&collect_server_key_stats::< + _, + DefaultDecomposer, + NttBackendU64, + ModularOpsU64>, + _, + >(parameters, &cks, &server_key_eval)); + } println!( - "Common reference seeded server key share key size size: {} Bits", - server_key_shares[0].size() + "Common reference seeded server key share key size: {} Bits", + server_key_share_size ); println!( "Rgsw nsm std log2 {}", @@ -565,10 +621,136 @@ mod tests { rlwe_q_modop.elwise_sub_mut(diff.as_mut_slice(), message.as_ref()); let mut stats = Stats::new(); - stats.add_more(&Vec::::try_convert_from( + stats.add_many_samples(&Vec::::try_convert_from( diff.as_slice(), parameters.rlwe_q(), )); println!("Noise std log2: {}", stats.std_dev().abs().log2()); } + + #[test] + fn mod_switch_noise() { + // Experiment to check mod switch noise using different secret dist in + // multi-party setting + + use itertools::izip; + use num_traits::ToPrimitive; + + use crate::{ + backend::{Modulus, ModulusPowerOf2}, + parameters::SecretKeyDistribution, + random::{DefaultSecureRng, RandomFillGaussian, RandomFillUniformInModulus}, + utils::{fill_random_ternary_secret_with_hamming_weight, tests::Stats}, + ArithmeticOps, ModInit, + }; + + fn mod_switch(v: u64, q_from: u64, q_to: u64) -> f64 { + (v as f64) * (q_to as f64) / q_from as f64 + } + + fn mod_switch_round(v: u64, q_from: u64, q_to: u64) -> u64 { + mod_switch(v, q_from, q_to).round().to_u64().unwrap() + } + + fn mod_switch_odd(v: u64, q_from: u64, q_to: u64) -> u64 { + let odd_v = mod_switch(v, q_from, q_to).floor().to_u64().unwrap(); + odd_v + ((odd_v & 1) ^ 1) + } + + fn sample_secret(n: usize, dist: &SecretKeyDistribution) -> Vec { + let mut s = vec![0i32; n]; + let mut rng = DefaultSecureRng::new(); + + match dist { + SecretKeyDistribution::ErrorDistribution => { + RandomFillGaussian::random_fill(&mut rng, s.as_mut_slice()); + } + SecretKeyDistribution::TernaryDistribution => { + fill_random_ternary_secret_with_hamming_weight(&mut s, n >> 1, &mut rng); + } + } + + s + } + + let parties = 2; + let q_from = 1 << 40; + let q_to = 1 << 20; + let n = 480; + let lweq_in_modop = ModulusPowerOf2::new(q_from); + let lweq_out_modop = ModulusPowerOf2::new(q_to); + let secret_dist = SecretKeyDistribution::ErrorDistribution; + + let mut stats_ms_noise = Stats::new(); + let mut stats_ms_rounding_err = Stats::new(); + + for _ in 0..1000000 { + let mut rng = DefaultSecureRng::new(); + + // sample secrets + + let s = { + let mut s = vec![0i32; n]; + for _ in 0..parties { + let temp = sample_secret(n, &secret_dist); + izip!(s.iter_mut(), temp.iter()).for_each(|(si, ti)| { + *si = *si + *ti; + }); + } + s + }; + + let m = 10; + + // LWE encryption without noise + let mut lwe_in = vec![0u64; n + 1]; + { + RandomFillUniformInModulus::random_fill(&mut rng, &q_from, &mut lwe_in[1..]); + let mut b = m; + izip!(lwe_in.iter().skip(1), s.iter()).for_each(|(ai, si)| { + b = lweq_in_modop.add( + &b, + &lweq_in_modop.mul(ai, &q_from.map_element_from_i64(*si as i64)), + ); + }); + lwe_in[0] = b; + } + + // Mod switch + let lwe_out = lwe_in + .iter() + .map(|v| { + // mod_switch_round(*v, q_from, q_to) + mod_switch_odd(*v, q_from, q_to) + }) + .collect_vec(); + + let rounding_errors = izip!(lwe_out.iter(), lwe_in.iter()) + .map(|(v_out, v_in)| { + let r_i = mod_switch(*v_in, q_from, q_to) - (*v_out as f64); + r_i + }) + .collect_vec(); + stats_ms_rounding_err.add_many_samples(&rounding_errors); + + // LWE decrypt and calculate ms noise + let mut m_back = 0; + izip!(lwe_out.iter().skip(1), s.iter()).for_each(|(ai, si)| { + m_back = lweq_out_modop.add( + &m_back, + &lweq_out_modop.mul(ai, &q_from.map_element_from_i64(*si as i64)), + ); + }); + m_back = lweq_out_modop.sub(&lwe_out[0], &m_back); + let noise = lweq_out_modop.sub(&m_back, &m); + stats_ms_noise.add_many_samples(&vec![q_to.map_element_to_i64(&noise)]); + } + + println!("ms noise variance: {}", stats_ms_noise.variance()); + println!("ms rounding errors mean: {}", stats_ms_rounding_err.mean()); + println!( + "ms rounding errors variance: {}", + stats_ms_rounding_err.variance() + ); + } } diff --git a/src/lib.rs b/src/lib.rs index 544973a..c4e5b66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -194,6 +194,7 @@ trait SizeInBitsWithLogModulus { /// 2^{log_modulus-1} < Q <= `2^log_modulus` fn size(&self, log_modulus: usize) -> usize; } + impl SizeInBitsWithLogModulus for Vec> { fn size(&self, log_modulus: usize) -> usize { let mut total = 0; diff --git a/src/rgsw/mod.rs b/src/rgsw/mod.rs index 2e0234c..a50608a 100644 --- a/src/rgsw/mod.rs +++ b/src/rgsw/mod.rs @@ -878,7 +878,7 @@ pub(crate) mod tests { let mut diff = want_m; mod_op.elwise_sub_mut(diff.as_mut(), got_m.as_ref()); - stats.add_more(&Vec::::try_convert_from(&diff, q)); + stats.add_many_samples(&Vec::::try_convert_from(&diff, q)); } // RLWE(\beta^j m) @@ -899,7 +899,7 @@ pub(crate) mod tests { let mut diff = want_m; mod_op.elwise_sub_mut(diff.as_mut(), got_m.as_ref()); - stats.add_more(&Vec::::try_convert_from(&diff, q)); + stats.add_many_samples(&Vec::::try_convert_from(&diff, q)); } stats diff --git a/src/utils.rs b/src/utils.rs index d81b14e..3cac804 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -271,7 +271,7 @@ impl TryConvertFrom1<[P::Element], P> for Vec { pub(crate) mod tests { use std::fmt::Debug; - use num_traits::{FromPrimitive, PrimInt}; + use num_traits::ToPrimitive; use crate::random::DefaultSecureRng; @@ -288,7 +288,7 @@ pub(crate) mod tests { } } - impl Stats + impl Stats where // T: for<'a> Sum<&'a T>, T: for<'a> std::iter::Sum<&'a T> + std::iter::Sum, @@ -301,7 +301,7 @@ pub(crate) mod tests { self.samples.iter().sum::().to_f64().unwrap() / (self.samples.len() as f64) } - pub(crate) fn std_dev(&self) -> f64 { + pub(crate) fn variance(&self) -> f64 { let mean = self.mean(); // diff @@ -315,12 +315,24 @@ pub(crate) mod tests { .into_iter() .sum::(); - (diff_sq / (self.samples.len() as f64)).sqrt() + diff_sq / (self.samples.len() as f64 - 1.0) + } + + pub(crate) fn std_dev(&self) -> f64 { + self.variance().sqrt() } - pub(crate) fn add_more(&mut self, values: &[T]) { + pub(crate) fn add_many_samples(&mut self, values: &[T]) { self.samples.extend(values.iter()); } + + pub(crate) fn add_sample(&mut self, value: T) { + self.samples.push(value) + } + + pub(crate) fn merge_in(&mut self, other: &Self) { + self.samples.extend(other.samples.iter()); + } } #[test]