xdeskv1.0.0
ConceptsConcepts

How pairing worksComment marche l'appairage

How a 9-character code proves who you are without ever being sent, and why a man in the middle or a guesser gets nothing.Comment un code de 9 caractères prouve qui tu es sans jamais être envoyé, et pourquoi un homme du milieu ou un devineur n'obtient rien.

In one sentence: the client proves it knows the code without sending it, and the proof only works on the connection it was made for, so nobody in between can reuse it.

Definition: a PAKE-like proof over TLS 1.3: proof = HMAC(Argon2id(code, salt), transcript), with the transcript bound to both nonces and to sha256(certificate) (tls-server-end-point, RFC 5929); the host answers with its own proof (mutual authentication).

En une phrase : le client prouve qu'il connaît le code sans l'envoyer, et la preuve ne marche que sur la connexion pour laquelle elle a été faite : personne au milieu ne peut la réutiliser.

Définition : une preuve proche d'un PAKE au-dessus de TLS 1.3 : preuve = HMAC(Argon2id(code, sel), transcript), le transcript étant lié aux deux nonces et à sha256(certificat) (tls-server-end-point, RFC 5929) ; l'hôte répond avec sa propre preuve (authentification mutuelle).

1. The ideaL'idée

Two people meet and must check they know the same password, in a room where someone may listen. Instead of saying it, each one writes a sum that only works with the password and today's date and room number. The listener learns nothing, and the note is useless in another room.

A shared low-entropy secret (45 bits) authenticates both sides of an already encrypted channel. The secret is stretched with Argon2id (m = 64 MiB, t = 3, p = 1, 16-byte salt per code) so each guess is expensive, then used as an HMAC key over a transcript that includes both nonces (no replay) and the fingerprint of the certificate the client sees (no relay: a man in the middle presents another certificate, so the proof does not match).

Deux personnes doivent vérifier qu'elles connaissent le même mot de passe, dans une pièce où quelqu'un écoute peut-être. Au lieu de le dire, chacune écrit un calcul qui ne marche qu'avec le mot de passe et la date du jour et le numéro de la pièce. L'espion n'apprend rien, et le papier ne sert à rien dans une autre pièce.

Un secret partagé de faible entropie (45 bits) authentifie les deux côtés d'un canal déjà chiffré. Le secret est étiré avec Argon2id (m = 64 Mio, t = 3, p = 1, sel de 16 octets par code) pour que chaque essai coûte cher, puis sert de clé HMAC sur un transcript qui contient les deux nonces (pas de rejeu) et l'empreinte du certificat vu par le client (pas de relais : un homme du milieu présente un autre certificat, la preuve ne correspond plus).

2. How it worksComment ça marche

  1. TLS. The client opens TLS 1.3 with the hybrid X25519MLKEM768 key exchange and checks the certificate against the fingerprint it pinned (first use: shown on both sides).
  2. hello. The client sends its kind, name, OS, device id and a random nonce.
  3. challenge. The host answers with its own nonce and the 16-byte Argon2id salt of the current code.
  4. auth. The client sends HMAC(Argon2id(code, salt), transcript): the transcript holds both nonces and the SHA-256 of the certificate it sees. The code itself never travels.
  5. pending. The proof is right; the device is unknown, so the person at the PC is asked (30 s, refused by default).
  6. welcome. Accepted: the session gets its rights, and the host proves it knows the code too, so a fake host is caught.
  7. Session. Frames, inputs and terminals flow on the same TLS connection (the video may move to encrypted UDP).
client host TLS 1.3, X25519MLKEM768 hello (nonce, device) challenge (nonce, salt) auth (proof) pending (approval) welcome (perms, proof) frames, inputs, terminals
  1. TLS. Le client ouvre TLS 1.3 avec l’échange de clés hybride X25519MLKEM768 et vérifie le certificat avec l’empreinte qu’il a épinglée (première fois : affichée des deux côtés).
  2. hello. Le client envoie son type, son nom, son OS, l’id de l’appareil et un nonce aléatoire.
  3. challenge. L’hôte répond avec son propre nonce et le sel Argon2id de 16 octets du code actuel.
  4. auth. Le client envoie HMAC(Argon2id(code, sel), transcript) : le transcript contient les deux nonces et le SHA-256 du certificat qu’il voit. Le code lui-même ne circule jamais.
  5. pending. La preuve est bonne ; l’appareil est inconnu, la personne devant le PC est donc sollicitée (30 s, refusé par défaut).
  6. welcome. Accepté : la session reçoit ses droits, et l’hôte prouve qu’il connaît aussi le code, pour démasquer un faux hôte.
  7. Session. Images, entrées et terminaux circulent sur la même connexion TLS (la vidéo peut passer en UDP chiffré).
client hôte TLS 1.3, X25519MLKEM768 hello (nonce, appareil) challenge (nonce, sel) auth (preuve) pending (approbation) welcome (droits, preuve) images, entrées, terminaux

3. How strongQuelle solidité

