Esempio n. 1
0
    BigUInt poly_infty_norm_coeffmod(const BigPoly &poly, const BigUInt &modulus, const MemoryPoolHandle &pool)
    {
        if (modulus.is_zero())
        {
            throw invalid_argument("modulus cannot be zero");
        }
        if (!pool)
        {
            throw invalid_argument("pool is uninitialized");
        }

        if (poly.is_zero())
        {
            return BigUInt();
        }

        int poly_coeff_count = poly.coeff_count();
        int poly_coeff_bit_count = poly.coeff_bit_count();
        int poly_coeff_uint64_count = divide_round_up(poly_coeff_bit_count, bits_per_uint64);

        Modulus mod(modulus.data(), modulus.uint64_count(), pool);
        BigUInt result(modulus.significant_bit_count());
        util::poly_infty_norm_coeffmod(poly.data(), poly_coeff_count, poly_coeff_uint64_count, mod, result.data(), pool);

        return result;
    }
    void Evaluator::negate(const BigPoly &encrypted, 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 (encrypted.coeff_count() != coeff_count || encrypted.coeff_bit_count() != coeff_bit_count)
        {
            throw invalid_argument("encrypted is not valid for encryption parameters");
        }
#ifdef _DEBUG
        if (encrypted.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(encrypted, coeff_modulus_))
        {
            throw invalid_argument("encrypted 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)
        {
            negate_poly_coeffmod(encrypted.pointer(), coeff_count, plain_modulus_.pointer(), coeff_uint64_count, destination.pointer());
            return;
        }

        // Negate polynomial.
        negate_poly_coeffmod(encrypted.pointer(), coeff_count, coeff_modulus_.pointer(), coeff_uint64_count, destination.pointer());
    }
Esempio n. 3
0
    BigUInt poly_infty_norm(const BigPoly &poly)
    {
        if (poly.is_zero())
        {
            return BigUInt();
        }

        int coeff_count = poly.coeff_count();
        int coeff_bit_count = poly.coeff_bit_count();
        int coeff_uint64_count = divide_round_up(coeff_bit_count, bits_per_uint64);

        BigUInt result(coeff_bit_count);
        util::poly_infty_norm(poly.data(), coeff_count, coeff_uint64_count, result.data());

        return result;
    }
Esempio n. 4
0
 BigPoly exponentiate_poly_polymod_coeffmod(const BigPoly &operand, const BigUInt &exponent,
     const BigPoly &poly_modulus, const BigUInt &coeff_modulus, const MemoryPoolHandle &pool)
 {
     BigPoly result(poly_modulus.coeff_count(), coeff_modulus.significant_bit_count());
     exponentiate_poly_polymod_coeffmod(operand, exponent, poly_modulus, coeff_modulus, result, pool);
     return result;
 }
    void Evaluator::add_many(const std::vector<BigPoly> &encrypteds, BigPoly &destination)
    {
        if (encrypteds.empty())
        {
            throw invalid_argument("encrypteds cannot be empty");
        }

        if (destination.coeff_count() != encrypteds[0].coeff_count() || destination.coeff_bit_count() != encrypteds[0].coeff_bit_count())
        {
            destination.resize(encrypteds[0].coeff_count(), encrypteds[0].coeff_bit_count());
        }

        destination = encrypteds[0];
        for (vector<BigPoly>::size_type i = 1; i < encrypteds.size(); ++i)
        {
            add(destination, encrypteds[i], destination);
        }
    }
    void Evaluator::exponentiate_norelin(const BigPoly &encrypted, int exponent, 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 (encrypted.coeff_count() != coeff_count || encrypted.coeff_bit_count() != coeff_bit_count)
        {
            throw invalid_argument("encrypted is not valid for encryption parameters");
        }
#ifdef _DEBUG
        if (encrypted.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(encrypted, coeff_modulus_))
        {
            throw invalid_argument("encrypted is not valid for encryption parameters");
        }
#endif
        if (exponent < 0)
        {
            throw invalid_argument("exponent must be non-negative");
        }

        if (exponent == 0)
        {
            if (destination.coeff_count() != coeff_count || destination.coeff_bit_count() != coeff_bit_count)
            {
                destination.resize(coeff_count, coeff_bit_count);
            }
            set_uint_uint(coeff_div_plain_modulus_.pointer(), coeff_uint64_count, destination.pointer());
            return;
        }
        if (exponent == 1)
        {
            encrypted.duplicate_to(destination);
            return;
        }

        vector<BigPoly> exp_vector(exponent, encrypted);
        multiply_norelin_many(exp_vector, destination);

        // Binary exponentiation
        /*
        if (exponent % 2 == 0)
        {
            exponentiate_norelin(multiply_norelin(encrypted, encrypted), exponent >> 1, destination);
            return;
        }
        multiply_norelin(exponentiate_norelin(multiply_norelin(encrypted, encrypted), (exponent - 1) >> 1), encrypted, destination);
        */
    }
    void Evaluator::relinearize(const BigPoly &encrypted, 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 (encrypted.coeff_count() != coeff_count || encrypted.coeff_bit_count() != coeff_bit_count)
        {
            throw invalid_argument("encrypted is not valid for encryption parameters");
        }
#ifdef _DEBUG
        if (encrypted.significant_coeff_count() == coeff_count || !are_poly_coefficients_less_than(encrypted, coeff_modulus_))
        {
            throw invalid_argument("encrypted 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)
        {
            set_poly_poly(encrypted.pointer(), coeff_count, coeff_uint64_count, destination.pointer());
            return;
        }

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

        // Relinearize polynomial.
        relinearize(encryptedptr.get(), destination.pointer());
    }
Esempio n. 8
0
    void poly_eval_uint_mod(const BigPoly &poly_to_evaluate, const BigUInt &value, const BigUInt &modulus, 
        BigUInt &destination, const MemoryPoolHandle &pool)
    {
        if (poly_to_evaluate.significant_coeff_bit_count() > modulus.significant_bit_count())
        {
            throw invalid_argument("poly_to_evaluate is not reduced");
        }
        if (value.significant_bit_count() > modulus.significant_bit_count())
        {
            throw invalid_argument("value is not reduced");
        }
        if (!pool)
        {
            throw invalid_argument("pool is uninitialized");
        }

        int poly_to_eval_coeff_uint64_count = poly_to_evaluate.coeff_uint64_count();
        int modulus_bit_count = modulus.significant_bit_count();

        if (poly_to_evaluate.is_zero())
        {
            destination.set_zero();
        }

        if (value.is_zero())
        {
            destination.resize(modulus_bit_count);
            modulo_uint(poly_to_evaluate.data(), poly_to_eval_coeff_uint64_count,
                Modulus(modulus.data(), modulus.uint64_count(), pool), 
                destination.data(), pool);
            return;
        }

        ConstPointer value_ptr = duplicate_uint_if_needed(value, modulus.uint64_count(), false, pool);

        destination.resize(modulus_bit_count);

        util::poly_eval_uint_mod(poly_to_evaluate.data(), poly_to_evaluate.coeff_count(), value_ptr.get(), 
            Modulus(modulus.data(), modulus.uint64_count(), pool), destination.data(), pool);
    }
Esempio n. 9
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());
        }
    }
Esempio n. 10
0
    void poly_eval_poly_polymod_coeffmod(const BigPoly &poly_to_evaluate, const BigPoly &poly_to_evaluate_at, 
        const BigPoly &poly_modulus, const BigUInt &coeff_modulus, BigPoly &destination, const MemoryPoolHandle &pool)
    {
        if (!pool)
        {
            throw invalid_argument("pool is uninitialized");
        }
        if (poly_to_evaluate.significant_coeff_count() > poly_modulus.coeff_count() ||
            poly_to_evaluate.significant_coeff_bit_count() > coeff_modulus.significant_bit_count())
        {
            throw invalid_argument("poly_to_evaluate is not reduced");
        }
        if (poly_to_evaluate_at.significant_coeff_count() > poly_modulus.coeff_count() ||
            poly_to_evaluate_at.significant_coeff_bit_count() > coeff_modulus.significant_bit_count())
        {
            throw invalid_argument("poly_to_evaluate_at is not reduced");
        }

        int poly_to_eval_coeff_uint64_count = poly_to_evaluate.coeff_uint64_count();
        int coeff_modulus_bit_count = coeff_modulus.significant_bit_count();

        if (poly_to_evaluate.is_zero())
        {
            destination.set_zero();
        }

        if (poly_to_evaluate_at.is_zero())
        {
            destination.resize(1, coeff_modulus_bit_count);
            modulo_uint(poly_to_evaluate.data(), poly_to_eval_coeff_uint64_count, 
                Modulus(coeff_modulus.data(), coeff_modulus.uint64_count(), pool), 
                destination.data(), pool);
            return;
        }

        ConstPointer poly_to_eval_ptr = duplicate_poly_if_needed(poly_to_evaluate, poly_modulus.coeff_count(), coeff_modulus.uint64_count(), false, pool);
        ConstPointer poly_to_eval_at_ptr = duplicate_poly_if_needed(poly_to_evaluate_at, poly_modulus.coeff_count(), coeff_modulus.uint64_count(), false, pool);

        destination.resize(poly_modulus.coeff_count(), coeff_modulus_bit_count);

        util::poly_eval_poly_polymod_coeffmod(poly_to_eval_ptr.get(), poly_to_eval_at_ptr.get(),
            PolyModulus(poly_modulus.data(), poly_modulus.coeff_count(), poly_modulus.coeff_uint64_count()), 
            Modulus(coeff_modulus.data(), coeff_modulus.uint64_count(), pool),
            destination.data(), pool);
    }
Esempio n. 11
0
    void poly_eval_poly(const BigPoly &poly_to_evaluate, const BigPoly &poly_to_evaluate_at, 
        BigPoly &destination, const MemoryPoolHandle &pool)
    {
        if (!pool)
        {
            throw invalid_argument("pool is uninitialized");
        }

        int poly_to_eval_coeff_uint64_count = divide_round_up(poly_to_evaluate.coeff_bit_count(), bits_per_uint64);
        int value_coeff_uint64_count = divide_round_up(poly_to_evaluate_at.coeff_bit_count(), bits_per_uint64);

        if (poly_to_evaluate.is_zero())
        {
            destination.set_zero();
            return;
        }
        if (poly_to_evaluate_at.is_zero())
        {
            destination.resize(1, poly_to_evaluate.coeff_bit_count());
            set_uint_uint(poly_to_evaluate.data(), poly_to_eval_coeff_uint64_count, destination.data());
            return;
        }

        int result_coeff_count = (poly_to_evaluate.significant_coeff_count() - 1) * (poly_to_evaluate_at.significant_coeff_count() - 1) + 1;
        int result_coeff_bit_count = poly_to_evaluate.coeff_bit_count() + (poly_to_evaluate.coeff_count() - 1) * poly_to_evaluate_at.coeff_bit_count();
        int result_coeff_uint64_count = divide_round_up(result_coeff_bit_count, bits_per_uint64);
        destination.resize(result_coeff_count, result_coeff_bit_count);

        util::poly_eval_poly(poly_to_evaluate.data(), poly_to_evaluate.coeff_count(), poly_to_eval_coeff_uint64_count, 
            poly_to_evaluate_at.data(), poly_to_evaluate_at.coeff_count(), value_coeff_uint64_count, 
            result_coeff_count, result_coeff_uint64_count, destination.data(), pool);
    }
Esempio n. 12
0
    void exponentiate_poly_polymod_coeffmod(const BigPoly &operand, const BigUInt &exponent, 
        const BigPoly &poly_modulus, const BigUInt &coeff_modulus, BigPoly &destination, const MemoryPoolHandle &pool)
    {
        if (operand.significant_coeff_count() > poly_modulus.coeff_count() ||
            operand.significant_coeff_bit_count() > coeff_modulus.significant_bit_count())
        {
            throw invalid_argument("operand is not reduced");
        }
        if (exponent < 0)
        {
            throw invalid_argument("exponent must be a non-negative integer");
        }
        if (operand.is_zero() && exponent == 0)
        {
            throw invalid_argument("undefined operation");
        }
        if (!pool)
        {
            throw invalid_argument("pool is uninitialized");
        }

        if (operand.is_zero())
        {
            destination.set_zero();
            return;
        }

        if (destination.coeff_bit_count() != coeff_modulus.significant_bit_count() || 
            destination.coeff_count() != poly_modulus.coeff_count())
        {
            destination.resize(poly_modulus.coeff_count(), coeff_modulus.significant_bit_count());
        }

        ConstPointer operand_ptr = duplicate_poly_if_needed(operand, poly_modulus.coeff_count(), coeff_modulus.uint64_count(), false, pool);
        util::exponentiate_poly_polymod_coeffmod(operand_ptr.get(), exponent.data(), exponent.uint64_count(),
            PolyModulus(poly_modulus.data(), poly_modulus.coeff_count(), poly_modulus.coeff_uint64_count()), 
            Modulus(coeff_modulus.data(), coeff_modulus.uint64_count(), pool), 
            destination.data(), pool);
    }
    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_);
    }
    void Evaluator::sub_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);
        }

        int plain2_coeff_uint64_count = divide_round_up(plain2.coeff_bit_count(), bits_per_uint64);
        if (mode_ == TEST_MODE)
        {
            // Handle test-mode case.
            set_poly_poly(plain2.pointer(), plain2.coeff_count(), plain2_coeff_uint64_count, coeff_count, coeff_uint64_count, destination.pointer());
            modulo_poly_coeffs(destination.pointer(), coeff_count, mod_, pool_);
            sub_poly_poly_coeffmod(encrypted1.pointer(), destination.pointer(), coeff_count, plain_modulus_.pointer(), coeff_uint64_count, destination.pointer());
            return;
        }

        // Multiply plain by scalar coeff_div_plaintext and reposition if in upper-half.
        preencrypt(plain2.pointer(), plain2.coeff_count(), plain2_coeff_uint64_count, destination.pointer());

        // Subtract encrypted polynomial and encrypted-version of plain2.
        sub_poly_poly_coeffmod(encrypted1.pointer(), destination.pointer(), coeff_count, coeff_modulus_.pointer(), coeff_uint64_count, destination.pointer());
    }
    void Evaluator::multiply_norelin(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();

        // 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);
        }

        // 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_);

        // Handle test-mode case.
        if (mode_ == TEST_MODE)
        {
            multiply_poly_poly_polymod_coeffmod(encrypted1ptr.get(), encrypted2ptr.get(), polymod_, mod_, destination.pointer(), pool_);
            return;
        }

        // Multiply encrypted polynomials without performing key switching.
        multiply(encrypted1ptr.get(), encrypted2ptr.get(), destination.pointer());
    }