Conception du protocole

Conception complète du protocole : TLS 1.3, authentification REALITY, Vision, QUIC, cryptographie et automates de connexion.

Version concernée 1.0.0-alphaDernière mise à jour Contenu vérifié

Cette référence technique présente la conception complète du protocole Umbra : un client et un serveur Rust, leurs composants, interfaces, formats réseau et choix de conception. Elle conserve les objectifs de conception et les révisions successives. Pour le déploiement actuel, consultez les guides pratiques ; la structure à un seul crate du §17 et les dépendances du §18 sont des ébauches historiques.

Guide d’utilisation · Architecture actuelle

0. Objectifs et choix d’architecture

Trois décisions structurent l’ensemble de la conception :

  1. Le transport emprunte l’identité d’un site réel, selon le principe de REALITY. L’authentification se trouve dans le ClientHello et le serveur la vérifie avant de répondre. Les connexions non authentifiées ou les sondes sont transmises sans modification, au niveau TCP/UDP, au site de couverture dest. Le correspondant négocie alors avec le vrai site et reçoit son certificat. Le nœud n’a pas besoin de son propre domaine ni d’un certificat d’autorité publique.
  2. Le client utilise une pile TLS 1.3 minimale développée pour le projet, comparable à uTLS en Rust. La négociation principale ne dépend pas de BoringSSL/rustls. Le ClientHello est construit manuellement, avec maîtrise de l’empreinte, de legacy_session_id et de la clé privée X25519 du keyshare ; la dérivation des clés TLS 1.3 est également implémentée. Cela permet de contrôler les octets du profil Chrome et de réutiliser le keyshare pour l’authentification. Le serveur dispose lui aussi d’une pile TLS 1.3 pour terminer localement la négociation et présenter le certificat reproduit.
  3. L’authentification suit la construction REALITY canonique et réutilise l’ECDH du keyshare. shared=X25519(C_priv,S_pub). AES-GCM chiffre le jeton dans session_id, avec le ClientHello complet comme AAD. Un nouveau keyshare est utilisé par connexion ; le jeton est lié à la négociation et présente des octets d’apparence aléatoire.

Le raccordement Vision, le préchargement du profil, mux, QUIC, Geneva, les primitives post-quantiques et le suivi des profils de navigateur font partie de la conception cible ; voir F–K.

1. Modèle de menace : techniques de censure

La conception s’appuie sur les comportements observés et les travaux de l’annexe C :

  • Classification entropique du trafic entièrement chiffré (USENIX Security 2023) : le premier paquet est classé, avec des exceptions pour les contenus imprimables ou de faible entropie. Le premier paquet de SS/obfs4 sans autre enveloppe peut être très entropique. Umbra place les données dans des enregistrements TLS/QUIC pour éviter le motif de texte chiffré brut visé par ce classificateur.
  • Sondage actif (NDSS 2019/2020 ; GFW Report) : connexion à une adresse suspecte, rejeu ou modification des données pour reconnaître un proxy. Une connexion non authentifiée doit être transmise au vrai site.
  • Empreintes TLS dans TLS (USENIX Security 2024) : les longueurs et directions des enregistrements peuvent révéler la négociation interne. F associe raccordement Vision et bourrage adaptatif.
  • Empreintes ClientHello/JA3/JA4 : les écarts avec un navigateur peuvent servir de signal. A/J construisent les octets à partir de profils Chrome.
  • Inspection du SNI et blocage ESNI/ECH : le SNI reste visible ; le nom utilisé est celui d’un vrai site de couverture accessible.
  • Injection TCP RST avec état (Geneva, CCS 2019) : H traite de segmentation TCP/désynchronisation TCB ; G fournit une voie QUIC/UDP distincte.
  • Rejeu : nouveaux keyshares ECDH, fenêtre temporelle et cache de nonces dans B.
  • Blocage résiduel : une adresse IP:port identifiée peut rester bloquée temporairement. Ports multiples, adresses de remplacement, destinations moins courantes et voie QUIC sont des options d’exploitation.
  • Canaux auxiliaires temporels : une sonde peut mesurer le délai avant le premier octet. K rapproche les temps de préparation des voies authentifiée et transférée.

Le modèle suppose l’absence de terminaison TLS MITM à grande échelle par le censeur, qui serait perturbatrice et détectable. E traite séparément l’authentification du certificat face à une interception par connexion. Ce sont des hypothèses et objectifs à vérifier, pas une promesse d’indétectabilité universelle.

2. Principes et influences

PrincipeInspirationProblème visé
TLS/QUIC réel et transfert du trafic non authentifiéREALITYGestion d’un domaine/certificat Trojan ; certificat autosigné visible aux sondes
Authentification dans ClientHello avant toute réponseREALITYAuthentification après exposition du certificat
Maîtrise des octets d’un ClientHello conforme au profil ChromeuTLSDifférences d’une pile TLS généraliste
Réutiliser ECDH pour une clé d’authentification propre à la connexionREALITYRéutiliser directement un mot de passe statique comme clé de connexion
Retirer la couche TLS supplémentaire par Vision lorsque le flux s’y prêteXTLS-VisionMotifs TLS dans TLS d’un tunnel ordinaire
Bourrage adaptatif et multiplexageanytlsUne connexion externe par destination et corrélation de leur nombre
Keyshare frais, horodatage et cache de noncesSS-2022 / VMessRejeu possible dans les anciennes variantes SS
KEM et signatures post-quantiquesChrome PQC / REALITY mldsaRisques à long terme d’une cryptographie exclusivement classique
Politique AEAD interne unique, sans négociation supplémentaireSS-2022Empreintes de négociation et possibilités de repli cryptographique

3. Architecture générale

Architecture générale
Architecture générale Ouvrir le schéma en taille réelle
Voir le code Mermaid
flowchart TB
  APP["Navigateur / application"] -->|SOCKS5| Cs
  subgraph Client["umbra client"]
    Cs["Entrée SOCKS5"] --> Cmux["Couche interne : mux + padding / Vision solo"]
    Cmux --> Ctls["A : client TLS 1.3 dédié<br/>B : authentification REALITY<br/>J : empreinte Chrome"]
    Ctls --> Cout{"G/H : transport externe<br/>Segmentation TCP ou QUIC"}
  end
  Cout ==>|"TLS 1.3 / QUIC, SNI = dest"| Sdisp
  subgraph Server["umbra server"]
    Sdisp["C : répartition<br/>Lire ClientHello et authentifier"] -->|"Échec d’authentification / sonde"| FWD["Relais TCP / UDP"]
    Sdisp -->|Authentification réussie| Sh["E : négociation et certificat temporaire approuvé<br/>D : profil dest préparé"]
    Sh --> Smux["Couche interne : mux + padding / Vision"]
    Smux --> TGT["Site cible"]
  end
  FWD ==> DEST["Site réel de couverture : dest<br/>Certificat CA réel"]

Quatre couches se partagent les responsabilités :

  1. Transport externe (G/H) : TLS 1.3 sur TCP avec la stratégie d’envoi configurée, ou QUIC/HTTP-3 ; SNI désigne le site de couverture.
  2. Authentification de la négociation (A/B) : construction du ClientHello selon le profil Chrome ; sur TCP, authentification via session_id et keyshare.
  3. Répartition et négociation locale (C/D/E) : décision avant réponse ; les clients authentifiés reçoivent un certificat temporaire lié à leur secret, les autres connexions sont transférées à dest.
  4. Transport interne (F) : mux avec bourrage adaptatif ou connexion Vision dédiée ; adresses de destination et données.

Composant A : pile TLS 1.3 minimale, équivalent Rust de uTLS

Objectif : contrôler les octets du ClientHello et la clé privée X25519, appliquer le profil Chrome choisi et prendre en charge B/E.

Périmètre : TLS 1.3 uniquement (RFC 8446), avec les suites et extensions nécessaires au profil, côté client et serveur. TLS 1.2 est exclu.

A.1 Construction du ClientHello octet par octet

  • En-tête d’enregistrement legacy_version=0x0303 ; random de 32 octets produit par CSPRNG ; legacy_session_id de 32 octets fourni par B ; suites, compression nulle et extensions. Le mode de compatibilité Chrome utilise également un identifiant aléatoire de 32 octets, dont le contenu n’entre pas dans JA3/JA4.
  • Ordre des suites : GREASE, TLS_AES_128_GCM_SHA256(0x1301), TLS_AES_256_GCM_SHA384(0x1302), TLS_CHACHA20_POLY1305_SHA256(0x1303).
  • Ensemble et ordre des extensions d’un profil, à vérifier par capture et à actualiser (J) : GREASE → server_name → extended_master_secret → renegotiation_info → supported_groups (X25519MLKEM768,X25519,secp256r1,secp384r1) → ec_point_formats → session_ticket → ALPN(h2,http/1.1) → status_request → signature_algorithms → signed_certificate_timestamp → key_share(GREASE,X25519MLKEM768,X25519) → psk_key_exchange_modes → supported_versions(GREASE,0x0304) → compress_certificate(brotli) → application_settings(ALPS) → GREASE → padding.
  • GREASE : insérer les valeurs RFC 8701 dans les suites, extensions, groupes, keyshares, versions et algorithmes de signature selon les positions et quantités du profil Chrome cible.
  • key_share : inclure l’hybride X25519MLKEM768 (I) et X25519 classique. Générer puis conserver la clé privée classique C_priv pour B.
  • Pour 0x11ec, le client envoie exactement ML-KEM-768 public key(1184) || X25519 public key(32) ; le serveur, ML-KEM-768 ciphertext(1088) || X25519 public key(32), conformément à RFC 10024 §4. La version 0.0.8 corrige l’ancien ordre inversé, incompatible.
  • Bourrage : suivre la distribution de longueurs du profil Chrome, souvent autour de multiples de 512 octets.

A.2 Dérivation des clés et machine à états cliente

Appliquer RFC 8446 :

  • Maintenir Transcript-Hash ; implémenter HKDF-Expand-Label et Derive-Secret.
  • Early=HKDF-Extract(0,PSK|0)Handshake=HKDF-Extract(Derive-Secret(Early,"derived",""),ECDHE)c/s hs traffic, Master, c/s ap traffic, exporter, resumption.
  • TCP et QUIC dérivent c/s ap traffic et exp master depuis la transcription jusqu’à Server Finished. res master utilise la transcription jusqu’à Client Finished ; l’API doit distinguer ces bornes.
  • RFC 8879 encode la longueur de la liste compress_certificate sur uint8. Brotli, algorithme 2, s’encode 02 00 02. Borner tailles compressée et décompressée ; conserver le message CompressedCertificate original dans la transcription.
  • Réassembler les messages entre enregistrements avec des limites et accepter les CCS de compatibilité valides. Ne pas supposer que le serveur envoie deux enregistrements. Vérifier version ServerHello, compression nulle, écho de session ID, unicité des extensions et présence des paramètres sélectionnés dans l’offre initiale. Refuser explicitement un HRR non pris en charge. Vérifier tous les algorithmes de signature TLS 1.3 annoncés avec des implémentations éprouvées ; les algorithmes réservés à TLS 1.2 ne conviennent pas à CertificateVerify TLS 1.3.
  • ECDHE : X25519, réutilisé pour l’authentification, et éventuellement secret hybride X25519MLKEM768 (I).
  • Protection des enregistrements : TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, via RustCrypto aes-gcm/chacha20poly1305. Clé, IV et nonce fondé sur le numéro de séquence sont propres à chaque direction.
  • ClientHello → (ServerHello, EncryptedExtensions, Certificate, CertificateVerify, Finished) → Client Finished. Le mode de compatibilité envoie un ChangeCipherSpec factice selon le profil.
  • Déléguer la validation du certificat à E : temporaire approuvé, site réel ou invalide.

A.3 Pile TLS 1.3 serveur

  • Reprendre via PrefixedStream, qui restitue le ClientHello lu par C. Produire ServerHello avec les paramètres de D, EncryptedExtensions, le certificat généré/reproduit par E, CertificateVerify signé avec la clé de feuille, puis Finished.
  • En mode de compatibilité TCP, legacy_session_id_echo reprend les 32 octets du client. Suite, groupe keyshare et ALPN proviennent de DestProfile. La règle QUIC d’identifiant vide est décrite dans G.

A.4 Modules et signatures

// tls13/clienthello.rs
pub struct ClientHelloParams { pub sni:String, pub session_id:[u8;32], pub x25519_priv:[u8;32],
    pub x25519_pub:[u8;32], pub mlkem: MlkemShare, pub profile: FingerprintProfile }
pub fn build_client_hello(p:&ClientHelloParams) -> Vec<u8>;         // Sérialisation octet par octet ; GREASE/ordre selon le profil

// tls13/handshake.rs (client)
pub struct Tls13Client { /* transcript, secrets, aead ... */ }
impl Tls13Client {
    pub fn start(params:ClientHelloParams) -> (Self, Vec<u8> /*Enregistrement ClientHello*/);
    pub fn drive(&mut self, inbound:&[u8], verify:&dyn CertVerify) -> DriveOut; // Avancer / produire les octets à envoyer / terminer
    pub fn app_seal(&mut self,pt:&[u8])->Vec<u8>; pub fn app_open(&mut self,ct:&[u8])->io::Result<Vec<u8>>;
}
pub trait CertVerify { fn verify(&self, leaf_der:&[u8], chain:&[Vec<u8>]) -> PeerKind; }
pub enum PeerKind { UmbraTrusted, RealSite, Invalid }

// tls13/server.rs (négociation miroir côté serveur)
pub struct Tls13Server { /* ... */ }
impl Tls13Server {
    pub fn accept(chello_raw:&[u8], leaf:ForgedCert, profile:&DestProfile) -> (Self, Vec<u8>);
    pub fn drive(&mut self, inbound:&[u8]) -> DriveOut;
    pub fn app_seal(&mut self,pt:&[u8])->Vec<u8>; pub fn app_open(&mut self,ct:&[u8])->io::Result<Vec<u8>>;
}

