diff --git a/script/research/tfhe/Cargo.toml b/script/research/tfhe/Cargo.toml index 5f5fd086a..19482afac 100644 --- a/script/research/tfhe/Cargo.toml +++ b/script/research/tfhe/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [workspace] [dependencies] -tfhe = {version = "0.2.3", features = ["boolean", "shortint", "x86_64"]} +tfhe = {version = "0.2.3", features = ["boolean", "shortint", "integer", "x86_64-unix"]} diff --git a/script/research/tfhe/src/main.rs b/script/research/tfhe/src/main.rs index 05d21c57a..f13a86637 100644 --- a/script/research/tfhe/src/main.rs +++ b/script/research/tfhe/src/main.rs @@ -16,13 +16,44 @@ * along with this program. If not, see . */ -use tfhe::shortint::{gen_keys, Parameters}; +use tfhe::{ + boolean::prelude::{gen_keys as boolean_gen_keys, *}, + integer::gen_keys_radix, + shortint::prelude::{gen_keys as shortint_gen_keys, *}, +}; fn main() { + // =============== + // Boolean circuit + // =============== // Generate a set of client/server keys, using the default parameters. // The client generates both keys. The server key is meant to be published // so that homomorphic circuits can be computed. - let (client_key, server_key) = gen_keys(Parameters::default()); + let (client_key, server_key) = boolean_gen_keys(); + + // Encrypt two messages using the (private) client key: + let msg1 = true; + let msg2 = false; + let ct_1 = client_key.encrypt(msg1); + let ct_2 = client_key.encrypt(msg2); + + // We use the server public key to execute a boolean circuit: + // if ((NOT ct_2) NAND (ct_1 AND ct_2)) then (NOT ct_2) else (ct_1 AND ct_2) + let ct_3 = server_key.not(&ct_2); + let ct_4 = server_key.and(&ct_1, &ct_2); + let ct_5 = server_key.nand(&ct_3, &ct_4); + let ct_6 = server_key.mux(&ct_5, &ct_3, &ct_4); + + // We use the client key to decrypt the output of the circuit + let output = client_key.decrypt(&ct_6); + assert!(output); + + // ================ + // Shortint circuit + // ================ + // Generate a set of client/server keys + // with 2 bits of message and 2 bits of carry + let (client_key, server_key) = shortint_gen_keys(PARAM_MESSAGE_2_CARRY_2); let msg1 = 3; let msg2 = 2; @@ -42,10 +73,27 @@ fn main() { let acc = server_key.generate_accumulator(f); // Compute the function over the ciphertext using the PBS - let ct_res = server_key.keyswitch_programmable_bootstrap(&ct_add, &acc); + let ct_res = server_key.apply_lookup_table(&ct_add, &acc); // Decrypt the ciphertext using the (private) client key let output = client_key.decrypt(&ct_res); assert_eq!(output, f(msg1 + msg2)); - println!("{:#b}", msg1 + msg2); + + // =============== + // Integer circuit + // =============== + // We create keys to create 16 bits integers + // using 8 blocks of 2 bits + let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, 8); + + let clear_a = 2382u16; + let clear_b = 29374u16; + + let mut a = cks.encrypt(clear_a as u64); + let mut b = cks.encrypt(clear_b as u64); + + let encrypted_max = sks.smart_max_parallelized(&mut a, &mut b); + let decrypted_max: u64 = cks.decrypt(&encrypted_max); + + assert_eq!(decrypted_max as u16, clear_a.max(clear_b)) }