lightphe 0.0.25__tar.gz → 0.0.27__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 (84) hide show
  1. lightphe-0.0.27/PKG-INFO +561 -0
  2. lightphe-0.0.27/README.md +543 -0
  3. lightphe-0.0.27/lightphe/__init__.py +1029 -0
  4. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/commons/key_validator.py +33 -1
  5. lightphe-0.0.27/lightphe/commons/safe_primes.py +30 -0
  6. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/Benaloh.py +12 -4
  7. lightphe-0.0.27/lightphe/cryptosystems/BrakerskiVaikuntanathan.py +398 -0
  8. lightphe-0.0.27/lightphe/cryptosystems/BressonCatalanoPointcheval.py +295 -0
  9. lightphe-0.0.27/lightphe/cryptosystems/CamenischShoup.py +602 -0
  10. lightphe-0.0.27/lightphe/cryptosystems/CastagnosLaguillaumie.py +390 -0
  11. lightphe-0.0.27/lightphe/cryptosystems/DamgardGeislerKroigaard.py +801 -0
  12. lightphe-0.0.27/lightphe/cryptosystems/EllipticCurvePaillier.py +324 -0
  13. lightphe-0.0.27/lightphe/cryptosystems/FreemanBonehGohNissim.py +451 -0
  14. lightphe-0.0.27/lightphe/cryptosystems/GentryHaleviVaikuntanathan.py +391 -0
  15. lightphe-0.0.27/lightphe/cryptosystems/IshaiPaskin.py +264 -0
  16. lightphe-0.0.27/lightphe/cryptosystems/JoyeLibert.py +227 -0
  17. lightphe-0.0.27/lightphe/cryptosystems/Regev.py +354 -0
  18. lightphe-0.0.27/lightphe/models/Algorithm.py +25 -0
  19. lightphe-0.0.27/lightphe/models/CatalanoFiore.py +293 -0
  20. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/models/Ciphertext.py +52 -0
  21. lightphe-0.0.27/lightphe/models/Homomorphic.py +204 -0
  22. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/models/Tensor.py +3 -0
  23. lightphe-0.0.27/lightphe.egg-info/PKG-INFO +561 -0
  24. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe.egg-info/SOURCES.txt +25 -0
  25. {lightphe-0.0.25 → lightphe-0.0.27}/setup.py +1 -1
  26. lightphe-0.0.27/tests/test_brakerskivaikuntanathan.py +234 -0
  27. lightphe-0.0.27/tests/test_bressoncatalanopointcheval.py +255 -0
  28. lightphe-0.0.27/tests/test_camenischshoup.py +317 -0
  29. lightphe-0.0.27/tests/test_castagnoslaguillaumie.py +88 -0
  30. lightphe-0.0.27/tests/test_catalanofiore.py +222 -0
  31. lightphe-0.0.27/tests/test_damgardgeislerkroigaard.py +568 -0
  32. lightphe-0.0.27/tests/test_ellipticcurvepaillier.py +169 -0
  33. lightphe-0.0.27/tests/test_freemanbonehgohnissim.py +187 -0
  34. lightphe-0.0.27/tests/test_gentryhalevivaikuntanathan.py +189 -0
  35. lightphe-0.0.27/tests/test_ishaipaskin.py +274 -0
  36. lightphe-0.0.27/tests/test_joyelibert.py +107 -0
  37. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_key_validator.py +29 -1
  38. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_paillier.py +6 -0
  39. lightphe-0.0.27/tests/test_regev.py +204 -0
  40. lightphe-0.0.25/PKG-INFO +0 -271
  41. lightphe-0.0.25/README.md +0 -253
  42. lightphe-0.0.25/lightphe/__init__.py +0 -499
  43. lightphe-0.0.25/lightphe/models/Algorithm.py +0 -14
  44. lightphe-0.0.25/lightphe/models/Homomorphic.py +0 -105
  45. lightphe-0.0.25/lightphe.egg-info/PKG-INFO +0 -271
  46. {lightphe-0.0.25 → lightphe-0.0.27}/LICENSE +0 -0
  47. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/commons/__init__.py +0 -0
  48. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/commons/logger.py +0 -0
  49. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/commons/phe_utils.py +0 -0
  50. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/BonehGohNissim.py +0 -0
  51. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/DamgardJurik.py +0 -0
  52. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/ElGamal.py +0 -0
  53. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/EllipticCurveElGamal.py +0 -0
  54. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/GoldwasserMicali.py +0 -0
  55. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/NaccacheStern.py +0 -0
  56. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/OkamotoUchiyama.py +0 -0
  57. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/Paillier.py +0 -0
  58. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/RSA.py +0 -0
  59. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/SanderYoungYung.py +0 -0
  60. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/cryptosystems/__init__.py +0 -0
  61. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe/models/__init__.py +0 -0
  62. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe.egg-info/dependency_links.txt +0 -0
  63. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe.egg-info/requires.txt +0 -0
  64. {lightphe-0.0.25 → lightphe-0.0.27}/lightphe.egg-info/top_level.txt +0 -0
  65. {lightphe-0.0.25 → lightphe-0.0.27}/requirements.txt +0 -0
  66. {lightphe-0.0.25 → lightphe-0.0.27}/setup.cfg +0 -0
  67. {lightphe-0.0.25 → lightphe-0.0.27}/tests/__init__.py +0 -0
  68. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_benaloh.py +0 -0
  69. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_bonehgohnissim.py +0 -0
  70. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_ciphertexts.py +0 -0
  71. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_cloud.py +0 -0
  72. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_damgard.py +0 -0
  73. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_elgamal.py +0 -0
  74. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_ellipticcurveelgamal.py +0 -0
  75. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_exporting_keys.py +0 -0
  76. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_goldwasser.py +0 -0
  77. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_keys.py +0 -0
  78. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_large_keys.py +0 -0
  79. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_naccache.py +0 -0
  80. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_okamoto.py +0 -0
  81. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_rsa.py +0 -0
  82. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_salary.py +0 -0
  83. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_sanderyoungyung.py +0 -0
  84. {lightphe-0.0.25 → lightphe-0.0.27}/tests/test_tensors.py +0 -0
