def load(self): path = self.pathEdit.text() try: e = EncryptionHandler() f = open(path, 'rb') data = f.read() f.close() password = self.passEdit.text().encode() priv, salt = split(data, 2400) iv = sha256(password + salt)[:16] e.generateAESObject(password, salt, iv, iterations=10000) priv = e.AESDecrypt(priv) self.dialog.rsa_key = priv msg = 'Successfully loaded keyfile!' self.dialog.addChatHistory(msg) self.logger.log(msg) except VerificationError as err: err_msg = 'Error decrypting the file: ' + str(err) + ' (Wrong password?)' self.logger.log(err_msg, True) self.dialog.appendNormalMessage(err_msg) self.dialog.rsa_key = None except IOError as err: err_msg = "IO-Error: Couldn't open 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 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 __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 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
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)