Skip to content

A Lightweight Partially Homomorphic Encryption Library for Python

License

Notifications You must be signed in to change notification settings

serengil/LightPHE

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

89 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LightPHE

PyPI Downloads Stars Tests License DOI

Blog YouTube Twitter

Support me on Patreon GitHub Sponsors Buy Me a Coffee

LightPHE is a lightweight partially homomorphic encryption library for python. It is a hybrid homomoprhic encryption library wrapping many schemes such as RSA, ElGamal, Exponential ElGamal, Elliptic Curve ElGamal, Paillier, Damgard-Jurik, Okamoto–Uchiyama, Benaloh, Naccache–Stern, Goldwasser–Micali.

Partially vs Fully Homomorphic Encryption

Even though fully homomorphic encryption (FHE) has become available in recent times, but when considering the trade-offs, LightPHE emerges as a more efficient and practical choice. If your specific task doesn't demand the full homomorphic capabilities, opting for partial homomorphism with LightPHE is the logical decision.

  • 🏎️ Notably faster
  • 💻 Demands fewer computational resources
  • 📏 Generating much smaller ciphertexts
  • 🔑 Distributing much smaller keys
  • 🧠 Well-suited for memory-constrained environments
  • ⚖️ Strikes a favorable balance for practical use cases

Installation PyPI

The easiest way to install the LightPHE package is to install it from python package index (PyPI).

pip install lightphe

Then you will be able to import the library and use its functionalities.

from lightphe import LightPHE

Summary of Homomorphic Features of Different Cryptosystems in LightPHE

In summary, LightPHE is covering following algorithms and these are partially homomorphic with respect to the operations mentioned in the following table.

Algorithm Multiplicatively
Homomorphic
Additively
Homomorphic
Multiplication with a Plain Constant Exclusively
Homomorphic
Regeneration
of Ciphertext
RSA
ElGamal
Exponential ElGamal
Elliptic Curve ElGamal
Paillier
Damgard-Jurik
Benaloh
Naccache-Stern
Okamoto-Uchiyama
Goldwasser-Micali

Building cryptosystem

Once you imported the library, then you can build a cryptosystem for several algorithms. This basically generates private and public key pair.

algorithms = [
  "RSA",
  "ElGamal",
  "Exponential-ElGamal",
  "Paillier",
  "Damgard-Jurik",
  "Okamoto-Uchiyama",
  "Benaloh",
  "Naccache-Stern",
  "Goldwasser-Micali",
  "EllipticCurve-ElGamal"
]

phe = LightPHE(algorithm_name = algorithms[0])

Encryption & Decryption

Once you built your cryptosystem, you will be able to encrypt and decrypt messages with the built cryptosystem.

# define plaintext
m = 17

# calculate ciphertext
c = phe.encrypt(m)

# proof of work
assert phe.decrypt(c) == m

Homomorphic Operations

Once you have the ciphertext, you will be able to perform homomorphic operations on encrypted data without holding private key. For instance, Paillier is homomorphic with respect to the addition. In other words, decryption of the addition of two ciphertexts is equivalent to addition of plaintexts.

On-Prem Encryption

This code snippet illustrates how to generate a random public-private key pair using the Paillier and encrypt a plaintext pair. The resulting ciphertext pair, c1 and c2, along with the public key, is then sent from the on-premises environment to the cloud.

def on_premise() -> Tuple[int, int, dict]:
    """
    Executes on-premise operations: initializes a cryptosystem by generating 
    a random public-private key pair, then encrypts two plaintext values.

    Returns:
       result (tuple): A tuple containing:
       - c1 (int): The first ciphertext
       - c2 (int): The second ciphertext
       - public_key (dict): The public key for the cryptosystem
    """
    # generate a random private-public key pair
    phe = LightPHE(algorithm_name = "Paillier")

    # define plaintexts
    m1 = 10000 # base salary in usd
    m2 = 500 # wage increase in usd

    # calculate ciphertexts
    c1 = phe.encrypt(m1).value
    c2 = phe.encrypt(m2).value

    return (c1, c2, phe.cs.public_key)

Homomorphic Operations on Cloud

This code snippet demonstrates how to perform homomorphic addition on the cloud side without using the private key. However, the cloud is unable to decrypt c3 itself, even though it is the one that calculated it.

