server using QUIC or another protocol, are treated as a server with respect to this specification.  2. 2.1. 4.1.1. The configuration agent selects a number of bytes of the server <del> connection ID (SCID) to encode individual server IDs, called the "routing bytes".  The number of bytes MUST have enough entropy to have a different code point for each server. </del>  It also assigns a server ID to each server. 4.2.1. The configuration agent selects an arbitrary set of bits of the <del> server connection ID (SCID) that it will use to route to a given server, called the "routing bits".  The number of bits MUST have enough entropy to have a different code point for each server, and SHOULD have enough entropy so that there are many codepoints for each </del>  server. The configuration agent MUST NOT select a routing mask with more than 4.2.2. Upon receipt of a QUIC packet, the load balancer extracts the <del> selected bits of the SCID and expresses them as an unsigned integer of that length.  The load balancer then divides the result by the chosen divisor.  The modulus of this operation maps to the modulus for the destination server. </del>  <del> Note that any SCID that contains a server's modulus, plus an </del>  arbitrary integer multiple of the divisor, in the routing bits is routable to that server regardless of the contents of the non-routing bits.  Outside observers that do not know the divisor or the routing <del> bits will therefore have difficulty identifying that two SCIDs route to the same server. </del>  Note also that not all Connection IDs are necessarily routable, as the computed modulus may not match one assigned to any server.  These

Server CID will collide with Source CID.
This is often used to mean Source Connection ID in other contexts. A collision here is likely to cause confusion.
Recommended name? SCCID?
I think it might just be best to call it the "Server CID" any other abbreviated term will get confused IMO.
"Server CID" doesn't seem to fit well with "Stream Cipher Connection ID"
So SCID didn't mean "Server Connection ID"? There is definitely confusion here. I'd just recommend not abbreviating anything more than CID.
I guess this proves MT's point. "STream Cipher Connection ID" (just like PCID, OCID, BCID)
Looking again, the document never uses any acronym for 'stream cipher connection ID', so there's no issue there. This is just about 'S' meaning "source" or "server".

server using QUIC or another protocol, are treated as a server with respect to this specification. <ins> For brevity, "Connection ID" will often be abbreviated as "CID". </ins> 2. 2.1. 4.1.1. The configuration agent selects a number of bytes of the server  <ins> connection ID to encode individual server IDs, called the "routing bytes".  The number of bytes MUST have enough entropy to have a different code point for each server. </ins> It also assigns a server ID to each server. 4.2.1. The configuration agent selects an arbitrary set of bits of the  <ins> server connection ID that it will use to route to a given server, called the "routing bits".  The number of bits MUST have enough entropy to have a different code point for each server, and SHOULD have enough entropy so that there are many codepoints for each </ins> server. The configuration agent MUST NOT select a routing mask with more than 4.2.2. Upon receipt of a QUIC packet, the load balancer extracts the  <ins> selected bits of the Server CID and expresses them as an unsigned integer of that length.  The load balancer then divides the result by the chosen divisor.  The modulus of this operation maps to the modulus for the destination server. </ins>  <ins> Note that any Server CID that contains a server's modulus, plus an </ins> arbitrary integer multiple of the divisor, in the routing bits is routable to that server regardless of the contents of the non-routing bits.  Outside observers that do not know the divisor or the routing  <ins> bits will therefore have difficulty identifying that two Server CIDs route to the same server. </ins> Note also that not all Connection IDs are necessarily routable, as the computed modulus may not match one assigned to any server.  These


4.4.  The Block Cipher CID Algorithm, by using a full 16 octets of plaintext and a 128-bit cipher, provides higher cryptographic protection and detection of non-compliant connection IDs.  However, it also requires connection IDs of at least 17 octets, increasing overhead of client-to-server packets. <del> 4.4.1. </del>  The configuration agent assigns a server ID to every server in its pool, and determines a server ID length (in octets) sufficiently The configuration agent also selects an 16-octet AES-ECB key to use for connection ID decryption. <del> 4.4.2. </del>  Upon receipt of a QUIC packet, the load balancer reads the first octet to obtain the config rotation bits.  It then decrypts the opaque server data in that order.  The load balancer uses the server ID octets for routing. <del> 4.4.3. </del>  When generating a routable connection ID, the server MUST choose a connection ID length between 17 and 20 octets.  The server writes its

