Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make reset simpler and invisible #574

Merged
merged 10 commits into from
Aug 1, 2017
241 changes: 164 additions & 77 deletions draft-ietf-quic-transport.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@ Version Negotiation packet, are not encrypted, but information sent in these
unencrypted handshake packets is later verified as part of cryptographic
processing.

PUBLIC_RESET packets that reset a connection are currently not authenticated.

## Connection Migration and Resilience to NAT Rebinding

Expand Down Expand Up @@ -345,9 +344,9 @@ describing the value of fields.

Any QUIC packet has either a long or a short header, as indicated by the Header
Form bit. Long headers are expected to be used early in the connection before
version negotiation and establishment of 1-RTT keys, and for public resets.
Short headers are minimal version-specific headers, which can be used after
version negotiation and 1-RTT keys are established.
version negotiation and establishment of 1-RTT keys. Short headers are minimal
version-specific headers, which can be used after version negotiation and 1-RTT
keys are established.

## Long Header

Expand All @@ -374,9 +373,9 @@ Long headers are used for packets that are sent prior to the completion of
version negotiation and establishment of 1-RTT keys. Once both conditions are
met, a sender SHOULD switch to sending short-form headers. While inefficient,
long headers MAY be used for packets encrypted with 1-RTT keys. The long form
allows for special packets, such as the Version Negotiation and the Public Reset
packets to be represented in this uniform fixed-length packet format. A long
header contains the following fields:
allows for special packets - such as the Version Negotiation packet - to be
represented in this uniform fixed-length packet format. A long header contains
the following fields:

Header Form:

Expand Down Expand Up @@ -422,7 +421,6 @@ The following packet types are defined:
| 0x06 | 0-RTT Protected | {{packet-protected}} |
| 0x07 | 1-RTT Protected (key phase 0) | {{packet-protected}} |
| 0x08 | 1-RTT Protected (key phase 1) | {{packet-protected}} |
| 0x09 | Public Reset | {{packet-public-reset}} |
{: #long-packet-types title="Long Header Packet Types"}

The header form, packet type, connection ID, packet number and version fields of
Expand Down Expand Up @@ -682,42 +680,6 @@ packet protection in detail. After decryption, the plaintext consists of a
sequence of frames, as described in {{frames}}.


## Public Reset Packet {#packet-public-reset}

A Public Reset packet is only sent by servers and is used to abruptly terminate
communications. Public Reset is provided as an option of last resort for a
server that does not have access to the state of a connection. This is intended
for use by a server that has lost state (for example, through a crash or
outage). A server that wishes to communicate a fatal connection error MUST use a
CONNECTION_CLOSE frame if it has sufficient state to do so.

A Public Reset packet uses long headers with a type value of 0x09.

The connection ID and packet number of fields together contain octets 1 through
12 from the packet that triggered the reset. For a client that sends a
connection ID on every packet, the Connection ID field is simply an echo of the
client's Connection ID, and the Packet Number field includes an echo of the
client's packet number. Depending on the client's packet number length it might
also include 0, 2, or 3 additional octets from the protected payload of the
client packet.

The version field contains the current QUIC version.

A Public Reset packet sent by a server indicates that it does not have the
state necessary to continue with a connection. In this case, the server will
include the fields that prove that it originally participated in the connection
(see {{public-reset-proof}} for details).

Upon receipt of a Public Reset packet that contains a valid proof, a client MUST
tear down state associated with the connection. The client MUST then cease
sending packets on the connection and SHOULD discard any subsequent packets that
arrive. A Public Reset that does not contain a valid proof MUST be ignored.

### Public Reset Proof

TODO: Details to be added.


## Connection ID {#connection-id}

QUIC connections are identified by their 64-bit Connection ID. All long headers
Expand Down Expand Up @@ -783,9 +745,9 @@ sending a packet with a number of 0x6b4264 requires a 16-bit or larger packet
number encoding; whereas a 32-bit packet number is needed to send a packet with
a number of 0x6bc107.

Version Negotiation ({{packet-version}}), Server Stateless Retry
({{packet-server-stateless}}), and Public Reset ({{packet-public-reset}})
packets have special rules for populating the packet number field.
Version Negotiation ({{packet-version}}) and Server Stateless Retry
({{packet-server-stateless}}) packets have special rules for populating the
packet number field.


### Initial Packet Number {#initial-packet-number}
Expand Down Expand Up @@ -1048,6 +1010,7 @@ language from Section 3 of {{!I-D.ietf-tls-tls13}}.
idle_timeout(3),
omit_connection_id(4),
max_packet_size(5),
stateless_reset_token(6),
(65535)
} TransportParameterId;

