Share @  cryptography, paillier, homomorphic_encryption, threshold
In [1]:
!pip install phe
import phe
from phe import paillier
import numpy as np
import random
from numba import jit
import sympy
import math

Collecting phe
Downloading https://files.pythonhosted.org/packages/32/0e/568e97b014eb14e794a1258a341361e9da351dc6240c63b89e1541e3341c/phe-1.4.0.tar.gz
Building wheels for collected packages: phe
Running setup.py bdist_wheel for phe ... - done
Stored in directory: /tmp/.cache/pip/wheels/f8/dc/36/dcb6bf0f1b9907e7b710ace63e64d08e7022340909315fdea4
Successfully built phe
Installing collected packages: phe
Successfully installed phe-1.4.0
You are using pip version 18.1, however version 19.0.3 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

In [2]:
class ThresholdPaillier(object):
def __init__(self,size_of_n):
#size_of_n = 1024
pub, priv = paillier.generate_paillier_keypair(n_length=size_of_n)
self.p1 = priv.p
self.q1 = priv.q

while sympy.primetest.isprime(2*self.p1 +1)!= True:
pub, priv = paillier.generate_paillier_keypair(n_length=size_of_n)
self.p1 = priv.p
while sympy.primetest.isprime(2*self.q1 +1)!= True:
pub, priv = paillier.generate_paillier_keypair(n_length=size_of_n)
self.q1 = priv.q

self.p = (2*self.p1) + 1
self.q = (2*self.q1) + 1
print(sympy.primetest.isprime(self.p),sympy.primetest.isprime(self.q),sympy.primetest.isprime(self.p1),sympy.primetest.isprime(self.q1))
self.n = self.p * self.q
self.s = 1
self.ns = pow(self.n, self.s)
self.nSPlusOne = pow(self.n,self.s+1)
self.nPlusOne = self.n + 1
self.nSquare = self.n*self.n

self.m = self.p1 * self.q1
self.nm = self.n*self.m
self.l = 5 # Number of shares of private key
self.w = 2 # The minimum of decryption servers needed to make a correct decryption.
self.delta = self.factorial(self.l)
self.rnd = random.randint(1,1e50)
self.combineSharesConstant = sympy.mod_inverse((4*self.delta*self.delta)%self.n, self.n)
self.d = self.m * sympy.mod_inverse(self.m, self.n)

self.ais = [self.d]
for i in range(1, self.w):
self.ais.append(random.randint(0,self.nm-1))

self.r = random.randint(1,self. p) ## Need to change upper limit from p to one in paper
while math.gcd(self.r,self.n) != 1:
self.r = random.randint(0, self.p)
self.v = (self.r*self.r) % self.nSquare

self.si = [0] * self.l
self.viarray = [0] * self.l

for i in range(self.l):
self.si[i] = 0
X = i + 1
for j in range(self.w):
self.si[i] += self.ais[j] * pow(X, j)
self.si[i] = self.si[i] % self.nm
self.viarray[i] = pow(self.v, self.si[i] * self.delta, self.nSquare)

self.priv_keys = []
for i in range(self.l):
self.priv_keys.append(ThresholdPaillierPrivateKey(self.n, self.l, self.combineSharesConstant, self.w, \
self.v, self.viarray, self.si[i], i+1, self.r, self.delta, self.nSPlusOne))
self.pub_key = ThresholdPaillierPublicKey(self.n, self.nSPlusOne, self.r, self.ns, self.w,\
self.delta, self.combineSharesConstant)

def factorial(self, n):
fact = 1
for i in range(1,n+1):
fact *= i
return fact

def computeGCD(self, x, y):
while(y):
x, y = y, x % y
return x

class PartialShare(object):
def __init__(self, share, server_id):
self.share = share
self.server_id =server_id

class ThresholdPaillierPrivateKey(object):
def __init__(self,n, l,combineSharesConstant, w, v, viarray, si, server_id, r, delta, nSPlusOne):
self.n = n
self.l = l
self.combineSharesConstant = combineSharesConstant
self.w = w
self.v = v
self.viarray = viarray
self.si = si
self.server_id = server_id
self.r = r
self.delta = delta
self.nSPlusOne = nSPlusOne

def partialDecrypt(self, c):
return PartialShare(pow(c.c, self.si*2*self.delta, self.nSPlusOne), self.server_id)

class ThresholdPaillierPublicKey(object):
def __init__(self,n, nSPlusOne, r, ns, w, delta, combineSharesConstant):
self.n = n
self.nSPlusOne = nSPlusOne
self.r = r
self.ns =ns
self.w = w
self.delta = delta
self.combineSharesConstant = combineSharesConstant

def encrypt(self, msg):
msg = msg % self.nSPlusOne if msg < 0 else msg
c = (pow(self.n+1, msg, self.nSPlusOne) * pow(self.r, self.ns, self.nSPlusOne)) % self.nSPlusOne
return EncryptedNumber(c, self.nSPlusOne, self.n)