This PR describes a three pass stream cipher option. The three passes are: 1) Encrypt the server-id bytes using the nonce bytes 2) Encrypt the nonce bytes using the server-id bytes 3) Encrypt the server-id bytes using the nonce bytes This is effectively a much simplified version of the FFX algorithm. The effect is complete encryption of server-id and nonce, and thus resiliency against bit flipping attacks. With the simple stream cipher, and attacker can flip bits in the server ID, resulting in a different decrypted value which could be used in attacks. With the triple stream cipher, flipping any bit will result in random flips of every other bits, which would be easy for servers or load balancers to detect. Of course, full encryption also has these properties. But full encryption requires fairly long CID, while stream encryption works reasonably with small CID.
Thanks for writing this. If I can get clarity on the threat model, I would see no reason to retain the original algorithm, and would simply replace it with this one. But I don't quite understand the bit flipping attack against the old algorithm. I can certainly flip the server ID bits to cause the LB to send it to a different server. But I can also do this by generating random CIDs -- only the addition of zero padding, or unallocated server ID space, gives the LB a chance of detecting invalid CIDs. How would LBs detect tampering using the 3 pass technique?
I would detect LB tampering by making the server ID bit longer than strictly necessary. With the three pass method, tampering or random generation will cause every server ID bit to have a random value. Forgeries will statistically be detected as out of range, with probability . Note that it should also be possible to make the nonce bit shorter than the original spec, because we are no more relying on the randomness of the nonce value. Each server could set the nonce to some kind of sequence number, incremented at each CID allocation. The size of the number should be enough to cover all allocations during a CID encryption key epoch, instead of twice that to cover the birthday paradox if using random allocations.
The bit flipping attack against the old algorithm involves using a side channel to obtain the server ID in existing CID, then flipping selected bits to target a specific server.
Another point about stream ciphers. Since we are using an algorithm similar to PN encryption, it is possible to "mask" some bits -- such as for example the two topmost bits. That means it is possible to use 6 bits of the "first octet" as part of the server-id. This is also possible for the clear method, and for the divider method.
I don't think this is correct. What is not clear in the current language is that the server may choose to use these 6 (5 given an outstanding PR) for length encoding, independent of what the LB wants. This is for crypto offload purposes. Encrypting the length bits would create problems.
NAME Sorry to be obtuse, but I'm still not following the threat model that the 3-pass algorithm addresses. I agree that avoiding the birthday problem is a nice result of your proposal. LB tampering detection: Yes, with stream cipher the only way to detect a randomly generated CID is for the server ID space to be >>> than the number of servers. The LB can then simply discard any packet that maps to an invalid server (unless it has a long header). But this true of the existing design, and isn't unique to 3-pass. For the whole time I've worked on this, I've dealt with people very concerned about DoS vs. a single server in the pool, and people who don't think this is a problem at all. I (weakly) tend toward the latter camp. (Also, if it is a problem, the plaintext algorithm is a walking DoS vector, but that's a different issue). Nevertheless, let's assume that it's a problem, and that the attacker has obtained both the positions of the server ID bytes and the server ID for two servers in the pool via a side-channel. Then I guess the attack is to connect to server A, gather a number valid CIDs via NCID frames that have different nonces, and then xor the SID field to route to server B. Is that accurate? If so, do you find that compelling? It seems strictly limited to the number of CIDs I can get from a valid server, so why not just DoS those CIDs to that server instead?
The current stream cipher proposal can only detect tampering if the server-id space is sparse, for a very specific definition of sparse. The property you want is, "if you pick at random a number of the same size as the server ID, there is a fixed probability X that this this a valid number". A linear numbering of the servers will not achieve that, even if you prepend a bunch of zeroes in front of the serial number. The three-pass proposal detects tampering even if the server ID space is used linearly, e.g. even if only numbers lower than N are valid, with N much smaller than the maximum number allowed by the size of the field. This is because the CID is effectively encrypted. In the absence of effective detection of tampering, the malleability of the stream cipher is dangerous. Suppose an attacker does a valid connection, and obtains a series of CID. Suppose the attacker also knows that there are 1024 servers in your pool. For each CID, it can flip the bottom 10 bits of the stream-ID and obtain 1023 new CID, which will all be recognized as valid by the load-balancer or by the firewall. Are you really sure that attackers cannot put that to good use? (Good for them, of course, not so good for the defender.)
Thanks for clarifying. I increasingly favor this change. The alternative would be that configuration agents SHOULD disperse server IDs over the available space, but that could be difficult if the server ID is an IPv4 address or something. I'll sleep on it, but I think I'll accept this PR next week.
Way back in , NAME said that his three-pass algorithm meant that But we never really did that: the nonce is in the range 8..16 octets, which is inherited from the original single-pass algorithm. NAME asked if we could shorten the minimum nonce to 32 bits to reduce overall CID length; if it were, we could support 2^24 servers with an 8-byte CID! This is compelling and we should definitely shorten the nonce to some value. But 32 bits means you need to roll over the keys for every 4 billion CIDs a server issues; given that some QUIC implementations are quite profligate issuing CIDs, is that enough? Even another byte would get us to a trillion CIDs while supporting up to 2^16 servers in 8 octets. On the other hand, the config agent can always pick a longer nonce length if the key lifetime is too short. This is how much we want to allow people to do something potentially dumb, so it's a good spot for discussion.
I think some flexibility and discussion of the pros and cons here would be very useful. I think 32 bits is a reasonable min for our use cases, but maybe there's a reason that would cause issues I'm not aware of? We are not profligate issuers of connection IDs and I think we just issue one extra one at any given point in time.
NAME wrote: "This is compelling and we should definitely shorten the nonce to some value. But 32 bits means you need to roll over the keys for every 4 billion CIDs a server issues; given that some QUIC implementations are quite profligate issuing CIDs, is that enough?" Short answer: yes. Assume a server handles 2,048 connections per second, and allocates 16 CID per connection. That leaves 17 bits, 128K connections, 64K seconds, about 18 hours. Long answer: to make that really work, you need a good rollover mechanism. Something like, if the load balancer observes that one server has rolled over, it triggers rollover for all other servers in the pool.
Most connections need 1 or 2 CIDs, so I think the practical bounds are higher. Also, 2048 connections isn't that much, but 2048 per second is quite a bit based on my experience.
So I wonder if there's an attack that continually changes the DCID it uses, which will cause some servers to issue way more CIDs than usual, thus forcing a rollover inside the normal key management cycle. Two countermeasures: servers SHOULD limit CID issuance to a sensible limit, and (as NAME says) do a good rollover mechanism.
NAME on a separate note, are there security implications for (noncelen < sidlen)? I can't see any, but you would know better than I.
Perhaps I'm over-thinking this, because I'm not sure nonce re-use actually matters here. Assuming that a single server isn't using multiple server IDs, we are essentially encrypting the same plaintext over and over again. So even if the nonce is re-used, that is only amounting to the exact same CID being issued again (modulo random bits in the server use field or where the length would go). While this isn't great, as long as it's not done in the same connection, it doesn't provide any useful information to the observer to correlate clients. If the observer is positioned to see all CIDs in use for the server pool, then over long time scales it may observer that two CIDs match, which for QUIC-LB means they were issued by the same server. Thanks to the config rotation bits, it can likely infer that the keys haven't rotated. But that has very little to do with correlating clients, which is the whole point of the document. A server that rolls over its connection IDs is going to have to generate new Stateless Reset keys, but this is a totally local operation. Am I not thinking about this correctly?
Encryption performs a bijection from the space of tuples to the space of numbers of size nbsid*nbnonce. I don't see a particular risk in having noncelen smaller or larger than sid_len. There is a risk in densely using the space of encrypted numbers. look at the decryption size. If an adversary picks a number at random, that number will decrypt to . The shorter the ID, the denser your utilization of the space, the larger the likelihood that the randomly picked "s" will correspond to a valid server, and thus the adversary's message hitting a server instead of being dropped at the LB. Same goes for the nonce, the adversary has more chances to get lucky if the space of nonces is densely used. But then, decryption will fail and the message will be ignored. So the main issue I see is that randomly creating SID might yield slightly more effective resource-consumption attacks.
Put another way, maybe Sec 11.6 is wrong for QUIC-LB: URL
The attacker will not be able to directly correlate cid values and servers. Changing one bit in the nonce statistically changes half the bits in the encrypted sid+nonce.
Thanks, can we change to a 32 bit nonce and add some more text about the risks and trade-offs of shorter vs longer nonces? I feel someone else might be better at the risks/trade-offs bit, but I'll write a PR to change the value.
I already have one to go up on monday
SG, I'll drop mine then. martinduke NAME wrote:
On the issue of nonce reuse: I toyed with idea of being permissive about this, but for the record here's why there's a problem: At the extreme, say a server uses an independent nonce counter for each of its connections, so the encrypted bits follow a predictable pattern for CIDs 1..n issued over the life of the connection. First, if the plaintext bits (first octet and server-use) are the same, we can't demultiplex connections, and everyone will have everyone else's stateless reset tokens. So at a minimum, the server has to keep the overall CID unique. But even it's unique, if the encrypted bits follow this pattern, then a client connected to that server can identify other clients linked to that server.
Section 5.3 specifies a Shared-State Retry Service describes a token format in which the token include the ODCID, a client IP encoded on 128 bits, and a 20 octets data-time, plus additional data. The token is encrypted using AES ECB. This seems sub-optimal: 1) Using AES GCM or another AEAD format seems more natural. AEAD checks will immediately detect an invalid token, while using ECB forces reliance on invalidity heuristics. 2) If using AEAD, there is no need to encode the IP address in the token. It can be derived from the IP header and placed in a pseudo header. The pseudo header can then be authenticated as part of AEAD decryption. 3) The pseudo header approach can be used to authenticate other fields, e.g. verify that the DCID matches the SCID sent in the Retry packet. 4) Encoding the time as 64 bits time64_t seems more natural than ASCII encoding, and also shorter.
The editor draft also encodes the server's SCID in the token. That too could be moved to a pseudo-header, since the field is assume present in the DCID of the post retry Initial.
This was closed in error, as deals with the Stream Cipher.
I filed a very similar issue and then closed it when I realized the duplication. The text of that issue: I'm not an encryption expert, but if IIUC it's insufficiently hard to forge a shared-state retry token. By inducing a Retry, an attacker can obtain the Retry SCID Length, and then focus entirely on an ODCIDs that allow the CID + CID Length part of the token to be a multiple of 16 B. For example, if RSCIDL= 10 B, then make ODCIDL = 20 B -> 32 Bytes for the block. This then breaks Retry forgery into three separate problems: (1) Obtaining the mapping of IP Address to the first 16 Bytes of the token. A well-positioned observer could build a database of these in the time scale between token key rotations. (2) Generate lots of Retry tokens with an ODCID of the correct length, so there is a range of valid CID blocks. (3) Obtain a valid Retry every few seconds, using the right ODCID length, so that we have a valid timestamp. Thus, the attacker has a database of valid encrypted IP addresses valid encrypted CIDs (really, it's the CID length that would cause validation to fail) valid timestamps As the spec uses AES-ECB, these blocks can be mixed and matched to create valid Retry tokens. This is not exactly trivially open to attack [1], but it does feel like we're conceding a lot of entropy here. I would like someone to propose an alternate design that restores some of that entropy. [1] Step (1) seems to require a fairly privileged position in the network.
NAME Could I trouble you for a PR on this?
Sure. I can basically propose what I am doing in picoquic. And yes, the repetitive nature of ECB enables attacks.
NAME please review PR

4.4. <ins> The Triple Stream Cipher CID algorithm is a variant of the stream cipher algorithm stream-cipher-cid-algorithm that provides true cryptographic protection and also mitigates the malleability issues with a simple stream cipher application.  The CID format is the the same as depicted in stream-cipher-cid-format. 4.4.1. The configuration agent actions are the same as in stream-cipher-cid- format-configuration. 4.4.2. Upon receipt of a QUIC packet that is not of type Initial or 0-RTT, the load balancer extracts as many of the earliest octets from the destination connection ID as necessary to match the nonce length. The server ID immediately follows. The load balancer decrypts the nonce and the server ID using the following three pass algorithm: Pass 1: The load balancer decrypts the server ID using 128-bit AES Electronic Codebook (ECB) mode, much like QUIC header protection. The encrypted nonce octets are zero-padded to 16 octets.  AES-ECB encrypts this encrypted nonce using its key to generate a mask which it applies to the encrypted server id.  This provides an intermediate value of the server ID, referred to as server-id intermediate. server_id_intermediate = encrypted_server_id ^ AES-ECB(key, padded- encrypted-nonce) Pass 2: The load balancer decrypts the nonce octets using 128-bit AES Electronic Codebook (ECB) mode, much like QUIC header protection, using the server-id intermediate as "nonce" for this pass.  The server-id intermediate octets are zero-padded to 16 octets.  AES-ECB encrypts this padded server-id intermediate using its key to generate a mask which it applies to the encrypted nonce.  This provides the decrypted nonce value. nonce = encrypted_nonce ^ AES-ECB(key, padded-server_id_intermediate) Pass 3: The load balancer decrypts the server ID using 128-bit AES Electronic Codebook (ECB) mode, much like QUIC header protection. The nonce octets are zero-padded to 16 octets.  AES-ECB encrypts this nonce using its key to generate a mask which it applies to the intermediate server id.  This provides the dercypted server ID. server_id = server_id_intermediate ^ AES-ECB(key, padded-nonce) This three pass algorithm is a simplified version of the FFX algorithm.  It has the same connection ID length requirements as the Stream Cipher CID Algorithm, with the additional property that each encrypted nonce value depends on all server ID bits, and each encrypted server ID bit depends on all nonce bits and all server ID bits.  This mitigates attacks against stream ciphers in which attackers simply flip encrypted server-ID bits. The output of the decryption is the server ID that the load balancer uses for routing. 4.4.3. When generating a routable connection ID, the server writes arbitrary bits into its nonce octets, and its provided server ID into the server ID octets.  Servers MAY opt to have a longer connection ID beyond the nonce and server ID.  The nonce and additional bits MAY encode additional information, but SHOULD appear essentially random to observers. The server encrypts the server ID using exactly the algorithm as described in triple-stream-cipher-load-balancer-actions, performing the three passes in reverse order. 4.5. </ins> The Block Cipher CID Algorithm, by using a full 16 octets of plaintext and a 128-bit cipher, provides higher cryptographic protection and detection of non-compliant connection IDs.  However, it also requires connection IDs of at least 17 octets, increasing overhead of client-to-server packets.  <ins> 4.5.1. </ins> The configuration agent assigns a server ID to every server in its pool, and determines a server ID length (in octets) sufficiently The configuration agent also selects an 16-octet AES-ECB key to use for connection ID decryption.  <ins> 4.5.2. </ins> Upon receipt of a QUIC packet, the load balancer reads the first octet to obtain the config rotation bits.  It then decrypts the opaque server data in that order.  The load balancer uses the server ID octets for routing.  <ins> 4.5.3. </ins> When generating a routable connection ID, the server MUST choose a connection ID length between 17 and 20 octets.  The server writes its


Load balancers SHOULD drop packets with non-compliant DCIDs in a short header.  Load balancers MUST forward packets with compliant DCIDs to a server in accordance with the chosen routing algorithm. the earliest octets from the destination connection ID as necessary to match the nonce length.  The server ID immediately follows. <del> The load balancer decrypts the server ID using 128-bit AES Electronic Codebook (ECB) mode, much like QUIC header protection.  The nonce octets are zero-padded to 16 octets.  AES-ECB encrypts this nonce using its key to generate a mask which it applies to the encrypted server id. server_id = encrypted_server_id ^ AES-ECB(key, padded-nonce) For example, if the nonce length is 10 octets and the server ID length is 2 octets, the connection ID can be as small as 13 octets. The load balancer uses the the second through eleventh of the connection ID for the nonce, zero-pads it to 16 octets using the first 6 octets of the token, and uses this to decrypt the server ID in the twelfth and thirteenth octet. The output of the decryption is the server ID that the load balancer uses for routing. 4.3.3. When generating a routable connection ID, the server writes arbitrary bits into its nonce octets, and its provided server ID into the server ID octets.  Servers MAY opt to have a longer connection ID beyond the nonce and server ID.  The nonce and additional bits MAY encode additional information, but SHOULD appear essentially random to observers. The server decrypts the server ID using 128-bit AES Electronic Codebook (ECB) mode, much like QUIC header protection.  The nonce octets are zero-padded to 16 octets using the as many of the first octets of the token as necessary.  AES-ECB encrypts this nonce using its key to generate a mask which it applies to the server id. encrypted_server_id = server_id ^ AES-ECB(key, padded-nonce) 4.4. The Triple Stream Cipher CID algorithm is a variant of the stream cipher algorithm stream-cipher-cid-algorithm that provides true cryptographic protection and also mitigates the malleability issues with a simple stream cipher application.  The CID format is the the same as depicted in stream-cipher-cid-format. 4.4.1. The configuration agent actions are the same as in stream-cipher-cid- format-configuration. 4.4.2. Upon receipt of a QUIC packet, the load balancer extracts as many of the earliest octets from the destination connection ID as necessary to match the nonce length.  The server ID immediately follows. </del> The load balancer decrypts the nonce and the server ID using the following three pass algorithm: server_id = server_id_intermediate ^ AES-ECB(key, padded-nonce)  This three-pass algorithm is a simplified version of the FFX <del> algorithm.  It has the same connection ID length requirements as the Stream Cipher CID Algorithm, with the additional property that each encrypted nonce value depends on all server ID bits, and each encrypted server ID bit depends on all nonce bits and all server ID bits.  This mitigates attacks against stream ciphers in which attackers simply flip encrypted server-ID bits. </del>  The output of the decryption is the server ID that the load balancer uses for routing. <del> 4.4.3. </del>  When generating a routable connection ID, the server writes arbitrary bits into its nonce octets, and its provided server ID into the server. The server encrypts the server ID using exactly the algorithm as <del> described in triple-stream-cipher-load-balancer-actions, performing the three passes in reverse order. </del>  <del> 4.5. </del>  The Block Cipher CID Algorithm, by using a full 16 octets of plaintext and a 128-bit cipher, provides higher cryptographic it also requires connection IDs of at least 17 octets, increasing overhead of client-to-server packets. <del> 4.5.1. </del>  The configuration agent assigns a server ID to every server in its pool, and determines a server ID length (in octets) sufficiently The configuration agent also selects an 16-octet AES-ECB key to use for connection ID decryption. <del> 4.5.2. </del>  Upon receipt of a QUIC packet, the load balancer reads the first octet to obtain the config rotation bits.  It then decrypts the opaque server data in that order.  The load balancer uses the server ID octets for routing. <del> 4.5.3. </del>  When generating a routable connection ID, the server MUST choose a connection ID length between 17 and 20 octets.  The server writes its

Although more complex, the three-pass algorithm seems strictly better.
I like it, but there is some imprecision in the text. I would like to see something about detecting corruption, probably in the load balancer actions.

Load balancers SHOULD drop packets with non-compliant DCIDs in a short header. <ins> A QUIC-LB configuration MAY significantly over-provision the server ID space (i.e., provide far more codepoints than there are servers) to increase the probability that a randomly generated Destination Connection ID is non- compliant. </ins> Load balancers MUST forward packets with compliant DCIDs to a server in accordance with the chosen routing algorithm. the earliest octets from the destination connection ID as necessary to match the nonce length.  The server ID immediately follows.  The load balancer decrypts the nonce and the server ID using the following three pass algorithm: server_id = server_id_intermediate ^ AES-ECB(key, padded-nonce) <ins> For example, if the nonce length is 10 octets and the server ID length is 2 octets, the connection ID can be as small as 13 octets. The load balancer uses the the second through eleventh octets of the connection ID for the nonce, zero-pads it to 16 octets, uses xors the result with the twelfth and thirteenth octet.  The result is padded with 14 octets of zeros and encrypted to obtain a mask that is xored with the nonce octets.  Finally, the nonce octets are padded with six octets of zeros, encrypted, and the first two octets xored with the server ID octets to obtain the actual server ID. </ins> This three-pass algorithm is a simplified version of the FFX  <ins> algorithm, with the property that each encrypted nonce value depends on all server ID bits, and each encrypted server ID bit depends on all nonce bits and all server ID bits.  This mitigates attacks against stream ciphers in which attackers simply flip encrypted server-ID bits. </ins> The output of the decryption is the server ID that the load balancer uses for routing.  <ins> 4.3.3. </ins> When generating a routable connection ID, the server writes arbitrary bits into its nonce octets, and its provided server ID into the server. The server encrypts the server ID using exactly the algorithm as  <ins> described in stream-cipher-load-balancer-actions, performing the three passes in reverse order. </ins>  <ins> 4.4. </ins> The Block Cipher CID Algorithm, by using a full 16 octets of plaintext and a 128-bit cipher, provides higher cryptographic it also requires connection IDs of at least 17 octets, increasing overhead of client-to-server packets.  <ins> 4.4.1. </ins> The configuration agent assigns a server ID to every server in its pool, and determines a server ID length (in octets) sufficiently The configuration agent also selects an 16-octet AES-ECB key to use for connection ID decryption.  <ins> 4.4.2. </ins> Upon receipt of a QUIC packet, the load balancer reads the first octet to obtain the config rotation bits.  It then decrypts the opaque server data in that order.  The load balancer uses the server ID octets for routing.  <ins> 4.4.3. </ins> When generating a routable connection ID, the server MUST choose a connection ID length between 17 and 20 octets.  The server writes its


3.1. <del> The first two bits of any connection-ID MUST encode the configuration phase of that ID.  QUIC-LB messages indicate the phase of the algorithm and parameters that they encode. A new configuration may change one or more parameters of the old configuration, or change the algorithm used. It is possible for servers to have mutually exclusive sets of supported algorithms, or for a transition from one algorithm to another to result in Fail Payloads.  The four states encoded in these two bits allow two mutually exclusive server pools to coexist, and for each of them to transition to a new set of parameters. </del>  When new configuration is distributed to servers, there will be a transition period when connection IDs reflecting old and new balancers to apply the correct routing algorithm and parameters to incoming packets. <del> Configuration Agents SHOULD make an effort to deliver new configurations to load balancers before doing so to servers, so that load balancers are ready to process CIDs using the new parameters when they arrive. </del>  A Configuration Agent SHOULD NOT use a codepoint to represent a new configuration until it takes precautions to make sure that all Servers MUST NOT generate new connection IDs using an old configuration after receiving a new one from the configuration agent. <del> Servers MUST send NEW_CONNECTION_ID frames that provide CIDS using </del>  the new configuration, and retire CIDs using the old configuration using the "Retire Prior To" field of that frame.  3.2. If a server has not received a valid QUIC-LB configuration, and believes that low-state, Connection-ID aware load balancers are in the path, it SHOULD generate connection IDs with the config rotation <del> bits set to '11' and SHOULD use the "disable_migration" transport parameter in all new QUIC connections.  It SHOULD NOT send </del>  NEW_CONNECTION_ID frames with new values. A load balancer that sees a connection ID with config rotation bits A server not using this functionality SHOULD make the six bits appear to be random.  4. In QUIC-LB, load balancers do not generate individual connection IDs 10.3.  A simple deployment of QUIC-LB in a cloud provider might use the same global QUIC-LB configuration across all its load balancers that route to customer servers.  An attacker could then simply become a masks and divisors (for the obfuscated algorithm).  The load balancers can distinguish these configurations by external IP address, or by assigning different values to the config rotation bits <del> (config-rotation).  Note that either of these techniques exposes information to outside observers, as traffic destined for each server set can be easily distinguished. </del>  These techniques are not necessary for the plaintext algorithm, as it does not attempt to conceal the server ID. <del> 10.4. </del>  Section 21.9 of QUIC-TRANSPORT discusses the Stateless Reset Oracle attack.  For a server deployment to be vulnerable, an attacking

Erik Fuller points out via email: He's right. We should just call it a configuration ID and be clearer on what's what.

3.1.  <ins> The first two bits of any connection ID MUST encode an identifier for the configuration that the connection ID uses.  This enables incremental deployment of new QUIC-LB settings (e.g., keys). </ins> When new configuration is distributed to servers, there will be a transition period when connection IDs reflecting old and new balancers to apply the correct routing algorithm and parameters to incoming packets.  <ins> Configuration Agents SHOULD deliver new configurations to load balancers before doing so to servers, so that load balancers are ready to process CIDs using the new parameters when they arrive. </ins> A Configuration Agent SHOULD NOT use a codepoint to represent a new configuration until it takes precautions to make sure that all Servers MUST NOT generate new connection IDs using an old configuration after receiving a new one from the configuration agent.  <ins> Servers MUST send NEW_CONNECTION_ID frames that provide CIDs using </ins> the new configuration, and retire CIDs using the old configuration using the "Retire Prior To" field of that frame. <ins> It also possible to use these bits for more long-lived distinction of different configurations, but this has privacy implications (see multiple-configs). </ins> 3.2. If a server has not received a valid QUIC-LB configuration, and believes that low-state, Connection-ID aware load balancers are in the path, it SHOULD generate connection IDs with the config rotation  <ins> bits set to '11' and SHOULD use the "disable_active_migration" transport parameter in all new QUIC connections.  It SHOULD NOT send </ins> NEW_CONNECTION_ID frames with new values. A load balancer that sees a connection ID with config rotation bits A server not using this functionality SHOULD make the six bits appear to be random. <ins> 3.4. The first octet has the following fields: CR: Config Rotation bits. CID Len: Length Self-Description (if applicable).  Encodes the length of the Connection ID following the First Octet. </ins> 4. In QUIC-LB, load balancers do not generate individual connection IDs 10.3. <ins> During the period in which there are multiple deployed configuration IDs (see config-rotation), there is a slight increase in linkability. The server space is effectively divided into segments with CIDs that have different config rotation bits.  Entities that manage servers SHOULD strive to minimize these periods by quickly deploying new configurations across the server pool. 10.4. </ins> A simple deployment of QUIC-LB in a cloud provider might use the same global QUIC-LB configuration across all its load balancers that route to customer servers.  An attacker could then simply become a masks and divisors (for the obfuscated algorithm).  The load balancers can distinguish these configurations by external IP address, or by assigning different values to the config rotation bits  <ins> (config-rotation).  Note that either solution has a privay impact; see multiple-configs. </ins> These techniques are not necessary for the plaintext algorithm, as it does not attempt to conceal the server ID.  <ins> 10.5. </ins> Section 21.9 of QUIC-TRANSPORT discusses the Stateless Reset Oracle attack.  For a server deployment to be vulnerable, an attacking


ID Length, Key, and Zero-Padding Length.  The Server ID is unique to each server, but the others MUST be global.  A full QUIC-LB configuration MUST also specify the information content of the first CID octet and the presence and mode of any Retry Service.

Updated the test vectors for the latest version of the spec, added PCID, and made them usable to test server implementations.

ID Length, Key, and Zero-Padding Length.  The Server ID is unique to each server, but the others MUST be global. <ins> Note that server IDs are opaque bytes, not integers, so there is no notion of network order or host order. </ins> A full QUIC-LB configuration MUST also specify the information content of the first CID octet and the presence and mode of any Retry Service.


The DCID might not be long enough for the routing algorithm to process.  The extracted server mapping might not correspond to an active server. Load balancers SHOULD drop packets with non-compliant DCIDs in a short header.  A QUIC-LB configuration MAY significantly over-provision the server ID space (i.e., provide far more codepoints than there are servers) to increase the probability that a randomly generated Destination

Make it clear that the server has to provide the client with a compliant DCID if the client isn't already using one.
Dear sir, As you know, there is no bit in the cid to mark whether the cid encoded the server info or not. In this case, no matter the server info encoded or not, the load balancer needs to decrypt or decode the cid. Do you think this is a useless try when the packet is the first initial packet? Do you think this is a useful idea to expand the cid format to use the first bit to mark the cid encoded or not? However, in this case, the client need to obey the rule.
In fact, there is a bit. If the first two bits are '11' that means that the server didn't encode its ID and the load balancer should use 4-tuple routing: URL The very first packet usually has a CID chosen by the client, which knows nothing about QUIC-LB. The beginning of Section 4 is mostly about how load balancers should handle these.
OK, I almost have forgot the first two Config Rotation bits can be used to mark the cid server info's validity. However, in this case, I want to know , for the very first packet, will the draft suggest the clients to generate the dcid with the first two bits "11" , if they know the QUIC-LB used. As, if so, the QUIC-LB know all packets' cid information to prevent useless decode/decrypt try for the very first packet.
No, this draft does not assume client cooperation at all. The very beginning of Section 4 explains how to handle client-generated CIDs.
Thanks for your reply. Actually, I understand your works and Section 4. However, my point is that , even this draft does not assume client cooperation at all, this draft can suggest the client to generate a cid with 11 for the very first initial packet. This is useful for QUIC-LB if client obeys the rule, if not , the Section 4 also works well.
In I'm adding CR bits as a reason for noncompliance. It's fine if the client wants to set '11' , but I don't think it's important to do that. At most all we're saving the LB is a very small decrypt operation.
Even thought the decryption has a low consumption, I insist that the cost can be avoided. I admit that the lb can't assume client cooperation at all, however, the draft can suggest the client to obey the rule: " As, it is impossible for the client to know the server information at the beginning of the QUIC conn, so the DCID in the initial can't encode the server info, in this case, the Client should make the first two bits with "11". "

The DCID might not be long enough for the routing algorithm to process. <ins> The config rotation bits config-rotation may not correspond to an active configuration. </ins> The extracted server mapping might not correspond to an active server. Load balancers SHOULD drop packets with non-compliant DCIDs in a short header. <ins> Servers that receive packets with noncompliant CIDs MUST use the available mechanisms to induce the client to use a compliant CID in future packets.  In QUIC version 1, this requires using a compliant CID in the Source CID field of server-generated long headers. </ins> A QUIC-LB configuration MAY significantly over-provision the server ID space (i.e., provide far more codepoints than there are servers) to increase the probability that a randomly generated Destination


packet.  The client IP address is 16 octets.  If an IPv4 address, the last 12 octets are zeroes. <del> date-time: The date-time string is a total of 20 octets and encodes the time the token was generated.  The format of date-time is described in Section 5.6 of RFC3339.  This ASCII field MUST use the "Z" character for time-offset. </del>  Opaque Data: The server may use this field to encode additional information, such as congestion window, RTT, or MTU.  Opaque data

As the draft describes: 'The date-time string is a total of 20 octets and encodes the time the token was generated. The format of date-time is described in Section 5.6 of [RFC3339].' this needs 20 octets in ascii, may be the unix time which is the the number of seconds since the Unix epoch in 8 octets is a better choice in transmission and computing ?
NAME have a look at the PR, please

packet.  The client IP address is 16 octets.  If an IPv4 address, the last 12 octets are zeroes.  <ins> Timestamp: The timestamp is a 64-bit integer, in network order, that expresses the number of seconds in POSIX time (see Sec. 4.16 of TIME_T). </ins> Opaque Data: The server may use this field to encode additional information, such as congestion window, RTT, or MTU.  Opaque data


Both server and service must have access to Universal time, though tight synchronization is unnecessary. <del> All tokens, generated by either the server or retry service, MUST use the following format.  This format is the cleartext version.  On the wire, these fields are encrypted using an AES-ECB cipher and the token key.  If the token is not a multiple of 16 octets, the last block is padded with zeroes. The tokens have the following fields: </del>  ODCIL: The original destination connection ID length.  Tokens in NEW_TOKEN frames MUST set this field to zero. Retry Source Connection ID: The server or Retry service copies this from the Source Connection ID of the Retry packet. <del> Client IP Address: The source IP address from the triggering Initial packet.  The client IP address is 16 octets.  If an IPv4 address, the last 12 octets are zeroes.  If there is a Network Address Translator (NAT) in the server infrastructure that changes the client IP, the Retry Service MUST either be positioned behind the NAT, or the NAT must have the token key to rewrite the Retry token accordingly. Timestamp: The timestamp is a 64-bit integer, in network order, that expresses the number of seconds in POSIX time (see Sec. 4.16 of TIME_T). </del>  Opaque Data: The server may use this field to encode additional information, such as congestion window, RTT, or MTU.  Opaque data MAY also allow servers to distinguish between retry tokens (which trigger use of certain transport parameters) and NEW_TOKEN frame tokens.  6.3.1. <del> The configuration agent generates and distributes a "token key" and a list of QUIC versions the service supports.  It must also inform the service if a NAT lies between the service and the servers. </del>  6.3.2.  When in active mode, the service MUST generate Retry tokens with the format described above when it receives a client Initial packet with no token. <del> In active mode, the service SHOULD decrypt the first 16 octets of incoming tokens.  The service SHOULD drop packets with an IP address that does not match, and SHOULD forward packets that do, regardless of the other fields. </del>  However, the service MUST NOT decrypt or validate tokens if there is a NAT between it and the servers. server so that the server can issue an up-to-date token to the client. <del> 6.3.3. </del>  <del> When issuing Retry or NEW_TOKEN tokens, the server MUST encode the client IP address in the first 16 octets and encrypt that block with the token key.  It MAY use any format or encryption for the remainder of the token.  However, it MUST include a means of distinguishing </del>  service-generated Retry tokens, server-generated Retry tokens (if different), and NEW_TOKEN tokens. timestamp field to apply its expiration limits for tokens.  This need not be precisely synchronized with the retry service.  However, servers MAY allow retry tokens marked as being a few seconds in the <del> future, due to possible clock synchronization issues. </del>  After decrypting the token, the server uses the corresponding fields to populate the original_destination_connection_id transport

This PR specifies how the retry token should be encoded using AEAD. This replaces the current method that uses AES ECB, and has potential vulnerabilities since encrypted addresses and encrypted ODCID might be encrypted independently. There are differences between the two solutions: AEAD requires key and IV, not just key. The IV will have to be provisioned with the key. AEAD requires a nonce. The proposed format defines a 64 bit "token sequence number". We may want to discuss this further, and specify how exactly this is generated. The client address is not encrypted as part of the token, but is verified through AEAD The AEAD protected token may end up 8 bytes longer than the ECB protected token: remove IP address (-16), add a nonce (+8), add the AEAD checksum (+16). On the other hand, the ECB must be padded to the nearest 16 bytes, while AEAD does not require that. Probably not very important. I assume we are using AES128-GCM, but I suppose the specific algorithm could be provisioned with the key.
Waiting for the resolution of the Token Sequence Number issue before updating the PR. I am tempted to just make that field 96 bits, and specify randomness.
NAME I think we have five options: (1) A 96-bit random number for the sequence number, and just assume there are no collisions (you've computed the very low probability) -- this is wire inefficient but very easy (2) For the shared-state case, the service simply forwards packets with server-generated tokens rather than trying to validate them. Therefore this format is only generated by the retry service and it can use all the sequence numbers without fear of collision. -- this is very easy and wire-efficient, but slightly reduces the usefulness of the Retry Service. Then there are three variations of "give each server some of the sequence number space": (3a) Just put the whole (up to 18B server ID) in the token: easy, but makes big tokens. The retry service must be configured with at least 1 server ID it can use for its own tokens. (3b) Force the Retry Service to have the same QUIC-LB decoder as the load balancer, so it can extract the server ID from the CID and use that as part of the IV: most wire-efficient, but expensive for the Retry service. (3c) Assign each server a shorter ID for Retry purposes, and put in the token: medium token size, but involves the most configuration. I weakly prefer (2). if you have a different preference, I'm willing to go along with it; or we could take it to the list.
I just did a PR in Picotls to make sure that the API can let us set a 96 bit "sequence number". Turns out that this is also required for handling multipath. Putting the whole server ID in the token really means "putting the whole server ID in the sequence number. If you do not do that, you risk AEAD collisions, and that's not too good. Putting the server ID in the token but not in the sequence number does not rid you of AEAD collisions, so 3a is not really an option. 3c can work if the server ID is < 32 bit. You would construct the 96 bit "sequence number" as 32 bits of "short" server ID and 64 bit of per server sequence number. I am not sure that I understand your option 2. There is no big issue for "retry tokens" sent through "retry" packets, since the protocol also indicates the CID to be used by the client. The LB can route based on the CID. There is only an issue for "new tokens" used in conjunction with a random IDCID.
A fuller explanation of option 2: This design is for the shared-state Retry token, which is for cases where the Retry Service is not guaranteed to be on the path, and therefore both the server and service have to be able to generate (and validate) their own tokens. The current design has both server and service generate tokens according to the same format. Therefore, if a service generates a token, the server can validate it; if the server generates a token, the service can validate it. Obviously, this saves the server from processing invalid tokens if the service can handle it. The problem, of course, is if we increment sequence numbers there will be collisions between server- and service-generated sequence numbers. Option 2 would make the following changes: Services produce a fixed-length token, in accordance with the standard format Servers MUST NOT generate tokens of this length, but need not follow any format Services MUST NOT process or filter Initial Packets with tokens that are not of the fixed length. This entirely solves the sequence number problem, but it also means the server has to do a bit more work. Thus the Retry Service is offloading some of the Token generation, but not any of the token processing. Writing it carefully out like this, I don't like Option 2 as much anymore.
"Putting the server ID in the token but not in the sequence number does not rid you of AEAD collisions, so 3a is not really an option" to be clear, it would be in the sequence number. The point of putting it in the token is to allow the decoder to reconstruct the IV. I imagine that 32 bits would be plenty of server IDs if it doesn't need to be sparsely populated.
To sum up, I think we only have the following 2 options: (1) A 96-bit random number for the sequence number, and just assume there are no collisions (you've computed the very low probability) -- this is wire inefficient but very easy (3c) Assign each server a shorter ID for Retry purposes, and put in the token: medium token size, but involves the most configuration. For AEAD, compose the 96 bit sequence as "(up to) 32 bit server ID" + "(up to) 64 bit server-chosen token ID". The advantage of (3c) over (1) it potentially reduced transmission overhead. For example, the server ID might well be just 16 bits, the token ID might be just 32 bits. If we have a server ID, the LB can use it to direct the incoming Initial to the server that allocated the token, which may or may not be useful. The drawback of (3c) is more configuration, and also exposing the server ID in cleartext. If server ID privacy is an issue, we need something like header protection as part of the token protection. In both cases, we need some kind of key phase bit in the token to understand which key to use.
I have tried to elaborate the two options for either random generation or server ID.
BTW I am happy to commit if you resolve the nits and we can have a longer discussion on your DISCUSS points.
NAME I think this is ready now, but you may want to review.
NAME I think the last commit fixes the last comments.
Section 5.3 specifies a Shared-State Retry Service describes a token format in which the token include the ODCID, a client IP encoded on 128 bits, and a 20 octets data-time, plus additional data. The token is encrypted using AES ECB. This seems sub-optimal: 1) Using AES GCM or another AEAD format seems more natural. AEAD checks will immediately detect an invalid token, while using ECB forces reliance on invalidity heuristics. 2) If using AEAD, there is no need to encode the IP address in the token. It can be derived from the IP header and placed in a pseudo header. The pseudo header can then be authenticated as part of AEAD decryption. 3) The pseudo header approach can be used to authenticate other fields, e.g. verify that the DCID matches the SCID sent in the Retry packet. 4) Encoding the time as 64 bits time64_t seems more natural than ASCII encoding, and also shorter.
The editor draft also encodes the server's SCID in the token. That too could be moved to a pseudo-header, since the field is assume present in the DCID of the post retry Initial.
This was closed in error, as deals with the Stream Cipher.
I filed a very similar issue and then closed it when I realized the duplication. The text of that issue: I'm not an encryption expert, but if IIUC it's insufficiently hard to forge a shared-state retry token. By inducing a Retry, an attacker can obtain the Retry SCID Length, and then focus entirely on an ODCIDs that allow the CID + CID Length part of the token to be a multiple of 16 B. For example, if RSCIDL= 10 B, then make ODCIDL = 20 B -> 32 Bytes for the block. This then breaks Retry forgery into three separate problems: (1) Obtaining the mapping of IP Address to the first 16 Bytes of the token. A well-positioned observer could build a database of these in the time scale between token key rotations. (2) Generate lots of Retry tokens with an ODCID of the correct length, so there is a range of valid CID blocks. (3) Obtain a valid Retry every few seconds, using the right ODCID length, so that we have a valid timestamp. Thus, the attacker has a database of valid encrypted IP addresses valid encrypted CIDs (really, it's the CID length that would cause validation to fail) valid timestamps As the spec uses AES-ECB, these blocks can be mixed and matched to create valid Retry tokens. This is not exactly trivially open to attack [1], but it does feel like we're conceding a lot of entropy here. I would like someone to propose an alternate design that restores some of that entropy. [1] Step (1) seems to require a fairly privileged position in the network.
NAME Could I trouble you for a PR on this?
Sure. I can basically propose what I am doing in picoquic. And yes, the repetitive nature of ECB enables attacks.
NAME please review PR
Looks great! If my proposed nits seem reasonable to you, hit commit on each of them and then I'll merge this.

Both server and service must have access to Universal time, though tight synchronization is unnecessary.  <ins> The tokens are protected using AES128-GCM AEAD, as explained in token-protection-with-aead.  All tokens, generated by either the server or retry service, MUST use the following format, which includes: A 96 bit unique token number transmitted in clear text, but protected as part of the AEAD associated data. An 8 bit token key identifier. A token body, encoding the Original Destination Connection ID, the Retry Source Connection ID, and the Timestamp, optionally followed by server specific Opaque Data. The token protection uses an 128 bit representation of the source IP address from the triggering Initial packet.  The client IP address is 16 octets.  If an IPv4 address, the last 12 octets are zeroes.  If there is a Network Address Translator (NAT) in the server infrastructure that changes the client IP, the Retry Service MUST either be positioned behind the NAT, or the NAT must have the token key to rewrite the Retry token accordingly. The 96 bit unique token number is set to a random value using a cryptography grade random number generator. The token key identifier and the corresponding AEAD key and AEAD IV are provisioned by the configuration agent. The token body is encoded as follows: ~~~~~ 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ODCIL | RSCIL | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Original Destination Connection ID (0..160) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ...  | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Retry Source Connection ID (0..160) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ...  | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + Timestamp (64) + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Opaque Data (optional) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~~~~~ The token body has the following fields: </ins> ODCIL: The original destination connection ID length.  Tokens in NEW_TOKEN frames MUST set this field to zero. Retry Source Connection ID: The server or Retry service copies this from the Source Connection ID of the Retry packet.  <ins> Timestamp: The Timestamp is a 64-bit integer, in network order, that expresses the expiration time of the token as a number of seconds in POSIX time (see Sec. 4.16 of TIME_T). </ins> Opaque Data: The server may use this field to encode additional information, such as congestion window, RTT, or MTU.  Opaque data MAY also allow servers to distinguish between retry tokens (which trigger use of certain transport parameters) and NEW_TOKEN frame tokens. <ins> Some implementations of QUIC encode in the token the Initial Packet Number used by the client, in order to verify that the client sends the retried Initial with a PN larger that the triggering Initial. Such implementations will encode the Initial Packet Number as part of the opaque data. </ins> 6.3.1.  <ins> On the wire, the token is presented as: ~~~~~ 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + | Unique Token Number (96) | + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Key seq.(8) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | AEAD Encrypted Token (variable) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | AEAD Checksum (variable, function of encryption algorithm) | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~~~ The tokens are protected using AES128-GCM as follows: The token key and IV are retrieved using the Key Sequence. The nonce, N, is formed by combining the IV with the 96 bit unique token number.  The 96 bits of the unique token number are left- padded with zeros to the size of the IV.  The exclusive OR of the padded unique token number and the IV forms the AEAD nonce. The associated data is a formatted as a pseudo header by combining the clear-text part of the token with the IP address of the client.  ~~~~~ 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- +-+-+-+-+-+-+-+-+-+ | | + | | IP Address (128) + | | + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- +-+-+ | Server ID (32) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- +-+-+-+-+-+-+-+-+-+-+-+-+ | | Token sequence number (64) + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Key seq.(8) | +-+-+-+-+-+-+-+-+ ~~~~~ The input plaintext for the AEAD is the token body.  The output ciphertext of the AEAD is transmitted in place of the token body. The AEAD Checksum is computed as part of the AEAD encryption process, and is verified during decryption. </ins> 6.3.2. <ins> The configuration agent generates and distributes a "token key", a "token IV", a key sequence, and a list of QUIC versions the service supports.  It must also inform the service if a NAT lies between the service and the servers. 6.3.3. </ins> When in active mode, the service MUST generate Retry tokens with the format described above when it receives a client Initial packet with no token.  <ins> In active mode, the service SHOULD decrypt the incoming tokens.  The service SHOULD drop packets with unknown key sequence, or an AEAD checksum that does not match the expected value.  (By construction, the AEAD checksum will only match is the client IP Address also matches.)  The service SHOULD drop packets if the Timestamp of the retry token points to a past date; however, in order to allow for clock skew, it SHOULD NOT drop packets if the Timestamp merely points to a a few seconds in the past.  It SHOULD forward packets if the AEAD checksum and the Timestamp can be verified, regardless of the other fields. </ins> However, the service MUST NOT decrypt or validate tokens if there is a NAT between it and the servers. server so that the server can issue an up-to-date token to the client.  <ins> 6.3.4. </ins>  <ins> When issuing Retry or NEW_TOKEN tokens, the server MUST include the client IP address in the authenticated data as specified in token- protection-with-aead.  It MUST include a means of distinguishing </ins> service-generated Retry tokens, server-generated Retry tokens (if different), and NEW_TOKEN tokens. timestamp field to apply its expiration limits for tokens.  This need not be precisely synchronized with the retry service.  However, servers MAY allow retry tokens marked as being a few seconds in the  <ins> past, due to possible clock synchronization issues. </ins> After decrypting the token, the server uses the corresponding fields to populate the original_destination_connection_id transport


lead to loss of packets that contain non-compliant DCIDs, and in extreme cases connection failure.  10. QUIC-LB is intended to prevent linkability.  Attacks would therefore

Some load balancers today switch based on the SNI. Obviously, this is not version-invariant. We should add some language about this. What should such an LB when it encounters an unknown version? It probably has no choice but to forward it based on CID and hope for the best.
If the differing domains trust each other, the servers could helpfully generate Retry packets to redirect the client to the right place. This assumes a bunch of shared state, and that those servers support the same version while the LB does not. Of course, if version support differs, the VN process has serious problems.
Maybe encrypt sni will be the real standard later(see URL), I just think that there is no need for quic lb considering sni

lead to loss of packets that contain non-compliant DCIDs, and in extreme cases connection failure. <ins> Some load balancers might inspect elements of the Server Name Indication (SNI) extension in the TLS Client Hello to make a routing decision.  Note that the format and cryptographic protection of this information may change in future versions or extensions of TLS or QUIC, and therefore this functionality is inherently not version- invariant. </ins> 10. QUIC-LB is intended to prevent linkability.  Attacks would therefore


Abstract <del> QUIC connection IDs allow continuation of connections across address/ port 4-tuple changes, and can store routing information for stateless or low-state load balancers.  They also can prevent linkability of connections across deliberate address migration through the use of protected communications between client and server.  This creates issues for load-balancing intermediaries.  This specification standardizes methods for encoding routing information given a small set of configuration parameters.  This framework also enables offload of other QUIC functions to trusted intermediaries, given the explicit cooperation of the QUIC server. </del>  1.

Less emphasis on load balancers, more on the other stuff. More text on the use cases and less on "how".

Abstract  <ins> The QUIC protocol design is resistant to transparent packet inspection, injection, and modification by intermediaries.  However, the server can explicitly cooperate with network services by agreeing to certain conventions and/or sharing state with those services. This specification provides a standardized means of solving three problems: (1) maintaining routability to servers via a low-state load balancer even when the connection IDs in use change; (2) explicit encoding of the connection ID length in all packets to assist hardware accelerators; and (3) injection of QUIC Retry packets by an anti-Denial-of-Service agent on behalf of the server. </ins> 1.


balancer uses the first two bits of the connection ID to multiplex incoming DCIDs over these schemes.  This section describes three participants: the configuration agent, the load balancer, and the server.

Addresses
The stream and block cipher configuration are locked to AES-ECB. AES-128-ECB too (this needs to be clear). This is a fine design. If a better design is required, that can be achieved by adding a new arm to . It might pay to say that and cite URL at the same time.
LGTM. A suggestion, but I'm OK either way.

balancer uses the first two bits of the connection ID to multiplex incoming DCIDs over these schemes. <ins> Encryption in the algorithms below uses the AES-128-ECB cipher. Future standards could add new algorithms that use other ciphers to provide cryptographic agility in accordance with RFC7696.  QUIC-LB implementations SHOULD be extensible to support new algorithms. </ins> This section describes three participants: the configuration agent, the load balancer, and the server.


ODCIL: The length of the original destination connection ID from the triggering Initial packet.  This is in cleartext to be readable for <del> the server, but authenticated later in the token. </del>  RSCIL: The retry source connection ID length. The token protection uses an 128 bit representation of the source IP address from the triggering Initial packet.  The client IP address is <del> 16 octets.  If an IPv4 address, the last 12 octets are zeroes.  If there is a Network Address Translator (NAT) in the server </del>  infrastructure that changes the client IP, the Retry Service MUST either be positioned behind the NAT, or the NAT must have the token <del> key to rewrite the Retry token accordingly. </del>  The 96 bit unique token number is set to a random value using a cryptography- grade random number generator. RSCIL: The retry source connection ID length.  Tokens in NEW_TOKEN frames MUST set this field to zero.  Original Destination Connection ID: The server or Retry Service copies this from the field in the client Initial packet. POSIX time (see Sec.  4.16 of TIME_T). Opaque Data: The server may use this field to encode additional <del> information, such as congestion window, RTT, or MTU.  Opaque data MAY also allow servers to distinguish between retry tokens (which trigger use of certain transport parameters) and NEW_TOKEN frame tokens. </del>  Some implementations of QUIC encode in the token the Initial Packet Number used by the client, in order to verify that the client sends the retried Initial with a PN larger that the triggering Initial. Such implementations will encode the Initial Packet Number as part of <del> the opaque data. </del>  6.3.1. the AEAD checksum will only match if the client IP Address also matches.)  The service SHOULD drop packets if the Timestamp of the retry token points to a past date; however, in order to allow for clock skew, it SHOULD NOT drop packets if the Timestamp merely points to a a few When issuing Retry or NEW_TOKEN tokens, the server MUST include the client IP address in the authenticated data as specified in token- <del> protection-with-aead.  It MUST include a means of distinguishing service-generated Retry tokens, server-generated Retry tokens (if different), and NEW_TOKEN tokens. </del>  The server MUST validate all tokens that arrive in Initial packets, as they may have bypassed the Retry service.

