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