lightphe 0.0.20__tar.gz → 0.0.21__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 (51) hide show
  1. {lightphe-0.0.20 → lightphe-0.0.21}/PKG-INFO +19 -15
  2. {lightphe-0.0.20 → lightphe-0.0.21}/README.md +18 -14
  3. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/__init__.py +13 -3
  4. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/commons/phe_utils.py +4 -0
  5. lightphe-0.0.21/lightphe/cryptosystems/BonehGohNissim.py +327 -0
  6. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/cryptosystems/EllipticCurveElGamal.py +22 -2
  7. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/cryptosystems/GoldwasserMicali.py +18 -10
  8. lightphe-0.0.21/lightphe/cryptosystems/SanderYoungYung.py +225 -0
  9. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/models/Algorithm.py +2 -0
  10. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/models/Ciphertext.py +24 -0
  11. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/models/Homomorphic.py +33 -10
  12. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe.egg-info/PKG-INFO +19 -15
  13. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe.egg-info/SOURCES.txt +4 -0
  14. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe.egg-info/requires.txt +1 -1
  15. {lightphe-0.0.20 → lightphe-0.0.21}/requirements.txt +1 -1
  16. {lightphe-0.0.20 → lightphe-0.0.21}/setup.py +1 -1
  17. lightphe-0.0.21/tests/test_bonehgohnissim.py +143 -0
  18. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_ellipticcurveelgamal.py +13 -0
  19. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_goldwasser.py +9 -0
  20. lightphe-0.0.21/tests/test_sanderyoungyung.py +71 -0
  21. {lightphe-0.0.20 → lightphe-0.0.21}/LICENSE +0 -0
  22. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/commons/__init__.py +0 -0
  23. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/commons/logger.py +0 -0
  24. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/cryptosystems/Benaloh.py +0 -0
  25. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/cryptosystems/DamgardJurik.py +0 -0
  26. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/cryptosystems/ElGamal.py +0 -0
  27. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/cryptosystems/NaccacheStern.py +0 -0
  28. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/cryptosystems/OkamotoUchiyama.py +0 -0
  29. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/cryptosystems/Paillier.py +0 -0
  30. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/cryptosystems/RSA.py +0 -0
  31. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/cryptosystems/__init__.py +0 -0
  32. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/models/Tensor.py +0 -0
  33. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe/models/__init__.py +0 -0
  34. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe.egg-info/dependency_links.txt +0 -0
  35. {lightphe-0.0.20 → lightphe-0.0.21}/lightphe.egg-info/top_level.txt +0 -0
  36. {lightphe-0.0.20 → lightphe-0.0.21}/setup.cfg +0 -0
  37. {lightphe-0.0.20 → lightphe-0.0.21}/tests/__init__.py +0 -0
  38. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_benaloh.py +0 -0
  39. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_ciphertexts.py +0 -0
  40. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_cloud.py +0 -0
  41. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_damgard.py +0 -0
  42. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_elgamal.py +0 -0
  43. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_exporting_keys.py +0 -0
  44. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_keys.py +0 -0
  45. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_large_keys.py +0 -0
  46. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_naccache.py +0 -0
  47. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_okamoto.py +0 -0
  48. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_paillier.py +0 -0
  49. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_rsa.py +0 -0
  50. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_salary.py +0 -0
  51. {lightphe-0.0.20 → lightphe-0.0.21}/tests/test_tensors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightphe
