示例#1
0
class TestLocalKey(TFCTestCase):
    def setUp(self):
        self.contact_list = ContactList()
        self.settings = Settings()
        self.queues = gen_queue_dict()
        self.args = self.contact_list, self.settings, self.queues

    def tearDown(self):
        tear_queues(self.queues)

    def test_new_local_key_when_traffic_masking_is_enabled_raises_fr(self):
        self.settings.traffic_masking = True
        self.contact_list.contacts = [create_contact(LOCAL_ID)]
        self.assert_fr("Error: Command is disabled during traffic masking.",
                       new_local_key, *self.args)

    @mock.patch('time.sleep', return_value=None)
    @mock.patch('builtins.input', side_effect=['bad', '', '61'])
    @mock.patch('os.getrandom',
                side_effect=[
                    SYMMETRIC_KEY_LENGTH * b'a', SYMMETRIC_KEY_LENGTH * b'a',
                    SYMMETRIC_KEY_LENGTH * b'a', XCHACHA20_NONCE_LENGTH * b'a',
                    SYMMETRIC_KEY_LENGTH * b'a', SYMMETRIC_KEY_LENGTH * b'a'
                ])
    @mock.patch('os.urandom', return_value=CONFIRM_CODE_LENGTH * b'a')
    def test_new_local_key(self, *_):
        # Setup
        self.settings.nc_bypass_messages = False
        self.settings.traffic_masking = False

        # Test
        self.assertIsNone(new_local_key(*self.args))
        local_contact = self.contact_list.get_contact_by_pub_key(LOCAL_PUBKEY)

        self.assertEqual(local_contact.onion_pub_key, LOCAL_PUBKEY)
        self.assertEqual(local_contact.nick, LOCAL_NICK)
        self.assertEqual(local_contact.tx_fingerprint,
                         bytes(FINGERPRINT_LENGTH))
        self.assertEqual(local_contact.rx_fingerprint,
                         bytes(FINGERPRINT_LENGTH))
        self.assertFalse(local_contact.log_messages)
        self.assertFalse(local_contact.file_reception)
        self.assertFalse(local_contact.notifications)

        self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 1)

        cmd, account, tx_key, rx_key, tx_hek, rx_hek = self.queues[
            KEY_MANAGEMENT_QUEUE].get()

        self.assertEqual(cmd, KDB_ADD_ENTRY_HEADER)
        self.assertEqual(account, LOCAL_PUBKEY)
        for key in [tx_key, rx_key, tx_hek, rx_hek]:
            self.assertIsInstance(key, bytes)
            self.assertEqual(len(key), SYMMETRIC_KEY_LENGTH)

    @mock.patch('time.sleep', return_value=None)
    @mock.patch('builtins.input', side_effect=KeyboardInterrupt)
    @mock.patch('os.getrandom', lambda x, flags: x * b'a')
    def test_keyboard_interrupt_raises_fr(self, *_):
        self.assert_fr("Local key setup aborted.", new_local_key, *self.args)
示例#2
0
class TestChNick(TFCTestCase):

    def setUp(self):
        self.ts           = datetime.now()
        self.contact_list = ContactList(nicks=['Alice'])
        self.window_list  = WindowList(contact_list=self.contact_list)
        self.group_list   = GroupList()
        self.args         = self.ts, self.window_list, self.contact_list
        self.window       = self.window_list.get_window(nick_to_pub_key("Alice"))
        self.window.type  = WIN_TYPE_CONTACT

    def test_unknown_account_raises_fr(self):
        # Setup
        cmd_data = nick_to_pub_key("Bob") + b'Bob_'

        # Test
        trunc_addr = nick_to_short_address('Bob')
        self.assert_fr(f"Error: Receiver has no contact '{trunc_addr}' to rename.", ch_nick, cmd_data, *self.args)

    def test_nick_change(self):
        # Setup
        cmd_data = nick_to_pub_key("Alice") + b'Alice_'

        # Test
        self.assertIsNone(ch_nick(cmd_data, *self.args))
        self.assertEqual(self.contact_list.get_contact_by_pub_key(nick_to_pub_key("Alice")).nick, 'Alice_')
        self.assertEqual(self.window.name, 'Alice_')
示例#3
0
class TestChangeNick(TFCTestCase):

    def setUp(self) -> None:
        """Pre-test actions."""
        self.contact_list = ContactList(nicks=['Alice'])
        self.group_list   = GroupList()
        self.settings     = Settings()
        self.queues       = gen_queue_dict()
        self.args         = self.contact_list, self.group_list, self.settings, self.queues

    def tearDown(self) -> None:
        """Post-test actions."""
        tear_queues(self.queues)

    def test_missing_nick_raises_soft_error(self) -> None:
        self.assert_se("Error: No nick specified.",
                       change_nick, UserInput("nick "), TxWindow(type=WIN_TYPE_CONTACT), *self.args)

    def test_invalid_nick_raises_soft_error(self) -> None:
        # Setup
        window = TxWindow(type=WIN_TYPE_CONTACT,
                          contact=create_contact('Bob'))

        # Test
        self.assert_se("Error: Nick must be printable.",
                       change_nick, UserInput("nick Alice\x01"), window, *self.args)

    def test_no_contact_raises_soft_error(self) -> None:
        # Setup
        window = TxWindow(type=WIN_TYPE_CONTACT,
                          contact=create_contact('Bob'))
        window.contact = None

        # Test
        self.assert_se("Error: Window does not have contact.",
                       change_nick, UserInput("nick Alice\x01"), window, *self.args)

    def test_successful_nick_change(self) -> None:
        # Setup
        window = TxWindow(name='Alice',
                          type=WIN_TYPE_CONTACT,
                          contact=self.contact_list.get_contact_by_address_or_nick('Alice'))

        # Test
        self.assertIsNone(change_nick(UserInput("nick Alice_"), window, *self.args))
        self.assertEqual(self.contact_list.get_contact_by_pub_key(nick_to_pub_key('Alice')).nick, 'Alice_')

    @mock.patch('time.sleep', return_value=None)
    def test_successful_group_nick_change(self, _: Any) -> None:
        # Setup
        group      = create_group('test_group')
        user_input = UserInput("nick group2")
        window     = TxWindow(name ='test_group',
                              type =WIN_TYPE_GROUP,
                              group=group,
                              uid  =group.group_id)

        # Test
        self.assert_se("Renamed group 'test_group' to 'group2'.", change_nick, user_input, window, *self.args)
        self.assertEqual(window.group.name, 'group2')