and
Thanks Christian, Your comments ended up as an editorial rewrite of the section, with port verification added.
The diagrams indicate they are in the range 0..160. This is incorrect.
SS Tokens can have this, because they could be NEW_TOKEN tokens. But the shared state format is limited to 64.
Fixed by
A few more issues with Retry tokens: For Retry, we're supposed to check the client port. Because this isn't true for NEW_TOKEN, we probably can't put it in the shared-state pseudoheader. Relatedly, we currently just say that the shared-state Retry Service has to be behind any NAT. This isn't enough. For non-shared-state, we're probably fine. For a Retry, the NAT will keep the 4-tuple binding so that on either side there is something to validate. For shared-state: if the service is in front of the NAT, the server can't validate the address. if the service is behind the NAT, it'll "work" but to add any value at all there really has to be a port in there somewhere. We can probably assume that the alternate path that creates the need for shared state won't cause a service-generated token to suddenly appear on the unprotected path.
Fixed by
We should tighten up the rules for Resumption Token processing by the Retry Service. When active, it should reject the packet, but it should send Retry. I believe we can distinguish resumption from Retry because the CID length fields are zero; as any Retry token must have a ODCIDL of at least 8, this would appear to be robust. On a related note, the requirement on servers to encode a way to distinguish the two token types is silly, because of this propery.
Fixed by
Currently the Initial Packet number MAY be encoded in the Retry Token. We must either: Include language that a server MUST NOT reject a token because this information is not present; OR Just make it part of the format. (IMO this would be bad, because then the Retry service would have to decrypt the packet number)
Most changes appear editorial, except for the addition of the port number field. If you add that port, you need to also specify how it should be processed!

