Пример #1
0
    def setUp(self):
        self.unittest_dir = cd_unittest()
        self.master_key = MasterKey()
        self.settings = Settings()
        self.file_name = f'{DIR_USER_DATA}{self.settings.software_operation}_groups'
        self.contact_list = ContactList(self.master_key, self.settings)
        self.group_list = GroupList(self.master_key, self.settings,
                                    self.contact_list)
        self.nicks = [
            'Alice', 'Bob', 'Charlie', 'David', 'Eric', 'Fido', 'Guido',
            'Heidi', 'Ivan', 'Joana', 'Karol'
        ]
        self.group_names = [
            'test_group_1', 'test_group_2', 'test_group_3', 'test_group_4',
            'test_group_5', 'test_group_6', 'test_group_7', 'test_group_8',
            'test_group_9', 'test_group_10', 'test_group_11'
        ]
        members = list(map(create_contact, self.nicks))

        self.contact_list.contacts = members

        self.group_list.groups = \
            [Group(name         =name,
                   group_id     =group_name_to_group_id(name),
                   log_messages =False,
                   notifications=False,
                   members      =members,
                   settings     =self.settings,
                   store_groups =self.group_list.store_groups)
             for name in self.group_names]

        self.single_member_data_len = (
            GROUP_STATIC_LENGTH + self.settings.max_number_of_group_members *
            ONION_SERVICE_PUBLIC_KEY_LENGTH)
Пример #2
0
    def test_add_contact(self):
        # Setup
        contact1 = Contact('*****@*****.**', '*****@*****.**', 'Alice',
                           32 * b'\x01', 32 * b'\x02', True, True, True)
        contact2 = Contact('*****@*****.**', '*****@*****.**', 'Charlie',
                           32 * b'\x01', 32 * b'\x02', True, True, True)
        settings = Settings(software_operation='ut', m_number_of_accnts=20)
        master_k = MasterKey()
        contact_l = ContactList(master_k, settings)
        contact_l.contacts = [contact1, contact2]

        contact_l.add_contact('*****@*****.**', '*****@*****.**', 'Alice',
                              32 * b'\x03', 32 * b'\x04', True, True, True)
        contact_l.add_contact('*****@*****.**', '*****@*****.**', 'David',
                              32 * b'\x03', 32 * b'\x04', True, True, True)

        contact_l2 = ContactList(master_k, settings)
        c_alice = contact_l2.get_contact('Alice')
        c_david = contact_l2.get_contact('David')

        # Test
        self.assertIsInstance(c_alice, Contact)
        self.assertIsInstance(c_david, Contact)
        self.assertEqual(c_alice.tx_fingerprint, 32 * b'\x03')
        self.assertEqual(c_david.tx_fingerprint, 32 * b'\x03')
Пример #3
0
    def test_add_contact(self):
        tx_fingerprint = FINGERPRINT_LENGTH * b'\x03'
        rx_fingerprint = FINGERPRINT_LENGTH * b'\x04'

        self.assertIsNone(
            self.contact_list.add_contact(
                nick_to_pub_key('Faye'), 'Faye', tx_fingerprint,
                rx_fingerprint, KEX_STATUS_UNVERIFIED,
                self.settings.log_messages_by_default,
                self.settings.accept_files_by_default,
                self.settings.show_notifications_by_default))

        # Test new contact was stored by loading
        # the database from file to another object
        contact_list2 = ContactList(MasterKey(), Settings())
        faye = contact_list2.get_contact_by_pub_key(nick_to_pub_key('Faye'))

        self.assertEqual(len(self.contact_list),
                         len(self.real_contact_list) + 1)
        self.assertIsInstance(faye, Contact)

        self.assertEqual(faye.tx_fingerprint, tx_fingerprint)
        self.assertEqual(faye.rx_fingerprint, rx_fingerprint)
        self.assertEqual(faye.kex_status, KEX_STATUS_UNVERIFIED)

        self.assertEqual(faye.log_messages,
                         self.settings.log_messages_by_default)
        self.assertEqual(faye.file_reception,
                         self.settings.accept_files_by_default)
        self.assertEqual(faye.notifications,
                         self.settings.show_notifications_by_default)
Пример #4
0
 def setUp(self):
     self.master_key = MasterKey()
     self.settings = Settings()
     self.contact_list = ContactList(self.master_key, self.settings)
     self.contact_list.contacts = list(
         map(create_contact,
             ['Alice', 'Benny', 'Charlie', 'David', 'Eric']))
Пример #5
0
 def test_contact_printing(self):
     # Setup
     contact_list = ContactList(MasterKey(), Settings())
     contact_list.contacts = [
         create_contact(n) for n in ['Alice', 'Bob', 'Charlie', 'David']
     ]
     # Teardown
     self.assertIsNone(contact_list.print_contacts())
Пример #6
0
    def test_len_returns_number_of_contacts(self):
        # Setup
        contact = Contact('*****@*****.**', '*****@*****.**', 'Alice',
                          32 * b'\x01', 32 * b'\x02', True, True, True)
        contact_l = ContactList(MasterKey(), Settings())
        contact_l.contacts = 5 * [contact]

        # Test
        self.assertEqual(len(contact_l), 5)
Пример #7
0
    def test_iterate_over_contacts(self):
        # Setup
        contact = Contact('*****@*****.**', '*****@*****.**', 'Alice',
                          32 * b'\x01', 32 * b'\x02', True, True, True)
        contact_l = ContactList(MasterKey(), Settings())
        contact_l.contacts = 5 * [contact]

        # Test
        for c in contact_l:
            self.assertIsInstance(c, Contact)
Пример #8
0
    def test_load_of_modified_database_raises_critical_error(self) -> None:
        self.contact_list.store_contacts()

        # Test reading works normally
        self.assertIsInstance(ContactList(self.master_key, self.settings), ContactList)

        # Test loading of tampered database raises CriticalError
        tamper_file(self.file_name, tamper_size=1)
        with self.assertRaises(SystemExit):
            ContactList(self.master_key, self.settings)
Пример #9
0
 def setUp(self) -> None:
     """Pre-test actions."""
     self.unit_test_dir         = cd_unit_test()
     self.master_key            = MasterKey()
     self.settings              = Settings()
     self.file_name             = f'{DIR_USER_DATA}{self.settings.software_operation}_contacts'
     self.contact_list          = ContactList(self.master_key, self.settings)
     self.full_contact_list     = ['Alice', 'Bob', 'Charlie', 'David', 'Eric', LOCAL_ID]
     self.contact_list.contacts = list(map(create_contact, self.full_contact_list))
     self.real_contact_list     = self.full_contact_list[:]
     self.real_contact_list.remove(LOCAL_ID)
