示例#1
0
 def setUp(self):
     self.unittest_dir         = cd_unittest()
     self.file_name            = f"{DIR_USER_DATA}{TX}_settings"
     self.master_key           = MasterKey()
     self.settings             = Settings(self.master_key, operation=TX, local_test=False)
     self.contact_list         = ContactList(nicks=[f'contact_{n}' for n in range(18)])
     self.group_list           = GroupList(groups=[f'group_{n}' for n in range(18)])
     self.group_list.groups[0] = create_group('group_0', [f'contact_{n}' for n in range(18)])
     self.args                 = self.contact_list, self.group_list
示例#2
0
    def test_load_of_modified_database_raises_critical_error(self) -> None:
        # Store settings to database
        self.settings.store_settings()

        # Test reading from database works normally
        self.assertIsInstance(Settings(self.master_key, operation=TX, local_test=False), Settings)

        # Test loading of the tampered database raises CriticalError
        tamper_file(self.file_name, tamper_size=1)
        with self.assertRaises(SystemExit):
            Settings(self.master_key, operation=TX, local_test=False)
示例#3
0
    def test_store_and_load_rx_settings(self) -> None:
        # Setup
        self.settings = Settings(self.master_key, operation=RX, local_test=False)

        # Test store
        self.assertFalse(self.settings.disable_gui_dialog)
        self.settings.disable_gui_dialog = True
        self.settings.store_settings()
        self.assertEqual(os.path.getsize(self.file_name), SETTING_LENGTH)

        # Test load
        settings2 = Settings(self.master_key, RX, False)
        self.assertTrue(settings2.disable_gui_dialog)
示例#4
0
 def setUp(self):
     self.o_input = builtins.input
     builtins.input = lambda _: 'yes'
     self.masterkey = MasterKey()
     self.settings = Settings(self.masterkey,
                              operation='ut',
                              local_test=False,
                              dd_sockets=False)
     self.contact_list = ContactList(
         nicks=['contact_{}'.format(n) for n in range(18)])
     self.group_list = GroupList(
         groups=['group_{}'.format(n) for n in range(18)])
     self.group_list.groups[0] = create_group(
         'group_0', ['contact_{}'.format(n) for n in range(18)])
示例#5
0
    def test_store_and_load_tx_settings(self) -> None:
        # Test store
        self.assertFalse(self.settings.disable_gui_dialog)
        self.settings.disable_gui_dialog = True
        self.settings.store_settings()
        self.assertEqual(os.path.getsize(self.file_name), SETTING_LENGTH)

        # Test load
        settings2 = Settings(self.master_key, TX, False)
        self.assertTrue(settings2.disable_gui_dialog)
示例#6
0
    def test_store_and_load_settings(self):
        # Test store
        self.assertFalse(self.settings.disable_gui_dialog)
        self.settings.disable_gui_dialog = True
        self.settings.store_settings()
        self.assertEqual(os.path.getsize(f"{DIR_USER_DATA}ut_settings"),
                         SETTING_LENGTH)

        # Test load
        settings2 = Settings(self.masterkey, 'ut', False, False)
        self.assertTrue(settings2.disable_gui_dialog)
