Quick Start

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::default().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.5.3", features = ["integer", "x86_64-unix"]}

Configuration options for different platforms can be found here. Other rust and homomorphic types features can be found here.

Imports.

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::*;

1. Configuring and creating keys.

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::default().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.

2. Setting the server key.

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::default().build();

    let (client_key, server_key) = generate_keys(config);

    set_server_key(server_key);
}

3. Encrypting data.

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);

4. Computation and decryption.

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 updated