lightphe 0.0.10__tar.gz → 0.0.11__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 (53) hide show
  1. {lightphe-0.0.10 → lightphe-0.0.11}/PKG-INFO +30 -13
  2. {lightphe-0.0.10 → lightphe-0.0.11}/README.md +29 -12
  3. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/__init__.py +58 -4
  4. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/EllipticCurveElGamal.py +56 -44
  5. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/elliptic_curve_forms/koblitz.py +3 -0
  6. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/elliptic_curve_forms/weierstrass.py +3 -0
  7. lightphe-0.0.11/lightphe/models/EllipticCurve.py +160 -0
  8. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe.egg-info/PKG-INFO +30 -13
  9. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe.egg-info/SOURCES.txt +1 -0
  10. {lightphe-0.0.10 → lightphe-0.0.11}/setup.py +1 -1
  11. lightphe-0.0.11/tests/test_ellipticcurve.py +75 -0
  12. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_ellipticcurveelgamal.py +86 -35
  13. lightphe-0.0.10/lightphe/models/EllipticCurve.py +0 -78
  14. {lightphe-0.0.10 → lightphe-0.0.11}/LICENSE +0 -0
  15. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/commons/__init__.py +0 -0
  16. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/commons/binary_operations.py +0 -0
  17. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/commons/logger.py +0 -0
  18. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/commons/phe_utils.py +0 -0
  19. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/Benaloh.py +0 -0
  20. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/DamgardJurik.py +0 -0
  21. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/ElGamal.py +0 -0
  22. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/GoldwasserMicali.py +0 -0
  23. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/NaccacheStern.py +0 -0
  24. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/OkamotoUchiyama.py +0 -0
  25. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/Paillier.py +0 -0
  26. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/RSA.py +0 -0
  27. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/__init__.py +0 -0
  28. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/elliptic_curve_forms/__init__.py +0 -0
  29. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/elliptic_curve_forms/edwards.py +0 -0
  30. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/Algorithm.py +0 -0
  31. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/Ciphertext.py +0 -0
  32. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/Curve.py +0 -0
  33. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/Homomorphic.py +0 -0
  34. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/Tensor.py +0 -0
  35. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/__init__.py +0 -0
  36. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe.egg-info/dependency_links.txt +0 -0
  37. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe.egg-info/requires.txt +0 -0
  38. {lightphe-0.0.10 → lightphe-0.0.11}/lightphe.egg-info/top_level.txt +0 -0
  39. {lightphe-0.0.10 → lightphe-0.0.11}/requirements.txt +0 -0
  40. {lightphe-0.0.10 → lightphe-0.0.11}/setup.cfg +0 -0
  41. {lightphe-0.0.10 → lightphe-0.0.11}/tests/__init__.py +0 -0
  42. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_benaloh.py +0 -0
  43. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_cloud.py +0 -0
  44. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_damgard.py +0 -0
  45. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_elgamal.py +0 -0
  46. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_exporting_keys.py +0 -0
  47. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_goldwasser.py +0 -0
  48. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_naccache.py +0 -0
  49. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_okamoto.py +0 -0
  50. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_paillier.py +0 -0
  51. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_rsa.py +0 -0
  52. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_salary.py +0 -0
  53. {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_tensors.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.11
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,8 @@ 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
+ <!-- [![DOI](http://img.shields.io/:DOI-10.48550/arXiv.2408.05219-blue.svg?style=flat)](https://arxiv.org/abs/2408.05219) -->
26
27
 
27
28
  [![Blog](https://img.shields.io/:blog-sefiks.com-blue.svg?style=flat&logo=wordpress)](https://sefiks.com)
28
29
  [![YouTube](https://img.shields.io/:youtube-@sefiks-red.svg?style=flat&logo=youtube)](https://www.youtube.com/@sefiks?sub_confirmation=1)
@@ -245,23 +246,39 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
245
246
 
246
247
  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
248
 
248
- ### Elliptic Curve Cryptography
249
+ ### Elliptic Curve Cryptography
249
250
 
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
+ 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.
252
+
253
+ 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
254
 
252
255
  ```python
253
- some_curves = [
254
- {"form": "weierstrass", "curve": "secp256k1"},
255
- {"form": "edwards", "curve": "ed25519"},
256
- {"form": "koblitz", "curve": "k163"}
257
- ]
256
+ from lightphe import ECC
257
+
258
+ forms = ["weierstrass", "edwards", "koblitz"]
259
+
260
+ # construct an elliptic curve
261
+ ed = ECC(form_name="edwards")
262
+
263
+ # Base Point
264
+ G = ed.G
258
265
 
259
- curve = some_curves[1]
266
+ _2G = G + G
267
+ _3G = _2G + G
268
+ _5G = _3G + _2G
269
+
270
+ _20G = 4 * G
271
+ _19G = _20G - G
272
+ ```
273
+
274
+ 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.
275
+
276
+ ```python
277
+ forms = ["weierstass", "edwards", "koblitz"]
260
278
 
261
279
  phe = LightPHE(
262
280
  algorithm_name="EllipticCurve-ElGamal",
263
- form=curve["form"],
264
- curve=curve["curve"],
281
+ form=forms[1],
265
282
  )
266
283
  ```
267
284
 
@@ -269,7 +286,7 @@ One of the crucial factors that define the security level of an elliptic curve c
269
286
 
270
287
  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
288
 
272
- See [`Curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for more details.
289
+ 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
290
 
274
291
  # Contributing
275
292
 
@@ -6,7 +6,8 @@
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
+ <!-- [![DOI](http://img.shields.io/:DOI-10.48550/arXiv.2408.05219-blue.svg?style=flat)](https://arxiv.org/abs/2408.05219) -->
10
11
 
11
12
  [![Blog](https://img.shields.io/:blog-sefiks.com-blue.svg?style=flat&logo=wordpress)](https://sefiks.com)
12
13
  [![YouTube](https://img.shields.io/:youtube-@sefiks-red.svg?style=flat&logo=youtube)](https://www.youtube.com/@sefiks?sub_confirmation=1)
@@ -229,23 +230,39 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
229
230
 
230
231
  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
232
 
232
- ### Elliptic Curve Cryptography
233
+ ### Elliptic Curve Cryptography
233
234
 
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
+ 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.
236
+
237
+ 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
238
 
236
239
  ```python
237
- some_curves = [
238
- {"form": "weierstrass", "curve": "secp256k1"},
239
- {"form": "edwards", "curve": "ed25519"},
240
- {"form": "koblitz", "curve": "k163"}
241
- ]
240
+ from lightphe import ECC
241
+
242
+ forms = ["weierstrass", "edwards", "koblitz"]
243
+
244
+ # construct an elliptic curve
245
+ ed = ECC(form_name="edwards")
246
+
247
+ # Base Point
248
+ G = ed.G
242
249
 
243
- curve = some_curves[1]
250
+ _2G = G + G
251
+ _3G = _2G + G
252
+ _5G = _3G + _2G
253
+
254
+ _20G = 4 * G
255
+ _19G = _20G - G
256
+ ```
257
+
258
+ 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.
259
+
260
+ ```python
261
+ forms = ["weierstass", "edwards", "koblitz"]
244
262
 
245
263
  phe = LightPHE(
246
264
  algorithm_name="EllipticCurve-ElGamal",
247
- form=curve["form"],
248
- curve=curve["curve"],
265
+ form=forms[1],
249
266
  )
250
267
  ```
251
268
 
@@ -253,7 +270,7 @@ One of the crucial factors that define the security level of an elliptic curve c
253
270
 
254
271
  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
272
 
256
- See [`Curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for more details.
273
+ 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
274
 
258
275
  # Contributing
259
276
 
@@ -1,10 +1,17 @@
1
+ # built-in dependencies
1
2
  import json
2
3
  from typing import Optional, Union, List
3
4
 
5
+ # project dependencies
4
6
  from lightphe.models.Homomorphic import Homomorphic
5
7
  from lightphe.models.Ciphertext import Ciphertext
6
8
  from lightphe.models.Algorithm import Algorithm
7
9
  from lightphe.models.Tensor import Fraction, EncryptedTensor
10
+ from lightphe.models.EllipticCurve import EllipticCurvePoint
11
+ from lightphe.commons import phe_utils
12
+ from lightphe.commons.logger import Logger
13
+
14
+ # cryptosystems
8
15
  from lightphe.cryptosystems.RSA import RSA
9
16
  from lightphe.cryptosystems.ElGamal import ElGamal
10
17
  from lightphe.cryptosystems.Paillier import Paillier
@@ -14,16 +21,20 @@ from lightphe.cryptosystems.Benaloh import Benaloh
14
21
  from lightphe.cryptosystems.NaccacheStern import NaccacheStern
15
22
  from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali
16
23
  from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal
17
- from lightphe.commons import phe_utils
18
- from lightphe.commons.logger import Logger
24
+ from lightphe.elliptic_curve_forms.weierstrass import Weierstrass
25
+ from lightphe.elliptic_curve_forms.edwards import TwistedEdwards
26
+ from lightphe.elliptic_curve_forms.koblitz import Koblitz
27
+
19
28
 
20
- # pylint: disable=eval-used, simplifiable-if-expression
29
+ # pylint: disable=eval-used, simplifiable-if-expression, too-few-public-methods
21
30
 
22
31
  logger = Logger(module="lightphe/__init__.py")
23
32
 
33
+ VERSION = "0.0.11"
34
+
24
35
 
25
36
  class LightPHE:
26
- __version__ = "0.0.10"
37
+ __version__ = VERSION
27
38
 
28
39
  def __init__(
29
40
  self,
@@ -53,6 +64,8 @@ class LightPHE:
53
64
  - e.g. ed25519, ed448 for edwards form
54
65
  - e.g. secp256k1 for weierstrass form
55
66
  - e.g. k-409 for koblitz form
67
+ List of all available curves:
68
+ github.com/serengil/LightPHE/blob/master/lightphe/elliptic_curve_forms/README.md
56
69
  This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
57
70
  """
58
71
  self.algorithm_name = algorithm_name
@@ -341,3 +354,44 @@ class LightPHE:
341
354
  return Ciphertext(
342
355
  algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext
343
356
  )
357
+
358
+
359
+ class ECC:
360
+ __version__ = VERSION
361
+
362
+ def __init__(
363
+ self, form_name: Optional[str] = None, curve_name: Optional[str] = None
364
+ ):
365
+ """
366
+ Construct an Elliptic Curve over a finite field (prime or binary)
367
+ Args:
368
+ form_name (str): specifies the form of the elliptic curve.
369
+ Options: 'weierstrass' (default), 'edwards', 'koblitz'.
370
+ curve_name (str): specifies the elliptic curve to use.
371
+ Options:
372
+ - e.g. ed25519, ed448 for edwards form
373
+ - e.g. secp256k1 for weierstrass form
374
+ - e.g. k-409 for koblitz form
375
+ List of all available curves:
376
+ github.com/serengil/LightPHE/blob/master/lightphe/elliptic_curve_forms/README.md
377
+ """
378
+ if form_name is None or form_name == "weierstrass":
379
+ self.curve = Weierstrass(curve=curve_name)
380
+ elif form_name in "edwards":
381
+ self.curve = TwistedEdwards(curve=curve_name)
382
+ elif form_name in "koblitz":
383
+ self.curve = Koblitz(curve=curve_name)
384
+ else:
385
+ raise ValueError(f"unimplemented curve form - {form_name}")
386
+
387
+ # base point
388
+ self.G = EllipticCurvePoint(self.curve.G[0], self.curve.G[1], self.curve)
389
+
390
+ # order of the curve
391
+ self.n = self.curve.n
392
+
393
+ # point at infinity or neutral / identity element
394
+ self.O = EllipticCurvePoint(self.curve.O[0], self.curve.O[1], self.curve)
395
+
396
+ # modulo
397
+ 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())
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())
52
49
  self.keys["public_key"]["form"] = form
53
50
  self.keys["private_key"]["form"] = form
54
- self.plaintext_modulo = self.curve.modulo
55
- self.ciphertext_modulo = self.curve.modulo
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
@@ -0,0 +1,160 @@
1
+ # built-in dependencies
2
+ from typing import Tuple
3
+ from abc import ABC, abstractmethod
4
+
5
+ # Signature for elliptic curve
6
+
7
+
8
+ class EllipticCurve(ABC):
9
+ # point at infinity or neutral / identity element
10
+ O: Tuple[int, int]
11
+
12
+ # base point G
13
+ G: Tuple[int, int]
14
+
15
+ # modulo (prime p or polynomial fx)
16
+ modulo: int
17
+
18
+ # order of the curve
19
+ n: int
20
+
21
+ # coefficients
22
+ a: int
23
+ b: int = 0 # for weierstrass & koblitz form
24
+ d: int = 0 # for edwards form
25
+
26
+ @abstractmethod
27
+ def add_points(
28
+ self,
29
+ P: Tuple[int, int],
30
+ Q: Tuple[int, int],
31
+ ) -> Tuple[int, int]:
32
+ pass
33
+
34
+ @abstractmethod
35
+ def double_point(self, P: Tuple[int, int]) -> Tuple[int, int]:
36
+ pass
37
+
38
+ @abstractmethod
39
+ def is_on_curve(self, P: Tuple[int, int]) -> bool:
40
+ pass
41
+
42
+ @abstractmethod
43
+ def negative_point(self, P: Tuple[int, int]) -> Tuple[int, int]:
44
+ pass
45
+
46
+ def double_and_add(self, G: Tuple[int, int], k: int) -> Tuple[int, int]:
47
+ """
48
+ Perform scalar multiplication over elliptic curve
49
+ Args:
50
+ G (Tuple[int, int]): a point on an elliptic curve
51
+ k (int): scalar value
52
+ Returns
53
+ kxG (Tuple[int, int]): a point on an elliptic curve
54
+ """
55
+ target_point = G
56
+
57
+ if k >= self.n:
58
+ k = k % self.n
59
+ if k == 0:
60
+ return self.O
61
+ if k < 0:
62
+ return self.negative_point(self.double_and_add(G, abs(k)))
63
+
64
+ k_binary = bin(k)[2:]
65
+
66
+ for i in range(1, len(k_binary)):
67
+ current_bit = k_binary[i : i + 1]
68
+
69
+ # doubling - always
70
+ target_point = self.double_point(target_point)
71
+
72
+ if current_bit == "1":
73
+ target_point = self.add_points(target_point, G)
74
+
75
+ assert (
76
+ self.is_on_curve(target_point) is True
77
+ ), f"{target_point} is not on the curve!"
78
+
79
+ return target_point
80
+
81
+
82
+ class EllipticCurvePoint:
83
+ """
84
+ Define a point on an elliptic curve
85
+ """
86
+
87
+ def __init__(self, x: int, y: int, curve: EllipticCurve):
88
+ self.x = x
89
+ self.y = y
90
+ self.curve = curve
91
+
92
+ assert self.curve.is_on_curve((x, y)), f"({x}, {y}) is not on the curve!"
93
+
94
+ def get_point(self) -> Tuple[int, int]:
95
+ return (self.x, self.y)
96
+
97
+ def __repr__(self):
98
+
99
+ if (
100
+ self.x == self.curve.O[0]
101
+ and self.y == self.curve.O[1]
102
+ and self.x == float("inf")
103
+ and self.y == float("inf")
104
+ ):
105
+ # because Edwards has neutral / identity element instead of point at infinity
106
+ return "\U0001D4AA" # unicode for "𝒪" (circle O)
107
+ return f"({self.x}, {self.y})"
108
+
109
+ def __str__(self):
110
+ return self.__repr__()
111
+
112
+ def __add__(self, other: "EllipticCurvePoint") -> "EllipticCurvePoint":
113
+ """
114
+ Calculate P + Q for two given points P and Q
115
+ """
116
+ if not isinstance(other, EllipticCurvePoint):
117
+ raise ValueError("Addition is only defined for 2 points")
118
+
119
+ if self.curve != other.curve:
120
+ raise ValueError("Points are not on the same curve")
121
+
122
+ x, y = self.curve.add_points((self.x, self.y), (other.x, other.y))
123
+ return EllipticCurvePoint(x, y, self.curve)
124
+
125
+ def __sub__(self, other: "EllipticCurvePoint") -> "EllipticCurvePoint":
126
+ """
127
+ Calculate P - Q for two given points P and Q
128
+ """
129
+ return self.__add__(other=other.__neg__())
130
+
131
+ def __mul__(self, k: int) -> "EllipticCurvePoint":
132
+ """
133
+ Calculate k*P for a given k and P
134
+ """
135
+ if not isinstance(k, int):
136
+ raise ValueError("Multiplication is only defined for an integer")
137
+
138
+ x, y = self.curve.double_and_add((self.x, self.y), k)
139
+ return EllipticCurvePoint(x, y, self.curve)
140
+
141
+ def __rmul__(self, k: int) -> "EllipticCurvePoint":
142
+ """
143
+ Calculate k*P for a given k and P
144
+ """
145
+ if not isinstance(k, int):
146
+ raise ValueError("Multiplication is only defined for an integer")
147
+ return self.__mul__(k)
148
+
149
+ def __neg__(self) -> "EllipticCurvePoint":
150
+ """
151
+ Calculate -P for a given P
152
+ """
153
+ x, y = self.curve.negative_point((self.x, self.y))
154
+ return EllipticCurvePoint(x, y, self.curve)
155
+
156
+ def __eq__(self, other: "EllipticCurvePoint") -> bool:
157
+ """
158
+ Check if two points are equal
159
+ """
160
+ return self.x == other.x and self.y == other.y and self.curve == other.curve
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightphe
3
- Version: 0.0.10
3
+ Version: 0.0.11
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,8 @@ 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
+ <!-- [![DOI](http://img.shields.io/:DOI-10.48550/arXiv.2408.05219-blue.svg?style=flat)](https://arxiv.org/abs/2408.05219) -->
26
27
 
27
28
  [![Blog](https://img.shields.io/:blog-sefiks.com-blue.svg?style=flat&logo=wordpress)](https://sefiks.com)
28
29
  [![YouTube](https://img.shields.io/:youtube-@sefiks-red.svg?style=flat&logo=youtube)](https://www.youtube.com/@sefiks?sub_confirmation=1)
@@ -245,23 +246,39 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t
245
246
 
246
247
  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
248
 
248
- ### Elliptic Curve Cryptography
249
+ ### Elliptic Curve Cryptography
249
250
 
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
+ 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.
252
+
253
+ 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
254
 
252
255
  ```python
253
- some_curves = [
254
- {"form": "weierstrass", "curve": "secp256k1"},
255
- {"form": "edwards", "curve": "ed25519"},
256
- {"form": "koblitz", "curve": "k163"}
257
- ]
256
+ from lightphe import ECC
257
+
258
+ forms = ["weierstrass", "edwards", "koblitz"]
259
+
260
+ # construct an elliptic curve
261
+ ed = ECC(form_name="edwards")
262
+
263
+ # Base Point
264
+ G = ed.G
258
265
 
259
- curve = some_curves[1]
266
+ _2G = G + G
267
+ _3G = _2G + G
268
+ _5G = _3G + _2G
269
+
270
+ _20G = 4 * G
271
+ _19G = _20G - G
272
+ ```
273
+
274
+ 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.
275
+
276
+ ```python
277
+ forms = ["weierstass", "edwards", "koblitz"]
260
278
 
261
279
  phe = LightPHE(
262
280
  algorithm_name="EllipticCurve-ElGamal",
263
- form=curve["form"],
264
- curve=curve["curve"],
281
+ form=forms[1],
265
282
  )
266
283
  ```
267
284
 
@@ -269,7 +286,7 @@ One of the crucial factors that define the security level of an elliptic curve c
269
286
 
270
287
  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
288
 
272
- See [`Curves`](https://github.com/serengil/LightPHE/tree/master/lightphe/elliptic_curve_forms) page for more details.
289
+ 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
290
 
274
291
  # Contributing
275
292
 
@@ -38,6 +38,7 @@ tests/test_benaloh.py
38
38
  tests/test_cloud.py
39
39
  tests/test_damgard.py
40
40
  tests/test_elgamal.py
41
+ tests/test_ellipticcurve.py
41
42
  tests/test_ellipticcurveelgamal.py
42
43
  tests/test_exporting_keys.py
43
44
  tests/test_goldwasser.py
@@ -8,7 +8,7 @@ with open("requirements.txt", "r", encoding="utf-8") as f:
8
8
 
9
9
  setuptools.setup(
10
10
  name="lightphe",
11
- version="0.0.10",
11
+ version="0.0.11",
12
12
  author="Sefik Ilkin Serengil",
13
13
  author_email="serengil@gmail.com",
14
14
  description="A Lightweight Partially Homomorphic Encryption Library for Python",
@@ -0,0 +1,75 @@
1
+ # project dependencies
2
+ import pytest
3
+
4
+ # project dependencies
5
+ from lightphe import ECC
6
+ from lightphe.commons.logger import Logger
7
+
8
+ logger = Logger(module="tests/test_ellipticcurve.py")
9
+
10
+
11
+ def test_elliptic_curve_interface():
12
+ forms = ["weierstrass", "edwards", "koblitz"]
13
+ for form in forms:
14
+ # construct an elliptic curve for the given form
15
+ ec = ECC(form_name=form, curve_name="test-curve")
16
+
17
+ # base point
18
+ G = ec.G
19
+
20
+ # order of the curve
21
+ n = ec.n
22
+
23
+ for i in range(1, n):
24
+ k = i + 1
25
+ operations = ["G"]
26
+ cumulative_point = G
27
+ for _ in range(0, i):
28
+ cumulative_point = cumulative_point + G
29
+ operations.append("G")
30
+
31
+ kG = k * G
32
+ Gk = G * k
33
+
34
+ assert kG == Gk
35
+ assert kG == cumulative_point
36
+
37
+ logger.debug(f"{'+'.join(operations)} = {cumulative_point}")
38
+ logger.debug(f"""{k} x G = {kG}""")
39
+ logger.debug(f"""G x {k} = {Gk}""")
40
+ logger.debug("--------")
41
+
42
+ minus_G = -G
43
+ assert ec.curve.negative_point(P=(G.x, G.y)) == (minus_G.x, minus_G.y)
44
+
45
+ _3G = 3 * G
46
+ _5G = 5 * G
47
+ minus_5G = -5 * G
48
+ assert ec.curve.negative_point(P=(_5G.x, _5G.y)) == (minus_5G.x, minus_5G.y)
49
+
50
+ _5G_minus_G = _5G - G
51
+ logger.debug(f"""5G - G = {_5G_minus_G}""")
52
+
53
+ _3G_plus_G = _3G + G
54
+ logger.debug(f"""3G + G = {_3G_plus_G}""")
55
+
56
+ assert _3G_plus_G == _5G_minus_G
57
+
58
+ # multiplication cannot be done for 2 points
59
+ with pytest.raises(
60
+ ValueError, match="Multiplication is only defined for an integer"
61
+ ):
62
+ _ = _5G * G
63
+
64
+ with pytest.raises(ValueError, match="Addition is only defined for 2 points"):
65
+ _ = _5G + 5
66
+
67
+ logger.info(f"✅ Test elliptic curve interface for {form} form done.")
68
+
69
+
70
+ def test_addition_of_2_points_of_different_curves():
71
+ ws = ECC(form_name="weierstrass", curve_name="test-curve")
72
+ ed = ECC(form_name="edwards", curve_name="test-curve")
73
+
74
+ with pytest.raises(ValueError, match="Points are not on the same curve"):
75
+ _ = ws.G + ed.G
@@ -23,8 +23,10 @@ def __test_build_curves():
23
23
  for curve in curves:
24
24
  cs = EllipticCurveElGamal(form=form, curve=curve)
25
25
  field = "binary" if form == "koblitz" else "prime"
26
- logger.info(f"| {form} | {curve} | {field} | {cs.curve.n.bit_length()} |")
27
- summary.append((form, curve, field, cs.curve.n.bit_length()))
26
+ logger.info(
27
+ f"| {form} | {curve} | {field} | {cs.ecc.curve.n.bit_length()} |"
28
+ )
29
+ summary.append((form, curve, field, cs.ecc.curve.n.bit_length()))
28
30
 
29
31
  import pandas as pd
30
32
 
@@ -43,6 +45,9 @@ def __test_elliptic_curve_elgamal():
43
45
  curves = inventory.list_curves(form)
44
46
  for curve in curves:
45
47
 
48
+ if curve in ["test-curve"]:
49
+ continue
50
+
46
51
  logger.debug(
47
52
  f"ℹ️ Elliptic Curve ElGamal test is running for EC form {form}&{curve}"
48
53
  )
@@ -89,7 +94,7 @@ def __test_elliptic_curve_elgamal():
89
94
 
90
95
  logger.info(
91
96
  f"✅ Elliptic Curve ElGamal test succeeded for EC form {form}&{curve}"
92
- f" ({cs.curve.n.bit_length()}-bit) in {duration} seconds"
97
+ f" ({cs.ecc.curve.n.bit_length()}-bit) in {duration} seconds"
93
98
  )
94
99
 
95
100
 
@@ -132,9 +137,9 @@ def test_api():
132
137
  def test_adding_a_point_with_its_negative():
133
138
  for form in FORMS:
134
139
  cs = EllipticCurveElGamal(form=form)
135
- G = cs.curve.G
136
- G_minus = cs.curve.negative_point(G)
137
- assert cs.curve.O == cs.curve.add_points(G, G_minus)
140
+ G = cs.ecc.curve.G
141
+ G_minus = cs.ecc.curve.negative_point(G)
142
+ assert cs.ecc.curve.O == cs.ecc.curve.add_points(G, G_minus)
138
143
 
139
144
  logger.info(f"✅ Adding a point with its negative tested for {form}")
140
145
 
@@ -142,57 +147,103 @@ def test_adding_a_point_with_its_negative():
142
147
  def test_zero_times_base_point():
143
148
  for form in FORMS:
144
149
  cs = EllipticCurveElGamal(form=form)
145
- G = cs.curve.G
146
- assert cs.curve.double_and_add(G, 0) == cs.curve.O
150
+ G = cs.ecc.curve.G
151
+ assert cs.ecc.curve.double_and_add(G, 0) == cs.ecc.curve.O
147
152
  logger.info(f"✅ Test 0 x G = O done for {form}")
148
153
 
149
154
 
150
155
  def test_double_and_add_with_negative_input():
151
156
  for form in FORMS:
152
157
  cs = EllipticCurveElGamal(form=form)
153
- G = cs.curve.G
154
- assert cs.curve.double_and_add(G, -10) == cs.curve.negative_point(
155
- cs.curve.double_and_add(G, 10)
158
+ G = cs.ecc.curve.G
159
+ assert cs.ecc.curve.double_and_add(G, -10) == cs.ecc.curve.negative_point(
160
+ cs.ecc.curve.double_and_add(G, 10)
156
161
  )
157
162
  logger.info(f"✅ Test (-10) x G = -(10 x G) done for {form}")
158
163
 
159
164
 
160
165
  def test_elliptic_curve_cyclic_group_on_test_curve():
161
- cs = EllipticCurveElGamal(form="weierstrass", curve="test-curve-pf-23")
162
166
 
163
- for k in range(0, 5 * cs.curve.n):
164
- P = cs.curve.double_and_add(cs.curve.G, k)
165
- logger.debug(f"{k} x G = {P}")
167
+ curves = ["weierstrass", "koblitz", "edwards"]
168
+
169
+ for form in curves:
170
+ logger.debug(f"ℹ️ Testing elliptic curve cyclic group on {form} test curve")
171
+ cs = EllipticCurveElGamal(form=form, curve="test-curve")
172
+
173
+ for k in range(0, 2 * cs.ecc.curve.n + 1):
174
+ P = cs.ecc.curve.double_and_add(cs.ecc.curve.G, k)
175
+ logger.debug(f"{k} x G = {P}")
166
176
 
167
- if k in [0, cs.curve.n]:
168
- assert P == cs.curve.O
177
+ if k in [0, cs.ecc.curve.n]:
178
+ assert P == cs.ecc.curve.O
169
179
 
170
- logger.info("✅ Test elliptic curve cyclic group on test curve done.")
180
+ logger.info(f"✅ Test elliptic curve cyclic group on test {form} curve done.")
171
181
 
172
182
 
173
- def test_point_addition_returning_point_at_infinity():
174
- cs = EllipticCurveElGamal(form="weierstrass", curve="test-curve-pf-23")
183
+ def test_weierstrass_point_addition_returning_point_at_infinity():
184
+ cs = EllipticCurveElGamal(form="weierstrass", curve="test-curve")
175
185
 
176
- # we know that 20G + 8 G = 28G = point at infinity
177
- P = cs.curve.add_points(
178
- cs.curve.double_and_add(cs.curve.G, 20), cs.curve.double_and_add(cs.curve.G, 8)
186
+ # we know that 20G + 8G = 28G = point at infinity
187
+ P = cs.ecc.curve.add_points(
188
+ cs.ecc.curve.double_and_add(cs.ecc.curve.G, 20),
189
+ cs.ecc.curve.double_and_add(cs.ecc.curve.G, 8),
179
190
  )
180
- assert P == cs.curve.O
191
+ assert P == cs.ecc.curve.O
181
192
 
182
- _14G = cs.curve.double_and_add(cs.curve.G, 14)
183
- Q = cs.curve.add_points(_14G, _14G)
184
- assert Q == cs.curve.O
193
+ _14G = cs.ecc.curve.double_and_add(cs.ecc.curve.G, 14)
194
+ Q = cs.ecc.curve.add_points(_14G, _14G)
195
+ assert Q == cs.ecc.curve.O
196
+
197
+ logger.info("✅ Test weierstras point addition returning point at infinity done.")
198
+
199
+
200
+ def test_koblitz_point_addition_returning_point_at_infinity():
201
+ cs = EllipticCurveElGamal(form="koblitz", curve="test-curve")
202
+
203
+ # we know that 12G + 4G = 16G = point at infinity
204
+ P = cs.ecc.curve.add_points(
205
+ cs.ecc.curve.double_and_add(cs.ecc.curve.G, 12),
206
+ cs.ecc.curve.double_and_add(cs.ecc.curve.G, 4),
207
+ )
208
+ assert P == cs.ecc.curve.O
185
209
 
186
- logger.info("✅ Test elliptic curve cyclic group on test curve done.")
210
+ _8G = cs.ecc.curve.double_and_add(cs.ecc.curve.G, 8)
211
+ Q = cs.ecc.curve.add_points(_8G, _8G)
212
+ assert Q == cs.ecc.curve.O
213
+
214
+ logger.info("✅ Test koblitz point addition returning point at infinity done.")
215
+
216
+
217
+ def test_edwards_point_addition_returning_point_at_infinity():
218
+ cs = EllipticCurveElGamal(form="edwards", curve="test-curve")
219
+
220
+ # we know that 6G + 2G = 8G = point at infinity
221
+ P = cs.ecc.curve.add_points(
222
+ cs.ecc.curve.double_and_add(cs.ecc.curve.G, 6),
223
+ cs.ecc.curve.double_and_add(cs.ecc.curve.G, 2),
224
+ )
225
+ assert P == cs.ecc.curve.O
226
+
227
+ _4G = cs.ecc.curve.double_and_add(cs.ecc.curve.G, 4)
228
+ Q = cs.ecc.curve.add_points(_4G, _4G)
229
+ assert Q == cs.ecc.curve.O
230
+
231
+ logger.info("✅ Test edwards point addition returning point at infinity done.")
187
232
 
188
233
 
189
234
  def test_double_and_add_for_k_close_to_n():
190
235
  for form in FORMS:
191
236
  cs = EllipticCurveElGamal(form=form)
192
237
 
193
- _ = cs.curve.double_and_add(cs.curve.G, cs.curve.n - 1)
194
- assert cs.curve.double_and_add(cs.curve.G, cs.curve.n) == cs.curve.O
195
- assert cs.curve.double_and_add(cs.curve.G, cs.curve.n + 1) == cs.curve.G
238
+ _ = cs.ecc.curve.double_and_add(cs.ecc.curve.G, cs.ecc.curve.n - 1)
239
+ assert (
240
+ cs.ecc.curve.double_and_add(cs.ecc.curve.G, cs.ecc.curve.n)
241
+ == cs.ecc.curve.O
242
+ )
243
+ assert (
244
+ cs.ecc.curve.double_and_add(cs.ecc.curve.G, cs.ecc.curve.n + 1)
245
+ == cs.ecc.curve.G
246
+ )
196
247
 
197
248
  logger.info(
198
249
  f"✅ Double and add for k being close to order test done for {form}"
@@ -203,10 +254,10 @@ def test_add_neutral_point():
203
254
  for form in FORMS:
204
255
  cs = EllipticCurveElGamal(form=form)
205
256
 
206
- _7G = cs.curve.double_and_add(cs.curve.G, 7)
257
+ _7G = cs.ecc.curve.double_and_add(cs.ecc.curve.G, 7)
207
258
 
208
- assert cs.curve.add_points(_7G, cs.curve.O) == _7G
209
- assert cs.curve.add_points(cs.curve.O, _7G) == _7G
210
- assert cs.curve.add_points(cs.curve.O, cs.curve.O) == cs.curve.O
259
+ assert cs.ecc.curve.add_points(_7G, cs.ecc.curve.O) == _7G
260
+ assert cs.ecc.curve.add_points(cs.ecc.curve.O, _7G) == _7G
261
+ assert cs.ecc.curve.add_points(cs.ecc.curve.O, cs.ecc.curve.O) == cs.ecc.curve.O
211
262
 
212
263
  logger.info(f"✅ Adding neutral point test done for {form}")
@@ -1,78 +0,0 @@
1
- from typing import Tuple
2
- from abc import ABC, abstractmethod
3
-
4
- # Signature for elliptic curve
5
-
6
-
7
- class EllipticCurve(ABC):
8
- # point at infinity or neutral / identity element
9
- O: Tuple[int, int]
10
-
11
- # base point G
12
- G: Tuple[int, int]
13
-
14
- # modulo (prime p or polynomial fx)
15
- modulo: int
16
-
17
- # order of the curve
18
- n: int
19
-
20
- # coefficients
21
- a: int
22
- b: int = 0 # for weierstrass & koblitz form
23
- d: int = 0 # for edwards form
24
-
25
- @abstractmethod
26
- def add_points(
27
- self,
28
- P: Tuple[int, int],
29
- Q: Tuple[int, int],
30
- ) -> Tuple[int, int]:
31
- pass
32
-
33
- @abstractmethod
34
- def double_point(self, P: Tuple[int, int]) -> Tuple[int, int]:
35
- pass
36
-
37
- @abstractmethod
38
- def is_on_curve(self, P: Tuple[int, int]) -> bool:
39
- pass
40
-
41
- @abstractmethod
42
- def negative_point(self, P: Tuple[int, int]) -> Tuple[int, int]:
43
- pass
44
-
45
- def double_and_add(self, G: Tuple[int, int], k: int) -> Tuple[int, int]:
46
- """
47
- Perform scalar multiplication over elliptic curve
48
- Args:
49
- G (Tuple[int, int]): a point on an elliptic curve
50
- k (int): scalar value
51
- Returns
52
- kxG (Tuple[int, int]): a point on an elliptic curve
53
- """
54
- target_point = G
55
-
56
- if k >= self.n:
57
- k = k % self.n
58
- if k == 0:
59
- return self.O
60
- if k < 0:
61
- return self.negative_point(self.double_and_add(G, abs(k)))
62
-
63
- k_binary = bin(k)[2:]
64
-
65
- for i in range(1, len(k_binary)):
66
- current_bit = k_binary[i : i + 1]
67
-
68
- # doubling - always
69
- target_point = self.double_point(target_point)
70
-
71
- if current_bit == "1":
72
- target_point = self.add_points(target_point, G)
73
-
74
- assert (
75
- self.is_on_curve(target_point) is True
76
- ), f"{target_point} is not on the curve!"
77
-
78
- return target_point
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes