lightphe 0.0.9__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.9 → lightphe-0.0.10}/PKG-INFO +28 -2
- {lightphe-0.0.9 → lightphe-0.0.10}/README.md +27 -1
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/__init__.py +49 -13
- lightphe-0.0.10/lightphe/commons/binary_operations.py +147 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/commons/phe_utils.py +22 -4
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/EllipticCurveElGamal.py +48 -28
- 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.9 → lightphe-0.0.10}/lightphe/models/Ciphertext.py +27 -8
- lightphe-0.0.10/lightphe/models/Curve.py +29 -0
- lightphe-0.0.10/lightphe/models/EllipticCurve.py +78 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/models/Tensor.py +13 -5
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe.egg-info/PKG-INFO +28 -2
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe.egg-info/SOURCES.txt +3 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/setup.py +1 -1
- lightphe-0.0.10/tests/test_ellipticcurveelgamal.py +212 -0
- lightphe-0.0.9/lightphe/elliptic_curve_forms/edwards.py +0 -61
- lightphe-0.0.9/lightphe/elliptic_curve_forms/weierstrass.py +0 -75
- lightphe-0.0.9/lightphe/models/EllipticCurve.py +0 -45
- lightphe-0.0.9/tests/test_ellipticcurveelgamal.py +0 -69
- {lightphe-0.0.9 → lightphe-0.0.10}/LICENSE +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/commons/__init__.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/commons/logger.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/Benaloh.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/DamgardJurik.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/ElGamal.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/GoldwasserMicali.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/NaccacheStern.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/OkamotoUchiyama.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/Paillier.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/RSA.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/__init__.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/elliptic_curve_forms/__init__.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/models/Algorithm.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/models/Homomorphic.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/models/__init__.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe.egg-info/dependency_links.txt +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe.egg-info/requires.txt +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/lightphe.egg-info/top_level.txt +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/requirements.txt +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/setup.cfg +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/__init__.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_benaloh.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_cloud.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_damgard.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_elgamal.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_exporting_keys.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_goldwasser.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_naccache.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_okamoto.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_paillier.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_rsa.py +0 -0
- {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_salary.py +0 -0
- {lightphe-0.0.9 → 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/) ([`Weierstrass`](https://sefiks.com/2016/03/13/the-math-behind-elliptic-curve-cryptography/) and [`Edwards`](https://sefiks.com/2018/12/19/a-gentle-introduction-to-edwards-curves/) forms) [`Paillier`](https://sefiks.com/2023/04/03/a-step-by-step-partially-homomorphic-encryption-example-with-paillier-in-python/), [`Damgard-Jurik`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-damgard-jurik-in-python/), [`Okamoto–Uchiyama`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-okamoto-uchiyama-in-python/), [`Benaloh`](https://sefiks.com/2023/10/06/a-step-by-step-partially-homomorphic-encryption-example-with-benaloh-in-python-from-scratch/), [`Naccache–Stern`](https://sefiks.com/2023/10/26/a-step-by-step-partially-homomorphic-encryption-example-with-naccache-stern-in-python/), [`Goldwasser–Micali`](https://sefiks.com/2023/10/27/a-step-by-step-partially-homomorphic-encryption-example-with-goldwasser-micali-in-python/).
|
|
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
|
|
|
@@ -245,6 +245,32 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
|
|
|
245
245
|
|
|
246
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
247
|
|
|
248
|
+
### Elliptic Curve Cryptography
|
|
249
|
+
|
|
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.
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
some_curves = [
|
|
254
|
+
{"form": "weierstrass", "curve": "secp256k1"},
|
|
255
|
+
{"form": "edwards", "curve": "ed25519"},
|
|
256
|
+
{"form": "koblitz", "curve": "k163"}
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
curve = some_curves[1]
|
|
260
|
+
|
|
261
|
+
phe = LightPHE(
|
|
262
|
+
algorithm_name="EllipticCurve-ElGamal",
|
|
263
|
+
form=curve["form"],
|
|
264
|
+
curve=curve["curve"],
|
|
265
|
+
)
|
|
266
|
+
```
|
|
267
|
+
|
|
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.
|
|
269
|
+
|
|
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.
|
|
271
|
+
|
|
272
|
+
See [`Curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for more details.
|
|
273
|
+
|
|
248
274
|
# Contributing
|
|
249
275
|
|
|
250
276
|
All PRs are more than welcome! If you are planning to contribute a large patch, please create an issue first to get any upfront questions or design decisions out of the way first.
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
<p align="center"><img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/phi.png" width="200" height="240"></p>
|
|
22
22
|
|
|
23
|
-
LightPHE is a lightweight partially homomorphic encryption library for python. It is a hybrid homomoprhic encryption library wrapping many schemes such as [`RSA`](https://sefiks.com/2023/03/06/a-step-by-step-partially-homomorphic-encryption-example-with-rsa-in-python/), [`ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Exponential ElGamal`](https://sefiks.com/2023/03/27/a-step-by-step-partially-homomorphic-encryption-example-with-elgamal-in-python/), [`Elliptic Curve ElGamal`](https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/) ([`Weierstrass`](https://sefiks.com/2016/03/13/the-math-behind-elliptic-curve-cryptography/) and [`Edwards`](https://sefiks.com/2018/12/19/a-gentle-introduction-to-edwards-curves/) forms) [`Paillier`](https://sefiks.com/2023/04/03/a-step-by-step-partially-homomorphic-encryption-example-with-paillier-in-python/), [`Damgard-Jurik`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-damgard-jurik-in-python/), [`Okamoto–Uchiyama`](https://sefiks.com/2023/10/20/a-step-by-step-partially-homomorphic-encryption-example-with-okamoto-uchiyama-in-python/), [`Benaloh`](https://sefiks.com/2023/10/06/a-step-by-step-partially-homomorphic-encryption-example-with-benaloh-in-python-from-scratch/), [`Naccache–Stern`](https://sefiks.com/2023/10/26/a-step-by-step-partially-homomorphic-encryption-example-with-naccache-stern-in-python/), [`Goldwasser–Micali`](https://sefiks.com/2023/10/27/a-step-by-step-partially-homomorphic-encryption-example-with-goldwasser-micali-in-python/).
|
|
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
|
|
|
@@ -229,6 +229,32 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
|
|
|
229
229
|
|
|
230
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
231
|
|
|
232
|
+
### Elliptic Curve Cryptography
|
|
233
|
+
|
|
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.
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
some_curves = [
|
|
238
|
+
{"form": "weierstrass", "curve": "secp256k1"},
|
|
239
|
+
{"form": "edwards", "curve": "ed25519"},
|
|
240
|
+
{"form": "koblitz", "curve": "k163"}
|
|
241
|
+
]
|
|
242
|
+
|
|
243
|
+
curve = some_curves[1]
|
|
244
|
+
|
|
245
|
+
phe = LightPHE(
|
|
246
|
+
algorithm_name="EllipticCurve-ElGamal",
|
|
247
|
+
form=curve["form"],
|
|
248
|
+
curve=curve["curve"],
|
|
249
|
+
)
|
|
250
|
+
```
|
|
251
|
+
|
|
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.
|
|
253
|
+
|
|
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.
|
|
255
|
+
|
|
256
|
+
See [`Curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for more details.
|
|
257
|
+
|
|
232
258
|
# Contributing
|
|
233
259
|
|
|
234
260
|
All PRs are more than welcome! If you are planning to contribute a large patch, please create an issue first to get any upfront questions or design decisions out of the way first.
|
|
@@ -23,7 +23,8 @@ logger = Logger(module="lightphe/__init__.py")
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class LightPHE:
|
|
26
|
-
__version__ = "0.0.
|
|
26
|
+
__version__ = "0.0.10"
|
|
27
|
+
|
|
27
28
|
def __init__(
|
|
28
29
|
self,
|
|
29
30
|
algorithm_name: str,
|
|
@@ -31,7 +32,8 @@ class LightPHE:
|
|
|
31
32
|
key_file: Optional[str] = None,
|
|
32
33
|
key_size: Optional[int] = None,
|
|
33
34
|
precision: int = 5,
|
|
34
|
-
form: str =
|
|
35
|
+
form: Optional[str] = None,
|
|
36
|
+
curve: Optional[str] = None,
|
|
35
37
|
):
|
|
36
38
|
"""
|
|
37
39
|
Build LightPHE class
|
|
@@ -43,19 +45,30 @@ class LightPHE:
|
|
|
43
45
|
key_file (str): if keys are exported, you can load them into cryptosystem
|
|
44
46
|
key_size (int): key size in bits
|
|
45
47
|
precision (int): precision for homomorphic operations on tensors
|
|
46
|
-
form (str): specifies the form of the elliptic curve.
|
|
47
|
-
Options: 'weierstrass' (default), 'edwards'.
|
|
48
|
+
form (str): specifies the form of the elliptic curve.
|
|
49
|
+
Options: 'weierstrass' (default), 'edwards', 'koblitz'.
|
|
50
|
+
This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
|
|
51
|
+
curve (str): specifies the elliptic curve to use.
|
|
52
|
+
Options:
|
|
53
|
+
- e.g. ed25519, ed448 for edwards form
|
|
54
|
+
- e.g. secp256k1 for weierstrass form
|
|
55
|
+
- e.g. k-409 for koblitz form
|
|
48
56
|
This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
|
|
49
57
|
"""
|
|
50
58
|
self.algorithm_name = algorithm_name
|
|
51
59
|
self.precision = precision
|
|
52
60
|
self.form = form
|
|
61
|
+
self.curve = curve
|
|
53
62
|
|
|
54
63
|
if key_file is not None:
|
|
55
64
|
keys = self.restore_keys(target_file=key_file)
|
|
56
65
|
|
|
57
66
|
self.cs: Homomorphic = self.__build_cryptosystem(
|
|
58
|
-
algorithm_name=algorithm_name,
|
|
67
|
+
algorithm_name=algorithm_name,
|
|
68
|
+
keys=keys,
|
|
69
|
+
key_size=key_size,
|
|
70
|
+
form=form,
|
|
71
|
+
curve=curve,
|
|
59
72
|
)
|
|
60
73
|
|
|
61
74
|
def __build_cryptosystem(
|
|
@@ -64,6 +77,7 @@ class LightPHE:
|
|
|
64
77
|
keys: Optional[dict] = None,
|
|
65
78
|
key_size: Optional[int] = None,
|
|
66
79
|
form: Optional[str] = None,
|
|
80
|
+
curve: Optional[str] = None,
|
|
67
81
|
) -> Union[
|
|
68
82
|
RSA,
|
|
69
83
|
ElGamal,
|
|
@@ -83,9 +97,14 @@ class LightPHE:
|
|
|
83
97
|
keys (dict): optional private-public key pair
|
|
84
98
|
key_file (str): if keys are exported, you can load them into cryptosystem
|
|
85
99
|
key_size (int): key size in bits
|
|
86
|
-
form (str): specifies the form of the elliptic curve.
|
|
100
|
+
form (str): specifies the form of the elliptic curve.
|
|
87
101
|
Options: 'weierstrass' (default), 'edwards'.
|
|
88
102
|
This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
|
|
103
|
+
curve (str): specifies the elliptic curve to use.
|
|
104
|
+
Options:
|
|
105
|
+
- ed25519, ed448 for edwards form
|
|
106
|
+
- secp256k1 for weierstrass form
|
|
107
|
+
This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
|
|
89
108
|
Returns
|
|
90
109
|
cryptosystem
|
|
91
110
|
"""
|
|
@@ -97,7 +116,9 @@ class LightPHE:
|
|
|
97
116
|
elif algorithm_name == Algorithm.ExponentialElGamal:
|
|
98
117
|
cs = ElGamal(keys=keys, key_size=key_size, exponential=True)
|
|
99
118
|
elif algorithm_name == Algorithm.EllipticCurveElGamal:
|
|
100
|
-
cs = EllipticCurveElGamal(
|
|
119
|
+
cs = EllipticCurveElGamal(
|
|
120
|
+
keys=keys, key_size=key_size, form=form, curve=curve
|
|
121
|
+
)
|
|
101
122
|
elif algorithm_name == Algorithm.Paillier:
|
|
102
123
|
cs = Paillier(keys=keys, key_size=key_size)
|
|
103
124
|
elif algorithm_name == Algorithm.DamgardJurik:
|
|
@@ -114,7 +135,9 @@ class LightPHE:
|
|
|
114
135
|
raise ValueError(f"unimplemented algorithm - {algorithm_name}")
|
|
115
136
|
return cs
|
|
116
137
|
|
|
117
|
-
def encrypt(
|
|
138
|
+
def encrypt(
|
|
139
|
+
self, plaintext: Union[int, float, list]
|
|
140
|
+
) -> Union[Ciphertext, EncryptedTensor]:
|
|
118
141
|
"""
|
|
119
142
|
Encrypt a plaintext with a built cryptosystem
|
|
120
143
|
Args:
|
|
@@ -130,9 +153,17 @@ class LightPHE:
|
|
|
130
153
|
return self.__encrypt_tensors(tensor=plaintext)
|
|
131
154
|
|
|
132
155
|
ciphertext = self.cs.encrypt(
|
|
133
|
-
plaintext=phe_utils.
|
|
156
|
+
plaintext=phe_utils.normalize_input(
|
|
157
|
+
value=plaintext, modulo=self.cs.plaintext_modulo
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
return Ciphertext(
|
|
161
|
+
algorithm_name=self.algorithm_name,
|
|
162
|
+
keys=self.cs.keys,
|
|
163
|
+
value=ciphertext,
|
|
164
|
+
form=self.form,
|
|
165
|
+
curve=self.curve,
|
|
134
166
|
)
|
|
135
|
-
return Ciphertext(algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext, form=self.form)
|
|
136
167
|
|
|
137
168
|
def decrypt(
|
|
138
169
|
self, ciphertext: Union[Ciphertext, EncryptedTensor]
|
|
@@ -171,7 +202,8 @@ class LightPHE:
|
|
|
171
202
|
plaintext=(m % self.cs.plaintext_modulo) * pow(10, self.precision)
|
|
172
203
|
)
|
|
173
204
|
abs_dividend_encrypted = self.cs.encrypt(
|
|
174
|
-
plaintext=(abs(m) % self.cs.plaintext_modulo)
|
|
205
|
+
plaintext=(abs(m) % self.cs.plaintext_modulo)
|
|
206
|
+
* pow(10, self.precision)
|
|
175
207
|
)
|
|
176
208
|
divisor_encrypted = self.cs.encrypt(plaintext=pow(10, self.precision))
|
|
177
209
|
c = Fraction(
|
|
@@ -205,7 +237,9 @@ class LightPHE:
|
|
|
205
237
|
encrypted_tensor.append(c)
|
|
206
238
|
return EncryptedTensor(fractions=encrypted_tensor, cs=self.cs)
|
|
207
239
|
|
|
208
|
-
def __decrypt_tensors(
|
|
240
|
+
def __decrypt_tensors(
|
|
241
|
+
self, encrypted_tensor: EncryptedTensor
|
|
242
|
+
) -> Union[List[int], List[float]]:
|
|
209
243
|
"""
|
|
210
244
|
Decrypt a given encrypted tensor
|
|
211
245
|
Args:
|
|
@@ -304,4 +338,6 @@ class LightPHE:
|
|
|
304
338
|
Returns:
|
|
305
339
|
Ciphertext
|
|
306
340
|
"""
|
|
307
|
-
return Ciphertext(
|
|
341
|
+
return Ciphertext(
|
|
342
|
+
algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext
|
|
343
|
+
)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module is heavily inspired by repo github.com/dimitrijray/ecc-binary-field/blob/master/binop.py
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def divide(a: int, b: int, p: int) -> int:
|
|
7
|
+
"""
|
|
8
|
+
Returns a_bin * (b_bin)^-1 (mod p)
|
|
9
|
+
Args:
|
|
10
|
+
a (int): nominator
|
|
11
|
+
b (int): denominator
|
|
12
|
+
p (int): modulo
|
|
13
|
+
Returns:
|
|
14
|
+
result (int): (a_bin * (b_bin)^-1 (mod p)) mod p
|
|
15
|
+
"""
|
|
16
|
+
result = mod(multi(a, inverse(b, p)), p)
|
|
17
|
+
return result
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def multi(a: int, b: int) -> int:
|
|
21
|
+
"""
|
|
22
|
+
Multiply two binary numbers in GF(2).
|
|
23
|
+
Args:
|
|
24
|
+
a (int): first number
|
|
25
|
+
b (int): second number
|
|
26
|
+
Returns:
|
|
27
|
+
result (int): carry-less multiplication between a and b
|
|
28
|
+
"""
|
|
29
|
+
result = 0
|
|
30
|
+
shift = 0
|
|
31
|
+
|
|
32
|
+
while b > 0:
|
|
33
|
+
if b & 1: # Check if the least significant bit of b is 1
|
|
34
|
+
result ^= a << shift
|
|
35
|
+
shift += 1
|
|
36
|
+
b >>= 1 # Shift b to the right by 1
|
|
37
|
+
|
|
38
|
+
return result
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def power_mod(num: int, exp: int, modulo: int) -> int:
|
|
42
|
+
"""
|
|
43
|
+
Calculate num^exp (mod m) in GF(2).
|
|
44
|
+
Args:
|
|
45
|
+
num (int): base
|
|
46
|
+
exp (int): exponent
|
|
47
|
+
modulo (int): modulo
|
|
48
|
+
Returns:
|
|
49
|
+
result (int): num^exp (mod m)
|
|
50
|
+
"""
|
|
51
|
+
result = 1
|
|
52
|
+
base = num % modulo
|
|
53
|
+
|
|
54
|
+
while exp > 0:
|
|
55
|
+
if exp & 1: # Check if the least significant bit of exp is 1
|
|
56
|
+
result = multi(base, result) # multiply result by base
|
|
57
|
+
result = mod(result, modulo) # apply modulo
|
|
58
|
+
|
|
59
|
+
base = square(base) # square the base
|
|
60
|
+
exp >>= 1 # right shift exp by 1
|
|
61
|
+
|
|
62
|
+
return result
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def square(num: int) -> int:
|
|
66
|
+
"""
|
|
67
|
+
Square a binary number in GF(2).
|
|
68
|
+
Args:
|
|
69
|
+
num (int): number
|
|
70
|
+
Returns:
|
|
71
|
+
result (int): square of num
|
|
72
|
+
"""
|
|
73
|
+
result = 0
|
|
74
|
+
shift = 0
|
|
75
|
+
|
|
76
|
+
while num > 0:
|
|
77
|
+
if num & 1: # Check if the least significant bit of num is 1
|
|
78
|
+
result ^= 1 << (2 * shift) # Set the appropriate bit in the result
|
|
79
|
+
|
|
80
|
+
num >>= 1 # Right shift num by 1
|
|
81
|
+
shift += 1
|
|
82
|
+
|
|
83
|
+
return result
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def mod(num: int, modulo: int) -> int:
|
|
87
|
+
"""
|
|
88
|
+
Perform modulo operation for binary numbers in GF(2).
|
|
89
|
+
Args:
|
|
90
|
+
num (int): number
|
|
91
|
+
modulo (int): modulo
|
|
92
|
+
Returns:
|
|
93
|
+
result (int): num mod modulo
|
|
94
|
+
"""
|
|
95
|
+
degP = num.bit_length() - 1
|
|
96
|
+
degR = modulo.bit_length() - 1
|
|
97
|
+
|
|
98
|
+
while degP >= degR and degR != 0:
|
|
99
|
+
shift = degP - degR
|
|
100
|
+
num ^= modulo << shift # Perform XOR to reduce the degree of num
|
|
101
|
+
degP = num.bit_length() - 1 # Update the degree of num
|
|
102
|
+
|
|
103
|
+
return num
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def div(num: int, modulo: int) -> int:
|
|
107
|
+
"""
|
|
108
|
+
Return the quotient of the polynomial division num / modulo in GF(2).
|
|
109
|
+
Args:
|
|
110
|
+
num (int): numerator
|
|
111
|
+
modulo (int): denominator
|
|
112
|
+
Returns:
|
|
113
|
+
result (int): quotient
|
|
114
|
+
"""
|
|
115
|
+
deg_p = num.bit_length() - 1
|
|
116
|
+
deg_r = modulo.bit_length() - 1
|
|
117
|
+
q = 0
|
|
118
|
+
|
|
119
|
+
while deg_p >= deg_r:
|
|
120
|
+
shift = deg_p - deg_r
|
|
121
|
+
q |= 1 << shift # Set the corresponding bit in the quotient
|
|
122
|
+
num ^= modulo << shift # Perform XOR to reduce the degree of num
|
|
123
|
+
deg_p = num.bit_length() - 1 # Update the degree of num
|
|
124
|
+
|
|
125
|
+
return q
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def inverse(num: int, modulo: int) -> int:
|
|
129
|
+
"""
|
|
130
|
+
Calculate the inverse of a binary number modulo a polynomial in GF(2).
|
|
131
|
+
Args:
|
|
132
|
+
num (int): number
|
|
133
|
+
modulo (int): modulo
|
|
134
|
+
Returns:
|
|
135
|
+
result (int): inverse of num mod modulo
|
|
136
|
+
"""
|
|
137
|
+
a, b = num, modulo
|
|
138
|
+
p1, p2 = 1, 0
|
|
139
|
+
|
|
140
|
+
while b != 1:
|
|
141
|
+
q = div(a, b)
|
|
142
|
+
r = mod(a, b)
|
|
143
|
+
a, b = b, r
|
|
144
|
+
p_a = p1 ^ multi(q, p2)
|
|
145
|
+
p1, p2 = p2, p_a
|
|
146
|
+
|
|
147
|
+
return p2
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
# built-in dependencies
|
|
1
2
|
from typing import Union, Tuple, Optional
|
|
2
3
|
from decimal import Decimal, getcontext
|
|
4
|
+
|
|
5
|
+
# project dependencies
|
|
3
6
|
from lightphe.commons.logger import Logger
|
|
4
7
|
|
|
5
8
|
logger = Logger(module="lightphe/commons/phe_utils.py")
|
|
@@ -7,8 +10,19 @@ logger = Logger(module="lightphe/commons/phe_utils.py")
|
|
|
7
10
|
# pylint: disable=no-else-return, no-else-break
|
|
8
11
|
|
|
9
12
|
|
|
10
|
-
def
|
|
11
|
-
|
|
13
|
+
def normalize_input(value: Union[int, float], modulo: Union[int, str]) -> int:
|
|
14
|
+
"""
|
|
15
|
+
Normaliz given value to be in the range of modulo
|
|
16
|
+
Args:
|
|
17
|
+
value (int, float): the value to be normalized
|
|
18
|
+
modulo (int): the modulo value
|
|
19
|
+
Returns:
|
|
20
|
+
result (int, str): the normalized value
|
|
21
|
+
"""
|
|
22
|
+
if isinstance(value, int) and isinstance(modulo, str):
|
|
23
|
+
# elliptic curve elgamal & koblitz curves
|
|
24
|
+
result = value % int(modulo, 2)
|
|
25
|
+
elif isinstance(value, int) and isinstance(modulo, int):
|
|
12
26
|
result = value % modulo
|
|
13
27
|
elif isinstance(value, float) and value >= 0:
|
|
14
28
|
dividend, divisor = fractionize(value=value, modulo=modulo)
|
|
@@ -23,7 +37,9 @@ def parse_int(value: Union[int, float], modulo: int) -> int:
|
|
|
23
37
|
return result
|
|
24
38
|
|
|
25
39
|
|
|
26
|
-
def fractionize(
|
|
40
|
+
def fractionize(
|
|
41
|
+
value: float, modulo: int, precision: Optional[int] = None
|
|
42
|
+
) -> Tuple[int, int]:
|
|
27
43
|
getcontext().prec = 50
|
|
28
44
|
|
|
29
45
|
if precision is None:
|
|
@@ -40,7 +56,9 @@ def fractionize(value: float, modulo: int, precision: Optional[int] = None) -> T
|
|
|
40
56
|
|
|
41
57
|
if scaling_factor > 10**precision:
|
|
42
58
|
# If scaling factor is too large, discard excess part of integer value
|
|
43
|
-
integer_value = int(
|
|
59
|
+
integer_value = int(
|
|
60
|
+
integer_value / (10 ** (scaling_factor - 10**precision))
|
|
61
|
+
)
|
|
44
62
|
break
|
|
45
63
|
elif scaling_factor < 10 ** (precision - 1):
|
|
46
64
|
# If scaling factor is too small, multiply dividend and divisor 10 times
|
|
@@ -3,6 +3,7 @@ from typing import Optional
|
|
|
3
3
|
from lightphe.models.Homomorphic import Homomorphic
|
|
4
4
|
from lightphe.elliptic_curve_forms.weierstrass import Weierstrass
|
|
5
5
|
from lightphe.elliptic_curve_forms.edwards import TwistedEdwards
|
|
6
|
+
from lightphe.elliptic_curve_forms.koblitz import Koblitz
|
|
6
7
|
from lightphe.commons.logger import Logger
|
|
7
8
|
|
|
8
9
|
logger = Logger(module="lightphe/cryptosystems/EllipticCurveElGamal.py")
|
|
@@ -17,7 +18,13 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
17
18
|
Ref: https://sefiks.com/2018/08/21/elliptic-curve-elgamal-encryption/
|
|
18
19
|
"""
|
|
19
20
|
|
|
20
|
-
def __init__(
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
keys: Optional[dict] = None,
|
|
24
|
+
key_size: Optional[int] = None,
|
|
25
|
+
form: Optional[str] = None,
|
|
26
|
+
curve: Optional[str] = None,
|
|
27
|
+
):
|
|
21
28
|
"""
|
|
22
29
|
Args:
|
|
23
30
|
keys (dict): private - public key pair.
|
|
@@ -26,19 +33,26 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
26
33
|
this is equivalent to 1024 bit RSA.
|
|
27
34
|
form (str): specifies the elliptic curve form.
|
|
28
35
|
Options are 'weierstrass' (default), 'edwards'.
|
|
36
|
+
curve (str): specifies the elliptic curve to use.
|
|
37
|
+
Options:
|
|
38
|
+
- ed25519, ed448 for edwards form
|
|
39
|
+
- secp256k1 for weierstrass form
|
|
40
|
+
This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
|
|
29
41
|
"""
|
|
30
42
|
if form is None or form == "weierstrass":
|
|
31
|
-
self.curve = Weierstrass()
|
|
43
|
+
self.curve = Weierstrass(curve=curve)
|
|
32
44
|
elif form in "edwards":
|
|
33
|
-
self.curve = TwistedEdwards()
|
|
45
|
+
self.curve = TwistedEdwards(curve=curve)
|
|
46
|
+
elif form in "koblitz":
|
|
47
|
+
self.curve = Koblitz(curve=curve)
|
|
34
48
|
else:
|
|
35
49
|
raise ValueError(f"unimplemented curve form - {form}")
|
|
36
50
|
|
|
37
|
-
self.keys = keys or self.generate_keys(key_size or
|
|
51
|
+
self.keys = keys or self.generate_keys(key_size or self.curve.n.bit_length())
|
|
38
52
|
self.keys["public_key"]["form"] = form
|
|
39
53
|
self.keys["private_key"]["form"] = form
|
|
40
|
-
self.plaintext_modulo = self.curve.
|
|
41
|
-
self.ciphertext_modulo = self.curve.
|
|
54
|
+
self.plaintext_modulo = self.curve.modulo
|
|
55
|
+
self.ciphertext_modulo = self.curve.modulo
|
|
42
56
|
|
|
43
57
|
def generate_keys(self, key_size: int):
|
|
44
58
|
"""
|
|
@@ -54,9 +68,12 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
54
68
|
|
|
55
69
|
# private key
|
|
56
70
|
ka = random.getrandbits(key_size)
|
|
71
|
+
logger.debug(
|
|
72
|
+
f"{key_size} bit private key generated for Elliptic Curve ElGamal."
|
|
73
|
+
)
|
|
57
74
|
|
|
58
75
|
# public key
|
|
59
|
-
Qa = self.curve.double_and_add(G=self.curve.G, k=ka
|
|
76
|
+
Qa = self.curve.double_and_add(G=self.curve.G, k=ka)
|
|
60
77
|
|
|
61
78
|
keys["public_key"]["Qa"] = Qa
|
|
62
79
|
keys["private_key"]["ka"] = ka
|
|
@@ -69,7 +86,11 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
69
86
|
Returns:
|
|
70
87
|
random key (int): one time random key for encryption
|
|
71
88
|
"""
|
|
72
|
-
return random.getrandbits(128)
|
|
89
|
+
# 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
|
+
)
|
|
73
94
|
|
|
74
95
|
def encrypt(self, plaintext: int, random_key: Optional[int] = None) -> tuple:
|
|
75
96
|
"""
|
|
@@ -80,9 +101,6 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
80
101
|
Returns
|
|
81
102
|
ciphertext (tuple): c1 and c2
|
|
82
103
|
"""
|
|
83
|
-
# modulo
|
|
84
|
-
p = self.curve.p
|
|
85
|
-
|
|
86
104
|
# base point
|
|
87
105
|
G = self.curve.G
|
|
88
106
|
|
|
@@ -92,12 +110,12 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
92
110
|
# random key
|
|
93
111
|
r = random_key or self.generate_random_key()
|
|
94
112
|
|
|
95
|
-
s = self.curve.double_and_add(G=G, k=plaintext
|
|
113
|
+
s = self.curve.double_and_add(G=G, k=plaintext)
|
|
96
114
|
|
|
97
|
-
c1 = self.curve.double_and_add(G=G, k=r
|
|
115
|
+
c1 = self.curve.double_and_add(G=G, k=r)
|
|
98
116
|
|
|
99
|
-
c2 = self.curve.double_and_add(G=Qa, k=r
|
|
100
|
-
c2 = self.curve.add_points(c2, s
|
|
117
|
+
c2 = self.curve.double_and_add(G=Qa, k=r)
|
|
118
|
+
c2 = self.curve.add_points(c2, s)
|
|
101
119
|
|
|
102
120
|
return c1, c2
|
|
103
121
|
|
|
@@ -109,17 +127,14 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
109
127
|
Returns:
|
|
110
128
|
plaintext (int): restored message
|
|
111
129
|
"""
|
|
112
|
-
# modulo
|
|
113
|
-
p = self.curve.p
|
|
114
|
-
|
|
115
130
|
# private key
|
|
116
131
|
ka = self.keys["private_key"]["ka"]
|
|
117
132
|
|
|
118
133
|
c1, c2 = ciphertext
|
|
119
134
|
|
|
120
|
-
c1_prime = self.curve.negative_point(c1
|
|
121
|
-
s_prime = self.curve.double_and_add(G=c1_prime, k=ka
|
|
122
|
-
s_prime = self.curve.add_points(P=c2, Q=s_prime
|
|
135
|
+
c1_prime = self.curve.negative_point(c1)
|
|
136
|
+
s_prime = self.curve.double_and_add(G=c1_prime, k=ka)
|
|
137
|
+
s_prime = self.curve.add_points(P=c2, Q=s_prime)
|
|
123
138
|
|
|
124
139
|
# s_prime is a point on the elliptic curve
|
|
125
140
|
# s_prime = k x G
|
|
@@ -130,12 +145,14 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
130
145
|
G = self.curve.G
|
|
131
146
|
k = 2
|
|
132
147
|
while True:
|
|
133
|
-
G = self.curve.add_points(P=G, Q=self.curve.G
|
|
148
|
+
G = self.curve.add_points(P=G, Q=self.curve.G)
|
|
134
149
|
if G[0] == s_prime[0] and G[1] == s_prime[1]:
|
|
135
150
|
return k
|
|
136
151
|
k = k + 1
|
|
137
152
|
if k > self.curve.n:
|
|
138
|
-
raise ValueError(
|
|
153
|
+
raise ValueError(
|
|
154
|
+
f"Cannot restore scalar from {s_prime} = k x {self.curve.G}"
|
|
155
|
+
)
|
|
139
156
|
|
|
140
157
|
def multiply(self, ciphertext1: tuple, ciphertext2: tuple) -> tuple:
|
|
141
158
|
raise ValueError(
|
|
@@ -152,8 +169,8 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
152
169
|
Returns
|
|
153
170
|
ciphertext (dict): Elliptic Curve ElGamal ciphertext consisting of c1 and c2 keys
|
|
154
171
|
"""
|
|
155
|
-
a = self.curve.add_points(P=ciphertext1[0], Q=ciphertext2[0]
|
|
156
|
-
b = self.curve.add_points(P=ciphertext1[1], Q=ciphertext2[1]
|
|
172
|
+
a = self.curve.add_points(P=ciphertext1[0], Q=ciphertext2[0])
|
|
173
|
+
b = self.curve.add_points(P=ciphertext1[1], Q=ciphertext2[1])
|
|
157
174
|
return a, b
|
|
158
175
|
|
|
159
176
|
def xor(self, ciphertext1: tuple, ciphertext2: tuple) -> int:
|
|
@@ -173,8 +190,11 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
173
190
|
ciphertext (int): new ciphertext created with Elliptic Curve ElGamal
|
|
174
191
|
"""
|
|
175
192
|
return self.curve.double_and_add(
|
|
176
|
-
G=ciphertext[0],
|
|
177
|
-
|
|
193
|
+
G=ciphertext[0],
|
|
194
|
+
k=constant,
|
|
195
|
+
), self.curve.double_and_add(G=ciphertext[1], k=constant)
|
|
178
196
|
|
|
179
197
|
def reencrypt(self, ciphertext: tuple) -> tuple:
|
|
180
|
-
raise ValueError(
|
|
198
|
+
raise ValueError(
|
|
199
|
+
"Elliptic Curve ElGamal does not support regeneration of ciphertext"
|
|
200
|
+
)
|