TFHE-rs v0.3.0: Faster Operations, Wider API, Shorter Keys

July 25, 2023
Jean-Baptiste Orfila

TFHE-rs 0.3 introduces quicker homomorphic operations, incorporates parallel processing for programmable bootstrapping, and implements a novel method that reduces the size of public keys.

New and Faster Homomorphic Operations

This new version of TFHE-rs supports more homomorphic operations. The list of newly available operations over encrypted data is:

  • Bitwise not: [.c-inline-code]![.c-inline-code]
  • Not equal: [.c-inline-code]ne[.c-inline-code]
  • Left/Right rotation: [.c-inline-code]rotate_left[.c-inline-code], [.c-inline-code]rotate_right[.c-inline-code]
  • Shift Left/Right: [.c-inline-code]<<[.c-inline-code], [.c-inline-code]>>[.c-inline-code]
  • Integer division: [.c-inline-code]/[.c-inline-code]
  • Remainder: [.c-inline-code]%[.c-inline-code]
  • Casting: [.c-inline-code]cast_into[.c-inline-code], [.c-inline-code]cast_from[.c-inline-code]

New operations between a ciphertext and a clear value:

  • Division: [.c-inline-code]/[.c-inline-code]
  • Remainder: [.c-inline-code]%[.c-inline-code]
  • Bitwise operations:  [.c-inline-code]&[.c-inline-code], [.c-inline-code]|[.c-inline-code], [.c-inline-code]^[.c-inline-code]
  • Comparisons: [.c-inline-code]eq[.c-inline-code], [.c-inline-code]ne[.c-inline-code], [.c-inline-code]lt[.c-inline-code], [.c-inline-code]gt[.c-inline-code], [.c-inline-code]le[.c-inline-code], [.c-inline-code]ge[.c-inline-code]
  • Rotations: [.c-inline-code]rotate_left[.c-inline-code], [.c-inline-code]rotate_right[.c-inline-code]

Many existing operations have been updated, to reach a performance gain between 10 and 90% depending on the operation. In the following table, benchmarks for operations between two ciphertexts are given. Main improvements come from using the carry lookahead approach instead of the classical one. The former gives better performance for operations slowed down by the carry propagation (like the multiplication, which requires many additions and thus carry propagations). Note that operations between an [.c-inline-code]FheUint[.c-inline-code] and a clear value have also been reworked to be more efficient depending on the input scalar size. 

Table 1. Timings for homomorphic operations between two ciphertexts depending on input sizes. 

Below, a short code example illustrating the use of a right shift, a casting and a division:

use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint32, FheUint8};

fn main() -> Result<(), Box<dyn std::error::Error>> {
	// Configuration with integers
	let config = ConfigBuilder::all_disabled()
    	.enable_default_integers()
    	.build();

	// Key generation
	let (keys, server_keys) = generate_keys(config);
	set_server_key(server_keys);

	let clear_a: u32 = 1344;
	let clear_b = 5;
	let clear_c = 7;

	let mut a = FheUint32::try_encrypt(clear_a, &keys)?;
	let b = FheUint32::try_encrypt(clear_b, &keys)?;
	let c = FheUint8::try_encrypt(clear_c, &keys)?;

	// Clear equivalent computations: 1344 >> 5 = 42
	a = a >> &b;

	// Clear equivalent computations: let mut a = a as u8;
	let mut a: FheUint8 = a.cast_into();

	// Clear equivalent computations: 42/7 = 6
	a = &a / &c;

	let dec_a: u8 = a.decrypt(&keys);
	assert_eq!(dec_a, (clear_a >> clear_b) as u8 / clear_c);

	Ok(())
}

Multithreaded Bootstrapping

The programmable bootstrapping (PBS) is an homomorphic operation which reduces the noise of a ciphertext and, and at the same time, computes homomorphically an univariate function over the encrypted message. By construction, this operation is sequential. However, TFHE-rs now proposes a multithreaded version of the PBS, called the multibit PBS. This approach offers a new trade-off between increasing the size of the bootstrapping key and improving the latency of the PBS. Without getting into details, the idea is to extend the bitwise homomorphic decryption of the PBS to a group of bits. The size of the group is a variable parameter that can be adapted depending on the machine constraints (e.g., available memory and number of CPU threads). In the following table, a summary of the new timings and sizes is given.

