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.
Files changed (55) hide show
  1. {lightphe-0.0.9 → lightphe-0.0.10}/PKG-INFO +28 -2
  2. {lightphe-0.0.9 → lightphe-0.0.10}/README.md +27 -1
  3. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/__init__.py +49 -13
  4. lightphe-0.0.10/lightphe/commons/binary_operations.py +147 -0
  5. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/commons/phe_utils.py +22 -4
  6. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/EllipticCurveElGamal.py +48 -28
  7. lightphe-0.0.10/lightphe/elliptic_curve_forms/edwards.py +89 -0
  8. lightphe-0.0.10/lightphe/elliptic_curve_forms/koblitz.py +163 -0
  9. lightphe-0.0.10/lightphe/elliptic_curve_forms/weierstrass.py +117 -0
  10. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/models/Ciphertext.py +27 -8
  11. lightphe-0.0.10/lightphe/models/Curve.py +29 -0
  12. lightphe-0.0.10/lightphe/models/EllipticCurve.py +78 -0
  13. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/models/Tensor.py +13 -5
  14. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe.egg-info/PKG-INFO +28 -2
  15. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe.egg-info/SOURCES.txt +3 -0
  16. {lightphe-0.0.9 → lightphe-0.0.10}/setup.py +1 -1
  17. lightphe-0.0.10/tests/test_ellipticcurveelgamal.py +212 -0
  18. lightphe-0.0.9/lightphe/elliptic_curve_forms/edwards.py +0 -61
  19. lightphe-0.0.9/lightphe/elliptic_curve_forms/weierstrass.py +0 -75
  20. lightphe-0.0.9/lightphe/models/EllipticCurve.py +0 -45
  21. lightphe-0.0.9/tests/test_ellipticcurveelgamal.py +0 -69
  22. {lightphe-0.0.9 → lightphe-0.0.10}/LICENSE +0 -0
  23. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/commons/__init__.py +0 -0
  24. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/commons/logger.py +0 -0
  25. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/Benaloh.py +0 -0
  26. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/DamgardJurik.py +0 -0
  27. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/ElGamal.py +0 -0
  28. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/GoldwasserMicali.py +0 -0
  29. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/NaccacheStern.py +0 -0
  30. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/OkamotoUchiyama.py +0 -0
  31. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/Paillier.py +0 -0
  32. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/RSA.py +0 -0
  33. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/cryptosystems/__init__.py +0 -0
  34. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/elliptic_curve_forms/__init__.py +0 -0
  35. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/models/Algorithm.py +0 -0
  36. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/models/Homomorphic.py +0 -0
  37. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe/models/__init__.py +0 -0
  38. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe.egg-info/dependency_links.txt +0 -0
  39. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe.egg-info/requires.txt +0 -0
  40. {lightphe-0.0.9 → lightphe-0.0.10}/lightphe.egg-info/top_level.txt +0 -0
  41. {lightphe-0.0.9 → lightphe-0.0.10}/requirements.txt +0 -0
  42. {lightphe-0.0.9 → lightphe-0.0.10}/setup.cfg +0 -0
  43. {lightphe-0.0.9 → lightphe-0.0.10}/tests/__init__.py +0 -0
  44. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_benaloh.py +0 -0
  45. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_cloud.py +0 -0
  46. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_damgard.py +0 -0
  47. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_elgamal.py +0 -0
  48. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_exporting_keys.py +0 -0
  49. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_goldwasser.py +0 -0
  50. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_naccache.py +0 -0
  51. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_okamoto.py +0 -0
  52. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_paillier.py +0 -0
  53. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_rsa.py +0 -0
  54. {lightphe-0.0.9 → lightphe-0.0.10}/tests/test_salary.py +0 -0
  55. {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.9
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.9"
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 = "weierstrass",
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, keys=keys, key_size=key_size, form=form,
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(keys=keys, key_size=key_size, form=form)
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(self, plaintext: Union[int, float, list]) -> Union[Ciphertext, EncryptedTensor]:
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.parse_int(value=plaintext, modulo=self.cs.plaintext_modulo)
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) * pow(10, self.precision)
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(self, encrypted_tensor: EncryptedTensor) -> Union[List[int], List[float]]:
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(algorithm_name=self.algorithm_name, keys=self.cs.keys, value=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 parse_int(value: Union[int, float], modulo: int) -> int:
11
- if isinstance(value, int):
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(value: float, modulo: int, precision: Optional[int] = None) -> Tuple[int, int]:
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(integer_value / (10 ** (scaling_factor - 10**precision)))
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__(self, keys: Optional[dict] = None, key_size: Optional[int] = None, form: str = "weierstrass"):
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 160)
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.p
41
- self.ciphertext_modulo = self.curve.p
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, p=self.curve.p)
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, p=p)
113
+ s = self.curve.double_and_add(G=G, k=plaintext)
96
114
 
97
- c1 = self.curve.double_and_add(G=G, k=r, p=p)
115
+ c1 = self.curve.double_and_add(G=G, k=r)
98
116
 
99
- c2 = self.curve.double_and_add(G=Qa, k=r, p=p)
100
- c2 = self.curve.add_points(c2, s, p)
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, p)
121
- s_prime = self.curve.double_and_add(G=c1_prime, k=ka, p=p)
122
- s_prime = self.curve.add_points(P=c2, Q=s_prime, p=p)
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, p=p)
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(f"Cannot restore scalar from {s_prime} = k x {self.curve.G}")
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], p=self.curve.p)
156
- b = self.curve.add_points(P=ciphertext1[1], Q=ciphertext2[1], p=self.curve.p)
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], k=constant, p=self.curve.p
177
- ), self.curve.double_and_add(G=ciphertext[1], k=constant, p=self.curve.p)
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("Elliptic Curve ElGamal does not support regeneration of ciphertext")
198
+ raise ValueError(
199
+ "Elliptic Curve ElGamal does not support regeneration of ciphertext"
200
+ )