示例#4
0
class TestAddNewContact(TFCTestCase):

    def setUp(self) -> None:
        """Pre-test actions."""
        self.contact_list  = ContactList(nicks=[LOCAL_ID])
        self.group_list    = GroupList()
        self.settings      = Settings(disable_gui_dialog=True)
        self.queues        = gen_queue_dict()
        self.onion_service = OnionService()
        self.args          = self.contact_list, self.group_list, self.settings, self.queues, self.onion_service

    def tearDown(self) -> None:
        """Post-test actions."""
        with ignored(OSError):
            os.remove(f'v4dkh.psk - Give to hpcra')
        tear_queues(self.queues)

    def test_adding_new_contact_during_traffic_masking_raises_soft_error(self) -> None:
        # Setup
        self.settings.traffic_masking = True

        # Test
        self.assert_se("Error: Command is disabled during traffic masking.", add_new_contact, *self.args)

    def test_contact_list_full_raises_soft_error(self) -> None:
        # Setup
        contact_list               = ContactList(nicks=[str(n) for n in range(50)])
        self.contact_list.contacts = contact_list.contacts

        # Test
        self.assert_se("Error: TFC settings only allow 50 accounts.", add_new_contact, *self.args)

    @mock.patch('builtins.input', side_effect=[nick_to_onion_address("Bob"), 'Bob', '', VALID_ECDHE_PUB_KEY, 'Yes',
                                               blake2b(nick_to_pub_key('Bob'), digest_size=CONFIRM_CODE_LENGTH).hex()])
    @mock.patch('shutil.get_terminal_size', return_value=[200, 200])
    @mock.patch('time.sleep',               return_value=None)
    def test_default_nick_ecdhe(self, *_: Any) -> None:
        self.assertIsNone(add_new_contact(*self.args))
        contact = self.contact_list.get_contact_by_address_or_nick("Bob")
        self.assertEqual(contact.nick, 'Bob')
        self.assertNotEqual(contact.tx_fingerprint, bytes(FINGERPRINT_LENGTH))

    @mock.patch('src.transmitter.key_exchanges.ARGON2_PSK_MEMORY_COST',  200)
    @mock.patch('src.common.statics.MIN_KEY_DERIVATION_TIME', 0.1)
    @mock.patch('src.common.statics.MAX_KEY_DERIVATION_TIME', 1.0)
    @mock.patch('builtins.input',  side_effect=[nick_to_onion_address("Alice"), 'Alice_', 'psk', '.', '', 'ff', 'fc'])
    @mock.patch('getpass.getpass', return_value='test_password')
    @mock.patch('time.sleep',      return_value=None)
    def test_standard_nick_psk_kex(self, *_: Any) -> None:
        self.onion_service.account = nick_to_onion_address('Bob').encode()
        self.assertIsNone(add_new_contact(*self.args))
        contact = self.contact_list.get_contact_by_pub_key(nick_to_pub_key("Alice"))
        self.assertEqual(contact.nick, 'Alice_')
        self.assertEqual(contact.tx_fingerprint, bytes(FINGERPRINT_LENGTH))

    @mock.patch('time.sleep',     return_value=None)
    @mock.patch('builtins.input', side_effect=KeyboardInterrupt)
    def test_keyboard_interrupt_raises_soft_error(self, *_: Any) -> None:
        self.assert_se('Contact creation aborted.', add_new_contact, *self.args)
