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)
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)