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.