示例#5
0
class TestPSK(TFCTestCase):

    def setUp(self) -> None:
        """Pre-test actions."""
        self.unit_test_dir = cd_unit_test()
        self.contact_list  = ContactList()
        self.settings      = Settings(disable_gui_dialog=True)
        self.queues        = gen_queue_dict()
        self.onion_service = OnionService()
        self.args          = self.contact_list, self.settings, self.onion_service, self.queues

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

        with ignored(OSError):
            os.remove(f"{self.onion_service.user_short_address}.psk - Give to {nick_to_short_address('Alice')}")

        tear_queues(self.queues)

    @mock.patch('builtins.input',  side_effect=['/root/', '.', 'fc'])
    @mock.patch('time.sleep',      return_value=None)
    @mock.patch('getpass.getpass', return_value='test_password')
    @mock.patch('src.transmitter.key_exchanges.ARGON2_PSK_MEMORY_COST', 1000)
    @mock.patch('src.transmitter.key_exchanges.ARGON2_PSK_TIME_COST',   1)
    def test_psk_creation(self, *_: Any) -> None:
        self.assertIsNone(create_pre_shared_key(nick_to_pub_key("Alice"), 'Alice', *self.args))

        contact = self.contact_list.get_contact_by_pub_key(nick_to_pub_key("Alice"))

        self.assertEqual(contact.onion_pub_key,  nick_to_pub_key("Alice"))
        self.assertEqual(contact.nick,           'Alice')
        self.assertEqual(contact.tx_fingerprint, bytes(FINGERPRINT_LENGTH))
        self.assertEqual(contact.rx_fingerprint, bytes(FINGERPRINT_LENGTH))
        self.assertEqual(contact.kex_status,     KEX_STATUS_NO_RX_PSK)

        self.assertFalse(contact.log_messages)
        self.assertFalse(contact.file_reception)
        self.assertTrue(contact.notifications)

        cmd, account, tx_key, rx_key, tx_hek, rx_hek = self.queues[KEY_MANAGEMENT_QUEUE].get()

        self.assertEqual(cmd,     KDB_ADD_ENTRY_HEADER)
        self.assertEqual(account, nick_to_pub_key("Alice"))

        for key in [tx_key, rx_key, tx_hek, rx_hek]:
            self.assertIsInstance(key, bytes)
            self.assertEqual(len(key), SYMMETRIC_KEY_LENGTH)

        self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 1)
        self.assertTrue(os.path.isfile(
            f"{self.onion_service.user_short_address}.psk - Give to {nick_to_short_address('Alice')}"))

    @mock.patch('time.sleep',      return_value=None)
    @mock.patch('getpass.getpass', side_effect=KeyboardInterrupt)
    def test_keyboard_interrupt_raises_se(self, *_: Any) -> None:
        self.assert_se("PSK generation aborted.", create_pre_shared_key, nick_to_pub_key("Alice"), 'Alice', *self.args)
