From 9d0f6dc2634a044e93ecac0fdf2ce007b8bad4bb Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Thu, 30 Mar 2023 12:15:43 +1300 Subject: [PATCH 01/18] gh-63284: Add support for TLS-PSK (pre-shared key) to the ssl module --- Doc/library/ssl.rst | 74 +++++++ .../pycore_global_objects_fini_generated.h | 2 + Include/internal/pycore_global_strings.h | 2 + .../internal/pycore_runtime_init_generated.h | 2 + .../internal/pycore_unicodeobject_generated.h | 6 + Lib/test/test_ssl.py | 56 +++++ Misc/ACKS | 1 + ...3-04-02-11-40-59.gh-issue-63284.oMYuon.rst | 1 + Modules/_ssl.c | 202 ++++++++++++++++++ Modules/clinic/_ssl.c.h | 137 +++++++++++- 10 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 4b60b7c643b62c..bd1713890198b7 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1987,6 +1987,80 @@ to speed up repeated connections from the same clients. >>> ssl.create_default_context().verify_mode # doctest: +SKIP +.. method:: SSLContext.set_psk_client_callback(callback) + + Enables TLS-PSK (pre-shared key) authentication on a client-side connection. + + In general, certificate based authentication should be preferred over this method. + + The parameter ``callback`` is a callable object with the signature: + ``def callback(hint: str | None) -> tuple[str | None, bytes]``. + The ``hint`` parameter is an optional identity hint sent by the server. + The return value is a tuple in the form (client-identity, psk). + Client-identity is an optional string which may be used by the server to + select a corresponding PSK for the client. PSK is a + :term:`bytes-like object` representing the pre-shared key. + + Setting ``callback`` to :const:`None` removes any existing callback. + + Example usage:: + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + context.maximum_version = ssl.TLSVersion.TLSv1_2 + context.set_ciphers('PSK') + + # A simple lambda: + psk = bytes.fromhex('deadbeef') + context.set_psk_client_callback(lambda hint: (None, psk)) + + # A table using the hint from the server: + psk_table = { 'ServerId_1': bytes.fromhex('deadbeef'), + 'ServerId_2': bytes.fromhex('cafebabe') + } + def callback(hint): + return 'ClientId_1', psk_table[hint] + context.set_psk_client_callback(callback) + + .. versionadded:: 3.12 + +.. method:: SSLContext.set_psk_server_callback(callback, identity_hint=None) + + Enables TLS-PSK (pre-shared key) authentication on a server-side connection. + + In general, certificate based authentication should be preferred over this method. + + The parameter ``callback`` is a callable object with the signature: + ``def callback(identity: str | None) -> bytes``. + The ``identity`` parameter is an optional identity sent by the client which can + be used to select a corresponding PSK. + The return value is a :term:`bytes-like object` representing the pre-shared key. + + Setting ``callback`` to :const:`None` removes any existing callback. + + The parameter ``identity_hint`` is an optional identity hint sent to the client. + + Example usage:: + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + context.maximum_version = ssl.TLSVersion.TLSv1_2 + context.set_ciphers('PSK') + + # A simple lambda: + psk = bytes.fromhex('deadbeef') + context.set_psk_server_callback(lambda identity: psk) + + # A table using the identity of the client: + psk_table = { 'ClientId_1': bytes.fromhex('deadbeef'), + 'ClientId_2': bytes.fromhex('cafed00d') + } + def callback(identity): + return psk_table[identity] + context.set_psk_server_callback(callback, 'ServerId_1') + + .. versionadded:: 3.12 + .. index:: single: certificates .. index:: single: X509 certificate diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 14dfd9ea5823ed..5a95897876b8f5 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -815,6 +815,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call_exception_handler)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call_soon)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(callback)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cancel)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(capath)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(category)); @@ -954,6 +955,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hook)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(id)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ident)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(identity_hint)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ignore)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(imag)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(importlib)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 6f430bb25eb8d3..88b29749d5e51f 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -301,6 +301,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(call) STRUCT_FOR_ID(call_exception_handler) STRUCT_FOR_ID(call_soon) + STRUCT_FOR_ID(callback) STRUCT_FOR_ID(cancel) STRUCT_FOR_ID(capath) STRUCT_FOR_ID(category) @@ -440,6 +441,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(hook) STRUCT_FOR_ID(id) STRUCT_FOR_ID(ident) + STRUCT_FOR_ID(identity_hint) STRUCT_FOR_ID(ignore) STRUCT_FOR_ID(imag) STRUCT_FOR_ID(importlib) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 0452c4c61551de..4f3442bf9ed504 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -807,6 +807,7 @@ extern "C" { INIT_ID(call), \ INIT_ID(call_exception_handler), \ INIT_ID(call_soon), \ + INIT_ID(callback), \ INIT_ID(cancel), \ INIT_ID(capath), \ INIT_ID(category), \ @@ -946,6 +947,7 @@ extern "C" { INIT_ID(hook), \ INIT_ID(id), \ INIT_ID(ident), \ + INIT_ID(identity_hint), \ INIT_ID(ignore), \ INIT_ID(imag), \ INIT_ID(importlib), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 7114a5416f2515..a0fefb8b67bdf8 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -756,6 +756,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(call_soon); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(callback); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(cancel); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); @@ -1173,6 +1176,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(ident); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(identity_hint); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(ignore); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index abf024fb89d2f3..e200cda81f17f5 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4203,6 +4203,62 @@ def test_session_handling(self): self.assertEqual(str(e.exception), 'Session refers to a different SSLContext.') + @requires_tls_version('TLSv1_2') + def test_psk(self): + psk = bytes.fromhex('deadbeef') + + client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + client_context.check_hostname = False + client_context.verify_mode = ssl.CERT_NONE + client_context.maximum_version = ssl.TLSVersion.TLSv1_2 + client_context.set_ciphers('PSK') + client_context.set_psk_client_callback(lambda hint: (None, psk)) + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_context.maximum_version = ssl.TLSVersion.TLSv1_2 + server_context.set_ciphers('PSK') + server_context.set_psk_server_callback(lambda identity: psk) + + # correct PSK should connect + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + + # incorrect PSK should fail + incorrect_psk = bytes.fromhex('cafebabe') + client_context.set_psk_client_callback(lambda hint: (None, incorrect_psk)) + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + with self.assertRaises(ssl.SSLError): + s.connect((HOST, server.port)) + + # identity_hint and client_identity should be sent to the other side + identity_hint = 'identity-hint' + client_identity = 'client-identity' + + def client_callback(hint): + self.assertEqual(hint, identity_hint) + return client_identity, psk + + def server_callback(identity): + self.assertEqual(identity, client_identity) + return psk + + client_context.set_psk_client_callback(client_callback) + 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)) + + # adding client callback to server or vice versa raises an exception + with self.assertRaisesRegex(ssl.SSLError, 'Cannot add PSK server callback'): + client_context.set_psk_server_callback(server_callback, identity_hint) + with self.assertRaisesRegex(ssl.SSLError, 'Cannot add PSK client callback'): + server_context.set_psk_client_callback(client_callback) + @unittest.skipUnless(has_tls_version('TLSv1_3'), "Test needs TLS 1.3") class TestPostHandshakeAuth(unittest.TestCase): diff --git a/Misc/ACKS b/Misc/ACKS index 8cf5166a2bb1f4..75df0886f05955 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1466,6 +1466,7 @@ Ajith Ramachandran Dhushyanth Ramasamy Ashwin Ramaswami Jeff Ramnani +Grant Ramsay Bayard Randel Varpu Rantala Brodie Rao diff --git a/Misc/NEWS.d/next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst b/Misc/NEWS.d/next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst new file mode 100644 index 00000000000000..d61f56df68dbe7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst @@ -0,0 +1 @@ +Added support for TLS-PSK (pre-shared key) to the ssl module diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 3fbb37332f67d3..38d27c3473805d 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -294,6 +294,8 @@ typedef struct { BIO *keylog_bio; /* Cached module state, also used in SSLSocket and SSLSession code. */ _sslmodulestate *state; + PyObject *psk_client_callback; + PyObject *psk_server_callback; } PySSLContext; typedef struct { @@ -3088,6 +3090,8 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) self->alpn_protocols = NULL; self->set_sni_cb = NULL; self->state = get_ssl_state(module); + self->psk_client_callback = NULL; + self->psk_server_callback = NULL; /* Don't check host name by default */ if (proto_version == PY_SSL_VERSION_TLS_CLIENT) { @@ -4614,6 +4618,202 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form) return NULL; } +static unsigned int psk_client_callback(SSL *s, + const char *hint, + char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len) +{ + PyGILState_STATE gstate = PyGILState_Ensure(); + + PySSLSocket *ssl = SSL_get_app_data(s); + if (ssl == NULL || ssl->ctx == NULL) { + goto error; + } + PyObject *callback = ssl->ctx->psk_client_callback; + if (callback == NULL) { + goto error; + } + + PyObject *hint_str = (hint != NULL) ? + PyUnicode_DecodeASCII(hint, strlen(hint), "strict") : + Py_NewRef(Py_None); + if (hint_str == NULL) { + goto error; + } + PyObject *result = PyObject_CallFunctionObjArgs(callback, hint_str, NULL); + Py_DECREF(hint_str); + + if (result == NULL) { + goto error; + } + + const char *psk_; + const char *identity_; + Py_ssize_t psk_len_; + Py_ssize_t identity_len_; + if (!PyArg_ParseTuple(result, "z#y#", &identity_, &identity_len_, &psk_, &psk_len_)) { + Py_DECREF(result); + goto error; + } + + if (identity_len_ + 1 > max_identity_len || psk_len_ > max_psk_len) { + Py_DECREF(result); + goto error; + } + memcpy(psk, psk_, psk_len_); + memcpy(identity, identity_, identity_len_); + identity[identity_len_] = 0; + + Py_DECREF(result); + + PyGILState_Release(gstate); + return (unsigned int)psk_len_; + +error: + PyGILState_Release(gstate); + return 0; +} + +/*[clinic input] +_ssl._SSLContext.set_psk_client_callback + callback: object + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_set_psk_client_callback_impl(PySSLContext *self, + PyObject *callback) +/*[clinic end generated code: output=0aba86f6ed75119e input=7627bae0e5ee7635]*/ +{ + if (self->protocol == PY_SSL_VERSION_TLS_SERVER) { + _setSSLError(get_state_ctx(self), + "Cannot add PSK client callback to a " + "PROTOCOL_TLS_SERVER context", 0, __FILE__, __LINE__); + return NULL; + } + + SSL_psk_client_cb_func ssl_callback; + if (callback == Py_None) { + callback = NULL; + // Delete the existing callback + ssl_callback = NULL; + } else { + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "callback must be callable"); + return NULL; + } + Py_INCREF(callback); + ssl_callback = psk_client_callback; + } + + if (self->psk_client_callback != NULL) { + Py_DECREF(self->psk_client_callback); + } + self->psk_client_callback = callback; + SSL_CTX_set_psk_client_callback(self->ctx, ssl_callback); + + Py_RETURN_NONE; +} + +static unsigned int psk_server_callback(SSL *s, + const char *identity, + unsigned char *psk, + unsigned int max_psk_len) +{ + PyGILState_STATE gstate = PyGILState_Ensure(); + + PySSLSocket *ssl = SSL_get_app_data(s); + if (ssl == NULL || ssl->ctx == NULL) { + goto error; + } + PyObject *callback = ssl->ctx->psk_server_callback; + if (callback == NULL) { + goto error; + } + + PyObject *identity_str = (identity[0] != '\0') ? + PyUnicode_DecodeASCII(identity, strlen(identity), "strict") : + Py_NewRef(Py_None); + if (identity_str == NULL) { + goto error; + } + PyObject *result = PyObject_CallFunctionObjArgs(callback, identity_str, NULL); + Py_DECREF(identity_str); + + if (result == NULL) { + goto error; + } + + const char *psk_; + Py_ssize_t psk_len_; + if (!PyArg_Parse(result, "y#", &psk_, &psk_len_)) { + Py_DECREF(result); + goto error; + } + + if (psk_len_ > max_psk_len) { + Py_DECREF(result); + goto error; + } + memcpy(psk, psk_, psk_len_); + + Py_DECREF(result); + + PyGILState_Release(gstate); + return (unsigned int)psk_len_; + +error: + PyGILState_Release(gstate); + return 0; +} + +/*[clinic input] +_ssl._SSLContext.set_psk_server_callback + callback: object + identity_hint: str(accept={str, NoneType}) = None + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_set_psk_server_callback_impl(PySSLContext *self, + PyObject *callback, + const char *identity_hint) +/*[clinic end generated code: output=1f4d6a4e09a92b03 input=65d4b6022aa85ea3]*/ +{ + if (self->protocol == PY_SSL_VERSION_TLS_CLIENT) { + _setSSLError(get_state_ctx(self), + "Cannot add PSK server callback to a " + "PROTOCOL_TLS_CLIENT context", 0, __FILE__, __LINE__); + return NULL; + } + + SSL_psk_server_cb_func ssl_callback; + if (callback == Py_None) { + callback = NULL; + // Delete the existing callback and hint + ssl_callback = NULL; + identity_hint = NULL; + } else { + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "callback must be callable"); + return NULL; + } + Py_INCREF(callback); + ssl_callback = psk_server_callback; + } + + if (self->psk_server_callback != NULL) { + Py_DECREF(self->psk_server_callback); + } + self->psk_server_callback = callback; + SSL_CTX_set_psk_server_callback(self->ctx, ssl_callback); + SSL_CTX_use_psk_identity_hint(self->ctx, identity_hint); + + Py_RETURN_NONE; +} + static PyGetSetDef context_getsetlist[] = { {"check_hostname", (getter) get_check_hostname, @@ -4668,6 +4868,8 @@ static struct PyMethodDef context_methods[] = { _SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF _SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF + _SSL__SSLCONTEXT_SET_PSK_CLIENT_CALLBACK_METHODDEF + _SSL__SSLCONTEXT_SET_PSK_SERVER_CALLBACK_METHODDEF {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 9f967ddc8e3061..c4c226b33934aa 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -1021,6 +1021,141 @@ _ssl__SSLContext_get_ca_certs(PySSLContext *self, PyObject *const *args, Py_ssiz return return_value; } +PyDoc_STRVAR(_ssl__SSLContext_set_psk_client_callback__doc__, +"set_psk_client_callback($self, /, callback)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_SET_PSK_CLIENT_CALLBACK_METHODDEF \ + {"set_psk_client_callback", _PyCFunction_CAST(_ssl__SSLContext_set_psk_client_callback), METH_FASTCALL|METH_KEYWORDS, _ssl__SSLContext_set_psk_client_callback__doc__}, + +static PyObject * +_ssl__SSLContext_set_psk_client_callback_impl(PySSLContext *self, + PyObject *callback); + +static PyObject * +_ssl__SSLContext_set_psk_client_callback(PySSLContext *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(callback), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"callback", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "set_psk_client_callback", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *callback; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + callback = args[0]; + return_value = _ssl__SSLContext_set_psk_client_callback_impl(self, callback); + +exit: + return return_value; +} + +PyDoc_STRVAR(_ssl__SSLContext_set_psk_server_callback__doc__, +"set_psk_server_callback($self, /, callback, identity_hint=None)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_SET_PSK_SERVER_CALLBACK_METHODDEF \ + {"set_psk_server_callback", _PyCFunction_CAST(_ssl__SSLContext_set_psk_server_callback), METH_FASTCALL|METH_KEYWORDS, _ssl__SSLContext_set_psk_server_callback__doc__}, + +static PyObject * +_ssl__SSLContext_set_psk_server_callback_impl(PySSLContext *self, + PyObject *callback, + const char *identity_hint); + +static PyObject * +_ssl__SSLContext_set_psk_server_callback(PySSLContext *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(callback), &_Py_ID(identity_hint), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"callback", "identity_hint", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "set_psk_server_callback", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *callback; + const char *identity_hint = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + if (!args) { + goto exit; + } + callback = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1] == Py_None) { + identity_hint = NULL; + } + else if (PyUnicode_Check(args[1])) { + Py_ssize_t identity_hint_length; + identity_hint = PyUnicode_AsUTF8AndSize(args[1], &identity_hint_length); + if (identity_hint == NULL) { + goto exit; + } + if (strlen(identity_hint) != (size_t)identity_hint_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + } + else { + _PyArg_BadArgument("set_psk_server_callback", "argument 'identity_hint'", "str or None", args[1]); + goto exit; + } +skip_optional_pos: + return_value = _ssl__SSLContext_set_psk_server_callback_impl(self, callback, identity_hint); + +exit: + return return_value; +} + static PyObject * _ssl_MemoryBIO_impl(PyTypeObject *type); @@ -1542,4 +1677,4 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=4d9b81fa81f520f0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7840abea7709dfa8 input=a9049054013a1b77]*/ From cfddf7b858716c7dc1520e38c29d5cb6773a4ba2 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Mon, 3 Apr 2023 16:33:27 +1200 Subject: [PATCH 02/18] Fix TLS-PSK for TLS 1.3 SSL_CTX_set_session_id_context() is a server-side only operation. Using this on the client-side is causing authentication errors --- Doc/library/ssl.rst | 9 +++++++++ Lib/test/test_ssl.py | 32 ++++++++++++++++++++++++++++++++ Modules/_ssl.c | 8 ++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index bd1713890198b7..10b06a7d0679c6 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -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`. + - client-identity must be a non-empty string. + Example usage:: context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) @@ -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) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index e200cda81f17f5..427ec45573dd82 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -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): diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 38d27c3473805d..baacdce0c804ac 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -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 From 18302e3c46a10757c82f34c7c7c4310addc98f83 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Mon, 5 Jun 2023 09:47:47 +1200 Subject: [PATCH 03/18] Decode TLS-PSK identities as UTF-8 rather than ASCII RFC4279 states these are UTF-8. Add unit test using non-ASCII chars --- Lib/test/test_ssl.py | 11 +++++++++++ Modules/_ssl.c | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 19ceaa3c4d50f2..6dacaa5ab5ce04 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4259,6 +4259,17 @@ def server_callback(identity): with self.assertRaisesRegex(ssl.SSLError, 'Cannot add PSK client callback'): server_context.set_psk_client_callback(client_callback) + # test with UTF-8 identities + identity_hint = '身份暗示' # Translation: "Identity hint" + client_identity = '客户身份' # Translation: "Customer identity" + + client_context.set_psk_client_callback(client_callback) + 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)) + @requires_tls_version('TLSv1_3') def test_psk_tls1_3(self): psk = bytes.fromhex('deadbeef') diff --git a/Modules/_ssl.c b/Modules/_ssl.c index ec1c98d4344e75..102be718022dd2 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4646,7 +4646,7 @@ static unsigned int psk_client_callback(SSL *s, } PyObject *hint_str = (hint != NULL) ? - PyUnicode_DecodeASCII(hint, strlen(hint), "strict") : + PyUnicode_DecodeUTF8(hint, strlen(hint), "strict") : Py_NewRef(Py_None); if (hint_str == NULL) { goto error; @@ -4743,7 +4743,7 @@ static unsigned int psk_server_callback(SSL *s, } PyObject *identity_str = (identity[0] != '\0') ? - PyUnicode_DecodeASCII(identity, strlen(identity), "strict") : + PyUnicode_DecodeUTF8(identity, strlen(identity), "strict") : Py_NewRef(Py_None); if (identity_str == NULL) { goto error; From 2efc8767cf6b531cdb5341f7ede4bf92e9625cb8 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Mon, 5 Jun 2023 09:49:12 +1200 Subject: [PATCH 04/18] Change TLS-PSK version added from 3.12 to 3.13 The PR has missed the 3.12 merge window --- Doc/library/ssl.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 9234c02b24c687..31d6ff979a4d37 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -2029,7 +2029,7 @@ to speed up repeated connections from the same clients. return 'ClientId_1', psk_table[hint] context.set_psk_client_callback(callback) - .. versionadded:: 3.12 + .. versionadded:: 3.13 .. method:: SSLContext.set_psk_server_callback(callback, identity_hint=None) @@ -2068,7 +2068,7 @@ to speed up repeated connections from the same clients. return psk_table[identity] context.set_psk_server_callback(callback, 'ServerId_1') - .. versionadded:: 3.12 + .. versionadded:: 3.13 .. index:: single: certificates From a56784ceb018721530b9427cbf2aa6904d3bedce Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 4 Jun 2023 20:39:53 -0700 Subject: [PATCH 05/18] ReSTify NEWS. --- .../next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst b/Misc/NEWS.d/next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst index d61f56df68dbe7..abb57dccd5a91a 100644 --- a/Misc/NEWS.d/next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst +++ b/Misc/NEWS.d/next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst @@ -1 +1 @@ -Added support for TLS-PSK (pre-shared key) to the ssl module +Added support for TLS-PSK (pre-shared key) mode to the :mod:`ssl` module. From 6f4100c3e466e49e12053240e31c70ee664860bc Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sun, 16 Jul 2023 10:40:57 +1200 Subject: [PATCH 06/18] Set python exceptions raised during C callbacks as unraisable --- Modules/_ssl.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 81b32ce2cde375..32613f9737e0e6 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4653,12 +4653,13 @@ static unsigned int psk_client_callback(SSL *s, unsigned int max_psk_len) { PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject *callback = NULL; PySSLSocket *ssl = SSL_get_app_data(s); if (ssl == NULL || ssl->ctx == NULL) { goto error; } - PyObject *callback = ssl->ctx->psk_client_callback; + callback = ssl->ctx->psk_client_callback; if (callback == NULL) { goto error; } @@ -4699,6 +4700,9 @@ static unsigned int psk_client_callback(SSL *s, return (unsigned int)psk_len_; error: + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(callback); + } PyGILState_Release(gstate); return 0; } @@ -4750,12 +4754,13 @@ static unsigned int psk_server_callback(SSL *s, unsigned int max_psk_len) { PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject *callback = NULL; PySSLSocket *ssl = SSL_get_app_data(s); if (ssl == NULL || ssl->ctx == NULL) { goto error; } - PyObject *callback = ssl->ctx->psk_server_callback; + callback = ssl->ctx->psk_server_callback; if (callback == NULL) { goto error; } @@ -4792,6 +4797,9 @@ static unsigned int psk_server_callback(SSL *s, return (unsigned int)psk_len_; error: + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(callback); + } PyGILState_Release(gstate); return 0; } From e4a97ecbc89b89f01118f55e52dc9618b030dd66 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sun, 16 Jul 2023 10:42:22 +1200 Subject: [PATCH 07/18] Add NULL check for defensive coding --- Modules/_ssl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 32613f9737e0e6..f9df7665efccd9 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4664,7 +4664,7 @@ static unsigned int psk_client_callback(SSL *s, goto error; } - PyObject *hint_str = (hint != NULL) ? + PyObject *hint_str = (hint != NULL && hint[0] != '\0') ? PyUnicode_DecodeUTF8(hint, strlen(hint), "strict") : Py_NewRef(Py_None); if (hint_str == NULL) { @@ -4765,7 +4765,7 @@ static unsigned int psk_server_callback(SSL *s, goto error; } - PyObject *identity_str = (identity[0] != '\0') ? + PyObject *identity_str = (identity != NULL && identity[0] != '\0') ? PyUnicode_DecodeUTF8(identity, strlen(identity), "strict") : Py_NewRef(Py_None); if (identity_str == NULL) { From 3d75982fdba8a112560974516d1a3bfacf2cc38a Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sun, 16 Jul 2023 10:44:59 +1200 Subject: [PATCH 08/18] Get single value using PyBytes_AsStringAndSize There is no need to use PyArg_Parse in this simple case --- Modules/_ssl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index f9df7665efccd9..fb7832b3f3d45e 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4778,9 +4778,9 @@ static unsigned int psk_server_callback(SSL *s, goto error; } - const char *psk_; + char *psk_; Py_ssize_t psk_len_; - if (!PyArg_Parse(result, "y#", &psk_, &psk_len_)) { + if (PyBytes_AsStringAndSize(result, &psk_, &psk_len_) < 0) { Py_DECREF(result); goto error; } From 539ed1fc04da50d031f62c262c99a0117320e8af Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sun, 16 Jul 2023 15:44:23 +1200 Subject: [PATCH 09/18] Do not raise a decode exception if remote side sends invalid UTF-8 during TLS-PSK negotiation In this case the remote side is breaking the standard (rfc4279). Just drop the connection --- Modules/_ssl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index fb7832b3f3d45e..a25482fcb81d75 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4668,6 +4668,10 @@ static unsigned int psk_client_callback(SSL *s, PyUnicode_DecodeUTF8(hint, strlen(hint), "strict") : Py_NewRef(Py_None); if (hint_str == NULL) { + /* The remote side has sent an invalid UTF-8 string + * (breaking the standard), drop the connection without + * raising a decode exception. */ + PyErr_Clear(); goto error; } PyObject *result = PyObject_CallFunctionObjArgs(callback, hint_str, NULL); @@ -4769,6 +4773,10 @@ static unsigned int psk_server_callback(SSL *s, PyUnicode_DecodeUTF8(identity, strlen(identity), "strict") : Py_NewRef(Py_None); if (identity_str == NULL) { + /* The remote side has sent an invalid UTF-8 string + * (breaking the standard), drop the connection without + * raising a decode exception. */ + PyErr_Clear(); goto error; } PyObject *result = PyObject_CallFunctionObjArgs(callback, identity_str, NULL); From 4634fc300572e6941dd68b7a6b3311707758d821 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sun, 16 Jul 2023 15:45:13 +1200 Subject: [PATCH 10/18] Free TLS-PSK callbacks when SSL context is deallocated --- Modules/_ssl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index a25482fcb81d75..dc6f88c018eebf 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -3232,6 +3232,8 @@ context_clear(PySSLContext *self) Py_CLEAR(self->set_sni_cb); Py_CLEAR(self->msg_cb); Py_CLEAR(self->keylog_filename); + Py_CLEAR(self->psk_client_callback); + Py_CLEAR(self->psk_server_callback); if (self->keylog_bio != NULL) { PySSL_BEGIN_ALLOW_THREADS BIO_free_all(self->keylog_bio); From d21c322bb4226f7c3683ecc53a8e91f3f2019215 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sun, 16 Jul 2023 15:46:25 +1200 Subject: [PATCH 11/18] Use Py_XINCREF and Py_XDECREF for tidier NULL checking --- Modules/_ssl.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index dc6f88c018eebf..af8ba7f5f5bde1 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4741,13 +4741,12 @@ _ssl__SSLContext_set_psk_client_callback_impl(PySSLContext *self, PyErr_SetString(PyExc_TypeError, "callback must be callable"); return NULL; } - Py_INCREF(callback); ssl_callback = psk_client_callback; } - if (self->psk_client_callback != NULL) { - Py_DECREF(self->psk_client_callback); - } + Py_XDECREF(self->psk_client_callback); + Py_XINCREF(callback); + self->psk_client_callback = callback; SSL_CTX_set_psk_client_callback(self->ctx, ssl_callback); @@ -4845,13 +4844,12 @@ _ssl__SSLContext_set_psk_server_callback_impl(PySSLContext *self, PyErr_SetString(PyExc_TypeError, "callback must be callable"); return NULL; } - Py_INCREF(callback); ssl_callback = psk_server_callback; } - if (self->psk_server_callback != NULL) { - Py_DECREF(self->psk_server_callback); - } + Py_XDECREF(self->psk_server_callback); + Py_XINCREF(callback); + self->psk_server_callback = callback; SSL_CTX_set_psk_server_callback(self->ctx, ssl_callback); SSL_CTX_use_psk_identity_hint(self->ctx, identity_hint); From 03fac4ddc7c42231e5d0e6c79af8db2274a0ece8 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sun, 16 Jul 2023 15:47:08 +1200 Subject: [PATCH 12/18] Check return value of SSL_CTX_use_psk_identity_hint --- Modules/_ssl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index af8ba7f5f5bde1..d3e10436112726 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4847,12 +4847,16 @@ _ssl__SSLContext_set_psk_server_callback_impl(PySSLContext *self, ssl_callback = psk_server_callback; } + if (SSL_CTX_use_psk_identity_hint(self->ctx, identity_hint) != 1) { + PyErr_SetString(PyExc_ValueError, "failed to set identity hint"); + return NULL; + } + Py_XDECREF(self->psk_server_callback); Py_XINCREF(callback); self->psk_server_callback = callback; SSL_CTX_set_psk_server_callback(self->ctx, ssl_callback); - SSL_CTX_use_psk_identity_hint(self->ctx, identity_hint); Py_RETURN_NONE; } From 3db3921e29121fb62930469a608d1bc171831842 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sun, 16 Jul 2023 16:05:48 +1200 Subject: [PATCH 13/18] Update TLS-PSK documentation Add length limits of identity and hint parameters. Add how to reject a connection --- Doc/library/ssl.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 31d6ff979a4d37..b40b459847eab8 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1998,8 +1998,10 @@ to speed up repeated connections from the same clients. The ``hint`` parameter is an optional identity hint sent by the server. The return value is a tuple in the form (client-identity, psk). Client-identity is an optional string which may be used by the server to - select a corresponding PSK for the client. PSK is a - :term:`bytes-like object` representing the pre-shared key. + select a corresponding PSK for the client. The string must be less than or + equal to ``256`` octets when UTF-8 encoded. PSK is a + :term:`bytes-like object` representing the pre-shared key. Return a zero + length PSK to reject the connection. Setting ``callback`` to :const:`None` removes any existing callback. @@ -2042,10 +2044,13 @@ to speed up repeated connections from the same clients. The ``identity`` parameter is an optional identity sent by the client which can be used to select a corresponding PSK. The return value is a :term:`bytes-like object` representing the pre-shared key. + Return a zero length PSK to reject the connection. Setting ``callback`` to :const:`None` removes any existing callback. - The parameter ``identity_hint`` is an optional identity hint sent to the client. + The parameter ``identity_hint`` is an optional identity hint string sent to + the client. The string must be less than or equal to ``256`` octets when + UTF-8 encoded. .. note:: When using TLS 1.3 the ``identity_hint`` parameter is not sent to the client. From a75f3a70d69268ab0275d147d7cf189659d77321 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sun, 16 Jul 2023 16:09:46 +1200 Subject: [PATCH 14/18] Regenerate news entry to update the timestamp --- ...4.oMYuon.rst => 2023-07-16-16-08-06.gh-issue-63284.q2Qi9q.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/Library/{2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst => 2023-07-16-16-08-06.gh-issue-63284.q2Qi9q.rst} (100%) diff --git a/Misc/NEWS.d/next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst b/Misc/NEWS.d/next/Library/2023-07-16-16-08-06.gh-issue-63284.q2Qi9q.rst similarity index 100% rename from Misc/NEWS.d/next/Library/2023-04-02-11-40-59.gh-issue-63284.oMYuon.rst rename to Misc/NEWS.d/next/Library/2023-07-16-16-08-06.gh-issue-63284.q2Qi9q.rst From 4c6897459860f89faaa1902781e6f2619f25ffdb Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Sun, 26 Nov 2023 19:31:43 +0000 Subject: [PATCH 15/18] Update example key strings in the doc. --- Doc/library/ssl.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 8840d108045a4e..fe7e315bf3c948 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -2039,12 +2039,12 @@ to speed up repeated connections from the same clients. context.set_ciphers('PSK') # A simple lambda: - psk = bytes.fromhex('deadbeef') + psk = bytes.fromhex('c0ffee') context.set_psk_client_callback(lambda hint: (None, psk)) # A table using the hint from the server: - psk_table = { 'ServerId_1': bytes.fromhex('deadbeef'), - 'ServerId_2': bytes.fromhex('cafebabe') + psk_table = { 'ServerId_1': bytes.fromhex('c0ffee'), + 'ServerId_2': bytes.fromhex('facade') } def callback(hint): return 'ClientId_1', psk_table[hint] @@ -2081,12 +2081,12 @@ to speed up repeated connections from the same clients. context.set_ciphers('PSK') # A simple lambda: - psk = bytes.fromhex('deadbeef') + psk = bytes.fromhex('c0ffee') context.set_psk_server_callback(lambda identity: psk) # A table using the identity of the client: - psk_table = { 'ClientId_1': bytes.fromhex('deadbeef'), - 'ClientId_2': bytes.fromhex('cafed00d') + psk_table = { 'ClientId_1': bytes.fromhex('c0ffee'), + 'ClientId_2': bytes.fromhex('facade') } def callback(identity): return psk_table[identity] From 0fb8a3e82b9c510c4795850cbc4ef27fe351c77a Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Mon, 27 Nov 2023 12:49:37 +1300 Subject: [PATCH 16/18] Update TLS-PSK documentation Demonstrate usage in a safer way that rejects unknown connection attempts --- Doc/library/ssl.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 8840d108045a4e..c9730a5069ec59 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -2047,7 +2047,7 @@ to speed up repeated connections from the same clients. 'ServerId_2': bytes.fromhex('cafebabe') } def callback(hint): - return 'ClientId_1', psk_table[hint] + return 'ClientId_1', psk_table.get(hint, b'') context.set_psk_client_callback(callback) .. versionadded:: 3.13 @@ -2089,7 +2089,7 @@ to speed up repeated connections from the same clients. 'ClientId_2': bytes.fromhex('cafed00d') } def callback(identity): - return psk_table[identity] + return psk_table.get(identity, b'') context.set_psk_server_callback(callback, 'ServerId_1') .. versionadded:: 3.13 From 77885893b0d9ece36b00fa49930c2743cdb81113 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Mon, 27 Nov 2023 12:50:14 +1300 Subject: [PATCH 17/18] Regenerate news entry to update the timestamp --- ...4.q2Qi9q.rst => 2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/Library/{2023-07-16-16-08-06.gh-issue-63284.q2Qi9q.rst => 2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst} (100%) diff --git a/Misc/NEWS.d/next/Library/2023-07-16-16-08-06.gh-issue-63284.q2Qi9q.rst b/Misc/NEWS.d/next/Library/2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst similarity index 100% rename from Misc/NEWS.d/next/Library/2023-07-16-16-08-06.gh-issue-63284.q2Qi9q.rst rename to Misc/NEWS.d/next/Library/2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst From 11735b997952530c922954ff07db688aad1dcbe6 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith [Google LLC]" Date: Mon, 27 Nov 2023 03:37:52 +0000 Subject: [PATCH 18/18] Code defensively around `z#` vs NULL and 0 length. --- Modules/_ssl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 010f5f5196746b..707e7ad9543acb 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4707,7 +4707,7 @@ static unsigned int psk_client_callback(SSL *s, const char *psk_; const char *identity_; Py_ssize_t psk_len_; - Py_ssize_t identity_len_; + Py_ssize_t identity_len_ = 0; if (!PyArg_ParseTuple(result, "z#y#", &identity_, &identity_len_, &psk_, &psk_len_)) { Py_DECREF(result); goto error; @@ -4718,7 +4718,9 @@ static unsigned int psk_client_callback(SSL *s, goto error; } memcpy(psk, psk_, psk_len_); - memcpy(identity, identity_, identity_len_); + if (identity_ != NULL) { + memcpy(identity, identity_, identity_len_); + } identity[identity_len_] = 0; Py_DECREF(result); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy