Skip to content

Commit

Permalink
Merge pull request #53 from serengil/feat-task-2702-bug-while-restori…
Browse files Browse the repository at this point in the history
…ng-keys

restore keys tests added
  • Loading branch information
serengil authored Feb 27, 2025
2 parents 4da15bf + 4acc5a5 commit cee7536
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 11 deletions.
14 changes: 10 additions & 4 deletions lightphe/cryptosystems/EllipticCurveElGamal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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()
Expand Down
20 changes: 17 additions & 3 deletions lightphe/cryptosystems/GoldwasserMicali.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
"""
Expand All @@ -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
Expand Down
14 changes: 10 additions & 4 deletions lightphe/cryptosystems/OkamotoUchiyama.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
"""
Expand Down Expand Up @@ -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
Expand Down
63 changes: 63 additions & 0 deletions tests/test_keys.py
Original file line number Diff line number Diff line change
@@ -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}")

0 comments on commit cee7536

Please sign in to comment.