Expand Down Expand Up @@ -1122,6 +1085,11 @@ idle_timeout (0x0003):
: The idle timeout is a value in seconds that is encoded as an unsigned 16-bit
integer. The maximum value is 600 seconds (10 minutes).

stateless_reset_token (0x0005):

: The Stateless Reset Token is used in verifying a stateless reset, see
{{stateless-reset}}. This parameter is a sequence of 16 octets.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TLS term is "vector". I would supply the PDU for this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure that I follow the PDU comment.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean do:

opaque stateless_reset_token[16];


An endpoint MAY use the following transport parameters:

omit_connection_id (0x0004):
Expand Down Expand Up @@ -1378,21 +1346,12 @@ way to retry them.

### Privacy Implications of Connection Migration {#migration-linkability}

Using a stable connection ID on multiple network paths allows a
passive observer to correlate activity between those paths. A client
that moves between networks might not wish to have their activity
correlated by any entity other than a server. The NEW_CONNECTION_ID
message can be sent by a server to provide an unlinkable connection ID
for use in case the client wishes to explicitly break linkability
between two points of network attachment.

A client which wishes to break linkability upon changing networks MUST
use the NEW_CONNECTION_ID as well as incrementing the packet sequence
number by an externally unpredictable value computed as described in
{{packet-number-gap}}. Packet number gaps are cumulative. A client
might skip connection IDs, but it MUST ensure that it applies the
associated packet number gaps in addition to the packet number gap
associated with the connection ID that it does use.
Using a stable connection ID on multiple network paths allows a passive observer
to correlate activity between those paths. A client that moves between networks
might not wish to have their activity correlated by any entity other than a
server. The NEW_CONNECTION_ID message can be sent by a server to provide an
unlinkable connection ID for use in case the client wishes to explicitly break
linkability between two points of network attachment.

A client might need to send packets on multiple networks without receiving any
response from the server. To ensure that the client is not linkable across each
Expand All @@ -1401,6 +1360,14 @@ network. To support this, a server sends multiple NEW_CONNECTION_ID messages.
Each NEW_CONNECTION_ID is marked with a sequence number. Connection IDs MUST be
used in the order in which they are numbered.

A client which wishes to break linkability upon changing networks MUST use the
connection ID provided by the server as well as incrementing the packet sequence
number by an externally unpredictable value computed as described in
{{packet-number-gap}}. Packet number gaps are cumulative. A client might skip
connection IDs, but it MUST ensure that it applies the associated packet number
gaps for connection IDs that it skips in addition to the packet number gap
associated with the connection ID that it does use.

A server that receives a packet that is marked with a new connection ID recovers
the packet number by adding the cumulative packet number gap to its expected
packet number. A server SHOULD discard packets that contain a smaller gap than
Expand All @@ -1412,6 +1379,7 @@ connection ID, it should expect packets on the new connection ID to start at 18.
A packet with the new connection ID and a packet number of 17 is discarded as
being in error.


#### Packet Number Gap

In order to avoid linkage, the packet number gap MUST be externally
Expand Down Expand Up @@ -1459,15 +1427,8 @@ of three ways:
enabled when it is expensive to send an explicit close, such as mobile
networks that must wake up the radio.

