Exemple #1
0
def multiply_sub_plain_with_delta(ct, pt, context_data):
    """Subtract plaintext from ciphertext.

    Args:
        ct (Ciphertext): ct is pre-computed carrier polynomial where we can add message data.
        pt (Plaintext): A plaintext representation of integer data to be encrypted.
        context (Context): Context for extracting encryption parameters.

    Returns:
        A Ciphertext object with the encrypted result of encryption process.
    """
    ct_param_id = ct.param_id
    coeff_modulus = context_data.param.coeff_modulus
    pt = pt.data
    plain_coeff_count = len(pt)
    delta = context_data.coeff_div_plain_modulus
    ct0, ct1 = ct.data  # here ct = pk * u * e

    # Coefficients of plain m multiplied by coeff_modulus q, divided by plain_modulus t,
    # and rounded to the nearest integer (rounded up in case of a tie). Equivalent to
    for i in range(plain_coeff_count):
        for j in range(len(coeff_modulus)):
            temp = round(delta[j] * pt[i]) % coeff_modulus[j]
            ct0[j][i] = (ct0[j][i] - temp) % coeff_modulus[j]

    return CipherText([ct0, ct1], ct_param_id)  # ct0 = pk0 * u * e - delta * pt
Exemple #2
0
def multiply_add_plain_with_delta(phase, message, context):
    """Add message (PlainText) into phase.

    Args:
        phase: phase is pre-computed carrier polynomial where we can add message data.
        message (Plaintext): A plaintext representation of integer data to be encrypted.
        context (Context): Context for extracting encryption parameters.

    Returns:
        A Ciphertext object with the encrypted result of encryption process.
    """
    coeff_modulus = context.param.coeff_modulus
    message = message.data
    plain_coeff_count = len(message)
    delta = context.coeff_div_plain_modulus
    phase0, phase1 = phase.data  # here phase = pk * u * e

    # Coefficients of plain m multiplied by coeff_modulus q, divided by plain_modulus t,
    # and rounded to the nearest integer (rounded up in case of a tie). Equivalent to
    for i in range(plain_coeff_count):
        for j in range(len(coeff_modulus)):
            temp = round(delta[j] * message[i]) % coeff_modulus[j]
            phase0[j][i] = (phase0[j][i] + temp) % coeff_modulus[j]

    return CipherText([phase0, phase1])  # phase0 = pk0 * u * e + delta * m
Exemple #3
0
    def _sub_cipher_cipher(self, ct1, ct2):
        """Subtract two ciphertexts.

        Args:
            ct1 (Ciphertext): First polynomial argument (Minuend).
            ct2 (Ciphertext): Second polynomial argument (Subtrahend).

        Returns:
            A Ciphertext object with value equivalent to result of subtraction of two provided
                arguments.
        """
        ct1, ct2 = copy.deepcopy(ct1.data), copy.deepcopy(ct2.data)
        result = ct2 if len(ct2) > len(ct1) else ct1
        min_size, max_size = min(len(ct1), len(ct2)), max(len(ct1), len(ct2))

        for i in range(min_size):
            for j in range(len(self.coeff_modulus)):
                result[i][j] = poly_sub_mod(ct1[i][j], ct2[i][j],
                                            self.coeff_modulus[j],
                                            self.poly_modulus)

        for i in range(min_size + 1, max_size):
            for j in range(len(self.coeff_modulus)):
                result[i][j] = poly_negate_mod(result[i][j],
                                               self.coeff_modulus[j])

        return CipherText(result)
Exemple #4
0
    def _add_cipher_cipher(self, ct1, ct2):
        """Adds two ciphertexts.

        Args:
            ct1 (Ciphertext): First polynomial argument (Augend).
            ct2 (Ciphertext): Second polynomial argument (Addend).

        Returns:
            A Ciphertext object with value equivalent to result of addition of two provided
                arguments.
        """
        param_id = ct1.param_id
        context_data = self.context.context_data_map[param_id]
        coeff_modulus = context_data.param.coeff_modulus

        ct1, ct2 = copy.deepcopy(ct1.data), copy.deepcopy(ct2.data)
        result = ct2 if len(ct2) > len(ct1) else ct1

        for i in range(min(len(ct1), len(ct2))):
            for j in range(len(coeff_modulus)):
                result[i][j] = poly_add_mod(ct1[i][j], ct2[i][j],
                                            coeff_modulus[j],
                                            self.poly_modulus)

        return CipherText(result, param_id)
Exemple #5
0
def encrypt_symmetric(context, secret_key):
    """Create encryption of zero values with a secret key which can be used in subsequent
    processes to add a message into it.

    Args:
        context (Context): A valid context required for extracting the encryption parameters.
        secret_key (SecretKey): A secret key generated with same encryption parameters.

    Returns:
        A ciphertext object containing encryption of zeroes by symmetric encryption procedure.
    """
    coeff_modulus = context.param.coeff_modulus
    coeff_mod_size = len(coeff_modulus)

    # Sample uniformly at random
    c1 = sample_poly_uniform(context.param)

    # Sample e <-- chi
    e = sample_poly_normal(context.param)

    # calculate -(a*s + e) (mod q) and store in c0

    c0 = [0] * coeff_mod_size

    for i in range(coeff_mod_size):
        c0[i] = poly_negate_mod(
            poly_add_mod(
                poly_mul_mod(c1[i], secret_key[i], coeff_modulus[i]), e[i], coeff_modulus[i]
            ),
            coeff_modulus[i],
        )

    return CipherText([c0, c1])
Exemple #6
0
def encrypt_asymmetric(context, public_key):
    """Create encryption of zero values with a public key which can be used in
    subsequent processes to add a message into it.

    Args:
        context (Context): A valid context required for extracting the encryption
            parameters.
        public_key (PublicKey): A public key generated with same encryption parameters.

    Returns:
        A ciphertext object containing encryption of zeroes by asymmetric encryption procedure.
    """
    param = context.param
    coeff_modulus = param.coeff_modulus
    coeff_mod_size = len(coeff_modulus)
    encrypted_size = len(public_key)

    # Generate u <-- R_3
    u = sample_poly_ternary(param)

    c_0 = [0] * coeff_mod_size

    c_1 = [0] * coeff_mod_size
    result = [c_0, c_1]

    # c[i] = u * public_key[i]
    # Generate e_j <-- chi
    # c[i] = public_key[i] * u + e[i]
    for j in range(encrypted_size):
        e = sample_poly_normal(param)
        for i in range(coeff_mod_size):
            result[j][i] = poly_add_mod(
                poly_mul_mod(public_key[j][i], u[i], coeff_modulus[i]), e[i], coeff_modulus[i]
            )
    return CipherText(result)
Exemple #7
0
    def negate(self, ct):
        """Negate a cipher i.e -(ct_value)

        Args:
            ct (Ciphertext): Ciphertext to be negated.

        Returns:
            A Ciphertext object with value equivalent to result of -(ct_value).
        """
        result = copy.deepcopy(ct.data)

        for i in range(len(result)):
            for j in range(len(result[i])):
                for k in range(len(result[i][j])):
                    result[i][j][k] = negate_mod(ct.data[i][j][k], self.coeff_modulus[j])

        return CipherText(result)
Exemple #8
0
    def _add_cipher_cipher(self, ct1, ct2):
        """Adds two ciphertexts.

        Args:
            ct1 (Ciphertext): First argument.
            ct2 (Ciphertext): Second argument.

        Returns:
            A Ciphertext object with value equivalent to result of addition of two provided
                arguments.
        """
        ct1, ct2 = copy.deepcopy(ct1.data), copy.deepcopy(ct2.data)
        result = ct2 if len(ct2) > len(ct1) else ct1

        for i in range(min(len(ct1), len(ct2))):
            for j in range(len(self.coeff_modulus)):
                result[i][j] = poly_add_mod(ct1[i][j], ct2[i][j], self.coeff_modulus[j])

        return CipherText(result)