3
- Version: 0.0.20
3
+ Version: 0.0.21
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/).
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`.
40
40
 
41
41
  # Partially vs Fully Homomorphic Encryption
42
42
 
@@ -67,18 +67,20 @@ from lightphe import LightPHE
67
67
 
68
68
  In summary, LightPHE is covering following algorithms and these are partially homomorphic with respect to the operations mentioned in the following table.
69
69
 
70
- | Algorithm | Multiplicatively<br>Homomorphic | Additively<br>Homomorphic | Scalar Multiplication | Exclusively<br>Homomorphic | Regeneration<br>of Ciphertext |
71
- | --- | --- | --- | --- | --- | --- |
72
- | RSA | ✅ | ❌ | ❌ | ❌ | ❌ |
73
- | ElGamal | ✅ | ❌ | ❌ | ❌ | ✅ |
74
- | Exponential ElGamal | ❌ | ✅ | ✅ | ❌ | ✅ |
75
- | Elliptic Curve ElGamal | ❌ | ✅ | ✅ | ❌ | ❌ |
76
- | Paillier | ❌ | ✅ | ✅ | ❌ | ✅ |
77
- | Damgard-Jurik | ❌ | ✅ | ✅ | ❌ | ✅ |
78
- | Benaloh | ❌ | ✅ | ✅ | ❌ | ✅ |
79
- | Naccache-Stern | ❌ | ✅ | ✅ | ❌ | ✅ |
80
- | Okamoto-Uchiyama | ❌ | ✅ | ✅ | ❌ | ✅ |
81
- | Goldwasser-Micali | ❌ | ❌ | ❌ | ✅ | ❌ |
70
+ | Algorithm | Multiplicatively<br>Homomorphic | Additively<br>Homomorphic | Scalar Multiplication | Bitwise-XOR Homomorphic | Bitwise-AND Homomorphic | Regeneration<br>of Ciphertext |
71
+ | --- | --- | --- | --- | --- | --- | --- |
72
+ | RSA | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
73
+ | ElGamal | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
74
+ | Exponential ElGamal | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
75
+ | Elliptic Curve ElGamal | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
76
+ | Paillier | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
77
+ | Damgard-Jurik | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
78
+ | Benaloh | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
79
+ | Naccache-Stern | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
80
+ | Okamoto-Uchiyama | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
81
+ | Goldwasser-Micali | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ |
82
+ | Sander-Young-Yung | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
83
+ | Boneh-Goh-Nissim | 1️⃣ | ✅ | ✅ | ❌ | ❌ | ✅ |
82
84
 
83
85
  # Building cryptosystem
84
86
 
@@ -95,7 +97,9 @@ algorithms = [
95
97
  "Benaloh",
96
98
  "Naccache-Stern",
97
99
  "Goldwasser-Micali",
98
- "EllipticCurve-ElGamal"
100
+ "EllipticCurve-ElGamal",
101
+ "Sander-Young-Yung",
102
+ "Boneh-Goh-Nissim",
99
103
  ]
100
104
 
101
105
  cs = LightPHE(algorithm_name = algorithms[0])
@@ -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/).
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`.
24
24
 
25
25
  # Partially vs Fully Homomorphic Encryption
26
26
 
@@ -51,18 +51,20 @@ from lightphe import LightPHE
51
51
 
52
52
  In summary, LightPHE is covering following algorithms and these are partially homomorphic with respect to the operations mentioned in the following table.
53
53
 
54
- | Algorithm | Multiplicatively<br>Homomorphic | Additively<br>Homomorphic | Scalar Multiplication | Exclusively<br>Homomorphic | Regeneration<br>of Ciphertext |
55
- | --- | --- | --- | --- | --- | --- |
56
- | RSA | ✅ | ❌ | ❌ | ❌ | ❌ |
57
- | ElGamal | ✅ | ❌ | ❌ | ❌ | ✅ |
58
- | Exponential ElGamal | ❌ | ✅ | ✅ | ❌ | ✅ |
59
- | Elliptic Curve ElGamal | ❌ | ✅ | ✅ | ❌ | ❌ |
60
- | Paillier | ❌ | ✅ | ✅ | ❌ | ✅ |
61
- | Damgard-Jurik | ❌ | ✅ | ✅ | ❌ | ✅ |
62
- | Benaloh | ❌ | ✅ | ✅ | ❌ | ✅ |
63
- | Naccache-Stern | ❌ | ✅ | ✅ | ❌ | ✅ |
64
- | Okamoto-Uchiyama | ❌ | ✅ | ✅ | ❌ | ✅ |
65
- | Goldwasser-Micali | ❌ | ❌ | ❌ | ✅ | ❌ |
54
+ | Algorithm | Multiplicatively<br>Homomorphic | Additively<br>Homomorphic | Scalar Multiplication | Bitwise-XOR Homomorphic | Bitwise-AND Homomorphic | Regeneration<br>of Ciphertext |
55
+ | --- | --- | --- | --- | --- | --- | --- |
56
+ | RSA | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
57
+ | ElGamal | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
58
+ | Exponential ElGamal | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
59
+ | Elliptic Curve ElGamal | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
60
+ | Paillier | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
61
+ | Damgard-Jurik | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
62
+ | Benaloh | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
63
+ | Naccache-Stern | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
64
+ | Okamoto-Uchiyama | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
65
+ | Goldwasser-Micali | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ |
66
+ | Sander-Young-Yung | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
67
+ | Boneh-Goh-Nissim | 1️⃣ | ✅ | ✅ | ❌ | ❌ | ✅ |
66
68
 