Priorité à la construction du ClientHello, à la dérivation des clés et aux enregistrements, vérifiés avec tls.peet.ws et JA4. Si le client annonce la compression Brotli du certificat, il doit savoir décompresser le certificat du correspondant.

Composant B : authentification REALITY par ECDH du keyshare

B.1 Clés et paramètres

  • Le serveur possède les clés statiques X25519 (S_priv,S_pub). S_pub sert d’identifiant d’accès aux clients : le garder confidentiel vis-à-vis des tiers non autorisés, malgré son nom mathématique de clé publique.
  • short_id contient 0 à 8 octets ; le serveur autorise un ensemble et le client choisit une entrée.
  • max_time_diff vaut 120 secondes par défaut. server_names contient les SNI autorisés ; dest vaut host:443 pour le site de couverture.

B.2 Charge utile dans legacy_session_id

Réutiliser (C_priv,C_pub), le keyshare X25519 classique de A :

  1. shared = X25519(C_priv, S_pub) (32 octets).
  2. auth_key = HKDF-SHA256(shared, salt="umbra-reality-v1", info="key")[..16] pour AES-128 ; nonce = HKDF-SHA256(shared, salt="umbra-reality-v1", info="nonce")[..12].
  3. Texte clair de 16 octets : P = ver(1)=0x01 || flags(1) || ts(u32 BE,4) || short_id(8) || reserved(2)=0.
  4. ct||tag = AES-128-GCM-Seal(auth_key, nonce, P, aad=HELLO0). HELLO0 est le message ClientHello complet, avec les 32 octets du session ID mis à zéro, sans en-têtes d’enregistrement TLS. La fragmentation entre enregistrements ne doit pas modifier l’AAD.
  5. Écrire session_id(32B) = ct(16) || tag(16) avant la sérialisation finale et le hachage de la transcription.

B.3 Vérifications serveur avant réponse

Tout échec mène au transfert vers dest par C :

  1. Extraire SNI, C_pub X25519 classique et le session ID de 32 octets ; produire HELLO0 en mettant ce dernier à zéro.
  2. Exiger SNI ∈ server_names.
  3. Calculer shared = X25519(S_priv, C_pub) et dériver auth_key,nonce.
  4. Déchiffrer P = AES-128-GCM-Open(auth_key, nonce, ct=session_id[..16], tag=session_id[16..], aad=HELLO0). Un échec GCM provoque le transfert.
  5. Vérifier version, reserved==0, |now-ts|≤max_time_diff, short ID autorisé et cache antirejeu. Recherche et insertion doivent être atomiques. Conserver l’entrée jusqu’à ts+max_time_diff, borne comprise, plutôt qu’une simple fenêtre après réception ; contrôler les débordements du calcul d’expiration. Si le cache est plein, refuser une nouvelle authentification locale et transférer sans évincer d’entrée valide. Vérifier l’horodatage même après nettoyage du cache.
  6. En cas de succès, transmettre shared à E.

Après correction de HELLO0 et des bornes des secrets applicatifs TLS, mettre à jour les deux extrémités. Ne pas réessayer l’ancien AAD ou l’ancienne dérivation non conforme après un échec.

B.4 Propriétés de sécurité et distribution

  • S_pub et une clé privée cliente fraîche permettent de calculer shared. L’AAD lie le jeton au ClientHello ; nouveaux keyshares, fenêtre temporelle et cache évitent la réutilisation de clés de connexion et le rejeu.
  • Le texte source évoque une confidentialité persistante de l’authentification par connexion. Cela ne constitue pas une garantie générale après compromission de la clé statique du serveur : le modèle précis de compromission doit être évalué séparément.
  • La fuite de S_pub/short_id compromet le contrôle d’accès dans ce modèle. Les distribuer par un canal hors bande fiable.

Composant C : répartition serveur et transfert des sondes

Choisir avant toute réponse TLS locale entre négociation authentifiée locale et transfert.

  1. read_client_hello_raw(conn) lit le ClientHello complet entre plusieurs enregistrements, avec délai global et limites d’octets/enregistrements. En cas d’en-tête ou corps partiel, expiration, dépassement ou EOF, conserver le préfixe déjà lu et le confier au transfert, sans fermer prématurément. L’interdiction concerne la réponse TLS locale avant classification, pas la réponse ultérieure du vrai site. Pour QUIC, voir G.
  2. Analyser SNI, C_pub, session_id avec le parseur interne ou tls-parser.
  3. Vérifier B. Succès : générer leaf = forge_cert(shared, SNI, dest_profile), poursuivre avec Tls13Server::accept(chello_raw, leaf, profile) et PrefixedStream(chello_raw, conn), puis entrer dans F. Échec, SNI incorrect ou rejeu : se connecter à dest, écrire tout le préfixe conservé, puis copy_bidirectional(conn, d). Le correspondant reçoit la vraie négociation et le vrai certificat.
  4. Ni limitation de débit spécifique au proxy ni fermeture anticipée des connexions transférées (K). maxUselessRecords borne la classification ; son dépassement doit rester conforme à la règle de transfert.
pub async fn dispatch(conn: Conn, cfg:&ServerCfg, prof:&DestProfile, replay:&ReplayCache) -> anyhow::Result<()>;
pub struct PrefixedStream<S>{/* Restituer prefix, puis relayer inner */}

Composant D : collecte et préchargement du profil de destination

Objectif : rapprocher la négociation locale des paramètres du vrai site : ServerHello, certificat, temporalité et OCSP.

  • Le démarrage exige un DestProfile validé. prebuild=true active aussi le renouvellement périodique ; false désactive seulement ce renouvellement, jamais la sonde initiale. L’échec initial empêche le démarrage. Le renouvellement remplace atomiquement le profil et conserve le précédent en cas d’échec.
  • Relever version TLS, suite, groupe keyshare, ALPN et EncryptedExtensions ; subject/issuer/validity/SAN/SCT du certificat réel, OCSP stapling et schéma de signature ; délai entre connexion et première réponse TLS, sans l’attente HTTP ultérieure, pour K.
  • DNS, connexion, TLS et métadonnées HTTP partagent un délai total borné. L’I/O synchrone ne doit pas bloquer l’exécuteur async. Une tâche bloquante qui survit à l’expiration reste comptée dans la concurrence limitée.
  • E utilise ce profil pour ServerHello et les champs du certificat. TLS 1.3 chiffre le certificat ; cette reproduction concerne aussi les corrélations plus avancées.
pub struct DestProfile { pub tls_ver:u16, pub cipher:u16, pub group:u16, pub alpn:Vec<Vec<u8>>,
    pub ee_exts:Vec<u16>, pub leaf_template:CertTemplate, pub ocsp:Option<Vec<u8>>, pub rtt:Duration }
pub async fn probe_dest(dest:&str) -> anyhow::Result<DestProfile>;

Composant E : négociation locale et certificat temporaire approuvé

Après authentification, le serveur termine TLS localement, sans transmettre cette négociation à dest. Le client vérifie le certificat grâce à shared.

E.1 Certificat feuille généré

  • Générer une clé éphémère dont la partie privée signe CertificateVerify. Reproduire les champs de DestProfile.leaf_template, dont CN/SAN=server_name et validité.
  • L’extension privée 1.3.6.1.4.1.62397.1 contient cert_mac = HMAC-SHA256(cert_key, leaf_SPKI_DER) (32 octets), avec cert_key = HKDF-SHA256(shared, salt="umbra-cert-v1", info=session_id).
  • L’extension post-quantique (I), OID ...62397.2, contient ML-DSA-65_Sign(mldsa_sk, leaf_SPKI_DER).

E.2 Vérification cliente du certificat

Le client connaît shared, grâce à C_priv, et son session ID :

  1. Dériver cert_key, vérifier le MAC en temps constant et la signature ML-DSA-65 avec mldsa_pk. Ces deux contrôles et CertificateVerify prouvant la possession de la clé privée sont nécessaires pour UmbraTrusted, seule classe autorisant le trafic proxy. Une signature d’autorité publique n’est pas requise pour ce certificat lié au secret.
  2. Sans liaison Umbra valide, vérifier indépendamment chaîne, racines configurées, nom attendu, dates et CertificateVerify. Tous doivent réussir pour RealSite. TCP peut visiter le chemin spider avec le protocole HTTP négocié pris en charge, sans cible proxy ni données applicatives ; un ALPN non pris en charge ne doit pas recevoir de requête mal formée. QUIC RealSite refuse localement le proxy : aucune clé applicative, connexion prête, réussite SOCKS ou préfixe de destination ; aucun repli TCP automatique ni prétention à un spider HTTP/3.
  3. Tout contrôle requis échoué donne Invalid, puis le traitement normal d’erreur TLS et la fermeture.
  4. Clés privées de certificat, clés de liaison, secrets de trafic et journaux de clés des sondes doivent être effacés en mémoire et masqués dans Debug. Les erreurs de configuration, y compris causes imbriquées, ne doivent conserver ni secrets ni extraits TOML.

La résistance à l’interception repose sur la liaison au secret partagé et la preuve du certificat. Un intermédiaire incapable de fournir la liaison requise relève de RealSite ou Invalid selon les contrôles indépendants.

Composant F : multiplexage avec bourrage et raccordement Vision

Après authentification, le transport interne gère adressage, multiplexage et mise en forme TLS dans TLS. Le mode se choisit par connexion :

  • Mux adaptatif par défaut : plusieurs flux logiques par connexion TLS, moins de négociations répétées et de corrélation du nombre de connexions.
  • Vision dédié : un seul flux possède la connexion externe ; bourrage pendant la négociation puis transfert direct des enregistrements TLS internes admissibles, sans seconde enveloppe.

F.1 Format de trame mux

MuxFrame = ver(1) || cmd(1) || stream_id(4,BE) || len(2,BE) || payload(len)
cmd: 0x01 SYN(payload=Adresse de destination) | 0x02 SYN_ACK | 0x03 DATA | 0x04 WINDOW_UPDATE(payload=incrément u32)
     | 0x05 FIN | 0x06 RST | 0x07 PADDING(payload=aléatoire ; ignorer toute la trame) | 0x08 PING
Adresse de destination(SYN.payload) = atyp(1) || addr(4|1+n|16) || port(2)   // 0x01 v4 / 0x03 nom de domaine / 0x04 v6
  • Fenêtre indépendante par flux, par exemple 256 KiB au départ, augmentée par WINDOW_UPDATE ; DATA au plus 16 384 octets.
  • Une connexion SOCKS5 ouvre un flux SYN. Les requêtes compatibles réutilisent une connexion externe saine. Le serveur connecte les destinations en parallèle et n’envoie SYN_ACK qu’après succès.
  • Conserver les lectures de trames partielles et l’état d’écriture sérialisée malgré les annulations. L’ouverture d’un flux ou l’attente de crédit ne doit pas absorber d’autres événements. Terminer une trame partiellement écrite ou fermer ; ne jamais entrelacer ses octets.
  • Le crédit de réception repose sur des réservations de buffers bornés et ne revient qu’après consommation réelle, pas après mise en file. Contrôler débordements, incréments nuls et commandes sur flux inconnus ; limiter flux et mémoire totale.
  • FIN ferme une direction après les DATA déjà en attente. RST réveille tous les demandeurs du flux ; libérer un flux ne termine pas les autres.
  • Une panne externe ne doit pas rejouer automatiquement le trafic. De nouvelles requêtes peuvent ouvrir une connexion de remplacement. Une association UDP stream-zero possède sa connexion et ne partage pas une session CONNECT.

F.1a Contrôle de flux adaptatif, depuis 0.0.9

Un nouveau client TCP CONNECT mux commence par SETTINGS(0x0a, stream=0) : UAF1 puis quatre u32 big-endian, fenêtres initiales de flux et de connexion puis leurs maximums. Valeurs initiales : 256 KiB et 1 MiB ; maximum par flux 32 MiB, maximum de connexion configurable jusqu’à 64 MiB. Réunir la première trame de réglages et le bourrage aléatoire configuré en une écriture, sans modifier le comptage de bourrage des écritures applicatives. Les anciens clients commençant par SYN/UDP conservent le contrôle historique.

CREDIT(0x0b) transporte deux u64 big-endian : limite cumulée d’envoi autorisé et position cumulée consommée par l’application. Stream zéro désigne le total de connexion. DATA respecte les deux crédits ; une augmentation de crédit ne vaut pas accusé de consommation. PROBE(0x0c) / PROBE_ACK(0x0d) transportent un nonce de 8 octets sur stream zéro pour mesurer le RTT externe.

Le récepteur agrandit les fenêtres selon consommation et RTT, après réservation du budget de processus/groupe d’identifiants. Le crédit engagé ne peut être retiré. FIN/RST règlent les positions cumulées en restant sûrs face à l’annulation. Transfert non authentifié et ClientHello restent inchangés. Voir débit et configuration pour réglages, comptabilité mémoire et limites des mesures.

F.2 Schéma de bourrage adaptatif

  • Un schéma configurable, proche d’anytls, définit pour la kième écriture une distribution de longueurs cibles ou une taille PADDING supplémentaire. Le défaut de conception ajoute des PADDING aléatoires aux quelque 16 premiers enregistrements de chaque direction, de longueur [100,1400], autour des premières trames applicatives, afin de perturber les motifs déterministes de la négociation interne.
  • Ensuite, insérer du bourrage avec une probabilité plus faible pour les motifs statistiques de longue durée.

F.3 Raccordement Vision, depuis 0.0.7

Sur TCP, mux=false choisit Vision dédié ; mux=true garde le multiplexage chiffré. Les deux extrémités doivent prendre en charge l’implémentation 0.0.7. L’ancien relais solo et le helper inutilisé ont été supprimés ; aucun interrupteur Vision supplémentaire.

  1. Le mode authentifié distingue solo et mux. Les extrémités de repli anciennes/non authentifiées ne reçoivent ni cible ni commandes applicatives.
  2. Envoyer l’adresse cible puis échanger les capacités authentifiées. Attendre la connexion serveur-cible avant réussite SOCKS ou DATA.
  3. Réassembler dans les deux sens, avec limites, pour reconnaître ClientHello/ServerHello TLS 1.3 valides et enregistrements protégés complets. Garder le chiffrement externe pour les flux non admissibles.
  4. Le client coordonne demande, accusé, engagement et accusé final, en vérifiant les bornes d’octets dans les deux sens. Vider les écritures externes, conserver les préfixes lus, puis transférer le contrôle du TCP brut.
  5. Transférer ensuite les enregistrements internes protégés, sans TLS externe, enveloppe ni bourrage ; maintenir vérification de structure et semi-fermeture.

0x17 peut aussi contenir une négociation chiffrée ou une alerte. L’observation passive ne vérifie pas le Finished interne ni le chiffrement réel d’octets émis par une application malveillante qui imite TLS. Le TLS de bout en bout de l’application reste responsable de l’authentification et de la confidentialité. Le transfert brut utilise l’I/O en espace utilisateur, sans promesse de zéro copie noyau ni de gain fixe.

La spécification wire approuvée détaille format, limites, refus/engagement, EOF, annulation et vecteurs. Les tests utilisent des extrémités rustls indépendantes : 256 KiB intacts dans chaque sens, suffixe réseau identique au chiffré interne et arrêt des appels seal/open externes après bascule. Le texte source documente aussi des requêtes Mac/serveur en 0.0.7 dans le compte rendu de validation.

Mux et raccordement brut s’excluent. QUIC n’emprunte pas cette bascule TCP. Aucune heuristique ne crée automatiquement une autre connexion solo selon le trafic.

F.4 Modules et signatures

// inner/mux.rs
pub struct MuxSession<IO>{/* streams, windows */}
impl<IO:AsyncRead+AsyncWrite> MuxSession<IO>{
  pub fn client(io:IO, pad:&PadScheme)->Self; pub fn server(io:IO, pad:&PadScheme)->Self;
  pub async fn open(&self, dst:&Addr)->Stream;   // Le client ouvre un flux (SYN)
  pub async fn accept(&self)->(Stream, Addr);      // Le serveur accepte le flux
}
// inner/vision.rs
pub async fn vision_relay(tls:TlsIo, target:TcpStream) -> io::Result<()>; // Inspecter → modeler → splice
// inner/padding.rs
pub struct PadScheme{/* Analysé depuis la chaîne de configuration */} pub fn parse_pad_scheme(s:&str)->PadScheme;
// inner/spider.rs
pub async fn spider(tls:TlsIo, spider_path:&str) -> io::Result<()>; // RealSite : visiter comme un navigateur, puis fermer

Composant G : transport externe QUIC / HTTP-3

Objectif de conception : utiliser UDP pour éviter les RST TCP, disposer de flux QUIC indépendants et étudier 0-RTT ; présenter un comportement proche de Chrome QUIC/HTTP-3, puis transporter les destinations authentifiées sur des flux QUIC. 0-RTT et le raccordement direct de flux proposé ici sont des objectifs, pas une liste des fonctions actuelles.

  • QUIC réutilise TLS 1.3 de A. ClientHello circule dans les trames CRYPTO Initial ; les clés Initial dérivent du DCID et d’un sel fixe, donc l’observateur peut le récupérer. supported_versions ne contient que TLS 1.3 et GREASE valide, jamais TLS 1.2 copié du profil TCP. Cette adaptation précède HELLO0/AAD et le jeton. L’API TLS QUIC ne doit pas réécrire silencieusement des paramètres déjà liés à l’authentification.
  • Le profil cible inclut versions QUIC, paramètres de transport et leur ordre, ALPN=h3, extensions dont quic_transport_parameters, longueur SCID et GREASE (J).
  • Support d’authentification différent de TCP : legacy_session_id est vide en QUIC. Le projet prévoit de placer les 32 octets ct||tag dans un paramètre GREASE de style Chrome, avec AAD sur tout ClientHello et ECDH X25519 classique. Numéro et longueur doivent être confrontés aux captures Chrome. Le texte évoque un SCID de 8 octets plus la capacité GREASE restante si nécessaire ; c’est une proposition à rapprocher du support réellement implémenté, pas à modifier isolément.
  • Lire Initial, extraire ClientHello, authentifier. En cas d’échec, transmettre Initial et les datagrammes suivants sans modification au QUIC du vrai site. En cas de succès, négocier localement selon E.
  • Le transport interne cible utiliserait les flux multiplexés de QUIC sans mux supplémentaire et envisage leur transfert direct. Cela n’implique pas la présence du raccordement Vision TCP sur QUIC.
// transport/quic.rs
pub struct QuicFingerprint{/* versions, tparams ordre/valeurs, alpn=h3, grease param */}
pub async fn quic_connect(server:&str, sni:&str, auth:&[u8;32], fp:&QuicFingerprint)->anyhow::Result<QuicConn>;
pub async fn quic_dispatch(dgram_sock:UdpSocket, cfg:&ServerCfg, prof:&DestProfile)->anyhow::Result<()>;

Une pile QUIC interne représente un travail conséquent. Le texte envisage quiche avec BoringSSL personnalisable ou quinn avec un autre fournisseur crypto pour A. Vérifier empreintes et support d’authentification par captures Chrome réelles.

Composant H : segmentation TCP de type Geneva

Prise en charge actuelle : envoi normal off et écritures segmentées ordonnées segment. Aucune stratégie avancée Geneva n’est présentée comme implémentée ou mesurée.

  • La concaténation doit reproduire exactement ClientHello. Une écriture n’équivaut pas nécessairement à un paquet TCP, et la segmentation ne prouve pas la résistance aux perturbations.
  • Rejeter le DSL Geneva non pris en charge lors de la validation ; ne pas le traiter silencieusement comme off. Aucun socket brut privilégié n’est ajouté ici.
  • Seule une erreur récupérable de préparation, avant tout octet envoyé, permet un envoi normal de secours. Après écriture partielle, retourner une erreur de transport ; recommencer doublerait le préfixe.
  • QUIC est une voie UDP distincte ; séparer les validations de transport et d’empreinte.
// transport/geneva.rs
pub struct TcpEvasion{/* strategy */}
pub fn parse_strategy(s:&str)->TcpEvasion;
pub async fn write_client_hello_evasive(sock:&TcpStream, chello:&[u8], ev:&TcpEvasion)->io::Result<()>;

Composant I : X25519MLKEM768 et ML-DSA-65

  • Échange post-quantique : ordre exact du secret hybride ML-KEM-768 shared secret(32) || X25519 shared secret(32), injecté dans TLS 1.3 selon RFC 10024 §4.3. RustCrypto ml-kem fournit ML-KEM-768. L’authentification REALITY réutilise toujours le keyshare X25519 classique séparé (B).
  • Signature de certificat : E ajoute ML-DSA-65 dans une extension privée avec RustCrypto ml-dsa. Le serveur dérive mldsa_sk depuis mldsa_seed, les clients reçoivent mldsa_pk. UmbraTrusted exige à la fois liaison HMAC au secret partagé et vérification ML-DSA.
