microsoft / SEAL

Microsoft SEAL is an easy-to-use and powerful homomorphic encryption library.
https://www.microsoft.com/en-us/research/group/cryptography-research/
MIT License
3.53k stars 706 forks source link

About serialization for ckks and bfv #606

Open hui09-m opened 1 year ago

hui09-m commented 1 year ago

When I run the ckks serialization example, I want to compute three numbers, so I modify the coode like this:

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license.

include "examples.h"

using namespace std; using namespace seal;

void example_serialization() { print_example_banner("Example: Serialization");

if (!defined(SEAL_USE_ZSTD) && !defined(SEAL_USE_ZLIB))

cout << "Neither ZLIB nor Zstandard support is enabled; this example is not available." << endl;
cout << endl;
return;

else

stringstream parms_stream; stringstream data_stream; stringstream sk_stream;

{
    EncryptionParameters parms(scheme_type::ckks);
    size_t poly_modulus_degree = 8192;
    parms.set_poly_modulus_degree(poly_modulus_degree);
    parms.set_coeff_modulus(CoeffModulus::Create(poly_modulus_degree, { 50, 30, 50 }));

    auto size = parms.save(parms_stream);

    print_line(__LINE__);
    cout << "EncryptionParameters: wrote " << size << " bytes" << endl;

    print_line(__LINE__);
    cout << "EncryptionParameters: data size upper bound (compr_mode_type::none): "
        << parms.save_size(compr_mode_type::none) << endl;
    cout << "             "
        << "EncryptionParameters: data size upper bound (compression): "
        << parms.save_size(/* Serialization::compr_mode_default */) << endl;

    vector<seal_byte> byte_buffer(static_cast<size_t>(parms.save_size()));
    parms.save(reinterpret_cast<seal_byte*>(byte_buffer.data()), byte_buffer.size());
    EncryptionParameters parms2;
    parms2.load(reinterpret_cast<const seal_byte*>(byte_buffer.data()), byte_buffer.size());

    print_line(__LINE__);
    cout << "EncryptionParameters: parms == parms2: " << boolalpha << (parms == parms2) << endl;
}
{
    EncryptionParameters parms;
    parms.load(parms_stream);

    parms_stream.seekg(0, parms_stream.beg);

    SEALContext context(parms);

    KeyGenerator keygen(context);
    auto sk = keygen.secret_key();
    PublicKey pk;
    keygen.create_public_key(pk);

    sk.save(sk_stream);

    Serializable<RelinKeys> rlk = keygen.create_relin_keys();

    RelinKeys rlk_big;
    keygen.create_relin_keys(rlk_big);
    auto size_rlk = rlk.save(data_stream);
    auto size_rlk_big = rlk_big.save(data_stream);

    print_line(__LINE__);
    cout << "Serializable<RelinKeys>: wrote " << size_rlk << " bytes" << endl;
    cout << "             "
        << "RelinKeys wrote " << size_rlk_big << " bytes" << endl;
    data_stream.seekp(-size_rlk_big, data_stream.cur);
    double scale = pow(2.0, 30);
    CKKSEncoder encoder(context);
    Plaintext plain1, plain2;
    Plaintext plain3;
    encoder.encode(2.3, scale, plain1);
    encoder.encode(4.5, scale, plain2);
    encoder.encode(2.2, scale, plain3);

    Encryptor encryptor(context, pk);

    encryptor.set_secret_key(sk);
    auto size_sym_encrypted1 = encryptor.encrypt_symmetric(plain1).save(data_stream);
    auto size_sym_encrypted2 = encryptor.encrypt_symmetric(plain2).save(data_stream);
    auto size_sym_encrypted3 = encryptor.encrypt_symmetric(plain3).save(data_stream);
    print_line(__LINE__);
    cout << "             "
        << "Serializable<Ciphertext> (seeded secret-key): wrote " << size_sym_encrypted2 << " bytes" << endl;

}
{
    EncryptionParameters parms;
    parms.load(parms_stream);
    parms_stream.seekg(0, parms_stream.beg);
    SEALContext context(parms);

    Evaluator evaluator(context);

    RelinKeys rlk;
    Ciphertext encrypted1, encrypted2;

// Ciphertext encrypted3; rlk.load(context, data_stream); encrypted1.load(context, data_stream); encrypted2.load(context, data_stream); // encrypted3.load(context, data_stream); Ciphertext encrypted_prod, encrypted_prod2;

    evaluator.multiply(encrypted1, encrypted2, encrypted_prod);    
    evaluator.relinearize_inplace(encrypted_prod, rlk);
    evaluator.rescale_to_next_inplace(encrypted_prod);     

    evaluator.multiply_inplace(encrypted_prod, encrypted3);
    evaluator.relinearize_inplace(encrypted_prod, rlk);
    evaluator.rescale_to_next_inplace(encrypted_prod);
    data_stream.seekp(0, parms_stream.beg);
    data_stream.seekg(0, parms_stream.beg);
    auto size_encrypted_prod = encrypted_prod.save(data_stream);

    print_line(__LINE__);
    cout << "Ciphertext (secret-key): wrote " << size_encrypted_prod << " bytes" << endl;
}

 {
    EncryptionParameters parms;
    parms.load(parms_stream);
    parms_stream.seekg(0, parms_stream.beg);
    SEALContext context(parms);
    SecretKey sk;
    sk.load(context, sk_stream);
    Decryptor decryptor(context, sk);
    CKKSEncoder encoder(context);

    Ciphertext encrypted_result;
    encrypted_result.load(context, data_stream);

    Plaintext plain_result;
    decryptor.decrypt(encrypted_result, plain_result);
    vector<double> result;
    encoder.decode(plain_result, result);

    print_line(__LINE__);
    cout << "Decrypt the loaded ciphertext" << endl;
    cout << "    + Expected result:" << endl;
    vector<double> true_result(encoder.slot_count(), 2.3 * 4.5 * 2.2);
    print_vector(true_result, 3, 7);

    cout << "    + Computed result ...... Correct." << endl;
    print_vector(result, 3, 7);
}

Plaintext pt("1x^2 + 3");
stringstream stream;
auto data_size = pt.save(stream);

Serialization::SEALHeader header;
Serialization::LoadHeader(stream, header);
print_line(__LINE__);
cout << "Size written to stream: " << data_size << " bytes" << endl;
cout << "             "
    << "Size indicated in SEALHeader: " << header.size << " bytes" << endl;
cout << endl;

endif

}