示例#6
0
class TestKeyExPSKTx(TFCTestCase):

    def setUp(self):
        """Pre-test actions."""
        self.ts           = datetime.fromtimestamp(1502750000)
        self.window_list  = WindowList(nicks=[LOCAL_ID])
        self.contact_list = ContactList()
        self.key_list     = KeyList()
        self.settings     = Settings()
        self.packet       = (nick_to_pub_key("Alice")
                             + SYMMETRIC_KEY_LENGTH * b'\x01'
                             + bytes(SYMMETRIC_KEY_LENGTH)
                             + SYMMETRIC_KEY_LENGTH * b'\x02'
                             + bytes(SYMMETRIC_KEY_LENGTH)
                             + str_to_bytes('Alice'))
        self.args         = self.packet, self.ts, self.window_list, self.contact_list, self.key_list, self.settings

    @mock.patch('time.sleep', return_value=None)
    def test_invalid_nick_raises_fr(self, _):
        self.packet = (nick_to_pub_key("Alice")
                       + SYMMETRIC_KEY_LENGTH * b'\x01'
                       + bytes(SYMMETRIC_KEY_LENGTH)
                       + SYMMETRIC_KEY_LENGTH * b'\x02'
                       + bytes(SYMMETRIC_KEY_LENGTH)
                       + UNDECODABLE_UNICODE)
        self.args   = self.packet, self.ts, self.window_list, self.contact_list, self.key_list, self.settings

        self.assert_fr("Error: Received invalid contact data", key_ex_psk_tx, *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_add_psk_tx_keys(self, _):
        self.assertIsNone(key_ex_psk_tx(*self.args))

        keyset = self.key_list.get_keyset(nick_to_pub_key("Alice"))
        self.assertIsInstance(keyset, KeySet)

        self.assertEqual(keyset.onion_pub_key, nick_to_pub_key("Alice"))
        self.assertEqual(keyset.tx_mk,         SYMMETRIC_KEY_LENGTH * b'\x01')
        self.assertEqual(keyset.rx_mk,         bytes(SYMMETRIC_KEY_LENGTH))
        self.assertEqual(keyset.tx_hk,         SYMMETRIC_KEY_LENGTH * b'\x02')
        self.assertEqual(keyset.rx_hk,         bytes(SYMMETRIC_KEY_LENGTH))

        contact = self.contact_list.get_contact_by_pub_key(nick_to_pub_key("Alice"))
        self.assertIsInstance(contact, Contact)

        self.assertEqual(contact.onion_pub_key,  nick_to_pub_key("Alice"))
        self.assertEqual(contact.nick,           'Alice')
        self.assertEqual(contact.tx_fingerprint, bytes(FINGERPRINT_LENGTH))
        self.assertEqual(contact.rx_fingerprint, bytes(FINGERPRINT_LENGTH))
示例#7
0
class TestKeyExchange(TFCTestCase):
    def setUp(self):
        self.contact_list = ContactList()
        self.settings = Settings()
        self.queues = gen_queue_dict()
        self.args = self.contact_list, self.settings, self.queues

    def tearDown(self):
        tear_queues(self.queues)

    @mock.patch('shutil.get_terminal_size', return_value=[200, 200])
    @mock.patch('builtins.input',
                return_value=b58encode(bytes(TFC_PUBLIC_KEY_LENGTH),
                                       public_key=True))
    def test_zero_public_key_raises_fr(self, *_):
        self.assert_fr("Error: Zero public key", start_key_exchange,
                       nick_to_pub_key("Alice"), 'Alice', *self.args)

    @mock.patch('shutil.get_terminal_size', return_value=[200, 200])
    @mock.patch('builtins.input',
                return_value=b58encode((TFC_PUBLIC_KEY_LENGTH - 1) * b'a',
                                       public_key=True))
    def test_invalid_public_key_length_raises_fr(self, *_):
        self.assert_fr("Error: Invalid public key length", start_key_exchange,
                       nick_to_pub_key("Alice"), 'Alice', *self.args)

    @mock.patch(
        'builtins.input',
        side_effect=[
            '',  # Empty message should resend key
            VALID_ECDHE_PUB_KEY[:-1],  # Short key should fail
            VALID_ECDHE_PUB_KEY + 'a',  # Long key should fail
            VALID_ECDHE_PUB_KEY[:-1] + 'a',  # Invalid key should fail
            VALID_ECDHE_PUB_KEY,  # Correct key
            'No'
        ])  # Fingerprint mismatch)
    @mock.patch('time.sleep', return_value=None)
    @mock.patch('shutil.get_terminal_size', return_value=[200, 200])
    def test_fingerprint_mismatch_raises_fr(self, *_):
        self.assert_fr("Error: Fingerprint mismatch", start_key_exchange,
                       nick_to_pub_key("Alice"), 'Alice', *self.args)

    @mock.patch(
        'builtins.input',
        side_effect=[
            '',  # Resend public key
            VALID_ECDHE_PUB_KEY,  # Correct key
            'Yes',  # Fingerprint match
            '',  # Resend contact data
            'ff',  # Invalid confirmation code
            blake2b(nick_to_pub_key('Alice'),
                    digest_size=CONFIRM_CODE_LENGTH).hex()
        ])
    @mock.patch('shutil.get_terminal_size', return_value=[200, 200])
    @mock.patch('time.sleep', return_value=None)
    def test_successful_exchange(self, *_):
        self.assertIsNone(
            start_key_exchange(nick_to_pub_key("Alice"), 'Alice', *self.args))

        contact = self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key("Alice"))
        self.assertEqual(contact.onion_pub_key, nick_to_pub_key("Alice"))
        self.assertEqual(contact.nick, 'Alice')
        self.assertEqual(contact.kex_status, KEX_STATUS_VERIFIED)
        self.assertIsInstance(contact.tx_fingerprint, bytes)
        self.assertIsInstance(contact.rx_fingerprint, bytes)
        self.assertEqual(len(contact.tx_fingerprint), FINGERPRINT_LENGTH)
        self.assertEqual(len(contact.rx_fingerprint), FINGERPRINT_LENGTH)
        self.assertFalse(contact.log_messages)
        self.assertFalse(contact.file_reception)
        self.assertTrue(contact.notifications)

        self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 2)
        self.assertEqual(self.queues[RELAY_PACKET_QUEUE].qsize(), 2)

        cmd, account, tx_key, rx_key, tx_hek, rx_hek = self.queues[
            KEY_MANAGEMENT_QUEUE].get()

        self.assertEqual(cmd, KDB_ADD_ENTRY_HEADER)
        self.assertEqual(account, nick_to_pub_key("Alice"))
        self.assertEqual(len(tx_key), SYMMETRIC_KEY_LENGTH)

        for key in [tx_key, rx_key, tx_hek, rx_hek]:
            self.assertIsInstance(key, bytes)
            self.assertEqual(len(key), SYMMETRIC_KEY_LENGTH)

    @mock.patch(
        'builtins.input',
        side_effect=[
            '',  # Resend public key
            VALID_ECDHE_PUB_KEY,  # Correct key
            KeyboardInterrupt,  # Skip fingerprint verification
            '',  # Manual proceed for warning message
            blake2b(nick_to_pub_key('Alice'),
                    digest_size=CONFIRM_CODE_LENGTH).hex()
        ])
    @mock.patch('time.sleep', return_value=None)
    @mock.patch('shutil.get_terminal_size', return_value=[200, 200])
    def test_successful_exchange_skip_fingerprint_verification(self, *_):
        self.assertIsNone(
            start_key_exchange(nick_to_pub_key("Alice"), 'Alice', *self.args))

        contact = self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key("Alice"))
        self.assertEqual(contact.onion_pub_key, nick_to_pub_key("Alice"))
        self.assertEqual(contact.nick, 'Alice')
        self.assertEqual(contact.kex_status, KEX_STATUS_UNVERIFIED)

    @mock.patch(
        'os.getrandom',
        side_effect=[SYMMETRIC_KEY_LENGTH * b'a', SYMMETRIC_KEY_LENGTH * b'a'])
    @mock.patch('builtins.input',
                side_effect=[
                    KeyboardInterrupt, VALID_ECDHE_PUB_KEY, 'Yes',
                    blake2b(nick_to_pub_key('Alice'),
                            digest_size=CONFIRM_CODE_LENGTH).hex()
                ])
    @mock.patch('time.sleep', return_value=None)
    @mock.patch('shutil.get_terminal_size', return_value=[200, 200])
    def test_successful_exchange_with_previous_key(self, *_):
        # Test caching of private key
        self.assert_fr("Key exchange interrupted.", start_key_exchange,
                       nick_to_pub_key('Alice'), 'Alice', *self.args)

        alice = self.contact_list.get_contact_by_address_or_nick('Alice')
        self.assertEqual(alice.kex_status, KEX_STATUS_PENDING)

        # Test re-using private key
        self.assertIsNone(
            start_key_exchange(nick_to_pub_key('Alice'), 'Alice', *self.args))
        self.assertIsNone(alice.tfc_private_key)
        self.assertEqual(alice.kex_status, KEX_STATUS_VERIFIED)