Пример #10
0
    def test_add_contact_that_replaces_an_existing_contact(self):
        alice = self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key('Alice'))
        new_nick = 'Alice2'
        new_tx_fingerprint = FINGERPRINT_LENGTH * b'\x03'
        new_rx_fingerprint = FINGERPRINT_LENGTH * b'\x04'

        # Verify that existing nick, kex status and fingerprints are
        # different from those that will replace the existing data
        self.assertNotEqual(alice.nick, new_nick)
        self.assertNotEqual(alice.tx_fingerprint, new_tx_fingerprint)
        self.assertNotEqual(alice.rx_fingerprint, new_rx_fingerprint)
        self.assertNotEqual(alice.kex_status, KEX_STATUS_UNVERIFIED)

        # Make sure each contact setting is opposite from default value
        alice.log_messages = not self.settings.log_messages_by_default
        alice.file_reception = not self.settings.accept_files_by_default
        alice.notifications = not self.settings.show_notifications_by_default

        # Replace the existing contact
        self.assertIsNone(
            self.contact_list.add_contact(
                nick_to_pub_key('Alice'), new_nick, new_tx_fingerprint,
                new_rx_fingerprint, KEX_STATUS_UNVERIFIED,
                self.settings.log_messages_by_default,
                self.settings.accept_files_by_default,
                self.settings.show_notifications_by_default))

        # Load database to another object from
        # file to verify new contact was stored
        contact_list2 = ContactList(MasterKey(), Settings())
        alice = contact_list2.get_contact_by_pub_key(nick_to_pub_key('Alice'))

        # Verify the content of loaded data
        self.assertEqual(len(contact_list2), len(self.real_contact_list))
        self.assertIsInstance(alice, Contact)

        # Test replaced contact replaced nick, fingerprints and kex status
        self.assertEqual(alice.nick, new_nick)
        self.assertEqual(alice.tx_fingerprint, new_tx_fingerprint)
        self.assertEqual(alice.rx_fingerprint, new_rx_fingerprint)
        self.assertEqual(alice.kex_status, KEX_STATUS_UNVERIFIED)

        # Test replaced contact kept settings set
        # to be opposite from default settings
        self.assertNotEqual(alice.log_messages,
                            self.settings.log_messages_by_default)
        self.assertNotEqual(alice.file_reception,
                            self.settings.accept_files_by_default)
        self.assertNotEqual(alice.notifications,
                            self.settings.show_notifications_by_default)
Пример #11
0
    def test_database_encryption_with_another_key(self, _: Any) -> None:
        # Setup
        window = RxWindow(type=WIN_TYPE_CONTACT,
                          uid=nick_to_pub_key('Alice'),
                          name='Alice',
                          type_print='contact')
        contact_list = ContactList(self.old_master_key, self.settings)
        contact_list.contacts = [create_contact('Alice')]
        group_list = GroupList()

        # Create temp file that must be removed.
        temp_file_data = os.urandom(LOG_ENTRY_LENGTH)
        with open(self.tmp_file_name, 'wb+') as f:
            f.write(temp_file_data)

        # Add a message from contact Alice to user (Bob).
        for p in assembly_packet_creator(MESSAGE, 'This is a short message'):
            write_log_entry(p,
                            nick_to_pub_key('Alice'),
                            self.message_log,
                            origin=ORIGIN_CONTACT_HEADER)

        # Add a message from user (Bob) to Alice.
        for p in assembly_packet_creator(MESSAGE, 'This is a short message'):
            write_log_entry(p, nick_to_pub_key('Alice'), self.message_log)

        # Check logfile content.
        message = (CLEAR_ENTIRE_SCREEN + CURSOR_LEFT_UP_CORNER + f"""\
Log file of message(s) sent to contact Alice
════════════════════════════════════════════════════════════════════════════════
{self.time} Alice: This is a short message
{self.time}    Me: This is a short message
<End of log file>

""")
        self.assertIsNone(
            change_log_db_key(self.old_master_key.master_key,
                              self.new_master_key.master_key, self.settings))

        with open(self.tmp_file_name, 'rb') as f:
            purp_temp_data = f.read()
        self.assertNotEqual(purp_temp_data, temp_file_data)

        # Test that decryption with new key is identical.
        replace_log_db(self.settings)
        self.assert_prints(message, access_logs, window, contact_list,
                           group_list, self.settings, self.new_master_key)

        # Test that temp file is removed.
        self.assertFalse(os.path.isfile(self.tmp_file_name))
Пример #12
0
    def test_add_contact(self):
        self.assertIsNone(
            self.contact_list.add_contact(f'*****@*****.**', '*****@*****.**',
                                          f'Faye', FINGERPRINT_LEN * b'\x03',
                                          FINGERPRINT_LEN * b'\x04', True,
                                          True, True))

        contact_list2 = ContactList(MasterKey(), Settings())
        c_alice = contact_list2.get_contact('Alice')
        c_faye = contact_list2.get_contact('Faye')

        self.assertEqual(len(self.contact_list), 6)
        self.assertIsInstance(c_alice, Contact)
        self.assertEqual(c_alice.tx_fingerprint, FINGERPRINT_LEN * b'\x01')
        self.assertEqual(c_faye.tx_fingerprint, FINGERPRINT_LEN * b'\x03')
Пример #13
0
    def setUp(self):
        self.unittest_dir = cd_unittest()
        self.master_key = MasterKey()
        self.settings = Settings()
        self.window = RxWindow(type=WIN_TYPE_CONTACT,
                               uid=nick_to_pub_key('Alice'),
                               name='Alice',
                               type_print='contact')

        self.contact_list = ContactList(self.master_key, self.settings)
        self.contact_list.contacts = list(
            map(create_contact, ['Alice', 'Charlie']))

        self.time = STATIC_TIMESTAMP

        self.group_list = GroupList(groups=['test_group'])
        self.group = self.group_list.get_group('test_group')
        self.group.members = self.contact_list.contacts
        self.args = self.window, self.contact_list, self.group_list, self.settings, self.master_key

        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."
        )
Пример #14
0
    def setUp(self) -> None:
        """Pre-test actions."""
        self.unit_test_dir = cd_unit_test()
        self.master_key = MasterKey()
        self.settings = Settings()
        self.time = STATIC_TIMESTAMP
        self.contact_list = ContactList(self.master_key, self.settings)
        self.group_list = GroupList(groups=['test_group'])
        self.file_name = f'{DIR_USER_DATA}{self.settings.software_operation}_logs'
        self.tmp_file_name = self.file_name + "_temp"
        self.tfc_log_database = MessageLog(self.file_name,
                                           self.master_key.master_key)
        self.args = self.contact_list, self.group_list, self.settings, self.master_key

        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."
        )