image

why can't compute the multipication for three numbers?

To make bfv acchieve serialization, I write code for it accoeding to the ckks serialization example like this:

include "examples.h"

using namespace std; using namespace seal;

void bfv_serialization() { print_example_banner("Example: Serialization for BFV");

if (!defined(SEAL_USE_ZSTD) && !defined(SEAL_USE_ZLIB))

cout << "Neither ZLIB nor Zstandard support is enabled; this example is not available." << endl;
cout << endl;
return;

else

stringstream parms_stream;
stringstream data_stream;
stringstream sk_stream;
//server determines the computation and sets encryption parameters
{
    cout << "Server:set encryption parameters" << endl;
    EncryptionParameters parms(scheme_type::bfv);
    size_t poly_modulus_degree = 8192;
    parms.set_poly_modulus_degree(poly_modulus_degree);
    parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree));

    parms.set_plain_modulus(PlainModulus::Batching(poly_modulus_degree, 20));

    auto size = parms.save(parms_stream);
    print_line(__LINE__);
    cout << "EncryptionParameters: wrote " << size << " bytes" << endl;
}
//Client starts by loading the encryption parameters, sets up the SEALContext,and creates the required keys.
{
    cout << "Client:load encryption parameters、set SEALContext、create keys" << endl;
    EncryptionParameters parms;
    parms.load(parms_stream);
    parms_stream.seekg(0, parms_stream.beg);

    SEALContext context(parms);
    KeyGenerator keygen(context);
    auto secret_key = keygen.secret_key();
    PublicKey public_key;
    keygen.create_public_key(public_key);
    Serializable<RelinKeys> rlk = keygen.create_relin_keys();//create as seeded objects to minimize communication cost
    GaloisKeys gal_keys;
    keygen.create_galois_keys(gal_keys);

    secret_key.save(sk_stream);
    Encryptor encryptor(context, public_key);

    print_line(__LINE__);
    string x;
    cout << "Please set plaintext :" << endl;
    cin >> x;
    Plaintext x_plain(x);
    cout << "Express x = " + x + " as a plaintext polynomial 0x" + x_plain.to_string() + "." << endl;

    Plaintext plain_one("1"), plain_three("3"), plain_four("4");

    auto one = encryptor.encrypt(plain_one);
    one.save(data_stream);
    auto four = encryptor.encrypt(plain_four);
    one.save(data_stream);
    auto three = encryptor.encrypt(plain_three);
    one.save(data_stream);
    auto x_value = encryptor.encrypt(x_plain);
    one.save(data_stream);
    /*
    auto size_encrypted0 = encryptor.encrypt_symmetric(plain_one).save(data_stream);
    auto size_encrypted1 = encryptor.encrypt_symmetric(plain_four).save(data_stream);
    auto size_encrypted3 = encryptor.encrypt_symmetric(plain_three).save(data_stream);
    auto size_encrypted = encryptor.encrypt_symmetric(x_plain).save(data_stream);
    */      
    cout << "Having encrypted three coefficiens" << endl;

}
//The server compute on the encrypted data.
{
    cout << "Server:compute the encrypted data" << endl;
    EncryptionParameters parms;
    parms.load(parms_stream);
    parms_stream.seekg(0, parms_stream.beg);
    SEALContext context(parms);
    Evaluator evaluator(context);
    RelinKeys rlk;
    Ciphertext encrypted3, encrypted1, encrypted0, encrypted;
    cout << "Server:define finished!" << endl;

    rlk.load(context, data_stream);
    encrypted0.load(context, data_stream);
    encrypted1.load(context, data_stream);
    encrypted3.load(context, data_stream);
    encrypted.load(context, data_stream);

    cout << "Server:compute" << endl;
    Ciphertext x_encrypted_sq;
    evaluator.square(encrypted, x_encrypted_sq);//x^2
    evaluator.relinearize_inplace(x_encrypted_sq, rlk);
    evaluator.multiply(encrypted, encrypted3, encrypted3);//3x
    evaluator.multiply_inplace(encrypted3, x_encrypted_sq);//x^2*3x
    evaluator.relinearize_inplace(x_encrypted_sq, rlk);

    evaluator.multiply(encrypted, encrypted1, encrypted1);//4x

    Ciphertext encrypted_prod;
    evaluator.add_inplace(encrypted3, encrypted1);
    evaluator.add(encrypted3, encrypted0, encrypted_prod);//3x^3+4x+1
    evaluator.relinearize_inplace(encrypted_prod, rlk);

    data_stream.seekp(0, parms_stream.beg);
    data_stream.seekg(0, parms_stream.beg);
    auto size_encrypted_prod = encrypted_prod.save(data_stream);
}
//Client decrpts the result
{
    cout << "Client:decrpts the result" << endl;
    EncryptionParameters parms;
    parms.load(parms_stream);
    parms_stream.seekg(0, parms_stream.beg);
    SEALContext context(parms);
    //Load back the secret key from sk_stream.
    SecretKey secret_key;
    secret_key.load(context, sk_stream);
    Decryptor decryptor(context, secret_key);
    Ciphertext encrypted_result;
    encrypted_result.load(context, data_stream);

    Plaintext plain_result;
    decryptor.decrypt(encrypted_result, plain_result);
    cout << "0x" << plain_result.to_string() << " ...... Correct." << endl;

}

endif

}

But it show error at the line with red point

image image

I don't know where wronged. Can you give me some advices?

kimlaine commented 1 year ago

Could you push your changes to some repository where it's easy to see them clearly?

hui09-m commented 1 year ago

Well, I'll show you the changes. For ckks serialization example, I add another number, so I add codes in line 287、290 and 313 like the following picture

image

Add codes in line 371-373 like the following picture

image

And true results change like this: vector true_result(encoder.slot_count(), 2.3 4.5 2.2);

As for bfv serialization codes, I just according to ckks serialization example. I make save data to two lines like this, but it seems can not load the data in server.

image

To see the changes in bfv and ckks, I just do not encode the plaintexts, is it affects? But SEAL do not support encode one data.