Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
tfhe = { version = "0.3.2", features = [ "boolean", "shortint", "integer", "x86_64-unix" ] }
use tfhe::prelude::*;
use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheUint8, PublicKey};
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, _) = generate_keys(config);
let public_key = PublicKey::new(&client_key);
let a = FheUint8::try_encrypt(255u8, &public_key).unwrap();
let clear: u8 = a.decrypt(&client_key);
assert_eq!(clear, 255u8);
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, sks) = generate_keys(config);
set_server_key(sks);
let a = FheUint8::try_encrypt_trivial(234u8).unwrap();
let clear: u8 = a.decrypt(&client_key);
assert_eq!(clear, 234);use tfhe::prelude::*;
use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheUint8, CompactPublicKey};
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_custom_integers(
tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS,
None,
)
.build();
let (client_key, _) = generate_keys(config);
let public_key = CompactPublicKey::new(&client_key);
let a = FheUint8::try_encrypt(255u8, &public_key).unwrap();
let clear: u8 = a.decrypt(&client_key);
assert_eq!(clear, 255u8);
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint32};
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, sks) = generate_keys(config);
set_server_key(sks);
// This is going to be faster
let a = FheUint32::try_encrypt(2097152u32, &client_key).unwrap();
let shift = 1u32;
let shifted = a << shift;
let clear: u32 = shifted.decrypt(&client_key);
assert_eq!(clear, 2097152 << 1);
// This is going to be slower
let a = FheUint32::try_encrypt(2097152u32, &client_key).unwrap();
let shift = FheUint32::try_encrypt_trivial(1).unwrap();
let shifted = a << shift;
let clear: u32 = shifted.decrypt(&client_key);
assert_eq!(clear, 2097152 << 1);use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint32};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled()
.enable_custom_integers(
tfhe::shortint::parameters::PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_3_KS_PBS,
None,
)
.build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 673;
let clear_b = 6;
let a = FheUint32::try_encrypt(clear_a, &keys)?;
let b = FheUint32::try_encrypt(clear_b, &keys)?;
let c = &a >> &b;
let decrypted: u32 = c.decrypt(&keys);
assert_eq!(decrypted, clear_a >> clear_b);
Ok(())
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint32};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled()
.enable_custom_integers(
tfhe::shortint::parameters::PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_3_KS_PBS.with_deterministic_execution(),
None,
)
.build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 673;
let clear_b = 6;
let a = FheUint32::try_encrypt(clear_a, &keys)?;
let b = FheUint32::try_encrypt(clear_b, &keys)?;
let c = &a >> &b;
let decrypted: u32 = c.decrypt(&keys);
assert_eq!(decrypted, clear_a >> clear_b);
Ok(())
}use std::fs::File;
use std::io::{Write, Read};
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 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();
let server_key_file = "/tmp/ser_example_server_key.bin";
let client_key_file = "/tmp/ser_example_client_key.bin";
// We write the keys to files:
let mut 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");
let mut 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:
let mut file = File::open(server_key_file)
.expect("failed to open server key file");
let mut encoded_server_key: Vec<u8> = Vec::new();
file.read_to_end(&mut encoded_server_key).expect("failed to read the key");
let mut file = File::open(client_key_file)
.expect("failed to open client key file");
let mut 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));
}# Cargo.toml
[dependencies]
# ...
bincode = "1.3.3"# Cargo.toml
[dependencies]
# ...
bincode = "1.3.3"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);
// 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);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 1;
let msg2 = 0;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys
let (client_key, _) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let public_key = PublicKey::new(&client_key);
let msg1 = 1;
let msg2 = 0;
// We use the client key to encrypt two messages:
let ct_1 = public_key.encrypt(msg1);
let ct_2 = public_key.encrypt(msg2);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 1;
let msg2 = 0;
let modulus = client_key.parameters.message_modulus().0;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 + msg2) % modulus as u64);
}// main.rs
use bincode;
use std::io::Cursor;
use tfhe::shortint::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 1;
let msg2 = 0;
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
let mut serialized_data = Vec::new();
bincode::serialize_into(&mut serialized_data, &server_key)?;
bincode::serialize_into(&mut serialized_data, &ct_1)?;
bincode::serialize_into(&mut serialized_data, &ct_2)?;
// Simulate sending serialized data to a server and getting
// back the serialized result
let serialized_result = server_function(&serialized_data)?;
let result: Ciphertext = bincode::deserialize(&serialized_result)?;
let output = client_key.decrypt(&result);
assert_eq!(output, msg1 + msg2);
Ok(())
}
fn server_function(serialized_data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut serialized_data = Cursor::new(serialized_data);
let server_key: ServerKey = bincode::deserialize_from(&mut serialized_data)?;
let ct_1: Ciphertext = bincode::deserialize_from(&mut serialized_data)?;
let ct_2: Ciphertext = bincode::deserialize_from(&mut serialized_data)?;
let result = server_key.unchecked_add(&ct_1, &ct_2);
let serialized_result = bincode::serialize(&result)?;
Ok(serialized_result)
}// main.rs
use bincode;
use std::io::Cursor;
use tfhe::integer::{gen_keys_radix, ServerKey, RadixCiphertext};
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, server_key) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
let msg1 = 201;
let msg2 = 12;
// message_modulus^vec_length
let modulus = client_key.parameters().message_modulus().0.pow(num_block as u32) as u64;
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
let mut serialized_data = Vec::new();
bincode::serialize_into(&mut serialized_data, &server_key)?;
bincode::serialize_into(&mut serialized_data, &ct_1)?;
bincode::serialize_into(&mut serialized_data, &ct_2)?;
// Simulate sending serialized data to a server and getting
// back the serialized result
let serialized_result = server_function(&serialized_data)?;
let result: RadixCiphertext = bincode::deserialize(&serialized_result)?;
let output: u64 = client_key.decrypt(&result);
assert_eq!(output, (msg1 + msg2) % modulus);
Ok(())
}
fn server_function(serialized_data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut serialized_data = Cursor::new(serialized_data);
let server_key: ServerKey = bincode::deserialize_from(&mut serialized_data)?;
let ct_1: RadixCiphertext = bincode::deserialize_from(&mut serialized_data)?;
let ct_2: RadixCiphertext = bincode::deserialize_from(&mut serialized_data)?;
let result = server_key.unchecked_add(&ct_1, &ct_2);
let serialized_result = bincode::serialize(&result)?;
Ok(serialized_result)
}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);
}if ct_1 {
return ct_2
} else {
return ct_3
}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});
}# Cargo.toml
[dependencies]
# ...
tfhe = { version = "0.3.2", features = ["integer","x86_64-unix"]}
bincode = "1.3.3"// main.rs
use bincode;
use std::io::Cursor;
use tfhe::{ConfigBuilder, ServerKey, generate_keys, set_server_key, FheUint8};
use tfhe::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>>{
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let ( client_key, server_key) = generate_keys(config);
let msg1 = 1;
let msg2 = 0;
let value_1 = FheUint8::encrypt(msg1, &client_key);
let value_2 = FheUint8::encrypt(msg2, &client_key);
// Prepare to send data to the server
// The ClientKey is _not_ sent
let mut serialized_data = Vec::new();
bincode::serialize_into(&mut serialized_data, &server_key)?;
bincode::serialize_into(&mut serialized_data, &value_1)?;
bincode::serialize_into(&mut serialized_data, &value_2)?;
// Simulate sending serialized data to a server and getting
// back the serialized result
let serialized_result = server_function(&serialized_data)?;
let result: FheUint8 = bincode::deserialize(&serialized_result)?;
let output: u8 = result.decrypt(&client_key);
assert_eq!(output, msg1 + msg2);
Ok(())
}
fn server_function(serialized_data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut serialized_data = Cursor::new(serialized_data);
let server_key: ServerKey = bincode::deserialize_from(&mut serialized_data)?;
let ct_1: FheUint8 = bincode::deserialize_from(&mut serialized_data)?;
let ct_2: FheUint8 = bincode::deserialize_from(&mut serialized_data)?;
set_server_key(server_key);
let result = ct_1 + ct_2;
let serialized_result = bincode::serialize(&result)?;
Ok(serialized_result)
}use tfhe::prelude::*;
use tfhe::{ConfigBuilder, generate_keys, set_server_key, CompressedFheUint16};
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, _) = generate_keys(config);
let clear = 12_837u16;
let compressed = CompressedFheUint16::try_encrypt(clear, &client_key).unwrap();
let decompressed = compressed.decompress();
let clear_decompressed: u16 = decompressed.decrypt(&client_key);
assert_eq!(clear_decompressed, clear);
}use tfhe::prelude::*;
use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheUint8, CompressedServerKey, ClientKey};
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let cks = ClientKey::generate(config);
let compressed_sks = CompressedServerKey::new(&cks);
let sks = compressed_sks.decompress();
set_server_key(sks);
let clear_a = 12u8;
let a = FheUint8::try_encrypt(clear_a, &cks).unwrap();
let c = a + 234u8;
let decrypted: u8 = c.decrypt(&cks);
assert_eq!(decrypted, clear_a.wrapping_add(234));
}use tfhe::prelude::*;
use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheUint8, CompressedPublicKey};
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, _) = generate_keys(config);
let public_key = CompressedPublicKey::new(&client_key);
let a = FheUint8::try_encrypt(213u8, &public_key).unwrap();
let clear: u8 = a.decrypt(&client_key);
assert_eq!(clear, 213u8);
}use tfhe::prelude::*;
use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheUint8, CompressedCompactPublicKey};
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_custom_integers(
tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS,
None,
)
.build();
let (client_key, _) = generate_keys(config);
let public_key_compressed = CompressedCompactPublicKey::new(&client_key);
let public_key = public_key_compressed.decompress();
let a = FheUint8::try_encrypt(255u8, &public_key).unwrap();
let clear: u8 = a.decrypt(&client_key);
assert_eq!(clear, 255u8);
}use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheUint8};
use tfhe::prelude::*;
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
// Client-side
let (client_key, server_key) = generate_keys(config);
let clear_a = 27u8;
let clear_b = 128u8;
let a = FheUint8::encrypt(clear_a, &client_key);
let b = FheUint8::encrypt(clear_b, &client_key);
//Server-side
set_server_key(server_key);
let result = a + b;
//Client-side
let decrypted_result: u8 = result.decrypt(&client_key);
let clear_result = clear_a + clear_b;
assert_eq!(decrypted_result, clear_result);
}tfhe = { version = "0.3.2", features = ["integer", "x86_64-unix"]}use tfhe::prelude::*;use tfhe::{ConfigBuilder, generate_keys};
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, server_key) = generate_keys(config);
}use tfhe::{ConfigBuilder, generate_keys, set_server_key};
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, server_key) = generate_keys(config);
set_server_key(server_key);
}let clear_a = 27u8;
let clear_b = 128u8;
let a = FheUint8::encrypt(clear_a, &client_key);
let b = FheUint8::encrypt(clear_b, &client_key);let result = a + b;let decrypted_result: u8 = result.decrypt(&client_key);
let clear_result = clear_a + clear_b;
assert_eq!(decrypted_result, clear_result);


