Beispiel #1
0
    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> &parameter_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;
    }