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

Support for TCP fast open / TLS early data #532

Open
njsmith opened this issue May 14, 2018 · 4 comments
Open

Support for TCP fast open / TLS early data #532

njsmith opened this issue May 14, 2018 · 4 comments
Labels
design discussion low-level polish TLS Relevant to our TLS/SSL implementation

Comments

@njsmith
Copy link
Member

njsmith commented May 14, 2018

TCP has a feature now where you can send some data along with the initial SYN packet, called TCP fast open (LWN article, rfc). (At least, if you've made a connection to the same server somewhat recently.) Linux has actually supported this for quite some time (some support since 3.7, complete support since 3.16). This is a bit tricky because the point of the 3-way handshake is to establish a reliable connection, and this lets you send data before that, so it's intrinsically unreliable. In particular a server might randomly seem to get multiple connections and read this data multiple times, even though the client only made one connection, so you can only use this as an opt-in thing with protocols where you know the initial data being sent is idempotent.

TLS 1.3 has a semantically very similar feature, where you can send data along with the initial handshake, called "early data". It has the same problem with requiring idempotency (and the same requirement that you have made a connection to the server before), so it has to be opt-in. It doesn't actually exist as something one can use yet though. (Needs openssl 1.1.1, plus support in wrappers.)

We should support these in our high-level API on both the client and server side. I think that on the client side, this would mean something like an extra early_data= argument to open_tcp_stream or open_ssl_over_tcp_stream. And on the server side, something like an allow_early_data argument to open_tcp_listeners/open_ssl_over_tcp_listeners (defaulting to False)? I guess for open_tcp_listeners this should be a "queue length" (to avoid DoS issues).

For TLS early open, we'll also want to expose some of the richer API that openssl exposes on the SSLStream API. (Or maybe this is the only thing we want to do on the server side?) For TCP fast open, AFAICT the socket API doesn't give any way to distinguish between regular data and fast-open data, so this is unnecessary.

Also, I believe that in general the initial part of a TLS client handshake is always idempotent, so open_ssl_over_tcp_stream should use TCP fast open by default, and likewise for open_ssl_over_tcp_listeners.

However, there is a subtlety when TLS early data is combined with TCP fast open: there is a rule that servers are supposed to reject re-use of TLS early data tokens, so (a) on the server side maybe we need to help implement that somehow?, (b) on the client side, we can't necessarily make one SSLObject and then pass its data to open_tcp_stream, because if happy-eyeballs kicks in then we'll end up sending that same data multiple times. OTOH maybe this is fine because (a) happy eyeballs rarely kicks in, and (b) when it does whichever connection wins is usually going to be the one whose SYN was received first, and (c) even if we do end up using a connection where the token was invalidated by another happy-eyeballs connection, TLS transparently falls back on a regular handshake. I think.

Testing this is going to be super annoying. I'm not sure how to even do it except by like, shelling out to tcpdump or something.

Cross-platform support will also be a bit tricky. On Windows you have to set TCP_FASTOPEN and then use ConnectEx, which means you MUST use IOCP. On MacOS, you have to use the odd connectx, which Python doesn't currently wrap.

This page has some useful notes: https://dnsprivacy.org/wiki/display/DP/TCP+Fast+Open#TCPFastOpen-OSX

@Fuyukai
Copy link
Member

Fuyukai commented May 14, 2018

Doesn't TLS early data rely on 0-RTT, which is super easy to replay attack?

@njsmith
Copy link
Member Author

njsmith commented May 15, 2018

TLS early data is indeed subject to replay attacks, which is why (a) servers are supposed to detect and reject when the same session token is reused, (b) it would be something that clients have to explicitly request via a special argument or something (presumably in cases where they know their particular early data is safe), not something that we can enable automatically. But if people want to use it at all with Trio then we need to eventually provide something.

It's true though that this is a somewhat niche case and it's not clear what people will actually use it for (if anything). And in any case we can't do anything about early data now anyway since openssl 1.1.1 isn't even out yet, and then it will be some time after that before we have any way to access the necessary new APIs from Python.

TCP fast open is something we could theoretically implement now and get some small win in the common case of setting up SSL-over-TCP, though it's not really urgent compared to all the other things we have to do.

@njsmith
Copy link
Member Author

njsmith commented Oct 21, 2018

Interesting update, though perhaps not relevant to us: torvalds/linux@19f6d3f

@oremanj oremanj added TLS Relevant to our TLS/SSL implementation low-level labels May 4, 2019
@njsmith
Copy link
Member Author

njsmith commented Aug 2, 2019

When implementing TLS early data, things to review and think about:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design discussion low-level polish TLS Relevant to our TLS/SSL implementation
Projects
None yet
Development

No branches or pull requests

3 participants