Example #1
0
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
Example #2
0
    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)