Exemple #9
0
    def _mul_cipher_plain(self, ct, pt):
        """Multiply two operands using FV scheme.

        Args:
            op1 (Ciphertext): First polynomial argument (Multiplicand).
            op2 (Plaintext): Second polynomial argument (Multiplier).

        Returns:
            A Ciphertext object with a value equivalent to the result of the product of two
                operands.
        """
        ct, pt = ct.data, pt.data
        result = copy.deepcopy(ct)

        for i in range(len(result)):
            for j in range(len(self.coeff_modulus)):
                result[i][j] = poly_mul_mod(ct[i][0], pt, self.coeff_modulus[j], self.poly_modulus)

        return CipherText(result)
Exemple #10
0
    def _encrypt(self, message, param_id, is_asymmetric):
        """Encrypts the message according to the key provided.

        Args:
            message (Plaintext): An Plaintext object which has to be encrypted.
            param_id: Parameter id for accessing the correct parameters from the context chain.
            is_asymmetric (bool): Based on the key provided for encryption select
                the mode of encryption.

        Returns:
            A Ciphertext object contating the encrypted result.
        """

        result = None
        if is_asymmetric:
            prev_context_id = self.context.context_data_map[
                param_id].prev_context_id

            if prev_context_id is not None:
                # Requires modulus switching
                prev_context_data = self.context.context_data_map[
                    prev_context_id]
                rns_tool = prev_context_data.rns_tool
                temp = encrypt_asymmetric(self.context, prev_context_id,
                                          self.key.data).data

                for j in range(2):
                    temp[j] = rns_tool.divide_and_round_q_last_inplace(temp[j])
                result = CipherText(temp, param_id)
            else:
                result = encrypt_asymmetric(
                    self.context, param_id,
                    self.key.data)  # Public key used for encryption

        else:
            result = encrypt_symmetric(
                self.context, param_id,
                self.key.data)  # Secret key used for encryption

        return multiply_add_plain_with_delta(
            result, message,
            self.context.context_data_map[self.context.first_param_id])
Exemple #11
0
    def negate(self, ct):
        """Negate a cipher i.e -(ct_value)

        Args:
            ct (Ciphertext): Ciphertext to be negated.

        Returns:
            A Ciphertext object with value equivalent to result of -(ct_value).
        """
        context_data = self.context.context_data_map[ct.param_id]
        coeff_modulus = context_data.param.coeff_modulus
        result = copy.deepcopy(ct.data)

        for i in range(len(result)):
            for j in range(len(coeff_modulus)):
                for k in range(len(result[i][j])):
                    result[i][j][k] = negate_mod(result[i][j][k],
                                                 coeff_modulus[j])

        return CipherText(result, ct.param_id)
Exemple #12
0
    def _mul_cipher_cipher(self, ct1, ct2):
        """Multiply two operands using FV scheme.

        Args:
            op1 (Ciphertext): First polynomial argument (Multiplicand).
            op2 (Ciphertext): Second polynomial argument (Multiplier).

        Returns:
            A Ciphertext object with a value equivalent to the result of the product of two
                operands.
        """
        ct1, ct2 = ct1.data, ct2.data

        if len(ct1) > 2:
            # TODO: perform relinearization operation.
            raise RuntimeError(
                "Cannot multiply ciphertext of size >2, Perform relinearization operation."
            )
        if len(ct2) > 2:
            # TODO: perform relinearization operation.
            raise RuntimeError(
                "Cannot multiply ciphertext of size >2, Perform relinearization operation."
            )

        # Now the size of ciphertexts is 2.
        # Multiplication operation of ciphertext:
        #   result = [r1, r2, r3] where
        #   r1 = ct1[0] * ct2[0]
        #   r2 = ct1[0] * ct2[1] + ct1[1] * ct2[0]
        #   r3 = ct1[1] * ct2[1]
        #
        # where ct1[i], ct2[i] are polynomials.

        ct10, ct11 = ct1
        ct20, ct21 = ct2

        result = [
            [0] * len(self.coeff_modulus),
            [0] * len(self.coeff_modulus),
            [0] * len(self.coeff_modulus),
        ]

        for i in range(len(self.coeff_modulus)):
            result[0][i] = poly_mul(ct10[i], ct20[i], self.poly_modulus)

            result[1][i] = poly_add(
                poly_mul(ct11[i], ct20[i], self.poly_modulus),
                poly_mul(ct10[i], ct21[i], self.poly_modulus),
                self.poly_modulus,
            )

            result[2][i] = poly_mul(ct11[i], ct21[i], self.poly_modulus)

        for i in range(len(result)):
            for j in range(len(self.coeff_modulus)):
                result[i][j] = [
                    round(((x * self.plain_modulus) / self.coeff_modulus[j])) %
                    self.coeff_modulus[j] for x in result[i][j]
                ]

        return CipherText(result)