67
69
  # Building cryptosystem
68
70
 
@@ -79,7 +81,9 @@ algorithms = [
79
81
  "Benaloh",
80
82
  "Naccache-Stern",
81
83
  "Goldwasser-Micali",
82
- "EllipticCurve-ElGamal"
84
+ "EllipticCurve-ElGamal",
85
+ "Sander-Young-Yung",
86
+ "Boneh-Goh-Nissim",
83
87
  ]
84
88
 
85
89
  cs = LightPHE(algorithm_name = algorithms[0])
@@ -28,13 +28,15 @@ from lightphe.cryptosystems.Benaloh import Benaloh
28
28
  from lightphe.cryptosystems.NaccacheStern import NaccacheStern
29
29
  from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali
30
30
  from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal
31
+ from lightphe.cryptosystems.SanderYoungYung import SanderYoungYung
32
+ from lightphe.cryptosystems.BonehGohNissim import BonehGohNissim
31
33
 
32
34
 
33
35
  # pylint: disable=eval-used, simplifiable-if-expression, too-few-public-methods
34
36
 
35
37
  logger = Logger(module="lightphe/__init__.py")
36
38
 
37
- VERSION = "0.0.20"
39
+ VERSION = "0.0.21"
38
40
 
39
41
 
40
42
  class LightPHE:
@@ -57,7 +59,7 @@ class LightPHE:
57
59
  Args:
58
60
  algorithm_name (str): RSA | ElGamal | Exponential-ElGamal | EllipticCurve-ElGamal
59
61
  | Paillier | Damgard-Jurik | Okamoto-Uchiyama | Benaloh | Naccache-Stern
60
- | Goldwasser-Micali
62
+ | Goldwasser-Micali | Sander-Young-Yung | Boneh-Goh-Nissim
61
63
  keys (dict): optional private-public key pair
62
64
  key_file (str): if keys were exported already, you can load them into cryptosystem
63
65
  key_size (int): key size in bits
@@ -116,13 +118,15 @@ class LightPHE:
116
118
  Benaloh,
117
119
  NaccacheStern,
118
120
  EllipticCurveElGamal,
121
+ SanderYoungYung,
122
+ BonehGohNissim,
119
123
  ]:
