lightphe 0.0.21__tar.gz → 0.0.23__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. {lightphe-0.0.21 → lightphe-0.0.23}/PKG-INFO +2 -2
  2. {lightphe-0.0.21 → lightphe-0.0.23}/README.md +1 -1
  3. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/__init__.py +1 -1
  4. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/BonehGohNissim.py +64 -16
  5. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/SanderYoungYung.py +2 -2
  6. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe.egg-info/PKG-INFO +2 -2
  7. {lightphe-0.0.21 → lightphe-0.0.23}/setup.py +1 -1
  8. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_benaloh.py +1 -32
  9. lightphe-0.0.23/tests/test_bonehgohnissim.py +251 -0
  10. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_ciphertexts.py +2 -4
  11. lightphe-0.0.23/tests/test_damgard.py +34 -0
  12. lightphe-0.0.23/tests/test_elgamal.py +64 -0
  13. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_ellipticcurveelgamal.py +2 -1
  14. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_exporting_keys.py +1 -1
  15. lightphe-0.0.23/tests/test_goldwasser.py +40 -0
  16. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_keys.py +2 -5
  17. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_naccache.py +0 -95
  18. lightphe-0.0.23/tests/test_okamoto.py +34 -0
  19. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_paillier.py +2 -36
  20. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_rsa.py +1 -31
  21. lightphe-0.0.23/tests/test_salary.py +24 -0
  22. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_sanderyoungyung.py +1 -1
  23. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_tensors.py +25 -11
  24. lightphe-0.0.21/tests/test_bonehgohnissim.py +0 -143
  25. lightphe-0.0.21/tests/test_damgard.py +0 -63
  26. lightphe-0.0.21/tests/test_elgamal.py +0 -139
  27. lightphe-0.0.21/tests/test_goldwasser.py +0 -83
  28. lightphe-0.0.21/tests/test_okamoto.py +0 -61
  29. lightphe-0.0.21/tests/test_salary.py +0 -53
  30. {lightphe-0.0.21 → lightphe-0.0.23}/LICENSE +0 -0
  31. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/commons/__init__.py +0 -0
  32. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/commons/logger.py +0 -0
  33. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/commons/phe_utils.py +0 -0
  34. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/Benaloh.py +0 -0
  35. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/DamgardJurik.py +0 -0
  36. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/ElGamal.py +0 -0
  37. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/EllipticCurveElGamal.py +0 -0
  38. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/GoldwasserMicali.py +0 -0
  39. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/NaccacheStern.py +0 -0
  40. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/OkamotoUchiyama.py +0 -0
  41. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/Paillier.py +0 -0
  42. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/RSA.py +0 -0
  43. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/cryptosystems/__init__.py +0 -0
  44. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/models/Algorithm.py +0 -0
  45. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/models/Ciphertext.py +0 -0
  46. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/models/Homomorphic.py +0 -0
  47. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/models/Tensor.py +0 -0
  48. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe/models/__init__.py +0 -0
  49. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe.egg-info/SOURCES.txt +0 -0
  50. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe.egg-info/dependency_links.txt +0 -0
  51. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe.egg-info/requires.txt +0 -0
  52. {lightphe-0.0.21 → lightphe-0.0.23}/lightphe.egg-info/top_level.txt +0 -0
  53. {lightphe-0.0.21 → lightphe-0.0.23}/requirements.txt +0 -0
  54. {lightphe-0.0.21 → lightphe-0.0.23}/setup.cfg +0 -0
  55. {lightphe-0.0.21 → lightphe-0.0.23}/tests/__init__.py +0 -0
  56. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_cloud.py +0 -0
  57. {lightphe-0.0.21 → lightphe-0.0.23}/tests/test_large_keys.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightphe
3
- Version: 0.0.21
3
+ Version: 0.0.23
4
4
  Summary: A Lightweight Partially Homomorphic Encryption Library for Python
5
5
  Home-page: https://github.com/serengil/LightPHE
6
6
  Author: Sefik Ilkin Serengil
@@ -36,7 +36,7 @@ License-File: LICENSE
36
36
 
37
37
  <p align="center"><img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/phi.png" width="200" height="240"></p>
38
38
 
