Skip to content

Commit

Permalink
Fix TLS-PSK for TLS 1.3
Browse files Browse the repository at this point in the history
SSL_CTX_set_session_id_context() is a server-side only operation.
Using this on the client-side is causing authentication errors
  • Loading branch information
grantramsay committed Apr 3, 2023
1 parent 9d0f6dc commit b481191
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 2 deletions.
9 changes: 9 additions & 0 deletions Doc/library/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2003,6 +2003,12 @@ to speed up repeated connections from the same clients.

Setting ``callback`` to :const:`None` removes any existing callback.

.. note::
When using TLS 1.3:

- the ``hint`` parameter is always :const:`None`.
- the OpenSSL implementation requires client-identity to be a non-empty string.

Example usage::

context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
Expand Down Expand Up @@ -2041,6 +2047,9 @@ to speed up repeated connections from the same clients.

The parameter ``identity_hint`` is an optional identity hint sent to the client.

.. note::
When using TLS 1.3 the ``identity_hint`` parameter is not sent to the client.

Example usage::

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Expand Down
32 changes: 32 additions & 0 deletions Lib/test/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4259,6 +4259,38 @@ def server_callback(identity):
with self.assertRaisesRegex(ssl.SSLError, 'Cannot add PSK client callback'):
server_context.set_psk_client_callback(client_callback)

@requires_tls_version('TLSv1_3')
def test_psk_tls1_3(self):
psk = bytes.fromhex('deadbeef')
identity_hint = 'identity-hint'
client_identity = 'client-identity'

def client_callback(hint):
# identity_hint is not sent to the client in TLS 1.3
self.assertIsNone(hint)
return client_identity, psk

def server_callback(identity):
self.assertEqual(identity, client_identity)
return psk

client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
client_context.check_hostname = False
client_context.verify_mode = ssl.CERT_NONE
client_context.minimum_version = ssl.TLSVersion.TLSv1_3
client_context.set_ciphers('PSK')
client_context.set_psk_client_callback(client_callback)

server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
server_context.minimum_version = ssl.TLSVersion.TLSv1_3
server_context.set_ciphers('PSK')
server_context.set_psk_server_callback(server_callback, identity_hint)

server = ThreadedEchoServer(context=server_context)
with server:
with client_context.wrap_socket(socket.socket()) as s:
s.connect((HOST, server.port))


@unittest.skipUnless(has_tls_version('TLSv1_3'), "Test needs TLS 1.3")
class TestPostHandshakeAuth(unittest.TestCase):
Expand Down
8 changes: 6 additions & 2 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3171,10 +3171,14 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
usage for no cost at all. */
SSL_CTX_set_mode(self->ctx, SSL_MODE_RELEASE_BUFFERS);

/* Setting the session id context is a server-side only operation.
* It can cause unexpected behaviour on client-side connections. */
if (proto_version == PY_SSL_VERSION_TLS_SERVER) {
#define SID_CTX "Python"
SSL_CTX_set_session_id_context(self->ctx, (const unsigned char *) SID_CTX,
sizeof(SID_CTX));
SSL_CTX_set_session_id_context(self->ctx, (const unsigned char *) SID_CTX,
sizeof(SID_CTX));
#undef SID_CTX
}

params = SSL_CTX_get0_param(self->ctx);
/* Improve trust chain building when cross-signed intermediate
Expand Down

0 comments on commit b481191

Please sign in to comment.