lightphe 0.0.4__tar.gz → 0.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {lightphe-0.0.4 → lightphe-0.0.6}/PKG-INFO +49 -3
  2. {lightphe-0.0.4 → lightphe-0.0.6}/README.md +48 -2
  3. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/__init__.py +114 -12
  4. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/commons/logger.py +2 -1
  5. lightphe-0.0.6/lightphe/commons/phe_utils.py +58 -0
  6. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/cryptosystems/Benaloh.py +1 -1
  7. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/cryptosystems/DamgardJurik.py +1 -1
  8. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/cryptosystems/ElGamal.py +3 -2
  9. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/cryptosystems/EllipticCurveElGamal.py +3 -2
  10. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/cryptosystems/GoldwasserMicali.py +3 -1
  11. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/cryptosystems/NaccacheStern.py +1 -1
  12. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/cryptosystems/OkamotoUchiyama.py +11 -8
  13. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/cryptosystems/Paillier.py +1 -1
  14. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/cryptosystems/RSA.py +3 -2
  15. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/elliptic/Weierstrass.py +1 -0
  16. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/models/Ciphertext.py +13 -13
  17. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/models/Homomorphic.py +5 -6
  18. lightphe-0.0.6/lightphe/models/Tensor.py +186 -0
  19. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe.egg-info/PKG-INFO +49 -3
  20. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe.egg-info/SOURCES.txt +6 -2
  21. lightphe-0.0.6/requirements.txt +2 -0
  22. {lightphe-0.0.4 → lightphe-0.0.6}/setup.py +2 -1
  23. {lightphe-0.0.4 → lightphe-0.0.6}/tests/__init__.py +1 -1
  24. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_benaloh.py +1 -1
  25. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_cloud.py +19 -10
  26. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_damgard.py +1 -1
  27. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_elgamal.py +8 -8
  28. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_ellipticcurveelgamal.py +1 -1
  29. lightphe-0.0.6/tests/test_exporting_keys.py +26 -0
  30. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_goldwasser.py +1 -1
  31. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_naccache.py +1 -1
  32. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_okamoto.py +1 -1
  33. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_paillier.py +1 -4
  34. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_rsa.py +4 -4
  35. {lightphe-0.0.4 → lightphe-0.0.6}/tests/test_salary.py +1 -1
  36. lightphe-0.0.6/tests/test_tensors.py +174 -0
  37. lightphe-0.0.4/lightphe/commons/calculations.py +0 -23
  38. {lightphe-0.0.4 → lightphe-0.0.6}/LICENSE +0 -0
  39. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/commons/__init__.py +0 -0
  40. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/cryptosystems/__init__.py +0 -0
  41. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/elliptic/__init__.py +0 -0
  42. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/models/Algorithm.py +0 -0
  43. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/models/EllipticCurve.py +0 -0
  44. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe/models/__init__.py +0 -0
  45. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe.egg-info/dependency_links.txt +0 -0
  46. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe.egg-info/requires.txt +0 -0
  47. {lightphe-0.0.4 → lightphe-0.0.6}/lightphe.egg-info/top_level.txt +0 -0
  48. {lightphe-0.0.4 → lightphe-0.0.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightphe
3
- Version: 0.0.4
3
+ Version: 0.0.6
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
@@ -19,7 +19,7 @@ License-File: LICENSE
19
19
  <div align="center">
20
20
 
21
21
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/lightphe?period=total&units=international_system&left_color=grey&right_color=blue&left_text=pypi%20downloads)](https://pepy.tech/project/lightphe)
22
- [![Stars](https://img.shields.io/github/stars/serengil/LightPHE?color=yellow&style=flat)](https://github.com/serengil/LightPHE/stargazers)
22
+ [![Stars](https://img.shields.io/github/stars/serengil/LightPHE?color=yellow&style=flat&label=%E2%AD%90%20stars)](https://github.com/serengil/LightPHE/stargazers)
23
23
  [![Tests](https://github.com/serengil/LightPHE/actions/workflows/tests.yml/badge.svg)](https://github.com/serengil/LightPHE/actions/workflows/tests.yml)
24
24
  [![License](http://img.shields.io/:license-MIT-green.svg?style=flat)](https://github.com/serengil/LightPHE/blob/master/LICENSE)
25
25
  [![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dserengil%26type%3Dpatrons&style=flat)](https://www.patreon.com/serengil?repo=lightphe)
@@ -41,7 +41,8 @@ Even though fully homomorphic encryption (FHE) has become available in recent ti
41
41
 
42
42
  - 🏎️ Notably faster
43
43
  - 💻 Demands fewer computational resources
44
- - 📏 Generating smaller ciphertexts
44
+ - 📏 Generating much smaller ciphertexts
45
+ - 🔑 Distributing much smaller keys
45
46
  - 🧠 Well-suited for memory-constrained environments
46
47
  - ⚖️ Strikes a favorable balance for practical use cases
47
48
 
@@ -173,6 +174,51 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
173
174
 
174
175
  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.
175
176
 
177
+ # Working with vectors
178
+
179
+ You can encrypt the output vectors of machine learning models with LightPHE. These encrypted tensors come with homomorphic operation support including homomorphic addition, element-wise multiplication and scalar multiplication.
180
+
181
+ ```python
182
+ # build an additively homomorphic cryptosystem
183
+ cs = LightPHE(algorithm_name="Paillier")
184
+
185
+ # define plain tensors
186
+ t1 = [1.005, 2.05, 3.5, 4]
187
+ t2 = [5, 6.2, 7.002, 8.02]
188
+
189
+ # encrypt tensors
190
+ c1 = cs.encrypt(t1)
191
+ c2 = cs.encrypt(t2)
192
+
193
+ # perform homomorphic addition
194
+ c3 = c1 + c2
195
+
196
+ # perform homomorphic element-wise multiplication
197
+ c4 = c1 * c2
198
+
199
+ # perform homomorphic scalar multiplication
200
+ k = 5
201
+ c5 = k * c1
202
+
203
+ # decrypt the addition tensor
204
+ t3 = cs.decrypt(c3)
205
+
206
+ # decrypt the element-wise multiplied tensor
207
+ t4 = cs.decrypt(c4)
208
+
209
+ # decrypt the scalar multiplied tensor
210
+ t5 = cs.decrypt(c5)
211
+
212
+ # data validations
213
+ threshold = 0.5
214
+ for i in range(0, len(t1)):
215
+ assert abs((t1[i] + t2[i]) - t3[i]) < threshold
216
+ assert abs((t1[i] * t2[i]) - t4[i]) < threshold
217
+ assert abs((t1[i] * k) - t5[i]) < threshold
218
+ ```
219
+
220
+ Unfortunately, vector multiplication (dot product) requires both homomorphic addition and homomorphic multiplication and this cannot be done with partially homomorphic encryption algorithms.
221
+
176
222
  # Contributing
177
223
 
178
224
  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.
@@ -3,7 +3,7 @@
3
3
  <div align="center">
4
4
 
5
5
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/lightphe?period=total&units=international_system&left_color=grey&right_color=blue&left_text=pypi%20downloads)](https://pepy.tech/project/lightphe)
6
- [![Stars](https://img.shields.io/github/stars/serengil/LightPHE?color=yellow&style=flat)](https://github.com/serengil/LightPHE/stargazers)
6
+ [![Stars](https://img.shields.io/github/stars/serengil/LightPHE?color=yellow&style=flat&label=%E2%AD%90%20stars)](https://github.com/serengil/LightPHE/stargazers)
7
7
  [![Tests](https://github.com/serengil/LightPHE/actions/workflows/tests.yml/badge.svg)](https://github.com/serengil/LightPHE/actions/workflows/tests.yml)
8
8
  [![License](http://img.shields.io/:license-MIT-green.svg?style=flat)](https://github.com/serengil/LightPHE/blob/master/LICENSE)
9
9
  [![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dserengil%26type%3Dpatrons&style=flat)](https://www.patreon.com/serengil?repo=lightphe)
@@ -25,7 +25,8 @@ Even though fully homomorphic encryption (FHE) has become available in recent ti
25
25
 
26
26
  - 🏎️ Notably faster
27
27
  - 💻 Demands fewer computational resources
28
- - 📏 Generating smaller ciphertexts
28
+ - 📏 Generating much smaller ciphertexts
29
+ - 🔑 Distributing much smaller keys
29
30
  - 🧠 Well-suited for memory-constrained environments
30
31
  - ⚖️ Strikes a favorable balance for practical use cases
31
32
 
@@ -157,6 +158,51 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
157
158
 
158
159
  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.
159
160
 
161
+ # Working with vectors
162
+
163
+ You can encrypt the output vectors of machine learning models with LightPHE. These encrypted tensors come with homomorphic operation support including homomorphic addition, element-wise multiplication and scalar multiplication.
164
+
165
+ ```python
166
+ # build an additively homomorphic cryptosystem
167
+ cs = LightPHE(algorithm_name="Paillier")
168
+
169
+ # define plain tensors
170
+ t1 = [1.005, 2.05, 3.5, 4]
171
+ t2 = [5, 6.2, 7.002, 8.02]
172
+
173
+ # encrypt tensors
174
+ c1 = cs.encrypt(t1)
175
+ c2 = cs.encrypt(t2)
176
+
177
+ # perform homomorphic addition
178
+ c3 = c1 + c2
179
+
180
+ # perform homomorphic element-wise multiplication
181
+ c4 = c1 * c2
182
+
183
+ # perform homomorphic scalar multiplication
184
+ k = 5
185
+ c5 = k * c1
186
+
187
+ # decrypt the addition tensor
188
+ t3 = cs.decrypt(c3)
189
+
190
+ # decrypt the element-wise multiplied tensor
191
+ t4 = cs.decrypt(c4)
192
+
193
+ # decrypt the scalar multiplied tensor
194
+ t5 = cs.decrypt(c5)
195
+
196
+ # data validations
197
+ threshold = 0.5
198
+ for i in range(0, len(t1)):
199
+ assert abs((t1[i] + t2[i]) - t3[i]) < threshold
200
+ assert abs((t1[i] * t2[i]) - t4[i]) < threshold
201
+ assert abs((t1[i] * k) - t5[i]) < threshold
202
+ ```
203
+
204
+ Unfortunately, vector multiplication (dot product) requires both homomorphic addition and homomorphic multiplication and this cannot be done with partially homomorphic encryption algorithms.
205
+
160
206
  # Contributing
161
207
 
162
208
  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.
@@ -1,8 +1,10 @@
1
1
  import json
2
- from typing import Optional, Union
2
+ from typing import Optional, Union, List
3
3
 
4
+ from lightphe.models.Homomorphic import Homomorphic
4
5
  from lightphe.models.Ciphertext import Ciphertext
5
6
  from lightphe.models.Algorithm import Algorithm
7
+ from lightphe.models.Tensor import Fraction, EncryptedTensor
6
8
  from lightphe.cryptosystems.RSA import RSA
7
9
  from lightphe.cryptosystems.ElGamal import ElGamal
8
10
  from lightphe.cryptosystems.Paillier import Paillier
@@ -12,12 +14,12 @@ from lightphe.cryptosystems.Benaloh import Benaloh
12
14
  from lightphe.cryptosystems.NaccacheStern import NaccacheStern
13
15
  from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali
14
16
  from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal
15
- from lightphe.commons import calculations
17
+ from lightphe.commons import phe_utils
16
18
  from lightphe.commons.logger import Logger
17
19
 
18
- # pylint: disable=eval-used
20
+ # pylint: disable=eval-used, simplifiable-if-expression
19
21
 
20
- logger = Logger()
22
+ logger = Logger(module="lightphe/__init__.py")
21
23
 
22
24
 
23
25
  class LightPHE:
@@ -27,6 +29,7 @@ class LightPHE:
27
29
  keys: Optional[dict] = None,
28
30
  key_file: Optional[str] = None,
29
31
  key_size: Optional[int] = None,
32
+ precision: int = 5,
30
33
  ):
31
34
  """
32
35
  Build LightPHE class
@@ -37,17 +40,19 @@ class LightPHE:
37
40
  keys (dict): optional private-public key pair
38
41
  key_file (str): if keys are exported, you can load them into cryptosystem
39
42
  key_size (int): key size in bits
43
+ precision (int): precision for homomorphic operations on tensors
40
44
  """
41
45
  self.algorithm_name = algorithm_name
46
+ self.precision = precision
42
47
 
43
48
  if key_file is not None:
44
49
  keys = self.restore_keys(target_file=key_file)
45
50
 
46
- self.cs = self.build_cryptosystem(
51
+ self.cs: Homomorphic = self.__build_cryptosystem(
47
52
  algorithm_name=algorithm_name, keys=keys, key_size=key_size
48
53
  )
49
54
 
50
- def build_cryptosystem(
55
+ def __build_cryptosystem(
51
56
  self,
52
57
  algorithm_name: str,
53
58
  keys: Optional[dict] = None,
@@ -103,22 +108,29 @@ class LightPHE:
103
108
  raise ValueError(f"unimplemented algorithm - {algorithm_name}")
104
109
  return cs
105
110
 
106
- def encrypt(self, plaintext: Union[int, float]) -> Ciphertext:
111
+ def encrypt(self, plaintext: Union[int, float, list]) -> Union[Ciphertext, EncryptedTensor]:
107
112
  """
108
113
  Encrypt a plaintext with a built cryptosystem
109
114
  Args:
110
- plaintext (int or float): message
115
+ plaintext (int, float or tensor): message
111
116
  Returns
112
117
  ciphertext (from lightphe.models.Ciphertext import Ciphertext): encrypted message
113
118
  """
119
+ if self.cs.keys.get("public_key") is None:
120
+ raise ValueError("You must have public key to perform encryption")
121
+
122
+ if isinstance(plaintext, list):
123
+ # then encrypt tensors
124
+ return self.__encrypt_tensors(tensor=plaintext)
125
+
114
126
  ciphertext = self.cs.encrypt(
115
- plaintext=calculations.parse_int(
116
- value=plaintext, modulo=self.cs.modulo or self.cs.plaintext_modulo
117
- )
127
+ plaintext=phe_utils.parse_int(value=plaintext, modulo=self.cs.plaintext_modulo)
118
128
  )
119
129
  return Ciphertext(algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext)
120
130
 
121
- def decrypt(self, ciphertext: Ciphertext) -> int:
131
+ def decrypt(
132
+ self, ciphertext: Union[Ciphertext, EncryptedTensor]
133
+ ) -> Union[int, List[int], List[float]]:
122
134
  """
123
135
  Decrypt a ciphertext with a buit cryptosystem
124
136
  Args:
@@ -126,8 +138,89 @@ class LightPHE:
126
138
  Returns:
127
139
  plaintext (int): restored message
128
140
  """
141
+ if self.cs.keys.get("private_key") is None:
142
+ raise ValueError("You must have private key to perform decryption")
143
+
144
+ if self.cs.keys.get("public_key") is None:
145
+ raise ValueError("You must have public key to perform decryption")
146
+
147
+ if isinstance(ciphertext, EncryptedTensor):
148
+ # then this is encrypted tensor
149
+ return self.__decrypt_tensors(encrypted_tensor=ciphertext)
150
+
129
151
  return self.cs.decrypt(ciphertext=ciphertext.value)
130
152
 
153
+ def __encrypt_tensors(self, tensor: list) -> EncryptedTensor:
154
+ """
155
+ Encrypt a given tensor
156
+ Args:
157
+ tensor (list of int or float)
158
+ Returns
159
+ encrypted tensor (list of encrypted tensor object)
160
+ """
161
+ encrypted_tensor: List[Fraction] = []
162
+ for m in tensor:
163
+ if isinstance(m, int):
164
+ dividend_encrypted = self.cs.encrypt(
165
+ plaintext=(m % self.cs.plaintext_modulo) * pow(10, self.precision)
166
+ )
167
+ abs_dividend_encrypted = self.cs.encrypt(
168
+ plaintext=(abs(m) % self.cs.plaintext_modulo) * pow(10, self.precision)
169
+ )
170
+ divisor_encrypted = self.cs.encrypt(plaintext=pow(10, self.precision))
171
+ c = Fraction(
172
+ dividend=dividend_encrypted,
173
+ divisor=divisor_encrypted,
174
+ abs_dividend=abs_dividend_encrypted,
175
+ sign=1 if m >= 0 else -1,
176
+ )
177
+ elif isinstance(m, float):
178
+ dividend, divisor = phe_utils.fractionize(
179
+ value=(m % self.cs.plaintext_modulo),
180
+ modulo=self.cs.plaintext_modulo,
181
+ precision=self.precision,
182
+ )
183
+ abs_dividend, _ = phe_utils.fractionize(
184
+ value=(abs(m) % self.cs.plaintext_modulo),
185
+ modulo=self.cs.plaintext_modulo,
186
+ precision=self.precision,
187
+ )
188
+ dividend_encrypted = self.cs.encrypt(plaintext=dividend)
189
+ abs_dividend_encrypted = self.cs.encrypt(plaintext=abs_dividend)
190
+ divisor_encrypted = self.cs.encrypt(plaintext=divisor)
191
+ c = Fraction(
192
+ dividend=dividend_encrypted,
193
+ divisor=divisor_encrypted,
194
+ abs_dividend=abs_dividend_encrypted,
195
+ sign=1 if m >= 0 else -1,
196
+ )
197
+ else:
198
+ raise ValueError(f"unimplemented type - {type(m)}")
199
+ encrypted_tensor.append(c)
200
+ return EncryptedTensor(fractions=encrypted_tensor, cs=self.cs)
201
+
202
+ def __decrypt_tensors(self, encrypted_tensor: EncryptedTensor) -> Union[List[int], List[float]]:
203
+ """
204
+ Decrypt a given encrypted tensor
205
+ Args:
206
+ encrypted_tensor (list of encrypted tensor)
207
+ Returns:
208
+ List of plain tensors
209
+ """
210
+ plain_tensor = []
211
+ for c in encrypted_tensor.fractions:
212
+ if isinstance(c, Fraction) is False:
213
+ raise ValueError("Ciphertext items must be type of Fraction")
214
+
215
+ sign = c.sign
216
+ abs_dividend = self.cs.decrypt(ciphertext=c.abs_dividend)
217
+ # dividend = self.cs.decrypt(ciphertext=c.dividend)
218
+ divisor = self.cs.decrypt(ciphertext=c.divisor)
219
+ m = sign * abs_dividend / divisor
220
+
221
+ plain_tensor.append(m)
222
+ return plain_tensor
223
+
131
224
  def regenerate_ciphertext(self, ciphertext: Ciphertext) -> Ciphertext:
132
225
  """
133
226
  Generate a different ciphertext belonging to same plaintext
@@ -136,6 +229,9 @@ class LightPHE:
136
229
  Returns:
137
230
  ciphertext (from lightphe.models.Ciphertext import Ciphertext): encrypted message
138
231
  """
232
+ if self.cs.keys.get("private_key") is None:
233
+ raise ValueError("You must have private key to perform decryption")
234
+
139
235
  ciphertext_new = self.cs.reencrypt(ciphertext=ciphertext.value)
140
236
  return Ciphertext(
141
237
  algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext_new
@@ -150,7 +246,9 @@ class LightPHE:
150
246
  to publicly.
151
247
  """
152
248
  keys = self.cs.keys
249
+ private_key = None
153
250
  if public is True and keys.get("private_key") is not None:
251
+ private_key = keys["private_key"]
154
252
  del keys["private_key"]
155
253
 
156
254
  if public is False:
@@ -162,6 +260,10 @@ class LightPHE:
162
260
  with open(target_file, "w", encoding="UTF-8") as file:
163
261
  file.write(json.dumps(keys))
164
262
 
263
+ # restore private key if you dropped
264
+ if private_key is not None:
265
+ self.cs.keys["private_key"] = private_key
266
+
165
267
  def restore_keys(self, target_file: str) -> dict:
166
268
  """
167
269
  Restore keys from a file
@@ -6,7 +6,8 @@ from datetime import datetime
6
6
 
7
7
 
8
8
  class Logger:
9
- def __init__(self):
9
+ def __init__(self, module):
10
+ self.module = module
10
11
  log_level = os.environ.get("LIGHTPHE_LOG_LEVEL", str(logging.INFO))
11
12
  try:
12
13
  self.log_level = int(log_level)
@@ -0,0 +1,58 @@
1
+ from typing import Union, Tuple, Optional
2
+ from decimal import Decimal, getcontext
3
+ from lightphe.commons.logger import Logger
4
+
5
+ logger = Logger(module="lightphe/commons/phe_utils.py")
6
+
7
+ # pylint: disable=no-else-return, no-else-break
8
+
9
+
10
+ def parse_int(value: Union[int, float], modulo: int) -> int:
11
+ if isinstance(value, int):
12
+ result = value % modulo
13
+ elif isinstance(value, float) and value >= 0:
14
+ dividend, divisor = fractionize(value=value, modulo=modulo)
15
+ logger.debug(f"{dividend}*{divisor}^-1 mod {modulo}")
16
+ result = (dividend * pow(divisor, -1, modulo)) % modulo
17
+ elif isinstance(value, float) and value < 0:
18
+ # TODO: think and implement this later
19
+ raise ValueError("Case constant float and negative not implemented yet")
20
+ else:
21
+ raise ValueError(f"Unimplemented case for constant type {type(value)}")
22
+
23
+ return result
24
+
25
+
26
+ def fractionize(value: float, modulo: int, precision: Optional[int] = None) -> Tuple[int, int]:
27
+ getcontext().prec = 50
28
+
29
+ if precision is None:
30
+ decimal_places = len(str(value).split(".")[1])
31
+ scaling_factor = 10**decimal_places
32
+ else:
33
+ scaling_factor = 10**precision
34
+
35
+ while True:
36
+ integer_value = int(Decimal(value) * Decimal(scaling_factor)) % modulo
37
+
38
+ if precision is None:
39
+ break
40
+
41
+ if scaling_factor > 10**precision:
42
+ # If scaling factor is too large, discard excess part of integer value
43
+ integer_value = int(integer_value / (10 ** (scaling_factor - 10**precision)))
44
+ break
45
+ elif scaling_factor < 10 ** (precision - 1):
46
+ # If scaling factor is too small, multiply dividend and divisor 10 times
47
+ value *= 10
48
+ scaling_factor *= 10
49
+ else:
50
+ break
51
+
52
+ logger.debug(f"{integer_value}*{scaling_factor}^-1 mod {modulo}")
53
+ return integer_value, scaling_factor
54
+
55
+
56
+ def solve_dlp():
57
+ # TODO: implement this later
58
+ pass
@@ -5,7 +5,7 @@ import sympy
5
5
  from lightphe.models.Homomorphic import Homomorphic
6
6
  from lightphe.commons.logger import Logger
7
7
 
8
- logger = Logger()
8
+ logger = Logger(module="lightphe/cryptosystems/Benaloh.py")
9
9
 
10
10
 
11
11
  class Benaloh(Homomorphic):
@@ -5,7 +5,7 @@ import sympy
5
5
  from lightphe.models.Homomorphic import Homomorphic
6
6
  from lightphe.commons.logger import Logger
7
7
 
8
- logger = Logger()
8
+ logger = Logger(module="lightphe/cryptosystems/DamgardJurik.py")
9
9
 
10
10
 
11
11
  class DamgardJurik(Homomorphic):
@@ -5,7 +5,7 @@ import sympy
5
5
  from lightphe.models.Homomorphic import Homomorphic
6
6
  from lightphe.commons.logger import Logger
7
7
 
8
- logger = Logger()
8
+ logger = Logger(module="lightphe/cryptosystems/ElGamal.py")
9
9
 
10
10
 
11
11
  class ElGamal(Homomorphic):
@@ -26,7 +26,8 @@ class ElGamal(Homomorphic):
26
26
  """
27
27
  self.exponential = exponential
28
28
  self.keys = keys or self.generate_keys(key_size)
29
- self.modulo = self.keys["public_key"]["p"]
29
+ self.plaintext_modulo = self.keys["public_key"]["p"]
30
+ self.ciphertext_modulo = self.keys["public_key"]["p"]
30
31
 
31
32
  def generate_keys(self, key_size: int):
32
33
  """
@@ -4,7 +4,7 @@ from lightphe.models.Homomorphic import Homomorphic
4
4
  from lightphe.elliptic.Weierstrass import Weierstrass
5
5
  from lightphe.commons.logger import Logger
6
6
 
7
- logger = Logger()
7
+ logger = Logger(module="lightphe/cryptosystems/EllipticCurveElGamal.py")
8
8
 
9
9
 
10
10
  class EllipticCurveElGamal(Homomorphic):
@@ -27,7 +27,8 @@ class EllipticCurveElGamal(Homomorphic):
27
27
  # TODO: add different forms and curves. e.g. Koblitz, Edwards (Ed25519)
28
28
  self.curve = Weierstrass()
29
29
  self.keys = keys or self.generate_keys(key_size)
30
- self.modulo = self.curve.p
30
+ self.plaintext_modulo = self.curve.p
31
+ self.ciphertext_modulo = self.curve.p
31
32
 
32
33
  def generate_keys(self, key_size: int):
33
34
  """
@@ -6,7 +6,7 @@ from sympy import jacobi_symbol
6
6
  from lightphe.models.Homomorphic import Homomorphic
7
7
  from lightphe.commons.logger import Logger
8
8
 
9
- logger = Logger()
9
+ logger = Logger(module="lightphe/cryptosystems/GoldwasserMicali.py")
10
10
 
11
11
  # pylint:disable=consider-using-enumerate
12
12
 
@@ -26,6 +26,8 @@ class GoldwasserMicali(Homomorphic):
26
26
  """
27
27
  self.keys = keys or self.generate_keys(key_size)
28
28
  self.ciphertext_modulo = self.keys["public_key"]["n"]
29
+ # TODO: not sure about the plaintext modulo
30
+ self.plaintext_modulo = self.keys["public_key"]["n"]
29
31
 
30
32
  def generate_keys(self, key_size: int) -> dict:
31
33
  """
@@ -6,7 +6,7 @@ from sympy.ntheory.modular import solve_congruence
6
6
  from lightphe.models.Homomorphic import Homomorphic
7
7
  from lightphe.commons.logger import Logger
8
8
 
9
- logger = Logger()
9
+ logger = Logger(module="lightphe/cryptosystems/NaccacheStern.py")
10
10
 
11
11
  # pylint: disable=simplifiable-if-expression, consider-using-enumerate
12
12
 
@@ -5,7 +5,7 @@ import sympy
5
5
  from lightphe.models.Homomorphic import Homomorphic
6
6
  from lightphe.commons.logger import Logger
7
7
 
8
- logger = Logger()
8
+ logger = Logger(module="lightphe/cryptosystems/OkamotoUchiyama.py")
9
9
 
10
10
 
11
11
  class OkamotoUchiyama(Homomorphic):
@@ -81,18 +81,21 @@ class OkamotoUchiyama(Homomorphic):
81
81
  Returns:
82
82
  ciphertext (int): encrypted message
83
83
  """
84
- p = self.keys["private_key"]["p"]
84
+
85
85
  g = self.keys["public_key"]["g"]
86
86
  n = self.keys["public_key"]["n"]
87
87
  h = self.keys["public_key"]["h"]
88
88
  r = random_key or self.generate_random_key()
89
89
 
90
- if plaintext > p:
91
- plaintext = plaintext % p
92
- logger.debug(
93
- f"plaintext must be in scale [0, {p=}] but this is exceeded."
94
- "New plaintext is {plaintext}"
95
- )
90
+ # having private key is not a must to encrypt but still if you have
91
+ if self.keys.get("private_key") is not None:
92
+ p = self.keys["private_key"]["p"]
93
+ if plaintext > p:
94
+ plaintext = plaintext % p
95
+ logger.debug(
96
+ f"plaintext must be in scale [0, {p=}] but this is exceeded."
97
+ "New plaintext is {plaintext}"
98
+ )
96
99
  return (pow(g, plaintext, n) * pow(h, r, n)) % n
97
100
 
98
101
  def decrypt(self, ciphertext: int):
@@ -5,7 +5,7 @@ import sympy
5
5
  from lightphe.models.Homomorphic import Homomorphic
6
6
  from lightphe.commons.logger import Logger
7
7
 
8
- logger = Logger()
8
+ logger = Logger(module="lightphe/cryptosystems/Paillier.py")
9
9
 
10
10
 
11
11
  class Paillier(Homomorphic):
@@ -5,7 +5,7 @@ import sympy
5
5
  from lightphe.models.Homomorphic import Homomorphic
6
6
  from lightphe.commons.logger import Logger
7
7
 
8
- logger = Logger()
8
+ logger = Logger(module="lightphe/cryptosystems/RSA.py")
9
9
 
10
10
 
11
11
  class RSA(Homomorphic):
@@ -28,7 +28,8 @@ class RSA(Homomorphic):
28
28
  and do decryption with private key d.
29
29
  """
30
30
  self.keys = keys or self.generate_keys(key_size)
31
- self.modulo = self.keys["public_key"]["n"]
31
+ self.plaintext_modulo = self.keys["public_key"]["n"]
32
+ self.ciphertext_modulo = self.keys["public_key"]["n"]
32
33
  self.encrypt_with_public = encrypt_with_public
33
34
 
34
35
  def generate_keys(self, key_size: int) -> dict:
@@ -10,6 +10,7 @@ class Weierstrass(EllipticCurve):
10
10
  if curve == "secp256k1":
11
11
  self.a = 0
12
12
  self.b = 7
13
+ # modulo
13
14
  self.p = (
14
15
  pow(2, 256)
15
16
  - pow(2, 32)
@@ -10,10 +10,10 @@ from lightphe.cryptosystems.Benaloh import Benaloh
10
10
  from lightphe.cryptosystems.NaccacheStern import NaccacheStern
11
11
  from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali
12
12
  from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal
13
- from lightphe.commons import calculations
13
+ from lightphe.commons import phe_utils
14
14
  from lightphe.commons.logger import Logger
15
15
 
16
- logger = Logger()
16
+ logger = Logger(module="lightphe/models/Ciphertext.py")
17
17
 
18
18
  # pylint: disable=too-few-public-methods, no-else-return
19
19
 
@@ -63,6 +63,9 @@ class Ciphertext:
63
63
  Returns:
64
64
  ciphertext (Ciphertext): homomorphic addition of ciphertext
65
65
  """
66
+ if self.cs.keys.get("public_key") is None:
67
+ raise ValueError("You must have public key to perform homomorphic addition")
68
+
66
69
  result = self.cs.add(ciphertext1=self.value, ciphertext2=other.value)
67
70
  return Ciphertext(algorithm_name=self.algorithm_name, keys=self.keys, value=result)
68
71
 
@@ -74,15 +77,16 @@ class Ciphertext:
74
77
  Returns
75
78
  homomorphic multiplication of ciphertexts | scalar multiplication of ciphertext
76
79
  """
80
+ if self.cs.keys.get("public_key") is None:
81
+ raise ValueError("You must have public key to perform homomorphic multiplication")
82
+
77
83
  if isinstance(other, Ciphertext):
78
84
  # Handle multiplication with another EncryptedObject
79
85
  result = self.cs.multiply(ciphertext1=self.value, ciphertext2=other.value)
80
86
  elif isinstance(other, int):
81
87
  result = self.cs.multiply_by_contant(ciphertext=self.value, constant=other)
82
88
  elif isinstance(other, float):
83
- constant = calculations.parse_int(
84
- value=other, modulo=self.cs.modulo or self.cs.plaintext_modulo
85
- )
89
+ constant = phe_utils.parse_int(value=other, modulo=self.cs.plaintext_modulo)
86
90
  result = self.cs.multiply_by_contant(ciphertext=self.value, constant=constant)
87
91
  else:
88
92
  raise ValueError(
@@ -98,14 +102,7 @@ class Ciphertext:
98
102
  Returns
99
103
  scalar multiplication of ciphertext
100
104
  """
101
- if isinstance(constant, float):
102
- constant = calculations.parse_int(
103
- value=constant, modulo=self.cs.modulo or self.cs.plaintext_modulo
104
- )
105
-
106
- # Handle multiplication with a constant on the right
107
- result = self.cs.multiply_by_contant(ciphertext=self.value, constant=constant)
108
- return Ciphertext(algorithm_name=self.algorithm_name, keys=self.keys, value=result)
105
+ return self.__mul__(other=constant)
109
106
 
110
107
  def __xor__(self, other: "Ciphertext") -> "Ciphertext":
111
108
  """
@@ -115,5 +112,8 @@ class Ciphertext:
115
112
  Returns
116
113
  homomorphic xor of ciphertexts
117
114
  """
115
+ if self.cs.keys.get("public_key") is None:
116
+ raise ValueError("You must have public key to perform homomorphic xor")
117
+
118
118
  result = self.cs.xor(ciphertext1=self.value, ciphertext2=other.value)
119
119
  return Ciphertext(algorithm_name=self.algorithm_name, keys=self.keys, value=result)