3. Abrupt Shutdown: An endpoint may send a Public Reset packet at any time
during the connection to abruptly terminate an active connection. A Public
Reset packet SHOULD only be used as a final recourse. Commonly, a public
reset is expected to be sent when a packet on an established connection is
received by an endpoint that is unable decrypt the packet. For instance, if
a server reboots mid-connection and loses any cryptographic state associated
with open connections, and then receives a packet on an open connection, it
should send a Public Reset packet in return. (TODO: articulate rules around
when a public reset should be sent.)
3. Stateless Reset: An endpoint that loses state can use this procedure to cause
the connection to terminate early, see {{stateless-reset}} for details.

After receiving either a CONNECTION_CLOSE frame or a Public Reset, an
endpoint MUST NOT send additional packets on that connection. After
Expand All @@ -1482,6 +1443,117 @@ instance by exponentially backing off the number of packets which are
received before sending a response. After this time, implementations
SHOULD respond to unexpected packets with a Public Reset packet.


## Stateless Reset {#stateless-reset}

A stateless reset is provided as an option of last resort for a server that does
not have access to the state of a connection. A server crash or outage might
result in clients continuing to send data to a server that is unable to properly
continue the connection. A server that wishes to communicate a fatal connection
error MUST use a CONNECTION_CLOSE frame if it has sufficient state to do so.

To support this process, the server sends a stateless_reset_token value during
the handshake in the transport parameters. This value is protected by
encryption, so only client and server know this value.

A server that receives packets that it cannot process sends a packet in the
following layout:

~~~
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
+-+-+-+-+-+-+-+-+
|0|C|K| 00001 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should CK be required to be 10 here? Keyphase makes little sense, and Connection ID is required.

| |
+ [Connection ID (64)] +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Stateless Reset Token (128) +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Random Octets (*) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~~~

This packet SHOULD use the short header form with the shortest possible packet
number encoding. This minimizes the perceived gap between the last packet that
the server sent and this packet. The leading octet of the Stateless Reset Token
will be interpreted as a packet number. A server MAY use a different short
header type, indicating a different packet number length, but this allows for
the message to be identified as a stateless reset more easily using heuristics.

A server copies the connection ID field from the packet that triggers the
stateless reset. A server omits the connection ID if explicitly configured to
do so, or if the client packet did not include a connection ID.

After the first short header octet and optional connection ID, the server
includes the value of the Stateless Reset Token that it included in its
transport parameters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the Connection ID optional here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it is - it is only the suggested reset token design that requires the connection ID. But if the reset is from a client that reboots and sends a reset, it cannot reproduce a unique 5-tuple, and so it requires that the connection can be identified by the reset token, unless the connection ID is available.

After the Stateless Reset Token, the endpoint pads the message with an arbitrary
number of octets containing random values.

This design ensures that a stateless reset packet is - to the extent possible -
indistinguishable from a regular packet.

A stateless reset is not appropriate for signaling error conditions. An
endpoint that wishes to communicate a fatal connection error MUST use a
CONNECTION_CLOSE frame if it has sufficient state to do so.


### Detecting a Stateless Reset

A client detects a potential stateless reset when a packet with a short header
cannot be decrypted. The client then performs a constant-time comparison of the
16 octets that follow the Connection ID with the Stateless Reset Token provided
by the server in its transport parameters. If this comparison is successful,
the connection MUST be terminated immediately. Otherwise, the packet can be
discarded.


### Calculating a Stateless Reset Token