Пример #15
0
    def setUp(self):
        self.masterkey = MasterKey()
        self.settings  = Settings()
        self.window    = RxWindow(type=WIN_TYPE_CONTACT, uid='*****@*****.**', name='Alice')

        self.contact_list          = ContactList(self.masterkey, self.settings)
        self.contact_list.contacts = list(map(create_contact, ['Alice', 'Charlie']))

        self.time = datetime.fromtimestamp(struct.unpack('<L', binascii.unhexlify('08ceae02'))[0]).strftime('%H:%M')

        self.group_list = GroupList(groups=['test_group'])
        group           = self.group_list.get_group('test_group')
        group.members   = self.contact_list.contacts

        self.o_struct = struct.pack
        struct.pack   = lambda *_: binascii.unhexlify('08ceae02')

        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.").encode()
Пример #16
0
    def test_get_contact(self):
        # Setup
        contact1 = Contact('*****@*****.**', '*****@*****.**', 'Alice',
                           32 * b'\x01', 32 * b'\x02', True, True, True)
        contact2 = Contact('*****@*****.**', '*****@*****.**', 'Charlie',
                           32 * b'\x01', 32 * b'\x02', True, True, True)
        settings = Settings()
        master_k = MasterKey()
        contact_l = ContactList(master_k, settings)
        contact_l.contacts = [contact1, contact2]

        # Test
        co1 = contact_l.get_contact('*****@*****.**')
        self.assertIsInstance(co1, Contact)
        self.assertEqual(co1.rx_account, '*****@*****.**')

        co2 = contact_l.get_contact('Alice')
        self.assertIsInstance(co2, Contact)
        self.assertEqual(co2.rx_account, '*****@*****.**')
Пример #17
0
    def setUp(self):
        self.master_key   = MasterKey()
        self.settings     = Settings()
        self.contact_list = ContactList(self.master_key, self.settings)
        self.group_list   = GroupList(self.master_key, self.settings, self.contact_list)
        members           = [create_contact(n) for n in ['Alice', 'Bob', 'Charlie', 'David', 'Eric',
                                                         'Fido', 'Guido', 'Heidi', 'Ivan', 'Joana', 'Karol']]
        self.contact_list.contacts = members

        groups = [Group(n, False, False, members, self.settings, self.group_list.store_groups)
                  for n in ['testgroup_1', 'testgroup_2', 'testgroup_3', 'testgroup_4', 'testgroup_5',
                            'testgroup_6', 'testgroup_7', 'testgroup_8', 'testgroup_9', 'testgroup_10',
                            'testgroup_11']]

        self.group_list.groups = groups
        self.group_list.store_groups()

        self.single_member_data = (PADDED_UTF32_STR_LEN
                                   + (2 * BOOLEAN_SETTING_LEN)
                                   + (self.settings.max_number_of_group_members * PADDED_UTF32_STR_LEN))
Пример #18
0
    def test_database_encryption_with_another_key(self):
        # Setup
        window                = RxWindow(type=WIN_TYPE_CONTACT, uid='*****@*****.**', name='Alice')
        contact_list          = ContactList(self.old_key, self.settings)
        contact_list.contacts = [create_contact()]
        group_list            = GroupList()

        # Create temp file that must be removed
        with open("user_data/ut_logs_temp", 'wb+') as f:
            f.write(os.urandom(LOG_ENTRY_LENGTH))

        for p in assembly_packet_creator(MESSAGE, b'This is a short message'):
            write_log_entry(p, '*****@*****.**', self.settings, self.old_key, origin=ORIGIN_CONTACT_HEADER)
        for p in assembly_packet_creator(MESSAGE, b'This is a short message'):
            write_log_entry(p, '*****@*****.**', self.settings, self.old_key)

        # Test
        self.assertPrints((CLEAR_ENTIRE_SCREEN + CURSOR_LEFT_UP_CORNER + f"""\
Logfile of messages to/from Alice
════════════════════════════════════════════════════════════════════════════════
{self.time} Alice: This is a short message
{self.time}    Me: This is a short message
<End of logfile>

"""), access_logs, window, contact_list, group_list, self.settings, self.old_key)

        self.assertIsNone(re_encrypt(self.old_key.master_key, self.new_key.master_key, self.settings))

        # Test that decryption works with new key
        self.assertPrints((CLEAR_ENTIRE_SCREEN + CURSOR_LEFT_UP_CORNER + f"""\
Logfile of messages to/from Alice
════════════════════════════════════════════════════════════════════════════════
{self.time} Alice: This is a short message
{self.time}    Me: This is a short message
<End of logfile>

"""), access_logs, window, contact_list, group_list, self.settings, self.new_key)

        # Test that temp file is removed
        self.assertFalse(os.path.isfile("user_data/ut_logs_temp"))
Пример #19
0
    def test_storing_and_loading_of_contacts(self) -> None:
        # Test store
        self.contact_list.store_contacts()
        self.assertEqual(os.path.getsize(self.file_name),
                         XCHACHA20_NONCE_LENGTH
                         + (self.settings.max_number_of_contacts + 1) * CONTACT_LENGTH
                         + POLY1305_TAG_LENGTH)

        # Test load
        contact_list2 = ContactList(self.master_key, self.settings)
        self.assertEqual(len(contact_list2),          len(self.real_contact_list))
        self.assertEqual(len(contact_list2.contacts), len(self.full_contact_list))
        for c in contact_list2:
            self.assertIsInstance(c, Contact)
Пример #20
0
    def test_invalid_content_raises_critical_error(self) -> None:
        # Setup
        invalid_data = b'a'
        pt_bytes     = b''.join([c.serialize_c() for c in self.contact_list.contacts
                                                        + self.contact_list._dummy_contacts()])
        ct_bytes     = encrypt_and_sign(pt_bytes + invalid_data, self.master_key.master_key)

        ensure_dir(DIR_USER_DATA)
        with open(self.file_name, 'wb+') as f:
            f.write(ct_bytes)

        # Test
        with self.assertRaises(SystemExit):
            ContactList(self.master_key, self.settings)
Пример #21
0
    def test_storing_and_loading_of_contacts(self):
        # Test store
        self.contact_list.store_contacts()
        self.assertTrue(os.path.isfile(f'{DIR_USER_DATA}ut_contacts'))
        self.assertEqual(
            os.path.getsize(f'{DIR_USER_DATA}ut_contacts'),
            XSALSA20_NONCE_LEN +
            self.settings.max_number_of_contacts * CONTACT_LENGTH +
            POLY1305_TAG_LEN)

        # Test load
        contact_list2 = ContactList(self.master_key, self.settings)
        self.assertEqual(len(contact_list2), 5)
        for c in contact_list2:
            self.assertIsInstance(c, Contact)
