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

Fboemer/faster decrypt #363

Merged
merged 3 commits into from
Sep 15, 2021
Merged
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
99 changes: 66 additions & 33 deletions native/src/seal/decryptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ namespace seal
case scheme_type::ckks:
ckks_decrypt(encrypted, destination, pool_);
return;

case scheme_type::bgv:
bgv_decrypt(encrypted, destination, pool_);
return;

default:
throw invalid_argument("unsupported scheme");
}

}

void Decryptor::bfv_decrypt(const Ciphertext &encrypted, Plaintext &destination, MemoryPoolHandle pool)
Expand Down Expand Up @@ -212,7 +212,7 @@ namespace seal
destination.resize(coeff_count);

context_data.rns_tool()->decrypt_modt(tmp_dest_modq, destination.data(), pool);

//Fix the plaintext after mod-switch operations.
uint64_t fix = 1;
for(size_t i = context_data.chain_index(); i < first_context_data.chain_index(); i++)
Expand All @@ -224,10 +224,10 @@ namespace seal
{
multiply_poly_scalar_coeffmod(CoeffIter(destination.data()), coeff_count, fix, plain_modulus, CoeffIter(destination.data()));
}

// How many non-zero coefficients do we really have in the result?
size_t plain_coeff_count = get_significant_uint64_count_uint(destination.data(), coeff_count);

// Resize destination to appropriate size
destination.resize(max(plain_coeff_count, size_t(1)));
}
Expand Down Expand Up @@ -314,39 +314,72 @@ namespace seal
// Make sure we have enough secret key powers computed
compute_secret_key_array(encrypted_size - 1);

// put < (c_1 , c_2, ... , c_{count-1}) , (s,s^2,...,s^{count-1}) > mod q in destination
// Now do the dot product of encrypted_copy and the secret key array using NTT.
// The secret key powers are already NTT transformed.

SEAL_ALLOCATE_GET_POLY_ITER(encrypted_copy, encrypted_size - 1, coeff_count, coeff_modulus_size, pool);
set_poly_array(encrypted.data(1), encrypted_size - 1, coeff_count, coeff_modulus_size, encrypted_copy);

