Modulus::Modulus(const std::uint64_t *modulus, int uint64_count, MemoryPool &pool) : modulus_(modulus), uint64_count_(uint64_count) { #ifdef SEAL_DEBUG if (modulus == nullptr) { throw invalid_argument("modulus"); } if (uint64_count <= 0) { throw invalid_argument("uint64_count"); } if (is_zero_uint(modulus, uint64_count)) { throw invalid_argument("modulus"); } #endif significant_bit_count_ = get_significant_bit_count_uint(modulus, uint64_count); power_of_two_minus_one_ = get_power_of_two_minus_one_uint(modulus, uint64_count); if (is_inverse_small(modulus, significant_bit_count_)) { // Calculate inverse modulus (clipped to modulus_bits). inverse_modulus_ = allocate_uint(uint64_count, pool); negate_uint(modulus, uint64_count, inverse_modulus_.get()); filter_highbits_uint(inverse_modulus_.get(), uint64_count, significant_bit_count_ - 1); inverse_significant_bit_count_ = get_significant_bit_count_uint(inverse_modulus_.get(), uint64_count); } }
ChooserPoly ChooserEvaluator::exponentiate(const ChooserPoly &operand, uint64_t exponent) { if (operand.max_coeff_count_ <= 0 || operand.comp_ == nullptr) { throw invalid_argument("operand is not correctly initialized"); } if (exponent == 0 && operand.max_abs_value_.is_zero()) { throw invalid_argument("undefined operation"); } if (exponent == 0) { return ChooserPoly(1, 1, new ExponentiateComputation(*operand.comp_, exponent)); } if (operand.max_abs_value_.is_zero()) { return ChooserPoly(1, 0, new ExponentiateComputation(*operand.comp_, exponent)); } // There is no known closed formula for the growth factor, but we use the asymptotic approximation // k^n * sqrt[6/((k-1)*(k+1)*Pi*n)], where k = max_coeff_count_, n = exponent. uint64_t growth_factor = static_cast<uint64_t>(pow(operand.max_coeff_count_, exponent) * sqrt(6 / ((operand.max_coeff_count_ - 1) * (operand.max_coeff_count_ + 1) * 3.1415 * exponent))); int result_bit_count = static_cast<int>(exponent) * operand.max_abs_value_.significant_bit_count() + get_significant_bit_count(growth_factor) + 1; int result_uint64_count = divide_round_up(result_bit_count, bits_per_uint64); Pointer result_max_abs_value(allocate_uint(result_uint64_count, pool_)); util::exponentiate_uint(operand.max_abs_value_.pointer(), operand.max_abs_value_.uint64_count(), &exponent, 1, result_uint64_count, result_max_abs_value.get(), pool_); ConstPointer temp_pointer(duplicate_uint_if_needed(result_max_abs_value.get(), result_uint64_count, result_uint64_count, true, pool_)); multiply_uint_uint(&growth_factor, 1, temp_pointer.get(), result_uint64_count, result_uint64_count, result_max_abs_value.get()); return ChooserPoly(static_cast<int>(exponent) * (operand.max_coeff_count_ - 1) + 1, BigUInt(result_bit_count, result_max_abs_value.get()), new ExponentiateComputation(*operand.comp_, exponent)); }
void Evaluator::preencrypt(const uint64_t *plain, int plain_coeff_count, int plain_coeff_uint64_count, 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); // Only care about coefficients up till coeff_count. if (plain_coeff_count > coeff_count) { plain_coeff_count = coeff_count; } // Multiply plain by scalar coeff_div_plaintext and reposition if in upper-half. if (plain == destination) { // If plain and destination are same poly, then need another storage for multiply output. Pointer temp(allocate_uint(coeff_uint64_count, pool_)); for (int i = 0; i < plain_coeff_count; ++i) { multiply_uint_uint(plain, plain_coeff_uint64_count, coeff_div_plain_modulus_.pointer(), coeff_uint64_count, coeff_uint64_count, temp.get()); bool is_upper_half = is_greater_than_or_equal_uint_uint(temp.get(), upper_half_threshold_.pointer(), coeff_uint64_count); if (is_upper_half) { add_uint_uint(temp.get(), upper_half_increment_.pointer(), coeff_uint64_count, destination); } else { set_uint_uint(temp.get(), coeff_uint64_count, destination); } plain += plain_coeff_uint64_count; destination += coeff_uint64_count; } } else { for (int i = 0; i < plain_coeff_count; ++i) { multiply_uint_uint(plain, plain_coeff_uint64_count, coeff_div_plain_modulus_.pointer(), coeff_uint64_count, coeff_uint64_count, destination); bool is_upper_half = is_greater_than_or_equal_uint_uint(destination, upper_half_threshold_.pointer(), coeff_uint64_count); if (is_upper_half) { add_uint_uint(destination, upper_half_increment_.pointer(), coeff_uint64_count, destination); } plain += plain_coeff_uint64_count; destination += coeff_uint64_count; } } // Zero any remaining coefficients. for (int i = plain_coeff_count; i < coeff_count; ++i) { set_zero_uint(coeff_uint64_count, destination); destination += coeff_uint64_count; } }
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()); } }
KeyGenerator::KeyGenerator(const EncryptionParameters &parms) : poly_modulus_(parms.poly_modulus()), coeff_modulus_(parms.coeff_modulus()), plain_modulus_(parms.plain_modulus()), noise_standard_deviation_(parms.noise_standard_deviation()), noise_max_deviation_(parms.noise_max_deviation()), decomposition_bit_count_(parms.decomposition_bit_count()), mode_(parms.mode()), random_generator_(parms.random_generator() != nullptr ? parms.random_generator()->create() : UniformRandomGeneratorFactory::default_factory()->create()) { // Verify required parameters are non-zero and non-nullptr. if (poly_modulus_.is_zero()) { throw invalid_argument("poly_modulus cannot be zero"); } if (coeff_modulus_.is_zero()) { throw invalid_argument("coeff_modulus cannot be zero"); } if (plain_modulus_.is_zero()) { throw invalid_argument("plain_modulus cannot be zero"); } if (noise_standard_deviation_ < 0) { throw invalid_argument("noise_standard_deviation must be non-negative"); } if (noise_max_deviation_ < 0) { throw invalid_argument("noise_max_deviation must be non-negative"); } if (decomposition_bit_count_ <= 0) { throw invalid_argument("decomposition_bit_count must be positive"); } // Verify parameters. if (plain_modulus_ >= coeff_modulus_) { throw invalid_argument("plain_modulus must be smaller than coeff_modulus"); } if (!are_poly_coefficients_less_than(poly_modulus_, coeff_modulus_)) { throw invalid_argument("poly_modulus cannot have coefficients larger than coeff_modulus"); } // Resize encryption parameters to consistent size. int coeff_count = poly_modulus_.significant_coeff_count(); int coeff_bit_count = coeff_modulus_.significant_bit_count(); int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64); if (poly_modulus_.coeff_count() != coeff_count || poly_modulus_.coeff_bit_count() != coeff_bit_count) { poly_modulus_.resize(coeff_count, coeff_bit_count); } if (coeff_modulus_.bit_count() != coeff_bit_count) { coeff_modulus_.resize(coeff_bit_count); } if (plain_modulus_.bit_count() != coeff_bit_count) { plain_modulus_.resize(coeff_bit_count); } if (decomposition_bit_count_ > coeff_bit_count) { decomposition_bit_count_ = coeff_bit_count; } // Calculate -1 (mod coeff_modulus). coeff_modulus_minus_one_.resize(coeff_bit_count); decrement_uint(coeff_modulus_.pointer(), coeff_uint64_count, coeff_modulus_minus_one_.pointer()); // Initialize public and secret key. public_key_.resize(coeff_count, coeff_bit_count); secret_key_.resize(coeff_count, coeff_bit_count); // Initialize evaluation keys. int evaluation_key_count = 0; Pointer evaluation_factor(allocate_uint(coeff_uint64_count, pool_)); set_uint(1, coeff_uint64_count, evaluation_factor.get()); while (!is_zero_uint(evaluation_factor.get(), coeff_uint64_count) && is_less_than_uint_uint(evaluation_factor.get(), coeff_modulus_.pointer(), coeff_uint64_count)) { left_shift_uint(evaluation_factor.get(), decomposition_bit_count_, coeff_uint64_count, evaluation_factor.get()); evaluation_key_count++; } evaluation_keys_.resize(evaluation_key_count); for (int i = 0; i < evaluation_key_count; ++i) { evaluation_keys_[i].resize(coeff_count, coeff_bit_count); } // Initialize moduli. polymod_ = PolyModulus(poly_modulus_.pointer(), coeff_count, coeff_uint64_count); mod_ = Modulus(coeff_modulus_.pointer(), coeff_uint64_count, pool_); }
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 exponentiate_poly(const std::uint64_t *poly, int poly_coeff_count, int poly_coeff_uint64_count, const uint64_t *exponent, int exponent_uint64_count, int result_coeff_count, int result_coeff_uint64_count, std::uint64_t *result, MemoryPool &pool) { #ifdef SEAL_DEBUG if (poly == nullptr) { throw invalid_argument("poly"); } if (poly_coeff_count <= 0) { throw invalid_argument("poly_coeff_count"); } if (poly_coeff_count <= 0) { throw invalid_argument("poly_coeff_uint64_count"); } if (exponent == nullptr) { throw invalid_argument("exponent"); } if (exponent_uint64_count <= 0) { throw invalid_argument("exponent_uint64_count"); } if (result == nullptr) { throw invalid_argument("result"); } 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 // Fast cases if (is_zero_uint(exponent, exponent_uint64_count)) { set_zero_poly(result_coeff_count, result_coeff_uint64_count, result); *result = 1; return; } if (is_equal_uint(exponent, exponent_uint64_count, 1)) { set_poly_poly(poly, poly_coeff_count, poly_coeff_uint64_count, result_coeff_count, result_coeff_uint64_count, result); return; } // Need to make a copy of exponent Pointer exponent_copy(allocate_uint(exponent_uint64_count, pool)); set_uint_uint(exponent, exponent_uint64_count, exponent_copy.get()); // Perform binary exponentiation. Pointer big_alloc(allocate_uint((static_cast<int64_t>(result_coeff_count) + result_coeff_count + result_coeff_count) * result_coeff_uint64_count, pool)); uint64_t *powerptr = big_alloc.get(); uint64_t *productptr = get_poly_coeff(powerptr, result_coeff_count, result_coeff_uint64_count); uint64_t *intermediateptr = get_poly_coeff(productptr, result_coeff_count, result_coeff_uint64_count); set_poly_poly(poly, poly_coeff_count, poly_coeff_uint64_count, result_coeff_count, result_coeff_uint64_count, powerptr); set_zero_poly(result_coeff_count, result_coeff_uint64_count, intermediateptr); *intermediateptr = 1; // Initially: power = operand and intermediate = 1, product is not initialized. while (true) { if ((*exponent_copy.get() % 2) == 1) { multiply_poly_poly(powerptr, result_coeff_count, result_coeff_uint64_count, intermediateptr, result_coeff_count, result_coeff_uint64_count, result_coeff_count, result_coeff_uint64_count, productptr, pool); swap(productptr, intermediateptr); } right_shift_uint(exponent_copy.get(), 1, exponent_uint64_count, exponent_copy.get()); if (is_zero_uint(exponent_copy.get(), exponent_uint64_count)) { break; } multiply_poly_poly(powerptr, result_coeff_count, result_coeff_uint64_count, powerptr, result_coeff_count, result_coeff_uint64_count, result_coeff_count, result_coeff_uint64_count, productptr, pool); swap(productptr, powerptr); } set_poly_poly(intermediateptr, result_coeff_count, result_coeff_uint64_count, result); }
void multiply_poly_poly(const uint64_t *operand1, int operand1_coeff_count, int operand1_coeff_uint64_count, const uint64_t *operand2, int operand2_coeff_count, int operand2_coeff_uint64_count, int result_coeff_count, int result_coeff_uint64_count, uint64_t *result, MemoryPool &pool) { #ifdef SEAL_DEBUG if (operand1 == nullptr && operand1_coeff_count > 0 && operand1_coeff_uint64_count > 0) { throw invalid_argument("operand1"); } if (operand1_coeff_count < 0) { throw invalid_argument("operand1_coeff_count"); } if (operand1_coeff_uint64_count < 0) { throw invalid_argument("operand1_coeff_uint64_count"); } if (operand2 == nullptr && operand2_coeff_count > 0 && operand2_coeff_uint64_count > 0) { throw invalid_argument("operand2"); } if (operand2_coeff_count < 0) { throw invalid_argument("operand2_coeff_count"); } if (operand2_coeff_uint64_count < 0) { throw invalid_argument("operand2_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"); } if (result == nullptr && result_coeff_count > 0 && result_coeff_uint64_count > 0) { throw invalid_argument("result"); } if (result != nullptr && (operand1 == result || operand2 == result)) { throw invalid_argument("result cannot point to the same value as operand1 or operand2"); } #endif Pointer intermediate(allocate_uint(result_coeff_uint64_count, pool)); // Clear product. set_zero_poly(result_coeff_count, result_coeff_uint64_count, result); operand1_coeff_count = get_significant_coeff_count_poly( operand1, operand1_coeff_count, operand1_coeff_uint64_count); operand2_coeff_count = get_significant_coeff_count_poly( operand2, operand2_coeff_count, operand2_coeff_uint64_count); for (int operand1_index = 0; operand1_index < operand1_coeff_count; operand1_index++) { const uint64_t *operand1_coeff = get_poly_coeff( operand1, operand1_index, operand1_coeff_uint64_count); for (int operand2_index = 0; operand2_index < operand2_coeff_count; operand2_index++) { int product_coeff_index = operand1_index + operand2_index; if (product_coeff_index >= result_coeff_count) { break; } const uint64_t *operand2_coeff = get_poly_coeff( operand2, operand2_index, operand2_coeff_uint64_count); multiply_uint_uint(operand1_coeff, operand1_coeff_uint64_count, operand2_coeff, operand2_coeff_uint64_count, result_coeff_uint64_count, intermediate.get()); uint64_t *result_coeff = get_poly_coeff( result, product_coeff_index, result_coeff_uint64_count); add_uint_uint(result_coeff, intermediate.get(), result_coeff_uint64_count, result_coeff); } } }
HkeyGen::HkeyGen(const EncryptionParameters &parms, const BigPoly &secret_key) : poly_modulus_(parms.poly_modulus()), coeff_modulus_(parms.coeff_modulus()), plain_modulus_(parms.plain_modulus()), secret_key_(secret_key), orig_plain_modulus_bit_count_(parms.plain_modulus().significant_bit_count()) { // Verify required parameters are non-zero and non-nullptr. if (poly_modulus_.is_zero()) { throw invalid_argument("poly_modulus cannot be zero"); } if (coeff_modulus_.is_zero()) { throw invalid_argument("coeff_modulus cannot be zero"); } if (plain_modulus_.is_zero()) { throw invalid_argument("plain_modulus cannot be zero"); } if (secret_key_.is_zero()) { throw invalid_argument("secret_key cannot be zero"); } // Verify parameters. if (plain_modulus_ >= coeff_modulus_) { throw invalid_argument("plain_modulus must be smaller than coeff_modulus"); } if (!are_poly_coefficients_less_than(poly_modulus_, coeff_modulus_)) { throw invalid_argument("poly_modulus cannot have coefficients larger than coeff_modulus"); } // Resize encryption parameters to consistent size. int coeff_count = poly_modulus_.significant_coeff_count(); int coeff_bit_count = coeff_modulus_.significant_bit_count(); int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64); if (poly_modulus_.coeff_count() != coeff_count || poly_modulus_.coeff_bit_count() != coeff_bit_count) { poly_modulus_.resize(coeff_count, coeff_bit_count); } if (coeff_modulus_.bit_count() != coeff_bit_count) { coeff_modulus_.resize(coeff_bit_count); } if (plain_modulus_.bit_count() != coeff_bit_count) { plain_modulus_.resize(coeff_bit_count); } if (secret_key_.coeff_count() != coeff_count || secret_key_.coeff_bit_count() != coeff_bit_count || 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"); } // Set the secret_key_array to have size 1 (first power of secret) secret_key_array_.resize(1, coeff_count, coeff_bit_count); set_poly_poly(secret_key_.pointer(), coeff_count, coeff_uint64_count, secret_key_array_.pointer(0)); MemoryPool &pool = *MemoryPool::default_pool(); // Calculate coeff_modulus / plain_modulus. coeff_div_plain_modulus_.resize(coeff_bit_count); Pointer temp(allocate_uint(coeff_uint64_count, pool)); divide_uint_uint(coeff_modulus_.pointer(), plain_modulus_.pointer(), coeff_uint64_count, coeff_div_plain_modulus_.pointer(), temp.get(), pool); // Calculate coeff_modulus / plain_modulus / 2. coeff_div_plain_modulus_div_two_.resize(coeff_bit_count); right_shift_uint(coeff_div_plain_modulus_.pointer(), 1, coeff_uint64_count, coeff_div_plain_modulus_div_two_.pointer()); // Calculate coeff_modulus / 2. upper_half_threshold_.resize(coeff_bit_count); half_round_up_uint(coeff_modulus_.pointer(), coeff_uint64_count, upper_half_threshold_.pointer()); // Calculate upper_half_increment. upper_half_increment_.resize(coeff_bit_count); multiply_truncate_uint_uint(plain_modulus_.pointer(), coeff_div_plain_modulus_.pointer(), coeff_uint64_count, upper_half_increment_.pointer()); sub_uint_uint(coeff_modulus_.pointer(), upper_half_increment_.pointer(), coeff_uint64_count, upper_half_increment_.pointer()); // Initialize moduli. polymod_ = PolyModulus(poly_modulus_.pointer(), coeff_count, coeff_uint64_count); mod_ = Modulus(coeff_modulus_.pointer(), coeff_uint64_count, pool); }
Evaluator::Evaluator(const EncryptionParameters &parms, const EvaluationKeys &evaluation_keys) : poly_modulus_(parms.poly_modulus()), coeff_modulus_(parms.coeff_modulus()), plain_modulus_(parms.plain_modulus()), decomposition_bit_count_(parms.decomposition_bit_count()), evaluation_keys_(evaluation_keys), mode_(parms.mode()) { // Verify required parameters are non-zero and non-nullptr. if (poly_modulus_.is_zero()) { throw invalid_argument("poly_modulus cannot be zero"); } if (coeff_modulus_.is_zero()) { throw invalid_argument("coeff_modulus cannot be zero"); } if (plain_modulus_.is_zero()) { throw invalid_argument("plain_modulus cannot be zero"); } if (decomposition_bit_count_ <= 0) { throw invalid_argument("decomposition_bit_count must be positive"); } if (evaluation_keys_.count() == 0) { throw invalid_argument("evaluation_keys cannot be empty"); } // Verify parameters. if (plain_modulus_ >= coeff_modulus_) { throw invalid_argument("plain_modulus must be smaller than coeff_modulus"); } if (!are_poly_coefficients_less_than(poly_modulus_, coeff_modulus_)) { throw invalid_argument("poly_modulus cannot have coefficients larger than coeff_modulus"); } // Resize encryption parameters to consistent size. int coeff_count = poly_modulus_.significant_coeff_count(); int coeff_bit_count = coeff_modulus_.significant_bit_count(); int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64); if (poly_modulus_.coeff_count() != coeff_count || poly_modulus_.coeff_bit_count() != coeff_bit_count) { poly_modulus_.resize(coeff_count, coeff_bit_count); } if (coeff_modulus_.bit_count() != coeff_bit_count) { coeff_modulus_.resize(coeff_bit_count); } if (plain_modulus_.bit_count() != coeff_bit_count) { plain_modulus_.resize(coeff_bit_count); } if (decomposition_bit_count_ > coeff_bit_count) { decomposition_bit_count_ = coeff_bit_count; } // Determine correct number of evaluation keys. int evaluation_key_count = 0; Pointer evaluation_factor(allocate_uint(coeff_uint64_count, pool_)); set_uint(1, coeff_uint64_count, evaluation_factor.get()); while (!is_zero_uint(evaluation_factor.get(), coeff_uint64_count) && is_less_than_uint_uint(evaluation_factor.get(), coeff_modulus_.pointer(), coeff_uint64_count)) { left_shift_uint(evaluation_factor.get(), decomposition_bit_count_, coeff_uint64_count, evaluation_factor.get()); evaluation_key_count++; } // Verify evaluation keys. if (evaluation_keys_.count() != evaluation_key_count) { throw invalid_argument("evaluation_keys is not valid for encryption parameters"); } for (int i = 0; i < evaluation_keys_.count(); ++i) { BigPoly &evaluation_key = evaluation_keys_[i]; if (evaluation_key.coeff_count() != coeff_count || evaluation_key.coeff_bit_count() != coeff_bit_count || evaluation_key.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(evaluation_key, coeff_modulus_)) { throw invalid_argument("evaluation_keys is not valid for encryption parameters"); } } // Calculate coeff_modulus / plain_modulus. coeff_div_plain_modulus_.resize(coeff_bit_count); Pointer temp(allocate_uint(coeff_uint64_count, pool_)); divide_uint_uint(coeff_modulus_.pointer(), plain_modulus_.pointer(), coeff_uint64_count, coeff_div_plain_modulus_.pointer(), temp.get(), pool_); // Calculate (plain_modulus + 1) / 2. plain_upper_half_threshold_.resize(coeff_bit_count); half_round_up_uint(plain_modulus_.pointer(), coeff_uint64_count, plain_upper_half_threshold_.pointer()); // Calculate coeff_modulus - plain_modulus. plain_upper_half_increment_.resize(coeff_bit_count); sub_uint_uint(coeff_modulus_.pointer(), plain_modulus_.pointer(), coeff_uint64_count, plain_upper_half_increment_.pointer()); // Calculate (plain_modulus + 1) / 2 * coeff_div_plain_modulus. upper_half_threshold_.resize(coeff_bit_count); multiply_truncate_uint_uint(plain_upper_half_threshold_.pointer(), coeff_div_plain_modulus_.pointer(), coeff_uint64_count, upper_half_threshold_.pointer()); // Calculate upper_half_increment. upper_half_increment_.resize(coeff_bit_count); multiply_truncate_uint_uint(plain_modulus_.pointer(), coeff_div_plain_modulus_.pointer(), coeff_uint64_count, upper_half_increment_.pointer()); sub_uint_uint(coeff_modulus_.pointer(), upper_half_increment_.pointer(), coeff_uint64_count, upper_half_increment_.pointer()); // Widen coeff modulus. int product_coeff_bit_count = coeff_bit_count + coeff_bit_count + get_significant_bit_count(static_cast<uint64_t>(coeff_count)); int plain_modulus_bit_count = plain_modulus_.significant_bit_count(); int wide_bit_count = product_coeff_bit_count + plain_modulus_bit_count; int wide_uint64_count = divide_round_up(wide_bit_count, bits_per_uint64); wide_coeff_modulus_.resize(wide_bit_count); wide_coeff_modulus_ = coeff_modulus_; // Calculate wide_coeff_modulus_ / 2. wide_coeff_modulus_div_two_.resize(wide_bit_count); right_shift_uint(wide_coeff_modulus_.pointer(), 1, wide_uint64_count, wide_coeff_modulus_div_two_.pointer()); // Initialize moduli. polymod_ = PolyModulus(poly_modulus_.pointer(), coeff_count, coeff_uint64_count); if (mode_ == TEST_MODE) { mod_ = Modulus(plain_modulus_.pointer(), coeff_uint64_count, pool_); } else { mod_ = Modulus(coeff_modulus_.pointer(), coeff_uint64_count, pool_); } }
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); } }