There are about 35 trillion possible codes. One address gets 5 tries, then it is banned for an hour and the code changes; the code also changes every 5 minutes. Guessing is hopeless.

QuantityValue
Code space32^9 = 2^45 = 35,184,372,088,832
Online tries per address5 (maxAuthFailures), then a 1 h ban and a new code
Chance that 5 tries hit one code5 / 2^45 ≈ 1.42e-13
Code lifetime300 s (codeLifetime), and one use
Offline guessingimpossible: an observer sees only TLS; the proof needs the transcript of a TLS session it is not part of

Il y a environ 35 000 milliards de codes possibles. Une adresse a 5 essais, puis elle est bannie une heure et le code change ; le code change aussi toutes les 5 minutes. Deviner est sans espoir.

GrandeurValeur
Espace des codes32^9 = 2^45 = 35 184 372 088 832
Essais en ligne par adresse5 (maxAuthFailures), puis un ban d'1 h et un nouveau code
Chance que 5 essais tombent sur un code5 / 2^45 ≈ 1,42e-13
Durée de vie du code300 s (codeLifetime), et un seul usage
Attaque hors ligneimpossible : un observateur ne voit que TLS ; la preuve demande le transcript d'une session TLS dont il ne fait pas partie

4. Trusted devicesAppareils de confiance

"Accept & trust" gives the device a 256-bit secret, used with the same HMAC proof instead of the code: it reconnects without code. The registry (devices.json) is signed with an HMAC key (registry.key, mode 0600): edited outside xdesk, it is refused and every trusted device must pair again.

« Accepter et faire confiance » donne à l'appareil un secret de 256 bits, utilisé avec la même preuve HMAC à la place du code : il se reconnecte sans code. Le registre (devices.json) est signé avec une clé HMAC (registry.key, mode 0600) : modifié hors de xdesk, il est refusé et chaque appareil de confiance doit être réappairé.

5. PitfallsPièges

  • Browsers cannot read the certificate: they send the code inside TLS (method = code), without the binding. Prefer the native clients on an untrusted network.
  • cloudflared terminates TLS at the relay: the relay sees the traffic. Every other exposure mode is end to end.
  • xdesk --regen-cert makes a new identity: the native clients refuse it until the new fingerprint is confirmed.
  • Les navigateurs ne peuvent pas lire le certificat : ils envoient le code dans TLS (method = code), sans la liaison. Préfère les clients natifs sur un réseau non sûr.
  • cloudflared termine TLS au relais : le relais voit le trafic. Tous les autres modes d'exposition sont de bout en bout.
  • xdesk --regen-cert crée une nouvelle identité : les clients natifs la refusent tant que la nouvelle empreinte n'est pas confirmée.

6. Check yourselfVérifie-toi

An attacker relays the TLS connection between the client and the host. Why does the proof fail?

The client sees the attacker's certificate: its SHA-256 is in the transcript, so the proof is computed for that connection, not for the host's.

Why Argon2id and not a plain hash of the code?

A 45-bit code is small: each guess must cost 64 MiB of memory and three passes, so trying many is slow.

What does the host's own proof in the welcome protect against?

A fake host: without the code (or the device secret) it cannot produce it.

Un attaquant relaie la connexion TLS entre le client et l'hôte. Pourquoi la preuve échoue-t-elle ?

Le client voit le certificat de l'attaquant : son SHA-256 est dans le transcript, la preuve est donc calculée pour cette connexion, pas pour celle de l'hôte.

Pourquoi Argon2id et pas un simple hash du code ?

Un code de 45 bits est petit : chaque essai doit coûter 64 Mio de mémoire et trois passes, pour qu'en essayer beaucoup soit lent.

Contre quoi protège la preuve de l'hôte dans le welcome ?

Un faux hôte : sans le code (ou le secret de l'appareil) il ne peut pas la produire.

7. GlossaryGlossaire

TermMeaning
Noncea random value used once, so an old message can't be replayed
Saltrandom bytes mixed with the code before hashing (16 bytes per code)
Transcriptthe label xdesk-auth-v1 and the role, both nonces, the certificate fingerprint
Pinningremembering the host fingerprint at first use and refusing any other
ML-KEM-768the post-quantum key encapsulation of the hybrid TLS group

Sources: include/xdesk/security/Crypto.hpp (XDESK_ARGON2_*, auth_transcript), PairingCode.hpp, RFC 5929 (channel bindings), RFC 9106 (Argon2).

TermeSens
Nonceune valeur aléatoire utilisée une fois, pour qu'un vieux message ne puisse pas être rejoué
Seldes octets aléatoires mélangés au code avant le hash (16 octets par code)
Transcriptle label xdesk-auth-v1 et le rôle, les deux nonces, l'empreinte du certificat
Épinglageretenir l'empreinte de l'hôte à la première connexion et refuser toute autre
ML-KEM-768l'encapsulation de clé post-quantique du groupe TLS hybride

Sources : include/xdesk/security/Crypto.hpp (XDESK_ARGON2_*, auth_transcript), PairingCode.hpp, RFC 5929 (liaisons de canal), RFC 9106 (Argon2).