lightphe 0.0.8__tar.gz → 0.0.10__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.
- {lightphe-0.0.8 → lightphe-0.0.10}/PKG-INFO +92 -97
- {lightphe-0.0.8 → lightphe-0.0.10}/README.md +91 -96
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/__init__.py +55 -49
- lightphe-0.0.10/lightphe/commons/binary_operations.py +147 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/commons/phe_utils.py +22 -4
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/cryptosystems/Benaloh.py +3 -2
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/cryptosystems/DamgardJurik.py +2 -2
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/cryptosystems/ElGamal.py +2 -2
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/cryptosystems/EllipticCurveElGamal.py +61 -30
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/cryptosystems/GoldwasserMicali.py +3 -2
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/cryptosystems/NaccacheStern.py +3 -2
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/cryptosystems/OkamotoUchiyama.py +2 -2
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/cryptosystems/Paillier.py +2 -2
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/cryptosystems/RSA.py +2 -2
- lightphe-0.0.10/lightphe/elliptic_curve_forms/edwards.py +89 -0
- lightphe-0.0.10/lightphe/elliptic_curve_forms/koblitz.py +163 -0
- lightphe-0.0.10/lightphe/elliptic_curve_forms/weierstrass.py +117 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/models/Ciphertext.py +28 -9
- lightphe-0.0.10/lightphe/models/Curve.py +29 -0
- lightphe-0.0.10/lightphe/models/EllipticCurve.py +78 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/models/Tensor.py +22 -5
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe.egg-info/PKG-INFO +92 -97
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe.egg-info/SOURCES.txt +6 -2
- {lightphe-0.0.8 → lightphe-0.0.10}/setup.py +1 -1
- lightphe-0.0.10/tests/__init__.py +0 -0
- lightphe-0.0.10/tests/test_ellipticcurveelgamal.py +212 -0
- lightphe-0.0.8/lightphe/elliptic/Weierstrass.py +0 -98
- lightphe-0.0.8/lightphe/models/EllipticCurve.py +0 -18
- lightphe-0.0.8/tests/__init__.py +0 -219
- lightphe-0.0.8/tests/test_ellipticcurveelgamal.py +0 -64
- {lightphe-0.0.8 → lightphe-0.0.10}/LICENSE +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/commons/__init__.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/commons/logger.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/cryptosystems/__init__.py +0 -0
- {lightphe-0.0.8/lightphe/elliptic → lightphe-0.0.10/lightphe/elliptic_curve_forms}/__init__.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/models/Algorithm.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/models/Homomorphic.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe/models/__init__.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe.egg-info/dependency_links.txt +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe.egg-info/requires.txt +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/lightphe.egg-info/top_level.txt +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/requirements.txt +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/setup.cfg +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_benaloh.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_cloud.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_damgard.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_elgamal.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_exporting_keys.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_goldwasser.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_naccache.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_okamoto.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_paillier.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_rsa.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_salary.py +0 -0
- {lightphe-0.0.8 → lightphe-0.0.10}/tests/test_tensors.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lightphe
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.10
|
|
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/), [`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/).
|
|
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
|
-
|
|
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 =
|
|
113
|
+
c = phe.encrypt(m)
|
|
114
114
|
|
|
115
115
|
# proof of work
|
|
116
|
-
assert
|
|
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
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
c1
|
|
160
|
-
c2
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
226
|
+
c1_prime = phe.regenerate_ciphertext(c1)
|
|
213
227
|
assert c1_prime.value != c1.value
|
|
214
|
-
assert
|
|
215
|
-
assert
|
|
228
|
+
assert phe.decrypt(c1_prime) == m1
|
|
229
|
+
assert phe.decrypt(c1) == m1
|
|
216
230
|
```
|
|
217
231
|
|
|
218
232
|
### Unsupported Operations
|
|
@@ -231,50 +245,31 @@ 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
|
-
|
|
248
|
+
### Elliptic Curve Cryptography
|
|
235
249
|
|
|
236
|
-
|
|
250
|
+
ECC is a powerful public-key cryptosystem based on the algebraic structure of elliptic curves over finite fields. 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. The library supports 100+ standard elliptic curves.
|
|
237
251
|
|
|
238
252
|
```python
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
|
253
|
+
some_curves = [
|
|
254
|
+
{"form": "weierstrass", "curve": "secp256k1"},
|
|
255
|
+
{"form": "edwards", "curve": "ed25519"},
|
|
256
|
+
{"form": "koblitz", "curve": "k163"}
|
|
257
|
+
]
|
|
259
258
|
|
|
260
|
-
|
|
261
|
-
t3 = cs.decrypt(c3)
|
|
259
|
+
curve = some_curves[1]
|
|
262
260
|
|
|
263
|
-
|
|
264
|
-
|
|
261
|
+
phe = LightPHE(
|
|
262
|
+
algorithm_name="EllipticCurve-ElGamal",
|
|
263
|
+
form=curve["form"],
|
|
264
|
+
curve=curve["curve"],
|
|
265
|
+
)
|
|
266
|
+
```
|
|
265
267
|
|
|
266
|
-
|
|
267
|
-
t5 = cs.decrypt(c5)
|
|
268
|
+
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.
|
|
268
269
|
|
|
269
|
-
|
|
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
|
-
```
|
|
270
|
+
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.
|
|
276
271
|
|
|
277
|
-
|
|
272
|
+
See [`Curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for more details.
|
|
278
273
|
|
|
279
274
|
# Contributing
|
|
280
275
|
|
|
@@ -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/), [`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/).
|
|
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
|
-
|
|
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 =
|
|
97
|
+
c = phe.encrypt(m)
|
|
98
98
|
|
|
99
99
|
# proof of work
|
|
100
|
-
assert
|
|
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
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
c1
|
|
144
|
-
c2
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
210
|
+
c1_prime = phe.regenerate_ciphertext(c1)
|
|
197
211
|
assert c1_prime.value != c1.value
|
|
198
|
-
assert
|
|
199
|
-
assert
|
|
212
|
+
assert phe.decrypt(c1_prime) == m1
|
|
213
|
+
assert phe.decrypt(c1) == m1
|
|
200
214
|
```
|
|
201
215
|
|
|
202
216
|
### Unsupported Operations
|
|
@@ -215,50 +229,31 @@ 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
|
-
|
|
232
|
+
### Elliptic Curve Cryptography
|
|
219
233
|
|
|
220
|
-
|
|
234
|
+
ECC is a powerful public-key cryptosystem based on the algebraic structure of elliptic curves over finite fields. 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. The library supports 100+ standard elliptic curves.
|
|
221
235
|
|
|
222
236
|
```python
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
|
237
|
+
some_curves = [
|
|
238
|
+
{"form": "weierstrass", "curve": "secp256k1"},
|
|
239
|
+
{"form": "edwards", "curve": "ed25519"},
|
|
240
|
+
{"form": "koblitz", "curve": "k163"}
|
|
241
|
+
]
|
|
243
242
|
|
|
244
|
-
|
|
245
|
-
t3 = cs.decrypt(c3)
|
|
243
|
+
curve = some_curves[1]
|
|
246
244
|
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
phe = LightPHE(
|
|
246
|
+
algorithm_name="EllipticCurve-ElGamal",
|
|
247
|
+
form=curve["form"],
|
|
248
|
+
curve=curve["curve"],
|
|
249
|
+
)
|
|
250
|
+
```
|
|
249
251
|
|
|
250
|
-
|
|
251
|
-
t5 = cs.decrypt(c5)
|
|
252
|
+
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.
|
|
252
253
|
|
|
253
|
-
|
|
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
|
-
```
|
|
254
|
+
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.
|
|
260
255
|
|
|
261
|
-
|
|
256
|
+
See [`Curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for more details.
|
|
262
257
|
|
|
263
258
|
# Contributing
|
|
264
259
|
|