def _step_2_password_verification(self, sock, password, salt): """ Verify password. :param sock: :param password: :param salt: :return: """ pw_hash = self.crypto_service.compute_pw_hash(password, salt) nonce = PacketOrganiser.genRandomNumber() msg = PacketOrganiser.prepare_packet(pw_hash, nonce) auth_2_msg = self.crypto_service.sym_encrypt(self.dh_key, msg) sock.sendto(auth_2_msg, self.server_addr) # step 3 recv_msg = util.get_one_response(sock, self.server_addr) # print("Receive msg from {}: {}".format(self.server_addr, recv_msg)) dec_msg = self.crypto_service.sym_decrypt(self.dh_key, recv_msg) n, msg_parts = PacketOrganiser.process_packet(dec_msg) if n != nonce: raise Exception(c.STEP_THREE_NONCE_FAIL_MSG) auth_result = msg_parts[0] if auth_result == c.AUTH_SUCCESS: self.auth_success = True elif auth_result == c.MSG_RESPONSE_WRONG_CR: print(c.WRONG_CR_MSG) return self.auth_success
def _authenticate_and_send_msg(self, username, chat_msg): # start peer authentication nonce = util.get_good_nonce(self.request_cache) new_auth = ClientClientAuthentication(username, self.auth.crypto_service, chat_msg) new_auth.timestamp = PacketOrganiser.get_new_timestamp() # timestamp when created, for packet resend key = self.auth.dh_key msg_to_send = PacketOrganiser.prepare_packet([c.MSG_TYPE_START_NEW_CHAT, username, ""], nonce=nonce) util.add_to_request_cache( self.request_cache, nonce, c.MSG_TYPE_START_NEW_CHAT, key, msg_to_send, self.auth.server_addr, new_auth ) # add to cache return username, self.auth.server_addr, self.serv.sym_encrypt(key, msg_to_send)
def handle(self): """ Handle requests from the clients. :return: """ global auth_dict, nonce_dict, crypto_service, password_hash_dict, user_addr_dict, chatting_service msg = self.request[0] sock = self.request[1] # get auth instance for client if self.client_address not in auth_dict: try: _, msg_parts = PacketOrganiser.process_packet(msg) except: return if msg_parts[0] != c.GREETING: return # new client, create an auth entry in the auth dictionary auth_dict[self.client_address] = Authentication( self.client_address, crypto_service, password_hash_dict) else: auth = auth_dict[self.client_address] if not PacketOrganiser.isValidTimeStampSeconds( auth.timestamp, c.KEEP_ALIVE_TIME): auth_dict.pop(self.client_address) cur_auth = auth_dict[self.client_address] assert isinstance(cur_auth, Authentication) rep = None if not cur_auth.is_auth(): rep = cur_auth.process_request(msg, user_addr_dict) else: # get decrypted msg dec_msg = crypto_service.sym_decrypt(cur_auth.dh_key, msg) n, msg_ps = PacketOrganiser.process_packet(dec_msg) auth_dict[ self. client_address].timestamp = PacketOrganiser.get_new_timestamp( ) # update timestamp rep = chatting_service.get_response(self.client_address, msg_ps) if rep is not None: rep = PacketOrganiser.prepare_packet(rep, n) rep = crypto_service.sym_encrypt(cur_auth.dh_key, rep) try: if rep is not None: sys.stdout.flush() sock.sendto(rep, self.client_address) elif cur_auth.is_auth(): cur_auth.loginfailures += 1 except socket.error: print(c.FAIL_MSG_FWD) return
def handle(self): """ Handle requests from the clients. :return: """ global auth_dict, nonce_dict, crypto_service, password_hash_dict, user_addr_dict, chatting_service msg = self.request[0] sock = self.request[1] # get auth instance for client if self.client_address not in auth_dict: try: _, msg_parts = PacketOrganiser.process_packet(msg) except: return if msg_parts[0] != c.GREETING: return # new client, create an auth entry in the auth dictionary auth_dict[self.client_address] = Authentication(self.client_address, crypto_service, password_hash_dict) else: auth = auth_dict[self.client_address] if not PacketOrganiser.isValidTimeStampSeconds(auth.timestamp,c.KEEP_ALIVE_TIME): auth_dict.pop(self.client_address) cur_auth = auth_dict[self.client_address] assert isinstance(cur_auth, Authentication) rep = None if not cur_auth.is_auth(): rep = cur_auth.process_request(msg, user_addr_dict) else: # get decrypted msg dec_msg = crypto_service.sym_decrypt(cur_auth.dh_key, msg) n, msg_ps = PacketOrganiser.process_packet(dec_msg) auth_dict[self.client_address].timestamp = PacketOrganiser.get_new_timestamp() # update timestamp rep = chatting_service.get_response(self.client_address, msg_ps) if rep is not None: rep = PacketOrganiser.prepare_packet(rep, n) rep = crypto_service.sym_encrypt(cur_auth.dh_key, rep) try: if rep is not None: sys.stdout.flush() sock.sendto(rep, self.client_address) elif cur_auth.is_auth(): cur_auth.loginfailures += 1 except socket.error: print(c.FAIL_MSG_FWD) return
def _step_0_get_challenge(self, sock): """ Challenge step. :param sock: :return: """ # send greeting to the server # print("Get Username: {}, Password: {}".format(username, password)) msg_to_send = PacketOrganiser.prepare_packet(c.GREETING, add_time=False) sock.sendto(msg_to_send, self.server_addr) recv_msg = util.get_one_response(sock, self.server_addr) # print("Receive msg from {}: {}".format(self.server_addr, recv_msg)) _, recv_msg_parts = PacketOrganiser.process_packet(recv_msg) # chl, k, ind = recv_msg_parts return recv_msg_parts
def run(self): """ A new thread for resending any message in the cache. This thread also sends keep-alive messages every thirty seconds. """ global addr_auths, user_addr_dict, active_users i = 0 while self.resending: time.sleep(c.RESEND_SLEEP_SEC) i = i + c.TIME_STEP if i == c.KEEP_ALIVE_INTERVAL: i = 0 self.send_keep_alive() caches_to_remove = [] for nonce, cache in self.request_cache.iteritems(): ts = cache[c.CACHE_TS_IND] if not PacketOrganiser.isValidTimeStamp(ts, c.TS_RESEND_MICRO_SEC): if not self.resend(cache): caches_to_remove.append(nonce) for n in caches_to_remove: cache_to_remove = self.request_cache.pop(n) # also remove addr_auths entry addr = cache_to_remove[c.CACHE_ADDR_IND] if addr in addr_auths: auth_to_delete = addr_auths.pop(addr) user_addr_dict.pop(auth_to_delete.username) active_users.pop(auth_to_delete.username)
def run(self): """ A new thread for resending any message in the cache. This thread also sends keep-alive messages every thirty seconds. """ global addr_auths, user_addr_dict, active_users i = 0 while self.resending: time.sleep(c.RESEND_SLEEP_SEC) i = i + c.TIME_STEP if i == c.KEEP_ALIVE_INTERVAL: i = 0 self.send_keep_alive() caches_to_remove = [] for nonce, cache in self.request_cache.iteritems(): ts = cache[c.CACHE_TS_IND] if not PacketOrganiser.isValidTimeStamp( ts, c.TS_RESEND_MICRO_SEC): if not self.resend(cache): caches_to_remove.append(nonce) for n in caches_to_remove: cache_to_remove = self.request_cache.pop(n) # also remove addr_auths entry addr = cache_to_remove[c.CACHE_ADDR_IND] if addr in addr_auths: auth_to_delete = addr_auths.pop(addr) user_addr_dict.pop(auth_to_delete.username) active_users.pop(auth_to_delete.username)
def send_keep_alive(self): """ Sends a keep alive message to the server. """ global server_auth res_msg = PacketOrganiser.prepare_packet(c.MSG_TYPE_KEEP_ALIVE) encrypt_msg = server_auth.crypto_service.sym_encrypt(server_auth.dh_key, res_msg) self.sock.sendto(encrypt_msg, server_auth.server_addr)
def send_keep_alive(self): """ Sends a keep alive message to the server. """ global server_auth res_msg = PacketOrganiser.prepare_packet(c.MSG_TYPE_KEEP_ALIVE) encrypt_msg = server_auth.crypto_service.sym_encrypt( server_auth.dh_key, res_msg) self.sock.sendto(encrypt_msg, server_auth.server_addr)
def replace_ts_in_msg(msg): """ :param msg: :return: """ msg_without_ts = msg[:-c.TS_LEN] new_msg = msg_without_ts + PacketOrganiser.get_new_timestamp() return new_msg
def display_user_message(raw_msg, username): """ :param raw_msg: :param username: :return: """ ts = PacketOrganiser.get_new_timestamp() msg = "{} <{}>: {}".format(username, ts, raw_msg) cmd_output(msg)
def send_confirmation(sock, crypto_service, key, n, r_addr, second_part=None): if second_part: conf_msg_parts = [c.MSG_RESPONSE_OK, second_part, ""] else: conf_msg_parts = c.MSG_RESPONSE_OK conf_msg = PacketOrganiser.prepare_packet(conf_msg_parts, n) enc_conf_msg = crypto_service.sym_encrypt(key, conf_msg) sock.sendto(enc_conf_msg, r_addr)
def _stage_0_generate_challenge(self): """ Generate and send challenge to client :return: """ # sent a challenge to client chl, ind, self.masksize = self.ra.get_challenge_tupple(self.loginfailures) self.stage = c.AUTH_STAGE_1 msg_to_send_parts = [chl, self.masksize, ind] msg_to_send = PacketOrganiser.prepare_packet(msg_to_send_parts, add_time=False) return msg_to_send
def _handle_single_cmd(self, msg_type): """ :param msg_type: the type of the message :return: Prepares and returns the encrypted message depending on the message type """ nonce = util.get_good_nonce(self.request_cache) res_msg = PacketOrganiser.prepare_packet(msg_type, nonce) util.add_to_request_cache( self.request_cache, nonce, msg_type, self.auth.dh_key, res_msg, self.auth.server_addr ) # add to cache return msg_type, self.auth.server_addr, self.serv.sym_encrypt(self.auth.dh_key, res_msg)
def handle_start_new_chat(self, a_addr, b_username): """ b_addr, k_ab, ttb, ts, n1 :param a_addr: Chat requesting client address :param b_addr: Address of client wanting to chat with :return: The server sends as response the ticket to b and a symettric key to talk to b. """ k_ab = self.auth_dict[a_addr].crypto_service.new_sym_key() a_username = self.auth_dict[a_addr].username b_addr = self.user_addr_dict[b_username] auth = self.auth_dict[b_addr] if not PacketOrganiser.isValidTimeStampSeconds(auth.timestamp, c.KEEP_ALIVE_TIME): self.auth_dict.pop(b_addr) return c.ERR_CLIENT_DOWN k_b = self.auth_dict[b_addr].dh_key ttb = PacketOrganiser.prepare_packet( [a_username, util.addr_to_str(a_addr), k_ab]) enc_ttb = self.crypto_service.sym_encrypt(k_b, ttb) signed_enc_ttb = enc_ttb + self.crypto_service.rsa_sign(enc_ttb) return [util.addr_to_str(b_addr), k_ab, signed_enc_ttb]
def resend(self, cache): """ :param cache: The parameter cache stores what message haven't acknowledged to resend them. :return: True if resend happened, False otherwise """ global server_auth # first check if the cache is more than 30 seconds old # if it is, we will drop the cache entry, which means the other side is off line, don't resend again if cache[c.CACHE_TYPE_IND] == c.MSG_TYPE_MSG: original_ts = self.get_original_ts(cache) if not PacketOrganiser.isValidTimeStampSeconds(original_ts, c.RESEND_TIMEOUT): util.cmd_output(c.WARNING_STOP_RESENDING) return False ts = PacketOrganiser.get_new_timestamp() cache[c.CACHE_TS_IND] = ts msg = util.replace_ts_in_msg(cache[c.CACHE_MSG_IND]) # cache[c.CACHE_MSG_IND] = msg enc_msg = server_auth.crypto_service.sym_encrypt(cache[c.CACHE_KEY_IND], msg) self.sock.sendto(enc_msg, cache[c.CACHE_ADDR_IND]) return True
def __init__(self, addr, remote_addr, crypto_service): assert isinstance(crypto_service, CryptoService) self.crypto_service = crypto_service self.addr = addr self.timestamp = time.time() self.ra = RequestAuthority.RequestAuthority() self.stage = 0 self.dh_key = 0 self.server_addr = remote_addr self.auth_success = False self.packetgen = PacketOrganiser() self.username = None
def handle_list(self): """ :return: Handles the list input and sends the list of active users as response. """ res = "" for user, addr in self.user_addr_dict.iteritems(): auth = self.auth_dict[addr] if not PacketOrganiser.isValidTimeStampSeconds( auth.timestamp, c.KEEP_ALIVE_TIME): self.auth_dict.pop(addr) continue res += user + "," return [c.MSG_TYPE_LIST, res[:-1], ""]
def _stage_2_pw_check(self, request, user_addr_dict): """ Stage 2 is to check if the password is correct :param request: Request is the incoming message which contains the password. :param user_addr_dict: The user_add_dict is the dictionary which has each user and associated address stored. :return: The method returns a encrypted response message """ # decrypt the message and check the password hash dec_request = self.crypto_service.sym_decrypt(self.dh_key, request) n, request_parts = PacketOrganiser.process_packet(dec_request) pw_hash = request_parts[0] if pw_hash != self.pw_dict[self.username][0]: msg = PacketOrganiser.prepare_packet(c.MSG_RESPONSE_WRONG_CR, nonce=n) enc_msg = self.crypto_service.sym_encrypt(self.dh_key, msg) self.stage = c.AUTH_STAGE_INIT self.loginfailures += 1 else: msg = PacketOrganiser.prepare_packet(c.AUTH_SUCCESS, nonce=n) enc_msg = self.crypto_service.sym_encrypt(self.dh_key, msg) self.stage = c.AUTH_STAGE_FINISHED user_addr_dict[self.username] = self.addr return enc_msg
def _send_msg(self, username, chat_msg): # send the message to user addr = self.user_addr_dict[username] auth = self.addr_auths[addr] key = auth.dh_key nonce = util.get_good_nonce(self.request_cache) auth.last_nonce = nonce hmac = CryptoService.generate_hmac_sign(key, chat_msg) chat_msg_hmac = chat_msg + hmac # add HMAC to chat_msg msg_to_send = PacketOrganiser.prepare_packet([c.MSG_TYPE_MSG, chat_msg_hmac, ""], nonce=nonce) encrypt_msg = self.serv.sym_encrypt(key, msg_to_send) util.add_to_request_cache(self.request_cache, nonce, c.MSG_TYPE_MSG, key, msg_to_send, addr) # add to cache return None, addr, encrypt_msg
def __init__(self, username, crypto_service, first_msg=None): assert isinstance(crypto_service, CryptoService) self.crypto_service = crypto_service self.timestamp = time.time() self.ra = RequestAuthority() self.stage = 0 self.dh_key = 0 self.pri_key = 0 self.auth_success = False self.packetgen = PacketOrganiser() self.first_msg = first_msg # message initialize the C/C authentication self.last_nonce = None self.username = username
def resend(self, cache): """ :param cache: The parameter cache stores what message haven't acknowledged to resend them. :return: True if resend happened, False otherwise """ global server_auth # first check if the cache is more than 30 seconds old # if it is, we will drop the cache entry, which means the other side is off line, don't resend again if cache[c.CACHE_TYPE_IND] == c.MSG_TYPE_MSG: original_ts = self.get_original_ts(cache) if not PacketOrganiser.isValidTimeStampSeconds( original_ts, c.RESEND_TIMEOUT): util.cmd_output(c.WARNING_STOP_RESENDING) return False ts = PacketOrganiser.get_new_timestamp() cache[c.CACHE_TS_IND] = ts msg = util.replace_ts_in_msg(cache[c.CACHE_MSG_IND]) # cache[c.CACHE_MSG_IND] = msg enc_msg = server_auth.crypto_service.sym_encrypt( cache[c.CACHE_KEY_IND], msg) self.sock.sendto(enc_msg, cache[c.CACHE_ADDR_IND]) return True
def start_authenticate(self, sock, auth_info, a_username): """ :param sock: :param auth_info: [b_addr, k_ab, ttb] :return: """ b_addr = util.str_to_addr(auth_info[0]) k_ab = auth_info[1] ttb = auth_info[2] # ttb, k_ab{a, pub_key, ts} self.pri_key = self.crypto_service.get_dh_pri_key() self.dh_key = k_ab pub_key = self.crypto_service.get_dh_pub_key(self.pri_key) inside_msg_parts = [a_username, pub_key, ""] inside_msg = PacketOrganiser.prepare_packet(inside_msg_parts) enc_inside_msg = self.crypto_service.sym_encrypt(k_ab, inside_msg) msg_parts = [ttb, enc_inside_msg, ""] pack_to_send = PacketOrganiser.prepare_packet(msg_parts) # send the hello message to the other client sock.sendto(pack_to_send, b_addr) return b_addr
def add_to_request_cache(cache, nonce, type, key, msg, addr, auth=None): """ :param cache: :param nonce: :param type: :param key: :param msg: :param addr: :param auth: :return: """ if nonce in cache: raise Exception(c.WARNING_EXISTED_NONCE) cache[nonce] = [type, key, msg, addr, PacketOrganiser.get_new_timestamp()] if auth: cache[nonce].append(auth)
def _step_1_dh_key_establish(self, sock, chl, k, ind, username): """ DH contribution exchange :param sock: :param chl: :param k: :param ind: :return: """ ans = self.ra.compute_answer(chl, k) dh_pri_key = self.crypto_service.get_dh_pri_key() dh_pub_key = self.crypto_service.get_dh_pub_key(dh_pri_key) msg_to_send_parts = [dh_pub_key, username, ""] nonce = PacketOrganiser.genRandomNumber() msg_to_send = PacketOrganiser.prepare_packet(msg_to_send_parts, nonce=nonce, add_time=False) enc_msg_to_send = self.crypto_service.rsa_encrypt(msg_to_send) auth_1_msg_parts = [ans, ind, enc_msg_to_send] auth_1_msg = PacketOrganiser.prepare_packet(auth_1_msg_parts, add_time=False) sock.sendto(auth_1_msg, self.server_addr) recv_msg = util.get_one_response(sock, self.server_addr) # print("Receive msg from {}, length: {}".format(self.server_addr, len(recv_msg))) _, msg_sign = PacketOrganiser.process_packet(recv_msg) if msg_sign[0] == c.MSG_RESPONSE_WRONG_CR: print(c.WRONG_CR_MSG) return elif msg_sign[0] == c.MSG_RESPONSE_USER_EXISTS: print(c.USER_ALREADY_LOGIN_MSG) return else: msg, sign, _ = msg_sign if not self.crypto_service.rsa_verify(msg, sign): # TODO for testing raise Exception(c.STEP_ONE_FAIL_MSG) _, pub_enc_salt = PacketOrganiser.process_packet(msg) other_pub_key, enc_salt, _ = pub_enc_salt other_pub_key = int(other_pub_key) self.dh_key = self.crypto_service.get_dh_secret( dh_pri_key, other_pub_key) dec_salt_pack = self.crypto_service.sym_decrypt( self.dh_key, enc_salt) n1, salt_parts = PacketOrganiser.process_packet(dec_salt_pack) salt = salt_parts[0] if n1 == nonce: return salt
def _stage_1_dh_key_exchange(self, request, user_addr_dict): """ check the challenge answer, decrypt the client DH public key and send DH public key back :param request: request from client :param user_addr_dict: dictionary which keep tracking of the username and corresponding IP address and port :return: """ try: _, request_parts = PacketOrganiser.process_packet(request) c_ans, ind, enc_client_msg = request_parts except: return None c_ans = int(c_ans) ind = int(ind) #k = self.ra.getMaskSize() # TODO flaw when mask size changed if self.ra.challengeComm.is_challenge_matched(self.masksize, ind, c_ans): dec_msg = self.crypto_service.rsa_decrypt(enc_client_msg) n1, dec_msg_parts = PacketOrganiser.process_packet(dec_msg) dec_dh_pub_client, username, _ = dec_msg_parts if username in user_addr_dict: # prevent duplicate login self.stage = c.AUTH_STAGE_INIT return PacketOrganiser.prepare_packet(c.MSG_RESPONSE_USER_EXISTS, nonce=n1) if username not in self.pw_dict: self.stage = c.AUTH_STAGE_INIT return PacketOrganiser.prepare_packet(c.MSG_RESPONSE_WRONG_CR, nonce=n1) self.username = username dec_dh_pub_client = int(dec_dh_pub_client) dh_pri_key = self.crypto_service.get_dh_pri_key() dh_pub_server = self.crypto_service.get_dh_pub_key(dh_pri_key) self.dh_key = self.crypto_service.get_dh_secret(dh_pri_key, dec_dh_pub_client) # compose response: public key, K{N1, salt}, sign whole message salt = self.pw_dict[self.username][1] # get salt from username salt_pack = PacketOrganiser.prepare_packet(salt, nonce=n1, add_time=False) enc_salt = self.crypto_service.sym_encrypt(self.dh_key, salt_pack) msg = PacketOrganiser.prepare_packet([dh_pub_server, enc_salt, ""], add_time=False) sign = self.crypto_service.rsa_sign(msg) signed_msg = PacketOrganiser.prepare_packet([msg, sign, ""], add_time=False) self.stage = c.AUTH_STAGE_2 return signed_msg
def _step_1_dh_key_establish(self, sock, chl, k, ind, username): """ DH contribution exchange :param sock: :param chl: :param k: :param ind: :return: """ ans = self.ra.compute_answer(chl, k) dh_pri_key = self.crypto_service.get_dh_pri_key() dh_pub_key = self.crypto_service.get_dh_pub_key(dh_pri_key) msg_to_send_parts = [dh_pub_key, username, ""] nonce = PacketOrganiser.genRandomNumber() msg_to_send = PacketOrganiser.prepare_packet(msg_to_send_parts, nonce=nonce, add_time=False) enc_msg_to_send = self.crypto_service.rsa_encrypt(msg_to_send) auth_1_msg_parts = [ans, ind, enc_msg_to_send] auth_1_msg = PacketOrganiser.prepare_packet(auth_1_msg_parts, add_time=False) sock.sendto(auth_1_msg, self.server_addr) recv_msg = util.get_one_response(sock, self.server_addr) # print("Receive msg from {}, length: {}".format(self.server_addr, len(recv_msg))) _, msg_sign = PacketOrganiser.process_packet(recv_msg) if msg_sign[0] == c.MSG_RESPONSE_WRONG_CR: print(c.WRONG_CR_MSG) return elif msg_sign[0] == c.MSG_RESPONSE_USER_EXISTS: print(c.USER_ALREADY_LOGIN_MSG) return else: msg, sign, _ = msg_sign if not self.crypto_service.rsa_verify(msg, sign): # TODO for testing raise Exception(c.STEP_ONE_FAIL_MSG) _, pub_enc_salt = PacketOrganiser.process_packet(msg) other_pub_key, enc_salt, _ = pub_enc_salt other_pub_key = int(other_pub_key) self.dh_key = self.crypto_service.get_dh_secret(dh_pri_key, other_pub_key) dec_salt_pack = self.crypto_service.sym_decrypt(self.dh_key, enc_salt) n1, salt_parts = PacketOrganiser.process_packet(dec_salt_pack) salt = salt_parts[0] if n1 == nonce: return salt
from CryptoService import CryptoService from ClientServerAuthentication import ClientServerAuthentication from ClientClientAuthentication import ClientClientAuthentication import Consts as c from UserInputHandler import UserInputHandler from PacketOrganiser import PacketOrganiser from ClientChattingService import ClientChattingService import time server_auth = None active_users = {} # active user list from server addr_auths = {} # addr : auth user_addr_dict = {} # username : addr request_cache = { } # nonce : [request type, msg, key, addr, ts, auth for pre-auth <get TTB from server> (if any)] packetorg = PacketOrganiser() class ListenThread(threading.Thread): def __init__(self, sock, saddr): """ sock: socket used for sending message to server saddr: server address """ threading.Thread.__init__(self) self.sock = sock self.listen = True # flag for terminate the thread self.server_addr = saddr def run(self): """
def get_good_nonce(dict): nonce = PacketOrganiser.genRandomNumber() while nonce in dict: nonce = PacketOrganiser.genRandomNumber() return nonce
def run_client(server_ip, server_port): """ Main function to start the client. server_ip: IP address of the server server_port: port number which server uses to communicate """ global server_auth, user_addr_dict, active_users g = 2 p = util.load_df_param_from_file(c.DH_CONFIG_PATH) crypto_service = CryptoService(rsa_pub_path=c.PUB_KEY_PATH, p=p, g=g) server_addr = (server_ip, server_port) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client_port = server_port + 1 local_ip = socket.gethostbyname(socket.gethostname()) client_addr = (local_ip, client_port) # try to find a available local port port_found = False while not port_found: try: sock.bind(client_addr) port_found = True except socket.error: client_addr = (client_addr[0], client_addr[1] + 1) continue server_auth = ClientServerAuthentication(client_addr, server_addr, crypto_service) try: server_auth.authenticate_with_server(sock) except socket.error: print Consts.FAIL_GRE_MSG return chat_service = ClientChattingService(active_users, server_auth) # start a background to handle user input t_listen = ListenThread(sock, server_addr) t_listen.start() # start a background to resend message t_resend = ResendThread(sock, request_cache) t_resend.start() sys.stdout.write(Consts.PROMPT) sys.stdout.flush() while True: try: # listening to the server and display the message recv_msg, r_addr = sock.recvfrom(20480) if r_addr == server_addr and recv_msg: dec_msg = crypto_service.sym_decrypt(server_auth.dh_key, recv_msg) n, msg_ps = PacketOrganiser.process_packet(dec_msg) # first check if it's a client/client authentication # response from server if n in request_cache: # check the type of request cache = request_cache[n] request_type = cache[c.CACHE_TYPE_IND] if request_type == c.MSG_TYPE_LOGOUT: # process logout confirmation if msg_ps[0] == c.MSG_RESPONSE_OK: request_cache.pop(n) break elif request_type == c.MSG_TYPE_START_NEW_CHAT: # process the auth response from server cur_auth = cache[c.CACHE_AUTH_IND] assert isinstance(cur_auth, ClientClientAuthentication) b_addr = cur_auth.start_authenticate( sock, msg_ps, server_auth.username) user_addr_dict[cur_auth.username] = b_addr addr_auths[b_addr] = cur_auth request_cache.pop(n) elif request_type == c.MSG_TYPE_LIST: # process the user list form server chat_service.process_message(msg_ps) request_cache.pop(n) elif request_type == c.ERR_CLIENT_DOWN: print(c.ERR_CLIENT_DOWN) request_cache.pop(n) elif r_addr in addr_auths: #Reply or chat request from client cur_auth = addr_auths[r_addr] dec_msg = crypto_service.sym_decrypt(cur_auth.dh_key, recv_msg) n, dec_msg_parts = PacketOrganiser.process_packet(dec_msg) type = dec_msg_parts[0] if cur_auth.auth_success: if type == c.MSG_RESPONSE_OK: if n in request_cache: cache = request_cache[n] request_type = cache[c.CACHE_TYPE_IND] if request_type == c.MSG_TYPE_MSG: request_cache.pop(n) continue elif type == c.MSG_TYPE_MSG: # display user message msg_hmac = dec_msg_parts[1] # check the HMAC msg, sign = PacketOrganiser.divide_signature(msg_hmac) if CryptoService.verify_hmac_sign( cur_auth.dh_key, msg, sign): util.display_user_message(msg, cur_auth.username) # send message confirmation back util.send_confirmation(sock, crypto_service, cur_auth.dh_key, n, r_addr) else: # deal with peer auth success msg if type == c.MSG_RESPONSE_OK: if n in request_cache: cache = request_cache[n] request_type = cache[c.CACHE_TYPE_IND] if request_type == c.MSG_TYPE_PUB_KEY: # verify nonce if n != cur_auth.last_nonce: continue else: cur_auth.auth_success = True request_cache.pop(n) # check the HMAC msg_hmac = dec_msg_parts[1] msg, sign = PacketOrganiser.divide_signature( msg_hmac) if CryptoService.verify_hmac_sign( cur_auth.dh_key, msg, sign): # display user message util.display_user_message( msg, cur_auth.username) # send message confirmation back util.send_confirmation( sock, crypto_service, cur_auth.dh_key, n, r_addr) elif type == c.MSG_TYPE_PUB_KEY: # finish peer authentication on Alice side b_pub_key = int(dec_msg_parts[1]) cur_auth.dh_key = crypto_service.get_dh_secret( cur_auth.pri_key, b_pub_key) cur_auth.auth_success = True cur_auth.last_nonce = n # send confirmation and first message to Bob hmac = CryptoService.generate_hmac_sign( cur_auth.dh_key, cur_auth.first_msg) first_msg_hmac = cur_auth.first_msg + hmac conf_msg_parts = [ c.MSG_RESPONSE_OK, first_msg_hmac, "" ] conf_msg = PacketOrganiser.prepare_packet( conf_msg_parts, n) enc_conf_msg = crypto_service.sym_encrypt( cur_auth.dh_key, conf_msg) # add to request cache # a hack here (TYPE_MSG instead of RESPONSE_OK) to make the confirmation work util.add_to_request_cache(request_cache, n, c.MSG_TYPE_MSG, cur_auth.dh_key, conf_msg, r_addr) sock.sendto(enc_conf_msg, r_addr) else: # the r_addr not in user_addr_dict, this can be a TTB # handle TTB from Alice _, msg_ps = PacketOrganiser.process_packet(recv_msg) signed_ttb, enc_inside_msg, _ = msg_ps ttb = signed_ttb[:-c.RSA_SIGN_LENGTH] sign = signed_ttb[-c.RSA_SIGN_LENGTH:] if not crypto_service.rsa_verify(ttb, sign): raise Exception(c.WARNING_TTB_INVALID) dec_ttb = crypto_service.sym_decrypt(server_auth.dh_key, ttb) _, ttb_parts = PacketOrganiser.process_packet( dec_ttb) # a_username, a_addr, k_ab a_username_ttb, a_addr, k_ab = ttb_parts a_addr = util.str_to_addr(a_addr) # decrypt inside message with k_ab dec_inside_msg = crypto_service.sym_decrypt( k_ab, enc_inside_msg) _, inside_msg_parts = PacketOrganiser.process_packet( dec_inside_msg) a_username, a_pub_key, _ = inside_msg_parts # print("username_ttb: {}, a_username: {}".format(a_username_ttb, a_username)) if a_username_ttb != a_username: raise Exception(c.WARNING_TTB_USERNAME_INVALID) new_auth = ClientClientAuthentication(a_username, crypto_service) pri_key = crypto_service.get_dh_pri_key() pub_key = crypto_service.get_dh_pub_key(pri_key) new_auth.dh_key = crypto_service.get_dh_secret( pri_key, int(a_pub_key)) nonce = util.get_good_nonce(request_cache) new_auth.last_nonce = nonce # send pub key to Alice msg_parts = [c.MSG_TYPE_PUB_KEY, pub_key, ""] plain_msg = PacketOrganiser.prepare_packet(msg_parts, nonce) enc_msg = crypto_service.sym_encrypt(k_ab, plain_msg) sock.sendto(enc_msg, a_addr) # add new auth to dict addr_auths[a_addr] = new_auth user_addr_dict[a_username] = a_addr util.add_to_request_cache(request_cache, nonce, c.MSG_TYPE_PUB_KEY, k_ab, plain_msg, a_addr) # add to request cache except socket.error: # sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # sock.bind(client_addr) continue except KeyboardInterrupt: # when seeing ctrl-c terminate the client t_listen.stop() t_listen.join() t_resend.stop() t_resend.join() print c.BYE server_auth.logout(sock) sock.close() return except: pass t_listen.stop() t_listen.join() t_resend.stop() t_resend.join() print c.BYE server_auth.logout(sock) sock.close()
def logout(self, sock): msg = PacketOrganiser.prepare_packet(c.MSG_TYPE_LOGOUT) enc_msg = self.crypto_service.sym_encrypt(self.dh_key, msg) sock.sendto(enc_msg, self.server_addr)
def run_client(server_ip, server_port): """ Main function to start the client. server_ip: IP address of the server server_port: port number which server uses to communicate """ global server_auth, user_addr_dict, active_users g = 2 p = util.load_df_param_from_file(c.DH_CONFIG_PATH) crypto_service = CryptoService(rsa_pub_path=c.PUB_KEY_PATH, p=p, g=g) server_addr = (server_ip, server_port) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client_port = server_port + 1 local_ip = socket.gethostbyname(socket.gethostname()) client_addr = (local_ip, client_port) # try to find a available local port port_found = False while not port_found: try: sock.bind(client_addr) port_found = True except socket.error: client_addr = (client_addr[0], client_addr[1] + 1) continue server_auth = ClientServerAuthentication(client_addr, server_addr, crypto_service) try: server_auth.authenticate_with_server(sock) except socket.error: print Consts.FAIL_GRE_MSG return chat_service = ClientChattingService(active_users, server_auth) # start a background to handle user input t_listen = ListenThread(sock, server_addr) t_listen.start() # start a background to resend message t_resend = ResendThread(sock, request_cache) t_resend.start() sys.stdout.write(Consts.PROMPT) sys.stdout.flush() while True: try: # listening to the server and display the message recv_msg, r_addr = sock.recvfrom(20480) if r_addr == server_addr and recv_msg: dec_msg = crypto_service.sym_decrypt(server_auth.dh_key, recv_msg) n, msg_ps = PacketOrganiser.process_packet(dec_msg) # first check if it's a client/client authentication # response from server if n in request_cache: # check the type of request cache = request_cache[n] request_type = cache[c.CACHE_TYPE_IND] if request_type == c.MSG_TYPE_LOGOUT: # process logout confirmation if msg_ps[0] == c.MSG_RESPONSE_OK: request_cache.pop(n) break elif request_type == c.MSG_TYPE_START_NEW_CHAT: # process the auth response from server cur_auth = cache[c.CACHE_AUTH_IND] assert isinstance(cur_auth, ClientClientAuthentication) b_addr = cur_auth.start_authenticate(sock, msg_ps, server_auth.username) user_addr_dict[cur_auth.username] = b_addr addr_auths[b_addr] = cur_auth request_cache.pop(n) elif request_type == c.MSG_TYPE_LIST: # process the user list form server chat_service.process_message(msg_ps) request_cache.pop(n) elif request_type == c.ERR_CLIENT_DOWN: print(c.ERR_CLIENT_DOWN) request_cache.pop(n) elif r_addr in addr_auths: #Reply or chat request from client cur_auth = addr_auths[r_addr] dec_msg = crypto_service.sym_decrypt(cur_auth.dh_key, recv_msg) n, dec_msg_parts = PacketOrganiser.process_packet(dec_msg) type = dec_msg_parts[0] if cur_auth.auth_success: if type == c.MSG_RESPONSE_OK: if n in request_cache: cache = request_cache[n] request_type = cache[c.CACHE_TYPE_IND] if request_type == c.MSG_TYPE_MSG: request_cache.pop(n) continue elif type == c.MSG_TYPE_MSG: # display user message msg_hmac = dec_msg_parts[1] # check the HMAC msg, sign = PacketOrganiser.divide_signature(msg_hmac) if CryptoService.verify_hmac_sign(cur_auth.dh_key, msg, sign): util.display_user_message(msg, cur_auth.username) # send message confirmation back util.send_confirmation(sock, crypto_service, cur_auth.dh_key, n, r_addr) else: # deal with peer auth success msg if type == c.MSG_RESPONSE_OK: if n in request_cache: cache = request_cache[n] request_type = cache[c.CACHE_TYPE_IND] if request_type == c.MSG_TYPE_PUB_KEY: # verify nonce if n != cur_auth.last_nonce: continue else: cur_auth.auth_success = True request_cache.pop(n) # check the HMAC msg_hmac = dec_msg_parts[1] msg, sign = PacketOrganiser.divide_signature(msg_hmac) if CryptoService.verify_hmac_sign(cur_auth.dh_key, msg, sign): # display user message util.display_user_message(msg, cur_auth.username) # send message confirmation back util.send_confirmation(sock, crypto_service, cur_auth.dh_key, n, r_addr) elif type == c.MSG_TYPE_PUB_KEY: # finish peer authentication on Alice side b_pub_key = int(dec_msg_parts[1]) cur_auth.dh_key = crypto_service.get_dh_secret(cur_auth.pri_key, b_pub_key) cur_auth.auth_success = True cur_auth.last_nonce = n # send confirmation and first message to Bob hmac = CryptoService.generate_hmac_sign(cur_auth.dh_key, cur_auth.first_msg) first_msg_hmac = cur_auth.first_msg + hmac conf_msg_parts = [c.MSG_RESPONSE_OK, first_msg_hmac, ""] conf_msg = PacketOrganiser.prepare_packet(conf_msg_parts, n) enc_conf_msg = crypto_service.sym_encrypt(cur_auth.dh_key, conf_msg) # add to request cache # a hack here (TYPE_MSG instead of RESPONSE_OK) to make the confirmation work util.add_to_request_cache(request_cache, n, c.MSG_TYPE_MSG, cur_auth.dh_key, conf_msg, r_addr) sock.sendto(enc_conf_msg, r_addr) else: # the r_addr not in user_addr_dict, this can be a TTB # handle TTB from Alice _, msg_ps = PacketOrganiser.process_packet(recv_msg) signed_ttb, enc_inside_msg, _ = msg_ps ttb = signed_ttb[:-c.RSA_SIGN_LENGTH] sign = signed_ttb[-c.RSA_SIGN_LENGTH:] if not crypto_service.rsa_verify(ttb, sign): raise Exception(c.WARNING_TTB_INVALID) dec_ttb = crypto_service.sym_decrypt(server_auth.dh_key, ttb) _, ttb_parts = PacketOrganiser.process_packet(dec_ttb) # a_username, a_addr, k_ab a_username_ttb, a_addr, k_ab = ttb_parts a_addr = util.str_to_addr(a_addr) # decrypt inside message with k_ab dec_inside_msg = crypto_service.sym_decrypt(k_ab, enc_inside_msg) _, inside_msg_parts = PacketOrganiser.process_packet(dec_inside_msg) a_username, a_pub_key, _ = inside_msg_parts # print("username_ttb: {}, a_username: {}".format(a_username_ttb, a_username)) if a_username_ttb != a_username: raise Exception(c.WARNING_TTB_USERNAME_INVALID) new_auth = ClientClientAuthentication(a_username, crypto_service) pri_key = crypto_service.get_dh_pri_key() pub_key = crypto_service.get_dh_pub_key(pri_key) new_auth.dh_key = crypto_service.get_dh_secret(pri_key, int(a_pub_key)) nonce = util.get_good_nonce(request_cache) new_auth.last_nonce = nonce # send pub key to Alice msg_parts = [c.MSG_TYPE_PUB_KEY, pub_key, ""] plain_msg = PacketOrganiser.prepare_packet(msg_parts, nonce) enc_msg = crypto_service.sym_encrypt(k_ab, plain_msg) sock.sendto(enc_msg, a_addr) # add new auth to dict addr_auths[a_addr] = new_auth user_addr_dict[a_username] = a_addr util.add_to_request_cache(request_cache, nonce, c.MSG_TYPE_PUB_KEY, k_ab, plain_msg, a_addr) # add to request cache except socket.error: # sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # sock.bind(client_addr) continue except KeyboardInterrupt: # when seeing ctrl-c terminate the client t_listen.stop() t_listen.join() t_resend.stop() t_resend.join() print c.BYE server_auth.logout(sock) sock.close() return except: pass t_listen.stop() t_listen.join() t_resend.stop() t_resend.join() print c.BYE server_auth.logout(sock) sock.close()