Using Zama concrete as public key encryption

Hi … As to my understanding of the FHE, we should be able to use it similar to public key cryptography. For example, if I generate FHE keypair: PKa and SKa, anyone can encrypt some message with PKa, and then only me I can decrypt the encrypted value by using SKa. I cannot find any example to do that using Python Concrete. Would you please let me know how we can achieve that maybe by sharing some keys of the circuit? Examples would be much appreciated.

Many thanks

PS. I see some example in the rust implementation Use Public Key Encryption - TFHE-rs. The issue is that rust implementation is very slow compared to python (at least from my experiments).

Hello @zakwanj

You are right, you could use FHE with Public Key encryption.
It’s already available in tfhe-rs but not yet in Concrete. It’s a work in progress for next version.

Thanks @alex for prompt reply. As far as I know, the python version is going to be deprecated. It seems this is not the case. Any time frame for the next version so we can plan accordingly? Many thanks

the python version (in fact it’s not really a python version it’s a python frontend to interact with our FHE compiler) will not be deprecated.

Normally version with Public Key encryption support is planned for next quarter so around beginning of January.

To go back to your statement “rust implementation is very slow compared to python”, could you share your experiments to understand why it’s slow with the Rust library (tfhe-rs I assume?)

1 Like

Sure. For example this code:

// Basic configuration to use homomorphic integers
    let config = ConfigBuilder::all_disabled()
        .enable_default_integers()
        .build();

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

    let clear_a = 1344u16;

    let start = Instant::now();

    // Encrypting the input data using the (private) client_key
    // FheUint32: Encrypted equivalent to u32
    let encrypted_a = FheUint32::try_encrypt(clear_a, &client_key)?;
    let encrypted_b = FheUint32::try_encrypt(clear_a, &client_key)?;
    let c = &encrypted_a + &encrypted_b;

    let duration = start.elapsed();
    println!("Time elapsed in expensive_function() is: {:?}", duration);

It took around 1.5 s just to add two encrypted numbers. In the Python version, I created the circuit and I was calculating the sum of 4 encrypted values, and it took around 300ms. That is why I am confused now because you mentioned that Python is just a frontend to the same rust library. Am I missing something here?

are you sure you are using the --release flag? it is a game changer :slight_smile:
it should be:
cargo run --release

Yes :slight_smile: … I noticed that. Anything else I can do for the performance? It is a critical issue in my use case.

1 Like

in the python version it was also with 32 bits values?

Yes. btw, is it normal to take that long (around 1.5s) to do one addition operation? (my machine: Intel(R) Core™ i5-6500T CPU @ 2.50GHz, 32G RAM).

According to Intel your CPU is from 2015 and as FHE is very hardware dependant you will get slower timings than for recent hardware.

Also this CPU has 4 threads, the current versions of tfhe-rs uses a highly parallelized algorithm to do the addition but since you have a low number of threads it is actually slows the operation down instead.

In the next release we will be branching to another algorithm for cpu with low number of threads so you will get better performances (but still lower than if your CPU had enough threads, with >= 16 threads on recent cpu it should take 127ms)

Thank you @tmontaigu for the explanation. I need to add two 256bits numbers. Because there is no way to do that, I divided each number to 16 parts (16-bits each). I encrypt the parts, then do the addition on the encrypted value. So if, one addition takes 127ms, it means it took around 2s to add two numbers. It is still too expensive. I am not sure if you have any idea on how to significantly improve the performance.

tfhe-rs already supports 256 bits integer, so you don’t have to re-implement it yourself.

On 128-cores machine it takes 350ms to add 2 256 bits numbers, on machine that do not have enough threads (my laptop for example) it takes ~2s.

So unless you use better/bigger hardware you won’t be able to gain much performances