Rust integration
This document explains how to use Fully Homomorphic Encryption (FHE) modules developed with concrete-python directly in Rust programs using the Concrete toolchain.
This workflow enables rapid prototyping in Python and seamless deployment in Rust, combining the flexibility of Python with the safety and performance of Rust.
Overview
Write and compile FHE modules in Python using
concrete-python
.Import the compiled module artifact into Rust using the
concrete-macro
crate.Use the generated Rust APIs for encryption, evaluation, and decryption.
Prerequisites
Python 3.8+
Rust 1.70+
concrete-python
(>=2.10)concrete
andconcrete-macro
Rust crates (>=2.10.1-rc1)
Regular example
Step 1: Define and Compile a Module in Python
Write your FHE logic in Python and compile it to an artifact compatible with the Rust toolchain. Here is an example of a small and simple module:
from concrete import fhe
@fhe.module()
class MyModule:
@fhe.function({"x": "encrypted"})
def inc(x):
return (x + 1) % 256
@fhe.function({"x": "encrypted"})
def dec(x):
return (x - 1) % 256
inputset = fhe.inputset(fhe.uint8)
module = MyModule.compile({"inc": inputset, "dec": inputset})
module.server.save(path="MyModule.zip", via_mlir=True)
This produces a MyModule.zip
artifact containing the compiled FHE module.
Step 2: Set Up the Rust Project
Initialize a new Rust project and add the required dependencies.
cargo init
cargo add concrete@=2.10.1-rc1 concrete-macro@=2.10.1-rc1
Place the MyModule.zip
artifact in your project directory.
Step 3: Import the Python-Compiled Module in Rust
Use the concrete_macro::from_concrete_python_export_zip!
macro to import the module at build time.
mod my_module {
use concrete_macro::from_concrete_python_export_zip;
from_concrete_python_export_zip!("MyModule.zip");
}
This macro unpacks the artifact, triggers recompilation, reads metadata, and generates Rust APIs for the module's functions.
Step 4: Use the Module in Rust
You can now use the FHE functions in Rust. The following example demonstrates a full FHE workflow:
use concrete::common::Tensor;
fn main() {
// Prepare input and expected output tensors
let input = Tensor::new(vec![5], vec![]);
let expected_output = Tensor::new(vec![6], vec![]);
// Key generation
let mut secret_csprng = concrete::common::SecretCsprng::new(0u128);
let mut encryption_csprng = concrete::common::EncryptionCsprng::new(0u128);
let keyset = my_module::new_keyset(secret_csprng.pin_mut(), encryption_csprng.pin_mut());
let client_keyset = keyset.get_client();
// Create client stub for the 'inc' function
let mut inc_client = my_module::client::inc::ClientFunction::new(&client_keyset, encryption_csprng);
// Encrypt input and obtain evaluation keys
let encrypted_input = inc_client.prepare_inputs(input);
let evaluation_keys = keyset.get_server();
// Create server stub for the 'inc' function
let mut inc_server = my_module::server::inc::ServerFunction::new();
// Evaluate the function on encrypted data
let encrypted_output = inc_server.invoke(&evaluation_keys, encrypted_input);
// Decrypt the output
let decrypted_output = inc_client.process_outputs(encrypted_output);
// Check correctness
assert_eq!(decrypted_output.values(), expected_output.values());
}
TFHE-rs Ciphertext Interoperability
Starting from Concrete v2.11, you can define and use modules that operate directly on TFHE-rs ciphertexts, enabling seamless interoperability between Concrete and TFHE-rs in Rust.
Step 1: Define and Compile a Module with TFHE-rs Types in Python
You can define a module in Python that uses TFHE-rs integer types as arguments and outputs. For example:
from concrete import fhe
from concrete.fhe import tfhers
TFHERS_UINT_8_3_2_4096 = tfhers.TFHERSIntegerType(
False,
bit_width=8,
carry_width=3,
msg_width=2,
params=tfhers.CryptoParams(
lwe_dimension=909,
glwe_dimension=1,
polynomial_size=4096,
pbs_base_log=15,
pbs_level=2,
lwe_noise_distribution=0,
glwe_noise_distribution=2.168404344971009e-19,
encryption_key_choice=tfhers.EncryptionKeyChoice.BIG,
),
)
@fhe.module()
class MyModule:
@fhe.function({"x": "encrypted", "y": "encrypted"})
def my_func(x, y):
x = tfhers.to_native(x)
y = tfhers.to_native(y)
return tfhers.from_native(x + y, TFHERS_UINT_8_3_2_4096)
def t(v):
return tfhers.TFHERSInteger(TFHERS_UINT_8_3_2_4096, v)
inputset = [(t(0), t(0)), (t(2**6), t(2**6))]
my_module = MyModule.compile({"my_func": inputset})
my_module.server.save("test_tfhers.zip", via_mlir=True)
This produces a test_tfhers.zip
artifact compatible with Rust and TFHE-rs.
Step 2: Use the Module with TFHE-rs Ciphertexts in Rust
You can import and use the module in Rust, passing and receiving native TFHE-rs ciphertexts:
mod precompile {
use concrete_macro::from_concrete_python_export_zip;
from_concrete_python_export_zip!("src/test_tfhers.zip");
}
use tfhe::prelude::{FheDecrypt, FheEncrypt};
use tfhe::shortint::parameters::v0_10::classic::gaussian::p_fail_2_minus_64::ks_pbs::V0_10_PARAM_MESSAGE_2_CARRY_3_KS_PBS_GAUSSIAN_2M64;
use tfhe::{generate_keys, FheUint8};
fn main() {
// Key generation for TFHE-rs
let config = tfhe::ConfigBuilder::with_custom_parameters(V0_10_PARAM_MESSAGE_2_CARRY_3_KS_PBS_GAUSSIAN_2M64);
let (client_key, _) = generate_keys(config);
// Build Concrete keyset with TFHE-rs client key
let mut secret_csprng = concrete::common::SecretCsprng::new(0u128);
let mut encryption_csprng = concrete::common::EncryptionCsprng::new(0u128);
let keyset = precompile::KeysetBuilder::new()
.with_key_for_my_func_0_arg(&client_key)
.generate(secret_csprng.pin_mut(), encryption_csprng.pin_mut());
let server_keyset = keyset.get_server();
// Encrypt inputs using TFHE-rs
let arg_0 = FheUint8::encrypt(6u8, &client_key);
let arg_1 = FheUint8::encrypt(4u8, &client_key);
// Evaluate the Concrete circuit on TFHE-rs ciphertexts
let mut server = precompile::server::my_func::ServerFunction::new();
let output = server.invoke(&server_keyset, arg_0, arg_1);
// Decrypt the result using TFHE-rs
let decrypted: u8 = output.decrypt(&client_key);
assert_eq!(decrypted, 10);
}
This workflow allows you to combine the high-level graph optimizations of Concrete with the operator-level flexibility of TFHE-rs, all within Rust.
Notes
The module must be compiled with
via_mlir=True
to be loaded in the Rust program.The Rust API is currently in beta and may evolve in future releases.
The Python and Rust environments must use compatible versions of the Concrete toolchain.
When using TFHE-rs ciphertext interoperability, ensure that the TFHE-rs client key used for encryption matches the one registered in the Concrete keyset for the corresponding argument.
Last updated
Was this helpful?