Exemple #1
0
 def test_invalid_digest_size_raises_critical_error(self) -> None:
     for invalid_digest_size in [
             -1, BLAKE2_DIGEST_LENGTH_MIN - 1, BLAKE2_DIGEST_LENGTH_MAX + 1,
             1000
     ]:
         with self.assertRaises(SystemExit):
             blake2b(self.test_string, digest_size=invalid_digest_size)
Exemple #2
0
    def test_invalid_size_blake2b_digest_raises_critical_error(
            self, mock_blake2b: MagicMock) -> None:
        with self.assertRaises(SystemExit):
            blake2b(self.test_string)
        with self.assertRaises(SystemExit):
            blake2b(self.test_string)

        mock_blake2b.assert_called()
Exemple #3
0
    def test_invalid_size_blake2b_digest_raises_critical_error(
            self, mock_blake2b):
        with self.assertRaises(SystemExit):
            blake2b(b'test_string')
        with self.assertRaises(SystemExit):
            blake2b(b'test_string')

        mock_blake2b.assert_called()
Exemple #4
0
    def test_x448_with_the_official_test_vectors(self) -> None:
        sk_alice_ = X448PrivateKey.from_private_bytes(TestX448.sk_alice)
        sk_bob_ = X448PrivateKey.from_private_bytes(TestX448.sk_bob)

        self.assertEqual(X448.derive_public_key(sk_alice_), TestX448.pk_alice)
        self.assertEqual(X448.derive_public_key(sk_bob_), TestX448.pk_bob)

        shared_secret1 = X448.shared_key(sk_alice_, TestX448.pk_bob)
        shared_secret2 = X448.shared_key(sk_bob_, TestX448.pk_alice)

        self.assertEqual(shared_secret1, blake2b(TestX448.shared_secret))
        self.assertEqual(shared_secret2, blake2b(TestX448.shared_secret))
Exemple #5
0
def rxp_load_psk(window:       'TxWindow',
                 contact_list: 'ContactList',
                 settings:     'Settings',
                 queues:       'QueueDict',
                 ) -> None:
    """Send command to Receiver Program to load PSK for active contact."""
    if settings.traffic_masking:
        raise SoftError("Error: Command is disabled during traffic masking.", head_clear=True)

    if window.type == WIN_TYPE_GROUP or window.contact is None:
        raise SoftError("Error: Group is selected.", head_clear=True)

    if not contact_list.get_contact_by_pub_key(window.uid).uses_psk():
        raise SoftError(f"Error: The current key was exchanged with {ECDHE}.", head_clear=True)

    c_code  = blake2b(window.uid, digest_size=CONFIRM_CODE_LENGTH)
    command = KEY_EX_PSK_RX + c_code + window.uid
    queue_command(command, settings, queues)

    while True:
        try:
            purp_code = ask_confirmation_code('Receiver')
            if purp_code == c_code.hex():
                window.contact.kex_status = KEX_STATUS_HAS_RX_PSK
                contact_list.store_contacts()
                raise SoftError(f"Removed PSK reminder for {window.name}.", tail_clear=True, delay=1)

            m_print("Incorrect confirmation code.", head=1)
            print_on_previous_line(reps=4, delay=2)

        except (EOFError, KeyboardInterrupt):
            raise SoftError("PSK install verification aborted.", tail_clear=True, delay=1, head=2)
Exemple #6
0
    def load_master_key(self) -> bytes:
        """Derive the master key from password and salt.

        Load the salt, hash, and key derivation settings from the login
        database. Derive the purported master key from the salt and
        entered password. If the BLAKE2b hash of derived master key
        matches the hash in the login database, accept the derived
        master key.
        """
        database_data = self.database.load_database()

        if len(database_data) != MASTERKEY_DB_SIZE:
            raise CriticalError(f"Invalid {self.file_name} database size.")

        salt, key_hash, time_bytes, memory_bytes, parallelism_bytes \
            = separate_headers(database_data, [ARGON2_SALT_LENGTH, BLAKE2_DIGEST_LENGTH,
                                               ENCODED_INTEGER_LENGTH, ENCODED_INTEGER_LENGTH])

        time_cost   = bytes_to_int(time_bytes)
        memory_cost = bytes_to_int(memory_bytes)
        parallelism = bytes_to_int(parallelism_bytes)

        while True:
            password = MasterKey.get_password()
            phase("Deriving master key", head=2, offset=len("Password correct"))
            purp_key = argon2_kdf(password, salt, time_cost, memory_cost, parallelism)

            if blake2b(purp_key) == key_hash:
                phase("Password correct", done=True, delay=1)
                clear_screen()
                return purp_key

            phase("Invalid password", done=True, delay=1)
            print_on_previous_line(reps=5)