@@ -0,0 +1,561 @@
1
+ Metadata-Version: 2.1
2
+ Name: lightphe
3
+ Version: 0.0.27
4
+ Summary: A Lightweight Partially Homomorphic Encryption Library for Python
5
+ Home-page: https://github.com/serengil/LightPHE
6
+ Author: Sefik Ilkin Serengil
7
+ Author-email: serengil@gmail.com
8
+ License: UNKNOWN
9
+ Platform: UNKNOWN
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.5.5
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+
17
+ # LightPHE
18
+
19
+ <div align="center">
20
+
21
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/lightphe?period=total&units=international_system&left_color=grey&right_color=blue&left_text=downloads)](https://pepy.tech/project/lightphe)
22
+ [![Stars](https://img.shields.io/github/stars/serengil/LightPHE?color=yellow&style=flat&label=%E2%AD%90%20stars)](https://github.com/serengil/LightPHE/stargazers)
23
+ [![License](http://img.shields.io/:license-MIT-green.svg?style=flat)](https://github.com/serengil/LightPHE/blob/master/LICENSE)
24
+ [![DOI](http://img.shields.io/:DOI-10.3390/sym18050832-blue.svg?style=flat)](https://www.mdpi.com/2073-8994/18/5/832)
25
+
26
+ </div>
27
+
28
+ <p align="center"><img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/phi.png" width="200" height="240"></p>
29
+
30
+ 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/), `Joye-Libert`, `Castagnos-Laguillaumie`, `Ishai-Paskin`, `Damgard-Geisler-Kroigaard`, `Bresson-Catalano-Pointcheval`, `Camenisch-Shoup`, `Elliptic Curve Paillier`, `Regev`, `Brakerski-Vaikuntanathan`, `Freeman-Boneh-Goh-Nissim`, `Gentry-Halevi-Vaikuntanathan`.
31
+
32
+ # Partially vs Fully Homomorphic Encryption
33
+
34
+ Even though fully homomorphic encryption (FHE) has become available in recent times, but when considering the trade-offs, LightPHE emerges as a more efficient and practical choice. If your specific task doesn't demand the full homomorphic capabilities, opting for partial homomorphism with LightPHE is the logical decision.
35
+
36
+ - 🏎️ Notably faster
37
+ - 💻 Demands fewer computational resources
38
+ - 📏 Generating much smaller ciphertexts
39
+ - 🔑 Distributing much smaller keys
40
+ - 🧠 Well-suited for memory-constrained environments
41
+ - ⚖️ Strikes a favorable balance for practical use cases
42
+
43
+ # Installation [![PyPI](https://img.shields.io/pypi/v/lightphe.svg)](https://pypi.org/project/lightphe/)
44
+
45
+ The easiest way to install the LightPHE package is to install it from python package index (PyPI).
46
+
47
+ ```shell
48
+ pip install lightphe
49
+ ```
50
+
51
+ Then you will be able to import the library and use its functionalities.
52
+
53
+ ```python
54
+ from lightphe import LightPHE
55
+ ```
56
+
57
+ # Summary of Homomorphic Features of Different Cryptosystems in LightPHE
58
+
59
+ In summary, LightPHE is covering following algorithms and these are partially homomorphic and somewhat homomorphic with respect to the operations mentioned in the following table.
60
+
61
+ | Algorithm | Multiplicatively<br>Homomorphic | Additively<br>Homomorphic | Scalar Multiplication | Bitwise-XOR Homomorphic | Bitwise-AND Homomorphic | Regeneration<br>of Ciphertext | Decision Tree<br>Evaluation | Secure<br>Comparison | Encrypted<br>Argmax |
62
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
63
+ | RSA | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
64
+ | ElGamal | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
65
+ | Exponential ElGamal | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
66
+ | Elliptic Curve ElGamal | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
67
+ | Paillier | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
68
+ | Elliptic Curve Paillier | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
69
+ | Damgard-Jurik | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
70
+ | Benaloh | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
71
+ | Naccache-Stern | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
72
+ | Okamoto-Uchiyama | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
73
+ | Joye-Libert | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
74
+ | Castagnos-Laguillaumie | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
75
+ | Damgard-Geisler-Kroigaard | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
76
+ | Bresson-Catalano-Pointcheval | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
77
+ | Camenisch-Shoup | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
78
+ | Ishai-Paskin | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ |
79
+ | Regev | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
80
+ | Goldwasser-Micali | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
81
+ | Sander-Young-Yung | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
82
+ | Boneh-Goh-Nissim | 1️⃣ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
83
+ | Freeman-Boneh-Goh-Nissim | 1️⃣ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
84
+ | Brakerski-Vaikuntanathan | 1️⃣ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
85
+ | Gentry-Halevi-Vaikuntanathan | 1️⃣ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
86
+
87
+ 1️⃣ Boneh-Goh-Nissim, Freeman-Boneh-Goh-Nissim, Brakerski-Vaikuntanathan and Gentry-Halevi-Vaikuntanathan support one multiplication natively. Most additively homomorphic algorithms can support one multiplication with [Catalano-Fiore transform](#adding-multiplication-support-to-additive-algorithms) as well.
88
+
89
+ # Building cryptosystem
90
+
91
+ Once you imported the library, then you can build a cryptosystem for several algorithms. This basically generates private and public key pair.
92
+
93
+ ```python
94
+ algorithms = [
95
+ "RSA",
96
+ "ElGamal",
97
+ "Exponential-ElGamal",
98
+ "Paillier",
99
+ "Damgard-Jurik",
100
+ "Okamoto-Uchiyama",
101
+ "Benaloh",
102
+ "Naccache-Stern",
103
+ "Goldwasser-Micali",
104
+ "EllipticCurve-ElGamal",
105
+ "Sander-Young-Yung",
106
+ "Boneh-Goh-Nissim",
107
+ "Joye-Libert",
108
+ "Castagnos-Laguillaumie",
109
+ "Ishai-Paskin",
110
+ "Damgard-Geisler-Kroigaard",
111
+ "Bresson-Catalano-Pointcheval",
112
+ "Camenisch-Shoup",
113
+ "EllipticCurve-Paillier",
114
+ "Regev",
115
+ "Brakerski-Vaikuntanathan",
116
+ "Freeman-Boneh-Goh-Nissim",
117
+ "Gentry-Halevi-Vaikuntanathan",
118
+ ]
119
+
120
+ cs = LightPHE(algorithm_name = algorithms[0])
121
+ ```
122
+
123
+ # Homomorphic Operations
124
+
125
+ The following example demonstrates a simple workflow using LightPHE with an additively homomorphic cryptosystem (e.g. Paillier). First, we build the cryptosystem on-premises and define two plaintext values. Next, we encrypt the plaintexts, which can be done either on-premises or in the cloud, and does not require the private key. Homomorphic operations, such as addition and scalar multiplication, can then be performed on the ciphertexts—these operations can be offloaded to the cloud, leveraging its computational power without revealing the private key or the plaintext. Finally, the results are decrypted on-premises using the private key, verifying that the homomorphic operations on encrypted data produce the expected plaintext results.
126
+
127
+ ```python
128
+ # build an additively homomorphic cryptosystem
129
+ cs = LightPHE(algorithm_name = "Paillier")
130
+
131
+ # define plaintexts
132
+ m1 = 10000 # base salary in usd
133
+ m2 = 500 # wage increase in usd
134
+
135
+ # encrypt plaintexts - private key is not required.
136
+ c1 = cs.encrypt(m1)
137
+ c2 = cs.encrypt(m2)
138
+
139
+ # homomorphic addition - private key is not required
140
+ c3 = c1 + c2
141
+
142
+ # homomorphic scalar multiplication - private key is not required
143
+ k = 1.05 # increase something 5%
144
+ c4 = k * c1
145
+
146
+ # decryption - private key is required
147
+ assert cs.decrypt(c3) == m1 + m2
148
+ assert cs.decrypt(c4) == k * m1
149
+ ```
150
+
151
+ On the other hand, if you adopt a multiplicatively homomorphic cryptosystem (e.g. RSA or ElGamal), you can multiply ciphertexts without revealing the private key or the plaintext.
152
+
153
+ ```python
154
+ # build a multiplicatively homomorphic cryptosystem
155
+ cs = LightPHE(algorithm_name = "RSA")
156
+
157
+ # define plaintexts
158
+ m1 = 17
159
+ m2 = 21
160
+
161
+ # encrypt plaintexts - private key is not required.
162
+ c1 = cs.encrypt(m1)
163
+ c2 = cs.encrypt(m2)
164
+
165
+ # homomorphic multiplication
166
+ c3 = c1 * c2
167
+
168
+ # decryption - private key is required
169
+ assert cs.decrypt(c3) == m1 * m2
170
+ ```
171
+
172
+ ### Ciphertext Regeneration
173
+
174
+ The most of homomorphic algorithms allow you to regenerate ciphertext while you are not breaking its plaintext restoration. You may consider to do this re-generation many times to have stronger ciphertexts.
175
+
176
+ ```python
177
+ c1_prime = cs.regenerate_ciphertext(c1)
178
+ assert c1_prime.value != c1.value
179
+ assert cs.decrypt(c1_prime) == m1
180
+ assert cs.decrypt(c1) == m1
181
+ ```
182
+
183
+ ### Elliptic Curve Cryptography
184
+
185
+ ECC is a powerful public-key cryptosystem based on the algebraic structure of elliptic curves over finite fields. The library supports 3 elliptic curve forms (weierstrass (default), edwards and koblitz) and 100+ standard elliptic curve configurations.
186
+
187
+ 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.
188
+
189
+ ```python
190
+ forms = ["weierstrass", "edwards", "koblitz"]
191
+ phe = LightPHE(
192
+ algorithm_name="EllipticCurve-ElGamal",
193
+ form="edwards",
194
+ # curve="ed448", # optinally you can specify the curve for given form
195
+ )
196
+ ```
197
+
198
+ One of the crucial factors that define the security level of an elliptic curve cryptosystem is the order of the curve. The order of a curve is the number of points on the curve, and it directly influences the strength of the encryption. A higher order typically corresponds to a stronger cryptosystem, making it more resistant to cryptographic attacks.
199
+
200
+ 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.
201
+
202
+ See [`curves`](https://github.com/serengil/LightECC#supported-curves) page for a list of all supported forms, curves and their details.
203
+
204
+ ### Decision Trees
205
+
206
+ LightPHE can evaluate decision trees privately. Suppose a bank has a secret decision tree that decides a customer's credit limit, and a customer has private data. The bank does not want to reveal its tree, and the customer does not want to reveal their data. The customer sends encrypted data, the bank runs its tree on it without seeing it, and only the customer can decrypt the result. The customer learns the credit limit but not the rest of the tree. Two cryptosystems support this: Ishai-Paskin for trees on yes / no answers, and Damgard-Geisler-Kroigaard for trees on numeric features.
207
+
208
+ #### Decision Trees with Nominal Decisions
209
+
210
+ Ishai-Paskin evaluates trees whose questions have yes / no answers, in a single round: the customer sends encrypted answers, and the bank sends back the encrypted result.
211
+
212
+ Let the bank's tree be the following, where questions are numbered 0, 1 and 2 and leaves are credit limits in thousand dollars.
213
+
214
+ ```
215
+ Q0: has stable income?
216
+ /no \yes
217
+ Q2: owns a house? Q1: has existing debt?
218
+ /no \yes /no \yes
219
+ 0 5 20 Q2: owns a house?
220
+ /no \yes
221
+ 5 10
222
+ ```
223
+
224
+ Each question is written as a tuple of `(question number, what to do if no, what to do if yes)`, and leaves are plain integers. So, the tree above becomes:
225
+
226
+ ```python
227
+ credit_limit_tree = (0, (2, 0, 5), (1, 20, (2, 5, 10)))
228
+ ```
229
+
230
+ Then, the customer and the bank run the following steps.
231
+
232
+ ```python
233
+ # customer builds the cryptosystem and keeps the private key
234
+ customer_cs = LightPHE(algorithm_name = "Ishai-Paskin")
235
+
236
+ # customer shares only the public key with the bank
237
+ customer_cs.export_keys(target_file = "public.txt", public = True)
238
+
239
+ # customer's answers: stable income = yes, existing debt = yes, owns a house = no
240
+ answers = [1, 1, 0]
241
+
242
+ # customer encrypts the answers and sends them to the bank. depth is the maximum
243
+ # number of questions on a path of the tree, the bank should share this value
244
+ encrypted_answers = customer_cs.encrypt_decision_tree_input(bits = answers, depth = 3)
245
+
246
+ # bank builds its own cryptosystem with the customer's public key
247
+ bank_cs = LightPHE(algorithm_name = "Ishai-Paskin", key_file = "public.txt")
248
+
249
+ # bank runs its tree on encrypted answers and sends the result back to the customer
250
+ encrypted_limit = bank_cs.evaluate_decision_tree(
251
+ tree = credit_limit_tree, encrypted_input = encrypted_answers
252
+ )
253
+
254
+ # customer decrypts the result - only the customer has the private key
255
+ assert customer_cs.decrypt(encrypted_limit) == 5
256
+ ```
257
+
258
+ The size of the result grows with the depth of the tree, not with the number of its nodes. Still, each extra level makes the computation noticeably slower, so this is practical for shallow trees.
259
+
260
+ #### Decision Trees with Numeric Decisions
261
+
262
+ Damgard-Geisler-Kroigaard evaluates decision trees with numeric thresholds instead of yes / no answers, using the secure comparison protocol below at each node. It requires a few rounds of interaction between the customer and the bank, but its cost grows linearly with the number of nodes.
263
+
264
+ Let the bank's tree be the following, where features are numbered 0 (age), 1 (income in thousand dollars) and 2 (number of open loans), and leaves are credit limits in thousand dollars.
265
+
266
+ ```
267
+ F1: income <= 40?
268
+ /yes \no
269
+ F0: age <= 25? F2: open loans <= 2?
270
+ /yes \no /yes \no
271
+ 0 5 20 10
272
+ ```
273
+
274
+ Each decision node is written as a tuple of `(feature index, threshold, what to do if feature <= threshold, what to do otherwise)`, and leaves are plain integers. So, the tree above becomes:
275
+
276
+ ```python
277
+ credit_limit_tree = (1, 40, (0, 25, 0, 5), (2, 2, 20, 10))
278
+ ```
279
+
280
+ Then, the customer and the bank run the following steps.
281
+
282
+ ```python
283
+ # customer: age = 30, income = 75 and open loans = 1
284
+ # so, the expected path is income > 40 -> open loans <= 2 -> credit limit of 20
285
+ customer_cs = LightPHE(algorithm_name = "Damgard-Geisler-Kroigaard")
286
+ encrypted_features = customer_cs.encrypt_decision_tree_input(
287
+ features = [30, 75, 1], bit_length = 8
288
+ )
289
+
290
+ # bank, having the customer's public key only
291
+ bank_cs = LightPHE(algorithm_name = "Damgard-Geisler-Kroigaard", key_file = "public.txt")
292
+ encrypted_comparisons, bank_state = bank_cs.evaluate_decision_tree_comparisons(
293
+ tree = credit_limit_tree, encrypted_features = encrypted_features
294
+ )
295
+
296
+ # customer answers randomly flipped comparisons, so cannot follow the path in the tree
297
+ encrypted_answers = customer_cs.answer_decision_tree_comparisons(encrypted_comparisons)
298
+
299
+ # bank keeps bank_state secret and finds the reached leaf in encrypted form
300
+ encrypted_leaves = bank_cs.evaluate_decision_tree_leaves(
301
+ server_state = bank_state, encrypted_answers = encrypted_answers
302
+ )
303
+
304
+ # customer learns only the credit limit
305
+ assert customer_cs.decrypt_decision_tree_output(encrypted_leaves) == 20
306
+ ```
307
+
308
+ ### Secure Comparison
309
+
310
+ Damgard-Geisler-Kroigaard lets a client and a server find out whether the client's private value is less than or equal to the server's private value, without revealing the values to each other. For instance, a bidder can check whether their bid beats the reserve price of an auction.
311
+
312
+ ```python
313
+ # client encrypts its value bit by bit
314
+ client_cs = LightPHE(algorithm_name = "Damgard-Geisler-Kroigaard")
315
+ encrypted_bid = client_cs.encrypt_comparison_input(value = 120, bit_length = 16)
316
+
317
+ # server compares it with its own value having the client's public key only
318
+ server_cs = LightPHE(algorithm_name = "Damgard-Geisler-Kroigaard", key_file = "public.txt")
319
+ encrypted_result = server_cs.evaluate_comparison(encrypted_input = encrypted_bid, value = 150)
320
+
321
+ # client learns only whether bid <= reserve price
322
+ assert client_cs.decrypt_comparison(encrypted_result) is True
323
+ ```
324
+
325
+ ### Encrypted Argmax
326
+
327
+ Damgard-Geisler-Kroigaard can also find the index of the maximum among encrypted values. For instance, a server can compute encrypted class scores of a linear classifier on a client's encrypted features, and then find the predicted class. The client learns only the index, neither the scores nor the intermediate comparisons, and the server learns nothing. The protocol needs a few round trips per value, so the server talks to the client through a callable. Locally, it can be the client's `answer_argmax` directly. Otherwise, wrap your network call with it.
328
+
329
+ ```python
330
+ # client encrypts its features
331
+ client_cs = LightPHE(algorithm_name = "Damgard-Geisler-Kroigaard")
332
+ encrypted_features = [client_cs.encrypt(feature) for feature in [3, 1, 4]]
333
+
334
+ # server computes class scores with its private weights homomorphically
335
+ server_cs = LightPHE(algorithm_name = "Damgard-Geisler-Kroigaard", key_file = "public.txt")
336
+ weights = [[1, 2, 0], [0, 1, 3], [2, 0, 1]]
337
+ encrypted_scores = []
338
+ for class_weights in weights:
339
+ score = encrypted_features[0] * class_weights[0]
340
+ for encrypted_feature, weight in zip(encrypted_features[1:], class_weights[1:]):
341
+ score = score + encrypted_feature * weight
342
+ encrypted_scores.append(score)
343
+
344
+ # server finds the argmax with the client's help - scores must fit into bit_length bits
345
+ encrypted_class = server_cs.evaluate_argmax(
346
+ encrypted_values = encrypted_scores, bit_length = 8, client = client_cs.answer_argmax
347
+ )
348
+
349
+ # client learns only the predicted class
350
+ assert client_cs.decrypt(encrypted_class) == 1
351
+ ```
352
+
353
+ ### Master Key Decryption
354
+
355
+ Bresson-Catalano-Pointcheval has two ways to decrypt a ciphertext. Each user decrypts with its own private key, while an authority holding the master key decrypts ciphertexts of all users. For instance, a company can let every employee encrypt with their own key, and still recover the data if an employee leaves. Keep in mind that the master key holder can read everything by design.
356
+
357
+ ```python
358
+ # authority generates the master key and publishes its public key
359
+ authority_cs = LightPHE(algorithm_name = "Bresson-Catalano-Pointcheval")
360
+ authority_cs.export_keys(target_file = "public.txt", public = True)
361
+
362
+ # each user generates its own key pair with the public key of the authority
363
+ public_cs = LightPHE(algorithm_name = "Bresson-Catalano-Pointcheval", key_file = "public.txt")
364
+ alice_cs = LightPHE(algorithm_name = "Bresson-Catalano-Pointcheval", keys = public_cs.generate_user_keys())
365
+ bob_cs = LightPHE(algorithm_name = "Bresson-Catalano-Pointcheval", keys = public_cs.generate_user_keys())
366
+
367
+ alice_ciphertext = alice_cs.encrypt(100) + alice_cs.encrypt(50)
368
+ bob_ciphertext = bob_cs.encrypt(200)
369
+
370
+ # users decrypt their own ciphertexts only
371
+ assert alice_cs.decrypt(alice_ciphertext) == 150
372
+ assert alice_cs.decrypt(bob_ciphertext) != 200
373
+
374
+ # authority decrypts ciphertexts of all users
375
+ assert authority_cs.decrypt_with_master_key(alice_ciphertext) == 150
376
+ assert authority_cs.decrypt_with_master_key(bob_ciphertext) == 200
377
+ ```
378
+
379
+ ### Verifiable Encryption
380
+
381
+ Camenisch-Shoup lets you encrypt a secret for someone else and prove that the ciphertext really contains it, without revealing the secret. For instance, Alice can escrow the private key of her crypto wallet with a trusted third party. A bank, which knows only her public key, verifies that the escrowed ciphertext contains its private key, and the trusted third party can recover it only under the conditions written in the label.
382
+
383
+ ```python
384
+ from lightecc import LightECC
385
+
386
+ # trusted third party builds the cryptosystem and publishes its public key
387
+ ttp_cs = LightPHE(algorithm_name = "Camenisch-Shoup")
388
+ ttp_cs.export_keys(target_file = "public.txt", public = True)
389
+
390
+ # alice's wallet key pair on secp256k1
391
+ ecc = LightECC(curve_name = "secp256k1")
392
+ private_key = 123456789
393
+ public_key = ecc.G * private_key
394
+
395
+ # alice encrypts her private key for the trusted third party with a proof
396
+ label = "release only if alice loses access to her wallet"
397
+ alice_cs = LightPHE(algorithm_name = "Camenisch-Shoup", key_file = "public.txt")
398
+ escrow = alice_cs.encrypt_verifiably(secret = private_key, label = label)
399
+
400
+ # bank verifies it with public keys only, without learning alice's private key
401
+ bank_cs = LightPHE(algorithm_name = "Camenisch-Shoup", key_file = "public.txt")
402
+ assert bank_cs.verify_encryption(escrow, public_point = public_key, label = label) is True
403
+
404
+ # trusted third party recovers the private key under the same label only
405
+ assert ttp_cs.decrypt_verifiable_ciphertext(escrow, label = label) == private_key
406
+ ```
407
+
408
+ Private keys of other curves can be escrowed by passing `form` and `curve` arguments while building the cryptosystem. Verifiable ciphertexts reject any modification, so homomorphic operations are applied to regular ciphertexts created with `encrypt`.
409
+
410
+ ### Adding Multiplication Support to Additive Algorithms
411
+
412
+ Additively homomorphic algorithms cannot multiply two ciphertexts. Boosting them with Catalano-Fiore transform adds one multiplication on top of them, without changing the algorithms themselves. This lets you evaluate degree-2 functions on encrypted data, such as dot products of two encrypted vectors, variances or squared distances. It supports Paillier, Damgard-Jurik, Bresson-Catalano-Pointcheval, Camenisch-Shoup, Elliptic Curve Paillier, Joye-Libert, Castagnos-Laguillaumie, Naccache-Stern, Benaloh and Damgard-Geisler-Kroigaard.
413
+
414
+ ```python
415
+ # client builds a boosted cryptosystem, and encrypts its private vectors
416
+ client_cs = LightPHE(algorithm_name = "Paillier", boost = "Catalano-Fiore")
417
+
418
+ x = [3, 1, 4]
419
+ y = [1, 5, 9]
420
+ encrypted_x = [client_cs.encrypt(value) for value in x]
421
+ encrypted_y = [client_cs.encrypt(value) for value in y]
422
+
423
+ # server computes dot product of encrypted vectors - ciphertexts carry the public key only
424
+ encrypted_dot = sum(ex * ey for ex, ey in zip(encrypted_x, encrypted_y))
425
+
426
+ # squared euclidean distance is also a degree-2 function
427
+ encrypted_distance = sum((ex - ey) * (ex - ey) for ex, ey in zip(encrypted_x, encrypted_y))
428
+
429
+ # client decrypts the results
430
+ assert client_cs.decrypt(encrypted_dot) == 44
431
+ assert client_cs.decrypt(encrypted_distance) == 45
432
+ ```
433
+
434
+ Multiplied ciphertexts can be added and multiplied with constants, but not multiplied with ciphertexts again. They keep a pair of ciphertexts for every product, so a dot product of length d grows d times. Only integers are supported.
435
+
436
+ ### Post-Quantum Security
437
+
438
+ Most algorithms in LightPHE rely on factoring or discrete logarithm problems, which a large enough quantum computer could solve with Shor's algorithm. Regev and Brakerski-Vaikuntanathan rely on learning with errors problems instead, which are believed to be hard for quantum computers too. Their default key sizes give about 128-bit security against quantum attacks.
439
+
440
+ Regev is additively homomorphic. Its key size is the lattice dimension, and its default is 2048.
441
+
442
+ ```python
443
+ cs = LightPHE(algorithm_name = "Regev")
444
+
445
+ c1 = cs.encrypt(1500)
446
+ c2 = cs.encrypt(2500)
447
+
448
+ assert cs.decrypt(c1 + c2) == 4000
449
+ assert cs.decrypt(c1 * 3) == 4500
450
+ ```
451
+
452
+ Brakerski-Vaikuntanathan additionally supports one multiplication of ciphertexts like Boneh-Goh-Nissim, but without pairings. Its key size is the degree of its polynomial ring, and its default is 4096.
453
+
454
+ ```python
455
+ cs = LightPHE(algorithm_name = "Brakerski-Vaikuntanathan")
456
+
457
+ c1 = cs.encrypt(15)
458
+ c2 = cs.encrypt(25)
459
+ c3 = cs.encrypt(100)
460
+
461
+ # one multiplication, then additions and multiplications with constants
462
+ assert cs.decrypt(c1 * c2 + c3) == 475
463
+ assert cs.decrypt((c1 * c2) * 2 + c1 * c3) == 2250
464
+ ```
465
+
466
+ Every ciphertext of these algorithms carries a small noise, which grows with each homomorphic operation, and decryption silently fails once it grows too much. With default settings, plaintexts must be less than 2^24. Regev has room for thousands of additions or multiplications with constants up to a few thousands, and a product of Brakerski-Vaikuntanathan has room for millions of times more noise. You can increase the plaintext space with `plaintext_limit` argument in exchange of less room for noise. Floats and tensors are not supported.
467
+
468
+ ### Vector Embeddings
469
+
470
+ LightPHE supports homomorphic encryption on vector embeddings. This is useful in privacy-preserving machine learning, secure aggregation, and confidential data processing.
471
+
472
+ ```python
473
+ # build an additively homomorphic cryptosystem (e.g. Paillier)
474
+ cs = LightPHE("Paillier")
475
+
476
+ # define plain embeddings
477
+ t1 = [1.005, 2.05, 3.6, 4, 4.02, 3.5]
478
+ t2 = [5, 6.2, 7.5, 8.02, 8.02, 4.5]
479
+ t3 = [1.03, 2.04, 3.05, 7.02, 2.01, 1.06]
480
+
481
+ # encrypt embeddings
482
+ c1, c2 = cs.encrypt(t1), cs.encrypt(t2)
483
+
484
+ # perform addition of two encrypted embeddings
485
+ c4 = c1 + c2
486
+
487
+ # perform scalar multiplication on an embedding
488
+ c5 = 3 * c1
489
+
490
+ # perform element-wise multiplication between an encrypted embedding and plain embedding
491
+ c6 = c1 * t3
492
+
493
+ # encrypted dot product (likewise cosine similarity)
494
+ c7 = c1 @ t3
495
+
496
+ # proof of work
497
+ assert np.allclose(cs.decrypt(c4), [a + b for a, b in zip(t1, t2)], rtol=1e-2)
498
+ assert np.allclose(cs.decrypt(c5), [a * 3 for a in t1], rtol=1e-2)
499
+ assert np.allclose(cs.decrypt(c6), [a * b for a, b in zip(t1, t3)], rtol=1e-2)
500
+ assert np.allclose(cs.decrypt(c7)[0], sum([a * b for a, b in zip(t1, t3)]), rtol=1e-2)
501
+ ```
502
+
503
+ # Contributing
504
+
505
+ 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.
506
+
507
+ You should be able run `make test` and `make lint` commands successfully before committing. Once a PR is created, GitHub test workflow will be run automatically and unit test results will be available in [GitHub actions](https://github.com/serengil/LightPHE/actions/workflows/tests.yml) before approval.
508
+
509
+ ## LightPHE Ecosystem
510
+
511
+ `LightPHE` is available across multiple programming languages with consistent APIs and native performance for each ecosystem. Choose the port that fits your stack:
512
+
513
+ | Language | Repository | Ecosystem Focus |
514
+ | :--- | :--- | :--- |
515
+ | **Python** | [`lightphe`](https://github.com/serengil/lightphe) | AI, Research & Prototyping |
516
+ | **Go** | [`lightphe-go`](https://github.com/serengil/lightphe-go) | Cloud-Native & High-Throughput Microservices |
517
+ | **Java** | [`lightphe4j`](https://github.com/serengil/lightphe4j) | Enterprise, Banking & Legacy Systems |
518
+ | **TypeScript** | [`lightphe-ts`](https://github.com/serengil/lightphe-ts) | Client-Side Privacy & Node.js Backend |
519
+
520
+ # Support
521
+
522
+ There are many ways to support a project - starring⭐️ the GitHub repo is just one 🙏
523
+
524
+ 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).
525
+
526
+ <a href="https://www.patreon.com/serengil?repo=lightphe">
527
+ <img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/patreon.png" width="30%">
528
+ </a>
529
+
530
+ <a href="https://buymeacoffee.com/serengil">
531
+ <img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/bmc-button.png" width="25%">
532
+ </a>
533
+
534
+ Also, your company's logo will be shown on README on GitHub if you become sponsor in gold, silver or bronze tiers.
535
+
536
+ # Citation
537
+
538
+ Please cite LightPHE in your publications if it helps your research. Here is its BibTex entry:
539
+
540
+ ```BibTeX
541
+ @article{lightphe,
542
+ title = {Sustainable Cryptography: Carbon Asymmetry in Partially Homomorphic Encryption in the Cloud},
543
+ author = {Ozpinar, Alper and Serengil, Sefik Ilkin},
544
+ journal = {Symmetry},
545
+ volume = {18},
546
+ number = {5},
547
+ pages = {832},
548
+ url = {https://www.mdpi.com/2073-8994/18/5/832},
549
+ doi = {10.3390/sym18050832},
550
+ note = {Special Issue: Symmetry in Cryptography and Cybersecurity},
551
+ year = {2026}
552
+ }
553
+ ```
554
+
555
+ Also, if you use LightPHE in your projects, please add `lightphe` in the `requirements.txt`.
556
+
557
+ # License
558
+
559
+ LightPHE is licensed under the MIT License - see [`LICENSE`](https://github.com/serengil/LightPHE/blob/master/LICENSE) for more details.
560
+
561
+