pailliers module

Minimal pure-Python implementation of Paillier’s additively homomorphic cryptosystem.

class secret(bit_length: int)[source]

Bases: Tuple[int, int, int, int]

Wrapper class for a tuple of four integers that represents a secret key. The bit_length argument specifies the bit length of each of the two prime integers found in the key. Furthermore, the product of these two primes (i.e., the modulus) is guaranteed to have a bit length that is exactly twice the value of bit_length.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> isinstance(secret_key, secret)
True

Any attempt to supply an argument that is of the wrong type or outside the supported range raises an exception.

>>> secret('abc')
Traceback (most recent call last):
  ...
TypeError: bit length must be an integer
>>> secret(0)
Traceback (most recent call last):
  ...
ValueError: bit length must be a positive integer
class public(secret_key: secret)[source]

Bases: Tuple[int, int]

Wrapper class for a pair of integers that represents a public key.

>>> public_key = public(secret(2048))
>>> isinstance(public_key, public)
True

Any attempt to supply an argument that is of the wrong type or outside the supported range raises an exception.

>>> public('abc')
Traceback (most recent call last):
  ...
TypeError: secret key required to create public key
class plain[source]

Bases: int

Wrapper class for an integer that represents a plaintext.

>>> isinstance(plain(123), plain)
True
class cipher(integer: int, public_key: Optional[public] = None)[source]

Bases: int

Wrapper class for an integer that represents a ciphertext.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, plain(123))
>>> isinstance(c, cipher)
True

This class defines a number of special methods corresponding to arithmetic operations so that Python’s built-in operators can be used when working with instances of this class. These operators will only work on instances of this class that have been constructed with a public key (which is the default behavior of the encrypt function).

>>> decrypt(secret_key, c + c)
246
>>> decrypt(secret_key, 2 * c)
246

To facilitate the use of instances that do not maintain internal copies of the same public key (e.g., in cases where memory constraints are an issue or ciphertexts are stored/communicated separately from key information), the add and mul functions can be used.

>>> c = cipher(int(c), public_key=public_key)
>>> decrypt(secret_key, c + c)
246
>>> n = int(c)
>>> c = cipher(n) # This instance has no internal copy of a public key.
>>> c + c
Traceback (most recent call last):
  ...
ValueError: public key is required for addition
>>> decrypt(secret_key, add(public_key, c, c))
246
>>> decrypt(secret_key, mul(public_key, c, 2))
246

Warning: When the true integer sum of two encrypted values — or the true product of an encrypted value and an integer scalar — exceeds the modulus within the public key, the decrypted result will not match the true result.

>>> secret_key = secret(8)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 2 ** 7)
>>> int(decrypt(secret_key, c)) == 2 ** 7
True
>>> int(decrypt(secret_key, c * (2 ** 9))) == (2 ** 7) * (2 ** 9)
False

Any attempt to invoke the constructor using arguments that do not have the expected types raises an exception.

>>> cipher('abc', public_key='abc')
Traceback (most recent call last):
  ...
ValueError: invalid literal for int() with base 10: 'abc'
>>> cipher(123, public_key='abc')
Traceback (most recent call last):
  ...
TypeError: public key must be an instance of the public class
__add__(other: cipher) cipher[source]

Perform addition of encrypted values to produce the encrypted result.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 22)
>>> d = encrypt(public_key, 33)
>>> r = c + d
>>> int(decrypt(secret_key, r))
55

At least one of the two arguments must have a public key.

>>> decrypt(secret_key, cipher(int(c)) + c)
44
>>> decrypt(secret_key, c + cipher(int(c)))
44
>>> cipher(int(c)) + cipher(int(c))
Traceback (most recent call last):
  ...
ValueError: public key is required for addition

If public keys are specified in both ciphertexts, they must match.

>>> secret_key_a = secret(2048)
>>> public_key_a = public(secret_key_a)
>>> secret_key_b = secret(2048)
>>> public_key_b = public(secret_key_b)
>>> encrypt(public_key_a, 123) + encrypt(public_key_b, 456)
Traceback (most recent call last):
  ...
ValueError: public keys of ciphertexts must match
__radd__(other: Union[int, cipher]) cipher[source]

This method makes it possible to use the built-in sum function.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 22)
>>> decrypt(secret_key, sum([c, c, c, c]))
88

This method should not be invoked for any other reason.

>>> 123 + c
Traceback (most recent call last):
  ...
TypeError: can only add ciphertexts
__iadd__(other: cipher) cipher[source]

Add an encrypted value to an existing encrypted value.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 22)
>>> d = encrypt(public_key, 33)
>>> c += d
>>> int(decrypt(secret_key, c))
55

At least one of the two arguments must have a public key.

