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.58k stars 709 forks source link

BFV scheme: behavior on exceeding noise #251

Open bcebere opened 3 years ago

bcebere commented 3 years ago

Hello. I have the following code snippet, just a simple ciphertext - ciphertext multiplication.

void replicate(vector<int64_t>& data, size_t times) {
    size_t init_size = data.size();
    data.reserve(times);
    for (size_t i = 0; i < times - init_size; i++) {
        data.push_back(data[i % init_size]);
    }
}

vector<int64_t> decrypt(Decryptor& decryptor, BatchEncoder& encoder,
                        const Ciphertext& ct) {
    vector<int64_t> result;
    Plaintext plaintext;

    decryptor.decrypt(ct, plaintext);
    encoder.decode(plaintext, result);

    return result;
}

TEST_F(BFVVectorTest, TestContextRegressionNoise) {
    EncryptionParameters parameters(scheme_type::bfv);
    parameters.set_poly_modulus_degree(4096);
    parameters.set_plain_modulus(1032193);
    parameters.set_coeff_modulus(CoeffModulus::BFVDefault(4096));

    auto ctx = SEALContext(parameters);
    auto keygen = KeyGenerator(ctx);
    auto sk = SecretKey(keygen.secret_key());

    PublicKey pk;
    keygen.create_public_key(pk);

    auto encryptor = Encryptor(ctx, pk);
    auto decryptor = Decryptor(ctx, sk);
    auto encoder = BatchEncoder(ctx);
    auto evaluator = Evaluator(ctx);

    vector<int64_t> data = {2};
    replicate(data, encoder.slot_count());

    Ciphertext initial(ctx);
    Plaintext plaintext;

    encoder.encode(data, plaintext);
    encryptor.encrypt(plaintext, initial);

    auto test_mul = initial;
    auto expected = 2;

    auto dec = decrypt(decryptor, encoder, test_mul);
    ASSERT_EQ(dec[0], expected);

    for (int step = 0; step < 2; ++step) {
        expected *= 2;
        evaluator.multiply_inplace(test_mul, initial);

        auto dec = decrypt(decryptor, encoder, test_mul);
        ASSERT_EQ(dec[0], expected);
    }
}

I know that it exceeds the limits of the parameters, but instead of throwing an exception, it decrypts to the wrong value

Failure
Expected equality of these values:
 dec[0]
    Which is: 467871
  expected
    Which is: 8

Is this the expected behavior? should we monitor the noise budget? I was hoping that SEAL would throw an exception in situations like this.

Tested with SEAL 3.6.1

WeiDaiWD commented 3 years ago

That is the expected behavior. You probably know how to use the function int Decryptor::invariant_noise_budget(...) to monitor noise growth. This function behaves differently from and takes longer than Decryptor::decrypt(...). Automatically monitoring budget in decryption will make decryption much slower. However, I do think that your suggestion is very useful in DEBUG mode, and I might add it to a future version.

Please do not close this issue. Thanks!

bcebere commented 3 years ago

Thank you for the explanation!

I have 2 more questions, sorry for the spam:

WeiDaiWD commented 3 years ago