ODCIL: The length of the original destination connection ID from the triggering Initial packet.  This is in cleartext to be readable for  <ins> the server, but authenticated later in the token.  The Retry Service SHOULD reject any token in which the value is less than 8. </ins> RSCIL: The retry source connection ID length. The token protection uses an 128 bit representation of the source IP address from the triggering Initial packet.  The client IP address is  <ins> 16 octets.  If an IPv4 address, the last 12 octets are zeroes. If there is a Network Address Translator (NAT) in the server </ins> infrastructure that changes the client IP, the Retry Service MUST either be positioned behind the NAT, or the NAT must have the token  <ins> key to rewrite the Retry token accordingly.  Note also that a host that obtains a token through a NAT and then attempts to connect over a path that does not have an identically configured NAT will fail address validation. </ins> The 96 bit unique token number is set to a random value using a cryptography- grade random number generator. RSCIL: The retry source connection ID length.  Tokens in NEW_TOKEN frames MUST set this field to zero. <ins> Port: The Source Port of the UDP datagram that triggered the Retry packet.  This field MUST be present if and only if the ODCIL is greater than zero.  This field is therefore always absent in tokens in NEW_TOKEN frames. </ins> Original Destination Connection ID: The server or Retry Service copies this from the field in the client Initial packet. POSIX time (see Sec.  4.16 of TIME_T). Opaque Data: The server may use this field to encode additional  <ins> information, such as congestion window, RTT, or MTU.  The Retry Service MUST have zero-length opaque data. </ins> Some implementations of QUIC encode in the token the Initial Packet Number used by the client, in order to verify that the client sends the retried Initial with a PN larger that the triggering Initial. Such implementations will encode the Initial Packet Number as part of  <ins> the opaque data.  As tokens may be generated by the Service, servers MUST NOT reject tokens because they lack opaque data and therefore the packet number. </ins> 6.3.1. the AEAD checksum will only match if the client IP Address also matches.) <ins> If the token checksum passes, and the ODCIL and RSCIL fields are both zero, then this is an expired NEW_TOKEN token generated by the server.  An active Retry Service MUST drop the packet and issue a Retry packet with the appropriate token. </ins> The service SHOULD drop packets if the Timestamp of the retry token points to a past date; however, in order to allow for clock skew, it SHOULD NOT drop packets if the Timestamp merely points to a a few When issuing Retry or NEW_TOKEN tokens, the server MUST include the client IP address in the authenticated data as specified in token-  <ins> protection-with-aead.  The ODCIL and RSCIL fields are zero for NEW_TOKEN tokens, making them easily distinguishable from Retry tokens. </ins> The server MUST validate all tokens that arrive in Initial packets, as they may have bypassed the Retry service.