Пример #22
0
    def test_local_contact(self):
        # Setup
        contact1 = Contact('*****@*****.**', '*****@*****.**', 'Alice',
                           32 * b'\x01', 32 * b'\x02', True, True, True)
        contact_l = ContactList(MasterKey(), Settings())
        contact_l.contacts = [contact1]
        o_get_tty_w = src.common.misc.get_tty_w
        src.common.misc.get_tty_w = lambda x: 1

        # Test
        self.assertFalse(contact_l.has_local_contact())

        contact_l.add_contact('local', 'local', 'local', 32 * b'\x03',
                              32 * b'\x04', True, True, True)

        self.assertTrue(contact_l.has_local_contact())
        self.assertIsNone(contact_l.print_contacts())
        self.assertIsNone(contact_l.print_contacts(spacing=True))

        # Teardown
        src.common.misc.get_tty_w = o_get_tty_w
Пример #23
0
    def test_getters(self):
        # Setup
        contact1 = Contact('*****@*****.**', '*****@*****.**', 'Alice',
                           32 * b'\x01', 32 * b'\x02', True, True, True)
        contact2 = Contact('*****@*****.**', '*****@*****.**', 'Charlie',
                           32 * b'\x01', 32 * b'\x02', True, True, True)
        settings = Settings()
        master_k = MasterKey()
        contact_l = ContactList(master_k, settings)
        contact_l.contacts = [contact1, contact2]

        # Test
        self.assertEqual(
            contact_l.contact_selectors(),
            ['*****@*****.**', '*****@*****.**', 'Alice', 'Charlie'])
        self.assertEqual(contact_l.get_list_of_accounts(),
                         ['*****@*****.**', '*****@*****.**'])
        self.assertEqual(contact_l.get_list_of_nicks(), ['Alice', 'Charlie'])
        self.assertEqual(contact_l.get_list_of_users_accounts(),
                         ['*****@*****.**'])
Пример #24
0
    def test_store_and_load_contacts(self):
        # Setup
        contact = Contact('*****@*****.**', '*****@*****.**', 'Alice',
                          32 * b'\x01', 32 * b'\x02', True, True, True)
        settings = Settings()
        master_k = MasterKey()
        contact_l = ContactList(master_k, settings)
        contact_l.contacts = 5 * [contact]
        contact_l.store_contacts()

        # Test
        contact_l2 = ContactList(master_k, settings)
        self.assertEqual(len(contact_l2), 5)
        for c in contact_l2:
            self.assertIsInstance(c, Contact)

        self.assertTrue(os.path.isfile(f'{DIR_USER_DATA}/ut_contacts'))
        self.assertEqual(
            os.path.getsize(f'{DIR_USER_DATA}/ut_contacts'),
            24 + 20 * (1024 + 1024 + 1024 + 32 + 32 + 1 + 1 + 1) + 16)
        os.remove(f'{DIR_USER_DATA}/ut_contacts')
