def serialize_c(self) -> bytes: """Return contact data as constant length byte string.""" return (str_to_bytes(self.rx_account) + str_to_bytes(self.tx_account) + str_to_bytes(self.nick) + self.tx_fingerprint + self.rx_fingerprint + bool_to_bytes(self.log_messages) + bool_to_bytes(self.file_reception) + bool_to_bytes(self.notifications))
def store_settings(self) -> None: """Store persistent settings to file.""" setting_data = int_to_bytes(self.serial_baudrate) setting_data += int_to_bytes(self.serial_error_correction) setting_data += bool_to_bytes(self.serial_usb_adapter) setting_data += bool_to_bytes(self.disable_gui_dialog) ensure_dir(DIR_USER_DATA) with open(self.file_name, 'wb+') as f: f.write(setting_data)
def dump_c(self) -> bytes: """Return contact data as constant length byte string.""" return str_to_bytes(self.rx_account) \ + str_to_bytes(self.tx_account) \ + str_to_bytes(self.nick) \ + self.tx_fingerprint \ + self.rx_fingerprint \ + bool_to_bytes(self.log_messages) \ + bool_to_bytes(self.file_reception) \ + bool_to_bytes(self.notifications)
def serialize_g(self) -> bytes: """Return group data as constant length byte string.""" name = str_to_bytes(self.name) log_messages = bool_to_bytes(self.log_messages) notifications = bool_to_bytes(self.notifications) members = self.get_list_of_member_accounts() num_of_dummies = self.settings.max_number_of_group_members - len( self.members) members += num_of_dummies * [DUMMY_MEMBER] member_bytes = b''.join([str_to_bytes(m) for m in members]) return name + log_messages + notifications + member_bytes
def generate_dummy_contact() -> bytes: """Generate byte string for dummy contact.""" rx_account = str_to_bytes('dummy_contact') tx_account = str_to_bytes('dummy_user') nick = str_to_bytes('dummy_nick') tx_fingerprint = bytes(32) rx_fingerprint = bytes(32) logging_bytes = bool_to_bytes(False) file_r_bytes = bool_to_bytes(False) notify_bytes = bool_to_bytes(False) return rx_account + tx_account + nick \ + tx_fingerprint + rx_fingerprint \ + logging_bytes + file_r_bytes + notify_bytes
def serialize_c(self) -> bytes: """Return contact data as a constant length byte string. This function serializes the contact's data into a byte string that has the exact length of 3*32 + 4*1 + 1024 = 1124 bytes. The length is guaranteed regardless of the content or length of the attributes' values, including the contact's nickname. The purpose of the constant length serialization is to hide any metadata about the contact the ciphertext length of the contact database would reveal. """ return (self.onion_pub_key + self.tx_fingerprint + self.rx_fingerprint + self.kex_status + bool_to_bytes(self.log_messages) + bool_to_bytes(self.file_reception) + bool_to_bytes(self.notifications) + str_to_bytes(self.nick))
def test_empty_whisper_msg_from_user(self, _): # Setup assembly_ct_list = assembly_packet_creator(MESSAGE, '', origin_header=ORIGIN_USER_HEADER, encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'), whisper_header=bool_to_bytes(True)) # Test for p in assembly_ct_list[:-1]: self.assertIsNone(process_message(self.ts, p, *self.args)) for p in assembly_ct_list[-1:]: self.assert_fr("Whisper message complete.", process_message, self.ts, p, *self.args) self.assertEqual(os.path.getsize(self.file_name), len(assembly_ct_list)*LOG_ENTRY_LENGTH)
def test_successful_export_command(self, *_): # Setup self.window.type = WIN_TYPE_CONTACT self.window.uid = nick_to_pub_key('Alice') whisper_header = bool_to_bytes(False) packet = split_to_assembly_packets( whisper_header + PRIVATE_MESSAGE_HEADER + b'test', MESSAGE)[0] write_log_entry(packet, nick_to_pub_key('Alice'), self.settings, self.master_key) # Test for command in ['export', 'export 1']: self.assert_fr(f"Exported log file of contact 'Alice'.", log_command, UserInput(command), self.window, ContactList(nicks=['Alice']), self.group_list, self.settings, self.queues, self.master_key)
def store_settings(self) -> None: """Store settings to encrypted database.""" attribute_list = [self.__getattribute__(k) for k in self.key_list] # Convert attributes into constant length byte string pt_bytes = b'' for a in attribute_list: if isinstance(a, bool): pt_bytes += bool_to_bytes(a) elif isinstance(a, int): pt_bytes += int_to_bytes(a) elif isinstance(a, float): pt_bytes += double_to_bytes(a) elif isinstance(a, str): pt_bytes += str_to_bytes(a) else: raise CriticalError("Invalid attribute type in settings.") ct_bytes = encrypt_and_sign(pt_bytes, self.master_key.master_key) ensure_dir(f'{DIR_USER_DATA}/') with open(self.file_name, 'wb+') as f: f.write(ct_bytes)
def change_setting(user_input: 'UserInput', window: 'TxWindow', contact_list: 'ContactList', group_list: 'GroupList', settings: 'Settings', queues: 'QueueDict', master_key: 'MasterKey', gateway: 'Gateway') -> None: """Change setting on Transmitter and Receiver Program.""" # Validate the KV-pair try: setting = user_input.plaintext.split()[1] except IndexError: raise FunctionReturn("Error: No setting specified.", head_clear=True) if setting not in (settings.key_list + gateway.settings.key_list): raise FunctionReturn(f"Error: Invalid setting '{setting}'.", head_clear=True) try: value = user_input.plaintext.split()[2] except IndexError: raise FunctionReturn("Error: No value for setting specified.", head_clear=True) # Check if the setting can be changed relay_settings = dict( serial_error_correction=UNENCRYPTED_EC_RATIO, serial_baudrate=UNENCRYPTED_BAUDRATE, allow_contact_requests=UNENCRYPTED_MANAGE_CONTACT_REQ) if settings.traffic_masking and (setting in relay_settings or setting == 'max_number_of_contacts'): raise FunctionReturn( "Error: Can't change this setting during traffic masking.", head_clear=True) if setting in ['use_serial_usb_adapter', 'built_in_serial_interface']: raise FunctionReturn( "Error: Serial interface setting can only be changed manually.", head_clear=True) if setting == 'ask_password_for_log_access': try: authenticated = master_key.load_master_key( ) == master_key.master_key except (EOFError, KeyboardInterrupt): raise FunctionReturn(f"Setting change aborted.", tail_clear=True, head=2, delay=1) if not authenticated: raise FunctionReturn("Error: No permission to change setting.", head_clear=True) # Change the setting if setting in gateway.settings.key_list: gateway.settings.change_setting(setting, value) else: settings.change_setting(setting, value, contact_list, group_list) receiver_command = CH_SETTING + setting.encode() + US_BYTE + value.encode() queue_command(receiver_command, settings, queues) if setting in relay_settings: if setting == 'allow_contact_requests': value = bool_to_bytes(settings.allow_contact_requests).decode() relay_command = UNENCRYPTED_DATAGRAM_HEADER + relay_settings[ setting] + value.encode() queue_to_nc(relay_command, queues[RELAY_PACKET_QUEUE]) # Propagate the effects of the setting if setting == 'max_number_of_contacts': contact_list.store_contacts() queues[KEY_MANAGEMENT_QUEUE].put((KDB_UPDATE_SIZE_HEADER, settings)) if setting in ['max_number_of_group_members', 'max_number_of_groups']: group_list.store_groups() if setting == 'traffic_masking': queues[SENDER_MODE_QUEUE].put(settings) queues[TRAFFIC_MASKING_QUEUE].put(settings.traffic_masking) window.deselect() if setting == 'log_file_masking': queues[LOGFILE_MASKING_QUEUE].put(settings.log_file_masking)
def queue_delayer(): """Place datagrams into queue after delay.""" o_sleep(test_delay) local_harac = INITIAL_HARAC tx_harac = INITIAL_HARAC local_hek = SYMMETRIC_KEY_LENGTH * b'a' file_key = SYMMETRIC_KEY_LENGTH * b'b' local_key = SYMMETRIC_KEY_LENGTH * b'a' tx_mk = SYMMETRIC_KEY_LENGTH * b'a' tx_hk = SYMMETRIC_KEY_LENGTH * b'a' # Queue local key packet local_key_packet = encrypt_and_sign(local_key + local_hek + conf_code, key=kek) queues[LOCAL_KEY_DATAGRAM_HEADER].put( (datetime.datetime.now(), local_key_packet)) o_sleep(test_delay) # Select file window command = WIN_SELECT + WIN_UID_FILE queue_packet(local_key, tx_hk, local_harac, command) local_key, local_harac = rotate_key(local_key, local_harac) o_sleep(test_delay) # Select local window command = WIN_SELECT + WIN_UID_LOCAL queue_packet(local_key, tx_hk, local_harac, command) local_key, local_harac = rotate_key(local_key, local_harac) o_sleep(test_delay) # A message that goes to buffer queue_packet( tx_mk, tx_hk, tx_harac, bool_to_bytes(False) + PRIVATE_MESSAGE_HEADER + b'Hi Bob', tx_pub_key) tx_mk, tx_harac = rotate_key(tx_mk, tx_harac) # ECDHE keyset for Bob command = KEY_EX_ECDHE + nick_to_pub_key("Bob") + ( 4 * SYMMETRIC_KEY_LENGTH * b'a') + str_to_bytes('Bob') queue_packet(local_key, tx_hk, local_harac, command) local_key, local_harac = rotate_key(local_key, local_harac) o_sleep(test_delay) # Message for Bob queue_packet( tx_mk, tx_hk, tx_harac, bool_to_bytes(False) + PRIVATE_MESSAGE_HEADER + b'Hi Bob', tx_pub_key) tx_mk, tx_harac = rotate_key(tx_mk, tx_harac) o_sleep(test_delay) # Enable file reception for Bob command = CH_FILE_RECV + ENABLE.upper() + US_BYTE queue_packet(local_key, tx_hk, local_harac, command) o_sleep(test_delay) # File packet from Bob ct = encrypt_and_sign(b'test', file_key) f_hash = blake2b(ct) packet = nick_to_pub_key('Bob') + ORIGIN_CONTACT_HEADER + ct queues[FILE_DATAGRAM_HEADER].put((datetime.datetime.now(), packet)) o_sleep(test_delay) # File key packet from Bob queue_packet( tx_mk, tx_hk, tx_harac, bool_to_bytes(False) + FILE_KEY_HEADER + base64.b85encode(f_hash + file_key), tx_pub_key) o_sleep(test_delay) # Queue exit message to break the loop o_sleep(0.5) queues[UNITTEST_QUEUE].put(EXIT) o_sleep(test_delay)