Protocol design

Complete protocol design: TLS 1.3, REALITY authentication, Vision, QUIC, cryptography and connection state machines.

Applies to 1.0.0-alphaLast updated Content reviewed

This technical reference presents the complete Umbra protocol design: one Rust client and server, with components, interfaces, wire formats and design decisions. It preserves the target design and subsequent revisions. Use the deployment guides for current setup instructions; the single-crate layout in §17 and dependency list in §18 are historical sketches.

Usage guide · Current architecture

0. Overall goals and architectural decisions

Three architectural decisions govern the entire design:

  1. Transport borrows a real site's identity, in the style of REALITY. Authentication is carried in the ClientHello and checked before the server responds. Unauthenticated or probing connections are forwarded unchanged at the TCP/UDP layer to the real cover destination, dest, so the peer completes the real site's handshake and sees its certificate. The node does not need its own domain or CA certificate.
  2. The client uses a purpose-built minimal TLS 1.3 stack: a Rust counterpart to uTLS. The primary handshake does not depend on BoringSSL/rustls. It constructs ClientHello manually, controlling the fingerprint, legacy_session_id and X25519 keyshare private key, and implements the TLS 1.3 key schedule. This gives the design control over Chrome-profile bytes and authentication that reuses the keyshare. The server also uses a purpose-built TLS 1.3 stack for the locally terminated handshake and mirrored certificate.
  3. Authentication follows the canonical REALITY construction, reusing keyshare ECDH. shared=X25519(C_priv,S_pub). AES-GCM encrypts the token into session_id, with the full ClientHello as AAD. The design uses a fresh keyshare per connection, binds the token to the handshake and makes its bytes appear random.

Vision splicing, prebuild, mux, QUIC, Geneva, post-quantum primitives and browser-profile tracking are all parts of the target design; see components F–K.

1. Threat model: censorship techniques

The design draws on observed behavior and the research listed in Appendix C:

  • Entropy classification of fully encrypted traffic (USENIX Security 2023): a classifier examines the first packet of each flow, with exemptions for printable or low-entropy content. Bare SS/obfs4 traffic can have a high-entropy first packet. Umbra instead places traffic inside TLS/QUIC protocol framing; the design aims to avoid the bare-ciphertext pattern addressed by that classifier.
  • Active probing (NDSS 2019/2020; GFW Report): a censor connects to a suspected endpoint and replays or mutates traffic to distinguish proxies. Unauthenticated connections must be forwarded to the real cover site.
  • TLS-in-TLS fingerprints (USENIX Security 2024): inner TLS handshakes can expose characteristic record lengths and directions. Component F combines Vision splicing with adaptive padding.
  • ClientHello/JA3/JA4 fingerprints: deviations from browser behavior can be signals. Components A/J provide byte-level construction driven by Chrome profiles.
  • SNI inspection and ESNI/ECH blocking: SNI is visible. The design uses the name of a reachable real cover site as SNI.
  • Stateful TCP RST injection (Geneva, CCS 2019): component H concerns TCP segmentation/TCB desynchronization; component G provides a separate QUIC/UDP path.
  • Replay: fresh ECDH keyshares, a timestamp window and nonce cache are handled by component B.
  • Residual blocking: an identified IP:port may remain blocked temporarily. Multiple ports, replaceable endpoints, less common destination IPs and a QUIC alternative are operational options.
  • Timing side channels: probes can measure time to first byte. Component K aligns preparation timing between authenticated and forwarded connections.

The threat model assumes no large-scale TLS MITM termination by the censor, which would be disruptive and detectable. Component E separately addresses certificate authentication against per-connection interception. These are design assumptions and verification goals, not a claim of universal undetectability.

2. Design principles and influences

PrincipleInspirationFailure mode addressed
Real TLS/QUIC to a real site; forward unauthenticated trafficREALITYOwning and maintaining a Trojan domain/CA certificate; exposing a self-signed certificate to probes
Authenticate inside ClientHello, before respondingREALITYAuthentication after the handshake, when a certificate has already been exposed
Byte-level control of a Chrome-shaped ClientHellouTLSDifferences introduced by a general-purpose TLS stack
Reuse keyshare ECDH for a fresh connection-specific authentication keyREALITYReusing a static password directly as the connection key
Use Vision splicing to remove the additional TLS record layer when eligibleXTLS-VisionTLS-in-TLS record patterns in an ordinary tunnel
Adaptive padding and multiplexinganytlsOne outer connection per destination and correlated connection counts
Replay checks: fresh keyshare, timestamp and nonce cacheSS-2022 / VMessReplay exposure in older SS variants
Post-quantum KEM and signaturesChrome PQC / REALITY mldsaLong-term risks of relying only on classical cryptography
A single strong inner AEAD policy, without extra negotiationSS-2022Additional downgrade or negotiation fingerprints

3. Overall architecture

Overall architecture
Overall architecture Open full-size diagram
View Mermaid source
flowchart TB
  APP["Browser / application"] -->|SOCKS5| Cs
  subgraph Client["umbra client"]
    Cs["SOCKS5 inbound"] --> Cmux["Inner: mux + padding / Vision solo"]
    Cmux --> Ctls["A: custom TLS 1.3 client<br/>B: REALITY authentication<br/>J: Chrome fingerprint"]
    Ctls --> Cout{"G/H: outer transport<br/>TCP segmentation or QUIC"}
  end
  Cout ==>|"TLS 1.3 / QUIC, SNI = dest"| Sdisp
  subgraph Server["umbra server"]
    Sdisp["C: dispatch<br/>Read ClientHello and authenticate"] -->|"Authentication failure / probe"| FWD["TCP / UDP forwarding"]
    Sdisp -->|Authenticated| Sh["E: handshake and temporary trusted certificate<br/>D: prebuilt dest profile"]
    Sh --> Smux["Inner: mux + padding / Vision"]
    Smux --> TGT["Target site"]
  end
  FWD ==> DEST["Real cover site: dest<br/>Real CA certificate"]

