def connect(name, ip, password, port, encryptionsalt, dialog): logger = dialog.logger logger.log('Connecting to {}:{}'.format(ip, port)) def signed_send(msg, ehandler, server, length=512): signature = ehandler.sign(msg) e_msg = ehandler.AESEncrypt(msg, length) server.sendall(e_msg + signature) def verified_recv(ehandler, server, length=512): length += 512 msg = server.recv(length) signature = msg[length-512:] msg = msg[:length-512] msg = ehandler.AESDecrypt(msg) if not ehandler.verify(msg, signature): raise VerificationError('Wrong signature from server!') return msg # # This will be the EncryptionHandler for the connection to # the server. It will later recieve the global AES-key. # salt = getSaltOutOfCursorPos() #e = EncryptionHandler(encryptionsalt.encode() + salt, logger, 'ServerEH') e = EncryptionHandler(encryptionsalt.encode() + salt) # # This key and initialization vector will be used for # this login and later be replaced with the global AES-key. # dialog.addChatHistory('Generating keys...') key, iv = e.generateAESObject((name + password).encode() + salt + encryptionsalt.encode() + toBytes(id(e)), iterations=15000) if dialog.rsa_key is None: e.generateRSAKeys() else: e.importPrivateKey(dialog.rsa_key) # # Creating the socket for communication over TCP. # s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: # # Connecting to the server. # dialog.addChatHistory('Connecting to {}/{}...'.format(ip, port)) s.connect((ip, int(port))) # # Recieving the public-key and a salt for hashing the password. # dialog.addChatHistory('Getting Public-Key...') data = s.recv(678) salt = data[:128] e.importPublicKey(data[128:]) dialog.addChatHistory('Sending Password...') pass_hash = pbkdf2(64, password.encode(), salt) data = e.RSAEncrypt(pass_hash + key + iv) s.sendall(data + e.exportPublicKey()) # # The servers response is a single byte. # dialog.addChatHistory('Waiting for response...') connected = s.recv(1) if connected == bytes([0]): # # The server rejected us. # dialog.addChatHistory('Wrong Password! Could not connect to server.') return False elif connected == bytes([1]): # # We entered the right password and can move on. # dialog.addChatHistory('Password is accepted!') else: # # The server gave us the wrong response. Login is aborted. # dialog.addChatHistory('Wrong response from server! Aborted login!') return False # # The names are exchanged. # signed_send(name.encode(), e, s) dialog.addChatHistory('Getting Servername...') server_name = verified_recv(e, s) dialog.appendNormalMessage('Connected to %s!' % (server_name.decode())) # # Sending our message for the server. # signed_send(b'CryptoChat [InDev]', e, s) # # We were accepted and recieved the global AES-key. We now have # a complete EncryptionHandler with public-key and AES-key, aswell # as a TCP-connection with the server. # encryptionkey = verified_recv(e, s) # # We recieve the session-id used for identifying us. # We also get the servers id. # session_ids = verified_recv(e, s) my_session_id = session_ids[:TOKEN_SIZE] server_session_id = session_ids[TOKEN_SIZE:] e.setAESObject(encryptionkey[:32], encryptionkey[32:]) # # Signals for the Qt-Gui. They can only be set once we have # a running serverListener. # dialog.listener = ServerListener(s, dialog) dialog.listener.server_encryption_Handler = e #dialog.listener.user_encryption_Handler = EncryptionHandler(my_session_id + salt, logger, 'ClientEH') dialog.listener.user_encryption_Handler = EncryptionHandler(my_session_id + salt) dialog.listener.user_encryption_Handler.__priv__ = e.__priv__ dialog.listener.name = name dialog.listener.server_name = server_name.decode() dialog.listener.session_id = my_session_id dialog.listener.server_id = server_session_id dialog.connect(dialog.listener, QtCore.SIGNAL('update'), dialog.addChatHistory) dialog.connect(dialog.listener, QtCore.SIGNAL('updateUsers'), dialog.updateUserList) dialog.connect(dialog.listener, QtCore.SIGNAL('appendServerMessage'), dialog.appendServerMessage) dialog.connect(dialog.listener, QtCore.SIGNAL('appendNormalMessage'), dialog.appendNormalMessage) success = True except ConnectionRefusedError as err: err_msg = 'Could not connect to server: Connection was refused.' dialog.appendNormalMessage(err_msg) dialog.appendNormalMessage('This could mean that the server is offline.') logger.log(err_msg, True) success = False except socket.error as err: err_msg = 'Could not connect to server: ' + str(err) dialog.appendNormalMessage(err_msg) logger.log(err_msg, True) success = False except VerificationError as err: err_msg = 'Error verifying: ' + str(err) dialog.appendNormalMessage(err_msg) logger.log(err_msg, True) success = False return success
def handleNewClient(self): '''Handles the log-in process for a new client''' def signed_send(msg, ehandler, client, length=512): signature = ehandler.sign(msg) e_msg = ehandler.AESEncrypt(msg, length) client.sendall(e_msg + signature) def verified_recv(ehandler, client, length=512): length += 512 msg = client.recv(length) signature = msg[length-512:] msg = msg[:length-512] user_msg = ehandler.AESDecrypt(msg) if not ehandler.verify(user_msg, signature): raise VerificationError('Wrong signature from client!') return user_msg try: client, addr = self.server.socket.accept() self.logger.log('New Client:' + str(addr)) # # This EncryptionHandler is only used for the # login of one user. # #e = EncryptionHandler(logger=self.logger, name='LoginEH') e = EncryptionHandler() e.importPrivateKey(self.server.encryption_Handler.exportPrivateKey()) # # Sending the public RSA key and a salt for hashing the # password. # self.logger.log('Sending pubkey and salt.') salt = e.random(128) client.sendall(salt + e.exportPublicKey()) # # The client sent us multiple information: # -The hashed password (64 Bytes) # -His AES-key we will be using for this login (32 Bytes) # -His AES-IV (16 Bytes) # -His RSA-public key (550 Bytes) # Because all this information was sent at once we can be sure # that only a user already knowing the serverpassword sent it to us. # Since the asymmetric decryption will fail, if only single bytes # have been changed we can be sure, that this information is # valid and has not been changed by a MITM-attacker. # client_info = client.recv(1062) self.logger.log('Recieved clientinfo.') client_public_key = client_info[512:] e.importPublicKey(client_public_key) client_info = e.RSADecrypt(client_info[:512]) client_password = client_info[:64] aes_key = client_info[64:96] aes_iv = client_info[96:] if client_password != pbkdf2(64, self.server.server_password.encode(), salt): # # A wrong password has been sent. The client is rejected. # self.logger.log(addr[0] + ' entered the wrong password and was rejected.') client.sendall(bytes([0])) client.close() else: # # The password was right and the login can continue. # client.sendall(bytes([1])) # # Using the clients AES-key and AES-IV from now on. # e.setAESObject(aes_key, aes_iv) # # The login proceeds with AES256 instead of PKCS1_OAEP. # Names are exchanged. The clients name needs to be # formatted properly. ',' and ';' are not allowed because # they are used in the client list. If a name already exists # a '.' is added. If simply no name was given the client gets # the name 'Anonymous'. # client_name = verified_recv(e, client) client_name = client_name.replace(b'\0',b'').replace(b'\\\\', b'').strip().decode() self.logger.log('Recieved client-name: ' + client_name) if len(client_name) == 0: client_name = 'Anonymous' ctr = 1 modified_client_name = client_name name_list = self.server.getNameList() while modified_client_name in name_list: modified_client_name = client_name + ' (' + str(ctr) + ')' ctr += 1 client_name = modified_client_name self.logger.log('Modified client-name: ' + client_name) signed_send(self.server.server_name.encode(), e, client) # # The clients version is transmitted. # client_ver = verified_recv(e, client).decode() self.logger.log(client_name + ' uses ' + client_ver) # # The servers AES-key is sent. # key = self.server.encryption_Handler.__aeskey__ iv = self.server.encryption_Handler.__aes__.IV self.logger.log('Sending key ({}) and IV ({})'.format(toHex(key), toHex(iv))) signed_send(key + iv, e, client) # # Creating and sending the clients session-id and the servers id aswell. # session_id = e.random(TOKEN_SIZE, client_name) while session_id in self.server.getTokenList() or session_id == BROADCAST_TOKEN: session_id = e.random(TOKEN_SIZE, client_name) self.logger.log('Clients session-id: {}'.format(toHex(session_id))) signed_send(session_id + self.server.token, e, client) # # The client is fully accepted and part of the normal routine. # self.clients.append(client) # Add socket to the handler # Add client to the server client = Client(client, client_name, session_id, client_public_key) self.server.clients[session_id] = client # If the client is the first on the Server he's the master if self.server.master_token is None: self.logger.log('Client is the key-master.') self.server.master_token = session_id self.logger.log('New master-token: {}'.format(toHex(session_id))) #self.server.to_checker.addClient(client) msg = '{} is connected'.format(client_name) self.logger.log(msg) self.server.send(msg) sleep(0.05) self.server.sendClientList() except VerificationError as err: client.close() self.logger.log('Error verifying client: ' + err.value)