For brevity, "Connection ID" will often be abbreviated as "CID".  2. 2.1. The first octet has the following fields: <del> CR: Config Rotation bits. </del>  <del> CID Len: Length Self-Description (if applicable).  Encodes the length of the Connection ID following the First Octet. </del>  4.

There are so many variable length fields that the ASCII art doesn't serve much of a purpose. Just switch to the notation used in quic-transport for the various CID and token formats.

For brevity, "Connection ID" will often be abbreviated as "CID". <ins> 1.2. All wire formats will be depicted using the notation defined in Section 1.3 of QUIC-TRANSPORT.  There is one addition: the function len() refers to the length of a field which can serve as a limit on a different field, so that the lengths of two fields can be concisely defined as limited to a sum, for example: x(A..B) y(C..B-len(x)) indicates that x can be of any length between A and B, and y can be of any length between C and B provided that (len(x) + len(y)) does not exceed B. The example below illustrates the basic framework: </ins> 2. 2.1. The first octet has the following fields:  <ins> Config Rotation: Indicates the configuration used to interpret the CID. </ins>  <ins> CID Len or Random Bits: Length Self-Description (if applicable), or random bits otherwise.  Encodes the length of the Connection ID following the First Octet. </ins> 4.


4.1.3. <del> The server chooses a connection ID length.  This MUST be at least one byte longer than the routing bytes. </del>  When a server needs a new connection ID, it encodes its assigned server ID in consecutive octets beginning with the second.  All other 4.2. <del> The ECMP CID Algorithm is similar to the plaintext CID algorithm, but allows the L4 load balancer to decide which server to send traffic to on a per-CID basis.  This still links connections to servers, but the connection is dynamic and slightly less linkable than the plaintext CID algorithm.  The format is depicted in the figure below. </del>  4.2.1. <del> The configuration agent selects a length for the server ID encoding. This length MUST have enough entropy to have a different code point for each server. </del>  4.2.2. <del> On each incoming packet, the load balancer extracts consecutive octets, beginning with the second octet.  If these octets match an existing server ID, the packets are routed to the new server ID.  If the octets are not a match and the packet is an Initial packet, then it is assumed to be a new flow and load balanced. </del>  4.2.3. <del> The server chooses a connection ID length.  This MUST be at least one byte longer than the routing bytes. </del>  <del> When a server needs a new connection ID, it selects a random CID from another connection which is known to route identically and randomizes the non-routing bytes of the CID. </del>  4.3. (IP address, UDP port, client-generated destination Connection ID) remain constant for all packets sent on the same connection.  While this document does not update the commitments in QUIC- INVARIANTS, the additional assumptions are minimal and narrowly scoped, and provide a likely set of constants that load balancers can