def cloud(c1: int, c2: int, public_key: dict) -> int:
    """
    Performs cloud-side operations: reconstructs a cryptosystem using the 
    provided public key and executes a homomorphic addition on two ciphertexts.

    Args:
       c1 (int): The first ciphertext
       c2 (int): The second ciphertext
       public_key (dict): The public key of an existing cryptosystem
    Retunrs:
       c3 (int): The resulting ciphertext after homomorphic addition
    """
    # restore cryptosystem with just the public key
    phe = LightPHE(algorithm_name = "Paillier", keys = public_key)

    # cast c1 and c2 to ciphertext objects
    c1 = phe.create_ciphertext_obj(c1)
    c2 = phe.create_ciphertext_obj(c2)

    # confirm that cloud cannot decrypt c1
    with pytest.raises(ValueError, match="You must have private key"):
      phe.decrypt(c1)

    # confirm that cloud cannot decrypt c2
    with pytest.raises(ValueError, match="You must have private key"):
      phe.decrypt(c2)

    # perform homomorphic addition
    c3 = c1 + c2

    # confirm that cloud cannot decrypt c3
    with pytest.raises(ValueError, match="You must have private key"):
      phe.decrypt(c3)
    
    return c3.value

On-Prem Decryption And Proof of Work

This code snippet demonstrates the proof of work. Even though c3 was calculated in the cloud by adding c1 and c2, on-premises can validate that its decryption must be equal to the addition of plaintexts m1 and m2.

# proof of work - private key required
assert phe.decrypt(c3) == m1 + m2

In this homomorphic pipeline, the cloud's computational power was utilized to calculate c3, but it can only be decrypted by the on-premises side. Additionally, while we performed the encryption on the on-premises side, this is not strictly necessary; only the public key is required for encryption. Therefore, encryption can also be performed on the non-premises side. This approach is particularly convenient when collecting data from multiple edge devices and storing all of it in the cloud simultaneously.

Scalar Multiplication

Besides, Paillier is supporting multiplying ciphertexts by a known plain constant. This code snippet demonstrates how to perform scalar multiplication on encrypted data using Paillier homomorphic encryption with the LightPHE library.

# increasing something 5%
k = 1.05

# scalar multiplication - cloud (private key is not required)
c4 = k * c1

# proof of work on-prem - private key is required
assert phe.decrypt(c4) == k * m1

Ciphertext Regeneration

Similar to the most of additively homomorphic algorithms, Paillier lets you to regenerate ciphertext while you are not breaking its plaintext restoration. You may consider to do this re-generation many times to have stronger ciphertexts.

c1_prime = phe.regenerate_ciphertext(c1)
assert c1_prime.value != c1.value
assert phe.decrypt(c1_prime) == m1
assert phe.decrypt(c1) == m1

Unsupported Operations

Finally, if you try to perform an operation that algorithm does not support, then an exception will be thrown. For instance, Paillier is not homomorphic with respect to the multiplication or xor. To put it simply, you cannot multiply two ciphertexts. If you enforce this calculation, you will have an exception.

# pailier is not multiplicatively homomorphic
with pytest.raises(ValueError, match="Paillier is not homomorphic with respect to the multiplication"):
  c1 * c2

# pailier is not exclusively homomorphic
with pytest.raises(ValueError, match="Paillier is not homomorphic with respect to the exclusive or"):
  c1 ^ c2

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.

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.

You should be able run make test and make lint commands successfully before committing. Once a PR is created, GitHub test workflow will be run automatically and unit test results will be available in GitHub actions before approval. Besides, workflow will evaluate the code with pylint as well.

Support

There are many ways to support a project - starring⭐️ the GitHub repo is just one 🙏

You can also support this work on Patreon, GitHub Sponsors or Buy Me a Coffee.

Also, your company's logo will be shown on README on GitHub if you become sponsor in gold, silver or bronze tiers.

Citation

Please cite LightPHE in your publications if it helps your research. Here is its BibTex entry:

@misc{serengil2024lightphe,
   title     = {LightPHE: Integrating Partially Homomorphic Encryption into Python with Extensive Cloud Environment Evaluations}, 
   author    = {Serengil, Sefik Ilkin and Ozpinar, Alper},
   year      = {2024},
   publisher = {arXiv},
   url       = {https://arxiv.org/abs/2408.05219},
   doi       = {10.48550/arXiv.2408.05219}
}

Also, if you use LightPHE in your projects, please add lightphe in the requirements.txt.

License

LightPHE is licensed under the MIT License - see LICENSE for more details.