Table 2. Size (MB) and timing (ms) comparisons between sequential and multithreaded PBS

The following code example is similar to the previous one, but uses the multithreaded PBS instead. The only difference lies in the ConfigBuilder, which relies on a different parameter set than the default one.

use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint32, FheUint8};

fn main() -> Result<(), Box<dyn std::error::Error>> {
	// Configuration to use the multithreaded PBS
	let config = ConfigBuilder::all_disabled()  	
           .enable_custom_integers(
                tfhe::shortint::parameters::DEFAULT_MULTI_BIT_GROUP_2,
                None
           )
    	     .build();

	// Key generation
	let (keys, server_keys) = generate_keys(config);
	set_server_key(server_keys);

	let clear_a: u32 = 1344;
	let clear_b = 5;
	let clear_c = 7;

	let mut a = FheUint32::try_encrypt(clear_a, &keys)?;
	let b = FheUint32::try_encrypt(clear_b, &keys)?;
	let c = FheUint8::try_encrypt(clear_c, &keys)?;

	// Clear equivalent computations: 1344 >> 5 = 42
	a = a >> &b;

	// Clear equivalent computations: let mut a = a as u8;
	let mut a: FheUint8 = a.cast_into();

	// Clear equivalent computations: 42/7 = 6
	a = &a / &c;

	let dec_a: u8 = a.decrypt(&keys);
	assert_eq!(dec_a, (clear_a >> clear_b) as u8 / clear_c);

	Ok(())
}

Compressed and Compact Public Keys

Similarly, the public encryption method has been updated. Public key sizes have been drastically reduced. The table below compares the key sizes using the classical encryption method and the new compact method depending on the input precision for one ciphertext block.

In what follows, the same code snippet which has been updated to use compact public keys. To do so, specific parameters are given, the public key has to be generated and the encryption key is changed to use the public one.

use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, CompactPublicKey, ConfigBuilder, FheUint32, FheUint8};

fn main() -> Result<(), Box<dyn std::error::Error>> {
	// Configuration to use compact public keys
	let config = ConfigBuilder::all_disabled()
    	.enable_custom_integers(
        	tfhe::shortint::parameters::DEFAULT_COMPACT_PK,
        	None,
    	)
    	.build();

	// Key generation
	let (keys, server_keys) = generate_keys(config);
	let public_key = CompactPublicKey::new(&keys);

	let clear_a = 1344u32;
	let clear_b = 5u32;
	let clear_c = 7u8;

	let mut a = FheUint32::try_encrypt(clear_a, &public_key)?;
	let b = FheUint32::try_encrypt(clear_b, &public_key)?;
	let c = FheUint8::try_encrypt(clear_c, &public_key)?;

	set_server_key(server_keys);

	// Clear equivalent computations: 1344 >> 5 = 42
	a = a >> &b;

	// Clear equivalent computations: let mut a = a as u8;
	let mut a: FheUint8 = a.cast_into();

	// Clear equivalent computations: 42/7 = 6
	a = &a / &c;

	let dec_a: u8 = a.decrypt(&keys);
	assert_eq!(dec_a, (clear_a >> clear_b) as u8 / clear_c);

	Ok(())
}

of a ciphertext, and at the same time,

Additional features

Some other features have been updated:

  • C API: all the functions available in the default API of TFHE-rs can be called from a C program;
  • WASM API: functions related to the client can be called from a web browser or a JS program; parallelism for key generation is available in Web browsers;
  • Transciphering: an example of transciphering between TFHE and the Trivium (or Kreyvium) cryptosystems has been added. This offers the possibility to encrypt ciphertexts using a symmetric cryptosystem, and then to convert them into TFHE-compatible ciphertexts. An application of transciphering is homomorphic random bit generation. More details will be provided in a future blog post.

What’s next?

The focus in the next release will be on continuing the performance enhancement of homomorphic operations. Furthermore, TFHE-rs will include a new homomorphic type: the FheInt for homomorphic signed integers. Finally, more control will be given to users, for example allowing them to change the ciphertext modulus.

Additional links

Read more related posts