Exemple #1
0
class TestKeyList(unittest.TestCase):
    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}_keys'
        self.keylist = KeyList(self.master_key, self.settings)
        self.full_contact_list = ['Alice', 'Bob', 'Charlie', LOCAL_ID]
        self.keylist.keysets = [
            create_keyset(n, store_f=self.keylist.store_keys)
            for n in self.full_contact_list
        ]

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

    def test_storing_and_loading_of_keysets(self):
        # Test store
        self.keylist.store_keys()
        self.assertEqual(
            os.path.getsize(self.file_name), XCHACHA20_NONCE_LENGTH +
            (self.settings.max_number_of_contacts + 1) * KEYSET_LENGTH +
            POLY1305_TAG_LENGTH)

        # Test load
        key_list2 = KeyList(MasterKey(), Settings())
        self.assertEqual(len(key_list2.keysets), len(self.full_contact_list))

    def test_load_of_modified_database_raises_critical_error(self):
        self.keylist.store_keys()

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

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

    def test_invalid_content_raises_critical_error(self):
        # Setup
        invalid_data = b'a'
        pt_bytes = b''.join([
            k.serialize_k()
            for k in self.keylist.keysets + self.keylist._dummy_keysets()
        ])
        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):
            KeyList(self.master_key, self.settings)

    def test_generate_dummy_keyset(self):
        dummy_keyset = self.keylist.generate_dummy_keyset()
        self.assertEqual(len(dummy_keyset.serialize_k()), KEYSET_LENGTH)
        self.assertIsInstance(dummy_keyset, KeySet)

    def test_dummy_keysets(self):
        dummies = self.keylist._dummy_keysets()
        self.assertEqual(len(dummies),
                         (self.settings.max_number_of_contacts + 1) -
                         len(self.full_contact_list))
        for c in dummies:
            self.assertIsInstance(c, KeySet)

    def test_add_keyset(self):
        new_key = bytes(SYMMETRIC_KEY_LENGTH)
        self.keylist.keysets = [create_keyset(LOCAL_ID)]

        # Check that KeySet exists and that its keys are different
        self.assertNotEqual(self.keylist.keysets[0].rx_hk, new_key)

        # Replace existing KeySet
        self.assertIsNone(
            self.keylist.add_keyset(LOCAL_PUBKEY, new_key, new_key, new_key,
                                    new_key))

        # Check that new KeySet replaced the old one
        self.assertEqual(self.keylist.keysets[0].onion_pub_key, LOCAL_PUBKEY)
        self.assertEqual(self.keylist.keysets[0].rx_hk, new_key)

    def test_remove_keyset(self):
        # Test KeySet for Bob exists
        self.assertTrue(self.keylist.has_keyset(nick_to_pub_key('Bob')))

        # Remove KeySet for Bob
        self.assertIsNone(self.keylist.remove_keyset(nick_to_pub_key('Bob')))

        # Test KeySet was removed
        self.assertFalse(self.keylist.has_keyset(nick_to_pub_key('Bob')))

    def test_change_master_key(self):
        key = SYMMETRIC_KEY_LENGTH * b'\x01'
        master_key2 = MasterKey(master_key=key)

        # Test that new key is different from existing one
        self.assertNotEqual(key, self.master_key.master_key)

        # Change master key
        self.assertIsNone(self.keylist.change_master_key(master_key2))

        # Test that master key has changed
        self.assertEqual(self.keylist.master_key.master_key, key)

        # Test that loading of the database with new key succeeds
        self.assertIsInstance(KeyList(master_key2, self.settings), KeyList)

    def test_update_database(self):
        self.assertEqual(os.path.getsize(self.file_name), 9016)
        self.assertIsNone(
            self.keylist.manage(KDB_UPDATE_SIZE_HEADER,
                                Settings(max_number_of_contacts=100)))
        self.assertEqual(os.path.getsize(self.file_name), 17816)
        self.assertEqual(self.keylist.settings.max_number_of_contacts, 100)

    def test_get_keyset(self):
        keyset = self.keylist.get_keyset(nick_to_pub_key('Alice'))
        self.assertIsInstance(keyset, KeySet)

    def test_get_list_of_pub_keys(self):
        self.assertEqual(self.keylist.get_list_of_pub_keys(), [
            nick_to_pub_key("Alice"),
            nick_to_pub_key("Bob"),
            nick_to_pub_key("Charlie")
        ])

    def test_has_keyset(self):
        self.keylist.keysets = []
        self.assertFalse(self.keylist.has_keyset(nick_to_pub_key("Alice")))

        self.keylist.keysets = [create_keyset('Alice')]
        self.assertTrue(self.keylist.has_keyset(nick_to_pub_key("Alice")))

    def test_has_rx_mk(self):
        self.assertTrue(self.keylist.has_rx_mk(nick_to_pub_key('Bob')))
        self.keylist.get_keyset(
            nick_to_pub_key('Bob')).rx_mk = bytes(SYMMETRIC_KEY_LENGTH)
        self.keylist.get_keyset(
            nick_to_pub_key('Bob')).rx_hk = bytes(SYMMETRIC_KEY_LENGTH)
        self.assertFalse(self.keylist.has_rx_mk(nick_to_pub_key('Bob')))

    def test_has_local_keyset(self):
        self.keylist.keysets = []
        self.assertFalse(self.keylist.has_local_keyset())

        self.assertIsNone(
            self.keylist.add_keyset(LOCAL_PUBKEY, bytes(SYMMETRIC_KEY_LENGTH),
                                    bytes(SYMMETRIC_KEY_LENGTH),
                                    bytes(SYMMETRIC_KEY_LENGTH),
                                    bytes(SYMMETRIC_KEY_LENGTH)))
        self.assertTrue(self.keylist.has_local_keyset())

    def test_manage(self):
        # Test that KeySet for David does not exist
        self.assertFalse(self.keylist.has_keyset(nick_to_pub_key('David')))

        # Test adding KeySet
        self.assertIsNone(
            self.keylist.manage(KDB_ADD_ENTRY_HEADER, nick_to_pub_key('David'),
                                bytes(SYMMETRIC_KEY_LENGTH),
                                bytes(SYMMETRIC_KEY_LENGTH),
                                bytes(SYMMETRIC_KEY_LENGTH),
                                bytes(SYMMETRIC_KEY_LENGTH)))
        self.assertTrue(self.keylist.has_keyset(nick_to_pub_key('David')))

        # Test removing KeySet
        self.assertIsNone(
            self.keylist.manage(KDB_REMOVE_ENTRY_HEADER,
                                nick_to_pub_key('David')))
        self.assertFalse(self.keylist.has_keyset(nick_to_pub_key('David')))

        # Test changing master key
        new_key = SYMMETRIC_KEY_LENGTH * b'\x01'

        self.assertNotEqual(self.master_key.master_key, new_key)
        self.assertIsNone(
            self.keylist.manage(KDB_CHANGE_MASTER_KEY_HEADER,
                                MasterKey(master_key=new_key)))
        self.assertEqual(self.keylist.master_key.master_key, new_key)

        # Test updating key_database with new settings changes database size.
        self.assertEqual(os.path.getsize(self.file_name), 9016)
        self.assertIsNone(
            self.keylist.manage(KDB_UPDATE_SIZE_HEADER,
                                Settings(max_number_of_contacts=100)))
        self.assertEqual(os.path.getsize(self.file_name), 17816)

        # Test invalid KeyList management command raises Critical Error
        with self.assertRaises(SystemExit):
            self.keylist.manage('invalid_key', None)
