chore(ci): add boolean layer to code coverage

This commit is contained in:
David Testé
2023-09-14 11:10:36 +02:00
committed by David Testé
parent e4769a8212
commit 48aab9d494
7 changed files with 169 additions and 85 deletions

View File

@@ -79,6 +79,11 @@ jobs:
run: |
make GEN_KEY_CACHE_COVERAGE_ONLY=TRUE gen_key_cache
- name: Run boolean coverage
if: steps.changed-files.outputs.tfhe_any_changed == 'true'
run: |
make test_boolean_cov
- name: Run shortint coverage
if: steps.changed-files.outputs.tfhe_any_changed == 'true'
run: |
@@ -89,8 +94,9 @@ jobs:
if: steps.changed-files.outputs.tfhe_any_changed == 'true'
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./coverage/
fail_ci_if_error: true
files: ./coverage/shortint/cobertura.xml
files: shortint/cobertura.xml,boolean/cobertura.xml
- name: Slack Notification
if: ${{ failure() }}

View File

@@ -190,7 +190,7 @@ clippy_concrete_csprng
gen_key_cache: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) run --profile $(CARGO_PROFILE) \
--example generates_test_keys \
--features=$(TARGET_ARCH_FEATURE),shortint,internal-keycache -p tfhe -- \
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,internal-keycache -p tfhe -- \
$(MULTI_BIT_ONLY) $(COVERAGE_ONLY)
.PHONY: build_core # Build core_crypto without experimental features
@@ -285,6 +285,14 @@ test_boolean: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE),boolean -p tfhe -- boolean::
.PHONY: test_boolean_cov # Run the tests of the boolean module with code coverage
test_boolean_cov: install_rs_check_toolchain install_tarpaulin
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) tarpaulin --profile $(CARGO_PROFILE) \
--out Xml --output-dir coverage/boolean --line --engine Llvm --timeout 500 \
--exclude-files $(COVERAGE_EXCLUDED_FILES) \
--features=$(TARGET_ARCH_FEATURE),boolean,internal-keycache,__coverage \
-p tfhe -- boolean::
.PHONY: test_c_api_rs # Run the rust tests for the C API
test_c_api_rs: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) test --profile $(CARGO_PROFILE) \

View File

