lightphe 0.0.14__tar.gz → 0.0.15__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.14 → lightphe-0.0.15}/PKG-INFO +46 -21
- {lightphe-0.0.14 → lightphe-0.0.15}/README.md +45 -20
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/__init__.py +7 -47
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/cryptosystems/Benaloh.py +0 -6
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/cryptosystems/DamgardJurik.py +0 -6
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/cryptosystems/ElGamal.py +0 -3
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/cryptosystems/EllipticCurveElGamal.py +5 -18
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/cryptosystems/GoldwasserMicali.py +0 -6
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/cryptosystems/NaccacheStern.py +0 -6
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/cryptosystems/OkamotoUchiyama.py +0 -10
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/cryptosystems/Paillier.py +0 -6
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/cryptosystems/RSA.py +0 -12
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/models/Homomorphic.py +15 -10
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe.egg-info/PKG-INFO +46 -21
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe.egg-info/SOURCES.txt +0 -13
- lightphe-0.0.14/requirements.txt → lightphe-0.0.15/lightphe.egg-info/requires.txt +2 -1
- lightphe-0.0.14/lightphe.egg-info/requires.txt → lightphe-0.0.15/requirements.txt +1 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/setup.py +1 -1
- lightphe-0.0.15/tests/test_ellipticcurveelgamal.py +134 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_okamoto.py +1 -1
- lightphe-0.0.14/lightphe/commons/binary_operations.py +0 -147
- lightphe-0.0.14/lightphe/elliptic_curve_forms/edwards.py +0 -89
- lightphe-0.0.14/lightphe/elliptic_curve_forms/koblitz.py +0 -166
- lightphe-0.0.14/lightphe/elliptic_curve_forms/weierstrass.py +0 -120
- lightphe-0.0.14/lightphe/models/Curve.py +0 -29
- lightphe-0.0.14/lightphe/models/EllipticCurve.py +0 -196
- lightphe-0.0.14/lightphe/standard_curves/__init__.py +0 -0
- lightphe-0.0.14/lightphe/standard_curves/edwards.py +0 -144
- lightphe-0.0.14/lightphe/standard_curves/inventory.py +0 -83
- lightphe-0.0.14/lightphe/standard_curves/koblitz.py +0 -433
- lightphe-0.0.14/lightphe/standard_curves/weierstrass.py +0 -936
- lightphe-0.0.14/tests/__init__.py +0 -0
- lightphe-0.0.14/tests/test_ellipticcurve.py +0 -75
- lightphe-0.0.14/tests/test_ellipticcurveelgamal.py +0 -263
- {lightphe-0.0.14 → lightphe-0.0.15}/LICENSE +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/commons/__init__.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/commons/logger.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/commons/phe_utils.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/cryptosystems/__init__.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/models/Algorithm.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/models/Ciphertext.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe/models/Tensor.py +0 -0
- {lightphe-0.0.14/lightphe/elliptic_curve_forms → lightphe-0.0.15/lightphe/models}/__init__.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe.egg-info/dependency_links.txt +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/lightphe.egg-info/top_level.txt +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/setup.cfg +0 -0
- {lightphe-0.0.14/lightphe/models → lightphe-0.0.15/tests}/__init__.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_benaloh.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_ciphertexts.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_cloud.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_damgard.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_elgamal.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_exporting_keys.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_goldwasser.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_keys.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_naccache.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_paillier.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_rsa.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/tests/test_salary.py +0 -0
- {lightphe-0.0.14 → lightphe-0.0.15}/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.15
|
|
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
|
|
@@ -252,11 +252,9 @@ ECC is a powerful public-key cryptosystem based on the algebraic structure of el
|
|
|
252
252
|
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.
|
|
253
253
|
|
|
254
254
|
```python
|
|
255
|
-
forms = ["weierstass", "edwards", "koblitz"]
|
|
256
|
-
|
|
257
255
|
phe = LightPHE(
|
|
258
256
|
algorithm_name="EllipticCurve-ElGamal",
|
|
259
|
-
form=
|
|
257
|
+
form="edwards", # or weierstrass, koblitz
|
|
260
258
|
)
|
|
261
259
|
```
|
|
262
260
|
|
|
@@ -264,7 +262,7 @@ One of the crucial factors that define the security level of an elliptic curve c
|
|
|
264
262
|
|
|
265
263
|
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.
|
|
266
264
|
|
|
267
|
-
See [`curves`](https://github.com/serengil/
|
|
265
|
+
See [`curves`](https://github.com/serengil/LightECC) page for a list of all supported forms, curves and their details.
|
|
268
266
|
|
|
269
267
|
### Vector Embeddings and Tensors
|
|
270
268
|
|
|
@@ -343,24 +341,42 @@ Many machine learning models rely on a two-tower architecture, including facial
|
|
|
343
341
|
For example, suppose all facial embeddings in your database are encrypted on-prem in advance. When verifying an identity, the attempted facial embedding is generated on an edge device or in the cloud. You can compute the encrypted similarity by performing a dot product between the encrypted vector and the plain vector, ensuring secure comparison without decrypting sensitive data. Only additively homomorphic cryptosystems offer encrypted similarity calculation.
|
|
344
342
|
|
|
345
343
|
```python
|
|
346
|
-
#
|
|
347
|
-
cs = LightPHE("Paillier")
|
|
348
|
-
|
|
349
|
-
# define plain vectors - suppose those are l2 normalized already
|
|
344
|
+
# define a plain vectors for source and target
|
|
350
345
|
alpha = [7.1, 5.2, 5.3, 2.4, 3.5, 4.6] # On-prem vector (user tower)
|
|
351
346
|
beta = [5.6, 3.7, 2.8, 4, 0, 5.9] # Cloud vector (item tower)
|
|
347
|
+
expected_similarity = sum(x * y for x, y in zip(alpha, beta))
|
|
352
348
|
|
|
353
|
-
#
|
|
349
|
+
# build an additively homomorphic cryptosystem (e.g. Paillier) on-prem
|
|
350
|
+
cs = LightPHE(algorithm_name = "Paillier", precision = 19)
|
|
351
|
+
|
|
352
|
+
# export keys
|
|
353
|
+
cs.export_keys("secret.txt")
|
|
354
|
+
cs.export_keys("public.txt", public=True)
|
|
355
|
+
|
|
356
|
+
# encrypt source embedding
|
|
354
357
|
encrypted_alpha = cs.encrypt(alpha)
|
|
355
358
|
|
|
356
|
-
#
|
|
359
|
+
# remove cryptosystem and plain alpha not to be leaked in cloud
|
|
360
|
+
del cs, alpha
|
|
361
|
+
|
|
362
|
+
# restore the cryptosystem in cloud with only public key
|
|
363
|
+
cloud_cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "public.txt")
|
|
364
|
+
|
|
365
|
+
# dot product of encrypted and plain embedding pair
|
|
357
366
|
encrypted_cosine_similarity = encrypted_alpha @ beta
|
|
358
367
|
|
|
359
|
-
# decrypt
|
|
360
|
-
|
|
368
|
+
# computed by the cloud but cloud cannot decrypt it
|
|
369
|
+
with pytest.raises(ValueError, match="must have private key"):
|
|
370
|
+
cloud_cs.decrypt(encrypted_cosine_similarity)
|
|
371
|
+
|
|
372
|
+
# restore the cryptosystem on-prem with secret key
|
|
373
|
+
cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "secret.txt")
|
|
374
|
+
|
|
375
|
+
# decrypt similarity (on prem)
|
|
376
|
+
calculated_similarity = cs.decrypt(encrypted_cosine_similarity)[0]
|
|
361
377
|
|
|
362
378
|
# proof of work
|
|
363
|
-
assert abs(
|
|
379
|
+
assert abs(calculated_similarity - expected_similarity) < 1e-2
|
|
364
380
|
```
|
|
365
381
|
|
|
366
382
|
# Contributing
|
|
@@ -390,13 +406,22 @@ Also, your company's logo will be shown on README on GitHub if you become sponso
|
|
|
390
406
|
Please cite LightPHE in your publications if it helps your research. Here is its BibTex entry:
|
|
391
407
|
|
|
392
408
|
```BibTeX
|
|
393
|
-
@
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
409
|
+
@article{serengil2024lightphe,
|
|
410
|
+
title={LightPHE: Integrating Partially Homomorphic Encryption into Python with Extensive Cloud Environment Evaluations},
|
|
411
|
+
author={Serengil, Sefik Ilkin and Ozpinar, Alper},
|
|
412
|
+
journal={arXiv preprint arXiv:2408.05219},
|
|
413
|
+
note={doi: 10.48550/arXiv.2408.05219. [Online]. Available: \url{https://arxiv.org/abs/2408.05219}},
|
|
414
|
+
year={2025}
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
```BibTeX
|
|
419
|
+
@article{serengil2025vectorsimilarity,
|
|
420
|
+
title={Encrypted Vector Similarity Computations Using Partially Homomorphic Encryption: Applications and Performance Analysis},
|
|
421
|
+
author={Serengil, Sefik and Ozpinar, Alper},
|
|
422
|
+
journal={arXiv preprint arXiv:2503.05850},
|
|
423
|
+
note={doi: 10.48550/arXiv.2503.05850. [Online]. Available: \url{https://arxiv.org/abs/2503.05850}},
|
|
424
|
+
year={2025}
|
|
400
425
|
}
|
|
401
426
|
```
|
|
402
427
|
|
|
@@ -236,11 +236,9 @@ ECC is a powerful public-key cryptosystem based on the algebraic structure of el
|
|
|
236
236
|
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.
|
|
237
237
|
|
|
238
238
|
```python
|
|
239
|
-
forms = ["weierstass", "edwards", "koblitz"]
|
|
240
|
-
|
|
241
239
|
phe = LightPHE(
|
|
242
240
|
algorithm_name="EllipticCurve-ElGamal",
|
|
243
|
-
form=
|
|
241
|
+
form="edwards", # or weierstrass, koblitz
|
|
244
242
|
)
|
|
245
243
|
```
|
|
246
244
|
|
|
@@ -248,7 +246,7 @@ One of the crucial factors that define the security level of an elliptic curve c
|
|
|
248
246
|
|
|
249
247
|
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.
|
|
250
248
|
|
|
251
|
-
See [`curves`](https://github.com/serengil/
|
|
249
|
+
See [`curves`](https://github.com/serengil/LightECC) page for a list of all supported forms, curves and their details.
|
|
252
250
|
|
|
253
251
|
### Vector Embeddings and Tensors
|
|
254
252
|
|
|
@@ -327,24 +325,42 @@ Many machine learning models rely on a two-tower architecture, including facial
|
|
|
327
325
|
For example, suppose all facial embeddings in your database are encrypted on-prem in advance. When verifying an identity, the attempted facial embedding is generated on an edge device or in the cloud. You can compute the encrypted similarity by performing a dot product between the encrypted vector and the plain vector, ensuring secure comparison without decrypting sensitive data. Only additively homomorphic cryptosystems offer encrypted similarity calculation.
|
|
328
326
|
|
|
329
327
|
```python
|
|
330
|
-
#
|
|
331
|
-
cs = LightPHE("Paillier")
|
|
332
|
-
|
|
333
|
-
# define plain vectors - suppose those are l2 normalized already
|
|
328
|
+
# define a plain vectors for source and target
|
|
334
329
|
alpha = [7.1, 5.2, 5.3, 2.4, 3.5, 4.6] # On-prem vector (user tower)
|
|
335
330
|
beta = [5.6, 3.7, 2.8, 4, 0, 5.9] # Cloud vector (item tower)
|
|
331
|
+
expected_similarity = sum(x * y for x, y in zip(alpha, beta))
|
|
336
332
|
|
|
337
|
-
#
|
|
333
|
+
# build an additively homomorphic cryptosystem (e.g. Paillier) on-prem
|
|
334
|
+
cs = LightPHE(algorithm_name = "Paillier", precision = 19)
|
|
335
|
+
|
|
336
|
+
# export keys
|
|
337
|
+
cs.export_keys("secret.txt")
|
|
338
|
+
cs.export_keys("public.txt", public=True)
|
|
339
|
+
|
|
340
|
+
# encrypt source embedding
|
|
338
341
|
encrypted_alpha = cs.encrypt(alpha)
|
|
339
342
|
|
|
340
|
-
#
|
|
343
|
+
# remove cryptosystem and plain alpha not to be leaked in cloud
|
|
344
|
+
del cs, alpha
|
|
345
|
+
|
|
346
|
+
# restore the cryptosystem in cloud with only public key
|
|
347
|
+
cloud_cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "public.txt")
|
|
348
|
+
|
|
349
|
+
# dot product of encrypted and plain embedding pair
|
|
341
350
|
encrypted_cosine_similarity = encrypted_alpha @ beta
|
|
342
351
|
|
|
343
|
-
# decrypt
|
|
344
|
-
|
|
352
|
+
# computed by the cloud but cloud cannot decrypt it
|
|
353
|
+
with pytest.raises(ValueError, match="must have private key"):
|
|
354
|
+
cloud_cs.decrypt(encrypted_cosine_similarity)
|
|
355
|
+
|
|
356
|
+
# restore the cryptosystem on-prem with secret key
|
|
357
|
+
cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "secret.txt")
|
|
358
|
+
|
|
359
|
+
# decrypt similarity (on prem)
|
|
360
|
+
calculated_similarity = cs.decrypt(encrypted_cosine_similarity)[0]
|
|
345
361
|
|
|
346
362
|
# proof of work
|
|
347
|
-
assert abs(
|
|
363
|
+
assert abs(calculated_similarity - expected_similarity) < 1e-2
|
|
348
364
|
```
|
|
349
365
|
|
|
350
366
|
# Contributing
|
|
@@ -374,13 +390,22 @@ Also, your company's logo will be shown on README on GitHub if you become sponso
|
|
|
374
390
|
Please cite LightPHE in your publications if it helps your research. Here is its BibTex entry:
|
|
375
391
|
|
|
376
392
|
```BibTeX
|
|
377
|
-
@
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
393
|
+
@article{serengil2024lightphe,
|
|
394
|
+
title={LightPHE: Integrating Partially Homomorphic Encryption into Python with Extensive Cloud Environment Evaluations},
|
|
395
|
+
author={Serengil, Sefik Ilkin and Ozpinar, Alper},
|
|
396
|
+
journal={arXiv preprint arXiv:2408.05219},
|
|
397
|
+
note={doi: 10.48550/arXiv.2408.05219. [Online]. Available: \url{https://arxiv.org/abs/2408.05219}},
|
|
398
|
+
year={2025}
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
```BibTeX
|
|
403
|
+
@article{serengil2025vectorsimilarity,
|
|
404
|
+
title={Encrypted Vector Similarity Computations Using Partially Homomorphic Encryption: Applications and Performance Analysis},
|
|
405
|
+
author={Serengil, Sefik and Ozpinar, Alper},
|
|
406
|
+
journal={arXiv preprint arXiv:2503.05850},
|
|
407
|
+
note={doi: 10.48550/arXiv.2503.05850. [Online]. Available: \url{https://arxiv.org/abs/2503.05850}},
|
|
408
|
+
year={2025}
|
|
384
409
|
}
|
|
385
410
|
```
|
|
386
411
|
|
|
@@ -9,13 +9,16 @@ import copy
|
|
|
9
9
|
|
|
10
10
|
# 3rd party dependencies
|
|
11
11
|
from tqdm import tqdm
|
|
12
|
+
from lightecc.interfaces.elliptic_curve import EllipticCurvePoint
|
|
13
|
+
from lightecc.forms.weierstrass import Weierstrass
|
|
14
|
+
from lightecc.forms.edwards import TwistedEdwards
|
|
15
|
+
from lightecc.forms.koblitz import Koblitz
|
|
12
16
|
|
|
13
17
|
# project dependencies
|
|
14
18
|
from lightphe.models.Homomorphic import Homomorphic
|
|
15
19
|
from lightphe.models.Ciphertext import Ciphertext
|
|
16
20
|
from lightphe.models.Algorithm import Algorithm
|
|
17
21
|
from lightphe.models.Tensor import Fraction, EncryptedTensor
|
|
18
|
-
from lightphe.models.EllipticCurve import EllipticCurvePoint
|
|
19
22
|
from lightphe.commons import phe_utils
|
|
20
23
|
from lightphe.commons.logger import Logger
|
|
21
24
|
|
|
@@ -29,16 +32,13 @@ from lightphe.cryptosystems.Benaloh import Benaloh
|
|
|
29
32
|
from lightphe.cryptosystems.NaccacheStern import NaccacheStern
|
|
30
33
|
from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali
|
|
31
34
|
from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal
|
|
32
|
-
from lightphe.elliptic_curve_forms.weierstrass import Weierstrass
|
|
33
|
-
from lightphe.elliptic_curve_forms.edwards import TwistedEdwards
|
|
34
|
-
from lightphe.elliptic_curve_forms.koblitz import Koblitz
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
# pylint: disable=eval-used, simplifiable-if-expression, too-few-public-methods
|
|
38
38
|
|
|
39
39
|
logger = Logger(module="lightphe/__init__.py")
|
|
40
40
|
|
|
41
|
-
VERSION = "0.0.
|
|
41
|
+
VERSION = "0.0.15"
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class LightPHE:
|
|
@@ -94,7 +94,7 @@ class LightPHE:
|
|
|
94
94
|
|
|
95
95
|
def __build_cryptosystem(
|
|
96
96
|
self,
|
|
97
|
-
algorithm_name: str,
|
|
97
|
+
algorithm_name: str = "Paillier",
|
|
98
98
|
keys: Optional[dict] = None,
|
|
99
99
|
key_size: Optional[int] = None,
|
|
100
100
|
form: Optional[str] = None,
|
|
@@ -115,6 +115,7 @@ class LightPHE:
|
|
|
115
115
|
algorithm_name (str): RSA | ElGamal | Exponential-ElGamal | EllipticCurve-ElGamal
|
|
116
116
|
| Paillier | Damgard-Jurik | Okamoto-Uchiyama | Benaloh | Naccache-Stern
|
|
117
117
|
| Goldwasser-Micali | Edwards-ElGamal
|
|
118
|
+
Default is Paillier.
|
|
118
119
|
keys (dict): optional private-public key pair
|
|
119
120
|
key_file (str): if keys are exported, you can load them into cryptosystem
|
|
120
121
|
key_size (int): key size in bits
|
|
@@ -464,44 +465,3 @@ def encrypt_float(
|
|
|
464
465
|
logger.error(f"Exception while running encrypt_float: {str(err)}")
|
|
465
466
|
logger.error(traceback.format_exc())
|
|
466
467
|
raise err
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
class ECC:
|
|
470
|
-
__version__ = VERSION
|
|
471
|
-
|
|
472
|
-
def __init__(
|
|
473
|
-
self, form_name: Optional[str] = None, curve_name: Optional[str] = None
|
|
474
|
-
):
|
|
475
|
-
"""
|
|
476
|
-
Construct an Elliptic Curve over a finite field (prime or binary)
|
|
477
|
-
Args:
|
|
478
|
-
form_name (str): specifies the form of the elliptic curve.
|
|
479
|
-
Options: 'weierstrass' (default), 'edwards', 'koblitz'.
|
|
480
|
-
curve_name (str): specifies the elliptic curve to use.
|
|
481
|
-
Options:
|
|
482
|
-
- e.g. ed25519, ed448 for edwards form
|
|
483
|
-
- e.g. secp256k1 for weierstrass form
|
|
484
|
-
- e.g. k-409 for koblitz form
|
|
485
|
-
List of all available curves:
|
|
486
|
-
github.com/serengil/LightPHE/blob/master/lightphe/elliptic_curve_forms/README.md
|
|
487
|
-
"""
|
|
488
|
-
if form_name is None or form_name == "weierstrass":
|
|
489
|
-
self.curve = Weierstrass(curve=curve_name)
|
|
490
|
-
elif form_name in "edwards":
|
|
491
|
-
self.curve = TwistedEdwards(curve=curve_name)
|
|
492
|
-
elif form_name in "koblitz":
|
|
493
|
-
self.curve = Koblitz(curve=curve_name)
|
|
494
|
-
else:
|
|
495
|
-
raise ValueError(f"unimplemented curve form - {form_name}")
|
|
496
|
-
|
|
497
|
-
# base point
|
|
498
|
-
self.G = EllipticCurvePoint(self.curve.G[0], self.curve.G[1], self.curve)
|
|
499
|
-
|
|
500
|
-
# order of the curve
|
|
501
|
-
self.n = self.curve.n
|
|
502
|
-
|
|
503
|
-
# point at infinity or neutral / identity element
|
|
504
|
-
self.O = EllipticCurvePoint(self.curve.O[0], self.curve.O[1], self.curve)
|
|
505
|
-
|
|
506
|
-
# modulo
|
|
507
|
-
self.modulo = self.curve.modulo
|
|
@@ -169,12 +169,6 @@ class Benaloh(Homomorphic):
|
|
|
169
169
|
n = self.keys["public_key"]["n"]
|
|
170
170
|
return (ciphertext1 * ciphertext2) % n
|
|
171
171
|
|
|
172
|
-
def multiply(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
173
|
-
raise ValueError("Benaloh is not homomorphic with respect to the multiplication")
|
|
174
|
-
|
|
175
|
-
def xor(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
176
|
-
raise ValueError("Benaloh is not homomorphic with respect to the exclusive or")
|
|
177
|
-
|
|
178
172
|
def multiply_by_contant(self, ciphertext: int, constant: int) -> int:
|
|
179
173
|
"""
|
|
180
174
|
Multiply a ciphertext with a plain constant.
|
|
@@ -125,12 +125,6 @@ class DamgardJurik(Homomorphic):
|
|
|
125
125
|
modulo = pow(n, s + 1)
|
|
126
126
|
return (ciphertext1 * ciphertext2) % modulo
|
|
127
127
|
|
|
128
|
-
def multiply(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
129
|
-
raise ValueError("Damgard-Jurik is not homomorphic with respect to the multiplication")
|
|
130
|
-
|
|
131
|
-
def xor(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
132
|
-
raise ValueError("Damgard-Jurik is not homomorphic with respect to the exclusive or")
|
|
133
|
-
|
|
134
128
|
def multiply_by_contant(self, ciphertext: int, constant: int) -> int:
|
|
135
129
|
"""
|
|
136
130
|
Multiply a ciphertext by a known plain constant
|
|
@@ -169,9 +169,6 @@ class ElGamal(Homomorphic):
|
|
|
169
169
|
p = self.keys["public_key"]["p"]
|
|
170
170
|
return (ciphertext1[0] * ciphertext2[0]) % p, (ciphertext1[1] * ciphertext2[1]) % p
|
|
171
171
|
|
|
172
|
-
def xor(self, ciphertext1: tuple, ciphertext2: tuple) -> int:
|
|
173
|
-
raise ValueError("ElGamal is not homomorphic with respect to the exclusive or")
|
|
174
|
-
|
|
175
172
|
def multiply_by_contant(self, ciphertext: tuple, constant: int) -> tuple:
|
|
176
173
|
if self.exponential is False:
|
|
177
174
|
raise ValueError("ElGamal is not supporting multiplying ciphertext by a known constant")
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
import random
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
+
# 3rd party dependencies
|
|
6
|
+
from lightecc import LightECC as ECC
|
|
7
|
+
from lightecc.interfaces.elliptic_curve import EllipticCurvePoint
|
|
8
|
+
|
|
5
9
|
# project dependencies
|
|
6
10
|
from lightphe.models.Homomorphic import Homomorphic
|
|
7
|
-
from lightphe.models.EllipticCurve import EllipticCurvePoint
|
|
8
11
|
from lightphe.commons.logger import Logger
|
|
9
12
|
|
|
10
13
|
logger = Logger(module="lightphe/cryptosystems/EllipticCurveElGamal.py")
|
|
@@ -40,9 +43,6 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
40
43
|
- secp256k1 for weierstrass form
|
|
41
44
|
This parameter is only used if `algorithm_name` is 'EllipticCurve-ElGamal'.
|
|
42
45
|
"""
|
|
43
|
-
# to avoid circular import, import ECC class here
|
|
44
|
-
from lightphe import ECC
|
|
45
|
-
|
|
46
46
|
self.ecc = ECC(form_name=form, curve_name=curve)
|
|
47
47
|
|
|
48
48
|
self.keys = keys or self.generate_keys(key_size or self.ecc.n.bit_length())
|
|
@@ -142,11 +142,6 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
142
142
|
|
|
143
143
|
return s_prime / self.ecc.G
|
|
144
144
|
|
|
145
|
-
def multiply(self, ciphertext1: tuple, ciphertext2: tuple) -> tuple:
|
|
146
|
-
raise ValueError(
|
|
147
|
-
"Elliptic Curve ElGamal is not homomorphic with respect to the multiplication"
|
|
148
|
-
)
|
|
149
|
-
|
|
150
145
|
def add(self, ciphertext1: tuple, ciphertext2: tuple) -> tuple:
|
|
151
146
|
"""
|
|
152
147
|
Perform homomorphic addition on encrypted data
|
|
@@ -171,10 +166,7 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
171
166
|
|
|
172
167
|
return a.get_point(), b.get_point()
|
|
173
168
|
|
|
174
|
-
|
|
175
|
-
raise ValueError(
|
|
176
|
-
"Elliptic Curve ElGamal is not homomorphic with respect to the exclusive or"
|
|
177
|
-
)
|
|
169
|
+
|
|
178
170
|
|
|
179
171
|
def multiply_by_contant(self, ciphertext: tuple, constant: int) -> tuple:
|
|
180
172
|
"""
|
|
@@ -198,8 +190,3 @@ class EllipticCurveElGamal(Homomorphic):
|
|
|
198
190
|
Q_prime = Q * constant
|
|
199
191
|
|
|
200
192
|
return P_prime.get_point(), Q_prime.get_point()
|
|
201
|
-
|
|
202
|
-
def reencrypt(self, ciphertext: tuple) -> tuple:
|
|
203
|
-
raise ValueError(
|
|
204
|
-
"Elliptic Curve ElGamal does not support regeneration of ciphertext"
|
|
205
|
-
)
|
|
@@ -193,9 +193,3 @@ class GoldwasserMicali(Homomorphic):
|
|
|
193
193
|
ciphertext3.append((c1 * c2) % self.ciphertext_modulo)
|
|
194
194
|
|
|
195
195
|
return ciphertext3
|
|
196
|
-
|
|
197
|
-
def multiply_by_contant(self, ciphertext: int, constant: int):
|
|
198
|
-
raise ValueError("Goldwasser-Micali does not support multiplying with constant")
|
|
199
|
-
|
|
200
|
-
def reencrypt(self, ciphertext: int):
|
|
201
|
-
raise ValueError("Goldwasser-Micali does not support re-encryption")
|
|
@@ -257,12 +257,6 @@ class NaccacheStern(Homomorphic):
|
|
|
257
257
|
"""
|
|
258
258
|
return (ciphertext1 * ciphertext2) % self.ciphertext_modulo
|
|
259
259
|
|
|
260
|
-
def multiply(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
261
|
-
raise ValueError("Naccache-Stern is not homomorphic with respect to the multiplication")
|
|
262
|
-
|
|
263
|
-
def xor(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
264
|
-
raise ValueError("Naccache-Stern is not homomorphic with respect to the exclusive or")
|
|
265
|
-
|
|
266
260
|
def multiply_by_contant(self, ciphertext: int, constant: int) -> int:
|
|
267
261
|
"""
|
|
268
262
|
Multiply a ciphertext with a plain constant.
|
|
@@ -127,16 +127,6 @@ class OkamotoUchiyama(Homomorphic):
|
|
|
127
127
|
n = self.keys["public_key"]["n"]
|
|
128
128
|
return (ciphertext1 * ciphertext2) % n
|
|
129
129
|
|
|
130
|
-
def multiply(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
131
|
-
raise ValueError(
|
|
132
|
-
"Okamoto-Uchiyama is not homomorphic with respect to the multiplication"
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
def xor(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
136
|
-
raise ValueError(
|
|
137
|
-
"Okamoto-Uchiyama is not homomorphic with respect to the exclusive or"
|
|
138
|
-
)
|
|
139
|
-
|
|
140
130
|
def multiply_by_contant(self, ciphertext: int, constant: int) -> int:
|
|
141
131
|
"""
|
|
142
132
|
Multiply a ciphertext with a plain constant.
|
|
@@ -112,12 +112,6 @@ class Paillier(Homomorphic):
|
|
|
112
112
|
n = self.keys["public_key"]["n"]
|
|
113
113
|
return (ciphertext1 * ciphertext2) % (n * n)
|
|
114
114
|
|
|
115
|
-
def multiply(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
116
|
-
raise ValueError("Paillier is not homomorphic with respect to the multiplication")
|
|
117
|
-
|
|
118
|
-
def xor(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
119
|
-
raise ValueError("Paillier is not homomorphic with respect to the exclusive or")
|
|
120
|
-
|
|
121
115
|
def multiply_by_contant(self, ciphertext: int, constant: int) -> int:
|
|
122
116
|
"""
|
|
123
117
|
Multiply a ciphertext with a plain constant.
|
|
@@ -129,15 +129,3 @@ class RSA(Homomorphic):
|
|
|
129
129
|
"""
|
|
130
130
|
n = self.keys["public_key"]["n"]
|
|
131
131
|
return (ciphertext1 * ciphertext2) % n
|
|
132
|
-
|
|
133
|
-
def add(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
134
|
-
raise ValueError("RSA is not homomorphic with respect to the addition")
|
|
135
|
-
|
|
136
|
-
def xor(self, ciphertext1: int, ciphertext2: int) -> int:
|
|
137
|
-
raise ValueError("RSA is not homomorphic with respect to the exclusive or")
|
|
138
|
-
|
|
139
|
-
def multiply_by_contant(self, ciphertext: int, constant: int) -> int:
|
|
140
|
-
raise ValueError("RSA is not supporting multiplying ciphertext by a known constant")
|
|
141
|
-
|
|
142
|
-
def reencrypt(self, ciphertext: int) -> int:
|
|
143
|
-
raise ValueError("RSA does not support re-encryption")
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
# built-in dependencies
|
|
1
2
|
from typing import Optional, Union
|
|
2
3
|
from abc import ABC, abstractmethod
|
|
3
4
|
|
|
5
|
+
# project dependencies
|
|
6
|
+
from lightphe.models.Algorithm import Algorithm
|
|
7
|
+
|
|
4
8
|
# Signature for supported cryptosystems
|
|
5
9
|
|
|
6
10
|
|
|
@@ -27,26 +31,27 @@ class Homomorphic(ABC):
|
|
|
27
31
|
def decrypt(self, ciphertext: Union[int, tuple, list]) -> int:
|
|
28
32
|
pass
|
|
29
33
|
|
|
30
|
-
@abstractmethod
|
|
31
34
|
def add(
|
|
32
35
|
self, ciphertext1: Union[int, tuple, list], ciphertext2: Union[int, tuple, list]
|
|
33
36
|
) -> Union[int, tuple, list]:
|
|
34
|
-
|
|
37
|
+
raise ValueError(f"{self.get_algorithm_name()} is not homomorphic with respect to the addition")
|
|
35
38
|
|
|
36
|
-
@abstractmethod
|
|
37
39
|
def multiply(
|
|
38
40
|
self, ciphertext1: Union[int, tuple, list], ciphertext2: Union[int, tuple, list]
|
|
39
41
|
) -> Union[int, tuple]:
|
|
40
|
-
|
|
42
|
+
raise ValueError(f"{self.get_algorithm_name()} is not homomorphic with respect to the multiplication")
|
|
41
43
|
|
|
42
|
-
@abstractmethod
|
|
43
44
|
def xor(self, ciphertext1: list, ciphertext2: list) -> list:
|
|
44
|
-
|
|
45
|
+
raise ValueError(f"{self.get_algorithm_name()} is not homomorphic with respect to the exclusive or")
|
|
45
46
|
|
|
46
|
-
@abstractmethod
|
|
47
47
|
def multiply_by_contant(self, ciphertext: Union[int, tuple, list], constant: int) -> int:
|
|
48
|
-
|
|
48
|
+
raise ValueError(f"{self.get_algorithm_name()} is not supporting multiplying ciphertext by a known constant")
|
|
49
49
|
|
|
50
|
-
@abstractmethod
|
|
51
50
|
def reencrypt(self, ciphertext: Union[int, tuple, list]) -> Union[int, tuple, list]:
|
|
52
|
-
|
|
51
|
+
raise ValueError(f"{self.get_algorithm_name()} does not support re-encryption")
|
|
52
|
+
|
|
53
|
+
def get_algorithm_name(self) -> str:
|
|
54
|
+
class_name = self.__class__.__name__
|
|
55
|
+
algorithm_name = getattr(Algorithm, class_name, None)
|
|
56
|
+
assert isinstance(algorithm_name, str), f"Algorithm name for {class_name} is not defined"
|
|
57
|
+
return algorithm_name
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lightphe
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.15
|
|
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
|
|
@@ -252,11 +252,9 @@ ECC is a powerful public-key cryptosystem based on the algebraic structure of el
|
|
|
252
252
|
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.
|
|
253
253
|
|
|
254
254
|
```python
|
|
255
|
-
forms = ["weierstass", "edwards", "koblitz"]
|
|
256
|
-
|
|
257
255
|
phe = LightPHE(
|
|
258
256
|
algorithm_name="EllipticCurve-ElGamal",
|
|
259
|
-
form=
|
|
257
|
+
form="edwards", # or weierstrass, koblitz
|
|
260
258
|
)
|
|
261
259
|
```
|
|
262
260
|
|
|
@@ -264,7 +262,7 @@ One of the crucial factors that define the security level of an elliptic curve c
|
|
|
264
262
|
|
|
265
263
|
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.
|
|
266
264
|
|
|
267
|
-
See [`curves`](https://github.com/serengil/
|
|
265
|
+
See [`curves`](https://github.com/serengil/LightECC) page for a list of all supported forms, curves and their details.
|
|
268
266
|
|
|
269
267
|
### Vector Embeddings and Tensors
|
|
270
268
|
|
|
@@ -343,24 +341,42 @@ Many machine learning models rely on a two-tower architecture, including facial
|
|
|
343
341
|
For example, suppose all facial embeddings in your database are encrypted on-prem in advance. When verifying an identity, the attempted facial embedding is generated on an edge device or in the cloud. You can compute the encrypted similarity by performing a dot product between the encrypted vector and the plain vector, ensuring secure comparison without decrypting sensitive data. Only additively homomorphic cryptosystems offer encrypted similarity calculation.
|
|
344
342
|
|
|
345
343
|
```python
|
|
346
|
-
#
|
|
347
|
-
cs = LightPHE("Paillier")
|
|
348
|
-
|
|
349
|
-
# define plain vectors - suppose those are l2 normalized already
|
|
344
|
+
# define a plain vectors for source and target
|
|
350
345
|
alpha = [7.1, 5.2, 5.3, 2.4, 3.5, 4.6] # On-prem vector (user tower)
|
|
351
346
|
beta = [5.6, 3.7, 2.8, 4, 0, 5.9] # Cloud vector (item tower)
|
|
347
|
+
expected_similarity = sum(x * y for x, y in zip(alpha, beta))
|
|
352
348
|
|
|
353
|
-
#
|
|
349
|
+
# build an additively homomorphic cryptosystem (e.g. Paillier) on-prem
|
|
350
|
+
cs = LightPHE(algorithm_name = "Paillier", precision = 19)
|
|
351
|
+
|
|
352
|
+
# export keys
|
|
353
|
+
cs.export_keys("secret.txt")
|
|
354
|
+
cs.export_keys("public.txt", public=True)
|
|
355
|
+
|
|
356
|
+
# encrypt source embedding
|
|
354
357
|
encrypted_alpha = cs.encrypt(alpha)
|
|
355
358
|
|
|
356
|
-
#
|
|
359
|
+
# remove cryptosystem and plain alpha not to be leaked in cloud
|
|
360
|
+
del cs, alpha
|
|
361
|
+
|
|
362
|
+
# restore the cryptosystem in cloud with only public key
|
|
363
|
+
cloud_cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "public.txt")
|
|
364
|
+
|
|
365
|
+
# dot product of encrypted and plain embedding pair
|
|
357
366
|
encrypted_cosine_similarity = encrypted_alpha @ beta
|
|
358
367
|
|
|
359
|
-
# decrypt
|
|
360
|
-
|
|
368
|
+
# computed by the cloud but cloud cannot decrypt it
|
|
369
|
+
with pytest.raises(ValueError, match="must have private key"):
|
|
370
|
+
cloud_cs.decrypt(encrypted_cosine_similarity)
|
|
371
|
+
|
|
372
|
+
# restore the cryptosystem on-prem with secret key
|
|
373
|
+
cs = LightPHE(algorithm_name = "Paillier", precision = 19, key_file = "secret.txt")
|
|
374
|
+
|
|
375
|
+
# decrypt similarity (on prem)
|
|
376
|
+
calculated_similarity = cs.decrypt(encrypted_cosine_similarity)[0]
|
|
361
377
|
|
|
362
378
|
# proof of work
|
|
363
|
-
assert abs(
|
|
379
|
+
assert abs(calculated_similarity - expected_similarity) < 1e-2
|
|
364
380
|
```
|
|
365
381
|
|
|
366
382
|
# Contributing
|
|
@@ -390,13 +406,22 @@ Also, your company's logo will be shown on README on GitHub if you become sponso
|
|
|
390
406
|
Please cite LightPHE in your publications if it helps your research. Here is its BibTex entry:
|
|
391
407
|
|
|
392
408
|
```BibTeX
|
|
393
|
-
@
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
409
|
+
@article{serengil2024lightphe,
|
|
410
|
+
title={LightPHE: Integrating Partially Homomorphic Encryption into Python with Extensive Cloud Environment Evaluations},
|
|
411
|
+
author={Serengil, Sefik Ilkin and Ozpinar, Alper},
|
|
412
|
+
journal={arXiv preprint arXiv:2408.05219},
|
|
413
|
+
note={doi: 10.48550/arXiv.2408.05219. [Online]. Available: \url{https://arxiv.org/abs/2408.05219}},
|
|
414
|
+
year={2025}
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
```BibTeX
|
|
419
|
+
@article{serengil2025vectorsimilarity,
|
|
420
|
+
title={Encrypted Vector Similarity Computations Using Partially Homomorphic Encryption: Applications and Performance Analysis},
|
|
421
|
+
author={Serengil, Sefik and Ozpinar, Alper},
|
|
422
|
+
journal={arXiv preprint arXiv:2503.05850},
|
|
423
|
+
note={doi: 10.48550/arXiv.2503.05850. [Online]. Available: \url{https://arxiv.org/abs/2503.05850}},
|
|
424
|
+
year={2025}
|
|
400
425
|
}
|
|
401
426
|
```
|
|
402
427
|
|