diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..996253d1e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +members = [ + "concrete-security-curves-rust", +] \ No newline at end of file diff --git a/Makefile b/Makefile index 49f67b0d9..9d10da393 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ SAGE_SECURITY_CURVES=$(SECURITY_LEVELS:%=$(SAGE_OBJECT_DIR)/%.sobj) SAGE_VERIFIED_CURVES=$(SAGE_OBJECT_DIR)/verified_curves.sobj CURVES_JSON_PATH=json/curves.json CURVES_CPP_GEN_H=concrete-security-curves-cpp/include/concrete/curves.gen.h -CURVES_RUST_GEN_TXT=concrete-security-curves-rust/src/curves.gen.rs +CURVES_RUST_GEN_TXT=concrete-security-curves-rust/src/gaussian/curves_gen.rs generate-code: generate-cpp generate-rust diff --git a/concrete-security-curves-rust/gen_table.py b/concrete-security-curves-rust/gen_table.py index 4646cbbc5..470503340 100644 --- a/concrete-security-curves-rust/gen_table.py +++ b/concrete-security-curves-rust/gen_table.py @@ -5,7 +5,8 @@ def print_curve(data): def print_rust_curves_declaration(datas): - print(f"const SECURITY_WEIGHTS_ARRAY: [(u64, SecurityWeights); {len(datas)}] = [") + print("use super::security_weights::SecurityWeights;") + print(f"pub const SECURITY_WEIGHTS_ARRAY: [(u64, SecurityWeights); {len(datas)}] = [") for data in datas: print_curve(data) print("];") diff --git a/concrete-security-curves-rust/src/curves.gen.rs b/concrete-security-curves-rust/src/gaussian/curves_gen.rs similarity index 80% rename from concrete-security-curves-rust/src/curves.gen.rs rename to concrete-security-curves-rust/src/gaussian/curves_gen.rs index e4ae04ce2..5604f2e67 100644 --- a/concrete-security-curves-rust/src/curves.gen.rs +++ b/concrete-security-curves-rust/src/gaussian/curves_gen.rs @@ -1,4 +1,5 @@ -const SECURITY_WEIGHTS_ARRAY: [(u64, SecurityWeights); 4] = [ +use super::security_weights::SecurityWeights; +pub const SECURITY_WEIGHTS_ARRAY: [(u64, SecurityWeights); 4] = [ (80, SecurityWeights { slope: -0.04045822621883835, bias: 1.7183812000404686, minimal_lwe_dimension: 450 }), (112, SecurityWeights { slope: -0.029881371645803536, bias: 2.6539316216894946, minimal_lwe_dimension: 450 }), (128, SecurityWeights { slope: -0.026599462343105267, bias: 2.981543184145991, minimal_lwe_dimension: 450 }), diff --git a/concrete-security-curves-rust/src/gaussian/mod.rs b/concrete-security-curves-rust/src/gaussian/mod.rs new file mode 100644 index 000000000..9d1f1c5c6 --- /dev/null +++ b/concrete-security-curves-rust/src/gaussian/mod.rs @@ -0,0 +1,3 @@ +pub mod curves_gen; +pub mod security_weights; +pub mod security; \ No newline at end of file diff --git a/concrete-security-curves-rust/src/gaussian/security.rs b/concrete-security-curves-rust/src/gaussian/security.rs new file mode 100644 index 000000000..1ed79ca9b --- /dev/null +++ b/concrete-security-curves-rust/src/gaussian/security.rs @@ -0,0 +1,71 @@ +use super::curves_gen::SECURITY_WEIGHTS_ARRAY; +use super::security_weights::SecurityWeights; + +pub fn supported_security_levels() -> impl std::iter::Iterator { + SECURITY_WEIGHTS_ARRAY + .iter() + .map(|(security_level, _)| *security_level) +} + +pub fn security_weight(security_level: u64) -> Option { + let index = SECURITY_WEIGHTS_ARRAY + .binary_search_by_key(&security_level, |(security_level, _weights)| { + *security_level + }) + .ok()?; + + Some(SECURITY_WEIGHTS_ARRAY[index].1) +} + +/// Noise ensuring security +pub fn minimal_variance_lwe( + lwe_dimension: u64, + ciphertext_modulus_log: u32, + security_level: u64, +) -> f64 { + minimal_variance_glwe(lwe_dimension, 1, ciphertext_modulus_log, security_level) +} + +/// Noise ensuring security +pub fn minimal_variance_glwe( + glwe_dimension: u64, + polynomial_size: u64, + ciphertext_modulus_log: u32, + security_level: u64, +) -> f64 { + let equiv_lwe_dimension = glwe_dimension * polynomial_size; + let security_weights = security_weight(security_level) + .unwrap_or_else(|| panic!("{security_level} bits of security is not supported")); + + let secure_log2_std = + security_weights.secure_log2_std(equiv_lwe_dimension, ciphertext_modulus_log as f64); + let log2_var = 2.0 * secure_log2_std; + f64::exp2(log2_var) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let weight = security_weight(128).unwrap(); + + let secure_log_2_std = weight.secure_log2_std(512, 64.); + + assert!((-12.0..-10.0).contains(&secure_log_2_std)); + } + + #[test] + fn security_security_glwe_variance_low() { + let integer_size = 64; + let golden_std_dev = 2.168_404_344_971_009e-19; + let security_level = 128; + + let actual_var = minimal_variance_glwe(10, 1 << 14, integer_size, security_level); + let actual_std_dev = actual_var.sqrt(); + let expected_std_dev = (0.99 * golden_std_dev)..(1.01 * golden_std_dev); + assert!(expected_std_dev.contains(&actual_std_dev)); + } + +} diff --git a/concrete-security-curves-rust/src/gaussian/security_weights.rs b/concrete-security-curves-rust/src/gaussian/security_weights.rs new file mode 100644 index 000000000..4bc80974b --- /dev/null +++ b/concrete-security-curves-rust/src/gaussian/security_weights.rs @@ -0,0 +1,23 @@ +#[derive(Clone, Copy)] +pub struct SecurityWeights { + pub(crate) slope: f64, + pub(crate) bias: f64, + pub minimal_lwe_dimension: u64, +} + +impl SecurityWeights { + pub fn secure_log2_std(&self, lwe_dimension: u64, ciphertext_modulus_log: f64) -> f64 { + // ensure to have a minimal on std deviation covering the 2 lowest bits on modular scale + let epsilon_log2_std_modular = 2.0; + let epsilon_log2_std = epsilon_log2_std_modular - (ciphertext_modulus_log); + // ensure the requested lwe_dimension is bigger than the minimal lwe dimension + if self.minimal_lwe_dimension <= lwe_dimension { + f64::max( + self.slope * lwe_dimension as f64 + self.bias, + epsilon_log2_std, + ) + } else { + ciphertext_modulus_log + } + } +} diff --git a/concrete-security-curves-rust/src/lib.rs b/concrete-security-curves-rust/src/lib.rs index 2e89d02a0..1e195d938 100644 --- a/concrete-security-curves-rust/src/lib.rs +++ b/concrete-security-curves-rust/src/lib.rs @@ -1,53 +1 @@ -include!("./curves.gen.rs"); - -#[derive(Clone, Copy)] -pub struct SecurityWeights { - slope: f64, - bias: f64, - minimal_lwe_dimension: u64, -} - -impl SecurityWeights { - pub fn secure_log2_std(&self, lwe_dimension: u64, ciphertext_modulus_log: f64) -> f64 { - // ensure to have a minimal on std deviation covering the 2 lowest bits on modular scale - let epsilon_log2_std_modular = 2.0; - let epsilon_log2_std = epsilon_log2_std_modular - (ciphertext_modulus_log); - // ensure the requested lwe_dimension is bigger than the minimal lwe dimension - if self.minimal_lwe_dimension <= lwe_dimension { - f64::max( - self.slope * lwe_dimension as f64 + self.bias, - epsilon_log2_std, - ) - } else { - ciphertext_modulus_log - } - } -} - -pub fn supported_security_levels() -> impl std::iter::Iterator { - SECURITY_WEIGHTS_ARRAY - .iter() - .map(|(security_level, _)| *security_level) -} - -pub fn security_weight(security_level: u64) -> Option { - let index = SECURITY_WEIGHTS_ARRAY - .binary_search_by_key(&security_level, |(security_level, _)| *security_level) - .ok()?; - - Some(SECURITY_WEIGHTS_ARRAY[index].1) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let weight = security_weight(128).unwrap(); - - let secure_log_2_std = weight.secure_log2_std(512, 64.); - - assert!((-12.0..-10.0).contains(&secure_log_2_std)); - } -} +pub mod gaussian;