Original text:
the server, but authenticated later in the token.  The Retry Service SHOULD reject any token in which the value is less than 8. <del> RSCIL: The retry source connection ID length. </del> Original Destination Connection ID: This also in cleartext and authenticated later. <del> Retry Source Connection ID: This also in cleartext and authenticated later. Opaque Data: This data MUST contain encrypted information that allows the retry service to validate the client's IP address, in accordance with the QUIC specification.  It MUST also provide a cryptographically secure means to validate the integrity of the entire token. </del>  Upon receipt of an Initial packet with a token that begins with '0', the retry service MUST validate the token in accordance with the QUIC processed in accordance with the QUIC specification.  If a server receives an Initial Packet with the first bit to '0', it is a Retry token and the server MUST NOT attempt to validate it.  Instead, it <del> MUST assume the address is validated and MUST extract the Original Destination Connection ID and Retry Source Connection ID, assuming the format described in nss-service-requirements. </del>  7.3. 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. <del> 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. </del>  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. </del>  If there is a Network Address Translator (NAT) in the server infrastructure that changes the client IP, the Retry Service MUST The token body has the following fields:  ODCIL: The original destination connection ID length.  Tokens in <del> NEW_TOKEN frames MUST set this field to zero. </del>  <del> RSCIL: The retry source connection ID length.  Tokens in NEW_TOKEN frames MUST set this field to zero. </del>  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. <del> Original Destination Connection ID: The server or Retry Service copies this from the field in the client Initial packet. Retry Source Connection ID: The server or Retry service copies this from the Source Connection ID of the Retry packet. 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). </del> Opaque Data: The server may use this field to encode additional information, such as congestion window, RTT, or MTU.  The Retry Service MUST have zero-length opaque data. The tokens are protected using AES128-GCM as follows: <del> The Key Sequence is the 8 bit identifier to retrieve the token key </del>  and IV. The AEAD IV, is a 96 bit data which produced by implementer's The associated data is a formatted as a pseudo header by combining the cleartext part of the token with the IP address of the client.  The input plaintext for the AEAD is the token body.  The output ciphertext of the AEAD is transmitted in place of the token body. 7.3.3. In inactive mode, the Retry service forwards all packets without <del> further inspection or processing. </del>  Retry services MUST NOT issue Retry packets except where explicitly allowed below, to avoid sending a Retry packet in response to a Retry token. <del> 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>  <del> The service SHOULD decrypt incoming tokens.  The service SHOULD drop packets with unknown key sequence, or an AEAD ICV that does not match the expected value.  (By construction, the AEAD ICV will only match if the client IP Address also matches.) </del>  <del> If the token verification passes, and the ODCIL and RSCIL fields are both 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. </del>  <del> 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. </del>  <del> 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. </del>  <del> 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. </del>  <del> All other packets SHOULD be forwarded to the server. </del>  7.3.4. <del> 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.  The ODCIL and RSCIL fields are zero for NEW_TOKEN tokens, making them easily distinguishable from Retry tokens. </del>  The server MUST validate all tokens that arrive in Initial packets, <del> as they may have bypassed the Retry service. </del>  <del> For Retry tokens that follow the format above, servers SHOULD use the 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 past, due to possible clock synchronization issues. </del>  <del> After decrypting the token, the server uses the corresponding fields to populate the original_destination_connection_id transport parameter, with a length equal to ODCIL, and the retry_source_connection_id transport parameter, with length equal to RSCIL. </del>  For QUIC versions the service does not support, the server MAY use any token format. <del> As discussed in RFC9000, a server MUST NOT send a Retry packet in response to an Initial packet that contains a retry token. </del> 8. QUIC-LB requires common configuration to synchronize understanding of

Reviewer comments:
Takes the RSCID out of the token body because the server can catch mismatches with the DCID if it's used as associated data.
I have read URL but still don't understand why we need to include it again in token. I'd thought that that server or retry service can read it from the Initial packet (invariant long header packet). What happen if they are different? Which one server should take to initiate the connection?
I recommend Section 7.3 of RFC 9000 to understand this: "The values provided by a peer for these transport parameters MUST match the values that an endpoint used in the Destination and Source Connection ID fields of Initial packets that it sent (and received, for servers). Endpoints MUST validate that received transport parameters match received connection ID values." The connection IDs in Retry and Initial packets are not effectively authenticated unless they are included in transport parameters, which are included in key computation. If the server doesn't have a trustworthy record of the CID it picked (this token), it has no way of confirming that the Retry's SCID wasn't modified in flight.
yeah, I get that server needs to verify SCID in Retry packet. I still do not get why we need to include Retry SCID again in the token.
How would it verify the SCID was unchanged if it wasn't in the token?
I think it'd be authenticated by including in the associated data, i.e
Hmm, this is a clever proposal. I'll reopen to reconsider properly.
Trying to wrap my head around this. The server wants to verify that the RSCID is correct, so the current solution is to copy that in the token. If the client messes up, the server compares the value in the token to the value sent by the client. Yes, putting that in the pseudo header will mostly work. If the client does not use the specified RSCID, the token will just not decode. But there are two potential issues: 1) Debugging is harder. For example, there will not be any way to differentiate between wrong RSCID and wrong IP 2) If the token came as "NEW TOKEN", there is no RSCID specified. There will need to be a special case for constructing the pseudo header, but that means exposing the difference between Retry Token and New Token in clear text. In any case, we would need additional text for the New Token case.
Christian, good point on (2). The NSS Token already distinguishes the types in cleartext, so perhaps that's not such a big deal. But yes, we'd need to take a bit from the key sequence to indicate the token type.
I filed , please take a look.