Exemple #7
0
 def test_rotate_tx_mk(self) -> None:
     self.assertIsNone(self.keyset.rotate_tx_mk())
     self.assertEqual(self.keyset.tx_mk, blake2b(bytes(SYMMETRIC_KEY_LENGTH) + int_to_bytes(INITIAL_HARAC),
                                                 digest_size=SYMMETRIC_KEY_LENGTH))
     self.assertEqual(self.keyset.rx_mk, bytes(SYMMETRIC_KEY_LENGTH))
     self.assertEqual(self.keyset.tx_hk, bytes(SYMMETRIC_KEY_LENGTH))
     self.assertEqual(self.keyset.rx_hk, bytes(SYMMETRIC_KEY_LENGTH))
     self.assertEqual(self.keyset.tx_harac, 1)
     self.assertEqual(self.keyset.rx_harac, INITIAL_HARAC)
Exemple #8
0
class TestInputLoop(unittest.TestCase):

    conf_code = blake2b(nick_to_pub_key('Alice'),
                        digest_size=CONFIRM_CODE_LENGTH).hex()
    input_list = [
        '61',  # Enter Relay confirmation code
        '61',  # Enter Receiver confirmation code
        nick_to_onion_address("Alice"),  # Enter rx-account for new contact
        'Alice',  # Enter nick for contact
        '',  # Enter to default for ECDHE
        VALID_ECDHE_PUB_KEY,  # Enter public key for contact
        'Yes',  # Accept key fingerprints for Alice
        conf_code,  # Confirmation code
        'Alice',  # Select Alice as the recipient
        'Test',  # Send test message
        '/file',  # Open file selection prompt
        '',  # Give empty string to abort
        '/exit'
    ]  # Enter exit command

    def setUp(self) -> None:
        """Pre-test actions."""
        self.settings = Settings(disable_gui_dialog=True)
        self.gateway = Gateway()
        self.contact_list = ContactList()
        self.group_list = GroupList()
        self.master_key = MasterKey()
        self.onion_service = OnionService()
        self.queues = gen_queue_dict()

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

    @mock.patch('builtins.input', side_effect=input_list)
    @mock.patch('os.fdopen', MagicMock())
    @mock.patch('os.getrandom', lambda n, flags: n * b'a')
    @mock.patch('os.urandom', lambda n: n * b'a')
    @mock.patch('shutil.get_terminal_size', return_value=[200, 200])
    @mock.patch('src.transmitter.commands.exit_tfc', side_effect=SystemExit)
    @mock.patch('sys.stdin', MagicMock())
    @mock.patch('time.sleep', return_value=None)
    @mock.patch('os.system', return_value=None)
    def test_input_loop_functions(self, *_: Any) -> None:
        with self.assertRaises(SystemExit):
            self.assertIsNone(
                input_loop(self.queues,
                           self.settings,
                           self.gateway,
                           self.contact_list,
                           self.group_list,
                           self.master_key,
                           self.onion_service,
                           stdin_fd=1))
Exemple #9
0
    def store_unencrypted_database(self, data: bytes) -> None:
        """Store unencrypted data into database.

        For future integrity check, concatenate the BLAKE2b
        digest of the database content to the database file.
        """
        ensure_dir(DIR_USER_DATA)
        self.ensure_temp_write(data + blake2b(data))

        # Replace the original file with a temp file. (`os.replace` is atomic as per
        # POSIX requirements): https://docs.python.org/3/library/os.html#os.replace
        os.replace(self.database_temp, self.database_name)