def __init__(self, server_auth, user_addr_dict, addr_auths, request_cache, active_users):
     self.auth = server_auth
     self.user_addr_dict = user_addr_dict
     self.addr_auths = addr_auths
     self.request_cache = request_cache
     self.active_users = active_users
     self.serv = CryptoService()
 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 run_server(port):
    """
    Main function to run the server.
    :param port: the port number which the server should run on
    :return:
    """
    # load config file for DH and username/password
    global crypto_service, password_hash_dict, chatting_service, user_addr_dict, auth_dict
    g = c.DH_GENERATOR
    p = util.load_df_param_from_file(c.DH_CONFIG_PATH)
    crypto_service = CryptoService(rsa_pri_path=c.PRI_KEY_PATH, p=p, g=g)
    chatting_service = ChattingService(user_addr_dict, auth_dict,
                                       crypto_service)
    password_hash_dict = util.load_pickle_file(c.PW_HASH_PATH)
    try:
        local_ip = socket.gethostbyname(socket.gethostname())
        print('Binding to ip: {}'.format(local_ip))
        serv_addr = (local_ip, port)
        serv = SocketServer.UDPServer(serv_addr, ClientRequestHandler)
        # dump the server address to config file for client using
        util.save(c.SERVER_CONFIG_PATH, serv_addr, True)
    except socket.error:
        print c.FAIL_SRV_INIT
        return

    print c.SRV_START

    try:
        serv.serve_forever()
    except KeyboardInterrupt:
        # Stop server when seeing ctrl-c
        pass
    except:
        print c.FAIL_SRV_START
    finally:
        serv.server_close()
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()
class UserInputHandler(object):
    def __init__(self, server_auth, user_addr_dict, addr_auths, request_cache, active_users):
        self.auth = server_auth
        self.user_addr_dict = user_addr_dict
        self.addr_auths = addr_auths
        self.request_cache = request_cache
        self.active_users = active_users
        self.serv = CryptoService()

    def handle_input(self, msg):
        """

        :param input:
        :return:
        """
        # LIST, CHAT
        msg = msg[:-1]
        match_res = re.match(c.USR_CMD_RE, msg)
        if not match_res:  # Reply from server or chat client
            return None, None, c.ERR_CMD_CHAT
        if msg == c.USR_CMD_LIST:
            return self._handle_list()
        elif msg == c.USR_CMD_LOGOUT:
            return self._handle_logout()
        elif match_res.group("chat") == c.USR_CMD_CHAT:
            return self._handle_chat(match_res)
        else:
            return None, None, c.ERR_CMD_CHAT

    def _handle_list(self):
        return self._handle_single_cmd(c.MSG_TYPE_LIST)

    def _handle_logout(self):
        return self._handle_single_cmd(c.MSG_TYPE_LOGOUT)

    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_chat(self, match_res):
        """
        :param match_res:
        :return: Returns the corresponding message for the username
        """
        username = match_res.group(c.USR_CMD_RE_GROUP_USER)
        chat_msg = match_res.group(c.USR_CMD_RE_GROUP_MSG)
        if username in self.user_addr_dict:
            return self._send_msg(username, chat_msg)
        elif username in self.active_users:
            return self._authenticate_and_send_msg(username, chat_msg)
        else:
            return None, None, c.ERR_CMD_NO_USER

    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 _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 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()