Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add invariant noise to decryptor #545

Open
wants to merge 1 commit into
base: contrib
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions native/src/seal/c/decryptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,26 @@ SEAL_C_FUNC Decryptor_Decrypt(void *thisptr, void *encrypted, void *destination)
}
}

SEAL_C_FUNC Decryptor_InvariantNoise(void *thisptr, void *encrypted, double *invariant_noise_budget)
{
Decryptor *decryptor = FromVoid<Decryptor>(thisptr);
IfNullRet(decryptor, E_POINTER);
Ciphertext *encryptedptr = FromVoid<Ciphertext>(encrypted);
IfNullRet(encryptedptr, E_POINTER);
IfNullRet(invariant_noise_budget, E_POINTER);

try
{
*invariant_noise_budget = decryptor->invariant_noise(*encryptedptr);
return S_OK;
}
catch (const invalid_argument &)
{
return E_INVALIDARG;
}
}


SEAL_C_FUNC Decryptor_InvariantNoiseBudget(void *thisptr, void *encrypted, int *invariant_noise_budget)
{
Decryptor *decryptor = FromVoid<Decryptor>(thisptr);
Expand Down
2 changes: 2 additions & 0 deletions native/src/seal/c/decryptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ SEAL_C_FUNC Decryptor_Destroy(void *thisptr);

SEAL_C_FUNC Decryptor_Decrypt(void *thisptr, void *encrypted, void *destination);

SEAL_C_FUNC Decryptor_InvariantNoise(void *thisptr, void *encrypted, double *invariant_noise_budget);

SEAL_C_FUNC Decryptor_InvariantNoiseBudget(void *thisptr, void *encrypted, int *invariant_noise_budget);
41 changes: 39 additions & 2 deletions native/src/seal/decryptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "seal/util/uintarith.h"
#include "seal/util/uintcore.h"
#include <algorithm>
#include <cmath>
#include <stdexcept>

using namespace std;
Expand Down Expand Up @@ -377,8 +378,7 @@ namespace seal
}
}

