classification
Title: Add set_post_handshake_auth for TLS 1.3
Type: behavior Stage: patch review
Components: SSL Versions: Python 3.8, Python 3.7, Python 3.6, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: christian.heimes Nosy List: benjamin.peterson, christian.heimes, miss-islington, ned.deily, njs, xnox
Priority: normal Keywords: patch

Created on 2018-09-14 00:16 by christian.heimes, last changed 2018-10-18 04:23 by njs.

Pull Requests
URL Status Linked Edit
PR 9460 merged christian.heimes, 2018-09-20 19:32
PR 9505 merged christian.heimes, 2018-09-23 06:41
PR 9507 merged christian.heimes, 2018-09-23 07:00
Messages (7)
msg325314 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2018-09-14 00:16
TLS 1.3 removed renegotiation in favor of rekeying and post handshake authentication (PHA). With PHA, a server can request a client certificate from a client at some point after the handshake. The feature is commonly used by HTTP server for conditional and path specific TLS client auth. For example a server can decide to require a cert based on HTTP method and/or path. A client must announce support for PHA during the handshake

Apache mod_ssl uses PHA, https://github.com/apache/httpd/blob/trunk/modules/ssl/ssl_engine_kernel.c#L1207

As of OpenSSL ticket https://github.com/openssl/openssl/issues/6933, TLS 1.3 clients no longer send the PHA TLS extension by default. Nikos and I requested the change, because PHA breaks some assumptions of TLS 1.2 clients. For on-demand auth, PHA extension must be enabled with SSL_CTX_set_post_handshake_auth(), https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_post_handshake_auth.html .

I propose to add a property on SSLContext to enable PHA and backport the change to Python 2.7, 3.6 and 3.7.

In order to test the feature, I'd also have to add some flags and a function for the server side: (SSL_VERIFY_CLIENT_ONCE, SSL_VERIFY_POST_HANDSHAKE, SSL_verify_client_post_handshake()).
msg325973 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2018-09-21 08:00
Please note that SSL_verify_client_post_handshake() doesn't perform any IO by itself.

A typical scenario for HTTP looks like this (actual flow may vary):

* client
  * send ``HTTP GET /path``
* server
  * recv
  * verify_client_post_handshake
  * send HTTP Connection Upgrade (emits CertRequest message)
* client
  * recv
  * send upgrade confirmation (emits Certificate, CertificateVerify, Finish message)
* server
  * recv
  * verify certificate
  * send payload or error (may emit TLS alert for unknown, invalid, or missing cert)
* client
  * recv (receive TLS alert or server response)
msg326138 - (view) Author: miss-islington (miss-islington) Date: 2018-09-23 06:32
New changeset 9fb051f032c36b9f6086b79086b4d6b7755a3d70 by Miss Islington (bot) (Christian Heimes) in branch 'master':
bpo-34670: Add TLS 1.3 post handshake auth (GH-9460)
https://github.com/python/cpython/commit/9fb051f032c36b9f6086b79086b4d6b7755a3d70
msg326141 - (view) Author: miss-islington (miss-islington) Date: 2018-09-23 07:22
New changeset 2756ef31656399a120589b7aa19c32e2b91a4758 by Miss Islington (bot) (Christian Heimes) in branch '3.7':
[3.7] bpo-34670: Add TLS 1.3 post handshake auth (GH-9460) (GH-9505)
https://github.com/python/cpython/commit/2756ef31656399a120589b7aa19c32e2b91a4758
msg326142 - (view) Author: miss-islington (miss-islington) Date: 2018-09-23 07:23
New changeset 94812f717dde8b11a9ad9c0fd5be66ff9bd53f58 by Miss Islington (bot) (Christian Heimes) in branch '3.6':
[3.6] bpo-34670: Add TLS 1.3 post handshake auth (GH-9460) (GH-9507)
https://github.com/python/cpython/commit/94812f717dde8b11a9ad9c0fd5be66ff9bd53f58
msg326458 - (view) Author: Dimitri John Ledkov (xnox) * Date: 2018-09-26 12:32
Will this be backported to the 2.7 branch as well? Pretty please =)
msg327932 - (view) Author: Nathaniel Smith (njs) * (Python committer) Date: 2018-10-18 04:23
FYI Christian, your "typical scenario for HTTP" doesn't make sense to me... you can't send HTTP Connection Upgrade in the middle of a regular request/response cycle. I feel like the typical scenario ought to be more like:

* client
  * send ``HTTP GET /path``
* server
  * recv
  * verify_client_post_handshake (maybe... via calling SSL_do_handshake again?)
* client
  * recv
  * send upgrade confirmation (emits Certificate, CertificateVerify, Finish message)
* server
  * recv
  * verify certificate
  * send either the requested response, or a 401 Unauthorized depending

But I don't really understand the underlying design here, either at the TLS 1.3 level or the openssl level, and haven't found very useful docs yet, so I could be wrong.
History
Date User Action Args
2018-10-18 04:23:03njssetnosy: + njs
messages: + msg327932
2018-09-26 12:32:36xnoxsetnosy: + xnox
messages: + msg326458
2018-09-23 07:23:05miss-islingtonsetmessages: + msg326142
2018-09-23 07:22:56miss-islingtonsetmessages: + msg326141
2018-09-23 07:00:03christian.heimessetpull_requests: + pull_request8914
2018-09-23 06:41:45christian.heimessetpull_requests: + pull_request8912
2018-09-23 06:32:35miss-islingtonsetnosy: + miss-islington
messages: + msg326138
2018-09-21 08:00:36christian.heimessetmessages: + msg325973
2018-09-20 19:32:19christian.heimessetkeywords: + patch
stage: needs patch -> patch review
pull_requests: + pull_request8874
2018-09-14 00:16:08christian.heimescreate