TFHE-rs
WebsiteLibrariesProduct & ServicesDevelopersSupport
0.6
0.6
  • Welcome to TFHE-rs
  • Get Started
    • What is TFHE-rs?
    • Installation
    • Quick start
    • Types & Operations
    • Benchmarks
    • Security and cryptography
  • Fundamentals
    • Configuration and key generation
    • Server key
    • Encryption
    • Computation on encrypted data
    • Decryption
    • Encrypted pseudo random values
    • Serialization/deserialization
    • Compressing ciphertexts/keys
    • Debugging
  • Guides
    • Rust configuration
    • GPU acceleration
    • Overflow detection
    • Data versioning
    • Public key encryption
    • Zero-knowledge proofs
    • Generic trait bounds
    • Parallelized PBS
    • High-level API in C
    • JS on WASM API
    • Multi-threading with Rayon crate
    • Trivial ciphertexts
    • PBS statistics
  • Tutorials
    • All tutorials
    • Homomorphic parity bit
    • Homomorphic case changing on Ascii string
    • SHA256 with Boolean API
  • References
    • API references
    • Fine-grained APIs
      • Quick start
      • Boolean
        • Operations
        • Cryptographic parameters
        • Serialization/Deserialization
      • Shortint
        • Operations
        • Cryptographic parameters
        • Serialization/Deserialization
      • Integer
        • Operations
        • Cryptographic parameters
        • Serialization/Deserialization
    • Core crypto API
      • Quick start
      • Tutorial
  • Explanations
    • TFHE deep dive
  • Developers
    • Contributing
    • Release note
    • Feature request
    • Bug report
Powered by GitBook

Libraries

  • TFHE-rs
  • Concrete
  • Concrete ML
  • fhEVM

Developers

  • Blog
  • Documentation
  • Github
  • FHE resources

Company

  • About
  • Introduction to FHE
  • Media
  • Careers
On this page

Was this helpful?

Export as PDF
  1. Tutorials

Homomorphic case changing on Ascii string

PreviousHomomorphic parity bitNextSHA256 with Boolean API

Last updated 11 months ago

Was this helpful?

This tutorial demonstrates how to build a data type that represents an ASCII string in Fully Homomorphic Encryption (FHE) by implementing to_lower and to_upper functions.

An ASCII character is stored in 7 bits. To store an encrypted ASCII, we use the FheUint8:

  • The uppercase letters are in the range [65, 90]

  • The lowercase letters are in the range [97, 122]

The relationship between uppercase and lowercase letters is defined as follows:

  • lower_case = upper_case + UP_LOW_DISTANCE

  • upper_case = lower_case - UP_LOW_DISTANCE

Where UP_LOW_DISTANCE = 32

Types and methods

This type stores the encrypted characters as a Vec<FheUint8> to implement case conversion functions.

To use the FheUint8 type, enable the integer feature:

# Cargo.toml

[dependencies]
# Default configuration for x86 Unix machines:
tfhe = { version = "0.6.4", features = ["integer", "x86_64-unix"]}

Refer to the for other configurations.

The FheAsciiString::encrypt function performs data validation to ensure the input string contains only ASCII characters.

In FHE operations, direct branching on encrypted values is not possible. However, you can evaluate a boolean condition to obtain the desired outcome. Here is an example to check and convert the 'char' to a lowercase without using a branch:

pub const UP_LOW_DISTANCE: u8 = 32;

fn to_lower(c: u8) -> u8 {
    if c > 64 && c < 91 {
        c + UP_LOW_DISTANCE
    } else {
        c
    }
}

You can remove the branch this way:

pub const UP_LOW_DISTANCE: u8 = 32;

fn to_lower(c: u8) -> u8 {
    c + ((c > 64) as u8 & (c < 91) as u8) * UP_LOW_DISTANCE
}

This method can adapt to operations on homomorphic integers:

use tfhe::prelude::*;
use tfhe::FheUint8;

pub const UP_LOW_DISTANCE: u8 = 32;

fn to_lower(c: &FheUint8) -> FheUint8 {
    c + FheUint8::cast_from(c.gt(64) & c.lt(91)) * UP_LOW_DISTANCE
}

Full example:

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

pub const UP_LOW_DISTANCE: u8 = 32;

struct FheAsciiString {
    bytes: Vec<FheUint8>,
}

fn to_upper(c: &FheUint8) -> FheUint8 {
    c - FheUint8::cast_from(c.gt(96) & c.lt(123)) * UP_LOW_DISTANCE
}

fn to_lower(c: &FheUint8) -> FheUint8 {
    c + FheUint8::cast_from(c.gt(64) & c.lt(91)) * UP_LOW_DISTANCE
}

impl FheAsciiString {
    fn encrypt(string: &str, client_key: &ClientKey) -> Self {
        assert!(
            string.chars().all(|char| char.is_ascii()),
            "The input string must only contain ascii letters"
        );

        let fhe_bytes: Vec<FheUint8> = string
            .bytes()
            .map(|b| FheUint8::encrypt(b, client_key))
            .collect();

        Self { bytes: fhe_bytes }
    }

    fn decrypt(&self, client_key: &ClientKey) -> String {
        let ascii_bytes: Vec<u8> = self
            .bytes
            .iter()
            .map(|fhe_b| fhe_b.decrypt(client_key))
            .collect();
        String::from_utf8(ascii_bytes).unwrap()
    }

    fn to_upper(&self) -> Self {
        Self {
            bytes: self.bytes.iter().map(to_upper).collect(),
        }
    }

    fn to_lower(&self) -> Self {
        Self {
            bytes: self.bytes.iter().map(to_lower).collect(),
        }
    }
}

fn main() {
    let config = ConfigBuilder::default()
        .build();

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

    set_server_key(server_key);

    let my_string = FheAsciiString::encrypt("Hello Zama, how is it going?", &client_key);
    let verif_string = my_string.decrypt(&client_key);
    println!("Start string: {verif_string}");

    let my_string_upper = my_string.to_upper();
    let verif_string = my_string_upper.decrypt(&client_key);
    println!("Upper string: {verif_string}");
    assert_eq!(verif_string, "HELLO ZAMA, HOW IS IT GOING?");

    let my_string_lower = my_string_upper.to_lower();
    let verif_string = my_string_lower.decrypt(&client_key);
    println!("Lower string: {verif_string}");
    assert_eq!(verif_string, "hello zama, how is it going?");
}
installation guide