-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathbb.py
232 lines (181 loc) · 6.75 KB
/
bb.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
"""
Bulletin Board (BB)
This module uses paillier's homomorphic properties to keep track of vote sums. It also performs ZKP to make sure user
knows his vote and that the vote is valid.
"""
import socket
import pickle
from Crypto.Hash import SHA256
from paillier import paillier
import common
import config
from bb_interface import *
import em_interface
import zkp
def setup():
"""
Setups up the server
"""
try:
state = _read_state()
print("Existing State found, loading it: {}".format(state))
except FileNotFoundError:
print("No previous state found, creating one")
state = BulletinBoardState()
_write_state(state)
print("Bulletin Board (BB) setup complete...")
def kick_off():
"""
Kicks off the server
creates/binds a socket and starts listening for requests
"""
# Get the keys
pub_keys = common.get_public_key_from_em()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(config.BB_ADDR)
sock.listen()
# Read the state
state = _read_state()
# Create initial sum if needed
if state.encrypted_sums is None:
# Prepare initial sum
state.encrypted_sums = paillier.encrypt(pub_keys.paillier_pub_key, 0)
_write_state(state)
print("Bulletin Board (BB) started ...")
# Message Processing Loop
while True:
conn, addr = sock.accept()
msg = common.read_message(conn)
if not msg:
# The client has sent an invalid message, we close the connection
# NOTE: We could also transmit an explanatory message to the client here
conn.close()
continue
_handle_message(msg, conn, state, pub_keys)
conn.close()
_write_state(state)
# --- Private ---
def _handle_message(msg, conn, state, pub_keys):
"""
Handles the message as appropriate
:param msg: message from the client socket
:param conn: the client socket
:param state: bulletin board state
:return:
"""
print("Received Message is: {}".format(msg))
if isinstance(msg, ReqCastVote):
_handleReqCastVote(msg, conn, state, pub_keys)
elif isinstance(msg, ReqCloseVoting):
_handleReqCloseVoting(msg, conn, state)
def _handleReqCastVote(msg, conn, state, pub_keys):
# is voting open?
if not state.voting_in_progress:
common.write_message(conn, common.RespError("Voting is NOT open"))
return
# check ZKP
if not _handleZKP(msg, conn, state, pub_keys.paillier_pub_key):
common.write_message(conn, common.RespError("Zero Knowledge Proof failed, cannot cast vote"))
return
# check signature
if not pub_keys.rsa_pub_key.verify(msg.enc_vote, (msg.signed_enc_vote, )):
common.write_message(conn, common.RespError("Signature verification failed"))
return
# check that user hasn't already voted
sha256 = SHA256.new()
sha256.update(str(msg.enc_vote).encode("utf-8"))
vote_hash = sha256.hexdigest()
if vote_hash in state.counted_votes.keys():
common.write_message(conn, common.RespError("Already counted this vote"))
return
# all checks passed, cast the vote
state.encrypted_sums = paillier.e_add(pub_keys.paillier_pub_key, state.encrypted_sums, msg.enc_vote)
state.counted_votes[vote_hash] = msg.enc_vote
resp = RespCastVoteSuccess()
if len(state.counted_votes.keys()) == config.NUM_VOTERS:
print("All voters have voted.")
_handleReqCloseVoting(msg, conn, state)
resp.is_voting_complete = True
common.write_message(conn, resp)
def _handleReqCloseVoting(msg, conn, state):
state.voting_in_progress = False
_write_bulletin_board(state)
print("\n\nBulletin Board has been written to :'{}'".format(config.BULLETIN_BOARD_FILENAME))
sock_to_em = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_to_em.connect(config.EM_ADDR)
req = em_interface.ReqDisplayResults(state.encrypted_sums)
common.write_message(sock_to_em, req)
# the socket will be closed by EM
def _handleZKP(msg, conn, state, pk):
valid_messages = zkp.compute_valid_messages(config.NUM_CANDIDATES, config.NUM_VOTERS)
inv_gmk_params = zkp.compute_inv_gmk(pk.g, pk.n, valid_messages)
e_max = zkp.compute_e_max(pk.n)
u_params = zkp.compute_u_params(msg.enc_vote, inv_gmk_params, pk.n_sq)
# ask for commitment
print("BB: requesting ZKP commitment")
common.write_message(conn, RespZKPProvideCommitment())
req_challenge = common.read_message(conn)
if not isinstance(req_challenge, ReqZKPChallenge):
return False
# read commitment
a_params = req_challenge.a_params
# challenge loop
for i in range(config.ZKP_ITERATIONS):
e_s = zkp.select_e_s(e_max)
print("BB: sending ZKP challenge")
# send challenge
resp_challenge = RespZKPChallenge(e_s)
common.write_message(conn, resp_challenge)
# read response
print("BB: verifying ZKP")
req_verification = common.read_message(conn)
if not isinstance(req_verification, ReqZKPVerify):
return False
e_params = req_verification.e_params
z_params = req_verification.z_params
# verify
if not zkp.verify(e_max, e_s, a_params, e_params, z_params, u_params, pk):
print("ZKP Verification failed")
return False
# continue if passed
return True
def _write_bulletin_board(state):
with open(config.BULLETIN_BOARD_FILENAME, mode='w', encoding='utf-8') as f:
f.write('BULLETIN BOARD\n\n')
f.write('Encrypted Tally: {}\n'.format(state.encrypted_sums))
f.write("\n")
f.write("{:>4}\t{:<65}\t{}\n".format("#", "HASH", "ENCRYPTED VOTE"))
i = 1
for k, v in state.counted_votes.items():
f.write("{:>4}\t{:<65}\t{}\n".format(i, k, v))
i += 1
# -- Bulletin Board State
class BulletinBoardState:
"""
A model to encapsulate Bulletin Board (BB) state
"""
def __init__(self):
self.voting_in_progress = True
self.counted_votes = {}
self.encrypted_sums = None
def __str__(self):
return "<BulletinBoardState: {}, {}, {}>".format(self.voting_in_progress, self.counted_votes,
self.encrypted_sums)
def _read_state():
"""
Reads Bulletin Board (BB) state from disk
:return: an BulletinBoardState instance (read from disk)
"""
with open("bb.pickle", "rb") as f:
state = pickle.load(f)
return state
def _write_state(state):
"""
Writes Bulletin Board (BB) state to the disk
:param state: the state to write to disk
"""
with open("bb.pickle", "wb") as f:
pickle.dump(state, f)
if __name__ == "__main__":
setup()
kick_off()