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.
- {lightphe-0.0.10 → lightphe-0.0.11}/PKG-INFO +30 -13
- {lightphe-0.0.10 → lightphe-0.0.11}/README.md +29 -12
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/__init__.py +58 -4
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/EllipticCurveElGamal.py +56 -44
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/elliptic_curve_forms/koblitz.py +3 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/elliptic_curve_forms/weierstrass.py +3 -0
- lightphe-0.0.11/lightphe/models/EllipticCurve.py +160 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe.egg-info/PKG-INFO +30 -13
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe.egg-info/SOURCES.txt +1 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/setup.py +1 -1
- lightphe-0.0.11/tests/test_ellipticcurve.py +75 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_ellipticcurveelgamal.py +86 -35
- lightphe-0.0.10/lightphe/models/EllipticCurve.py +0 -78
- {lightphe-0.0.10 → lightphe-0.0.11}/LICENSE +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/commons/__init__.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/commons/binary_operations.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/commons/logger.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/commons/phe_utils.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/Benaloh.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/DamgardJurik.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/ElGamal.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/GoldwasserMicali.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/NaccacheStern.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/OkamotoUchiyama.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/Paillier.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/RSA.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/cryptosystems/__init__.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/elliptic_curve_forms/__init__.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/elliptic_curve_forms/edwards.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/Algorithm.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/Ciphertext.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/Curve.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/Homomorphic.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/Tensor.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe/models/__init__.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe.egg-info/dependency_links.txt +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe.egg-info/requires.txt +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/lightphe.egg-info/top_level.txt +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/requirements.txt +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/setup.cfg +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/__init__.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_benaloh.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_cloud.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_damgard.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_elgamal.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_exporting_keys.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_goldwasser.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_naccache.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_okamoto.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_paillier.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_rsa.py +0 -0
- {lightphe-0.0.10 → lightphe-0.0.11}/tests/test_salary.py +0 -0
- {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.
|
|
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
|
[](https://github.com/serengil/LightPHE/stargazers)
|
|
23
23
|
[](https://github.com/serengil/LightPHE/actions/workflows/tests.yml)
|
|
24
24
|
[](https://github.com/serengil/LightPHE/blob/master/LICENSE)
|
|
25
|
-
[](https://arxiv.org/abs/2408.05219)
|
|
26
|
+
<!-- [](https://arxiv.org/abs/2408.05219) -->
|
|
26
27
|
|
|
27
28
|
[](https://sefiks.com)
|
|
28
29
|
[](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
|
-
###
|
|
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.
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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=
|
|
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 [`
|
|
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
|
[](https://github.com/serengil/LightPHE/stargazers)
|
|
7
7
|
[](https://github.com/serengil/LightPHE/actions/workflows/tests.yml)
|
|
8
8
|
[](https://github.com/serengil/LightPHE/blob/master/LICENSE)
|
|
9
|
-
[](https://arxiv.org/abs/2408.05219)
|
|
10
|
+
<!-- [](https://arxiv.org/abs/2408.05219) -->
|
|
10
11
|
|
|
11
12
|
[](https://sefiks.com)
|
|
12
13
|
[](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
|
-
###
|
|
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.
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
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=
|
|
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 [`
|
|
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.
|
|
18
|
-
from lightphe.
|
|
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__ =
|
|
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.
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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.
|
|
55
|
-
self.ciphertext_modulo = self.
|
|
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.
|
|
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.
|
|
114
|
-
|
|
115
|
-
c1 = self.curve.double_and_add(G=G, k=r)
|
|
106
|
+
s = self.ecc.G * plaintext
|
|
116
107
|
|
|
117
|
-
|
|
118
|
-
c2 =
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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.
|
|
138
|
+
G = self.ecc.G
|
|
146
139
|
k = 2
|
|
147
140
|
while True:
|
|
148
|
-
G =
|
|
149
|
-
|
|
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.
|
|
147
|
+
if k > self.ecc.n:
|
|
153
148
|
raise ValueError(
|
|
154
|
-
f"Cannot restore scalar from {s_prime} = k x {self.
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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(
|
|
@@ -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.
|
|
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
|
[](https://github.com/serengil/LightPHE/stargazers)
|
|
23
23
|
[](https://github.com/serengil/LightPHE/actions/workflows/tests.yml)
|
|
24
24
|
[](https://github.com/serengil/LightPHE/blob/master/LICENSE)
|
|
25
|
-
[](https://arxiv.org/abs/2408.05219)
|
|
26
|
+
<!-- [](https://arxiv.org/abs/2408.05219) -->
|
|
26
27
|
|
|
27
28
|
[](https://sefiks.com)
|
|
28
29
|
[](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
|
-
###
|
|
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.
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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=
|
|
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 [`
|
|
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
|
|
|
@@ -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.
|
|
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(
|
|
27
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
168
|
-
|
|
177
|
+
if k in [0, cs.ecc.curve.n]:
|
|
178
|
+
assert P == cs.ecc.curve.O
|
|
169
179
|
|
|
170
|
-
|
|
180
|
+
logger.info(f"✅ Test elliptic curve cyclic group on test {form} curve done.")
|
|
171
181
|
|
|
172
182
|
|
|
173
|
-
def
|
|
174
|
-
cs = EllipticCurveElGamal(form="weierstrass", curve="test-curve
|
|
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 +
|
|
177
|
-
P = cs.curve.add_points(
|
|
178
|
-
cs.curve.double_and_add(cs.curve.G, 20),
|
|
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
|
-
|
|
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
|
|
195
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|