Key updates

Martin Thomson <> Mon, 06 August 2018 05:55 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id BC977130EC1 for <>; Sun, 5 Aug 2018 22:55:52 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -2
X-Spam-Status: No, score=-2 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Authentication-Results: (amavisd-new); dkim=pass (2048-bit key)
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id Sfy9T2cw1vyH for <>; Sun, 5 Aug 2018 22:55:50 -0700 (PDT)
Received: from ( [IPv6:2607:f8b0:4003:c06::22b]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by (Postfix) with ESMTPS id A0130130E8B for <>; Sun, 5 Aug 2018 22:55:50 -0700 (PDT)
Received: by with SMTP id k12-v6so20072547oiw.8 for <>; Sun, 05 Aug 2018 22:55:50 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=iBDfb+eyhQ6fHkMeNW0QFh7tynj+IAinl40i9Am6GKQ=; b=IcsHu266Uv0UKNRrWykT/mcsoNuTIqSdChbRwPjkLCrwpwCthQog0KhMTnRWvsdqmU mpx2TshohgI4+CYF9es09PH64SRIgHaVN5jEMPB2XDBkK03Wq/Y/Wl62z6/E60jGRHjV hr35Z0V4yaAsmdABTrSz/BjmP7M7FFFzHhhBunFrFsJr0UzAKTOViba51fk62FuPyuWC sSAcL7QKqdXGo6gtilUD/g5QYfGXrYX39c2g7w8si8rlyCME8v9PX8WSOFvOwBC2fziD wgjAw8/WzfQx5OZEJ1ibKH3OvhKBuAvXcu4P2/4Py73nXw34D2vsLvebfGu3siM7EzXI YKnA==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=iBDfb+eyhQ6fHkMeNW0QFh7tynj+IAinl40i9Am6GKQ=; b=lYrITxAsVzmcHFwkIk4PgTyy5UA81NiQb89Kq55nuv0Ka5lJWxd5/KMTMnDBTJLS9D 54OKOpqqHdUtqxV7Ie7lXeXi38cbAk6NgwuptJE5eDu56aIn42vHau/XaE+WyWBGStSB pi/prfjpc6INwj4oOYbRFltPmzfJP17qFWEit4t/VMf+XXiRsRzQTY/hn535Vhc3GcsE u+RBQbrwgOYvw2bkRd8bW655XPsnpaMSI0DUdl/U9uUqxnrq3rSy22eL2WOHWqA32Vym e39W+F63vEZVb9X4vJfrjnPQ9hQqWz0usSVm89A5RTCq5NwyTU2jsQv/xVc0VeIp497I FFQQ==
X-Gm-Message-State: AOUpUlFiBHZ+0f+hsiQqKskLRWZHIGa5oIZMjM9WW3fDMpHtsjB5+WpC I0/M2/OSLUkR1++lIQaJxe39DHtJPUaYeGr9EGx9ereu
X-Google-Smtp-Source: AAOMgpccL9DVg1g408+m4bSdBs1UnPZQIMdnAHzC8Lm/Lnj0oc3rRbHNi1Z00Tq+oQaclyCA6FyoXXUw0VjiRP/TXt0=
X-Received: by 2002:aca:df42:: with SMTP id w63-v6mr12411553oig.295.1533534949670; Sun, 05 Aug 2018 22:55:49 -0700 (PDT)
MIME-Version: 1.0
From: Martin Thomson <>
Date: Mon, 6 Aug 2018 15:55:39 +1000
Message-ID: <>
Subject: Key updates
To: QUIC WG <>
Content-Type: text/plain; charset="UTF-8"
Archived-At: <>
X-Mailman-Version: 2.1.27
Precedence: list
List-Id: Main mailing list of the IETF QUIC working group <>
List-Unsubscribe: <>, <>
List-Archive: <>
List-Post: <>
List-Help: <>
List-Subscribe: <>, <>
X-List-Received-Date: Mon, 06 Aug 2018 05:55:58 -0000

One of the open items out of the design team work was the interaction
between TLS and key updates.  As you might know, TLS key updates are
synchronous, which works well in TCP, but less well in QUIC, where you
need at least two active keys for reception around a key change.

As it is, I think that there are two serious options:

1. Use KEY_PHASE to signal a change.  This requires that both
endpoints are loosely synchronized, which disadvantages asymmetric
data exchanges.

2. Use a prepare-commit style.  DTLS does this by having each peer
send a KeyUpdate message, then requiring that to be acknowledged
before they can switch to the new epoch. QUIC could do the same. The
change is still signaled in the key phase bit, but only after this
signaling exchange.

The weakness of the first option is that packets marked with a change
of key phase trigger trial decryption.  And it requires synchronized
changes, where if one endpoint moves to epoch N (epoch == encryption
level with fewer characters to type), then the other has to follow
before either can move to epoch N+1.  The second has more moving
parts, but avoids both of these problems.

I tend to think that the second is a better design on balance, if only
to dispense with the trial decryption, which we've managed to avoid in
every other case thus far.  The complication might also present some
opportunities, which I will explain below, but that's a secondary

If we want to use this signaling method, we need to decide who drives
key updating, TLS or QUIC:

TLS: The DTLS design might work in QUIC, but with the KeyUpdate
message hidden in an opaque CRYPTO frame, it could be a little tricky
to manage properly.  If we let TLS drive the key update, then it will
send a KeyUpdate message in a CRYPTO frame and inform QUIC of the
change in sending keys.  From here, QUIC can keep using old keys until
all CRYPTO frames from that epoch are acknowledged.  The receiver will
inform QUIC of new receive keys when it receives KeyUpdate, which can
be installed.

QUIC: Here we define a new frame type (KEY_UPDATE, say) that QUIC
sends when it wants to update keys.  When this is acknowledged, new
keys can be installed and used for sending.  A new API is needed to
TLS to support cranking the key schedule forward.  Invoking that API
would lead to TLS signaling the availability of new keys.  The receive
side isn't any different from the TLS option.  If this was to have
parity with TLS, this would probably need to be two frame types so
that a unilateral update could be distinguished from an update that
includes a request for a mutual update.

In both cases, QUIC will install new receive keys, but retain old
receive keys so that it can deal with reordering.  A few round trips,
an RTO, 2MSL, or whatever we decide for the handshake is probably
fine.  Sending keys can be discarded immediately.

The advantage of having TLS drive is that it uses the same machinery
we have for the handshake.  And there is no disparity between what TLS
and QUIC understand to be current: when TLS wants to send something (a
NewSessionTicket message for example), then it asks for an epoch that
matches what QUIC is using.

The disadvantage of using TLS is that there are more moving parts
involved.  It fits the rules we have for the handshake neatly: for
instance, the CRYPTO frame would be retransmitted using the strict
encryption-level rules, reusing that code from the handshake.  But
those rules aren't really necessary given that the rules for a
KEY_UPDATE easily have the same effect.

My inclination here is to have TLS drive this.  The additional
complexity seems to be manageable in the sense that it uses mechanisms
that should already exist for the handshake.  I also don't think that
a new API for this feature is a good way to ensure that it is used.

Those who are implementing, I know that key updates are often a long
way down the list of priorities, but has anyone spent time considering
the options here?  (I personally implemented key updates for TLS, but
I still haven't managed to find time to do it for DTLS, which might
say something...)

Addendum: Optional Key Phase ?

One of the nice things about either prepare-commit design is that it
opens a new option for how we use bits in headers.  If there is a
concern about spending a bit on every packet for the key phase (a
whole bit!), we could design an encoding that only included the key
phase if a longer packet number encoding was in use.

The idea would be that you could use the longer packet number encoding
while you have a key update outstanding, so that the key phase was
available for the entirety of the changeover.  Once packets with the
new epoch were acknowledged, you could switch back to a shorter
encoding that didn't include a key phase.  The receiver would switch
to expecting the new epoch after receiving the first packet in the new

That's a saving that might allow endpoints to use the shortest packet
number encoding in more cases.  Of course, the header design is
already complex enough, so maybe we don't actually *want* more options
to consider there :)