lightphe 0.0.10__tar.gz → 0.0.12__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.10 → lightphe-0.0.12}/PKG-INFO +31 -13
- {lightphe-0.0.10 → lightphe-0.0.12}/README.md +30 -12
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/__init__.py +174 -43
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/cryptosystems/EllipticCurveElGamal.py +58 -46
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/elliptic_curve_forms/koblitz.py +3 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/elliptic_curve_forms/weierstrass.py +3 -0
- lightphe-0.0.12/lightphe/models/EllipticCurve.py +160 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/models/Tensor.py +107 -7
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe.egg-info/PKG-INFO +31 -13
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe.egg-info/SOURCES.txt +1 -0
- lightphe-0.0.12/lightphe.egg-info/requires.txt +3 -0
- lightphe-0.0.10/lightphe.egg-info/requires.txt → lightphe-0.0.12/requirements.txt +1 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/setup.py +1 -1
- lightphe-0.0.12/tests/test_ellipticcurve.py +75 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_ellipticcurveelgamal.py +86 -35
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_tensors.py +95 -8
- lightphe-0.0.10/lightphe/models/EllipticCurve.py +0 -78
- lightphe-0.0.10/requirements.txt +0 -2
- {lightphe-0.0.10 → lightphe-0.0.12}/LICENSE +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/commons/__init__.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/commons/binary_operations.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/commons/logger.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/commons/phe_utils.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/cryptosystems/Benaloh.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/cryptosystems/DamgardJurik.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/cryptosystems/ElGamal.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/cryptosystems/GoldwasserMicali.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/cryptosystems/NaccacheStern.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/cryptosystems/OkamotoUchiyama.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/cryptosystems/Paillier.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/cryptosystems/RSA.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/cryptosystems/__init__.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/elliptic_curve_forms/__init__.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/elliptic_curve_forms/edwards.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/models/Algorithm.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/models/Ciphertext.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/models/Curve.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/models/Homomorphic.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe/models/__init__.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe.egg-info/dependency_links.txt +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/lightphe.egg-info/top_level.txt +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/setup.cfg +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/__init__.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_benaloh.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_cloud.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_damgard.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_elgamal.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_exporting_keys.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_goldwasser.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_naccache.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_okamoto.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_paillier.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_rsa.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.12}/tests/test_salary.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.12
|
|
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
|
|
@@ -22,7 +22,9 @@ License-File: LICENSE
|
|
|
22
22
|
[](https://github.com/serengil/LightPHE/stargazers)
|
|
23
23
|
[](https://github.com/serengil/LightPHE/actions/workflows/tests.yml)
|
|
24
24
|
[](https://github.com/serengil/LightPHE/blob/master/LICENSE)
|
|
25
|
-
[](https://arxiv.org/abs/2408.05219)
|
|
26
|
+
[](https://news.ycombinator.com/item?id=43024283)
|
|
27
|
+
<!-- [](https://arxiv.org/abs/2408.05219) -->
|
|
26
28
|
|
|
27
29
|
[](https://sefiks.com)
|
|
28
30
|
[](https://www.youtube.com/@sefiks?sub_confirmation=1)
|
|
@@ -245,23 +247,39 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
|
|
|
245
247
|
|
|
246
248
|
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
249
|
|
|
248
|
-
###
|
|
250
|
+
### Elliptic Curve Cryptography
|
|
249
251
|
|
|
250
|
-
ECC is a powerful public-key cryptosystem based on the algebraic structure of elliptic curves over finite fields.
|
|
252
|
+
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.
|
|
253
|
+
|
|
254
|
+
Building an elliptic curve with LightPHE is very straightforward. Once the curve is initialized, you can perform operations such as point addition and scalar multiplication.
|
|
251
255
|
|
|
252
256
|
```python
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
257
|
+
from lightphe import ECC
|
|
258
|
+
|
|
259
|
+
forms = ["weierstrass", "edwards", "koblitz"]
|
|
260
|
+
|
|
261
|
+
# construct an elliptic curve
|
|
262
|
+
ed = ECC(form_name="edwards")
|
|
263
|
+
|
|
264
|
+
# Base Point
|
|
265
|
+
G = ed.G
|
|
258
266
|
|
|
259
|
-
|
|
267
|
+
_2G = G + G
|
|
268
|
+
_3G = _2G + G
|
|
269
|
+
_5G = _3G + _2G
|
|
270
|
+
|
|
271
|
+
_20G = 4 * _5G
|
|
272
|
+
_19G = _20G - G
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
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.
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
forms = ["weierstass", "edwards", "koblitz"]
|
|
260
279
|
|
|
261
280
|
phe = LightPHE(
|
|
262
281
|
algorithm_name="EllipticCurve-ElGamal",
|
|
263
|
-
form=
|
|
264
|
-
curve=curve["curve"],
|
|
282
|
+
form=forms[1],
|
|
265
283
|
)
|
|
266
284
|
```
|
|
267
285
|
|
|
@@ -269,7 +287,7 @@ One of the crucial factors that define the security level of an elliptic curve c
|
|
|
269
287
|
|
|
270
288
|
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.
|
|
271
289
|
|
|
272
|
-
See [`
|
|
290
|
+
See [`curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for a list of all supported forms, curves and their details.
|
|
273
291
|
|
|
274
292
|
# Contributing
|
|
275
293
|
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
[](https://github.com/serengil/LightPHE/stargazers)
|
|
7
7
|
[](https://github.com/serengil/LightPHE/actions/workflows/tests.yml)
|
|
8
8
|
[](https://github.com/serengil/LightPHE/blob/master/LICENSE)
|
|
9
|
-
[](https://arxiv.org/abs/2408.05219)
|
|
10
|
+
[](https://news.ycombinator.com/item?id=43024283)
|
|
11
|
+
<!-- [](https://arxiv.org/abs/2408.05219) -->
|
|
10
12
|
|
|
11
13
|
[](https://sefiks.com)
|
|
12
14
|
[](https://www.youtube.com/@sefiks?sub_confirmation=1)
|
|
@@ -229,23 +231,39 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
|
|
|
229
231
|
|
|
230
232
|
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
233
|
|
|
232
|
-
###
|
|
234
|
+
### Elliptic Curve Cryptography
|
|
233
235
|
|
|
234
|
-
ECC is a powerful public-key cryptosystem based on the algebraic structure of elliptic curves over finite fields.
|
|
236
|
+
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.
|
|
237
|
+
|
|
238
|
+
Building an elliptic curve with LightPHE is very straightforward. Once the curve is initialized, you can perform operations such as point addition and scalar multiplication.
|
|
235
239
|
|
|
236
240
|
```python
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
241
|
+
from lightphe import ECC
|
|
242
|
+
|
|
243
|
+
forms = ["weierstrass", "edwards", "koblitz"]
|
|
244
|
+
|
|
245
|
+
# construct an elliptic curve
|
|
246
|
+
ed = ECC(form_name="edwards")
|
|
247
|
+
|
|
248
|
+
# Base Point
|
|
249
|
+
G = ed.G
|
|
242
250
|
|
|
243
|
-
|
|
251
|
+
_2G = G + G
|
|
252
|
+
_3G = _2G + G
|
|
253
|
+
_5G = _3G + _2G
|
|
254
|
+
|
|
255
|
+
_20G = 4 * _5G
|
|
256
|
+
_19G = _20G - G
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
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.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
forms = ["weierstass", "edwards", "koblitz"]
|
|
244
263
|
|
|
245
264
|
phe = LightPHE(
|
|
246
265
|
algorithm_name="EllipticCurve-ElGamal",
|
|
247
|
-
form=
|
|
248
|
-
curve=curve["curve"],
|
|
266
|
+
form=forms[1],
|
|
249
267
|
)
|
|
250
268
|
```
|
|
251
269
|
|
|
@@ -253,7 +271,7 @@ One of the crucial factors that define the security level of an elliptic curve c
|
|
|
253
271
|
|
|
254
272
|
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.
|
|
255
273
|
|
|
256
|
-
See [`
|
|
274
|
+
See [`curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for a list of all supported forms, curves and their details.
|
|
257
275
|
|
|
258
276
|
# Contributing
|
|
259
277
|
|
|
@@ -1,10 +1,23 @@
|
|
|
1
|
+
# built-in dependencies
|
|
2
|
+
import time
|
|
1
3
|
import json
|
|
2
4
|
from typing import Optional, Union, List
|
|
5
|
+
import multiprocessing
|
|
6
|
+
from contextlib import closing
|
|
3
7
|
|
|
8
|
+
# 3rd party dependencies
|
|
9
|
+
from tqdm import tqdm
|
|
10
|
+
|
|
11
|
+
# project dependencies
|
|
4
12
|
from lightphe.models.Homomorphic import Homomorphic
|
|
5
13
|
from lightphe.models.Ciphertext import Ciphertext
|
|
6
14
|
from lightphe.models.Algorithm import Algorithm
|
|
7
15
|
from lightphe.models.Tensor import Fraction, EncryptedTensor
|
|
16
|
+
from lightphe.models.EllipticCurve import EllipticCurvePoint
|
|
17
|
+
from lightphe.commons import phe_utils
|
|
18
|
+
from lightphe.commons.logger import Logger
|
|
19
|
+
|
|
20
|
+
# cryptosystems
|
|
8
21
|
from lightphe.cryptosystems.RSA import RSA
|
|
9
22
|
from lightphe.cryptosystems.ElGamal import ElGamal
|
|
10
23
|
from lightphe.cryptosystems.Paillier import Paillier
|
|
@@ -14,16 +27,20 @@ from lightphe.cryptosystems.Benaloh import Benaloh
|
|
|
14
27
|
from lightphe.cryptosystems.NaccacheStern import NaccacheStern
|
|
15
28
|
from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali
|
|
16
29
|
from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal
|
|
17
|
-
from lightphe.
|
|
18
|
-
from lightphe.
|
|
30
|
+
from lightphe.elliptic_curve_forms.weierstrass import Weierstrass
|
|
31
|
+
from lightphe.elliptic_curve_forms.edwards import TwistedEdwards
|
|
32
|
+
from lightphe.elliptic_curve_forms.koblitz import Koblitz
|
|
33
|
+
|
|
19
34
|
|
|
20
|
-
# pylint: disable=eval-used, simplifiable-if-expression
|
|
35
|
+
# pylint: disable=eval-used, simplifiable-if-expression, too-few-public-methods
|
|
21
36
|
|
|
22
37
|
logger = Logger(module="lightphe/__init__.py")
|
|
23
38
|
|
|
39
|
+
VERSION = "0.0.12"
|
|
40
|
+
|
|
24
41
|
|
|
25
42
|
class LightPHE:
|
|
26
|
-
__version__ =
|
|
43
|
+
__version__ = VERSION
|
|
27
44
|
|
|
28
45
|
def __init__(
|
|
29
46
|
self,
|
|
@@ -53,6 +70,8 @@ class LightPHE:
|
|
|
53
70
|
- e.g. ed25519, ed448 for edwards form
|
|
54
71
|
- e.g. secp256k1 for weierstrass form
|
|
55
72
|
- e.g. k-409 for koblitz form
|
|
73
|
+
List of all available curves:
|
|
74
|
+
github.com/serengil/LightPHE/blob/master/lightphe/elliptic_curve_forms/README.md
|
|
56
75
|
This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
|
|
57
76
|
"""
|
|
58
77
|
self.algorithm_name = algorithm_name
|
|
@@ -196,46 +215,49 @@ class LightPHE:
|
|
|
196
215
|
encrypted tensor (list of encrypted tensor object)
|
|
197
216
|
"""
|
|
198
217
|
encrypted_tensor: List[Fraction] = []
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
abs_dividend, _ = phe_utils.fractionize(
|
|
222
|
-
value=(abs(m) % self.cs.plaintext_modulo),
|
|
223
|
-
modulo=self.cs.plaintext_modulo,
|
|
224
|
-
precision=self.precision,
|
|
225
|
-
)
|
|
226
|
-
dividend_encrypted = self.cs.encrypt(plaintext=dividend)
|
|
227
|
-
abs_dividend_encrypted = self.cs.encrypt(plaintext=abs_dividend)
|
|
228
|
-
divisor_encrypted = self.cs.encrypt(plaintext=divisor)
|
|
229
|
-
c = Fraction(
|
|
230
|
-
dividend=dividend_encrypted,
|
|
231
|
-
divisor=divisor_encrypted,
|
|
232
|
-
abs_dividend=abs_dividend_encrypted,
|
|
233
|
-
sign=1 if m >= 0 else -1,
|
|
218
|
+
|
|
219
|
+
encrypted_zero = self.cs.encrypt(plaintext=0)
|
|
220
|
+
divisor_encrypted = self.cs.encrypt(plaintext=10**self.precision)
|
|
221
|
+
is_positive_tensor = all(m >= 0 for m in tensor)
|
|
222
|
+
|
|
223
|
+
num_workers = min(len(tensor), 2 * multiprocessing.cpu_count())
|
|
224
|
+
logger.debug(f"encrypting tensors in {num_workers} parallel")
|
|
225
|
+
|
|
226
|
+
with closing(multiprocessing.Pool(num_workers)) as pool:
|
|
227
|
+
funclist = []
|
|
228
|
+
|
|
229
|
+
for m in tensor:
|
|
230
|
+
f = pool.apply_async(
|
|
231
|
+
encrypt_something,
|
|
232
|
+
(
|
|
233
|
+
m,
|
|
234
|
+
divisor_encrypted,
|
|
235
|
+
self.cs,
|
|
236
|
+
self.precision,
|
|
237
|
+
encrypted_zero,
|
|
238
|
+
is_positive_tensor,
|
|
239
|
+
),
|
|
234
240
|
)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
241
|
+
funclist.append(f)
|
|
242
|
+
|
|
243
|
+
tic = time.time()
|
|
244
|
+
encrypted_tensor = []
|
|
245
|
+
for f in tqdm(
|
|
246
|
+
funclist,
|
|
247
|
+
desc="Encrypting tensors",
|
|
248
|
+
disable=True if len(tensor) < 100 else False,
|
|
249
|
+
):
|
|
250
|
+
result = f.get(timeout=10)
|
|
251
|
+
encrypted_tensor.append(result)
|
|
252
|
+
|
|
253
|
+
toc = time.time()
|
|
254
|
+
logger.debug(f"encryption took {toc - tic} seconds")
|
|
255
|
+
|
|
256
|
+
return EncryptedTensor(
|
|
257
|
+
fractions=encrypted_tensor,
|
|
258
|
+
cs=self.cs,
|
|
259
|
+
precision=self.precision,
|
|
260
|
+
)
|
|
239
261
|
|
|
240
262
|
def __decrypt_tensors(
|
|
241
263
|
self, encrypted_tensor: EncryptedTensor
|
|
@@ -341,3 +363,112 @@ class LightPHE:
|
|
|
341
363
|
return Ciphertext(
|
|
342
364
|
algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext
|
|
343
365
|
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def encrypt_something(
|
|
369
|
+
m: Union[int, float],
|
|
370
|
+
divisor_encrypted,
|
|
371
|
+
cs: Homomorphic,
|
|
372
|
+
precision: int,
|
|
373
|
+
encrypted_zero: int,
|
|
374
|
+
is_positive_tensor: bool,
|
|
375
|
+
) -> Fraction:
|
|
376
|
+
if m == 0:
|
|
377
|
+
# this is very common in VGG-Face embeddings
|
|
378
|
+
c = Fraction(
|
|
379
|
+
dividend=encrypted_zero,
|
|
380
|
+
divisor=divisor_encrypted,
|
|
381
|
+
abs_dividend=encrypted_zero,
|
|
382
|
+
sign=0,
|
|
383
|
+
)
|
|
384
|
+
elif isinstance(m, int):
|
|
385
|
+
dividend_encrypted = cs.encrypt(
|
|
386
|
+
plaintext=(m % cs.plaintext_modulo) * pow(10, precision)
|
|
387
|
+
)
|
|
388
|
+
abs_dividend_encrypted = (
|
|
389
|
+
dividend_encrypted
|
|
390
|
+
if is_positive_tensor
|
|
391
|
+
else cs.encrypt(
|
|
392
|
+
plaintext=(abs(m) % cs.plaintext_modulo) * pow(10, precision)
|
|
393
|
+
)
|
|
394
|
+
)
|
|
395
|
+
# divisor_encrypted = self.cs.encrypt(plaintext=pow(10, self.precision))
|
|
396
|
+
c = Fraction(
|
|
397
|
+
dividend=dividend_encrypted,
|
|
398
|
+
divisor=divisor_encrypted,
|
|
399
|
+
abs_dividend=abs_dividend_encrypted,
|
|
400
|
+
sign=1 if m >= 0 else -1,
|
|
401
|
+
)
|
|
402
|
+
elif isinstance(m, float):
|
|
403
|
+
dividend, _ = phe_utils.fractionize(
|
|
404
|
+
value=(m % cs.plaintext_modulo),
|
|
405
|
+
modulo=cs.plaintext_modulo,
|
|
406
|
+
precision=precision,
|
|
407
|
+
)
|
|
408
|
+
abs_dividend = (
|
|
409
|
+
dividend
|
|
410
|
+
if is_positive_tensor
|
|
411
|
+
else phe_utils.fractionize(
|
|
412
|
+
value=(abs(m) % cs.plaintext_modulo),
|
|
413
|
+
modulo=cs.plaintext_modulo,
|
|
414
|
+
precision=precision,
|
|
415
|
+
)[0]
|
|
416
|
+
)
|
|
417
|
+
dividend_encrypted = cs.encrypt(plaintext=dividend)
|
|
418
|
+
abs_dividend_encrypted = (
|
|
419
|
+
dividend_encrypted
|
|
420
|
+
if is_positive_tensor
|
|
421
|
+
else cs.encrypt(plaintext=abs_dividend)
|
|
422
|
+
)
|
|
423
|
+
# divisor_encrypted = self.cs.encrypt(plaintext=_divisor)
|
|
424
|
+
c = Fraction(
|
|
425
|
+
dividend=dividend_encrypted,
|
|
426
|
+
divisor=divisor_encrypted,
|
|
427
|
+
abs_dividend=abs_dividend_encrypted,
|
|
428
|
+
sign=1 if m >= 0 else -1,
|
|
429
|
+
)
|
|
430
|
+
else:
|
|
431
|
+
raise ValueError(f"unimplemented type - {type(m)}")
|
|
432
|
+
|
|
433
|
+
return c
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
class ECC:
|
|
437
|
+
__version__ = VERSION
|
|
438
|
+
|
|
439
|
+
def __init__(
|
|
440
|
+
self, form_name: Optional[str] = None, curve_name: Optional[str] = None
|
|
441
|
+
):
|
|
442
|
+
"""
|
|
443
|
+
Construct an Elliptic Curve over a finite field (prime or binary)
|
|
444
|
+
Args:
|
|
445
|
+
form_name (str): specifies the form of the elliptic curve.
|
|
446
|
+
Options: 'weierstrass' (default), 'edwards', 'koblitz'.
|
|
447
|
+
curve_name (str): specifies the elliptic curve to use.
|
|
448
|
+
Options:
|
|
449
|
+
- e.g. ed25519, ed448 for edwards form
|
|
450
|
+
- e.g. secp256k1 for weierstrass form
|
|
451
|
+
- e.g. k-409 for koblitz form
|
|
452
|
+
List of all available curves:
|
|
453
|
+
github.com/serengil/LightPHE/blob/master/lightphe/elliptic_curve_forms/README.md
|
|
454
|
+
"""
|
|
455
|
+
if form_name is None or form_name == "weierstrass":
|
|
456
|
+
self.curve = Weierstrass(curve=curve_name)
|
|
457
|
+
elif form_name in "edwards":
|
|
458
|
+
self.curve = TwistedEdwards(curve=curve_name)
|
|
459
|
+
elif form_name in "koblitz":
|
|
460
|
+
self.curve = Koblitz(curve=curve_name)
|
|
461
|
+
else:
|
|
462
|
+
raise ValueError(f"unimplemented curve form - {form_name}")
|
|
463
|
+
|
|
464
|
+
# base point
|
|
465
|
+
self.G = EllipticCurvePoint(self.curve.G[0], self.curve.G[1], self.curve)
|
|
466
|
+
|
|
467
|
+
# order of the curve
|
|
468
|
+
self.n = self.curve.n
|
|
469
|
+
|
|
470
|
+
# point at infinity or neutral / identity element
|
|
471
|
+
self.O = EllipticCurvePoint(self.curve.O[0], self.curve.O[1], self.curve)
|
|
472
|
+
|
|
473
|
+
# modulo
|
|
474
|
+
self.modulo = self.curve.modulo
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
# built-in dependencies
|
|
1
2
|
import random
|
|
2
3
|
from typing import Optional
|
|
4
|
+
|
|
5
|
+
# project dependencies
|
|
3
6
|
from lightphe.models.Homomorphic import Homomorphic
|
|
4
|
-
from lightphe.
|
|
5
|
-
from lightphe.elliptic_curve_forms.edwards import TwistedEdwards
|
|
6
|
-
from lightphe.elliptic_curve_forms.koblitz import Koblitz
|
|
7
|
+
from lightphe.models.EllipticCurve import EllipticCurvePoint
|
|
7
8
|
from lightphe.commons.logger import Logger
|
|
8
9
|
|
|
9
10
|
logger = Logger(module="lightphe/cryptosystems/EllipticCurveElGamal.py")
|
|
@@ -39,20 +40,16 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
39
40
|
- secp256k1 for weierstrass form
|
|
40
41
|
This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
|
|
41
42
|
"""
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
self.
|
|
52
|
-
self.keys["public_key"]["form"] = form
|
|
53
|
-
self.keys["private_key"]["form"] = form
|
|
54
|
-
self.plaintext_modulo = self.curve.modulo
|
|
55
|
-
self.ciphertext_modulo = self.curve.modulo
|
|
43
|
+
# to avoid circular import, import ECC class here
|
|
44
|
+
from lightphe import ECC
|
|
45
|
+
|
|
46
|
+
self.ecc = ECC(form_name=form, curve_name=curve)
|
|
47
|
+
|
|
48
|
+
self.keys = keys or self.generate_keys(key_size or self.ecc.n.bit_length())
|
|
49
|
+
self.keys["curve"] = curve
|
|
50
|
+
self.keys["form"] = form
|
|
51
|
+
self.plaintext_modulo = self.ecc.modulo
|
|
52
|
+
self.ciphertext_modulo = self.ecc.modulo
|
|
56
53
|
|
|
57
54
|
def generate_keys(self, key_size: int):
|
|
58
55
|
"""
|
|
@@ -72,8 +69,10 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
72
69
|
f"{key_size} bit private key generated for Elliptic Curve ElGamal."
|
|
73
70
|
)
|
|
74
71
|
|
|
72
|
+
# base point
|
|
73
|
+
|
|
75
74
|
# public key
|
|
76
|
-
Qa = self.
|
|
75
|
+
Qa = self.ecc.G * ka
|
|
77
76
|
|
|
78
77
|
keys["public_key"]["Qa"] = Qa
|
|
79
78
|
keys["private_key"]["ka"] = ka
|
|
@@ -87,10 +86,7 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
87
86
|
random key (int): one time random key for encryption
|
|
88
87
|
"""
|
|
89
88
|
# return random.getrandbits(128)
|
|
90
|
-
return random.getrandbits(
|
|
91
|
-
(isinstance(self.curve.n, int) and self.curve.n.bit_length())
|
|
92
|
-
or (isinstance(self.curve.n, str) and len(self.curve.n))
|
|
93
|
-
)
|
|
89
|
+
return random.getrandbits(self.ecc.n.bit_length())
|
|
94
90
|
|
|
95
91
|
def encrypt(self, plaintext: int, random_key: Optional[int] = None) -> tuple:
|
|
96
92
|
"""
|
|
@@ -101,23 +97,18 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
101
97
|
Returns
|
|
102
98
|
ciphertext (tuple): c1 and c2
|
|
103
99
|
"""
|
|
104
|
-
# base point
|
|
105
|
-
G = self.curve.G
|
|
106
|
-
|
|
107
100
|
# public key
|
|
108
101
|
Qa = self.keys["public_key"]["Qa"]
|
|
109
102
|
|
|
110
103
|
# random key
|
|
111
104
|
r = random_key or self.generate_random_key()
|
|
112
105
|
|
|
113
|
-
s = self.
|
|
114
|
-
|
|
115
|
-
c1 = self.curve.double_and_add(G=G, k=r)
|
|
106
|
+
s = self.ecc.G * plaintext
|
|
116
107
|
|
|
117
|
-
|
|
118
|
-
c2 =
|
|
108
|
+
c1 = self.ecc.G * r
|
|
109
|
+
c2 = (Qa * r) + s
|
|
119
110
|
|
|
120
|
-
return c1, c2
|
|
111
|
+
return c1.get_point(), c2.get_point()
|
|
121
112
|
|
|
122
113
|
def decrypt(self, ciphertext: tuple) -> int:
|
|
123
114
|
"""
|
|
@@ -130,11 +121,13 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
130
121
|
# private key
|
|
131
122
|
ka = self.keys["private_key"]["ka"]
|
|
132
123
|
|
|
124
|
+
# c1 and c2 as tuple of integers
|
|
133
125
|
c1, c2 = ciphertext
|
|
134
126
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
127
|
+
c1 = EllipticCurvePoint(x=c1[0], y=c1[1], curve=self.ecc.curve)
|
|
128
|
+
c2 = EllipticCurvePoint(x=c2[0], y=c2[1], curve=self.ecc.curve)
|
|
129
|
+
|
|
130
|
+
s_prime = (-c1 * ka) + c2
|
|
138
131
|
|
|
139
132
|
# s_prime is a point on the elliptic curve
|
|
140
133
|
# s_prime = k x G
|
|
@@ -142,16 +135,18 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
142
135
|
# this requires to solve ECDLP
|
|
143
136
|
|
|
144
137
|
# base point
|
|
145
|
-
G = self.
|
|
138
|
+
G = self.ecc.G
|
|
146
139
|
k = 2
|
|
147
140
|
while True:
|
|
148
|
-
G =
|
|
149
|
-
|
|
141
|
+
G = G + self.ecc.G
|
|
142
|
+
|
|
143
|
+
if G == s_prime:
|
|
150
144
|
return k
|
|
145
|
+
|
|
151
146
|
k = k + 1
|
|
152
|
-
if k > self.
|
|
147
|
+
if k > self.ecc.n:
|
|
153
148
|
raise ValueError(
|
|
154
|
-
f"Cannot restore scalar from {s_prime} = k x {self.
|
|
149
|
+
f"Cannot restore scalar from {s_prime} = k x {self.ecc.G}"
|
|
155
150
|
)
|
|
156
151
|
|
|
157
152
|
def multiply(self, ciphertext1: tuple, ciphertext2: tuple) -> tuple:
|
|
@@ -169,9 +164,19 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
169
164
|
Returns
|
|
170
165
|
ciphertext (dict): Elliptic Curve ElGamal ciphertext consisting of c1 and c2 keys
|
|
171
166
|
"""
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
167
|
+
c1_1, c1_2 = ciphertext1
|
|
168
|
+
c2_1, c2_2 = ciphertext2
|
|
169
|
+
|
|
170
|
+
# cast them to elliptic curve points
|
|
171
|
+
c1_1 = EllipticCurvePoint(x=c1_1[0], y=c1_1[1], curve=self.ecc.curve)
|
|
172
|
+
c1_2 = EllipticCurvePoint(x=c1_2[0], y=c1_2[1], curve=self.ecc.curve)
|
|
173
|
+
c2_1 = EllipticCurvePoint(x=c2_1[0], y=c2_1[1], curve=self.ecc.curve)
|
|
174
|
+
c2_2 = EllipticCurvePoint(x=c2_2[0], y=c2_2[1], curve=self.ecc.curve)
|
|
175
|
+
|
|
176
|
+
a = c1_1 + c2_1
|
|
177
|
+
b = c1_2 + c2_2
|
|
178
|
+
|
|
179
|
+
return a.get_point(), b.get_point()
|
|
175
180
|
|
|
176
181
|
def xor(self, ciphertext1: tuple, ciphertext2: tuple) -> int:
|
|
177
182
|
raise ValueError(
|
|
@@ -189,10 +194,17 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
189
194
|
Returns:
|
|
190
195
|
ciphertext (int): new ciphertext created with Elliptic Curve ElGamal
|
|
191
196
|
"""
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
197
|
+
# Both P and Q are tuples of integers
|
|
198
|
+
P, Q = ciphertext
|
|
199
|
+
|
|
200
|
+
# cast P and Q to EllipticCurvePoint
|
|
201
|
+
P = EllipticCurvePoint(x=P[0], y=P[1], curve=self.ecc.curve)
|
|
202
|
+
Q = EllipticCurvePoint(x=Q[0], y=Q[1], curve=self.ecc.curve)
|
|
203
|
+
|
|
204
|
+
P_prime = P * constant
|
|
205
|
+
Q_prime = Q * constant
|
|
206
|
+
|
|
207
|
+
return P_prime.get_point(), Q_prime.get_point()
|
|
196
208
|
|
|
197
209
|
def reencrypt(self, ciphertext: tuple) -> tuple:
|
|
198
210
|
raise ValueError(
|
|
@@ -111,6 +111,9 @@ class Weierstrass(EllipticCurve):
|
|
|
111
111
|
Returns
|
|
112
112
|
is_on_curve (boolean): returns True if point is on the curve
|
|
113
113
|
"""
|
|
114
|
+
if P == self.O:
|
|
115
|
+
return True
|
|
116
|
+
|
|
114
117
|
x, y = P
|
|
115
118
|
return (y * y) % self.modulo == (
|
|
116
119
|
pow(x, 3, self.modulo) + self.a * x + self.b
|