class EncryptedNumber(object):
def __init__(self, c, nSPlusOne, n):
self.c = c
self.nSPlusOne = nSPlusOne
self.n = n

def __mul__(self, cons):
if cons < 0:
return EncryptedNumber(pow(sympy.mod_inverse(self.c, self.nSPlusOne), -cons, self.nSPlusOne), self.nSPlusOne, self.n)
else:
return EncryptedNumber(pow(self.c, cons, self.nSPlusOne), self.nSPlusOne, self.n)

def __add__(self, c2):
return EncryptedNumber((self.c * c2.c) % self.nSPlusOne, self.nSPlusOne, self.n)

def combineShares(shrs, w, delta, combineSharesConstant, nSPlusOne, n, ns):
cprime = 1
for i in range(w):
ld = delta
for iprime in range(w):
if i != iprime:
if shrs[i].server_id != shrs[iprime].server_id:
ld = (ld * -shrs[iprime].server_id) // (shrs[i].server_id - shrs[iprime].server_id)
#print(ld)
shr = sympy.mod_inverse(shrs[i].share, nSPlusOne) if ld < 0 else shrs[i].share
ld = -1*ld if ld <1 else ld
temp = pow(shr, 2 * ld, nSPlusOne)
cprime = (cprime * temp) % nSPlusOne
L = (cprime - 1) // n
result = (L * combineSharesConstant) % n
return result - ns if result > (ns // 2) else result

In [3]:
tp = ThresholdPaillier(1024)

True True True True

In [4]:
priv_keys = tp.priv_keys
pub_key = tp.pub_key

In [5]:
c = pub_key.encrypt(123456789123456789123456789123456789123456789)

share1 = priv_keys[0].partialDecrypt(c)
share2 = priv_keys[1].partialDecrypt(c)
print(combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

share1 = priv_keys[0].partialDecrypt(c)
share2 = priv_keys[3].partialDecrypt(c)
print(combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

share1 = priv_keys[2].partialDecrypt(c)
share2 = priv_keys[3].partialDecrypt(c)
print(combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

c2 = pub_key.encrypt(-102)
share1 = priv_keys[2].partialDecrypt(c2)
share2 = priv_keys[3].partialDecrypt(c2)
print(combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

c1 = pub_key.encrypt(10)
c2 = c1 * -4
share1 = priv_keys[2].partialDecrypt(c2)
share2 = priv_keys[3].partialDecrypt(c2)
print('10 x -4 = %d' %combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

c1 = pub_key.encrypt(-4)
c2 = c1 * 30
share1 = priv_keys[2].partialDecrypt(c2)
share2 = priv_keys[3].partialDecrypt(c2)
print('-4 x 30 = %d'%combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

c1 = pub_key.encrypt(-10)
c2 = c1 * -7
share1 = priv_keys[2].partialDecrypt(c2)
share2 = priv_keys[3].partialDecrypt(c2)
print('-10 x -7 = %d'%combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

c1 = pub_key.encrypt(10)
c2 = c1 * 4
share1 = priv_keys[2].partialDecrypt(c2)
share2 = priv_keys[3].partialDecrypt(c2)
print('10 x 4 = %d'%combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

c1 = pub_key.encrypt(-10)
c2 = pub_key.encrypt(102)
c3 = c1 + c2
share1 = priv_keys[2].partialDecrypt(c3)
share2 = priv_keys[3].partialDecrypt(c3)
print('-10 + 102 = %d'%combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

c1 = pub_key.encrypt(-10)
c2 = pub_key.encrypt(-72)
c3 = c1 + c2
share1 = priv_keys[2].partialDecrypt(c3)
share2 = priv_keys[3].partialDecrypt(c3)
print('-10 + -72 = %d'%combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

c1 = pub_key.encrypt(10)
c2 = pub_key.encrypt(-67)
c3 = c1 + c2
share1 = priv_keys[2].partialDecrypt(c3)
share2 = priv_keys[3].partialDecrypt(c3)
print('10 + -67 = %d'%combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

c1 = pub_key.encrypt(10)
c2 = pub_key.encrypt(2)
c3 = c1 + c2
share1 = priv_keys[2].partialDecrypt(c3)
share2 = priv_keys[3].partialDecrypt(c3)
print('10 + 2 = %d'%combineShares([share1, share2], pub_key.w, pub_key.delta, pub_key.combineSharesConstant, pub_key.nSPlusOne, pub_key.n, pub_key.ns))

123456789123456789123456789123456789123456789
123456789123456789123456789123456789123456789
123456789123456789123456789123456789123456789
-102
10 x -4 = -40
-4 x 30 = -120
-10 x -7 = 70
10 x 4 = 40
-10 + 102 = 92
-10 + -72 = -82
10 + -67 = -57
10 + 2 = 12


Let other learners know about this article @
Sunny Solanki