예제 #1
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)
예제 #2
0
    def _mul_ct_sk(self, encrypted):
        """Calculate [c0 + c1 * sk + c2 * sk^2 ...]_q

        where [c0, c1, ...] represents ciphertext element and sk^n represents
        secret key raised to the power n.

        Args:
            encrypted: A ciphertext object of encrypted data.

        Returns:
            A 2-dim list containing result of [c0 + c1 * sk + c2 * sk^2 ...]_q.
        """
        phase = encrypted[0]

        secret_key_array = self._get_sufficient_sk_power(len(encrypted) - 1)

        for j in range(1, len(encrypted)):
            for i in range(len(self._coeff_modulus)):
                phase[i] = poly_add_mod(
                    poly_mul_mod(
                        encrypted[j][i],
                        secret_key_array[j - 1][i],
                        self._coeff_modulus[i],
                        self._coeff_count,
                    ),
                    phase[i],
                    self._coeff_modulus[i],
                    self._coeff_count,
                )
        return phase
예제 #3
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])
예제 #4
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)
예제 #5
0
    def _add_plain_plain(self, pt1, pt2):
        """Adds two plaintexts object.

        Args:
            pt1 (Plaintext): First argument.
            pt2 (Plaintext): Second argument.

        Returns:
            A Plaintext object with value equivalent to result of addition of two provided
                arguments.
        """
        pt1, pt2 = copy.deepcopy(pt1), copy.deepcopy(pt2)
        return PlainText(poly_add_mod(pt1.data, pt2.data, self.plain_modulus))
예제 #6
0
    def _generate_one_kswitch_key(self, sk_power_2):
        decomp_mod_count = len(self._coeff_modulus) - 1
        result = [0] * (decomp_mod_count)
        for i in range(decomp_mod_count):
            result[i] = encrypt_symmetric(self._context,
                                          self._context.key_param_id,
                                          self._secret_key).data
            factor = self._coeff_modulus[-1] % self._coeff_modulus[i]

            temp = [(x * factor) for x in sk_power_2[i]]

            result[i][0][i] = poly_add_mod(result[i][0][i], temp,
                                           self._coeff_modulus[i],
                                           self._poly_modulus)
        return result
예제 #7
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)
예제 #8
0
def test_poly_add_mod(op1, op2, mod, result):
    assert poly_add_mod(op1, op2, mod) == result
예제 #9
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)
예제 #10
0
파일: evaluator.py 프로젝트: znbdata/PySyft
    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)