Paillier is a public key homomorphic encryption scheme. Python library paillier provides an implementation of a paillier cryptosystem.

Below are a list of homomorphic properties :

- Encrypted numbers can be multiplied by a non-encrypted scalar numbers.
- Two encrypted numbers can be added.
- Non-encrypted scalar can be added to Encrypted numbers.

Paillier does not come with default python package or conda package that's why we'll need to install it.

- pip install phe

In [1]:

```
!pip install --upgrade pip
!pip install phe
```

In [2]:

```
import phe
from phe import paillier
import json
```

`paillier.generate_paillier_keypair(n_length=2048)`

- It generates a public/private key pair of default length 2048 bits. A developer can change this length according to their need. It takes a bit of time to generate initial pairs.

In [3]:

```
%%time
pub_key,priv_key = paillier.generate_paillier_keypair() ## Generating public/private key pair
## %%time is magic command to find out time taken to execute this cell
```

`public_key.encrypt(value, precision=None, r_value=None)`

- It's used to encrypt`value`

with precision provided as`precision`

for float numbers. User can supply random value to be used during encryption as`r_value`

. Returns object of class`EncryptedNumber`

.`private_key.decrypt()`

- It's used to decrypt encrypted value.

In [4]:

```
enc1 = pub_key.encrypt(5)
enc2 = pub_key.encrypt(5.649)
enc3 = pub_key.encrypt(5.5397,precision=1e-2)
priv_key.decrypt(enc1), priv_key.decrypt(enc2), priv_key.decrypt(enc3)
```

Out[4]:

If developers are planning to use more that one public/private key sets then they can main list of private keys through keyring.

`paillier.PaillierPrivateKeyring(private_keys=None)`

- Lets developer generates keyring which will main list of private keys give as input to it. Developers can supply all private keys as list initially or can add later as well using`add()`

method.

The benefit of using the ring is that the developer does not need to loop through all private keys to decrypt any encrypted number.

In [5]:

```
keyring = paillier.PaillierPrivateKeyring()
pub_keys = []
for i in range(5):
pub,priv = paillier.generate_paillier_keypair()
pub_keys.append(pub)
keyring.add(priv)
enc1= pub_keys[0].encrypt(5.5)
enc2= pub_keys[2].encrypt(13.6)
enc3= pub_keys[3].encrypt(3.14)
## Notice below keyring will findout right private key for decrypting number without developer manually keeping track of it..
keyring.decrypt(enc1), keyring.decrypt(enc2), keyring.decrypt(enc3)
```

Out[5]:

Below we'll verify homomorphic properties using various examples.

In [6]:

```
enc1 = pub_key.encrypt(5.5)
enc2 = pub_key.encrypt(8.3)
enc3 = pub_key.encrypt(12.6)
print(priv_key.decrypt(enc1))
enc1 = enc1 + 3.3
print(priv_key.decrypt(enc1))
enc1 = enc1 - 3.3
print(priv_key.decrypt(enc1))
enc4 = enc2 + enc3
print(priv_key.decrypt(enc4))
enc5 = enc3 - enc2
print(priv_key.decrypt(enc5))
enc6 = -5 + enc5
print(priv_key.decrypt(enc6))
```

In [7]:

```
enc1 = enc1 * 2.2
print(priv_key.decrypt(enc1))
enc1 = enc1 / 10
print(priv_key.decrypt(enc1))
enc7 = enc1 * -2
print(priv_key.decrypt(enc7))
enc8 = enc1 / -2
print(priv_key.decrypt(enc8))
```

Below steps explain the serialisation of an encrypted number along with public key and then deserializing & reconstructing encrypted number and public key.

In [8]:

```
print(priv_key.decrypt(enc1))
enc_with_pub_key = {}
enc_with_pub_key['public_key'] = { 'g':pub_key.g, 'n':pub_key.n}
enc_with_pub_key['enc_value'] = (str(enc1.ciphertext()),enc1.exponent)
serialised = json.dumps(enc_with_pub_key)
```

In [9]:

```
received_dict = json.loads(serialised)
pk = received_dict['public_key']
public_key_rec = paillier.PaillierPublicKey(n=int(pk['n']))
enc_nums_rec = paillier.EncryptedNumber(public_key_rec, int(received_dict['enc_value'][0]), int(received_dict['enc_value'][1]))
priv_key.decrypt(enc_nums_rec)
```

Out[9]:

Sunny Solanki

functools (annotations)

glob

random

functools (functions)