lightphe 0.0.10__tar.gz → 0.0.12__tar.gz

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