示例#7
0
class TestSettings(TFCTestCase):
    def setUp(self):
        self.o_input = builtins.input
        builtins.input = lambda _: 'yes'
        self.masterkey = MasterKey()
        self.settings = Settings(self.masterkey,
                                 operation='ut',
                                 local_test=False,
                                 dd_sockets=False)
        self.contact_list = ContactList(
            nicks=['contact_{}'.format(n) for n in range(18)])
        self.group_list = GroupList(
            groups=['group_{}'.format(n) for n in range(18)])
        self.group_list.groups[0] = create_group(
            'group_0', ['contact_{}'.format(n) for n in range(18)])

    def tearDown(self):
        cleanup()
        builtins.input = self.o_input

    def test_invalid_type_raises_critical_error_on_store(self):
        self.settings.serial_error_correction = b'bytestring'
        with self.assertRaises(SystemExit):
            self.settings.store_settings()

    def test_invalid_type_raises_critical_error_on_load(self):
        with self.assertRaises(SystemExit):
            self.settings.nh_bypass_messages = b'bytestring'
            self.settings.load_settings()

    def test_store_and_load_settings(self):
        # Test store
        self.assertFalse(self.settings.disable_gui_dialog)
        self.settings.disable_gui_dialog = True
        self.settings.store_settings()
        self.assertEqual(os.path.getsize(f"{DIR_USER_DATA}ut_settings"),
                         SETTING_LENGTH)

        # Test load
        settings2 = Settings(self.masterkey, 'ut', False, False)
        self.assertTrue(settings2.disable_gui_dialog)

    def test_invalid_type_raises_critical_error_when_changing_settings(self):
        self.settings.traffic_masking = b'bytestring'
        with self.assertRaises(SystemExit):
            self.assertIsNone(
                self.settings.change_setting('traffic_masking', 'True',
                                             self.contact_list,
                                             self.group_list))

    def test_change_settings(self):
        self.assertFR("Error: Invalid value 'Falsee'",
                      self.settings.change_setting, 'disable_gui_dialog',
                      'Falsee', self.contact_list, self.group_list)
        self.assertFR("Error: Invalid value '1.1'",
                      self.settings.change_setting,
                      'max_number_of_group_members', '1.1', self.contact_list,
                      self.group_list)
        self.assertFR("Error: Invalid value '-1.1'",
                      self.settings.change_setting,
                      'max_duration_of_random_delay', '-1.1',
                      self.contact_list, self.group_list)
        self.assertFR("Error: Invalid value '18446744073709551616'",
                      self.settings.change_setting, 'serial_error_correction',
                      str(2**64), self.contact_list, self.group_list)
        self.assertFR("Error: Invalid value 'True'",
                      self.settings.change_setting,
                      'traffic_masking_static_delay', 'True',
                      self.contact_list, self.group_list)

        self.assertIsNone(
            self.settings.change_setting('serial_error_correction', '10',
                                         self.contact_list, self.group_list))
        self.assertIsNone(
            self.settings.change_setting('rxm_usb_serial_adapter', 'True',
                                         self.contact_list, self.group_list))
        self.assertIsNone(
            self.settings.change_setting('traffic_masking', 'True',
                                         self.contact_list, self.group_list))

    def test_validate_key_value_pair(self):
        self.assertFR(
            "Error: Database padding settings must be divisible by 10.",
            self.settings.validate_key_value_pair,
            'max_number_of_group_members', 0, self.contact_list,
            self.group_list)
        self.assertFR(
            "Error: Database padding settings must be divisible by 10.",
            self.settings.validate_key_value_pair,
            'max_number_of_group_members', 18, self.contact_list,
            self.group_list)
        self.assertFR(
            "Error: Database padding settings must be divisible by 10.",
            self.settings.validate_key_value_pair, 'max_number_of_groups', 18,
            self.contact_list, self.group_list)
        self.assertFR(
            "Error: Database padding settings must be divisible by 10.",
            self.settings.validate_key_value_pair, 'max_number_of_contacts',
            18, self.contact_list, self.group_list)
        self.assertFR("Error: Can't set max number of members lower than 20.",
                      self.settings.validate_key_value_pair,
                      'max_number_of_group_members', 10, self.contact_list,
                      self.group_list)
        self.assertFR("Error: Can't set max number of groups lower than 20.",
                      self.settings.validate_key_value_pair,
                      'max_number_of_groups', 10, self.contact_list,
                      self.group_list)
        self.assertFR("Error: Can't set max number of contacts lower than 20.",
                      self.settings.validate_key_value_pair,
                      'max_number_of_contacts', 10, self.contact_list,
                      self.group_list)
        self.assertFR("Error: Specified baud rate is not supported.",
                      self.settings.validate_key_value_pair, 'serial_baudrate',
                      10, self.contact_list, self.group_list)
        self.assertFR("Error: Invalid value for error correction ratio.",
                      self.settings.validate_key_value_pair,
                      'serial_error_correction', 0, self.contact_list,
                      self.group_list)
        self.assertFR("Error: Invalid value for error correction ratio.",
                      self.settings.validate_key_value_pair,
                      'serial_error_correction', -1, self.contact_list,
                      self.group_list)
        self.assertFR("Error: Too small value for message notify duration.",
                      self.settings.validate_key_value_pair,
                      'new_message_notify_duration', 0.04, self.contact_list,
                      self.group_list)

        self.assertIsNone(
            self.settings.validate_key_value_pair("serial_baudrate", 9600,
                                                  self.contact_list,
                                                  self.group_list))

    def test_too_narrow_terminal_raises_fr_when_printing_settings(self):
        # Setup
        o_get_terminal_size = shutil.get_terminal_size
        shutil.get_terminal_size = lambda: [64, 64]

        # Test
        self.assertFR("Error: Screen width is too small.",
                      self.settings.print_settings)

        # Teardown
        shutil.get_terminal_size = o_get_terminal_size

    def test_setup(self):
        # Setup
        builtins.input = lambda _: 'No'

        # Test
        self.settings.software_operation = TX
        self.settings.setup()
        self.assertFalse(self.settings.txm_usb_serial_adapter)

        self.settings.software_operation = RX
        self.settings.setup()
        self.assertFalse(self.settings.rxm_usb_serial_adapter)

    def test_print_settings(self):
        self.settings.max_number_of_group_members = 30
        self.settings.log_messages_by_default = True
        self.settings.traffic_masking_static_delay = 10.2
        self.assertPrints(
            CLEAR_ENTIRE_SCREEN + CURSOR_LEFT_UP_CORNER + """\

Setting name                    Current value   Default value   Description
────────────────────────────────────────────────────────────────────────────────
disable_gui_dialog              False           False           True replaces
                                                                Tkinter dialogs
                                                                with CLI prompts

max_number_of_group_members     30              20              Max members in
                                                                group (TxM/RxM
                                                                must have the
                                                                same value)

max_number_of_groups            20              20              Max number of
                                                                groups (TxM/RxM
                                                                must have the
                                                                same value)

max_number_of_contacts          20              20              Max number of
                                                                contacts
                                                                (TxM/RxM must
                                                                have the same
                                                                value)

serial_baudrate                 19200           19200           The speed of
                                                                serial interface
                                                                in bauds per
                                                                second

serial_error_correction         5               5               Number of byte
                                                                errors serial
                                                                datagrams can
                                                                recover from

log_messages_by_default         True            False           Default logging
                                                                setting for new
                                                                contacts/groups

accept_files_by_default         False           False           Default file
                                                                reception
                                                                setting for new
                                                                contacts

show_notifications_by_default   True            True            Default message
                                                                notification
                                                                setting for new
                                                                contacts/groups

logfile_masking                 False           False           True hides real
                                                                size of logfile
                                                                during traffic
                                                                masking

txm_usb_serial_adapter          True            True            False uses
                                                                system's
                                                                integrated
                                                                serial interface

nh_bypass_messages              True            True            False removes NH
                                                                bypass interrupt
                                                                messages

confirm_sent_files              True            True            False sends
                                                                files without
                                                                asking for
                                                                confirmation

double_space_exits              False           False           True exits,
                                                                False clears
                                                                screen with
                                                                double space
                                                                command

traffic_masking                 False           False           True enables
                                                                traffic masking
                                                                to hide metadata

traffic_masking_static_delay    10.2            2.0             Static delay
                                                                between traffic
                                                                masking packets

traffic_masking_random_delay    2.0             2.0             Max random delay
                                                                for traffic
                                                                masking timing
                                                                obfuscation

multi_packet_random_delay       False           False           True adds IM
                                                                server spam
                                                                guard evading
                                                                delay

max_duration_of_random_delay    10.0            10.0            Maximum time for
                                                                random spam
                                                                guard evasion
                                                                delay

rxm_usb_serial_adapter          True            True            False uses
                                                                system's
                                                                integrated
                                                                serial interface

new_message_notify_preview      False           False           When True, shows
                                                                preview of
                                                                received message

new_message_notify_duration     1.0             1.0             Number of
                                                                seconds new
                                                                message
                                                                notification
                                                                appears


""", self.settings.print_settings)
示例#8
0
def main() -> None:
    """Load persistent data and launch the Transmitter/Receiver Program.

    This function decrypts user data from databases and launches
    processes for Transmitter or Receiver Program. It then monitors the
    EXIT_QUEUE for EXIT/WIPE signals and each process in case one of
    them dies.

    If you're reading this code to get the big picture on how TFC works,
    start by looking at the loop functions below, defined as the target
    for each process, from top to bottom:
        From `input_loop` process, you can see how the Transmitter
    Program processes a message or command from the user, creates
    assembly packets for a message/file/command, and how those are
    eventually pushed into a multiprocessing queue, from where they are
    loaded by the `sender_loop`.
        The `sender_loop` process encrypts outgoing assembly packets,
    and outputs the encrypted datagrams to the Networked Computer. The
    process also sends assembly packets to the `log_writer_loop`.
        The `log_writer_loop` process filters out non-message assembly
    packets and if logging for contact is enabled, stores the message
    assembly packet into an encrypted log database.
        The `noise_loop` processes are used to provide the `sender_loop`
    an interface identical to that of the `input_loop`. The
    `sender_loop` uses the interface to load noise packets/commands when
    traffic masking is enabled.

    Refer to the file `relay.py` to see how the Relay Program on
    Networked Computer manages datagrams between the network and
    Source/Destination Computer.

    In Receiver Program (also launched by this file), the `gateway_loop`
    process acts as a buffer for incoming datagrams. This buffer is
    consumed by the `receiver_loop` process that organizes datagrams
    loaded from the buffer into a set of queues depending on datagram
    type. Finally, the `output_loop` process loads and processes
    datagrams from the queues in the order of priority.
    """
    working_dir = f'{os.getenv("HOME")}/{DIR_TFC}'
    ensure_dir(working_dir)
    os.chdir(working_dir)

    operation, local_test, data_diode_sockets = process_arguments()

    check_kernel_version()
    check_kernel_entropy()

    print_title(operation)

    master_key   = MasterKey(              operation, local_test)
    gateway      = Gateway(                operation, local_test, data_diode_sockets)
    settings     = Settings(   master_key, operation, local_test)
    contact_list = ContactList(master_key, settings)
    key_list     = KeyList(    master_key, settings)
    group_list   = GroupList(  master_key, settings, contact_list)

    if settings.software_operation == TX:
        onion_service = OnionService(master_key)

        queues = {MESSAGE_PACKET_QUEUE:    Queue(),  # Standard              messages
                  COMMAND_PACKET_QUEUE:    Queue(),  # Standard              commands
                  TM_MESSAGE_PACKET_QUEUE: Queue(),  # Traffic masking       messages
                  TM_FILE_PACKET_QUEUE:    Queue(),  # Traffic masking       files
                  TM_COMMAND_PACKET_QUEUE: Queue(),  # Traffic masking       commands
                  TM_NOISE_PACKET_QUEUE:   Queue(),  # Traffic masking noise packets
                  TM_NOISE_COMMAND_QUEUE:  Queue(),  # Traffic masking noise commands
                  RELAY_PACKET_QUEUE:      Queue(),  # Unencrypted datagrams to Networked Computer
                  LOG_PACKET_QUEUE:        Queue(),  # `log_writer_loop` assembly packets to be logged
                  LOG_SETTING_QUEUE:       Queue(),  # `log_writer_loop` logging state management between noise packets
                  TRAFFIC_MASKING_QUEUE:   Queue(),  # `log_writer_loop` traffic masking setting management commands
                  LOGFILE_MASKING_QUEUE:   Queue(),  # `log_writer_loop` logfile masking setting management commands
                  KEY_MANAGEMENT_QUEUE:    Queue(),  # `sender_loop` key database management commands
                  SENDER_MODE_QUEUE:       Queue(),  # `sender_loop` default/traffic masking mode switch commands
                  WINDOW_SELECT_QUEUE:     Queue(),  # `sender_loop` window selection commands during traffic masking
                  EXIT_QUEUE:              Queue()   # EXIT/WIPE signal from `input_loop` to `main`
                  }  # type: Dict[bytes, Queue]

        process_list = [Process(target=input_loop,      args=(queues, settings, gateway, contact_list, group_list,
                                                              master_key, onion_service, sys.stdin.fileno())),
                        Process(target=sender_loop,     args=(queues, settings, gateway, key_list)),
                        Process(target=log_writer_loop, args=(queues, settings)),
                        Process(target=noise_loop,      args=(queues, contact_list)),
                        Process(target=noise_loop,      args=(queues,))]

    else:
        queues = {GATEWAY_QUEUE:             Queue(),  # Buffer for incoming datagrams
                  LOCAL_KEY_DATAGRAM_HEADER: Queue(),  # Local key datagrams
                  MESSAGE_DATAGRAM_HEADER:   Queue(),  # Message   datagrams
                  FILE_DATAGRAM_HEADER:      Queue(),  # File      datagrams
                  COMMAND_DATAGRAM_HEADER:   Queue(),  # Command   datagrams
                  EXIT_QUEUE:                Queue()   # EXIT/WIPE signal from `output_loop` to `main`
                  }

        process_list = [Process(target=gateway_loop,  args=(queues, gateway)),
                        Process(target=receiver_loop, args=(queues, gateway)),
                        Process(target=output_loop,   args=(queues, gateway, settings, contact_list, key_list,
                                                            group_list, master_key, sys.stdin.fileno()))]

    for p in process_list:
        p.start()

    monitor_processes(process_list, settings.software_operation, queues)
