Re: Key updates

Mikkel Fahnøe Jørgensen <> Mon, 06 August 2018 09:22 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 8A875130EB5 for <>; Mon, 6 Aug 2018 02:22:29 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -0.998
X-Spam-Status: No, score=-0.998 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, FREEMAIL_REPLY=1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_PASS=-0.001, UNPARSEABLE_RELAY=0.001] autolearn=no autolearn_force=no
Authentication-Results: (amavisd-new); dkim=pass (2048-bit key)
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id aBYH1G6u2Kdj for <>; Mon, 6 Aug 2018 02:22:27 -0700 (PDT)
Received: from ( [IPv6:2607:f8b0:4001:c0b::234]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by (Postfix) with ESMTPS id 90AF3130DE9 for <>; Mon, 6 Aug 2018 02:22:27 -0700 (PDT)
Received: by with SMTP id p81-v6so17180452itp.1 for <>; Mon, 06 Aug 2018 02:22:27 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025; h=from:in-reply-to:references:mime-version:date:message-id:subject:to; bh=kGzrdD+3rAXitRbL+P5cdWtghAGdyfC5hlTBBdYtzgw=; b=ZVn1j3tZoTjP8SoWsTpb9eCZEol2r0XN+kQYmQLU3miFNtbDeGq+rQhdaoxQcfffGe aZgp1E1j45vst+LIqeXDqaqW253SVZNk+f3eDjBr7+S3XGew1kkUa5FGaXbhKTOhHuuw vrpawFOXm9TJgepDtlFiQdvLEKzU5EbuVeG35hp8ODIjMBNxZXNwEcdzT+dUSOLU5E/9 R21ejFHl4YakY9HUxzp6QM3Nh02tuyyRR9iBh1fMbOv9SGt3RdYvL1XQ7FwLJSAGhcoF VoAS/vBk1K3Fucz6zupLerG/lOIdUgFdpf4Tl191eps5su6DYkCRb4MkV/WBuK+wd9ZV v9dQ==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025; h=x-gm-message-state:from:in-reply-to:references:mime-version:date :message-id:subject:to; bh=kGzrdD+3rAXitRbL+P5cdWtghAGdyfC5hlTBBdYtzgw=; b=fuzPRlf5V6xlBc50Bzyj38cPuqTWCxossSfMcmA12w3krxaM+M3znKQjSAuDxopruf Wtomz88qMCPVTcURRnwys2Goxvju2s3c+52f9xuj8BverRNSKgkw+9dcMuOzey7LUJQB 4Ar8xwfUGAQv76ipbij/7uQ8CqhZ57DePUvh7nzByrRuHCkRFXuBK142YGyRh4hDcuDn io+t04FbDiGdeFdsgaBe36i1B0d0NlKi2hW5p1HpgS5TDjyEjStw3p6kWemkOW8KL7dw 0QgyJYL8e74GU9ku5DPZ+nBtNlBUnHajFh/gL6mj5NHPWBHgqMMtEO9+v1hejsCLM+Mo xoUA==
X-Gm-Message-State: AOUpUlGaYm3YKazM46sBI1eRZc94XyTMTvBn68o8ClFvy+GfkV4m0F0M ZhUKY4BZnkp8rqvFhV7XjUmSYsYmCdB3X31LZVE=
X-Google-Smtp-Source: AAOMgpdXuQeVT/MWCjvAs3Uolh2189CSKEYJbAwoA+emeVknvXCkR9LrT1KKGld9oZMSxDP7jOLtIoD39H0xyCUeb/M=
X-Received: by 2002:a02:9833:: with SMTP id t48-v6mr12175468jaj.111.1533547346873; Mon, 06 Aug 2018 02:22:26 -0700 (PDT)
Received: from 1058052472880 named unknown by with HTTPREST; Mon, 6 Aug 2018 02:22:26 -0700
From: =?UTF-8?Q?Mikkel_Fahn=C3=B8e_J=C3=B8rgensen?= <>
In-Reply-To: <>
References: <>
X-Mailer: Airmail (420)
MIME-Version: 1.0
Date: Mon, 6 Aug 2018 02:22:26 -0700
Message-ID: <>
Subject: Re: Key updates
To: Martin Thomson <>, QUIC WG <>
Content-Type: multipart/alternative; boundary="000000000000db52ae0572c0d096"
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 09:22:29 -0000

On the last part: using a longer packet number - that would create an
observable signal similar to an unencrypted key-phase bit because many
packets would tend to have similar sizes, then suddenly spike a few bytes
in length.

Key phase could also be tied to connection id, and perhaps some
consideration should be given to a possible multi-path design before opting
for the more complex solution.

As I understand it, the commit option still requires synchronised key
updates in both directions. How about one-sided key updates?

On 6 August 2018 at 07.56.18, Martin Thomson (

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 :)