lightphe 0.0.8__tar.gz → 0.0.9__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 (52) hide show
  1. {lightphe-0.0.8 → lightphe-0.0.9}/PKG-INFO +75 -106
  2. {lightphe-0.0.8 → lightphe-0.0.9}/README.md +74 -105
  3. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/__init__.py +14 -44
  4. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/cryptosystems/Benaloh.py +3 -2
  5. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/cryptosystems/DamgardJurik.py +2 -2
  6. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/cryptosystems/ElGamal.py +2 -2
  7. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/cryptosystems/EllipticCurveElGamal.py +24 -13
  8. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/cryptosystems/GoldwasserMicali.py +3 -2
  9. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/cryptosystems/NaccacheStern.py +3 -2
  10. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/cryptosystems/OkamotoUchiyama.py +2 -2
  11. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/cryptosystems/Paillier.py +2 -2
  12. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/cryptosystems/RSA.py +2 -2
  13. lightphe-0.0.9/lightphe/elliptic_curve_forms/edwards.py +61 -0
  14. lightphe-0.0.9/lightphe/elliptic_curve_forms/weierstrass.py +75 -0
  15. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/models/Ciphertext.py +3 -3
  16. lightphe-0.0.9/lightphe/models/EllipticCurve.py +45 -0
  17. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/models/Tensor.py +9 -0
  18. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe.egg-info/PKG-INFO +75 -106
  19. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe.egg-info/SOURCES.txt +3 -2
  20. {lightphe-0.0.8 → lightphe-0.0.9}/setup.py +1 -1
  21. lightphe-0.0.9/tests/__init__.py +0 -0
  22. lightphe-0.0.9/tests/test_ellipticcurveelgamal.py +69 -0
  23. lightphe-0.0.8/lightphe/elliptic/Weierstrass.py +0 -98
  24. lightphe-0.0.8/lightphe/models/EllipticCurve.py +0 -18
  25. lightphe-0.0.8/tests/__init__.py +0 -219
  26. lightphe-0.0.8/tests/test_ellipticcurveelgamal.py +0 -64
  27. {lightphe-0.0.8 → lightphe-0.0.9}/LICENSE +0 -0
  28. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/commons/__init__.py +0 -0
  29. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/commons/logger.py +0 -0
  30. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/commons/phe_utils.py +0 -0
  31. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/cryptosystems/__init__.py +0 -0
  32. {lightphe-0.0.8/lightphe/elliptic → lightphe-0.0.9/lightphe/elliptic_curve_forms}/__init__.py +0 -0
  33. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/models/Algorithm.py +0 -0
  34. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/models/Homomorphic.py +0 -0
  35. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe/models/__init__.py +0 -0
  36. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe.egg-info/dependency_links.txt +0 -0
  37. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe.egg-info/requires.txt +0 -0
  38. {lightphe-0.0.8 → lightphe-0.0.9}/lightphe.egg-info/top_level.txt +0 -0
  39. {lightphe-0.0.8 → lightphe-0.0.9}/requirements.txt +0 -0
  40. {lightphe-0.0.8 → lightphe-0.0.9}/setup.cfg +0 -0
  41. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_benaloh.py +0 -0
  42. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_cloud.py +0 -0
  43. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_damgard.py +0 -0
  44. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_elgamal.py +0 -0
  45. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_exporting_keys.py +0 -0
  46. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_goldwasser.py +0 -0
  47. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_naccache.py +0 -0
  48. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_okamoto.py +0 -0
  49. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_paillier.py +0 -0
  50. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_rsa.py +0 -0
  51. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_salary.py +0 -0
  52. {lightphe-0.0.8 → lightphe-0.0.9}/tests/test_tensors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightphe
