TFHE-rs v0.11: Strings, Faster Zero Knowledge Proof, Encrypted Arrays on GPU

January 14, 2025
Jean-Baptiste Orfila, Arthur Meyre, Agnes Leroy

TFHE-rs v0.11 brings several major improvements and new features. This release introduces significantly enhanced performance for Zero Knowledge Proof and a new module for encrypted strings usable in the High Level API via the [.c-inline-code]FheAsciiString[.c-inline-code] type. Besides, default cryptographic parameters now follow a tweaked uniform (TUniform) noise distribution instead of a Gaussian one. GPU performance has also been improved: the 64-bit addition is 30% faster compared to v0.8. Finally, it is now possible to perform computations on arrays of ciphertexts easily on GPU.

Zero Knowledge Proof v2

The new Zero Knowledge proof construction is now faster both for generating and verifying a proof, as detailed in the benchmark table below.

You can follow along the documentation guide to use Zero Knowledge Proofs in the High-Level API.

FHE strings

One of the highlights of this release is the FHE strings module, based on an open source contribution from user JoseSK999 during the Zama Bounty Program Season 5. It is also available in the High-Level API via the [.c-inline-code]FheAsciiString[.c-inline-code] type. FHE strings support the same APIs as Rust’s str type. You can find the guide on FHE strings and how to use them in the documentation.

Here is a preview of what you can do with the new primitive with the integer and strings features enabled:

use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ClearString, ConfigBuilder, FheAsciiString};

fn main() {
    let config = ConfigBuilder::default().build();
    let (cks, sks) = generate_keys(config);

    set_server_key(sks);

    // Encrypt without padding, does not hide the string length and has better performance
    let string = FheAsciiString::try_encrypt("TFHE-rs rocks!", &cks).unwrap();

    // Encrypt with padding, hide the true length of the string
    let search_string = FheAsciiString::try_encrypt_with_padding("is meh", 1, &cks).unwrap();

    // We can also use clear strings
    let clear_search_string = ClearString::new("rocks".to_string());

    // Does our initial string contain "is meh"?
    let does_not_contain = string.contains(&search_string);

    // Does our initial string contain "rocks"?
    let contains = string.contains(&clear_search_string);

    // Decrypt
    let decrypted_does_not_contain = does_not_contain.decrypt(&cks);
    let decrypted_contains = contains.decrypt(&cks);

    // Check all worked properly
    assert!(!decrypted_does_not_contain);
    assert!(decrypted_contains);
}

We expect most string processing to consist mostly of comparison and contains operation, here are the current benchmarks on an hpc7a.96xlarge AWS machine:

Computing over encrypted arrays on GPU

TFHE-rs v0.11 introduces n-dimensional arrays (or tensors) for encrypted data on GPU. The supported operations are the same as on CPU, including the comparison between vectors and the search in a vector of encrypted data.

The following example displays how to use array types on GPU: it shows how to extract submatrices of size 2x2 from two 4x4 matrices, perform addition, and then add a clear matrix to the previous results. You can find more details on how to use homomorphic arrays in the documentation.

use tfhe::{ConfigBuilder, set_server_key, CpuFheUint32Array, ClearArray, ClientKey, CompressedServerKey};
use tfhe::array::GpuFheUint32Array;
use tfhe::prelude::*;

fn main() {
    let config = ConfigBuilder::default().build();
    let cks = ClientKey::generate(config);
    let compressed_server_key = CompressedServerKey::new(&cks);

    let gpu_key = compressed_server_key.decompress_to_gpu();

    set_server_key(gpu_key);

    let num_elems = 4 * 4;
    let clear_xs = (0..num_elems as u32).collect::<Vec<_>>();
    let clear_ys = vec![1u32; num_elems];

    // Encrypted 2D array with values
    // [[  0,  1,  2,  3]
    //  [  4,  5,  6,  7]
    //  [  8,  9, 10, 11]
    //  [ 12, 13, 14, 15]]
    // and shape 4x4
    let xs = GpuFheUint32Array::try_encrypt((clear_xs.as_slice(), vec![4, 4]), &cks).unwrap();
    // Encrypted 2D array with values
    // [[  1,  1,  1,  1]
    //  [  1,  1,  1,  1]
    //  [  1,  1,  1,  1]
    //  [  1,  1,  1,  1]]
    // and shape 4x4
    let ys = GpuFheUint32Array::try_encrypt((clear_ys.as_slice(), vec![4, 4]), &cks).unwrap();

    assert_eq!(xs.num_dim(), 2);
    assert_eq!(xs.shape(), &[4, 4]);
    assert_eq!(ys.num_dim(), 2);
    assert_eq!(ys.shape(), &[4, 4]);

    // Take a sub slice
    //  [[ 10, 11]
    //   [ 14, 15]]
    let xss = xs.slice(&[2..4, 2..4]);
    // Take a sub slice
    //  [[  1,  1]
    //   [  1,  1]]
    let yss = ys.slice(&[2..4, 2..4]);

    assert_eq!(xss.num_dim(), 2);
    assert_eq!(xss.shape(), &[2, 2]);
    assert_eq!(yss.num_dim(), 2);
    assert_eq!(yss.shape(), &[2, 2]);

    let r = &xss + &yss;

    // Result is
    //  [[ 11, 12]
    //   [ 15, 16]]
    let result: Vec = r.decrypt(&cks);
    assert_eq!(result, vec![11, 12, 15, 16]);

    // Clear 2D array with values
    //  [[  10,  20]
    //   [  30,  40]]
    let clear_array = ClearArray::new(vec![10u32, 20u32, 30u32, 40u32], vec![2, 2]);
}

Improved GPU performance

This version brings performance improvements to the GPU backend. The 64-bit addition is 30% faster than v0.8. The Figure 1 below shows the current performance on CPU & GPU for the 64-bit addition and multiplication.

The optimal number of GPUs per operation varies depending on the operation itself and the integer precision specified by the user. Comprehensive arrays of benchmark results for both single and multiple GPUs across all specified precisions are available in the documentation.

Additional features and improvements

TFHE-rs v0.11 introduces several other features or improvements, including:

  • New GPU operations: Support for absolute value and division on homomorphic signed integers.
  • Faster CPU operations: Encrypted shifts/rotations on the CPU are now 28% faster.
  • Parameter updates: All default cryptographic parameters have been updated to use a new noise distribution, called TUniform. The main advantage of this distribution is to be bounded, whereas the usual Gaussian one is not. In some practical cases, this can simplify the use of homomorphic computation. The mathematical definition can be found in this documentation.
  • Code stability: This release is focused on improving the overall code stability. The complete list of novelties is available in the release note.

The next release of TFHE-rs will continue to improve performance, features and stability! Stay tuned for the upcoming update!

Additional links

Read more related posts

No items found.