void Evaluator::relinearize(const uint64_t *encrypted, uint64_t *destination) { // Extract encryption parameters. int coeff_count = poly_modulus_.coeff_count(); int coeff_bit_count = poly_modulus_.coeff_bit_count(); int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64); // Clear destatintion. set_zero_poly(coeff_count, coeff_uint64_count, destination); // Create polynomial to store decomposed polynomial (one at a time). Pointer decomp_poly(allocate_poly(coeff_count, coeff_uint64_count, pool_)); Pointer decomp_eval_poly(allocate_poly(coeff_count, coeff_uint64_count, pool_)); int shift = 0; for (int decomp_index = 0; decomp_index < evaluation_keys_.count(); ++decomp_index) { // Isolate decomposition_bit_count_ bits for each coefficient. for (int coeff_index = 0; coeff_index < coeff_count; ++coeff_index) { const uint64_t *productmoded_coeff = get_poly_coeff(encrypted, coeff_index, coeff_uint64_count); uint64_t *decomp_coeff = get_poly_coeff(decomp_poly.get(), coeff_index, coeff_uint64_count); right_shift_uint(productmoded_coeff, shift, coeff_uint64_count, decomp_coeff); filter_highbits_uint(decomp_coeff, coeff_uint64_count, decomposition_bit_count_); } // Multiply decomposed poly by evaluation key and accumulate to result. const BigPoly &evaluation_key = evaluation_keys_[decomp_index]; multiply_poly_poly_polymod_coeffmod(decomp_poly.get(), evaluation_key.pointer(), polymod_, mod_, decomp_eval_poly.get(), pool_); add_poly_poly_coeffmod(decomp_eval_poly.get(), destination, coeff_count, coeff_modulus_.pointer(), coeff_uint64_count, destination); // Increase shift by decomposition_bit_count_ for next iteration. shift += decomposition_bit_count_; } }
void poly_eval_poly(const uint64_t *poly_to_eval, int poly_to_eval_coeff_count, int poly_to_eval_coeff_uint64_count, const uint64_t *value, int value_coeff_count, int value_coeff_uint64_count, int result_coeff_count, int result_coeff_uint64_count, uint64_t *result, MemoryPool &pool) { #ifdef SEAL_DEBUG if (poly_to_eval == nullptr) { throw invalid_argument("poly_to_eval"); } if (value == nullptr) { throw invalid_argument("value"); } if (result == nullptr) { throw invalid_argument("result"); } if (poly_to_eval_coeff_count <= 0) { throw invalid_argument("poly_to_eval_coeff_count"); } if (poly_to_eval_coeff_uint64_count <= 0) { throw invalid_argument("poly_to_eval_coeff_uint64_count"); } if (value_coeff_count <= 0) { throw invalid_argument("value_coeff_count"); } if (value_coeff_uint64_count <= 0) { throw invalid_argument("value_coeff_uint64_count"); } if (result_coeff_count <= 0) { throw invalid_argument("result_coeff_count"); } if (result_coeff_uint64_count <= 0) { throw invalid_argument("result_coeff_uint64_count"); } #endif // Evaluate poly at value using Horner's method Pointer temp1(allocate_poly(result_coeff_count, result_coeff_uint64_count, pool)); Pointer temp2(allocate_zero_poly(result_coeff_count, result_coeff_uint64_count, pool)); uint64_t *productptr = temp1.get(); uint64_t *intermediateptr = temp2.get(); for (int coeff_index = poly_to_eval_coeff_count - 1; coeff_index >= 0; coeff_index--) { multiply_poly_poly(intermediateptr, result_coeff_count, result_coeff_uint64_count, value, value_coeff_count, value_coeff_uint64_count, result_coeff_count, result_coeff_uint64_count, productptr, pool); const uint64_t *curr_coeff = get_poly_coeff(poly_to_eval, coeff_index, poly_to_eval_coeff_uint64_count); add_uint_uint(productptr, result_coeff_uint64_count, curr_coeff, poly_to_eval_coeff_uint64_count, false, result_coeff_uint64_count, productptr); swap(productptr, intermediateptr); } set_poly_poly(intermediateptr, result_coeff_count, result_coeff_uint64_count, result); }
void Evaluator::multiply(const BigPoly &encrypted1, const BigPoly &encrypted2, BigPoly &destination) { // Extract encryption parameters. int coeff_count = poly_modulus_.coeff_count(); int coeff_bit_count = poly_modulus_.coeff_bit_count(); int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64); // Verify parameters. if (encrypted1.coeff_count() != coeff_count || encrypted1.coeff_bit_count() != coeff_bit_count) { throw invalid_argument("encrypted1 is not valid for encryption parameters"); } if (encrypted2.coeff_count() != coeff_count || encrypted2.coeff_bit_count() != coeff_bit_count) { throw invalid_argument("encrypted2 is not valid for encryption parameters"); } #ifdef _DEBUG if (encrypted1.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(encrypted1, coeff_modulus_)) { throw invalid_argument("encrypted1 is not valid for encryption parameters"); } if (encrypted2.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(encrypted2, coeff_modulus_)) { throw invalid_argument("encrypted2 is not valid for encryption parameters"); } #endif if (destination.coeff_count() != coeff_count || destination.coeff_bit_count() != coeff_bit_count) { destination.resize(coeff_count, coeff_bit_count); } // Handle test-mode case. if (mode_ == TEST_MODE) { // Get pointer to inputs (duplicated if needed). ConstPointer encrypted1ptr = duplicate_poly_if_needed(encrypted1, encrypted1.pointer() == destination.pointer(), pool_); ConstPointer encrypted2ptr = duplicate_poly_if_needed(encrypted2, encrypted2.pointer() == destination.pointer(), pool_); multiply_poly_poly_polymod_coeffmod(encrypted1ptr.get(), encrypted2ptr.get(), polymod_, mod_, destination.pointer(), pool_); return; } // Multiply encrypted polynomials and perform key switching. Pointer product(allocate_poly(coeff_count, coeff_uint64_count, pool_)); multiply(encrypted1.pointer(), encrypted2.pointer(), product.get()); relinearize(product.get(), destination.pointer()); }
void KeyGenerator::generate() { // Handle test-mode case. if (mode_ == TEST_MODE) { public_key_.set_zero(); public_key_[0] = 1; secret_key_.set_zero(); secret_key_[0] = 1; for (int i = 0; i < evaluation_keys_.count(); ++i) { evaluation_keys_[i].set_zero(); evaluation_keys_[i][0] = 1; } return; } // Extract encryption parameters. int coeff_count = poly_modulus_.coeff_count(); int coeff_bit_count = poly_modulus_.coeff_bit_count(); int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64); // Loop until find a valid secret key. uint64_t *secret_key = secret_key_.pointer(); set_zero_poly(coeff_count, coeff_uint64_count, secret_key); Pointer secret_key_inv(allocate_poly(coeff_count, coeff_uint64_count, pool_)); while (true) { // Create noise with random [-1, 1] coefficients. set_poly_coeffs_zero_one_negone(secret_key); // Calculate secret_key * plaintext_modulus + 1. multiply_poly_scalar_coeffmod(secret_key, coeff_count, plain_modulus_.pointer(), mod_, secret_key, pool_); uint64_t *constant_coeff = get_poly_coeff(secret_key, 0, coeff_uint64_count); increment_uint_mod(constant_coeff, coeff_modulus_.pointer(), coeff_uint64_count, constant_coeff); // Attempt to invert secret_key. if (try_invert_poly_coeffmod(secret_key, poly_modulus_.pointer(), coeff_count, mod_, secret_key_inv.get(), pool_)) { // Secret_key is invertible, so is valid break; } } // Calculate plaintext_modulus * noise * secret_key_inv. Pointer noise(allocate_poly(coeff_count, coeff_uint64_count, pool_)); set_poly_coeffs_zero_one_negone(noise.get()); uint64_t *public_key = public_key_.pointer(); multiply_poly_poly_polymod_coeffmod(noise.get(), secret_key_inv.get(), polymod_, mod_, noise.get(), pool_); multiply_poly_scalar_coeffmod(noise.get(), coeff_count, plain_modulus_.pointer(), mod_, public_key, pool_); // Create evaluation keys. Pointer evaluation_factor(allocate_uint(coeff_uint64_count, pool_)); set_uint(1, coeff_uint64_count, evaluation_factor.get()); for (int i = 0; i < evaluation_keys_.count(); ++i) { // Multiply secret_key by evaluation_factor (mod coeff modulus). uint64_t *evaluation_key = evaluation_keys_[i].pointer(); multiply_poly_scalar_coeffmod(secret_key, coeff_count, evaluation_factor.get(), mod_, evaluation_key, pool_); // Multiply public_key*normal noise and add into evaluation_key. set_poly_coeffs_normal(noise.get()); multiply_poly_poly_polymod_coeffmod(noise.get(), public_key, polymod_, mod_, noise.get(), pool_); add_poly_poly_coeffmod(noise.get(), evaluation_key, coeff_count, coeff_modulus_.pointer(), coeff_uint64_count, evaluation_key); // Add-in more normal noise to evaluation_key. set_poly_coeffs_normal(noise.get()); add_poly_poly_coeffmod(noise.get(), evaluation_key, coeff_count, coeff_modulus_.pointer(), coeff_uint64_count, evaluation_key); // Left shift evaluation factor. left_shift_uint(evaluation_factor.get(), decomposition_bit_count_, coeff_uint64_count, evaluation_factor.get()); } }
void KeyGenerator::generate(const BigPoly &secret_key, uint64_t power) { // Validate arguments. if (secret_key.is_zero()) { throw invalid_argument("secret_key cannot be zero"); } if (power == 0) { throw invalid_argument("power cannot be zero"); } // Handle test-mode case. if (mode_ == TEST_MODE) { public_key_.set_zero(); public_key_[0] = 1; secret_key_.set_zero(); secret_key_[0] = 1; for (int i = 0; i < evaluation_keys_.count(); ++i) { evaluation_keys_[i].set_zero(); evaluation_keys_[i][0] = 1; } return; } // Extract encryption parameters. int coeff_count = poly_modulus_.coeff_count(); int coeff_bit_count = poly_modulus_.coeff_bit_count(); int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64); // Verify secret key looks valid. secret_key_ = secret_key; if (secret_key_.coeff_count() != coeff_count || secret_key_.coeff_bit_count() != coeff_bit_count) { throw invalid_argument("secret_key is not valid for encryption parameters"); } #ifdef _DEBUG if (secret_key_.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(secret_key_, coeff_modulus_)) { throw invalid_argument("secret_key is not valid for encryption parameters"); } #endif // Raise level of secret key. if (power > 1) { exponentiate_poly_polymod_coeffmod(secret_key_.pointer(), &power, 1, polymod_, mod_, secret_key_.pointer(), pool_); } // Attempt to invert secret_key. Pointer secret_key_inv(allocate_poly(coeff_count, coeff_uint64_count, pool_)); if (!try_invert_poly_coeffmod(secret_key_.pointer(), poly_modulus_.pointer(), coeff_count, mod_, secret_key_inv.get(), pool_)) { // Secret_key is not invertible, so not valid. throw invalid_argument("secret_key is not valid for encryption parameters"); } // Calculate plaintext_modulus * noise * secret_key_inv. Pointer noise(allocate_poly(coeff_count, coeff_uint64_count, pool_)); set_poly_coeffs_zero_one_negone(noise.get()); uint64_t *public_key = public_key_.pointer(); multiply_poly_poly_polymod_coeffmod(noise.get(), secret_key_inv.get(), polymod_, mod_, noise.get(), pool_); multiply_poly_scalar_coeffmod(noise.get(), coeff_count, plain_modulus_.pointer(), mod_, public_key, pool_); // Create evaluation keys. Pointer evaluation_factor(allocate_uint(coeff_uint64_count, pool_)); set_uint(1, coeff_uint64_count, evaluation_factor.get()); for (int i = 0; i < evaluation_keys_.count(); ++i) { // Multiply secret_key by evaluation_factor (mod coeff modulus). uint64_t *evaluation_key = evaluation_keys_[i].pointer(); multiply_poly_scalar_coeffmod(secret_key_.pointer(), coeff_count, evaluation_factor.get(), mod_, evaluation_key, pool_); // Multiply public_key*normal noise and add into evaluation_key. set_poly_coeffs_normal(noise.get()); multiply_poly_poly_polymod_coeffmod(noise.get(), public_key, polymod_, mod_, noise.get(), pool_); add_poly_poly_coeffmod(noise.get(), evaluation_key, coeff_count, coeff_modulus_.pointer(), coeff_uint64_count, evaluation_key); // Add-in more normal noise to evaluation_key. set_poly_coeffs_normal(noise.get()); add_poly_poly_coeffmod(noise.get(), evaluation_key, coeff_count, coeff_modulus_.pointer(), coeff_uint64_count, evaluation_key); // Left shift evaluation factor. left_shift_uint(evaluation_factor.get(), decomposition_bit_count_, coeff_uint64_count, evaluation_factor.get()); } }
void Evaluator::multiply(const uint64_t *encrypted1, const uint64_t *encrypted2, uint64_t *destination) { // Extract encryption parameters. int coeff_count = poly_modulus_.coeff_count(); int coeff_bit_count = poly_modulus_.coeff_bit_count(); int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64); // Clear destatintion. set_zero_poly(coeff_count, coeff_uint64_count, destination); // Determine if FFT can be used. bool use_fft = polymod_.coeff_count_power_of_two() >= 0 && polymod_.is_one_zero_one(); if (use_fft) { // Use FFT to multiply polynomials. // Allocate polynomial to store product of two polynomials, with poly but no coeff modulo yet (and signed). int product_coeff_bit_count = coeff_bit_count + coeff_bit_count + get_significant_bit_count(static_cast<uint64_t>(coeff_count)) + 2; int product_coeff_uint64_count = divide_round_up(product_coeff_bit_count, bits_per_uint64); Pointer product(allocate_poly(coeff_count, product_coeff_uint64_count, pool_)); // Use FFT to multiply polynomials. set_zero_uint(product_coeff_uint64_count, get_poly_coeff(product.get(), coeff_count - 1, product_coeff_uint64_count)); fftmultiply_poly_poly_polymod(encrypted1, encrypted2, polymod_.coeff_count_power_of_two(), coeff_uint64_count, product_coeff_uint64_count, product.get(), pool_); // For each coefficient in product, multiply by plain_modulus and divide by coeff_modulus and then modulo by coeff_modulus. int plain_modulus_bit_count = plain_modulus_.significant_bit_count(); int plain_modulus_uint64_count = divide_round_up(plain_modulus_bit_count, bits_per_uint64); int intermediate_bit_count = product_coeff_bit_count + plain_modulus_bit_count - 1; int intermediate_uint64_count = divide_round_up(intermediate_bit_count, bits_per_uint64); Pointer intermediate(allocate_uint(intermediate_uint64_count, pool_)); Pointer quotient(allocate_uint(intermediate_uint64_count, pool_)); for (int coeff_index = 0; coeff_index < coeff_count; ++coeff_index) { uint64_t *product_coeff = get_poly_coeff(product.get(), coeff_index, product_coeff_uint64_count); bool coeff_is_negative = is_high_bit_set_uint(product_coeff, product_coeff_uint64_count); if (coeff_is_negative) { negate_uint(product_coeff, product_coeff_uint64_count, product_coeff); } multiply_uint_uint(product_coeff, product_coeff_uint64_count, plain_modulus_.pointer(), plain_modulus_uint64_count, intermediate_uint64_count, intermediate.get()); add_uint_uint(intermediate.get(), wide_coeff_modulus_div_two_.pointer(), intermediate_uint64_count, intermediate.get()); divide_uint_uint_inplace(intermediate.get(), wide_coeff_modulus_.pointer(), intermediate_uint64_count, quotient.get(), pool_); modulo_uint_inplace(quotient.get(), intermediate_uint64_count, mod_, pool_); uint64_t *dest_coeff = get_poly_coeff(destination, coeff_index, coeff_uint64_count); if (coeff_is_negative) { negate_uint_mod(quotient.get(), coeff_modulus_.pointer(), coeff_uint64_count, dest_coeff); } else { set_uint_uint(quotient.get(), coeff_uint64_count, dest_coeff); } } } else { // Use normal multiplication to multiply polynomials. // Allocate polynomial to store product of two polynomials, with no poly or coeff modulo yet. int product_coeff_count = coeff_count + coeff_count - 1; int product_coeff_bit_count = coeff_bit_count + coeff_bit_count + get_significant_bit_count(static_cast<uint64_t>(coeff_count)); int product_coeff_uint64_count = divide_round_up(product_coeff_bit_count, bits_per_uint64); Pointer product(allocate_poly(product_coeff_count, product_coeff_uint64_count, pool_)); // Multiply polynomials. multiply_poly_poly(encrypted1, coeff_count, coeff_uint64_count, encrypted2, coeff_count, coeff_uint64_count, product_coeff_count, product_coeff_uint64_count, product.get(), pool_); // For each coefficient in product, multiply by plain_modulus and divide by coeff_modulus and then modulo by coeff_modulus. int plain_modulus_bit_count = plain_modulus_.significant_bit_count(); int plain_modulus_uint64_count = divide_round_up(plain_modulus_bit_count, bits_per_uint64); int intermediate_bit_count = product_coeff_bit_count + plain_modulus_bit_count; int intermediate_uint64_count = divide_round_up(intermediate_bit_count, bits_per_uint64); Pointer intermediate(allocate_uint(intermediate_uint64_count, pool_)); Pointer quotient(allocate_uint(intermediate_uint64_count, pool_)); Pointer productmoded(allocate_poly(product_coeff_count, coeff_uint64_count, pool_)); for (int coeff_index = 0; coeff_index < product_coeff_count; ++coeff_index) { const uint64_t *product_coeff = get_poly_coeff(product.get(), coeff_index, product_coeff_uint64_count); multiply_uint_uint(product_coeff, product_coeff_uint64_count, plain_modulus_.pointer(), plain_modulus_uint64_count, intermediate_uint64_count, intermediate.get()); add_uint_uint(intermediate.get(), wide_coeff_modulus_div_two_.pointer(), intermediate_uint64_count, intermediate.get()); divide_uint_uint_inplace(intermediate.get(), wide_coeff_modulus_.pointer(), intermediate_uint64_count, quotient.get(), pool_); modulo_uint_inplace(quotient.get(), intermediate_uint64_count, mod_, pool_); uint64_t *productmoded_coeff = get_poly_coeff(productmoded.get(), coeff_index, coeff_uint64_count); set_uint_uint(quotient.get(), coeff_uint64_count, productmoded_coeff); } // Perform polynomial modulo. modulo_poly_inplace(productmoded.get(), product_coeff_count, polymod_, mod_, pool_); // Copy to destination. set_poly_poly(productmoded.get(), coeff_count, coeff_uint64_count, destination); } }
void Evaluator::multiply_plain(const BigPoly &encrypted1, const BigPoly &plain2, BigPoly &destination) { // Extract encryption parameters. int coeff_count = poly_modulus_.coeff_count(); int coeff_bit_count = poly_modulus_.coeff_bit_count(); int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64); // Verify parameters. if (encrypted1.coeff_count() != coeff_count || encrypted1.coeff_bit_count() != coeff_bit_count) { throw invalid_argument("encrypted1 is not valid for encryption parameters"); } #ifdef _DEBUG if (encrypted1.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(encrypted1, coeff_modulus_)) { throw invalid_argument("encrypted1 is not valid for encryption parameters"); } if (plain2.significant_coeff_count() >= coeff_count || !are_poly_coefficients_less_than(plain2, plain_modulus_)) { throw invalid_argument("plain2 is too large to be represented by encryption parameters"); } #endif if (destination.coeff_count() != coeff_count || destination.coeff_bit_count() != coeff_bit_count) { destination.resize(coeff_count, coeff_bit_count); } // Get pointer to inputs (duplicated if needed). ConstPointer encrypted1ptr = duplicate_poly_if_needed(encrypted1, encrypted1.pointer() == destination.pointer(), pool_); // Handle test-mode case. if (mode_ == TEST_MODE) { // Get pointer to inputs (duplicated and resized if needed). ConstPointer plain2ptr = duplicate_poly_if_needed(plain2, coeff_count, coeff_uint64_count, plain2.pointer() == destination.pointer(), pool_); // Resize second operand if needed. multiply_poly_poly_polymod_coeffmod(encrypted1ptr.get(), plain2ptr.get(), polymod_, mod_, destination.pointer(), pool_); return; } // Reposition coefficients. Pointer moved2ptr(allocate_poly(coeff_count, coeff_uint64_count, pool_)); int plain_coeff_count = min(plain2.significant_coeff_count(), coeff_count); int plain2_coeff_uint64_count = plain2.coeff_uint64_count(); const uint64_t *plain2_coeff = plain2.pointer(); uint64_t *moved2_coeff = moved2ptr.get(); for (int i = 0; i < plain_coeff_count; ++i) { set_uint_uint(plain2_coeff, plain2_coeff_uint64_count, coeff_uint64_count, moved2_coeff); bool is_upper_half = is_greater_than_or_equal_uint_uint(moved2_coeff, plain_upper_half_threshold_.pointer(), coeff_uint64_count); if (is_upper_half) { add_uint_uint(moved2_coeff, plain_upper_half_increment_.pointer(), coeff_uint64_count, moved2_coeff); } moved2_coeff += coeff_uint64_count; plain2_coeff += plain2_coeff_uint64_count; } for (int i = plain_coeff_count; i < coeff_count; ++i) { set_zero_uint(coeff_uint64_count, moved2_coeff); moved2_coeff += coeff_uint64_count; } // Use normal polynomial multiplication. multiply_poly_poly_polymod_coeffmod(encrypted1ptr.get(), moved2ptr.get(), polymod_, mod_, destination.pointer(), pool_); }