Revised text:
the server, but authenticated later in the token.  The Retry Service SHOULD reject any token in which the value is less than 8.  Original Destination Connection ID: This also in cleartext and authenticated later.  <ins> Opaque Data: This data contains the information necessary to authenticate the Retry token in accordance with the QUIC specification.  A straightforward implementation would encode the Retry Source Connection ID, client IP address, and a timestamp in the Opaque Data.  A more space-efficient implementation would use the Retry Source Connection ID and Client IP as associated data in an encryption operation, and encode only the timestamp and the authentication tag in the Opaque Data.  If the Initial Packet has altered the Connection ID or source IP address, authentication of the token will fail. </ins> Upon receipt of an Initial packet with a token that begins with '0', the retry service MUST validate the token in accordance with the QUIC processed in accordance with the QUIC specification.  If a server receives an Initial Packet with the first bit to '0', it is a Retry token and the server MUST NOT attempt to validate it.  Instead, it  <ins> MUST assume the address is validated, MUST include the packet's Destination Connection ID in a Retry Source Connection ID transport parameter, and MUST extract the Original Destination Connection ID from the token cleartext for use in the transport parameter of the same name. </ins> 7.3. server or retry service, MUST use the following format, which includes: <ins> A 1 bit token type identifier. A 7 bit token key identifier. </ins> A 96 bit unique token number transmitted in clear text, but protected as part of the AEAD associated data.  <ins> A token body, encoding the Original Destination Connection ID and the Timestamp, optionally followed by server specific Opaque Data. </ins> 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.  It also uses the Source Connection ID of the Retry packet, which will cause an authentication failure if it differs from the Destination Connection ID of the packet bearing the token. </ins> If there is a Network Address Translator (NAT) in the server infrastructure that changes the client IP, the Retry Service MUST The token body has the following fields: <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> ODCIL: The original destination connection ID length.  Tokens in  <ins> NEW_TOKEN frames do not have this field. </ins>  <ins> Original Destination Connection ID: The server or Retry Service copies this from the field in the client Initial packet.  Tokens in NEW_TOKEN frames do not have this field. </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.  Opaque Data: The server may use this field to encode additional information, such as congestion window, RTT, or MTU.  The Retry Service MUST have zero-length opaque data. The tokens are protected using AES128-GCM as follows:  <ins> The Key Sequence is the 7 bit identifier to retrieve the token key </ins> and IV. The AEAD IV, is a 96 bit data which produced by implementer's The associated data is a formatted as a pseudo header by combining the cleartext part of the token with the IP address of the client. <ins> The format of the pseudoheader depends on whether the Token Type bit is '1' (a NEW_TOKEN token) or '0' (a Retry token). RSCIL: The Retry Source Connection ID Length in octets.  This field is only present when the Token Type is '0'. Retry Source Connection ID: To create a Retry Token, populate this field with the Source Connection ID the Retry packet will use.  To validate a Retry token, populate it with the Destination Connection ID of the Initial packet that carries the token.  This field is only present when the Token Type is '0'. </ins> The input plaintext for the AEAD is the token body.  The output ciphertext of the AEAD is transmitted in place of the token body. 7.3.3. In inactive mode, the Retry service forwards all packets without  <ins> further inspection or processing.  The rest of this section only applies to a service in active mode. </ins> 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> The service MUST generate Retry tokens with the format described above when it receives a client Initial packet with no token. If there is a token of either type, the service MUST attempt to decrypt it. To decrypt a packet, the service checks the Token Type and constructs a pseudoheader with the appropriate format for that type, using the bearing packet's Destination Connection ID to populate the Retry Source Connection ID field, if any. </ins>  <ins> A token is invalid if: </ins>  <ins> it uses unknown key sequence, </ins>  <ins> the AEAD ICV does not match the expected value (By construction, it will only match if the client IP Address, and any Retry Source Connection ID, also matches), </ins>  <ins> the ODCIL, if present, is invalid for a client-generated CID (less than 8 or more than 20 in QUIC version 1), </ins>  <ins> the Timestamp of a token points to time in the past (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), or </ins>  <ins> the port number, if present, does not match the source port in the encapsulating UDP header. Packets with valid tokens MUST be forwarded to the server. The service MUST drop packets with invalid tokens.  If the token is of type '1' (NEW_TOKEN), it MUST respond with a Retry packet.  If of type '0', it MUST NOT respond with a Retry packet. </ins> 7.3.4.  <ins> The server MAY issue Retry or NEW_TOKEN tokens in accordance with RFC9000.  When doing so, it MUST follow the format above. </ins> The server MUST validate all tokens that arrive in Initial packets,  <ins> as they may have bypassed the Retry service.  It determines validity using the procedure in ss-service. </ins>  <ins> If a valid Retry token, the server populates the original_destination_connection_id transport parameter using the corresponding token field.  It populates the retry_source_connection_id transport parameter with the Destination Connection ID of the packet bearing the token. </ins>  <ins> In all other respects, the server processes both valid and invalid tokens in accordance with RFC9000. </ins> For QUIC versions the service does not support, the server MAY use any token format.  8. QUIC-LB requires common configuration to synchronize understanding of