This contains the operations available in tfhe::boolean, along with code examples.
The NOT unary gate
use tfhe::boolean::prelude::*;fnmain() {// We generate a set of client/server keys, using the default parameters:let (client_key, server_key) =gen_keys();// We use the client secret key to encrypt a message:let ct_1 = client_key.encrypt(true);// We use the server public key to execute the NOT gate:let ct_not = server_key.not(&ct_1);// We use the client key to decrypt the output of the circuit:let output = client_key.decrypt(&ct_not);assert_eq!(output, false);}
Binary gates
use tfhe::boolean::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys();
// We use the client secret key to encrypt a message:
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
// We use the server public key to execute the XOR gate:
let ct_xor = server_key.xor(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_xor);
assert_eq!(output, true^false);
}
The MUX ternary gate
Let ct_1, ct_2, ct_3 be three Boolean ciphertexts. Then, the MUX gate (abbreviation of MUltipleXer) is equivalent to the operation:
if ct_1 {
return ct_2
} else {
return ct_3
}
This example shows how to use the MUX ternary gate:
use tfhe::boolean::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys();
let bool1 = true;
let bool2 = false;
let bool3 = true;
// We use the client secret key to encrypt a message:
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
let ct_3 = client_key.encrypt(false);
// We use the server public key to execute the NOT gate:
let ct_xor = server_key.mux(&ct_1, &ct_2, &ct_3);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_xor);
assert_eq!(output, if bool1 {bool2} else {bool3});
}
Cryptographic parameters
Default parameters
The TFHE cryptographic scheme relies on a variant of Regev cryptosystem and is based on a problem so difficult that it is even post-quantum resistant.
Some cryptographic parameters will require tuning to ensure both the correctness of the result and the security of the computation.
To make it simpler, we've provided two sets of parameters, which ensure correct computations for a certain probability with the standard security of 128 bits. There exists an error probability due to the probabilistic nature of the encryption, which requires adding randomness (noise) following a Gaussian distribution. If this noise is too large, the decryption will not give a correct result. There is a trade-off between efficiency and correctness: generally, using a less efficient parameter set (in terms of computation time) leads to a smaller risk of having an error during homomorphic evaluation.
In the two proposed sets of parameters, the only difference lies in this error probability. The default parameter set ensures an error probability of at most 2−40 when computing a programmable bootstrapping (i.e., any gates but the not). The other one is closer to the error probability claimed in the original TFHE paper, namely 2−165, but it is up-to-date regarding security requirements.
The following array summarizes this:
Parameter set
Error probability
DEFAULT_PARAMETERS
2−40
TFHE_LIB_PARAMETERS
2−165
User-defined parameters
You can also create your own set of parameters. This is an unsafe operation as failing to properly fix the parameters will result in an incorrect and/or insecure computation:
use tfhe::boolean::prelude::*;
fn main() {
// WARNING: might be insecure and/or incorrect
// You can create your own set of parameters
let parameters = unsafe {
BooleanParameters::new(
LweDimension(586),
GlweDimension(2),
PolynomialSize(512),
DynamicDistribution::new_gaussian_from_std_dev(
StandardDev(0.00008976167396834998),
),
DynamicDistribution::new_gaussian_from_std_dev(
StandardDev(0.00000002989040792967434),
),
DecompositionBaseLog(8),
DecompositionLevelCount(2),
DecompositionBaseLog(2),
DecompositionLevelCount(5),
EncryptionKeyChoice::Small,
)
};
}
Serialization/Deserialization
Since the ServerKey and ClientKey types both implement the Serialize and Deserialize traits, you are free to use any serializer that suits you to save and load the keys to disk.
Here is an example using the bincode serialization library, which serializes to a binary format:
use std::fs::{File, create_dir_all};use std::io::{Write, Read};use tfhe::boolean::prelude::*;fnmain() {// We generate a set of client/server keys, using the default parameters:let (client_key, server_key) =gen_keys();// We serialize the keys to bytes:let encoded_server_key:Vec<u8> = bincode::serialize(&server_key).unwrap();let encoded_client_key:Vec<u8> = bincode::serialize(&client_key).unwrap();// Create a tmp dir with the current user name to avoid cluttering the /tmp dirlet user = std::env::var("USER").unwrap_or_else(|_|"unknown_user".to_string());let tmp_dir_for_user =&format!("/tmp/{user}");create_dir_all(tmp_dir_for_user).unwrap();let server_key_file =&format!("{tmp_dir_for_user}/ser_example_server_key.bin");let client_key_file =&format!("{tmp_dir_for_user}/ser_example_client_key.bin");// We write the keys to files:letmut file =File::create(server_key_file).expect("failed to create server key file"); file.write_all(encoded_server_key.as_slice()).expect("failed to write key to file");letmut file =File::create(client_key_file).expect("failed to create client key file"); file.write_all(encoded_client_key.as_slice()).expect("failed to write key to file");// We retrieve the keys:letmut file =File::open(server_key_file).expect("failed to open server key file");letmut encoded_server_key:Vec<u8> =Vec::new(); file.read_to_end(&mut encoded_server_key).expect("failed to read the key");letmut file =File::open(client_key_file).expect("failed to open client key file");letmut encoded_client_key:Vec<u8> =Vec::new(); file.read_to_end(&mut encoded_client_key).expect("failed to read the key");// We deserialize the keys:let loaded_server_key:ServerKey= bincode::deserialize(&encoded_server_key[..]).expect("failed to deserialize");let loaded_client_key:ClientKey= bincode::deserialize(&encoded_client_key[..]).expect("failed to deserialize");let ct_1 = client_key.encrypt(false);// We check for equality:assert_eq!(false, loaded_client_key.decrypt(&ct_1));}