Conception du protocole
Conception complète du protocole : TLS 1.3, authentification REALITY, Vision, QUIC, cryptographie et automates de connexion.
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 :
- 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. - 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_idet 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. - 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 danssession_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
| Principe | Inspiration | Problème visé |
|---|---|---|
| TLS/QUIC réel et transfert du trafic non authentifié | REALITY | Gestion d’un domaine/certificat Trojan ; certificat autosigné visible aux sondes |
| Authentification dans ClientHello avant toute réponse | REALITY | Authentification après exposition du certificat |
| Maîtrise des octets d’un ClientHello conforme au profil Chrome | uTLS | Différences d’une pile TLS généraliste |
| Réutiliser ECDH pour une clé d’authentification propre à la connexion | REALITY | Ré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ête | XTLS-Vision | Motifs TLS dans TLS d’un tunnel ordinaire |
| Bourrage adaptatif et multiplexage | anytls | Une connexion externe par destination et corrélation de leur nombre |
| Keyshare frais, horodatage et cache de nonces | SS-2022 / VMess | Rejeu possible dans les anciennes variantes SS |
| KEM et signatures post-quantiques | Chrome PQC / REALITY mldsa | Risques à long terme d’une cryptographie exclusivement classique |
| Politique AEAD interne unique, sans négociation supplémentaire | SS-2022 | Empreintes de négociation et possibilités de repli cryptographique |
3. Architecture générale
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 :
- 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.
- Authentification de la négociation (A/B) : construction du ClientHello selon le profil Chrome ; sur TCP, authentification via
session_idet keyshare. - 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. - 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;randomde 32 octets produit par CSPRNG ;legacy_session_idde 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 classiqueC_privpour B. - Pour
0x11ec, le client envoie exactementML-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émenterHKDF-Expand-LabeletDerive-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 trafficetexp masterdepuis la transcription jusqu’à Server Finished.res masterutilise la transcription jusqu’à Client Finished ; l’API doit distinguer ces bornes. - RFC 8879 encode la longueur de la liste
compress_certificatesur uint8. Brotli, algorithme 2, s’encode02 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 RustCryptoaes-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_echoreprend 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.wset 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_pubsert 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_idcontient 0 à 8 octets ; le serveur autorise un ensemble et le client choisit une entrée.max_time_diffvaut 120 secondes par défaut.server_namescontient les SNI autorisés ;destvauthost:443pour le site de couverture.
B.2 Charge utile dans legacy_session_id
Réutiliser (C_priv,C_pub), le keyshare X25519 classique de A :
shared = X25519(C_priv, S_pub)(32 octets).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].- Texte clair de 16 octets :
P = ver(1)=0x01 || flags(1) || ts(u32 BE,4) || short_id(8) || reserved(2)=0. 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.- É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 :
- Extraire SNI,
C_pubX25519 classique et le session ID de 32 octets ; produire HELLO0 en mettant ce dernier à zéro. - Exiger
SNI ∈ server_names. - Calculer
shared = X25519(S_priv, C_pub)et dériverauth_key,nonce. - 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. - 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. - 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_pubet une clé privée cliente fraîche permettent de calculershared. 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_idcompromet 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.
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.- Analyser
SNI, C_pub, session_idavec le parseur interne outls-parser. - Vérifier B. Succès : générer
leaf = forge_cert(shared, SNI, dest_profile), poursuivre avecTls13Server::accept(chello_raw, leaf, profile)etPrefixedStream(chello_raw, conn), puis entrer dans F. Échec, SNI incorrect ou rejeu : se connecter àdest, écrire tout le préfixe conservé, puiscopy_bidirectional(conn, d). Le correspondant reçoit la vraie négociation et le vrai certificat. - Ni limitation de débit spécifique au proxy ni fermeture anticipée des connexions transférées (K).
maxUselessRecordsborne 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=trueactive aussi le renouvellement périodique ;falsedé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_nameet validité. - L’extension privée
1.3.6.1.4.1.62397.1contientcert_mac = HMAC-SHA256(cert_key, leaf_SPKI_DER)(32 octets), aveccert_key = HKDF-SHA256(shared, salt="umbra-cert-v1", info=session_id). - L’extension post-quantique (I), OID
...62397.2, contientML-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 :
- Dériver
cert_key, vérifier le MAC en temps constant et la signature ML-DSA-65 avecmldsa_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. - 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.
- Tout contrôle requis échoué donne Invalid, puis le traitement normal d’erreur TLS et la fermeture.
- 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.
- 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.
- Envoyer l’adresse cible puis échanger les capacités authentifiées. Attendre la connexion serveur-cible avant réussite SOCKS ou DATA.
- 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.
- 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.
- 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 fermerComposant 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_versionsne 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 dontquic_transport_parameters, longueur SCID et GREASE (J). - Support d’authentification différent de TCP :
legacy_session_idest vide en QUIC. Le projet prévoit de placer les 32 octetsct||tagdans 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
quicheavec BoringSSL personnalisable ouquinnavec 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. RustCryptoml-kemfournit 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érivemldsa_skdepuismldsa_seed, les clients reçoiventmldsa_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/allet 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ôleComposant 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_pathavec 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/Eumbra-reality-v1etumbra-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 chargeclient.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-rejeuSignatures 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 ;
rcgengénère le DER du certificat feuille. - Vérifier A/G avec de vraies captures Chrome,
tls.peet.ws/api/allet 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_clientou 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_nameset unserver_nameclient 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; distribuerS_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
destpar 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
| Étape | Calcul |
|---|---|
| shared | X25519(C_priv,S_pub) ; serveur : X25519(S_priv,C_pub) |
| auth_key | HKDF-SHA256(shared,"umbra-reality-v1","key")[..16] (AES-128) |
| nonce | HKDF-SHA256(shared,"umbra-reality-v1","nonce")[..12] |
| P (16B) | ver(1) || flags(1) || ts(u32 BE,4) || short_id(8) || reserved(2)=0 |
| AAD | ClientHello 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ément | Calcul |
|---|---|
| cert_key | HKDF-SHA256(shared,"umbra-cert-v1", session_id) (32B) |
| cert_mac | HMAC-SHA256(cert_key, leaf_SPKI_DER) (32B), OID …62397.1 |
| pq_sig | ML-DSA-65_Sign(mldsa_sk, leaf_SPKI_DER), OID …62397.2 |
MuxFrame
| Décalage | Champ | Longueur | Signification |
|---|---|---|---|
| 0 | ver | 1 | 0x01 |
| 1 | cmd | 1 | SYN/SYN_ACK/DATA/WINDOW_UPDATE/FIN/RST/PADDING/PING |
| 2 | stream_id | 4 | Big-endian |
| 6 | len | 2 | Big-endian |
| 8 | payload | len | SYN=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
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
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
- Wu et al. How the Great Firewall of China Detects and Blocks Fully Encrypted Traffic. USENIX Security 2023.
- Frolov, Wustrow. The use of TLS in Censorship Circumvention. NDSS 2019.
- Frolov et al. Detecting Probe-Resistant Proxies. NDSS 2020.
- Fingerprinting Obfuscated Proxy Traffic with Encapsulated TLS Handshakes. USENIX Security 2024.
- Bock et al. Geneva: Evolving Censorship Evasion Strategies. ACM CCS 2019.
- GFW Report / net4people. How China Detects and Blocks Shadowsocks. 2020.
- XTLS/REALITY, Xray-core
transport/internet/reality, XTLS-Vision (xtls-rprx-vision), VLESS. - anytls / anytls-go : bourrage et multiplexage.
- uTLS (
refraction-networking/utls) ; empreintes QUIC et comportement Chrome. - RFC 8446 (TLS 1.3), RFC 8448 (vecteurs), RFC 8701 (GREASE), RFC 9000/9001 (QUIC/QUIC-TLS).
- RFC 10024 (X25519MLKEM768) ; FIPS 203 (ML-KEM), FIPS 204 (ML-DSA).
- 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.