Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ECH seems not working when 0RTT is used #2442

Closed
KershawChang opened this issue Feb 13, 2025 · 7 comments
Closed

ECH seems not working when 0RTT is used #2442

KershawChang opened this issue Feb 13, 2025 · 7 comments

Comments

@KershawChang
Copy link
Collaborator

See bug 1947968.

It appears that this behavior occurs only when 0-RTT is used.
The screenshot below indicates that ECH is not being utilized.

Image

Please note that in Necko, enable_ech is called, and echConfig should be passed to Neqo.

@KershawChang
Copy link
Collaborator Author

I've tried to write a test case to test using 0RTT and ech together.

fn zero_rtt_with_ech() {
    let mut server = default_server();
    let (sk, pk) = generate_ech_keys().unwrap();
    server
        .server_enable_ech(ECH_CONFIG_ID, ECH_PUBLIC_NAME, &sk, &pk)
        .unwrap();

    let mut client = default_client();
    client.client_enable_ech(server.ech_config()).unwrap();

    connect(&mut client, &mut server);

    assert!(client.tls_info().unwrap().ech_accepted());
    assert!(server.tls_info().unwrap().ech_accepted());

    let token = exchange_ticket(&mut client, &mut server, now());
    let mut client = default_client();
    client
        .enable_resumption(now(), token)
        .expect("should set token");
    client.client_enable_ech(server.ech_config()).unwrap();

    let mut server = resumed_server(&client);
    server
        .server_enable_ech(ECH_CONFIG_ID, ECH_PUBLIC_NAME, &sk, &pk)
        .unwrap();

    connect(&mut client, &mut server);
    assert!(client.tls_info().unwrap().early_data_accepted());
    assert!(server.tls_info().unwrap().early_data_accepted());
    assert!(client.tls_info().unwrap().ech_accepted());
    assert!(server.tls_info().unwrap().ech_accepted());
}

The test does failed at the second check of assert!(client.tls_info().unwrap().early_data_accepted());.
However, I am not sure if the test case above is correct or not.

@martinthomson
Copy link
Member

I can confirm that neither ECH nor 0-RTT were accepted in this case (flipping the two checks around shows that you also don't have ECH). I can't see anything obviously wrong with the test case, but I'm a bit rusty on the details, so I might have missed something.

@martinthomson
Copy link
Member

Looking at the logs, I see the client attempting 0-RTT and the server accepting it. The client only sends NEW_CONNECTION_ID in 0-RTT, but it is successfully read by the server. @dennisjackson, can you remind me what the story is with resumption and ECH?

From what I can see, everything seems right: the PSK is accepted, 0-RTT is accepted, and the handshake succeeds. However, it seems like the connection information is showing that 0-RTT is not accepted and nor is ECH. Do we carry ECH status over with session tickets in ECH? I can see tests in NSS for resumption, which match what I'm seeing in the neqo logs, but I don't see any that test calls to SSL_GetPreliminaryChannelInfo or even SSL_GetChannelInfo. It's been a while though.

@KershawChang
Copy link
Collaborator Author

I just found that if I flip the order of calling enable_resumption and client_enable_ech the test passed.

    let mut client = default_client();
    client.client_enable_ech(server.ech_config()).unwrap();
    client
        .enable_resumption(now(), token)
        .expect("should set token");

So, maybe we need to call client_enable_ech first?
I think I need an NSS expert to confirm what is the correct order.

@martinthomson
Copy link
Member

@KershawChang, I worked it out. It's not an NSS problem, but exactly due to the ordering issue you identified.

When you enable resumption, we start the TLS client. That includes forcing a handshake, which generates the ClientHello and commits NSS to a non-ECH handshake. As far as I can see ECH and resumption are pretty independent in NSS, so we should still be resuming the connection. But we won't be using ECH. However, I do see 0-RTT being sent and accepted with the backwards ordering.

@KershawChang
Copy link
Collaborator Author

Thanks for testing this.

I flipped the order (setting ECH before setting the resumption token) in necko HTTP/3 code, and I hit this assertion failure. It’s possible that there’s something wrong on the necko side; I’ll keep digging into this.

For your information, the order for HTTP/2 is different—we set the resumption token first, then ECH.

@KershawChang
Copy link
Collaborator Author

This issue can be closed now, as #2551 shows the correct way to set both ECH and resumption token.

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

No branches or pull requests

2 participants