Пример #25
0
class TestContactList(TFCTestCase):
    def setUp(self):
        """Pre-test actions."""
        self.unit_test_dir = cd_unit_test()
        self.master_key = MasterKey()
        self.settings = Settings()
        self.file_name = f'{DIR_USER_DATA}{self.settings.software_operation}_contacts'
        self.contact_list = ContactList(self.master_key, self.settings)
        self.full_contact_list = [
            'Alice', 'Bob', 'Charlie', 'David', 'Eric', LOCAL_ID
        ]
        self.contact_list.contacts = list(
            map(create_contact, self.full_contact_list))
        self.real_contact_list = self.full_contact_list[:]
        self.real_contact_list.remove(LOCAL_ID)

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

    def test_contact_list_iterates_over_contact_objects(self):
        for c in self.contact_list:
            self.assertIsInstance(c, Contact)

    def test_len_returns_the_number_of_contacts_and_excludes_the_local_key(
            self):
        self.assertEqual(len(self.contact_list), len(self.real_contact_list))

    def test_storing_and_loading_of_contacts(self):
        # Test store
        self.contact_list.store_contacts()
        self.assertEqual(
            os.path.getsize(self.file_name), XCHACHA20_NONCE_LENGTH +
            (self.settings.max_number_of_contacts + 1) * CONTACT_LENGTH +
            POLY1305_TAG_LENGTH)

        # Test load
        contact_list2 = ContactList(self.master_key, self.settings)
        self.assertEqual(len(contact_list2), len(self.real_contact_list))
        self.assertEqual(len(contact_list2.contacts),
                         len(self.full_contact_list))
        for c in contact_list2:
            self.assertIsInstance(c, Contact)

    def test_invalid_content_raises_critical_error(self):
        # Setup
        invalid_data = b'a'
        pt_bytes = b''.join([
            c.serialize_c() for c in self.contact_list.contacts +
            self.contact_list._dummy_contacts()
        ])
        ct_bytes = encrypt_and_sign(pt_bytes + invalid_data,
                                    self.master_key.master_key)

        ensure_dir(DIR_USER_DATA)
        with open(self.file_name, 'wb+') as f:
            f.write(ct_bytes)

        # Test
        with self.assertRaises(SystemExit):
            ContactList(self.master_key, self.settings)

    def test_load_of_modified_database_raises_critical_error(self):
        self.contact_list.store_contacts()

        # Test reading works normally
        self.assertIsInstance(ContactList(self.master_key, self.settings),
                              ContactList)

        # Test loading of tampered database raises CriticalError
        tamper_file(self.file_name, tamper_size=1)
        with self.assertRaises(SystemExit):
            ContactList(self.master_key, self.settings)

    def test_generate_dummy_contact(self):
        dummy_contact = ContactList.generate_dummy_contact()
        self.assertIsInstance(dummy_contact, Contact)
        self.assertEqual(len(dummy_contact.serialize_c()), CONTACT_LENGTH)

    def test_dummy_contacts(self):
        dummies = self.contact_list._dummy_contacts()
        self.assertEqual(
            len(dummies),
            self.settings.max_number_of_contacts - len(self.real_contact_list))
        for c in dummies:
            self.assertIsInstance(c, Contact)

    def test_add_contact(self):
        tx_fingerprint = FINGERPRINT_LENGTH * b'\x03'
        rx_fingerprint = FINGERPRINT_LENGTH * b'\x04'

        self.assertIsNone(
            self.contact_list.add_contact(
                nick_to_pub_key('Faye'), 'Faye', tx_fingerprint,
                rx_fingerprint, KEX_STATUS_UNVERIFIED,
                self.settings.log_messages_by_default,
                self.settings.accept_files_by_default,
                self.settings.show_notifications_by_default))

        # Test new contact was stored by loading
        # the database from file to another object
        contact_list2 = ContactList(MasterKey(), Settings())
        faye = contact_list2.get_contact_by_pub_key(nick_to_pub_key('Faye'))

        self.assertEqual(len(self.contact_list),
                         len(self.real_contact_list) + 1)
        self.assertIsInstance(faye, Contact)

        self.assertEqual(faye.tx_fingerprint, tx_fingerprint)
        self.assertEqual(faye.rx_fingerprint, rx_fingerprint)
        self.assertEqual(faye.kex_status, KEX_STATUS_UNVERIFIED)

        self.assertEqual(faye.log_messages,
                         self.settings.log_messages_by_default)
        self.assertEqual(faye.file_reception,
                         self.settings.accept_files_by_default)
        self.assertEqual(faye.notifications,
                         self.settings.show_notifications_by_default)

    def test_add_contact_that_replaces_an_existing_contact(self):
        alice = self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key('Alice'))
        new_nick = 'Alice2'
        new_tx_fingerprint = FINGERPRINT_LENGTH * b'\x03'
        new_rx_fingerprint = FINGERPRINT_LENGTH * b'\x04'

        # Verify that existing nick, kex status and fingerprints are
        # different from those that will replace the existing data
        self.assertNotEqual(alice.nick, new_nick)
        self.assertNotEqual(alice.tx_fingerprint, new_tx_fingerprint)
        self.assertNotEqual(alice.rx_fingerprint, new_rx_fingerprint)
        self.assertNotEqual(alice.kex_status, KEX_STATUS_UNVERIFIED)

        # Make sure each contact setting is opposite from default value
        alice.log_messages = not self.settings.log_messages_by_default
        alice.file_reception = not self.settings.accept_files_by_default
        alice.notifications = not self.settings.show_notifications_by_default

        # Replace the existing contact
        self.assertIsNone(
            self.contact_list.add_contact(
                nick_to_pub_key('Alice'), new_nick, new_tx_fingerprint,
                new_rx_fingerprint, KEX_STATUS_UNVERIFIED,
                self.settings.log_messages_by_default,
                self.settings.accept_files_by_default,
                self.settings.show_notifications_by_default))

        # Load database to another object from
        # file to verify new contact was stored
        contact_list2 = ContactList(MasterKey(), Settings())
        alice = contact_list2.get_contact_by_pub_key(nick_to_pub_key('Alice'))

        # Verify the content of loaded data
        self.assertEqual(len(contact_list2), len(self.real_contact_list))
        self.assertIsInstance(alice, Contact)

        # Test replaced contact replaced nick, fingerprints and kex status
        self.assertEqual(alice.nick, new_nick)
        self.assertEqual(alice.tx_fingerprint, new_tx_fingerprint)
        self.assertEqual(alice.rx_fingerprint, new_rx_fingerprint)
        self.assertEqual(alice.kex_status, KEX_STATUS_UNVERIFIED)

        # Test replaced contact kept settings set
        # to be opposite from default settings
        self.assertNotEqual(alice.log_messages,
                            self.settings.log_messages_by_default)
        self.assertNotEqual(alice.file_reception,
                            self.settings.accept_files_by_default)
        self.assertNotEqual(alice.notifications,
                            self.settings.show_notifications_by_default)

    def test_remove_contact_by_pub_key(self):
        # Verify both contacts exist
        self.assertTrue(self.contact_list.has_pub_key(nick_to_pub_key('Bob')))
        self.assertTrue(
            self.contact_list.has_pub_key(nick_to_pub_key('Charlie')))

        self.assertIsNone(
            self.contact_list.remove_contact_by_pub_key(
                nick_to_pub_key('Bob')))
        self.assertFalse(self.contact_list.has_pub_key(nick_to_pub_key('Bob')))
        self.assertTrue(
            self.contact_list.has_pub_key(nick_to_pub_key('Charlie')))

    def test_remove_contact_by_address_or_nick(self):
        # Verify both contacts exist
        self.assertTrue(self.contact_list.has_pub_key(nick_to_pub_key('Bob')))
        self.assertTrue(
            self.contact_list.has_pub_key(nick_to_pub_key('Charlie')))

        # Test removal with address
        self.assertIsNone(
            self.contact_list.remove_contact_by_address_or_nick(
                nick_to_onion_address('Bob')))
        self.assertFalse(self.contact_list.has_pub_key(nick_to_pub_key('Bob')))
        self.assertTrue(
            self.contact_list.has_pub_key(nick_to_pub_key('Charlie')))

        # Test removal with nick
        self.assertIsNone(
            self.contact_list.remove_contact_by_address_or_nick('Charlie'))
        self.assertFalse(self.contact_list.has_pub_key(nick_to_pub_key('Bob')))
        self.assertFalse(
            self.contact_list.has_pub_key(nick_to_pub_key('Charlie')))

    def test_get_contact_by_pub_key(self):
        self.assertIs(
            self.contact_list.get_contact_by_pub_key(nick_to_pub_key('Bob')),
            self.contact_list.get_contact_by_address_or_nick('Bob'))

    def test_get_contact_by_address_or_nick_returns_the_same_contact_object_with_address_and_nick(
            self):
        for selector in [nick_to_onion_address('Bob'), 'Bob']:
            self.assertIsInstance(
                self.contact_list.get_contact_by_address_or_nick(selector),
                Contact)

        self.assertIs(
            self.contact_list.get_contact_by_address_or_nick('Bob'),
            self.contact_list.get_contact_by_address_or_nick(
                nick_to_onion_address('Bob')))

    def test_get_list_of_contacts(self):
        self.assertEqual(len(self.contact_list.get_list_of_contacts()),
                         len(self.real_contact_list))
        for c in self.contact_list.get_list_of_contacts():
            self.assertIsInstance(c, Contact)

    def test_get_list_of_addresses(self):
        self.assertEqual(self.contact_list.get_list_of_addresses(), [
            nick_to_onion_address('Alice'),
            nick_to_onion_address('Bob'),
            nick_to_onion_address('Charlie'),
            nick_to_onion_address('David'),
            nick_to_onion_address('Eric')
        ])

    def test_get_list_of_nicks(self):
        self.assertEqual(self.contact_list.get_list_of_nicks(),
                         ['Alice', 'Bob', 'Charlie', 'David', 'Eric'])

    def test_get_list_of_pub_keys(self):
        self.assertEqual(self.contact_list.get_list_of_pub_keys(), [
            nick_to_pub_key('Alice'),
            nick_to_pub_key('Bob'),
            nick_to_pub_key('Charlie'),
            nick_to_pub_key('David'),
            nick_to_pub_key('Eric')
        ])

    def test_get_list_of_pending_pub_keys(self):
        # Set key exchange statuses to pending
        for nick in ['Alice', 'Bob']:
            contact = self.contact_list.get_contact_by_address_or_nick(nick)
            contact.kex_status = KEX_STATUS_PENDING

        # Test pending contacts are returned
        self.assertEqual(self.contact_list.get_list_of_pending_pub_keys(),
                         [nick_to_pub_key('Alice'),
                          nick_to_pub_key('Bob')])

    def test_get_list_of_existing_pub_keys(self):
        self.contact_list.get_contact_by_address_or_nick(
            'Alice').kex_status = KEX_STATUS_UNVERIFIED
        self.contact_list.get_contact_by_address_or_nick(
            'Bob').kex_status = KEX_STATUS_VERIFIED
        self.contact_list.get_contact_by_address_or_nick(
            'Charlie').kex_status = KEX_STATUS_HAS_RX_PSK
        self.contact_list.get_contact_by_address_or_nick(
            'David').kex_status = KEX_STATUS_NO_RX_PSK
        self.contact_list.get_contact_by_address_or_nick(
            'Eric').kex_status = KEX_STATUS_PENDING

        self.assertEqual(self.contact_list.get_list_of_existing_pub_keys(), [
            nick_to_pub_key('Alice'),
            nick_to_pub_key('Bob'),
            nick_to_pub_key('Charlie'),
            nick_to_pub_key('David')
        ])

    def test_contact_selectors(self):
        self.assertEqual(self.contact_list.contact_selectors(), [
            nick_to_onion_address('Alice'),
            nick_to_onion_address('Bob'),
            nick_to_onion_address('Charlie'),
            nick_to_onion_address('David'),
            nick_to_onion_address('Eric'), 'Alice', 'Bob', 'Charlie', 'David',
            'Eric'
        ])

    def test_has_contacts(self):
        self.assertTrue(self.contact_list.has_contacts())
        self.contact_list.contacts = []
        self.assertFalse(self.contact_list.has_contacts())

    def test_has_only_pending_contacts(self):
        # Change all to pending
        for contact in self.contact_list.get_list_of_contacts():
            contact.kex_status = KEX_STATUS_PENDING
        self.assertTrue(self.contact_list.has_only_pending_contacts())

        # Change one from pending
        alice = self.contact_list.get_contact_by_address_or_nick('Alice')
        alice.kex_status = KEX_STATUS_UNVERIFIED
        self.assertFalse(self.contact_list.has_only_pending_contacts())

    def test_has_pub_key(self):
        self.contact_list.contacts = []
        self.assertFalse(self.contact_list.has_pub_key(nick_to_pub_key('Bob')))
        self.assertFalse(self.contact_list.has_pub_key(nick_to_pub_key('Bob')))

        self.contact_list.contacts = list(
            map(create_contact, ['Bob', 'Charlie']))
        self.assertTrue(self.contact_list.has_pub_key(nick_to_pub_key('Bob')))
        self.assertTrue(
            self.contact_list.has_pub_key(nick_to_pub_key('Charlie')))

    def test_has_local_contact(self):
        self.contact_list.contacts = []
        self.assertFalse(self.contact_list.has_local_contact())

        self.contact_list.contacts = [create_contact(LOCAL_ID)]
        self.assertTrue(self.contact_list.has_local_contact())

    def test_print_contacts(self):
        self.contact_list.contacts.append(create_contact(LOCAL_ID))
        self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key('Alice')).log_messages = False
        self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key('Alice')).kex_status = KEX_STATUS_PENDING
        self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key('Bob')).notifications = False
        self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key('Charlie')).kex_status = KEX_STATUS_UNVERIFIED
        self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key('Bob')).file_reception = False
        self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key('Bob')).kex_status = KEX_STATUS_VERIFIED
        self.contact_list.get_contact_by_pub_key(nick_to_pub_key(
            'David')).rx_fingerprint = bytes(FINGERPRINT_LENGTH)
        self.contact_list.get_contact_by_pub_key(
            nick_to_pub_key('David')).kex_status = bytes(KEX_STATUS_NO_RX_PSK)
        self.assert_prints(
            CLEAR_ENTIRE_SCREEN + CURSOR_LEFT_UP_CORNER + f"""\

Contact    Account    Logging    Notify    Files     Key Ex
────────────────────────────────────────────────────────────────────────────────
Alice      hpcra      No         Yes       Accept    {ECDHE} (Pending)
Bob        zwp3d      Yes        No        Reject    {ECDHE} (Verified)
Charlie    n2a3c      Yes        Yes       Accept    {ECDHE} (Unverified)
David      u22uy      Yes        Yes       Accept    {PSK}  (No contact key)
Eric       jszzy      Yes        Yes       Accept    {ECDHE} (Verified)


""", self.contact_list.print_contacts)
Пример #26
0
 def test_generate_dummy_contact(self):
     dummy_contact = ContactList.generate_dummy_contact()
     self.assertIsInstance(dummy_contact, Contact)
     self.assertEqual(len(dummy_contact.serialize_c()), CONTACT_LENGTH)
