void HkeyGen::compute_secret_key_array(int max_power)
    {
        if (max_power < 1)
        {
            throw invalid_argument("max_power cannot be less than 1");
        }

        int old_count = secret_key_array_.size();
        int new_count = max(max_power, secret_key_array_.size());

        if (old_count == new_count)
        {
            return;
        }

        int coeff_count = poly_modulus_.coeff_count();
        int coeff_bit_count = coeff_modulus_.bit_count();
        int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64);

        // Compute powers of secret key until max_power
        secret_key_array_.resize(new_count, coeff_count, coeff_bit_count);

        MemoryPool &pool = *MemoryPool::default_pool();

        int poly_ptr_increment = coeff_count * coeff_uint64_count;
        uint64_t *prev_poly_ptr = secret_key_array_.pointer(old_count - 1);
        uint64_t *next_poly_ptr = prev_poly_ptr + poly_ptr_increment;
        for (int i = old_count; i < new_count; ++i)
        {
            multiply_poly_poly_polymod_coeffmod(prev_poly_ptr, secret_key_.pointer(), polymod_, mod_, next_poly_ptr, pool);
            prev_poly_ptr = next_poly_ptr;
            next_poly_ptr += poly_ptr_increment;
        }
    }
    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 Evaluator::multiply(const BigPoly &encrypted1, const BigPoly &encrypted2, BigPoly &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);

        // Verify parameters.
        if (encrypted1.coeff_count() != coeff_count || encrypted1.coeff_bit_count() != coeff_bit_count)
        {
            throw invalid_argument("encrypted1 is not valid for encryption parameters");
        }
        if (encrypted2.coeff_count() != coeff_count || encrypted2.coeff_bit_count() != coeff_bit_count)
        {
            throw invalid_argument("encrypted2 is not valid for encryption parameters");
        }
#ifdef _DEBUG
        if (encrypted1.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(encrypted1, coeff_modulus_))
        {
            throw invalid_argument("encrypted1 is not valid for encryption parameters");
        }
        if (encrypted2.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(encrypted2, coeff_modulus_))
        {
            throw invalid_argument("encrypted2 is not valid for encryption parameters");
        }
#endif
        if (destination.coeff_count() != coeff_count || destination.coeff_bit_count() != coeff_bit_count)
        {
            destination.resize(coeff_count, coeff_bit_count);
        }

        // Handle test-mode case.
        if (mode_ == TEST_MODE)
        {
            // Get pointer to inputs (duplicated if needed).
            ConstPointer encrypted1ptr = duplicate_poly_if_needed(encrypted1, encrypted1.pointer() == destination.pointer(), pool_);
            ConstPointer encrypted2ptr = duplicate_poly_if_needed(encrypted2, encrypted2.pointer() == destination.pointer(), pool_);

            multiply_poly_poly_polymod_coeffmod(encrypted1ptr.get(), encrypted2ptr.get(), polymod_, mod_, destination.pointer(), pool_);
            return;
        }

        // Multiply encrypted polynomials and perform key switching.
        Pointer product(allocate_poly(coeff_count, coeff_uint64_count, pool_));
        multiply(encrypted1.pointer(), encrypted2.pointer(), product.get());
        relinearize(product.get(), destination.pointer());
    }
Beispiel #4
0
    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());
        }
    }
Beispiel #5
0
    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 Evaluator::multiply_plain(const BigPoly &encrypted1, const BigPoly &plain2, BigPoly &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);

        // Verify parameters.
        if (encrypted1.coeff_count() != coeff_count || encrypted1.coeff_bit_count() != coeff_bit_count)
        {
            throw invalid_argument("encrypted1 is not valid for encryption parameters");
        }
#ifdef _DEBUG
        if (encrypted1.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(encrypted1, coeff_modulus_))
        {
            throw invalid_argument("encrypted1 is not valid for encryption parameters");
        }
        if (plain2.significant_coeff_count() >= coeff_count || !are_poly_coefficients_less_than(plain2, plain_modulus_))
        {
            throw invalid_argument("plain2 is too large to be represented by encryption parameters");
        }
#endif
        if (destination.coeff_count() != coeff_count || destination.coeff_bit_count() != coeff_bit_count)
        {
            destination.resize(coeff_count, coeff_bit_count);
        }

        // Get pointer to inputs (duplicated if needed).
        ConstPointer encrypted1ptr = duplicate_poly_if_needed(encrypted1, encrypted1.pointer() == destination.pointer(), pool_);

        // Handle test-mode case.
        if (mode_ == TEST_MODE)
        {
            // Get pointer to inputs (duplicated and resized if needed).
            ConstPointer plain2ptr = duplicate_poly_if_needed(plain2, coeff_count, coeff_uint64_count, plain2.pointer() == destination.pointer(), pool_);

            // Resize second operand if needed.
            multiply_poly_poly_polymod_coeffmod(encrypted1ptr.get(), plain2ptr.get(), polymod_, mod_, destination.pointer(), pool_);
            return;
        }

        // Reposition coefficients.
        Pointer moved2ptr(allocate_poly(coeff_count, coeff_uint64_count, pool_));
        int plain_coeff_count = min(plain2.significant_coeff_count(), coeff_count);
        int plain2_coeff_uint64_count = plain2.coeff_uint64_count();
        const uint64_t *plain2_coeff = plain2.pointer();
        uint64_t *moved2_coeff = moved2ptr.get();
        for (int i = 0; i < plain_coeff_count; ++i)
        {
            set_uint_uint(plain2_coeff, plain2_coeff_uint64_count, coeff_uint64_count, moved2_coeff);
            bool is_upper_half = is_greater_than_or_equal_uint_uint(moved2_coeff, plain_upper_half_threshold_.pointer(), coeff_uint64_count);
            if (is_upper_half)
            {
                add_uint_uint(moved2_coeff, plain_upper_half_increment_.pointer(), coeff_uint64_count, moved2_coeff);
            }
            moved2_coeff += coeff_uint64_count;
            plain2_coeff += plain2_coeff_uint64_count;
        }
        for (int i = plain_coeff_count; i < coeff_count; ++i)
        {
            set_zero_uint(coeff_uint64_count, moved2_coeff);
            moved2_coeff += coeff_uint64_count;
        }

        // Use normal polynomial multiplication.
        multiply_poly_poly_polymod_coeffmod(encrypted1ptr.get(), moved2ptr.get(), polymod_, mod_, destination.pointer(), pool_);
    }