Re: [TLS] Refactoring the negotiation syntax

Ilari Liusvaara <> Wed, 13 July 2016 20:36 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id CF10112D848 for <>; Wed, 13 Jul 2016 13:36:23 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -3.187
X-Spam-Status: No, score=-3.187 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, RP_MATCHES_RCVD=-1.287] autolearn=ham autolearn_force=no
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id RUwDpvM_GLT1 for <>; Wed, 13 Jul 2016 13:36:20 -0700 (PDT)
Received: from ( []) by (Postfix) with ESMTP id EA57612D534 for <>; Wed, 13 Jul 2016 13:36:19 -0700 (PDT)
Received: from localhost (localhost []) by (Postfix) with ESMTP id 69F2680E6; Wed, 13 Jul 2016 23:36:18 +0300 (EEST)
X-Virus-Scanned: Debian amavisd-new at
Received: from ([IPv6:::ffff:]) by localhost ( [::ffff:]) (amavisd-new, port 10024) with ESMTP id qYQWy2ToDS_Y; Wed, 13 Jul 2016 23:36:17 +0300 (EEST)
Received: from LK-Perkele-V2 ( []) (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by (Postfix) with ESMTPSA id DE231285; Wed, 13 Jul 2016 23:36:17 +0300 (EEST)
Date: Wed, 13 Jul 2016 23:36:14 +0300
From: Ilari Liusvaara <>
To: Eric Rescorla <>
Message-ID: <>
References: <>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Disposition: inline
In-Reply-To: <>
User-Agent: Mutt/1.6.0 (2016-04-01)
Archived-At: <>
Cc: "" <>
Subject: Re: [TLS] Refactoring the negotiation syntax
X-Mailman-Version: 2.1.17
Precedence: list
List-Id: "This is the mailing list for the Transport Layer Security working group of the IETF." <>
List-Unsubscribe: <>, <>
List-Archive: <>
List-Post: <>
List-Help: <>
List-Subscribe: <>, <>
X-List-Received-Date: Wed, 13 Jul 2016 20:36:24 -0000

On Wed, Jul 13, 2016 at 10:12:54AM -0700, Eric Rescorla wrote:
> Hi folks,
> There have been a lot of discussions about whether we should try to
> refactor cipher-suite negotiation to be less monolithic in the cipher
> suite. I've generally been on the "no" side of that on cost/benefit
> grounds as well as not quite seeing how it would fit into the rest
> of the infrastructure. However, now that we're starting to get full
> implementations, and it's becoming clearer how the new elements
> (principally PSK-resumption and key_shares) interact with cipher
> suites, I've started to think that in fact we may be able to clean
> things up. One proposal for this is below [0]. I know this is
> pretty late in the process, but if we do want this change it's
> better to do it now.
> I'm sure we'll need to discuss this in Berlin, but in the meantime,
> fire away.
> The basic idea here is to factor out the TLS 1.3 negotiation into three
> mostly orthogonal axes.
> - Symmetric cipher/PRF  -- indicated by the cipher suite list as in TLS 1.2
> - Key exchange -- indicated by the key_shares and pre_shared_key extensions
> - Authentication -- indicated the signature_algorithms and pre_shared_key
>   extensions
> A proposal for how to do this is below. See the end for other options,
> caveats, etc.

Those axes aren't properly orthogonal. IMO, the most orthogonal axes

- Key exchange main type: PSK vs. GDHE-PSK vs. GDHE-CERT.
- Key exchange function (for GDHE-*)
- Signature algorithm (for GDHE-CERT)
- Record protection
- PRF-hash.

And the most logical way to choose from above list is to first choose
the major type, and then mostly independently (might want to tweak
protection/PRF priorities based on key exchange function strength) the
rest (also, might want to check ther is OK choice for kex function
before chosing main type).

Now, the issue is that orthogonality is completely broken in TLS 1.2.
The factors that most break it are group types and legacy signature
types and how those can interact with record protection or PRF.

Send a ClientHello that does not have cartesian product of group types,
legacy signature types and protections/prfs and you can get some very
surprising results (or at worst, bugs where server selects ciphersuite
that isn't on your list!).

OTOH, the problem with changing it is that TLS 1.2 and 1.3-14 parameter
negotiation works mostly the same way (other than lack of the broken
RSA key exchange and the DHE downnegotiation issue, which is mostly client
problem rather than server problem)

So any dual 1.2/1.3 server would have to implement both mechanisms if
those are changed. So only 1.3-only servers would benefit.

> If we take PSK out of the picture, this gives us a very simple structure:
> - The client offers a set of key_shares and the server picks one that it
>   likes [1].
> - The client offers a list of signature_algorithms and the server picks
>   a certificate/key that matches that list and signs with it.
> In other words, we just eliminate the redundancy with the cipher suite
> indications. This leaves is with the question of how to handle the
> existing TLS 1.2 cipher suites. We can either assign new cipher suites
> or say that any cipher suite with *_aead_alg_hash means that we
> support aead_alg_hash. Matter of taste.

Yes, that's the key exchange function and signature algorithm parts
from the above.
> PSK is handled by extending the concept of PSK flags that we already
> have in NewSessionTicket to also include uses of PSKs where you
> indicate the way in which you are using the PSK (or want it to be
> used). There a bunch of ways to encode this. I'll give you the one I
> mostly prefer below and then a one that's a smaller change but I think
> a little less elegant at the end [note #4].
> First, we replace the flags word of the different KE modes for the
> ticket with lists of code points, as below:
>    enum {
>      psk_ke(0),          // PSK key exchange
>      psk_dhe_ke(1),      // PSK + DHE key exchange
>      (255)
>    } PskKeModes;

In the orthogonalization above, the second entry would be PSK+GDHE
(any supported key exchange function), but that's probably what you
meant already.

> And then add new code points for being able to use the PSK with and
> without signatures (from the server). We had pretty rough consensus in
> B-A that we needed this mode and [draft-thomson-tls-0rtt-and-certs-01]
> is part of the motivation for this idea.
>    enum {
>      psk_auth(0),        // PSK only
>      psk_sign_auth(1),   // PSK + a signature (as in draft-thomson) [5]
>      (255)
>    } PskAuthModes;

I think that PSK+signature modes are so different that those warrant
promotion to a new major key exchange type.

Depending on the actual implementation, those might have some new message
sequence (e.g. omitting Certificate if the key can't change).

> This gives us the following NewSessionTicket message where we have
> replaced the flags word with two (potentially ordered) lists:
>    struct {
>        uint32 ticket_lifetime;
>        PskAuthModes auth_modes<1..255>;
>        PskKeModes ke_modes<1..255>;
>        TicketExtension extensions<2..2^16-2>;
>        opaque ticket<0..2^16-1>;
>    } NewSessionTicket;
> PreSharedKeyExtension becomes:
>     struct {
>        PskAuthMode auth_modes<1..255>;
>        PskKeModee ke_modes<1..255>;
>        opaque identity<0..2^16-1>;
>     } PskIdentity;
>     struct {
>          select (Role) {
>              case client:
>                  PskIdentity identities<2..2^16-1>;
>               case server:
>                  PskAuthMode auth_mode;
>                  PskKeMode ke_mode;
>                  uint16 selected_identity;
>          }
>      } PreSharedKeyExtension;

This seems somehow bit questionable to me (taking this as clean-
slate design), but I can't quite put my finger on it...

OTOH, if one wants to combine PSK and auth, one probably want's
to keep it separate like it is done above...

> You might ask why you need the server to indicate what it did.  The
> reason is that we would like the client to know in advance (at the
> time of the ServerHello) whether the server has sent
> Certificate/CertificateVerify rather than having to figure it out from
> what messages the server sends. The ke_mode field is redundant
> (because you also infer it from the server key_shares) but I added it
> for parity.

Well, figuring out from ServerHello (and even EncryptedExtensions)
would not be so bad if the rules were clear.

> I haven't implemented this yet (am going to try to take a crack at that
> before Berlin) but I believe based on experience with the NSS
> negotiation that it will be simpler. I know it removes a bunch of odd
> edge cases which have accumulated over the years, but maybe it adds
> others.

Maybe I could take a crack too (maybe)...

> 2. One question that comes up at the same time is whether we should
> allow multiple key shares to be used, which is a structure that this
> makes pretty easy. Basically, the server would just supply as many
> counter-shares as it wanted and then we'd need a defined order for how
> they were inserted into the key schedule. This feature would be nice
> for enabling post-quantum, but probably better to just define
> <PQ-Algorithm + Curve> code points.

In order of group number? Or in order of server reply?

(Both orders would be well-defined). Would also avoid the bit of
special case of zero key in pure-PSK mode...

> 3. Note that because of this PSK-PRF interaction, PSK isn't totally
> orthogonal with AEAD/PRF. I.e., you cannot use the same PSK with
> HKDF-SHA256 and HKDF-SHA384 if you want to be on the cryptographic
> fairway, but I think it should be easy enough to filter out the cipher
> suites to make that work.

The 0-RTT locking the protection/prf is surprisingly easy (other than
the checks if such locking can give anything sane due to couplings
in ciphersuites).
> 4. The other major alternative is just to use the flags bits. So we
> would extend and generalize the flags as shown below.
>    enum {
>      early_data(1),
>      dhe_psk_ke(2),
>      psk_ke(4),
>      psk_auth(8),         // New (no server cert)
>      sig_psk_auth(16)     // New (sign with server cert)
>    } PskUsageFlags;
> These new flags have the expected meaning, namely:
> psk_auth         The server will do connections with just
>                  PSK authentication (equivalent to PSK now)
> sig_psk_auth     The server will do connections with PSK
>                  plus signature (not currently specified).
> Then we update the PreSharedKeyExtension as follows:
>     struct {
>           uint32 usage_flags;
>           opaque identity<0..2^16-1>;
>     } PskIdentity;
>     struct {
>          select (Role) {
>              case client:
>                  PskIdentity identities<2..2^16-1>;
>               case server:
>                  uint32 usage_flags;  // New (one from each category)
>                  uint16 selected_identity;
>          }
>      } PreSharedKeyExtension;
> The idea here is:
> - The client indicates how each PSK can be used (by flags, which
>   need to be a subset of the ticket flags).
> - The server can pick a PSK and indicate how it was actually
>   used.

Based on quick look, I like this less than the decoupled version...
> 5. Note: this would allow for modes where the server signs over a PSK
> handshake with no DHE at all. The resumption_ctx mechanism is intended
> to ensure that that is OK, but we'd need to confirm with analysis.

I should also figure out all the attacks I figured out against server
auth in pure-PSK (at most sidenotes, given how TLS 1.3 hasn't supported
any such modes).

Yeah, if resumption_ctx stuff is actually used, it should do the trick,
as it binds the PSK key used (and the normal handshake hash binds the

The resumption_ctx would also bind the key if one kept it as constant
and omitted Certificate message.