void KeyGenerator::set_poly_coeffs_normal(uint64_t *poly) const { 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); if (noise_standard_deviation_ == 0 || noise_max_deviation_ == 0) { set_zero_poly(coeff_count, coeff_uint64_count, poly); return; } RandomToStandardAdapter engine(random_generator_.get()); ClippedNormalDistribution random(0, noise_standard_deviation_, noise_max_deviation_); for (int i = 0; i < coeff_count - 1; ++i) { int64_t noise = static_cast<int64_t>(random(engine)); if (noise > 0) { set_uint(static_cast<uint64_t>(noise), coeff_uint64_count, poly); } else if (noise < 0) { noise = -noise; set_uint(static_cast<uint64_t>(noise), coeff_uint64_count, poly); sub_uint_uint(coeff_modulus_.pointer(), poly, coeff_uint64_count, poly); } else { set_zero_uint(coeff_uint64_count, poly); } poly += coeff_uint64_count; } set_zero_uint(coeff_uint64_count, poly); }
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 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 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); } } }
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); } }