def new_reputation(self, msg_args): """Handles a request to add a layer of encryption to the reputations. Passes on the reputation to the next server at the end. secret: Secret for ElGamal decryption. reputation: The reputation ciphertext. server_pub_keys: A list of the server public keys so far. init_id: The id of the server that was the first in the ring. """ secret, rep, server_pub_keys, init_id = msg_args if init_id != self.server_id: if init_id == Constants.INIT_ID: init_id = self.server_id # encrypt client reputation server_pub_keys.append(self.pub_key) self.secret = powm(secret, self.pri_key) secret = (secret * powm(Constants.G, self.eph_key)) % Constants.P rep = (rep * self.secret) % Constants.P rep = self.encryptElGamal(rep, server_pub_keys) # pass reputation to next server send(self.next_addr, [ Constants.NEW_REPUTATION, secret, rep, server_pub_keys, init_id ]) else: send(self.ciphertext_socket, [secret, rep]) self.ciphertext_socket.close()
def verify_signature(self, msg, stp, sig): """Verifies signature in message posting. msg: The message to verify stp: The public key of the user sig: The signature Returns whether the signature is valid or not. """ r, s = sig u = powm(self.generator, msg_hash(msg, sha1)) v = (powm(stp, r) * powm(r, s)) % Constants.P return u == v
def _prove_simple(sock, gamma, r, s, g=Constants.G, p=Constants.P, q=Constants.Q): assert (len(r) == len(s)) n = len(r) # step 1 t = recv(sock) # step 2 r_hat = [(r[i] - t) % q for i in range(n)] s_hat = [(s[i] - gamma * t) % q for i in range(n)] theta = [0] + [randkey(0, q - 1) for _ in range(2 * n - 1)] Theta = [] for i in range(2 * n): if i < n: exp = (theta[i] * r_hat[i]) % q exp -= (theta[i + 1] * s_hat[i]) % q exp %= q Theta.append(powm(g, exp, p)) else: exp = (theta[i] * gamma) % q exp -= theta[(i + 1) % (2 * n)] exp %= q Theta.append(powm(g, exp, p)) send(sock, Theta) # step 3 c = recv(sock) # step 4 alpha = [c] tmp = c for i in range(2 * n - 1): if i < n: tmp = (tmp * divide(r_hat[i], s_hat[i], q)) % q alpha.append((tmp + theta[i + 1]) % q) elif i == n: inv = modinv(gamma, q) tmp = (c * powm(inv, (2 * n - 1) - i, q)) % q alpha.append((tmp + theta[i + 1]) % q) else: tmp = (tmp * gamma) % q alpha.append((tmp + theta[i + 1]) % q) send(sock, alpha)
def post(self, msg): """Post a message.""" generator = sendrecv(self.server_addr, [Constants.GET_GENERATOR]) stp = powm(generator, self.pri_key) sig = self.sign(msg, generator) send(self.server_addr, [Constants.NEW_MESSAGE, msg, stp, sig])
def sign(msg, x_i, idx, L, g=Constants.G, p=Constants.P, q=Constants.Q): """Signs a message using a linkable ring signature. msg: The message to be signed x_i: The private key of the signer idx: The index of the public key in L L: List of public keys Returns the signature. """ n = len(L) c = [0 for _ in range(n)] s = [0 for _ in range(n)] # step 1 h = H2(L, g, p, q) t = powm(h, x_i, p) # step 2 u = randkey(0, q - 1) c[(idx + 1) % n] = H1([L, t, msg, powm(g, u, p), powm(h, u, p)]) # step 3 i = (idx + 1) % n while i != idx: s[i] = randkey(0, q - 1) z_1 = (powm(g, s[i], p) * powm(L[i], c[i], p)) % p z_2 = (powm(h, s[i], p) * powm(t, c[i], p)) % p c[(i + 1) % n] = H1([L, t, msg, z_1, z_2]) i = (i + 1) % n # step 4 s[idx] = (u - ((x_i * c[idx]) % q)) % q return (c[0], s, t)
def lrs_sign(self, msg): """Sign for LRS.""" generator = sendrecv(self.server_addr, [Constants.GET_GENERATOR]) stp_array = sendrecv(self.server_addr, [Constants.GET_STP_ARRAY]) stp = powm(generator, self.pri_key) stp_idx = stp_array.index(stp) # modify stp_array to prevent duplicate voting stp_array.append(msg_hash(msg, sha1)) return lrs.sign(msg, self.pri_key, stp_idx, stp_array, g=generator)
def announcement_fwd(self, ann_list, sec_inv): """Encrypts pseudonyms and decrypts reputations.""" new_ann_list = [[], []] for nym in ann_list[0]: new_nym = powm(nym, self.eph_key) self.nym_list[new_nym] = nym new_ann_list[0].append(new_nym) for sec, rep in ann_list[1]: new_rep = self.decryptElGamal(sec_inv, rep) new_ann_list[1].append((sec, new_rep)) return new_ann_list
def verify(msg, L, c_0, s, t, g=Constants.G, p=Constants.P, q=Constants.Q): """Verifies a message signed with a linkable ring signature. msg: The message to be signed L: List of public keys c_0, s, t: The values returned by sign() Returns whether the signature is valid. """ n = len(L) c = [0 for _ in range(n)] c[0] = c_0 h = H2(L, g, p, q) for i in range(n): z_1 = (powm(g, s[i], p) * powm(L[i], c[i], p)) % p z_2 = (powm(h, s[i], p) * powm(t, c[i], p)) % p c[(i + 1) % n] = H1([L, t, msg, z_1, z_2]) return c_0 == c[0]
def sign(self, msg, generator): """Sign with ElGamal signature.""" r, s = 0, 0 while s == 0: k = randkeyRP(1, Constants.P - 2) r = powm(generator, k) s = (msg_hash(msg, sha1) - self.pri_key * r) * modinv( k, Constants.P - 1) s %= Constants.P - 1 return (r, s)
def __init__(self, server_host, server_port, private_key=None): self.name = 'CLIENT' # core variables self.pri_key = randkey() if private_key is None else private_key self.pub_key = powm(Constants.G, self.pri_key) # socket variables self.server_addr = (server_host, server_port) # new client sendrecv(self.server_addr, [Constants.NEW_CLIENT, self.pub_key])
def _verify_simple(sock, Gamma, R, S, g=Constants.G, p=Constants.P, q=Constants.Q): assert (len(R) == len(S)) n = len(R) # step 1 t = randkey(0, q - 1) send(sock, t) # step 2 Theta = recv(sock) # step 3 c = randkey(0, q - 1) send(sock, c) # step 4 alpha = recv(sock) # step 5 U = powm(g, -t % q, p) W = powm(Gamma, -t % q, p) R_hat = [(R[i] * U) % p for i in range(n)] S_hat = [(S[i] * W) % p for i in range(n)] ret = True for i in range(2 * n): if i < n: ver = powm(R_hat[i], alpha[i], p) * powm(S_hat[i], -alpha[i + 1] % q, p) ver %= p ret = ret and (Theta[i] == ver) else: ver = powm(Gamma, alpha[i], p) * powm( g, -alpha[(i + 1) % (2 * n)] % q, p) ver %= p ret = ret and (Theta[i] == ver) return ret
def new_announcement(self, s, msg_args): """Handles a request to carry out one step of the announcement phase. generator: Generator for this server to use. ann_list_pre: Announcement list before the previous server's modifications. ann_list_post: Announcement list after the previous server's modifications. g_, h_: Verifiable shuffle parameters init_id: The id of the server that was the first in the ring. """ generator, ann_list_pre, ann_list_post, g_, h_, init_id = msg_args ann_list = ann_list_post if init_id != self.server_id: if init_id == Constants.INIT_ID: init_id = self.server_id ann_list = [] ann_list.append(list(self.ltp_list.keys())) ann_list.append([(self.secret, v) for v in self.ltp_list.values()]) else: # verify shuffle from prev server if not shuffle.verify(s, ann_list_pre, ann_list_post, g_, h_): eprint(self.name, 'Verifiable shuffle failed.') s.close() return s.close() # update announcement list secret_inv = modinv(powm(self.secret, self.pri_key)) new_ann_list = self.announcement_fwd(ann_list, secret_inv) # shuffle announcement list n = len(new_ann_list[0]) pi = shuffle.generate_permutation(n) new_ann_list = [shuffle.shuffle(elts, pi) for elts in new_ann_list] # update generator and parameters new_generator = powm(generator, self.eph_key) beta = 1 g_ = 1 h_ = secret_inv # pass announcement list to next server s = socket.socket() s.connect(self.next_addr) send(s, [ Constants.NEW_ANNOUNCEMENT, new_generator, ann_list, new_ann_list, g_, h_, init_id ]) # prove shuffle to next server (if more than one server) if self.addr != self.next_addr: shuffle.prove(s, ann_list, new_ann_list, pi, beta, g_, h_) else: # verify shuffle from prev server (if more than one server) if self.addr != self.prev_addr: if not shuffle.verify(s, ann_list_pre, ann_list_post, g_, h_): eprint(self.name, 'Verifiable shuffle failed.') s.close() return s.close() # initialize add announcement stp_list = list(zip(ann_list[0], [rep for sec, rep in ann_list[1]])) stp_args = [stp_list, generator, Constants.INIT_ID] self.replace_stp(stp_args)
def __init__(self, host, port): self.name = 'SERVER' # identification self.server_id = Constants.INIT_ID self.prev_addr = None self.next_addr = None self.secret = None self.client_rep = None # core variables self.eph_key = randkey() self.pri_key = randkey() self.pub_key = powm(Constants.G, self.pri_key) self.secret = None # secret used for encryption/decryption self.ltp_list = { } # long-term pseudonyms and encrypted reputation scores self.stp_list = { } # short-term pseudonyms and decrypted reputation scores self.stp_array = [] # short-term pseudonym array self.generator = None # round-based global generator self.nym_list = {} # pseudonym list used for decryption self.lrs_duplicates = set() # duplicate feedback set # socket variables self.addr = (host, port) self.ss = socket.socket() self.ss.bind(self.addr) self.ss.listen(5) self.server_started = False send(config.COORDINATOR_ADDR, [Constants.NEW_SERVER, self.addr, self.pub_key]) # respond dict for received messages self.respond = { Constants.NEW_CLIENT: self.new_client, Constants.NEW_REPUTATION: self.new_reputation, Constants.NEW_ANNOUNCEMENT: self.new_announcement, Constants.REPLACE_STP: self.replace_stp, Constants.NEW_MESSAGE: self.new_message, Constants.NEW_FEEDBACK: self.new_feedback, Constants.REV_ANNOUNCEMENT: self.rev_announcement, Constants.REPLACE_LTP: self.replace_ltp, Constants.UPDATE_ID: self.update_id, Constants.UPDATE_NEIGHBORS: self.update_neighbors, Constants.GET_GENERATOR: self.get_generator, Constants.GET_STP_ARRAY: self.get_stp_array, Constants.GET_CIPHERTEXTS: self.get_ciphertexts, Constants.GET_CLIENTS: self.get_clients, Constants.UPDATE_CLIENTS: self.update_clients, } # message type dict for received messages self.msg_types = { Constants.NEW_CLIENT: [int], Constants.NEW_REPUTATION: [int, int, list, int], Constants.NEW_ANNOUNCEMENT: [int, list, list, int, int, int], Constants.REPLACE_STP: [list, int, int], Constants.NEW_MESSAGE: [str, int, list], Constants.NEW_FEEDBACK: [int, str, int, list], Constants.REV_ANNOUNCEMENT: [int, list, list, list, int, int, int], Constants.REPLACE_LTP: [list, int, int], Constants.UPDATE_ID: [int], Constants.UPDATE_NEIGHBORS: [list, list], Constants.GET_GENERATOR: [], Constants.GET_STP_ARRAY: [], Constants.GET_CIPHERTEXTS: [], Constants.GET_CLIENTS: [], Constants.UPDATE_CLIENTS: [int, int, list], } assert set(self.respond.keys()) == set(self.msg_types.keys())
def H2(msg, g, p, q): """Hash function 2.""" val = H1(msg) % q return powm(g, val, p)
def verify(sock, elts_pre, elts_post, g_, h_, g=Constants.G, p=Constants.P, q=Constants.Q): """Verify a zero-knowledge proof for the verifiable shuffle. sock: Socket to send messages through elts_pre: Elements before cryptographic operation elts_post: Elements after cryptographic operation g_, h_: Verifiable shuffle parameters """ nym_pre = elts_pre[0] nym_post = elts_post[0] XY_pre = elts_pre[1] XY_post = elts_post[1] assert (len(nym_pre) == len(nym_post)) assert (len(XY_pre) == len(XY_post)) assert (len(nym_pre) == len(XY_pre)) n = len(nym_pre) # step 1 A, C, U, W, Gamma, Lambda_1, Lambda_2 = recv(sock) # step 2 rho = [randkey(0, q - 1) for _ in range(n)] B = [divide(powm(g, rho[i], p), U[i], p) for i in range(n)] send(sock, rho) # step 3 D = recv(sock) # step 4 lam = randkey(0, q - 1) send(sock, lam) # step 5 tau, sigma = recv(sock) # step 6 R = [(A[i] * powm(B[i], lam, p)) % p for i in range(n)] S = [(C[i] * powm(D[i], lam, p)) % p for i in range(n)] ret = _verify_simple(sock, Gamma, R, S, g, p, q) # step 7 Phi_1 = 1 Phi_2 = 1 for i in range(n): X_i, Y_i = XY_pre[i] X_ibar, Y_ibar = XY_post[i] Phi_1 = (Phi_1 * powm(X_ibar, sigma[i], p) * powm(X_i, -rho[i] % q, p)) % p Phi_2 = (Phi_2 * powm(Y_ibar, sigma[i], p) * powm(Y_i, -rho[i] % q, p)) % p for i in range(n): ret = ret and (powm(Gamma, sigma[i], p) == (W[i] * D[i]) % p) ret = ret and (Phi_1 == (Lambda_1 * powm(g_, tau, p)) % p) ret = ret and (Phi_2 == (Lambda_2 * powm(h_, tau, p)) % p) return ret
def rev_announcement(self, s, msg_args): """Handles doing a reverse announcement. secret: Secret for ElGamal decryption. server_pub_keys: Public keys of servers. ann_list_pre: Announcement list before the previous server's modifications. ann_list_post: Announcement list after the previous server's modifications. g_, h_: Verifiable shuffle parameters init_id: The id of the server that was the first in the ring. """ (secret, server_pub_keys, ann_list_pre, ann_list_post, g_, h_, init_id) = msg_args ann_list = ann_list_post if init_id != self.server_id: if init_id == Constants.INIT_ID: init_id = self.server_id ann_list = [] ann_list.append([k for k in self.stp_list.keys()]) ann_list.append([(secret, v) for v in self.stp_list.values()]) else: # verify shuffle from prev server if not shuffle.verify(s, ann_list_pre, ann_list_post, g_, h_): eprint(self.name, 'Verifiable shuffle failed.') s.close() return s.close() self.eph_key = randkey() # update announcement list server_pub_keys.append(self.pub_key) self.secret = powm(secret, self.pri_key) secret = (secret * powm(Constants.G, self.eph_key)) % Constants.P ann_list[1] = [(sec, (rep * self.secret) % Constants.P) for sec, rep in ann_list[1]] new_ann_list = self.announcement_bwd(ann_list, secret, server_pub_keys) # shuffle announcement list n = len(new_ann_list[0]) pi = shuffle.generate_permutation(n) new_ann_list = [shuffle.shuffle(elts, pi) for elts in new_ann_list] # update parameters beta = self.eph_key g_ = Constants.G h_ = 1 for server_pub_key in server_pub_keys: h_ = (h_ * server_pub_key) % Constants.P # pass announcement list to next server s = socket.socket() s.connect(self.prev_addr) send(s, [ Constants.REV_ANNOUNCEMENT, secret, server_pub_keys, ann_list, new_ann_list, g_, h_, init_id ]) # prove shuffle to prev server if self.addr != self.prev_addr: shuffle.prove(s, ann_list, new_ann_list, pi, beta, g_, h_) else: # verify shuffle from next server (if more than one server) if self.addr != self.next_addr: if not shuffle.verify(s, ann_list_pre, ann_list_post, g_, h_): eprint(self.name, 'Verifiable shuffle failed.') s.close() return s.close() # initialize add announcement ltp_list = list(zip(ann_list[0], [rep for sec, rep in ann_list[1]])) ann_args = [ltp_list, secret, Constants.INIT_ID] self.replace_ltp(ann_args)
def encryptElGamal(self, rep, server_pub_keys): """ElGamal encryption of rep using server_pub_keys.""" for server_pub_key in server_pub_keys: rep = (rep * powm(server_pub_key, self.eph_key)) % Constants.P return rep
def prove(sock, elts_pre, elts_post, pi, beta, g_, h_, g=Constants.G, p=Constants.P, q=Constants.Q): """Generate a zero-knowledge proof for the verifiable shuffle. sock: Socket to send messages through elts_pre: Elements before cryptographic operation elts_post: Elements after cryptographic operation pi: Permutation list beta, g_, h_: Verifiable shuffle parameters """ nym_pre = elts_pre[0] nym_post = elts_post[0] XY_pre = elts_pre[1] XY_post = elts_post[1] assert (len(nym_pre) == len(nym_post)) assert (len(XY_pre) == len(XY_post)) assert (len(nym_pre) == len(XY_pre)) n = len(nym_pre) pi_inv = [_ for _ in range(n)] for i in range(n): pi_inv[pi[i]] = i # step 1 a = [randkey(0, q - 1) for _ in range(n)] u = [randkey(0, q - 1) for _ in range(n)] w = [randkey(0, q - 1) for _ in range(n)] tau_0 = randkey(0, q - 1) gamma = randkey(1, q - 1) Gamma = powm(g, gamma, p) A = [powm(g, a[i], p) for i in range(n)] C = [powm(A[pi[i]], gamma, p) for i in range(n)] U = [powm(g, u[i], p) for i in range(n)] W = [powm(g, gamma * w[i], p) for i in range(n)] Lambda_1 = powm(g_, tau_0 + sum([(w[i] * beta) % q for i in range(n)]), p) Lambda_2 = powm(h_, tau_0 + sum([(w[i] * beta) % q for i in range(n)]), p) for i in range(n): X_i, Y_i = XY_pre[i] Lambda_1 = (Lambda_1 * powm(X_i, (w[pi_inv[i]] - u[i]) % q, p)) % p Lambda_2 = (Lambda_2 * powm(Y_i, (w[pi_inv[i]] - u[i]) % q, p)) % p send(sock, [A, C, U, W, Gamma, Lambda_1, Lambda_2]) # step 2 rho = recv(sock) # step 3 b = [(rho[i] - u[i]) % q for i in range(n)] d = [(gamma * b[pi[i]]) % q for i in range(n)] D = [powm(g, d[i], p) for i in range(n)] send(sock, D) # step 4 lam = recv(sock) # step 5 r = [(a[i] + lam * b[i]) % q for i in range(n)] s = [(gamma * r[pi[i]]) % q for i in range(n)] sigma = [(w[i] + b[pi[i]]) % q for i in range(n)] tau = (-tau_0 + sum([(b[i] * beta) % q for i in range(n)])) % q send(sock, [tau, sigma]) # step 6 _prove_simple(sock, gamma, r, s, g, p, q)