def update_user(username, data): """ Method to update a user from the database :param username: Username that targets the user to be updated :param data: Data to be saved """ hashed_username = base64.b64encode( Cryptography.hash(username).digest()).decode() with open(getcwd() + Database.__DB_FILENAME) as file_input,\ open(getcwd() + Database.__DB_FILENAME + '.temp', 'w') as file_output: for entry in file_input: new_entry = entry if entry.split(':')[0] == hashed_username: iv, ciphered_data = Cryptography.cipher( Cryptography.get_passphrase(), data) new_entry = hashed_username + ':' + ciphered_data.hex( ) + '.' + iv.hex() + '\n' file_output.write(new_entry) if os.path.exists(getcwd() + Database.__DB_FILENAME): os.remove(getcwd() + Database.__DB_FILENAME) os.rename(getcwd() + Database.__DB_FILENAME + '.temp', getcwd() + Database.__DB_FILENAME)
def update_client(username, hostname, port, cert): """ Method to update a client from the database :param username: Username that targets the client to be updated :param hostname: Hostname to be updated :param port: Port to be updated :param cert: Certificate to be updated """ hashed_username = base64.b64encode( Cryptography.hash(username).digest()).decode() data = f'{hostname}:{port}:{cert}' with open(getcwd() + ClientsStore.__DB_FILENAME) as file_input, \ open(getcwd() + ClientsStore.__DB_FILENAME + '.temp', 'w') as file_output: for entry in file_input: new_entry = entry if entry.split(':')[0] == hashed_username: iv, ciphered_data = Cryptography.cipher( Cryptography.get_passphrase(), data) new_entry = hashed_username + ':' + ciphered_data.hex( ) + '.' + iv.hex() + '\n' file_output.write(new_entry) if path.exists(getcwd() + ClientsStore.__DB_FILENAME): remove(getcwd() + ClientsStore.__DB_FILENAME) rename(getcwd() + ClientsStore.__DB_FILENAME + '.temp', getcwd() + ClientsStore.__DB_FILENAME)
def get_client(username): """ Method to fetch a User's Client :param username: User whose Client will be fetch :return: Client's data """ data = None hashed_username = base64.b64encode( Cryptography.hash(username).digest()).decode() with open(getcwd() + ClientsStore.__DB_FILENAME, 'r') as f: for entry in f: parts = entry.split(':') if parts[0] == hashed_username: material = parts[1].split('.') ciphertext = bytearray.fromhex(material[0]) iv = bytearray.fromhex(material[1]) data = Cryptography.decipher(Cryptography.get_passphrase(), ciphertext, iv).decode() client_info = data.split(':') return { 'hostname': client_info[0], 'port': client_info[1], 'cert': client_info[2] }
def register_user(client_socket, parts): """ Method to register users and their clients :param client_socket: Socket where a connection with a client is happening :param parts: Parts of the message that was sent by the client """ username = parts[1] one_time_id = parts[2] signature = base64.b64decode(parts[3]) hostname = parts[4] port = parts[5] client_cert = parts[6] valid_signature = Cryptography.verify_signature( base64.b64decode(client_cert).decode(), one_time_id, signature) if valid_signature and User.login(username, one_time_id): ClientsStore.save_client(username, hostname, port, client_cert) ClientsStore.set_online(username, hostname, port) Administration.__socket_map[client_socket] = username client_socket.send('OK'.encode()) else: client_socket.send('NOTOK'.encode())
def register_client(client, server): """ Method to login a user in the server :param client: Client instance, whose properties can be fetched :param server: Server connection """ username = input('Please insert the username:\n> ') one_time_id = input('Please insert the One Time ID:\n> ') signed_one_time_id = base64.b64encode( Cryptography.sign(Connection.get_key(), one_time_id)).decode() server.send( f'LOGIN:{username}:{one_time_id}:{signed_one_time_id}:{client.get_hostname()}' f':{str(client.get_port())}:{Connection.get_cert()}'.encode()) response = server.recv(100000).decode() if response == 'OK': pin = input('Please insert your PIN:\n> ') client.save_pin(username, pin) client.set_username(username) print('Login successful and PIN set') return True else: print('Login was not successful, failed on the server') return False
def list_users(client, server): """ Method that accepts list requests :param client: Client instance, whose properties can be fetched :param server: Server connection """ signed_username = base64.b64encode( Cryptography.sign(Connection.get_key(), client.get_username())).decode() server.send(f'LIST:{client.get_username()}:{signed_username}'.encode()) response = server.recv(100000000) if response[0] != 0x80: if response.decode() == 'UNAUTHORIZED': print("You don't have permissions to perform this operation!") return None user_list = pickle.loads(response) if len(user_list) > 0: print('Online users are:') for user in user_list: print('> ' + user['username']) client.add_user(user['username'], user['hostname'], user['port'], user['cert']) else: print('No users online!')
def perform_root(client, server, number, n): """ Method to ask the server to calculate a root of a number :param client: Client instance, whose properties can be fetched :param server: Server connection that will be used to ask for the operation :param number: Server number on which the root will be calculated :param n: N of the root that needs to be computed :return: Result of the number's N root """ signed_username = base64.b64encode( Cryptography.sign(Connection.get_key(), client.get_username())).decode() server.send( f'OP:{client.get_username()}:{signed_username}:{n}:{number}'. encode()) response = server.recv(100000).decode() if response == 'UNAUTHORIZED': print("You don't have permissions to perform this operation!") return None if response == 'INVALID': print("The values you inserted are not valid!") return None return float(response)
def create(): """ Static method to create a new user. This method asks the system administrator for the relevant information :return: User's username, User's one time id """ full_name = input('Please insert the User\'s full name: ') email = input('Please insert the User\'s email: ') username = '' username_exists = True # Username needs to have 12 characters at most while len(username) > 12 or username_exists: username = input( 'Please insert the User\'s username(max of 12 characters): ') username_exists = Database.username_exists(username) if username_exists: print('Username already exists! Please choose another.') clearance_level = 0 # clearance level must be between 1 and 3 while 1 > clearance_level or clearance_level > 3: clearance_level = int( input('Please insert the User\'s clearance level[1-3]: ')) one_time_id = generate_one_time_id() # Creates a new user with all the information user = User(full_name, email, username, clearance_level, Cryptography.hash_data(one_time_id)) # Saves user to the database user_data = json.dumps(user.to_dictionary()) Database.save_user(user.username, user_data) return username, one_time_id
def save_user(username, data): """ Method to save a user into the database :param username: Username used as the index :param data: Data to be saved """ hashed_username = base64.b64encode( Cryptography.hash(username).digest()).decode() file = open(getcwd() + Database.__DB_FILENAME, 'a') iv, ciphered_data = Cryptography.cipher(Cryptography.get_passphrase(), data) file.write(hashed_username + ':' + ciphered_data.hex() + '.' + iv.hex() + '\n') file.flush() file.close()
def save_client(username, hostname, port, cert): """ Method to save a new client that is currently online :param username: Username of the user that is using the client :param hostname: Client's hostname :param port: Client's port :param cert: Client's certificate """ data = f'{hostname}:{port}:{cert}' hashed_username = base64.b64encode( Cryptography.hash(username).digest()).decode() file = open(getcwd() + ClientsStore.__DB_FILENAME, 'a') iv, ciphered_data = Cryptography.cipher(Cryptography.get_passphrase(), data) file.write(hashed_username + ':' + ciphered_data.hex() + '.' + iv.hex() + '\n') file.flush() file.close()
def verify_pin(username, pin): """ Method to verify the PIN inserted :return: True if it is the same as the one saved, false otherwise """ with open(Client._LOCAL_DATA_FILE, 'r') as f: pin_hash = f.readline().strip() f.close() return Cryptography.verify_hash(f'{username}:{pin}', pin_hash)
def load_user(username): """ Method to load a user information given a username :param username: User's username to be be loaded :return: User's information """ hashed_username = base64.b64encode( Cryptography.hash(username).digest()).decode() with open(getcwd() + Database.__DB_FILENAME, 'r') as f: for entry in f: parts = entry.split(':') if parts[0] == hashed_username: material = parts[1].split('.') ciphertext = bytearray.fromhex(material[0]) iv = bytearray.fromhex(material[1]) return Cryptography.decipher(Cryptography.get_passphrase(), ciphertext, iv) f.close() return None
def save_message(message): """ Method to persist a message :param message: Message to be saved """ file = open(Client._LOCAL_DATA_FILE, 'a') iv, ciphered_data = Cryptography.cipher(f'{Client.get_pin(): <32}', message) file.write(ciphered_data.hex() + '.' + iv.hex() + '\n') file.flush() file.close()
def save_pin(username, pin): """ Method to persist the user's PIN :param username: Username to be saved into the filesystem :param pin: PIN to be saved into the filesystem """ hashed_pin = Cryptography.hash_data(f'{username}:{pin}') with open(Client._LOCAL_DATA_FILE, 'w') as f: f.write(hashed_pin.hex() + '\n') f.close() Client.set_pin(pin)
def run_administration(server): """ Method that runs the administration panel :param server: Server instance, whose properties can be fetched """ if Cryptography.get_passphrase() == "": while len(Cryptography.get_passphrase()) != 32: passphrase = input( "Please insert the server's passphrase (32 characters): ") Cryptography.set_passphrase(passphrase) """ Method to start the menu used by the administrator """ banner = """What do you want to do? 1 - Create user 2 - Quit > """ print('Welcome to the administration panel!') while True: option = int(input(banner)) if option == 1: username, one_time_id = User.create() print( f'\nUser created with success!\n\tUsername: {username}\n\tOne Time ID: {one_time_id}\n\n' ) elif option == 2: print('Quitting server...') logging.info('Quitting server...') Administration.RUNNING = False socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect( (server.get_hostname(), server.get_port())) _thread.interrupt_main() quit(0) else: print('Option not valid! Try again.')
def list_users(client_socket, parts): username = parts[1] signature = base64.b64decode(parts[2]) user = User.load_user(username) client = ClientsStore.get_client(username) valid_signature = Cryptography.verify_signature( base64.b64decode(client['cert']).decode(), username, signature) if valid_signature: user_list = ClientsStore.list_users(username, user.get_clearance_level()) encoded_list = pickle.dumps(user_list) client_socket.send(encoded_list) else: client_socket.send('UNAUTHORIZED'.encode())
def client_exists(username): """ Method to check if a given user is online (has a registered client online) :param username: Username whose connection will be check :return: True if the user has a client stored, False if it doesn't """ hashed_username = base64.b64encode( Cryptography.hash(username).digest()).decode() if path.exists(getcwd() + ClientsStore.__DB_FILENAME): with open(getcwd() + ClientsStore.__DB_FILENAME, 'r') as f: for entry in f: parts = entry.split(':') if parts[0] == hashed_username: return True return False else: return False
def username_exists(username): """ Method to check if a user exists in the database :param username: Username whose existence will be check :return: True if the user with that username exists, False otherwise """ hashed_username = base64.b64encode( Cryptography.hash(username).digest()).decode() if os.path.exists(getcwd() + Database.__DB_FILENAME): with open(getcwd() + Database.__DB_FILENAME, 'r') as f: for entry in f: parts = entry.split(':') if parts[0] == hashed_username: return True return False else: return False
def perform_operations(client_socket, parts): """ Method to execute the privileged operations :param client_socket: Socket where a connection with a client is happening :param parts: Parts of the message that was sent by the client """ username = parts[1] signature = base64.b64decode(parts[2]) n = int(parts[3]) number = int(parts[4]) user = User.load_user(username) client = ClientsStore.get_client(username) valid_signature = Cryptography.verify_signature( base64.b64decode(client['cert']).decode(), username, signature) if n <= 0.0: client_socket.send('INVALID'.encode()) return if valid_signature: clearance = user.get_clearance_level() if clearance == 1 and n > 2: client_socket.send('UNAUTHORIZED'.encode()) return if clearance == 2 and n > 3: client_socket.send('UNAUTHORIZED'.encode()) return if n == 2: result = Functions.square_root(number) elif n == 3: result = Functions.cubic_root(number) else: result = Functions.n_root(number, n) client_socket.send(str(result).encode()) else: client_socket.send('UNAUTHORIZED'.encode())
def login(username, one_time_id): """ Method to perform a login of a user :param username: User that we want to perform the challenge on :param one_time_id: One Time ID passed to perform the login :return: True if the username and one time id combination match, false otherwise """ user = User.load_user(username) # Checks if a user was found (and hence, is not None), the one time id passed matches with the one stored and # did not yet performed any other login with this one time id if user is not None and Cryptography.verify_hash( one_time_id, user.one_time_id_hash) and not user.login_done: user.login_done = True # Save change to the database user_data = json.dumps(user.to_dictionary()) Database.update_user(user.username, user_data) return True if user is not None and user.login_done: logging.debug(f'Login retry on {username}') return False