39
- LightPHE is a lightweight partially homomorphic encryption library for python. It is a hybrid homomoprhic encryption library wrapping many schemes such as [`RSA`](https://sefiks.com/2023/03/06/a-step-by-step-partially-homomorphic-encryption-example-with-rsa-in-python/), [`ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Exponential ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Elliptic Curve ElGamal`](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) ([`Weierstrass`](https://sefiks.com/2016/03/13/the-math-behind-elliptic-curve-cryptography/), [`Koblitz`](sefiks.com/2016/03/13/the-math-behind-elliptic-curves-over-binary-field/) and [`Edwards`](https://sefiks.com/2018/12/19/a-gentle-introduction-to-edwards-curves/) forms), [`Paillier`](https://sefiks.com/2023/04/03/a-step-by-step-partially-homomorphic-encryption-example-with-paillier-in-python/), [`Damgard-Jurik`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-damgard-jurik-in-python/), [`Okamoto–Uchiyama`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-okamoto-uchiyama-in-python/), [`Benaloh`](https://sefiks.com/2023/10/06/a-step-by-step-partially-homomorphic-encryption-example-with-benaloh-in-python-from-scratch/), [`Naccache–Stern`](https://sefiks.com/2023/10/26/a-step-by-step-partially-homomorphic-encryption-example-with-naccache-stern-in-python/), [`Goldwasser–Micali`](https://sefiks.com/2023/10/27/a-step-by-step-partially-homomorphic-encryption-example-with-goldwasser-micali-in-python/), `Sander-Young-Yung`, `Boneh-Goh-Nissim`.
39
+ LightPHE is a lightweight homomorphic encryption library for python supporting various partially and somewhat homomorphic encryptions schemes such as [`RSA`](https://sefiks.com/2023/03/06/a-step-by-step-partially-homomorphic-encryption-example-with-rsa-in-python/), [`ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Exponential ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Elliptic Curve ElGamal`](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) ([`Weierstrass`](https://sefiks.com/2016/03/13/the-math-behind-elliptic-curve-cryptography/), [`Koblitz`](sefiks.com/2016/03/13/the-math-behind-elliptic-curves-over-binary-field/) and [`Edwards`](https://sefiks.com/2018/12/19/a-gentle-introduction-to-edwards-curves/) forms), [`Paillier`](https://sefiks.com/2023/04/03/a-step-by-step-partially-homomorphic-encryption-example-with-paillier-in-python/), [`Damgard-Jurik`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-damgard-jurik-in-python/), [`Okamoto–Uchiyama`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-okamoto-uchiyama-in-python/), [`Benaloh`](https://sefiks.com/2023/10/06/a-step-by-step-partially-homomorphic-encryption-example-with-benaloh-in-python-from-scratch/), [`Naccache–Stern`](https://sefiks.com/2023/10/26/a-step-by-step-partially-homomorphic-encryption-example-with-naccache-stern-in-python/), [`Goldwasser–Micali`](https://sefiks.com/2023/10/27/a-step-by-step-partially-homomorphic-encryption-example-with-goldwasser-micali-in-python/), [`Sander-Young-Yung`](https://sefiks.com/2026/04/02/a-step-by-step-partially-homomorphic-sander-young-yung-example-in-python/), [`Boneh-Goh-Nissim`](https://sefiks.com/2026/04/02/a-step-by-step-somewhat-homomorphic-encryption-example-with-boneh-goh-nissim-in-python/).
40
40
 
41
41
  # Partially vs Fully Homomorphic Encryption
42
42
 
@@ -20,7 +20,7 @@
20
20
 
21
21
  <p align="center"><img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/phi.png" width="200" height="240"></p>
22
22
 
23
- LightPHE is a lightweight partially homomorphic encryption library for python. It is a hybrid homomoprhic encryption library wrapping many schemes such as [`RSA`](https://sefiks.com/2023/03/06/a-step-by-step-partially-homomorphic-encryption-example-with-rsa-in-python/), [`ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Exponential ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Elliptic Curve ElGamal`](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) ([`Weierstrass`](https://sefiks.com/2016/03/13/the-math-behind-elliptic-curve-cryptography/), [`Koblitz`](sefiks.com/2016/03/13/the-math-behind-elliptic-curves-over-binary-field/) and [`Edwards`](https://sefiks.com/2018/12/19/a-gentle-introduction-to-edwards-curves/) forms), [`Paillier`](https://sefiks.com/2023/04/03/a-step-by-step-partially-homomorphic-encryption-example-with-paillier-in-python/), [`Damgard-Jurik`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-damgard-jurik-in-python/), [`Okamoto–Uchiyama`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-okamoto-uchiyama-in-python/), [`Benaloh`](https://sefiks.com/2023/10/06/a-step-by-step-partially-homomorphic-encryption-example-with-benaloh-in-python-from-scratch/), [`Naccache–Stern`](https://sefiks.com/2023/10/26/a-step-by-step-partially-homomorphic-encryption-example-with-naccache-stern-in-python/), [`Goldwasser–Micali`](https://sefiks.com/2023/10/27/a-step-by-step-partially-homomorphic-encryption-example-with-goldwasser-micali-in-python/), `Sander-Young-Yung`, `Boneh-Goh-Nissim`.
23
+ LightPHE is a lightweight homomorphic encryption library for python supporting various partially and somewhat homomorphic encryptions schemes such as [`RSA`](https://sefiks.com/2023/03/06/a-step-by-step-partially-homomorphic-encryption-example-with-rsa-in-python/), [`ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Exponential ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Elliptic Curve ElGamal`](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) ([`Weierstrass`](https://sefiks.com/2016/03/13/the-math-behind-elliptic-curve-cryptography/), [`Koblitz`](sefiks.com/2016/03/13/the-math-behind-elliptic-curves-over-binary-field/) and [`Edwards`](https://sefiks.com/2018/12/19/a-gentle-introduction-to-edwards-curves/) forms), [`Paillier`](https://sefiks.com/2023/04/03/a-step-by-step-partially-homomorphic-encryption-example-with-paillier-in-python/), [`Damgard-Jurik`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-damgard-jurik-in-python/), [`Okamoto–Uchiyama`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-okamoto-uchiyama-in-python/), [`Benaloh`](https://sefiks.com/2023/10/06/a-step-by-step-partially-homomorphic-encryption-example-with-benaloh-in-python-from-scratch/), [`Naccache–Stern`](https://sefiks.com/2023/10/26/a-step-by-step-partially-homomorphic-encryption-example-with-naccache-stern-in-python/), [`Goldwasser–Micali`](https://sefiks.com/2023/10/27/a-step-by-step-partially-homomorphic-encryption-example-with-goldwasser-micali-in-python/), [`Sander-Young-Yung`](https://sefiks.com/2026/04/02/a-step-by-step-partially-homomorphic-sander-young-yung-example-in-python/), [`Boneh-Goh-Nissim`](https://sefiks.com/2026/04/02/a-step-by-step-somewhat-homomorphic-encryption-example-with-boneh-goh-nissim-in-python/).
24
24
 
25
25
  # Partially vs Fully Homomorphic Encryption
26
26
 
@@ -36,7 +36,7 @@ from lightphe.cryptosystems.BonehGohNissim import BonehGohNissim
36
36
 
37
37
  logger = Logger(module="lightphe/__init__.py")
38
38
 
39
- VERSION = "0.0.21"
39
+ VERSION = "0.0.23"
40
40
 
41
41
 
42
42
  class LightPHE:
@@ -21,8 +21,8 @@ class BonehGohNissim(Homomorphic):
21
21
  """
22
22
  Boneh-Goh-Nissim algorithm is homomorphic with respect to the addition.
23
23
  It's also somehow homomorphic with respect to the multiplication
24
- only for one multiplication per ciphertext
25
- TODO: add a tutorial for this algorithm
24
+ only for one multiplication per ciphertext.
25
+ Ref: https://sefiks.com/2026/04/02/a-step-by-step-somewhat-homomorphic-encryption-example-with-boneh-goh-nissim-in-python/
26
26
  """
27
27
 
28
28
  def __init__(
@@ -54,7 +54,10 @@ class BonehGohNissim(Homomorphic):
54
54
  },
55
55
  )
56
56
 
57
- self.plaintext_modulo = self.ec.modulo
57
+ if "private_key" in self.keys and "q2" in self.keys["private_key"]:
58
+ self.plaintext_modulo = self.keys["private_key"]["q2"]
59
+ else:
60
+ self.plaintext_modulo = self.keys["public_key"]["curve"]["n"]
58
61
  self.ciphertext_modulo = self.ec.modulo
59
62
 
60
63
  def generate_keys(
@@ -160,14 +163,28 @@ class BonehGohNissim(Homomorphic):
160
163
 
161
164
  raise Exception(f"Failed to find a generator after {max_tries} attempts")
162
165
 
166
+ def _has_private_key(self) -> bool:
167
+ return "private_key" in self.keys and "q2" in self.keys["private_key"]
168
+
163
169
  def generate_random_key(self) -> int:
164
170
  """
165
171
  Boneh-Goh-Nissim requires to generate one-time random key per encryption
166
172
  Returns:
167
173
  random key (int): one time random key for encryption
168
174
  """
169
- q2 = self.keys["private_key"]["q2"]
170
- return random.randint(1, q2 - 1)
175
+ if self._has_private_key():
176
+ q2 = self.keys["private_key"]["q2"]
177
+ return random.randint(1, q2 - 1)
178
+
179
+ n = self.keys["public_key"]["curve"]["n"]
180
+ logger.warn(
181
+ "Private key is not available. Random key will be bounded by n"
182
+ " instead of q2. Encryption will work for small plaintexts,"
183
+ " but negative number encoding via plaintext_modulo will not"
184
+ " round-trip correctly because plaintext_modulo is set to n"
185
+ " (public) instead of q2 (private)."
186
+ )
187
+ return random.randint(1, n - 1)
171
188
 
172
189
  def encrypt(
173
190
  self, plaintext: int, random_key: Optional[int] = None
@@ -200,23 +217,28 @@ class BonehGohNissim(Homomorphic):
200
217
  plaintext (int): restored message
201
218
  """
202
219
  q1 = self.keys["private_key"]["q1"]
220
+ q2 = self.keys["private_key"]["q2"]
203
221
 
204
222
  if isinstance(ciphertext, tuple):
205
- return self._decrypt_gt(ciphertext, q1)
223
+ return self._decrypt_gt(ciphertext, q1, q2)
206
224
 
207
225
  G = self.ec.G
208
226
 
209
227
  mP = ciphertext * q1
210
228
  P = G * q1
211
229
 
212
- for i in range(1, q1 + 1):
230
+ # P has order q2, so DLP result is in [0, q2)
231
+ if mP == self.ec.O:
232
+ return 0
233
+
234
+ for i in range(1, q2):
213
235
  iP = P * i
214
236
  if iP == mP:
215
237
  return i
216
238
 
217
239
  raise Exception("Decryption failed")
218
240
 
219
- def _decrypt_gt(self, ciphertext: tuple, q1: int) -> int:
241
+ def _decrypt_gt(self, ciphertext: tuple, q1: int, q2: int) -> int:
220
242
  """
221
243
  Decrypt a G_T ciphertext (result of homomorphic multiplication).
222
244
 
@@ -241,9 +263,12 @@ class BonehGohNissim(Homomorphic):
241
263
  base = _fp2_pow(e_gg, q1, p)
242
264
  target = _fp2_pow(ciphertext, q1, p)
243
265
 
244
- # brute-force DLP in G_T
266
+ # brute-force DLP in G_T — result is in [0, q2)
267
+ if target == (1, 0):
268
+ return 0
269
+
245
270
  acc = (1, 0)
246
- for i in range(1, q1 + 1):
271
+ for i in range(1, q2):
247
272
  acc = _fp2_mul(acc, base, p)
248
273
  if acc == target:
249
274
  return i
@@ -251,8 +276,10 @@ class BonehGohNissim(Homomorphic):
251
276
  raise Exception("Decryption of G_T ciphertext failed")
252
277
 
253
278
  def add(
254
- self, ciphertext1: EllipticCurvePoint, ciphertext2: EllipticCurvePoint
255
- ) -> EllipticCurvePoint:
279
+ self,
280
+ ciphertext1: Union[EllipticCurvePoint, Tuple[int, int]],
281
+ ciphertext2: Union[EllipticCurvePoint, Tuple[int, int]],
282
+ ) -> Union[EllipticCurvePoint, Tuple[int, int]]:
256
283
  """
257
284
  Add two ciphertexts with Boneh-Goh-Nissim
258
285
  Args:
@@ -261,7 +288,15 @@ class BonehGohNissim(Homomorphic):
261
288
  Returns:
262
289
  result (tuple): resulting ciphertext after addition
263
290
  """
264
- return ciphertext1 + ciphertext2
291
+ if isinstance(ciphertext1, EllipticCurvePoint) and isinstance(
292
+ ciphertext2, EllipticCurvePoint
293
+ ):
294
+ return ciphertext1 + ciphertext2
295
+
296
+ if isinstance(ciphertext1, tuple) and isinstance(ciphertext2, tuple):
297
+ return _fp2_mul(ciphertext1, ciphertext2, self.ec.modulo)
298
+
299
+ raise ValueError("Both ciphertexts must be of the same type for addition.")
265
300
 
266
301
  def multiply(
267
302
  self,
@@ -300,8 +335,8 @@ class BonehGohNissim(Homomorphic):
300
335
  return result
301
336
 
302
337
  def multiply_by_constant(
303
- self, ciphertext: EllipticCurvePoint, constant: int
304
- ) -> EllipticCurvePoint:
338
+ self, ciphertext: Union[EllipticCurvePoint, Tuple[int, int]], constant: int
339
+ ) -> Union[EllipticCurvePoint, Tuple[int, int]]:
305
340
  """
306
341
  Multiply a ciphertext by a constant with Boneh-Goh-Nissim
307
342
  Args:
@@ -310,7 +345,20 @@ class BonehGohNissim(Homomorphic):
310
345
  Returns:
311
346
  result (tuple): resulting ciphertext after multiplication
312
347
  """
313
- return ciphertext * constant
348
+ if isinstance(ciphertext, EllipticCurvePoint):
349
+ return ciphertext * constant
350
+ if isinstance(ciphertext, tuple):
351
+ # For GT elements, we can use exponentiation to achieve scalar multiplication
352
+ base = ciphertext
353
+ result = (1, 0) # identity element in F_{p^2}*
354
+ for _ in range(constant):
355
+ result = _fp2_mul(result, base, self.ec.modulo)
356
+ return result
357
+
358
+ raise ValueError(
359
+ "Ciphertext must be either an EllipticCurvePoint or a tuple "
360
+ "representing an F_{p^2} element for multiplication by constant."
361
+ )
314
362
 
315
363
  def reencrypt(self, ciphertext: EllipticCurvePoint) -> EllipticCurvePoint:
316
364
  """
@@ -17,8 +17,8 @@ logger = Logger(module="lightphe/cryptosystems/SanderYoungYung.py")
17
17
 
18
18
  class SanderYoungYung(Homomorphic):
19
19
  """
20
- Sander-Young-Yung algorithm is homomorphic with respect to the addition.
21
- TODO: add a tutorial for this algorithm.
20
+ Sander-Young-Yung algorithm is homomorphic with respect to bitwise AND.
21
+ Ref: https://sefiks.com/2026/04/02/a-step-by-step-partially-homomorphic-sander-young-yung-example-in-python/
22
22
  """
23
23
 
24
24
  def __init__(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightphe
3
- Version: 0.0.21
3
+ Version: 0.0.23
4
4
  Summary: A Lightweight Partially Homomorphic Encryption Library for Python
5
5
  Home-page: https://github.com/serengil/LightPHE
6
6
  Author: Sefik Ilkin Serengil
@@ -36,7 +36,7 @@ License-File: LICENSE
36
36
 
37
37
  <p align="center"><img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/phi.png" width="200" height="240"></p>
38
38
 
39
- LightPHE is a lightweight partially homomorphic encryption library for python. It is a hybrid homomoprhic encryption library wrapping many schemes such as [`RSA`](https://sefiks.com/2023/03/06/a-step-by-step-partially-homomorphic-encryption-example-with-rsa-in-python/), [`ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Exponential ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Elliptic Curve ElGamal`](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) ([`Weierstrass`](https://sefiks.com/2016/03/13/the-math-behind-elliptic-curve-cryptography/), [`Koblitz`](sefiks.com/2016/03/13/the-math-behind-elliptic-curves-over-binary-field/) and [`Edwards`](https://sefiks.com/2018/12/19/a-gentle-introduction-to-edwards-curves/) forms), [`Paillier`](https://sefiks.com/2023/04/03/a-step-by-step-partially-homomorphic-encryption-example-with-paillier-in-python/), [`Damgard-Jurik`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-damgard-jurik-in-python/), [`Okamoto–Uchiyama`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-okamoto-uchiyama-in-python/), [`Benaloh`](https://sefiks.com/2023/10/06/a-step-by-step-partially-homomorphic-encryption-example-with-benaloh-in-python-from-scratch/), [`Naccache–Stern`](https://sefiks.com/2023/10/26/a-step-by-step-partially-homomorphic-encryption-example-with-naccache-stern-in-python/), [`Goldwasser–Micali`](https://sefiks.com/2023/10/27/a-step-by-step-partially-homomorphic-encryption-example-with-goldwasser-micali-in-python/), `Sander-Young-Yung`, `Boneh-Goh-Nissim`.
39
+ LightPHE is a lightweight homomorphic encryption library for python supporting various partially and somewhat homomorphic encryptions schemes such as [`RSA`](https://sefiks.com/2023/03/06/a-step-by-step-partially-homomorphic-encryption-example-with-rsa-in-python/), [`ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Exponential ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Elliptic Curve ElGamal`](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) ([`Weierstrass`](https://sefiks.com/2016/03/13/the-math-behind-elliptic-curve-cryptography/), [`Koblitz`](sefiks.com/2016/03/13/the-math-behind-elliptic-curves-over-binary-field/) and [`Edwards`](https://sefiks.com/2018/12/19/a-gentle-introduction-to-edwards-curves/) forms), [`Paillier`](https://sefiks.com/2023/04/03/a-step-by-step-partially-homomorphic-encryption-example-with-paillier-in-python/), [`Damgard-Jurik`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-damgard-jurik-in-python/), [`Okamoto–Uchiyama`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-okamoto-uchiyama-in-python/), [`Benaloh`](https://sefiks.com/2023/10/06/a-step-by-step-partially-homomorphic-encryption-example-with-benaloh-in-python-from-scratch/), [`Naccache–Stern`](https://sefiks.com/2023/10/26/a-step-by-step-partially-homomorphic-encryption-example-with-naccache-stern-in-python/), [`Goldwasser–Micali`](https://sefiks.com/2023/10/27/a-step-by-step-partially-homomorphic-encryption-example-with-goldwasser-micali-in-python/), [`Sander-Young-Yung`](https://sefiks.com/2026/04/02/a-step-by-step-partially-homomorphic-sander-young-yung-example-in-python/), [`Boneh-Goh-Nissim`](https://sefiks.com/2026/04/02/a-step-by-step-somewhat-homomorphic-encryption-example-with-boneh-goh-nissim-in-python/).
40
40
 
41
41
  # Partially vs Fully Homomorphic Encryption
42
42
 
@@ -8,7 +8,7 @@ with open("requirements.txt", "r", encoding="utf-8") as f:
8
8
 
9
9
  setuptools.setup(
10
10
  name="lightphe",
11
- version="0.0.21",
11
+ version="0.0.23",
12
12
  author="Sefik Ilkin Serengil",
13
13
  author_email="serengil@gmail.com",
14
14
  description="A Lightweight Partially Homomorphic Encryption Library for Python",
@@ -1,44 +1,13 @@
1
1
  import pytest
2
- from lightphe.cryptosystems.Benaloh import Benaloh
3
2
  from lightphe.commons.logger import Logger
4
3
 
5
4
  logger = Logger(module="tests/test_benaloh.py")
6
5
 
7
6
 
8
- def test_benaloh():
9
- bn = Benaloh()
10
-
11
- m1 = bn.plaintext_modulo + 18
12
- m2 = bn.plaintext_modulo + 22
13
-
14
- c1 = bn.encrypt(m1)
15
- c2 = bn.encrypt(m2)
16
-
17
- # supported homomorphic operations
18
- assert bn.decrypt(bn.add(c1, c2)) == (m1 + m2) % bn.plaintext_modulo
19
- assert (
20
- bn.decrypt(bn.multiply_by_constant(c1, m2)) == (m1 * m2) % bn.plaintext_modulo
21
- )
22
-
23
- # unsupported homomorphic operations
24
- with pytest.raises(ValueError):
25
- bn.multiply(c1, m2)
26
-
27
- with pytest.raises(ValueError):
28
- bn.xor(c1, c2)
29
-
30
- # re-encryption
31
- c1_prime = bn.reencrypt(c1)
32
- assert c1_prime != c1
33
- assert bn.decrypt(c1_prime) == m1 % bn.plaintext_modulo
34
-
35
- logger.info("✅ Benaloh test succeeded")
36
-
37
-
38
7
  def test_api():
39
8
  from lightphe import LightPHE
40
9
 
41
- cs = LightPHE(algorithm_name="Benaloh") # , key_size=50)
10
+ cs = LightPHE(algorithm_name="Benaloh", key_size=50)
42
11
 
43
12
  n = cs.cs.keys["public_key"]["n"]
44
13
  r = cs.cs.keys["public_key"]["r"]
@@ -0,0 +1,251 @@
1
+ import time
2
+
3
+ import pytest
4
+
5
+ from lightphe import LightPHE
6
+ from lightphe.commons.logger import Logger
7
+
8
+ logger = Logger(module="tests/test_bonehgodnissim.py")
9
+
10
+ cs = LightPHE(
11
+ algorithm_name="Boneh-Goh-Nissim",
12
+ key_size=50,
13
+ max_tries=10000,
14
+ )
15
+ security_level = cs.cs.ec.n.bit_length()
16
+
17
+
18
+ def test_api():
19
+ tic = time.time()
20
+
21
+ m1 = 83
22
+ m2 = 31
23
+
24
+ c1 = cs.encrypt(plaintext=m1)
25
+ c2 = cs.encrypt(plaintext=m2)
26
+
27
+ # proof of decryption
28
+ assert cs.decrypt(c1) == m1
29
+ assert cs.decrypt(c2) == m2
30
+
31
+ # homomorphic addition
32
+ assert cs.decrypt(c1 + c2) == (m1 + m2) % cs.cs.plaintext_modulo
33
+
34
+ # homomorphic multiplication (once)
35
+ c1_times_c2 = c1 * c2
36
+ assert cs.decrypt(c1_times_c2) == (m1 * m2) % cs.cs.plaintext_modulo
37
+
38
+ # scalar multiplication
39
+ k = 2
40
+ assert cs.decrypt(c1 * k) == (m1 * k) % cs.cs.plaintext_modulo
41
+
42
+ # unsupported homomorphic operations
43
+ with pytest.raises(
44
+ ValueError,
45
+ match="Boneh-Goh-Nissim only supports multiplication ciphertexts once!",
46
+ ):
47
+ _ = c1_times_c2 * c2
48
+
49
+ with pytest.raises(ValueError):
50
+ _ = c1 & c2
51
+
52
+ with pytest.raises(ValueError):
53
+ _ = c1 ^ c2
54
+
55
+ # re-randomization
56
+ c1_prime = cs.cs.reencrypt(c1.value)
57
+ c2_prime = cs.cs.reencrypt(c2.value)
58
+ # assert c1.value != c1_prime
59
+ # assert c2.value != c2_prime
60
+ assert cs.cs.decrypt(c1_prime) == m1
61
+ assert cs.cs.decrypt(c2_prime) == m2
62
+
63
+ logger.info(
64
+ f"✅ Boneh-God-Nissim test succeeded ({security_level} bit ECC security level)"
65
+ f" in {time.time() - tic:.2f} seconds"
66
+ )
67
+
68
+
69
+ def test_linear_regression():
70
+ tic = time.time()
71
+
72
+ x1 = 5
73
+ w1 = 7
74
+
75
+ x2 = 3
76
+ w2 = 4
77
+
78
+ x1_enc = cs.encrypt(plaintext=x1)
79
+ w1_enc = cs.encrypt(plaintext=w1)
80
+
81
+ x2_enc = cs.encrypt(plaintext=x2)
82
+ w2_enc = cs.encrypt(plaintext=w2)
83
+
84
+ x1w1_enc = x1_enc * w1_enc
85
+ x2w2_enc = x2_enc * w2_enc
86
+
87
+ sum_enc = x1w1_enc + x2w2_enc
88
+
89
+ sum_decrypted = cs.decrypt(sum_enc)
90
+
91
+ assert sum_decrypted == (x1 * w1 + x2 * w2)
92
+
93
+ # multiplication by constant
94
+ _5x1w1_enc = cs.decrypt(x1w1_enc * 5)
95
+ assert _5x1w1_enc == (5 * x1 * w1)
96
+
97
+ # unsupported homomorphic operations
98
+ with pytest.raises(
99
+ ValueError,
100
+ match="Boneh-Goh-Nissim only supports multiplication ciphertexts once!",
101
+ ):
102
+ _ = x1w1_enc * w2_enc
103
+
104
+ logger.info(
105
+ f"✅ Boneh-God-Nissim linear regression test succeeded "
106
+ f"({security_level} bit ECC security level)"
107
+ f" in {time.time() - tic:.2f} seconds"
108
+ )
109
+
110
+
111
+ def test_cosine_similarity():
112
+ tic = time.time()
113
+ i1 = 5
114
+ i2 = 2
115
+ i3 = 8
116
+
117
+ # v2 = [1, 1, 2]
118
+ j1 = 1
119
+ j2 = 1
120
+ j3 = 2
121
+
122
+ i1_enc = cs.encrypt(i1)
123
+ i2_enc = cs.encrypt(i2)
124
+ i3_enc = cs.encrypt(i3)
125
+
126
+ j1_enc = cs.encrypt(j1)
127
+ j2_enc = cs.encrypt(j2)
128
+ j3_enc = cs.encrypt(j3)
129
+
130
+ # homomorphic operation
131
+ cosine_sim_enc = i1_enc * j1_enc + i2_enc * j2_enc + i3_enc * j3_enc
132
+
133
+ # proof of work
134
+ assert cs.decrypt(cosine_sim_enc) == i1 * j1 + i2 * j2 + i3 * j3
135
+
136
+ logger.info(
137
+ f"✅ Boneh-God-Nissim cosine similarity test succeeded "
138
+ f"({security_level} bit ECC security level)"
139
+ f" in {time.time() - tic:.2f} seconds"
140
+ )
141
+
142
+
143
+ def test_euclidean_distance():
144
+ tic = time.time()
145
+
146
+ # v1 = [5, 2, 3]
147
+ i1 = 5
148
+ i2 = 2
149
+ i3 = 8
150
+
151
+ # v2 = [1, 1, 2]
152
+ j1 = 1
153
+ j2 = 1
154
+ j3 = 2
155
+
156
+ i1_enc = cs.encrypt(i1)
157
+ i2_enc = cs.encrypt(i2)
158
+ i3_enc = cs.encrypt(i3)
159
+
160
+ j1_enc = cs.encrypt(-j1)
161
+ j2_enc = cs.encrypt(-j2)
162
+ j3_enc = cs.encrypt(-j3)
163
+
164
+ euclidean_sqrt_enc = (
165
+ (i1_enc + j1_enc) * (i1_enc + j1_enc)
166
+ + (i2_enc + j2_enc) * (i2_enc + j2_enc)
167
+ + (i3_enc + j3_enc) * (i3_enc + j3_enc)
168
+ )
169
+
170
+ assert (
171
+ cs.decrypt(euclidean_sqrt_enc)
172
+ == (i1 - j1) ** 2 + (i2 - j2) ** 2 + (i3 - j3) ** 2
173
+ )
174
+
175
+ logger.info(
176
+ f"✅ Boneh-God-Nissim euclidean distance test succeeded "
177
+ f"({security_level} bit ECC security level)"
178
+ f" in {time.time() - tic:.2f} seconds"
179
+ )
180
+
181
+
182
+ def test_api_with_predefined_keys():
183
+ keys = {
184
+ "private_key": {"q1": 1260048562698661, "q2": 1495813357973021},
185
+ "public_key": {
186
+ "curve": {
187
+ "a": 1,
188
+ "b": 0,
189
+ "p": 1477681217875020437035023206706703,
190
+ "G": (
191
+ 404050258967758769169929337984186,
192
+ 548422986186523785500578682099939,
193
+ ),
194
+ "n": 1884797471779362802340590824881,
195
+ },
196
+ "G": (404050258967758769169929337984186, 548422986186523785500578682099939),
197
+ "u": (
198
+ 496699107331579016081758964967239,
199
+ 1209427529257066171815901459972102,
200
+ ),
201
+ "h": (557434130026147587760748115479880, 109859910481881270030204038710204),
202
+ "l": 784,
203
+ },
204
+ }
205
+
206
+ bgn_cs = LightPHE(
207
+ algorithm_name="Boneh-Goh-Nissim",
208
+ keys=keys,
209
+ )
210
+ security_level = bgn_cs.cs.ec.n.bit_length()
211
+
212
+ m1 = 3
213
+ m2 = 2
214
+
215
+ c1 = bgn_cs.encrypt(plaintext=m1)
216
+ c2 = bgn_cs.encrypt(plaintext=m2)
217
+
218
+ # proof of decryption
219
+ assert bgn_cs.decrypt(c1) == m1
220
+ assert bgn_cs.decrypt(c2) == m2
221
+
222
+ # homomorphic addition
223
+ assert bgn_cs.decrypt(c1 + c2) == (m1 + m2) % bgn_cs.cs.plaintext_modulo
224
+
225
+ # homomorphic multiplication
226
+ c1_times_c2 = c1 * c2
227
+ assert bgn_cs.decrypt(c1_times_c2) == (m1 * m2) % bgn_cs.cs.plaintext_modulo
228
+
229
+ # scalar multiplication
230
+ k = 2
231
+ assert bgn_cs.decrypt(c1 * k) == (m1 * k) % bgn_cs.cs.plaintext_modulo
232
+
233
+ # unsupported homomorphic operations
234
+
235
+ with pytest.raises(ValueError):
236
+ _ = c1 & c2
237
+
238
+ with pytest.raises(ValueError):
239
+ _ = c1 ^ c2
240
+
241
+ # re-randomization
242
+ c1_prime = bgn_cs.cs.reencrypt(c1.value)
243
+ c2_prime = bgn_cs.cs.reencrypt(c2.value)
244
+ # assert c1.value != c1_prime
245
+ # assert c2.value != c2_prime
246
+ assert bgn_cs.cs.decrypt(c1_prime) == m1
247
+ assert bgn_cs.cs.decrypt(c2_prime) == m2
248
+
249
+ logger.info(
250
+ f"✅ Boneh-God-Nissim test succeeded ({security_level} bit ECC security level)"
251
+ )
@@ -6,10 +6,10 @@ from lightphe.commons.logger import Logger
6
6
 
7
7
  logger = Logger(module="tests/test_ciphertexts.py")
8
8
 
9
+ onprem_cs = LightPHE(algorithm_name="Paillier", key_size=50)
9
10
 
10
- def test_private_key_not_available_in_ciphertext():
11
- onprem_cs = LightPHE(algorithm_name="Paillier")
12
11
 
12
+ def test_private_key_not_available_in_ciphertext():
13
13
  m = 17
14
14
 
15
15
  c = onprem_cs.encrypt(m)
@@ -21,8 +21,6 @@ def test_private_key_not_available_in_ciphertext():
21
21
 
22
22
 
23
23
  def test_private_key_not_available_in_encrypted_tensor():
24
- onprem_cs = LightPHE(algorithm_name="Paillier")
25
-
26
24
  t = [1.5, 2.4, 3.3, 4.2, 5.1]
27
25
 
28
26
  c = onprem_cs.encrypt(t, silent=True)
@@ -0,0 +1,34 @@
1
+ import pytest
2
+ from lightphe.commons.logger import Logger
3
+
4
+ logger = Logger(module="tests/test_damgard.py")
5
+
6
+
7
+ def test_api():
8
+ from lightphe import LightPHE
9
+
10
+ cs = LightPHE(algorithm_name="Damgard-Jurik", key_size=50)
11
+
12
+ m1 = 17
13
+ m2 = 21
14
+
15
+ c1 = cs.encrypt(plaintext=m1)
16
+ c2 = cs.encrypt(plaintext=m2)
17
+
18
+ # homomorphic addition
19
+ assert cs.decrypt(c1 + c2) == m1 + m2
20
+
21
+ # homomorphic scalar multiplication
22
+ assert cs.decrypt(c1 * m2) == m1 * m2
23
+ assert cs.decrypt(c2 * m1) == m1 * m2
24
+ assert cs.decrypt(m2 * c1) == m1 * m2
25
+ assert cs.decrypt(m1 * c2) == m1 * m2
26
+
27
+ # unsupported homomorphic operations
28
+ with pytest.raises(ValueError):
29
+ _ = c1 * c2
30
+
31
+ with pytest.raises(ValueError):
32
+ _ = c1 ^ c2
33
+
34
+ logger.info("✅ Damgard-Jurik api test succeeded")
@@ -0,0 +1,64 @@
1
+ import pytest
2
+ from lightphe.commons.logger import Logger
3
+
4
+ logger = Logger(module="tests/test_elgamal.py")
5
+
6
+
7
+ def test_api_for_multiplicative():
8
+ from lightphe import LightPHE
9
+
10
+ cs = LightPHE(algorithm_name="ElGamal", key_size=50)
11
+
12
+ m1 = 17
13
+ m2 = 21
14
+
15
+ c1 = cs.encrypt(plaintext=m1)
16
+ c2 = cs.encrypt(plaintext=m2)
17
+
18
+ # homomorphic addition
19
+ assert cs.decrypt(c1 * c2) == m1 * m2
20
+
21
+ # unsupported homomorphic operations
22
+ with pytest.raises(ValueError):
23
+ _ = c1 + c2
24
+
25
+ with pytest.raises(ValueError):
26
+ _ = c1 ^ c2
27
+
28
+ with pytest.raises(ValueError):
29
+ _ = c1 * 5
30
+
31
+ with pytest.raises(ValueError):
32
+ _ = 5 * c1
33
+
34
+ logger.info("✅ ElGamal api test succeeded")
35
+
36
+
37
+ def test_api_for_additive():
38
+ from lightphe import LightPHE
39
+
40
+ cs = LightPHE(algorithm_name="Exponential-ElGamal", key_size=50)
41
+
42
+ m1 = 17
43
+ m2 = 21
44
+
45
+ c1 = cs.encrypt(plaintext=m1)
46
+ c2 = cs.encrypt(plaintext=m2)
47
+
48
+ # homomorphic addition
49
+ assert cs.decrypt(c1 + c2) == m1 + m2
50
+
51
+ # scalar multiplication
52
+ assert cs.decrypt(c1 * m2) == m1 * m2
53
+ assert cs.decrypt(c2 * m1) == m1 * m2
54
+ assert cs.decrypt(m2 * c1) == m1 * m2
55
+ assert cs.decrypt(m1 * c2) == m1 * m2
56
+
57
+ # unsupported homomorphic operations
58
+ with pytest.raises(ValueError):
59
+ _ = c1 * c2
60
+
61
+ with pytest.raises(ValueError):
62
+ _ = c1 ^ c2
63
+
64
+ logger.info("✅ Exponential ElGamal api test succeeded")