Пример #27
0
class TestGroupList(TFCTestCase):
    def setUp(self):
        self.unittest_dir = cd_unittest()
        self.master_key = MasterKey()
        self.settings = Settings()
        self.file_name = f'{DIR_USER_DATA}{self.settings.software_operation}_groups'
        self.contact_list = ContactList(self.master_key, self.settings)
        self.group_list = GroupList(self.master_key, self.settings,
                                    self.contact_list)
        self.nicks = [
            'Alice', 'Bob', 'Charlie', 'David', 'Eric', 'Fido', 'Guido',
            'Heidi', 'Ivan', 'Joana', 'Karol'
        ]
        self.group_names = [
            'test_group_1', 'test_group_2', 'test_group_3', 'test_group_4',
            'test_group_5', 'test_group_6', 'test_group_7', 'test_group_8',
            'test_group_9', 'test_group_10', 'test_group_11'
        ]
        members = list(map(create_contact, self.nicks))

        self.contact_list.contacts = members

        self.group_list.groups = \
            [Group(name         =name,
                   group_id     =group_name_to_group_id(name),
                   log_messages =False,
                   notifications=False,
                   members      =members,
                   settings     =self.settings,
                   store_groups =self.group_list.store_groups)
             for name in self.group_names]

        self.single_member_data_len = (
            GROUP_STATIC_LENGTH + self.settings.max_number_of_group_members *
            ONION_SERVICE_PUBLIC_KEY_LENGTH)

    def tearDown(self):
        cleanup(self.unittest_dir)

    def test_group_list_iterates_over_group_objects(self):
        for g in self.group_list:
            self.assertIsInstance(g, Group)

    def test_len_returns_the_number_of_groups(self):
        self.assertEqual(len(self.group_list), len(self.group_names))

    def test_storing_and_loading_of_groups(self):
        self.group_list.store_groups()

        self.assertTrue(os.path.isfile(self.file_name))
        self.assertEqual(
            os.path.getsize(self.file_name),
            XCHACHA20_NONCE_LENGTH + GROUP_DB_HEADER_LENGTH +
            self.settings.max_number_of_groups * self.single_member_data_len +
            POLY1305_TAG_LENGTH)

        # Reduce setting values from 20 to 10
        self.settings.max_number_of_groups = 10
        self.settings.max_number_of_group_members = 10

        group_list2 = GroupList(self.master_key, self.settings,
                                self.contact_list)
        self.assertEqual(len(group_list2), 11)

        # Check that `_load_groups()` increased setting values back to 20 so it fits the 11 groups
        self.assertEqual(self.settings.max_number_of_groups, 20)
        self.assertEqual(self.settings.max_number_of_group_members, 20)

        # Check that removed contact from contact list updates group
        self.contact_list.remove_contact_by_address_or_nick('Alice')
        group_list3 = GroupList(self.master_key, self.settings,
                                self.contact_list)
        self.assertEqual(len(group_list3.get_group('test_group_1').members),
                         10)

    def test_invalid_content_raises_critical_error(self):
        # Setup
        invalid_data = b'a'
        pt_bytes = self.group_list._generate_group_db_header()
        pt_bytes += b''.join([
            g.serialize_g()
            for g in (self.group_list.groups + self.group_list._dummy_groups())
        ])
        ct_bytes = encrypt_and_sign(pt_bytes + invalid_data,
                                    self.master_key.master_key)

        ensure_dir(DIR_USER_DATA)
        with open(self.file_name, 'wb+') as f:
            f.write(ct_bytes)

        # Test
        with self.assertRaises(SystemExit):
            GroupList(self.master_key, self.settings, self.contact_list)

    def test_load_of_modified_database_raises_critical_error(self):
        self.group_list.store_groups()

        # Test reading works normally
        self.assertIsInstance(
            GroupList(self.master_key, self.settings, self.contact_list),
            GroupList)

        # Test loading of the tampered database raises CriticalError
        tamper_file(self.file_name, tamper_size=1)
        with self.assertRaises(SystemExit):
            GroupList(self.master_key, self.settings, self.contact_list)

    def test_check_db_settings(self):
        self.assertFalse(
            self.group_list._check_db_settings(
                number_of_actual_groups=self.settings.max_number_of_groups,
                members_in_largest_group=self.settings.
                max_number_of_group_members))

        self.assertTrue(
            self.group_list._check_db_settings(
                number_of_actual_groups=self.settings.max_number_of_groups + 1,
                members_in_largest_group=self.settings.
                max_number_of_group_members))

        self.assertTrue(
            self.group_list._check_db_settings(
                number_of_actual_groups=self.settings.max_number_of_groups,
                members_in_largest_group=self.settings.
                max_number_of_group_members + 1))

    def test_generate_group_db_header(self):
        header = self.group_list._generate_group_db_header()
        self.assertEqual(len(header), GROUP_DB_HEADER_LENGTH)
        self.assertIsInstance(header, bytes)

    def test_generate_dummy_group(self):
        dummy_group = self.group_list._generate_dummy_group()
        self.assertIsInstance(dummy_group, Group)
        self.assertEqual(len(dummy_group.serialize_g()),
                         self.single_member_data_len)

    def test_dummy_groups(self):
        dummies = self.group_list._dummy_groups()
        self.assertEqual(
            len(dummies),
            self.settings.max_number_of_contacts - len(self.nicks))
        for g in dummies:
            self.assertIsInstance(g, Group)

    def test_add_group(self):
        members = [create_contact('Laura')]
        self.group_list.add_group('test_group_12', bytes(GROUP_ID_LENGTH),
                                  False, False, members)
        self.group_list.add_group('test_group_12', bytes(GROUP_ID_LENGTH),
                                  False, True, members)
        self.assertTrue(
            self.group_list.get_group('test_group_12').notifications)
        self.assertEqual(len(self.group_list), len(self.group_names) + 1)

    def test_remove_group_by_name(self):
        self.assertEqual(len(self.group_list), len(self.group_names))

        # Remove non-existing group
        self.assertIsNone(
            self.group_list.remove_group_by_name('test_group_12'))
        self.assertEqual(len(self.group_list), len(self.group_names))

        # Remove existing group
        self.assertIsNone(
            self.group_list.remove_group_by_name('test_group_11'))
        self.assertEqual(len(self.group_list), len(self.group_names) - 1)

    def test_remove_group_by_id(self):
        self.assertEqual(len(self.group_list), len(self.group_names))

        # Remove non-existing group
        self.assertIsNone(
            self.group_list.remove_group_by_id(
                group_name_to_group_id('test_group_12')))
        self.assertEqual(len(self.group_list), len(self.group_names))

        # Remove existing group
        self.assertIsNone(
            self.group_list.remove_group_by_id(
                group_name_to_group_id('test_group_11')))
        self.assertEqual(len(self.group_list), len(self.group_names) - 1)

    def test_get_group(self):
        self.assertEqual(
            self.group_list.get_group('test_group_3').name, 'test_group_3')

    def test_get_group_by_id(self):
        members = [create_contact('Laura')]
        group_id = os.urandom(GROUP_ID_LENGTH)
        self.group_list.add_group('test_group_12', group_id, False, False,
                                  members)
        self.assertEqual(
            self.group_list.get_group_by_id(group_id).name, 'test_group_12')

    def test_get_list_of_group_names(self):
        self.assertEqual(self.group_list.get_list_of_group_names(),
                         self.group_names)

    def test_get_list_of_group_ids(self):
        self.assertEqual(self.group_list.get_list_of_group_ids(),
                         list(map(group_name_to_group_id, self.group_names)))

    def test_get_list_of_hr_group_ids(self):
        self.assertEqual(self.group_list.get_list_of_hr_group_ids(), [
            b58encode(gid)
            for gid in list(map(group_name_to_group_id, self.group_names))
        ])

    def test_get_group_members(self):
        members = self.group_list.get_group_members(
            group_name_to_group_id('test_group_1'))
        for c in members:
            self.assertIsInstance(c, Contact)

    def test_has_group(self):
        self.assertTrue(self.group_list.has_group('test_group_11'))
        self.assertFalse(self.group_list.has_group('test_group_12'))

    def test_has_group_id(self):
        members = [create_contact('Laura')]
        group_id = os.urandom(GROUP_ID_LENGTH)
        self.assertFalse(self.group_list.has_group_id(group_id))
        self.group_list.add_group('test_group_12', group_id, False, False,
                                  members)
        self.assertTrue(self.group_list.has_group_id(group_id))

    def test_largest_group(self):
        self.assertEqual(self.group_list.largest_group(), len(self.nicks))

    def test_print_group(self):
        self.group_list.get_group("test_group_1").name = "group"
        self.group_list.get_group("test_group_2").log_messages = True
        self.group_list.get_group("test_group_3").notifications = True
        self.group_list.get_group("test_group_4").log_messages = True
        self.group_list.get_group("test_group_4").notifications = True
        self.group_list.get_group("test_group_5").members = []
        self.group_list.get_group("test_group_6").members = list(
            map(create_contact,
                ['Alice', 'Bob', 'Charlie', 'David', 'Eric', 'Fido']))
        self.assert_prints(
            """\
Group            Group ID         Logging     Notify    Members
────────────────────────────────────────────────────────────────────────────────
group            2drs4c4VcDdrP    No          No        Alice, Bob, Charlie,
                                                        David, Eric, Fido,
                                                        Guido, Heidi, Ivan,
                                                        Joana, Karol

test_group_2     2dnGTyhkThmPi    Yes         No        Alice, Bob, Charlie,
                                                        David, Eric, Fido,
                                                        Guido, Heidi, Ivan,
                                                        Joana, Karol

test_group_3     2df7s3LZhwLDw    No          Yes       Alice, Bob, Charlie,
                                                        David, Eric, Fido,
                                                        Guido, Heidi, Ivan,
                                                        Joana, Karol

test_group_4     2djy3XwUQVR8q    Yes         Yes       Alice, Bob, Charlie,
                                                        David, Eric, Fido,
                                                        Guido, Heidi, Ivan,
                                                        Joana, Karol

test_group_5     2dvbcgnjiLLMo    No          No        <Empty group>

test_group_6     2dwBRWAqWKHWv    No          No        Alice, Bob, Charlie,
                                                        David, Eric, Fido

test_group_7     2eDPg5BAM6qF4    No          No        Alice, Bob, Charlie,
                                                        David, Eric, Fido,
                                                        Guido, Heidi, Ivan,
                                                        Joana, Karol

test_group_8     2dqdayy5TJKcf    No          No        Alice, Bob, Charlie,
                                                        David, Eric, Fido,
                                                        Guido, Heidi, Ivan,
                                                        Joana, Karol

test_group_9     2e45bLYvSX3C8    No          No        Alice, Bob, Charlie,
                                                        David, Eric, Fido,
                                                        Guido, Heidi, Ivan,
                                                        Joana, Karol

test_group_10    2dgkncX9xRibh    No          No        Alice, Bob, Charlie,
                                                        David, Eric, Fido,
                                                        Guido, Heidi, Ivan,
                                                        Joana, Karol

test_group_11    2e6vAGmHmSEEJ    No          No        Alice, Bob, Charlie,
                                                        David, Eric, Fido,
                                                        Guido, Heidi, Ivan,
                                                        Joana, Karol


""", self.group_list.print_groups)
Пример #28
0
    def test_class(self):
        # Setup
        master_key = MasterKey()
        settings = Settings()
        contact_list = ContactList(master_key, settings)
        group_list = GroupList(master_key, settings, contact_list)
        members = [
            create_contact(n) for n in [
                'Alice', 'Bob', 'Charlie', 'David', 'Eric', 'Fido', 'Gunter',
                'Heidi', 'Ivan', 'Joana', 'Karol'
            ]
        ]
        contact_list.contacts = members
        groups = [
            Group(n, False, False, members, settings,
                  group_list.store_groups())
            for n in [
                'testgroup_1', 'testgroup_2', 'testgroup3', 'testgroup_4',
                'testgroup_5', 'testgroup_6', 'testgroup_7', 'testgroup8',
                'testgroup_9', 'testgroup_10', 'testgroup_11'
            ]
        ]
        group_list.groups = groups
        group_list.store_groups()

        # Test
        for g in group_list:
            self.assertIsInstance(g, Group)
        self.assertEqual(len(group_list), 11)

        self.assertTrue(os.path.isfile(f'{DIR_USER_DATA}/ut_groups'))
        self.assertEqual(os.path.getsize(f'{DIR_USER_DATA}/ut_groups'),
                         24 + 32 + 20 * (1024 + 2 + (20 * 1024)) + 16)

        settings.m_number_of_groups = 10
        settings.m_members_in_group = 10

        group_list2 = GroupList(master_key, settings, contact_list)

        self.assertEqual(len(group_list2), 11)

        self.assertEqual(settings.m_number_of_groups, 20)
        self.assertEqual(settings.m_members_in_group, 20)

        bytestring = group_list2.generate_header()
        self.assertEqual(len(bytestring), 32)
        self.assertIsInstance(bytestring, bytes)

        dg_bytestring = group_list2.generate_dummy_group()
        self.assertEqual(len(dg_bytestring), (1024 + 2 + (20 * 1024)))
        self.assertIsInstance(dg_bytestring, bytes)

        members.append(create_contact('Laura'))
        group_list2.add_group('testgroup_12', False, False, members)
        group_list2.add_group('testgroup_12', False, True, members)
        self.assertTrue(group_list2.get_group('testgroup_12').notifications)
        self.assertEqual(len(group_list2), 12)
        self.assertEqual(group_list2.largest_group(), 12)

        g_names = [
            'testgroup_1', 'testgroup_2', 'testgroup3', 'testgroup_4',
            'testgroup_5', 'testgroup_6', 'testgroup_7', 'testgroup8',
            'testgroup_9', 'testgroup_10', 'testgroup_11', 'testgroup_12'
        ]
        self.assertEqual(group_list2.get_list_of_group_names(), g_names)

        g_o = group_list2.get_group('testgroup_1')
        self.assertIsInstance(g_o, Group)
        self.assertEqual(g_o.name, 'testgroup_1')
        self.assertTrue(group_list2.has_group('testgroup_12'))
        self.assertFalse(group_list2.has_group('testgroup_13'))
        self.assertTrue(group_list2.has_groups(), True)

        members = group_list2.get_group_members('testgroup_1')
        for c in members:
            self.assertIsInstance(c, Contact)

        self.assertEqual(len(group_list2), 12)
        group_list2.remove_group('testgroup_13')
        self.assertEqual(len(group_list2), 12)
        group_list2.remove_group('testgroup_12')
        self.assertEqual(len(group_list2), 11)
        self.assertIsNone(group_list2.print_groups())

        # Teardown
        cleanup()
Пример #29
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)
Пример #30
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