Re: [quicwg/base-drafts] Rework Key Update (#2237)

Christian Huitema <notifications@github.com> Wed, 13 February 2019 07:28 UTC

Return-Path: <noreply@github.com>
X-Original-To: quic-issues@ietfa.amsl.com
Delivered-To: quic-issues@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id B942C12D4E6 for <quic-issues@ietfa.amsl.com>; Tue, 12 Feb 2019 23:28:44 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -8.001
X-Spam-Level:
X-Spam-Status: No, score=-8.001 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, HTML_MESSAGE=0.001, MAILING_LIST_MULTI=-1, RCVD_IN_DNSWL_HI=-5, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Authentication-Results: ietfa.amsl.com (amavisd-new); dkim=pass (1024-bit key) header.d=github.com
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id VoXcrgc4jW52 for <quic-issues@ietfa.amsl.com>; Tue, 12 Feb 2019 23:28:42 -0800 (PST)
Received: from out-5.smtp.github.com (out-5.smtp.github.com [192.30.252.196]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id C13A61274D0 for <quic-issues@ietf.org>; Tue, 12 Feb 2019 23:28:41 -0800 (PST)
Date: Tue, 12 Feb 2019 23:28:40 -0800
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=github.com; s=pf2014; t=1550042920; bh=WManmhrfpU+swKu/YqHtInZnJK1gIc1xD1QddS7U5qg=; h=Date:From:Reply-To:To:Cc:In-Reply-To:References:Subject:List-ID: List-Archive:List-Post:List-Unsubscribe:From; b=RuNvARJBRyiqbPDme+880+ZrU6g+Xa7kUTk2ejwiIqRlzzzCz8eKlW4sGRrAnNWvd PLfHyFOxjIHROJhRXYC3rG6jdh4YLeWbGEwCtIEHzLNXmsAFeZyme6+5txzKI6YwDN OToB9dCSGjU+1rDDWNl2r3XJ3FajzI7ON8GYvjhY=
From: Christian Huitema <notifications@github.com>
Reply-To: quicwg/base-drafts <reply+0166e4ab91806f628b3ecfe2c982884c7406879b4536609c92cf00000001187b892892a169ce1770e975@reply.github.com>
To: quicwg/base-drafts <base-drafts@noreply.github.com>
Cc: Subscribed <subscribed@noreply.github.com>
Message-ID: <quicwg/base-drafts/pull/2237/review/203046954@github.com>
In-Reply-To: <quicwg/base-drafts/pull/2237@github.com>
References: <quicwg/base-drafts/pull/2237@github.com>
Subject: Re: [quicwg/base-drafts] Rework Key Update (#2237)
Mime-Version: 1.0
Content-Type: multipart/alternative; boundary="--==_mimepart_5c63c7286e8df_2d753fbd2d2d45b88204b6"; charset="UTF-8"
Content-Transfer-Encoding: 7bit
Precedence: list
X-GitHub-Sender: huitema
X-GitHub-Recipient: quic-issues
X-GitHub-Reason: subscribed
X-Auto-Response-Suppress: All
X-GitHub-Recipient-Address: quic-issues@ietf.org
Archived-At: <https://mailarchive.ietf.org/arch/msg/quic-issues/2pPYxqZZJour8gB-sy8_TGeGpKg>
X-BeenThere: quic-issues@ietf.org
X-Mailman-Version: 2.1.29
List-Id: Notification list for GitHub issues related to the QUIC WG <quic-issues.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/quic-issues>, <mailto:quic-issues-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/quic-issues/>
List-Post: <mailto:quic-issues@ietf.org>
List-Help: <mailto:quic-issues-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/quic-issues>, <mailto:quic-issues-request@ietf.org?subject=subscribe>
X-List-Received-Date: Wed, 13 Feb 2019 07:28:45 -0000

huitema commented on this pull request.

Some nits. We need a bit more clarity on the sending of KEY ACTIVE during the handshake, but otherwise this is good.

>  
-The KEY_PHASE bit allows a recipient to detect a change in keying material
-without necessarily needing to receive the first packet that triggered the
-change.  An endpoint that notices a changed KEY_PHASE bit can update keys and
-decrypt the packet that contains the changed bit.
+The Key Phase bit is used to indicate which packet protection keys are used to

I am confused. Line 866 mentions a Key Update bit, and here we see a Key Phase bit. Is that the same bit, or do we now have 2 bits?

> @@ -863,7 +863,7 @@ This protection applies to the least-significant bits of the first byte, plus
 the Packet Number field.  The four least-significant bits of the first byte are
 protected for packets with long headers; the five least significant bits of the
 first byte are protected for packets with short headers.  For both header forms,
-this covers the reserved bits and the Packet Number Length field; the Key Phase
+this covers the reserved bits and the Packet Number Length field; the Key Update
 bit is also protected for packets with a short header.

See comments in Key Update section. I would vote for keeping the Key Phase name.

> +corresponding key and IV are created from that secret as defined in
+{{protection-keys}}.  The header protection key is not updated.
+
+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 a key update prior to having received a
+KEYS_ACTIVE 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_ACTIVE 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 successfully processed a packet with the same key phase, it
+can send a KEYS_ACTIVE frame.  Endpoints MAY defer sending a KEYS_ACTIVE frame
+after a key update (see {{key-update-old-keys}}).
+

I assume that "processed a packet" means "received, successfully decrypted, and processed the content of the frames." But I have to guess. Encrypting  and sending is a form of processing too. The term first occurs in section 8.1, and it is a bit puzzling there too. I would suggest adding a formal definition in "1.2. Terms and Definitions ".

> +## Responding to a Key Update
+
+An endpoint that sends a KEYS_ACTIVE frame can accept further key updates.  A
+key update can happen even without seeing a KEYS_ACTIVE 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_ACTIVE 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}}.  An endpoint that responds to a key update MUST send a
+KEYS_ACTIVE 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}}).
+

