0

I am implementing an authentication process between a server and a client in order to exchange some information. I have created a function called serverToClientAuth on both server and client that carries out a basic back and forth communication to establish authenticated integrity checks of each other before exchanging some data.

When I run the server, followed by the client, the exchange occurs perfectly with encryption and hashing working as required but on the very last message from the server to the Client, the client seems to hang and is unable to exit the function and go back to normal operation Although the first 3 communications work fine, the last one does not for some reason.

I thought at first that it had something to do with the encryption/Decryption functions but as the first 3 operations work, I don't see why the last doesn't? It seems that the recieve_msg function is grabbing the last message instead of the authentication function, this is the final '12' output on the client window as seen below.

I have included both the server and client as well as the output of each below. If anyone has any idea as to why this hang is occurring, it would be greatly appreciated.

#Server.py
from socket import AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR, socket
from threading import Thread
from signal import signal, SIGINT
import sys
import os
import random
import rsa

WELCOME_MSG = 'Server is active and awaiting connections'

def nonceGenerator():
    num = ""
    for i in range(5):
        rand = random.randint(0,1)
        num += str(rand)
    return num

def generateAKeys():
    (publicKey, privateKey) = rsa.newkeys(1024)
    with open('keys/APubKey.pem', 'wb') as p:
        p.write(publicKey.save_pkcs1('PEM'))
    with open('keys/APrivKey.pem', 'wb') as p:
        p.write(privateKey.save_pkcs1('PEM'))

def generateBKeys():
    (publicKey, privateKey) = rsa.newkeys(1024)
    with open('keys/BPubKey.pem', 'wb') as p:
        p.write(publicKey.save_pkcs1('PEM'))
    with open('keys/BPrivKey.pem', 'wb') as p:
        p.write(privateKey.save_pkcs1('PEM'))

def generateCKeys():
    (publicKey, privateKey) = rsa.newkeys(1024)
    with open('keys/CPubKey.pem', 'wb') as p:
        p.write(publicKey.save_pkcs1('PEM'))
    with open('keys/CPrivKey.pem', 'wb') as p:
        p.write(privateKey.save_pkcs1('PEM'))

def generateSKeys():
    (publicKey, privateKey) = rsa.newkeys(1024)
    with open('keys/SPubKey.pem', 'wb') as p:
        p.write(publicKey.save_pkcs1('PEM'))
    with open('keys/SPrivKey.pem', 'wb') as p:
        p.write(privateKey.save_pkcs1('PEM'))       


def loadKeys():
    with open('keys/SPubKey.pem', 'rb') as p:
        SPubKey = rsa.PublicKey.load_pkcs1(p.read())
    with open('keys/SPrivKey.pem', 'rb') as p:
        SPrivKey = rsa.PrivateKey.load_pkcs1(p.read())
    with open('keys/APubKey.pem', 'rb') as p:
        APubKey = rsa.PublicKey.load_pkcs1(p.read())
    with open('keys/APrivKey.pem', 'rb') as p:
        APrivKey = rsa.PrivateKey.load_pkcs1(p.read())
    with open('keys/BPubKey.pem', 'rb') as p:
        BPubKey = rsa.PublicKey.load_pkcs1(p.read())
    with open('keys/BPrivKey.pem', 'rb') as p:
        BPrivKey = rsa.PrivateKey.load_pkcs1(p.read())
    with open('keys/CPubKey.pem', 'rb') as p:
        CPubKey = rsa.PublicKey.load_pkcs1(p.read())
    with open('keys/CPrivKey.pem', 'rb') as p:
        CPrivKey = rsa.PrivateKey.load_pkcs1(p.read())
    return SPubKey, SPrivKey, APubKey, APrivKey, BPubKey, BPrivKey, CPubKey, CPrivKey

def encrypt(message, key):
    return rsa.encrypt(message.encode('utf8'), key)

def decrypt(ciphertext, key):
    try:
        return rsa.decrypt(ciphertext, key).decode('utf8')
    except:
        return False

def sign(message, key):
    return rsa.sign(message.encode('utf8'), key, 'SHA-1')

def verify(message, signature, key):
    try:
        return rsa.verify(message.encode('utf8'), signature, key,) == 'SHA-1'
    except:
        return False

def incoming_connections():

    while True:

        client, addr = SERVER.accept()
        print(f'A client has connected {addr}')
        # client.send(WELCOME_MSG.encode())
        Thread(target=single_client, args=(client,)).start()
        print('Client connected')