120
124
  """
121
125
  Build a cryptosystem among partially homomorphic algorithms
122
126
  Args:
123
127
  algorithm_name (str): RSA | ElGamal | Exponential-ElGamal | EllipticCurve-ElGamal
124
128
  | Paillier | Damgard-Jurik | Okamoto-Uchiyama | Benaloh | Naccache-Stern
125
- | Goldwasser-Micali | Edwards-ElGamal
129
+ | Goldwasser-Micali | Edwards-ElGamal | Sander-Young-Yung | Boneh-Goh-Nissim
126
130
  Default is Paillier.
127
131
  keys (dict): optional private-public key pair
128
132
  key_file (str): if keys are exported, you can load them into cryptosystem
@@ -172,6 +176,12 @@ class LightPHE:
172
176
  cs = NaccacheStern(keys=keys, key_size=key_size, max_tries=max_tries)
173
177
  elif algorithm_name == Algorithm.GoldwasserMicali:
174
178
  cs = GoldwasserMicali(keys=keys, key_size=key_size, max_tries=max_tries)
179
+ elif algorithm_name == Algorithm.SanderYoungYung:
180
+ cs = SanderYoungYung(
181
+ keys=keys, key_size=key_size, plaintext_limit=plaintext_limit
182
+ )
183
+ elif algorithm_name == Algorithm.BonehGohNissim:
184
+ cs = BonehGohNissim(keys=keys, key_size=key_size, max_tries=max_tries)
175
185
  else:
176
186
  raise ValueError(f"unimplemented algorithm - {algorithm_name}")
177
187
  return cs
@@ -23,6 +23,10 @@ def normalize_input(value: Union[int, float], modulo: Union[int, str]) -> int:
23
23
  # elliptic curve elgamal & koblitz curves
24
24
  result = value % int(modulo, 2)
25
25
  elif isinstance(value, int) and isinstance(modulo, int):
26
+ if value > modulo:
27
+ logger.warn(
28
+ f"Value {value} is greater than modulo {modulo}. Normalizing it to be in the range of modulo."
29
+ )
26
30
  result = value % modulo
27
31
  elif isinstance(value, float) and value >= 0:
28
32
  dividend, divisor = fractionize(value=value, modulo=modulo)
@@ -0,0 +1,327 @@
1
+ # built-in dependencies
2
+ import random
3
+ import math
4
+ from typing import Optional, Tuple, Union
5
+
6
+ # third-party dependencies
7
+ from lightecc import LightECC
8
+ from lightecc.interfaces.elliptic_curve import EllipticCurvePoint
9
+ from lightecc.commons.pairing import _fp2_pow, _fp2_mul
10
+ import sympy
11
+ from sympy.ntheory.residue_ntheory import sqrt_mod
12
+
13
+ # project dependencies
14
+ from lightphe.models.Homomorphic import Homomorphic
15
+ from lightphe.commons.logger import Logger
16
+
17
+ logger = Logger(module="lightphe/cryptosystems/BonehGohNissim.py")
18
+
19
+
20
+ class BonehGohNissim(Homomorphic):
21
+ """
22
+ Boneh-Goh-Nissim algorithm is homomorphic with respect to the addition.
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
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ keys: Optional[dict] = None,
31
+ key_size: Optional[int] = None,
32
+ max_tries: int = 10000,
33
+ ):
34
+ """
35
+ Args:
36
+ keys (dict): private - public key pair.
37
+ set this to None if you want to generate random keys.
38
+ key_size (int): size of the keys to generate in bits. Default is 1024.
39
+ """
40
+ self.keys = keys or self.generate_keys(
41
+ key_size=key_size or 1024,
42
+ max_tries=max_tries,
43
+ )
44
+
45
+ self.ec = LightECC(
46
+ form_name="weierstrass",
47
+ curve_name="custom",
48
+ config={
49
+ "a": self.keys["public_key"]["curve"]["a"],
50
+ "b": self.keys["public_key"]["curve"]["b"],
51
+ "p": self.keys["public_key"]["curve"]["p"],
52
+ "G": self.keys["public_key"]["curve"]["G"],
53
+ "n": self.keys["public_key"]["curve"]["n"],
54
+ },
55
+ )
56
+
57
+ self.plaintext_modulo = self.ec.modulo
58
+ self.ciphertext_modulo = self.ec.modulo
59
+
60
+ def generate_keys(
61
+ self,
62
+ key_size: int,
63
+ max_tries: int = 10000,
64
+ ):
65
+ """
66
+ Generate public and private keys of Boneh-Goh-Nissim cryptosystem
67
+ Returns:
68
+ keys (dict): having private_key and public_key keys
69
+ """
70
+ keys = {}
71
+ keys["private_key"] = {}
72
+ keys["public_key"] = {}
73
+
74
+ for key_attempt in range(max_tries):
75
+ # choose large odd distinct primes q1 and q2
76
+ q1 = sympy.randprime(2 ** (key_size // 2), 2 ** (key_size // 2 + 1))
77
+ q2 = sympy.randprime(2 ** (key_size // 2), 2 ** (key_size // 2 + 1))
78
+ while q1 == q2:
79
+ q2 = sympy.randprime(2 ** (key_size // 2), 2 ** (key_size // 2 + 1))
80
+
81
+ n = q1 * q2
82
+
83
+ # Find l such that p = n*l - 1 is prime and p ≡ 3 (mod 4).
84
+ # Supersingularity of y² = x³ + x requires p ≡ 3 (mod 4).
85
+ # Since n is odd (product of two odd primes), n*l ≡ 0 (mod 4)
86
+ # iff l ≡ 0 (mod 4). So we iterate multiples of 4 systematically.
87
+ l = None
88
+ for l_candidate in range(4, max_tries * 4 + 4, 4):
89
+ p_candidate = n * l_candidate - 1
90
+ if sympy.isprime(p_candidate):
91
+ l = l_candidate
92
+ break
93
+
94
+ if l is None:
95
+ logger.debug(
96
+ f"No valid l found for attempt {key_attempt + 1}, retrying"
97
+ )
98
+ continue
99
+
100
+ p = n * l - 1
101
+ a = 1
102
+ b = 0
103
+
104
+ G = self.__find_generator(p, max_tries=max_tries)
105
+
106
+ # order of the curve may not be exactly n, but it should be
107
+ self.ec = LightECC(
108
+ form_name="weierstrass",
109
+ curve_name="custom",
110
+ config={"a": a, "b": b, "p": p, "G": G, "n": None},
111
+ )
112
+
113
+ nG = n * self.ec.G
114
+ if (
115
+ nG == self.ec.O
116
+ ): # check if nG is the point at infinity / identity element / neutral element
117
+ logger.debug("nG is the point at infinity, as expected.")
118
+ logger.debug(f"q1 = {q1}, q2 = {q2}, n = {n}, p = {p}, l = {l}")
119
+ break
120
+
121
+ logger.debug(f"nG not infinity, retrying (attempt {key_attempt + 1})")
122
+ else:
123
+ raise Exception(f"Failed to generate keys after {max_tries} attempts")
124
+
125
+ while True:
126
+ r = random.randint(2, n - 1)
127
+ if math.gcd(r, n) == 1:
128
+ break
129
+
130
+ u = r * self.ec.G
131
+ h = u * q2
132
+
133
+ keys["public_key"]["curve"] = {
134
+ "a": a,
135
+ "b": b,
136
+ "p": p,
137
+ "G": G,
138
+ "n": n,
139
+ }
140
+
141
+ keys["private_key"]["q1"] = q1
142
+ keys["private_key"]["q2"] = q2
143
+ keys["public_key"]["G"] = G
144
+ keys["public_key"]["u"] = u.get_point()
145
+ keys["public_key"]["h"] = h.get_point()
146
+ keys["public_key"]["l"] = l
147
+ return keys
148
+
149
+ @staticmethod
150
+ def __find_generator(p, max_tries=10000) -> Tuple[int, int]:
151
+ if not sympy.isprime(p):
152
+ raise ValueError("p must be prime")
153
+
154
+ for _ in range(max_tries):
155
+ x = random.randint(0, p - 1)
156
+ rhs = (pow(x, 3, p) + x) % p
157
+ y_list = sqrt_mod(rhs, p, all_roots=True) # get all square roots
158
+ if y_list: # if there is at least one solution
159
+ return x, y_list[0] # just return the first one
160
+
161
+ raise Exception(f"Failed to find a generator after {max_tries} attempts")
162
+
163
+ def generate_random_key(self) -> int:
164
+ """
165
+ Boneh-Goh-Nissim requires to generate one-time random key per encryption
166
+ Returns:
167
+ random key (int): one time random key for encryption
168
+ """
169
+ q2 = self.keys["private_key"]["q2"]
170
+ return random.randint(1, q2 - 1)
171
+
172
+ def encrypt(
173
+ self, plaintext: int, random_key: Optional[int] = None
174
+ ) -> EllipticCurvePoint:
175
+ """
176
+ Encrypt a given plaintext for optionally given random key with Boneh-Goh-Nissim
177
+ Args:
178
+ plaintext (int): message to encrypt
179
+ random_key (int): one time random key for encryption
180
+ Returns:
181
+ ciphertext (EllipticCurvePoint): encrypted message as a point on the elliptic curve
182
+ """
183
+ r = random_key or self.generate_random_key()
184
+ G = self.ec.G
185
+ h = self.keys["public_key"]["h"]
186
+ h = EllipticCurvePoint(x=h[0], y=h[1], curve=self.ec.curve)
187
+ c = (G * plaintext) + (h * r)
188
+ return c
189
+
190
+ def decrypt(self, ciphertext: Union[EllipticCurvePoint, Tuple[int, int]]) -> int:
191
+ """
192
+ Decrypt a given ciphertext with Boneh-Goh-Nissim.
193
+ Handles both G1 ciphertexts (EllipticCurvePoint, from encryption/addition)
194
+ and G_T ciphertexts (tuple in F_{p^2}, from homomorphic multiplication).
195
+
196
+ Args:
197
+ ciphertext: encrypted message — either an EllipticCurvePoint or
198
+ a tuple (a, b) representing a + b*i in F_{p^2}
199
+ Returns:
200
+ plaintext (int): restored message
201
+ """
202
+ q1 = self.keys["private_key"]["q1"]
203
+
204
+ if isinstance(ciphertext, tuple):
205
+ return self._decrypt_gt(ciphertext, q1)
206
+
207
+ G = self.ec.G
208
+
209
+ mP = ciphertext * q1
210
+ P = G * q1
211
+
212
+ for i in range(1, q1 + 1):
213
+ iP = P * i
214
+ if iP == mP:
215
+ return i
216
+
217
+ raise Exception("Decryption failed")
218
+
219
+ def _decrypt_gt(self, ciphertext: tuple, q1: int) -> int:
220
+ """
221
+ Decrypt a G_T ciphertext (result of homomorphic multiplication).
222
+
223
+ e(C1, C2)^q1 = e(G, G)^(q1 * m1 * m2)
224
+ Solve DLP: find m such that base^m == target, where
225
+ base = e(G, G)^q1
226
+ target = e(C1, C2)^q1
227
+
228
+ Args:
229
+ ciphertext: pairing value (a, b) in F_{p^2}
230
+ q1: private key component
231
+ Returns:
232
+ plaintext product m1 * m2
233
+ """
234
+ p = self.ec.modulo
235
+ G = self.ec.G
236
+
237
+ # e(G, G) — pairing of generator with itself via distortion map
238
+ e_gg = self.ec.pairing(G, G)
239
+
240
+ # base = e(G, G)^q1, target = e(C1, C2)^q1
241
+ base = _fp2_pow(e_gg, q1, p)
242
+ target = _fp2_pow(ciphertext, q1, p)
243
+
244
+ # brute-force DLP in G_T
245
+ acc = (1, 0)
246
+ for i in range(1, q1 + 1):
247
+ acc = _fp2_mul(acc, base, p)
248
+ if acc == target:
249
+ return i
250
+
251
+ raise Exception("Decryption of G_T ciphertext failed")
252
+
253
+ def add(
254
+ self, ciphertext1: EllipticCurvePoint, ciphertext2: EllipticCurvePoint
255
+ ) -> EllipticCurvePoint:
256
+ """
257
+ Add two ciphertexts with Boneh-Goh-Nissim
258
+ Args:
259
+ ciphertext1 (EllipticCurvePoint): first encrypted message as a point on the elliptic curve
260
+ ciphertext2 (EllipticCurvePoint): second encrypted message as a point on the elliptic curve
261
+ Returns:
262
+ result (tuple): resulting ciphertext after addition
263
+ """
264
+ return ciphertext1 + ciphertext2
265
+
266
+ def multiply(
267
+ self,
268
+ ciphertext1: EllipticCurvePoint,
269
+ ciphertext2: EllipticCurvePoint,
270
+ ) -> Tuple[int, int]:
271
+ """
272
+ Homomorphic multiplication of two ciphertexts using the bilinear pairing.
273
+ The result is an element of F_{p^2} (the pairing target group G_T),
274
+ not a point on the elliptic curve. BGN supports only one multiplication
275
+ per ciphertext — the result cannot be multiplied again.
276
+
277
+ Args:
278
+ ciphertext1: first encrypted message (point on E(F_p))
279
+ ciphertext2: second encrypted message (point on E(F_p))
280
+ Returns:
281
+ result (tuple): pairing value (a, b) representing a + b*i in F_{p^2}
282
+ """
283
+ # The tuple (a, b) represents a + b·i where i² = -1 mod p
284
+ if isinstance(ciphertext1, tuple) or isinstance(ciphertext2, tuple):
285
+ raise ValueError(
286
+ "Boneh-Goh-Nissim only supports multiplication ciphertexts once!"
287
+ )
288
+ result = self.ec.pairing(ciphertext1, ciphertext2)
289
+ if not isinstance(result, tuple):
290
+ raise ValueError(
291
+ "Boneh-Goh-Nissim requires a supersingular curve with embedding degree 2. "
292
+ "The pairing returned an F_p element instead of an F_{p^2} element."
293
+ )
294
+
295
+ # the pairing result is an element of F_{p²}, not a point on the curve.
296
+ # In BGN, the pairing maps two curve points to a field element
297
+ # (in the multiplicative group G_T). The result of e(C1, C2) lives
298
+ # in F_{p²}*, not on the elliptic curve. The homomorphic multiplication
299
+ # works in G_T (the target group of the pairing), not back on the curve.
300
+ return result
301
+
302
+ def multiply_by_constant(
303
+ self, ciphertext: EllipticCurvePoint, constant: int
304
+ ) -> EllipticCurvePoint:
305
+ """
306
+ Multiply a ciphertext by a constant with Boneh-Goh-Nissim
307
+ Args:
308
+ ciphertext (tuple): encrypted message as a tuple of two points on the elliptic curve
309
+ constant (int): constant to multiply with the ciphertext
310
+ Returns:
311
+ result (tuple): resulting ciphertext after multiplication
312
+ """
313
+ return ciphertext * constant
314
+
315
+ def reencrypt(self, ciphertext: EllipticCurvePoint) -> EllipticCurvePoint:
316
+ """
317
+ Re-encrypt a given ciphertext with Boneh-Goh-Nissim
318
+ Args:
319
+ ciphertext (tuple): encrypted message as a tuple of two points on the elliptic curve
320
+ Returns:
321
+ reencrypted_ciphertext (tuple): re-encrypted message as a tuple of two points on the elliptic curve
322
+ """
323
+ h = self.keys["public_key"]["h"]
324
+ h = EllipticCurvePoint(x=h[0], y=h[1], curve=self.ec.curve)
325
+ r = self.generate_random_key()
326
+ c_prime = ciphertext + (r * h)
327
+ return c_prime
@@ -166,8 +166,6 @@ class EllipticCurveElGamal(Homomorphic):
166
166
 
167
167
  return a.get_point(), b.get_point()
168
168
 
169
-
170
-
171
169
  def multiply_by_constant(self, ciphertext: tuple, constant: int) -> tuple:
172
170
  """
173
171
  Multiply a ciphertext with a plain constant.
@@ -190,3 +188,25 @@ class EllipticCurveElGamal(Homomorphic):
190
188
  Q_prime = Q * constant
191
189
 
192
190
  return P_prime.get_point(), Q_prime.get_point()
191
+
192
+ def reencrypt(self, ciphertext: tuple) -> tuple:
193
+ """
194
+ Re-encrypt a ciphertext with a new random key
195
+ Args:
196
+ ciphertext (tuple): c1 and c2
197
+ Returns:
198
+ ciphertext (tuple): new c1 and c2
199
+ """
200
+ r_prime = self.generate_random_key()
201
+
202
+ x, y = self.keys["public_key"]["Qa"]
203
+ Qa = EllipticCurvePoint(x=x, y=y, curve=self.ecc.curve)
204
+
205
+ # c1 and c2 as tuple of integers
206
+ c1, c2 = ciphertext
207
+ c1 = EllipticCurvePoint(x=c1[0], y=c1[1], curve=self.ecc.curve)
208
+ c2 = EllipticCurvePoint(x=c2[0], y=c2[1], curve=self.ecc.curve)
209
+
210
+ c1_prime = c1 + r_prime * self.ecc.G
211
+ c2_prime = c2 + r_prime * Qa
212
+ return c1_prime.get_point(), c2_prime.get_point()
@@ -171,16 +171,6 @@ class GoldwasserMicali(Homomorphic):
171
171
  m_binary = "".join(m_binaries)
172
172
  return int(m_binary, 2)
173
173
 
174
- def add(self, ciphertext1: list, ciphertext2: list) -> list:
175
- raise ValueError(
176
- "Goldwasser-Micali is not homomorphic with respect to the addition"
177
- )
178
-
179
- def multiply(self, ciphertext1: int, ciphertext2: int) -> int:
180
- raise ValueError(
181
- "Goldwasser-Micali is not homomorphic with respect to the multiplication"
182
- )
183
-
184
174
  def xor(self, ciphertext1: List[int], ciphertext2: List[int]) -> List[int]:
185
175
  """
186
176
  Perform homomorphic xor on encrypted data.
@@ -211,3 +201,21 @@ class GoldwasserMicali(Homomorphic):
211
201
  ciphertext3.append((c1 * c2) % self.ciphertext_modulo)
212
202
 
213
203
  return ciphertext3
204
+
205
+ def reencrypt(self, ciphertext: List[int]) -> List[int]:
206
+ """
207
+ Re-encrypt a given ciphertext with Goldwasser-Micali
208
+ Args:
209
+ ciphertext (int): encrypted message
210
+ Returns:
211
+ ciphertext (int): re-encrypted message
212
+ """
213
+ n = self.keys["public_key"]["n"]
214
+
215
+ c_reencrypted = []
216
+ for ci in ciphertext:
217
+ ri = self.generate_random_key()
218
+ ci_reencrypted = (pow(ri, 2, n) * ci) % n
219
+ c_reencrypted.append(ci_reencrypted)
220
+
221
+ return c_reencrypted