Skip to content

Key Exchange Flow

Yura Filatov edited this page Jun 27, 2026 · 1 revision

Key Exchange Flow

The exchange uses two Apple frameworks in concert: MultipeerConnectivity (MC) for peer discovery and data transport, and NearbyInteraction (NI) for proximity measurement. On iOS 26+, a third phase adds ML-KEM-1024 mutual encapsulation for post-quantum protection.

Phase 1 — Discovery (MC):
  1. Both devices advertise and browse on the local network.
  2. On mutual discovery, an MC session is established.
  3. Both devices exchange NI discovery tokens over MC.

Phase 2 — Proximity Gate (NI):
  4. NI session starts using the received discovery token.
  5. UWB measures the distance between the devices.
  6. When distance ≤ 0.25m, the proximity gate opens.
  7. The P-256 public key is transmitted over MC.
  8. On iOS 26+: the ML-KEM-1024 encapsulation key is transmitted alongside.

Phase 3 — ML-KEM Encapsulation (iOS 26+ only):
  9. Each device encapsulates against the other's ML-KEM public key.
  10. Ciphertexts are exchanged over MC.
  11. Each device decapsulates the received ciphertext using the SE private key.
  12. Both sides now hold two independent ML-KEM shared secrets.

Phase 4 — Verification:
  13. Both devices derive Diceware verification words from the combined key material.
  14. Users read words aloud and confirm they match.
  15. On confirmation, the contact (keys + ML-KEM material) is stored.

Security Properties of the Exchange

  • The P-256 and ML-KEM public keys are only transmitted after NI confirms distance ≤ 25 cm.
  • Each device uses a random UUID as its MC peer display name, preventing fingerprinting across sessions.
  • A MITM guard checks that the inbound identity packet came from the same MC peer ID confirmed by UWB proximity. The peer ID is set the moment NI confirms proximity, before key generation begins.
  • Phase 3 is skipped entirely when exchanging with a v1 peer (no ML-KEM public key received).
  • The ML-KEM-1024 private key lives in the Secure Enclave for the duration of the exchange and is released after decapsulation.
  • All delegate callbacks (MC and NI) are dispatched to the main queue for thread safety and @Observable correctness.

Clone this wiki locally