diff --git a/src/bool/keys.rs b/src/bool/keys.rs index a40bf3e..a927747 100644 --- a/src/bool/keys.rs +++ b/src/bool/keys.rs @@ -54,7 +54,8 @@ pub struct ClientKey { 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 @@ mod impl_ck { 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 { + 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 @@ mod impl_ck { ::sk_rlwe(&self) } fn sk_u_rlwe(&self) -> Vec { + 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 @@ pub(super) mod tests { bool::ClientKey, decomposer::NumInfo, lwe::decrypt_lwe, - parameters::CiphertextModulus, + parameters::{CiphertextModulus, I_2P}, utils::TryConvertFrom1, ArithmeticOps, Row, }; @@ -1540,4 +1560,11 @@ pub(super) mod tests { .unwrap() .log2() } + // #[test] + // fn trial() { + // let parameters = I_2P; + // let ck = ClientKey::new(parameters); + // let lwe = ck.sk_lwe(); + // dbg!(lwe); + // } } diff --git a/src/bool/ni_mp_api.rs b/src/bool/ni_mp_api.rs index 5ada3ec..8328e9a 100644 --- a/src/bool/ni_mp_api.rs +++ b/src/bool/ni_mp_api.rs @@ -447,23 +447,26 @@ mod tests { 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 @@ mod tests { 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 @@ 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 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 @@ mod tests { ct1 = ct0; ct0 = ct_out; } + + println!("Noise std dev log2: {}", stats.std_dev().abs().log2()); } } diff --git a/src/bool/parameters.rs b/src/bool/parameters.rs index d5e35ab..8a2e0d9 100644 --- a/src/bool/parameters.rs +++ b/src/bool/parameters.rs @@ -78,6 +78,16 @@ impl SingleDecomposerParams for (DecompostionLogBase, DecompositionCount) { } } +#[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, @@ -86,6 +96,10 @@ pub(crate) enum ParameterVariant { } #[derive(Clone, PartialEq)] pub struct BoolParameters { + /// 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, /// LWE ciphertext modulus q (usually referred to as Q_{ks}) @@ -153,6 +167,14 @@ pub struct BoolParameters { } impl BoolParameters { + 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 { &self.rlwe_q } @@ -502,6 +524,8 @@ where } pub(crate) const MP_BOOL_PARAMS: 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, @@ -524,6 +548,8 @@ pub(crate) const MP_BOOL_PARAMS: BoolParameters = BoolParameters:: { }; 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), br_q: 1 << 11, @@ -546,6 +572,8 @@ pub(crate) const SMALL_MP_BOOL_PARAMS: BoolParameters = 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 << 15), br_q: 1 << 11, @@ -568,6 +596,8 @@ pub(crate) const I_2P: 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 << 15), br_q: 1 << 11, @@ -593,6 +623,8 @@ pub(crate) const NI_2P: BoolParameters = BoolParameters:: { }; pub(crate) const NI_4P: 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, @@ -619,6 +651,8 @@ pub(crate) const NI_4P: BoolParameters = BoolParameters:: { #[cfg(test)] pub(crate) const SP_TEST_BOOL_PARAMS: BoolParameters = BoolParameters:: { + 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, diff --git a/src/random.rs b/src/random.rs index a7a164c..1745a94 100644 --- a/src/random.rs +++ b/src/random.rs @@ -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}; @@ -36,6 +36,15 @@ where fn random_fill(&mut self, container: &mut M); } +pub trait RandomFillGaussian +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); +} + pub trait RandomFillUniformInModulus where M: ?Sized, @@ -133,6 +142,23 @@ where } } +impl 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(); + }); + } +} + impl RandomFill<[T; 32]> for DefaultSecureRng where T: PrimInt + SampleUniform,