From f08cef91fdcfadc03f4e4155e0e6d16cf2faa6ab Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Tue, 12 Dec 2023 17:33:01 +0000 Subject: [PATCH 1/8] broad exception caught moved to module --- .pylintrc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.pylintrc b/.pylintrc index f864635..4ea72fc 100644 --- a/.pylintrc +++ b/.pylintrc @@ -440,8 +440,7 @@ disable=raw-checker-failed, consider-iterating-dictionary, unexpected-keyword-arg, arguments-differ, - line-too-long, - broad-exception-caught + line-too-long # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option From fa956a155f12202d1494dd604dc89b662c4d5f09 Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Tue, 12 Dec 2023 17:34:01 +0000 Subject: [PATCH 2/8] support to encrypt decrypt tensors --- README.md | 20 ++++++++++++++++++++ lightphe/models/Tensor.py | 34 ++++++++++++++++++++++++++++++++++ tests/test_tensors.py | 26 ++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 lightphe/models/Tensor.py create mode 100644 tests/test_tensors.py diff --git a/README.md b/README.md index 92c3650..8500d28 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,26 @@ with pytest.raises(ValueError, match="Paillier is not homomorphic with respect t 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. +# Encrypt & Decrypt Tensors + +You can encrypt the output tensors of machine learning models with LightPHE. + +```python +cs = LightPHE(algorithm_name="Paillier") + +# define plain tensor +tensor = [1.005, 2.005, 3.005, -4.005, 5.005] + +# encrypt tensor +encrypted_tensors = cs.encrypt(tensor) + +# decrypt tensor +decrypted_tensors = cs.decrypt(encrypted_tensors) + +for i, decrypted_tensor in enumerate(decrypted_tensors): + assert tensor[i] == decrypted_tensor +``` + # Contributing All PRs are more than welcome! If you are planning to contribute a large patch, please create an issue first to get any upfront questions or design decisions out of the way first. diff --git a/lightphe/models/Tensor.py b/lightphe/models/Tensor.py new file mode 100644 index 0000000..1eed18f --- /dev/null +++ b/lightphe/models/Tensor.py @@ -0,0 +1,34 @@ +from typing import Union, List + + +# pylint: disable=too-few-public-methods +class EncryptedTensor: + def __init__( + self, + dividend: Union[int, tuple, list], + divisor: Union[int, tuple, list], + sign: Union[int, tuple, list], + ): + self.dividend = dividend + self.divisor = divisor + self.sign = sign + + def __str__(self): + return f"EncryptedTensor({self.sign} * {self.dividend} / {self.divisor})" + + def __repr__(self): + return self.__str__() + + +class EncryptedTensors: + def __init__(self, encrypted_tensor: List[EncryptedTensor]): + self.encrypted_tensor = encrypted_tensor + + def __str__(self): + results = [] + for i in self.encrypted_tensor: + results.append(f"{i}") + return ", ".join(results) + + def __repr__(self): + return self.__str__() diff --git a/tests/test_tensors.py b/tests/test_tensors.py new file mode 100644 index 0000000..2a390d7 --- /dev/null +++ b/tests/test_tensors.py @@ -0,0 +1,26 @@ +from typing import List +from lightphe.commons import phe_utils +from lightphe import LightPHE + +from lightphe.commons.logger import Logger + +logger = Logger(module="tests/test_tensors.py") + + +def test_tensor_operations(): + cs = LightPHE(algorithm_name="Paillier", key_size=25) + + tensor = [1.005, 2.005, 3.005, -4.005, 5.005] + + encrypted_tensors = cs.encrypt(tensor) + + decrypted_tensors = cs.decrypt(encrypted_tensors) + + for i, decrypted_tensor in enumerate(decrypted_tensors): + assert abs(tensor[i] - decrypted_tensor) <= 0.5 + + logger.info("✅ Tensor tests succeeded") + + # TODO: add homomorphic operations on encrypted tensors + # homomorphic multiplication and scalar multiplication are easy + # but addition requires it to cast int From 05cf020c461d5b15bfa5bdddf6cb9b78d95e88fa Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Tue, 12 Dec 2023 17:34:25 +0000 Subject: [PATCH 3/8] mandatory module added into logger --- lightphe/commons/logger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightphe/commons/logger.py b/lightphe/commons/logger.py index 7411823..53dab62 100644 --- a/lightphe/commons/logger.py +++ b/lightphe/commons/logger.py @@ -6,7 +6,8 @@ class Logger: - def __init__(self): + def __init__(self, module): + self.module = module log_level = os.environ.get("LIGHTPHE_LOG_LEVEL", str(logging.INFO)) try: self.log_level = int(log_level) From 20c419387077fa930971cfb7ebeb57dbac0f9c27 Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Tue, 12 Dec 2023 17:35:07 +0000 Subject: [PATCH 4/8] util function renamed, and fractionizing function added --- lightphe/commons/calculations.py | 23 --------------------- lightphe/commons/phe_utils.py | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 23 deletions(-) delete mode 100644 lightphe/commons/calculations.py create mode 100644 lightphe/commons/phe_utils.py diff --git a/lightphe/commons/calculations.py b/lightphe/commons/calculations.py deleted file mode 100644 index ceb6392..0000000 --- a/lightphe/commons/calculations.py +++ /dev/null @@ -1,23 +0,0 @@ -from lightphe.commons.logger import Logger - -logger = Logger() - -# pylint: disable=no-else-return - - -def parse_int(value, modulo) -> int: - if isinstance(value, int) and value >= 0: - return value - elif isinstance(value, int) and value < 0: - return value % modulo - elif isinstance(value, float) and value >= 0: - decimal_places = len(str(value).split(".")[1]) - scaling_factor = 10**decimal_places - integer_value = int(value * scaling_factor) - logger.debug(f"{integer_value}*{scaling_factor}^-1 mod {modulo}") - return integer_value * pow(scaling_factor, -1, modulo) - elif isinstance(value, float) and value < 0: - # TODO: think and implement this later - raise ValueError("Case constant float and negative not implemented yet") - else: - raise ValueError(f"Unimplemented case for constant type {type(value)}") diff --git a/lightphe/commons/phe_utils.py b/lightphe/commons/phe_utils.py new file mode 100644 index 0000000..264f4d3 --- /dev/null +++ b/lightphe/commons/phe_utils.py @@ -0,0 +1,35 @@ +from typing import Union, Tuple +from lightphe.commons.logger import Logger + +logger = Logger(module="lightphe/commons/phe_utils.py") + +# pylint: disable=no-else-return + + +def parse_int(value: Union[int, float], modulo: int) -> int: + if isinstance(value, int): + result = value % modulo + elif isinstance(value, float) and value >= 0: + dividend, divisor = fractionize(value=value, modulo=modulo) + logger.debug(f"{dividend}*{divisor}^-1 mod {modulo}") + result = (dividend * pow(divisor, -1, modulo)) % modulo + elif isinstance(value, float) and value < 0: + # TODO: think and implement this later + raise ValueError("Case constant float and negative not implemented yet") + else: + raise ValueError(f"Unimplemented case for constant type {type(value)}") + + return result + + +def fractionize(value: float, modulo: int) -> Tuple[int, int]: + decimal_places = len(str(value).split(".")[1]) + scaling_factor = 10**decimal_places + integer_value = int(value * scaling_factor) % modulo + logger.debug(f"{integer_value}*{scaling_factor}^-1 mod {modulo}") + return integer_value, scaling_factor + + +def solve_dlp(): + # TODO: implement this later + pass From 45e501fda1cd92c83d2d34e19b348859a6b98719 Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Tue, 12 Dec 2023 17:35:23 +0000 Subject: [PATCH 5/8] increase version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 46d00f2..33cfe90 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name="lightphe", - version="0.0.4", + version="0.0.5", author="Sefik Ilkin Serengil", author_email="serengil@gmail.com", description="A Lightweight Partially Homomorphic Encryption Library for Python", From f0f63bed99f811db1c48e0ee7dd8d30ddda263d8 Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Tue, 12 Dec 2023 17:36:08 +0000 Subject: [PATCH 6/8] encrypt decrypt tensor method added --- lightphe/__init__.py | 114 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 12 deletions(-) diff --git a/lightphe/__init__.py b/lightphe/__init__.py index 44d316d..f249c8f 100644 --- a/lightphe/__init__.py +++ b/lightphe/__init__.py @@ -1,8 +1,10 @@ import json -from typing import Optional, Union +from typing import Optional, Union, List +from lightphe.models.Homomorphic import Homomorphic from lightphe.models.Ciphertext import Ciphertext from lightphe.models.Algorithm import Algorithm +from lightphe.models.Tensor import EncryptedTensor, EncryptedTensors from lightphe.cryptosystems.RSA import RSA from lightphe.cryptosystems.ElGamal import ElGamal from lightphe.cryptosystems.Paillier import Paillier @@ -12,12 +14,12 @@ from lightphe.cryptosystems.NaccacheStern import NaccacheStern from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal -from lightphe.commons import calculations +from lightphe.commons import phe_utils from lightphe.commons.logger import Logger -# pylint: disable=eval-used +# pylint: disable=eval-used, simplifiable-if-expression -logger = Logger() +logger = Logger(module="lightphe/__init__.py") class LightPHE: @@ -43,11 +45,11 @@ def __init__( if key_file is not None: keys = self.restore_keys(target_file=key_file) - self.cs = self.build_cryptosystem( + self.cs: Homomorphic = self.__build_cryptosystem( algorithm_name=algorithm_name, keys=keys, key_size=key_size ) - def build_cryptosystem( + def __build_cryptosystem( self, algorithm_name: str, keys: Optional[dict] = None, @@ -103,22 +105,29 @@ def build_cryptosystem( raise ValueError(f"unimplemented algorithm - {algorithm_name}") return cs - def encrypt(self, plaintext: Union[int, float]) -> Ciphertext: + def encrypt(self, plaintext: Union[int, float, list]) -> Union[Ciphertext, EncryptedTensors]: """ Encrypt a plaintext with a built cryptosystem Args: - plaintext (int or float): message + plaintext (int, float or tensor): message Returns ciphertext (from lightphe.models.Ciphertext import Ciphertext): encrypted message """ + if self.cs.keys.get("private_key") is None: + raise ValueError("You must have private key to perform encryption") + + if isinstance(plaintext, list): + # then encrypt tensors + return self.__encrypt_tensors(tensor=plaintext) + ciphertext = self.cs.encrypt( - plaintext=calculations.parse_int( - value=plaintext, modulo=self.cs.modulo or self.cs.plaintext_modulo - ) + plaintext=phe_utils.parse_int(value=plaintext, modulo=self.cs.plaintext_modulo) ) return Ciphertext(algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext) - def decrypt(self, ciphertext: Ciphertext) -> int: + def decrypt( + self, ciphertext: Union[Ciphertext, EncryptedTensors] + ) -> Union[int, List[int], List[float]]: """ Decrypt a ciphertext with a buit cryptosystem Args: @@ -126,8 +135,86 @@ def decrypt(self, ciphertext: Ciphertext) -> int: Returns: plaintext (int): restored message """ + if self.cs.keys.get("private_key") is None: + raise ValueError("You must have private key to perform decryption") + + if isinstance(ciphertext, EncryptedTensors): + # then this is encrypted tensor + return self.__decrypt_tensors(encrypted_tensor=ciphertext) + return self.cs.decrypt(ciphertext=ciphertext.value) + def __encrypt_tensors(self, tensor: list) -> EncryptedTensors: + """ + Encrypt a given tensor + Args: + tensor (list of int or float) + Returns + encrypted tensor (list of encrypted tensor object) + """ + encrypted_tensor: List[EncryptedTensor] = [] + for m in tensor: + sign = 1 if m >= 0 else -1 + # get rid of sign anyway + m = m * sign + sign_encrypted = self.cs.encrypt(plaintext=sign) + if isinstance(m, int): + dividend_encrypted = self.cs.encrypt(plaintext=m) + divisor_encrypted = self.cs.encrypt(plaintext=1) + c = EncryptedTensor( + dividend=dividend_encrypted, + divisor=divisor_encrypted, + sign=sign_encrypted, + ) + elif isinstance(m, float): + dividend, divisor = phe_utils.fractionize(value=m, modulo=self.cs.plaintext_modulo) + dividend_encrypted = self.cs.encrypt(plaintext=dividend) + divisor_encrypted = self.cs.encrypt(plaintext=divisor) + c = EncryptedTensor( + dividend=dividend_encrypted, + divisor=divisor_encrypted, + sign=sign_encrypted, + ) + else: + raise ValueError(f"unimplemented type - {type(m)}") + encrypted_tensor.append(c) + return EncryptedTensors(encrypted_tensor=encrypted_tensor) + + def __decrypt_tensors( + self, encrypted_tensor: EncryptedTensors + ) -> Union[List[int], List[float]]: + """ + Decrypt a given encrypted tensor + Args: + encrypted_tensor (list of encrypted tensor) + Returns: + List of plain tensors + """ + plain_tensor = [] + for c in encrypted_tensor.encrypted_tensor: + if isinstance(c, EncryptedTensor) is False: + raise ValueError("Ciphertext items must be EncryptedTensor") + + encrypted_dividend = c.dividend + encrypted_divisor = c.divisor + encrypted_sign = c.sign + + dividend = self.cs.decrypt(ciphertext=encrypted_dividend) + divisor = self.cs.decrypt(ciphertext=encrypted_divisor) + sign = self.cs.decrypt(ciphertext=encrypted_sign) + + if sign == self.cs.plaintext_modulo - 1: + sign = -1 + elif sign == 1: + sign = 1 + else: + raise ValueError("this cannot be true!") + + m = sign * (dividend / divisor) + + plain_tensor.append(m) + return plain_tensor + def regenerate_ciphertext(self, ciphertext: Ciphertext) -> Ciphertext: """ Generate a different ciphertext belonging to same plaintext @@ -136,6 +223,9 @@ def regenerate_ciphertext(self, ciphertext: Ciphertext) -> Ciphertext: Returns: ciphertext (from lightphe.models.Ciphertext import Ciphertext): encrypted message """ + if self.cs.keys.get("private_key") is None: + raise ValueError("You must have private key to perform decryption") + ciphertext_new = self.cs.reencrypt(ciphertext=ciphertext.value) return Ciphertext( algorithm_name=self.algorithm_name, keys=self.cs.keys, value=ciphertext_new From b4e19df49a85235126f548067753f77c888f3a43 Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Tue, 12 Dec 2023 17:36:46 +0000 Subject: [PATCH 7/8] homomorphic operations require public key --- lightphe/models/Ciphertext.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lightphe/models/Ciphertext.py b/lightphe/models/Ciphertext.py index d143b62..27625af 100644 --- a/lightphe/models/Ciphertext.py +++ b/lightphe/models/Ciphertext.py @@ -10,10 +10,10 @@ from lightphe.cryptosystems.NaccacheStern import NaccacheStern from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal -from lightphe.commons import calculations +from lightphe.commons import phe_utils from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="lightphe/models/Ciphertext.py") # pylint: disable=too-few-public-methods, no-else-return @@ -63,6 +63,9 @@ def __add__(self, other: "Ciphertext") -> "Ciphertext": Returns: ciphertext (Ciphertext): homomorphic addition of ciphertext """ + if self.cs.keys.get("public_key") is None: + raise ValueError("You must have public key to perform homomorphic addition") + result = self.cs.add(ciphertext1=self.value, ciphertext2=other.value) return Ciphertext(algorithm_name=self.algorithm_name, keys=self.keys, value=result) @@ -74,15 +77,16 @@ def __mul__(self, other: Union["Ciphertext", int, float]) -> "Ciphertext": Returns homomorphic multiplication of ciphertexts | scalar multiplication of ciphertext """ + if self.cs.keys.get("public_key") is None: + raise ValueError("You must have public key to perform homomorphic multiplication") + if isinstance(other, Ciphertext): # Handle multiplication with another EncryptedObject result = self.cs.multiply(ciphertext1=self.value, ciphertext2=other.value) elif isinstance(other, int): result = self.cs.multiply_by_contant(ciphertext=self.value, constant=other) elif isinstance(other, float): - constant = calculations.parse_int( - value=other, modulo=self.cs.modulo or self.cs.plaintext_modulo - ) + constant = phe_utils.parse_int(value=other, modulo=self.cs.plaintext_modulo) result = self.cs.multiply_by_contant(ciphertext=self.value, constant=constant) else: raise ValueError( @@ -98,10 +102,11 @@ def __rmul__(self, constant: Union[int, float]) -> "Ciphertext": Returns scalar multiplication of ciphertext """ + if self.cs.keys.get("public_key") is None: + raise ValueError("You must have public key to perform scalar multiplication") + if isinstance(constant, float): - constant = calculations.parse_int( - value=constant, modulo=self.cs.modulo or self.cs.plaintext_modulo - ) + constant = phe_utils.parse_int(value=constant, modulo=self.cs.plaintext_modulo) # Handle multiplication with a constant on the right result = self.cs.multiply_by_contant(ciphertext=self.value, constant=constant) @@ -115,5 +120,8 @@ def __xor__(self, other: "Ciphertext") -> "Ciphertext": Returns homomorphic xor of ciphertexts """ + if self.cs.keys.get("public_key") is None: + raise ValueError("You must have public key to perform homomorphic xor") + result = self.cs.xor(ciphertext1=self.value, ciphertext2=other.value) return Ciphertext(algorithm_name=self.algorithm_name, keys=self.keys, value=result) From ca9b98db678623b8b52cd465f16af11b8cc3758b Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Tue, 12 Dec 2023 17:37:06 +0000 Subject: [PATCH 8/8] module name added into loggers --- lightphe/cryptosystems/Benaloh.py | 2 +- lightphe/cryptosystems/DamgardJurik.py | 2 +- lightphe/cryptosystems/ElGamal.py | 5 +++-- lightphe/cryptosystems/EllipticCurveElGamal.py | 5 +++-- lightphe/cryptosystems/GoldwasserMicali.py | 4 +++- lightphe/cryptosystems/NaccacheStern.py | 2 +- lightphe/cryptosystems/OkamotoUchiyama.py | 2 +- lightphe/cryptosystems/Paillier.py | 2 +- lightphe/cryptosystems/RSA.py | 5 +++-- lightphe/elliptic/Weierstrass.py | 1 + lightphe/models/Homomorphic.py | 11 +++++------ tests/__init__.py | 2 +- tests/test_benaloh.py | 2 +- tests/test_cloud.py | 18 +++++++++++++----- tests/test_damgard.py | 2 +- tests/test_elgamal.py | 16 ++++++++-------- tests/test_ellipticcurveelgamal.py | 2 +- tests/test_goldwasser.py | 2 +- tests/test_naccache.py | 2 +- tests/test_okamoto.py | 2 +- tests/test_paillier.py | 5 +---- tests/test_rsa.py | 8 ++++---- tests/test_salary.py | 2 +- 23 files changed, 57 insertions(+), 47 deletions(-) diff --git a/lightphe/cryptosystems/Benaloh.py b/lightphe/cryptosystems/Benaloh.py index 476e196..ddbe46e 100644 --- a/lightphe/cryptosystems/Benaloh.py +++ b/lightphe/cryptosystems/Benaloh.py @@ -5,7 +5,7 @@ from lightphe.models.Homomorphic import Homomorphic from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="lightphe/cryptosystems/Benaloh.py") class Benaloh(Homomorphic): diff --git a/lightphe/cryptosystems/DamgardJurik.py b/lightphe/cryptosystems/DamgardJurik.py index b068041..b9a3cb1 100644 --- a/lightphe/cryptosystems/DamgardJurik.py +++ b/lightphe/cryptosystems/DamgardJurik.py @@ -5,7 +5,7 @@ from lightphe.models.Homomorphic import Homomorphic from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="lightphe/cryptosystems/DamgardJurik.py") class DamgardJurik(Homomorphic): diff --git a/lightphe/cryptosystems/ElGamal.py b/lightphe/cryptosystems/ElGamal.py index 54dcc60..d0f33b7 100644 --- a/lightphe/cryptosystems/ElGamal.py +++ b/lightphe/cryptosystems/ElGamal.py @@ -5,7 +5,7 @@ from lightphe.models.Homomorphic import Homomorphic from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="lightphe/cryptosystems/ElGamal.py") class ElGamal(Homomorphic): @@ -26,7 +26,8 @@ def __init__(self, keys: Optional[dict] = None, exponential=False, key_size: int """ self.exponential = exponential self.keys = keys or self.generate_keys(key_size) - self.modulo = self.keys["public_key"]["p"] + self.plaintext_modulo = self.keys["public_key"]["p"] + self.ciphertext_modulo = self.keys["public_key"]["p"] def generate_keys(self, key_size: int): """ diff --git a/lightphe/cryptosystems/EllipticCurveElGamal.py b/lightphe/cryptosystems/EllipticCurveElGamal.py index d481da6..d0e5160 100644 --- a/lightphe/cryptosystems/EllipticCurveElGamal.py +++ b/lightphe/cryptosystems/EllipticCurveElGamal.py @@ -4,7 +4,7 @@ from lightphe.elliptic.Weierstrass import Weierstrass from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="lightphe/cryptosystems/EllipticCurveElGamal.py") class EllipticCurveElGamal(Homomorphic): @@ -27,7 +27,8 @@ def __init__(self, keys: Optional[dict] = None, key_size: int = 160): # TODO: add different forms and curves. e.g. Koblitz, Edwards (Ed25519) self.curve = Weierstrass() self.keys = keys or self.generate_keys(key_size) - self.modulo = self.curve.p + self.plaintext_modulo = self.curve.p + self.ciphertext_modulo = self.curve.p def generate_keys(self, key_size: int): """ diff --git a/lightphe/cryptosystems/GoldwasserMicali.py b/lightphe/cryptosystems/GoldwasserMicali.py index 18b2b25..7bcc51c 100644 --- a/lightphe/cryptosystems/GoldwasserMicali.py +++ b/lightphe/cryptosystems/GoldwasserMicali.py @@ -6,7 +6,7 @@ from lightphe.models.Homomorphic import Homomorphic from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="lightphe/cryptosystems/GoldwasserMicali.py") # pylint:disable=consider-using-enumerate @@ -26,6 +26,8 @@ def __init__(self, keys: Optional[dict] = None, key_size=100): """ self.keys = keys or self.generate_keys(key_size) self.ciphertext_modulo = self.keys["public_key"]["n"] + # TODO: not sure about the plaintext modulo + self.plaintext_modulo = self.keys["public_key"]["n"] def generate_keys(self, key_size: int) -> dict: """ diff --git a/lightphe/cryptosystems/NaccacheStern.py b/lightphe/cryptosystems/NaccacheStern.py index 95c4a4a..b32fe60 100644 --- a/lightphe/cryptosystems/NaccacheStern.py +++ b/lightphe/cryptosystems/NaccacheStern.py @@ -6,7 +6,7 @@ from lightphe.models.Homomorphic import Homomorphic from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="lightphe/cryptosystems/NaccacheStern.py") # pylint: disable=simplifiable-if-expression, consider-using-enumerate diff --git a/lightphe/cryptosystems/OkamotoUchiyama.py b/lightphe/cryptosystems/OkamotoUchiyama.py index ed1552c..671d19d 100644 --- a/lightphe/cryptosystems/OkamotoUchiyama.py +++ b/lightphe/cryptosystems/OkamotoUchiyama.py @@ -5,7 +5,7 @@ from lightphe.models.Homomorphic import Homomorphic from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="lightphe/cryptosystems/OkamotoUchiyama.py") class OkamotoUchiyama(Homomorphic): diff --git a/lightphe/cryptosystems/Paillier.py b/lightphe/cryptosystems/Paillier.py index 8bf8dbd..8606671 100644 --- a/lightphe/cryptosystems/Paillier.py +++ b/lightphe/cryptosystems/Paillier.py @@ -5,7 +5,7 @@ from lightphe.models.Homomorphic import Homomorphic from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="lightphe/cryptosystems/Paillier.py") class Paillier(Homomorphic): diff --git a/lightphe/cryptosystems/RSA.py b/lightphe/cryptosystems/RSA.py index 8951235..3785fba 100644 --- a/lightphe/cryptosystems/RSA.py +++ b/lightphe/cryptosystems/RSA.py @@ -5,7 +5,7 @@ from lightphe.models.Homomorphic import Homomorphic from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="lightphe/cryptosystems/RSA.py") class RSA(Homomorphic): @@ -28,7 +28,8 @@ def __init__(self, keys: Optional[dict] = None, key_size: int = 1024, encrypt_wi and do decryption with private key d. """ self.keys = keys or self.generate_keys(key_size) - self.modulo = self.keys["public_key"]["n"] + self.plaintext_modulo = self.keys["public_key"]["n"] + self.ciphertext_modulo = self.keys["public_key"]["n"] self.encrypt_with_public = encrypt_with_public def generate_keys(self, key_size: int) -> dict: diff --git a/lightphe/elliptic/Weierstrass.py b/lightphe/elliptic/Weierstrass.py index f00fdaf..622ea95 100644 --- a/lightphe/elliptic/Weierstrass.py +++ b/lightphe/elliptic/Weierstrass.py @@ -10,6 +10,7 @@ def __init__(self, curve="secp256k1"): if curve == "secp256k1": self.a = 0 self.b = 7 + # modulo self.p = ( pow(2, 256) - pow(2, 32) diff --git a/lightphe/models/Homomorphic.py b/lightphe/models/Homomorphic.py index e8489c3..4b5cf3c 100644 --- a/lightphe/models/Homomorphic.py +++ b/lightphe/models/Homomorphic.py @@ -5,10 +5,9 @@ class Homomorphic(ABC): - keys: Optional[dict] = None - modulo: Optional[int] = None - plaintext_modulo: Optional[int] = None - ciphertext_modulo: Optional[int] = None + keys: dict + plaintext_modulo: int + ciphertext_modulo: int @abstractmethod def generate_keys(self, key_size: int, s: Optional[int] = None) -> dict: @@ -36,7 +35,7 @@ def add( @abstractmethod def multiply( - self, ciphertext1: Union[int, tuple], ciphertext2: Union[int, tuple] + self, ciphertext1: Union[int, tuple, list], ciphertext2: Union[int, tuple, list] ) -> Union[int, tuple]: pass @@ -45,7 +44,7 @@ def xor(self, ciphertext1: list, ciphertext2: list) -> list: pass @abstractmethod - def multiply_by_contant(self, ciphertext: int, constant: int) -> int: + def multiply_by_contant(self, ciphertext: Union[int, tuple, list], constant: int) -> int: pass @abstractmethod diff --git a/tests/__init__.py b/tests/__init__.py index da0886f..4d84ae8 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -16,7 +16,7 @@ # pylint: disable=eval-used -logger = Logger() +logger = Logger(module="tests/__init__.py") class LightPHE: diff --git a/tests/test_benaloh.py b/tests/test_benaloh.py index b92c49a..d33ecec 100644 --- a/tests/test_benaloh.py +++ b/tests/test_benaloh.py @@ -2,7 +2,7 @@ from lightphe.cryptosystems.Benaloh import Benaloh from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="tests/test_benaloh.py") def test_benaloh(): diff --git a/tests/test_cloud.py b/tests/test_cloud.py index b14b4bc..821394b 100644 --- a/tests/test_cloud.py +++ b/tests/test_cloud.py @@ -1,7 +1,8 @@ +import pytest from lightphe.commons.logger import Logger from lightphe import LightPHE -logger = Logger() +logger = Logger(module="tests/test_cloud.py") # pre-generated 20-bit RSA keys PRIVATE = {"private_key": {"d": 30365}, "public_key": {"n": 175501, "e": 101753}} @@ -15,16 +16,14 @@ def test_encryption(): m1 = 10000 c1 = cs.encrypt(m1) - # m2 = 1.05 # TODO: add support - m2 = 5 + m2 = 1.05 c2 = cs.encrypt(m2) assert cs.decrypt(c1) == m1 - assert cs.decrypt(c2) == m2 logger.info("✅ Cloud encryption tests done") - c3_val = homomorphic_operations(c1.value, c2.value) + c3_val = homomorphic_operations(c1=c1.value, c2=c2.value) c3 = cs.create_ciphertext_obj(c3_val) assert cs.decrypt(c3) == m1 * m2 @@ -39,6 +38,15 @@ def homomorphic_operations(c1: int, c2: int): cs = LightPHE(algorithm_name="RSA", keys=PUBLIC) c1_obj = cs.create_ciphertext_obj(c1) c2_obj = cs.create_ciphertext_obj(c2) + + # one cannot perform decryoption without private key + with pytest.raises(ValueError): + cs.decrypt(c1_obj) + + with pytest.raises(ValueError): + cs.decrypt(c2_obj) + + # no need private key! c3 = c1_obj * c2_obj logger.info("✅ Cloud homomorphic operation tests done") return c3.value diff --git a/tests/test_damgard.py b/tests/test_damgard.py index cc8e98f..7e4510b 100644 --- a/tests/test_damgard.py +++ b/tests/test_damgard.py @@ -2,7 +2,7 @@ from lightphe.cryptosystems.DamgardJurik import DamgardJurik from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="tests/test_damgard.py") def test_damgardjurik(): diff --git a/tests/test_elgamal.py b/tests/test_elgamal.py index 53215cc..eeeab0c 100644 --- a/tests/test_elgamal.py +++ b/tests/test_elgamal.py @@ -2,21 +2,21 @@ from lightphe.cryptosystems.ElGamal import ElGamal from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="tests/test_elgamal.py") def test_elgamal(): eg = ElGamal() - m1 = eg.modulo + 100 - m2 = eg.modulo + 150 + m1 = eg.plaintext_modulo + 100 + m2 = eg.plaintext_modulo + 150 c1 = eg.encrypt(plaintext=m1) c2 = eg.encrypt(plaintext=m2) c3 = eg.multiply(c1, c2) # homomorphic operations - assert eg.decrypt(c3) == (m1 * m2) % eg.modulo + assert eg.decrypt(c3) == (m1 * m2) % eg.plaintext_modulo # unsupported homomorphic operations with pytest.raises(ValueError): @@ -44,18 +44,18 @@ def test_exponential_elgamal(): logger.debug(additive_eg.keys) - m1 = additive_eg.modulo + 222 - m2 = additive_eg.modulo + 111 + m1 = additive_eg.plaintext_modulo + 222 + m2 = additive_eg.plaintext_modulo + 111 c1 = additive_eg.encrypt(plaintext=m1) c2 = additive_eg.encrypt(plaintext=m2) c3 = additive_eg.add(c1, c2) # homomorphic operations - assert additive_eg.decrypt(c3) == (m1 + m2) % additive_eg.modulo + assert additive_eg.decrypt(c3) == (m1 + m2) % additive_eg.plaintext_modulo assert ( additive_eg.decrypt(additive_eg.multiply_by_contant(c1, m2)) - == (m1 * m2) % additive_eg.modulo + == (m1 * m2) % additive_eg.plaintext_modulo ) # unsupported homomorphic operations diff --git a/tests/test_ellipticcurveelgamal.py b/tests/test_ellipticcurveelgamal.py index e09e624..d00454c 100644 --- a/tests/test_ellipticcurveelgamal.py +++ b/tests/test_ellipticcurveelgamal.py @@ -2,7 +2,7 @@ from lightphe.cryptosystems.EllipticCurveElGamal import EllipticCurveElGamal from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="tests/test_ellipticcurveelgamal.py") def test_elliptic_curve_elgamal(): diff --git a/tests/test_goldwasser.py b/tests/test_goldwasser.py index d67bc45..0298945 100644 --- a/tests/test_goldwasser.py +++ b/tests/test_goldwasser.py @@ -2,7 +2,7 @@ from lightphe.cryptosystems.GoldwasserMicali import GoldwasserMicali from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="tests/test_goldwasser.py") def test_goldwasser(): diff --git a/tests/test_naccache.py b/tests/test_naccache.py index a0c8d8e..028b3e6 100644 --- a/tests/test_naccache.py +++ b/tests/test_naccache.py @@ -2,7 +2,7 @@ from lightphe.cryptosystems.NaccacheStern import NaccacheStern from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="tests/test_naccache.py") def test_naccache_on_plaintexts(): diff --git a/tests/test_okamoto.py b/tests/test_okamoto.py index 9fd9b25..ec6eac2 100644 --- a/tests/test_okamoto.py +++ b/tests/test_okamoto.py @@ -2,7 +2,7 @@ from lightphe.cryptosystems.OkamotoUchiyama import OkamotoUchiyama from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="tests/test_okamoto.py") def test_okamoto(): diff --git a/tests/test_paillier.py b/tests/test_paillier.py index a0ced9c..e4bd6a2 100644 --- a/tests/test_paillier.py +++ b/tests/test_paillier.py @@ -2,7 +2,7 @@ from lightphe.cryptosystems.Paillier import Paillier from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="tests/test_paillier.py") def test_paillier(): @@ -92,6 +92,3 @@ def test_float_operations(): k3 = -20 assert cs.decrypt(c1 * k3) == (m1 * k3) % cs.cs.plaintext_modulo assert cs.decrypt(k3 * c1) == (m1 * k3) % cs.cs.plaintext_modulo - - # TODO: think and implement this later - _ = -1.05 diff --git a/tests/test_rsa.py b/tests/test_rsa.py index 75560aa..0b19ee7 100644 --- a/tests/test_rsa.py +++ b/tests/test_rsa.py @@ -4,21 +4,21 @@ from lightphe.commons.logger import Logger from lightphe import LightPHE -logger = Logger() +logger = Logger(module="tests/test_rsa.py") def test_rsa(): rsa = RSA() - m1 = rsa.modulo + 9 - m2 = rsa.modulo + 11 + m1 = rsa.plaintext_modulo + 9 + m2 = rsa.plaintext_modulo + 11 c1 = rsa.encrypt(m1) c2 = rsa.encrypt(m2) c3 = rsa.multiply(c1, c2) # homomorphic operations - assert rsa.decrypt(c3) == (m1 * m2) % rsa.modulo + assert rsa.decrypt(c3) == (m1 * m2) % rsa.plaintext_modulo # unsupported homomorphic operations with pytest.raises(ValueError): diff --git a/tests/test_salary.py b/tests/test_salary.py index 93a0731..1adb950 100644 --- a/tests/test_salary.py +++ b/tests/test_salary.py @@ -1,7 +1,7 @@ from lightphe.cryptosystems.Paillier import Paillier from lightphe.commons.logger import Logger -logger = Logger() +logger = Logger(module="tests/test_salary.py") def test_salary():