Skip to content

Commit 1876686

Browse files
authored
Merge pull request #54 from serengil/feat-task-2702-goldwasser-micali-xor-with-different-bit-length-inputs
different length of inputs are working now
2 parents cee7536 + 2820e32 commit 1876686

File tree

3 files changed

+46
-33
lines changed

3 files changed

+46
-33
lines changed

lightphe/cryptosystems/GoldwasserMicali.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
# built-in dependencies
12
import random
2-
from typing import Optional
3+
from typing import Optional, List
34
import math
5+
6+
# 3rd party dependencies
47
import sympy
58
from sympy import jacobi_symbol
9+
10+
# project dependencies
611
from lightphe.models.Homomorphic import Homomorphic
712
from lightphe.commons.logger import Logger
813

@@ -14,7 +19,7 @@
1419
class GoldwasserMicali(Homomorphic):
1520
"""
1621
Goldwasser-Micali algorithm is homomorphic with respect to the Exclusively OR (XOR).
17-
Ref: https://sefiks.com/2023/10/27/a-step-by-step-partially-homomorphic-encryption-example-with-goldwasser-micali-in-python/
22+
Ref: sefiks.com/2023/10/27/a-step-by-step-partially-homomorphic-encryption-example-with-goldwasser-micali-in-python/
1823
"""
1924

2025
def __init__(self, keys: Optional[dict] = None, key_size: Optional[int] = None):
@@ -44,9 +49,11 @@ def generate_keys(self, key_size: int) -> dict:
4449

4550
# picking a prime p
4651
p = sympy.randprime(200, 2 ** int(key_size / 2) - 1)
52+
assert isinstance(p, int)
4753

4854
# picking a prime q
4955
q = sympy.randprime(200, 2 ** int(key_size / 2) - 1)
56+
assert isinstance(q, int)
5057

5158
n = p * q
5259

@@ -81,7 +88,7 @@ def generate_random_key(self) -> int:
8188
break
8289
return r
8390

84-
def encrypt(self, plaintext: int, random_key: Optional[int] = None) -> list:
91+
def encrypt(self, plaintext: int, random_key: Optional[int] = None) -> List[int]:
8592
"""
8693
Encrypt a given plaintext for optionally given random key with Goldwasser-Micali
8794
Args:
@@ -116,7 +123,7 @@ def encrypt(self, plaintext: int, random_key: Optional[int] = None) -> list:
116123

117124
return c
118125

119-
def decrypt(self, ciphertext: list) -> int:
126+
def decrypt(self, ciphertext: List[int]) -> int:
120127
"""
121128
Decrypt a given ciphertext with Goldwasser-Micali
122129
Args:
@@ -156,22 +163,28 @@ def multiply(self, ciphertext1: int, ciphertext2: int) -> int:
156163
"Goldwasser-Micali is not homomorphic with respect to the multiplication"
157164
)
158165

159-
def xor(self, ciphertext1: int, ciphertext2: int) -> list:
166+
def xor(self, ciphertext1: List[int], ciphertext2: List[int]) -> List[int]:
160167
"""
161168
Perform homomorphic xor on encrypted data.
162169
Result of this must be equal to E(m1 ^ m2) = E(m1) ^ E(m2)
163170
Encryption calculations are done in module n
164171
Args:
165-
ciphertext1 (int): 1st ciphertext created with Goldwasser-Micali
166-
ciphertext2 (int): 2nd ciphertext created with Goldwasser-Micali
172+
ciphertext1 (list of int): 1st ciphertext created with Goldwasser-Micali
173+
ciphertext2 (list of int): 2nd ciphertext created with Goldwasser-Micali
167174
Returns:
168-
ciphertext3 (int): 3rd ciphertext created with Goldwasser-Micali
175+
ciphertext3 (list of int): 3rd ciphertext created with Goldwasser-Micali
169176
"""
170-
if len(ciphertext1) != len(ciphertext2):
171-
raise ValueError(
172-
"Ciphertexts must be of the same length while xoring in Goldwasser-Micali."
173-
f"But c1 is length of {len(ciphertext1)} and c2 is length of {len(ciphertext2)}."
174-
)
177+
if len(ciphertext1) > len(ciphertext2):
178+
ciphertext2 = [self.encrypt(0)[0]] * (
179+
len(ciphertext1) - len(ciphertext2)
180+
) + ciphertext2
181+
elif len(ciphertext2) > len(ciphertext1):
182+
ciphertext1 = [self.encrypt(0)[0]] * (
183+
len(ciphertext2) - len(ciphertext1)
184+
) + ciphertext1
185+
186+
assert len(ciphertext1) == len(ciphertext2)
187+
175188
ciphertext3 = []
176189
for i in range(0, len(ciphertext1)):
177190
c1 = ciphertext1[i]

tests/test_goldwasser.py

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,29 @@ def test_api():
3939

4040
cs = LightPHE(algorithm_name="Goldwasser-Micali")
4141

42-
m1 = 17
43-
m2 = 21
42+
# try different bit size messages
43+
ms = [(17, 21), (117, 23), (23, 117), (1117, 23), (12, 1118)]
4444

45-
c1 = cs.encrypt(plaintext=m1)
46-
c2 = cs.encrypt(plaintext=m2)
45+
for m1, m2 in ms:
46+
c1 = cs.encrypt(plaintext=m1)
47+
c2 = cs.encrypt(plaintext=m2)
4748

48-
# homomorphic addition
49-
assert cs.decrypt(c1 ^ c2) == m1 ^ m2
49+
# homomorphic addition
50+
assert cs.decrypt(c1 ^ c2) == m1 ^ m2
5051

51-
# unsupported homomorphic operations
52-
with pytest.raises(ValueError):
53-
_ = c1 * c2
52+
# unsupported homomorphic operations
53+
with pytest.raises(ValueError):
54+
_ = c1 * c2
5455

55-
with pytest.raises(ValueError):
56-
_ = c1 + c2
56+
with pytest.raises(ValueError):
57+
_ = c1 + c2
5758

58-
with pytest.raises(ValueError):
59-
_ = c1 * 5
59+
with pytest.raises(ValueError):
60+
_ = c1 * 5
6061

61-
with pytest.raises(ValueError):
62-
_ = 5 * c1
62+
with pytest.raises(ValueError):
63+
_ = 5 * c1
6364

64-
logger.info("✅ Goldwasser-Micali api test succeeded")
65+
logger.info(
66+
f"✅ Goldwasser-Micali api test succeeded for {m1.bit_length()}&{m2.bit_length()} bit plaintexts"
67+
)

tests/test_keys.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import traceback
2-
31
# 3rd party dependencies
4-
import pytest
52
from lightphe import LightPHE
63

74
# project dependencies
@@ -34,7 +31,7 @@ def test_key_restoration():
3431

3532
cloud_cs = LightPHE(algorithm_name=algorithm_name, key_file=public_key_file)
3633

37-
m1 = 17
34+
m1 = 217
3835
m2 = 23
3936
k = 3
4037

0 commit comments

Comments
 (0)