-
-
Notifications
You must be signed in to change notification settings - Fork 404
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
Utilize NEW_TOKEN frames #1912
base: main
Are you sure you want to change the base?
Utilize NEW_TOKEN frames #1912
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this looks pretty good, and seems well motivated. Thanks!
Thanks also for your patience while I got around to this; day job has been very busy lately.
c453b28
to
250e69d
Compare
bdf45e5
to
b3a469a
Compare
Normally I wouldn't mark your own comments as resolved. But since your comments were from when this was a draft PR, I marked as resolved the ones that seem definitely irrelevant to the current version of it. As mentioned on Discord, the MSRV CI failure does not seem to actually be caused by this PR. |
b3a469a
to
46e204a
Compare
bbebc09
to
84b70fe
Compare
a178d97
to
fcfbd63
Compare
quinn-proto/src/config/mod.rs
Outdated
@@ -209,6 +209,10 @@ pub struct ServerConfig { | |||
/// rebinding. Enabled by default. | |||
pub(crate) migration: bool, | |||
|
|||
pub(crate) validation_token_lifetime: Duration, | |||
pub(crate) validation_token_log: Option<Arc<dyn TokenLog>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the TokenLog
docs, it sounds like the behavior of None
here is equivalent to a log that always returns Err
. Would it be simpler to omit the Option
wrapper and provide a ZST impl with that behavior?
quinn-proto/src/config/mod.rs
Outdated
@@ -460,6 +460,9 @@ pub struct ClientConfig { | |||
/// Cryptographic configuration to use | |||
pub(crate) crypto: Arc<dyn crypto::ClientConfig>, | |||
|
|||
/// Validation token store to use | |||
pub(crate) token_store: Option<Arc<dyn TokenStore>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be simpler to omit the Option
and provide an impl that drops everything?
fcfbd63
to
5dc9d49
Compare
Experiment commit pushed to side branch: gretchenfrage@1dbc6eb. Cons:
Pros:
|
I like it, but I don't feel strongly; happy to defer to other judgement if someone else does. |
5dc9d49
to
4f15496
Compare
Will wait to hear djc's thoughts on it then. Everything else should be addressed. |
The experimental commit LGTM, and I agree that moving complexity loci out of common paths is probably a better direction. (Will try to schedule a full review soon.) |
4f15496
to
1bcf2a2
Compare
Naming bikeshed: I realized that this PR in its current state adds
|
d8a856d
to
5f27a96
Compare
I think obviousness easily trumps verbosity for identifiers that are typically used once or never like obscure config fields, so I'm in favor of keeping the prefix for |
5f27a96
to
0b37f5b
Compare
Moves all the fields of Token to a new RetryTokenPayload struct, and makes Token have a single `payload: RetryTokenPayload` field. This may seem strange at first, but it sets up for the next commit, which adds an additional field to Token.
Previously, retry tokens were encrypted using the retry src cid as the key derivation input. This has been described by a reputable individual as "cheeky" (who, coincidentially, wrote that code in the first place). More importantly, this presents obstacles to using NEW_TOKEN frames. With this commit, tokens carry a random 128-bit value, which is used to derive the key for encrypting the rest of the token.
The ability for the server to process tokens from NEW_TOKEN frames will create the possibility of Incoming which are validated, but may still be retried. This commit creates an API for that. This means that rather than Incoming.remote_address_validated being tied to retry_src_cid, it is tied to a new `validated: bool` of `IncomingToken`. Currently, this field is initialized to true iff retry_src_cid is some. However, subsequent commits will introduce the possibility for divergence.
As of this commit, it only has a single variant, which is Retry. However, the next commit will add an additional variant. In addition to pure refactors, a discriminant byte is used when encoding.
When a path becomes validated, the server may send the client NEW_TOKEN frames. These may cause an Incoming to be validated. - Adds TokenPayload::Validation variant - Adds relevant configuration to ServerConfig - Adds `TokenLog` object to server to mitigate token reuse As of this commit, the only provided implementation of TokenLog is NoneTokenLog, which is equivalent to the lack of a token log, and is the default.
When a client receives a token from a NEW_TOKEN frame, it submits it to a TokenStore object for storage. When an endpoint connects to a server, it queries the TokenStore object for a token applicable to the server name, and uses it if one is retrieved. As of this commit, the only provided implementation of TokenStore is NoneTokenStore, which is equivalent to the lack of a token store, and is the default.
When we first added tests::util::IncomingConnectionBehavior, we opted to use an enum instead of a callback because it seemed cleaner. However, the number of variants have grown, and adding integration tests for validation tokens from NEW_TOKEN frames threatens to make this logic even more complicated. Moreover, there is another advantage to callbacks we have not been exploiting: a stateful FnMut can assert that incoming connection handling within a test follows a certain expected sequence of Incoming properties. As such, this commit replaces TestEndpoint.incoming_connection_behavior with a handle_incoming callback, modifies some existing tests to exploit this functionality to test more things than they were previously, and adds new integration tests for server and client usage of tokens from NEW_TOKEN frames.
0b37f5b
to
6cbecb2
Compare
The server now sends the client NEW_TOKEN frames, and the client now stores and utilizes them.
The main motivation is that this allows 0.5-RTT data to not be subject to anti-amplification limits. This is a scenario likely to occur in HTTP/3 requests, as one example: a client makes a 0-RTT GET request for something like a jpeg, such that the response will be much bigger than the request, and so unless NEW_TOKEN frames are used, the response may begin to be transmitted but then hit the anti-amplification limit and have to pause until the full 1-RTT handshake completes.
For example, here's some experimental data that should be similar in the relevant ways:
sudo tc qdisc add dev lo root netem delay 100ms
(and undone withsudo tc qdisc del dev lo root netem
)This experiment was performed on Nov/24 with 2edf192 as main and 478b325 as feature.
For responses in a certain size range, avoiding the anti-amplification limits by using NEW_TOKEN frames made the request/response complete in 1 RTT on this branch versus 2 RTT on main.
Reproducible experimental setup
newtoken.rs
can be placed intoquinn/examples/
:science.py
crates the data:graph_it.py
graphs the data, after you've manually renamed the files:Here's a nix-shell for the Python graphing:
Other motivations may include:
.retry()
, this means that requests take a minimum of 3 round trips to complete even for 1-RTT data, and makes 0-RTT impossible. If NEW_TOKENs are used, however, 1-RTT requests can once more be done in only 2 round trips, and 0-RTT requests become possible again.TokenLog
has false negatives, which may range from "sometimes" to "never," in contrast to the current situation of "always."