示例#9
0
class TestSettings(TFCTestCase):

    def setUp(self) -> None:
        """Pre-test actions."""
        self.unit_test_dir        = cd_unit_test()
        self.file_name            = f"{DIR_USER_DATA}{TX}_settings"
        self.master_key           = MasterKey()
        self.settings             = Settings(self.master_key, operation=TX, local_test=False)
        self.contact_list         = ContactList(nicks=[f'contact_{n}' for n in range(18)])
        self.group_list           = GroupList(groups=[f'group_{n}' for n in range(18)])
        self.group_list.groups[0] = create_group('group_0', [f'contact_{n}' for n in range(18)])
        self.args                 = self.contact_list, self.group_list

    def tearDown(self) -> None:
        """Post-test actions."""
        cleanup(self.unit_test_dir)

    def test_invalid_type_raises_critical_error_on_store(self) -> None:
        self.settings.tm_random_delay = b'bytestring'
        with self.assertRaises(SystemExit):
            self.settings.store_settings()

    def test_invalid_type_raises_critical_error_on_load(self) -> None:
        with self.assertRaises(SystemExit):
            self.settings.nc_bypass_messages = b'bytestring'
            self.settings.load_settings()

    def test_store_and_load_tx_settings(self) -> None:
        # Test store
        self.assertFalse(self.settings.disable_gui_dialog)
        self.settings.disable_gui_dialog = True
        self.settings.store_settings()
        self.assertEqual(os.path.getsize(self.file_name), SETTING_LENGTH)

        # Test load
        settings2 = Settings(self.master_key, TX, False)
        self.assertTrue(settings2.disable_gui_dialog)

    def test_store_and_load_rx_settings(self) -> None:
        # Setup
        self.settings = Settings(self.master_key, operation=RX, local_test=False)

        # Test store
        self.assertFalse(self.settings.disable_gui_dialog)
        self.settings.disable_gui_dialog = True
        self.settings.store_settings()
        self.assertEqual(os.path.getsize(self.file_name), SETTING_LENGTH)

        # Test load
        settings2 = Settings(self.master_key, RX, False)
        self.assertTrue(settings2.disable_gui_dialog)

    def test_load_of_modified_database_raises_critical_error(self) -> None:
        # Store settings to database
        self.settings.store_settings()

        # Test reading from database works normally
        self.assertIsInstance(Settings(self.master_key, operation=TX, local_test=False), Settings)

        # Test loading of the tampered database raises CriticalError
        tamper_file(self.file_name, tamper_size=1)
        with self.assertRaises(SystemExit):
            Settings(self.master_key, operation=TX, local_test=False)

    def test_invalid_type_raises_critical_error_when_changing_settings(self) -> None:
        self.settings.traffic_masking = b'bytestring'
        with self.assertRaises(SystemExit):
            self.assertIsNone(self.settings.change_setting('traffic_masking', 'True', *self.args))

    def test_change_settings(self) -> None:
        self.assert_se("Error: Invalid setting value 'Falsee'.",
                       self.settings.change_setting, 'disable_gui_dialog', 'Falsee', *self.args)
        self.assert_se("Error: Invalid setting value '1.1'.",
                       self.settings.change_setting, 'max_number_of_group_members',     '1.1', *self.args)
        self.assert_se("Error: Invalid setting value '18446744073709551616'.",
                       self.settings.change_setting, 'max_number_of_contacts', str(2 ** 64), *self.args)
        self.assert_se("Error: Invalid setting value '-1.1'.",
                       self.settings.change_setting, 'tm_static_delay',                '-1.1', *self.args)
        self.assert_se("Error: Invalid setting value 'True'.",
                       self.settings.change_setting, 'tm_static_delay',                'True', *self.args)

        self.assertIsNone(self.settings.change_setting('traffic_masking',              'True', *self.args))
        self.assertIsNone(self.settings.change_setting('max_number_of_group_members',  '100',  *self.args))

    @mock.patch('builtins.input', side_effect=['No', 'Yes'])
    def test_validate_key_value_pair(self, _: Any) -> None:
        self.assert_se("Error: Database padding settings must be divisible by 10.",
                       self.settings.validate_key_value_pair, 'max_number_of_group_members', 0, *self.args)
        self.assert_se("Error: Database padding settings must be divisible by 10.",
                       self.settings.validate_key_value_pair, 'max_number_of_group_members', 18, *self.args)
        self.assert_se("Error: Database padding settings must be divisible by 10.",
                       self.settings.validate_key_value_pair, 'max_number_of_groups', 18, *self.args)
        self.assert_se("Error: Database padding settings must be divisible by 10.",
                       self.settings.validate_key_value_pair, 'max_number_of_contacts', 18, *self.args)
        self.assert_se("Error: Can't set the max number of members lower than 20.",
                       self.settings.validate_key_value_pair, 'max_number_of_group_members', 10, *self.args)
        self.assert_se("Error: Can't set the max number of groups lower than 20.",
                       self.settings.validate_key_value_pair, 'max_number_of_groups', 10, *self.args)
        self.assert_se("Error: Can't set the max number of contacts lower than 20.",
                       self.settings.validate_key_value_pair, 'max_number_of_contacts', 10, *self.args)
        self.assert_se("Error: Too small value for message notify duration.",
                       self.settings.validate_key_value_pair, 'new_message_notify_duration', 0.04, *self.args)
        self.assert_se("Error: Can't set static delay lower than 0.1.",
                       self.settings.validate_key_value_pair, 'tm_static_delay', 0.01, *self.args)
        self.assert_se("Error: Can't set random delay lower than 0.1.",
                       self.settings.validate_key_value_pair, 'tm_random_delay', 0.01, *self.args)
        self.assert_se("Aborted traffic masking setting change.",
                       self.settings.validate_key_value_pair, 'tm_random_delay', 0.1, *self.args)

        self.assertIsNone(self.settings.validate_key_value_pair("serial_baudrate",  9600, *self.args))
        self.assertIsNone(self.settings.validate_key_value_pair("tm_static_delay",     1, *self.args))

    @mock.patch('shutil.get_terminal_size', return_value=(64, 64))
    def test_too_narrow_terminal_raises_fr_when_printing_settings(self, _: Any) -> None:
        # Test
        self.assert_se("Error: Screen width is too small.", self.settings.print_settings)

    def test_print_settings(self) -> None:
        self.settings.max_number_of_group_members = 30
        self.settings.log_messages_by_default     = True
        self.settings.tm_static_delay             = 10.2
        self.assert_prints(CLEAR_ENTIRE_SCREEN + CURSOR_LEFT_UP_CORNER + """\

Setting name                    Current value   Default value   Description
────────────────────────────────────────────────────────────────────────────────
disable_gui_dialog              False           False           True replaces
                                                                GUI dialogs with
                                                                CLI prompts

max_number_of_group_members     30              50              Maximum number
                                                                of members in a
                                                                group

max_number_of_groups            50              50              Maximum number
                                                                of groups

max_number_of_contacts          50              50              Maximum number
                                                                of contacts

log_messages_by_default         True            False           Default logging
                                                                setting for new
                                                                contacts/groups

accept_files_by_default         False           False           Default file
                                                                reception
                                                                setting for new
                                                                contacts

show_notifications_by_default   True            True            Default message
                                                                notification
                                                                setting for new
                                                                contacts/groups

log_file_masking                False           False           True hides real
                                                                size of log file
                                                                during traffic
                                                                masking

ask_password_for_log_access     True            True            False disables
                                                                password prompt
                                                                when viewing/exp
                                                                orting logs

nc_bypass_messages              False           False           False removes
                                                                Networked
                                                                Computer bypass
                                                                interrupt
                                                                messages

confirm_sent_files              True            True            False sends
                                                                files without
                                                                asking for
                                                                confirmation

double_space_exits              False           False           True exits,
                                                                False clears
                                                                screen with
                                                                double space
                                                                command

traffic_masking                 False           False           True enables
                                                                traffic masking
                                                                to hide metadata

tm_static_delay                 10.2            2.0             The static delay
                                                                between traffic
                                                                masking packets

tm_random_delay                 2.0             2.0             Max random delay
                                                                for traffic
                                                                masking timing
                                                                obfuscation

allow_contact_requests          True            True            When False, does
                                                                not show TFC
                                                                contact requests

new_message_notify_preview      False           False           When True, shows
                                                                a preview of the
                                                                received message

new_message_notify_duration     1.0             1.0             Number of
                                                                seconds new
                                                                message
                                                                notification
                                                                appears

max_decompress_size             100000000       100000000       Max size
                                                                Receiver accepts
                                                                when
                                                                decompressing
                                                                file

""", self.settings.print_settings)
示例#10
0
文件: tfc.py 项目: barleyj/tfc
def main() -> None:
    """Derive master key, decrypt databases and initialize processes."""
    os.chdir(sys.path[0])
    init_entropy()

    operation, local_test, dd_sockets = process_arguments()

    clear_screen()
    c_print("TFC", head=1, tail=1)

    master_key = MasterKey(operation, local_test)
    settings = Settings(master_key, operation, local_test, dd_sockets)
    contact_list = ContactList(master_key, settings)
    key_list = KeyList(master_key, settings)
    group_list = GroupList(master_key, settings, contact_list)
    gateway = Gateway(settings)
    process_list = []

    if settings.software_operation == 'tx':

        queues = {
            MESSAGE_PACKET_QUEUE: Queue(),
            FILE_PACKET_QUEUE: Queue(),
            COMMAND_PACKET_QUEUE: Queue(),
            LOG_PACKET_QUEUE: Queue(),
            NOISE_PACKET_QUEUE: Queue(),
            NOISE_COMMAND_QUEUE: Queue(),
            KEY_MANAGEMENT_QUEUE: Queue(),
            WINDOW_SELECT_QUEUE: Queue()
        }

        if settings.session_trickle:
            np_filler = Process(target=noise_process,
                                args=(P_N_HEADER, queues[NOISE_PACKET_QUEUE],
                                      contact_list))
            nc_filler = Process(target=noise_process,
                                args=(C_N_HEADER, queues[NOISE_COMMAND_QUEUE]))
            process_list.extend([np_filler, nc_filler])
            for p in [np_filler, nc_filler]:
                p.start()
            while any([
                    q.qsize() < 1000 for q in
                [queues[NOISE_PACKET_QUEUE], queues[NOISE_COMMAND_QUEUE]]
            ]):
                time.sleep(0.1)

        sender_process = Process(target=sender_loop,
                                 args=(settings, queues, gateway, key_list))
        input_process = Process(target=tx_loop,
                                args=(settings, queues, gateway,
                                      contact_list, group_list, master_key,
                                      sys.stdin.fileno()))
        log_process = Process(target=log_writer,
                              args=(queues[LOG_PACKET_QUEUE], ))
        process_list.extend([sender_process, input_process, log_process])
        for p in [sender_process, input_process, log_process]:
            p.start()

    elif settings.software_operation == 'rx':

        queues = {
            LOCAL_KEY_PACKET_HEADER: Queue(),
            PUBLIC_KEY_PACKET_HEADER: Queue(),
            MESSAGE_PACKET_HEADER: Queue(),
            COMMAND_PACKET_HEADER: Queue(),
            IMPORTED_FILE_CT_HEADER: Queue(),
            GATEWAY_QUEUE: Queue()
        }

        gateway_process = Process(target=gw_incoming,
                                  args=(gateway, queues[GATEWAY_QUEUE]))
        receiver_process = Process(target=receiver_loop,
                                   args=(settings, queues))
        output_process = Process(target=rx_loop,
                                 args=(settings, queues, contact_list,
                                       key_list, group_list, master_key,
                                       sys.stdin.fileno()))
        process_list.extend(
            [gateway_process, receiver_process, output_process])
        for p in [gateway_process, receiver_process, output_process]:
            p.start()

    while True:
        try:
            time.sleep(0.1)
            if not all([p.is_alive() for p in process_list]):
                for p in process_list:
                    p.terminate()
                exit()
        except (EOFError, KeyboardInterrupt):
            pass
示例#11
0
    def test_class(self):
        # Setup
        masterkey            = MasterKey()
        o_input              = builtins.input
        builtins.input       = lambda x: 'yes'
        settings             = Settings(masterkey, 'ut', False, False)
        contact_list         = ContactList(nicks=['contact_{}'.format(n) for n in range(18)])
        group_list           = GroupList(groups =['group_{}'.format(n) for n in range(18)])
        group_list.groups[0] = create_group('group_0', ['contact_{}'.format(n) for n in range(18)])

        # Test store/load
        self.assertFalse(settings.disable_gui_dialog)
        settings.disable_gui_dialog = True
        settings.store_settings()

        self.assertTrue(os.path.isfile(f"{DIR_USER_DATA}/ut_settings"))
        self.assertEqual(os.path.getsize(f"{DIR_USER_DATA}/ut_settings"), 24 + 1024 + 9*8 + 12*1 + 16)

        settings2 = Settings(masterkey, 'ut', False, False)
        self.assertTrue(settings2.disable_gui_dialog)

        settings2.format_of_logfiles = b'invalid'
        with self.assertRaises(SystemExit):
            settings2.store_settings()
        with self.assertRaises(SystemExit):
            settings2.change_setting('format_of_logfiles', '%Y-%m-%d %H:%M:%S', contact_list, group_list)
        settings2.format_of_logfiles = '%Y-%m-%d %H:%M:%S'

        # Test change_setting
        self.assertFR('Invalid value Falsee.',                   settings2.change_setting, 'disable_gui_dialog', 'Falsee',              contact_list, group_list)
        self.assertFR('Invalid value 1.1.',                      settings2.change_setting, 'm_members_in_group', '1.1',                 contact_list, group_list)
        self.assertFR('Invalid value 7378697629483820650.',      settings2.change_setting, 'm_members_in_group', '7378697629483820650', contact_list, group_list)
        self.assertFR('Invalid value True.',                     settings2.change_setting, 'trickle_stat_delay', 'True',                contact_list, group_list)
        self.assertFR("Setting must be shorter than 256 chars.", settings2.change_setting, 'format_of_logfiles', 256*'a',               contact_list, group_list)

        self.assertIsNone(settings2.change_setting('format_of_logfiles', '%Y-%m-%d %H:%M:%S', contact_list, group_list))
        self.assertIsNone(settings2.change_setting('e_correction_ratio', '10',                contact_list, group_list))
        self.assertIsNone(settings2.change_setting('rxm_serial_adapter', 'True',              contact_list, group_list))
        self.assertIsNone(settings2.change_setting('trickle_connection', 'True',              contact_list, group_list))

        self.assertFR("Database padding settings must be divisible by 10.", settings2.validate_key_value_pair, 'm_members_in_group', '18', contact_list, group_list)
        self.assertFR("Database padding settings must be divisible by 10.", settings2.validate_key_value_pair, 'm_number_of_groups', '18', contact_list, group_list)
        self.assertFR("Database padding settings must be divisible by 10.", settings2.validate_key_value_pair, 'm_number_of_accnts', '18', contact_list, group_list)
        self.assertFR("Can't set max number of members lower than 20.",     settings2.validate_key_value_pair, 'm_members_in_group', '10', contact_list, group_list)
        self.assertFR("Can't set max number of groups lower than 20.",      settings2.validate_key_value_pair, 'm_number_of_groups', '10', contact_list, group_list)
        self.assertFR("Can't set max number of contacts lower than 20.",    settings2.validate_key_value_pair, 'm_number_of_accnts', '10', contact_list, group_list)
        self.assertFR("Specified baud rate is not supported.",              settings2.validate_key_value_pair, 'serial_iface_speed', '10', contact_list, group_list)
        self.assertFR("Invalid value for error correction ratio.",          settings2.validate_key_value_pair, 'e_correction_ratio', '0',  contact_list, group_list)
        self.assertFR("Invalid value for error correction ratio.",          settings2.validate_key_value_pair, 'e_correction_ratio', 'a',  contact_list, group_list)
        self.assertFR("Invalid value for error correction ratio.",          settings2.validate_key_value_pair, 'e_correction_ratio', '-1', contact_list, group_list)

        self.assertIsNone(settings2.print_settings())

        builtins.input = o_input
