예제 #1
0
    def add(self, ciph1, ciph2):
        """Adds two ciphertexts.

        Adds two ciphertexts within the context.

        Args:
            ciph1 (Ciphertext): First ciphertext.
            ciph2 (Ciphertext): Second ciphertext.

        Returns:
            A Ciphertext which is the sum of the two ciphertexts.
        """
        assert isinstance(ciph1, Ciphertext)
        assert isinstance(ciph2, Ciphertext)
        assert ciph1.scaling_factor == ciph2.scaling_factor, "Scaling factors are not equal. " \
            + "Ciphertext 1 scaling factor: %d bits, Ciphertext 2 scaling factor: %d bits" \
            % (math.log(ciph1.scaling_factor, 2), math.log(ciph2.scaling_factor, 2))
        assert ciph1.modulus == ciph2.modulus, "Moduli are not equal. " \
            + "Ciphertext 1 modulus: %d bits, Ciphertext 2 modulus: %d bits" \
            % (math.log(ciph1.modulus, 2), math.log(ciph2.modulus, 2))

        modulus = ciph1.modulus

        c0 = ciph1.c0.add(ciph2.c0, modulus)
        c0 = c0.mod_small(modulus)
        c1 = ciph1.c1.add(ciph2.c1, modulus)
        c1 = c1.mod_small(modulus)
        return Ciphertext(c0, c1, ciph1.scaling_factor, modulus)
예제 #2
0
    def encrypt(self, plain):
        """Encrypts a message.

        Encrypts the message and returns the corresponding ciphertext.

        Args:
            plain (Plaintext): Plaintext to be encrypted.

        Returns:
            A ciphertext consisting of a pair of polynomials in the ciphertext
            space.
        """
        p0 = self.public_key.p0
        p1 = self.public_key.p1

        random_vec = Polynomial(self.poly_degree,
                                sample_triangle(self.poly_degree))
        error1 = Polynomial(self.poly_degree,
                            sample_triangle(self.poly_degree))
        error2 = Polynomial(self.poly_degree,
                            sample_triangle(self.poly_degree))

        c0 = p0.multiply(random_vec, self.coeff_modulus, crt=self.crt_context)
        c0 = error1.add(c0, self.coeff_modulus)
        c0 = c0.add(plain.poly, self.coeff_modulus)
        c0 = c0.mod_small(self.coeff_modulus)

        c1 = p1.multiply(random_vec, self.coeff_modulus, crt=self.crt_context)
        c1 = error2.add(c1, self.coeff_modulus)
        c1 = c1.mod_small(self.coeff_modulus)

        return Ciphertext(c0, c1, plain.scaling_factor, self.coeff_modulus)
예제 #3
0
    def subtract(self, ciph1, ciph2):
        """Subtracts second ciphertext from first ciphertext.

        Computes ciph1 - ciph2.

        Args:
            ciph1 (Ciphertext): First ciphertext.
            ciph2 (Ciphertext): Second ciphertext.

        Returns:
            A Ciphertext which is the difference between the two ciphertexts.
        """
        assert isinstance(ciph1, Ciphertext)
        assert isinstance(ciph2, Ciphertext)
        assert ciph1.scaling_factor == ciph2.scaling_factor, "Scaling factors are not equal. " \
            + "Ciphertext 1 scaling factor: %d bits, Ciphertext 2 scaling factor: %d bits" \
            % (math.log(ciph1.scaling_factor, 2), math.log(ciph2.scaling_factor, 2))
        assert ciph1.modulus == ciph2.modulus, "Moduli are not equal. " \
            + "Ciphertext 1 modulus: %d bits, Ciphertext 2 modulus: %d bits" \
            % (math.log(ciph1.modulus, 2), math.log(ciph2.modulus, 2))

        modulus = ciph1.modulus

        c0 = ciph1.c0.subtract(ciph2.c0, modulus)
        c0 = c0.mod_small(modulus)
        c1 = ciph1.c1.subtract(ciph2.c1, modulus)
        c1 = c1.mod_small(modulus)
        return Ciphertext(c0, c1, ciph1.scaling_factor, modulus)
예제 #4
0
    def switch_key(self, ciph, key):
        """Outputs ciphertext with switching key.

        Performs KS procedure as described in CKKS paper.

        Args:
            ciph (Ciphertext): Ciphertext to change.
            switching_key (PublicKey): Switching key.

        Returns:
            A Ciphertext which encrypts the same message under a different key.
        """

        c0 = key.p0.multiply(ciph.c1,
                             ciph.modulus * self.big_modulus,
                             crt=self.crt_context)
        c0 = c0.mod_small(ciph.modulus * self.big_modulus)
        c0 = c0.scalar_integer_divide(self.big_modulus)
        c0 = c0.add(ciph.c0, ciph.modulus)
        c0 = c0.mod_small(ciph.modulus)

        c1 = key.p1.multiply(ciph.c1,
                             ciph.modulus * self.big_modulus,
                             crt=self.crt_context)
        c1 = c1.mod_small(ciph.modulus * self.big_modulus)
        c1 = c1.scalar_integer_divide(self.big_modulus)
        c1 = c1.mod_small(ciph.modulus)

        return Ciphertext(c0, c1, ciph.scaling_factor, ciph.modulus)
예제 #5
0
    def encrypt(self, message):
        """Encrypts a message.

        Encrypts the message and returns the corresponding ciphertext.

        Args:
            message (Plaintext): Plaintext to be encrypted.

        Returns:
            A ciphertext consisting of a pair of polynomials in the ciphertext
            space.
        """
        p0 = self.public_key.p0
        p1 = self.public_key.p1
        scaled_message = message.poly.scalar_multiply(self.scaling_factor,
                                                      self.coeff_modulus)

        random_vec = Polynomial(self.poly_degree,
                                sample_triangle(self.poly_degree))
        error1 = Polynomial(self.poly_degree,
                            sample_triangle(self.poly_degree))
        error1 = Polynomial(self.poly_degree, [0] * self.poly_degree)
        error2 = Polynomial(self.poly_degree,
                            sample_triangle(self.poly_degree))
        error2 = Polynomial(self.poly_degree, [0] * self.poly_degree)
        c0 = error1.add(p0.multiply(random_vec, self.coeff_modulus),
                        self.coeff_modulus).add(scaled_message,
                                                self.coeff_modulus)
        c1 = error2.add(p1.multiply(random_vec, self.coeff_modulus),
                        self.coeff_modulus)

        return Ciphertext(c0, c1)
예제 #6
0
    def relinearize(self, relin_key, c0, c1, c2):
        """Relinearizes a 3-dimensional ciphertext.

        Reduces 3-dimensional ciphertext back down to 2 dimensions.

        Args:
            relin_key (RelinKey): Relinearization keys.
            c0 (Polynomial): First component of ciphertext.
            c1 (Polynomial): Second component of ciphertext.
            c2 (Polynomial): Third component of ciphertext.

        Returns:
            A Ciphertext which has only two components.
        """
        keys = relin_key.keys
        base = relin_key.base
        num_levels = len(keys)

        c2_decomposed = c2.base_decompose(base, num_levels)

        new_c0 = c0
        new_c1 = c1

        for i in range(num_levels):
            new_c0 = new_c0.add(
                keys[i][0].multiply(c2_decomposed[i], self.coeff_modulus),
                self.coeff_modulus)
            new_c1 = new_c1.add(
                keys[i][1].multiply(c2_decomposed[i], self.coeff_modulus),
                self.coeff_modulus)

        return Ciphertext(new_c0, new_c1)