Exemple #13
0
    def _mul_cipher_cipher(self, ct1, ct2):
        """Multiply two operands using FV scheme.

        Args:
            op1 (Ciphertext): First polynomial argument (Multiplicand).
            op2 (Ciphertext): Second polynomial argument (Multiplier).

        Returns:
            A Ciphertext object with a value equivalent to the result of the product of two
                operands.
        """
        ct1, ct2 = ct1.data, ct2.data

        if len(ct1) > 2 or len(ct2) > 2:
            # TODO: perform relinearization operation.
            raise Warning("Multiplying ciphertext of size >2 should be avoided.")

        rns_tool = self.context.rns_tool
        bsk_base_mod = rns_tool.base_Bsk.base
        bsk_base_mod_count = len(bsk_base_mod)
        dest_count = len(ct2) + len(ct1) - 1

        # Step 0: fast base convert from q to Bsk U {m_tilde}
        # Step 1: reduce q-overflows in Bsk
        # Iterate over all the ciphertexts inside ct1
        tmp_encrypted1_bsk = []
        for i in range(len(ct1)):
            tmp_encrypted1_bsk.append(rns_tool.sm_mrq(rns_tool.fastbconv_m_tilde(ct1[i])))

        # Similarly, itterate over all the ciphertexts inside ct2
        tmp_encrypted2_bsk = []
        for i in range(len(ct2)):
            tmp_encrypted2_bsk.append(rns_tool.sm_mrq(rns_tool.fastbconv_m_tilde(ct2[i])))

        tmp_des_coeff_base = [
            [[0] for y in range(len(self.coeff_modulus))] for x in range(dest_count)
        ]

        tmp_des_bsk_base = [[[0] for y in range(bsk_base_mod_count)] for x in range(dest_count)]

        for m in range(dest_count):
            # Loop over encrypted1 components [i], seeing if a match exists with an encrypted2
            # component [j] such that [i+j]=[m]
            # Only need to check encrypted1 components up to and including [m],
            # and strictly less than [encrypted_array.size()]

            current_encrypted1_limit = min(len(ct1), m + 1)

            for encrypted1_index in range(current_encrypted1_limit):
                # check if a corresponding component in encrypted2 exists
                if len(ct2) > m - encrypted1_index:
                    encrypted2_index = m - encrypted1_index

                    for i in range(len(self.coeff_modulus)):
                        tmp_des_coeff_base[m][i] = poly_add_mod(
                            poly_mul_mod(
                                ct1[encrypted1_index][i],
                                ct2[encrypted2_index][i],
                                self.coeff_modulus[i],
                                self.poly_modulus,
                            ),
                            tmp_des_coeff_base[m][i],
                            self.coeff_modulus[i],
                            self.poly_modulus,
                        )

                    for i in range(bsk_base_mod_count):
                        tmp_des_bsk_base[m][i] = poly_add_mod(
                            poly_mul_mod(
                                tmp_encrypted1_bsk[encrypted1_index][i],
                                tmp_encrypted2_bsk[encrypted2_index][i],
                                bsk_base_mod[i],
                                self.poly_modulus,
                            ),
                            tmp_des_bsk_base[m][i],
                            bsk_base_mod[i],
                            self.poly_modulus,
                        )

        # Now we multiply plain modulus to both results in base q and Bsk and
        # allocate them together in one container as
        # (te0)q(te'0)Bsk | ... |te count)q (te' count)Bsk to make it ready for
        # fast_floor
        result = []
        for i in range(dest_count):
            temp = []
            for j in range(len(self.coeff_modulus)):
                temp.append(
                    [
                        (x * self.plain_modulus) % self.coeff_modulus[j]
                        for x in tmp_des_coeff_base[i][j]
                    ]
                )
            for j in range(bsk_base_mod_count):
                temp.append(
                    [(x * self.plain_modulus) % bsk_base_mod[j] for x in tmp_des_bsk_base[i][j]]
                )
            result.append(rns_tool.fastbconv_sk(rns_tool.fast_floor(temp)))

        return CipherText(result)
