REALITY.5c: encrypted server-flight emission + server-side stream (byte-match dest record framing) #86
No reviewers
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
femboy/yip!86
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/reality-5c-server-flight-emit"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
REALITY.5c — encrypted server-flight emission + server-side stream
Stacked on #85 (REALITY.5b). Base is
feat/reality-5b-serverhello-emit; review the 5c delta here, merge after #85. PR 3 of 4 for REALITY.5 (#76).Completes the server side of the hand-rolled TLS 1.3 handshake begun in 5b: the relay emits its encrypted flight (
EncryptedExtensions / Certificate / CertificateVerify / Finished) sealed under the 5b handshake keys and framed to byte-match the borroweddest's captured record lengths, then serves the data phase. Together with 5b's cleartext ServerHello, the relay's entire authed server flight is now indistinguishable to a passive DPI from a real Chrome↔destsession — replacing the BoringSSLSslAcceptorthe authed path used.What's here (all
yip-utls)HandshakeKeys::server_hs_traffic— exposed (base secret for the serverFinished).record_seal_padded— a TLS 1.3 record with trailing record padding;record_sealnow delegates to it (pad_len = 0), so the existing seal KATs still cover the zero-padding path.sign_certificate_verify— the ECDSA-P256 signing mirror of the shipped 4bverify_certificate_verify(identical RFC 8446 §4.4.3 signed content). This signature is the REALITY.4b relay-verification binding, server side.emit_server_flight— assemblesEE / Certificate / CertificateVerify / Finished, seals them under the handshake keys, and greedily frames them acrossrecord_lengths, prefixed with the middlebox CCS. ReturnsServerFlight { wire, app_keys }. Fail-safeError::FlightTooLargewhen the flight can't fit the captured framing → 5d degrades to splice.RealityStream— renamed the direction fields (client_*/server_*→egress_*/ingress_*) and addedclient()/server()constructors, so one record state machine serves both roles.serve— the mirror ofconnect: emits the flight, writes it, drains the client's CCS +Finished(contents unchecked — the outer TLS is zero client-auth by design), and returns the server-role stream on the derived application keys.Fidelity strategy
A passive DPI sees only the encrypted record framing, so 5c matches
record_lengthsexactly;EE/CertVerify/Finishedare natural-sized and the difference is absorbed by TLS 1.3 record padding. Sizing the forged leaf todest'sleaf_der_lenis 5d's forging job.record_lengths(from 5a'scapture_dest_flight, which breaks at the serverFinished) covers only the handshake-key records — so 5c seals the whole flight under the handshake keys, no app-key sealing and no NewSessionTicket.Correctness gate
The shipped client
connect(verify = on)round-trips againstservein-process: the mockrun_mock_tls13_server_with_certwas refactored to emit its flight throughemit_server_flight/serve, so the fourconnect_verify_*tests now exercise real 5c code end-to-end — the client completes the handshake, verifies the 4b binding, and application data flows both directions (application keys agree). The reject tests (WrongLeafKey,BadSignature) prove the binding still fails closed through the new emission path.A framing bug the gate caught (and fixed)
A pure front-greedy split left trailing pure-padding records sealed under the handshake keys after the record containing
Finished. Sinceconnectstops reading atfind_finished_endand then switches to the application keys, those leftover handshake-key records would fail to open in the data phase. Fixed by reserving one content byte per later record (chunk_len = cap.min(remaining.saturating_sub(records_after))) so theFinishedtail always lands in the last record and the client reads every record. A regression test (..._with_flight_split_finished_last,record_lengths = [4096, 600]) reproduces the exact failure and now passes; the four verify tests run multi-record[600, 4096].Scope / non-goals
yip-utlsonly. Noyip-rendezvouswiring, no rcgen leaf forging, no epoll pump — all 5d. No post-FinishedNST. No P256/P384 + HelloRetryRequest (#84).5d hand-off notes
dest'sleaf_der_len; over-capacity →FlightTooLarge→ splice (fail-safe).serve's CCS-skip drain is unbounded (a hostile client sending endlessChangeCipherSpeccould hang it — a pre-existing crate pattern,connectincluded); 5d must apply a read timeout at the caller, like 4a's relay-dialHANDSHAKE_TIMEOUT.RandomSource.Tests
record_seal_paddedKATs;sign_certificate_verifysign↔verify (accept / wrong-key / tampered-transcript);emit_server_flightbyte-framing (outer lengths ==record_lengths, re-open recovers the four messages) + fail-safe (empty/< 17/ over-capacity →Err); the multi-record round-trip gate + regression test. Fullyip-utlssuite green (69 lib tests), clippy-D warningsclean.Deferred cleanups (non-blocking, from the final review)
handshake::Error::RecordTooLargeis overloaded for the handshake-message u24 guard — a dedicatedMessageTooLargewould read clearer.u32::try_from+> 0xFF_FFFF) is duplicated 3× — extract a small helper.Final whole-branch review (opus): READY TO MERGE — transcript/key-agreement correct, the 4b binding is exercised in both accept and reject directions, the framing fix is provably complete (every feasible template lands
Finishedlast or fails closed), no fail-open / no reachable panic / no seq or direction mix-up,forbid-unsafe/ noas/ no bare#[allow].