From 4acc5a5afae8c6ecdf6f7e1438555582fb271357 Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Thu, 27 Feb 2025 09:03:03 +0000 Subject: [PATCH] restore keys tests added --- .../cryptosystems/EllipticCurveElGamal.py | 14 +++-- lightphe/cryptosystems/GoldwasserMicali.py | 20 +++++- lightphe/cryptosystems/OkamotoUchiyama.py | 14 +++-- tests/test_keys.py | 63 +++++++++++++++++++ 4 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 tests/test_keys.py diff --git a/lightphe/cryptosystems/EllipticCurveElGamal.py b/lightphe/cryptosystems/EllipticCurveElGamal.py index 62e9f4d..26b4cd1 100644 --- a/lightphe/cryptosystems/EllipticCurveElGamal.py +++ b/lightphe/cryptosystems/EllipticCurveElGamal.py @@ -46,8 +46,13 @@ def __init__( self.ecc = ECC(form_name=form, curve_name=curve) self.keys = keys or self.generate_keys(key_size or self.ecc.n.bit_length()) - self.keys["curve"] = curve - self.keys["form"] = form + + if curve is not None: + self.keys["curve"] = curve + + if form is not None: + self.keys["form"] = form + self.plaintext_modulo = self.ecc.modulo self.ciphertext_modulo = self.ecc.modulo @@ -74,7 +79,7 @@ def generate_keys(self, key_size: int): # public key Qa = self.ecc.G * ka - keys["public_key"]["Qa"] = Qa + keys["public_key"]["Qa"] = Qa.get_point() keys["private_key"]["ka"] = ka return keys @@ -98,7 +103,8 @@ def encrypt(self, plaintext: int, random_key: Optional[int] = None) -> tuple: ciphertext (tuple): c1 and c2 """ # public key - Qa = self.keys["public_key"]["Qa"] + x, y = self.keys["public_key"]["Qa"] + Qa = EllipticCurvePoint(x=x, y=y, curve=self.ecc.curve) # random key r = random_key or self.generate_random_key() diff --git a/lightphe/cryptosystems/GoldwasserMicali.py b/lightphe/cryptosystems/GoldwasserMicali.py index 7585e50..99563e1 100644 --- a/lightphe/cryptosystems/GoldwasserMicali.py +++ b/lightphe/cryptosystems/GoldwasserMicali.py @@ -53,7 +53,11 @@ def generate_keys(self, key_size: int) -> dict: # find non-residue x while True: x = random.randint(1, n - 1) - if math.gcd(x, n) == 1 and jacobi_symbol(x, p) == -1 and jacobi_symbol(x, q) == -1: + if ( + math.gcd(x, n) == 1 + and jacobi_symbol(x, p) == -1 + and jacobi_symbol(x, q) == -1 + ): break keys["public_key"]["n"] = n @@ -143,10 +147,14 @@ def decrypt(self, ciphertext: list) -> int: return int(m_binary, 2) def add(self, ciphertext1: list, ciphertext2: list) -> list: - raise ValueError("Goldwasser-Micali is not homomorphic with respect to the addition") + raise ValueError( + "Goldwasser-Micali is not homomorphic with respect to the addition" + ) def multiply(self, ciphertext1: int, ciphertext2: int) -> int: - raise ValueError("Goldwasser-Micali is not homomorphic with respect to the multiplication") + raise ValueError( + "Goldwasser-Micali is not homomorphic with respect to the multiplication" + ) def xor(self, ciphertext1: int, ciphertext2: int) -> list: """ @@ -159,10 +167,16 @@ def xor(self, ciphertext1: int, ciphertext2: int) -> list: Returns: ciphertext3 (int): 3rd ciphertext created with Goldwasser-Micali """ + if len(ciphertext1) != len(ciphertext2): + raise ValueError( + "Ciphertexts must be of the same length while xoring in Goldwasser-Micali." + f"But c1 is length of {len(ciphertext1)} and c2 is length of {len(ciphertext2)}." + ) ciphertext3 = [] for i in range(0, len(ciphertext1)): c1 = ciphertext1[i] c2 = ciphertext2[i] + ciphertext3.append((c1 * c2) % self.ciphertext_modulo) return ciphertext3 diff --git a/lightphe/cryptosystems/OkamotoUchiyama.py b/lightphe/cryptosystems/OkamotoUchiyama.py index b4c7dbd..32fb2c6 100644 --- a/lightphe/cryptosystems/OkamotoUchiyama.py +++ b/lightphe/cryptosystems/OkamotoUchiyama.py @@ -22,7 +22,7 @@ def __init__(self, keys: Optional[dict] = None, key_size: Optional[int] = None): key_size (int): key size in bits """ self.keys = keys or self.generate_keys(key_size or 1024) - self.plaintext_modulo = self.keys["private_key"]["p"] + self.plaintext_modulo = self.keys["public_key"]["n"] self.ciphertext_modulo = self.keys["public_key"]["n"] def generate_keys(self, key_size: int) -> dict: @@ -128,10 +128,14 @@ def add(self, ciphertext1: int, ciphertext2: int) -> int: return (ciphertext1 * ciphertext2) % n def multiply(self, ciphertext1: int, ciphertext2: int) -> int: - raise ValueError("Okamoto-Uchiyama is not homomorphic with respect to the multiplication") + raise ValueError( + "Okamoto-Uchiyama is not homomorphic with respect to the multiplication" + ) def xor(self, ciphertext1: int, ciphertext2: int) -> int: - raise ValueError("Okamoto-Uchiyama is not homomorphic with respect to the exclusive or") + raise ValueError( + "Okamoto-Uchiyama is not homomorphic with respect to the exclusive or" + ) def multiply_by_contant(self, ciphertext: int, constant: int) -> int: """ @@ -175,7 +179,9 @@ def lx(self, x: int) -> int: """ p = self.keys["private_key"]["p"] if x % p != 1: - raise ValueError(f"Input passed to lx ({x}) must be identical to 1 in modulo {p}") + raise ValueError( + f"Input passed to lx ({x}) must be identical to 1 in modulo {p}" + ) if math.gcd(x, p * p) != 1: raise ValueError(f"gcd({x}, {p}^2) must be equal to 1") y = (x - 1) // p diff --git a/tests/test_keys.py b/tests/test_keys.py new file mode 100644 index 0000000..d85fce3 --- /dev/null +++ b/tests/test_keys.py @@ -0,0 +1,63 @@ +import traceback + +# 3rd party dependencies +import pytest +from lightphe import LightPHE + +# project dependencies +from lightphe.commons.logger import Logger + +logger = Logger(module="tests/test_keys.py") + + +def test_key_restoration(): + algorithms = [ + "RSA", + "ElGamal", + "Exponential-ElGamal", + "Paillier", + "Damgard-Jurik", + "Okamoto-Uchiyama", + "Benaloh", + "Naccache-Stern", + "Goldwasser-Micali", + "EllipticCurve-ElGamal", + ] + + for algorithm_name in algorithms: + private_key_file = f"/tmp/{algorithm_name}_secret.json" + public_key_file = f"/tmp/{algorithm_name}_public.json" + onprem_cs = LightPHE(algorithm_name=algorithm_name) + onprem_cs.export_keys(private_key_file) + onprem_cs.export_keys(public_key_file, public=True) + del onprem_cs + + cloud_cs = LightPHE(algorithm_name=algorithm_name, key_file=public_key_file) + + m1 = 17 + m2 = 23 + k = 3 + + c1 = cloud_cs.encrypt(m1) + c2 = cloud_cs.encrypt(m2) + + if algorithm_name in ["RSA", "ElGamal"]: + c3 = c1 * c2 + elif algorithm_name in ["Goldwasser-Micali"]: + c3 = c1 ^ c2 + else: + c3 = c1 + c2 + c4 = k * c1 + + # restore on prem cryptosystem + onprem_cs = LightPHE(algorithm_name=algorithm_name, key_file=private_key_file) + + if algorithm_name in ["RSA", "ElGamal"]: + assert onprem_cs.decrypt(c3) == m1 * m2 + elif algorithm_name in ["Goldwasser-Micali"]: + assert onprem_cs.decrypt(c3) == m1 ^ m2 + else: + assert onprem_cs.decrypt(c3) == m1 + m2 + assert onprem_cs.decrypt(c4) == k * m1 + + logger.info(f"✅ Key restoration tesst succeeded for {algorithm_name}")