The four layers have distinct responsibilities:

  1. Outer transport (G/H): TLS 1.3 over TCP, with the configured TCP sending strategy, or QUIC/HTTP-3; SNI names the cover destination.
  2. Handshake authentication (A/B): the custom TLS stack constructs the Chrome-profile ClientHello; TCP authentication uses session_id and keyshare.
  3. Dispatch and local handshake (C/D/E): decide before responding; authenticated clients complete a local handshake with a temporary trusted certificate, while other connections are forwarded to dest.
  4. Inner transport (F): mux with adaptive padding, or a dedicated Vision connection; carries destination addresses and data.

Component A: minimal TLS 1.3 stack, a Rust counterpart to uTLS

Purpose: control ClientHello bytes and the X25519 keyshare private key, implement the selected Chrome profile, and support components B/E.

Scope: TLS 1.3 only (RFC 8446), with the suites and extensions needed for the selected Chrome profile. Provide client and server stacks; TLS 1.2 is outside this scope.

A.1 Byte-level ClientHello construction

  • Record-layer legacy_version=0x0303; 32-byte CSPRNG random; 32-byte legacy_session_id supplied by component B; then cipher_suites, null compression and extensions. Chrome compatibility mode also uses a random 32-byte session ID; its contents are not part of JA3/JA4.
  • Chrome suite order: GREASE, TLS_AES_128_GCM_SHA256(0x1301), TLS_AES_256_GCM_SHA384(0x1302), TLS_CHACHA20_POLY1305_SHA256(0x1303).
  • The extension set and order below describe a profile, subject to real captures and updates (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: inject RFC 8701 values in suites, extensions, supported groups, keyshare, supported versions and signature algorithms as required by the target Chrome profile. Positions and counts must match that profile.
  • key_share: include hybrid X25519MLKEM768 (I) and classical X25519. Generate and retain the classical private key C_priv for authentication in B.
  • For group 0x11ec, the client value is exactly ML-KEM-768 public key(1184) || X25519 public key(32); the server value is ML-KEM-768 ciphertext(1088) || X25519 public key(32), following RFC 10024 §4. Version 0.0.8 corrected the earlier reversed order; the incorrect format is incompatible.
  • Padding: shape ClientHello lengths according to the selected Chrome profile, commonly around multiples of 512 bytes.

A.2 TLS 1.3 key schedule and client state machine

Implement RFC 8446:

  • Maintain Transcript-Hash; implement HKDF-Expand-Label and 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.
  • Both TCP and QUIC derive c/s ap traffic and exp master using the transcript through Server Finished. res master uses the transcript through Client Finished. APIs must distinguish these boundaries.
  • RFC 8879 compress_certificate uses a uint8 algorithm-list length. Brotli algorithm 2 is encoded as 02 00 02. Bound both compressed and decompressed sizes; retain the original CompressedCertificate message in the transcript.
  • Reassemble handshake messages across records with explicit bounds and accept valid compatibility CCS. Do not assume the server always sends two records. Validate ServerHello version, null compression, session-ID echo, extension uniqueness and that selected parameters were actually offered. Explicitly reject unsupported HRR. Use established implementations to verify every advertised TLS 1.3 signature algorithm; TLS-1.2-only algorithms cannot verify TLS 1.3 CertificateVerify.
  • ECDHE uses X25519, whose classical keyshare is reused for authentication, and optionally the X25519MLKEM768 hybrid secret (I).
  • Record protection supports TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384 and TLS_CHACHA20_POLY1305_SHA256, using RustCrypto aes-gcm/chacha20poly1305. Each direction has its own key, IV and sequence-number nonce.
  • Messages: ClientHello → (ServerHello, EncryptedExtensions, Certificate, CertificateVerify, Finished) → Client Finished. Compatibility mode sends one dummy ChangeCipherSpec, matching the profile.
  • Delegate certificate verification to E's callback, distinguishing temporary trusted, real-site and invalid certificates.

A.3 TLS 1.3 server stack

  • Continue from PrefixedStream, which replays the ClientHello already read by C. Produce ServerHello using D's destination parameters, EncryptedExtensions, E's generated/mirrored Certificate, CertificateVerify signed by the leaf private key, and Finished.
  • In TCP TLS 1.3 compatibility mode, legacy_session_id_echo must echo the client's 32 bytes. Cipher suite, keyshare group and ALPN come from DestProfile. QUIC's empty session-ID rule is described in G.

A.4 Modules and 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>;         // Byte-level serialization; GREASE/order from profile

// tls13/handshake.rs (client)
pub struct Tls13Client { /* transcript, secrets, aead ... */ }
impl Tls13Client {
    pub fn start(params:ClientHelloParams) -> (Self, Vec<u8> /*ClientHello record*/);
    pub fn drive(&mut self, inbound:&[u8], verify:&dyn CertVerify) -> DriveOut; // Advance handshake / produce outbound bytes / complete
    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 (server-side mirrored handshake)
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>>;
}

Prioritize ClientHello construction, key schedule and record-layer correctness, checked with tls.peet.ws and JA4 tools. For certificate compression, the client needs to decompress the peer's Brotli certificate when advertising compress_certificate.

Component B: REALITY authentication using keyshare ECDH

B.1 Keys and parameters

  • The server has static X25519 keys (S_priv,S_pub). Clients receive S_pub as an access credential; keep it confidential from unauthorized observers, despite its mathematical name as a public key.
  • short_id is 0–8 bytes. The server configures an allowed set and the client chooses one entry.
  • max_time_diff defaults to 120 seconds. server_names is the SNI allowlist; dest is the cover site's host:443.

B.2 Client authentication payload in legacy_session_id

Reuse A's classical X25519 keyshare (C_priv,C_pub):

  1. shared = X25519(C_priv, S_pub) (32 bytes).
  2. auth_key = HKDF-SHA256(shared, salt="umbra-reality-v1", info="key")[..16] is the AES-128 key; nonce = HKDF-SHA256(shared, salt="umbra-reality-v1", info="nonce")[..12].
  3. The 16-byte plaintext is 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 is the complete ClientHello handshake message with all 32 session-ID bytes zeroed, excluding TLS record headers. Changing record fragmentation must not change AAD; the token remains bound to the full handshake message.
  5. Set session_id(32B) = ct(16) || tag(16) before final serialization and transcript hashing.

B.3 Server checks before responding

Any failure transfers the connection to C's destination-forwarding path:

  1. Parse SNI, classical X25519 C_pub and the 32-byte session ID from the raw ClientHello. Construct HELLO0 by zeroing the session-ID bytes.
  2. Require SNI ∈ server_names.
  3. Compute shared = X25519(S_priv, C_pub) and derive auth_key,nonce.
  4. Open P = AES-128-GCM-Open(auth_key, nonce, ct=session_id[..16], tag=session_id[16..], aad=HELLO0). A GCM failure is a forwarding decision.
  5. Validate version, reserved==0, |now-ts|≤max_time_diff, allowed short ID and replay state. Replay lookup and insertion must be atomic. Retain entries through ts+max_time_diff, including the boundary, rather than for just one window after receipt; check expiry arithmetic for overflow. If full, reject new local authentication and forward to dest without evicting valid entries. Timestamp validation remains necessary after cache cleanup.
  6. On success, pass shared to E.

After the HELLO0 and TLS application-secret boundary corrections, upgrade both endpoints together. Authentication failures must not retry the old nonstandard AAD or key derivation.

B.4 Security properties and credential handling

  • Knowing S_pub and a fresh client private key permits derivation of shared; the handshake-specific AAD prevents moving a token to another ClientHello. Fresh keyshares and the timestamp/replay cache address connection-key reuse and replay.
  • The source design describes connection-specific forward secrecy for authentication. This must not be confused with a blanket forward-secrecy claim after compromise of a static server key; evaluate the precise key-compromise model separately.
  • Exposure of S_pub/short_id compromises access control, as with the corresponding REALITY deployment model. Distribute them through a trusted out-of-band channel.

Component C: server dispatch and probe forwarding

Decide before any local TLS response whether to terminate the authenticated handshake or forward the connection.

  1. read_client_hello_raw(conn) reads a complete ClientHello across TLS records with a total deadline and byte/record limits. Preserve every byte already read when a partial header/body, timeout, limit or EOF occurs, and hand the prefix to destination forwarding rather than closing early. The restriction is on local TLS responses before classification, not on subsequent responses from the real destination. See G for QUIC.
  2. Parse SNI, C_pub, session_id using the custom parser or tls-parser.
  3. Run B's checks. On success, generate leaf = forge_cert(shared, SNI, dest_profile) and continue with Tls13Server::accept(chello_raw, leaf, profile) and PrefixedStream(chello_raw, conn), then enter F. On failure, SNI mismatch or replay, connect to dest, write the entire preserved ClientHello prefix and call copy_bidirectional(conn, d). The peer completes a real handshake with the destination and receives its real certificate.
  4. Do not add proxy-specific throttling or early closure to forwarded connections (K). maxUselessRecords bounds classification; its handling must follow the forwarding rule rather than silently introducing early rejection.
pub async fn dispatch(conn: Conn, cfg:&ServerCfg, prof:&DestProfile, replay:&ReplayCache) -> anyhow::Result<()>;
pub struct PrefixedStream<S>{/* Replay prefix, then relay inner */}

Component D: prebuild and destination profiling

Purpose: make the locally generated handshake follow relevant destination properties: ServerHello parameters, certificate fields, timing and OCSP.

  • Startup must obtain one validated DestProfile. prebuild=true additionally enables periodic refresh; false disables only refresh and never permits startup with a fabricated default profile. Initial probe failure fails startup. Refresh replaces the profile atomically and retains the last valid profile on failure.
  • Collect negotiated TLS version, cipher suite, keyshare group, ALPN and EncryptedExtensions; real leaf certificate subject/issuer/validity/SAN/SCT, OCSP stapling and signature scheme; and time from connecting to the destination until its first TLS response, excluding later HTTP wait time, for K.
  • DNS, connection, TLS and HTTP metadata collection share a bounded total deadline. Synchronous I/O must not block the async executor; blocking tasks that outlive a timeout still count against bounded concurrency.
  • E uses the profile for ServerHello and visible certificate fields. TLS 1.3 encrypts the certificate contents; mirroring also concerns more advanced correlation analysis.
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>;

Component E: local handshake and temporary trusted certificates

After authentication, the server terminates TLS locally, without proxying that handshake to dest. The client verifies the temporary certificate using shared.

E.1 Generated leaf certificate

  • Generate an ephemeral leaf key whose private key signs CertificateVerify. Mirror fields from DestProfile.leaf_template, including CN/SAN=server_name and validity.
  • Private extension OID 1.3.6.1.4.1.62397.1 carries cert_mac = HMAC-SHA256(cert_key, leaf_SPKI_DER) (32 bytes), where cert_key = HKDF-SHA256(shared, salt="umbra-cert-v1", info=session_id).
  • The additional post-quantum signature (I), OID ...62397.2, is ML-DSA-65_Sign(mldsa_sk, leaf_SPKI_DER).

E.2 Client certificate verification

The client knows shared from its retained C_priv and its own session ID:

  1. Derive cert_key, verify the MAC in constant time and verify ML-DSA-65 with mldsa_pk. Both checks and proof of private-key possession through CertificateVerify must pass for UmbraTrusted, the only classification that permits proxy traffic. A public CA signature is not required for this bound certificate.
  2. Without a valid Umbra binding, independently validate the certificate chain, configured trust roots, expected hostname, validity and CertificateVerify. Only success in all checks yields RealSite. TCP may visit the spider path using the supported negotiated HTTP protocol, without sending a proxy destination or business data; an unsupported ALPN must not receive a malformed HTTP request. QUIC RealSite rejects proxy establishment locally: no business keys, ready connection, SOCKS success or target prefix, and no automatic TCP downgrade or claimed HTTP/3 spider behavior.
  3. Any required verification failure yields Invalid and follows the normal TLS error/close path.
  4. Certificate private keys, binding keys, traffic secrets and probe key logs require zeroizing ownership and redacted Debug output. Configuration errors and nested causes must not retain secret values or excerpts from TOML input.

Interception resistance relies on verifying the shared-secret binding and certificate proof. An intermediary that cannot produce the required binding is classified as RealSite or Invalid according to the independent certificate checks.

Component F: adaptive padded multiplexing and Vision splicing

After authentication, the inner transport handles destination addressing, multiplexing and TLS-in-TLS shaping. Choose the mode per connection:

  • Default mux with adaptive padding: one TLS connection carries multiple logical streams, reducing repeated handshakes and connection-count correlation.
  • Dedicated Vision mode: one stream owns the outer TLS connection. Pad during the handshake, then forward eligible inner TLS records directly without a second envelope; intended for eligible TLS traffic and throughput-oriented use.

F.1 Mux frame format

MuxFrame = ver(1) || cmd(1) || stream_id(4,BE) || len(2,BE) || payload(len)
cmd: 0x01 SYN(payload=Destination address) | 0x02 SYN_ACK | 0x03 DATA | 0x04 WINDOW_UPDATE(payload=u32 increment)
     | 0x05 FIN | 0x06 RST | 0x07 PADDING(payload=random; discard entire frame) | 0x08 PING
Destination address(SYN.payload) = atyp(1) || addr(4|1+n|16) || port(2)   // 0x01 v4 / 0x03 domain name / 0x04 v6
  • Each stream has an independent flow-control window, for example 256 KiB initially, increased by WINDOW_UPDATE. DATA chunks are at most 16,384 bytes.
  • A SOCKS5 connection opens a SYN stream. Compatible requests reuse a healthy outer connection. The server connects destinations concurrently and sends SYN_ACK only after connection success.
  • Sessions retain partial-frame read state and serialized write state across cancellation. Opening a stream or waiting for credit must not consume unrelated events. Finish a partially written frame or close the connection; never interleave frame bytes.
  • Receive credit is backed by bounded buffer reservations and returned only after actual application consumption; queueing is not consumption. Check overflow, zero increments and controls for unknown streams; bound stream counts and total buffering.
  • FIN closes one direction after previously queued DATA. RST wakes all waiters on that stream. Releasing one stream must not end others.
  • An outer failure must not automatically replay business traffic. New requests may establish a replacement connection. A stream-zero UDP association owns its outer connection and cannot share a CONNECT session.

F.1a Adaptive flow control, introduced in 0.0.9

New TCP CONNECT mux clients begin with SETTINGS(0x0a, stream=0): UAF1 followed by four big-endian u32 values for initial stream window, initial connection window, maximum stream window and maximum connection window. Initial windows are 256 KiB and 1 MiB; the per-stream maximum is 32 MiB, and the configurable connection maximum is capped at 64 MiB. Combine the initial settings frame and configured random padding in one write, without changing ordinary business-write padding counts. Older clients starting with SYN/UDP retain legacy flow control.

CREDIT(0x0b) contains two big-endian u64 values: cumulative permitted send limit and cumulative application-consumed position. Stream zero represents the connection total. DATA must satisfy both stream and connection credit; increasing credit must not pretend that bytes were consumed. PROBE(0x0c) and PROBE_ACK(0x0d) carry an 8-byte nonce on stream zero to sample the outer connection's RTT.

Receivers grow windows from actual consumption and RTT, reserving process/credential-group budget before granting credit. Committed credit cannot be withdrawn. FIN/RST settle cumulative positions while preserving cancellation safety. Unauthenticated forwarding and ClientHello construction are unchanged. See throughput and configuration for settings, memory accounting and measurement limits.

F.2 Adaptive padding scheme

  • A configurable scheme, similar to anytls, describes target-length distributions or additional PADDING lengths for the kth write event. The design's default injects random PADDING frames into approximately the first 16 records in each direction, with lengths from [100,1400], and surrounds initial business frames with padding to disturb deterministic inner-handshake length/direction patterns.
  • Afterwards, insert padding at a lower probability to address longer-term statistical patterns.

F.3 Vision splicing, introduced in 0.0.7

For TCP, mux=false selects the dedicated Vision implementation; mux=true retains encrypted multiplexing. Both endpoints must support the 0.0.7 implementation. The older plain solo relay and unused helper were removed; there is no additional independent Vision switch.

  1. An authenticated mode flag distinguishes the new solo path from mux. Unauthenticated or older fallback endpoints receive no target or business-control data.
  2. Send the target address, then exchange authenticated capabilities. SOCKS success and business DATA wait until the server has connected to the target.
  3. Use bounded bidirectional reassembly to observe valid TLS 1.3 ClientHello/ServerHello messages and complete protected records. Ineligible traffic keeps outer encryption.
  4. The client coordinates request, acknowledgment, commit and final acknowledgment, checking byte boundaries in both directions. Drain outer writes, retain already-read prefixes, then hand off the raw TCP stream.
  5. The raw phase forwards the original protected inner TLS records without outer TLS encryption, envelope or padding; retain record-structure checks and half-close behavior.

Record type 0x17 may also carry encrypted handshake or alert messages. Passive observation cannot validate the inner Finished or prove that bytes from a maliciously imitating application are actually encrypted. The application's end-to-end TLS still authenticates the destination and provides confidentiality. Raw forwarding uses userspace I/O; it does not imply kernel zero-copy or a fixed speed improvement.

See the approved wire specification for exact bytes, limits, rejection/commit states, EOF, cancellation and vectors. Runtime tests use independent rustls endpoints, checking complete 256 KiB transfers in both directions, byte-for-byte equality between the wire suffix and inner ciphertext, and cessation of outer seal/open calls after handoff. The source also records real Mac/server requests with 0.0.7; see the acceptance record.

Mux and raw splicing are mutually exclusive. QUIC does not enter this TCP handoff path. No heuristic automatically creates another solo connection for selected traffic.

F.4 Modules and 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;   // Client opens stream (SYN)
  pub async fn accept(&self)->(Stream, Addr);      // Server accepts stream
}
// inner/vision.rs
pub async fn vision_relay(tls:TlsIo, target:TcpStream) -> io::Result<()>; // Inspect → shape → splice
// inner/padding.rs
pub struct PadScheme{/* Parsed from configuration string */} pub fn parse_pad_scheme(s:&str)->PadScheme;
// inner/spider.rs
pub async fn spider(tls:TlsIo, spider_path:&str) -> io::Result<()>; // RealSite: visit like a browser, then close

Component G: QUIC / HTTP-3 outer transport

Design objective: use UDP to avoid TCP RST injection, provide independent QUIC streams and explore 0-RTT. Present Chrome-like QUIC/HTTP-3 behavior and carry authenticated destinations on QUIC streams. This section includes target design; 0-RTT and the proposed QUIC stream-splicing description are not a statement of current supported behavior.

  • Reuse A's TLS 1.3 logic for QUIC handshakes. ClientHello is carried in Initial CRYPTO frames. Initial keys derive from the DCID and a fixed salt, so an observer can recover ClientHello. QUIC supported_versions contains only TLS 1.3 and valid GREASE; do not copy TLS 1.2 from a TCP profile. Apply this derivation before computing HELLO0/AAD and the authentication token. A direct QUIC TLS API must not silently rewrite already-bound parameters.
  • The target profile covers QUIC versions, transport-parameter values and order, ALPN=h3, ClientHello extensions including quic_transport_parameters, SCID length and GREASE transport parameters (J).
  • Authentication carrier differs from TCP: QUIC ClientHello has an empty legacy_session_id. The design instead places B's 32-byte ct||tag in a Chrome-style GREASE transport parameter, with full-ClientHello AAD and classical X25519 keyshare ECDH. The proposed parameter number and length must be checked against Chrome captures; the source discusses an 8-byte SCID plus remaining GREASE capacity if 32 bytes do not fit. This is a design proposal to reconcile with the implemented carrier, not permission to alter it independently.
  • Dispatch reads Initial packets, recovers ClientHello and checks authentication. Failure forwards the original Initial and subsequent UDP datagrams to the real destination's QUIC service. Success completes E's local QUIC-TLS handshake.
  • The target inner design uses QUIC's native multiplexed streams rather than an extra mux layer and discusses stream-direct forwarding. TCP Vision handoff remains a separate implementation and must not be inferred from that proposal.
// transport/quic.rs
pub struct QuicFingerprint{/* versions, tparams order/values, 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<()>;

A custom QUIC stack is substantial work. The source considers quiche with customizable BoringSSL integration or quinn with a replacement crypto provider for A's handshake/profile logic. Verify QUIC fingerprints and the authentication carrier against real Chrome captures.

Component H: Geneva-style TCP segmentation

Current support: off sends normally; segment performs ordered split writes. Advanced Geneva packet strategies are not presented as implemented or measured.

  • Concatenating segment writes must reproduce the original ClientHello bytes. A write is not guaranteed to become a separate TCP packet, and segmentation alone is not proof of interference resistance.
  • Reject unsupported Geneva DSL during configuration validation; never silently treat it as off. This revision does not add privileged raw sockets.
  • Only a recoverable preparation failure before any bytes are sent may fall back to normal sending. A failure after a partial write returns a transport error; restarting from byte zero would duplicate the prefix.
  • QUIC is a separate UDP path. Do not conflate transport or fingerprint verification across TCP and QUIC.
// 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<()>;

Component I: X25519MLKEM768 and ML-DSA-65

  • Post-quantum key exchange: the hybrid keyshare uses the exact secret order ML-KEM-768 shared secret(32) || X25519 shared secret(32) as input to the TLS 1.3 schedule, following RFC 10024 §4.3. RustCrypto ml-kem supplies ML-KEM-768. REALITY-style authentication still reuses the separate classical X25519 keyshare (B).
  • Post-quantum certificate signature: E adds an ML-DSA-65 signature in a private extension, using RustCrypto ml-dsa. The server derives mldsa_sk from mldsa_seed; clients receive mldsa_pk. UmbraTrusted requires both the shared-secret HMAC binding and ML-DSA verification, combining classical binding with the additional signature.
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;

Component J: maintaining Chrome fingerprint profiles

Purpose: a custom stack does not automatically inherit Chrome behavior from BoringSSL. Profiles must be explicit, data-driven and replaceable.

  • Store the target Chrome version's visible ClientHello properties: cipher list, extension set and order, GREASE slots, supported groups, signature algorithms, ALPN, ALPS, certificate compression, keyshare combinations and padding. QUIC profiles also include transport parameters/order, h3 and GREASE.
  • Capture a real Chrome ClientHello, or use tls.peet.ws/api/all and JA4 tools, then parse it into profile data. Bundle one or two stable profiles with their Chrome version. Keep profile data separate from code so it can follow browser updates.
  • Compare generated ClientHello JA3/JA4 values with the target profile in CI/startup checks and warn on mismatch. Such checks cover their measured fields; complete wire behavior requires the capture-based verification described above.
pub struct FingerprintProfile{/* ciphers, ext_order, grease_slots, groups, sigalgs, alpn, alps, ... */}
pub fn load_profile(name:&str)->FingerprintProfile;    // e.g. "chrome-latest"
pub fn ja3_ja4(chello:&[u8])->(String,String);          // For self-checks

