lightphe 0.0.1__tar.gz → 0.0.3__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.1 → lightphe-0.0.3}/PKG-INFO +44 -14
- {lightphe-0.0.1 → lightphe-0.0.3}/README.md +43 -13
- lightphe-0.0.3/lightphe/__init__.py +230 -0
- lightphe-0.0.3/lightphe/commons/__init__.py +0 -0
- lightphe-0.0.3/lightphe/commons/logger.py +39 -0
- lightphe-0.0.3/lightphe/cryptosystems/Benaloh.py +208 -0
- lightphe-0.0.3/lightphe/cryptosystems/DamgardJurik.py +177 -0
- lightphe-0.0.3/lightphe/cryptosystems/ElGamal.py +202 -0
- lightphe-0.0.3/lightphe/cryptosystems/EllipticCurveElGamal.py +168 -0
- lightphe-0.0.3/lightphe/cryptosystems/GoldwasserMicali.py +166 -0
- lightphe-0.0.3/lightphe/cryptosystems/NaccacheStern.py +301 -0
- lightphe-0.0.3/lightphe/cryptosystems/OkamotoUchiyama.py +180 -0
- lightphe-0.0.3/lightphe/cryptosystems/Paillier.py +166 -0
- lightphe-0.0.3/lightphe/cryptosystems/RSA.py +142 -0
- lightphe-0.0.3/lightphe/cryptosystems/__init__.py +0 -0
- lightphe-0.0.3/lightphe/elliptic/Weierstrass.py +97 -0
- lightphe-0.0.3/lightphe/elliptic/__init__.py +0 -0
- lightphe-0.0.3/lightphe/models/Algorithm.py +12 -0
- lightphe-0.0.3/lightphe/models/Ciphertext.py +141 -0
- lightphe-0.0.3/lightphe/models/EllipticCurve.py +18 -0
- lightphe-0.0.3/lightphe/models/Homomorphic.py +53 -0
- lightphe-0.0.3/lightphe/models/__init__.py +0 -0
- {lightphe-0.0.1 → lightphe-0.0.3}/lightphe.egg-info/PKG-INFO +44 -14
- lightphe-0.0.3/lightphe.egg-info/SOURCES.txt +40 -0
- lightphe-0.0.3/lightphe.egg-info/top_level.txt +2 -0
- {lightphe-0.0.1 → lightphe-0.0.3}/setup.py +1 -1
- lightphe-0.0.3/tests/__init__.py +219 -0
- lightphe-0.0.3/tests/test_benaloh.py +63 -0
- lightphe-0.0.3/tests/test_cloud.py +44 -0
- lightphe-0.0.3/tests/test_damgard.py +63 -0
- lightphe-0.0.3/tests/test_elgamal.py +139 -0
- lightphe-0.0.3/tests/test_ellipticcurveelgamal.py +64 -0
- lightphe-0.0.3/tests/test_goldwasser.py +64 -0
- lightphe-0.0.3/tests/test_naccache.py +94 -0
- lightphe-0.0.3/tests/test_okamoto.py +61 -0
- lightphe-0.0.3/tests/test_paillier.py +97 -0
- lightphe-0.0.3/tests/test_rsa.py +65 -0
- lightphe-0.0.3/tests/test_salary.py +53 -0
- lightphe-0.0.1/lightphe.egg-info/SOURCES.txt +0 -8
- lightphe-0.0.1/lightphe.egg-info/top_level.txt +0 -1
- {lightphe-0.0.1 → lightphe-0.0.3}/LICENSE +0 -0
- {lightphe-0.0.1 → lightphe-0.0.3}/lightphe.egg-info/dependency_links.txt +0 -0
- {lightphe-0.0.1 → lightphe-0.0.3}/lightphe.egg-info/requires.txt +0 -0
- {lightphe-0.0.1 → lightphe-0.0.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lightphe
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
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
|
|
@@ -31,13 +31,21 @@ License-File: LICENSE
|
|
|
31
31
|
|
|
32
32
|
</div>
|
|
33
33
|
|
|
34
|
-
<p align="center"><img src="icons/phi.png" width="200" height="240"></p>
|
|
34
|
+
<p align="center"><img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/phi.png" width="200" height="240"></p>
|
|
35
35
|
|
|
36
36
|
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/).
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
# Partially vs Fully Homomorphic Encryption
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
Even though fully homomorphic encryption (FHE) has become available in recent times, but when considering the trade-offs, LightPHE emerges as a more efficient and practical choice. If your specific task doesn't demand the full homomorphic capabilities, opting for partial homomorphism with LightPHE is the logical decision.
|
|
41
|
+
|
|
42
|
+
- 🏎️ Notably faster
|
|
43
|
+
- 💻 Demands fewer computational resources
|
|
44
|
+
- 📏 Generating smaller ciphertexts
|
|
45
|
+
- 🧠 Well-suited for memory-constrained environments
|
|
46
|
+
- ⚖️ Strikes a favorable balance for practical use cases
|
|
47
|
+
|
|
48
|
+
# Installation [](https://pypi.org/project/lightphe/)
|
|
41
49
|
|
|
42
50
|
The easiest way to install the LightPHE package is to install it from python package index (PyPI).
|
|
43
51
|
|
|
@@ -118,18 +126,30 @@ m2 = 23
|
|
|
118
126
|
c1 = cs.encrypt(m1)
|
|
119
127
|
c2 = cs.encrypt(m2)
|
|
120
128
|
|
|
121
|
-
#
|
|
122
|
-
|
|
129
|
+
# homomorphic addition - private key is not required!
|
|
130
|
+
c3 = c1 + c2
|
|
131
|
+
|
|
132
|
+
# proof of work
|
|
133
|
+
assert cs.decrypt(c3) == m1 + m2
|
|
123
134
|
```
|
|
124
135
|
|
|
125
|
-
|
|
136
|
+
⚡ Notice that once can perform `c1 + c2` here without holding private key. However, just the data owner with private key can perform encryption and decryption. This is the basic definition of homomorphic encryption.
|
|
137
|
+
|
|
138
|
+
Besides, Paillier is supporting multiplying ciphertexts by a known plain constant. Simply put, decryption of scalar multiplication of ciphertext is equivalent to that constant times plaintext as well.
|
|
126
139
|
|
|
127
140
|
```python
|
|
128
|
-
#
|
|
129
|
-
k =
|
|
130
|
-
|
|
141
|
+
# increasing something 5%
|
|
142
|
+
k = 1.05
|
|
143
|
+
|
|
144
|
+
# scalar multiplication - private key is not required!
|
|
145
|
+
c4 = k * c1
|
|
146
|
+
|
|
147
|
+
# proof of work
|
|
148
|
+
assert cs.decrypt(c4) == k * m1
|
|
131
149
|
```
|
|
132
150
|
|
|
151
|
+
⚡ Herein, `k * c1` operation can be performed by anyone without holding private key.
|
|
152
|
+
|
|
133
153
|
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.
|
|
134
154
|
|
|
135
155
|
```python
|
|
@@ -142,11 +162,11 @@ assert cs.decrypt(c1) == m1
|
|
|
142
162
|
Finally, if you try to perform an operation that algorithm does not support, then an exception will be thrown. For instance, Paillier is not homomorphic with respect to the multiplication or xor. To put it simply, you cannot multiply two ciphertexts. If you enforce this calculation, you will have an exception.
|
|
143
163
|
|
|
144
164
|
```python
|
|
145
|
-
# pailier is not homomorphic
|
|
165
|
+
# pailier is not multiplicatively homomorphic
|
|
146
166
|
with pytest.raises(ValueError, match="Paillier is not homomorphic with respect to the multiplication"):
|
|
147
167
|
c1 * c2
|
|
148
168
|
|
|
149
|
-
# pailier is not homomorphic
|
|
169
|
+
# pailier is not exclusively homomorphic
|
|
150
170
|
with pytest.raises(ValueError, match="Paillier is not homomorphic with respect to the exclusive or"):
|
|
151
171
|
c1 ^ c2
|
|
152
172
|
```
|
|
@@ -159,6 +179,16 @@ All PRs are more than welcome! If you are planning to contribute a large patch,
|
|
|
159
179
|
|
|
160
180
|
You should be able run `make test` and `make lint` commands successfully before committing. Once a PR is created, GitHub test workflow will be run automatically and unit test results will be available in [GitHub actions](https://github.com/serengil/LightPHE/actions/workflows/tests.yml) before approval. Besides, workflow will evaluate the code with pylint as well.
|
|
161
181
|
|
|
182
|
+
# Support
|
|
183
|
+
|
|
184
|
+
There are many ways to support a project - starring⭐️ the GitHub repo is just one 🙏
|
|
185
|
+
|
|
186
|
+
You can also support this work on [Patreon](https://www.patreon.com/serengil?repo=lightphe) or [GitHub Sponsors](https://github.com/sponsors/serengil).
|
|
187
|
+
|
|
188
|
+
<a href="https://www.patreon.com/serengil?repo=lightphe">
|
|
189
|
+
<img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/patreon.png" width="30%" height="30%">
|
|
190
|
+
</a>
|
|
191
|
+
|
|
162
192
|
# Citation
|
|
163
193
|
|
|
164
194
|
Please cite LightPHE in your publications if it helps your research. Here is its BibTex entry:
|
|
@@ -173,10 +203,10 @@ Please cite LightPHE in your publications if it helps your research. Here is its
|
|
|
173
203
|
}
|
|
174
204
|
```
|
|
175
205
|
|
|
176
|
-
Also, if you use LightPHE in your projects, please add `
|
|
206
|
+
Also, if you use LightPHE in your projects, please add `lightphe` in the `requirements.txt`.
|
|
177
207
|
|
|
178
208
|
# License
|
|
179
209
|
|
|
180
|
-
LightPHE is licensed under the MIT License - see [`LICENSE`](LICENSE) for more details.
|
|
210
|
+
LightPHE is licensed under the MIT License - see [`LICENSE`](https://github.com/serengil/LightPHE/blob/master/LICENSE) for more details.
|
|
181
211
|
|
|
182
212
|
|
|
@@ -15,13 +15,21 @@
|
|
|
15
15
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
-
<p align="center"><img src="icons/phi.png" width="200" height="240"></p>
|
|
18
|
+
<p align="center"><img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/phi.png" width="200" height="240"></p>
|
|
19
19
|
|
|
20
20
|
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/).
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
# Partially vs Fully Homomorphic Encryption
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Even though fully homomorphic encryption (FHE) has become available in recent times, but when considering the trade-offs, LightPHE emerges as a more efficient and practical choice. If your specific task doesn't demand the full homomorphic capabilities, opting for partial homomorphism with LightPHE is the logical decision.
|
|
25
|
+
|
|
26
|
+
- 🏎️ Notably faster
|
|
27
|
+
- 💻 Demands fewer computational resources
|
|
28
|
+
- 📏 Generating smaller ciphertexts
|
|
29
|
+
- 🧠 Well-suited for memory-constrained environments
|
|
30
|
+
- ⚖️ Strikes a favorable balance for practical use cases
|
|
31
|
+
|
|
32
|
+
# Installation [](https://pypi.org/project/lightphe/)
|
|
25
33
|
|
|
26
34
|
The easiest way to install the LightPHE package is to install it from python package index (PyPI).
|
|
27
35
|
|
|
@@ -102,18 +110,30 @@ m2 = 23
|
|
|
102
110
|
c1 = cs.encrypt(m1)
|
|
103
111
|
c2 = cs.encrypt(m2)
|
|
104
112
|
|
|
105
|
-
#
|
|
106
|
-
|
|
113
|
+
# homomorphic addition - private key is not required!
|
|
114
|
+
c3 = c1 + c2
|
|
115
|
+
|
|
116
|
+
# proof of work
|
|
117
|
+
assert cs.decrypt(c3) == m1 + m2
|
|
107
118
|
```
|
|
108
119
|
|
|
109
|
-
|
|
120
|
+
⚡ Notice that once can perform `c1 + c2` here without holding private key. However, just the data owner with private key can perform encryption and decryption. This is the basic definition of homomorphic encryption.
|
|
121
|
+
|
|
122
|
+
Besides, Paillier is supporting multiplying ciphertexts by a known plain constant. Simply put, decryption of scalar multiplication of ciphertext is equivalent to that constant times plaintext as well.
|
|
110
123
|
|
|
111
124
|
```python
|
|
112
|
-
#
|
|
113
|
-
k =
|
|
114
|
-
|
|
125
|
+
# increasing something 5%
|
|
126
|
+
k = 1.05
|
|
127
|
+
|
|
128
|
+
# scalar multiplication - private key is not required!
|
|
129
|
+
c4 = k * c1
|
|
130
|
+
|
|
131
|
+
# proof of work
|
|
132
|
+
assert cs.decrypt(c4) == k * m1
|
|
115
133
|
```
|
|
116
134
|
|
|
135
|
+
⚡ Herein, `k * c1` operation can be performed by anyone without holding private key.
|
|
136
|
+
|
|
117
137
|
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.
|
|
118
138
|
|
|
119
139
|
```python
|
|
@@ -126,11 +146,11 @@ assert cs.decrypt(c1) == m1
|
|
|
126
146
|
Finally, if you try to perform an operation that algorithm does not support, then an exception will be thrown. For instance, Paillier is not homomorphic with respect to the multiplication or xor. To put it simply, you cannot multiply two ciphertexts. If you enforce this calculation, you will have an exception.
|
|
127
147
|
|
|
128
148
|
```python
|
|
129
|
-
# pailier is not homomorphic
|
|
149
|
+
# pailier is not multiplicatively homomorphic
|
|
130
150
|
with pytest.raises(ValueError, match="Paillier is not homomorphic with respect to the multiplication"):
|
|
131
151
|
c1 * c2
|
|
132
152
|
|
|
133
|
-
# pailier is not homomorphic
|
|
153
|
+
# pailier is not exclusively homomorphic
|
|
134
154
|
with pytest.raises(ValueError, match="Paillier is not homomorphic with respect to the exclusive or"):
|
|
135
155
|
c1 ^ c2
|
|
136
156
|
```
|
|
@@ -143,6 +163,16 @@ All PRs are more than welcome! If you are planning to contribute a large patch,
|
|
|
143
163
|
|
|
144
164
|
You should be able run `make test` and `make lint` commands successfully before committing. Once a PR is created, GitHub test workflow will be run automatically and unit test results will be available in [GitHub actions](https://github.com/serengil/LightPHE/actions/workflows/tests.yml) before approval. Besides, workflow will evaluate the code with pylint as well.
|
|
145
165
|
|
|
166
|
+
# Support
|
|
167
|
+
|
|
168
|
+
There are many ways to support a project - starring⭐️ the GitHub repo is just one 🙏
|
|
169
|
+
|
|
170
|
+
You can also support this work on [Patreon](https://www.patreon.com/serengil?repo=lightphe) or [GitHub Sponsors](https://github.com/sponsors/serengil).
|
|
171
|
+
|
|
172
|
+
<a href="https://www.patreon.com/serengil?repo=lightphe">
|
|
173
|
+
<img src="https://raw.githubusercontent.com/serengil/LightPHE/master/icons/patreon.png" width="30%" height="30%">
|
|
174
|
+
</a>
|
|
175
|
+
|
|
146
176
|
# Citation
|
|
147
177
|
|
|
148
178
|
Please cite LightPHE in your publications if it helps your research. Here is its BibTex entry:
|
|
@@ -157,8 +187,8 @@ Please cite LightPHE in your publications if it helps your research. Here is its
|
|
|
157
187
|
}
|
|
158
188
|
```
|
|
159
189
|
|
|
160
|
-
Also, if you use LightPHE in your projects, please add `
|
|
190
|
+
Also, if you use LightPHE in your projects, please add `lightphe` in the `requirements.txt`.
|
|
161
191
|
|
|
162
192
|
# License
|
|
163
193
|
|
|
164
|
-
LightPHE is licensed under the MIT License - see [`LICENSE`](LICENSE) for more details.
|
|
194
|
+
LightPHE is licensed under the MIT License - see [`LICENSE`](https://github.com/serengil/LightPHE/blob/master/LICENSE) for more details.
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Optional, Union
|
|
3
|
+
|
|
4
|
+
from lightphe.models.Ciphertext import Ciphertext
|
|
5
|
+
from lightphe.models.Algorithm import Algorithm
|
|
6
|
+
from lightphe.cryptosystems.RSA import RSA
|
|
7
|
+
from lightphe.cryptosystems.ElGamal import ElGamal
|
|
8
|
+
from lightphe.cryptosystems.Paillier import Paillier
|
|
9
|
+
from lightphe.cryptosystems.DamgardJurik import DamgardJurik
|
|
10
|
+
from lightphe.cryptosystems.OkamotoUchiyama import OkamotoUchiyama
|
|
11
|
+
from lightphe.cryptosystems.Benaloh import Benaloh
|
|
12
|
+
from lightphe.cryptosystems.NaccacheStern import NaccacheStern
|
|
13
|
+
from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali
|
|
14
|
+
from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal
|
|
15
|
+
from lightphe.commons.logger import Logger
|
|
16
|
+
|
|
17
|
+
# pylint: disable=eval-used
|
|
18
|
+
|
|
19
|
+
logger = Logger()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LightPHE:
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
algorithm_name: str,
|
|
26
|
+
keys: Optional[dict] = None,
|
|
27
|
+
key_file: Optional[str] = None,
|
|
28
|
+
key_size: Optional[int] = None,
|
|
29
|
+
):
|
|
30
|
+
"""
|
|
31
|
+
Build LightPHE class
|
|
32
|
+
Args:
|
|
33
|
+
algorithm_name (str): RSA | ElGamal | Exponential-ElGamal | EllipticCurve-ElGamal
|
|
34
|
+
| Paillier | Damgard-Jurik | Okamoto-Uchiyama | Benaloh | Naccache-Stern
|
|
35
|
+
| Goldwasser-Micali
|
|
36
|
+
keys (dict): optional private-public key pair
|
|
37
|
+
key_file (str): if keys are exported, you can load them into cryptosystem
|
|
38
|
+
key_size (int): key size in bits
|
|
39
|
+
"""
|
|
40
|
+
self.algorithm_name = algorithm_name
|
|
41
|
+
|
|
42
|
+
if key_file is not None:
|
|
43
|
+
keys = self.restore_keys(target_file=key_file)
|
|
44
|
+
|
|
45
|
+
self.cs = self.build_cryptosystem(
|
|
46
|
+
algorithm_name=algorithm_name, keys=keys, key_size=key_size
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def build_cryptosystem(
|
|
50
|
+
self,
|
|
51
|
+
algorithm_name: str,
|
|
52
|
+
keys: Optional[dict] = None,
|
|
53
|
+
key_size: Optional[int] = None,
|
|
54
|
+
) -> Union[
|
|
55
|
+
RSA,
|
|
56
|
+
ElGamal,
|
|
57
|
+
Paillier,
|
|
58
|
+
DamgardJurik,
|
|
59
|
+
OkamotoUchiyama,
|
|
60
|
+
Benaloh,
|
|
61
|
+
NaccacheStern,
|
|
62
|
+
EllipticCurveElGamal,
|
|
63
|
+
]:
|
|
64
|
+
"""
|
|
65
|
+
Build a cryptosystem among partially homomorphic algorithms
|
|
66
|
+
Args:
|
|
67
|
+
algorithm_name (str): RSA | ElGamal | Exponential-ElGamal | EllipticCurve-ElGamal
|
|
68
|
+
| Paillier | Damgard-Jurik | Okamoto-Uchiyama | Benaloh | Naccache-Stern
|
|
69
|
+
| Goldwasser-Micali
|
|
70
|
+
keys (dict): optional private-public key pair
|
|
71
|
+
key_file (str): if keys are exported, you can load them into cryptosystem
|
|
72
|
+
key_size (int): key size in bits
|
|
73
|
+
Returns
|
|
74
|
+
cryptosystem
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
if key_size is None:
|
|
78
|
+
key_size = self.recommend_key_size(algorithm_name=algorithm_name)
|
|
79
|
+
|
|
80
|
+
# build cryptosystem
|
|
81
|
+
if algorithm_name == Algorithm.RSA:
|
|
82
|
+
cs = RSA(keys=keys, key_size=key_size)
|
|
83
|
+
elif algorithm_name == Algorithm.ElGamal:
|
|
84
|
+
cs = ElGamal(keys=keys, key_size=key_size)
|
|
85
|
+
elif algorithm_name == Algorithm.ExponentialElGamal:
|
|
86
|
+
cs = ElGamal(keys=keys, key_size=key_size, exponential=True)
|
|
87
|
+
elif algorithm_name == Algorithm.EllipticCurveElGamal:
|
|
88
|
+
cs = EllipticCurveElGamal(keys=keys, key_size=key_size)
|
|
89
|
+
elif algorithm_name == Algorithm.Paillier:
|
|
90
|
+
cs = Paillier(keys=keys, key_size=key_size)
|
|
91
|
+
elif algorithm_name == Algorithm.DamgardJurik:
|
|
92
|
+
cs = DamgardJurik(keys=keys, key_size=key_size)
|
|
93
|
+
elif algorithm_name == Algorithm.OkamotoUchiyama:
|
|
94
|
+
cs = OkamotoUchiyama(keys=keys, key_size=key_size)
|
|
95
|
+
elif algorithm_name == Algorithm.Benaloh:
|
|
96
|
+
cs = Benaloh(keys=keys, key_size=key_size)
|
|
97
|
+
elif algorithm_name == Algorithm.NaccacheStern:
|
|
98
|
+
cs = NaccacheStern(keys=keys, key_size=key_size)
|
|
99
|
+
elif algorithm_name == Algorithm.GoldwasserMicali:
|
|
100
|
+
cs = GoldwasserMicali(keys=keys, key_size=key_size)
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError(f"unimplemented algorithm - {algorithm_name}")
|
|
103
|
+
return cs
|
|
104
|
+
|
|
105
|
+
def encrypt(self, plaintext: int) -> Ciphertext:
|
|
106
|
+
"""
|
|
107
|
+
Encrypt a plaintext with a built cryptosystem
|
|
108
|
+
Args:
|
|
109
|
+
plaintext (int): message
|
|
110
|
+
Returns
|
|
111
|
+
ciphertext (from lightphe.models.Ciphertext import Ciphertext): encrypted message
|
|
112
|
+
"""
|
|
113
|
+
ciphertext = self.cs.encrypt(plaintext=plaintext)
|
|
114
|
+
return Ciphertext(algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext)
|
|
115
|
+
|
|
116
|
+
def decrypt(self, ciphertext: Ciphertext) -> int:
|
|
117
|
+
"""
|
|
118
|
+
Decrypt a ciphertext with a buit cryptosystem
|
|
119
|
+
Args:
|
|
120
|
+
ciphertext (from lightphe.models.Ciphertext import Ciphertext): encrypted message
|
|
121
|
+
Returns:
|
|
122
|
+
plaintext (int): restored message
|
|
123
|
+
"""
|
|
124
|
+
return self.cs.decrypt(ciphertext=ciphertext.value)
|
|
125
|
+
|
|
126
|
+
def regenerate_ciphertext(self, ciphertext: Ciphertext) -> Ciphertext:
|
|
127
|
+
"""
|
|
128
|
+
Generate a different ciphertext belonging to same plaintext
|
|
129
|
+
Args:
|
|
130
|
+
ciphertext (from lightphe.models.Ciphertext import Ciphertext): encrypted message
|
|
131
|
+
Returns:
|
|
132
|
+
ciphertext (from lightphe.models.Ciphertext import Ciphertext): encrypted message
|
|
133
|
+
"""
|
|
134
|
+
ciphertext_new = self.cs.reencrypt(ciphertext=ciphertext.value)
|
|
135
|
+
return Ciphertext(
|
|
136
|
+
algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext_new
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def export_keys(self, target_file: str, public: bool = False) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Export keys to a file
|
|
142
|
+
Args:
|
|
143
|
+
target_file (str): target file name
|
|
144
|
+
public (bool): set this to True if you will publish this
|
|
145
|
+
to publicly.
|
|
146
|
+
"""
|
|
147
|
+
keys = self.cs.keys
|
|
148
|
+
if public is True and keys.get("private_key") is not None:
|
|
149
|
+
del keys["private_key"]
|
|
150
|
+
|
|
151
|
+
if public is False:
|
|
152
|
+
logger.warn(
|
|
153
|
+
"You did not set public arg to True. So, exported key has private key information."
|
|
154
|
+
"Do not share this to anyone"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
with open(target_file, "w", encoding="UTF-8") as file:
|
|
158
|
+
file.write(json.dumps(keys))
|
|
159
|
+
|
|
160
|
+
def restore_keys(self, target_file: str) -> dict:
|
|
161
|
+
"""
|
|
162
|
+
Restore keys from a file
|
|
163
|
+
Args:
|
|
164
|
+
target_file (str): target file name
|
|
165
|
+
Returns:
|
|
166
|
+
keys (dict): private public key pair
|
|
167
|
+
"""
|
|
168
|
+
with open(target_file, "r", encoding="UTF-8") as file:
|
|
169
|
+
dict_str = file.read()
|
|
170
|
+
|
|
171
|
+
keys = eval(dict_str)
|
|
172
|
+
if not isinstance(keys, dict):
|
|
173
|
+
raise ValueError(
|
|
174
|
+
f"The content of the file {target_file} does not represent a valid dictionary."
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if "private_key" in keys.keys():
|
|
178
|
+
logger.info(f"private-public key pair is restored from {target_file}")
|
|
179
|
+
elif "public_key" in keys.keys():
|
|
180
|
+
logger.info(f"public key is restored from {target_file}")
|
|
181
|
+
else:
|
|
182
|
+
raise ValueError(f"File {target_file} must have public_key key")
|
|
183
|
+
return keys
|
|
184
|
+
|
|
185
|
+
def recommend_key_size(self, algorithm_name: str) -> int:
|
|
186
|
+
"""
|
|
187
|
+
Recommend a key size in bits if it is not mentioned by the user
|
|
188
|
+
Args:
|
|
189
|
+
algorithm_name (str): algorithm name
|
|
190
|
+
Returns
|
|
191
|
+
key_size (int)
|
|
192
|
+
"""
|
|
193
|
+
if algorithm_name == Algorithm.RSA:
|
|
194
|
+
key_size = 1024
|
|
195
|
+
elif algorithm_name == Algorithm.ElGamal:
|
|
196
|
+
key_size = 1024
|
|
197
|
+
elif algorithm_name == Algorithm.ExponentialElGamal:
|
|
198
|
+
key_size = 1024
|
|
199
|
+
# 160-bit ECC is equivalent to 1024-bit RSA
|
|
200
|
+
elif algorithm_name == Algorithm.EllipticCurveElGamal:
|
|
201
|
+
key_size = 160
|
|
202
|
+
elif algorithm_name == Algorithm.Paillier:
|
|
203
|
+
key_size = 1024
|
|
204
|
+
elif algorithm_name == Algorithm.DamgardJurik:
|
|
205
|
+
key_size = 1024
|
|
206
|
+
elif algorithm_name == Algorithm.OkamotoUchiyama:
|
|
207
|
+
key_size = 1024
|
|
208
|
+
# n should be several hundred bits or more
|
|
209
|
+
elif algorithm_name == Algorithm.GoldwasserMicali:
|
|
210
|
+
key_size = 100
|
|
211
|
+
# Benaloh and Naccache-Stern require to solve DLP in decryption
|
|
212
|
+
# so small key is recommended
|
|
213
|
+
elif algorithm_name == Algorithm.Benaloh:
|
|
214
|
+
key_size = 50
|
|
215
|
+
elif algorithm_name == Algorithm.NaccacheStern:
|
|
216
|
+
key_size = 37
|
|
217
|
+
else:
|
|
218
|
+
raise ValueError(f"unimplemented algorithm - {algorithm_name}")
|
|
219
|
+
return key_size
|
|
220
|
+
|
|
221
|
+
def create_ciphertext_obj(self, ciphertext: Union[int, tuple, list]) -> Ciphertext:
|
|
222
|
+
"""
|
|
223
|
+
Ciphertext objects have keys in addition ciphertext itself to perform
|
|
224
|
+
homomorphic operations.
|
|
225
|
+
Args:
|
|
226
|
+
ciphertext (int or tuple or list): ciphertext content
|
|
227
|
+
Returns:
|
|
228
|
+
Ciphertext
|
|
229
|
+
"""
|
|
230
|
+
return Ciphertext(algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext)
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Logger:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
log_level = os.environ.get("LIGHTPHE_LOG_LEVEL", str(logging.INFO))
|
|
9
|
+
try:
|
|
10
|
+
self.log_level = int(log_level)
|
|
11
|
+
except Exception as err:
|
|
12
|
+
self.dump_log(
|
|
13
|
+
f"Exception while parsing $LIGHTPHE_LOG_LEVEL."
|
|
14
|
+
f"Expected int but it is {log_level} ({str(err)})"
|
|
15
|
+
)
|
|
16
|
+
self.log_level = logging.INFO
|
|
17
|
+
|
|
18
|
+
def debug(self, message):
|
|
19
|
+
if self.log_level <= logging.DEBUG:
|
|
20
|
+
self.dump_log(message)
|
|
21
|
+
|
|
22
|
+
def info(self, message):
|
|
23
|
+
if self.log_level <= logging.INFO:
|
|
24
|
+
self.dump_log(message)
|
|
25
|
+
|
|
26
|
+
def warn(self, message):
|
|
27
|
+
if self.log_level <= logging.WARNING:
|
|
28
|
+
self.dump_log(message)
|
|
29
|
+
|
|
30
|
+
def error(self, message):
|
|
31
|
+
if self.log_level <= logging.ERROR:
|
|
32
|
+
self.dump_log(message)
|
|
33
|
+
|
|
34
|
+
def critical(self, message):
|
|
35
|
+
if self.log_level <= logging.CRITICAL:
|
|
36
|
+
self.dump_log(message)
|
|
37
|
+
|
|
38
|
+
def dump_log(self, message):
|
|
39
|
+
print(f"{str(datetime.now())[2:-7]} - {message}")
|