예제 #7
0
    def encrypt_with_secret_key(self, plain):
        """Encrypts a message with secret key encryption.

        Encrypts the message for secret key encryption and returns the corresponding ciphertext.

        Args:
            plain (Plaintext): Plaintext to be encrypted.

        Returns:
            A ciphertext consisting of a pair of polynomials in the ciphertext
            space.
        """
        assert self.secret_key != None, 'Secret key does not exist'

        sk = self.secret_key.s
        random_vec = Polynomial(self.poly_degree,
                                sample_triangle(self.poly_degree))
        error = Polynomial(self.poly_degree, sample_triangle(self.poly_degree))

        c0 = sk.multiply(random_vec, self.coeff_modulus, crt=self.crt_context)
        c0 = error.add(c0, self.coeff_modulus)
        c0 = c0.add(plain.poly, self.coeff_modulus)
        c0 = c0.mod_small(self.coeff_modulus)

        c1 = random_vec.scalar_multiply(-1, self.coeff_modulus)
        c1 = c1.mod_small(self.coeff_modulus)

        return Ciphertext(c0, c1, plain.scaling_factor, self.coeff_modulus)
예제 #8
0
    def relinearize(self, relin_key, c0, c1, c2, new_scaling_factor, modulus):
        """Relinearizes a 3-dimensional ciphertext.

        Reduces 3-dimensional ciphertext back down to 2 dimensions.

        Args:
            relin_key (PublicKey): Relinearization keys.
            c0 (Polynomial): First component of ciphertext.
            c1 (Polynomial): Second component of ciphertext.
            c2 (Polynomial): Third component of ciphertext.
            new_scaling_factor (float): New scaling factor for ciphertext.
            modulus (int): Ciphertext modulus.

        Returns:
            A Ciphertext which has only two components.
        """
        new_c0 = relin_key.p0.multiply(c2,
                                       modulus * self.big_modulus,
                                       crt=self.crt_context)
        new_c0 = new_c0.mod_small(modulus * self.big_modulus)
        new_c0 = new_c0.scalar_integer_divide(self.big_modulus)
        new_c0 = new_c0.add(c0, modulus)
        new_c0 = new_c0.mod_small(modulus)

        new_c1 = relin_key.p1.multiply(c2,
                                       modulus * self.big_modulus,
                                       crt=self.crt_context)
        new_c1 = new_c1.mod_small(modulus * self.big_modulus)
        new_c1 = new_c1.scalar_integer_divide(self.big_modulus)
        new_c1 = new_c1.add(c1, modulus)
        new_c1 = new_c1.mod_small(modulus)

        return Ciphertext(new_c0, new_c1, new_scaling_factor, modulus)
예제 #9
0
    def add_plain(self, cipher, plain):
        assert isinstance(cipher, Ciphertext)
        assert isinstance(plain, Plaintext)
        assert cipher.scaling_factor == plain.scaling_factor, "Scaling factors are not equal. " \
                                    + "Ciphertext scaling factor: %d bits, Plaintext scaling factor: %d bits" \
                                    % (math.log(cipher.scaling_factor, 2),
                                    math.log(plain.scaling_factor, 2))
        c0 = cipher.c0.add(plain.poly, cipher.modulus)
        c0 = c0.mod_small(cipher.modulus)

        return Ciphertext(c0, cipher.c1, cipher.scaling_factor, cipher.modulus)
예제 #10
0
    def add(self, cipher1, cipher2):
        assert isinstance(cipher1, Ciphertext)
        assert isinstance(cipher2, Ciphertext)
        assert cipher1.scaling_factor == cipher2.scaling_factor, "Scaling factors are not equal. " \
            + "Ciphertext 1 scaling factor: %d bits, Ciphertext 2 scaling factor: %d bits" \
            % (math.log(cipher1.scaling_factor, 2), math.log(cipher2.scaling_factor, 2))
        assert cipher1.modulus == cipher2.modulus, "Moduli are not equal. " \
            + "Ciphertext 1 modulus: %d bits, Ciphertext 2 modulus: %d bits" \
            % (math.log(cipher1.modulus, 2), math.log(cipher2.modulus, 2))

        modulus = cipher1.modulus
        c0 = cipher1.c0.add(cipher2.c0, modulus)
        c1 = cipher1.c1.add(cipher2.c1, modulus)
        return Ciphertext(c0, c1, cipher1.scaling_factor, cipher1.modulus)
