def change_master_key(user_input: 'UserInput', contact_list: 'ContactList', group_list: 'GroupList', settings: 'Settings', queues: 'QueueDict', master_key: 'MasterKey', onion_service: 'OnionService') -> None: """Change the master key on Transmitter/Receiver Program.""" try: if settings.traffic_masking: raise FunctionReturn( "Error: Command is disabled during traffic masking.", head_clear=True) try: device = user_input.plaintext.split()[1].lower() except IndexError: raise FunctionReturn( f"Error: No target-system ('{TX}' or '{RX}') specified.", head_clear=True) if device not in [TX, RX]: raise FunctionReturn(f"Error: Invalid target system '{device}'.", head_clear=True) if device == RX: queue_command(CH_MASTER_KEY, settings, queues) return None old_master_key = master_key.master_key[:] new_master_key = master_key.master_key = master_key.new_master_key() phase("Re-encrypting databases") queues[KEY_MANAGEMENT_QUEUE].put( (KDB_CHANGE_MASTER_KEY_HEADER, master_key)) ensure_dir(DIR_USER_DATA) if os.path.isfile( f'{DIR_USER_DATA}{settings.software_operation}_logs'): change_log_db_key(old_master_key, new_master_key, settings) contact_list.store_contacts() group_list.store_groups() settings.store_settings() onion_service.store_onion_service_private_key() phase(DONE) m_print("Master key successfully changed.", bold=True, tail_clear=True, delay=1, head=1) except (EOFError, KeyboardInterrupt): raise FunctionReturn("Password change aborted.", tail_clear=True, delay=1, head=2)
def ch_master_key(ts: 'datetime', window_list: 'WindowList', contact_list: 'ContactList', group_list: 'GroupList', key_list: 'KeyList', settings: 'Settings', master_key: 'MasterKey') -> None: """Prompt the user for a new master password and derive a new master key from that.""" if not master_key.authenticate_action(): raise SoftError("Error: Invalid password.", tail_clear=True, delay=1, head=2) # Cache old master key to allow log file re-encryption. old_master_key = master_key.master_key[:] # Create new master key but do not store new master key data into any database. new_master_key = master_key.master_key = master_key.new_master_key( replace=False) phase("Re-encrypting databases") # Update encryption keys for databases contact_list.database.database_key = new_master_key key_list.database.database_key = new_master_key group_list.database.database_key = new_master_key settings.database.database_key = new_master_key # Create temp databases for each database, do not replace original. with ignored(SoftError): change_log_db_key(old_master_key, new_master_key, settings) contact_list.store_contacts(replace=False) key_list.store_keys(replace=False) group_list.store_groups(replace=False) settings.store_settings(replace=False) # At this point all temp files exist and they have been checked to be valid by the respective # temp file writing function. It's now time to create a temp file for the new master key # database. Once the temp master key database is created, the `replace_database_data()` method # will also run the atomic `os.replace()` command for the master key database. master_key.replace_database_data() # Next we do the atomic `os.replace()` for all other files too. replace_log_db(settings) contact_list.database.replace_database() key_list.database.replace_database() group_list.database.replace_database() settings.database.replace_database() phase(DONE) m_print("Master password successfully changed.", bold=True, tail_clear=True, delay=1, head=1) cmd_win = window_list.get_command_window() cmd_win.add_new(ts, "Changed Receiver master password.")
def test_database_encryption_with_another_key(self, _: Any) -> None: # Setup window = RxWindow(type=WIN_TYPE_CONTACT, uid=nick_to_pub_key('Alice'), name='Alice', type_print='contact') contact_list = ContactList(self.old_master_key, self.settings) contact_list.contacts = [create_contact('Alice')] group_list = GroupList() # Create temp file that must be removed. temp_file_data = os.urandom(LOG_ENTRY_LENGTH) with open(self.tmp_file_name, 'wb+') as f: f.write(temp_file_data) # Add a message from contact Alice to user (Bob). for p in assembly_packet_creator(MESSAGE, 'This is a short message'): write_log_entry(p, nick_to_pub_key('Alice'), self.message_log, origin=ORIGIN_CONTACT_HEADER) # Add a message from user (Bob) to Alice. for p in assembly_packet_creator(MESSAGE, 'This is a short message'): write_log_entry(p, nick_to_pub_key('Alice'), self.message_log) # Check logfile content. message = (CLEAR_ENTIRE_SCREEN + CURSOR_LEFT_UP_CORNER + f"""\ Log file of message(s) sent to contact Alice ════════════════════════════════════════════════════════════════════════════════ {self.time} Alice: This is a short message {self.time} Me: This is a short message <End of log file> """) self.assertIsNone( change_log_db_key(self.old_master_key.master_key, self.new_master_key.master_key, self.settings)) with open(self.tmp_file_name, 'rb') as f: purp_temp_data = f.read() self.assertNotEqual(purp_temp_data, temp_file_data) # Test that decryption with new key is identical. replace_log_db(self.settings) self.assert_prints(message, access_logs, window, contact_list, group_list, self.settings, self.new_master_key) # Test that temp file is removed. self.assertFalse(os.path.isfile(self.tmp_file_name))
def ch_master_key(ts: 'datetime', window_list: 'WindowList', contact_list: 'ContactList', group_list: 'GroupList', key_list: 'KeyList', settings: 'Settings', master_key: 'MasterKey') -> None: """Prompt the user for a new master password and derive a new master key from that.""" try: old_master_key = master_key.master_key[:] master_key.master_key = master_key.new_master_key() phase("Re-encrypting databases") ensure_dir(DIR_USER_DATA) file_name = f'{DIR_USER_DATA}{settings.software_operation}_logs' if os.path.isfile(file_name): change_log_db_key(old_master_key, master_key.master_key, settings) key_list.store_keys() settings.store_settings() contact_list.store_contacts() group_list.store_groups() phase(DONE) m_print("Master password successfully changed.", bold=True, tail_clear=True, delay=1, head=1) local_win = window_list.get_local_window() local_win.add_new(ts, "Changed Receiver master password.") except (EOFError, KeyboardInterrupt): raise FunctionReturn("Password change aborted.", tail_clear=True, delay=1, head=2)
def change_master_key(user_input: 'UserInput', contact_list: 'ContactList', group_list: 'GroupList', settings: 'Settings', queues: 'QueueDict', master_key: 'MasterKey', onion_service: 'OnionService') -> None: """Change the master key on Transmitter/Receiver Program.""" if settings.traffic_masking: raise SoftError("Error: Command is disabled during traffic masking.", head_clear=True) try: device = user_input.plaintext.split()[1].lower() except IndexError: raise SoftError( f"Error: No target-system ('{TX}' or '{RX}') specified.", head_clear=True) if device not in [TX, RX]: raise SoftError(f"Error: Invalid target system '{device}'.", head_clear=True) if device == RX: queue_command(CH_MASTER_KEY, settings, queues) return None authenticated = master_key.authenticate_action() if authenticated: # Cache old master key to allow log file re-encryption. old_master_key = master_key.master_key[:] # Create new master key but do not store new master key data into any database. new_master_key = master_key.master_key = master_key.new_master_key( replace=False) phase("Re-encrypting databases") # Halt `sender_loop` for the duration of database re-encryption. queues[KEY_MANAGEMENT_QUEUE].put((KDB_M_KEY_CHANGE_HALT_HEADER, )) wait_for_key_db_halt(queues) # Load old key_list from database file as it's not used on input_loop side. key_list = KeyList(master_key, settings) # Update encryption keys for databases contact_list.database.database_key = new_master_key key_list.database.database_key = new_master_key group_list.database.database_key = new_master_key settings.database.database_key = new_master_key onion_service.database.database_key = new_master_key # Create temp databases for each database, do not replace original. with ignored(SoftError): change_log_db_key(old_master_key, new_master_key, settings) contact_list.store_contacts(replace=False) key_list.store_keys(replace=False) group_list.store_groups(replace=False) settings.store_settings(replace=False) onion_service.store_onion_service_private_key(replace=False) # At this point all temp files exist and they have been checked to be valid by the respective # temp file writing function. It's now time to create a temp file for the new master key # database. Once the temp master key database is created, the `replace_database_data()` method # will also run the atomic `os.replace()` command for the master key database. master_key.replace_database_data() # Next we do the atomic `os.replace()` for all other files too. replace_log_db(settings) contact_list.database.replace_database() key_list.database.replace_database() group_list.database.replace_database() settings.database.replace_database() onion_service.database.replace_database() # Now all databases have been updated. It's time to let # the key database know what the new master key is. queues[KEY_MANAGEMENT_QUEUE].put(new_master_key) wait_for_key_db_ack(new_master_key, queues) phase(DONE) m_print("Master key successfully changed.", bold=True, tail_clear=True, delay=1, head=1)