Exemple #14
0
    def _switch_key_inplace(self, ct, key):

        if not isinstance(key, RelinKey):
            raise RuntimeError("Relinearization key is invalid")

        param_id = ct.param_id
        ct = ct.data
        key_vector = key.data
        context_data = self.context.context_data_map[param_id]
        key_context = self.context.context_data_map[self.context.key_param_id]

        coeff_modulus = context_data.param.coeff_modulus
        decomp_mod_count = len(coeff_modulus)
        key_mod = key_context.param.coeff_modulus
        key_mod_count = len(key_mod)
        rns_mod_count = decomp_mod_count + 1

        target = ct[-1]  # Last component of ciphertext

        modswitch_factors = key_context.rns_tool.inv_q_last_mod_q

        for i in range(decomp_mod_count):

            local_small_poly_0 = copy.deepcopy(target[i])

            temp_poly = [[[0] for x in range(rns_mod_count)],
                         [[0] for x in range(rns_mod_count)]]

            for j in range(rns_mod_count):
                index = key_mod_count - 1 if j == decomp_mod_count else j

                if key_mod[i] <= key_mod[index]:
                    local_small_poly_1 = copy.deepcopy(local_small_poly_0)
                else:
                    local_small_poly_1 = [
                        x % key_mod[index] for x in local_small_poly_0
                    ]

                for k in range(2):
                    local_small_poly_2 = poly_mul_mod(
                        local_small_poly_1,
                        key_vector[i][k][index],
                        key_mod[index],
                        self.poly_modulus,
                    )
                    temp_poly[k][j] = poly_add_mod(local_small_poly_2,
                                                   temp_poly[k][j],
                                                   key_mod[index],
                                                   self.poly_modulus)

        # Results are now stored in temp_poly[k]
        # Modulus switching should be performed
        for k in range(2):
            temp_poly_ptr = temp_poly[k][decomp_mod_count]
            temp_last_poly_ptr = temp_poly[k][decomp_mod_count]

            temp_poly_ptr = [x % key_mod[-1] for x in temp_poly_ptr]

            # Add (p-1)/2 to change from flooring to rounding.
            half = key_mod[-1] >> 1
            temp_last_poly_ptr = [(x + half) % key_mod[-1]
                                  for x in temp_last_poly_ptr]

            encrypted_ptr = ct[k]
            for j in range(decomp_mod_count):
                temp_poly_ptr = temp_poly[k][j]

                temp_poly_ptr = [x % key_mod[j] for x in temp_poly_ptr]
                local_small_poly = [x % key_mod[j] for x in temp_last_poly_ptr]
                half_mod = half % key_mod[j]

                local_small_poly = [(x - half_mod) % key_mod[j]
                                    for x in local_small_poly]

                # ((ct mod qi) - (ct mod qk)) mod qi
                temp_poly_ptr = poly_sub_mod(temp_poly_ptr, local_small_poly,
                                             key_mod[j], self.poly_modulus)

                # qk^(-1) * ((ct mod qi) - (ct mod qk)) mod qi
                temp_poly_ptr = [(x * modswitch_factors[j]) % key_mod[j]
                                 for x in temp_poly_ptr]

                encrypted_ptr[j] = poly_add_mod(temp_poly_ptr,
                                                encrypted_ptr[j], key_mod[j],
                                                self.poly_modulus)

        return CipherText(ct[0:2], param_id)