1
#! /usr/bin/env python3
2
3
"""
4
KSW.py
5
6
Written by Geremy Condra
7
Licensed under GPLv3
8
Released 15 October 2009
9
10
An implementation of the Katz-Sahai-Waters predicate
11
encryption scheme in Python3.
12
"""
13
14
from pypbc import *
15
16
#############################################
17
#					Utilities								     #
18
#############################################
19
20
def dot_product(x, y, n):
21
	"""Takes the dot product of lists x and y over F_n"""
22
	
23
	if len(x) != len(y):
24
		raise ValueError("x and y must be the same length!")
25
	if not isinstance(n, int):
26
		raise ValueError("n must be an integer!")
27
	
28
	return sum([(x_i * y[i]) % n for i, x_i in enumerate(x)])
29
30
#############################################
31
#						Cryptosystem						     #
32
#############################################	
33
34
class PublicKey:
35
	g_G_p = None
36
	g_G_r = None
37
	Q = None
38
	vector = None
39
	def __init__(self, gen_p, gen_r, Q, vector):
40
		self.g_G_p = gen_p
41
		self.g_G_r = gen_r
42
		self.Q = Q
43
		self.vector = vector
44
	
45
class MasterSecretKey:
46
	p = None
47
	q = None
48
	r = None
49
	g_G_q = None
50
	Hs = None
51
	def __init__(self, p, q, r, g_G_q, Hs):
52
		self.p = p
53
		self.q = q
54
		self.r = r
55
		self.g_G_q = g_G_q
56
		self.Hs = Hs
57
58
59
class Cryptosystem:
60
	
61
	def __init__(self, security) -> "(PK, SK)":
62
		self.security = security
63
		# select p, q, r
64
		p = get_random_prime(100)
65
		q = get_random_prime(100)
66
		r = get_random_prime(100)
67
		# make n
68
		self.n = p*q*r
69
		# build the params
70
		params = Parameters(n=self.n)
71
		# build the pairing
72
		self.pairing = Pairing(params)
73
		# find the generators for the G_p, G_q, and G_r subgroups
74
		g_G_p = Element.random(self.pairing, G1)**(q*r)
75
		g_G_r = Element.random(self.pairing, G1)**(p*q)
76
		g_G_q = Element.random(self.pairing, G1)**(p*r)
77
		# choose R0
78
		R0 = g_G_r ** Element.random(self.pairing, Zr)
79
		# choose the random R's
80
		Rs = [(g_G_r**Element.random(self.pairing, Zr), g_G_r**Element.random(self.pairing, Zr)) for i in range(security)]
81
		hs = [(g_G_p**Element.random(self.pairing, Zr), g_G_p**Element.random(self.pairing, Zr)) for i in range(security)]
82
		# choose the random H's
83
		Hs = []
84
		for i in range(security):
85
			Hs.append((hs[i][0] * Rs[i][0], hs[i][1] * Rs[i][1]))
86
		# calculate Q
87
		Q = g_G_q * R0
88
		# build the public and master secret keys
89
		self.pk = PublicKey(g_G_p, g_G_r, Q, Hs)
90
		self.sk = MasterSecretKey(p, q, r, g_G_q, hs)
91
92
93
	def keygen(self, v: "description of a predicate") -> "SK_f":
94
		R5 = self.pk.g_G_r**Element.random(self.pairing, Zr)
95
		Q6 = self.sk.g_G_q**Element.random(self.pairing, Zr)
96
		Rs = []
97
		for i in range(self.security):
98
			# build r1
99
			r1 = Element(self.pairing, Zr, get_random(self.sk.p))
100
			# build r2
101
			r2 = Element(self.pairing, Zr, get_random(self.sk.p))
102
			Rs.append((r1, r2))
103
		f1 = Element(self.pairing, Zr, get_random(self.sk.q))
104
		f2 = Element(self.pairing, Zr, get_random(self.sk.q))
105
		K = R5*Q6
106
		for pos in range(self.security):
107
			# get h1, h2
108
			h1, h2 = self.sk.Hs[pos]
109
			# get r1, r2
110
			r1, r2 = Rs[pos]
111
			# form the intermediate value
112
			i1 = h1**(-r1)
113
			i2 = h2**(-r2)
114
			K += i1 * i2
115
		Ks = []
116
		for pos in range(self.security):
117
			r1, r2 = Rs[pos]
118
			K1 = (self.pk.g_G_p**r1) * (self.sk.g_G_q**(f1*v[pos]))
119
			K2 = (self.pk.g_G_p**r2) * (self.sk.g_G_q**(f2*v[pos]))
120
			Ks.append((K1, K2))
121
		return (K, Ks)
122
		
123
	
124
	def encrypt(self, x: "vector of elements in Zr") -> "ciphertext":
125
		s = Element.random(self.pairing, Zr) 
126
		a = Element.random(self.pairing, Zr)
127
		b = Element.random(self.pairing, Zr)
128
		Rs = []
129
		for i in range(self.security):
130
			r1 = self.pk.g_G_r**Element.random(self.pairing, Zr)
131
			r2 = self.pk.g_G_r**Element.random(self.pairing, Zr)
132
			Rs.append((r1, r2))
133
		C0 = self.pk.g_G_p**s
134
		Cs = []
135
		for i in range(self.security):
136
			c1i = (self.pk.vector[i][0]**s)
137
			c1i2 = (self.pk.Q**(a*x[i]))
138
			c1 = c1i*c1i2*Rs[i][0]
139
			c2i = (self.pk.vector[i][1]**s)
140
			c2i2 = (self.pk.Q**(b*x[i]))
141
			c2 = c2i*c2i2*Rs[i][1]
142
			Cs.append((c1, c2))
143
		return (C0, Cs)
144
	
145
	def decrypt(self, c: "ciphertext", sk_f: "secret key corresponding to predicate f") -> "message or T":
146
		output = self.pairing.apply(c[0], sk_f[0])
147
		for i in range(self.security):
148
			j = self.pairing.apply(c[1][i][0], sk_f[1][i][0])
149
			k = self.pairing.apply(c[1][i][1], sk_f[1][i][1])
150
			output *= j*k
151
		return output
152
153
#############################################
154
#						Test logic							     #
155
#############################################
156
157
def test():
158
	# build the polynomial vector
159
	Pv = [1, -27, 152] # = X^2 - 27X + 152 = (X - 8)(X - 19)
160
	# build the X vector
161
	Xv = [19**3, 19**2, 19**1] 
162
	# build the random primes
163
	p = get_random_prime(100)
164
	q = get_random_prime(100)
165
	r = get_random_prime(100)
166
	n = p*q*r
167
	# build the parameters
168
	params = Parameters(n=n)
169
	# build the pairing
170
	pairing = Pairing(params)
171
	# get the generators for G_p, G_q, G_r
172
	g_G_p = Element.random(pairing, G1)**q*r
173
	g_G_q = Element.random(pairing, G1)**p*r
174
	g_G_r = Element.random(pairing, G1)**p*q
175
	# test the generators
176
	assert(pairing.apply(g_G_p, g_G_r) == 1)
177
	assert(pairing.apply(g_G_r, g_G_q) == 1)
178
	# select the random integers from Zn
179
	a = Element.random(pairing, Zr)
180
	b = Element.random(pairing, Zr)
181
	# get random integers from Zq
182
	f1 = Element(pairing, Zr, get_random(q))
183
	f2 = Element(pairing, Zr, get_random(q))
184
	# perform the check
185
	result = Element.zero(pairing, GT)
186
	for pos, i in enumerate(Pv):
187
		result += pairing.apply(g_G_q, g_G_q)**(((a*f1+b*f2)) * Xv[pos]*i)
188
	assert(result == 1)
189
	# work backwards one step
190
	# make s
191
	s = Element.random(pairing, Zr)
192
	# make the h vector
193
	hv = [(g_G_p**Element.random(pairing, Zr), g_G_p**Element.random(pairing, Zr)) for i in range(3)]
194
	# make the r vector
195
	rv = [(Element(pairing, Zr, get_random(p)), Element(pairing, Zr, get_random(p))) for i in range(3)]
196
	# perform the hv<>rv product operation
197
	product = Element.one(pairing, G1)
198
	for pos, i in enumerate(hv):
199
		h1, h2 = i
200
		r1, r2 = rv[pos]
201
		product *= (h1**-r1)*(h2**-r2)
202
	# get the initial result
203
	result = pairing.apply(g_G_p**s, product)
204
	# perform the secondary product operation
205
	for pos, i in enumerate(hv):
206
		h1, h2 = i
207
		r1, r2 = rv[pos]
208
		x = Xv[pos]
209
		v = Pv[pos]
210
		arg1 = (h1**s)*(g_G_q**(a*x))
211
		arg2 = (g_G_p**r1)*(g_G_q**(f1*v))
212
		part1 = pairing.apply(arg1, arg2)
213
		arg1 = (h2**s)*(g_G_q**(b*x))
214
		arg2 = (g_G_p**r2)*(g_G_q**(f2*v))
215
		part2 = pairing.apply(arg1, arg2)
216
		result += part1*part2
217
	assert(result == 1)
218
	# work backwards another step
219
	# build R0
220
	R0 = g_G_r**Element.random(pairing, Zr)
221
	# build Q
222
	Q = g_G_q * R0
223
	# build R5
224
	R5 = g_G_r**Element.random(pairing, Zr)
225
	# build Q6
226
	Q6 = g_G_q**Element.random(pairing, Zr)
227
	# build the R vector
228
	Rv = [(g_G_r**Element.random(pairing, Zr), g_G_r**Element.random(pairing, Zr)) for i in range(3)]
229
	# build the H vector
230
	Hv = []
231
	for i in range(3):
232
		Hv.append((hv[i][0] * Rv[i][0], hv[i][1] * Rv[i][1]))
233
	# build the initial pairing value
234
	product = Element.one(pairing, G1)
235
	for pos, i in enumerate(hv):
236
		h1, h2 = i
237
		r1, r2 = rv[pos]
238
		product *= (h1**-r1)*(h2**-r2)
239
	# get the initial result
240
	result = pairing.apply(g_G_p**s, R5*Q6*product)
241
	for pos, i in enumerate(Hv):
242
		H1, H2 = i
243
		R3, R4 = Rv[pos]
244
		r1, r2 = rv[pos]
245
		part1 = pairing.apply((H1**s)*(Q**(a*Xv[pos])*R3), (g_G_p**r1)*(g_G_q**(f1*Pv[pos])))
246
		part2 = pairing.apply((H2**s)*(Q**(b*Xv[pos])*R4), (g_G_p**r2)*(g_G_q**(f2*Pv[pos])))
247
		result *= part1*part2
248
	assert(result == 1)
249
	# done with the proof, test the cryptosystem against it
250
	c = Cryptosystem(3)
251
	# build the secret key corresponding to the above polynomial
252
	skf = c.keygen(Pv)
253
	# encrypt the value given
254
	e = c.encrypt(Xv)
255
	# decrypt it
256
	assert(c.decrypt(e, skf) == 1)
257
258
if __name__ == "__main__":
259
	test()