Subject: Re: [quicwg/base-drafts] token-based greasing / initial packet protection (#3166)

From: Martin Thomson <[email-masked]>
Date: Mon, 04 Nov 2019 22:50:38 -0800

martinthomson commented on this pull request.

I think that this is generally the right design, though I think that the framing here could be improved.

"Seeding" doesn't seem to be the right concept here.  The point here is that this creates **version aliases** for QUIC version 1.  As such, I think that this belongs as a subsection of [Section 15](https://quicwg.org/base-drafts/draft-ietf-quic-transport.html#versions).  It almost makes sense to move this much earlier to [Section 6](https://quicwg.org/base-drafts/draft-ietf-quic-transport.html#version-negotiation), but that is before the concept of address validation is introduced.  I would however suggest that this section have a forward-pointer to this new text.

This PR is heavily biased toward server behaviour.  For instance, there is no subsection for client procedures.  I think that this would read more clearly if client behaviour were specified, as that is relatively straightforward: when you use a token that came from NEW_TOKEN, these values replace the equivalent values in the protocol.

More inline, including a rewrite :/

> @@ -2721,6 +2724,96 @@ between endpoints.  Application protocols SHOULD define rules for handling
 streams that are prematurely cancelled by either endpoint.

+# Seeding {#seeding}
+
+In order to avoid ossification of the cleartext and obfuscated fields of QUIC
+packets, a server can announce a set of alternative initial values to be used,
+which is comprised of:

I think that these definitions are better left to NEW_TOKEN.  That reduces the scope of this text considerably.

> +which is comprised of:
+
+* Version number; a 32-bit unsigned number that is to be presented on wire in
+  place of the version number specified in this document.  This value MUST NOT
+  be a reserved version ({{versions}}).
+
+* Packet type modifier; a two-bit value that obfuscates the Long Packet Type of
+  a long header packet ({{long-header}}).  The long packet type bits of a long
+  header packet is encoded as an bit-wise exclusive or (XOR) of the packet type
+  modifier and the type numbers defined in {{long-packet-types}}.
+
+* Initial salt; a 16-byte binary blob that is to be used in place of the initial
+  salt defined in section 5.2 of {{QUIC-TLS}}.
+
+A server advertises these seeds using a NEW_TOKEN frame {{frame-new-token}}.
+The token MUST permit the server to recover these seeds.  This property can be

Tokens don't permit anything.  I think that what you want to say here is that the server needs to properly recognize this new version and associated parameters as equivalent to QUIC version 1 for the full duration described by the Lifetime field.

> +* Initial salt; a 16-byte binary blob that is to be used in place of the initial
+  salt defined in section 5.2 of {{QUIC-TLS}}.
+
+A server advertises these seeds using a NEW_TOKEN frame {{frame-new-token}}.
+The token MUST permit the server to recover these seeds.  This property can be
+achieved for example by embedding these seeds in the encrypted token.
+
+For the non-default seeds to work, all the servers within the scope of a
+NEW_TOKEN token are required to have a shared knowledge of those seeds being
+issued and / or how they can be recovered from the tokens. Certain server
+deployments might have difficulty in meeting such a requirement.
+
+A server (or a set of servers) that cannot satisfy this requirement can stick to
+using the default values by consistently advertising the default version number,
+default initial salt, and a packet type modifier of zero in the NEW_TOKEN frames
+it sends.

I think that the mailing list discussion has provided sufficient input on this text that it is probably time to revise it.

> +
+A server (or a set of servers) that cannot satisfy this requirement can stick to
+using the default values by consistently advertising the default version number,
+default initial salt, and a packet type modifier of zero in the NEW_TOKEN frames
+it sends.
+
+The rest of this section applies to the servers that advertise non-default
+values as their seeds.
+
+
+## Server Behavior
+
+Typically, a server that advertises the alternative seeds would act in the
+following steps:
+
+* The server pre-allocates a set of unused version numbers as the alternative

This implies that you can't provide a different salt for version 1.  But I am fairly certain that you can with this design.  The same applies to ?a?a?a?a.

If you only support versions that include tokens, then those tokens can encode the real version and you can fill the real version number with noise (that you should still authenticate, of course).

If there are different real versions that carry tokens in different places, you might have some trouble finding tokens.  But I believe that this is achievable if you reorder or restructure token contents.

If a real version you support doesn't carry a token in the Initial-equivalent packet, then you just need to avoid clobbering that version (or versions, if it uses a different aliasing scheme).

> +issued and / or how they can be recovered from the tokens. Certain server
+deployments might have difficulty in meeting such a requirement.
+
+A server (or a set of servers) that cannot satisfy this requirement can stick to
+using the default values by consistently advertising the default version number,
+default initial salt, and a packet type modifier of zero in the NEW_TOKEN frames
+it sends.
+
+The rest of this section applies to the servers that advertise non-default
+values as their seeds.
+
+
+## Server Behavior
+
+Typically, a server that advertises the alternative seeds would act in the
+following steps:

I think that this advice is too constraining.  I would instead say, probably as the sum of total of this section:

> To protect against the possibility of third parties fixating on specific aspects of the encoding of unprotected Initial packets, an optional version aliasing function is provided that is bound to address validation tokens provided using NEW_TOKEN.
>
> A server that supports version aliasing includes an alias version, a packet type mask, and replacement Initial salt with every NEW_TOKEN message.  This provides obfuscation of Initial packets for subsequent connections to the same server. Use of this capability is optional for servers.  A server MAY use default values to avoid creating aliased versions.
>
> A client MUST use aliased versions if tokens from NEW_TOKEN are used.  Details of how version alias fields are used are included in {{frame-new-token}}.
>
> A server commits to supporting an aliased version for the entire time that the token is valid (as indicated by the Lifetime field). Therefore, servers need to ensure that the process used to select version alias fields is the same for all servers that might accept the token, both across different server instances in the same deployment and across potential crashes or reboots that might cause servers to lose state.
>
> The purpose of version aliases are to confound attempts by third parties to fixate on a specific form of Initial packets. Therefore, the information that a server uses to interpret aliased versions does not need to be based on secret information and it does not have to change frequently.  Consequently, building values into software is a viable means of ensuring that values are not lost.
>
> As an example of an implementation that is both robust against loss of state and still difficult for third parties to fixate on, a server could generate an aliased version and associated values using a pseudorandom function that uses a static key.  Including a key identifier in the token should allow the server to identify the key that is in use, which allows for periodic key rotation.  As long as old keys are retained until all tokens issued using that key have expired, the server is able to correctly recognize any valid aliased version.
>
> A server that provides aliased versions needs to remove any version aliasing prior to determining whether to send a Version Negotiation packet when processing incoming packets as described in {{server-pkt-handling}}.

I think that you might also want to update the text in [Section 5.2.2](https://quicwg.org/base-drafts/draft-ietf-quic-transport.html#server-pkt-handling) to refer to this new section.

> @@ -2721,6 +2724,96 @@ between endpoints.  Application protocols SHOULD define rules for handling
 streams that are prematurely cancelled by either endpoint.

+# Seeding {#seeding}
+
+In order to avoid ossification of the cleartext and obfuscated fields of QUIC
+packets, a server can announce a set of alternative initial values to be used,
+which is comprised of:
+
+* Version number; a 32-bit unsigned number that is to be presented on wire in
+  place of the version number specified in this document.  This value MUST NOT
+  be a reserved version ({{versions}}).

I don't think that it is necessary to prohibit the use of any allocated version, including the reserved versions.

> +  server, thereby conforming to the requirements in {{validate-future}}.  After
+  that, the server sends a NEW_TOKEN frame that contains the generated token and
+  the seeds that have been embedded into that token.
+
+* When the client reconnects to the server by using the provided token and the
+  seeds, the server first checks if the version number field of the incoming
+  packet contains one of the alternative version numbers it advertises, then if
+  that is the case, applies the corresponding packet type modifier to recover
+  the correct packet type.  If the recovered packet type is an Initial packet
+  and that packet contains a NEW_TOKEN token, the server decrypts the embedded
+  token and recovers the initial salt, uses that to decrypt the payload of the
+  Initial packet.
+
+* When sending a Retry in response to an Initial packet carrying an alternative
+  version number, the server embeds the NEW_TOKEN token found in the Initial
+  packet within the retry token it issues.  Once the server receives a response

This is an important design point that is hidden: the aliased version applies to all Initial packets that are sent.  That means that the alternative salt is used when the server provides an updated connection ID.  Do the Handshake and 0-RTT packets also use the aliased version?  That needs to be right up front.

> +numbers it issues.  Such design is not recommended, as an active attacker might
+build a list of known alternative version numbers and their initial salts and
+use that list to decrypt the payload of Initial packets using those alternative
+version numbers.  But still, having a set of version numbers and initial salts
+used concurrently is considered better than just using the default values of
+QUIC in terms of preventing ossification.
+
+A server MUST NOT send a Version Negotitation packet in response to a long
+header packet with an alternative version number it has advertised.
+
+
+## Distributing the Seeds
+
+This specification defines how the seeds are used as well as how they are
+advertised using a NEW_TOKEN frame.  Other specifications MAY define other
+methods for distributing or deducing these seeds.

I don't think that we need to say this here.  I think that this subsection can be removed.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/pull/3166#pullrequestreview-311511865

---

From: Kazuho Oku <[email-masked]>
Date: Mon, 04 Nov 2019 22:58:47 -0800

kazuho commented on this pull request.

Am I correct in assuming that what you are looking for is a clarification that servers can announce a set of alternative versions, each of them tied to a fixed initial salt and packet type modifier?

I think that this is the correct text explaining the multi-CDN use-case, where servers cannot agree on anything.

To paraphrase, we'd be explaining:
1. a server that generates a new initial salt for every connection and embeds it into NEW_TOKEN
2. a server that has a fixed mapping between alternative versions vs. salt (and packet type modifier).
3. a server that does not do greasing at all (multi-CDN).

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/pull/3166#discussion_r342404704

---

From: Mike Bishop <[email-masked]>
Date: Tue, 05 Nov 2019 07:28:23 -0800

MikeBishop commented on this pull request.

+

That's true as well, but the processing flow is:

- Server recognizes version alias, which permits it to find the token field
- Server is able to obtain the Initial salt using the combination of version and token (whether it needs both is implementation-dependent)
- Server uses salt to decrypt Initial packet contents

I suggested the "permit ... to recover" language to remove the implication that the token *contains* the salt, but a server could certainly send the same version alias to different clients with different salts if the token enables the mapping.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/pull/3166#discussion_r342604097

---

From: Mike Bishop <[email-masked]>
Date: Tue, 05 Nov 2019 07:32:40 -0800

Your "MUST use aliased versions" text seems to override the "MAY use default values" text in the preceding line.  I don't think you mean it to, but it could be read that way.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/pull/3166#discussion_r342606836