[TLS] Refactoring client auth/re-key
Eric Rescorla <ekr@rtfm.com> Sat, 18 October 2014 14:33 UTC
Return-Path: <ekr@rtfm.com>
X-Original-To: tls@ietfa.amsl.com
Delivered-To: tls@ietfa.amsl.com
Received: from localhost (ietfa.amsl.com [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id A7AE91A888D for <tls@ietfa.amsl.com>; Sat, 18 Oct 2014 07:33:06 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -0.078
X-Spam-Level:
X-Spam-Status: No, score=-0.078 tagged_above=-999 required=5 tests=[BAYES_20=-0.001, FM_FORGED_GMAIL=0.622, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_LOW=-0.7] autolearn=ham
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 puSQz5u3GVhJ for <tls@ietfa.amsl.com>; Sat, 18 Oct 2014 07:33:02 -0700 (PDT)
Received: from mail-wi0-f179.google.com (mail-wi0-f179.google.com [209.85.212.179]) (using TLSv1 with cipher ECDHE-RSA-RC4-SHA (128/128 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 5BCC11A888B for <tls@ietf.org>; Sat, 18 Oct 2014 07:33:02 -0700 (PDT)
Received: by mail-wi0-f179.google.com with SMTP id d1so3234956wiv.12 for <tls@ietf.org>; Sat, 18 Oct 2014 07:33:01 -0700 (PDT)
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:from:date:message-id:subject:to :content-type; bh=SxCV7d5sulDvZcdepCcOgTTq55n61CIUxhcUnbhuJig=; b=DEmrCTyk//7+DWJFFrgwSN0qjJgoLLVV2FmkziblSz2/As6pLmIO53iwwKYlF2ZkXO iNvpaGSDZGK5blC7YJrKc6921WCwDLxDIpRN+Om6cOFraIHdPb3WiZE/vlTZsKahoUr5 aZK3tjXhnhPMtvxUViktINKiFr2B1xLEkvPfWVZh5EaP9RFbX1RQAe/cdiSH3pEMAZm4 JMVDXnsszK2Z5J1J3U+fLkjIlDAsuldW29Lsscisb0ATc5S4BFGAo4Y5ivo4HWox58xo U/CxlqjaCvsJDxUwsMYk61H3bVdhRU7bMUlUebpToMPdrb/BJtNF+ccMe+5wgjj3QpBb ybIQ==
X-Gm-Message-State: ALoCoQkB0LZhCAga31Q/ajdzSuL8p2ngxTkxNtkKReocbZKxotY5e70GMx4HNMX2MqM0BGfqLyKD
X-Received: by 10.180.103.233 with SMTP id fz9mr6319510wib.80.1413642780931; Sat, 18 Oct 2014 07:33:00 -0700 (PDT)
MIME-Version: 1.0
Received: by 10.216.49.198 with HTTP; Sat, 18 Oct 2014 07:32:20 -0700 (PDT)
From: Eric Rescorla <ekr@rtfm.com>
Date: Sat, 18 Oct 2014 15:32:20 +0100
Message-ID: <CABcZeBOdpK_JEH4EwnsoTA8Rje5pS5CtSbFGh8rHQzse92m9Wg@mail.gmail.com>
To: "tls@ietf.org" <tls@ietf.org>
Content-Type: multipart/alternative; boundary="f46d044280cacc36dd0505b35b09"
Archived-At: http://mailarchive.ietf.org/arch/msg/tls/UI3PSESGqUM-MVDr0VlBnr8YAFs
Subject: [TLS] Refactoring client auth/re-key
X-BeenThere: tls@ietf.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id: "This is the mailing list for the Transport Layer Security working group of the IETF." <tls.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/tls>, <mailto:tls-request@ietf.org?subject=unsubscribe>
List-Archive: <http://www.ietf.org/mail-archive/web/tls/>
List-Post: <mailto:tls@ietf.org>
List-Help: <mailto:tls-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/tls>, <mailto:tls-request@ietf.org?subject=subscribe>
X-List-Received-Date: Sat, 18 Oct 2014 14:33:06 -0000
Folks, What follows is a drafty proposal for tackling a number of different desiderata for 1.3 while also simplifying much of the protocol analysis. The following integrates a bunch of separate discussions starting with the YYZ interim and ideas from Adam Langley, Alfredo Pironti, Andrei Popov, Martin Thomson and others. The idea here is to unify: 1. Session hash. 2. Key refresh (based on MT's proposal from YYZ) 3. Client authentication As background, the current handshake looks like this. ClientHello ClientKeyShare --------> ServerHello ServerKeyShare [ChangeCipherSpec] EncryptedExtensions* Certificate* CertificateRequest* CertificateVerify* <-------- Finished [ChangeCipherSpec] Certificate* CertificateVerify* Finished --------> Now that we are adopting session-hash, we want to use it for TLS 1.3 as well, but a straight adaptation is inconvenient because: 1. We have moved encryption up so we need to generate the keys before we have either cert, but we then can't incorporate them into the session hash. 2. We want the server to be able to send data in his first flight if he doesn't care about client auth. In addition, there are a couple of other pieces of which would be nice: 3. Be able to add client auth at any part of the handshake. 4. Be able to regenerate the traffic keys now that we have removed renegotiation. These last two are the major purposes served by renegotiation. We could try to address each of these individually, but taking a look at this, it seems like we could solve a number of these problems together by pulling client auth out of the main handshake and into a followon protocol (see below). This would leave us with a single, uniform, handshake whether client auth is being used or not. ClientHello ClientKeyShare --------> ServerHello ServerKeyShare <-- A EncryptedExtensions* Certificate* CertificateVerify* <-------- Finished <-- B Finished --------> This would integrate with session-hash as follows: - At point A we would generate one set of keys based on the MS and the transcript so far. Those keys would be used to encipher the rest of the handshake. - At point B we would generate a new set of keys based on the MS and the transcript so far. Those keys would be used to encipher the traffic and the Finished messages. [0] REKEY Of course, we now need to have rekeying and client auth. I propose to merge those into a single mechanism called Update. For simplicity, let's consider Update without client auth first. enum { rekey(1), ack(2), authentication(3), (255) } UpdateType; struct { UpdateType update_type; switch (update_type) { case rekey: Random random; case ack: opaque digest<0..255>; /* Based on the PRF */ ... } } Update; In order to force a re-key, an implementation sends an Update message with a random nonce. It then digests the existing traffic keys in the sending direction with the Update contents (see below). The other side must respond with an UpdateAck which includes a digest of the Update message, computed using the PRF and similarly digests its update message with its sending keys. The result is that the sending keys in both directions now depend on the entire sequence of Update messages that have been sent. KEY SCHEDULE There are a lot of different things we could do for the key schedule but it's useful to have a concrete proposal, so consider the following. Currently TLS has a single Master Secret which is used to compute directional traffic keys. Instead, we should have two unidirectional MSs, each initially generated from the initial PMS. E.g., master_secret_client = PRF(pre_master_secret, "master secret_client", ClientHello.random + ServerHello.random) [0..47]; master_secret_server = PRF(pre_master_secret, "master secret_server", ClientHello.random + ServerHello.random) [0..47]; Every time an Update is sent by element X, we recompute master_secret_X, as follows: master_secret_X_{i+1} = PRF(master_secret_X_i, "master_secret_update", Update_message) We then recompute the traffic keys based on the current master secret. This produces the following pattern in each direction: X Y | | v v PMS --> MS_0 ---> MS_1 ---> MS_2 ---> ... | | | v v v Traffic Traffic Traffic Keys 0 Keys 1 Keys 1 As should be clear, once MS_{i+1} has been computed from MS_{i} we can discard MS_{i}. This provides a form of protection for the previous traffic, since if you compromise either side at (for instance) the point labelled Y, then it's not possible to decrypt traffic sent at X, since you would need MS_0 or TK_0, but these can have been discarded at this point, and since the PRF is nominally one-way, you can't compute MS_{i} from MS_{i+1}. [1] SIMULTANEOUS UPDATE It's obviously possible that both sides will try to update at once. That shouldn't be a problem here since the order of the Updates and hence key changes is deterministic on both sides. For example: Client Server [C_MS_0] [S_MS_0] Update (A) ------------------------> [C_MS_1] <------------------------ Update (B) [S_MS_1] <------------------------ ACK (C) [S_MS_2] ACK (D) ------------------------> [C_MS_2] The key computations are as follows: C_MS_1 = PRF(C_MS_0, A) S_MS_1 = PRF(S_MS_0, B) C_MS_2 = PRF(C_MS_1, D) [D depends only on B] S_MS_2 = PRF(S_MS_1, C) [C depends only on A, but S_MS_2 depends on S_MS_1 which depends on B]. Note: the above needs double-checking since I haven't coded it up and it's possible there's some way to get confused. The important property we are trying to have is that we don't need to worry about which of two messages that cross in flight was sent first. CLIENT AUTH We can easily extend this mechanism to allow client auth (as well as adding keys to the server, I suppose) by allowing the Update message to contain a Certificate and CertificateVerify. I.e., enum { rekey(1), ack(2), authentication(3), (255) } UpdateType; struct { UpdateType update_type; switch (update_type) { case rekey: Random random; case ack: opaque digest<0..255>; /* Based on the PRF */ case authentication: Certificate certificate; CertificateVerify verify; } } Update; Note: it might be nice to always have the Random value. One open issue is what the CertificateVerify should cover. Probably what's most convenient is to maintain a running hash of the initial transcript plus every Update that has been sent in the forward direction. Note that we don't want to include Updates sent in the reverse direction, since that would create race conditions where it's not clear if a CertificateVerify from the client covers an Update sent at approximately the same time from the server. Note that this causes the simultaneous update (above) to result in S_MS_2 to depend on Update(B). Though B is part of a different update exchange, it appears before C in the transcript (and they are sent by the same side) and is therefore covered by the hash used in the certificate verify Important note: I've removed CertificateRequest entirely. As Andrei Popov has pointed out, it's (a) complicated and (b) insufficiently flexible. The idea here is to push this up to the application layer where we can be more expressive and avoid the clunky CR syntax we have now. It is also obviously possible for the client to unilaterally offer client auth or the server to unilaterally add a new server certificate [This last may be regarded as a bug or a feature.] -Ekr [0] One could also imagine using the original keys to encipher the Finished message, or generating yet more keys. When Alfredo and I discussed this, he didn't think this affected the proof, so I've proposed the simplest option. [1] Note that this doesn't address the question of resumption. If we retain resumption, we need to keep a secret which can be used for resumption. It's easy to make it distinct from the C_MS_0 and C_MS_1 generated in the full handshake by generating a third independent resumption secret R_MS from the initial PMS. This would be used for resumed connections; obviously this means that if you recover R_MS you can attack all the resumed connections but not the original connecn. We could probably invent some techniques for keeping those connections, but it's hard to do without keeping state on the server.
- [TLS] Refactoring client auth/re-key Eric Rescorla
- Re: [TLS] Refactoring client auth/re-key Watson Ladd
- Re: [TLS] Refactoring client auth/re-key Eric Rescorla
- Re: [TLS] Refactoring client auth/re-key Ilari Liusvaara
- Re: [TLS] Refactoring client auth/re-key Manuel Pégourié-Gonnard
- Re: [TLS] Refactoring client auth/re-key Eric Rescorla
- Re: [TLS] Refactoring client auth/re-key Watson Ladd
- Re: [TLS] Refactoring client auth/re-key Karthikeyan Bhargavan
- Re: [TLS] Refactoring client auth/re-key Ilari Liusvaara
- Re: [TLS] Refactoring client auth/re-key Karthikeyan Bhargavan
- Re: [TLS] Refactoring client auth/re-key Martin Thomson
- Re: [TLS] Refactoring client auth/re-key Martin Thomson