Getting Wrong Output When using comparison operation

Contains of compare.rs
use tfhe::prelude::*;
use csv::ReaderBuilder;
use tfhe::{FheInt8, ClientKey};
use base64::{engine::general_purpose::STANDARD, Engine as _};

const ENCRYPTED_CSV: &str = "encrypted.csv";
const TARGET_COLUMN: usize = 1;

pub fn compare_encrypted_values(keys: &ClientKey, reference_value: i8) {

    let mut reader = ReaderBuilder::new().has_headers(true).from_path(ENCRYPTED_CSV)
        .expect("Failed to read CSV");

    let reference_cipher = FheInt8::encrypt(reference_value, keys);

    println!("Performing encrypted comparisons with reference value: {}", reference_value);

    for result in reader.records() {
        let record = result.expect("Failed to read record");

        if let Some(encrypted_str) = record.get(TARGET_COLUMN) {
            if let Ok(decoded_bytes) = STANDARD.decode(encrypted_str) {
                let encrypted_value: FheInt8 = bincode::deserialize(&decoded_bytes).expect("Failed to deserialize");

                let gt = encrypted_value.gt(&reference_cipher);
                let eq = encrypted_value.eq(&reference_cipher);
                let lt = encrypted_value.lt(&reference_cipher);

                let dec_gt: bool = gt.decrypt(keys);
                let dec_eq: bool = eq.decrypt(keys);
                let dec_lt: bool = lt.decrypt(keys);

                let decrypted_val: i8 = encrypted_value.decrypt(keys);
                let ref_val: i8 = reference_cipher.decrypt(keys);
                println!("Decrypted: {} vs Reference: {}", decrypted_val, ref_val);


                println!(
                    "Encrypted Value: Comparisons -> (> {}: {}, == {}: {}, < {}: {})", 
                    reference_value, dec_gt, reference_value, dec_eq, reference_value, dec_lt
                );
            }
        }
    }
}

Contains of main.rs  

mod key_gen;
mod encrypting_csv;
mod decrypt;
mod max_finder;
mod compare; 

use tfhe::set_server_key;
//use tfhe::prelude::*;  

fn main() {
    // Step 1: Initialize Keys
    let (keys, server_key) = key_gen::initialize_keys();

    // Step 2: Set server key ONCE globally
    set_server_key(server_key.clone());

    // Step 3: Encrypt CSV File
    encrypting_csv::encrypt_csv(&keys);

    // Step 4: Decrypt CSV File (optional for debugging)
    decrypt::decrypt_csv(&keys);

    // Step 5: Compare Each Encrypted Value with a Reference
    let reference_value: i8 = 5; // Change this as needed
    compare::compare_encrypted_values(&keys, reference_value);

}

and output is following
Keys loaded from file.
Encryption complete. Encrypted CSV saved to encrypted.csv
Decryption complete. Decrypted CSV saved to decrypted.csv
Performing encrypted comparisons with reference value: 5
Decrypted: 6 vs Reference: 5
Encrypted Value: Comparisons -> (> 5: true, == 5: false, < 5: true)
Decrypted: 8 vs Reference: 5
Encrypted Value: Comparisons -> (> 5: true, == 5: false, < 5: false)
Decrypted: 10 vs Reference: 5
Encrypted Value: Comparisons -> (> 5: true, == 5: false, < 5: true)
^C *  Terminal will be reused by tasks, press any key to close it.

**I get  wrong output I don't understand how it happen please give some solution.**


Can you share a github repo with the full code ?

So the problem is in your initialize_keys

pub fn initialize_keys() -> (ClientKey, ServerKey) {
    if let Ok(key_data) = load_from_file(KEY_FILE) {
        let keys: ClientKey = bincode::deserialize(&key_data).expect("Failed to deserialize keys");
        let config = ConfigBuilder::default().build();
        let (_, server_keys) = generate_keys(config);
        set_server_key(server_keys.clone());
        println!("Keys loaded from file.");
        (keys, server_keys)
    } else {
        let config = ConfigBuilder::default().build();
        let (keys, server_keys) = generate_keys(config);
        save_to_file(KEY_FILE, &bincode::serialize(&keys).unwrap()).expect("Failed to save keys");
        set_server_key(server_keys.clone());
        println!("New keys generated and saved.");
        (keys, server_keys)
    }
}

You are saving to the keyfile.bin only the client key, which could be fine but, when loading that file, you generate a totally new pair of client and server keys and use the newly created server key to do computations, however that server key can only work with the newly created client key.

So you should save both

pub fn initialize_keys() -> (ClientKey, ServerKey) {
    if let Ok(key_data) = load_from_file(KEY_FILE) {
        let (keys, server_keys): (ClientKey, ServerKey) = bincode::deserialize(&key_data).expect("Failed to deserialize keys");
        println!("Keys loaded from file.");
        (keys, server_keys)
    } else {
        let config = ConfigBuilder::default().build();
        let (keys, server_keys) = generate_keys(config);
        save_to_file(KEY_FILE, &bincode::serialize(&(&keys, &server_keys)).unwrap()).expect("Failed to save keys");
        println!("New keys generated and saved.");
        (keys, server_keys)
    }
}
And if you want a smaller file yo could use a CompressedServerKey
pub fn initialize_keys() -> (ClientKey, ServerKey) {
    if let Ok(key_data) = load_from_file(KEY_FILE) {
        let (keys, compressed_server_keys): (ClientKey, CompressedServerKey) = bincode::deserialize(&key_data).expect("Failed to deserialize keys");
        println!("Keys loaded from file.");
        (keys, compressed_server_keys.decompress())
    } else {
        let config = ConfigBuilder::default().build();
        let keys = ClientKey::generate(config);
        let compressed_server_keys = CompressedServerKey::new(&keys);
        save_to_file(KEY_FILE, &bincode::serialize(&(&keys, &compressed_server_keys)).unwrap()).expect("Failed to save keys");
        let server_keys = compressed_server_keys.decompress();
        println!("New keys generated and saved.");
        (keys, server_keys)
    }
}

Or regenerating a server_key for the client key you just loaded from the file using ServerKey::new(&keys) should also work

1 Like