Isn't that a restatement of the lines 1167-1169? Why do we have the same text twice, with slightly different phrasing, "processed" vs. "packet protection is successfully removed" ?

> +corresponding key and IV are created from that secret as defined in
+{{protection-keys}}.  The header protection key is not updated.
+
+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 a key update prior to having received a
+KEYS_ACTIVE 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_ACTIVE 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 successfully processed a packet with the same key phase, it
+can send a KEYS_ACTIVE frame.  Endpoints MAY defer sending a KEYS_ACTIVE frame
+after a key update (see {{key-update-old-keys}}).
+

A lot of that text is going to be specified again in the following paragraphs. Do we need a paraphrase here?

> +place of discarded keys when key updates are not permitted.
+
+
+## Using Old Keys {#key-update-old-keys}
+
+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.
+
+After a key update, an endpoint MAY delay sending the KEYS_ACTIVE 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_ACTIVE during and immediately after the handshake.
+

"key updates that use the Key Phase bit". That's a weird way to put it. Maybe a reference to the ladder diagram in the transport spec?


> +corresponding key and IV are created from that secret as defined in
+{{protection-keys}}.  The header protection key is not updated.
+
+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 a key update prior to having received a
+KEYS_ACTIVE 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_ACTIVE 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 successfully processed a packet with the same key phase, it
+can send a KEYS_ACTIVE frame.  Endpoints MAY defer sending a KEYS_ACTIVE frame
+after a key update (see {{key-update-old-keys}}).
+

Initial to Handshake, and Handshake to 1-RTT. Are these special cases of Key Update?

> @@ -1259,15 +1259,13 @@ Client                                                  Server
 Initial[0]: CRYPTO[CH] ->
 
                                  Initial[0]: CRYPTO[SH] ACK[0]
-                       Handshake[0]: CRYPTO[EE, CERT, CV, FIN]
+           Handshake[0]: KEYS_ACTIVE, CRYPTO[EE, CERT, CV, FIN]
                                  <- 1-RTT[0]: STREAM[1, "..."]

Sending the KEY ACTIVE there seems wrong. The logic "I have processed a packet at this key level". That has not happened yet, will only happen at the sending of `<- Handshake[1]: ACK[0]`. That's the same reason why you correctly do not place a Key Active frame in the packet `<- 1-RTT[0]: STREAM[1, "..."]`

> @@ -1283,15 +1281,13 @@ Initial[0]: CRYPTO[CH]
 0-RTT[0]: STREAM[0, "..."] ->
 
                                  Initial[0]: CRYPTO[SH] ACK[0]
-                        Handshake[0] CRYPTO[EE, CERT, CV, FIN]
+           Handshake[0]: KEYS_ACTIVE, CRYPTO[EE, CERT, CV, FIN]
                           <- 1-RTT[0]: STREAM[1, "..."] ACK[0]

Same issue. You cannot send Key Active there because the server has not yet received an Handshake packet from the peer.

> @@ -3520,7 +3520,7 @@ carries ACKs in either direction.
 
 ~~~
 +-+-+-+-+-+-+-+-+
-|1|1| 0 |R R|P P|
+|1|1| 0 | R | P |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Unrelated change. Makes the PR harder to read.

>  #### 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
-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}}).
+Endpoints cease both sending and processing Initial packets when it both sends
+and receives a Handshake or 1-RTT packet containing a KEYS_ACTIVE 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}}).
 

The server side specification feels wrong. The server does not know that the peer has received the server initial until it receives the KEY ACTIVE (Handshake) from the client. Yes, there is an "implicit" dependency that the Handshake cannot be processed without receiving first the Initial packet. There will be cases where Initial is lost, and both Initial and Handshake have to be retransmitted.

> @@ -3843,7 +3847,7 @@ short packet header.
  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|1|S|R|R|K|P P|
+|0|1|S| R |K| P |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Unrelated change.

-- 
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/2237#pullrequestreview-203046954