def single_client(client):

    client_name = client.recv(BUFFERSIZE).decode()
    #client_name = 'Anonymous'
    welcome_msg = f'Welcome {client_name}.\nType exit() or press CTRL+D or CTRL+C to exit.\n'
    client.send(welcome_msg.encode())
    chat_msg = f'{client_name} has joined the room'
    broadcast_msg(chat_msg.encode())
    clients[client] = client_name

    serverToClientAuth(client) #Begin Authentication process upon reciving client

    while True:
        msg = client.recv(BUFFERSIZE)

        if msg == 'online()'.encode('utf8'):            
            real_clients_num, real_clients_name = get_clients()
            client.send(f'Online users {real_clients_num} : {real_clients_name}'.encode('utf8'))
        elif msg == EXIT_CMD.encode('utf8'):
            print(f'{clients[client]} has disconnected ')
            client.send('You are leaving the room...'.encode())
            client.close()
            client_leaving = clients[client]
            del clients[client]
            broadcast_msg(f'{client_leaving} has left the room!'.encode())
            break
        elif '@'.encode('utf8') in msg:
            unicast_msg(msg, client)
        else:
            broadcast_msg(msg, clients[client] + ': ')

def get_clients():
    
    real_clients_num = 0
    real_clients_name = []

    for k,v in clients.items():
        if v != 'Anonymous':
            real_clients_num += 1
            real_clients_name.append(v)

    return real_clients_num, real_clients_name

def broadcast_msg(msg, name=""):

    for client in clients:
        client.send(name.encode() + msg)

def unicast_msg(msg, client):
    # Get only name by removing @
    msg = msg.decode('utf8')
    refered_client, client_msg = msg.split(' ',1)
    client_to_connect = refered_client.strip('@')
    for k,v in clients.items():
        if v == client_to_connect:
            k.send(f'{clients[client]} -> {client_to_connect}: {client_msg}'.encode('utf8'))

def receive_msg():
    while True:
        try:
            msg = client_socket.recv(BUFFERSIZE).decode("utf8")
            print(msg)
        except OSError as error:
            return error
        
def serverToClientAuth(client):
    Ka=bytes('1', 'utf8')
    Kb=bytes('2', 'utf8')
    Kc=3
    user = clients[client] #A, B or C
    message1 = client.recv(1024) #Nonce Na, Nb or Nc
    NEnc, sig1 = message1[:128], message1[128:]
    N = decrypt(NEnc, SPrivKey)
    if verify(N, sig1, APubKey):
        Ns = nonceGenerator() #Servers Nonce
        NNs = "{}|{}".format(N, Ns)
        NNsEnc = encrypt(NNs, APubKey)
        sig2 = sign(NNs, SPrivKey)
        message2 = NNsEnc + sig2
        client.send(message2)

        message3 = client.recv(1024)
        NsXX = decrypt(message3, SPrivKey)
        print(NsXX)
        NsXX = NsXX.split("|")
        if NsXX[0] == Ns: #Does the Nonce that client returned match what was sent?
            if NsXX[1] == 'B' and NsXX[2] == 'C':
                message4 = Ka + Kb
                client.send(message4)
                print("Client " + user + " has been authenticated to the server\n")
            elif NsXX[1] == 'A' and NsXX[2] == 'C':
                message4 = "{}|{}".format(Ka, Kc)
                client.send(message4.encode('utf8'))
                print("Client " + user + " has been authenticated to the server\n")
            elif NsXX[1] == 'A' and NsXX[2] == 'B':
                message4 = "{}|{}".format(Ka, Kb)
                client.send(message4.encode('utf8'))
                print("Client " + user + " has been authenticated to the server\n")

            else:
                print("Unrecognised identifier")

        else:
            print("Replied nonce from Client does not match")

    else:
        print('The message signature could not be verified')


if __name__ == "__main__":

    clients = {}
    generateAKeys()
    generateBKeys()
    generateCKeys()
    generateSKeys()
    SPubKey, SPrivKey, APubKey, APrivKey, BPubKey, BPrivKey, CPubKey, CPrivKey = loadKeys()

    HOST = '127.0.0.1'
    PORT = 5000
    BUFFERSIZE = 1024
    ADDR = (HOST, PORT)
    EXIT_CMD = "exit()"
    SERVER = socket(AF_INET, SOCK_STREAM)
    SERVER.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    SERVER.bind(ADDR)
    SERVER.listen(2)

    print("Waiting for connection...")
    ACCEPT_THREAD = Thread(target=incoming_connections)
    ACCEPT_THREAD.start()
    ACCEPT_THREAD.join()
    SERVER.close()

The server file ^^^

#Client A
from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread
from signal import signal, SIGINT
import sys
import os 
import random
import rsa

def nonceGenerator():
    num = ""
    for i in range(5):
        rand = random.randint(0,1)
        num += str(rand)
    return num

def loadKeys():
    with open('keys/APubKey.pem', 'rb') as p:
        APubKey = rsa.PublicKey.load_pkcs1(p.read())
    with open('keys/APrivKey.pem', 'rb') as p:
        APrivKey = rsa.PrivateKey.load_pkcs1(p.read())
    with open('keys/SPubKey.pem', 'rb') as p:
        SPubKey = rsa.PublicKey.load_pkcs1(p.read())
    return APubKey, APrivKey, SPubKey