@@ -1,4 +1,10 @@
use clap::{Arg, ArgAction, Command};
use tfhe::boolean;
use tfhe::boolean::parameters::{
BooleanParameters, DEFAULT_PARAMETERS, DEFAULT_PARAMETERS_KS_PBS,
PARAMETERS_ERROR_PROB_2_POW_MINUS_165, PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS,
TFHE_LIB_PARAMETERS,
};
use tfhe::keycache::NamedParam;
use tfhe::shortint::keycache::{KEY_CACHE, KEY_CACHE_KSK, KEY_CACHE_WOPBS};
use tfhe::shortint::parameters::key_switching::{
@@ -47,7 +53,7 @@ fn client_server_keys() {
if multi_bit_only {
generate_pbs_multi_bit_keys(&ALL_MULTI_BIT_PARAMETER_VEC);
} else if coverage_only {
println!("Generating shortint (ClientKey, ServerKey) for coverage");
println!("Generating keys (ClientKey, ServerKey) for coverage");
const PBS_PARAMS: [ClassicPBSParameters; 5] = [
PARAM_MESSAGE_1_CARRY_1_KS_PBS,
@@ -90,6 +96,15 @@ fn client_server_keys() {
WOPBS_PARAM_MESSAGE_2_CARRY_2_KS_PBS,
)];
generate_wopbs_keys(&WOPBS_PARAMS);
const BOOLEAN_PARAMS: [BooleanParameters; 5] = [
DEFAULT_PARAMETERS,
DEFAULT_PARAMETERS_KS_PBS,
TFHE_LIB_PARAMETERS,
PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS,
];
generate_boolean_keys(&BOOLEAN_PARAMS);
} else {
generate_pbs_keys(&ALL_PARAMETER_VEC);
@@ -221,6 +236,30 @@ fn generate_wopbs_keys(params: &[(ClassicPBSParameters, WopbsParameters)]) {
}
}
fn generate_boolean_keys(params: &[BooleanParameters]) {
println!("Generating boolean (ClientKey, ServerKey)");
for (i, param) in params.iter().copied().enumerate() {
println!(
"Generating [{} / {}] : {}",
i + 1,
params.len(),
param.name()
);
let start = std::time::Instant::now();
let _ = boolean::keycache::KEY_CACHE.get_from_param(param);
let stop = start.elapsed().as_secs();
println!("Generation took {stop} seconds");
// Clear keys as we go to avoid filling the RAM
boolean::keycache::KEY_CACHE.clear_in_memory_cache()
}
}
fn main() {
let work_dir = std::env::current_dir().unwrap();
println!("work_dir: {}", std::env::current_dir().unwrap().display());

View File

@@ -0,0 +1,64 @@
use crate::boolean::parameters::*;
use crate::boolean::{ClientKey, ServerKey};
use crate::keycache::*;
use crate::named_params_impl;
use lazy_static::*;
named_params_impl!( BooleanParameters =>
DEFAULT_PARAMETERS,
DEFAULT_PARAMETERS_KS_PBS,
PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS,
TFHE_LIB_PARAMETERS
);
impl From<BooleanParameters> for (ClientKey, ServerKey) {
fn from(param: BooleanParameters) -> Self {
let cks = ClientKey::new(&param);
let sks = ServerKey::new(&cks);
(cks, sks)
}
}
pub struct Keycache {
inner: ImplKeyCache<BooleanParameters, (ClientKey, ServerKey), FileStorage>,
}
impl Default for Keycache {
fn default() -> Self {
Self {
inner: ImplKeyCache::new(FileStorage::new(
"../keys/boolean/client_server".to_string(),
)),
}
}
}
pub struct SharedKey {
inner: GenericSharedKey<(ClientKey, ServerKey)>,
}
impl SharedKey {
pub fn client_key(&self) -> &ClientKey {
&self.inner.0
}
pub fn server_key(&self) -> &ServerKey {
&self.inner.1
}
}
impl Keycache {
pub fn get_from_param(&self, param: BooleanParameters) -> SharedKey {
SharedKey {
inner: self.inner.get(param),
}
}
pub fn clear_in_memory_cache(&self) {
self.inner.clear_in_memory_cache();
}
}
lazy_static! {
pub static ref KEY_CACHE: Keycache = Default::default();
}

View File

@@ -63,6 +63,8 @@ pub mod ciphertext;
pub mod client_key;
pub mod engine;
pub mod key_switching_key;
#[cfg(any(test, doctest, feature = "internal-keycache"))]
pub mod keycache;
pub mod parameters;
pub mod prelude;
pub mod public_key;

View File

@@ -24,8 +24,6 @@ pub use crate::core_crypto::commons::parameters::{
LweDimension, PolynomialSize,
};
#[cfg(any(test, doctest, feature = "internal-keycache"))]
use crate::keycache::NamedParam;
use serde::{Deserialize, Serialize};
/// A set of cryptographic parameters for homomorphic Boolean circuit evaluation.
@@ -197,16 +195,3 @@ pub const TFHE_LIB_PARAMETERS: BooleanParameters = BooleanParameters {
};
pub const VEC_BOOLEAN_PARAM: [BooleanParameters; 2] = [DEFAULT_PARAMETERS, TFHE_LIB_PARAMETERS];
#[cfg(any(test, doctest, feature = "internal-keycache"))]
impl NamedParam for BooleanParameters {
fn name(&self) -> String {
if *self == DEFAULT_PARAMETERS {
"DEFAULT_PARAMETERS".to_string()
} else if *self == TFHE_LIB_PARAMETERS {
"TFHE_LIB_PARAMETERS".to_string()
} else {
panic!("Unknown parameters, missing name implementation")
}
}
}

View File

@@ -1,17 +1,27 @@
use crate::boolean::ciphertext::Ciphertext;
use crate::boolean::client_key::ClientKey;
use crate::boolean::keycache::KEY_CACHE;
use crate::boolean::parameters::BooleanParameters;
use crate::boolean::server_key::{BinaryBooleanGates, BinaryBooleanGatesAssign, ServerKey};
use crate::boolean::{random_boolean, random_integer};
/// Number of assert in randomized tests
#[cfg(not(feature = "__coverage"))]
const NB_TEST: usize = 128;
// Use lower numbers for coverage to ensure fast tests to counter balance slowdown due to code
// instrumentation
#[cfg(feature = "__coverage")]
const NB_TEST: usize = 1;
/// Number of ciphertext in the deep circuit test
const NB_CT: usize = 8;
/// Number of gates computed in the deep circuit test
#[cfg(not(feature = "__coverage"))]
const NB_GATE: usize = 1 << 11;
#[cfg(feature = "__coverage")]
const NB_GATE: usize = 1 << 5;
mod default_parameters_tests {
use super::*;
@@ -245,11 +255,8 @@ mod tfhe_lib_parameters_tests {
/// test encryption and decryption with the LWE secret key
fn test_encrypt_decrypt_lwe_secret_key(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
let keys = KEY_CACHE.get_from_param(parameters);
let (cks, sks) = (keys.client_key(), keys.server_key());
for _ in 0..NB_TEST {
// encryption of false
@@ -297,11 +304,8 @@ fn random_enum_encryption(cks: &ClientKey, sks: &ServerKey, message: bool) -> Ci
}
fn test_and_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
let keys = KEY_CACHE.get_from_param(parameters);
let (cks, sks) = (keys.client_key(), keys.server_key());
for _ in 0..NB_TEST {
// generation of two random booleans
@@ -310,10 +314,10 @@ fn test_and_gate(parameters: BooleanParameters) {
let expected_result = b1 && b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
let ct1 = random_enum_encryption(cks, sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
let ct2 = random_enum_encryption(cks, sks, b2);
// AND gate -> "left: {:?}, right: {:?}",ct1, ct2
let ct_res = sks.and(&ct1, &ct2);
@@ -375,11 +379,8 @@ fn test_and_gate(parameters: BooleanParameters) {
}
fn test_mux_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
let keys = KEY_CACHE.get_from_param(parameters);
let (cks, sks) = (keys.client_key(), keys.server_key());
for _ in 0..NB_TEST {
// generation of three random booleans
@@ -389,13 +390,13 @@ fn test_mux_gate(parameters: BooleanParameters) {
let expected_result = if b1 { b2 } else { b3 };
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
let ct1 = random_enum_encryption(cks, sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
let ct2 = random_enum_encryption(cks, sks, b2);
// encryption of b3
let ct3 = random_enum_encryption(&cks, &sks, b3);
let ct3 = random_enum_encryption(cks, sks, b3);
// MUX gate
let ct_res = sks.mux(&ct1, &ct2, &ct3);
@@ -412,11 +413,8 @@ fn test_mux_gate(parameters: BooleanParameters) {
}
fn test_nand_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
let keys = KEY_CACHE.get_from_param(parameters);
let (cks, sks) = (keys.client_key(), keys.server_key());
for _ in 0..NB_TEST {
// generation of two random booleans
@@ -425,10 +423,10 @@ fn test_nand_gate(parameters: BooleanParameters) {
let expected_result = !(b1 && b2);
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
let ct1 = random_enum_encryption(cks, sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
let ct2 = random_enum_encryption(cks, sks, b2);
// NAND gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.nand(&ct1, &ct2);
@@ -490,11 +488,8 @@ fn test_nand_gate(parameters: BooleanParameters) {
}
fn test_nor_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
let keys = KEY_CACHE.get_from_param(parameters);
let (cks, sks) = (keys.client_key(), keys.server_key());
for _ in 0..NB_TEST {
// generation of two random booleans
@@ -503,10 +498,10 @@ fn test_nor_gate(parameters: BooleanParameters) {
let expected_result = !(b1 || b2);
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
let ct1 = random_enum_encryption(cks, sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
let ct2 = random_enum_encryption(cks, sks, b2);
// NOR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.nor(&ct1, &ct2);
@@ -568,11 +563,8 @@ fn test_nor_gate(parameters: BooleanParameters) {
}
fn test_not_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
let keys = KEY_CACHE.get_from_param(parameters);
let (cks, sks) = (keys.client_key(), keys.server_key());
for _ in 0..NB_TEST {
// generation of one random booleans
@@ -580,7 +572,7 @@ fn test_not_gate(parameters: BooleanParameters) {
let expected_result = !b1;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
let ct1 = random_enum_encryption(cks, sks, b1);
// NOT gate
let ct_res = sks.not(&ct1);
@@ -604,11 +596,8 @@ fn test_not_gate(parameters: BooleanParameters) {
}
fn test_or_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
let keys = KEY_CACHE.get_from_param(parameters);
let (cks, sks) = (keys.client_key(), keys.server_key());
for _ in 0..NB_TEST {
// generation of two random booleans
@@ -617,10 +606,10 @@ fn test_or_gate(parameters: BooleanParameters) {
let expected_result = b1 || b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
let ct1 = random_enum_encryption(cks, sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
let ct2 = random_enum_encryption(cks, sks, b2);
// OR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.or(&ct1, &ct2);
@@ -682,11 +671,8 @@ fn test_or_gate(parameters: BooleanParameters) {
}
fn test_xnor_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
let keys = KEY_CACHE.get_from_param(parameters);
let (cks, sks) = (keys.client_key(), keys.server_key());
for _ in 0..NB_TEST {
// generation of two random booleans
@@ -695,10 +681,10 @@ fn test_xnor_gate(parameters: BooleanParameters) {
let expected_result = b1 == b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
let ct1 = random_enum_encryption(cks, sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
let ct2 = random_enum_encryption(cks, sks, b2);
// XNOR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.xnor(&ct1, &ct2);
@@ -760,11 +746,8 @@ fn test_xnor_gate(parameters: BooleanParameters) {
}
fn test_xor_gate(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
let keys = KEY_CACHE.get_from_param(parameters);
let (cks, sks) = (keys.client_key(), keys.server_key());
for _ in 0..NB_TEST {
// generation of two random booleans
@@ -773,10 +756,10 @@ fn test_xor_gate(parameters: BooleanParameters) {
let expected_result = b1 ^ b2;
// encryption of b1
let ct1 = random_enum_encryption(&cks, &sks, b1);
let ct1 = random_enum_encryption(cks, sks, b1);
// encryption of b2
let ct2 = random_enum_encryption(&cks, &sks, b2);
let ct2 = random_enum_encryption(cks, sks, b2);
// XOR gate -> left: Ciphertext, right: Ciphertext
let ct_res = sks.xor(&ct1, &ct2);
@@ -899,11 +882,8 @@ fn random_gate_all(ct_tab: &mut [Ciphertext], bool_tab: &mut [bool], sks: &Serve
}
fn test_deep_circuit(parameters: BooleanParameters) {
// generate the client key set
let cks = ClientKey::new(&parameters);
// generate the server key set
let sks = ServerKey::new(&cks);
let keys = KEY_CACHE.get_from_param(parameters);
let (cks, sks) = (keys.client_key(), keys.server_key());
// create an array of ciphertexts
let mut ct_tab: Vec<Ciphertext> = vec![cks.encrypt(true); NB_CT];
@@ -919,7 +899,7 @@ fn test_deep_circuit(parameters: BooleanParameters) {
// compute NB_GATE gates
for _ in 0..NB_GATE {
random_gate_all(&mut ct_tab, &mut bool_tab, &sks);
random_gate_all(&mut ct_tab, &mut bool_tab, sks);
}
// decrypt and assert equality