change LWE secret distribution to Error distribution for all parameters
@@ -54,7 +54,8 @@ }
mod impl_ck { use crate::{ - random::DefaultSecureRng, + parameters::SecretKeyDistribution, + random::{DefaultSecureRng, RandomFillGaussian}, utils::{fill_random_ternary_secret_with_hamming_weight, puncture_p_rng}, };@@ -76,15 +77,29 @@ let mut p_rng = DefaultSecureRng::new_seeded(self.seed);
let lwe_seed = puncture_p_rng::<[u8; 32], DefaultSecureRng>(&mut p_rng, 2); let mut lwe_prng = DefaultSecureRng::new_seeded(lwe_seed); + let mut out = vec![0i32; self.parameters.lwe_n().0]; - fill_random_ternary_secret_with_hamming_weight( - &mut out, - self.parameters.lwe_n().0 >> 1, - &mut lwe_prng, - ); + + match self.parameters.lwe_secret_key_dist() { + &SecretKeyDistribution::ErrorDistribution => { + RandomFillGaussian::random_fill(&mut lwe_prng, &mut out); + } + &SecretKeyDistribution::TernaryDistribution => { + fill_random_ternary_secret_with_hamming_weight( + &mut out, + self.parameters.lwe_n().0 >> 1, + &mut lwe_prng, + ); + } + } out } fn sk_rlwe(&self) -> Vec<Self::Element> { + assert!( + self.parameters.rlwe_secret_key_dist() + == &SecretKeyDistribution::TernaryDistribution + ); + let mut p_rng = DefaultSecureRng::new_seeded(self.seed); let rlwe_seed = puncture_p_rng::<[u8; 32], DefaultSecureRng>(&mut p_rng, 1);@@ -120,6 +135,11 @@ fn sk_rlwe(&self) -> Vec<Self::Element> {
<Self as SinglePartyClientKey>::sk_rlwe(&self) } fn sk_u_rlwe(&self) -> Vec<Self::Element> { + assert!( + self.parameters.rlwe_secret_key_dist() + == &SecretKeyDistribution::TernaryDistribution + ); + let mut p_rng = DefaultSecureRng::new_seeded(self.seed); let rlwe_seed = puncture_p_rng::<[u8; 32], DefaultSecureRng>(&mut p_rng, 3);@@ -1486,7 +1506,7 @@ backend::{GetModulus, Modulus},
bool::ClientKey, decomposer::NumInfo, lwe::decrypt_lwe, - parameters::CiphertextModulus, + parameters::{CiphertextModulus, I_2P}, utils::TryConvertFrom1, ArithmeticOps, Row, };@@ -1540,4 +1560,11 @@ .to_f64()
.unwrap() .log2() } + // #[test] + // fn trial() { + // let parameters = I_2P; + // let ck = ClientKey::new(parameters); + // let lwe = ck.sk_lwe(); + // dbg!(lwe); + // } }
@@ -447,23 +447,26 @@ use itertools::Itertools;
use rand::{thread_rng, RngCore}; use crate::{ + backend::Modulus, bool::{ keys::tests::{ideal_sk_rlwe, measure_noise_lwe}, BooleanGates, }, - Encoder, Encryptor, KeySwitchWithId, MultiPartyDecryptor, + lwe::decrypt_lwe, + utils::tests::Stats, + ArithmeticOps, Encoder, Encryptor, KeySwitchWithId, MultiPartyDecryptor, }; use super::*; #[test] fn non_interactive_mp_bool_nand() { - set_parameter_set(ParameterSelector::NonInteractiveLTE4Party); + set_parameter_set(ParameterSelector::NonInteractiveLTE2Party); let mut seed = [0u8; 32]; thread_rng().fill_bytes(&mut seed); set_common_reference_seed(seed); - let parties = 3; + let parties = 2; let cks = (0..parties).map(|_| gen_client_key()).collect_vec();@@ -495,6 +498,8 @@ let ct = ct.key_switch(1);
ct.extract(0) }; + let mut stats = Stats::new(); + for _ in 0..1000 { // let now = std::time::Instant::now(); let ct_out =@@ -510,13 +515,17 @@
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 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)]); } assert!(m_out == m_expected, "Expected {m_expected} but got {m_out}");@@ -527,5 +536,7 @@
ct1 = ct0; ct0 = ct_out; } + + println!("Noise std dev log2: {}", stats.std_dev().abs().log2()); } }
@@ -79,6 +79,16 @@ }
} #[derive(Clone, PartialEq, Debug)] +pub(crate) enum SecretKeyDistribution { + /// Elements of secret key are sample from Gaussian distribitution with + /// \sigma = 3.19 and \mu = 0.0 + ErrorDistribution, + /// Elements of secret key are chosen from the set {1,0,-1} with hamming + /// weight `floor(N/2)` where `N` is the secret dimension. + TernaryDistribution, +} + +#[derive(Clone, PartialEq, Debug)] pub(crate) enum ParameterVariant { SingleParty, MultiParty,@@ -86,6 +96,10 @@ NonInteractiveMultiParty,
} #[derive(Clone, PartialEq)] pub struct BoolParameters<El> { + /// RLWE secret key distribution + rlwe_secret_key_dist: SecretKeyDistribution, + /// LWE secret key distribtuion + lwe_secret_key_dist: SecretKeyDistribution, /// RLWE ciphertext modulus Q rlwe_q: CiphertextModulus<El>, /// LWE ciphertext modulus q (usually referred to as Q_{ks})@@ -153,6 +167,14 @@ variant: ParameterVariant,
} impl<El> BoolParameters<El> { + pub(crate) fn rlwe_secret_key_dist(&self) -> &SecretKeyDistribution { + &self.rlwe_secret_key_dist + } + + pub(crate) fn lwe_secret_key_dist(&self) -> &SecretKeyDistribution { + &self.lwe_secret_key_dist + } + pub(crate) fn rlwe_q(&self) -> &CiphertextModulus<El> { &self.rlwe_q }@@ -502,6 +524,8 @@ }
} pub(crate) const MP_BOOL_PARAMS: BoolParameters<u64> = BoolParameters::<u64> { + 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,@@ -524,6 +548,8 @@ variant: ParameterVariant::MultiParty,
}; pub(crate) const SMALL_MP_BOOL_PARAMS: BoolParameters<u64> = BoolParameters::<u64> { + 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), br_q: 1 << 11,@@ -546,6 +572,8 @@ variant: ParameterVariant::MultiParty,
}; pub(crate) const I_2P: BoolParameters<u64> = BoolParameters::<u64> { + 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 << 15), br_q: 1 << 11,@@ -568,6 +596,8 @@ variant: ParameterVariant::MultiParty,
}; pub(crate) const NI_2P: BoolParameters<u64> = BoolParameters::<u64> { + 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 << 15), br_q: 1 << 11,@@ -593,6 +623,8 @@ variant: ParameterVariant::NonInteractiveMultiParty,
}; pub(crate) const NI_4P: BoolParameters<u64> = BoolParameters::<u64> { + 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,@@ -619,6 +651,8 @@ };
#[cfg(test)] pub(crate) const SP_TEST_BOOL_PARAMS: BoolParameters<u64> = BoolParameters::<u64> { + rlwe_secret_key_dist: SecretKeyDistribution::TernaryDistribution, + lwe_secret_key_dist: SecretKeyDistribution::ErrorDistribution, rlwe_q: CiphertextModulus::new_non_native(268369921u64), lwe_q: CiphertextModulus::new_non_native(1 << 16), br_q: 1 << 9,
@@ -1,7 +1,7 @@
use std::cell::RefCell; use itertools::izip; -use num_traits::{PrimInt, Zero}; +use num_traits::{FromPrimitive, PrimInt, Zero}; use rand::{distributions::Uniform, Rng, RngCore, SeedableRng}; use rand_chacha::ChaCha8Rng; use rand_distr::{uniform::SampleUniform, Distribution};@@ -33,6 +33,15 @@ where
M: ?Sized, { /// Fill container with random elements of type of its elements + fn random_fill(&mut self, container: &mut M); +} + +pub trait RandomFillGaussian<M> +where + M: ?Sized, +{ + /// Fill container with random elements sampled from normal distribtuion + /// with \mu = 0.0 and \sigma = 3.19. fn random_fill(&mut self, container: &mut M); }@@ -129,6 +138,23 @@ container.iter_mut()
) .for_each(|(from, to)| { *to = from; + }); + } +} + +impl<T> RandomFillGaussian<[T]> for DefaultSecureRng +where + T: FromPrimitive, +{ + fn random_fill(&mut self, container: &mut [T]) { + izip!( + rand_distr::Normal::new(0.0, 3.19f64) + .unwrap() + .sample_iter(&mut self.rng), + container.iter_mut() + ) + .for_each(|(from, to)| { + *to = T::from_f64(from).unwrap(); }); } }