Component K: probe-resistance hardening

  • Timing: use D's connection-to-first-TLS-response interval, subtract local preparation time after classification, and wait only the remaining nonnegative time before ServerHello. If local processing is already slower, add no delay. Indistinguishability requires measurement.
  • Useless-record limits: exceeding classification limits forwards to dest. Reject configurations that request unauthenticated early closure.
  • Forwarding behavior: no Umbra-specific throttling or garbage-triggered early close. A request half-close still permits the return response.
  • Spider mode: only after complete certificate verification, a TCP client visits spider_path with a supported negotiated ALPN protocol. QUIC RealSite rejects proxy use locally.
  • Ports/IPs: alternate listeners and IPs can reduce residual-blocking impact; TCP and QUIC provide separately configurable paths.

15. Cryptographic summary

  • ECDH: X25519 (x25519-dalek); hybrid KEM: ML-KEM-768 (ml-kem).
  • KDF: HKDF-SHA256 (hkdf + sha2), using B/E labels umbra-reality-v1 and umbra-cert-v1.
  • Authentication token: AES-128-GCM (aes-gcm) in the TCP session ID, with full ClientHello AAD and zeroed session-ID bytes.
  • Certificate binding: HMAC-SHA256 (hmac) plus ML-DSA-65 (ml-dsa); secret/tag comparisons use constant-time primitives (subtle).
  • TLS 1.3 records: AES-128/256-GCM and ChaCha20-Poly1305 (aes-gcm/chacha20poly1305).
  • Randomness: OS CSPRNG (rand::rngs::OsRng).
  • Do not add a second business-data AEAD layer: TLS/QUIC already provides confidentiality and integrity, avoiding extra framing and overhead.

