Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
 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)
Exemplo n.º 4
0
 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)
Exemplo n.º 5
0
 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)
Exemplo n.º 6
0
    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()
Exemplo n.º 7
0
    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)
Exemplo n.º 8
0
    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()
Exemplo n.º 9
0
    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)