int Decryptor::invariant_noise_budget(const Ciphertext &encrypted)
{
util::Pointer<uint64_t> Decryptor::invariant_noise_internal(const Ciphertext &encrypted) {
// Verify that encrypted is valid.
if (!is_valid_for(encrypted, context_))
{
Expand Down Expand Up @@ -438,6 +438,43 @@ namespace seal
StrideIter<const uint64_t *> wide_noise_poly((*noise_poly).ptr(), coeff_modulus_size);
poly_infty_norm_coeffmod(wide_noise_poly, coeff_count, context_data.total_coeff_modulus(), norm.get(), pool_);

return norm;
}

double Decryptor::invariant_noise(const Ciphertext &encrypted) {
double invariant_noise = 0.0;

auto &context_data = *context_.get_context_data(encrypted.parms_id());
auto &parms = context_data.parms();
auto &coeff_modulus = parms.coeff_modulus();
size_t coeff_modulus_size = coeff_modulus.size();

auto norm = invariant_noise_internal(encrypted);

for (size_t i = 0; i < coeff_modulus_size; i++) {
auto power = static_cast<double>(sizeof(uint64_t) * 8 * i);
auto word = static_cast<double>(norm.get()[i]);
invariant_noise += word * exp2(power);
Copy link
Author

@rickwebiii rickwebiii Aug 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking through other code, I suspect this is the wrong way to reduce a coefficient into a double precision number as this doesn't take into account the coefficient modulus values.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, I now think this is correct. The coefficients are indeed stored as RNS, which is what led to my doubt. However, invariant_noise_internal (the code I factored out of the existing invariant_noise_budget function) calls compose_array(noise_poly, coeff_count, pool_) before computing the infinity norm. This converts the RNS to multi-precision, so the infinity norm is a multi-precision value, which is what I was assuming here.

}

double total_coeff = 1.0;

for (auto coeff_mod : coeff_modulus) {
total_coeff *= static_cast<double>(coeff_mod.value());
}

return invariant_noise / total_coeff;
}

int Decryptor::invariant_noise_budget(const Ciphertext &encrypted)
{
auto norm = invariant_noise_internal(encrypted);

auto &context_data = *context_.get_context_data(encrypted.parms_id());
auto &parms = context_data.parms();
auto &coeff_modulus = parms.coeff_modulus();
size_t coeff_modulus_size = coeff_modulus.size();

// The -1 accounts for scaling the invariant noise by 2;
// note that we already took plain_modulus into account in compose
// so no need to subtract log(plain_modulus) from this
Expand Down
27 changes: 25 additions & 2 deletions native/src/seal/decryptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,27 @@ namespace seal
*/
void decrypt(const Ciphertext &encrypted, Plaintext &destination);

/*
Computes the invariant noise of a ciphertext. The invariant noise is
a value that increases with FHE operations. This function only works
with the BFV scheme.

@par Invariant Noise
The invariant noise polynomial of a ciphertext is a rational coefficient
polynomial, such that a ciphertext decrypts correctly as long as the
coefficients of the invariant noise polynomial are of absolute value less
than 1/2. Thus, we call the infinity-norm of the invariant noise polynomial
the invariant noise, and for correct decryption require it to be less than
1/2.

@param[in] encrypted The ciphertext
@throws std::invalid_argument if the scheme is not BFV
@throws std::invalid_argument if encrypted is not valid for the encryption
parameters
@throws std::invalid_argument if encrypted is in NTT form
*/
SEAL_NODISCARD double invariant_noise(const Ciphertext &encrypted);

/*
Computes the invariant noise budget (in bits) of a ciphertext. The
invariant noise budget measures the amount of room there is for the noise
Expand All @@ -82,9 +103,9 @@ namespace seal
@par Invariant Noise Budget
The invariant noise polynomial of a ciphertext is a rational coefficient
polynomial, such that a ciphertext decrypts correctly as long as the
coefficients of the invariantnoise polynomial are of absolute value less
coefficients of the invariant noise polynomial are of absolute value less
than 1/2. Thus, we call the infinity-norm of the invariant noise polynomial
the invariant noise, and for correct decryption requireit to be less than
the invariant noise, and for correct decryption require it to be less than
1/2. If v denotes the invariant noise, we define the invariant noise budget
as -log2(2v). Thus, the invariant noise budget starts from some initial
value, which depends on the encryption parameters, and decreases when
Expand Down Expand Up @@ -121,6 +142,8 @@ namespace seal
// destination has the size of an RNS polynomial.
void dot_product_ct_sk_array(const Ciphertext &encrypted, util::RNSIter destination, MemoryPoolHandle pool);

util::Pointer<uint64_t> invariant_noise_internal(const Ciphertext &encrypted);

// We use a fresh memory pool with `clear_on_destruction' enabled.
MemoryPoolHandle pool_ = MemoryManager::GetPool(mm_prof_opt::mm_force_new, true);

Expand Down
1 change: 1 addition & 0 deletions native/tests/seal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ target_sources(sealtest
${CMAKE_CURRENT_LIST_DIR}/ciphertext.cpp
${CMAKE_CURRENT_LIST_DIR}/ckks.cpp
${CMAKE_CURRENT_LIST_DIR}/context.cpp
${CMAKE_CURRENT_LIST_DIR}/decryptor.cpp
${CMAKE_CURRENT_LIST_DIR}/encryptionparams.cpp
${CMAKE_CURRENT_LIST_DIR}/encryptor.cpp
${CMAKE_CURRENT_LIST_DIR}/evaluator.cpp
Expand Down
46 changes: 46 additions & 0 deletions native/tests/seal/decryptor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

#include "seal/batchencoder.h"
#include "seal/context.h"
#include "seal/decryptor.h"
#include "seal/encryptor.h"
#include "seal/keygenerator.h"
#include "seal/modulus.h"
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <ctime>
#include "gtest/gtest.h"

using namespace seal;
using namespace std;

namespace sealtest
{
TEST(DecryptorTest, InvariantNoiseAndBudget)
{
EncryptionParameters parms(scheme_type::bgv);
Modulus plain_modulus(1 << 6);
parms.set_plain_modulus(plain_modulus);
parms.set_poly_modulus_degree(64);
parms.set_coeff_modulus(CoeffModulus::Create(64, { 60, 60, 60 }));
SEALContext context(parms, true, sec_level_type::none);
KeyGenerator keygen(context);
PublicKey pk;
keygen.create_public_key(pk);

Encryptor encryptor(context, pk, keygen.secret_key());
Decryptor decryptor(context, keygen.secret_key());

Ciphertext ct;

encryptor.encrypt_zero(ct);
auto invariant_noise = decryptor.invariant_noise(ct);
auto invariant_noise_budget = decryptor.invariant_noise_budget(ct);

auto calculated_noise_budget = floor(-log2(2. * invariant_noise));

ASSERT_DOUBLE_EQ(calculated_noise_budget, static_cast<double>(invariant_noise_budget));
}
}