follow-on to , also fixing
NAME asks if there is added value in expressing server ID lengths in bits instead octets. This is a bit of implementation complexity and a lot of churn in the spec and the handful of implementations that exist, but it does give the configuration agent a little more granularity. Does anyone find the value of this granularity to be compelling?
My thinking is that increasing/decreasing the server ID space by 256 is quite a large jump and could make config rotation a bit more complex. Adding an extra bit to the server ID doubles the number of IDs, which is a fairly widely used increase in data structures and algorithms.
While having such fine control would optimize for efficiency, splitting up bytes like this would complicate implementation. I'm also doubt that 1B of overhead is really going to affect anything. Closing this issue due to old age; can reopen if anyone wants to fight it out.
NAME proposed a PCID design that assigns SIDs on the fly instead of having to pre-configure them. There are drawbacks but it has some nice properties.
This is merged, but I'm going to take the question of what we can do with these dueling plaintext designs to the list.
List discussion was not helpful; closing this with the dynamic method still in the draft.

4.1.3.  <ins> The server chooses how many octets to reserve for its own use, which MUST be at least one octet. </ins> When a server needs a new connection ID, it encodes its assigned server ID in consecutive octets beginning with the second.  All other 4.2.  <ins> The Low-Config CID Algorithm is similar to the plaintext CID algorithm, but allows the load balancer to assign server IDs dynamically so that configuration does not require bespoke server ID assignment.  This also reduces linkability.  However, it requires state at the load balancer that roughly scales with the number of connections, until the server ID codespace is exhausted. </ins> 4.2.1.  <ins> The configuration agent selects a length for the server ID encoding that MUST have at least enough entropy to have a different code point for each server.  It MUST be no more than seven octets. The configuration agent also selects an LB Timeout. </ins> 4.2.2.  <ins> The load balancer maintains a table of all assigned server IDs, which is initialized empty.  On each incoming packet, the load balancer extracts consecutive octets, beginning with the second octet.  If these octets match an existing server ID, the packets are routed to the matching server ID.  As there are no assigned server IDs at first, the first incoming packet has a non-compliant DCID by definition. The server ID table is distinct for each instance of low-config CID algorithm configuration, even if two configurations use the same server ID length.  Thus a given server ID might correspond to a different server in each configuration. Note that each server will usually have many server IDs assigned to it. The load balancer MUST keep track of the most recent observation of each server ID in the table and delete the entries when the time since that observation exceeds the LB Timeout. 4.2.2.1. In QUIC version 1, incoming Connection IDs generated by clients should be no less than 8 octets, so all non-compliant DCIDs should not be due to insufficient length.  Load balancers that are operating the Low-Config CID algorithm SHOULD drop incoming packets with CIDs too short to execute it unless it can be routed using another configured algorithm. If the DCID is non-compliant solely because the server ID is unassigned, the load balancers follows the procedures in routing- algorithms and adds the server ID and assignment the table. If the DCID references the 4-tuple routing bits or an undefined configuration, use the following procedure to establish a predictable template for server ID extraction: Identify the instance of Low-Config CID configuration with the largest config rotation codepoint.  For example, if configurations 0b10, 0b01, and 0b00 all use the low-config CID algorithm and have server ID lengths of 3, 5, and 7 octets, respectively, and a packet comes in with codepoint 0b11, the load balancer would extract 3 octets for the server ID. Extract the appropriate number of octets. If the server ID matches one already in the table, forward the packet to that server. If not, the load balancer runs the algorithm of its choosing and adds the extracted server ID to the table corresponding to the highest-value Low-Config CID Configuration codepoint. </ins> 4.2.3.  <ins> The server chooses how many octets to reserve for its own use, which MUST be at least one octet.  It also maintains a list of server IDs it has observed and the last time it received a packet with that ID. This list is distinct for each instance of Low-Config CID Algorithm configuration. When a server receives a packet, it extracts the server ID.  If the config rotation bits in the CID encode 4-tuple routing or an undefined configuration, it uses the highest-value Low-Config CID config rotation codepoint and extracts the server ID as if that configuration applied. </ins>  <ins> If the server ID is not currently in the list, the server adds it. The server ID's entry is also updated with the most recent arrival of that server ID. When a server needs a new connection ID, it uses one of the server IDs in its list to populate the server ID field of that CID.  It SHOULD vary this selection to reduce linkability within a connection. If a server has not received a connection ID encoding a particular server ID within the LB timeout, it MUST retire any outstanding CIDs that use that server ID and cease generating any new ones. A server SHOULD have a mechanism to stop using some server IDs if the list gets large relative to its share of the codepoint space, so that these allocations time out and are freed for reuse by servers that have recently joined the pool. </ins> 4.3. (IP address, UDP port, client-generated destination Connection ID) remain constant for all packets sent on the same connection. <ins> The Low-Config CID algorithm is dependent on client-generated Destination CIDs in Initial Packets being at least 8 octets in length.  If they are not, the load balancer may not be able to extract a valid server ID to add to its table.  Configuring a shorter server ID length can increase robustness to a change. </ins> While this document does not update the commitments in QUIC- INVARIANTS, the additional assumptions are minimal and narrowly scoped, and provide a likely set of constants that load balancers can