pub struct MlkemShare{/* encaps key(client) / ciphertext(server) */}
pub fn mlkem_keygen()->(/*ek*/Vec<u8>,/*dk*/Vec<u8>);
pub fn mldsa_keygen_from_seed(seed:&[u8;32])->(/*pk*/Vec<u8>,/*sk*/Vec<u8>);
pub fn mldsa_sign(sk:&[u8], msg:&[u8])->Vec<u8>; pub fn mldsa_verify(pk:&[u8],msg:&[u8],sig:&[u8])->bool;

Composant J : maintenance des profils Chrome

Objectif : une pile personnalisée n’hérite pas automatiquement du comportement Chrome. Les profils doivent être explicites, sous forme de données remplaçables.

  • Décrire toutes les propriétés visibles du ClientHello de la version cible : suites, extensions et ordre, emplacements GREASE, groupes, signatures, ALPN, ALPS, compression, keyshares et bourrage. Pour QUIC, ajouter paramètres et ordre, h3 et GREASE.
  • Capturer un ClientHello Chrome réel, ou utiliser tls.peet.ws/api/all et JA4, puis produire les données du profil. Fournir un ou deux profils stables avec leur version Chrome et les séparer du code pour faciliter les mises à jour.
  • En CI/au démarrage, comparer JA3/JA4 générés au profil et signaler les écarts. Ces contrôles ne couvrent que leurs champs ; le comportement complet exige les captures décrites plus haut.
pub struct FingerprintProfile{/* ciphers, ext_order, grease_slots, groups, sigalgs, alpn, alps, ... */}
pub fn load_profile(name:&str)->FingerprintProfile;    // p. ex. "chrome-latest"
pub fn ja3_ja4(chello:&[u8])->(String,String);          // Pour l’autocontrôle

Composant K : renforcement face aux sondes

  • Temporalité : prendre le délai de D jusqu’à la première réponse TLS, soustraire la préparation locale après classification et attendre seulement le reste positif avant ServerHello. Si le traitement est déjà plus lent, ne pas ajouter d’attente ; l’indiscernabilité doit être mesurée.
  • Enregistrements inutiles : dépasser les limites de classification transfère à dest. Refuser les réglages imposant une fermeture non authentifiée anticipée.
  • Transfert : aucun débit limité spécifiquement par Umbra ni fermeture déclenchée par des données parasites ; conserver la réponse après semi-fermeture de la requête.
  • Spider : après validation complète du certificat, TCP visite spider_path avec l’ALPN pris en charge. QUIC RealSite refuse localement le proxy.
  • Ports/IP : plusieurs points d’écoute et adresses de secours peuvent limiter le blocage résiduel ; les voies TCP/QUIC se configurent séparément.

15. Récapitulatif cryptographique

  • ECDH : X25519 (x25519-dalek) ; KEM hybride : ML-KEM-768 (ml-kem).
  • KDF : HKDF-SHA256 (hkdf + sha2), labels B/E umbra-reality-v1 et umbra-cert-v1.
  • Jeton : AES-128-GCM (aes-gcm), session ID TCP, AAD sur tout ClientHello avec session ID à zéro.
  • Liaison du certificat : HMAC-SHA256 (hmac) et ML-DSA-65 (ml-dsa) ; primitives de comparaison en temps constant (subtle).
  • Enregistrements : AES-128/256-GCM et ChaCha20-Poly1305 (aes-gcm/chacha20poly1305).
  • Aléatoire : CSPRNG du système (rand::rngs::OsRng).
  • Pas de seconde couche AEAD applicative : TLS/QUIC fournit déjà confidentialité et intégrité, sans enveloppe supplémentaire.

16. Spécification de configuration

server.toml

listen        = "0.0.0.0:443"          # TCP ; définir udp_listen séparément pour QUIC
udp_listen    = "0.0.0.0:443"          # Composant G:QUIC/HTTP-3
private_key   = "BASE64(X25519 32B clé privée)"   # umbra keygen
short_ids     = ["", "0123456789abcdef"]
dest          = "www.microsoft.com:443"     # Site de couverture (critères : §20)
server_names  = ["www.microsoft.com"]
max_time_diff = "120s"
mldsa_seed    = "BASE64(32B)"           # Composant I:Graine de signature post-quantique du certificat
prebuild      = true                    # Composant D:Actualisation périodique ; sondage initial requis même avec false
padding_scheme= "default"               # Composant F Politique de remplissage adaptatif
tcp_evasion   = "segment"               # Composant H:off | segment;Geneva DSL n’est pas encore pris en charge

client.toml

server        = "SERVER_IP:443"
transport     = "tcp"                   # tcp | quic
public_key    = "BASE64(X25519 32B clé publique)"   # = clé publique du serveur ; identifiant à garder secret
short_id      = "0123456789abcdef"
server_name   = "www.microsoft.com"     # SNI ; doit figurer dans server_names
fingerprint   = "chrome-latest"         # Composant J Profil
mldsa_verify  = "BASE64(ML-DSA-65 clé publique)" # Composant I Vérification de signature
spider_path   = "/"                     # Utilisé pour RealSite ; varier le chemin par client
socks_listen  = "127.0.0.1:1080"
mux           = true                    # Composant F:mux par défaut ; false=solo/Vision
padding_scheme= "default"
tcp_evasion   = "segment"

17. Organisation Rust et correspondance des modules

Le plan historique à crate unique décrit un binaire et les sous-commandes umbra server|client|keygen. Le dépôt actuel est un workspace Cargo ; sa structure réelle est décrite dans la référence d’architecture. L’arborescence suivante conserve la correspondance composants/modules du document de conception.

umbra/
├── Cargo.toml
├── DESIGN.md
├── fingerprints/            # Composant J:Profils d’empreinte Chrome (fichiers de données)
│   ├── chrome-latest.toml
│   └── chrome-latest-quic.toml
├── examples/{server.toml,client.toml}
└── src/
    ├── main.rs              # Répartition des sous-commandes clap
    ├── config.rs            # Configuration
    ├── tls13/               # Composant A:TLS 1.3 dédié
    │   ├── clienthello.rs   #   Construction de ClientHello octet par octet (GREASE/ordre)
    │   ├── handshake.rs     #   Automate client + dérivation des clés
    │   ├── server.rs        #   Pile TLS miroir côté serveur
    │   ├── records.rs       #   AEAD de la couche enregistrements
    │   ├── keyschedule.rs   #   HKDF-Expand-Label/Derive-Secret
    │   └── parse.rs         #   Analyse de ClientHello (serveur)
    ├── fingerprint/         # Composant J:Chargement du profil + autocontrôle JA3/JA4
    ├── reality/             # Composant B/E
    │   ├── auth.rs          #   Charge d’authentification session_id (seal/open) + ReplayCache
    │   ├── cert.rs          #   Certificat terminal miroir + cert_mac + extension ML-DSA
    │   └── prebuild.rs      #   Composant D:probe_dest / DestProfile
    ├── dispatch.rs          # Composant C:Répartition + PrefixedStream + relais vers dest
    ├── inner/               # Composant F
    │   ├── mux.rs           #   Multiplexage (par défaut)
    │   ├── padding.rs       #   Schéma de remplissage adaptatif
    │   ├── vision.rs        #   Raccordement Vision (solo)
    │   ├── address.rs       #   Encodage/décodage des adresses de destination
    │   └── spider.rs        #   Mode de navigation RealSite
    ├── transport/           # Composant G/H
    │   ├── tcp.rs           #   Transport externe TCP
    │   ├── quic.rs          #   Transport externe QUIC/HTTP-3
    │   └── geneva.rs        #   Segmentation TCP contre l’injection RST
    ├── pq/                  # Composant I:Interfaces mlkem / mldsa
    ├── socks.rs             # Entrée SOCKS5
    ├── relay.rs             # Relais / semi-fermeture
    ├── server.rs / client.rs# Orchestration
    └── replay.rs            # Cache anti-rejeu