>>> c += cipher(int(c))
>>> decrypt(secret_key, c)
110
>>> d = cipher(int(d))
>>> d += c
>>> decrypt(secret_key, d)
143
>>> d = cipher(int(d))
>>> d += cipher(int(c))
Traceback (most recent call last):
  ...
ValueError: public key is required for addition

An integer base value can be used when accumulating iteratively.

>>> b = 0
>>> b += encrypt(public_key, 1)
>>> b += encrypt(public_key, 2)
>>> b += encrypt(public_key, 3)
>>> decrypt(secret_key, b)
6
__mul__(scalar: int) cipher[source]

Perform multiplication of an encrypted value by a scalar to produce the encrypted result.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 22)
>>> r = c * 3
>>> int(decrypt(secret_key, r))
66

This instance must have a public key.

>>> c = cipher(int(c))
>>> c * 3
Traceback (most recent call last):
  ...
ValueError: public key is required for scalar multiplication
__rmul__(scalar: int) cipher[source]

Perform multiplication of an encrypted value by a scalar (that appears on the left side of the operator) to produce the encrypted result.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 22)
>>> r = 3 * c
>>> int(decrypt(secret_key, r))
66

This instance must have a public key.

>>> c = cipher(int(c))
>>> c * 3
Traceback (most recent call last):
  ...
ValueError: public key is required for scalar multiplication
__imul__(scalar: int) cipher[source]

Perform multiplication of an encrypted value by a scalar.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 22)
>>> c *= 3
>>> int(decrypt(secret_key, c))
66

This instance must have a public key.

>>> c = cipher(int(c))
>>> c * 3
Traceback (most recent call last):
  ...
ValueError: public key is required for scalar multiplication
encrypt(public_key: public, plaintext: Union[plain, int]) cipher[source]

Encrypt the supplied plaintext using the supplied public key.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 123)
>>> isinstance(c, cipher)
True

Any attempt to invoke this function using arguments that do not have the expected types raises an exception.

>>> encrypt(secret_key, 123)
Traceback (most recent call last):
  ...
TypeError: can only encrypt using a public key
decrypt(secret_key: secret, ciphertext: cipher) plain[source]

Decrypt the supplied plaintext using the supplied secret key.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 123)
>>> decrypt(secret_key, c)
123

Any attempt to invoke this function using arguments that do not have the expected types raises an exception.

>>> decrypt(public_key, c)
Traceback (most recent call last):
  ...
TypeError: can only decrypt using a secret key
>>> decrypt(secret_key, 123)
Traceback (most recent call last):
  ...
TypeError: can only decrypt a ciphertext
add(public_key: public, *ciphertexts: cipher) cipher[source]

Perform addition of encrypted values to produce the encrypted result.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 22)
>>> d = encrypt(public_key, 33)
>>> r = add(public_key, c, d)
>>> int(decrypt(secret_key, r))
55

This function supports one or more ciphertexts. If only one ciphertext is supplied, that same ciphertext is returned.

>>> x = encrypt(public_key, 4)
>>> y = encrypt(public_key, 5)
>>> z = encrypt(public_key, 6)
>>> r = add(public_key, x, y, z)
>>> int(decrypt(secret_key, r))
15
>>> r = add(public_key, x)
>>> int(decrypt(secret_key, r))
4

Iterables of ciphertexts can be provided with the help of unpacking via * (thus allowing this function to be used in a manner that resembles the way that the built-in sum function can be used).

>>> r = add(public_key, *(c for c in [x, y, z]))
>>> int(decrypt(secret_key, r))
15

Any attempt to invoke this function using arguments that do not have the expected types raises an exception.

>>> add(secret_key, c, d)
Traceback (most recent call last):
  ...
TypeError: can only perform operation using a public key
>>> add(public_key, c, 123)
Traceback (most recent call last):
  ...
TypeError: can only add ciphertexts
>>> add(public_key)
Traceback (most recent call last):
  ...
ValueError: at least one ciphertext is required
mul(public_key: public, ciphertext: cipher, scalar: int) cipher[source]

Perform multiplication of an encrypted value by a scalar to produce the encrypted result.

>>> secret_key = secret(2048)
>>> public_key = public(secret_key)
>>> c = encrypt(public_key, 22)
>>> r = mul(public_key, c, 3)
>>> int(decrypt(secret_key, r))
66

Any attempt to invoke this function using arguments that do not have the expected types raises an exception.

>>> mul(secret_key, c, 3)
Traceback (most recent call last):
  ...
TypeError: can only perform operation using a public key
>>> mul(public_key, 123, 3)
Traceback (most recent call last):
  ...
TypeError: can only multiply a ciphertext
>>> mul(public_key, c, 'abc')
Traceback (most recent call last):
  ...
TypeError: can only multiply by an integer scalar