Quick Start
The basic steps for using the high-level API of TFHE-rs are:
- 1.Importing the TFHE-rs prelude;
- 2.Client-side: Configuring and creating keys;
- 3.Client-side: Encrypting data;
- 4.Server-side: Setting the server key;
- 5.Server-side: Computing over encrypted data;
- 6.Client-side: Decrypting data.
Here is a full example (combining the client and server parts):
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);
}
The default configuration for x86 Unix machines:
tfhe = { version = "0.3.1", features = ["integer", "x86_64-unix"]}
tfhe
uses traits
to have a consistent API for creating FHE types and enable users to write generic functions. To be able to use associated functions and methods of a trait, the trait has to be in scope.To make it easier, the
prelude
'pattern' is used. All of the important tfhe
traits are in a prelude
module that you can glob import. With this, there is no need to remember or know the traits that you want to import.use tfhe::prelude::*;
The first step is the creation of the configuration. The configuration is used to declare which type you will (or will not) use, as well as enabling you to use custom crypto-parameters for these types. Custom parameters should only be used for more advanced usage and/or testing.
A configuration can be created by using the ConfigBuilder type.
In this example, 8-bit unsigned integers with default parameters are used. The
integers
feature must also be enabled, as per the table on this page.The config is generated by first creating a builder with all types deactivated. Then, the integer types with default parameters are activated, since we are going to use FheUint8 values.
use tfhe::{ConfigBuilder, generate_keys};
fn main() {
let config = ConfigBuilder::all_disabled()
.enable_default_integers()
.build();
let (client_key, server_key) = generate_keys(config);
}
The
generate_keys
command returns a client key and a server key.The
client_key
is meant to stay private and not leave the client, whereas the server_key
can be made public and sent to a server for it to enable FHE computations.The next step is to call
set_server_key
This function will move the server key to an internal state of the crate and manage the details to give a simpler interface.
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);
}
Encrypting data is achieved via the
encrypt
associated function of the FheEncrypt trait.Types exposed by this crate implement at least one of FheEncrypt or FheTryEncrypt to allow encryption.
let clear_a = 27u8;
let clear_b = 128u8;
let a = FheUint8::encrypt(clear_a, &client_key);
let b = FheUint8::encrypt(clear_b, &client_key);
Computations should be as easy as normal Rust to write, thanks to the usage of operator overloading.
let result = a + b;
The decryption is achieved by using the
decrypt
method, which comes from the FheDecrypt trait.let decrypted_result: u8 = result.decrypt(&client_key);
let clear_result = clear_a + clear_b;
assert_eq!(decrypted_result, clear_result);
Last modified 1mo ago