Signatures essentielles, en complément des sections précédentes :

// reality/auth.rs
pub fn seal_session_id(shared:&[u8;32], short_id:&[u8], hello0:&[u8], now:u64) -> [u8;32];
pub fn open_session_id(shared:&[u8;32], session_id:&[u8;32], hello0:&[u8],
    allowed:&[Vec<u8>], now:u64, max_diff:u64, replay:&ReplayCache) -> anyhow::Result<AuthOk>;
pub fn cert_mac(shared:&[u8;32], session_id:&[u8;32], spki_der:&[u8]) -> [u8;32];

// Orchestration
pub async fn run_server(cfg:ServerCfg)->anyhow::Result<()>;
pub async fn run_client(cfg:ClientCfg)->anyhow::Result<()>;
pub fn run_keygen();   // Afficher X25519 priv/pub + ML-DSA-65 seed/pub (base64)

18. Dépendances et compilation

Liste illustrative de la conception, ni lockfile actuel ni manifeste Cargo à copier tel quel. Compiler avec le catalogue du workspace et Cargo.lock du dépôt.

[dependencies]
tokio        = { version = "1", features = ["full"] }
x25519-dalek = "2"                      # Composant B ECDH
ml-kem       = "0.2"                     # Composant I ML-KEM-768
ml-dsa       = "0.0"                     # Composant I ML-DSA-65(RustCrypto ; vérifier version/disponibilité)
aes-gcm      = "0.10"                     # session_id + Enregistrements TLS
chacha20poly1305 = "0.10"                 # Enregistrements TLS
hkdf         = "0.12"
sha2         = "0.10"
hmac         = "0.12"
subtle       = "2"                        # Temps constant
rand         = "0.8"
tls-parser   = "0.11"                     # Analyse serveur de ClientHello (ou parse.rs dédié)
rcgen        = "0.13"                     # Composant E Générer le certificat terminal + extensions personnalisées
socket2      = "0.5"                      # Composant H Segmentation simple (IP_TTL/NODELAY/découpage manuel)
# Composant G (au choix):quiche = "..." (basé sur BoringSSL, empreinte personnalisable) ou quinn = "..." (nécessite de remplacer le fournisseur crypto)
base64="0.22"
serde={version="1",features=["derive"]}
toml="0.8"
humantime-serde="1"
clap={version="4",features=["derive"]}
anyhow="1"
thiserror="1"
tracing="0.1"
tracing-subscriber={version="0.3",features=["env-filter"]}
lru="0.12"

Points d’implémentation :

  • BoringSSL/rustls n’assure pas la négociation TLS principale de cette conception ; rcgen génère le DER du certificat feuille.
  • Vérifier A/G avec de vraies captures Chrome, tls.peet.ws/api/all et JA4 ; actualiser les profils avec le navigateur.
  • Les stratégies H avancées nécessiteraient CAP_NET_RAW/sockets bruts. La portée actuelle est celle de H ; le repli n’est autorisé qu’avant tout octet envoyé.
  • Aligner versions des crates ML-KEM/ML-DSA et identifiants de protocole sur les normes applicables et le profil cible, à l’aide de spécifications et captures actuelles.

19. Tests et validation

  • Tests unitaires : seal/open session ID, altération, expiration, rejeu, changement AAD ; MAC et ML-DSA positifs/négatifs ; vecteurs HKDF/enregistrements RFC 8448.
  • Empreintes : JA3/JA4 du ClientHello face au profil Chrome ; contrôles QUIC distincts.
  • Interopérabilité : négociation complète client/serveur internes et client avec de vrais sites TLS 1.3 par transfert/spider.
  • Sondes actives : openssl s_client ou entrées aléatoires doivent suivre le comportement du site réel, avec son certificat lorsque pertinent, sans différence de fermeture anticipée ou bridage propre au proxy.
  • Rejeu : réémettre un ClientHello capturé et vérifier le transfert vers dest.
  • TLS dans TLS : observer longueur/direction des 8–16 premiers enregistrements de chaque sens ; variation sous mux/bourrage et correspondance avec TLS interne après Vision.
  • Temporalité : comparer les distributions de délai au premier octet des deux chemins.
  • Interférences TCP et QUIC : mesurer séparément connectivité/stabilité sur réseaux dégradés ou perturbés.
  • Déploiements réels autorisés : connexions longues, gros transferts, réseaux faibles et blocage résiduel.

20. Déploiement et choix de destination

Critères du site de couverture dans la conception :

  • Site externe accessible, TLS 1.3 et H2/H3, domaine utilisé pour le service plutôt que seulement pour une redirection.
  • De préférence proche du réseau serveur, avec comportement de négociation adapté — le texte cite les messages chiffrés après ServerHello de dl.google.com — et OCSP stapling si disponible.
  • Le texte évoque aussi la restriction du trafic de retour vers les réseaux censurés, le transfert TCP/80 et UDP/443 lorsque nécessaire, et une IP moins courante ou plus stable. Ce sont des choix de déploiement, pas des réglages automatiques.
  • Renseigner les noms autorisés dans server_names et un server_name client correspondant.

Exploitation : 443 TCP/UDP est courant. Éviter les plages IP inadaptées ; protéger private_key/mldsa_seed, distribuer public_key, mldsa_verify, short_id par canaux fiables. Envisager BBR, un ulimit -n adapté, systemd et journald sans journaliser destinations ou trafic utilisateur par défaut. Préparer au besoin ports/IP de secours et voie QUIC validée séparément.

21. Sécurité et exploitation responsable

  • Finalité : confidentialité, résistance à la censure et accès à l’internet ouvert, dans le respect du droit applicable.
  • Secrets : protéger S_priv/mldsa_seed ; distribuer S_pub, short ID et clé de vérification ML-DSA par canaux fiables.
  • Employer des primitives éprouvées en temps constant pour MAC/tags et vérification cryptographique.
  • Borner ReplayCache et nettoyer les entrées expirées contre l’épuisement mémoire.
  • Fixer dest par configuration fiable, pas par entrée utilisateur, pour éviter une SSRF de repli.
  • Verrouiller Cargo.lock, auditer les dépendances et maintenir les données post-quantiques et profils en suivant l’amont.

Annexe A : formats binaires

Jeton TCP de style REALITY dans legacy_session_id, 32 octets