示例#8
0
class TestProcessMessagePacket(TFCTestCase):

    def setUp(self) -> None:
        """Pre-test actions."""
        self.unit_test_dir = cd_unit_test()

        self.msg = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean condimentum consectetur purus quis"
                    " dapibus. Fusce venenatis lacus ut rhoncus faucibus. Cras sollicitudin commodo sapien, sed bibendu"
                    "m velit maximus in. Aliquam ac metus risus. Sed cursus ornare luctus. Integer aliquet lectus id ma"
                    "ssa blandit imperdiet. Ut sed massa eget quam facilisis rutrum. Mauris eget luctus nisl. Sed ut el"
                    "it iaculis, faucibus lacus eget, sodales magna. Nunc sed commodo arcu. In hac habitasse platea dic"
                    "tumst. Integer luctus aliquam justo, at vestibulum dolor iaculis ac. Etiam laoreet est eget odio r"
                    "utrum, vel malesuada lorem rhoncus. Cras finibus in neque eu euismod. Nulla facilisi. Nunc nec ali"
                    "quam quam, quis ullamcorper leo. Nunc egestas lectus eget est porttitor, in iaculis felis sceleris"
                    "que. In sem elit, fringilla id viverra commodo, sagittis varius purus. Pellentesque rutrum loborti"
                    "s neque a facilisis. Mauris id tortor placerat, aliquam dolor ac, venenatis arcu.")

        self.ts         = datetime.now()
        self.master_key = MasterKey()
        self.settings   = Settings(log_file_masking=True)
        self.file_name  = f'{DIR_USER_DATA}{self.settings.software_operation}_logs'

        self.contact_list = ContactList(nicks=['Alice', 'Bob', 'Charlie', LOCAL_ID])
        self.key_list     = KeyList(    nicks=['Alice', 'Bob', 'Charlie', LOCAL_ID])
        self.group_list   = GroupList( groups=['test_group'])
        self.packet_list  = PacketList(contact_list=self.contact_list, settings=self.settings)
        self.window_list  = WindowList(contact_list=self.contact_list, settings=self.settings,
                                       group_list=self.group_list, packet_list=self.packet_list)
        self.group_id     = group_name_to_group_id('test_group')
        self.file_keys    = dict()

        self.log_file         = f'{DIR_USER_DATA}{self.settings.software_operation}_logs'
        self.tfc_log_database = MessageLog(self.log_file, self.master_key.master_key)

        self.group_list.get_group('test_group').log_messages = True
        self.args = (self.window_list, self.packet_list, self.contact_list, self.key_list,
                     self.group_list, self.settings, self.file_keys, self.tfc_log_database)

        ensure_dir(DIR_USER_DATA)

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

    # Invalid packets
    @mock.patch('time.sleep', return_value=None)
    def test_invalid_origin_header_raises_soft_error(self, _: Any) -> None:
        # Setup
        invalid_origin_header = b'e'
        packet = nick_to_pub_key('Alice') + invalid_origin_header + MESSAGE_LENGTH * b'm'

        # Test
        self.assert_se("Error: Received packet had an invalid origin-header.",
                       process_message_packet, self.ts, packet, *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_masqueraded_command_raises_soft_error(self, _: Any) -> None:
        for origin_header in [ORIGIN_USER_HEADER, ORIGIN_CONTACT_HEADER]:
            # Setup
            packet = LOCAL_PUBKEY + origin_header + MESSAGE_LENGTH * b'm'

            # Test
            self.assert_se("Warning! Received packet masqueraded as a command.",
                           process_message_packet, self.ts, packet, *self.args)

    # Private messages
    @mock.patch('time.sleep', return_value=None)
    def test_private_msg_from_contact(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(MESSAGE, self.msg, origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'))

        # Test
        for p in assembly_ct_list:
            self.assertIsNone(process_message_packet(self.ts, p, *self.args))

    @mock.patch('time.sleep', return_value=None)
    def test_private_msg_from_user(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(MESSAGE, self.msg, origin_header=ORIGIN_USER_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'))

        # Test
        for p in assembly_ct_list:
            self.assertIsNone(process_message_packet(self.ts, p, *self.args))

    # Whispered messages
    @mock.patch('time.sleep', return_value=None)
    def test_whisper_msg_from_contact(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(MESSAGE, self.msg, origin_header=ORIGIN_CONTACT_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_packet(self.ts, p, *self.args))

        for p in assembly_ct_list[-1:]:
            self.assert_se("Whisper message complete.",
                           process_message_packet, self.ts, p, *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_whisper_msg_from_user(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(MESSAGE, self.msg, 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_packet(self.ts, p, *self.args))

        for p in assembly_ct_list[-1:]:
            self.assert_se("Whisper message complete.", process_message_packet, self.ts, p, *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_empty_whisper_msg_from_user(self, _: Any) -> None:
        # 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_packet(self.ts, p, *self.args))

        for p in assembly_ct_list[-1:]:
            self.assert_se("Whisper message complete.", process_message_packet, self.ts, p, *self.args)

    # File key messages
    @mock.patch('time.sleep', return_value=None)
    def test_user_origin_raises_soft_error(self, _: Any) -> None:
        assembly_ct_list = assembly_packet_creator(MESSAGE, ' ', origin_header=ORIGIN_USER_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'),
                                                   message_header=FILE_KEY_HEADER)

        for p in assembly_ct_list[-1:]:
            self.assert_se("File key message from the user.", process_message_packet, self.ts, p, *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_invalid_file_key_data_raises_soft_error(self, _: Any) -> None:
        assembly_ct_list = assembly_packet_creator(MESSAGE, ' ', origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'),
                                                   message_header=FILE_KEY_HEADER)

        for p in assembly_ct_list[-1:]:
            self.assert_se("Error: Received an invalid file key message.",
                           process_message_packet, self.ts, p, *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_too_large_file_key_data_raises_soft_error(self, _: Any) -> None:
        assembly_ct_list = assembly_packet_creator(MESSAGE, base64.b85encode(BLAKE2_DIGEST_LENGTH * b'a'
                                                                             + SYMMETRIC_KEY_LENGTH * b'b'
                                                                             + b'a').decode(),
                                                   origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'),
                                                   message_header=FILE_KEY_HEADER)

        for p in assembly_ct_list[-1:]:
            self.assert_se("Error: Received an invalid file key message.",
                           process_message_packet, self.ts, p, *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_valid_file_key_message(self, _: Any) -> None:
        assembly_ct_list = assembly_packet_creator(MESSAGE, base64.b85encode(BLAKE2_DIGEST_LENGTH * b'a'
                                                                             + SYMMETRIC_KEY_LENGTH * b'b').decode(),
                                                   origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'),
                                                   message_header=FILE_KEY_HEADER)
        for p in assembly_ct_list[-1:]:
            self.assert_se("Received file decryption key from Alice",
                           process_message_packet, self.ts, p, *self.args)

    # Group messages
    @mock.patch('time.sleep', return_value=None)
    def test_invalid_message_header_raises_soft_error(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(MESSAGE, 'test_message', origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'),
                                                   message_header=b'Z')

        # Test
        self.assert_se("Error: Message from contact had an invalid header.",
                       process_message_packet, self.ts, assembly_ct_list[0], *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_invalid_window_raises_soft_error(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(MESSAGE, 'test_message', origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'),
                                                   group_id=self.group_id)

        self.group_list.get_group('test_group').group_id = GROUP_ID_LENGTH * b'a'

        # Test
        self.assert_se("Error: Received message to an unknown group.",
                       process_message_packet, self.ts, assembly_ct_list[0], *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_invalid_message_raises_soft_error(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(MESSAGE, ' ', origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'),
                                                   group_id=self.group_id, tamper_plaintext=True)

        # Test
        self.assert_se("Error: Received an invalid group message.",
                       process_message_packet, self.ts, assembly_ct_list[0], *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_invalid_whisper_header_raises_soft_error(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(MESSAGE, '', origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'),
                                                   whisper_header=b'', message_header=b'')

        # Test
        self.assert_se("Error: Message from contact had an invalid whisper header.",
                       process_message_packet, self.ts, assembly_ct_list[0], *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_contact_not_in_group_raises_soft_error(self, _: Any) -> None:
        # Setup

        assembly_ct_list = assembly_packet_creator(MESSAGE, 'test_message', origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, group_id=self.group_id,
                                                   onion_pub_key=nick_to_pub_key('Charlie'))

        # Test
        self.assert_se("Error: Account is not a member of the group.",
                       process_message_packet, self.ts, assembly_ct_list[0], *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_normal_group_msg_from_contact(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(MESSAGE, self.msg, origin_header=ORIGIN_CONTACT_HEADER,
                                                   group_id=self.group_id, encrypt_packet=True,
                                                   onion_pub_key=nick_to_pub_key('Alice'))

        for p in assembly_ct_list:
            self.assertIsNone(process_message_packet(self.ts, p, *self.args))

    @mock.patch('time.sleep', return_value=None)
    def test_normal_group_msg_from_user(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(MESSAGE, self.msg, origin_header=ORIGIN_USER_HEADER,
                                                   group_id=self.group_id, encrypt_packet=True,
                                                   onion_pub_key=nick_to_pub_key('Alice'))

        for p in assembly_ct_list:
            self.assertIsNone(process_message_packet(self.ts, p, *self.args))

    # Files
    @mock.patch('time.sleep', return_value=None)
    def test_file(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(FILE, origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'))

        # Test
        for p in assembly_ct_list[:-1]:
            self.assertIsNone(process_message_packet(self.ts, p, *self.args))

        for p in assembly_ct_list[-1:]:
            self.assert_se("File storage complete.",
                           process_message_packet, self.ts, p, *self.args)

    @mock.patch('time.sleep', return_value=None)
    def test_file_when_reception_is_disabled(self, _: Any) -> None:
        # Setup
        assembly_ct_list = assembly_packet_creator(FILE, origin_header=ORIGIN_CONTACT_HEADER,
                                                   encrypt_packet=True, onion_pub_key=nick_to_pub_key('Alice'))

        self.contact_list.get_contact_by_pub_key(nick_to_pub_key('Alice')).file_reception = False

        # Test
        self.assert_se("Alert! File transmission from Alice but reception is disabled.",
                       process_message_packet, self.ts, assembly_ct_list[0], *self.args)
示例#9
0
class TestTxWindow(TFCTestCase):
    def setUp(self) -> None:
        """Pre-test actions."""
        self.contact_list = ContactList(['Alice', 'Bob', LOCAL_ID])
        self.group_list = GroupList(groups=['test_group', 'test_group_2'])
        self.window = TxWindow(self.contact_list, self.group_list)
        self.window.group = self.group_list.get_group('test_group')
        self.window.type = WIN_TYPE_GROUP
        self.settings = Settings()
        self.queues = gen_queue_dict()
        self.onion_service = OnionService()
        self.gateway = Gateway()
        self.args = self.settings, self.queues, self.onion_service, self.gateway

    def tearDown(self) -> None:
        """Post-test actions."""
        tear_queues(self.queues)

    def test_window_iterates_over_contacts(self) -> None:
        # Setup
        self.window.window_contacts = self.contact_list.contacts

        # Test
        for c in self.window:
            self.assertIsInstance(c, Contact)

    def test_len_returns_number_of_contacts_in_window(self) -> None:
        # Setup
        self.window.window_contacts = [
            self.contact_list.get_contact_by_pub_key(nick_to_pub_key('Alice')),
            self.contact_list.get_contact_by_pub_key(nick_to_pub_key('Bob'))
        ]

        # Test
        self.assertEqual(len(self.window), 2)

    def test_group_window_change_during_traffic_masking_raises_soft_error(
            self) -> None:
        # Setup
        self.settings.traffic_masking = True
        self.window.uid = 'test_group'

        # Test
        self.assert_se("Error: Can't change window during traffic masking.",
                       self.window.select_tx_window,
                       *self.args,
                       selection='test_group_2',
                       cmd=True)

    def test_contact_window_change_during_traffic_masking_raises_soft_error(
            self) -> None:
        # Setup
        self.settings.traffic_masking = True
        self.window.uid = nick_to_pub_key("Alice")

        # Test
        self.assert_se("Error: Can't change window during traffic masking.",
                       self.window.select_tx_window,
                       *self.args,
                       selection=nick_to_onion_address("Bob"),
                       cmd=True)

    def test_contact_window_reload_during_traffic_masking(self) -> None:
        # Setup
        self.settings.traffic_masking = True
        self.window.uid = nick_to_pub_key("Alice")

        # Test
        self.assertIsNone(
            self.window.select_tx_window(
                *self.args, selection=nick_to_onion_address("Alice"),
                cmd=True))
        self.assertEqual(self.window.uid, nick_to_pub_key("Alice"))

    def test_group_window_reload_during_traffic_masking(self) -> None:
        # Setup
        self.settings.traffic_masking = True
        self.window.name = 'test_group'
        self.window.uid = group_name_to_group_id('test_group')

        # Test
        self.assertIsNone(
            self.window.select_tx_window(*self.args,
                                         selection='test_group',
                                         cmd=True))
        self.assertEqual(self.window.uid, group_name_to_group_id('test_group'))

    def test_invalid_selection_raises_soft_error(self) -> None:
        # Setup
        self.window.uid = nick_to_pub_key("Alice")

        # Test
        self.assert_se("Error: No contact/group was found.",
                       self.window.select_tx_window,
                       *self.args,
                       selection=nick_to_onion_address("Charlie"),
                       cmd=True)

    @mock.patch('builtins.input', return_value=nick_to_onion_address("Bob"))
    def test_window_selection_during_traffic_masking(self, *_: Any) -> None:
        # Setup
        self.settings.traffic_masking = True
        self.window.uid = None

        # Test
        self.assertIsNone(self.window.select_tx_window(*self.args))
        self.assertEqual(self.queues[WINDOW_SELECT_QUEUE].qsize(), 1)

    @mock.patch('builtins.input', return_value=nick_to_onion_address("Bob"))
    def test_contact_window_selection_from_input(self, *_: Any) -> None:
        # Setup
        self.window.uid = None

        # Test
        self.assertIsNone(self.window.select_tx_window(*self.args))
        self.assertEqual(self.window.uid, nick_to_pub_key("Bob"))

    def test_group_window_selection_from_command(self) -> None:
        # Setup
        self.window.uid = None

        self.assertIsNone(
            self.window.select_tx_window(*self.args,
                                         selection='test_group',
                                         cmd=True))
        self.assertEqual(self.window.uid, group_name_to_group_id('test_group'))

    def test_deselect_window(self) -> None:
        # Setup
        self.window.window_contacts = self.contact_list.contacts
        self.window.contact = self.contact_list.get_contact_by_address_or_nick(
            "Bob")
        self.window.name = 'Bob'
        self.window.type = WIN_TYPE_CONTACT
        self.window.uid = nick_to_pub_key("Bob")

        # Test
        self.assertIsNone(self.window.deselect())
        self.assertIsNone(self.window.contact)
        self.assertEqual(self.window.name, '')
        self.assertEqual(self.window.type, '')
        self.assertEqual(self.window.uid, b'')

    def test_is_selected(self) -> None:
        self.window.name = ''
        self.assertFalse(self.window.is_selected())

        self.window.name = nick_to_pub_key("Bob")
        self.assertTrue(self.window.is_selected())

    def test_update_log_messages_for_contact(self) -> None:
        # Setup
        self.window.type = WIN_TYPE_CONTACT
        self.window.log_messages = None
        self.window.contact = self.contact_list.get_contact_by_address_or_nick(
            'Alice')
        self.window.contact.log_messages = False

        # Test
        self.assertIsNone(self.window.update_log_messages())
        self.assertFalse(self.window.log_messages)

    def test_update_log_messages_for_group(self) -> None:
        # Setup
        self.window.type = WIN_TYPE_GROUP
        self.window.log_messages = None
        self.window.group = self.group_list.get_group('test_group')
        self.window.group.log_messages = False

        # Test
        self.assertIsNone(self.window.update_log_messages())
        self.assertFalse(self.window.log_messages)

    def test_update_group_win_members_if_group_is_available(self) -> None:
        # Setup
        self.window.window_contacts = []
        self.window.group = None
        self.window.group_id = group_name_to_group_id('test_group')
        self.window.name = 'test_group'
        self.window.type = WIN_TYPE_GROUP

        # Test
        self.assertIsNone(self.window.update_window(self.group_list))
        self.assertEqual(self.window.group,
                         self.group_list.get_group('test_group'))
        self.assertEqual(self.window.window_contacts,
                         self.window.group.members)

    def test_window_contact_is_reloaded_when_contact_is_active(self) -> None:
        # Setup
        self.window.type = WIN_TYPE_CONTACT
        self.window.contact = create_contact('Alice')
        self.window.window_contacts = [self.window.contact]
        self.assertIsNot(
            self.window.contact,
            self.window.contact_list.get_contact_by_pub_key(
                nick_to_pub_key('Alice')))
        self.assertIsNot(
            self.window.window_contacts[0],
            self.window.contact_list.get_contact_by_pub_key(
                nick_to_pub_key('Alice')))

        # Test
        self.assertIsNone(self.window.update_window(self.group_list))
        self.assertIs(
            self.window.contact,
            self.window.contact_list.get_contact_by_pub_key(
                nick_to_pub_key('Alice')))
        self.assertIs(
            self.window.window_contacts[0],
            self.window.contact_list.get_contact_by_pub_key(
                nick_to_pub_key('Alice')))

    def test_deactivate_window_if_group_is_not_available(self) -> None:
        # Setup
        self.window.window_contacts = []
        self.window.group = None
        self.window.name = 'test_group_3'
        self.window.type = WIN_TYPE_GROUP

        # Test
        self.assertIsNone(self.window.update_window(self.group_list))
        self.assertIsNone(self.window.contact)
        self.assertEqual(self.window.name, '')
        self.assertEqual(self.window.type, '')
        self.assertEqual(self.window.uid, b'')

    @mock.patch('time.sleep', return_value=None)
    @mock.patch('builtins.input',
                side_effect=[
                    'Alice', VALID_ECDHE_PUB_KEY, 'yes',
                    blake2b(nick_to_pub_key('Alice'),
                            digest_size=CONFIRM_CODE_LENGTH).hex()
                ])
    @mock.patch('shutil.get_terminal_size', return_value=[200, 200])
    def test_selecting_pending_contact_starts_key_exchange(self,
                                                           *_: Any) -> None:
        # Setup
        alice = self.contact_list.get_contact_by_address_or_nick('Alice')
        bob = self.contact_list.get_contact_by_address_or_nick('Bob')
        alice.kex_status = KEX_STATUS_PENDING
        bob.kex_status = KEX_STATUS_PENDING

        # Test
        self.assertIsNone(self.window.select_tx_window(*self.args))
        self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 2)
        self.assertEqual(self.queues[WINDOW_SELECT_QUEUE].qsize(), 0)
        self.assertEqual(alice.kex_status, KEX_STATUS_VERIFIED)

    @mock.patch('time.sleep', return_value=None)
    @mock.patch('builtins.input',
                side_effect=[
                    '/add',
                    nick_to_onion_address('Alice'), 'Alice', '',
                    VALID_ECDHE_PUB_KEY, 'yes',
                    blake2b(nick_to_pub_key('Alice'),
                            digest_size=CONFIRM_CODE_LENGTH).hex()
                ])
    @mock.patch('shutil.get_terminal_size', return_value=[200, 200])
    def test_adding_new_contact_from_contact_selection(self, *_: Any) -> None:
        # Setup
        alice = self.contact_list.get_contact_by_address_or_nick('Alice')
        alice.kex_status = KEX_STATUS_PENDING

        # Test
        self.assert_se('New contact added.', self.window.select_tx_window,
                       *self.args)
        self.assertEqual(self.queues[COMMAND_PACKET_QUEUE].qsize(), 1)
        self.assertEqual(self.queues[WINDOW_SELECT_QUEUE].qsize(), 0)
        self.assertEqual(alice.kex_status, KEX_STATUS_VERIFIED)

    @mock.patch('time.sleep', return_value=None)
    @mock.patch('builtins.input', side_effect=['/rm '])
    def test_missing_account_when_removing_raises_soft_error(self,
                                                             *_: Any) -> None:
        self.assert_se("Error: No account specified.",
                       self.window.select_tx_window, *self.args)

    @mock.patch('time.sleep', return_value=None)
    @mock.patch('builtins.input', side_effect=['/rm Charlie', 'yes'])
    def test_unknown_account_when_removing_raises_soft_error(self,
                                                             *_: Any) -> None:
        self.assert_se("Error: Unknown contact 'Charlie'.",
                       self.window.select_tx_window, *self.args)

    @mock.patch('time.sleep', return_value=None)
    @mock.patch('builtins.input', side_effect=['/rm Alice', 'no'])
    def test_abort_removal_of_contact_form_contact_selection(self,
                                                             *_: Any) -> None:
        self.assert_se("Removal of contact aborted.",
                       self.window.select_tx_window, *self.args)

    @mock.patch('time.sleep', return_value=None)
    @mock.patch('builtins.input', side_effect=['/rm Alice', 'yes'])
    def test_removing_pending_contact_from_contact_selection(self,
                                                             *_: Any) -> None:
        self.assert_se("Removed contact 'Alice'.",
                       self.window.select_tx_window, *self.args)

    @mock.patch('time.sleep', return_value=None)
    @mock.patch('builtins.input', side_effect=['/connect', b'a'.hex()])
    def test_sending_onion_service_data_from_contact_selection(
            self, *_: Any) -> None:
        self.assertIsNone(self.window.select_tx_window(*self.args))
        self.assertEqual(len(self.gateway.packets), 1)

    @mock.patch('time.sleep', return_value=None)
    @mock.patch('builtins.input', side_effect=['/help'])
    def test_invalid_command_raises_soft_error(self, *_: Any) -> None:
        self.assert_se("Error: Invalid command.", self.window.select_tx_window,
                       *self.args)