Subject: [quicwg/base-drafts] Stateless reset fixes (#1346)

From: Martin Thomson <[email-masked]>
Date: Wed, 09 May 2018 21:55:10 -0700

Inspired by @martinduke's #1328, I set to cleaning a few things up.  You can see separate commits here that:

* Take Martin's simplification suggestions
* Reword a few things based on review
* Make stateless reset symmetric
* Add a paragraph limiting the size of stateless reset (for amplification attack reasons)
* Add a point that @kazuho and I discussed somewhere about the scope of the stateless reset key and connection IDs (which opens a different DoS vector if they don't line up correctly).  That's partly addressed by the inclusion of an instance identifier in the calculation of the token, but the point is that you probably want instance X to be able to reset connections for instance Y if Y is out of commission, so you end up with the potential for DoS if you aren't careful to ensure that X and Y use different connection IDs.

Closes #466, #1328.
You can view, comment on, or merge this pull request online at:

  https://github.com/quicwg/base-drafts/pull/1346

-- Commit Summary --

  * Stateless Reset Cleanup
  * Update draft-ietf-quic-transport.md
  * Polishing PR#1328
  * wip
  * Make stateless reset symmetric
  * DoS considerations for stateless reset
  * Require coextant connection IDs and stateless reset keys

-- File Changes --

    M draft-ietf-quic-transport.md (150)

-- Patch Links --

https://github.com/quicwg/base-drafts/pull/1346.patch
https://github.com/quicwg/base-drafts/pull/1346.diff

-- 
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/1346

---

From: martinduke <[email-masked]>
Date: Wed, 09 May 2018 22:13:59 -0700

martinduke approved this pull request.

Is it possible to send a stateless reset in response to a long header? This would not happen very often, but if so we probably ought to send SR with a Long Header, and specify that somewhere in here.

Otherwise, this looks good to me.

> -
-This design relies on the client always sending a connection ID in its packets
-so that the server can use the connection ID from a packet to reset the
-connection.  A server that uses this design cannot allow clients to use a
+keying material, with instance and connection identifiers as salt).  The output
+of this function is truncated to 16 octets to produce the Stateless Reset Token
+for that connection.
+
+An endpoint that loses state can use the same method to generate a valid
+Stateless Reset Secret.  The connection ID comes from the packet that the
+endpoint receives.  An instance that receives a packet for another instance
+might recover the instance identifier from the connection ID.
+
+This design relies on the peer always sending a connection ID in its packets so
+that the endpoint can use the connection ID from a packet to reset the
+connection.  An endpoint that uses this design cannot allow its peers to use a

"... cannot allow its peers to send packets with a zero-length destination connection ID"

-- 
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/1346#pullrequestreview-118979836

---

From: Martin Thomson <[email-masked]>
Date: Wed, 09 May 2018 22:57:30 -0700

@martinthomson pushed 1 commit.

770fffc  Fixup for Martin's comments

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/1346/files/5266bccaeb118d006a332ce4a1d1111172c4f334..770fffcf2e392bef246b4224e8b63617bc8c80e8

---

From: Martin Thomson <[email-masked]>
Date: Wed, 09 May 2018 23:04:07 -0700

Thanks for the quick review.

Nothing specifically prevents an endpoints from sending stateless reset in response to a long header.  I've opened #1348 in case I missed something, because I have to move on to something else now :(.

-- 
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/1346#issuecomment-387960991

---

From: martinduke <[email-masked]>
Date: Thu, 10 May 2018 12:32:43 -0700

martinduke commented on this pull request.

One nit.

> @@ -2160,8 +2160,8 @@ might recover the instance identifier from the connection ID.
 
 This design relies on the peer always sending a connection ID in its packets so
 that the endpoint can use the connection ID from a packet to reset the
-connection.  An endpoint that uses this design cannot allow its peers to use a
-zero-length connection ID.
+connection.  An endpoint that uses this design cannot allow its peers to send
+packets with a zero-length connection ID.

"...zero-length destination connection ID"

-- 
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/1346#pullrequestreview-119216830

---

From: Mike Bishop <[email-masked]>
Date: Fri, 11 May 2018 14:46:37 -0700

MikeBishop commented on this pull request.

> -ID, and static key cannot occur for another connection.  A connection ID from a
-connection that is reset by revealing the Stateless Reset Token cannot be
-reused for new connections at the same server without first changing to use a
-different static key or server identifier.
+Stateless Reset Token means that the combination of instance, connection ID, and
+static key cannot occur for another connection.  A connection ID from a
+connection that is reset by revealing the Stateless Reset Token cannot be reused
+for new connections at the same instance without first changing to use a
+different static key or instance identifier.
+
+Any set of instances that share a static key for the purposes of stateless reset
+MUST allocate connection IDs from the same space without reusing connection IDs
+as long as the key is valid.  If instances that share a stateless reset key
+allow connections with the same connection ID to be created, then the stateless
+reset token for one connection could be used to terminate any connection that
+has the same connection ID.

This looks like you're trying to close #1258 and #1259, too.  However, I don't think this language is strong enough.  It's not just that it MUST allocate from the same space without reusing; it MUST NOT generate a stateless reset for a connection ID that it would not have allocated, even if it has the proper key to do so.

-- 
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/1346#pullrequestreview-119589287

---

From: Mike Bishop <[email-masked]>
Date: Fri, 11 May 2018 14:55:08 -0700

Revealing the Stateless Reset Token allows any entity to terminate the
 connection, so a value can only be used once.  This method for choosing the
-Stateless Reset Token means that the combination of server instance, connection
-ID, and static key cannot occur for another connection.  A connection ID from a
-connection that is reset by revealing the Stateless Reset Token cannot be
-reused for new connections at the same server without first changing to use a
-different static key or server identifier.
+Stateless Reset Token means that the combination of instance, connection ID, and
+static key cannot occur for another connection.  A connection ID from a

By the same logic, I think the combination of connection ID and static key cannot be allowed to recur, even if it's on a different instance.

-- 
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/1346#pullrequestreview-119590981

---

From: Mike Bishop <[email-masked]>
Date: Fri, 11 May 2018 14:56:51 -0700

Actually, I think the "instance identifier" discussed above would mitigate this.  It will generate an SR, but it won't be valid from a different instance.

-- 
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/1346#discussion_r187742473

---

From: Martin Thomson <[email-masked]>
Date: Tue, 15 May 2018 15:56:02 -0700

Notes from editor's meeting: the last paragraph can be removed, then we will add another PR to address the routing issue underlying all this..

-- 
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/1346#issuecomment-389339607

---

From: Martin Thomson <[email-masked]>
Date: Wed, 16 May 2018 05:35:43 -0700

75ff6ab  Update the header octet

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/1346/files/770fffcf2e392bef246b4224e8b63617bc8c80e8..75ff6ab9eb1f371b5d2b4bf1eba2fd47d7e58ce1

---

From: Martin Thomson <[email-masked]>
Date: Thu, 17 May 2018 22:37:42 -0700

@martinthomson pushed 2 commits.

3980979  destination connection id
4175c40  Remove ill-advised fixes for #1256/#1259

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/1346/files/75ff6ab9eb1f371b5d2b4bf1eba2fd47d7e58ce1..4175c4012d24f704ab681bbd0e847a83840a9e87

---

From: janaiyengar <[email-masked]>
Date: Tue, 22 May 2018 17:57:51 -0700

janaiyengar commented on this pull request.

Looks great! Just a few comments.

> @@ -2070,91 +2069,107 @@ following layout:
 This design ensures that a stateless reset packet is - to the extent possible -
 indistinguishable from a regular packet with a short header.
 
-A server generates a random 18-octet Destination Connection ID field.  For a
-client that depends on the server including a connection ID, this will mean that
-this value differs from previous packets.  Ths results in two problems:
+The message consists of a header octet, followed by random octets of arbitrary
+length, followed by a Stateless Reset Token.
+
+The endpoint SHOULD send a packet with a short header.

This seems like a strange SHOULD. The Stateless Reset packet is defined here with a short header. How can this be a long header?

> @@ -2070,91 +2069,107 @@ following layout:
 This design ensures that a stateless reset packet is - to the extent possible -
 indistinguishable from a regular packet with a short header.
 
-A server generates a random 18-octet Destination Connection ID field.  For a
-client that depends on the server including a connection ID, this will mean that
-this value differs from previous packets.  Ths results in two problems:
+The message consists of a header octet, followed by random octets of arbitrary
+length, followed by a Stateless Reset Token.
+
+The endpoint SHOULD send a packet with a short header.
+
+Assuming a short header, the Random Octets field needs to include at least 20

If the above comment holds, remove "Assuming a short header"

-* The packet might not reach the client.  If the Destination Connection ID is
-  critical for routing toward the client, then this packet could be incorrectly
+An endpoint SHOULD NOT send a stateless reset that is significantly larger than
+the packet it receives.  Endpoints MUST discard packets that are too small to be
+valid QUIC packets.  With the set of AEAD functions defined in {{QUIC-TLS}},
+packets less than 19 octets long are never valid.
+
+An endpoint cannot determine the Source Connection ID from a packet with a short
+header, therefore it cannot set the Destination Connection ID in the stateless
+reset packet.  The destination connection ID will therefore differ from the
+value used in previous packets.  A random Destination Connection ID makes the
+connection ID appear to be the result of moving to new connection ID that was

"moving to new connection ID" -> "moving to a new connection ID"

-* The packet might not reach the client.  If the Destination Connection ID is
-  critical for routing toward the client, then this packet could be incorrectly
+An endpoint SHOULD NOT send a stateless reset that is significantly larger than
+the packet it receives.  Endpoints MUST discard packets that are too small to be
+valid QUIC packets.  With the set of AEAD functions defined in {{QUIC-TLS}},
+packets less than 19 octets long are never valid.
+
+An endpoint cannot determine the Source Connection ID from a packet with a short
+header, therefore it cannot set the Destination Connection ID in the stateless
+reset packet.  The destination connection ID will therefore differ from the
+value used in previous packets.  A random Destination Connection ID makes the
+connection ID appear to be the result of moving to new connection ID that was
+provided using the NEW_CONNECTION_ID frame ({{frame-new-connection-id}}).

s/the/a/

>  (or might have supported prior to losing state).  Designers of new versions of
 QUIC need to be aware of this and either reuse this design, or use a portion of
 the packet other than the last 16 octets for carrying data.

 #### Detecting a Stateless Reset
 
-A client detects a potential stateless reset when a packet with a short header
-either cannot be decrypted or is marked as a duplicate packet.  The client then
-compares the last 16 octets of the packet with the Stateless Reset Token
-provided by the server in its transport parameters.  If these values are
-identical, the client MUST enter the draining period and not send any further
-packets on this connection.  If the comparison fails, the packet can be
-discarded.
+An endpoint detects a potential stateless reset when a packet with a short
+header either cannot be decrypted or is marked as a duplicate packet.  The
+endpoint then compares the last 16 octets of the packet with the Stateless Reset
+Token provided by its peer, either from the NEW_CONNECTION_ID frame or the

s/from/in/
s/the NEW_CONNECTION_ID frame/a NEW_CONNECTION_ID frame/
s/server/server's/

 A single static key can be used across all connections to the same endpoint by
 generating the proof using a second iteration of a preimage-resistant function
-that takes three inputs: the static key, the server's connection ID (see
-{{connection-id}}), and an identifier for the server instance.  A server could
-use HMAC {{?RFC2104}} (for example, HMAC(static_key, server_id ||
+that takes three inputs: the static key, the connection ID chosen by the
+endpoint (see {{connection-id}}), and an instance identifier.  An endpoint could
+use HMAC {{?RFC2104}} (for example, HMAC(static_key, instance_id ||

IIUC, incorporating an instance_id doesn't work for connections that are diverted to a new instance, which is a common case, possibly even the most common case. Server restarts are unlikely to be a common use case, since usually servers are drained before restarting, and server crashes are not frequent. Short-term routing flaps however are common.  I think the text should say that for Stateless Reset to work across instances, the HMAC could exclude the instance_id. (Correct me if my understanding is wrong.)

-- 
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/1346#pullrequestreview-122400433

---

From: Martin Thomson <[email-masked]>
Date: Wed, 23 May 2018 00:03:51 -0700

martinthomson commented on this pull request.

I've addressed that below.  But I've expanded on it a little in my latest changes.  The trick here is that you can recover the instance ID using the connection ID.  I will follow up with a change that includes the fixes we discussed.  Part of that change involves removing the instance ID from this.

-- 
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/1346#discussion_r190141169

---

From: Martin Thomson <[email-masked]>
Date: Wed, 23 May 2018 00:04:15 -0700

be417ab  Review comments

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/1346/files/4175c4012d24f704ab681bbd0e847a83840a9e87..be417abf98f01a22543856f084ce4235c7fd159a

---

From: Mike Bishop <[email-masked]>
Date: Wed, 23 May 2018 09:39:54 -0700

And of course, the issue is that if you enable servers to SR after a routing flap, you also enable those servers to be used as oracles.  The only defense is to make it impossible for the routing to be affected by the attacker.

-- 
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/1346#discussion_r190319312

---

From: janaiyengar <[email-masked]>
Date: Wed, 23 May 2018 12:40:27 -0700

@martinthomson : Ah, right, I see it's addressed below. Given this is an example construction, I would suggest making that explicitly clear up at the beginning of this paragraph.
@MikeBishop : Yes, that is true, but defending the routing infrastructure against attacks seems out of scope for us, since similar attacks can be launched against TCP as well, where you'd receive an RST or ICMP unreachable.

-- 
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/1346#discussion_r190374682

---

From: janaiyengar <[email-masked]>
Date: Wed, 23 May 2018 12:41:46 -0700

janaiyengar approved this pull request.

LGTM, modulo my earlier (take-it-or-leave-it) comment.

-- 
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/1346#pullrequestreview-122737658

---

From: Mike Bishop <[email-masked]>
Date: Wed, 23 May 2018 15:34:25 -0700

Defending it is out of scope.  Noting that you create an attack vector if you fail to defend it yourself, however, bears mentioning.

-- 
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/1346#discussion_r190419720

---

From: Martin Thomson <[email-masked]>
Date: Wed, 23 May 2018 21:52:14 -0700

Merged #1346.

-- 
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/1346#event-1643335356

---

From: Martin Thomson <[email-masked]>
Date: Wed, 23 May 2018 21:52:28 -0700

See #1386 for the follow-up.

-- 
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/1346#issuecomment-391587474

---

From: Pascalh2001 <[email-masked]>
Date: Mon, 28 May 2018 02:15:30 -0700

Hi, 
 generating the proof using a second iteration of a preimage-resistant function
that takes three inputs: the static key, the connection ID chosen by the
endpoint (see {{connection-id}}), and an instance identifier.

But on the other side (section about Connection IDs) : 
> Short headers only include the Destination Connection ID and omit the explicit
length.  The length of the Destination Connection ID field is expected to be
known to endpoints.

So if the server loose state, how do you retrieve the Destination Connection Id from a short header ?
Is it mandatory for an endpoint to use the same length for all CIDs it uses and to be able to remind this length after a hard reboot ? 

('ve been reading your work and discussions since recently, great project!) 

-- 
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/1346#issuecomment-392470123

---

From: MikkelFJ <[email-masked]>
Date: Mon, 28 May 2018 03:00:43 -0700

@Pascalh2001 you could imagine one server cluster settling on 16-bit identifiers throughout, and another cluster that uses a 4-bit type prefix from which the length can be derived, and a third cluster that register connection ID's in an in-memory database.

You save bandwidth by leaving it up to the endpoint rather than forcing the length to be transported at all times.

-- 
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/1346#issuecomment-392481452

---

From: Mike Bishop <[email-masked]>
Date: Tue, 29 May 2018 20:27:01 +0000 (UTC)

A cluster that wants the length present simply includes the length of the CID as part of the CID it tells the client to send it.  The encoding inside is totally up to the server, so that might be the explicit length, or a four-bit encoding of it, or even a single bit that toggles between the two lengths the cluster typically uses.

-- 
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/1346#issuecomment-392931936