Skip to content

REALITY.5a: dest server-flight template capture (Chrome-faithful probe, read-only foundation for 5b/c/d)#83

Merged
vxfemboy merged 8 commits into
mainfrom
feat/reality-5a-dest-flight-capture
Jul 19, 2026
Merged

REALITY.5a: dest server-flight template capture (Chrome-faithful probe, read-only foundation for 5b/c/d)#83
vxfemboy merged 8 commits into
mainfrom
feat/reality-5a-dest-flight-capture

Conversation

@vxfemboy

Copy link
Copy Markdown
Member

REALITY.5a — dest server-flight template capture

PR 1 of the REALITY.5 milestone (server-flight fidelity, #76). Read-only foundation — captures, per configured server_name, the structure of the real dest's TLS 1.3 server flight so later sub-milestones (5b/5c/5d) can emit an authed-path flight indistinguishable from a genuine Chrome↔dest session. No emission here.

Key insight

In TLS 1.3 only the ServerHello is cleartext (later sub-milestones must byte-match it); everything after (EncryptedExtensions, Certificate, CertificateVerify, Finished) is encrypted — a passive DPI sees only the encrypted records' lengths + framing. So the template captures the ServerHello structure and the encrypted flight's shape, not the cert contents. yip_utls is ideally placed: it reads the cleartext ServerHello raw and decrypts the flight, so it sees both.

What's in it

  • yip_utls::capture_dest_flight — a Chrome-faithful probe (a focused sibling of connect; connect's crux path is byte-for-byte untouched — the probe duplicates its ~15-line flight-read loop plus one bookkeeping line rather than refactor it). It sends the same Chrome ClientHello (random legacy_session_id, no REALITY seal — probing the real dest), reads + parses the ServerHello structure, derives handshake keys, decrypts + walks the flight, and returns a ServerFlightTemplate + the leaf DER — stopping after the server Finished (no client Finished, no app phase).
  • ServerFlightTemplate: ServerHelloShape (cipher, ordered extensions incl. GREASE, key_share_group) + EncryptedFlightShape (per-record ciphertext-payload lengths + per-message lengths — record_lengths[i] − 17 is the per-record plaintext 5c reproduces, 16-byte tag + 1 content-type) + CertChainShape { leaf_der_len, intermediates_der }intermediates captured verbatim (public CA certs; 5c appends them after the forged leaf for full chain parity at zero extra forging).
  • yip-rendezvous — the dest probe is unified on this Chrome-faithful capture (replacing the boring cert fetch, so dest now responds to the exact hello our clients send); the template is cached per SNI (template_for, dead-code-gated until 5b) alongside StolenFields, at prewarm + refresh. REALITY.3/4b (forged acceptor, per-SNI degrade, staleness, per-connection binding) and anti-replay are untouched (0 diff lines).

Review

Built subagent-driven (4 tasks, two-stage review each). The per-task review caught that Task 2's shared-helper refactor widened 4b's verify path to depend on the whole cert chain (safe per RFC 8446 §4.4.2, now covered by a multi-cert verify test). The final whole-branch review confirmed the capture chain composes correctly (proven by a mock with a non-canonical extension order + 2-record split + 2-cert chain), connect untouched, and the migration is fail-closed.

Follow-ups recorded for 5b/5c (in the milestone spec): record-framing and message-framing are independent knobs (5c pads both); the middlebox-compat CCS is a fixed invariant to emit unconditionally.

Tests

yip-utls 53 + JA4 diff, yip-rendezvous 81, all green. clippy --workspace -D warnings + fmt clean. forbid-unsafe (outside yip-io/yip-device); no as; no bare #[allow]. The dest probe migration preserves StolenFields (incl. #75 AIA); a live #[ignore] cloudflare capture is included.

vxfemboy added 8 commits July 17, 2026 16:09
…mplate capture (record_lengths=ciphertext, verbatim intermediates)
…review)

Task 2 refactored certificate_leaf_der (used by the 4b live verify path
scan_cert_flight -> verify_reality_binding) to share certificate_chain_ders,
which walks the ENTIRE Certificate chain instead of only the leaf entry.
Every existing connect_verify_* test used a single-cert chain, so the
multi-entry parse path in the live verify flow was never exercised.

Add connect_verify_accepts_correct_binder_with_intermediate_chain: a mock
server presents leaf (correctly bound) + a dummy but wire-well-formed
intermediate CertificateEntry (RFC 8446 SS4.4.2 framing), and connect(verify:
true) must still succeed since verification only pins the first entry.
Factor the existing build_certificate_message test helper into a shared
build_certificate_entries_message plus a new
build_certificate_message_with_intermediate, and add an include_intermediate
flag to run_mock_tls13_server_with_cert (test-only changes only).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant