Compressing ciphertexts/keys

This document explains the mechanism and steps to compress ciphertext and keys.

TFHE-rs includes features to compress both keys and ciphertexts, reducing their storage and transmission sizes.

Most TFHE-rs entities contain random numbers generated by a Pseudo Random Number Generator (PRNG). Since a PRNG is deterministic, storing only the random seed used to generate those numbers preserves all necessary information. When decompressing the entity, using the same PRNG and the same seed will reconstruct the full chain of random values.

In TFHE-rs, compressible entities are prefixed with Compressed. For instance, a compressed FheUint256 is declared as CompressedFheUint256.

In the following example code, we use the bincode crate dependency to serialize in a binary format and compare serialized sizes.

Compressed ciphertexts

This example shows how to compress a ciphertext encrypting messages over 16 bits:

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

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

    let clear = 12_837u16;
    let compressed = CompressedFheUint16::try_encrypt(clear, &client_key).unwrap();
    println!(
        "compressed size  : {}",
        bincode::serialize(&compressed).unwrap().len()
    );
    
    let decompressed = compressed.decompress();
    
    println!(
        "decompressed size: {}",
        bincode::serialize(&decompressed).unwrap().len()
    );

    let clear_decompressed: u16 = decompressed.decrypt(&client_key);
    assert_eq!(clear_decompressed, clear);
}

Compressed server keys

This example shows how to compress the server keys:

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

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

    let cks = ClientKey::generate(config);
    let compressed_sks = CompressedServerKey::new(&cks);

    println!(
        "compressed size  : {}",
        bincode::serialize(&compressed_sks).unwrap().len()
    );

    let sks = compressed_sks.decompress();

    println!(
        "decompressed size: {}",
        bincode::serialize(&sks).unwrap().len()
    );

    set_server_key(sks);

    let clear_a = 12u8;
    let a = FheUint8::try_encrypt(clear_a, &cks).unwrap();

    let c = a + 234u8;
    let decrypted: u8 = c.decrypt(&cks);
    assert_eq!(decrypted, clear_a.wrapping_add(234));
}

Compressed public keys

This example shows how to compress the classical public keys:

It is not currently recommended to use the CompressedPublicKey to encrypt ciphertexts without first decompressing them. If the resulting PublicKey is too large to fit in memory, it may result in significant slowdowns.

This issue has been identified and will be addressed in future releases.

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

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

    let compressed_public_key = CompressedPublicKey::new(&client_key);

    println!("compressed size  : {}", bincode::serialize(&compressed_public_key).unwrap().len());

    let public_key = compressed_public_key.decompress();

    println!("decompressed size: {}", bincode::serialize(&public_key).unwrap().len());


    let a = FheUint8::try_encrypt(213u8, &public_key).unwrap();
    let clear: u8 = a.decrypt(&client_key);
    assert_eq!(clear, 213u8);
}

Compressed compact public key

This example shows how to use compressed compact public keys:

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

fn main() {
    let config = ConfigBuilder::default()
        .use_custom_parameters(
            tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS,
            None,
        )
        .build();
    let (client_key, _) = generate_keys(config);

    let public_key_compressed = CompressedCompactPublicKey::new(&client_key);

    println!(
        "compressed size  : {}",
        bincode::serialize(&public_key_compressed).unwrap().len()
    );

    let public_key = public_key_compressed.decompress();

    println!(
        "decompressed size: {}",
        bincode::serialize(&public_key).unwrap().len()
    );

    let a = FheUint8::try_encrypt(255u8, &public_key).unwrap();
    let clear: u8 = a.decrypt(&client_key);
    assert_eq!(clear, 255u8);
}

Last updated