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