// Transform c_1, c_2, ... to NTT form unless they already are
if (!is_ntt_form)
if (encrypted_size == 2)
{
ntt_negacyclic_harvey_lazy(encrypted_copy, encrypted_size - 1, ntt_tables);
}
ConstRNSIter secret_key_array(secret_key_array_.get(), coeff_count);
ConstRNSIter c0(encrypted.data(0), coeff_count);
ConstRNSIter c1(encrypted.data(1), coeff_count);
if (is_ntt_form)
{
SEAL_ITERATE(
iter(c0, c1, secret_key_array, coeff_modulus, destination), coeff_modulus_size,
[&](auto I) {
// put < c_1 * s > mod q in destination
dyadic_product_coeffmod(get<1>(I), get<2>(I), coeff_count, get<3>(I), get<4>(I));
// add c_0 to the result; note that destination should be in the same (NTT) form as encrypted
add_poly_coeffmod(get<4>(I), get<0>(I), coeff_count, get<3>(I), get<4>(I));
});
}
else
{
SEAL_ITERATE(
iter(c0, c1, secret_key_array, coeff_modulus, ntt_tables, destination), coeff_modulus_size,
[&](auto I) {
set_uint(get<1>(I), coeff_count, get<5>(I));
// Transform c_1 to NTT form
ntt_negacyclic_harvey_lazy(get<5>(I), get<4>(I));
// put < c_1 * s > mod q in destination
dyadic_product_coeffmod(get<5>(I), get<2>(I), coeff_count, get<3>(I), get<5>(I));
// Transform back
inverse_ntt_negacyclic_harvey(get<5>(I), get<4>(I));
// add c_0 to the result; note that destination should be in the same (NTT) form as encrypted
add_poly_coeffmod(get<5>(I), get<0>(I), coeff_count, get<3>(I), get<5>(I));
});
}
} else {
WeiDaiWD marked this conversation as resolved.
Show resolved Hide resolved
// put < (c_1 , c_2, ... , c_{count-1}) , (s,s^2,...,s^{count-1}) > mod q in destination
// Now do the dot product of encrypted_copy and the secret key array using NTT.
// The secret key powers are already NTT transformed.
SEAL_ALLOCATE_GET_POLY_ITER(encrypted_copy, encrypted_size - 1, coeff_count, coeff_modulus_size, pool);
set_poly_array(encrypted.data(1), encrypted_size - 1, coeff_count, coeff_modulus_size, encrypted_copy);

// Transform c_1, c_2, ... to NTT form unless they already are
if (!is_ntt_form)
{
ntt_negacyclic_harvey_lazy(encrypted_copy, encrypted_size - 1, ntt_tables);
}

// Compute dyadic product with secret power array
auto secret_key_array = PolyIter(secret_key_array_.get(), coeff_count, key_coeff_modulus_size);
SEAL_ITERATE(iter(encrypted_copy, secret_key_array), encrypted_size - 1, [&](auto I) {
dyadic_product_coeffmod(get<0>(I), get<1>(I), coeff_modulus_size, coeff_modulus, get<0>(I));
});

// Compute dyadic product with secret power array
auto secret_key_array = PolyIter(secret_key_array_.get(), coeff_count, key_coeff_modulus_size);
SEAL_ITERATE(iter(encrypted_copy, secret_key_array), encrypted_size - 1, [&](auto I) {
dyadic_product_coeffmod(get<0>(I), get<1>(I), coeff_modulus_size, coeff_modulus, get<0>(I));
});
// Aggregate all polynomials together to complete the dot product
set_zero_poly(coeff_count, coeff_modulus_size, destination);
SEAL_ITERATE(encrypted_copy, encrypted_size - 1, [&](auto I) {
add_poly_coeffmod(destination, I, coeff_modulus_size, coeff_modulus, destination);
});

// Aggregate all polynomials together to complete the dot product
set_zero_poly(coeff_count, coeff_modulus_size, destination);
SEAL_ITERATE(encrypted_copy, encrypted_size - 1, [&](auto I) {
add_poly_coeffmod(destination, I, coeff_modulus_size, coeff_modulus, destination);
});
if (!is_ntt_form)
{
// If the input was not in NTT form, need to transform back
inverse_ntt_negacyclic_harvey(destination, coeff_modulus_size, ntt_tables);
}

if (!is_ntt_form)
{
// If the input was not in NTT form, need to transform back
inverse_ntt_negacyclic_harvey(destination, coeff_modulus_size, ntt_tables);
// Finally add c_0 to the result; note that destination should be in the same (NTT) form as encrypted
add_poly_coeffmod(destination, *iter(encrypted), coeff_modulus_size, coeff_modulus, destination);
}

// Finally add c_0 to the result; note that destination should be in the same (NTT) form as encrypted
add_poly_coeffmod(destination, *iter(encrypted), coeff_modulus_size, coeff_modulus, destination);
}

int Decryptor::invariant_noise_budget(const Ciphertext &encrypted)
Expand Down
2 changes: 1 addition & 1 deletion native/src/seal/evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ namespace seal
encrypted1.scale() = new_scale;
}

void Evaluator::bgv_multiply(Ciphertext &encrypted1, Ciphertext &encrypted2, MemoryPoolHandle pool)
void Evaluator::bgv_multiply(Ciphertext &encrypted1, Ciphertext &encrypted2, MemoryPoolHandle pool) const
{
if (encrypted1.is_ntt_form() || encrypted2.is_ntt_form())
{
Expand Down
4 changes: 2 additions & 2 deletions native/src/seal/evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ namespace seal
*/
inline void rotate_columns_inplace(
Ciphertext &encrypted, const GaloisKeys &galois_keys, MemoryPoolHandle pool = MemoryManager::GetPool()) const
{
{
auto scheme = context_.key_context_data()->parms().scheme();
if (scheme != scheme_type::bfv && scheme != scheme_type::bgv)
{
Expand Down Expand Up @@ -1204,7 +1204,7 @@ namespace seal

void ckks_multiply(Ciphertext &encrypted1, const Ciphertext &encrypted2, MemoryPoolHandle pool) const;

void bgv_multiply(Ciphertext &encrypted1, Ciphertext &encrypted2, MemoryPoolHandle pool);
void bgv_multiply(Ciphertext &encrypted1, Ciphertext &encrypted2, MemoryPoolHandle pool) const;

void bfv_square(Ciphertext &encrypted, MemoryPoolHandle pool) const;

Expand Down