FHERMA
RESEARCH01 AUG 2024 · 7 MIN

OpenFHE-rs tutorial

Rust tutorial

AMAlexandra MirzuitovaContributor

OpenFHE-rs tutorial

Introduction

OpenFHE-rs, a joint project by FairMath & OpenFHE, is a Rust interface for the OpenFHE library that offers a comprehensive suite of Fully Homomorphic Encryption (FHE) schemes implemented in C++. Previously, application development using OpenFHE could be carried out in C++ and Python via the Python Wrapper, and now Rust is joining this list, expanding the possibilities of developing FHE applications, especially in the Web3 domain, where Rust is regarded as one of the most popular programming languages. Whether you're developing secure data processing applications or privacy-focused tools, OpenFHE-rs enables you to leverage the powerful encryption technologies of OpenFHE seamlessly within your Rust projects.

This tutorial provides a step-by-step guide to installing and using the openfhe-rs crate for leveraging homomorphic encryption in Rust. If you have more questions, take a look at our documentation; if you encounter any issues or have any suggestions, check out the Support section in the end of this article.

About the library

See also the Limitations page from our documentation.

Cryptography schemes

OpenFHE Rust wrapper currently support three PKE schemes:

  • CKKS
  • BFV
  • BGV

Many of the wrapper's methods contain the suffix referring to the scheme used.

Params

The Params type stores flags and parameters for the FHE algorithms to use. There are different ways to generate a Params type instance, but the main way is to create a mutable object by calling ffi::GenParams%scheme_name% and setting its options after that; for the CKKS scheme, it will be ffi::GenParamsCKKSRNS.

CryptoContext

CryptoContext type, as its name stands, stores the metadata of your FHE context. You usually create its instance using one of the following functions:

  • By generating the CryptoContext directly - ffi::DCRTPolyGenCryptoContextByParamsCKKSRNS
  • By deserializing a CryptoContext generated somewhere else - ffi::DCRTPolyDeserializeCryptoContextFromFile

Installation and project setup

To use OpenFHE-rs, you'll need to install several dependencies and follow the installation steps for both the core OpenFHE library and the Rust crate.

Prerequisites

Ensure you have the following dependencies installed:

  • CMake >= 3.5.1
  • G++ >= 11.4
  • Rust >= 1.78
  • Git
  • Cargo, if you don't have it installed with Rust

Core OpenFHE library installation

To build and install the OpenFHE library:

  1. Clone the repository
git clone https://github.com/openfheorg/openfhe-development.git
cd openfhe-development
  1. Configure CMake
cmake -B ./build -DBUILD_SHARED=ON .
  1. Build and install the C++ OpenFHE library
make -C ./build -j$(nproc)
make -C ./build install

Depending on the chosen installation path, you might need root privileges to run make install.

  1. Update the cache for the linker
sudo ldconfig
  1. To check your OpenFHE installation, run the tests from the build directory:
cd build/
make testall

It might take some time, though, 10 to 30 minutes.

If you encounter any problems, refer to the OpenFHE's installation documentation which contains OS-specific instructions or check their Discourse group to see whether anyone had similar issues.

Configuring your project to use the crate

Template repository

We have a template repository which you can clone/fork instead of manually building and setting up a project: template repository.

Rust crate installation via Cargo

To use the OpenFHE crate in your project, add it to the existing or new Rust project as a dependency from crates.io:

cargo add openfhe

This will automatically update your Cargo.toml with the new dependency. You will also need to add a small piece of code for the core dependencies' configuration in your build.rs file. Here's what it should look like:

fn main()
{
    println!("cargo::rerun-if-changed=src/main.rs");

    // linking openFHE
    println!("cargo::rustc-link-arg=-L/usr/local/lib");
    println!("cargo::rustc-link-arg=-lOPENFHEpke");
    println!("cargo::rustc-link-arg=-lOPENFHEbinfhe");
    println!("cargo::rustc-link-arg=-lOPENFHEcore");

    // linking OpenMP
    println!("cargo::rustc-link-arg=-fopenmp");
    
    // necessary to avoid LD_LIBRARY_PATH
    println!("cargo::rustc-link-arg=-Wl,-rpath,/usr/local/lib");
}

After that, check if everything is alright by running cargo build.

Building the project manually

To build the crate from sources, follow the instructions in the project's README.

Launching the built-in example

We have a complete working example of FHE computations, Polynomial evaluation via CKKS, for our Rust wrapper.

You can find it: * in the OpenFHE-rs-template repository - thesrc/main.rs file. * in the OpenFHE-rs project built from sources, navigate to the crate_usage directory, also the src/main.rs file. * or just copy the code from the crate_usage to your Rust project.

Once you have the example,

  1. Build the application
cargo build
  1. Run it with
cargo run

If everything works correctly, you will see the result of evaluating a polynomial with 2 sets of coefficients, like the following output:

======EXAMPLE FOR EVALPOLY========

Generating evaluation key for homomorphic multiplication...Completed.

 Original Plaintext #1:
(0.5, 0.7, 0.9, 0.95, 0.93,  ... ); Estimated precision: 50 bits
 Result of evaluating a polynomial with coefficients [ 0.15 0.75 0 1.25 0 0 1 0 1 2 0 1 0 0 0 0 1 ]
(0.705191, 1.38285, 3.97211, 5.60216, 4.86358,  ... ); Estimated precision: 36 bits
 Expected result: (0.70519107, 1.38285078, 3.97211180, 5.60215665, 4.86357575)
 Evaluation time: 561ms

 Result of evaluating a polynomial with coefficients [ 1 2 3 4 5 -1 -2 -3 -4 -5 0.1 0.2 0.3 0.4 0.5 -0.1 -0.2 -0.3 -0.4 -0.5 0.1 0.2 0.3 0.4 0.5 -0.1 -0.2 -0.3 -0.4 -0.5 ]