예제 #11
0
    def multiply_plain(self, cipher, plain):
        assert isinstance(cipher, Ciphertext)
        assert isinstance(plain, Plaintext)

        c0 = cipher.c0.multiply(plain.poly,
                                cipher.modulus,
                                crt=self.crt_context)
        c0 = c0.mod_small(cipher.modulus)
        c1 = cipher.c1.multiply(plain.poly,
                                cipher.modulus,
                                crt=self.crt_context)
        c1 = c1.mod_small(cipher.modulus)

        return Ciphertext(c0, c1, cipher.scaling_factor * plain.scaling_factor,
                          cipher.modulus)
예제 #12
0
    def encrypt(self, plain):
        p0 = self.public_key[0]
        p1 = self.public_key[1]
        random_vec = Poly(self.poly_degree, sample_triangle(self.poly_degree))
        error1 = Poly(self.poly_degree, sample_triangle(self.poly_degree))
        error2 = Poly(self.poly_degree, sample_triangle(self.poly_degree))
        c0 = p0.multiply(random_vec, self.coeff_modulus, crt=self.crt_context)
        c0 = error1.add(c0, self.coeff_modulus)
        c0 = c0.add(plain.poly, self.coeff_modulus)
        c0 = c0.mod_small(self.coeff_modulus)

        c1 = p1.multiply(random_vec, self.coeff_modulus, crt=self.crt_context)
        c1 = error2.add(c1, self.coeff_modulus)
        c1 = c1.mod_small(self.coeff_modulus)

        return Ciphertext(c0, c1, plain.scaling_factor, self.coeff_modulus)
