def _check_db_settings(self, number_of_actual_groups: int, members_in_largest_group: int) -> bool: """\ Adjust TFC's settings automatically if the loaded group database was stored using larger database setting values. If settings had to be adjusted, return True so the method `self._load_groups` knows to write changes to a new database. """ update_db = False if number_of_actual_groups > self.settings.max_number_of_groups: self.settings.max_number_of_groups = round_up( number_of_actual_groups) update_db = True if members_in_largest_group > self.settings.max_number_of_group_members: self.settings.max_number_of_group_members = round_up( members_in_largest_group) update_db = True if update_db: self.settings.store_settings() return update_db
def validate_key_value_pair(key: str, value: str, contact_list: 'ContactList', group_list: 'GroupList') -> None: """Check values of some settings in closer detail.""" if key in [ 'm_members_in_group', 'm_number_of_groups', 'm_number_of_accnts' ]: if eval(value) % 10 != 0: raise FunctionReturn( "Database padding settings must be divisible by 10.") if key == 'm_members_in_group': min_size = round_up(group_list.largest_group()) if eval(value) < min_size: raise FunctionReturn( f"Can't set max number of members lower than {min_size}.") if key == 'm_number_of_groups': min_size = round_up(len(group_list)) if eval(value) < min_size: raise FunctionReturn( f"Can't set max number of groups lower than {min_size}.") if key == 'm_number_of_accnts': min_size = round_up(len(contact_list)) if eval(value) < min_size: raise FunctionReturn( f"Can't set max number of contacts lower than {min_size}.") if key == 'serial_iface_speed': if eval(value) not in serial.Serial().BAUDRATES: raise FunctionReturn("Specified baud rate is not supported.") c_print("Baud rate will change on restart.", head=1, tail=1) if key == 'e_correction_ratio': if not value.isdigit() or eval(value) < 1: raise FunctionReturn( "Invalid value for error correction ratio.") c_print("Error correction ratio will change on restart.", head=1, tail=1) if key in ['rxm_serial_adapter', 'txm_serial_adapter']: c_print("Interface will change on restart.", head=1, tail=1) if key in [ 'trickle_connection', 'trickle_stat_delay', 'trickle_rand_delay' ]: c_print("Trickle setting will change on restart.", head=1, tail=1)
def test_round_up(self): self.assertEqual(round_up(1), 10) self.assertEqual(round_up(5), 10) self.assertEqual(round_up(8), 10) self.assertEqual(round_up(10), 10) self.assertEqual(round_up(11), 20) self.assertEqual(round_up(15), 20) self.assertEqual(round_up(18), 20) self.assertEqual(round_up(20), 20) self.assertEqual(round_up(21), 30)
def validate_max_number_of_contacts(key: str, value: 'SettingType', contact_list: 'ContactList' ) -> None: """Validate setting value for maximum number of contacts.""" if key == "max_number_of_contacts": min_size = round_up(len(contact_list)) if value < min_size: raise SoftError(f"Error: Can't set the max number of contacts lower than {min_size}.", head_clear=True)
def validate_max_number_of_group_members(key: str, value: 'SettingType', group_list: 'GroupList' ) -> None: """Validate setting value for maximum number of group members.""" if key == "max_number_of_group_members": min_size = round_up(group_list.largest_group()) if value < min_size: raise SoftError(f"Error: Can't set the max number of members lower than {min_size}.", head_clear=True)
def load_groups(self) -> None: """Load groups from encrypted database.""" ensure_dir(f'{DIR_USER_DATA}/') with open(self.file_name, 'rb') as f: ct_bytes = f.read() pt_bytes = auth_and_decrypt(ct_bytes, self.master_key.master_key) update_db = False # Slice and decode headers padding_for_g = bytes_to_int(pt_bytes[0:8]) padding_for_m = bytes_to_int(pt_bytes[8:16]) n_of_actual_g = bytes_to_int(pt_bytes[16:24]) largest_group = bytes_to_int(pt_bytes[24:32]) if n_of_actual_g > self.settings.m_number_of_groups: self.settings.m_number_of_groups = round_up(n_of_actual_g) self.settings.store_settings() update_db = True print( "Group database had {} groups. Increased max number of groups to {}." .format(n_of_actual_g, self.settings.m_number_of_groups)) if largest_group > self.settings.m_members_in_group: self.settings.m_members_in_group = round_up(largest_group) self.settings.store_settings() update_db = True print( "A group in group database had {} members. Increased max size of groups to {}." .format(largest_group, self.settings.m_members_in_group)) # Strip header bytes pt_bytes = pt_bytes[32:] # ( no_fields * (padding + BOM) * bytes/char) + booleans bytes_per_group = ((1 + padding_for_m) * (255 + 1) * 4) + 2 # Remove dummy groups no_dummy_groups = padding_for_g - n_of_actual_g pt_bytes = pt_bytes[:-(no_dummy_groups * bytes_per_group)] groups = split_byte_string(pt_bytes, item_len=bytes_per_group) for g in groups: # Remove padding name = bytes_to_str(g[0:1024]) log_messages = bytes_to_bool(g[1024:1025]) notifications = bytes_to_bool(g[1025:1026]) members_b = split_byte_string(g[1026:], item_len=1024) members = [bytes_to_str(m) for m in members_b] # Remove dummy members members_df = [m for m in members if not m == 'dummy_member'] # Load contacts based on stored rx_account group_members = [ self.contact_list.get_contact(m) for m in members_df if self.contact_list.has_contact(m) ] self.groups.append( Group(name, log_messages, notifications, group_members, self.settings, self.store_groups)) if update_db: self.store_groups()
def validate_key_value_pair( key: str, # Name of the setting value: Union[int, float, bool], # Value of the setting contact_list: 'ContactList', group_list: 'GroupList') -> None: """Evaluate values for settings that have further restrictions.""" if key in [ 'max_number_of_group_members', 'max_number_of_groups', 'max_number_of_contacts' ]: if value % 10 != 0 or value == 0: raise FunctionReturn( "Error: Database padding settings must be divisible by 10.", head_clear=True) if key == 'max_number_of_group_members': min_size = round_up(group_list.largest_group()) if value < min_size: raise FunctionReturn( f"Error: Can't set the max number of members lower than {min_size}.", head_clear=True) if key == 'max_number_of_groups': min_size = round_up(len(group_list)) if value < min_size: raise FunctionReturn( f"Error: Can't set the max number of groups lower than {min_size}.", head_clear=True) if key == 'max_number_of_contacts': min_size = round_up(len(contact_list)) if value < min_size: raise FunctionReturn( f"Error: Can't set the max number of contacts lower than {min_size}.", head_clear=True) if key == 'new_message_notify_duration' and value < 0.05: raise FunctionReturn( "Error: Too small value for message notify duration.", head_clear=True) if key in ['tm_static_delay', 'tm_random_delay']: for key_, name, min_setting in [ ('tm_static_delay', 'static', TRAFFIC_MASKING_MIN_STATIC_DELAY), ('tm_random_delay', 'random', TRAFFIC_MASKING_MIN_RANDOM_DELAY) ]: if key == key_ and value < min_setting: raise FunctionReturn( f"Error: Can't set {name} delay lower than {min_setting}.", head_clear=True) if contact_list.settings.software_operation == TX: m_print([ "WARNING!", "Changing traffic masking delay can make your endpoint and traffic look unique!" ], bold=True, head=1, tail=1) if not yes("Proceed anyway?"): raise FunctionReturn( "Aborted traffic masking setting change.", head_clear=True) m_print("Traffic masking setting will change on restart.", head=1, tail=1)
def load_groups(self) -> None: """Load groups from encrypted database.""" with open(self.file_name, 'rb') as f: ct_bytes = f.read() pt_bytes = auth_and_decrypt(ct_bytes, self.master_key.master_key) update_db = False # Slice and decode headers padding_for_group_db = bytes_to_int(pt_bytes[0:8]) padding_for_members = bytes_to_int(pt_bytes[8:16]) number_of_actual_groups = bytes_to_int(pt_bytes[16:24]) largest_group = bytes_to_int(pt_bytes[24:32]) if number_of_actual_groups > self.settings.max_number_of_groups: self.settings.max_number_of_groups = round_up( number_of_actual_groups) self.settings.store_settings() update_db = True print( "Group database had {} groups. Increased max number of groups to {}." .format(number_of_actual_groups, self.settings.max_number_of_groups)) if largest_group > self.settings.max_number_of_group_members: self.settings.max_number_of_group_members = round_up(largest_group) self.settings.store_settings() update_db = True print( "A group in group database had {} members. Increased max size of groups to {}." .format(largest_group, self.settings.max_number_of_group_members)) group_name_field = 1 string_fields_in_group = padding_for_members + group_name_field bytes_per_group = string_fields_in_group * PADDED_UTF32_STR_LEN + 2 * BOOLEAN_SETTING_LEN # Remove group header and dummy groups dummy_group_data = (padding_for_group_db - number_of_actual_groups) * bytes_per_group group_data = pt_bytes[GROUP_DB_HEADER_LEN:-dummy_group_data] groups = split_byte_string(group_data, item_len=bytes_per_group) for g in groups: assert len(g) == bytes_per_group name = bytes_to_str(g[0:1024]) log_messages = bytes_to_bool(g[1024:1025]) notifications = bytes_to_bool(g[1025:1026]) members_bytes = split_byte_string(g[1026:], item_len=PADDED_UTF32_STR_LEN) members_w_dummies = [bytes_to_str(m) for m in members_bytes] members = [m for m in members_w_dummies if m != DUMMY_MEMBER] # Load contacts based on stored rx_account group_members = [ self.contact_list.get_contact(m) for m in members if self.contact_list.has_contact(m) ] # Update group database if any member has been removed from contact database if not all(m in self.contact_list.get_list_of_accounts() for m in members): update_db = True self.groups.append( Group(name, log_messages, notifications, group_members, self.settings, self.store_groups)) if update_db: self.store_groups()
def validate_key_value_pair(key: str, value: Union[int, float, bool], contact_list: 'ContactList', group_list: 'GroupList') -> None: """\ Perform further evaluation on settings the values of which have restrictions. """ if key in [ 'max_number_of_group_members', 'max_number_of_groups', 'max_number_of_contacts' ]: if value % 10 != 0 or value == 0: raise FunctionReturn( "Error: Database padding settings must be divisible by 10." ) if key == 'max_number_of_group_members': min_size = round_up(group_list.largest_group()) if value < min_size: raise FunctionReturn( f"Error: Can't set max number of members lower than {min_size}." ) if key == 'max_number_of_groups': min_size = round_up(len(group_list)) if value < min_size: raise FunctionReturn( f"Error: Can't set max number of groups lower than {min_size}." ) if key == 'max_number_of_contacts': min_size = round_up(len(contact_list)) if value < min_size: raise FunctionReturn( f"Error: Can't set max number of contacts lower than {min_size}." ) if key == 'serial_baudrate': if value not in serial.Serial().BAUDRATES: raise FunctionReturn( "Error: Specified baud rate is not supported.") c_print("Baud rate will change on restart.", head=1, tail=1) if key == 'serial_error_correction': if value < 1: raise FunctionReturn( "Error: Invalid value for error correction ratio.") c_print("Error correction ratio will change on restart.", head=1, tail=1) if key == 'new_message_notify_duration' and value < 0.05: raise FunctionReturn( "Error: Too small value for message notify duration.") if key in ['rxm_usb_serial_adapter', 'txm_usb_serial_adapter']: c_print("Interface will change on restart.", head=1, tail=1) if key in [ 'traffic_masking', 'traffic_masking_static_delay', 'traffic_masking_random_delay' ]: c_print("Traffic masking setting will change on restart.", head=1, tail=1)