use tfhe::{ConfigBuilder, generate_keys, set_server_key, FheUint8};
use tfhe::prelude::*;
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, server_key) = generate_keys(config);
set_server_key(server_key);
let clear_a = 27u8;
let clear_b = 128u8;
let a = FheUint8::encrypt(clear_a, &client_key);
let b = FheUint8::encrypt(clear_b, &client_key);
let result = a + b;
let decrypted_result: u8 = result.decrypt(&client_key);
let clear_result = clear_a + clear_b;
assert_eq!(decrypted_result, clear_result);
}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 two messages:
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
// 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_eq!(output, true);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys
// using parameters with 2 bits of message and 2 bits of carry
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2);
let msg1 = 1;
let msg2 = 0;
let modulus = client_key.parameters.message_modulus().0;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 + msg2) % modulus as u64);
}use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
fn main() {
// We create keys for radix represention 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))
}use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, server_key) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
}use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, server_key) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
let msg1 = 128u64;
let msg2 = 13u64;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
}use tfhe::integer::gen_keys_radix;
use tfhe::integer::PublicKey;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, _) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
//We generate the public key from the secret client key:
let public_key = PublicKey::new(&client_key);
//encryption
let msg1 = 128u64;
let msg2 = 13u64;
// We use the public key to encrypt two messages:
let ct_1 = public_key.encrypt_radix(msg1, num_block);
let ct_2 = public_key.encrypt_radix(msg2, num_block);
}use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, server_key) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
let msg1 = 128;
let msg2 = 13;
// message_modulus^vec_length
let modulus = client_key.parameters().message_modulus().0.pow(num_block as u32) as u64;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output: u64 = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 + msg2) % modulus);
}
const {
init_panic_hook,
ShortintParametersName,
ShortintParameters,
TfheClientKey,
TfheCompactPublicKey,
TfheCompressedServerKey,
TfheConfigBuilder,
CompactFheUint32List
} = require("./pkg/tfhe.js");
function fhe_uint32_example() {
// Makes it so that if a rust thread panics,
// the error message will be displayed in the console
init_panic_hook();
const block_params = new ShortintParameters(ShortintParametersName.PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
let config = TfheConfigBuilder.all_disabled()
.enable_default_integers()
.build();
let clientKey = TfheClientKey.generate(config);
let compressedServerKey = TfheCompressedServerKey.new(clientKey);
let publicKey = TfheCompactPublicKey.new(clientKey);
let values = [0, 1, 2394, U32_MAX];
let compact_list = CompactFheUint32List.encrypt_with_compact_public_key(values, publicKey);
let serialized_list = compact_list.serialize();
let deserialized_list = CompactFheUint32List.deserialize(serialized_list);
let encrypted_list = deserialized_list.expand();
assert.deepStrictEqual(encrypted_list.length, values.length);
for (let i = 0; i < values.length; i++)
{
let decrypted = encrypted_list[i].decrypt(clientKey);
assert.deepStrictEqual(decrypted, values[i]);
}
}import init, {
initThreadPool, // only available with parallelism
init_panic_hook,
ShortintParametersName,
ShortintParameters,
TfheClientKey,
TfhePublicKey,
} from "./pkg/tfhe.js";
async function example() {
await init()
await initThreadPool(navigator.hardwareConcurrency);
await init_panic_hook();
const block_params = new ShortintParameters(ShortintParametersName.PARAM_SMALL_MESSAGE_2_CARRY_2_COMPACT_PK);
// ....
}$ git clone https://github.com/zama-ai/tfhe-rs.git
Cloning into 'tfhe-rs'...
...
Resolving deltas: 100% (3866/3866), done.
$ cd tfhe-rs
$ cd tfhe
$ rustup run wasm-pack build --release --target=nodejs --features=boolean-client-js-wasm-api,shortint-client-js-wasm-api
[INFO]: Compiling to Wasm...
...
[INFO]: :-) Your wasm pkg is ready to publish at ...$ ls pkg
LICENSE index.html package.json tfhe.d.ts tfhe.js tfhe_bg.txt tfhe_bg.wasm tfhe_bg.wasm.d.ts
$// Here import assert to check the decryption went well and panic otherwise
const assert = require('node:assert').strict;
// Import the Shortint module from the TFHE-rs package generated earlier
const { Shortint } = require("/path/to/built/tfhe/pkg");
function shortint_example() {
// Get pre-defined parameters from the shortint module to manage messages with 4 bits of useful
// information in total (2 bits of "message" and 2 bits of "carry")
let params = Shortint.get_parameters(2, 2);
// Create a new secret ClientKey, this must not be shared
console.log("Generating client keys...")
let cks = Shortint.new_client_key(params);
// Encrypt 3 in a ciphertext
console.log("Encrypting 3...")
let ct = Shortint.encrypt(cks, BigInt(3));
// Demonstrate ClientKey serialization (for example saving it on disk on the user device)
let serialized_cks = Shortint.serialize_client_key(cks);
// Deserialization
let deserialized_cks = Shortint.deserialize_client_key(serialized_cks);
// Demonstrate ciphertext serialization to send over the network
let serialized_ct = Shortint.serialize_ciphertext(ct);
// Deserialize a ciphertext received over the network for example
let deserialized_ct = Shortint.deserialize_ciphertext(serialized_ct);
// Decrypt with the deserialized objects
console.log("Decrypting ciphertext...")
let decrypted = Shortint.decrypt(deserialized_cks, deserialized_ct);
// Check decryption works as expected
assert.deepStrictEqual(decrypted, BigInt(3));
console.log("Decryption successful!")
// Generate public evaluation keys, also called ServerKey
console.log("Generating compressed ServerKey...")
let sks = Shortint.new_compressed_server_key(cks);
// Can be serialized to send over the network to the machine doing the evaluation
let serialized_sks = Shortint.serialize_compressed_server_key(sks);
let deserialized_sks = Shortint.deserialize_compressed_server_key(serialized_sks);
console.log("All done!")
}
shortint_example();$ node example.js
Generating client keys...
Encrypting 3...
Decrypting ciphertext...
Decryption successful!
Generating compressed ServerKey...
All done!
$fn to_lower(string: &String) -> String {
let mut result = String::with_capacity(string.len());
for char in string.chars() {
if char.is_uppercase() {
result.extend(char.to_lowercase().to_string().chars())
}
}
result
}# Cargo.toml
[dependencies]
# Default configuration for x86 Unix machines:
tfhe = { version = "0.3.2", features = ["integer", "x86_64-unix"]}use tfhe::{FheUint8, ConfigBuilder, generate_keys, set_server_key, ClientKey};
use tfhe::prelude::*;
struct FheLatinString{
bytes: Vec<FheUint8>,
// Constant used to switch lower case <=> upper case
cst: FheUint8,
}
impl FheLatinString {
fn encrypt(string: &str, client_key: &ClientKey) -> Self {
assert!(
string.chars().all(|char| char.is_ascii_alphabetic()),
"The input string must only contain ascii letters"
);
let has_mixed_case = string.as_bytes().windows(2).any(|window| {
let first = char::from(*window.first().unwrap());
let second = char::from(*window.last().unwrap());
(first.is_ascii_lowercase() && second.is_ascii_uppercase())
|| (first.is_ascii_uppercase() && second.is_ascii_lowercase())
});
assert!(
!has_mixed_case,
"The input string cannot mix lower case and upper case letters"
);
let fhe_bytes = string
.bytes()
.map(|b| FheUint8::encrypt(b, client_key))
.collect::<Vec<FheUint8>>();
let cst = FheUint8::encrypt(32u8, client_key);
Self {
bytes: fhe_bytes,
cst,
}
}
fn decrypt(&self, client_key: &ClientKey) -> String {
let ascii_bytes = self
.bytes
.iter()
.map(|fhe_b| fhe_b.decrypt(client_key))
.collect::<Vec<u8>>();
String::from_utf8(ascii_bytes).unwrap()
}
fn to_upper(&self) -> Self {
Self {
bytes: self
.bytes
.iter()
.map(|b| b - &self.cst)
.collect::<Vec<FheUint8>>(),
cst: self.cst.clone(),
}
}
fn to_lower(&self) -> Self {
Self {
bytes: self
.bytes
.iter()
.map(|b| b + &self.cst)
.collect::<Vec<FheUint8>>(),
cst: self.cst.clone(),
}
}
}
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, server_key) = generate_keys(config);
set_server_key(server_key);
let my_string = FheLatinString::encrypt("zama", &client_key);
let verif_string = my_string.decrypt(&client_key);
println!("{}", verif_string);
let my_string_upper = my_string.to_upper();
let verif_string = my_string_upper.decrypt(&client_key);
println!("{}", verif_string);
assert_eq!(verif_string, "ZAMA");
let my_string_lower = my_string_upper.to_lower();
let verif_string = my_string_lower.decrypt(&client_key);
println!("{}", verif_string);
assert_eq!(verif_string, "zama");
}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),
StandardDev(0.00008976167396834998),
StandardDev(0.00000002989040792967434),
DecompositionBaseLog(8),
DecompositionLevelCount(2),
DecompositionBaseLog(2),
DecompositionLevelCount(5),
EncryptionKeyChoice::Small,
)
};
}server keystfhe.h header as well as the static (.a) and dynamic (.so) libtfhe binaries can then be found in "${REPO_ROOT}/target/release/"RUSTFLAGS="-C target-cpu=native" cargo +nightly build --release --features=x86_64-unix,high-level-c-api -p tfheuse tfhe::boolean::prelude::*;
fn main() {
// We generate the client key and the server key,
// using the default parameters:
let (client_key, server_key): (ClientKey, ServerKey) = gen_keys();
}use std::fs::File;
use std::io::{Write, Read};
use tfhe::boolean::prelude::*;
fn main() {
//---------------------------- CLIENT SIDE ----------------------------
// We generate a client key and a server key, using the default parameters:
let (client_key, server_key) = gen_keys();
// We serialize the server key to bytes, and store them in a file:
let encoded: Vec<u8> = bincode::serialize(&server_key).unwrap();
let server_key_file = "/tmp/tutorial_server_key.bin";
// We write the server key to a file:
let mut file = File::create(server_key_file)
.expect("failed to create server key file");
file.write_all(encoded.as_slice()).expect("failed to write key to file");
// ...
// We send the key to server side
// ...
//---------------------------- SERVER SIDE ----------------------------
// We read the file:
let mut file = File::open(server_key_file)
.expect("failed to open server key file");
let mut encoded: Vec<u8> = Vec::new();
file.read_to_end(&mut encoded).expect("failed to read key");
// We deserialize the server key:
let key: ServerKey = bincode::deserialize(&encoded[..])
.expect("failed to deserialize");
}use tfhe::boolean::prelude::*;
fn main() {
// Don't consider the following line; you should follow the procedure above.
let (client_key, _) = gen_keys();
//---------------------------- SERVER SIDE
// We use the client key to encrypt the messages:
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
// We serialize the ciphertexts:
let encoded_1: Vec<u8> = bincode::serialize(&ct_1).unwrap();
let encoded_2: Vec<u8> = bincode::serialize(&ct_2).unwrap();
// ...
// And we send them to the server somehow
// ...
}use tfhe::boolean::prelude::*;
fn main() {
// Don't consider the following line; you should follow the procedure above.
let (client_key, _) = gen_keys();
let public_key = PublicKey::new(&client_key);
//---------------------------- SERVER SIDE
// We use the public key to encrypt the messages:
let ct_1 = public_key.encrypt(true);
let ct_2 = public_key.encrypt(false);
// We serialize the ciphertexts:
let encoded_1: Vec<u8> = bincode::serialize(&ct_1).unwrap();
let encoded_2: Vec<u8> = bincode::serialize(&ct_2).unwrap();
// ...
// And we send them to the server somehow
// ...
}use std::fs::File;
use std::io::{Write, Read};
use tfhe::boolean::prelude::*;
fn main() {
// Don't consider the following lines; you should follow the procedure above.
let (client_key, server_key) = gen_keys();
let ct_1 = client_key.encrypt(true);
let ct_2 = client_key.encrypt(false);
let encoded_1: Vec<u8> = bincode::serialize(&ct_1).unwrap();
let encoded_2: Vec<u8> = bincode::serialize(&ct_2).unwrap();
//---------------------------- ON SERVER SIDE ----------------------------
// We deserialize the ciphertexts:
let ct_1: Ciphertext = bincode::deserialize(&encoded_1[..])
.expect("failed to deserialize");
let ct_2: Ciphertext = bincode::deserialize(&encoded_2[..])
.expect("failed to deserialize");
// We use the server key to execute the 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);
// Then we serialize the output of the circuit:
let encoded_output: Vec<u8> = bincode::serialize(&ct_6)
.expect("failed to serialize output");
// ...
// And we send the output to the client
// ...
}use std::fs::File;
use std::io::{Write, Read};
use tfhe::boolean::prelude::*;
fn main() {
// Don't consider the following lines; you should follow the procedure above.
let (client_key, server_key) = gen_keys();
let ct_6 = client_key.encrypt(true);
let encoded_output: Vec<u8> = bincode::serialize(&ct_6).unwrap();
//---------------------------- ON CLIENT SIDE
// We deserialize the output ciphertext:
let output: Ciphertext = bincode::deserialize(&encoded_output[..])
.expect("failed to deserialize");
// Finally, we decrypt the output:
let output = client_key.decrypt(&output);
// And check that the result is the expected one:
assert_eq!(output, true);
}RUSTFLAGS="-C target-cpu=native" cargo build +nightly --release --features=aarch64-unix,high-level-c-api -p tfheproject(my-project)
cmake_minimum_required(VERSION 3.16)
set(TFHE_C_API "/path/to/tfhe-rs/binaries/and/header")
include_directories(${TFHE_C_API})
add_library(tfhe STATIC IMPORTED)
set_target_properties(tfhe PROPERTIES IMPORTED_LOCATION ${TFHE_C_API}/libtfhe.a)
if(APPLE)
find_library(SECURITY_FRAMEWORK Security)
if (NOT SECURITY_FRAMEWORK)
message(FATAL_ERROR "Security framework not found")
endif()
endif()
set(EXECUTABLE_NAME my-executable)
add_executable(${EXECUTABLE_NAME} main.c)
target_include_directories(${EXECUTABLE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${EXECUTABLE_NAME} LINK_PUBLIC tfhe m pthread dl)
if(APPLE)
target_link_libraries(${EXECUTABLE_NAME} LINK_PUBLIC ${SECURITY_FRAMEWORK})
endif()
target_compile_options(${EXECUTABLE_NAME} PRIVATE -Werror)# /!\ Be sure to update CMakeLists.txt to give the absolute path to the compiled tfhe library
$ ls
CMakeLists.txt main.c
$ mkdir build && cd build
$ cmake .. -DCMAKE_BUILD_TYPE=RELEASE
...
$ make
...
$ ./my-executable
Result: 2
$#include <tfhe.h>
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
int main(void)
{
int ok = 0;
// Prepare the config builder for the high level API and choose which types to enable
ConfigBuilder *builder;
Config *config;
// Put the builder in a default state without any types enabled
config_builder_all_disabled(&builder);
// Enable the uint128 type using the small LWE key for encryption
config_builder_enable_default_uint128_small(&builder);
// Populate the config
config_builder_build(builder, &config);
ClientKey *client_key = NULL;
ServerKey *server_key = NULL;
// Generate the keys using the config
generate_keys(config, &client_key, &server_key);
// Set the server key for the current thread
set_server_key(server_key);
FheUint128 *lhs = NULL;
FheUint128 *rhs = NULL;
FheUint128 *result = NULL;
// Encrypt a u128 using 64 bits words, we encrypt 20 << 64 | 10
ok = fhe_uint128_try_encrypt_with_client_key_u128(10, 20, client_key, &lhs);
assert(ok == 0);
// Encrypt a u128 using words, we encrypt 2 << 64 | 1
ok = fhe_uint128_try_encrypt_with_client_key_u128(1, 2, client_key, &rhs);
assert(ok == 0);
// Compute the subtraction
ok = fhe_uint128_sub(lhs, rhs, &result);
assert(ok == 0);
uint64_t w0, w1;
// Decrypt
ok = fhe_uint128_decrypt(result, client_key, &w0, &w1);
assert(ok == 0);
// Here the subtraction allows us to compare each word
assert(w0 == 9);
assert(w1 == 18);
// Destroy the ciphertexts
fhe_uint128_destroy(lhs);
fhe_uint128_destroy(rhs);
fhe_uint128_destroy(result);
// Destroy the keys
client_key_destroy(client_key);
server_key_destroy(server_key);
return EXIT_SUCCESS;
}rustup toolchain install nightlyuse tfhe::core_crypto::prelude::*;
// DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
// computations
// Define parameters for LweCiphertext creation
let lwe_dimension = LweDimension(742);
let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
let ciphertext_modulus = CiphertextModulus::new_native();
// Create the PRNG
let mut seeder = new_seeder();
let seeder = seeder.as_mut();
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
let mut secret_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
// Create the LweSecretKey
let lwe_secret_key =
allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
// Create the plaintext
let msg = 3u64;
let plaintext = Plaintext(msg << 60);
// Create a new LweCiphertext
let mut lwe = LweCiphertext::new(0u64, lwe_dimension.to_lwe_size(), ciphertext_modulus);
encrypt_lwe_ciphertext(
&lwe_secret_key,
&mut lwe,
plaintext,
lwe_modular_std_dev,
&mut encryption_generator,
);
let decrypted_plaintext = decrypt_lwe_ciphertext(&lwe_secret_key, &lwe);
// Round and remove encoding
// First create a decomposer working on the high 4 bits corresponding to our encoding.
let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
let rounded = decomposer.closest_representable(decrypted_plaintext.0);
// Remove the encoding
let cleartext = rounded >> 60;
// Check we recovered the original message
assert_eq!(cleartext, msg);use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 3;
let msg2 = 2;
// We use the client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
}use tfhe::shortint::prelude::*;
fn main() {
let param = unsafe {
ClassicPBSParameters::new(
LweDimension(656),
GlweDimension(2),
PolynomialSize(512),
StandardDev(0.000034119201269311964),
StandardDev(0.00000004053919869756513),
DecompositionBaseLog(8),
DecompositionLevelCount(2),
DecompositionBaseLog(3),
DecompositionLevelCount(4),
MessageModulus(4),
CarryModulus(1),
CiphertextModulus::new_native(),
EncryptionKeyChoice::Big,
)
};
}cargo +nightly build
cargo +nightly testrustup override set nightly
# cargo will use the `nightly` toolchain.
cargo buildrustup showcargo +nightly build --features=nightly-avx512core_crypto modulecore_crypto module.#Boolean benchmarks:
make bench_boolean
#Integer benchmarks:
make bench_integer
#Shortint benchmarks:
make bench_shortint#Integer benchmarks:
make AVX512_SUPPORT=ON bench_integertfhe = { version = "0.3.2", features = [ "x86_64-unix" ] }tfhe = { version = "0.3.2", features = ["x86_64-unix"] }tfhe = { version = "0.3.2", features = ["aarch64-unix"] }tfhe = { version = "0.3.2", features = ["x86_64"] }use tfhe::core_crypto::prelude::*;
pub fn main() {
// DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
// computations
// Define the parameters for a 4 bits message able to hold the doubled 2 bits message
let small_lwe_dimension = LweDimension(742);
let glwe_dimension = GlweDimension(1);
let polynomial_size = PolynomialSize(2048);
let lwe_modular_std_dev = StandardDev(0.000007069849454709433);
let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
let pbs_base_log = DecompositionBaseLog(23);
let pbs_level = DecompositionLevelCount(1);
let ciphertext_modulus = CiphertextModulus::new_native();
// Request the best seeder possible, starting with hardware entropy sources and falling back to
// /dev/random on Unix systems if enabled via cargo features
let mut boxed_seeder = new_seeder();
// Get a mutable reference to the seeder as a trait object from the Box returned by new_seeder
let seeder = boxed_seeder.as_mut();
// Create a generator which uses a CSPRNG to generate secret keys
let mut secret_generator =
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
// Create a generator which uses two CSPRNGs to generate public masks and secret encryption
// noise
let mut encryption_generator =
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
println!("Generating keys...");
// Generate an LweSecretKey with binary coefficients
let small_lwe_sk =
LweSecretKey::generate_new_binary(small_lwe_dimension, &mut secret_generator);
// Generate a GlweSecretKey with binary coefficients
let glwe_sk =
GlweSecretKey::generate_new_binary(glwe_dimension, polynomial_size, &mut secret_generator);
// Create a copy of the GlweSecretKey re-interpreted as an LweSecretKey
let big_lwe_sk = glwe_sk.clone().into_lwe_secret_key();
// Generate the bootstrapping key, we use the parallel variant for performance reason
let std_bootstrapping_key = par_allocate_and_generate_new_lwe_bootstrap_key(
&small_lwe_sk,
&glwe_sk,
pbs_base_log,
pbs_level,
glwe_modular_std_dev,
ciphertext_modulus,
&mut encryption_generator,
);
// Create the empty bootstrapping key in the Fourier domain
let mut fourier_bsk = FourierLweBootstrapKey::new(
std_bootstrapping_key.input_lwe_dimension(),
std_bootstrapping_key.glwe_size(),
std_bootstrapping_key.polynomial_size(),
std_bootstrapping_key.decomposition_base_log(),
std_bootstrapping_key.decomposition_level_count(),
);
// Use the conversion function (a memory optimized version also exists but is more complicated
// to use) to convert the standard bootstrapping key to the Fourier domain
convert_standard_lwe_bootstrap_key_to_fourier(&std_bootstrapping_key, &mut fourier_bsk);
// We don't need the standard bootstrapping key anymore
drop(std_bootstrapping_key);
// Our 4 bits message space
let message_modulus = 1u64 << 4;
// Our input message
let input_message = 3u64;
// Delta used to encode 4 bits of message + a bit of padding on u64
let delta = (1_u64 << 63) / message_modulus;
// Apply our encoding
let plaintext = Plaintext(input_message * delta);
// Allocate a new LweCiphertext and encrypt our plaintext
let lwe_ciphertext_in: LweCiphertextOwned<u64> = allocate_and_encrypt_new_lwe_ciphertext(
&small_lwe_sk,
plaintext,
lwe_modular_std_dev,
ciphertext_modulus,
&mut encryption_generator,
);
// Compute a cleartext multiplication by 2
let mut cleartext_multiplication_ct = lwe_ciphertext_in.clone();
println!("Performing cleartext multiplication...");
lwe_ciphertext_cleartext_mul(
&mut cleartext_multiplication_ct,
&lwe_ciphertext_in,
Cleartext(2),
);
// Decrypt the cleartext multiplication result
let cleartext_multiplication_plaintext: Plaintext<u64> =
decrypt_lwe_ciphertext(&small_lwe_sk, &cleartext_multiplication_ct);
// Create a SignedDecomposer to perform the rounding of the decrypted plaintext
// We pass a DecompositionBaseLog of 5 and a DecompositionLevelCount of 1 indicating we want to
// round the 5 MSB, 1 bit of padding plus our 4 bits of message
let signed_decomposer =
SignedDecomposer::new(DecompositionBaseLog(5), DecompositionLevelCount(1));
// Round and remove our encoding
let cleartext_multiplication_result: u64 =
signed_decomposer.closest_representable(cleartext_multiplication_plaintext.0) / delta;
println!("Checking result...");
assert_eq!(6, cleartext_multiplication_result);
println!(
"Cleartext multiplication result is correct! \
Expected 6, got {cleartext_multiplication_result}"
);
// Now we will use a PBS to compute the same multiplication, it is NOT the recommended way of
// doing this operation in terms of performance as it's much more costly than a multiplication
// with a cleartext, however it resets the noise in a ciphertext to a nominal level and allows
// to evaluate arbitrary functions so depending on your use case it can be a better fit.
// Here we will define a helper function to generate an accumulator for a PBS
fn generate_accumulator<F>(
polynomial_size: PolynomialSize,
glwe_size: GlweSize,
message_modulus: usize,
ciphertext_modulus: CiphertextModulus<u64>,
delta: u64,
f: F,
) -> GlweCiphertextOwned<u64>
where
F: Fn(u64) -> u64,
{
// N/(p/2) = size of each block, to correct noise from the input we introduce the notion of
// box, which manages redundancy to yield a denoised value for several noisy values around
// a true input value.
let box_size = polynomial_size.0 / message_modulus;
// Create the accumulator
let mut accumulator_u64 = vec![0_u64; polynomial_size.0];
// Fill each box with the encoded denoised value
for i in 0..message_modulus {
let index = i * box_size;
accumulator_u64[index..index + box_size]
.iter_mut()
.for_each(|a| *a = f(i as u64) * delta);
}
let half_box_size = box_size / 2;
// Negate the first half_box_size coefficients to manage negacyclicity and rotate
for a_i in accumulator_u64[0..half_box_size].iter_mut() {
*a_i = (*a_i).wrapping_neg();
}
// Rotate the accumulator
accumulator_u64.rotate_left(half_box_size);
let accumulator_plaintext = PlaintextList::from_container(accumulator_u64);
let accumulator =
allocate_and_trivially_encrypt_new_glwe_ciphertext(
glwe_size,
&accumulator_plaintext,
ciphertext_modulus,
);
accumulator
}
// Generate the accumulator for our multiplication by 2 using a simple closure
let accumulator: GlweCiphertextOwned<u64> = generate_accumulator(
polynomial_size,
glwe_dimension.to_glwe_size(),
message_modulus as usize,
ciphertext_modulus,
delta,
|x: u64| 2 * x,
);
// Allocate the LweCiphertext to store the result of the PBS
let mut pbs_multiplication_ct = LweCiphertext::new(
0u64,
big_lwe_sk.lwe_dimension().to_lwe_size(),
ciphertext_modulus,
);
println!("Computing PBS...");
programmable_bootstrap_lwe_ciphertext(
&lwe_ciphertext_in,
&mut pbs_multiplication_ct,
&accumulator,
&fourier_bsk,
);
// Decrypt the PBS multiplication result
let pbs_multiplication_plaintext: Plaintext<u64> =
decrypt_lwe_ciphertext(&big_lwe_sk, &pbs_multiplication_ct);
// Round and remove our encoding
let pbs_multiplication_result: u64 =
signed_decomposer.closest_representable(pbs_multiplication_plaintext.0) / delta;
println!("Checking result...");
assert_eq!(6, pbs_multiplication_result);
println!(
"Mulitplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
);
}
use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let num_block = 4;
let (client_key, server_key) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
}use tfhe::integer::CrtClientKey;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
let basis = vec![2, 3, 5];
let cks = CrtClientKey::new(PARAM_MESSAGE_2_CARRY_2_KS_PBS, basis);
}use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
let num_block = 4;
let (client_key, server_key) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
let msg1 = 12u64;
let msg2 = 11u64;
let msg3 = 9u64;
let scalar = 3u64;
// message_modulus^vec_length
let modulus = client_key.parameters().message_modulus().0.pow(num_block as u32) as u64;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
let ct_3 = client_key.encrypt(msg2);
server_key.unchecked_small_scalar_mul_assign(&mut ct_1, scalar);
server_key.unchecked_sub_assign(&mut ct_1, &ct_2);
server_key.unchecked_add_assign(&mut ct_1, &ct_3);
// We use the client key to decrypt the output of the circuit:
let output: u64 = client_key.decrypt(&ct_1);
// The carry buffer has been overflowed, the result is not correct
assert_ne!(output, ((msg1 * scalar as u64 - msg2) + msg3) % modulus as u64);
}use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
let num_block = 2;
let (client_key, server_key) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
let msg1 = 12u64;
let msg2 = 11u64;
let msg3 = 9u64;
let scalar = 3u64;
// message_modulus^vec_length
let modulus = client_key.parameters().message_modulus().0.pow(num_block as u32) as u64;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
let ct_3 = client_key.encrypt(msg3);
let result = server_key.checked_small_scalar_mul_assign(&mut ct_1, scalar);
assert!(result.is_ok());
let result = server_key.checked_sub_assign(&mut ct_1, &ct_2);
assert!(result.is_err());
// We use the client key to decrypt the output of the circuit:
// Only the scalar multiplication could be done
let output: u64 = client_key.decrypt(&ct_1);
assert_eq!(output, (msg1 * scalar) % modulus as u64);
}use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
let num_block = 4;
let (client_key, server_key) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
let msg1 = 12u64;
let msg2 = 11u64;
let msg3 = 9u64;
let scalar = 3u64;
// message_modulus^vec_length
let modulus = client_key.parameters().message_modulus().0.pow(num_block as u32) as u64;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let mut ct_2 = client_key.encrypt(msg2);
let mut ct_3 = client_key.encrypt(msg3);
server_key.smart_scalar_mul_assign(&mut ct_1, scalar);
server_key.smart_sub_assign(&mut ct_1, &mut ct_2);
server_key.smart_add_assign(&mut ct_1, &mut ct_3);
// We use the client key to decrypt the output of the circuit:
let output: u64 = client_key.decrypt(&ct_1);
assert_eq!(output, ((msg1 * scalar as u64 - msg2) + msg3) % modulus as u64);
}use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
fn main() {
let num_block = 4;
let (client_key, server_key) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2_KS_PBS, num_block);
let msg1 = 12u64;
let msg2 = 11u64;
let msg3 = 9u64;
let scalar = 3u64;
// message_modulus^vec_length
let modulus = client_key.parameters().message_modulus().0.pow(num_block as u32) as u64;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let mut ct_2 = client_key.encrypt(msg2);
let mut ct_3 = client_key.encrypt(msg3);
server_key.scalar_mul_assign_parallelized(&mut ct_1, scalar);
server_key.sub_assign_parallelized(&mut ct_1, &mut ct_2);
server_key.add_assign_parallelized(&mut ct_1, &mut ct_3);
// We use the client key to decrypt the output of the circuit:
let output: u64 = client_key.decrypt(&ct_1);
assert_eq!(output, ((msg1 * scalar as u64 - msg2) + msg3) % modulus as u64);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 3;
let msg2 = 3;
let scalar = 4;
let modulus = client_key.parameters.message_modulus().0;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
server_key.unchecked_scalar_mul_assign(&mut ct_1, scalar);
server_key.unchecked_sub_assign(&mut ct_1, &ct_2);
server_key.unchecked_mul_lsb_assign(&mut ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_1);
println!("expected {}, found {}", ((msg1 * scalar as u64 - msg2) * msg2) % modulus as u64, output);
}use tfhe::shortint::prelude::*;
use std::error::Error;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 3;
let msg2 = 3;
let scalar = 4;
let modulus = client_key.parameters.message_modulus().0;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
let mut ops = || -> Result<(), Box<dyn Error>> {
server_key.checked_scalar_mul_assign(&mut ct_1, scalar)?;
server_key.checked_sub_assign(&mut ct_1, &ct_2)?;
server_key.checked_mul_lsb_assign(&mut ct_1, &ct_2)?;
Ok(())
};
match ops() {
Ok(_) => (),
Err(e) => {
println!("correctness of operations is not guaranteed due to error: {}", e);
return;
},
}
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_1);
assert_eq!(output, ((msg1 * scalar as u64 - msg2) * msg2) % modulus as u64);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 3;
let msg2 = 3;
let scalar = 4;
let modulus = client_key.parameters.message_modulus().0;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let mut ct_2 = client_key.encrypt(msg2);
server_key.smart_scalar_mul_assign(&mut ct_1, scalar);
server_key.smart_sub_assign(&mut ct_1, &mut ct_2);
server_key.smart_mul_lsb_assign(&mut ct_1, &mut ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_1);
assert_eq!(output, ((msg1 * scalar as u64 - msg2) * msg2) % modulus as u64);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys, using the default parameters:
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 3;
let msg2 = 3;
let scalar = 4;
let modulus = client_key.parameters.message_modulus().0;
// We use the client key to encrypt two messages:
let mut ct_1 = client_key.encrypt(msg1);
let mut ct_2 = client_key.encrypt(msg2);
server_key.scalar_mul_assign(&mut ct_1, scalar);
server_key.sub_assign(&mut ct_1, &mut ct_2);
server_key.mul_lsb_assign(&mut ct_1, &mut ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_1);
assert_eq!(output, ((msg1 * scalar as u64 - msg2) * msg2) % modulus as u64);
}use tfhe::shortint::prelude::*;
fn main() {
// Generate the client key and the server key:
let (cks, _) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let pks = PublicKey::new(&cks);
let msg = 2;
// Encryption of one message:
let ct = pks.encrypt(msg);
// Decryption:
let dec = cks.decrypt(&ct);
assert_eq!(dec, msg);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys to compute over Z/2^2Z, with 2 carry bits
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 2;
let msg2 = 1;
let modulus = client_key.parameters.message_modulus().0;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 + msg2) % modulus as u64);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys to compute over Z/2^2Z, with 2 carry bits
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 2;
let msg2 = 1;
let modulus = client_key.parameters.message_modulus().0;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to homomorphically compute a bitwise AND:
let ct_3 = server_key.unchecked_bitand(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 & msg2) % modulus as u64);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys to compute over Z/2^2Z, with 2 carry bits
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 2;
let msg2 = 1;
let modulus = client_key.parameters.message_modulus().0;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
let ct_3 = server_key.unchecked_greater_or_equal(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
assert_eq!(output, (msg1 >= msg2) as u64 % modulus as u64);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys to compute over Z/2^2Z, with 2 carry bits
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 3;
let modulus = client_key.parameters.message_modulus().0;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let acc = server_key.generate_lookup_table(|n| n.count_ones().into());
// add the two ciphertexts
let ct_res = server_key.apply_lookup_table(&ct_1, &acc);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_res);
assert_eq!(output, msg1.count_ones() as u64);
}use tfhe::shortint::prelude::*;
fn main() {
// We generate a set of client/server keys to compute over Z/2^2Z, with 2 carry bits
let (client_key, server_key) = gen_keys(PARAM_MESSAGE_2_CARRY_2_KS_PBS);
let msg1 = 3;
let msg2 = 2;
let modulus = client_key.parameters.message_modulus().0 as u64;
// We use the private client key to encrypt two messages:
let ct_1 = client_key.encrypt(msg1);
let mut ct_2 = client_key.encrypt(msg2);
// Compute the lookup table for the bivariate functions
let acc = server_key.generate_lookup_table_bivariate(|x,y| (x.count_ones()
+ y.count_ones()) as u64 % modulus );
let ct_res = server_key.smart_apply_lookup_table_bivariate(&ct_1, &mut ct_2, &acc);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_res);
assert_eq!(output, (msg1.count_ones() as u64 + msg2.count_ones() as u64) % modulus);
}
# Cargo.toml
# Default configuration for x86 Unix machines:
tfhe = { version = "0.3.2", features = ["boolean", "x86_64-unix"]}use tfhe::FheBool;
use tfhe::prelude::*;
#[derive(Copy, Clone, Debug)]
enum ParityMode {
// The sum bits of message + parity bit must an odd number
Odd,
// The sum bits of message + parity bit must an even number
Even,
}
fn compute_parity_bit(fhe_bits: &[FheBool], mode: ParityMode) -> FheBool {
let mut parity_bit = fhe_bits[0].clone();
for fhe_bit in &fhe_bits[1..] {
parity_bit = fhe_bit ^ parity_bit
}
match mode {
ParityMode::Odd => !parity_bit,
ParityMode::Even => parity_bit,
}
}
fn is_even(n: u8) -> bool {
(n & 1) == 0
}
fn is_odd(n: u8) -> bool {
!is_even(n)
}
fn check_parity_bit_validity(bits: &[bool], mode: ParityMode, parity_bit: bool) -> bool {
let num_bit_set = bits
.iter()
.map(|bit| *bit as u8)
.fold(parity_bit as u8, |acc, bit| acc + bit);
match mode {
ParityMode::Even => is_even(num_bit_set),
ParityMode::Odd => is_odd(num_bit_set),
}
}use tfhe::{FheBool, ConfigBuilder, generate_keys, set_server_key};
use tfhe::prelude::*;
#[derive(Copy, Clone, Debug)]
enum ParityMode {
// The sum bits of message + parity bit must an odd number
Odd,
// The sum bits of message + parity bit must an even number
Even,
}
fn compute_parity_bit(fhe_bits: &[FheBool], mode: ParityMode) -> FheBool {
let mut parity_bit = fhe_bits[0].clone();
for fhe_bit in &fhe_bits[1..] {
parity_bit = fhe_bit ^ parity_bit
}
match mode {
ParityMode::Odd => !parity_bit,
ParityMode::Even => parity_bit,
}
}
fn is_even(n: u8) -> bool {
(n & 1) == 0
}
fn is_odd(n: u8) -> bool {
!is_even(n)
}
fn check_parity_bit_validity(bits: &[bool], mode: ParityMode, parity_bit: bool) -> bool {
let num_bit_set = bits
.iter()
.map(|bit| *bit as u8)
.fold(parity_bit as u8, |acc, bit| acc + bit);
match mode {
ParityMode::Even => is_even(num_bit_set),
ParityMode::Odd => is_odd(num_bit_set),
}
}
fn main() {
let config = ConfigBuilder::all_disabled().enable_default_bool().build();
let (client_key, server_key) = generate_keys(config);
set_server_key(server_key);
let clear_bits = [0, 1, 0, 0, 0, 1, 1].map(|b| (b != 0) as bool);
let fhe_bits = clear_bits
.iter()
.map(|bit| FheBool::encrypt(*bit, &client_key))
.collect::<Vec<FheBool>>();
let mode = ParityMode::Odd;
let fhe_parity_bit = compute_parity_bit(&fhe_bits, mode);
let decrypted_parity_bit = fhe_parity_bit.decrypt(&client_key);
let is_parity_bit_valid = check_parity_bit_validity(&clear_bits, mode, decrypted_parity_bit);
println!("Parity bit is set: {} for mode: {:?}", decrypted_parity_bit, mode);
assert!(is_parity_bit_valid);
let mode = ParityMode::Even;
let fhe_parity_bit = compute_parity_bit(&fhe_bits, mode);
let decrypted_parity_bit = fhe_parity_bit.decrypt(&client_key);
let is_parity_bit_valid = check_parity_bit_validity(&clear_bits, mode, decrypted_parity_bit);
println!("Parity bit is set: {} for mode: {:?}", decrypted_parity_bit, mode);
assert!(is_parity_bit_valid);
}fn check_parity_bit_validity(
fhe_bits: &[FheBool],
mode: ParityMode,
) -> boolfn compute_parity_bit<BoolType>(
fhe_bits: &[BoolType],
mode: ParityMode,
) -> BoolTypewhere
BoolType: Clone + Not<Output = BoolType>,
BoolType: BitXor<BoolType, Output=BoolType>,---- src/user_doc_tests.rs - user_doc_tests (line 199) stdout ----
error[E0369]: no implementation for `&BoolType ^ BoolType`
--> src/user_doc_tests.rs:218:30
|
21 | parity_bit = fhe_bit ^ parity_bit
| ------- ^ ---------- BoolType
| |
| &BoolType
|
help: consider extending the `where` bound, but there might be an alternative better way to express this requirement
|
17 | BoolType: BitXor<BoolType, Output=BoolType>, &BoolType: BitXor<BoolType>
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous errorwhere
BoolType: Clone + Not<Output = BoolType>,
&BoolType: BitXor<BoolType, Output=BoolType>,---- src/user_doc_tests.rs - user_doc_tests (line 236) stdout ----
error[E0637]: `&` without an explicit lifetime name cannot be used here
--> src/user_doc_tests.rs:251:5
|
17 | &BoolType: BitXor<BoolType, Output=BoolType>,
| ^ explicit lifetime name needed here
error[E0310]: the parameter type `BoolType` may not live long enough
--> src/user_doc_tests.rs:251:16
|
17 | &BoolType: BitXor<BoolType, Output=BoolType>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the reference type `&'static BoolType` does not outlive the data it points at
|
help: consider adding an explicit lifetime bound...
|
15 | BoolType: Clone + Not<Output = BoolType> + 'static,
|where
BoolType: Clone + Not<Output = BoolType>,
for<'a> &'a BoolType: BitXor<BoolType, Output = BoolType>,use std::ops::{Not, BitXor};
#[derive(Copy, Clone, Debug)]
enum ParityMode {
// The sum bits of message + parity bit must an odd number
Odd,
// The sum bits of message + parity bit must an even number
Even,
}
fn compute_parity_bit<BoolType>(fhe_bits: &[BoolType], mode: ParityMode) -> BoolType
where
BoolType: Clone + Not<Output = BoolType>,
for<'a> &'a BoolType: BitXor<BoolType, Output = BoolType>,
{
let mut parity_bit = fhe_bits[0].clone();
for fhe_bit in &fhe_bits[1..] {
parity_bit = fhe_bit ^ parity_bit
}
match mode {
ParityMode::Odd => !parity_bit,
ParityMode::Even => parity_bit,
}
}use tfhe::{FheBool, ConfigBuilder, generate_keys, set_server_key};
use tfhe::prelude::*;
use std::ops::{Not, BitXor};
#[derive(Copy, Clone, Debug)]
enum ParityMode {
// The sum bits of message + parity bit must an odd number
Odd,
// The sum bits of message + parity bit must an even number
Even,
}
fn compute_parity_bit<BoolType>(fhe_bits: &[BoolType], mode: ParityMode) -> BoolType
where
BoolType: Clone + Not<Output=BoolType>,
for<'a> &'a BoolType: BitXor<BoolType, Output=BoolType>,
{
let mut parity_bit = fhe_bits[0].clone();
for fhe_bit in &fhe_bits[1..] {
parity_bit = fhe_bit ^ parity_bit
}
match mode {
ParityMode::Odd => !parity_bit,
ParityMode::Even => parity_bit,
}
}
fn is_even(n: u8) -> bool {
(n & 1) == 0
}
fn is_odd(n: u8) -> bool {
!is_even(n)
}
fn check_parity_bit_validity(bits: &[bool], mode: ParityMode, parity_bit: bool) -> bool {
let num_bit_set = bits
.iter()
.map(|bit| *bit as u8)
.fold(parity_bit as u8, |acc, bit| acc + bit);
match mode {
ParityMode::Even => is_even(num_bit_set),
ParityMode::Odd => is_odd(num_bit_set),
}
}
fn main() {
let config = ConfigBuilder::all_disabled().enable_default_bool().build();
let ( client_key, server_key) = generate_keys(config);
set_server_key(server_key);
let clear_bits = [0, 1, 0, 0, 0, 1, 1].map(|b| (b != 0) as bool);
let fhe_bits = clear_bits
.iter()
.map(|bit| FheBool::encrypt(*bit, &client_key))
.collect::<Vec<FheBool>>();
let mode = ParityMode::Odd;
let clear_parity_bit = compute_parity_bit(&clear_bits, mode);
let fhe_parity_bit = compute_parity_bit(&fhe_bits, mode);
let decrypted_parity_bit = fhe_parity_bit.decrypt(&client_key);
let is_parity_bit_valid = check_parity_bit_validity(&clear_bits, mode, decrypted_parity_bit);
println!("Parity bit is set: {} for mode: {:?}", decrypted_parity_bit, mode);
assert!(is_parity_bit_valid);
assert_eq!(decrypted_parity_bit, clear_parity_bit);
let mode = ParityMode::Even;
let clear_parity_bit = compute_parity_bit(&clear_bits, mode);
let fhe_parity_bit = compute_parity_bit(&fhe_bits, mode);
let decrypted_parity_bit = fhe_parity_bit.decrypt(&client_key);
let is_parity_bit_valid = check_parity_bit_validity(&clear_bits, mode, decrypted_parity_bit);
println!("Parity bit is set: {} for mode: {:?}", decrypted_parity_bit, mode);
assert!(is_parity_bit_valid);
assert_eq!(decrypted_parity_bit, clear_parity_bit);
}
Ch(x, y, z) = (x AND y) XOR ((NOT x) AND z)
Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z)
Σ0(x) = ROTR-2(x) XOR ROTR-13(x) XOR ROTR-22(x)
Σ1(x) = ROTR-6(x) XOR ROTR-11(x) XOR ROTR-25(x)
σ0(x) = ROTR-7(x) XOR ROTR-18(x) XOR SHR-3(x)
σ1(x) = ROTR-17(x) XOR ROTR-19(x) XOR SHR-10(x)Maj(x, y, z) = (x AND (y XOR z)) XOR (y AND z)fn sha256(padded_input: Vec<bool>) -> [bool; 256] {
// Initialize hash values with constant values
let mut hash: [[bool; 32]; 8] = [
hex_to_bools(0x6a09e667), hex_to_bools(0xbb67ae85),
hex_to_bools(0x3c6ef372), hex_to_bools(0xa54ff53a),
hex_to_bools(0x510e527f), hex_to_bools(0x9b05688c),
hex_to_bools(0x1f83d9ab), hex_to_bools(0x5be0cd19),
];
let chunks = padded_input.chunks(512);
for chunk in chunks {
let mut w = [[false; 32]; 64];
// Copy first 16 words from current chunk
for i in 0..16 {
w[i].copy_from_slice(&chunk[i * 32..(i + 1) * 32]);
}
// Compute the other 48 words
for i in 16..64 {
w[i] = add(add(add(sigma1(&w[i - 2]), w[i - 7]), sigma0(&w[i - 15])), w[i - 16]);
}
let mut a = hash[0];
let mut b = hash[1];
let mut c = hash[2];
let mut d = hash[3];
let mut e = hash[4];
let mut f = hash[5];
let mut g = hash[6];
let mut h = hash[7];
// Compression loop, each iteration uses a specific constant from K
for i in 0..64 {
let temp1 = add(add(add(add(h, ch(&e, &f, &g)), w[i]), hex_to_bools(K[i])), sigma_upper_case_1(&e));
let temp2 = add(sigma_upper_case_0(&a), maj(&a, &b, &c));
h = g;
g = f;
f = e;
e = add(d, temp1);
d = c;
c = b;
b = a;
a = add(temp1, temp2);
}
hash[0] = add(hash[0], a);
hash[1] = add(hash[1], b);
hash[2] = add(hash[2], c);
hash[3] = add(hash[3], d);
hash[4] = add(hash[4], e);
hash[5] = add(hash[5], f);
hash[6] = add(hash[6], g);
hash[7] = add(hash[7], h);
}
// Concatenate the final hash values to produce a 256-bit hash
let mut output = [false; 256];
for i in 0..8 {
output[i * 32..(i + 1) * 32].copy_from_slice(&hash[i]);
}
output
}fn rotate_right(x: &[Ciphertext; 32], n: usize) -> [Ciphertext; 32] {
let mut result = x.clone();
result.rotate_right(n);
result
}
fn shift_right(x: &[Ciphertext; 32], n: usize, sk: &ServerKey) -> [Ciphertext; 32] {
let mut result = x.clone();
result.rotate_right(n);
result[..n].fill_with(|| sk.trivial_encrypt(false));
result
}fn xor(a: &[Ciphertext; 32], b: &[Ciphertext; 32], sk: &ServerKey) -> [Ciphertext; 32] {
let mut result = a.clone();
result.par_iter_mut()
.zip(a.par_iter().zip(b.par_iter()))
.for_each(|(dst, (lhs, rhs))| *dst = sk.xor(lhs, rhs));
result
}pub fn add(a: &[Ciphertext; 32], b: &[Ciphertext; 32], sk: &ServerKey) -> [Ciphertext; 32] {
let propagate = xor(a, b, sk); // Parallelized bitwise XOR
let generate = and(a, b, sk); // Parallelized bitwise AND
let carry = compute_carry(&propagate, &generate, sk);
let sum = xor(&propagate, &carry, sk); // Parallelized bitwise XOR
sum
}
fn compute_carry(propagate: &[Ciphertext; 32], generate: &[Ciphertext; 32], sk: &ServerKey) -> [Ciphertext; 32] {
let mut carry = trivial_bools(&[false; 32], sk);
carry[31] = sk.trivial_encrypt(false);
for i in (0..31).rev() {
carry[i] = sk.or(&generate[i + 1], &sk.and(&propagate[i + 1], &carry[i + 1]));
}
carry
}Carry = Maj(A, B, C)
Sum = A XOR B XOR Clet (temp1, temp2) = rayon::join(
|| {
let ((sum, carry), s1) = rayon::join(
|| {
let ((sum, carry), ch) = rayon::join(
|| csa(&h, &w[i], &trivial_bools(&hex_to_bools(K[i]), sk), sk),
|| ch(&e, &f, &g, sk),
);
csa(&sum, &carry, &ch, sk)
},
|| sigma_upper_case_1(&e, sk)
);
let (sum, carry) = csa(&sum, &carry, &s1, sk);
add(&sum, &carry, sk)
},
|| {
add(&sigma_upper_case_0(&a, sk), &maj(&a, &b, &c, sk), sk)
},
);fn main() {
let matches = Command::new("Homomorphic sha256")
.arg(Arg::new("ladner_fischer")
.long("ladner-fischer")
.help("Use the Ladner Fischer parallel prefix algorithm for additions")
.action(ArgAction::SetTrue))
.get_matches();
// If set using the command line flag "--ladner-fischer" this algorithm will be used in additions
let ladner_fischer: bool = matches.get_flag("ladner_fischer");
// INTRODUCE INPUT FROM STDIN
let mut input = String::new();
println!("Write input to hash:");
io::stdin()
.read_line(&mut input)
.expect("Failed to read line");
input = input.trim_end_matches('\n').to_string();
println!("You entered: \"{}\"", input);
// CLIENT PADS DATA AND ENCRYPTS IT
let (ck, sk) = gen_keys();
let padded_input = pad_sha256_input(&input);
let encrypted_input = encrypt_bools(&padded_input, &ck);
// SERVER COMPUTES OVER THE ENCRYPTED PADDED DATA
println!("Computing the hash");
let encrypted_output = sha256_fhe(encrypted_input, ladner_fischer, &sk);
// CLIENT DECRYPTS THE OUTPUT
let output = decrypt_bools(&encrypted_output, &ck);
let outhex = bools_to_hex(output);
println!("{}", outhex);
}./target/release/examples/sha256_bool < input.txtserver_key is of type tfhe::integer::ServerKeylet total_sell_volume: u16 = sell_orders.iter().sum();
let total_buy_volume: u16 = buy_orders.iter().sum();(total_sell_volume > total_buy_volume) * (total_buy_volume − total_sell_volume) + total_sell_volumelet total_volume = std::cmp::min(total_buy_volume, total_sell_volume);let mut volume_left_to_transact = total_volume;
for sell_order in sell_orders.iter_mut() {
let filled_amount = std::cmp::min(volume_left_to_transact, *sell_order);
*sell_order = filled_amount;
volume_left_to_transact -= filled_amount;
}let mut volume_left_to_transact = total_volume;
for buy_order in buy_orders.iter_mut() {
let filled_amount = std::cmp::min(volume_left_to_transact, *buy_order);
*buy_order = filled_amount;
volume_left_to_transact -= filled_amount;
}fn volume_match_plain(sell_orders: &mut Vec<u16>, buy_orders: &mut Vec<u16>) {
let total_sell_volume: u16 = sell_orders.iter().sum();
let total_buy_volume: u16 = buy_orders.iter().sum();
let total_volume = std::cmp::min(total_buy_volume, total_sell_volume);
let mut volume_left_to_transact = total_volume;
for sell_order in sell_orders.iter_mut() {
let filled_amount = std::cmp::min(volume_left_to_transact, *sell_order);
*sell_order = filled_amount;
volume_left_to_transact -= filled_amount;
}
let mut volume_left_to_transact = total_volume;
for buy_order in buy_orders.iter_mut() {
let filled_amount = std::cmp::min(volume_left_to_transact, *buy_order);
*buy_order = filled_amount;
volume_left_to_transact -= filled_amount;
}
}let mut total_sell_volume = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
for sell_order in sell_orders.iter_mut() {
server_key.smart_add_assign(&mut total_sell_volume, sell_order);
}
let mut total_buy_volume = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
for buy_order in buy_orders.iter_mut() {
server_key.smart_add_assign(&mut total_buy_volume, buy_order);
}let total_volume = server_key.smart_min(&mut total_sell_volume, &mut total_buy_volume);let fill_orders = |orders: &mut [RadixCiphertext]| {
let mut volume_left_to_transact = total_volume.clone();
for mut order in orders.iter_mut() {
let mut filled_amount = server_key.smart_min(&mut volume_left_to_transact, &mut order);
server_key.smart_sub_assign(&mut volume_left_to_transact, &mut filled_amount);
*order = filled_amount;
}
};
fill_orders(sell_orders);
fill_orders(buy_orders);const NUMBER_OF_BLOCKS: usize = 8;
fn volume_match_fhe(
sell_orders: &mut [RadixCiphertext],
buy_orders: &mut [RadixCiphertext],
server_key: &ServerKey,
) {
let mut total_sell_volume = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
for sell_order in sell_orders.iter_mut() {
server_key.smart_add_assign(&mut total_sell_volume, sell_order);
}
let mut total_buy_volume = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
for buy_order in buy_orders.iter_mut() {
server_key.smart_add_assign(&mut total_buy_volume, buy_order);
}
let total_volume = server_key.smart_min(&mut total_sell_volume, &mut total_buy_volume);
let fill_orders = |orders: &mut [RadixCiphertext]| {
let mut volume_left_to_transact = total_volume.clone();
for mut order in orders.iter_mut() {
let mut filled_amount = server_key.smart_min(&mut volume_left_to_transact, &mut order);
server_key.smart_sub_assign(&mut volume_left_to_transact, &mut filled_amount);
*order = filled_amount;
}
};
fill_orders(sell_orders);
fill_orders(buy_orders);
}
let parallel_vector_sum = |vec: &mut [RadixCiphertext]| {
vec.to_vec().into_par_iter().reduce(
|| server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS),
|mut acc: RadixCiphertext, mut ele: RadixCiphertext| {
server_key.smart_add_parallelized(&mut acc, &mut ele)
},
)
};let (mut total_sell_volume, mut total_buy_volume) =
rayon::join(|| vector_sum(sell_orders), || vector_sum(buy_orders));rayon::join(|| fill_orders(sell_orders), || fill_orders(buy_orders));fn volume_match_fhe_parallelized(
sell_orders: &mut [RadixCiphertext],
buy_orders: &mut [RadixCiphertext],
server_key: &ServerKey,
) {
let parallel_vector_sum = |vec: &mut [RadixCiphertext]| {
vec.to_vec().into_par_iter().reduce(
|| server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS),
|mut acc: RadixCiphertext, mut ele: RadixCiphertext| {
server_key.smart_add_parallelized(&mut acc, &mut ele)
},
)
};
let (mut total_sell_volume, mut total_buy_volume) = rayon::join(
|| parallel_vector_sum(sell_orders),
|| parallel_vector_sum(buy_orders),
);
let total_volume =
server_key.smart_min_parallelized(&mut total_sell_volume, &mut total_buy_volume);
let fill_orders = |orders: &mut [RadixCiphertext]| {
let mut volume_left_to_transact = total_volume.clone();
for mut order in orders.iter_mut() {
let mut filled_amount =
server_key.smart_min_parallelized(&mut volume_left_to_transact, &mut order);
server_key
.smart_sub_assign_parallelized(&mut volume_left_to_transact, &mut filled_amount);
*order = filled_amount;
}
};
rayon::join(|| fill_orders(sell_orders), || fill_orders(buy_orders));
}let fill_orders = |orders: &mut [u64], prefix_sum: &[u64], total_orders: u64|{
orders.iter().for_each(|order : &mut u64| {
if (total_orders >= prefix_sum[i]) {
continue;
} else if total_orders >= prefix_sum.get(i-1).unwrap_or(0) {
*order = total_orders - prefix_sum.get(i-1).unwrap_or(0);
} else {
*order = 0;
}
});
};
let fill_orders = |orders: &mut [u64], prefix_sum: &[u64], total_orders: u64| {
orders.iter().for_each(|order| : &mut){
*order = *order + ((total_orders >= prefix_sum - std::cmp::min(total_orders, prefix_sum.get(i - 1).unwrap_or(&0).clone()) - *order);
}
};fn volume_match_fhe_modified(
sell_orders: &mut [RadixCiphertext],
buy_orders: &mut [RadixCiphertext],
server_key: &ServerKey,
) {
let compute_prefix_sum = |arr: &[RadixCiphertext]| {
if arr.is_empty() {
return arr.to_vec();
}
let mut prefix_sum: Vec<RadixCiphertext> = (0..arr.len().next_power_of_two())
.into_par_iter()
.map(|i| {
if i < arr.len() {
arr[i].clone()
} else {
server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS)
}
})
.collect();
// Up sweep
for d in 0..(prefix_sum.len().ilog2() as u32) {
prefix_sum
.par_chunks_exact_mut(2_usize.pow(d + 1))
.for_each(move |chunk| {
let length = chunk.len();
let mut left = chunk.get((length - 1) / 2).unwrap().clone();
server_key.smart_add_assign_parallelized(chunk.last_mut().unwrap(), &mut left)
});
}
// Down sweep
let last = prefix_sum.last().unwrap().clone();
*prefix_sum.last_mut().unwrap() = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
for d in (0..(prefix_sum.len().ilog2() as u32)).rev() {
prefix_sum
.par_chunks_exact_mut(2_usize.pow(d + 1))
.for_each(move |chunk| {
let length = chunk.len();
let t = chunk.last().unwrap().clone();
let mut left = chunk.get((length - 1) / 2).unwrap().clone();
server_key.smart_add_assign_parallelized(chunk.last_mut().unwrap(), &mut left);
chunk[(length - 1) / 2] = t;
});
}
prefix_sum.push(last);
prefix_sum[1..=arr.len()].to_vec()
};
println!("Creating prefix sum arrays...");
let time = Instant::now();
let (prefix_sum_sell_orders, prefix_sum_buy_orders) = rayon::join(
|| compute_prefix_sum(sell_orders),
|| compute_prefix_sum(buy_orders),
);
println!("Created prefix sum arrays in {:?}", time.elapsed());
let fill_orders = |total_orders: &RadixCiphertext,
orders: &mut [RadixCiphertext],
prefix_sum_arr: &[RadixCiphertext]| {
orders
.into_par_iter()
.enumerate()
.for_each(move |(i, order)| {
server_key.smart_add_assign_parallelized(
order,
&mut server_key.smart_mul_parallelized(
&mut server_key
.smart_ge_parallelized(&mut order.clone(), &mut total_orders.clone()),
&mut server_key.smart_sub_parallelized(
&mut server_key.smart_sub_parallelized(
&mut total_orders.clone(),
&mut server_key.smart_min_parallelized(
&mut total_orders.clone(),
&mut prefix_sum_arr
.get(i - 1)
.unwrap_or(
&server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS),
)
.clone(),
),
),
&mut order.clone(),
),
),
);
});
};
let total_buy_orders = &mut prefix_sum_buy_orders
.last()
.unwrap_or(&server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS))
.clone();
let total_sell_orders = &mut prefix_sum_sell_orders
.last()
.unwrap_or(&server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS))
.clone();
println!("Matching orders...");
let time = Instant::now();
rayon::join(
|| fill_orders(total_sell_orders, buy_orders, &prefix_sum_buy_orders),
|| fill_orders(total_buy_orders, sell_orders, &prefix_sum_sell_orders),
);
println!("Matched orders in {:?}", time.elapsed());
}# Runs FHE implementation
cargo run --release --package tfhe --example dark_market --features="integer internal-keycache" -- fhe
# Runs parallelized FHE implementation
cargo run --release --package tfhe --example dark_market --features="integer internal-keycache" -- fhe-parallel
# Runs modified FHE implementation
cargo run --release --package tfhe --example dark_market --features="integer internal-keycache" -- fhe-modified
# Runs plain implementation
cargo run --release --package tfhe --example dark_market --features="integer internal-keycache" -- plain
# Multiple implementations can be run within same instance
cargo run --release --package tfhe --example dark_market --features="integer internal-keycache" -- plain fhe-parallelStart := 'a'Start := 'a' | 'b'Start := Digit+
Digit := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'Start := '/' '^'? Regex '$'? '/' Modifier?
Regex := Term '|' Term
| Term
Term := Factor*
Factor := Atom '?'
| Repeated
| Atom
Repeated := Atom '*'
| Atom '+'
| Atom '{' Digit* ','? '}'
| Atom '{' Digit+ ',' Digit* '}'
Atom := '.'
| '\' .
| Character
| '[' Range ']'
| '(' Regex ')'
Range := '^' Range
| AlphaNum '-' AlphaNum
| AlphaNum+
Digit := '0' .. '9'
Character := AlphaNum
| '&' | ';' | ':' | ',' | '`' | '~' | '-' | '_' | '!' | '@' | '#' | '%' | '\'' | '\"'
AlphaNum := 'a' .. 'z'
| 'A' .. 'Z'
| '0' .. '9'
Modifier := 'i'enum RegExpr {
Char { c: char }, // matching against a single character (Atom.2 and Atom.3)
AnyChar, // matching _any_ character (Atom.1)
SOF, // matching only at the beginning of the content ('^' in Start.1)
EOF, // matching only at the end of the content (the '$' in Start.1)
Range { cs: Vec<char> }, // matching on a list of characters (Range.3, eg '[acd]')
Between { from: char, to: char }, // matching between 2 characters based on ascii ordering (Range.2, eg '[a-g]')
}enum RegExpr {
...
Seq { re_xs: Vec<RegExpr> }, // matching sequences of RegExpr components (Term.1)
}enum RegExpr {
...
Optional { opt_re: Box<RegExpr> }, // matching optionally (Factor.1)
Not { not_re: Box<RegExpr> }, // matching inversely on a range (Range.1)
Either { l_re: Box<RegExpr>, r_re: Box<RegExpr> }, // matching the left or right regex (Regex.1)
}// note: this is pseudocode
c = <the encrypted character under inspection>;
sk = <the server key, aka the public key>
ge_from = sk.ge(c, 'a');
le_to = sk.le(c, 'z');
result = sk.bitand(ge_from, le_to);
type StringCiphertext = Vec<RadixCiphertext>;
type ResultCiphertext = RadixCiphertext;
fn match(
sk: &ServerKey,
content: &StringCipherText,
re: &RegExpr,
content_pos: usize,
) -> Vec<(ResultCiphertext, usize)> {
let content_char = &content[c_pos];
match re {
...
}
}case RegExpr::Char { c } => {
vec![(sk.eq(content_char, c), c_pos + 1)]
},sk.bitor(sk.eq(content[0], a), sk.bitor(sk.eq(content[1], a), sk.eq(content[2], a)))case RegExpr::AnyChar => {
// note: ct_true is just some constant representing True that is trivially encoded into ciphertext
return vec![(ct_true, c_pos + 1)];
}case RegExpr::Seq { re_xs } => {
re_xs.iter().fold(|prev_results, re_x| {
prev_results.iter().flat_map(|(prev_res, prev_c_pos)| {
(x_res, new_c_pos) = match(sk, content, re_x, prev_c_pos);
(sk.bitand(prev_res, x_res), new_c_pos)
})
}, (ct_true, c_pos))
},use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint3};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_uint3().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 7;
let clear_b = 3;
let clear_c = 2;
let mut a = FheUint3::try_encrypt(clear_a, &keys)?;
let mut b = FheUint3::try_encrypt(clear_b, &keys)?;
let mut c = FheUint3::try_encrypt(clear_c, &keys)?;
a = a * &b; // Clear equivalent computations: 7 * 3 mod 8 = 5
b = &b + &c; // Clear equivalent computations: 3 + 2 mod 8 = 5
b = b - 5; // Clear equivalent computations: 5 - 5 mod 8 = 0
let dec_a = a.decrypt(&keys);
let dec_b = b.decrypt(&keys);
// We homomorphically swapped values using bitwise operations
assert_eq!(dec_a, (clear_a * clear_b) % 8);
assert_eq!(dec_b, ((clear_b + clear_c) - 5) % 8);
Ok(())
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint3};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_uint3().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 7;
let clear_b = 3;
let mut a = FheUint3::try_encrypt(clear_a, &keys)?;
let mut b = FheUint3::try_encrypt(clear_b, &keys)?;
a = a ^ &b;
b = b ^ &a;
a = a ^ &b;
let dec_a = a.decrypt(&keys);
let dec_b = b.decrypt(&keys);
// We homomorphically swapped values using bitwise operations
assert_eq!(dec_a, clear_b);
assert_eq!(dec_b, clear_a);
Ok(())
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint3};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_uint3().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 7;
let clear_b = 3;
let mut a = FheUint3::try_encrypt(clear_a, &keys)?;
let mut b = FheUint3::try_encrypt(clear_b, &keys)?;
assert_eq!(a.gt(&b).decrypt(&keys) != 0, true);
assert_eq!(b.le(&a).decrypt(&keys) != 0, true);
Ok(())
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint4};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_uint4().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let pow_5 = |value: u64| {
value.pow(5) % FheUint4::MODULUS as u64
};
let clear_a = 12;
let a = FheUint4::try_encrypt(12, &keys)?;
let c = a.map(pow_5);
let decrypted = c.decrypt(&keys);
assert_eq!(decrypted, pow_5(clear_a) as u8);
Ok(())
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint2};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_uint2().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 1;
let clear_b = 3;
let a = FheUint2::try_encrypt(clear_a, &keys)?;
let b = FheUint2::try_encrypt(clear_b, &keys)?;
let c = a.bivariate_function(&b, std::cmp::max);
let decrypted = c.decrypt(&keys);
assert_eq!(decrypted, std::cmp::max(clear_a, clear_b) as u8);
Ok(())
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 15_u64;
let clear_b = 27_u64;
let clear_c = 43_u64;
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
let mut c = FheUint8::try_encrypt(clear_c, &keys)?;
a = a * &b; // Clear equivalent computations: 15 * 27 mod 256 = 149
b = &b + &c; // Clear equivalent computations: 27 + 43 mod 256 = 70
b = b - 76u8; // Clear equivalent computations: 70 - 76 mod 256 = 250
let dec_a: u8 = a.decrypt(&keys);
let dec_b: u8 = b.decrypt(&keys);
assert_eq!(dec_a, ((clear_a * clear_b) % 256_u64) as u8);
assert_eq!(dec_b, (((clear_b + clear_c).wrapping_sub(76_u64)) % 256_u64) as u8);
Ok(())
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a = 164;
let clear_b = 212;
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
a = a ^ &b;
b = b ^ &a;
a = a ^ &b;
let dec_a: u8 = a.decrypt(&keys);
let dec_b: u8 = b.decrypt(&keys);
// We homomorphically swapped values using bitwise operations
assert_eq!(dec_a, clear_b);
assert_eq!(dec_b, clear_a);
Ok(())
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a:u8 = 164;
let clear_b:u8 = 212;
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
let greater = a.gt(&b);
let greater_or_equal = a.ge(&b);
let lower = a.lt(&b);
let lower_or_equal = a.le(&b);
let equal = a.eq(&b);
let dec_gt : u8 = greater.decrypt(&keys);
let dec_ge : u8 = greater_or_equal.decrypt(&keys);
let dec_lt : u8 = lower.decrypt(&keys);
let dec_le : u8 = lower_or_equal.decrypt(&keys);
let dec_eq : u8 = equal.decrypt(&keys);
// We homomorphically swapped values using bitwise operations
assert_eq!(dec_gt, (clear_a > clear_b ) as u8);
assert_eq!(dec_ge, (clear_a >= clear_b) as u8);
assert_eq!(dec_lt, (clear_a < clear_b ) as u8);
assert_eq!(dec_le, (clear_a <= clear_b) as u8);
assert_eq!(dec_eq, (clear_a == clear_b) as u8);
Ok(())
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
let (keys, server_keys) = generate_keys(config);
set_server_key(server_keys);
let clear_a:u8 = 164;
let clear_b:u8 = 212;
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
let min = a.min(&b);
let max = a.max(&b);
let dec_min : u8 = min.decrypt(&keys);
let dec_max : u8 = max.decrypt(&keys);
// We homomorphically swapped values using bitwise operations
assert_eq!(dec_min, u8::min(clear_a, clear_b));
assert_eq!(dec_max, u8::max(clear_a, clear_b));
Ok(())
}use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8, FheUint32, FheUint16};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, server_key) = generate_keys(config);
// Casting requires server_key to set
// (encryptions/decryptions do not need server_key to be set)
set_server_key(server_key);
{
let clear = 12_837u16;
let a = FheUint16::encrypt(clear, &client_key);
// Downcasting
let a: FheUint8 = a.cast_into();
let da: u8 = a.decrypt(&client_key);
assert_eq!(da, clear as u8);
// Upcasting
let a: FheUint32 = a.cast_into();
let da: u32 = a.decrypt(&client_key);
assert_eq!(da, (clear as u8) as u32);
}
{
let clear = 12_837u16;
let a = FheUint16::encrypt(clear, &client_key);
// Upcasting
let a = FheUint32::cast_from(a);
let da: u32 = a.decrypt(&client_key);
assert_eq!(da, clear as u32);
// Downcasting
let a = FheUint8::cast_from(a);
let da: u8 = a.decrypt(&client_key);
assert_eq!(da, (clear as u32) as u8);
}
Ok(())
}