16. Configuration specification

server.toml

listen        = "0.0.0.0:443"          # TCP; configure udp_listen separately for QUIC
udp_listen    = "0.0.0.0:443"          # Component G:QUIC/HTTP-3
private_key   = "BASE64(X25519 32B private key)"   # umbra keygen
short_ids     = ["", "0123456789abcdef"]
dest          = "www.microsoft.com:443"     # Cover site (selection criteria: §20)
server_names  = ["www.microsoft.com"]
max_time_diff = "120s"
mldsa_seed    = "BASE64(32B)"           # Component I:Post-quantum certificate signing seed
prebuild      = true                    # Component D:Periodic refresh; false still requires startup probing
padding_scheme= "default"               # Component F Adaptive padding policy
tcp_evasion   = "segment"               # Component H:off | segment;Geneva DSL is not yet supported

client.toml

server        = "SERVER_IP:443"
transport     = "tcp"                   # tcp | quic
public_key    = "BASE64(X25519 32B public key)"   # = server public key; treat as a secret credential
short_id      = "0123456789abcdef"
server_name   = "www.microsoft.com"     # SNI; must belong to server_names
fingerprint   = "chrome-latest"         # Component J Profile
mldsa_verify  = "BASE64(ML-DSA-65 public key)" # Component I Signature verification
spider_path   = "/"                     # Used for RealSite; prefer a different path per client
socks_listen  = "127.0.0.1:1080"
mux           = true                    # Component F:mux by default; false=solo/Vision
padding_scheme= "default"
tcp_evasion   = "segment"

17. Rust layout and module mapping

The source's historical single-crate layout describes one binary with umbra server|client|keygen subcommands. The current repository is a Cargo workspace; consult the architecture reference for its actual crate layout. The tree below preserves the design's component-to-module mapping.

