コード例 #1
0
ファイル: test_crypto.py プロジェクト: xprog12/tfc
    def test_non_unique_keys_raise_critical_error(self) -> None:
        # Setup
        shared_key = os.urandom(SYMMETRIC_KEY_LENGTH)
        tx_public_key = os.urandom(TFC_PUBLIC_KEY_LENGTH)

        # Test
        with self.assertRaises(SystemExit):
            X448.derive_keys(shared_key, tx_public_key, tx_public_key)
コード例 #2
0
ファイル: test_crypto.py プロジェクト: xprog12/tfc
    def test_x448_key_derivation(self) -> None:
        # Setup
        shared_key = os.urandom(SYMMETRIC_KEY_LENGTH)
        tx_public_key = os.urandom(TFC_PUBLIC_KEY_LENGTH)
        rx_public_key = os.urandom(TFC_PUBLIC_KEY_LENGTH)

        # Test
        key_set = X448.derive_keys(shared_key, tx_public_key, rx_public_key)

        # Test that correct number of keys were returned
        self.assertEqual(len(key_set), 6)

        # Test that each key is unique
        self.assertEqual(len(set(key_set)), 6)
コード例 #3
0
def start_key_exchange(
    onion_pub_key: bytes,  # Public key of contact's v3 Onion Service
    nick: str,  # Contact's nickname
    contact_list: 'ContactList',  # ContactList object
    settings: 'Settings',  # Settings object
    queues: 'QueueDict'  # Dictionary of multiprocessing queues
) -> None:
    """Start X448 key exchange with the recipient.

    This function first creates the X448 key pair. It then outputs the
    public key to Relay Program on Networked Computer, that passes the
    public key to contact's Relay Program where it is displayed. When
    the contact's public key reaches the user's Relay Program, the user
    will manually type the key into their Transmitter Program.

    The X448 shared secret is used to create unidirectional message and
    header keys, that will be used in forward secret communication. This
    is followed by the fingerprint verification where the user manually
    authenticates the public key.

    Once the fingerprint has been accepted, this function will add the
    contact/key data to contact/key databases, and export that data to
    the Receiver Program on Destination Computer. The transmission is
    encrypted with the local key.

    ---

    TFC provides proactive security by making fingerprint verification
    part of the key exchange. This prevents the situation where the
    users don't know about the feature, and thus helps minimize the risk
    of MITM attack.

    The fingerprints can be skipped by pressing Ctrl+C. This feature is
    not advertised however, because verifying fingerprints the only
    strong way to be sure TFC is not under MITM attack. When
    verification is skipped, TFC marks the contact's X448 keys as
    "Unverified". The fingerprints can later be verified with the
    `/verify` command: answering `yes` to the question on whether the
    fingerprints match, marks the X448 keys as "Verified".

    Variable naming:
        tx = user's key     rx = contact's key    fp = fingerprint
        mk = message key    hk = header key
    """
    if not contact_list.has_pub_key(onion_pub_key):
        contact_list.add_contact(onion_pub_key, nick,
                                 bytes(FINGERPRINT_LENGTH),
                                 bytes(FINGERPRINT_LENGTH), KEX_STATUS_PENDING,
                                 settings.log_messages_by_default,
                                 settings.accept_files_by_default,
                                 settings.show_notifications_by_default)
    contact = contact_list.get_contact_by_pub_key(onion_pub_key)

    # Generate new private key or load cached private key
    if contact.tfc_private_key is None:
        tfc_private_key_user = X448.generate_private_key()
    else:
        tfc_private_key_user = contact.tfc_private_key

    try:
        tfc_public_key_user = X448.derive_public_key(tfc_private_key_user)
        kdk_hash = contact_list.get_contact_by_pub_key(
            LOCAL_PUBKEY).tx_fingerprint
        tfc_public_key_contact = exchange_public_keys(onion_pub_key,
                                                      tfc_public_key_user,
                                                      kdk_hash, contact,
                                                      settings, queues)

        validate_contact_public_key(tfc_public_key_contact)

        dh_shared_key = X448.shared_key(tfc_private_key_user,
                                        tfc_public_key_contact)

        tx_mk, rx_mk, tx_hk, rx_hk, tx_fp, rx_fp \
            = X448.derive_keys(dh_shared_key, tfc_public_key_user, tfc_public_key_contact)

        kex_status = validate_contact_fingerprint(tx_fp, rx_fp)

        deliver_contact_data(KEY_EX_ECDHE, nick, onion_pub_key, tx_mk, rx_mk,
                             tx_hk, rx_hk, queues, settings)

        # Store contact data into databases
        contact.tfc_private_key = None
        contact.tx_fingerprint = tx_fp
        contact.rx_fingerprint = rx_fp
        contact.kex_status = kex_status
        contact_list.store_contacts()

        queues[KEY_MANAGEMENT_QUEUE].put(
            (KDB_ADD_ENTRY_HEADER, onion_pub_key, tx_mk, csprng(), tx_hk,
             csprng()))

        m_print(f"Successfully added {nick}.",
                bold=True,
                tail_clear=True,
                delay=1,
                head=1)

    except (EOFError, KeyboardInterrupt):
        contact.tfc_private_key = tfc_private_key_user
        raise SoftError("Key exchange interrupted.",
                        tail_clear=True,
                        delay=1,
                        head=2)