Exemple #2
0
class TestKeyList(unittest.TestCase):
    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}_keys'
        self.keylist = KeyList(self.master_key, self.settings)
        self.full_contact_list = ['Alice', 'Bob', 'Charlie', LOCAL_ID]
        self.keylist.keysets = [
            create_keyset(n, store_f=self.keylist.store_keys)
            for n in self.full_contact_list
        ]

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

    def test_storing_and_loading_of_keysets(self) -> None:
        # Test store
        self.keylist.store_keys()
        self.assertEqual(
            os.path.getsize(self.file_name), XCHACHA20_NONCE_LENGTH +
            (self.settings.max_number_of_contacts + 1) * KEYSET_LENGTH +
            POLY1305_TAG_LENGTH)

        # Test load
        key_list2 = KeyList(MasterKey(), Settings())
        self.assertEqual(len(key_list2.keysets), len(self.full_contact_list))

    def test_load_of_modified_database_raises_critical_error(self) -> None:
        self.keylist.store_keys()

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

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

    def test_invalid_content_raises_critical_error(self) -> None:
        # Setup
        invalid_data = b'a'
        pt_bytes = b''.join([
            k.serialize_k()
            for k in self.keylist.keysets + self.keylist._dummy_keysets()
        ])
        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):
            KeyList(self.master_key, self.settings)

    def test_generate_dummy_keyset(self) -> None:
        dummy_keyset = self.keylist.generate_dummy_keyset()
        self.assertEqual(len(dummy_keyset.serialize_k()), KEYSET_LENGTH)
        self.assertIsInstance(dummy_keyset, KeySet)

    def test_dummy_keysets(self) -> None:
        dummies = self.keylist._dummy_keysets()
        self.assertEqual(len(dummies),
                         (self.settings.max_number_of_contacts + 1) -
                         len(self.full_contact_list))
        for c in dummies:
            self.assertIsInstance(c, KeySet)

    def test_add_keyset(self) -> None:
        new_key = bytes(SYMMETRIC_KEY_LENGTH)
        self.keylist.keysets = [create_keyset(LOCAL_ID)]

        # Check that KeySet exists and that its keys are different from the new ones
        self.assertNotEqual(self.keylist.keysets[0].rx_hk, new_key)

        # Replace the existing KeySet
        self.assertIsNone(
            self.keylist.add_keyset(LOCAL_PUBKEY, new_key, new_key, new_key,
                                    new_key))

        # Check that the new KeySet replaced the old one
        self.assertEqual(self.keylist.keysets[0].onion_pub_key, LOCAL_PUBKEY)
        self.assertEqual(self.keylist.keysets[0].rx_hk, new_key)

    def test_remove_keyset(self) -> None:
        # Test that the KeySet for Bob exists
        self.assertTrue(self.keylist.has_keyset(nick_to_pub_key('Bob')))

        # Remove the KeySet for Bob
        self.assertIsNone(self.keylist.remove_keyset(nick_to_pub_key('Bob')))

        # Test that the KeySet was removed
        self.assertFalse(self.keylist.has_keyset(nick_to_pub_key('Bob')))

    @mock.patch('builtins.input', side_effect=['test_password'])
    def test_change_master_key(self, _: Any) -> None:
        # Setup
        key = SYMMETRIC_KEY_LENGTH * b'\x01'
        master_key2 = MasterKey(master_key=key)
        queues = gen_queue_dict()

        def queue_delayer() -> None:
            """Place packet to the key management queue after timer runs out."""
            time.sleep(0.1)
            queues[KEY_MANAGEMENT_QUEUE].put(master_key2.master_key)

        threading.Thread(target=queue_delayer).start()

        # Test that the new key is different from the existing one
        self.assertNotEqual(key, self.master_key.master_key)

        # Change the master key
        self.assertIsNone(self.keylist.change_master_key(queues))

        # Test that the master key was changed
        self.assertEqual(self.keylist.master_key.master_key, key)
        self.assertEqual(self.keylist.database.database_key, key)

        self.assertEqual(queues[KEY_MGMT_ACK_QUEUE].get(), KDB_HALT_ACK_HEADER)
        self.assertEqual(queues[KEY_MGMT_ACK_QUEUE].get(), key)

    def test_update_database(self) -> None:
        # Setup
        queues = gen_queue_dict()

        # Test
        self.assertEqual(os.path.getsize(self.file_name), 9016)
        self.assertIsNone(
            self.keylist.manage(queues, KDB_UPDATE_SIZE_HEADER,
                                Settings(max_number_of_contacts=100)))
        self.assertEqual(os.path.getsize(self.file_name), 17816)
        self.assertEqual(self.keylist.settings.max_number_of_contacts, 100)

    def test_get_keyset(self) -> None:
        keyset = self.keylist.get_keyset(nick_to_pub_key('Alice'))
        self.assertIsInstance(keyset, KeySet)

    def test_get_list_of_pub_keys(self) -> None:
        self.assertEqual(self.keylist.get_list_of_pub_keys(), [
            nick_to_pub_key("Alice"),
            nick_to_pub_key("Bob"),
            nick_to_pub_key("Charlie")
        ])

    def test_has_keyset(self) -> None:
        self.keylist.keysets = []
        self.assertFalse(self.keylist.has_keyset(nick_to_pub_key("Alice")))

        self.keylist.keysets = [create_keyset('Alice')]
        self.assertTrue(self.keylist.has_keyset(nick_to_pub_key("Alice")))

    def test_has_rx_mk(self) -> None:
        self.assertTrue(self.keylist.has_rx_mk(nick_to_pub_key('Bob')))
        self.keylist.get_keyset(
            nick_to_pub_key('Bob')).rx_mk = bytes(SYMMETRIC_KEY_LENGTH)
        self.keylist.get_keyset(
            nick_to_pub_key('Bob')).rx_hk = bytes(SYMMETRIC_KEY_LENGTH)
        self.assertFalse(self.keylist.has_rx_mk(nick_to_pub_key('Bob')))

    def test_has_local_keyset(self) -> None:
        self.keylist.keysets = []
        self.assertFalse(self.keylist.has_local_keyset())

        self.assertIsNone(
            self.keylist.add_keyset(LOCAL_PUBKEY, bytes(SYMMETRIC_KEY_LENGTH),
                                    bytes(SYMMETRIC_KEY_LENGTH),
                                    bytes(SYMMETRIC_KEY_LENGTH),
                                    bytes(SYMMETRIC_KEY_LENGTH)))
        self.assertTrue(self.keylist.has_local_keyset())

    def test_manage(self) -> None:
        # Setup
        queues = gen_queue_dict()

        # Test that the KeySet for David does not exist
        self.assertFalse(self.keylist.has_keyset(nick_to_pub_key('David')))

        # Test adding the KeySet for David
        self.assertIsNone(
            self.keylist.manage(queues, KDB_ADD_ENTRY_HEADER,
                                nick_to_pub_key('David'),
                                bytes(SYMMETRIC_KEY_LENGTH),
                                bytes(SYMMETRIC_KEY_LENGTH),
                                bytes(SYMMETRIC_KEY_LENGTH),
                                bytes(SYMMETRIC_KEY_LENGTH)))
        self.assertTrue(self.keylist.has_keyset(nick_to_pub_key('David')))

        # Test removing David's KeySet
        self.assertIsNone(
            self.keylist.manage(queues, KDB_REMOVE_ENTRY_HEADER,
                                nick_to_pub_key('David')))
        self.assertFalse(self.keylist.has_keyset(nick_to_pub_key('David')))

        # Test changing the master key
        new_key = SYMMETRIC_KEY_LENGTH * b'\x01'

        self.assertNotEqual(self.master_key.master_key, new_key)

        queues[KEY_MANAGEMENT_QUEUE].put(new_key)
        self.assertIsNone(
            self.keylist.manage(queues, KDB_M_KEY_CHANGE_HALT_HEADER))

        self.assertEqual(self.keylist.master_key.master_key, new_key)
        self.assertEqual(self.keylist.database.database_key, new_key)

        # Test an invalid KeyList management command raises CriticalError
        with self.assertRaises(SystemExit):
            self.keylist.manage(queues, 'invalid_key', None)