예제 #13
0
    def rescale(self, ciph, division_factor):
        """Rescales a ciphertext to a new scaling factor.

        Divides ciphertext by division factor, and updates scaling factor
        and ciphertext. modulus.

        Args:
            ciph (Ciphertext): Ciphertext to modify.
            division_factor (float): Factor to divide by.

        Returns:
            Rescaled ciphertext.
        """
        c0 = ciph.c0.scalar_integer_divide(division_factor)
        c1 = ciph.c1.scalar_integer_divide(division_factor)
        return Ciphertext(c0, c1, ciph.scaling_factor // division_factor,
                          ciph.modulus // division_factor)
예제 #14
0
    def lower_modulus(self, ciph, division_factor):
        """Rescales a ciphertext to a new scaling factor.

        Divides ciphertext by division factor, and updates scaling factor
        and ciphertext modulus.

        Args:
            ciph (Ciphertext): Ciphertext to modify.
            division_factor (float): Factor to divide by.

        Returns:
            Rescaled ciphertext.
        """
        new_modulus = ciph.modulus // division_factor
        c0 = ciph.c0.mod_small(new_modulus)
        c1 = ciph.c1.mod_small(new_modulus)
        return Ciphertext(c0, c1, ciph.scaling_factor, new_modulus)
예제 #15
0
    def add(self, ciph1, ciph2):
        """Adds two ciphertexts.

        Adds two ciphertexts within the context.

        Args:
            ciph1 (Ciphertext): First ciphertext.   
            ciph2 (Ciphertext): Second ciphertext. 

        Returns:
            A Ciphertext which is the sum of the two ciphertexts.
        """
        assert isinstance(ciph1, Ciphertext)
        assert isinstance(ciph2, Ciphertext)

        new_ciph_c0 = ciph1.c0.add(ciph2.c0, self.coeff_modulus)
        new_ciph_c1 = ciph1.c1.add(ciph2.c1, self.coeff_modulus)
        return Ciphertext(new_ciph_c0, new_ciph_c1)
예제 #16
0
    def conjugate(self, ciph, conj_key):
        """Conjugates the ciphertext.

        Returns a ciphertext for a plaintext which is conjugated.

        Args:
            ciph (Ciphertext): Ciphertext to conjugate.
            conj_key (PublicKey): Conjugation key.

        Returns:
            A Ciphertext which is the encryption of the conjugation of the original
            plaintext.
        """

        conj_ciph0 = ciph.c0.conjugate().mod_small(ciph.modulus)
        conj_ciph1 = ciph.c1.conjugate().mod_small(ciph.modulus)
        conj_ciph = Ciphertext(conj_ciph0, conj_ciph1, ciph.scaling_factor,
                               ciph.modulus)
        return self.switch_key(conj_ciph, conj_key)
예제 #17
0
    def rotate(self, ciph, rotation, rot_key):
        """Rotates a ciphertext by the amount specified in rotation.

        Returns a ciphertext for a plaintext which is rotated by the amount
        in rotation.

        Args:
            ciph (Ciphertext): Ciphertext to rotate.
            rotation (int): Amount to rotate by.
            rot_key (RotationKey): Rotation key corresponding to the rotation.

        Returns:
            A Ciphertext which is the encryption of the rotation of the original
            plaintext.
        """
        rot_ciph0 = ciph.c0.rotate(rotation)
        rot_ciph1 = ciph.c1.rotate(rotation)
        rot_ciph = Ciphertext(rot_ciph0, rot_ciph1, ciph.scaling_factor,
                              ciph.modulus)
        return self.switch_key(rot_ciph, rot_key.key)
예제 #18
0
    def run_test_simple_rotate(self, message, rot):
        poly = Polynomial(self.degree // 2, message)

        plain = self.encoder.encode(message, self.scaling_factor)

        rot_message = [0] * poly.ring_degree
        for i in range(poly.ring_degree):
            rot_message[i] = poly.coeffs[(i + rot) % poly.ring_degree]

        ciph = self.encryptor.encrypt(plain)
        ciph_rot0 = ciph.c0.rotate(rot).mod_small(self.ciph_modulus)
        ciph_rot1 = ciph.c1.rotate(rot).mod_small(self.ciph_modulus)
        ciph_rot = Ciphertext(ciph_rot0, ciph_rot1, ciph.scaling_factor,
                              self.ciph_modulus)
        decryptor = CKKSDecryptor(self.params,
                                  SecretKey(self.secret_key.s.rotate(rot)))
        decrypted_rot = decryptor.decrypt(ciph_rot)
        decoded_rot = self.encoder.decode(decrypted_rot)

        check_complex_vector_approx_eq(rot_message, decoded_rot, error=0.005)
예제 #19
0
    def add_plain(self, ciph, plain):
        """Adds a ciphertext with a plaintext.

        Adds a ciphertext with a plaintext polynomial within the context.

        Args:
            ciph (Ciphertext): A ciphertext to add.
            plain (Plaintext): A plaintext to add.

        Returns:
            A Ciphertext which is the sum of the ciphertext and plaintext.
        """
        assert isinstance(ciph, Ciphertext)
        assert isinstance(plain, Plaintext)
        assert ciph.scaling_factor == plain.scaling_factor, "Scaling factors are not equal. " \
            + "Ciphertext scaling factor: %d bits, Plaintext scaling factor: %d bits" \
            % (math.log(ciph.scaling_factor, 2), math.log(plain.scaling_factor, 2))

        c0 = ciph.c0.add(plain.poly, ciph.modulus)
        c0 = c0.mod_small(ciph.modulus)

        return Ciphertext(c0, ciph.c1, ciph.scaling_factor, ciph.modulus)
예제 #20
0
    def multiply_plain(self, ciph, plain):
        """Multiplies a ciphertext with a plaintext.

        Multiplies a ciphertext with a plaintext polynomial within the context.

        Args:
            ciph (Ciphertext): A ciphertext to multiply.
            plain (Plaintext): A plaintext to multiply.

        Returns:
            A Ciphertext which is the product of the ciphertext and plaintext.
        """
        assert isinstance(ciph, Ciphertext)
        assert isinstance(plain, Plaintext)

        c0 = ciph.c0.multiply(plain.poly, ciph.modulus, crt=self.crt_context)
        c0 = c0.mod_small(ciph.modulus)

        c1 = ciph.c1.multiply(plain.poly, ciph.modulus, crt=self.crt_context)
        c1 = c1.mod_small(ciph.modulus)

        return Ciphertext(c0, c1, ciph.scaling_factor * plain.scaling_factor,
                          ciph.modulus)