def generate(self): try: path = self.pathEdit.text() password = self.passEdit.text().encode() e = EncryptionHandler(id(password)) salt = e.random(256) e.generateRSAKeys() iv = sha256(password + salt)[:16] e.generateAESObject(password, salt, iv, iterations=10000) priv = e.exportPrivateKey() self.dialog.rsa_key = priv priv = e.AESEncrypt(priv, 2400) if os.path.exists(path): os.remove(path) f = open(path, 'wb') f.write(priv + salt) f.close() msg = 'Successfully generated new keyfile!' self.dialog.addChatHistory(msg) self.logger.log(msg) except IOError as err: err_msg = "IO-Error: Couldn't save file." self.logger.log(err_msg, True) self.dialog.appendNormalMessage(err_msg) except BaseException as err: self.logger.log(str(err), True) self.dialog.appendNormalMessage(str(err)) finally: self.close()
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
class Server(): '''Server handling all information about clients''' def __init__(self): self.logger = Logger('Server', onlyErrors=False) self.logger.log('Starting CryptoChatServer(preAlpha).') # Input #self.server_name = input('Server-Name: ') #self.server_port = int(input('Server-Port: ')) #self.server_password = input('Password: '******'CryptoChatServer(preAlpha)' self.server_port = 50000 self.server_password = '******' #ek = input('Encryptionkey: ').encode() ek = 'Beispielschlüssel sind eine tolle Sache!!!'.encode() # Data for managing clients #self.encryption_Handler = EncryptionHandler(ek, self.logger, 'MainEH') Logging EncryptionHandler self.encryption_Handler = EncryptionHandler(ek) self.clients = {} self.master_token = None self.logger.log('Generating token and encryption keys.') self.token = self.encryption_Handler.random(16) while self.token == BROADCAST_TOKEN: self.token = self.encryption_Handler.random(16) self.socket = None self.handler = None self.encryption_Handler.generateAESObject(ek) self.encryption_Handler.generateRSAKeys() set_zero(ek) # Create TCP-Socket self.logger.log('Setting up TCP-Socket.') self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # For double usage of same IP after restart self.socket.settimeout(2) self.logger.log('Binding to port {}.'.format(self.server_port)) self.socket.bind(('', self.server_port)) self.socket.listen(1) self.logger.log('Starting Handler.') self.handler = ClientHandler(self) self.handler.start() self.logger.log('Handler started.') self.ping_checker = PingThread(self) self.ping_checker.start() self.logger.log('PingThread started.') self.logger.log('Server-Init done!') self.logger.log('Server-Name: ' + self.server_name) self.logger.log('Server-Port: ' + str(self.server_port)) self.logger.log('Server-Token: ' + toHex(self.token)) def getTokenList(self): '''Returns a list with all tokens''' return list(self.clients.keys()) def getNameList(self): '''Returns a list with all names''' name_list = [] for token in self.clients: name_list.append( self.clients[token].name ) return name_list def send(self, msg, token=BROADCAST_TOKEN): '''Send a message to a certain client or all clients''' msg = toBytes(msg) header = self.encryption_Handler.AESEncrypt(self.token + token + SERVER_OPERATION, 128) data = self.encryption_Handler.AESEncrypt(msg, DATA_SIZE-128) msg = header + data write = select.select([self.socket] + self.handler.clients, self.handler.clients, [])[1] try: if token == BROADCAST_TOKEN: for sock in write: sock.sendall(msg) else: sock = self.clients[token].con sock.sendall(msg) # Connectionerrors will be ignored in this method, because they can be # handled more properly by the handler. except BrokenPipeError as err: self.logger.log('Broken Pipe while sending. Ignoring the error.', True) except ConnectionResetError as err: self.logger.log('Connection has been resetwhile sending. Ignoring the error.', True) except BaseException as err: self.logger.log('Unknown Error while sending: ' + str(err), True) def sendClientList(self): '''Send the client list to all users''' self.logger.log('Sending client list.') for token in self.clients: self.sendClientInfo(self.clients[token], True) def sendClientInfo(self, info, new_client): '''Send a single information about a new or lost client''' # Specify wether this information is about a new or lost client if new_client: SEmsg = b'CI+' log_msg = 'CI+ ' else: SEmsg = b'CI-' log_msg = 'CI- ' # Adding token and public key SEmsg += info.token + info.pubkey log_msg += toHex(info.token) + ' pubkey' # Specifying wether this client is the keymaster if self.master_token == info.token: SEmsg += b'M' log_msg += ' M' else: SEmsg += b'S' log_msg += ' S' # Specifying wether this client is afk if info.afk: SEmsg += b'T' log_msg += ' T ' else: SEmsg += b'F' log_msg += ' F ' # Adding name SEmsg += info.name.encode() log_msg += info.name self.logger.log('Client info: ' + log_msg) # Sending information to all users self.send(SEmsg) def sendServerMessage(self, msg): '''Send a server message that will be displayed by the clients''' self.logger.log('Broadcast: ' + msg) SEmsg = SERVER_MESSAGE + toBytes(msg) self.send(SEmsg) def kick(self, identity): # Todo pass def renewUserKeys(self): '''Forcing all clients to renew their session key''' self.logger.log('Renewing the user encryption...') self.logger.log('Making all users forget their keys.') # Letting all users forget ther session key self.send(FORGET_KEY) self.logger.log('Sending client list.') # Sending the client list will force all clients to check who the master # is. Since all user forgot their old session key they are forced to # send new key requests from the master. self.sendClientList() def ping(self, token): '''Send a ping to a single client''' self.logger.log('Sending ping to ' + toHex(token)) self.send(PING, token) def pong(self, token): '''Renews a clients "last pong"''' self.clients[token].last_pong = time() def checkToken(self, user_msg, sock): '''Checks the provided source token for a message''' try: try: msg = self.encryption_Handler.AESDecrypt(user_msg[:128]) session_id = msg[:TOKEN_SIZE] self.logger.log('Checking token: ' + toHex(session_id)) msg = msg[TOKEN_SIZE:] dest_session_id = msg[:TOKEN_SIZE] msg = msg[TOKEN_SIZE:] operation = msg[:3] client_info = self.clients[session_id] except: # If the 'clients' list has an index error this exception will be raised. raise VerificationError('The client sent a false session-id.') if sock == client_info.con: self.logger.log('Correct token: ' + toHex(session_id)) # If an AFK report was sent change the AFK status if operation == AFK_REPORT: client_info.afk = not client_info.afk else: client_info.afk = False return True, dest_session_id, operation, session_id else: raise VerificationError('The client tried to fake their identity.') except VerificationError as err: self.logger.log('Incorrect token: ' + err.value, True) self.sendServerMessage(client_info.name + ' will be kicked! Reason: ' + err.value) self.kick(client_info.name) return False, None, None, None