Skip to content

Conversation

@toidiu
Copy link
Contributor

@toidiu toidiu commented Dec 4, 2025

This PR adds server support for 0-RTT. This is done by transitioning the Handshake to Application when we have 0-RTT keys. The PR also adds an extension header IsInEarlyData to forward the early data request status so application can detect when a request was received with 0-RTT keys (and take appropriate security precautions).

@toidiu toidiu requested a review from a team as a code owner December 4, 2025 23:56
@toidiu toidiu changed the title Akothari/0 rtt WIP: feat[tokio-quiche]: enable 0-rtt for the server Dec 4, 2025
Comment on lines 125 to 127
_ctx: &mut ConnectionStageContext<A>,
) -> ControlFlow<QuicResult<()>> {
if qconn.is_established() {
if qconn.is_established() || qconn.is_in_early_data() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what helps us transition to Application space so we can then handle the early_data request.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about your comment about requests after early data having trouble. Is there something that prevents the handshake from continuing and completing after taking a break to handle early data?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth adding some logging on handshake completion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about your comment about requests after early data having trouble. Is there something that prevents the handshake from continuing and completing after taking a break to handle early data?

Thats exactly what I need to debug next. My mental model is that it should invoke the work_loop and continue so need to double check that and compare against the no early_data variation.

Copy link
Contributor

@evanrittenhouse evanrittenhouse Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The primary concern I see here is that work_loop's wait phase will not call wait_for_quiche, which wakes the IOW when a TLS callback progresses, in the RunningApplication phase. It depends solely on the AOQ's wait_for_data implementation.`

I'm not super familiar with early data, is it possible that the 0-RTT request/response finishes before the PK callback? If so, we may delay the handshake until the next received packet/Quiche timer fires, rather than when the callback actually completes. From Boring docs it sounds like there's no guarantee about that:

As a server, if early data is accepted, SSL_do_handshake will complete as soon as the ClientHello is processed and server flight sent. SSL_write may be used to send half-RTT data. SSL_read will consume early data and transition to 1-RTT data as appropriate. Prior to the transition, SSL_in_init will report the handshake is still in progress. Callers may use it or SSL_in_early_data to defer or reject requests as needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly: wait_for_quiche is used to progress the TLS Handshake callbacks. In RunningApplication phase, we do not call the wait_for_quiche. The concern is that we might stall the Handshake we transition to RunningApplication in early data (since technically the Handshake is not complete).

We currently use quiche for two callbacks private_key_callback (not called when we resume) and async_select_cert_callback (called when before processing ClientHello). So for our usecase this the Handshake should not stall.

One way to make this clean would be to and another phase Handshake -> ZeroRtt -> RunningApplication. But that doesn't seem necessary at the moment. I'll leave a comment documenting this behavior and something we might need to visit if we decide to use more callbacks.

Comment on lines 125 to 127
_ctx: &mut ConnectionStageContext<A>,
) -> ControlFlow<QuicResult<()>> {
if qconn.is_established() {
if qconn.is_established() || qconn.is_in_early_data() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about your comment about requests after early data having trouble. Is there something that prevents the handshake from continuing and completing after taking a break to handle early data?

@toidiu toidiu marked this pull request as draft December 5, 2025 01:33
@toidiu toidiu force-pushed the akothari/0-rtt branch 7 times, most recently from c0a7cad to f5b3c4e Compare December 8, 2025 19:39
@toidiu toidiu force-pushed the akothari/0-rtt branch 2 times, most recently from 5a15fee to 2635dff Compare December 8, 2025 21:22
@toidiu toidiu changed the title WIP: feat[tokio-quiche]: enable 0-rtt for the server feat[tokio-quiche]: implement server-side support for 0-rtt Dec 8, 2025
@toidiu toidiu changed the title feat[tokio-quiche]: implement server-side support for 0-rtt feat[tokio-quiche]: implement server-side support for 0-RTT Dec 8, 2025
@toidiu toidiu marked this pull request as ready for review December 8, 2025 22:00
Comment on lines 125 to 127
_ctx: &mut ConnectionStageContext<A>,
) -> ControlFlow<QuicResult<()>> {
if qconn.is_established() {
if qconn.is_established() || qconn.is_in_early_data() {
Copy link
Contributor

@evanrittenhouse evanrittenhouse Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The primary concern I see here is that work_loop's wait phase will not call wait_for_quiche, which wakes the IOW when a TLS callback progresses, in the RunningApplication phase. It depends solely on the AOQ's wait_for_data implementation.`

I'm not super familiar with early data, is it possible that the 0-RTT request/response finishes before the PK callback? If so, we may delay the handshake until the next received packet/Quiche timer fires, rather than when the callback actually completes. From Boring docs it sounds like there's no guarantee about that:

As a server, if early data is accepted, SSL_do_handshake will complete as soon as the ClientHello is processed and server flight sent. SSL_write may be used to send half-RTT data. SSL_read will consume early data and transition to 1-RTT data as appropriate. Prior to the transition, SSL_in_init will report the handshake is still in progress. Callers may use it or SSL_in_early_data to defer or reject requests as needed.

@toidiu
Copy link
Contributor Author

toidiu commented Dec 13, 2025

@evanrittenhouse 👏 helped confirm that we want to record the handshake duration as soon as we resume the early connection.


@LPardue @ghedo

@evanrittenhouse and I discovered that we set the handshake duration right after resumption with 0-rtt. Wondering if this is ok or if we want to record the duration when the full handshake finishes.

https://github.com/cloudflare/quiche/blob/master/tokio-quiche/src/quic/io/worker.rs#L787-L788

@toidiu toidiu requested a review from LPardue December 13, 2025 03:08
@toidiu toidiu force-pushed the akothari/0-rtt branch 2 times, most recently from 7b4fd49 to 942bee3 Compare December 13, 2025 21:57
This PR adds a setting to enable 0-RTT resumption and early data
processing for the server. Early data uses 0-RTT keys, which are
established from a previous session data (aka. session resumption). The
most obvious downside of this is the ability for an attacker to replay a
request to the server. Application therefore need to be careful when
enabling 0-RTT and processing early data requests.

For more information see BoringSSL docs:
https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#Early-data
@toidiu toidiu requested a review from ghedo December 15, 2025 22:47
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.

5 participants