3
- Version: 0.0.8
3
+ Version: 0.0.9
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/), [`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/) 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/).
40
40
 
41
41
  # Partially vs Fully Homomorphic Encryption
42
42
 
@@ -98,7 +98,7 @@ algorithms = [
98
98
  "EllipticCurve-ElGamal"
99
99
  ]
100
100
 
101
- cs = LightPHE(algorithm_name = algorithms[0])
101
+ phe = LightPHE(algorithm_name = algorithms[0])
102
102
  ```
103
103
 
104
104
  # Encryption & Decryption
@@ -110,10 +110,10 @@ Once you built your cryptosystem, you will be able to encrypt and decrypt messag
110
110
  m = 17
111
111
 
112
112
  # calculate ciphertext
113
- c = cs.encrypt(m)
113
+ c = phe.encrypt(m)
114
114
 
115
115
  # proof of work
116
- assert cs.decrypt(c) == m
116
+ assert phe.decrypt(c) == m
117
117
  ```
118
118
 
119
119
  # Homomorphic Operations
@@ -122,76 +122,90 @@ Once you have the ciphertext, you will be able to perform homomorphic operations
122
122
 
123
123
  ### On-Prem Encryption
124
124
 
125
- This Python code snippet demonstrates how to generate a private-public key pair using the Paillier cryptosystem via the LightPHE library. First, an instance of the LightPHE class is created with the Paillier algorithm. Then, the public key is exported and saved to a file named "public.txt" to build the same cryptosystem with only public key in the cloud side later. The code defines two plaintext values, m1 and m2, respectively. These plaintext values are encrypted to generate ciphertexts c1 and c2 using the public key. Finally, the ciphertexts c1 and c2 are prepared to be sent to a cloud system for secure processing or storage.
125
+ This code snippet illustrates how to generate a random public-private key pair using the Paillier and encrypt a plaintext pair. The resulting ciphertext pair, c1 and c2, along with the public key, is then sent from the on-premises environment to the cloud.
126
126
 
127
127
  ```python
128
- # generate private-public key pair
129
- cs = LightPHE(algorithm_name = "Paillier")
130
-
131
- # export public key to build same cryptosystem with only public key in the cloud
132
- cs.export_keys(target_file = "public.txt", public = True)
133
-
134
- # export private key to build same cryptosystem on-prem later
135
- cs.export_keys(target_file = "private.txt", public = False)
136
-
137
- # define plaintexts
138
- m1 = 17
139
- m2 = 23
140
-
141
- # calculate ciphertexts
142
- c1 = cs.encrypt(m1).value
143
- c2 = cs.encrypt(m2).value
144
-
145
- # send c1 and c2 pair to a cloud system
128
+ def on_premise() -> Tuple[int, int, dict]:
129
+ """
130
+ Executes on-premise operations: initializes a cryptosystem by generating
131
+ a random public-private key pair, then encrypts two plaintext values.
132
+
133
+ Returns:
134
+ result (tuple): A tuple containing:
135
+ - c1 (int): The first ciphertext
136
+ - c2 (int): The second ciphertext
137
+ - public_key (dict): The public key for the cryptosystem
138
+ """
139
+ # generate a random private-public key pair
140
+ phe = LightPHE(algorithm_name = "Paillier")
141
+
142
+ # define plaintexts
143
+ m1 = 10000 # base salary in usd
144
+ m2 = 500 # wage increase in usd
145
+
146
+ # calculate ciphertexts
147
+ c1 = phe.encrypt(m1).value
148
+ c2 = phe.encrypt(m2).value
149
+
150
+ return (c1, c2, phe.cs.public_key)
146
151
  ```
147
152
 
148
153
  ### Homomorphic Operations on Cloud
149
154
 
150
- This Python code snippet illustrates how to handle encrypted data on the cloud side using the Paillier cryptosystem with the LightPHE library. Upon receiving the encrypted values c1 and c2, the cloud system initializes the cryptosystem using the exported public key stored in public.txt. To ensure the security of the data, a test is performed to confirm that the cloud system cannot decrypt c1 and c2 without the private key. This is done using the pytest library, which raises a ValueError if decryption is attempted, verifying that decryption is not possible without the private key. Finally, the code demonstrates homomorphic addition by adding the two ciphertexts, resulting in a new ciphertext c3 that represents the encrypted sum of the original plaintext values.
155
+ This code snippet demonstrates how to perform homomorphic addition on the cloud side without using the private key. However, the cloud is unable to decrypt c3 itself, even though it is the one that calculated it.
151
156
 
152
157
  ```python
153
- # cloud side receives encrypted c1 and c2
154
-
155
- # restore cryptosystem with the exported public key
156
- cs = LightPHE(algorithm_name = "Paillier", key_file = "public.txt")
157
-
158
- # convert c1 and c2 to ciphertext objects
159
- c1 = cs.create_ciphertext_obj(c1)
160
- c2 = cs.create_ciphertext_obj(c2)
161
-
162
- # confirm that cloud cannot decrypt c1
163
- with pytest.raises(ValueError, match="You must have private key to perform decryption"):
164
- cs.decrypt(c1)
165
-
166
- # confirm that cloud cannot decrypt c2
167
- with pytest.raises(ValueError, match="You must have private key to perform decryption"):
168
- cs.decrypt(c2)
169
-
170
- # homomorphic addition - private key not required
171
- c3 = c1 + c2
172
-
173
- # confirm that cloud cannot decrypt c3
174
- with pytest.raises(ValueError, match="You must have private key to perform decryption"):
175
- cs.decrypt(c3)
158
+ def cloud(c1: int, c2: int, public_key: dict) -> int:
159
+ """
160
+ Performs cloud-side operations: reconstructs a cryptosystem using the
161
+ provided public key and executes a homomorphic addition on two ciphertexts.
162
+
163
+ Args:
164
+ c1 (int): The first ciphertext
165
+ c2 (int): The second ciphertext
166
+ public_key (dict): The public key of an existing cryptosystem
167
+ Retunrs:
168
+ c3 (int): The resulting ciphertext after homomorphic addition
169
+ """
170
+ # restore cryptosystem with just the public key
171
+ phe = LightPHE(algorithm_name = "Paillier", keys = public_key)
172
+
173
+ # cast c1 and c2 to ciphertext objects
174
+ c1 = phe.create_ciphertext_obj(c1)
175
+ c2 = phe.create_ciphertext_obj(c2)
176
+
177
+ # confirm that cloud cannot decrypt c1
178
+ with pytest.raises(ValueError, match="You must have private key"):
179
+ phe.decrypt(c1)
180
+
181
+ # confirm that cloud cannot decrypt c2
182
+ with pytest.raises(ValueError, match="You must have private key"):
183
+ phe.decrypt(c2)
184
+
185
+ # perform homomorphic addition
186
+ c3 = c1 + c2
187
+
188
+ # confirm that cloud cannot decrypt c3
189
+ with pytest.raises(ValueError, match="You must have private key"):
190
+ phe.decrypt(c3)
191
+
192
+ return c3.value
176
193
  ```
177
194
 
178
- ### On-Prem Decryption
195
+ ### On-Prem Decryption And Proof of Work
179
196
 
180
- This Python code snippet demonstrates the final step in a secure computation process using homomorphic encryption with the LightPHE library. After receiving the ciphertext c3 from the cloud, which is the result of homomorphic addition of two ciphertexts c1 and c2, the on-premises system (which has the private key) decrypts c3 to verify the result. The decrypted value is then asserted to be equal to the sum of the original plaintext values m1 and m2. This step ensures the correctness of the homomorphic computation performed by the cloud.
197
+ This code snippet demonstrates the proof of work. Even though c3 was calculated in the cloud by adding c1 and c2, on-premises can validate that its decryption must be equal to the addition of plaintexts m1 and m2.
181
198
 
182
199
  ```python
183
- # on-prem side receives c3 from cloud
184
-
185
- # restore cryptosystem with the exported private key
186
- cs = LightPHE(algorithm_name = "Paillier", key_file = "private.txt")
187
-
188
200
  # proof of work - private key required
189
- assert cs.decrypt(c3) == m1 + m2
201
+ assert phe.decrypt(c3) == m1 + m2
190
202
  ```
191
203
 
204
+ In this homomorphic pipeline, the cloud's computational power was utilized to calculate c3, but it can only be decrypted by the on-premises side. Additionally, while we performed the encryption on the on-premises side, this is not strictly necessary; only the public key is required for encryption. Therefore, encryption can also be performed on the non-premises side. This approach is particularly convenient when collecting data from multiple edge devices and storing all of it in the cloud simultaneously.
205
+
192
206
  ### Scalar Multiplication
193
207
 
194
- Besides, Paillier is supporting multiplying ciphertexts by a known plain constant. This Python code snippet demonstrates how to perform scalar multiplication on encrypted data using homomorphic encryption with the LightPHE library. The factor k is set to 1.05, representing a 5% increase. On the cloud side, this factor is used to multiply the ciphertext c1, resulting in a new ciphertext c4. When the on-premises system, which holds the private key, receives c4, it decrypts it and verifies that the decrypted value matches the original plaintext m1 scaled by k (i.e., 1.05 * m1). This ensures that the homomorphic scalar multiplication was performed correctly on the encrypted data.
208
+ Besides, Paillier is supporting multiplying ciphertexts by a known plain constant. This code snippet demonstrates how to perform scalar multiplication on encrypted data using Paillier homomorphic encryption with the LightPHE library.
195
209
 
196
210
  ```python
197
211
  # increasing something 5%
@@ -201,7 +215,7 @@ k = 1.05
201
215
  c4 = k * c1
202
216
 
203
217
  # proof of work on-prem - private key is required
204
- assert cs.decrypt(c4) == k * m1
218
+ assert phe.decrypt(c4) == k * m1
205
219
  ```
206
220
 
207
221
  ### Ciphertext Regeneration
@@ -209,10 +223,10 @@ assert cs.decrypt(c4) == k * m1
209
223
  Similar to the most of additively homomorphic algorithms, Paillier lets 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.
210
224
 
211
225
  ```python
212
- c1_prime = cs.regenerate_ciphertext(c1)
226
+ c1_prime = phe.regenerate_ciphertext(c1)
213
227
  assert c1_prime.value != c1.value
214
- assert cs.decrypt(c1_prime) == m1
215
- assert cs.decrypt(c1) == m1
228
+ assert phe.decrypt(c1_prime) == m1
229
+ assert phe.decrypt(c1) == m1
216
230
  ```
217
231
 
218
232
  ### Unsupported Operations
@@ -231,51 +245,6 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
231
245
 
232
246
  However, if you tried to multiply ciphertexts with RSA, or xor ciphertexts with Goldwasser-Micali, these will be succeeded because those cryptosystems support those homomorphic operations.
233
247
 
234
- # Working with vectors
235
-
236
- You can encrypt the output vectors of machine learning models with LightPHE. These encrypted tensors come with homomorphic operation support including homomorphic addition, element-wise multiplication and scalar multiplication.
237
-
238
- ```python
239
- # build an additively homomorphic cryptosystem
240
- cs = LightPHE(algorithm_name="Paillier")
241
-
242
- # define plain tensors
243
- t1 = [1.005, 2.05, 3.5, 4]
244
- t2 = [5, 6.2, 7.002, 8.02]
245
-
246
- # encrypt tensors
247
- c1 = cs.encrypt(t1)
248
- c2 = cs.encrypt(t2)
249
-
250
- # perform homomorphic addition
251
- c3 = c1 + c2
252
-
253
- # perform homomorphic element-wise multiplication
254
- c4 = c1 * c2
255
-
256
- # perform homomorphic scalar multiplication
257
- k = 5
258
- c5 = k * c1
259
-
260
- # decrypt the addition tensor
261
- t3 = cs.decrypt(c3)
262
-
263
- # decrypt the element-wise multiplied tensor
264
- t4 = cs.decrypt(c4)
265
-
266
- # decrypt the scalar multiplied tensor
267
- t5 = cs.decrypt(c5)
268
-
269
- # data validations
270
- threshold = 0.5
271
- for i in range(0, len(t1)):
272
- assert abs((t1[i] + t2[i]) - t3[i]) < threshold
273
- assert abs((t1[i] * t2[i]) - t4[i]) < threshold
274
- assert abs((t1[i] * k) - t5[i]) < threshold
275
- ```
276
-
277
- Unfortunately, vector multiplication (dot product) requires both homomorphic addition and homomorphic multiplication and this cannot be done with partially homomorphic encryption algorithms.
278
-
279
248
  # Contributing
280
249
 
281
250
  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.
@@ -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/), [`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/) 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/).
24
24
 