def encrypt(message, key):
    return rsa.encrypt(message.encode('utf8'), key)

def decrypt(ciphertext, key):
    try:
        return rsa.decrypt(ciphertext, key).decode('utf8')
    except:
        return False

def sign(message, key):
    return rsa.sign(message.encode('utf8'), key, 'SHA-1')

def verify(message, signature, key):
    try:
        return rsa.verify(message.encode('utf8'), signature, key,) == 'SHA-1'
    except:
        return False

def receive_msg():
    while True:
        try:
            msg = client_socket.recv(BUFFERSIZE).decode("utf8")
            print(msg)
        except OSError as error:
            return error

def keyBoard_Input():
    while True:
        try:
            msg = input()
            if msg != 'exit()':
                client_socket.send(msg.encode('utf8'))
            else:
                clean_exit()
        except EOFError:
            clean_exit()

def send_msg(data):
    try:
        msg = data
        if msg != 'exit()':
            client_socket.send(msg.encode('utf8'))
        else:
            clean_exit()
    except EOFError:
        clean_exit()

def clean_exit():
    client_socket.send('exit()'.encode('utf8'))
    client_socket.close()
    sys.exit(0)

def handler(signal_received, frame):
    clean_exit()

def serverToClientAuth(client):
    Na = nonceGenerator()
    NaEnc = encrypt(Na, SPubKey)
    sig1 = sign(Na, APrivKey)
    message1 = NaEnc + sig1
    client_socket.send(message1) 

    message2 = client_socket.recv(1024)
    NaNsEnc, sig2 = message2[:128], message2[128:]     
    NaNs = decrypt(NaNsEnc, APrivKey)
    if verify(NaNs, sig2, SPubKey):
        NaNs = NaNs.split("|")
        if NaNs[0] == Na:
            Ns = NaNs[1]
            print(Ns)
            
            NsBC = "{}|{}|{}".format(Ns, 'B', 'C')
            NsBCEnc = encrypt(NsBC, SPubKey) #No signature needed anymore
            message3 = NsBCEnc
            client_socket.send(message3)
            print('Client gets stuick here')
            message4 = client_socket.recv(1024)
            print(message4)
            message4 = message4.split("|")    
            APubKey, CPubKey = message3[0], message3[1]
            print("Client B is Authenticated to the Server")
            print("Ka = " + APubKey)
            print("Kc = " + CPubKey)


        else:
            print("Replied nonce from server does not match")
    else:
        print('The message signature could not be verified')


if __name__ == '__main__':
    
    signal(SIGINT, handler)
    
    HOST = '127.0.0.1'
    PORT = 5000

    BUFFERSIZE = 1024
    ADDR = (HOST, PORT)

    client_socket = socket(AF_INET, SOCK_STREAM)
    client_socket.connect(ADDR)
    receive_thread = Thread(target=receive_msg)
    receive_thread.start()
    
    APubKey, APrivKey, SPubKey = loadKeys()
    send_msg('A') #Assign Client ID at the moment the connection is initiated
    serverToClientAuth(client_socket)
    keyBoard_Input()

Client File ^^^^

Waiting for connection...
A client has connected ('127.0.0.1', 51407)
Client connected
01001|B|C
Client A has been authenticated to the server

Server response ^^^

Welcome A.
Type exit() or press CTRL+D or CTRL+C to exit.

01001
Client gets stuick here
12

Client Response ^^^

The '12' displayed here should be a '1|2' as that is the message sent by the server. However, it seems that the receive_msg function is actually taking the message instead of the function, causing the function to hang and wait for a message.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
S_G
  • 17
  • 7
  • It seems like your client is not getting 1024 bytes. Maybe try using a smaller chunk size to find the length of the message that you're actually receiving. For example, you could use a for loop and use `recv(128)` 8 times. Maybe the server is sending data but not enough to fill the client recv buffer – aaossa Apr 03 '22 at 01:23
  • @aaossa I tried setting it down to 128 and still no change. As the data being transferred between each step are comparable in size, it doesn't make sense that the first 3 recv(1024) work and the last one does not for such a small amount of data. – S_G Apr 03 '22 at 10:15
  • I know it's comparable. I recommended that to help you debug your code. Does the error happen still? I guess if that's the case, you printed each 128 bytes chunk and confirmed that the last one is smaller right? – aaossa Apr 03 '22 at 14:08
  • Sockets don't work that way. [This answer](https://stackoverflow.com/a/43420503/238704) explains why. – President James K. Polk Apr 03 '22 at 22:26
  • @PresidentJamesK.Polk Thank you for that. As i am implementing an agreement protocol, I will need to be able to send and recieve messages in sequential order, do you have any recommendations as to how to achieve that? – S_G Apr 04 '22 at 11:41

0 Answers0