(3.45151, 5.37528, 4.89931, 3.2495, 4.04852,  ... ); Estimated precision: 34 bits
 Expected result: (3.4515092326, 5.3752765397, 4.8993108833, 3.2495023573, 4.0485229982) 
 Evaluation time: 623ms

More examples can be found at OpenFHE-rs examples folder.

Using OpenFHE-rs

Here's an example of homomorphic addition and multiplication of integers via BFV scheme:

use openfhe::cxx::CxxVector;
use openfhe::ffi as ffi;

fn main() {
    // Step 1: Generate BFV-RNS Scheme Parameters
    let mut _cc_params_bfvrns = ffi::GenParamsBFVRNS();
    _cc_params_bfvrns.pin_mut().SetPlaintextModulus(65537); // Set plaintext modulus
    _cc_params_bfvrns.pin_mut().SetMultiplicativeDepth(2);  // Set multiplicative depth

    // Step 2: Generate Crypto Context
    let _cc = ffi::DCRTPolyGenCryptoContextByParamsBFVRNS(&_cc_params_bfvrns);
    _cc.EnableByFeature(ffi::PKESchemeFeature::PKE);        // Enable public key encryption
    _cc.EnableByFeature(ffi::PKESchemeFeature::KEYSWITCH);  // Enable key switching
    _cc.EnableByFeature(ffi::PKESchemeFeature::LEVELEDSHE); // Enable leveled SHE

    // Step 3: Key generation and evaluation key generation
    let _key_pair = _cc.KeyGen(); // Generate key pair
    _cc.EvalMultKeyGen(&_key_pair.GetPrivateKey()); // Generate multiplication key with the private key

    // Step 4: Define three plaintext vectors: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [3, 2, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12], and [1, 2, 5, 2, 5, 6, 7, 8, 9, 10, 11, 12]
    let mut _vector_of_ints_1 = CxxVector::<i64>::new();
    let vector_1_values = (1..=12).collect::<Vec<i64>>();
    for &value in &vector_1_values {
        _vector_of_ints_1.pin_mut().push(value);
    }
    let _plain_text_1 = _cc.MakePackedPlaintext(&_vector_of_ints_1, 1, 0);

    let mut _vector_of_ints_2 = CxxVector::<i64>::new();
    let vector_2_values = [3, 2, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12];
    for &value in &vector_2_values {
        _vector_of_ints_2.pin_mut().push(value);
    }
    let _plain_text_2 = _cc.MakePackedPlaintext(&_vector_of_ints_2, 1, 0);

    let mut _vector_of_ints_3 = CxxVector::<i64>::new();
    let vector_3_values = [1, 2, 5, 2, 5, 6, 7, 8, 9, 10, 11, 12];
    for &value in &vector_3_values {
        _vector_of_ints_3.pin_mut().push(value);
    }
    let _plain_text_3 = _cc.MakePackedPlaintext(&_vector_of_ints_3, 1, 0);

    // Step 5: Encrypt the plaintexts using the public key
    let _cipher_text_1 = _cc.EncryptByPublicKey(&_key_pair.GetPublicKey(), &_plain_text_1);
    let _cipher_text_2 = _cc.EncryptByPublicKey(&_key_pair.GetPublicKey(), &_plain_text_2);
    let _cipher_text_3 = _cc.EncryptByPublicKey(&_key_pair.GetPublicKey(), &_plain_text_3);

    // Step 6: Perform homomorphic addition with encrypted vectors
    let _cipher_text_add_1_2 = _cc.EvalAddByCiphertexts(&_cipher_text_1, &_cipher_text_2);
    let _cipher_text_add_result = _cc.EvalAddByCiphertexts(&_cipher_text_add_1_2, &_cipher_text_3);

    // Step 7: Perform homomorphic multiplication with encrypted vectors
    let _cipher_text_mul_1_2 = _cc.EvalMultByCiphertexts(&_cipher_text_1, &_cipher_text_2);
    let _cipher_text_mult_result = _cc.EvalMultByCiphertexts(&_cipher_text_mul_1_2, &_cipher_text_3);

    // Step 8: Decrypt results
    let mut _plain_text_add_result = ffi::GenNullPlainText();
    _cc.DecryptByPrivateKeyAndCiphertext(&_key_pair.GetPrivateKey(), &_cipher_text_add_result, _plain_text_add_result.pin_mut());
    
    let mut _plain_text_mult_result = ffi::GenNullPlainText();
    _cc.DecryptByPrivateKeyAndCiphertext(&_key_pair.GetPrivateKey(), &_cipher_text_mult_result, _plain_text_mult_result.pin_mut());
    
    // Output the results
    println!("Plaintext #1: {}", _plain_text_1.GetString());
    println!("Plaintext #2: {}", _plain_text_2.GetString());
    println!("Plaintext #3: {}", _plain_text_3.GetString());

    println!("\nResults of homomorphic computations");
    println!("#1 + #2 + #3: {}", _plain_text_add_result.GetString());
    println!("#1 * #2 * #3: {}", _plain_text_mult_result.GetString());
}

Running this as example

  1. Ensure the openfhe-rs library is installed and properly configured.
  2. Save the example code provided above to a file, e.g., as main.rs, in the /src folder.
  3. Compile and run your Rust code with:
cargo run

This should output the results of the computations, homomorphic addition and multiplication, to your console.

Contributing

Contributions are always welcome! If you encounter any problems, have feature requests, or want to contribute code, please open an issue or pull request on the GitHub repository.

Support

If you have any questions, you can:

Useful links

OpenFHE-rs tutorial · FHERMA