umbra/
├── Cargo.toml
├── DESIGN.md
├── fingerprints/            # Component J:Chrome fingerprint profiles (data files)
│   ├── chrome-latest.toml
│   └── chrome-latest-quic.toml
├── examples/{server.toml,client.toml}
└── src/
    ├── main.rs              # clap subcommand dispatch
    ├── config.rs            # Configuration
    ├── tls13/               # Component A:Custom TLS 1.3
    │   ├── clienthello.rs   #   Byte-level ClientHello construction (GREASE/order)
    │   ├── handshake.rs     #   Client state machine + key schedule
    │   ├── server.rs        #   Server-side mirrored TLS stack
    │   ├── records.rs       #   Record-layer AEAD
    │   ├── keyschedule.rs   #   HKDF-Expand-Label/Derive-Secret
    │   └── parse.rs         #   ClientHello parser (server)
    ├── fingerprint/         # Component J:Profile loading + JA3/JA4 self-check
    ├── reality/             # Component B/E
    │   ├── auth.rs          #   session_id authentication payload (seal/open) + ReplayCache
    │   ├── cert.rs          #   Mirrored leaf + cert_mac + ML-DSA extension
    │   └── prebuild.rs      #   Component D:probe_dest / DestProfile
    ├── dispatch.rs          # Component C:Dispatch + PrefixedStream + dest forwarding
    ├── inner/               # Component F
    │   ├── mux.rs           #   Multiplexing (default)
    │   ├── padding.rs       #   Adaptive padding scheme
    │   ├── vision.rs        #   Vision splicing (solo)
    │   ├── address.rs       #   Destination address encoding/decoding
    │   └── spider.rs        #   RealSite crawler mode
    ├── transport/           # Component G/H
    │   ├── tcp.rs           #   Outer TCP transport
    │   ├── quic.rs          #   Outer QUIC/HTTP-3 transport
    │   └── geneva.rs        #   TCP segmentation against RST injection
    ├── pq/                  # Component I:mlkem / mldsa wrappers
    ├── socks.rs             # SOCKS5 inbound
    ├── relay.rs             # Relay / half-close
    ├── server.rs / client.rs# Orchestration
    └── replay.rs            # Replay cache

Key signatures, in addition to those in the component sections:

// 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();   // Print X25519 priv/pub + ML-DSA-65 seed/pub (base64)

18. Dependencies and building

This is the design's illustrative dependency list, not the current lockfile or a ready-to-paste Cargo manifest. Use the repository's workspace dependency catalog and Cargo.lock for actual builds.

[dependencies]
tokio        = { version = "1", features = ["full"] }
x25519-dalek = "2"                      # Component B ECDH
ml-kem       = "0.2"                     # Component I ML-KEM-768
ml-dsa       = "0.0"                     # Component I ML-DSA-65(RustCrypto; check version/availability)
aes-gcm      = "0.10"                     # session_id + TLS records
chacha20poly1305 = "0.10"                 # TLS records
hkdf         = "0.12"
sha2         = "0.10"
hmac         = "0.12"
subtle       = "2"                        # Constant time
rand         = "0.8"
tls-parser   = "0.11"                     # Server ClientHello parsing (or custom parse.rs)
rcgen        = "0.13"                     # Component E Generate leaf certificate + custom extensions
socket2      = "0.5"                      # Component H Basic segmentation (IP_TTL/NODELAY/manual splitting)
# Component G (choose one):quiche = "..." (BoringSSL-based, easier fingerprint customization) or quinn = "..." (requires replacing the crypto provider)
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"

Implementation notes:

  • BoringSSL/rustls is not the primary TLS handshake implementation in this design; rcgen generates leaf-certificate DER.
  • Verify A/G fingerprints using real Chrome captures, tls.peet.ws/api/all and JA4 tools, updating profiles with Chrome.
  • Advanced H strategies would require CAP_NET_RAW/raw sockets. Current support is the bounded sending behavior described in H; fallback is allowed only before any bytes have been written.
  • Keep ML-KEM/ML-DSA crate versions and protocol codepoints aligned with the applicable specifications and target browser profile; consult current standards and captures.

19. Testing and verification

  • Unit tests: session-ID seal/open with tampering, expiry, replay and AAD changes; positive/negative certificate MAC and ML-DSA checks; HKDF/record vectors from RFC 8448.
  • Fingerprint checks: generated ClientHello JA3/JA4 against the target Chrome profile; separate QUIC checks.
  • Interoperability: the full custom client/server handshake, plus client handshakes with real TLS 1.3 sites using the forwarding/spider path.
  • Active probes: openssl s_client and random inputs should follow the real destination's behavior, with its certificate where applicable and no proxy-specific early-close or throttling differences.
  • Replay: resend a captured ClientHello and confirm that replay detection forwards it to dest.
  • TLS-in-TLS: inspect lengths/directions of the first 8–16 records per direction. Mux/padding should vary the pattern; after Vision handoff, raw records should match the inner TLS stream.
  • Timing: compare time-to-first-byte distributions for authenticated and forwarding paths.
  • TCP interference and QUIC: measure connectivity/stability separately under weak or interfered network conditions.
  • Real deployments: observe long-lived connections, large transfers, poor networks and residual blocking with authorized client/server environments.

20. Deployment and destination selection

Cover-destination criteria in the design:

  • A reachable external site supporting TLS 1.3 and H2/H3, whose domain is used for the service itself rather than solely for redirection.
  • Prefer a destination near the server's network location, suitable handshake behavior such as encrypted post-ServerHello messages (the source mentions dl.google.com), and OCSP stapling where available.
  • The source also discusses restricting return traffic to censored networks, forwarding TCP/80 and UDP/443 where the deployment requires it, and choosing a less common or more stable destination IP. These are deployment choices, not automatic defaults.
  • Set server_names to allowed destination names and use a matching client server_name.

Operations: TCP/UDP port 443 is a common choice. Avoid unsuitable IP ranges; protect private_key and mldsa_seed, and distribute public_key, mldsa_verify and short_id through trusted channels. Consider BBR and appropriate ulimit -n, systemd supervision and journald without logging user destinations or traffic by default. Prepare alternative ports/IPs and a separately validated QUIC path as needed.

21. Security and responsible operation

  • Purpose: privacy protection, resistance to censorship and access to the open internet; operate within applicable law.
  • Secrets: protect S_priv/mldsa_seed; distribute S_pub, short ID and the ML-DSA verification key through trusted channels.
  • Use established constant-time primitives for MAC/tag checks and cryptographic signature verification.
  • Bound ReplayCache capacity and clean up expired entries to limit memory-exhaustion attacks.
  • Keep dest fixed by trusted configuration rather than user-controlled input, preventing fallback SSRF.
  • Lock Cargo.lock, audit dependencies and maintain post-quantum and browser-profile inputs as upstream changes.

