lightphe 0.0.16__tar.gz → 0.0.17__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 (46) hide show
  1. {lightphe-0.0.16 → lightphe-0.0.17}/PKG-INFO +41 -214
  2. {lightphe-0.0.16 → lightphe-0.0.17}/README.md +40 -213
  3. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/__init__.py +1 -1
  4. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/cryptosystems/Benaloh.py +30 -18
  5. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/cryptosystems/GoldwasserMicali.py +4 -1
  6. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/cryptosystems/NaccacheStern.py +1 -1
  7. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe.egg-info/PKG-INFO +41 -214
  8. {lightphe-0.0.16 → lightphe-0.0.17}/setup.py +1 -1
  9. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_benaloh.py +10 -3
  10. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_keys.py +1 -1
  11. {lightphe-0.0.16 → lightphe-0.0.17}/LICENSE +0 -0
  12. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/commons/__init__.py +0 -0
  13. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/commons/logger.py +0 -0
  14. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/commons/phe_utils.py +0 -0
  15. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/cryptosystems/DamgardJurik.py +0 -0
  16. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/cryptosystems/ElGamal.py +0 -0
  17. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/cryptosystems/EllipticCurveElGamal.py +0 -0
  18. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/cryptosystems/OkamotoUchiyama.py +0 -0
  19. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/cryptosystems/Paillier.py +0 -0
  20. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/cryptosystems/RSA.py +0 -0
  21. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/cryptosystems/__init__.py +0 -0
  22. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/models/Algorithm.py +0 -0
  23. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/models/Ciphertext.py +0 -0
  24. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/models/Homomorphic.py +0 -0
  25. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/models/Tensor.py +0 -0
  26. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe/models/__init__.py +0 -0
  27. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe.egg-info/SOURCES.txt +0 -0
  28. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe.egg-info/dependency_links.txt +0 -0
  29. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe.egg-info/requires.txt +0 -0
  30. {lightphe-0.0.16 → lightphe-0.0.17}/lightphe.egg-info/top_level.txt +0 -0
  31. {lightphe-0.0.16 → lightphe-0.0.17}/requirements.txt +0 -0
  32. {lightphe-0.0.16 → lightphe-0.0.17}/setup.cfg +0 -0
  33. {lightphe-0.0.16 → lightphe-0.0.17}/tests/__init__.py +0 -0
  34. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_ciphertexts.py +0 -0
  35. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_cloud.py +0 -0
  36. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_damgard.py +0 -0
  37. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_elgamal.py +0 -0
  38. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_ellipticcurveelgamal.py +0 -0
  39. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_exporting_keys.py +0 -0
  40. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_goldwasser.py +0 -0
  41. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_naccache.py +0 -0
  42. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_okamoto.py +0 -0
  43. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_paillier.py +0 -0
  44. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_rsa.py +0 -0
  45. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_salary.py +0 -0
  46. {lightphe-0.0.16 → lightphe-0.0.17}/tests/test_tensors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightphe
3
- Version: 0.0.16
3
+ Version: 0.0.17
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
@@ -67,7 +67,7 @@ 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 | Multiplication with a Plain Constant | Exclusively<br>Homomorphic | Regeneration<br>of Ciphertext |
70
+ | Algorithm | Multiplicatively<br>Homomorphic | Additively<br>Homomorphic | Scalar Multiplication | Exclusively<br>Homomorphic | Regeneration<br>of Ciphertext |
71
71
  | --- | --- | --- | --- | --- | --- |
72
72
  | RSA | ✅ | ❌ | ❌ | ❌ | ❌ |
73
73
  | ElGamal | ✅ | ❌ | ❌ | ❌ | ✅ |
@@ -98,124 +98,56 @@ algorithms = [
98
98
  "EllipticCurve-ElGamal"
99
99
  ]
100
100
 
101
- phe = LightPHE(algorithm_name = algorithms[0])
102
- ```
103
-
104
- # Encryption & Decryption
105
-
106
- Once you built your cryptosystem, you will be able to encrypt and decrypt messages with the built cryptosystem.
107
-
108
- ```python
109
- # define plaintext
110
- m = 17
111
-
112
- # calculate ciphertext
113
- c = phe.encrypt(m)
114
-
115
- # proof of work
116
- assert phe.decrypt(c) == m
101
+ cs = LightPHE(algorithm_name = algorithms[0])
117
102
  ```
118
103
 
119
104
  # Homomorphic Operations
120
105
 
121
- Once you have the ciphertext, you will be able to perform homomorphic operations on encrypted data without holding private key. For instance, Paillier is homomorphic with respect to the addition. In other words, decryption of the addition of two ciphertexts is equivalent to addition of plaintexts.
122
-
123
- ### On-Prem Encryption
124
-
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.
106
+ 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
107
 
127
108
  ```python
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)
151
- ```
152
-
153
- ### Homomorphic Operations on Cloud
109
+ # build an additively homomorphic cryptosystem
110
+ cs = LightPHE(algorithm_name = "Paillier")
154
111
 
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.
112
+ # define plaintexts
113
+ m1 = 10000 # base salary in usd
114
+ m2 = 500 # wage increase in usd
156
115
 
157
- ```python
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
193
- ```
116
+ # encrypt plaintexts - private key is not required.
117
+ c1 = cs.encrypt(m1)
118
+ c2 = cs.encrypt(m2)
194
119
 
195
- ### On-Prem Decryption And Proof of Work
120
+ # homomorphic addition - private key is not required
121
+ c3 = c1 + c2
196
122
 
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.
123
+ # homomorphic scalar multiplication - private key is not required
124
+ k = 1.05 # increase something 5%
125
+ c4 = k * c1
198
126
 
199
- ```python
200
- # proof of work - private key required
201
- assert phe.decrypt(c3) == m1 + m2
127
+ # decryption - private key is required
128
+ assert cs.decrypt(c3) == m1 + m2
129
+ assert cs.decrypt(c4) == k * m1
202
130
  ```
203
131
 
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.
132
+ On the other hand, if you adopt a multiplicatively homomorphic cryptosyste (e.g. RSA or ElGamal), you can multiply ciphertexts without revealing the private key or the plaintext.
205
133
 
206
- ### Scalar Multiplication
134
+ ```python
135
+ # build a multiplicatively homomorphic cryptosystem
136
+ cs = LightPHE(algorithm_name = "RSA")
207
137
 
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.
138
+ # define plaintexts
139
+ m1 = 17
140
+ m2 = 21
209
141
 
210
- ```python
211
- # increasing something 5%
212
- k = 1.05
142
+ # encrypt plaintexts - private key is not required.
143
+ c1 = cs.encrypt(m1)
144
+ c2 = cs.encrypt(m2)
213
145
 
214
- # scalar multiplication - cloud (private key is not required)
215
- c4 = k * c1
146
+ # homomorphic multiplication
147
+ c3 = c1 * c2
216
148
 
217
- # proof of work on-prem - private key is required
218
- assert phe.decrypt(c4) == k * m1
149
+ # decryption - private key is required
150
+ assert cs.decrypt(c3) == m1 * m2
219
151
  ```
220
152
 
221
153
  ### Ciphertext Regeneration
@@ -223,28 +155,12 @@ assert phe.decrypt(c4) == k * m1
223
155
  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.
224
156
 
225
157
  ```python
226
- c1_prime = phe.regenerate_ciphertext(c1)
158
+ c1_prime = cs.regenerate_ciphertext(c1)
227
159
  assert c1_prime.value != c1.value
228
- assert phe.decrypt(c1_prime) == m1
229
- assert phe.decrypt(c1) == m1
160
+ assert cs.decrypt(c1_prime) == m1
161
+ assert cs.decrypt(c1) == m1
230
162
  ```
231
163
 
232
- ### Unsupported Operations
233
-
234
- Finally, if you try to perform an operation that algorithm does not support, then an exception will be thrown. For instance, Paillier is not homomorphic with respect to the multiplication or xor. To put it simply, you cannot multiply two ciphertexts. If you enforce this calculation, you will have an exception.
235
-
236
- ```python
237
- # pailier is not multiplicatively homomorphic
238
- with pytest.raises(ValueError, match="Paillier is not homomorphic with respect to the multiplication"):
239
- c1 * c2
240
-
241
- # pailier is not exclusively homomorphic
242
- with pytest.raises(ValueError, match="Paillier is not homomorphic with respect to the exclusive or"):
243
- c1 ^ c2
244
- ```
245
-
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.
247
-
248
164
  ### Elliptic Curve Cryptography
249
165
 
250
166
  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, edwards and koblitz) and 100+ standard elliptic curve configurations.
@@ -252,9 +168,10 @@ ECC is a powerful public-key cryptosystem based on the algebraic structure of el
252
168
  In LightPHE, the [Elliptic Curve ElGamal](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) scheme is implemented, offering a secure and efficient homomorphic encryption option.
253
169
 
254
170
  ```python
171
+ forms = ["weierstrass", "edwards", "koblitz"]
255
172
  phe = LightPHE(
256
173
  algorithm_name="EllipticCurve-ElGamal",
257
- form="edwards", # or weierstrass, koblitz
174
+ form=forms[1],
258
175
  )
259
176
  ```
260
177
 
@@ -264,48 +181,9 @@ Each curve in LightPHE has a specific order, which is carefully chosen to balanc
264
181
 
265
182
  See [`curves`](https://github.com/serengil/LightECC#supported-curves) page for a list of all supported forms, curves and their details.
266
183
 
267
- ### Vector Embeddings and Tensors
184
+ ### Vector Embeddings
268
185
 
269
- LightPHE supports homomorphic encryption on vector embeddings and tensors. This is useful in privacy-preserving machine learning, secure aggregation, and confidential data processing.
270
-
271
- ```python
272
- # build a cryptosystem
273
- cs = LightPHE(algorithm_name="Paillier")
274
-
275
- # define plain embedding
276
- tensor = [1.005, 2.05, 3.005, 4.005, -5.05, 6, 7.003, 7.002]
277
-
278
- # encrypt vector embedding
279
- encrypted_tensors = cs.encrypt(tensor)
280
-
281
- # restore embedding
282
- decrypted_tensors = cs.decrypt(encrypted_tensors)
283
-
284
- # proof of work
285
- assert all(abs(original - decrypted) < 1e-2 for original, decrypted in zip(tensor, decrypted_tensors))
286
- ```
287
-
288
- Encrypted embeddings retain their homomorphic properties, enabling secure computations on encrypted data without decryption. For example, two embeddings encrypted using a multiplicatively homomorphic algorithm can be multiplied element-wise.
289
-
290
- ```python
291
- # build a multiplicatively homomorphic cryptosystem (e.g. RSA)
292
- cs = LightPHE("RSA")
293
-
294
- # define plain embeddings
295
- t1 = [1.005, 2.05, -3.5, 3.1, -4]
296
- t2 = [5, 6.2, -7.002, -7.1, 8.02]
297
-
298
- # encrypt embeddings
299
- c1, c2 = cs.encrypt(t1), cs.encrypt(t2)
300
-
301
- # perform element-wise homomorphic multiplication
302
- c3 = c1 * c2
303
-
304
- # proof of work
305
- assert np.allclose(cs.decrypt(c3), [a * b for a, b in zip(t1, t2)], rtol=1e-2)
306
- ```
307
-
308
- Similarly, two embeddings encrypted with an additively homomorphic algorithm can be added. Additionally, an encrypted embedding can be multiplied by a constant or a plain embedding.
186
+ LightPHE supports homomorphic encryption on vector embeddings. This is useful in privacy-preserving machine learning, secure aggregation, and confidential data processing.
309
187
 
310
188
  ```python
311
189
  # build an additively homomorphic cryptosystem (e.g. Paillier)
@@ -334,57 +212,6 @@ assert np.allclose(cs.decrypt(c5), [a * 3 for a in t1], rtol=1e-2)
334
212
  assert np.allclose(cs.decrypt(c6), [a * b for a, b in zip(t1, t3)], rtol=1e-2)
335
213
  ```
336
214
 
337
- ### Vector Similarity Search with PHE
338
-
339
- Many machine learning models rely on a two-tower architecture, including facial recognition, reverse image search, recommendation engines, large language models, and more. In this setup, user and item inputs are separately mapped to vector embeddings.
340
-
341
- For example, suppose all facial embeddings in your database are encrypted on-prem in advance. When verifying an identity, the attempted facial embedding is generated on an edge device or in the cloud. You can compute the encrypted similarity by performing a dot product between the encrypted vector and the plain vector, ensuring secure comparison without decrypting sensitive data. Only additively homomorphic cryptosystems offer encrypted similarity calculation.
342
-
343
- ```python
344
- # define a plain vectors for source and target
345
- alpha = [7.1, 5.2, 5.3, 2.4, 3.5, 4.6] # On-prem vector (user tower)
346
- beta = [5.6, 3.7, 2.8, 4, 0, 5.9] # Cloud vector (item tower)
347
- expected_similarity = sum(x * y for x, y in zip(alpha, beta))
348
-
349
- # build an additively homomorphic cryptosystem (e.g. Paillier) on-prem
350
- cs = LightPHE(algorithm_name = "Paillier", precision = 19)
351
-
352
- # export keys
353
- cs.export_keys("secret.txt")
354
- cs.export_keys("public.txt", public=True)
355
-
356
- # encrypt source embedding
357
- encrypted_alpha = cs.encrypt(alpha)
358
-
359
- # remove cryptosystem and plain alpha not to be leaked in cloud
360
- del cs, alpha
361
-
362
- # restore the cryptosystem in cloud with only public key
363
- cloud_cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "public.txt")
364
-
365
- # dot product of encrypted and plain embedding pair
366
- encrypted_cosine_similarity = encrypted_alpha @ beta
367
-
368
- # computed by the cloud but cloud cannot decrypt it
369
- with pytest.raises(ValueError, match="must have private key"):
370
- cloud_cs.decrypt(encrypted_cosine_similarity)
371
-
372
- # restore the cryptosystem on-prem with secret key
373
- cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "secret.txt")
374
-
375
- # decrypt similarity (on prem)
376
- calculated_similarity = cs.decrypt(encrypted_cosine_similarity)[0]
377
-
378
- # proof of work
379
- assert abs(calculated_similarity - expected_similarity) < 1e-2
380
- ```
381
-
382
- ## Security Concerns and Considerations
383
-
384
- While LightPHE enables encryption using public keys and supports homomorphic operations on encrypted data, it's important to understand a subtle but critical security implication of this approach. Since the it is homomorphic, the cloud (or any third-party processor) can perform computations without knowing the plaintext. However, a malicious actor can still encrypt arbitrary values and craft valid ciphertexts because encryption depends on your public key and it is publicly known. For example, even if a salary is encrypted, an attacker could encrypt 500 USD using your public key and update the encrypted salary with this forged ciphertext — without needing to know the original value.
385
-
386
- To mitigate this, we recommend combining LightPHE with digital signature schemes. By keeping a signed audit trail of update operations in a separate log table, you can detect and reject unauthorized changes, and even restore the original data in case of tampering. We suggest checking out [`LightDSA`](https://github.com/serengil/LightDSA), which provides a lightweight cryptographic interface similar to LightPHE and supports popular digital signature algorithms like RSA, DSA, ECDSA, and EdDSA.
387
-
388
215
  # Contributing
389
216
 
390
217
  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.
@@ -51,7 +51,7 @@ 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 | Multiplication with a Plain Constant | Exclusively<br>Homomorphic | Regeneration<br>of Ciphertext |
54
+ | Algorithm | Multiplicatively<br>Homomorphic | Additively<br>Homomorphic | Scalar Multiplication | Exclusively<br>Homomorphic | Regeneration<br>of Ciphertext |
55
55
  | --- | --- | --- | --- | --- | --- |
56
56
  | RSA | ✅ | ❌ | ❌ | ❌ | ❌ |
57
57
  | ElGamal | ✅ | ❌ | ❌ | ❌ | ✅ |
@@ -82,124 +82,56 @@ algorithms = [
82
82
  "EllipticCurve-ElGamal"
83
83
  ]
84
84
 
85
- phe = LightPHE(algorithm_name = algorithms[0])
86
- ```
87
-
88
- # Encryption & Decryption
89
-
90
- Once you built your cryptosystem, you will be able to encrypt and decrypt messages with the built cryptosystem.
91
-
92
- ```python
93
- # define plaintext
94
- m = 17
95
-
96
- # calculate ciphertext
97
- c = phe.encrypt(m)
98
-
99
- # proof of work
100
- assert phe.decrypt(c) == m
85
+ cs = LightPHE(algorithm_name = algorithms[0])
101
86
  ```
102
87
 
103
88
  # Homomorphic Operations
104
89
 
105
- Once you have the ciphertext, you will be able to perform homomorphic operations on encrypted data without holding private key. For instance, Paillier is homomorphic with respect to the addition. In other words, decryption of the addition of two ciphertexts is equivalent to addition of plaintexts.
106
-
107
- ### On-Prem Encryption
108
-
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.
90
+ 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.
110
91
 
111
92
  ```python
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)
135
- ```
136
-
137
- ### Homomorphic Operations on Cloud
93
+ # build an additively homomorphic cryptosystem
94
+ cs = LightPHE(algorithm_name = "Paillier")
138
95
 
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.
96
+ # define plaintexts
97
+ m1 = 10000 # base salary in usd
98
+ m2 = 500 # wage increase in usd
140
99
 
141
- ```python
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
177
- ```
100
+ # encrypt plaintexts - private key is not required.
101
+ c1 = cs.encrypt(m1)
102
+ c2 = cs.encrypt(m2)
178
103
 
179
- ### On-Prem Decryption And Proof of Work
104
+ # homomorphic addition - private key is not required
105
+ c3 = c1 + c2
180
106
 
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.
107
+ # homomorphic scalar multiplication - private key is not required
108
+ k = 1.05 # increase something 5%
109
+ c4 = k * c1
182
110
 
183
- ```python
184
- # proof of work - private key required
185
- assert phe.decrypt(c3) == m1 + m2
111
+ # decryption - private key is required
112
+ assert cs.decrypt(c3) == m1 + m2
113
+ assert cs.decrypt(c4) == k * m1
186
114
  ```
187
115
 
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.
116
+ On the other hand, if you adopt a multiplicatively homomorphic cryptosyste (e.g. RSA or ElGamal), you can multiply ciphertexts without revealing the private key or the plaintext.
189
117
 
190
- ### Scalar Multiplication
118
+ ```python
119
+ # build a multiplicatively homomorphic cryptosystem
120
+ cs = LightPHE(algorithm_name = "RSA")
191
121
 
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.
122
+ # define plaintexts
123
+ m1 = 17
124
+ m2 = 21
193
125
 
194
- ```python
195
- # increasing something 5%
196
- k = 1.05
126
+ # encrypt plaintexts - private key is not required.
127
+ c1 = cs.encrypt(m1)
128
+ c2 = cs.encrypt(m2)
197
129
 
198
- # scalar multiplication - cloud (private key is not required)
199
- c4 = k * c1
130
+ # homomorphic multiplication
131
+ c3 = c1 * c2
200
132
 
201
- # proof of work on-prem - private key is required
202
- assert phe.decrypt(c4) == k * m1
133
+ # decryption - private key is required
134
+ assert cs.decrypt(c3) == m1 * m2
203
135
  ```
204
136
 
205
137
  ### Ciphertext Regeneration
@@ -207,28 +139,12 @@ assert phe.decrypt(c4) == k * m1
207
139
  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.
208
140
 
209
141
  ```python
210
- c1_prime = phe.regenerate_ciphertext(c1)
142
+ c1_prime = cs.regenerate_ciphertext(c1)
211
143
  assert c1_prime.value != c1.value
212
- assert phe.decrypt(c1_prime) == m1
213
- assert phe.decrypt(c1) == m1
144
+ assert cs.decrypt(c1_prime) == m1
145
+ assert cs.decrypt(c1) == m1
214
146
  ```
215
147
 
216
- ### Unsupported Operations
217
-
218
- Finally, if you try to perform an operation that algorithm does not support, then an exception will be thrown. For instance, Paillier is not homomorphic with respect to the multiplication or xor. To put it simply, you cannot multiply two ciphertexts. If you enforce this calculation, you will have an exception.
219
-
220
- ```python
221
- # pailier is not multiplicatively homomorphic
222
- with pytest.raises(ValueError, match="Paillier is not homomorphic with respect to the multiplication"):
223
- c1 * c2
224
-
225
- # pailier is not exclusively homomorphic
226
- with pytest.raises(ValueError, match="Paillier is not homomorphic with respect to the exclusive or"):
227
- c1 ^ c2
228
- ```
229
-
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.
231
-
232
148
  ### Elliptic Curve Cryptography
233
149
 
234
150
  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, edwards and koblitz) and 100+ standard elliptic curve configurations.
