lightphe 0.0.14__tar.gz → 0.0.16__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 (64) hide show
  1. {lightphe-0.0.14 → lightphe-0.0.16}/PKG-INFO +55 -24
  2. {lightphe-0.0.14 → lightphe-0.0.16}/README.md +54 -23
  3. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/__init__.py +7 -47
  4. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/cryptosystems/Benaloh.py +1 -7
  5. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/cryptosystems/DamgardJurik.py +1 -7
  6. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/cryptosystems/ElGamal.py +1 -4
  7. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/cryptosystems/EllipticCurveElGamal.py +6 -19
  8. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/cryptosystems/GoldwasserMicali.py +33 -36
  9. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/cryptosystems/NaccacheStern.py +103 -100
  10. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/cryptosystems/OkamotoUchiyama.py +1 -11
  11. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/cryptosystems/Paillier.py +1 -7
  12. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/cryptosystems/RSA.py +0 -12
  13. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/models/Ciphertext.py +2 -2
  14. lightphe-0.0.16/lightphe/models/Homomorphic.py +71 -0
  15. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/models/Tensor.py +2 -2
  16. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe.egg-info/PKG-INFO +55 -24
  17. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe.egg-info/SOURCES.txt +0 -13
  18. lightphe-0.0.16/lightphe.egg-info/requires.txt +4 -0
  19. lightphe-0.0.16/requirements.txt +4 -0
  20. {lightphe-0.0.14 → lightphe-0.0.16}/setup.py +1 -1
  21. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_benaloh.py +1 -1
  22. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_damgard.py +1 -1
  23. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_elgamal.py +2 -2
  24. lightphe-0.0.16/tests/test_ellipticcurveelgamal.py +134 -0
  25. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_goldwasser.py +10 -3
  26. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_keys.py +7 -2
  27. lightphe-0.0.16/tests/test_naccache.py +203 -0
  28. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_okamoto.py +2 -2
  29. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_paillier.py +1 -1
  30. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_rsa.py +1 -1
  31. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_salary.py +1 -1
  32. lightphe-0.0.14/lightphe/commons/binary_operations.py +0 -147
  33. lightphe-0.0.14/lightphe/elliptic_curve_forms/edwards.py +0 -89
  34. lightphe-0.0.14/lightphe/elliptic_curve_forms/koblitz.py +0 -166
  35. lightphe-0.0.14/lightphe/elliptic_curve_forms/weierstrass.py +0 -120
  36. lightphe-0.0.14/lightphe/models/Curve.py +0 -29
  37. lightphe-0.0.14/lightphe/models/EllipticCurve.py +0 -196
  38. lightphe-0.0.14/lightphe/models/Homomorphic.py +0 -52
  39. lightphe-0.0.14/lightphe/standard_curves/__init__.py +0 -0
  40. lightphe-0.0.14/lightphe/standard_curves/edwards.py +0 -144
  41. lightphe-0.0.14/lightphe/standard_curves/inventory.py +0 -83
  42. lightphe-0.0.14/lightphe/standard_curves/koblitz.py +0 -433
  43. lightphe-0.0.14/lightphe/standard_curves/weierstrass.py +0 -936
  44. lightphe-0.0.14/lightphe.egg-info/requires.txt +0 -3
  45. lightphe-0.0.14/requirements.txt +0 -3
  46. lightphe-0.0.14/tests/__init__.py +0 -0
  47. lightphe-0.0.14/tests/test_ellipticcurve.py +0 -75
  48. lightphe-0.0.14/tests/test_ellipticcurveelgamal.py +0 -263
  49. lightphe-0.0.14/tests/test_naccache.py +0 -94
  50. {lightphe-0.0.14 → lightphe-0.0.16}/LICENSE +0 -0
  51. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/commons/__init__.py +0 -0
  52. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/commons/logger.py +0 -0
  53. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/commons/phe_utils.py +0 -0
  54. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/cryptosystems/__init__.py +0 -0
  55. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe/models/Algorithm.py +0 -0
  56. {lightphe-0.0.14/lightphe/elliptic_curve_forms → lightphe-0.0.16/lightphe/models}/__init__.py +0 -0
  57. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe.egg-info/dependency_links.txt +0 -0
  58. {lightphe-0.0.14 → lightphe-0.0.16}/lightphe.egg-info/top_level.txt +0 -0
  59. {lightphe-0.0.14 → lightphe-0.0.16}/setup.cfg +0 -0
  60. {lightphe-0.0.14/lightphe/models → lightphe-0.0.16/tests}/__init__.py +0 -0
  61. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_ciphertexts.py +0 -0
  62. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_cloud.py +0 -0
  63. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_exporting_keys.py +0 -0
  64. {lightphe-0.0.14 → lightphe-0.0.16}/tests/test_tensors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightphe
3
- Version: 0.0.14
3
+ Version: 0.0.16
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
@@ -28,7 +28,7 @@ License-File: LICENSE
28
28
  [![YouTube](https://img.shields.io/:youtube-@sefiks-red.svg?style=flat&logo=youtube)](https://www.youtube.com/@sefiks?sub_confirmation=1)
29
29
  [![Twitter](https://img.shields.io/:follow-@serengil-blue.svg?style=flat&logo=x)](https://twitter.com/intent/user?screen_name=serengil)
30
30
 
31
- [![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dserengil%26type%3Dpatrons&style=flat)](https://www.patreon.com/serengil?repo=lightphe)
31
+ [![Patreon](https://img.shields.io/:become-patron-f96854.svg?style=flat&logo=patreon)](https://www.patreon.com/serengil?repo=lightphe)
32
32
  [![GitHub Sponsors](https://img.shields.io/github/sponsors/serengil?logo=GitHub&color=lightgray)](https://github.com/sponsors/serengil)
33
33
  [![Buy Me a Coffee](https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee)](https://buymeacoffee.com/serengil)
34
34
 
@@ -252,11 +252,9 @@ ECC is a powerful public-key cryptosystem based on the algebraic structure of el
252
252
  In LightPHE, the [Elliptic Curve ElGamal](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) scheme is implemented, offering a secure and efficient homomorphic encryption option.
253
253
 
254
254
  ```python
255
- forms = ["weierstass", "edwards", "koblitz"]
256
-
257
255
  phe = LightPHE(
258
256
  algorithm_name="EllipticCurve-ElGamal",
259
- form=forms[1],
257
+ form="edwards", # or weierstrass, koblitz
260
258
  )
261
259
  ```
262
260
 
@@ -264,7 +262,7 @@ One of the crucial factors that define the security level of an elliptic curve c
264
262
 
265
263
  Each curve in LightPHE has a specific order, which is carefully chosen to balance performance and security. By selecting an elliptic curve with a larger order, you increase the security of your cryptographic system, but this may come with a trade-off in computational efficiency. Therefore, choosing the appropriate curve order is a crucial decision based on your application’s security and performance requirements.
266
264
 
267
- See [`curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for a list of all supported forms, curves and their details.
265
+ See [`curves`](https://github.com/serengil/LightECC#supported-curves) page for a list of all supported forms, curves and their details.
268
266
 
269
267
  ### Vector Embeddings and Tensors
270
268
 
@@ -343,26 +341,50 @@ Many machine learning models rely on a two-tower architecture, including facial
343
341
  For example, suppose all facial embeddings in your database are encrypted on-prem in advance. When verifying an identity, the attempted facial embedding is generated on an edge device or in the cloud. You can compute the encrypted similarity by performing a dot product between the encrypted vector and the plain vector, ensuring secure comparison without decrypting sensitive data. Only additively homomorphic cryptosystems offer encrypted similarity calculation.
344
342
 
345
343
  ```python
346
- # build an additively homomorphic cryptosystem (e.g. Paillier)
347
- cs = LightPHE("Paillier")
348
-
349
- # define plain vectors - suppose those are l2 normalized already
344
+ # define a plain vectors for source and target
350
345
  alpha = [7.1, 5.2, 5.3, 2.4, 3.5, 4.6] # On-prem vector (user tower)
351
346
  beta = [5.6, 3.7, 2.8, 4, 0, 5.9] # Cloud vector (item tower)
347
+ expected_similarity = sum(x * y for x, y in zip(alpha, beta))
348
+
349
+ # build an additively homomorphic cryptosystem (e.g. Paillier) on-prem
350
+ cs = LightPHE(algorithm_name = "Paillier", precision = 19)
352
351
 
353
- # encrypt embedding
352
+ # export keys
353
+ cs.export_keys("secret.txt")
354
+ cs.export_keys("public.txt", public=True)
355
+
356
+ # encrypt source embedding
354
357
  encrypted_alpha = cs.encrypt(alpha)
355
358
 
356
- # dot product of encrypted embedding and plain embedding
359
+ # remove cryptosystem and plain alpha not to be leaked in cloud
360
+ del cs, alpha
361
+
362
+ # restore the cryptosystem in cloud with only public key
363
+ cloud_cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "public.txt")
364
+
365
+ # dot product of encrypted and plain embedding pair
357
366
  encrypted_cosine_similarity = encrypted_alpha @ beta
358
367
 
359
- # decrypt similarity
360
- cosine_similarity = cs.decrypt(encrypted_cosine_similarity)[0]
368
+ # computed by the cloud but cloud cannot decrypt it
369
+ with pytest.raises(ValueError, match="must have private key"):
370
+ cloud_cs.decrypt(encrypted_cosine_similarity)
371
+
372
+ # restore the cryptosystem on-prem with secret key
373
+ cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "secret.txt")
374
+
375
+ # decrypt similarity (on prem)
376
+ calculated_similarity = cs.decrypt(encrypted_cosine_similarity)[0]
361
377
 
362
378
  # proof of work
363
- assert abs(cosine_similarity - sum(x * y for x, y in zip(alpha, beta))) < 1e-2
379
+ assert abs(calculated_similarity - expected_similarity) < 1e-2
364
380
  ```
365
381
 
382
+ ## Security Concerns and Considerations
383
+
384
+ While LightPHE enables encryption using public keys and supports homomorphic operations on encrypted data, it's important to understand a subtle but critical security implication of this approach. Since the it is homomorphic, the cloud (or any third-party processor) can perform computations without knowing the plaintext. However, a malicious actor can still encrypt arbitrary values and craft valid ciphertexts because encryption depends on your public key and it is publicly known. For example, even if a salary is encrypted, an attacker could encrypt 500 USD using your public key and update the encrypted salary with this forged ciphertext — without needing to know the original value.
385
+
386
+ To mitigate this, we recommend combining LightPHE with digital signature schemes. By keeping a signed audit trail of update operations in a separate log table, you can detect and reject unauthorized changes, and even restore the original data in case of tampering. We suggest checking out [`LightDSA`](https://github.com/serengil/LightDSA), which provides a lightweight cryptographic interface similar to LightPHE and supports popular digital signature algorithms like RSA, DSA, ECDSA, and EdDSA.
387
+
366
388
  # Contributing
367
389
 
368
390
  All PRs are more than welcome! If you are planning to contribute a large patch, please create an issue first to get any upfront questions or design decisions out of the way first.
@@ -376,11 +398,11 @@ There are many ways to support a project - starring⭐️ the GitHub repo is jus
376
398
  You can also support this work on [Patreon](https://www.patreon.com/serengil?repo=lightphe), [GitHub Sponsors](https://github.com/sponsors/serengil) or [Buy Me a Coffee](https://buymeacoffee.com/serengil).
377
399
 
378
400
  <a href="https://www.patreon.com/serengil?repo=lightphe">
379
- <img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/patreon.png" width="30%" height="30%">
401
+ <img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/patreon.png" width="30%">
380
402
  </a>
381
403
 
382
404
  <a href="https://buymeacoffee.com/serengil">
383
- <img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/bmc-button.png" width="25%" height="25%">
405
+ <img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/bmc-button.png" width="25%">
384
406
  </a>
385
407
 
386
408
  Also, your company's logo will be shown on README on GitHub if you become sponsor in gold, silver or bronze tiers.
@@ -390,13 +412,22 @@ Also, your company's logo will be shown on README on GitHub if you become sponso
390
412
  Please cite LightPHE in your publications if it helps your research. Here is its BibTex entry:
391
413
 
392
414
  ```BibTeX
393
- @misc{serengil2024lightphe,
394
- title = {LightPHE: Integrating Partially Homomorphic Encryption into Python with Extensive Cloud Environment Evaluations},
395
- author = {Serengil, Sefik Ilkin and Ozpinar, Alper},
396
- year = {2024},
397
- publisher = {arXiv},
398
- url = {https://arxiv.org/abs/2408.05219},
399
- doi = {10.48550/arXiv.2408.05219}
415
+ @article{serengil2024lightphe,
416
+ title={LightPHE: Integrating Partially Homomorphic Encryption into Python with Extensive Cloud Environment Evaluations},
417
+ author={Serengil, Sefik Ilkin and Ozpinar, Alper},
418
+ journal={arXiv preprint arXiv:2408.05219},
419
+ note={doi: 10.48550/arXiv.2408.05219. [Online]. Available: \url{https://arxiv.org/abs/2408.05219}},
420
+ year={2025}
421
+ }
422
+ ```
423
+
424
+ ```BibTeX
425
+ @article{serengil2025vectorsimilarity,
426
+ title={Encrypted Vector Similarity Computations Using Partially Homomorphic Encryption: Applications and Performance Analysis},
427
+ author={Serengil, Sefik and Ozpinar, Alper},
428
+ journal={arXiv preprint arXiv:2503.05850},
429
+ note={doi: 10.48550/arXiv.2503.05850. [Online]. Available: \url{https://arxiv.org/abs/2503.05850}},
430
+ year={2025}
400
431
  }
401
432
  ```
402
433
 
@@ -12,7 +12,7 @@
12
12
  [![YouTube](https://img.shields.io/:youtube-@sefiks-red.svg?style=flat&logo=youtube)](https://www.youtube.com/@sefiks?sub_confirmation=1)
13
13
  [![Twitter](https://img.shields.io/:follow-@serengil-blue.svg?style=flat&logo=x)](https://twitter.com/intent/user?screen_name=serengil)
14
14
 
15
- [![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dserengil%26type%3Dpatrons&style=flat)](https://www.patreon.com/serengil?repo=lightphe)
15
+ [![Patreon](https://img.shields.io/:become-patron-f96854.svg?style=flat&logo=patreon)](https://www.patreon.com/serengil?repo=lightphe)
16
16
  [![GitHub Sponsors](https://img.shields.io/github/sponsors/serengil?logo=GitHub&color=lightgray)](https://github.com/sponsors/serengil)
17
17
  [![Buy Me a Coffee](https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee)](https://buymeacoffee.com/serengil)
18
18
 
@@ -236,11 +236,9 @@ ECC is a powerful public-key cryptosystem based on the algebraic structure of el
236
236
  In LightPHE, the [Elliptic Curve ElGamal](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) scheme is implemented, offering a secure and efficient homomorphic encryption option.
237
237
 
238
238
  ```python
239
- forms = ["weierstass", "edwards", "koblitz"]
240
-
241
239
  phe = LightPHE(
242
240
  algorithm_name="EllipticCurve-ElGamal",
243
- form=forms[1],
241
+ form="edwards", # or weierstrass, koblitz
244
242
  )
245
243
  ```
246
244
 
@@ -248,7 +246,7 @@ One of the crucial factors that define the security level of an elliptic curve c
248
246
 
249
247
  Each curve in LightPHE has a specific order, which is carefully chosen to balance performance and security. By selecting an elliptic curve with a larger order, you increase the security of your cryptographic system, but this may come with a trade-off in computational efficiency. Therefore, choosing the appropriate curve order is a crucial decision based on your application’s security and performance requirements.
250
248
 
251
- See [`curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for a list of all supported forms, curves and their details.
249
+ See [`curves`](https://github.com/serengil/LightECC#supported-curves) page for a list of all supported forms, curves and their details.
252
250
 
253
251
  ### Vector Embeddings and Tensors
254
252
 
@@ -327,26 +325,50 @@ Many machine learning models rely on a two-tower architecture, including facial
327
325
  For example, suppose all facial embeddings in your database are encrypted on-prem in advance. When verifying an identity, the attempted facial embedding is generated on an edge device or in the cloud. You can compute the encrypted similarity by performing a dot product between the encrypted vector and the plain vector, ensuring secure comparison without decrypting sensitive data. Only additively homomorphic cryptosystems offer encrypted similarity calculation.
328
326
 
329
327
  ```python
330
- # build an additively homomorphic cryptosystem (e.g. Paillier)
331
- cs = LightPHE("Paillier")
332
-
333
- # define plain vectors - suppose those are l2 normalized already
328
+ # define a plain vectors for source and target
334
329
  alpha = [7.1, 5.2, 5.3, 2.4, 3.5, 4.6] # On-prem vector (user tower)
335
330
  beta = [5.6, 3.7, 2.8, 4, 0, 5.9] # Cloud vector (item tower)
331
+ expected_similarity = sum(x * y for x, y in zip(alpha, beta))
332
+
333
+ # build an additively homomorphic cryptosystem (e.g. Paillier) on-prem
334
+ cs = LightPHE(algorithm_name = "Paillier", precision = 19)
336
335
 
337
- # encrypt embedding
336
+ # export keys
337
+ cs.export_keys("secret.txt")
338
+ cs.export_keys("public.txt", public=True)
339
+
340
+ # encrypt source embedding
338
341
  encrypted_alpha = cs.encrypt(alpha)
339
342
 
340
- # dot product of encrypted embedding and plain embedding
343
+ # remove cryptosystem and plain alpha not to be leaked in cloud
344
+ del cs, alpha
345
+
346
+ # restore the cryptosystem in cloud with only public key
347
+ cloud_cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "public.txt")
348
+
349
+ # dot product of encrypted and plain embedding pair
341
350
  encrypted_cosine_similarity = encrypted_alpha @ beta
342
351
 
343
- # decrypt similarity
344
- cosine_similarity = cs.decrypt(encrypted_cosine_similarity)[0]
352
+ # computed by the cloud but cloud cannot decrypt it
353
+ with pytest.raises(ValueError, match="must have private key"):
354
+ cloud_cs.decrypt(encrypted_cosine_similarity)
355
+
356
+ # restore the cryptosystem on-prem with secret key
357
+ cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "secret.txt")
358
+
359
+ # decrypt similarity (on prem)
360
+ calculated_similarity = cs.decrypt(encrypted_cosine_similarity)[0]
345
361
 
346
362
  # proof of work
347
- assert abs(cosine_similarity - sum(x * y for x, y in zip(alpha, beta))) < 1e-2
363
+ assert abs(calculated_similarity - expected_similarity) < 1e-2
348
364
  ```
349
365
 
366
+ ## Security Concerns and Considerations
367
+
368
+ While LightPHE enables encryption using public keys and supports homomorphic operations on encrypted data, it's important to understand a subtle but critical security implication of this approach. Since the it is homomorphic, the cloud (or any third-party processor) can perform computations without knowing the plaintext. However, a malicious actor can still encrypt arbitrary values and craft valid ciphertexts because encryption depends on your public key and it is publicly known. For example, even if a salary is encrypted, an attacker could encrypt 500 USD using your public key and update the encrypted salary with this forged ciphertext — without needing to know the original value.
369
+
370
+ To mitigate this, we recommend combining LightPHE with digital signature schemes. By keeping a signed audit trail of update operations in a separate log table, you can detect and reject unauthorized changes, and even restore the original data in case of tampering. We suggest checking out [`LightDSA`](https://github.com/serengil/LightDSA), which provides a lightweight cryptographic interface similar to LightPHE and supports popular digital signature algorithms like RSA, DSA, ECDSA, and EdDSA.
371
+
350
372
  # Contributing
351
373
 
352
374
  All PRs are more than welcome! If you are planning to contribute a large patch, please create an issue first to get any upfront questions or design decisions out of the way first.
@@ -360,11 +382,11 @@ There are many ways to support a project - starring⭐️ the GitHub repo is jus
360
382
  You can also support this work on [Patreon](https://www.patreon.com/serengil?repo=lightphe), [GitHub Sponsors](https://github.com/sponsors/serengil) or [Buy Me a Coffee](https://buymeacoffee.com/serengil).
361
383
 
362
384
  <a href="https://www.patreon.com/serengil?repo=lightphe">
363
- <img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/patreon.png" width="30%" height="30%">
385
+ <img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/patreon.png" width="30%">
364
386
  </a>
365
387
 
366
388
  <a href="https://buymeacoffee.com/serengil">
367
- <img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/bmc-button.png" width="25%" height="25%">
389
+ <img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/bmc-button.png" width="25%">
368
390
  </a>
369
391
 
370
392
  Also, your company's logo will be shown on README on GitHub if you become sponsor in gold, silver or bronze tiers.
@@ -374,13 +396,22 @@ Also, your company's logo will be shown on README on GitHub if you become sponso
374
396
  Please cite LightPHE in your publications if it helps your research. Here is its BibTex entry:
375
397
 
376
398
  ```BibTeX
377
- @misc{serengil2024lightphe,
378
- title = {LightPHE: Integrating Partially Homomorphic Encryption into Python with Extensive Cloud Environment Evaluations},
379
- author = {Serengil, Sefik Ilkin and Ozpinar, Alper},
380
- year = {2024},
381
- publisher = {arXiv},
382
- url = {https://arxiv.org/abs/2408.05219},
383
- doi = {10.48550/arXiv.2408.05219}
399
+ @article{serengil2024lightphe,
400
+ title={LightPHE: Integrating Partially Homomorphic Encryption into Python with Extensive Cloud Environment Evaluations},
401
+ author={Serengil, Sefik Ilkin and Ozpinar, Alper},
402
+ journal={arXiv preprint arXiv:2408.05219},
403
+ note={doi: 10.48550/arXiv.2408.05219. [Online]. Available: \url{https://arxiv.org/abs/2408.05219}},
404
+ year={2025}
405
+ }
406
+ ```
407
+
408
+ ```BibTeX
409
+ @article{serengil2025vectorsimilarity,
410
+ title={Encrypted Vector Similarity Computations Using Partially Homomorphic Encryption: Applications and Performance Analysis},
411
+ author={Serengil, Sefik and Ozpinar, Alper},
412
+ journal={arXiv preprint arXiv:2503.05850},
413
+ note={doi: 10.48550/arXiv.2503.05850. [Online]. Available: \url{https://arxiv.org/abs/2503.05850}},
414
+ year={2025}
384
415
  }
385
416
  ```
386
417
 
@@ -9,13 +9,16 @@ import copy
9
9
 
10
10
  # 3rd party dependencies
11
11
  from tqdm import tqdm
12
+ from lightecc.interfaces.elliptic_curve import EllipticCurvePoint
13
+ from lightecc.forms.weierstrass import Weierstrass
14
+ from lightecc.forms.edwards import TwistedEdwards
15
+ from lightecc.forms.koblitz import Koblitz
12
16
 
13
17
  # project dependencies
14
18
  from lightphe.models.Homomorphic import Homomorphic
15
19
  from lightphe.models.Ciphertext import Ciphertext
16
20
  from lightphe.models.Algorithm import Algorithm
17
21
  from lightphe.models.Tensor import Fraction, EncryptedTensor
18
- from lightphe.models.EllipticCurve import EllipticCurvePoint
19
22
  from lightphe.commons import phe_utils
20
23
  from lightphe.commons.logger import Logger
21
24
 
@@ -29,16 +32,13 @@ from lightphe.cryptosystems.Benaloh import Benaloh
29
32
  from lightphe.cryptosystems.NaccacheStern import NaccacheStern
30
33
  from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali
31
34
  from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal
32
- from lightphe.elliptic_curve_forms.weierstrass import Weierstrass
33
- from lightphe.elliptic_curve_forms.edwards import TwistedEdwards
34
- from lightphe.elliptic_curve_forms.koblitz import Koblitz
35
35
 
36
36
 
37
37
  # pylint: disable=eval-used, simplifiable-if-expression, too-few-public-methods
38
38
 
39
39
  logger = Logger(module="lightphe/__init__.py")
40
40
 
41
- VERSION = "0.0.14"
41
+ VERSION = "0.0.16"
42
42
 
43
43
 
44
44
  class LightPHE:
@@ -94,7 +94,7 @@ class LightPHE:
94
94
 
95
95
  def __build_cryptosystem(
96
96
  self,
97
- algorithm_name: str,
97
+ algorithm_name: str = "Paillier",
98
98
  keys: Optional[dict] = None,
99
99
  key_size: Optional[int] = None,
100
100
  form: Optional[str] = None,
@@ -115,6 +115,7 @@ class LightPHE:
115
115
  algorithm_name (str): RSA | ElGamal | Exponential-ElGamal | EllipticCurve-ElGamal
116
116
  | Paillier | Damgard-Jurik | Okamoto-Uchiyama | Benaloh | Naccache-Stern
117
117
  | Goldwasser-Micali | Edwards-ElGamal
118
+ Default is Paillier.
118
119
  keys (dict): optional private-public key pair
119
120
  key_file (str): if keys are exported, you can load them into cryptosystem
120
121
  key_size (int): key size in bits
@@ -464,44 +465,3 @@ def encrypt_float(
464
465
  logger.error(f"Exception while running encrypt_float: {str(err)}")
465
466
  logger.error(traceback.format_exc())
466
467
  raise err
467
-
468
-
469
- class ECC:
470
- __version__ = VERSION
471
-
472
- def __init__(
473
- self, form_name: Optional[str] = None, curve_name: Optional[str] = None
474
- ):
475
- """
476
- Construct an Elliptic Curve over a finite field (prime or binary)
477
- Args:
478
- form_name (str): specifies the form of the elliptic curve.
479
- Options: 'weierstrass' (default), 'edwards', 'koblitz'.
480
- curve_name (str): specifies the elliptic curve to use.
481
- Options:
482
- - e.g. ed25519, ed448 for edwards form
483
- - e.g. secp256k1 for weierstrass form
484
- - e.g. k-409 for koblitz form
485
- List of all available curves:
486
- github.com/serengil/LightPHE/blob/master/lightphe/elliptic_curve_forms/README.md
487
- """
488
- if form_name is None or form_name == "weierstrass":
489
- self.curve = Weierstrass(curve=curve_name)
490
- elif form_name in "edwards":
491
- self.curve = TwistedEdwards(curve=curve_name)
492
- elif form_name in "koblitz":
493
- self.curve = Koblitz(curve=curve_name)
494
- else:
495
- raise ValueError(f"unimplemented curve form - {form_name}")
496
-
497
- # base point
498
- self.G = EllipticCurvePoint(self.curve.G[0], self.curve.G[1], self.curve)
499
-
500
- # order of the curve
501
- self.n = self.curve.n
502
-
503
- # point at infinity or neutral / identity element
504
- self.O = EllipticCurvePoint(self.curve.O[0], self.curve.O[1], self.curve)
505
-
506
- # modulo
507
- self.modulo = self.curve.modulo
@@ -169,13 +169,7 @@ class Benaloh(Homomorphic):
169
169
  n = self.keys["public_key"]["n"]
170
170
  return (ciphertext1 * ciphertext2) % n
171
171
 
172
- def multiply(self, ciphertext1: int, ciphertext2: int) -> int:
173
- raise ValueError("Benaloh is not homomorphic with respect to the multiplication")
174
-
175
- def xor(self, ciphertext1: int, ciphertext2: int) -> int:
176
- raise ValueError("Benaloh is not homomorphic with respect to the exclusive or")
177
-
178
- def multiply_by_contant(self, ciphertext: int, constant: int) -> int:
172
+ def multiply_by_constant(self, ciphertext: int, constant: int) -> int:
179
173
  """
180
174
  Multiply a ciphertext with a plain constant.
181
175
  Result of this must be equal to E(m1 * constant) where E(m1) = ciphertext
@@ -125,13 +125,7 @@ class DamgardJurik(Homomorphic):
125
125
  modulo = pow(n, s + 1)
126
126
  return (ciphertext1 * ciphertext2) % modulo
127
127
 
128
- def multiply(self, ciphertext1: int, ciphertext2: int) -> int:
129
- raise ValueError("Damgard-Jurik is not homomorphic with respect to the multiplication")
130
-
131
- def xor(self, ciphertext1: int, ciphertext2: int) -> int:
132
- raise ValueError("Damgard-Jurik is not homomorphic with respect to the exclusive or")
133
-
134
- def multiply_by_contant(self, ciphertext: int, constant: int) -> int:
128
+ def multiply_by_constant(self, ciphertext: int, constant: int) -> int:
135
129
  """
136
130
  Multiply a ciphertext by a known plain constant
137
131
  Result of this must be equal to E(m1 * m2), where E(m1) = ciphertext
@@ -169,10 +169,7 @@ class ElGamal(Homomorphic):
169
169
  p = self.keys["public_key"]["p"]
170
170
  return (ciphertext1[0] * ciphertext2[0]) % p, (ciphertext1[1] * ciphertext2[1]) % p
171
171
 
172
- def xor(self, ciphertext1: tuple, ciphertext2: tuple) -> int:
173
- raise ValueError("ElGamal is not homomorphic with respect to the exclusive or")
174
-
175
- def multiply_by_contant(self, ciphertext: tuple, constant: int) -> tuple:
172
+ def multiply_by_constant(self, ciphertext: tuple, constant: int) -> tuple:
176
173
  if self.exponential is False:
177
174
  raise ValueError("ElGamal is not supporting multiplying ciphertext by a known constant")
178
175
  p = self.keys["public_key"]["p"]
@@ -2,9 +2,12 @@
2
2
  import random
3
3
  from typing import Optional
4
4
 
5
+ # 3rd party dependencies
6
+ from lightecc import LightECC as ECC
7
+ from lightecc.interfaces.elliptic_curve import EllipticCurvePoint
8
+
5
9
  # project dependencies
6
10
  from lightphe.models.Homomorphic import Homomorphic
7
- from lightphe.models.EllipticCurve import EllipticCurvePoint
8
11
  from lightphe.commons.logger import Logger
9
12
 
10
13
  logger = Logger(module="lightphe/cryptosystems/EllipticCurveElGamal.py")
@@ -40,9 +43,6 @@ class EllipticCurveElGamal(Homomorphic):
40
43
  - secp256k1 for weierstrass form
41
44
  This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
42
45
  """
43
- # to avoid circular import, import ECC class here
44
- from lightphe import ECC
45
-
46
46
  self.ecc = ECC(form_name=form, curve_name=curve)
47
47
 
48
48
  self.keys = keys or self.generate_keys(key_size or self.ecc.n.bit_length())
@@ -142,11 +142,6 @@ class EllipticCurveElGamal(Homomorphic):
142
142
 
143
143
  return s_prime / self.ecc.G
144
144
 
145
- def multiply(self, ciphertext1: tuple, ciphertext2: tuple) -> tuple:
146
- raise ValueError(
147
- "Elliptic Curve ElGamal is not homomorphic with respect to the multiplication"
148
- )
149
-
150
145
  def add(self, ciphertext1: tuple, ciphertext2: tuple) -> tuple:
151
146
  """
152
147
  Perform homomorphic addition on encrypted data
@@ -171,12 +166,9 @@ class EllipticCurveElGamal(Homomorphic):
171
166
 
172
167
  return a.get_point(), b.get_point()
173
168
 
174
- def xor(self, ciphertext1: tuple, ciphertext2: tuple) -> int:
175
- raise ValueError(
176
- "Elliptic Curve ElGamal is not homomorphic with respect to the exclusive or"
177
- )
178
169
 
179
- def multiply_by_contant(self, ciphertext: tuple, constant: int) -> tuple:
170
+
171
+ def multiply_by_constant(self, ciphertext: tuple, constant: int) -> tuple:
180
172
  """
181
173
  Multiply a ciphertext with a plain constant.
182
174
  Result of this must be equal to k x E(m1) = E(m1 * k)
@@ -198,8 +190,3 @@ class EllipticCurveElGamal(Homomorphic):
198
190
  Q_prime = Q * constant
199
191
 
200
192
  return P_prime.get_point(), Q_prime.get_point()
201
-
202
- def reencrypt(self, ciphertext: tuple) -> tuple:
203
- raise ValueError(
204
- "Elliptic Curve ElGamal does not support regeneration of ciphertext"
205
- )
@@ -6,6 +6,7 @@ import math
6
6
  # 3rd party dependencies
7
7
  import sympy
8
8
  from sympy import jacobi_symbol
9
+ from tqdm import tqdm
9
10
 
10
11
  # project dependencies
11
12
  from lightphe.models.Homomorphic import Homomorphic
@@ -29,51 +30,53 @@ class GoldwasserMicali(Homomorphic):
29
30
  set this to None if you want to generate random keys.
30
31
  key_size (int): key size in bits
31
32
  """
32
- # key size should be several hundred bits or more
33
- self.keys = keys or self.generate_keys(key_size or 100)
33
+
34
+ self.keys = keys or self.generate_keys(key_size or 1024)
34
35
  self.ciphertext_modulo = self.keys["public_key"]["n"]
35
36
  # TODO: not sure about the plaintext modulo
36
37
  self.plaintext_modulo = self.keys["public_key"]["n"]
37
38
 
38
- def generate_keys(self, key_size: int) -> dict:
39
+ def generate_keys(self, key_size: int, max_tries: int = 10000) -> dict:
39
40
  """
40
41
  Generate public and private keys of Goldwasser-Micali cryptosystem
41
42
  Args:
42
43
  key_size (int): key size in bits
44
+ max_tries (int): maximum number of tries to generate keys
43
45
  Returns:
44
46
  keys (dict): having private_key and public_key keys
45
47
  """
46
- keys = {}
47
- keys["private_key"] = {}
48
- keys["public_key"] = {}
48
+ for attempt in tqdm(range(max_tries), disable=True):
49
+ # pick large random primes p and q
50
+ p = sympy.randprime(2 ** (key_size // 2 - 100), 2 ** (key_size // 2) - 1)
51
+ q = sympy.randprime(2 ** (key_size // 2 - 100), 2 ** (key_size // 2) - 1)
52
+
53
+ # to prevent factorizatin attacks, it is recommended that n should be
54
+ # several hundred bits or more
55
+ n = p * q
56
+
57
+ # find quadratic non-residue x
58
+ for _ in range(1000): # try max 1000 random x
59
+ x = random.randint(2, n - 1)
60
+ if math.gcd(x, n) != 1:
61
+ continue
62
+ if jacobi_symbol(x, p) == -1 and jacobi_symbol(x, q) == -1:
63
+ break
64
+ else:
65
+ # # If no suitable x is found after 1000 tries, discard the current p–q pair and retry
66
+ continue
49
67
 
50
- # picking a prime p
51
- p = sympy.randprime(200, 2 ** int(key_size / 2) - 1)
52
- assert isinstance(p, int)
68
+ keys = {
69
+ "public_key": {"n": n, "x": x},
70
+ "private_key": {"p": p, "q": q},
71
+ }
53
72
 
54
- # picking a prime q
55
- q = sympy.randprime(200, 2 ** int(key_size / 2) - 1)
56
- assert isinstance(q, int)
73
+ logger.debug(
74
+ f"Goldwasser-Micali keys generated after {attempt+1} attempts, n bits: {n.bit_length()}"
75
+ )
57
76
 
58
- n = p * q
77
+ return keys
59
78
 
60
- # find non-residue x
61
- while True:
62
- x = random.randint(1, n - 1)
63
- if (
64
- math.gcd(x, n) == 1
65
- and jacobi_symbol(x, p) == -1
66
- and jacobi_symbol(x, q) == -1
67
- ):
68
- break
69
-
70
- keys["public_key"]["n"] = n
71
- keys["public_key"]["x"] = x
72
-
73
- keys["private_key"]["p"] = p
74
- keys["private_key"]["q"] = q
75
-
76
- return keys
79
+ raise RuntimeError(f"Failed to generate keys after {max_tries} attempts")
77
80
 
78
81
  def generate_random_key(self) -> int:
79
82
  """
@@ -193,9 +196,3 @@ class GoldwasserMicali(Homomorphic):
193
196
  ciphertext3.append((c1 * c2) % self.ciphertext_modulo)
194
197
 
195
198
  return ciphertext3
196
-
197
- def multiply_by_contant(self, ciphertext: int, constant: int):
198
- raise ValueError("Goldwasser-Micali does not support multiplying with constant")
199
-
200
- def reencrypt(self, ciphertext: int):
201
- raise ValueError("Goldwasser-Micali does not support re-encryption")