25
25
  # Partially vs Fully Homomorphic Encryption
26
26
 
@@ -82,7 +82,7 @@ algorithms = [
82
82
  "EllipticCurve-ElGamal"
83
83
  ]
84
84
 
85
- cs = LightPHE(algorithm_name = algorithms[0])
85
+ phe = LightPHE(algorithm_name = algorithms[0])
86
86
  ```
87
87
 
88
88
  # Encryption & Decryption
@@ -94,10 +94,10 @@ Once you built your cryptosystem, you will be able to encrypt and decrypt messag
94
94
  m = 17
95
95
 
96
96
  # calculate ciphertext
97
- c = cs.encrypt(m)
97
+ c = phe.encrypt(m)
98
98
 
99
99
  # proof of work
100
- assert cs.decrypt(c) == m
100
+ assert phe.decrypt(c) == m
101
101
  ```
102
102
 
103
103
  # Homomorphic Operations
@@ -106,76 +106,90 @@ Once you have the ciphertext, you will be able to perform homomorphic operations
106
106
 
107
107
  ### On-Prem Encryption
108
108
 
109
- This Python code snippet demonstrates how to generate a private-public key pair using the Paillier cryptosystem via the LightPHE library. First, an instance of the LightPHE class is created with the Paillier algorithm. Then, the public key is exported and saved to a file named "public.txt" to build the same cryptosystem with only public key in the cloud side later. The code defines two plaintext values, m1 and m2, respectively. These plaintext values are encrypted to generate ciphertexts c1 and c2 using the public key. Finally, the ciphertexts c1 and c2 are prepared to be sent to a cloud system for secure processing or storage.
109
+ This code snippet illustrates how to generate a random public-private key pair using the Paillier and encrypt a plaintext pair. The resulting ciphertext pair, c1 and c2, along with the public key, is then sent from the on-premises environment to the cloud.
110
110
 
