def main(): poly_degree = 8 ciph_modulus = 1 << 600 big_modulus = 1 << 1200 scaling_factor = 1 << 30 params = CKKSParameters(poly_degree=poly_degree, ciph_modulus=ciph_modulus, big_modulus=big_modulus, scaling_factor=scaling_factor) key_generator = CKKSKeyGenerator(params) public_key = key_generator.public_key secret_key = key_generator.secret_key relin_key = key_generator.relin_key encoder = CKKSEncoder(params) encryptor = CKKSEncryptor(params, public_key, secret_key) decryptor = CKKSDecryptor(params, secret_key) evaluator = CKKSEvaluator(params) message1 = [0.5, 0.3 + 0.2j, 0.78, 0.88j] message2 = [0.2, 0.11, 0.4 + 0.67j, 0.9 + 0.99j] plain1 = encoder.encode(message1, scaling_factor) plain2 = encoder.encode(message2, scaling_factor) ciph1 = encryptor.encrypt(plain1) ciph2 = encryptor.encrypt(plain2) ciph_prod = evaluator.multiply(ciph1, ciph2, relin_key) decrypted_prod = decryptor.decrypt(ciph_prod) decoded_prod = encoder.decode(decrypted_prod) print(decoded_prod)
class TestEncryptDecrypt(unittest.TestCase): def setUp(self): self.degree = 64 self.ciph_modulus = 1 << 1200 self.big_modulus = 1 << 1200 self.scaling_factor = 1 << 30 self.params = CKKSParameters(poly_degree=self.degree, ciph_modulus=self.ciph_modulus, big_modulus=self.big_modulus, scaling_factor=self.scaling_factor) key_generator = CKKSKeyGenerator(self.params) public_key = key_generator.public_key secret_key = key_generator.secret_key self.encoder = CKKSEncoder(self.params) self.encryptor = CKKSEncryptor(self.params, public_key, secret_key) self.decryptor = CKKSDecryptor(self.params, secret_key) def run_test_encrypt_decrypt(self, message): plain = self.encoder.encode(message, self.scaling_factor) ciphertext = self.encryptor.encrypt(plain) decrypted = self.decryptor.decrypt(ciphertext) decoded = self.encoder.decode(decrypted) check_complex_vector_approx_eq(message, decoded, 0.001) def run_test_secret_key_encrypt_decrypt(self, message): plain = self.encoder.encode(message, self.scaling_factor) ciphertext = self.encryptor.encrypt_with_secret_key(plain) decrypted = self.decryptor.decrypt(ciphertext) decoded = self.encoder.decode(decrypted) check_complex_vector_approx_eq(message, decoded, 0.001) def test_encrypt_decrypt_01(self): vec = sample_random_complex_vector(self.degree // 2) self.run_test_encrypt_decrypt(vec) def test_secret_key_encrypt_decrypt_01(self): vec = sample_random_complex_vector(self.degree // 2) self.run_test_secret_key_encrypt_decrypt(vec)
class TestBootstrapping(unittest.TestCase): def setUp(self): self.degree = int(arg) self.ciph_modulus = 1 << 40 self.big_modulus = 1 << 1200 self.scaling_factor = 1 << 30 self.params = CKKSParameters(poly_degree=self.degree, ciph_modulus=self.ciph_modulus, big_modulus=self.big_modulus, scaling_factor=self.scaling_factor, taylor_iterations=7, prime_size=None) self.key_generator = CKKSKeyGenerator(self.params) public_key = self.key_generator.public_key secret_key = self.key_generator.secret_key self.relin_key = self.key_generator.relin_key self.encoder = CKKSEncoder(self.params) self.encryptor = CKKSEncryptor(self.params, public_key, secret_key) self.decryptor = CKKSDecryptor(self.params, secret_key) self.evaluator = CKKSEvaluator(self.params) def run_test_bootstrap(self, message): num_slots = len(message) plain = self.encoder.encode(message, self.scaling_factor) ciph = self.encryptor.encrypt(plain) rot_keys = {} for i in range(num_slots): rot_keys[i] = self.key_generator.generate_rot_key(i) conj_key = self.key_generator.generate_conj_key() start_time = time.clock() ciph, new_ciph = self.evaluator.bootstrap(ciph, rot_keys, conj_key, self.relin_key, self.encoder) total_time = time.clock() - start_time new_plain = self.decryptor.decrypt(new_ciph) new_plain = self.encoder.decode(new_plain) check_complex_vector_approx_eq(message, new_plain, error=0.05) return total_time def test_bootstrap_time(self): self.params.print_parameters() num_iterations = 1 print("Number of bootstraps: %d" % (num_iterations)) total_time = 0 for _ in range(num_iterations): vec = sample_random_complex_vector(self.degree // 2) total_time += self.run_test_bootstrap(vec) print("Average time per bootstrap operation: %f seconds" % (total_time / num_iterations))
class TestMultiply(unittest.TestCase): def setUp(self): self.degree = int(arg) self.ciph_modulus = 1 << 600 self.big_modulus = 1 << 1200 self.scaling_factor = 1 << 30 self.params = CKKSParameters(poly_degree=self.degree, ciph_modulus=self.ciph_modulus, big_modulus=self.big_modulus, scaling_factor=self.scaling_factor) self.key_generator = CKKSKeyGenerator(self.params) public_key = self.key_generator.public_key secret_key = self.key_generator.secret_key self.relin_key = self.key_generator.relin_key self.encoder = CKKSEncoder(self.params) self.encryptor = CKKSEncryptor(self.params, public_key, secret_key) self.decryptor = CKKSDecryptor(self.params, secret_key) self.evaluator = CKKSEvaluator(self.params) def run_test_multiply(self, message1, message2): num_slots = len(message1) plain1 = self.encoder.encode(message1, self.scaling_factor) plain2 = self.encoder.encode(message2, self.scaling_factor) plain_prod = [0] * num_slots for i in range(num_slots): plain_prod[i] = message1[i] * message2[i] ciph1 = self.encryptor.encrypt(plain1) ciph2 = self.encryptor.encrypt(plain2) start_time = time.clock() ciph_prod = self.evaluator.multiply(ciph1, ciph2, self.relin_key) total_time = time.clock() - start_time decrypted_prod = self.decryptor.decrypt(ciph_prod) decoded_prod = self.encoder.decode(decrypted_prod) check_complex_vector_approx_eq(plain_prod, decoded_prod, error=0.01) return total_time def test_multiply_time(self): self.params.print_parameters() num_iterations = 1 print("Number of multiplications: %d" % (num_iterations)) total_time = 0 for _ in range(num_iterations): vec1 = sample_random_complex_vector(self.degree // 2) vec2 = sample_random_complex_vector(self.degree // 2) total_time += self.run_test_multiply(vec1, vec2) print("Average time per multiply operation: %f seconds" % (total_time / num_iterations))
class TestCKKSEncoder(unittest.TestCase): def setUp(self): self.ciph_modulus = 1 << 40 self.big_modulus = 1 << 1200 self.scaling_factor = 1 << 30 self.degree = 2048 params = CKKSParameters(poly_degree=self.degree, ciph_modulus=self.ciph_modulus, big_modulus=self.big_modulus, scaling_factor=self.scaling_factor) self.encoder = CKKSEncoder(params) def run_test_encode_decode(self, vec): """Checks that encode and decode are inverses. Encodes the input vector, decodes the result, and checks that they match. Args: vec (list (complex)): Vector of complex numbers to encode. Raises: ValueError: An error if test fails. """ plain = self.encoder.encode(vec, self.scaling_factor) value = self.encoder.decode(plain) check_complex_vector_approx_eq(vec, value, error=0.1) def run_test_multiply(self, vec1, vec2): """Checks that encode satisfies homomorphic multiplication. Encodes two input vectors, and check that their product matches before and after encoding. Before encoding, the product is component-wise, and after encoding, the product is polynomial, since the encoding includes an inverse FFT operation. Args: vec1 (list (complex)): First vector. vec2 (list (complex)): Second vector. Raises: ValueError: An error if test fails. """ orig_prod = [0] * (self.degree // 2) for i in range(self.degree // 2): orig_prod[i] = vec1[i] * vec2[i] plain1 = self.encoder.encode(vec1, self.scaling_factor) plain2 = self.encoder.encode(vec2, self.scaling_factor) plain_prod = Plaintext(plain1.poly.multiply_naive(plain2.poly), scaling_factor=self.scaling_factor**2) expected = self.encoder.decode(plain_prod) check_complex_vector_approx_eq(expected, orig_prod, error=0.1) def test_encode_decode_01(self): vec = sample_random_complex_vector(self.degree // 2) self.run_test_encode_decode(vec) def test_multiply_01(self): vec1 = sample_random_complex_vector(self.degree // 2) vec2 = sample_random_complex_vector(self.degree // 2) self.run_test_multiply(vec1, vec2)
class TestRotation(unittest.TestCase): def setUp(self): self.degree = 16 self.ciph_modulus = 1 << 1200 self.big_modulus = 1 << 1200 self.scaling_factor = 1 << 30 self.params = CKKSParameters(poly_degree=self.degree, ciph_modulus=self.ciph_modulus, big_modulus=self.big_modulus, scaling_factor=self.scaling_factor) self.key_generator = CKKSKeyGenerator(self.params) public_key = self.key_generator.public_key self.secret_key = self.key_generator.secret_key self.relin_key = self.key_generator.relin_key self.encoder = CKKSEncoder(self.params) self.encryptor = CKKSEncryptor(self.params, public_key) self.decryptor = CKKSDecryptor(self.params, self.secret_key) self.evaluator = CKKSEvaluator(self.params) 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) def run_test_rotate(self, message, r): 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 + r) % poly.ring_degree] ciph = self.encryptor.encrypt(plain) rot_key = self.key_generator.generate_rot_key(r) ciph_rot = self.evaluator.rotate(ciph, r, rot_key) decrypted_rot = self.decryptor.decrypt(ciph_rot) decoded_rot = self.encoder.decode(decrypted_rot) check_complex_vector_approx_eq(rot_message, decoded_rot, error=0.005) def run_test_conjugate(self, message): poly = Polynomial(self.degree // 2, message) plain = self.encoder.encode(message, self.scaling_factor) conj_message = [c.conjugate() for c in poly.coeffs] ciph = self.encryptor.encrypt(plain) conj_key = self.key_generator.generate_conj_key() ciph_conj = self.evaluator.conjugate(ciph, conj_key) decrypted_conj = self.decryptor.decrypt(ciph_conj) decoded_conj = self.encoder.decode(decrypted_conj) check_complex_vector_approx_eq(conj_message, decoded_conj, error=0.005) def run_test_multiply_matrix(self, message, mat): matrix_prod_message = matrix_vector_multiply(mat, message) plain = self.encoder.encode(message, self.scaling_factor) ciph = self.encryptor.encrypt(plain) rot_keys = {} matrix_len = len(mat) matrix_len_factor1 = int(sqrt(matrix_len)) if matrix_len != matrix_len_factor1 * matrix_len_factor1: matrix_len_factor1 = int(sqrt(2 * matrix_len)) matrix_len_factor2 = matrix_len // matrix_len_factor1 for i in range(1, matrix_len_factor1): rot_keys[i] = self.key_generator.generate_rot_key(i) for j in range(matrix_len_factor2): rot_keys[matrix_len_factor1 * j] = self.key_generator.generate_rot_key( matrix_len_factor1 * j) for i in range(matrix_len): rot_keys[i] = self.key_generator.generate_rot_key(i) ciph_prod = self.evaluator.multiply_matrix(ciph, mat, rot_keys, self.encoder) decrypted_prod = self.decryptor.decrypt(ciph_prod) decoded_prod = self.encoder.decode(decrypted_prod) check_complex_vector_approx_eq(matrix_prod_message, decoded_prod, error=0.01) def test_simple_rotate(self): vec = sample_random_complex_vector(self.degree // 2) rot = 1 self.run_test_simple_rotate(vec, rot) def test_rotate_01(self): vec = sample_random_complex_vector(self.degree // 2) rot = 2 self.run_test_rotate(vec, rot) def test_conjugate_01(self): vec = sample_random_complex_vector(self.degree // 2) self.run_test_conjugate(vec) def test_multiply_matrix_01(self): vec = sample_random_complex_vector(self.degree // 2) mat = [ sample_random_complex_vector(self.degree // 2) for _ in range(self.degree // 2) ] self.run_test_multiply_matrix(vec, mat)
class TestBootstrappingMethods(unittest.TestCase): def setUp(self): self.degree = 16 self.ciph_modulus = 1 << 600 self.big_modulus = 1 << 1200 self.scaling_factor = 1 << 30 self.params = CKKSParameters(poly_degree=self.degree, ciph_modulus=self.ciph_modulus, big_modulus=self.big_modulus, scaling_factor=self.scaling_factor) self.key_generator = CKKSKeyGenerator(self.params) public_key = self.key_generator.public_key secret_key = self.key_generator.secret_key self.relin_key = self.key_generator.relin_key self.encoder = CKKSEncoder(self.params) self.encryptor = CKKSEncryptor(self.params, public_key, secret_key) self.decryptor = CKKSDecryptor(self.params, secret_key) self.evaluator = CKKSEvaluator(self.params) def run_test_coeff_to_slot(self, message): num_slots = len(message) plain = self.encoder.encode(message, self.scaling_factor) plain_ans1 = mat.scalar_multiply(plain.poly.coeffs[:num_slots], 1 / self.scaling_factor) plain_ans2 = mat.scalar_multiply(plain.poly.coeffs[num_slots:], 1 / self.scaling_factor) ciph = self.encryptor.encrypt(plain) rot_keys = {} for i in range(num_slots): rot_keys[i] = self.key_generator.generate_rot_key(i) conj_key = self.key_generator.generate_conj_key() ciph1, ciph2 = self.evaluator.coeff_to_slot(ciph, rot_keys, conj_key, self.encoder) decrypted_1 = self.decryptor.decrypt(ciph1) decrypted_1 = self.encoder.decode(decrypted_1) decrypted_2 = self.decryptor.decrypt(ciph2) decrypted_2 = self.encoder.decode(decrypted_2) check_complex_vector_approx_eq(plain_ans1, decrypted_1, error=0.01) check_complex_vector_approx_eq(plain_ans2, decrypted_2, error=0.01) def run_test_slot_to_coeff(self, message): num_slots = len(message) plain = self.encoder.encode(message, self.scaling_factor) ciph = self.encryptor.encrypt(plain) rot_keys = {} for i in range(num_slots): rot_keys[i] = self.key_generator.generate_rot_key(i) conj_key = self.key_generator.generate_conj_key() ciph1, ciph2 = self.evaluator.coeff_to_slot(ciph, rot_keys, conj_key, self.encoder) decrypted_1 = self.decryptor.decrypt(ciph1) decrypted_1 = self.encoder.decode(decrypted_1) decrypted_2 = self.decryptor.decrypt(ciph2) decrypted_2 = self.encoder.decode(decrypted_2) ciph_ans = self.evaluator.slot_to_coeff(ciph1, ciph2, rot_keys, self.encoder) decrypted = self.decryptor.decrypt(ciph_ans) plain_ans1 = mat.scalar_multiply(decrypted.poly.coeffs[:num_slots], 1 / decrypted.scaling_factor) plain_ans2 = mat.scalar_multiply(decrypted.poly.coeffs[num_slots:], 1 / decrypted.scaling_factor) prim_root = math.e**(math.pi * 1j / 2 / num_slots) primitive_roots = [prim_root] * (num_slots) for i in range(1, num_slots): primitive_roots[i] = primitive_roots[i - 1]**5 mat_0 = [[1] * (num_slots) for _ in range(num_slots)] mat_1 = [[1] * (num_slots) for _ in range(num_slots)] for i in range(num_slots): for k in range(1, num_slots): mat_0[i][k] = mat_0[i][k - 1] * primitive_roots[i] for i in range(num_slots): mat_1[i][0] = mat_0[i][-1] * primitive_roots[i] for i in range(num_slots): for k in range(1, num_slots): mat_1[i][k] = mat_1[i][k - 1] * primitive_roots[i] plain_1 = mat.matrix_vector_multiply(mat_0, decrypted_1) plain_2 = mat.matrix_vector_multiply(mat_1, decrypted_2) new_plain = [plain_1[i] + plain_2[i] for i in range(num_slots)] encoded = self.encoder.encode(new_plain, self.scaling_factor) plain_check1 = mat.scalar_multiply(encoded.poly.coeffs[:num_slots], 1 / decrypted.scaling_factor) plain_check2 = mat.scalar_multiply(encoded.poly.coeffs[num_slots:], 1 / decrypted.scaling_factor) decrypted = self.encoder.decode(decrypted) check_complex_vector_approx_eq(decrypted, new_plain, error=0.001) check_complex_vector_approx_eq(plain_check1, plain_ans1, error=0.001) check_complex_vector_approx_eq(plain_check2, plain_ans2, error=0.001) check_complex_vector_approx_eq(decrypted_1, plain_ans1, error=0.001) check_complex_vector_approx_eq(decrypted_2, plain_ans2, error=0.001) check_complex_vector_approx_eq(message, new_plain, error=0.001) def run_test_exp(self, message): plain = self.encoder.encode(message, self.scaling_factor) const = 2 * math.pi plain_exp = taylor_exp(message, const, num_iterations=5) ciph = self.encryptor.encrypt(plain) ciph_exp = self.evaluator.exp(ciph, const, self.relin_key, self.encoder) decrypted_exp = self.decryptor.decrypt(ciph_exp) decrypted_exp = self.encoder.decode(decrypted_exp) check_complex_vector_approx_eq(plain_exp, decrypted_exp, error=0.1) def test_coeff_to_slot_01(self): vec = sample_random_complex_vector(self.degree // 2) self.run_test_coeff_to_slot(vec) def test_slot_to_coeff_01(self): vec = sample_random_complex_vector(self.degree // 2) self.run_test_slot_to_coeff(vec) def test_exp_01(self): vec = sample_random_complex_vector(self.degree // 2) self.run_test_exp(vec)
class TestBootstrapping(unittest.TestCase): def setUp(self): self.degree = 16 self.ciph_modulus = 1 << 40 self.big_modulus = 1 << 1200 self.scaling_factor = 1 << 30 self.params = CKKSParameters(poly_degree=self.degree, ciph_modulus=self.ciph_modulus, big_modulus=self.big_modulus, scaling_factor=self.scaling_factor) self.key_generator = CKKSKeyGenerator(self.params) public_key = self.key_generator.public_key secret_key = self.key_generator.secret_key self.relin_key = self.key_generator.relin_key self.encoder = CKKSEncoder(self.params) self.encryptor = CKKSEncryptor(self.params, public_key, secret_key) self.decryptor = CKKSDecryptor(self.params, secret_key) self.evaluator = CKKSEvaluator(self.params) self.num_taylor_exp_iterations = 12 def run_test_bootstrap_steps(self, message): # ------------------- SETUP -------------------- # num_slots = self.degree // 2 plain = self.encoder.encode(message, self.scaling_factor) ciph = self.encryptor.encrypt(plain) rot_keys = {} for i in range(num_slots): rot_keys[i] = self.key_generator.generate_rot_key(i) conj_key = self.key_generator.generate_conj_key() # Raise modulus. old_modulus = ciph.modulus old_scaling_factor = self.scaling_factor self.evaluator.raise_modulus(ciph) print(message) print("-----------------------") print(plain.poly.coeffs) plain = self.decryptor.decrypt(ciph) test_plain = Plaintext(plain.poly.mod_small(self.ciph_modulus), self.scaling_factor) print("-------- TEST --------") print(test_plain.poly.coeffs) print(self.encoder.decode(test_plain)) print("---------- BIT SIZE ------------") print(math.log(self.scaling_factor, 2)) print(math.log(self.ciph_modulus, 2)) print(math.log(abs(plain.poly.coeffs[0]), 2)) print("---------- MOD ------------") print(plain.poly.coeffs[0]) print(plain.poly.coeffs[0] > self.ciph_modulus / 2) print(math.sin(2 * math.pi * plain.poly.coeffs[0] / self.ciph_modulus)) print(2 * math.pi * (plain.p.coeffs[0] % self.ciph_modulus) / self.ciph_modulus) print( math.sin(2 * math.pi * plain.poly.coeffs[0] / self.ciph_modulus) * self.ciph_modulus / 2 / math.pi) print(plain.poly.coeffs[0] % self.ciph_modulus) # Coeff to slot. ciph0, ciph1 = self.evaluator.coeff_to_slot(ciph, rot_keys, conj_key, self.encoder) plain_slots0 = [ plain.poly.coeffs[i] / self.evaluator.scaling_factor for i in range(num_slots) ] plain_slots1 = [ plain.poly.coeffs[i] / self.evaluator.scaling_factor for i in range(num_slots, 2 * num_slots) ] print("----- COEFF TO SLOT -------") print(plain_slots0) print(plain_slots1) decrypted0 = self.decryptor.decrypt(ciph0) decoded0 = self.encoder.decode(decrypted0) decrypted1 = self.decryptor.decrypt(ciph1) decoded1 = self.encoder.decode(decrypted1) check_complex_vector_approx_eq(decoded0, plain_slots0, error_message="COEFF TO SLOT FAILED") check_complex_vector_approx_eq(decoded1, plain_slots1, error_message="COEFF TO SLOT FAILED") # Exponentiate. const = self.evaluator.scaling_factor / old_modulus * 2 * math.pi * 1j ciph_exp0 = self.evaluator.exp(ciph0, const, self.relin_key, self.encoder) ciph_neg_exp0 = self.evaluator.conjugate(ciph_exp0, conj_key) ciph_exp1 = self.evaluator.exp(ciph1, const, self.relin_key, self.encoder) ciph_neg_exp1 = self.evaluator.conjugate(ciph_exp1, conj_key) pre_exp0 = [plain_slots0[i] * const for i in range(num_slots)] pre_exp1 = [plain_slots1[i] * const for i in range(num_slots)] exp0 = [cmath.exp(pre_exp0[i]) for i in range(num_slots)] exp1 = [cmath.exp(pre_exp1[i]) for i in range(num_slots)] taylor_exp0 = taylor_exp(plain_slots0, const, num_iterations=self.num_taylor_exp_iterations) taylor_exp1 = taylor_exp(plain_slots1, const, num_iterations=self.num_taylor_exp_iterations) neg_exp0 = [cmath.exp(-pre_exp0[i]) for i in range(num_slots)] neg_exp1 = [cmath.exp(-pre_exp1[i]) for i in range(num_slots)] print("----- EXP -------") print("----- argument -----") print(pre_exp0) print(pre_exp1) print("---- actual exp ------") print(exp0) print(exp1) print("---- taylor series exp ----") print(taylor_exp0) print(taylor_exp1) print("---- actual negative exp -----") print(neg_exp0) print(neg_exp1) decrypted_exp0 = self.decryptor.decrypt(ciph_exp0) decoded_exp0 = self.encoder.decode(decrypted_exp0) decrypted_neg_exp0 = self.decryptor.decrypt(ciph_neg_exp0) decoded_neg_exp0 = self.encoder.decode(decrypted_neg_exp0) decrypted_exp1 = self.decryptor.decrypt(ciph_exp1) decoded_exp1 = self.encoder.decode(decrypted_exp1) decrypted_neg_exp1 = self.decryptor.decrypt(ciph_neg_exp1) decoded_neg_exp1 = self.encoder.decode(decrypted_neg_exp1) check_complex_vector_approx_eq(decoded_exp0, exp0, error=0.001, error_message="EXP FAILED") check_complex_vector_approx_eq(decoded_exp1, exp1, error=0.001, error_message="EXP FAILED") check_complex_vector_approx_eq(decoded_neg_exp0, neg_exp0, error=0.001, error_message="EXP FAILED") check_complex_vector_approx_eq(decoded_neg_exp1, neg_exp1, error=0.001, error_message="EXP FAILED") # Compute sine. ciph_sin0 = self.evaluator.subtract(ciph_exp0, ciph_neg_exp0) ciph_sin1 = self.evaluator.subtract(ciph_exp1, ciph_neg_exp1) sin0 = [(exp0[i] - neg_exp0[i]) / 2 / 1j for i in range(num_slots)] sin1 = [(exp1[i] - neg_exp1[i]) / 2 / 1j for i in range(num_slots)] # Scale sine. const = self.evaluator.create_complex_constant_plain( old_modulus / self.evaluator.scaling_factor * 0.25 / math.pi / 1j, self.encoder) ciph0 = self.evaluator.multiply_plain(ciph_sin0, const) ciph1 = self.evaluator.multiply_plain(ciph_sin1, const) ciph0 = self.evaluator.rescale(ciph0, self.evaluator.scaling_factor) ciph1 = self.evaluator.rescale(ciph1, self.evaluator.scaling_factor) print("----- SIN -------") print(sin0) print(sin1) sin_check0 = [cmath.sin(pre_exp0[i]) for i in range(num_slots)] sin_check1 = [cmath.sin(pre_exp1[i]) for i in range(num_slots)] print(sin_check0) print(sin_check1) scaled_sin0 = [ sin0[i] * self.ciph_modulus / self.evaluator.scaling_factor / 2 / math.pi for i in range(num_slots) ] scaled_sin1 = [ sin1[i] * self.ciph_modulus / self.evaluator.scaling_factor / 2 / math.pi for i in range(num_slots) ] print("----- SCALED SIN -------") print(scaled_sin0) print(scaled_sin1) expected_slots0 = [(plain.poly.coeffs[i] % self.ciph_modulus) / self.evaluator.scaling_factor for i in range(num_slots)] expected_slots1 = [(plain.poly.coeffs[i] % self.ciph_modulus) / self.evaluator.scaling_factor for i in range(num_slots, 2 * num_slots)] print(expected_slots0) print(expected_slots1) decrypted0 = self.decryptor.decrypt(ciph0) decoded0 = self.encoder.decode(decrypted0) decrypted1 = self.decryptor.decrypt(ciph1) decoded1 = self.encoder.decode(decrypted1) check_complex_vector_approx_eq(decoded0, scaled_sin0, error=0.1, error_message="SIN FAILED") check_complex_vector_approx_eq(decoded1, scaled_sin1, error=0.1, error_message="SIN FAILED") # Slot to coeff. ciph = self.evaluator.slot_to_coeff(ciph0, ciph1, rot_keys, self.encoder) # Reset scaling factor. self.scaling_factor = old_scaling_factor ciph.scaling_factor = self.scaling_factor new_plain = self.decryptor.decrypt(ciph) new_plain = self.encoder.decode(new_plain)\ print("-------- ANSWER -------") print(new_plain) check_complex_vector_approx_eq(message, new_plain, error=0.05, error_message="FINAL CHECK FAILED") print("------------ BOOTSTRAPPING MODULUS CHANGES -------------") print("Old modulus q: %d bits" % (int(math.log(old_modulus, 2)))) print("Raised modulus Q_0: %d bits" % (int(math.log(self.big_modulus, 2)))) print("Final modulus Q_1: %d bits" % (int(math.log(ciph.modulus, 2)))) def run_test_bootstrap(self, message): num_slots = len(message) plain = self.encoder.encode(message, self.scaling_factor) ciph = self.encryptor.encrypt(plain) rot_keys = {} for i in range(num_slots): rot_keys[i] = self.key_generator.generate_rot_key(i) conj_key = self.key_generator.generate_conj_key() ciph, new_ciph = self.evaluator.bootstrap(ciph, rot_keys, conj_key, self.relin_key, self.encoder) new_plain = self.decryptor.decrypt(new_ciph) new_plain = self.encoder.decode(new_plain) check_complex_vector_approx_eq(message, new_plain, error=0.05) def test_bootstrap_01(self): vec = sample_random_complex_vector(self.degree // 2) try: self.run_test_bootstrap(vec) except Exception: self.run_test_bootstrap_steps(vec)
class TestEvaluator(unittest.TestCase): def setUp(self): self.degree = 16 self.ciph_modulus = 1 << 600 self.big_modulus = 1 << 1200 self.scaling_factor = 1 << 30 self.params = CKKSParameters(poly_degree=self.degree, ciph_modulus=self.ciph_modulus, big_modulus=self.big_modulus, scaling_factor=self.scaling_factor) self.key_generator = CKKSKeyGenerator(self.params) public_key = self.key_generator.public_key secret_key = self.key_generator.secret_key self.relin_key = self.key_generator.relin_key self.encoder = CKKSEncoder(self.params) self.encryptor = CKKSEncryptor(self.params, public_key, secret_key) self.decryptor = CKKSDecryptor(self.params, secret_key) self.evaluator = CKKSEvaluator(self.params) def run_test_add(self, message1, message2): poly1 = Polynomial(self.degree // 2, message1) poly2 = Polynomial(self.degree // 2, message2) plain1 = self.encoder.encode(message1, self.scaling_factor) plain2 = self.encoder.encode(message2, self.scaling_factor) plain_sum = poly1.add(poly2) ciph1 = self.encryptor.encrypt(plain1) ciph2 = self.encryptor.encrypt(plain2) ciph_sum = self.evaluator.add(ciph1, ciph2) decrypted_sum = self.decryptor.decrypt(ciph_sum) decoded_sum = self.encoder.decode(decrypted_sum) check_complex_vector_approx_eq(plain_sum.coeffs, decoded_sum, error=0.005) def run_test_subtract(self, message1, message2): poly1 = Polynomial(self.degree // 2, message1) poly2 = Polynomial(self.degree // 2, message2) plain1 = self.encoder.encode(message1, self.scaling_factor) plain2 = self.encoder.encode(message2, self.scaling_factor) plain_diff = poly1.subtract(poly2) ciph1 = self.encryptor.encrypt(plain1) ciph2 = self.encryptor.encrypt(plain2) ciph_diff = self.evaluator.subtract(ciph1, ciph2) decrypted_diff = self.decryptor.decrypt(ciph_diff) decoded_diff = self.encoder.decode(decrypted_diff) check_complex_vector_approx_eq(plain_diff.coeffs, decoded_diff, error=0.005) def run_test_secret_key_add(self, message1, message2): poly1 = Polynomial(self.degree // 2, message1) poly2 = Polynomial(self.degree // 2, message2) plain1 = self.encoder.encode(message1, self.scaling_factor) plain2 = self.encoder.encode(message2, self.scaling_factor) plain_sum = poly1.add(poly2) ciph1 = self.encryptor.encrypt_with_secret_key(plain1) ciph2 = self.encryptor.encrypt_with_secret_key(plain2) ciph_sum = self.evaluator.add(ciph1, ciph2) decrypted_sum = self.decryptor.decrypt(ciph_sum) decoded_sum = self.encoder.decode(decrypted_sum) check_complex_vector_approx_eq(plain_sum.coeffs, decoded_sum, error=0.001) def run_test_add_plain(self, message1, message2): plain1 = self.encoder.encode(message1, self.scaling_factor) plain2 = self.encoder.encode(message2, self.scaling_factor) plain_sum = [0] * (self.degree // 2) for i in range(self.degree // 2): plain_sum[i] = message1[i] + message2[i] ciph1 = self.encryptor.encrypt(plain1) ciph_sum = self.evaluator.add_plain(ciph1, plain2) decrypted_sum = self.decryptor.decrypt(ciph_sum) decoded_sum = self.encoder.decode(decrypted_sum) check_complex_vector_approx_eq(plain_sum, decoded_sum, error=0.001) def run_test_multiply(self, message1, message2): num_slots = len(message1) plain1 = self.encoder.encode(message1, self.scaling_factor) plain2 = self.encoder.encode(message2, self.scaling_factor) plain_prod = [0] * num_slots for i in range(num_slots): plain_prod[i] = message1[i] * message2[i] ciph1 = self.encryptor.encrypt(plain1) ciph2 = self.encryptor.encrypt(plain2) ciph_prod = self.evaluator.multiply(ciph1, ciph2, self.relin_key) decrypted_prod = self.decryptor.decrypt(ciph_prod) decoded_prod = self.encoder.decode(decrypted_prod) check_complex_vector_approx_eq(plain_prod, decoded_prod, error=0.01) def run_test_secret_key_multiply(self, message1, message2): num_slots = len(message1) plain1 = self.encoder.encode(message1, self.scaling_factor) plain2 = self.encoder.encode(message2, self.scaling_factor) plain_prod = [0] * num_slots for i in range(num_slots): plain_prod[i] = message1[i] * message2[i] ciph1 = self.encryptor.encrypt_with_secret_key(plain1) ciph2 = self.encryptor.encrypt_with_secret_key(plain2) ciph_prod = self.evaluator.multiply(ciph1, ciph2, self.relin_key) decrypted_prod = self.decryptor.decrypt(ciph_prod) decoded_prod = self.encoder.decode(decrypted_prod) check_complex_vector_approx_eq(plain_prod, decoded_prod, error=0.001) def run_test_multiply_plain(self, message1, message2): plain1 = self.encoder.encode(message1, self.scaling_factor) plain2 = self.encoder.encode(message2, self.scaling_factor) plain_prod = [0] * (self.degree // 2) for i in range(self.degree // 2): plain_prod[i] = message1[i] * message2[i] ciph1 = self.encryptor.encrypt(plain1) ciph_prod = self.evaluator.multiply_plain(ciph1, plain2) decrypted_prod = self.decryptor.decrypt(ciph_prod) decoded_prod = self.encoder.decode(decrypted_prod) check_complex_vector_approx_eq(plain_prod, decoded_prod, error=0.001) def test_add_01(self): vec1 = sample_random_complex_vector(self.degree // 2) vec2 = sample_random_complex_vector(self.degree // 2) self.run_test_add(vec1, vec2) def test_secret_key_add_01(self): vec1 = sample_random_complex_vector(self.degree // 2) vec2 = sample_random_complex_vector(self.degree // 2) self.run_test_secret_key_add(vec1, vec2) def test_subtract_01(self): vec1 = sample_random_complex_vector(self.degree // 2) vec2 = sample_random_complex_vector(self.degree // 2) self.run_test_subtract(vec1, vec2) def test_multiply_01(self): vec1 = sample_random_complex_vector(self.degree // 2) vec2 = sample_random_complex_vector(self.degree // 2) self.run_test_multiply(vec1, vec2) def test_secret_key_multiply_01(self): vec1 = sample_random_complex_vector(self.degree // 2) vec2 = sample_random_complex_vector(self.degree // 2) self.run_test_secret_key_multiply(vec1, vec2) def test_multiply_plain_01(self): vec1 = sample_random_complex_vector(self.degree // 2) vec2 = sample_random_complex_vector(self.degree // 2) self.run_test_multiply_plain(vec1, vec2)