示例#12
0
def main() -> None:
    """Derive master key, decrypt databases and initialize processes."""
    os.chdir(sys.path[0])

    check_kernel_version()
    check_kernel_entropy()

    operation, local_test, dd_sockets = process_arguments()

    clear_screen()
    c_print(TFC, head=1, tail=1)

    master_key = MasterKey(operation, local_test)
    settings = Settings(master_key, operation, local_test, dd_sockets)
    contact_list = ContactList(master_key, settings)
    key_list = KeyList(master_key, settings)
    group_list = GroupList(master_key, settings, contact_list)
    gateway = Gateway(settings)

    if settings.software_operation == TX:
        queues = {
            MESSAGE_PACKET_QUEUE: Queue(),
            FILE_PACKET_QUEUE: Queue(),
            COMMAND_PACKET_QUEUE: Queue(),
            NH_PACKET_QUEUE: Queue(),
            LOG_PACKET_QUEUE: Queue(),
            EXIT_QUEUE: Queue(),
            NOISE_PACKET_QUEUE: Queue(),
            NOISE_COMMAND_QUEUE: Queue(),
            KEY_MANAGEMENT_QUEUE: Queue(),
            WINDOW_SELECT_QUEUE: Queue()
        }

        process_list = [
            Process(target=input_loop,
                    args=(queues, settings, gateway, contact_list, group_list,
                          master_key, sys.stdin.fileno())),
            Process(target=sender_loop,
                    args=(queues, settings, gateway, key_list)),
            Process(target=log_writer_loop, args=(queues, ))
        ]

        if settings.session_traffic_masking:
            process_list.extend([
                Process(target=noise_loop,
                        args=(P_N_HEADER, queues[NOISE_PACKET_QUEUE],
                              contact_list)),
                Process(target=noise_loop,
                        args=(C_N_HEADER, queues[NOISE_COMMAND_QUEUE]))
            ])

    else:
        queues = {
            LOCAL_KEY_PACKET_HEADER: Queue(),
            PUBLIC_KEY_PACKET_HEADER: Queue(),
            MESSAGE_PACKET_HEADER: Queue(),
            COMMAND_PACKET_HEADER: Queue(),
            IMPORTED_FILE_HEADER: Queue(),
            EXIT_QUEUE: Queue(),
            GATEWAY_QUEUE: Queue()
        }

        process_list = [
            Process(target=gateway_loop, args=(queues, gateway)),
            Process(target=receiver_loop, args=(queues, settings)),
            Process(target=output_loop,
                    args=(queues, settings, contact_list, key_list, group_list,
                          master_key, sys.stdin.fileno()))
        ]

    for p in process_list:
        p.start()

    while True:
        with ignored(EOFError, KeyboardInterrupt):
            time.sleep(0.1)
            if not all([p.is_alive() for p in process_list]):
                for p in process_list:
                    p.terminate()
                sys.exit(1)

            if not queues[EXIT_QUEUE].empty():
                command = queues[EXIT_QUEUE].get()
                for p in process_list:
                    p.terminate()
                if command == WIPE:
                    subprocess.Popen(
                        f"find {DIR_USER_DATA} -name '{operation}*' -type f -exec shred -n 3 -z -u {{}} \;",
                        shell=True).wait()
                    os.system('poweroff')
                else:
                    sys.exit(0)