111
111
  ```python
112
- # generate private-public key pair
113
- cs = LightPHE(algorithm_name = "Paillier")
114
-
115
- # export public key to build same cryptosystem with only public key in the cloud
116
- cs.export_keys(target_file = "public.txt", public = True)
117
-
118
- # export private key to build same cryptosystem on-prem later
119
- cs.export_keys(target_file = "private.txt", public = False)
120
-
121
- # define plaintexts
122
- m1 = 17
123
- m2 = 23
124
-
125
- # calculate ciphertexts
126
- c1 = cs.encrypt(m1).value
127
- c2 = cs.encrypt(m2).value
128
-
129
- # send c1 and c2 pair to a cloud system
112
+ def on_premise() -> Tuple[int, int, dict]:
113
+ """
114
+ Executes on-premise operations: initializes a cryptosystem by generating
115
+ a random public-private key pair, then encrypts two plaintext values.
116
+
117
+ Returns:
118
+ result (tuple): A tuple containing:
119
+ - c1 (int): The first ciphertext
120
+ - c2 (int): The second ciphertext
121
+ - public_key (dict): The public key for the cryptosystem
122
+ """
123
+ # generate a random private-public key pair
124
+ phe = LightPHE(algorithm_name = "Paillier")
125
+
126
+ # define plaintexts
127
+ m1 = 10000 # base salary in usd
128
+ m2 = 500 # wage increase in usd
129
+
130
+ # calculate ciphertexts
131
+ c1 = phe.encrypt(m1).value
132
+ c2 = phe.encrypt(m2).value
133
+
134
+ return (c1, c2, phe.cs.public_key)
130
135
  ```
131
136
 
132
137
  ### Homomorphic Operations on Cloud
133
138
 
134
- This Python code snippet illustrates how to handle encrypted data on the cloud side using the Paillier cryptosystem with the LightPHE library. Upon receiving the encrypted values c1 and c2, the cloud system initializes the cryptosystem using the exported public key stored in public.txt. To ensure the security of the data, a test is performed to confirm that the cloud system cannot decrypt c1 and c2 without the private key. This is done using the pytest library, which raises a ValueError if decryption is attempted, verifying that decryption is not possible without the private key. Finally, the code demonstrates homomorphic addition by adding the two ciphertexts, resulting in a new ciphertext c3 that represents the encrypted sum of the original plaintext values.
139
+ This code snippet demonstrates how to perform homomorphic addition on the cloud side without using the private key. However, the cloud is unable to decrypt c3 itself, even though it is the one that calculated it.
135
140
 
136
141
  ```python
137
- # cloud side receives encrypted c1 and c2
138
-
139
- # restore cryptosystem with the exported public key
140
- cs = LightPHE(algorithm_name = "Paillier", key_file = "public.txt")
141
-
142
- # convert c1 and c2 to ciphertext objects
143
- c1 = cs.create_ciphertext_obj(c1)
144
- c2 = cs.create_ciphertext_obj(c2)
145
-
146
- # confirm that cloud cannot decrypt c1
147
- with pytest.raises(ValueError, match="You must have private key to perform decryption"):
148
- cs.decrypt(c1)
149
-
150
- # confirm that cloud cannot decrypt c2
151
- with pytest.raises(ValueError, match="You must have private key to perform decryption"):
152
- cs.decrypt(c2)
153
-
154
- # homomorphic addition - private key not required
155
- c3 = c1 + c2
156
-
157
- # confirm that cloud cannot decrypt c3
158
- with pytest.raises(ValueError, match="You must have private key to perform decryption"):
159
- cs.decrypt(c3)
142
+ def cloud(c1: int, c2: int, public_key: dict) -> int:
143
+ """
144
+ Performs cloud-side operations: reconstructs a cryptosystem using the
145
+ provided public key and executes a homomorphic addition on two ciphertexts.
146
+
147
+ Args:
148
+ c1 (int): The first ciphertext
149
+ c2 (int): The second ciphertext
150
+ public_key (dict): The public key of an existing cryptosystem
151
+ Retunrs:
152
+ c3 (int): The resulting ciphertext after homomorphic addition
153
+ """
154
+ # restore cryptosystem with just the public key
155
+ phe = LightPHE(algorithm_name = "Paillier", keys = public_key)
156
+
157
+ # cast c1 and c2 to ciphertext objects
158
+ c1 = phe.create_ciphertext_obj(c1)
159
+ c2 = phe.create_ciphertext_obj(c2)
160
+
161
+ # confirm that cloud cannot decrypt c1
162
+ with pytest.raises(ValueError, match="You must have private key"):
163
+ phe.decrypt(c1)
164
+
165
+ # confirm that cloud cannot decrypt c2
166
+ with pytest.raises(ValueError, match="You must have private key"):
167
+ phe.decrypt(c2)
168
+
169
+ # perform homomorphic addition
170
+ c3 = c1 + c2
171
+
172
+ # confirm that cloud cannot decrypt c3
173
+ with pytest.raises(ValueError, match="You must have private key"):
174
+ phe.decrypt(c3)
175
+
176
+ return c3.value
160
177
  ```