The stateless reset token MUST be difficult to guess. In order to create a
Stateless Reset Token, a server could randomly generate {{!RFC4086}} a secret
for every connection that it creates. However, this presents a coordination
problem when there are multiple servers in a cluster or a storage problem for a
server that might lose state. Stateless reset specifically exists to handle the
case where state is lost, so this approach is suboptimal.

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, a the connection ID for the connection
(see {{connection-id}}), and an identifier for the server instance. A server
could use HMAC {{?RFC2104}} (for example, HMAC(static_key, server_id ||
connection_id)) or HKDF {{?RFC5869}} (for example, using the static key as input
keying material, with server and connection identifiers as salt). The output of
this function is truncated to 16 octets to produce the Stateless Reset Token
for that connection.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A server could not use a connection id because the connection id might have changed, and even if didn't change, it might be available in a short header. It also would not be able to use IP and port tuple because it also might have changed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The connection ID change is a good point -- we would need a way to update the proof when getting a new connection ID.

If the incoming packet doesn't include Connection ID, you're basically stuck -- if you want the ability to reset, you'll need to request Connection ID. The text talks about this two paragraphs later.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say MUST be, because otherwise you are leaking

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, not seeing an action here. The token is attached to NEW_CONNECTION_ID and I explain that the technique here relies on having connection ID later in the text.


A server that loses state can use the same method to generate a valid Stateless
Reset Secret. The connection ID comes from the packet that the server receives.

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 omit a
connection ID (that is, it cannot use the truncate_connection_id transport
parameter {{transport-parameter-definitions}}).

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.


# Frame Types and Formats

As described in {{frames}}, Regular packets contain one or more frames.
Expand Down Expand Up @@ -1816,6 +1888,14 @@ The NEW_CONNECTION_ID is as follows:
+ Connection ID (64) +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Stateless Reset Token (128) +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~~~

The fields are:
Expand All @@ -1833,6 +1913,11 @@ Connection ID:

: A 64-bit connection ID.

Stateless Reset Token:

: A 128-bit value that will be used to for a stateless reset when the associated
connection ID is used (see {{stateless-reset}}).


## ACK Frame {#frame-ack}

Expand Down Expand Up @@ -2191,7 +2276,7 @@ transmission efficiency to underfilled packets.
# Packetization and Reliability {#packetization}

The Path Maximum Transmission Unit (PMTU) is the maximum size of the entire IP
header, UDP header, and UDP payload. The UDP payload includes the QUIC public
header, UDP header, and UDP payload. The UDP payload includes the QUIC packet
header, protected payload, and any authentication fields.

All QUIC packets SHOULD be sized to fit within the estimated PMTU to avoid IP
Expand Down Expand Up @@ -2797,9 +2882,10 @@ The most appropriate error code ({{error-codes}}) SHOULD be included in the
frame that signals the error. Where this specification identifies error
conditions, it also identifies the error code that is used.

Public Reset is not suitable for any error that can be signaled with a
CONNECTION_CLOSE or RST_STREAM frame. Public Reset MUST NOT be sent by an
endpoint that has the state necessary to send a frame on the connection.
A stateless reset ({{stateless-reset}}) is not suitable for any error that can
be signaled with a CONNECTION_CLOSE or RST_STREAM frame. A stateless reset MUST
NOT be used by an endpoint that has the state necessary to send a frame on the
connection.


## Connection Errors
Expand All @@ -2818,8 +2904,8 @@ effort expended on terminated connections.

An endpoint that chooses not to retransmit packets containing CONNECTION_CLOSE
risks a peer missing the first such packet. The only mechanism available to an
endpoint that continues to receive data for a terminated connection is to send a
Public Reset packet.
endpoint that continues to receive data for a terminated connection is to use
the stateless reset process ({{stateless-reset}}).

An endpoint that receives an invalid CONNECTION_CLOSE frame MUST NOT signal the
existence of the error to its peer.
Expand Down Expand Up @@ -3072,6 +3158,7 @@ The initial contents of this registry are shown in
| 0x0003 | idle_timeout | {{transport-parameter-definitions}} |
| 0x0004 | omit_connection_id | {{transport-parameter-definitions}} |
| 0x0005 | max_packet_size | {{transport-parameter-definitions}} |
| 0x0006 | stateless_reset_token | {{transport-parameter-definitions}} |
{: #iana-tp-table title="Initial QUIC Transport Parameters Entries"}


Expand Down