ChooserPoly ChooserEvaluator::multiply_plain(const ChooserPoly &operand, int plain_max_coeff_count, const BigUInt &plain_max_abs_value) { if (operand.max_coeff_count_ <= 0 || operand.comp_ == nullptr) { throw invalid_argument("operand is not correctly initialized"); } if (plain_max_coeff_count <= 0) { throw invalid_argument("plain_max_coeff_count must be positive"); } if (plain_max_abs_value.is_zero()) { return ChooserPoly(1, 0, new MultiplyPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); } if (operand.max_abs_value_.is_zero()) { return ChooserPoly(1, 0, new MultiplyPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); } uint64_t growth_factor = min(operand.max_coeff_count_, plain_max_coeff_count); int prod_bit_count = operand.max_abs_value_.significant_bit_count() + plain_max_abs_value.significant_bit_count() + get_significant_bit_count(growth_factor) + 1; int prod_uint64_count = divide_round_up(prod_bit_count, bits_per_uint64); Pointer prod_max_abs_value(allocate_zero_uint(prod_uint64_count, pool_)); ConstPointer wide_operand_max_abs_value(duplicate_uint_if_needed(operand.max_abs_value_.pointer(), operand.max_abs_value_.uint64_count(), prod_uint64_count, false, pool_)); multiply_uint_uint(&growth_factor, 1, plain_max_abs_value.pointer(), plain_max_abs_value.uint64_count(), prod_uint64_count, prod_max_abs_value.get()); ConstPointer temp_pointer(duplicate_uint_if_needed(prod_max_abs_value.get(), prod_uint64_count, prod_uint64_count, true, pool_)); multiply_uint_uint(wide_operand_max_abs_value.get(), prod_uint64_count, temp_pointer.get(), prod_uint64_count, prod_uint64_count, prod_max_abs_value.get()); return ChooserPoly(operand.max_coeff_count_ + plain_max_coeff_count - 1, BigUInt(prod_bit_count, prod_max_abs_value.get()), new MultiplyPlainComputation(*operand.comp_, plain_max_coeff_count, plain_max_abs_value)); }
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; } }
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)); }
ChooserPoly ChooserEvaluator::multiply_many(const vector<ChooserPoly> &operands) { if (operands.empty()) { throw invalid_argument("operands vector can not be empty"); } int prod_max_coeff_count = 1; uint64_t growth_factor = 1; int prod_max_abs_value_bit_count = 1; vector<Computation*> comps; for (vector<ChooserPoly>::size_type i = 0; i < operands.size(); ++i) { // Throw if any of the operands is not initialized correctly if (operands[i].max_coeff_count_ <= 0 || operands[i].comp_ == nullptr) { throw invalid_argument("input operand is not correctly initialized"); } // Return early if the product is trivially zero if (operands[i].max_abs_value_.is_zero()) { return ChooserPoly(1, 0, new MultiplyManyComputation(comps)); } prod_max_coeff_count += operands[i].max_coeff_count_ - 1; prod_max_abs_value_bit_count += operands[i].max_abs_value().significant_bit_count(); growth_factor *= (i == 0 ? 1 : min(operands[i].max_coeff_count_, prod_max_coeff_count)); comps.push_back(operands[i].comp_); } prod_max_abs_value_bit_count += get_significant_bit_count(growth_factor); int prod_max_abs_value_uint64_count = divide_round_up(prod_max_abs_value_bit_count, bits_per_uint64); Pointer prod_max_abs_value(allocate_zero_uint(prod_max_abs_value_uint64_count, pool_)); *prod_max_abs_value.get() = growth_factor; for (vector<ChooserPoly>::size_type i = 0; i < operands.size(); ++i) { ConstPointer temp_pointer(duplicate_uint_if_needed(prod_max_abs_value.get(), prod_max_abs_value_uint64_count, prod_max_abs_value_uint64_count, true, pool_)); multiply_uint_uint(temp_pointer.get(), prod_max_abs_value_uint64_count, operands[i].max_abs_value_.pointer(), operands[i].max_abs_value_.uint64_count(), prod_max_abs_value_uint64_count, prod_max_abs_value.get()); } return ChooserPoly(prod_max_coeff_count, BigUInt(prod_max_abs_value_bit_count, prod_max_abs_value.get()), new MultiplyManyComputation(comps)); }
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); } }