Device identity
Each Mac has a static Curve25519 identity key. Host and Client already know the peer identity expected for the session.
Security
Screen Ferry authenticates Host and Client to each other, derives session-specific traffic keys at the endpoints, and seals every application datagram above both relay and P2P carriers.
flowchart LR H["Host · static + fresh ephemeral keys"] R["Relay · validates legs · forwards opaque bytes"] C["Client · static + fresh ephemeral keys"] H ==>|"ChaCha20-Poly1305 sealed session"| C H -. "QUIC relay carrier" .-> R R -. "opaque datagrams" .-> C H -. "qualified direct UDP carrier" .-> C
Each Mac has a static Curve25519 identity key. Host and Client already know the peer identity expected for the session.
A two-message, Noise-KK-shaped X25519 exchange combines ephemeral-static, static-static, ephemeral-ephemeral, and static-ephemeral results.
The relay session ID is mixed into the key-exchange transcript, tying the encrypted channel to the specific brokered session.
Host and Client derive the application traffic keys. The relay holds separate leg-credential key material and cannot decrypt sealed Screen Ferry application datagrams.
ChaCha20-Poly1305
Each sealed datagram authenticates a 64-bit sequence number as associated data. A modified header or payload fails authentication instead of becoming corrupted media or control input.
The receiver maintains a sliding 1,024-datagram replay window. Legitimate network reordering is accepted inside the window; duplicates and over-aged datagrams are rejected.
Key ratcheting
Host-to-Client and Client-to-Host traffic never share direction key material. The authenticated sequence number determines the epoch, allowing both endpoints to advance without renegotiating the session.
Superseded Screen Ferry key boxes are erased during ratchet and teardown. CryptoKit-owned ephemeral private-key references are released when exchange completes or fails.
flowchart LR K0["Direction key · epoch 0"] -->|"2²⁰ datagrams"| K1["Direction key · epoch 1"] K1 -->|"2²⁰ datagrams"| K2["Direction key · epoch 2"] K0 -.-> E0["Superseded material erased"] K1 -.-> E1["Superseded material erased"]
Content-blind relay
The Rust data plane validates credentials bound to session, device, role, and relay leg before forwarding. Its hot path moves the application datagram bytes verbatim.
It handles the connection information required to authenticate devices, route sessions, enforce service controls, and report operational counters. It does not decode Screen Ferry video, audio, input, cursor, clipboard, or control messages.
Same protection on every route
Application sealing lives above the carrier. The same session keys, authenticated sequences, replay defense, and ratchet protect traffic over the QUIC relay and a direct UDP path.
Malformed and unauthenticated traffic is counted and rejected and can trigger route revalidation.