161
178
 
162
- ### On-Prem Decryption
179
+ ### On-Prem Decryption And Proof of Work
163
180
 
164
- This Python code snippet demonstrates the final step in a secure computation process using homomorphic encryption with the LightPHE library. After receiving the ciphertext c3 from the cloud, which is the result of homomorphic addition of two ciphertexts c1 and c2, the on-premises system (which has the private key) decrypts c3 to verify the result. The decrypted value is then asserted to be equal to the sum of the original plaintext values m1 and m2. This step ensures the correctness of the homomorphic computation performed by the cloud.
181
+ This code snippet demonstrates the proof of work. Even though c3 was calculated in the cloud by adding c1 and c2, on-premises can validate that its decryption must be equal to the addition of plaintexts m1 and m2.
165
182
 
166
183
  ```python
167
- # on-prem side receives c3 from cloud
168
-
169
- # restore cryptosystem with the exported private key
170
- cs = LightPHE(algorithm_name = "Paillier", key_file = "private.txt")
171
-
172
184
  # proof of work - private key required
173
- assert cs.decrypt(c3) == m1 + m2
185
+ assert phe.decrypt(c3) == m1 + m2
174
186
  ```
175
187
 
188
+ In this homomorphic pipeline, the cloud's computational power was utilized to calculate c3, but it can only be decrypted by the on-premises side. Additionally, while we performed the encryption on the on-premises side, this is not strictly necessary; only the public key is required for encryption. Therefore, encryption can also be performed on the non-premises side. This approach is particularly convenient when collecting data from multiple edge devices and storing all of it in the cloud simultaneously.
189
+
176
190
  ### Scalar Multiplication
177
191
 
178
- Besides, Paillier is supporting multiplying ciphertexts by a known plain constant. This Python code snippet demonstrates how to perform scalar multiplication on encrypted data using homomorphic encryption with the LightPHE library. The factor k is set to 1.05, representing a 5% increase. On the cloud side, this factor is used to multiply the ciphertext c1, resulting in a new ciphertext c4. When the on-premises system, which holds the private key, receives c4, it decrypts it and verifies that the decrypted value matches the original plaintext m1 scaled by k (i.e., 1.05 * m1). This ensures that the homomorphic scalar multiplication was performed correctly on the encrypted data.
192
+ Besides, Paillier is supporting multiplying ciphertexts by a known plain constant. This code snippet demonstrates how to perform scalar multiplication on encrypted data using Paillier homomorphic encryption with the LightPHE library.
179
193
 
180
194
  ```python
181
195
  # increasing something 5%
@@ -185,7 +199,7 @@ k = 1.05
185
199
  c4 = k * c1
186
200
 
187
201
  # proof of work on-prem - private key is required
188
- assert cs.decrypt(c4) == k * m1
202
+ assert phe.decrypt(c4) == k * m1
189
203
  ```
190
204
 
191
205
  ### Ciphertext Regeneration
@@ -193,10 +207,10 @@ assert cs.decrypt(c4) == k * m1
193
207
  Similar to the most of additively homomorphic algorithms, Paillier lets 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.
194
208
 
195
209
  ```python
196
- c1_prime = cs.regenerate_ciphertext(c1)
210
+ c1_prime = phe.regenerate_ciphertext(c1)
197
211
  assert c1_prime.value != c1.value
198
- assert cs.decrypt(c1_prime) == m1
199
- assert cs.decrypt(c1) == m1
212
+ assert phe.decrypt(c1_prime) == m1
213
+ assert phe.decrypt(c1) == m1
200
214
  ```
201
215
 
202
216
  ### Unsupported Operations
@@ -215,51 +229,6 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
215
229
 
216
230
  However, if you tried to multiply ciphertexts with RSA, or xor ciphertexts with Goldwasser-Micali, these will be succeeded because those cryptosystems support those homomorphic operations.
217
231
 
218
- # Working with vectors
219
-
220
- You can encrypt the output vectors of machine learning models with LightPHE. These encrypted tensors come with homomorphic operation support including homomorphic addition, element-wise multiplication and scalar multiplication.
221
-
222
- ```python
223
- # build an additively homomorphic cryptosystem
224
- cs = LightPHE(algorithm_name="Paillier")
225
-
226
- # define plain tensors
227
- t1 = [1.005, 2.05, 3.5, 4]
228
- t2 = [5, 6.2, 7.002, 8.02]
229
-
230
- # encrypt tensors
231
- c1 = cs.encrypt(t1)
232
- c2 = cs.encrypt(t2)
233
-
234
- # perform homomorphic addition
235
- c3 = c1 + c2
236
-
237
- # perform homomorphic element-wise multiplication
238
- c4 = c1 * c2
239
-
240
- # perform homomorphic scalar multiplication
241
- k = 5
242
- c5 = k * c1
243
-
244
- # decrypt the addition tensor
245
- t3 = cs.decrypt(c3)
246
-
247
- # decrypt the element-wise multiplied tensor
248
- t4 = cs.decrypt(c4)
249
-
250
- # decrypt the scalar multiplied tensor
251
- t5 = cs.decrypt(c5)
252
-
253
- # data validations
254
- threshold = 0.5
255
- for i in range(0, len(t1)):
256
- assert abs((t1[i] + t2[i]) - t3[i]) < threshold
257
- assert abs((t1[i] * t2[i]) - t4[i]) < threshold
258
- assert abs((t1[i] * k) - t5[i]) < threshold
259
- ```
260
-
261
- Unfortunately, vector multiplication (dot product) requires both homomorphic addition and homomorphic multiplication and this cannot be done with partially homomorphic encryption algorithms.
262
-
263
232
  # Contributing
264
233
 
265
234
  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.
@@ -23,6 +23,7 @@ logger = Logger(module="lightphe/__init__.py")
23
23
 
24
24
 
25
25
  class LightPHE:
26
+ __version__ = "0.0.9"
26
27
  def __init__(
27
28
  self,
28
29
  algorithm_name: str,
@@ -30,6 +31,7 @@ class LightPHE:
30
31
  key_file: Optional[str] = None,
31
32
  key_size: Optional[int] = None,
32
33
  precision: int = 5,
34
+ form: str = "weierstrass",
33
35
  ):
34
36
  """
35
37
  Build LightPHE class
@@ -41,15 +43,19 @@ class LightPHE:
41
43
  key_file (str): if keys are exported, you can load them into cryptosystem
42
44
  key_size (int): key size in bits
43
45
  precision (int): precision for homomorphic operations on tensors
46
+ form (str): specifies the form of the elliptic curve.
47
+ Options: 'weierstrass' (default), 'edwards'.
48
+ This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
44
49
  """
45
50
  self.algorithm_name = algorithm_name
46
51
  self.precision = precision
52
+ self.form = form
47
53
 
48
54
  if key_file is not None:
49
55
  keys = self.restore_keys(target_file=key_file)
50
56
 
51
57
  self.cs: Homomorphic = self.__build_cryptosystem(
52
- algorithm_name=algorithm_name, keys=keys, key_size=key_size
58
+ algorithm_name=algorithm_name, keys=keys, key_size=key_size, form=form,
53
59
  )
54
60
 
55
61
  def __build_cryptosystem(
@@ -57,6 +63,7 @@ class LightPHE:
57
63
  algorithm_name: str,
58
64
  keys: Optional[dict] = None,
59
65
  key_size: Optional[int] = None,
66
+ form: Optional[str] = None,
60
67
  ) -> Union[
61
68
  RSA,
62
69
  ElGamal,
@@ -72,17 +79,16 @@ class LightPHE:
72
79
  Args:
73
80
  algorithm_name (str): RSA | ElGamal | Exponential-ElGamal | EllipticCurve-ElGamal
74
81
  | Paillier | Damgard-Jurik | Okamoto-Uchiyama | Benaloh | Naccache-Stern
75
- | Goldwasser-Micali
82
+ | Goldwasser-Micali | Edwards-ElGamal
76
83
  keys (dict): optional private-public key pair
77
84
  key_file (str): if keys are exported, you can load them into cryptosystem
78
85
  key_size (int): key size in bits
86
+ form (str): specifies the form of the elliptic curve.
87
+ Options: 'weierstrass' (default), 'edwards'.
88
+ This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
79
89
  Returns
80
90
  cryptosystem
81
91
  """
82
-
83
- if key_size is None:
84
- key_size = self.recommend_key_size(algorithm_name=algorithm_name)
85
-
86
92
  # build cryptosystem
87
93
  if algorithm_name == Algorithm.RSA:
88
94
  cs = RSA(keys=keys, key_size=key_size)
@@ -91,7 +97,7 @@ class LightPHE:
91
97
  elif algorithm_name == Algorithm.ExponentialElGamal:
92
98
  cs = ElGamal(keys=keys, key_size=key_size, exponential=True)
93
99
  elif algorithm_name == Algorithm.EllipticCurveElGamal:
94
- cs = EllipticCurveElGamal(keys=keys, key_size=key_size)
100
+ cs = EllipticCurveElGamal(keys=keys, key_size=key_size, form=form)
95
101
  elif algorithm_name == Algorithm.Paillier:
96
102
  cs = Paillier(keys=keys, key_size=key_size)
97
103
  elif algorithm_name == Algorithm.DamgardJurik:
@@ -126,7 +132,7 @@ class LightPHE:
126
132
  ciphertext = self.cs.encrypt(
127
133
  plaintext=phe_utils.parse_int(value=plaintext, modulo=self.cs.plaintext_modulo)
128
134
  )
129
- return Ciphertext(algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext)
135
+ return Ciphertext(algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext, form=self.form)
130
136
 
131
137
  def decrypt(
132
138
  self, ciphertext: Union[Ciphertext, EncryptedTensor]
@@ -289,42 +295,6 @@ class LightPHE:
289
295
  raise ValueError(f"File {target_file} must have public_key key")
290
296
  return keys
291
297
 
292
- def recommend_key_size(self, algorithm_name: str) -> int:
293
- """
294
- Recommend a key size in bits if it is not mentioned by the user
295
- Args:
296
- algorithm_name (str): algorithm name
297
- Returns
298
- key_size (int)
299
- """
300
- if algorithm_name == Algorithm.RSA:
301
- key_size = 1024
302
- elif algorithm_name == Algorithm.ElGamal:
303
- key_size = 1024
304
- elif algorithm_name == Algorithm.ExponentialElGamal:
305
- key_size = 1024
306
- # 160-bit ECC is equivalent to 1024-bit RSA
307
- elif algorithm_name == Algorithm.EllipticCurveElGamal:
308
- key_size = 160
309
- elif algorithm_name == Algorithm.Paillier:
310
- key_size = 1024
311
- elif algorithm_name == Algorithm.DamgardJurik:
312
- key_size = 1024
313
- elif algorithm_name == Algorithm.OkamotoUchiyama:
314
- key_size = 1024
315
- # n should be several hundred bits or more
316
- elif algorithm_name == Algorithm.GoldwasserMicali:
317
- key_size = 100
318
- # Benaloh and Naccache-Stern require to solve DLP in decryption
319
- # so small key is recommended
320
- elif algorithm_name == Algorithm.Benaloh:
321
- key_size = 50
322
- elif algorithm_name == Algorithm.NaccacheStern:
323
- key_size = 37
324
- else:
325
- raise ValueError(f"unimplemented algorithm - {algorithm_name}")
326
- return key_size
327
-
328
298
  def create_ciphertext_obj(self, ciphertext: Union[int, tuple, list]) -> Ciphertext:
329
299
  """
330
300
  Ciphertext objects have keys in addition ciphertext itself to perform