|
| 1 | +# built-in dependencies |
1 | 2 | import random
|
2 |
| -from typing import Optional |
| 3 | +from typing import Optional, List |
3 | 4 | import math
|
| 5 | + |
| 6 | +# 3rd party dependencies |
4 | 7 | import sympy
|
5 | 8 | from sympy import jacobi_symbol
|
| 9 | + |
| 10 | +# project dependencies |
6 | 11 | from lightphe.models.Homomorphic import Homomorphic
|
7 | 12 | from lightphe.commons.logger import Logger
|
8 | 13 |
|
|
14 | 19 | class GoldwasserMicali(Homomorphic):
|
15 | 20 | """
|
16 | 21 | 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/ |
18 | 23 | """
|
19 | 24 |
|
20 | 25 | def __init__(self, keys: Optional[dict] = None, key_size: Optional[int] = None):
|
@@ -44,9 +49,11 @@ def generate_keys(self, key_size: int) -> dict:
|
44 | 49 |
|
45 | 50 | # picking a prime p
|
46 | 51 | p = sympy.randprime(200, 2 ** int(key_size / 2) - 1)
|
| 52 | + assert isinstance(p, int) |
47 | 53 |
|
48 | 54 | # picking a prime q
|
49 | 55 | q = sympy.randprime(200, 2 ** int(key_size / 2) - 1)
|
| 56 | + assert isinstance(q, int) |
50 | 57 |
|
51 | 58 | n = p * q
|
52 | 59 |
|
@@ -81,7 +88,7 @@ def generate_random_key(self) -> int:
|
81 | 88 | break
|
82 | 89 | return r
|
83 | 90 |
|
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]: |
85 | 92 | """
|
86 | 93 | Encrypt a given plaintext for optionally given random key with Goldwasser-Micali
|
87 | 94 | Args:
|
@@ -116,7 +123,7 @@ def encrypt(self, plaintext: int, random_key: Optional[int] = None) -> list:
|
116 | 123 |
|
117 | 124 | return c
|
118 | 125 |
|
119 |
| - def decrypt(self, ciphertext: list) -> int: |
| 126 | + def decrypt(self, ciphertext: List[int]) -> int: |
120 | 127 | """
|
121 | 128 | Decrypt a given ciphertext with Goldwasser-Micali
|
122 | 129 | Args:
|
@@ -156,22 +163,28 @@ def multiply(self, ciphertext1: int, ciphertext2: int) -> int:
|
156 | 163 | "Goldwasser-Micali is not homomorphic with respect to the multiplication"
|
157 | 164 | )
|
158 | 165 |
|
159 |
| - def xor(self, ciphertext1: int, ciphertext2: int) -> list: |
| 166 | + def xor(self, ciphertext1: List[int], ciphertext2: List[int]) -> List[int]: |
160 | 167 | """
|
161 | 168 | Perform homomorphic xor on encrypted data.
|
162 | 169 | Result of this must be equal to E(m1 ^ m2) = E(m1) ^ E(m2)
|
163 | 170 | Encryption calculations are done in module n
|
164 | 171 | 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 |
167 | 174 | Returns:
|
168 |
| - ciphertext3 (int): 3rd ciphertext created with Goldwasser-Micali |
| 175 | + ciphertext3 (list of int): 3rd ciphertext created with Goldwasser-Micali |
169 | 176 | """
|
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 | + |
175 | 188 | ciphertext3 = []
|
176 | 189 | for i in range(0, len(ciphertext1)):
|
177 | 190 | c1 = ciphertext1[i]
|
|
0 commit comments