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_); }
bool ChooserEvaluator::select_parameters(const std::vector<ChooserPoly> &operands, double noise_standard_deviation, double noise_max_deviation, const std::map<int, BigUInt> ¶meter_options, EncryptionParameters &destination) { if (noise_standard_deviation < 0) { throw invalid_argument("noise_standard_deviation can not be negative"); } if (noise_max_deviation < 0) { throw invalid_argument("noise_max_deviation can not be negative"); } if (parameter_options.size() == 0) { throw invalid_argument("parameter_options must contain at least one entry"); } if (operands.empty()) { throw invalid_argument("operands cannot be empty"); } int largest_bit_count = 0; int largest_coeff_count = 0; for (vector<ChooserPoly>::size_type i = 0; i < operands.size(); ++i) { if (operands[i].comp_ == nullptr) { throw logic_error("no operation history to simulate"); } int current_bit_count = operands[i].max_abs_value_.significant_bit_count(); largest_bit_count = (current_bit_count > largest_bit_count) ? current_bit_count : largest_bit_count; int current_coeff_count = operands[i].max_coeff_count_; largest_coeff_count = (current_coeff_count > largest_coeff_count) ? current_coeff_count : largest_coeff_count; } // We restrict to plain moduli that are powers of two. Here largest_bit_count is the largest positive // coefficient that we can expect to appear. Thus, we need one more bit. destination.plain_modulus() = 1; destination.plain_modulus() <<= largest_bit_count; bool found_good_parms = false; map<int, BigUInt>::const_iterator iter = parameter_options.begin(); while (iter != parameter_options.end() && !found_good_parms) { int dimension = iter->first; if (dimension < 512 || (dimension & (dimension - 1)) != 0) { throw invalid_argument("parameter_options keys invalid"); } if (dimension > largest_coeff_count && destination.plain_modulus() < iter->second) { // Set the polynomial destination.coeff_modulus() = iter->second; destination.poly_modulus().resize(dimension + 1, 1); destination.poly_modulus().set_zero(); destination.poly_modulus()[0] = 1; destination.poly_modulus()[dimension] = 1; // The bound needed for GapSVP->search-LWE reduction //parms.noise_standard_deviation() = round(sqrt(dimension / (2 * 3.1415)) + 0.5); // Use constant (small) standard deviation. destination.noise_standard_deviation() = noise_standard_deviation; // We truncate the gaussian at noise_max_deviation. destination.noise_max_deviation() = noise_max_deviation; // Start initially with the maximum decomposition_bit_count, then decrement until decrypts(). destination.decomposition_bit_count() = destination.coeff_modulus().significant_bit_count(); // We bound the decomposition bit count value by 1/8 of the maximum. A too small // decomposition bit count slows down multiplication significantly. This is not an // issue when the user wants to use multiply_norelin() instead of multiply(), as it // only affects the relinearization step. The fraction 1/8 is not an optimal choice // in any sense, but was rather arbitrarily chosen. An expert user might want to tweak this // value to be smaller or larger depending on their use case. // To do: Figure out a somewhat optimal bound. int min_decomposition_bit_count = destination.coeff_modulus().significant_bit_count() / 8; while (!found_good_parms && destination.decomposition_bit_count() > min_decomposition_bit_count) { found_good_parms = true; for (vector<ChooserPoly>::size_type i = 0; i < operands.size(); ++i) { // If one of the operands does not decrypt, set found_good_parms to false. found_good_parms = operands[i].simulate(destination).decrypts() ? found_good_parms : false; } if (!found_good_parms) { --destination.decomposition_bit_count(); } else { // We found some good parameters. But in fact we can still decrease the decomposition count // a little bit without hurting performance at all. int old_dbc = destination.decomposition_bit_count(); int num_parts = destination.coeff_modulus().significant_bit_count() / old_dbc + (destination.coeff_modulus().significant_bit_count() % old_dbc != 0); destination.decomposition_bit_count() = destination.coeff_modulus().significant_bit_count() / num_parts + (destination.coeff_modulus().significant_bit_count() % num_parts != 0); } } } // This dimension/coeff_modulus are to small. Move on to the next pair. ++iter; } if (!found_good_parms) { destination = EncryptionParameters(); } return found_good_parms; }
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); }
bool ChooserEvaluator::select_parameters( const std::vector<ChooserPoly> &operands, int budget_gap, double noise_standard_deviation, const map<int, vector<SmallModulus> > &coeff_modulus_options, EncryptionParameters &destination) { if (budget_gap < 0) { throw std::invalid_argument("budget_gap cannot be negative"); } if (noise_standard_deviation < 0) { throw invalid_argument("noise_standard_deviation can not be negative"); } if (coeff_modulus_options.size() == 0) { throw invalid_argument("parameter_options must contain at least one entry"); } if (operands.empty()) { throw invalid_argument("operands cannot be empty"); } int largest_bit_count = 0; int largest_coeff_count = 0; for (size_t i = 0; i < operands.size(); i++) { if (operands[i].comp_ == nullptr) { throw logic_error("no operation history to simulate"); } int current_bit_count = get_significant_bit_count(operands[i].max_abs_value_); largest_bit_count = (current_bit_count > largest_bit_count) ? current_bit_count : largest_bit_count; int current_coeff_count = operands[i].max_coeff_count_; largest_coeff_count = (current_coeff_count > largest_coeff_count) ? current_coeff_count : largest_coeff_count; } // We restrict to plain moduli that are powers of two. Here largest_bit_count // is the largest positive coefficient that we can expect to appear. Thus, we // need one more bit. uint64_t new_plain_modulus; if (largest_bit_count >= SEAL_USER_MODULO_BIT_BOUND) { // The plain_modulus needed is too big return false; } new_plain_modulus = 1ULL << largest_bit_count; destination.set_plain_modulus(new_plain_modulus); bool found_good_parms = false; map<int, vector<SmallModulus> >::const_iterator iter = coeff_modulus_options.begin(); while (iter != coeff_modulus_options.end() && !found_good_parms) { int dimension = iter->first; if (dimension < 512 || (dimension & (dimension - 1)) != 0) { throw invalid_argument("coeff_modulus_options keys invalid"); } int coeff_bit_count = 0; for(auto mod : iter->second) { coeff_bit_count += mod.bit_count(); } if (dimension > largest_coeff_count && coeff_bit_count > destination.plain_modulus().bit_count()) { // Set the polynomial destination.set_coeff_modulus(iter->second); BigPoly new_poly_modulus(dimension + 1, 1); new_poly_modulus.set_zero(); new_poly_modulus[0] = 1; new_poly_modulus[dimension] = 1; destination.set_poly_modulus(new_poly_modulus); // The bound needed for GapSVP->search-LWE reduction //parms.noise_standard_deviation() = round(sqrt(dimension / (2 * 3.1415)) + 0.5); // Use constant (small) standard deviation. destination.set_noise_standard_deviation(noise_standard_deviation); found_good_parms = true; for (size_t i = 0; i < operands.size(); i++) { // If one of the operands does not decrypt, set found_good_parms to false. found_good_parms = operands[i].simulate(destination).decrypts(budget_gap) ? found_good_parms : false; } } // This dimension/coeff_modulus are to small. Move on to the next pair. iter++; } if (!found_good_parms) { destination = EncryptionParameters(); } return found_good_parms; }
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_); } }