Appendix A: wire-layout quick reference

TCP REALITY-style token in legacy_session_id (32 bytes)

StepCalculation
sharedX25519(C_priv,S_pub); server: 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
AADFull ClientHello with its 32 session-ID bytes zeroed: HELLO0
session_id (32B)ct(16) || tag(16) = AES-128-GCM-Seal(auth_key,nonce,P,HELLO0)

Temporary certificate binding

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

MuxFrame

OffsetFieldLengthMeaning
0ver10x01
1cmd1SYN/SYN_ACK/DATA/WINDOW_UPDATE/FIN/RST/PADDING/PING
2stream_id4Big-endian
6len2Big-endian
8payloadlenSYN=destination; DATA=data; PADDING=random bytes

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

Appendix B: state machines and data flow

Server dispatch and handshake

Server state machine
Server state machine Open full-size diagram
View Mermaid source
stateDiagram-v2
    state "Read ClientHello" as Read
    state "Parse SNI, keyshare and authentication carrier" as Parse
    state "Verify authentication token" as Verify
    state "Forward to dest" as Forward
    state "Complete handshake locally" as Handshake
    state "Align timing" as Timing
    state "Send temporary trusted certificate" as Certificate
    state "Inner mux / Vision" as Inner
    [*] --> Read
    Read --> Parse
    Parse --> Forward: SNI mismatch / missing keyshare
    Parse --> Verify: Valid parameters
    Verify --> Forward: GCM / time / short_id / replay check failed
    Verify --> Handshake: Accepted; shared available
    Handshake --> Timing: dest.rtt minus local processing time
    Timing --> Certificate: cert_mac + ML-DSA-65
    Certificate --> Inner
    Inner --> [*]: Stream finished
    Forward --> [*]: Bidirectional relay to the real site

Client

Client state machine
Client state machine Open full-size diagram
View Mermaid source
stateDiagram-v2
    state "Choose TCP / QUIC" as Transport
    state "Build ClientHello" as Hello
    state "Drive handshake" as Handshake
    state "Verify and classify certificate" as Certificate
    state "Inner proxy" as Inner
    state "RealSite: check transport" as RealSite
    state "TCP: visit site using negotiated protocol" as Spider
    state "QUIC: reject proxy establishment" as Reject
    [*] --> SOCKS5
    SOCKS5 --> Transport
    Transport --> Hello: A fingerprint and B/G authentication carrier
    Hello --> Handshake
    Handshake --> Certificate
    Certificate --> Inner: UmbraTrusted: binding and ML-DSA verified
    Certificate --> RealSite: RealSite: ordinary certificate validation passed
    Certificate --> [*]: Invalid: TLS error
    RealSite --> Spider: TCP and supported ALPN
    RealSite --> Reject: QUIC
    Inner --> [*]: Stream finished
    Spider --> [*]: Close after visit
    Reject --> [*]: Send no proxy data

Appendix C: references

  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 and Xray-core transport/internet/reality; XTLS-Vision (xtls-rprx-vision); VLESS.
  8. anytls / anytls-go: padding schemes and multiplexing.
  9. uTLS (refraction-networking/utls); QUIC fingerprints and Chrome QUIC behavior.
  10. RFC 8446 (TLS 1.3), RFC 8448 (test vectors), RFC 8701 (GREASE), RFC 9000/9001 (QUIC/QUIC-TLS).
  11. RFC 10024 (X25519MLKEM768); FIPS 203 (ML-KEM), FIPS 204 (ML-DSA).
  12. Rust libraries: x25519-dalek, ml-kem, ml-dsa, aes-gcm, chacha20poly1305, hkdf, rcgen, quiche/quinn, socket2, tls-parser.

Values, thresholds, extension order and codepoints in a design reference are tied to its revision and profile. Censorship behavior, Chrome fingerprints and post-quantum standards evolve. Check current specifications and measured captures before implementation, and keep component J's profiles up to date.

On this page

0. Overall goals and architectural decisions1. Threat model: censorship techniques2. Design principles and influences3. Overall architectureComponent A: minimal TLS 1.3 stack, a Rust counterpart to uTLSA.1 Byte-level ClientHello constructionA.2 TLS 1.3 key schedule and client state machineA.3 TLS 1.3 server stackA.4 Modules and signaturesComponent B: REALITY authentication using keyshare ECDHB.1 Keys and parametersB.2 Client authentication payload in legacy_session_idB.3 Server checks before respondingB.4 Security properties and credential handlingComponent C: server dispatch and probe forwardingComponent D: prebuild and destination profilingComponent E: local handshake and temporary trusted certificatesE.1 Generated leaf certificateE.2 Client certificate verificationComponent F: adaptive padded multiplexing and Vision splicingF.1 Mux frame formatF.1a Adaptive flow control, introduced in 0.0.9F.2 Adaptive padding schemeF.3 Vision splicing, introduced in 0.0.7F.4 Modules and signaturesComponent G: QUIC / HTTP-3 outer transportComponent H: Geneva-style TCP segmentationComponent I: X25519MLKEM768 and ML-DSA-65Component J: maintaining Chrome fingerprint profilesComponent K: probe-resistance hardening15. Cryptographic summary16. Configuration specification17. Rust layout and module mapping18. Dependencies and building19. Testing and verification20. Deployment and destination selection21. Security and responsible operationAppendix A: wire-layout quick referenceAppendix B: state machines and data flowAppendix C: references