ÉtapeCalcul
sharedX25519(C_priv,S_pub) ; serveur : X25519(S_priv,C_pub)
auth_keyHKDF-SHA256(shared,"umbra-reality-v1","key")[..16] (AES-128)
nonceHKDF-SHA256(shared,"umbra-reality-v1","nonce")[..12]
P (16B)ver(1) || flags(1) || ts(u32 BE,4) || short_id(8) || reserved(2)=0
AADClientHello complet, 32 octets de session ID à zéro : HELLO0
session_id (32B)ct(16) || tag(16) = AES-128-GCM-Seal(auth_key,nonce,P,HELLO0)

Liaison du certificat temporaire

ÉlémentCalcul
cert_keyHKDF-SHA256(shared,"umbra-cert-v1", session_id) (32B)
cert_macHMAC-SHA256(cert_key, leaf_SPKI_DER) (32B), OID …62397.1
pq_sigML-DSA-65_Sign(mldsa_sk, leaf_SPKI_DER), OID …62397.2

MuxFrame

DécalageChampLongueurSignification
0ver10x01
1cmd1SYN/SYN_ACK/DATA/WINDOW_UPDATE/FIN/RST/PADDING/PING
2stream_id4Big-endian
6len2Big-endian
8payloadlenSYN=destination ; DATA=données ; PADDING=octets aléatoires

Adresse cible : atyp(1) \|\| addr(4 / 1+n / 16) \|\| port(2, BE).

Annexe B : machines à états et flux de données

Répartition et négociation serveur

Automate du serveur
Automate du serveur Ouvrir le schéma en taille réelle
Voir le code Mermaid
stateDiagram-v2
    state "Lire ClientHello" as Read
    state "Analyser SNI, keyshare et le support d’authentification" as Parse
    state "Vérifier le jeton d’authentification" as Verify
    state "Relayer vers dest" as Forward
    state "Terminer la négociation localement" as Handshake
    state "Aligner les délais" as Timing
    state "Envoyer le certificat temporaire approuvé" as Certificate
    state "Couche interne mux / Vision" as Inner
    [*] --> Read
    Read --> Parse
    Parse --> Forward: SNI incorrect / keyshare absent
    Parse --> Verify: Paramètres valides
    Verify --> Forward: Échec GCM / délai / short_id / anti-rejeu
    Verify --> Handshake: Accepté ; shared disponible
    Handshake --> Timing: dest.rtt moins le traitement local
    Timing --> Certificate: cert_mac + ML-DSA-65
    Certificate --> Inner
    Inner --> [*]: Flux terminé
    Forward --> [*]: Relais bidirectionnel avec le site réel

Client

Automate du client
Automate du client Ouvrir le schéma en taille réelle
Voir le code Mermaid
stateDiagram-v2
    state "Choisir TCP / QUIC" as Transport
    state "Construire ClientHello" as Hello
    state "Faire avancer la négociation" as Handshake
    state "Vérifier et classer le certificat" as Certificate
    state "Proxy interne" as Inner
    state "RealSite : vérifier le transport" as RealSite
    state "TCP : visiter le site selon le protocole négocié" as Spider
    state "QUIC : refuser le proxy" as Reject
    [*] --> SOCKS5
    SOCKS5 --> Transport
    Transport --> Hello: Empreinte A et support d’authentification B/G
    Hello --> Handshake
    Handshake --> Certificate
    Certificate --> Inner: UmbraTrusted : liaison et ML-DSA validées
    Certificate --> RealSite: RealSite : validation classique du certificat réussie
    Certificate --> [*]: Invalid : erreur TLS
    RealSite --> Spider: TCP et ALPN pris en charge
    RealSite --> Reject: QUIC
    Inner --> [*]: Flux terminé
    Spider --> [*]: Fermer après la visite
    Reject --> [*]: Ne pas envoyer de données proxy

Annexe C : références

  1. Wu et al. How the Great Firewall of China Detects and Blocks Fully Encrypted Traffic. USENIX Security 2023.
  2. Frolov, Wustrow. The use of TLS in Censorship Circumvention. NDSS 2019.
  3. Frolov et al. Detecting Probe-Resistant Proxies. NDSS 2020.
  4. Fingerprinting Obfuscated Proxy Traffic with Encapsulated TLS Handshakes. USENIX Security 2024.
  5. Bock et al. Geneva: Evolving Censorship Evasion Strategies. ACM CCS 2019.
  6. GFW Report / net4people. How China Detects and Blocks Shadowsocks. 2020.
  7. XTLS/REALITY, Xray-core transport/internet/reality, XTLS-Vision (xtls-rprx-vision), VLESS.
  8. anytls / anytls-go : bourrage et multiplexage.
  9. uTLS (refraction-networking/utls) ; empreintes QUIC et comportement Chrome.
  10. RFC 8446 (TLS 1.3), RFC 8448 (vecteurs), RFC 8701 (GREASE), RFC 9000/9001 (QUIC/QUIC-TLS).
  11. RFC 10024 (X25519MLKEM768) ; FIPS 203 (ML-KEM), FIPS 204 (ML-DSA).
  12. Bibliothèques Rust : x25519-dalek, ml-kem, ml-dsa, aes-gcm, chacha20poly1305, hkdf, rcgen, quiche/quinn, socket2, tls-parser.

Valeurs, seuils, ordre des extensions et identifiants de protocole dépendent de la révision et du profil. Censure, Chrome et normes post-quantiques évoluent. Vérifier spécifications et captures actuelles avant implémentation et maintenir les profils de J.

Sur cette page

0. Objectifs et choix d’architecture1. Modèle de menace : techniques de censure2. Principes et influences3. Architecture généraleComposant A : pile TLS 1.3 minimale, équivalent Rust de uTLSA.1 Construction du ClientHello octet par octetA.2 Dérivation des clés et machine à états clienteA.3 Pile TLS 1.3 serveurA.4 Modules et signaturesComposant B : authentification REALITY par ECDH du keyshareB.1 Clés et paramètresB.2 Charge utile dans legacy_session_idB.3 Vérifications serveur avant réponseB.4 Propriétés de sécurité et distributionComposant C : répartition serveur et transfert des sondesComposant D : collecte et préchargement du profil de destinationComposant E : négociation locale et certificat temporaire approuvéE.1 Certificat feuille généréE.2 Vérification cliente du certificatComposant F : multiplexage avec bourrage et raccordement VisionF.1 Format de trame muxF.1a Contrôle de flux adaptatif, depuis 0.0.9F.2 Schéma de bourrage adaptatifF.3 Raccordement Vision, depuis 0.0.7F.4 Modules et signaturesComposant G : transport externe QUIC / HTTP-3Composant H : segmentation TCP de type GenevaComposant I : X25519MLKEM768 et ML-DSA-65Composant J : maintenance des profils ChromeComposant K : renforcement face aux sondes15. Récapitulatif cryptographique16. Spécification de configuration17. Organisation Rust et correspondance des modules18. Dépendances et compilation19. Tests et validation20. Déploiement et choix de destination21. Sécurité et exploitation responsableAnnexe A : formats binairesAnnexe B : machines à états et flux de donnéesAnnexe C : références