-
Notifications
You must be signed in to change notification settings - Fork 18
6. Advanced Topics — Device Link
Device Link lets a desktop vault be unlocked with the approval of a paired mobile device on the same local network. The phone acts as an authenticator holding a secret on the desktop's behalf and provisions an authentication value only after the user approves a prompt. This section describes the protocol and its cryptographic model. The two participants are:
- SecureFolderFS Desktop — the device on which the vault is found. It always initiates discovery, pairing, and authentication.
- SecureFolderFS Mobile — the device that stores the credential and answers authentication requests. It listens for connections and prompts the user.
Device Link operates entirely over the local network with no server or extranet dependency.
-
Discovery uses UDP broadcast on port 41234. The desktop broadcasts a discovery request (a fixed magic value
SecureFolderFS-DeviceLink, a protocol version byte, and its name). Every listening mobile endpoint replies with its device ID, name, type, and the TCP port to connect on. - Communication uses TCP on port 41235. Each message is framed with a 4-byte little-endian length prefix followed by the payload, where the first byte is the message type.
Note
Neither the discovery magic nor the TCP connection authenticates the peer on its own. The authentication is established cryptographically during pairing and attestation, as described below.
Every pairing and every authentication session performs a fresh hybrid key exchange that combines a classical and a post-quantum primitive:
- ECDH (Elliptic-Curve Diffie–Hellman) over the prime256v1 curve provides forward secrecy.
- ML-KEM-768 (Module-Lattice-Based Key-Encapsulation Mechanism; FIPS 203 — CRYSTALS-Kyber) provides post-quantum confidentiality. The initiator's side encapsulates against the responder's ML-KEM public key, producing a ciphertext and a shared secret; only the holder of the ML-KEM private key can decapsulate the same secret.
The two shared secrets are combined with HKDF-SHA256:
hybridSecret = HKDF(ikm = ecdhSecret, salt = mlKemSecret, info = "DeviceLink-HybridSharedSecret-v1")
Because the ECDH secret is the input keying material and the ML-KEM secret is the salt, both components must be reproduced to derive the same hybrid secret. An adversary must break prime256v1 and ML-KEM-768 to recover it, so the channel is secure against both classical and future quantum attackers. Fresh ECDH and ML-KEM key pairs are generated per session, so compromising one session reveals nothing about any other.
Note
While the official specification for CRYSTALS-Kyber suggests that the algorithm may be used on its own and provides no mathematical or cryptographic advantage when used in combination with ECDH, the hybrid approach used in SecureFolderFS provides dual protection against a hypothetical scenario wherein a flaw were to be discovered in ML-KEM-768.
Once a hybrid secret is established, an AES-256-GCM channel protects all subsequent traffic. Its key is derived as:
channelKey = HKDF(ikm = hybridSecret, salt = sessionNonces, info = "DeviceLink-Encryption-v1" || bindingSecret)
- Session nonces A 16-byte random value from each side, concatenated desktop-then-mobile, which salts the derivation so each session key is unique.
-
Binding secret is an optional long-term value folded into the HKDF
infoparameter. It is used during authentication (not pairing) and is the mechanism that ties an otherwise-anonymous ephemeral handshake to an established pairing. See Authentication below.
Each encrypted message carries a 12-byte nonce (a 4-byte monotonic sequence number followed by 8 random bytes), the ciphertext, and a 16-byte GCM tag. The sequence number drives a 64-message sliding-window replay filter: a sequence that has already been seen, or that falls before the window, is rejected before decryption is attempted. Combined with the per-session key, this prevents both in-session replay and cross-session replay (a captured message fails authentication under a different session key).
After the hybrid exchange during pairing, both sides derive a 6-digit code from the shared secret:
code = HKDF(ikm = sharedSecret, info = "DeviceLink-VerificationCode-v1") mod 1,000,000
The code is shown on both devices, and the user confirms they match. An active man-in-the-middle (MITM) who completes separate handshakes with each side would produce different shared secrets and therefore different codes, which the user would notice. The user's comparison is what authenticates the pairing — without it, the ephemeral exchange is anonymous.
Pairing binds a vault to a mobile credential. It is initiated by the desktop while the mobile is listening with Device Link setting enabled.
-
Request. The desktop generates ECDH and ML-KEM key pairs and sends a new pairing request carrying its name, type, and both public keys.
-
Response. The mobile generates its own ECDH key pair, derives the ECDH secret, and encapsulates against the desktop's ML-KEM public key. It combines the hybrid secret and replies with a pairing response carrying its ECDH public key and the ML-KEM ciphertext.
-
Verification. The desktop decapsulates to obtain the same hybrid secret. Both sides compute the 6-digit verification code and display it. The user confirms the codes match on both devices. Either side rejecting aborts the pairing.
-
Confirmation. Once the code is confirmed, both sides open an AES-256-GCM channel keyed on the hybrid secret. No binding secret exists at this stage, as the SAS (Short-Authenticated-String, i.e. the verification code) comparison already authenticated the exchange. The desktop sends an encrypted pairing confirmation containing the credential ID, vault name, pairing ID, and the vault's persistent challenge.
-
Credential creation. The mobile generates a fresh random 256-bit HMAC key, encrypts it with AES-256-GCM under a key derived from the hybrid secret, and persists it (the encryption key is stored in the device's secure property store). It records the challenge and pairing metadata.
-
Completion. The mobile computes two independent, domain-separated values from its HMAC key and returns both in an encrypted pairing completion response:
- the key contribution
K = HMAC-SHA256(hmacKey, credentialId || challenge)— the secret that the Device Link factor contributes to the vault's key derivation; - the binding secret
B = HMAC-SHA256(hmacKey, "DeviceLink-Binding-v1" || credentialId || challenge)— used to authenticate future authentication sessions.
The desktop feeds
Kinto the vault's key derivation transiently and persists the Device Link configuration (pairing ID, credential ID, mobile device identity, challenge, the binding secret, and a SHA-256 hash ofKas a verifier) in the vault folder.Kitself is never written to disk on the desktop; conversely, every unlock must obtain it from the mobile device. Because the two derivations are domain-separated, knowing the stored binding secret reveals nothing aboutK. - the key contribution
Authentication is initiated by the desktop each time the vault is unlocked with the Device Link factor. The desktop discovers the paired mobile by its stored device ID (retrying a few times to tolerate transient network conditions).
- Session request. The desktop generates fresh ephemeral ECDH and ML-KEM key pairs plus a session nonce and sends a new session creation request carrying the pairing ID, the nonce, and both public keys.
-
Session acceptance and binding. The mobile looks up the credential by pairing ID, performs the hybrid exchange, and generates its own session nonce. Importantly, it then decrypts its stored HMAC key, recomputes the binding secret
B, and folds it into the channel key. An acceptance response is sent which includes the nonce, ECDH public key, and the ML-KEM ciphertext. -
Channel binding on the desktop. The desktop derives the same hybrid secret and builds its channel using its stored binding secret. Because only a device that holds the credential's HMAC key can reproduce
B, the binding authenticates the otherwise-anonymous ephemeral handshake to the established pairing. A man-in-the-middle or a Device ID impersonator that lacks the secret derives a different channel key, so every AES-256-GCM operation fails and it can neither read the challenge nor forge a response. - Authentication request. The desktop sends a secure authentication request containing the credential ID, the challenge, a current timestamp, and a fresh, randomly generated request nonce.
- Validation and approval. The mobile rejects the request if the timestamp is outside a 30-second validity window, if the credential ID does not match, or if the challenge does not match the stored one. Otherwise, it prompts the user to approve.
-
Authentication response. On approval, the mobile recomputes the key contribution
Kand returns it encrypted as a secure authentication response, together with the echoed request nonce, proving the response was produced for this exact request rather than any earlier one. -
Verification. The desktop decrypts the response, verifies the echoed nonce against the one it sent, and verifies the
SHA-256of the received contribution against its stored verifier. Only then isKhanded back as the Device Link factor's key contribution and fed into the vault's KDF pipeline.
- Mobile. For each pairing, the mobile stores the credential metadata (credential ID, pairing ID, vault name, desktop identity, challenge) and the HMAC key encrypted with AES-256-GCM. The key that decrypts it is held separately in either Keychain or KeyStore, associated by the pairing ID. The raw HMAC key is decrypted only transiently to compute an HMAC and is zeroed immediately afterward.
- Desktop. The vault folder holds a Device Link configuration file containing the pairing ID, credential ID, mobile device identity, the persistent challenge, the binding secret, and the SHA-256 verifier of the key contribution. The key contribution itself is never persisted on the desktop — it exists there only transiently, between the phone's approval and the vault unlock.
Important
Device Link is designed for convenience on a trusted local network, and its guarantees should be understood in that context.
-
A vault-folder thief can still ask the phone to unlock. The vault key contribution is never stored on the desktop, so copying the vault folder alone is not enough to unlock it. However, the folder does contain the binding secret and challenge, which is everything needed to impersonate the desktop towards the phone. An attacker holding a copy of the vault folder, on the same network as the phone, can initiate an authentication session that looks legitimate. The remaining defense is the approval prompt; An unlock request appearing on the phone when the user did not initiate one should always be rejected.
-
The key contribution is a stable value. Because it must reproduce the same vault key on every unlock, the contribution the phone releases is identical each time, as this is inherent to key material, not a protocol weakness. Per-unlock freshness is provided at the protocol level instead: each request carries a fresh nonce that the response must echo inside the authenticated channel, and each session uses a unique channel key.
-
Local-network trust. Any device on the network can complete discovery and open a TCP connection. Pairing is protected by the user's verification-code comparison, and authentication is protected by the channel binding; neither pairing nor authentication can succeed without those, but the listener does accept and process unauthenticated connection attempts.
-
The verification code is 6 digits. This is a deliberate usability trade-off. An active MITM attack has a one-in-a-million chance of producing matching codes on both devices; the security of pairing depends on the user actually comparing them.
-
Metadata is observable. Device names, types, and the existence of pairings are visible to network observers during discovery, and the vault's Device Link configuration file reveals the paired device's identity.