-
Notifications
You must be signed in to change notification settings - Fork 18k
crypto/tls: expose SessionState extra information #60539
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
Comments
cc @neild and @FiloSottile It would be great if we could come to a conclusion here before the 1.21 release and we've committed to the |
This proposal has been added to the active column of the proposals project |
A primary purpose of This can all be made to work, mostly, but I there's a missing API here for adding data to the session ticket. (We discussed this a bit in https://go.dev/cl/496822, which resulted in I think the general shape of that API will be a means of adding data to the ticket for a connection, and a means of getting the data from the ticket (if any) which was used in a resumed session. Perhaps something along the lines of:
This separates the concern of ticket storage ( This doesn't address the concern of composing stored ticket data from different layers, such as QUIC and HTTP/3, however. The proposal here is to use a I think the right approach is to address this at a layer above With that approach, it might be slightly simpler for the QUIC layer if So my inclination is to say that we should leave things as-is, and add a better API for storing data in tickets in 1.22. |
This is actually pretty straightforward. I implemented a test in quic-go to test this:
In
The fact that the server can receive a ClientHello containing multiple session tickets, and the fact that crypto/tls could (in principle) include multiple session tickets when establishing a new connection, makes it necessary to expose some kind of API that allows the application to learn which session ticket was actually chosen. https://go.dev/cl/496822 tried to solve this by exposing the I suggested that this could be easily be solved by instead exposing an opaque identifier on the As crypto/tls currently only uses a single session ticket on the client side, and on the server side, only the first session ticket offered by the client can be used for 0-RTT, this API is not needed to make 0-RTT support work in Go 1.21.
First of all, the QUIC layer needs to be able handle It seems like you're suggesting to add a new API. I believe this API would be very unclean for multiple reasons:
That's a huge additional API surface to expose, just to solve exactly the same problem that we've just solved with the two new
Having a Having crypto/tls define how the
As I've described above, I don't think that the new API we have now is terrible. It has been in the making for years (for example, the issue to add session ID support is 5 years old). Quite a lot of thought has gone into it, and it solves a wide variety of use cases. fwiw, wrapping callbacks in the |
Making a higher level API for storing session data is complex because there isn't even a unique definition of what a session is. Does the data propagate just one hop or through a chain of resumptions? Is the data available at UnwrapSession time (so you can choose whether to reject the ticket or not, which is necessary for 0-RTT) or only if the session is selected? It also doesn't solve the original issue here of letting various layers (application, HTTP/3, QUIC) store each their own data. As I understand it, @neild suggests having each layer give the data to the one immediately below it, but @marten-seemann wants an application that works over crypto/tls to also work over QUIC without using two different APIs. At this point in the process, I think we have space for very limited changes. Either we back something out, or we make minor alterations like turning What's the consensus on |
I'm okay with Another problem with setting the session data in |
I agree that it's not ideal, but it seems like we haven't been able to come up with a better API.
That would be a significant improvement over |
Draft implementation in https://go.dev/cl/501306. It turns out the RFC 8446 presentation language only allows for length prefixes measured in bytes, so this adds 3 bytes for the prefix rather than the 1 I suggested. |
Change https://go.dev/cl/501306 mentions this issue: |
Sounds like |
Based on the discussion above, this proposal seems like a likely accept. |
Is that so? https://www.rfc-editor.org/rfc/rfc8446#section-3.4 has some examples for 2-byte length fields, and some of the slices in handshake messages are encoded using uint16s (e.g. |
Change https://go.dev/cl/501675 mentions this issue: |
RFC 8446 presentation language encodes the bytes length, not the items length, as the prefix. (This is so unknown fields can be skipped without introspecting them.) If you want a field to be potentially more than 16kB, you need a uint24 for the size. |
@golang/release flagging for freeze exception to fix a new Go 1.21 API. |
@FiloSottile I'm unsure this needs one, and we're already tracking this since it has a release-blocker label in the Go1.21 milestone (but thanks for flagging anyway). Fixing a new Go 1.21 API sounds like work that's permitted by the freeze policy. That is, if there's a problem with an API new to this release and it warrants being a release-blocker, then it needs to be resolved (one way or another) for the release to be issued. If after re-reading https://go.dev/s/release#freeze-exceptions you still think an exception is needed, can you please file it as a new issue and provide a rationale? I'll revert the title for now. Thanks. |
Thanks for confirming, then my understanding of the representation language was correct. I don't think we want to have session tickets larger than 64k. This kind of defeats the purpose of session resumption speeding up the handshake. I don't think it's even possible, since RFC 8446, Section 4.2.11 uses a uint16 for the label prefix. |
Change https://go.dev/cl/501775 mentions this issue: |
No change in consensus, so accepted. 🎉 |
In #60105 (comment), @marten-seemann wrote the text below. Filing a separate proposal for this.
After playing around with this API in quic-go for the last week, and writing some tests where the application stores random data und one that uses session IDs, here's what I've found:
First of all, it works: The QUIC stack can store the QUIC transport parameters (and other connection parameters, such as the RTT) in the session ticket. That's great news!
I'd like to propose a small change to the API though:
SessionState
currently has anExtra []byte
field, which the application can use to store application-level information with the session ticket. This is exactly what the QUIC stack does for the transport parameters.However, the QUIC stack might not be the only one that's interested in using that slice to store additional information. For example, the HTTP/3 layer also needs to store certain values in the session ticket (see Section 7.2.4.2 of RFC 9114). Other implementations running on top of QUIC will probably want to do the same, and there could be multiple layers that want to do that.
This is pretty inconvenient, since at the time the
WrapSession
callback is called for the application, the QUIC layer will already have filled theExtra
slice. Now the application will have to take care of adding its data into the same slice, in a way that allows it to extract that information in theUnwrapSession
callback later, and then restore the data before passing it on to the QUIC layer'sUnwrapSession
callback.Most likely, this means coming up with some kind of serialization format. It's possible, but every layer that wants to store information in the session ticket will have to invent its own way of doing that. Doing that in a forwards-compatible way most likely also means adding a versioning scheme...
What if we made it more explicit that this is a stack that every layer in the application can push to (on
Wrap
) and pop from (onUnwrap
):Any layer of the application (incl. the QUIC stack) would call
PushExtra
in theirWrapSession
callback, and retrieve that data saved usingPopExtra
inUnwrapSession
. As today, serializing this field would be handled by crypto/tls, it's just that now we're serialzing a[][]byte
instead of a[]byte
.The text was updated successfully, but these errors were encountered: