Skip to content

Commit

Permalink
KEYS_READY frame
Browse files Browse the repository at this point in the history
This frame solves several problems.

1. it allows us to get rid of Initial keys very quickly, without relying
on the existing implicit mess.

2. it allows us to get rid of Handshake keys quickly, without timers.

3. it allows us to regulate the rate of key update, without risk of
becoming unsynchronized.

The big design question here is whether to include an explicit marker
for the current key phase, or to pull that in from the packet.  I've
chosen the latter, but I am willing to be convinced that this is a bad
idea.  There is a risk that retransmissions of KEYS_READY will cause
problems, but that risk is easily addressed by purging unacknowledged
KEYS_READY frames any time you initiate a key update.  Or, you could not
retransmit frames blindly.

The alternative means pulling in the notion of an epoch, likely copying
from DTLS (0=Initial, 1=0-RTT, 2=Handshake, 3+=1-RTT).  Thats
complicated and we would still need to work out what to do with the
validation code in that case.

I've tried to keep the request to minimize the number of active keys, by
allowing KEYS_READY to be deferred by up to 3PTO.  That seems to work
well.

Closes #2267, #1951.

Prerequisite for fixing #2309.
  • Loading branch information
martinthomson committed Feb 13, 2019
1 parent a2a860f commit e378a88
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 71 deletions.
116 changes: 52 additions & 64 deletions draft-ietf-quic-tls.md
Expand Up @@ -1118,7 +1118,7 @@ message as a connection error of type 0x10a, equivalent to a fatal TLS alert of
unexpected_message (see {{tls-errors}}).

{{ex-key-update}} shows a key update process, with keys used identified with @M
and @N. Values in brackets \[] indicate the value of Key Phase bits.
and @N. Values in brackets \[] indicate the value of Key Phase bit.

~~~
Initiating Peer Responding Peer
Expand Down Expand Up @@ -1155,87 +1155,75 @@ uses the KDF function provided by TLS with a label of "quic ku". The
corresponding key and IV are created from that secret as defined in
{{protection-keys}}. The header protection key is not updated.

The endpoint clears the Key Update Permitted bit, and toggles the value of the
Key Phase bit, and uses the updated key and IV to protect all subsequent
packets.
The endpoint toggles the value of the Key Phase bit, and uses the updated key
and IV to protect all subsequent packets.

An endpoint MUST NOT initiate more than one key update at a time. A subsequent
key update can only be performed after the endpoint has successfully processed a
packet with a matching key phase and the Key Update Permitted bit set.
Together, these indicate that the key update was received and acted on.
An endpoint MUST NOT initiate a key update prior to having received a KEYS_READY
frame in a packet from the current key phase. A subsequent key update can only
be performed after the endpoint has successfully processed a KEYS_READY frame
from a packet with a matching key phase. This ensures that keys are available
to both peers before another can be initiated.

Once an endpoint has received and successfully processed packets with the same
key phase, this indicates that the peer has also updated keys. The
endpoint can then set the Key Update Permitted bit to 1 on packets it
subsequently sends. An endpoint MUST NOT set Key Update Permitted to 1 on
packets it sends unless it has successfully processed packets with a matching
key phase. An endpoint MAY defer setting Key Update Permitted to 1 until it has
discarded old keys, see {{key-update-old-keys}}.

Using Key Update Permitted in this manner guarantees at least one round trip
between updates, preventing multiple updates that could result in endpoints
being unable to process any packets.

An endpoint that receives a packet protected with old keys that includes an
acknowledgement for a packet protected with newer keys MAY treat that as a
connection error of type KEY_UDPATE_ERROR.
Once an endpoint has successfully processed a packet with the same key phase, it
can send a KEYS_READY frame. Endpoints MAY defer sending a KEYS_READY frame
after a key update (see {{key-update-old-keys}}).


## Responding to a Key Update

An endpoint that sets Key Update Permitted to 1 on packets it sends is willing
to accept key updates. If a packet is received with a key phase that differs
from the value the endpoint expects, the endpoint creates a new packet
protection secret for reading and the corresponding key and IV. The endpoint
uses the same key derivation process as its peer uses to generate keys for
receiving.
An endpoint that sends a KEYS_READY frame can accept further key updates. A key
update can happen even without seeing a KEYS_READY frame from the peer. If a
packet is received with a key phase that differs from the value the endpoint
used to protect the packet containing its last KEYS_READY frame, the endpoint
creates a new packet protection secret for reading and the corresponding key and
IV. An endpoint uses the same key derivation process as its peer uses to
generate keys for receiving.

If the packet protection is successfully removed using the updated key and IV,
then the keys the endpoint initiates a key update in response, as described in
{{key-update-initiate}}. However, as packets with a matching key phase have
been received, the Key Update Permitted bit can be set to 1 on the next packet
it sends.
{{key-update-initiate}}. An endpoint that responds to a key update MUST send a
KEYS_READY frame to indicate that it is both sending and receiving with updated
keys, though it MAY defer sending the frame (see {{key-update-old-keys}}).

An endpoint does not always need to send packets when it detects that its peer
has updated keys. The next packet that it sends will simply use the new keys.
If an endpoint detects a second update before it has sent any packets with
updated keys, it indicates that its peer has updated keys twice without awaiting
a reciprocal update. An endpoint MUST treat consecutive key updates as a
connection error of type KEY_UDPATE_ERROR.
has updated keys. The next packet that it sends use the new keys and include
the KEYS_READY frame. If an endpoint detects a second update before it has sent
any packets with updated keys or a KEYS_READY frame, it indicates that its peer
has updated keys twice without awaiting a reciprocal update. An endpoint MUST
treat consecutive key updates as a connection error of type KEY_UPDATE_ERROR.

Endpoints responding to an apparent key update MUST NOT generate a timing
side-channel signal that might indicate that the Key Phase bit was invalid (see
{{header-protect-analysis}}). Endpoints can use dummy packet protection keys in
place of discarded keys when key updates are not permitted.


## Using Old Keys {#key-update-old-keys}

If the most recent packet sent by the endpoint contained a Key Update Permitted
bit set to 0, a key phase other than the value expected indicates that the
packet was protected with old keys. If those old keys are available, then they
can be used to remove packet protection.
During a key update, packets protected with older keys might arrive if they were
delayed by the network. If those old keys are available, then they can be used
to remove packet protection.

Old keys might be used to remove protection from packets that were are reordered
in the network. However, it is never valid for old keys to be used to protect
packets with packets that have higher packet numbers than packets that were
protected with newer keys. An endpoint that successfully removes protection
with old keys when newer keys were used for packets with lower packet numbers
MUST treat this as a connection error of type KEY_UPDATE_ERROR.
After a key update, an endpoint MAY delay sending the KEYS_READY frame by up to
three times the Probe Timeout (PTO, see {{QUIC-RECOVERY}}) to minimize the
number of active keys it maintains. During this time, an endpoint can use old
keys to process delayed packets rather than enabling a new key update. This
only applies to key updates that use the Key Phase bit; endpoints MUST NOT defer
sending of KEYS_READY during and immediately after the handshake.

Even if old keys are available, those keys MUST NOT be used to protect packets
with packets that have higher packet numbers than packets that were protected
with newer keys. An endpoint that successfully removes protection with old keys
when newer keys were used for packets with lower packet numbers MUST treat this
as a connection error of type KEY_UPDATE_ERROR.

An endpoint SHOULD retain old read keys for a period of no more than three times
the Probe Timeout (PTO, see {{QUIC-RECOVERY}}). After this period, old read
keys and their corresponding secrets SHOULD be discarded.

An endpoint MAY keep the Key Update Permitted bit set to 0 until it discards old
read keys to limit the number of keys it maintains. An endpoint MAY also
prevent key update until it discards keys from the handshake, including any
0-RTT keys. An endpoint SHOULD set the Key Update Permitted bit when possible.

Once set, the Key Update Permitted bit MUST NOT be cleared for packets with the
same key phase. An endpoint MAY treat receipt of a packet with the Key Update
Permitted bit cleared as a connection error of type KEY_UPDATE_ERROR if the bit
was previously set on packets protected with the same keys.

Endpoints MUST NOT generate a timing side-channel signal that might indicate
that the Key Update field was invalid (see {{header-protect-analysis}}).
Endpoints can use dummy packet protection keys in place of discarded keys when
key updates are not permitted.
the current PTO. After this period, old read keys and their corresponding
secrets SHOULD be discarded.

An endpoint that receives a packet protected with old keys that includes an
acknowledgement for a packet protected with newer keys MAY treat that as a
connection error of type KEY_UPDATE_ERROR.


## Key Update Frequency
Expand Down
58 changes: 51 additions & 7 deletions draft-ietf-quic-transport.md
Expand Up @@ -2821,6 +2821,7 @@ frames are explained in more detail in {{frame-formats}}.
| 0x1a | PATH_CHALLENGE | {{frame-path-challenge}} |
| 0x1b | PATH_RESPONSE | {{frame-path-response}} |
| 0x1c - 0x1d | CONNECTION_CLOSE | {{frame-connection-close}} |
| 0x1e | KEYS_READY | {{frame-keys-ready}} |
{: #frame-types title="Frame Types"}

All QUIC frames are idempotent in this version of QUIC. That is, a valid
Expand Down Expand Up @@ -3035,6 +3036,9 @@ containing that information is acknowledged.
* PING and PADDING frames contain no information, so lost PING or PADDING frames
do not require repair.

* KEYS_READY frames are sent until acknowledged or until newer keys are used for
sending packets.

Endpoints SHOULD prioritize retransmission of data over sending new data, unless
priorities specified by the application indicate otherwise (see
{{stream-prioritization}}).
Expand Down Expand Up @@ -3700,19 +3704,20 @@ and will contain a CRYPTO frame with an offset matching the size of the CRYPTO
frame sent in the first Initial packet. Cryptographic handshake messages
subsequent to the first do not need to fit within a single UDP datagram.


#### Abandoning Initial Packets {#discard-initial}

A client stops both sending and processing Initial packets when it sends its
first Handshake packet. A server stops sending and processing Initial packets
when it receives its first Handshake packet. Though packets might still be in
flight or awaiting acknowledgment, no further Initial packets need to be
Endpoints cease both sending and processing Initial packets when it receives a
Handshake packet containing a KEYS_READY frame. Though packets might still be
in flight or awaiting acknowledgment, no further Initial packets need to be
exchanged beyond this point. Initial packet protection keys are discarded (see
Section 4.10 of {{QUIC-TLS}}) along with any loss recovery and congestion
control state (see Sections 5.3.1.2 and 6.9 of {{QUIC-RECOVERY}}).

Any data in CRYPTO frames is discarded - and no longer retransmitted - when
Initial keys are discarded.


### 0-RTT {#packet-0rtt}

A 0-RTT packet uses long headers with a type value of 0x1, followed by the
Expand Down Expand Up @@ -3821,8 +3826,10 @@ ACK frames. Handshake packets MAY contain CONNECTION_CLOSE frames. Endpoints
MUST treat receipt of Handshake packets with other frames as a connection error.

Like Initial packets (see {{discard-initial}}), data in CRYPTO frames at the
Handshake encryption level is discarded - and no longer retransmitted - when
Handshake protection keys are discarded.
Handshake encryption level and the corresponding keys are discarded - and data
is no longer retransmitted - when a KEYS_READY frame is received in a 1-RTT
packet.


### Retry Packet {#packet-retry}

Expand Down Expand Up @@ -3936,6 +3943,7 @@ failed validation as a connection error of type TRANSPORT_PARAMETER_ERROR.
A Retry packet does not include a packet number and cannot be explicitly
acknowledged by a client.


## Short Header Packets {#short-header}

This version of QUIC defines a single packet type which uses the
Expand Down Expand Up @@ -5126,7 +5134,43 @@ Reason Phrase:
This SHOULD be a UTF-8 encoded string {{!RFC3629}}.


## KEYS_READY Frame {#frame-
## KEYS_READY Frame {#frame-keys-ready}

An endpoint sends a KEYS_READY frame (type=0x1e) to signal that it has installed
keys for reading and writing packets. Receipt of this frame in a packet
indicates that all earlier keys can be safely discarded.

The KEYS_READY frame contains no additional fields.

The packet that carries a KEYS_READY frame determines which keys are ready. The
keys with the same key phase as those used in the packet that carries the
KEYS_READY frame are present.

An endpoint MUST send a KEYS_READY packet in the first packet it sends using
keys, but only after having successfully processed a packet using the
corresponding keys.

KEYS_READY frames are retransmitted when declared lost, however implementations
need to take care not to retransmit lost KEYS_READY frames if they initiate a
subsequent key update. This can happen if an acknowledgment for a packet
containing a KEYS_READY frame is lost.

Endpoints MUST NOT send KEYS_READY frames in Initial or 0-RTT packets.

A KEYS_READY frame used during the handshake can be used to indicate the
availability of Handshake keys by including it in a Handshake packet. An
endpoint sends this frame in its first Handshake packet. Once received, an
endpoint can discard Initial keys.

A KEYS_READY frame used after the completion of the handshake in 1-RTT packets
indicates that Handshake keys are no longer needed. A client sends this frame
in its first 1-RTT packet, and a server sends this frame in the first packet it
sends after completing the handshake. Note that a server might send 1-RTT keys
prior to this.

An endpoint uses the KEYS_READY frame in 1-RTT packets to indicate that it is
able to receive a key update (see Section 6 of {{QUIC-TLS}}).


## Extension Frames

Expand Down

0 comments on commit e378a88

Please sign in to comment.