connection, it is a sufficient means of avoiding an Oracle without additional measures.  11. There are no IANA requirements.

NAME please take a look.
Thanks Christian, this looks great.
NAME has done some nice analysis on the constraints of using a 96-bit random nonce to encrypt the Shared-state retry keys. This analysis should be in security considerations.
I did a nice analysis? I am collecting here the relevant comments on PR : 1- (by NAME ) Birthday paradox says with a 64 bit random field you will expect collisions after 2^32 tokens -- and that you have to change the key sooner than that if you want to minimize the risk. If you want the risk of collision below 10E-5 (five nines...) then you should not use the key more than 19 million times. It should be technically possible to make the identifier 12 bytes long -- that's the normal size of the IV for AES GCM. The same reasoning says that the key should not be use more that 4.0 E+14 times -- 400 trillion times. That may be the way to go. Better than trying to squeeze an 18 bytes ID there. Or we would have to use something a bit more exotic than AES GCM, but I would rather not go there. 2- (by NAME ) I think we have five options: (1) A 96-bit random number for the sequence number, and just assume there are no collisions (you've computed the very low probability) -- this is wire inefficient but very easy (2) For the shared-state case, the service simply forwards packets with server-generated tokens rather than trying to validate them. Therefore this format is only generated by the retry service and it can use all the sequence numbers without fear of collision. -- this is very easy and wire-efficient, but slightly reduces the usefulness of the Retry Service. 3- (by NAME we only have the following 2 options: (1) A 96-bit random number for the sequence number, and just assume there are no collisions (you've computed the very low probability) -- this is wire inefficient but very easy. (3c) Assign each server a shorter ID for Retry purposes, and put in the token: medium token size, but involves the most configuration. For AEAD, compose the 96 bit sequence as "(up to) 32 bit server ID" + "(up to) 64 bit server-chosen token ID". The advantage of (3c) over (1) it potentially reduced transmission overhead. For example, the server ID might well be just 16 bits, the token ID might be just 32 bits. If we have a server ID, the LB can use it to direct the incoming Initial to the server that allocated the token, which may or may not be useful. The drawback of (3c) is more configuration, and also exposing the server ID in cleartext. If server ID privacy is an issue, we need something like header protection as part of the token protection. After discussion, we realized that the "96 random bits" option was preferable overall, because: 1) It does not require managing short "configuration identifiers" in complement to server ID; 2) It does create some configuration overhead, but that's limited to at most 4 bytes per token, thus manageable; 3) Birthday paradox tells us that the first collision has a 50% chance of happening after 2^48 tokens have been allocated, which is a fairly large number in practice. We will want to rotate the token encryption key sooner than that.
Or do you want to compare AES-GCM to other solutions, such as AES-ECB? ECB encryption has a predictability issue, which is demonstrated for example by the penguin picture in . That weakness can expose repetitive patterns in 16-bytes chunks of the token, server-id for example.
AEAD encryption also has the advantage of immediate detection of forged tokens.
But there is one weakness in the "96 bit random" proposal. It requires that the generators actually use a crypto grade random number generator.
I was referring to the specific bit about how long it is safe to use a given key because we have 96 bits of entropy. Not the rest.
Oh, that. We should ask CFRG, but the conservative behavior would be to follow the guidelines in QUIC-TLS section 6.6: "For AEADAES128GCM and AEADAES256GCM, the confidentiality limit is 2^23 encrypted packets; see Appendix B.1. For AEADCHACHA20POLY1305, the confidentiality limit is greater than the number of possible packets (2^62) and so can be disregarded. For AEADAES128_CCM, the confidentiality limit is 2^21.5 encrypted packets; see Appendix B.2. Applying a limit reduces the probability that an attacker can distinguish the AEAD in use from a random permutation; see [AEBounds], [ROBUST], and [GCM-MU]." So, basically, 8 million tickets.
But the CFRG computations assume messages that are 10K long. Tickets would typically be 1/100th of that, that may well make a difference. Or maybe it doesn't. Got to ask.
The 96 bits make a difference with 64 bits in the risk of collisions. If there are 2^32 tickets issued with the same key, the chances of at least one collision with 64 bit unique number is 50%; with 96 bits, it is about 1.1E-10. Even if there are only 2^26 tickets issued with that key, the chances of at least one collision with 64 bit unique number is still 0.016%, which many will find too high for comfort.
Do you feel comfortable writing a paragraph about this in the Security Considerations?

connection, it is a sufficient means of avoiding an Oracle without additional measures. <ins> 10.6. The Shared-State Retry Service defined in shared-state-retry describes the format of retry tokens or new tokens protected and encrypted using AES128-GCM.  Each token includes a 96 bit randomly generated unique token number, and an 8 bit identifier of the AES-GCM encryption key.  There are three important security considerations for these tokens: An attacker that obtains a copy of the encryption key will be able to decrypt and forge tokens. Attackers may be able to retrieve the key if they capture a sufficently large number of retry tokens encrypted with a given key. Confidentiality of the token data will fail if separate tokens reuse the same 96 bit unique token number and the same key. To protect against disclosure of keys to attackers, service and servers MUST ensure that the keys are stored securely.  To limit the consequences of potential exposures, the time to live of any given key should be limited. Section 6.6 of QUIC-TLS states that "Endpoints MUST count the number of encrypted packets for each set of keys.  If the total number of encrypted packets with the same key exceeds the confidentiality limit for the selected AEAD, the endpoint MUST stop using those keys."  It goes on with the specific limit: "For AEAD_AES_128_GCM and AEAD_AES_256_GCM, the confidentiality limit is 2^23 encrypted packets; see Appendix B.1."  It is prudent to adopt the same limit here, and configure the service in such a way that no more than 2^23 tokens are generated with the same key. In order to protect against collisions, the 96 bit unique token numbers should be generated using a cryptographically secure pseudorandom number generator (CSPRNG), as specified in Appendix C.1 of the TLS 1.3 specificationRFC8446.  With proper random numbers, if fewer than 2^40 tokens are generated with a single key, the risk of collisions is lower than 0.001%. </ins> 11. There are no IANA requirements.


6.3.3.  When in active mode, the service MUST generate Retry tokens with the format described above when it receives a client Initial packet with no token. <del> In active mode, the service SHOULD decrypt the incoming tokens.  The service SHOULD drop packets with unknown key sequence, or an AEAD checksum that does not match the expected value.  (By construction, the AEAD checksum will only match if the client IP Address also matches.) </del>  If the token checksum passes, and the ODCIL and RSCIL fields are both <del> zero, then this is an expired NEW_TOKEN token generated by the server.  An active Retry Service MUST drop the packet and issue a Retry packet with the appropriate token. The service SHOULD drop packets if the Timestamp of the retry token points to a past date; however, in order to allow for clock skew, it SHOULD NOT drop packets if the Timestamp merely points to a a few seconds in the past. It SHOULD forward packets if the AEAD checksum and the Timestamp can be verified, regardless of the other fields. However, the service MUST NOT decrypt or validate tokens if there is a NAT between it and the servers. In inactive mode, the service SHOULD forward all packets to the server so that the server can issue an up-to-date token to the client. </del>  6.3.4.

Ended up rewriting the Retry Requirements for shared-state while adding the bit about port verification. Inspired by the review here: URL

6.3.3. <ins> In inactive mode, the Retry service forwards all packets without further inspection or processing. Retry services MUST NOT issue Retry packets except where explicitly allowed below, to avoid sending a Retry packet in response to a Retry token. </ins> When in active mode, the service MUST generate Retry tokens with the format described above when it receives a client Initial packet with no token.  <ins> The service SHOULD decrypt incoming tokens.  The service SHOULD drop packets with unknown key sequence, or an AEAD checksum that does not match the expected value.  (By construction, the AEAD checksum will only match if the client IP Address also matches.) </ins> If the token checksum passes, and the ODCIL and RSCIL fields are both  <ins> zero, then this is a NEW_TOKEN token generated by the server. Processing of NEW_TOKEN tokens is subtly different from Retry tokens, as described below. The service SHOULD drop a packet containing a token where the ODCIL is greater than zero and less than the minimum number of octets for a client-generated CID (8 in QUIC version 1).  The service also SHOULD drop a packet containing a token where the ODCIL is zero and RSCIL is nonzero. If the Timestamp of a token points to time in the past, the token has expired; however, in order to allow for clock skew, it SHOULD NOT consider tokens to be expired if the Timestamp encodes a few seconds in the past.  An active Retry service SHOULD drop packets with expired tokens.  If a NEW_TOKEN token, the service MUST generate a Retry packet in response.  It MUST NOT generate a Retry packet in response to an expired Retry token. If a Retry token, the service SHOULD drop packets where the port number encoded in the token does not match the source port in the encapsulating UDP header. All other packets SHOULD be forwarded to the server. </ins> 6.3.4.


connection basis, through a stochastic process or based on client address.  A retry service MUST forward all packets for a QUIC version it does <del> not understand.  Note that if servers support versions the retry service does not, this may increase load on the servers.  However, dropping these packets would introduce chokepoints to block deployment of new QUIC versions.  Note that future versions of QUIC might not have Retry packets, require different information in Retry, or use different packet type indicators. </del>  6.2. 6.2.1. <del> The configuration agent distributes a list of QUIC versions to be served by the Retry Service. </del>  6.2.2. 6.3.2. The configuration agent generates and distributes a "token key", a <del> "token IV", a key sequence, and a list of QUIC versions the service supports.  It must also inform the service if a NAT lies between the service and the servers. </del>  6.3.3. server also must have the token key. A non-shared-state Retry Service need only be configured with the <del> QUIC versions it supports.  A shared-state Retry Service also needs the token key, and to be aware if a NAT sits between it and the servers. </del>  The following pseudocode describes the data items necessary to store a full QUIC-LB configuration at the server.  It is meant to describe

Hopefully this will close the gap between what we want Retry Services to do and reality.
It would help the anti-DDoS properties if the Retry Service could receive explicit instructions about which QUIC versions the server might support. This provides a way to deploy new versions without having to upgrade the retry service software or hardware.

connection basis, through a stochastic process or based on client address. <ins> A configuration agent MUST distribute a list of QUIC versions the Retry Service supports.  It MAY also distribute either an "Allow- List" or a "Deny-List" of other QUIC versions.  It MUST NOT distribute both an Allow-List and a Deny-List. The Allow-List or Deny-List MUST NOT include any versions included for Retry Service Support. The Configuration Agent MUST provide a means for the entity that controls the Retry Service to report its supported version(s) to the configuration Agent.  If the entity has not reported this information, it MUST NOT activate the Retry Service and the configuration agent MUST NOT distribute configuration that activates it. The configuration agent MAY delete versions from the final supported version list if policy does not require the Retry Service to operate on those versions. The configuration Agent MUST provide a means for the entities that control servers behind the Retry Service to report either an Allow- List or a Deny-List. If all entities supply Allow-Lists, the consolidated list MUST be the union of these sets.  If all entities supply Deny-Lists, the consolidated list MUST be the intersection of these sets. If entities provide a mixture of Allow-Lists and Deny-Lists, the consolidated list MUST be a Deny-List that is the intersection of all provided Deny-Lists and the inverses of all Allow-Lists. If no entities that control servers have reported Allow-Lists or Deny-Lists, the default is a Deny-List with the null set (i.e., all unsupported versions will be admitted).  This preserves the future extensibilty of QUIC. </ins> A retry service MUST forward all packets for a QUIC version it does  <ins> not support that are not on a Deny-List or absent from an Allow-List. Note that if servers support versions the retry service does not, this may increase load on the servers. Note that future versions of QUIC might not have Retry packets, require different information in Retry, or use different packet type indicators. </ins> 6.2. 6.2.1.  <ins> See common-requirements. </ins> 6.2.2. 6.3.2. The configuration agent generates and distributes a "token key", a  <ins> "token IV", a key sequence, and the information described in common- requirements. </ins> 6.3.3. server also must have the token key. A non-shared-state Retry Service need only be configured with the  <ins> QUIC versions it supports, and an Allow- or Deny-List.  A shared- state Retry Service also needs the token key, and to be aware if a NAT sits between it and the servers. </ins> The following pseudocode describes the data items necessary to store a full QUIC-LB configuration at the server.  It is meant to describe