@@ -236,9 +152,10 @@ ECC is a powerful public-key cryptosystem based on the algebraic structure of el
236
152
  In LightPHE, the [Elliptic Curve ElGamal](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) scheme is implemented, offering a secure and efficient homomorphic encryption option.
237
153
 
238
154
  ```python
155
+ forms = ["weierstrass", "edwards", "koblitz"]
239
156
  phe = LightPHE(
240
157
  algorithm_name="EllipticCurve-ElGamal",
241
- form="edwards", # or weierstrass, koblitz
158
+ form=forms[1],
242
159
  )
243
160
  ```
244
161
 
@@ -248,48 +165,9 @@ Each curve in LightPHE has a specific order, which is carefully chosen to balanc
248
165
 
249
166
  See [`curves`](https://github.com/serengil/LightECC#supported-curves) page for a list of all supported forms, curves and their details.
250
167
 
251
- ### Vector Embeddings and Tensors
168
+ ### Vector Embeddings
252
169
 
253
- LightPHE supports homomorphic encryption on vector embeddings and tensors. This is useful in privacy-preserving machine learning, secure aggregation, and confidential data processing.
254
-
255
- ```python
256
- # build a cryptosystem
257
- cs = LightPHE(algorithm_name="Paillier")
258
-
259
- # define plain embedding
260
- tensor = [1.005, 2.05, 3.005, 4.005, -5.05, 6, 7.003, 7.002]
261
-
262
- # encrypt vector embedding
263
- encrypted_tensors = cs.encrypt(tensor)
264
-
265
- # restore embedding
266
- decrypted_tensors = cs.decrypt(encrypted_tensors)
267
-
268
- # proof of work
269
- assert all(abs(original - decrypted) < 1e-2 for original, decrypted in zip(tensor, decrypted_tensors))
270
- ```
271
-
272
- Encrypted embeddings retain their homomorphic properties, enabling secure computations on encrypted data without decryption. For example, two embeddings encrypted using a multiplicatively homomorphic algorithm can be multiplied element-wise.
273
-
274
- ```python
275
- # build a multiplicatively homomorphic cryptosystem (e.g. RSA)
276
- cs = LightPHE("RSA")
277
-
278
- # define plain embeddings
279
- t1 = [1.005, 2.05, -3.5, 3.1, -4]
280
- t2 = [5, 6.2, -7.002, -7.1, 8.02]
281
-
282
- # encrypt embeddings
283
- c1, c2 = cs.encrypt(t1), cs.encrypt(t2)
284
-
285
- # perform element-wise homomorphic multiplication
286
- c3 = c1 * c2
287
-
288
- # proof of work
289
- assert np.allclose(cs.decrypt(c3), [a * b for a, b in zip(t1, t2)], rtol=1e-2)
290
- ```
291
-
292
- Similarly, two embeddings encrypted with an additively homomorphic algorithm can be added. Additionally, an encrypted embedding can be multiplied by a constant or a plain embedding.
170
+ LightPHE supports homomorphic encryption on vector embeddings. This is useful in privacy-preserving machine learning, secure aggregation, and confidential data processing.
293
171
 
294
172
  ```python
295
173
  # build an additively homomorphic cryptosystem (e.g. Paillier)
@@ -318,57 +196,6 @@ assert np.allclose(cs.decrypt(c5), [a * 3 for a in t1], rtol=1e-2)
318
196
  assert np.allclose(cs.decrypt(c6), [a * b for a, b in zip(t1, t3)], rtol=1e-2)
319
197
  ```
320
198
 
321
- ### Vector Similarity Search with PHE
322
-
323
- Many machine learning models rely on a two-tower architecture, including facial recognition, reverse image search, recommendation engines, large language models, and more. In this setup, user and item inputs are separately mapped to vector embeddings.
324
-
325
- For example, suppose all facial embeddings in your database are encrypted on-prem in advance. When verifying an identity, the attempted facial embedding is generated on an edge device or in the cloud. You can compute the encrypted similarity by performing a dot product between the encrypted vector and the plain vector, ensuring secure comparison without decrypting sensitive data. Only additively homomorphic cryptosystems offer encrypted similarity calculation.
326
-
327
- ```python
328
- # define a plain vectors for source and target
329
- alpha = [7.1, 5.2, 5.3, 2.4, 3.5, 4.6] # On-prem vector (user tower)
330
- beta = [5.6, 3.7, 2.8, 4, 0, 5.9] # Cloud vector (item tower)
331
- expected_similarity = sum(x * y for x, y in zip(alpha, beta))
332
-
333
- # build an additively homomorphic cryptosystem (e.g. Paillier) on-prem
334
- cs = LightPHE(algorithm_name = "Paillier", precision = 19)
335
-
336
- # export keys
337
- cs.export_keys("secret.txt")
338
- cs.export_keys("public.txt", public=True)
339
-
340
- # encrypt source embedding
341
- encrypted_alpha = cs.encrypt(alpha)
342
-
343
- # remove cryptosystem and plain alpha not to be leaked in cloud
344
- del cs, alpha
345
-
346
- # restore the cryptosystem in cloud with only public key
347
- cloud_cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "public.txt")
348
-
349
- # dot product of encrypted and plain embedding pair
350
- encrypted_cosine_similarity = encrypted_alpha @ beta
351
-
352
- # computed by the cloud but cloud cannot decrypt it
353
- with pytest.raises(ValueError, match="must have private key"):
354
- cloud_cs.decrypt(encrypted_cosine_similarity)
355
-
356
- # restore the cryptosystem on-prem with secret key
357
- cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "secret.txt")
358
-
359
- # decrypt similarity (on prem)
360
- calculated_similarity = cs.decrypt(encrypted_cosine_similarity)[0]
361
-
362
- # proof of work
363
- assert abs(calculated_similarity - expected_similarity) < 1e-2
364
- ```
365
-
366
- ## Security Concerns and Considerations
367
-
368
- While LightPHE enables encryption using public keys and supports homomorphic operations on encrypted data, it's important to understand a subtle but critical security implication of this approach. Since the it is homomorphic, the cloud (or any third-party processor) can perform computations without knowing the plaintext. However, a malicious actor can still encrypt arbitrary values and craft valid ciphertexts because encryption depends on your public key and it is publicly known. For example, even if a salary is encrypted, an attacker could encrypt 500 USD using your public key and update the encrypted salary with this forged ciphertext — without needing to know the original value.
369
-
370
- To mitigate this, we recommend combining LightPHE with digital signature schemes. By keeping a signed audit trail of update operations in a separate log table, you can detect and reject unauthorized changes, and even restore the original data in case of tampering. We suggest checking out [`LightDSA`](https://github.com/serengil/LightDSA), which provides a lightweight cryptographic interface similar to LightPHE and supports popular digital signature algorithms like RSA, DSA, ECDSA, and EdDSA.
371
-
372
199
  # Contributing
373
200
 
374
201
  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.
@@ -38,7 +38,7 @@ from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal
38
38
 
39
39
  logger = Logger(module="lightphe/__init__.py")
40
40
 
41
- VERSION = "0.0.16"
41
+ VERSION = "0.0.17"
42
42
 
43
43
 
44
44
  class LightPHE: