-
-
Notifications
You must be signed in to change notification settings - Fork 30.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add support for tls/ssl sessions in asyncio #79152
Comments
cpython has had TLS session support since 3.6, using the SSLContext.wrap_* methods. Unfortunately, this support is not available when using asyncio's create_connection. While I've managed to monkeypatch asyncio.sslproto._SSLPipe from my own code (it's a filthy hack but it's short and it gets the job done) running on 3.6.6, I feel this should be properly supported out of the box. A patch is ready (tests work), a github PR will be created shortly. Notes in no particular order:
TIA for considering this bug and patch |
TLS session support is awesome. IFAIK ssl_proto.py is under heavy reconstruction now. |
Hi Andrew, How should I proceed? What's the best avenue to get in touch with Yuri? Thanks |
Don't use the existing session feature, yet. It only works for TLS 1.2 connections. TLS 1.3 behaves differently. There are multiple session tickets (usually two) and tickets are sent after handshake. Further more, Python lacks clear shutdown of a connection, which causes further problems with session handling. See https://www.openssl.org/docs/manmaster/man3/SSL_get_session.html |
Hi Christian, Could you tell me more about this new openssl API? Right now my patch works with whatever the ssl module provides. Are you suggesting the ssl module is in some way incomplete? Would supporting TLS 1.3 sessions be incompatible with the current session API? I'd like to help wherever possible, but I'm probably missing some context and/or knowledge around all things TLS in cpython. Thanks |
The session code of the ssl is not compatible with TLS 1.3. Actually the whole API doesn't work with TLS 1.3. In TLS 1.2 and before, sessions had multiple security implications. For example they break PFS. TLS 1.3 changed when sessions are exchanged and how session are resumed. Session data is no longer part of the handshake. Instead the server can send session tickets at any point after the handshake. A server can send multiple tickets (usually two) and tickets must only be reused once. |
So, IOW, the ssl module needs a good shakeup wrt TLS 1.3 sessions before any asyncio work can be merged. Am I getting this right? In which case, a whole other issue/PR is needed and possibly better folks than me. I try to stay clear of low-level crypto APIs because I don't trust myself to get things right. Well… I certainly can look at it, but I fear I may be punching above my weight with this. |
Christian, do you think the sessions support shouldn't be added to asyncio in 3.8? |
Anything I can do to get the ball rolling? Let me know who to get in touch with and *how*, and I will. Thanks |
A quick solution while SSL sessions aren't officially supported import ssl
import socket
import asyncio
class SSLSessionBoundContext:
"""ssl.SSLContext bound to an existing SSL session.
Actually asyncio doesn't support TLS session resumption, the loop.create_connection() API
does not take any TLS session related argument. There is ongoing work to add support for this
at https://github.com/python/cpython/issues/79152.
The loop.create_connection() API takes a SSL context argument though, the SSLSessionBoundContext
is used to wrap a SSL context and inject a SSL session on calls to
- SSLSessionBoundContext.wrap_socket()
- SSLSessionBoundContext.wrap_bio()
This wrapper is compatible with any TLS application which calls only the methods above when
making new TLS connections. This class is NOT a subclass of ssl.SSLContext, so it will be
rejected by applications which ensure the SSL context is an instance of ssl.SSLContext. Not being
a subclass of ssl.SSLContext makes this wrapper lightweight.
"""
__slots__ = ('_context', '_session')
def __init__(self, context: ssl.SSLContext, session: ssl.SSLSession):
self._context = context
self._session = session
@property
def session(self):
return self._session
def wrap_socket(
self,
sock: socket.socket,
server_side: bool=False,
do_handshake_on_connect: bool=True,
suppress_ragged_eofs: bool=True,
server_hostname: bool=None,
session: ssl.SSLSession=None
) -> ssl.SSLSocket:
if session is not None:
raise ValueError('expected session to be None')
return self._context.wrap_socket(
sock=sock,
server_hostname=server_hostname,
server_side=server_side,
do_handshake_on_connect=do_handshake_on_connect,
suppress_ragged_eofs=suppress_ragged_eofs,
session=self._session
)
def wrap_bio(
self,
incoming: ssl.MemoryBIO,
outgoing: ssl.MemoryBIO,
server_side: bool=False,
server_hostname: bool=None,
session: ssl.SSLSession=None
) -> ssl.SSLObject:
if session is not None:
raise ValueError('expected session to be None')
return self._context.wrap_bio(
incoming=incoming,
outgoing=outgoing,
server_hostname=server_hostname,
server_side=server_side,
session=self._session
)
async def test():
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
_, writer = await asyncio.open_connection('docs.python.org', 443, ssl=context)
sslobj = writer.transport.get_extra_info('ssl_object')
session = sslobj.session
writer.close()
await writer.wait_closed()
# try to reuse session
_, writer = await asyncio.open_connection(
'docs.python.org',
443,
ssl=SSLSessionBoundContext(context, session)
)
sslobj = writer.transport.get_extra_info('ssl_object')
print('Session reused:', sslobj.session_reused)
writer.close()
await writer.wait_closed()
asyncio.run(test()) Note that it just makes asyncio use an existing SSL session, this solution doesn't show how to correctly handle sessions as by the TLS spec. The standard SSL module still lacks needed bindings to handle TLS1.3 sessions, but from what I have seen |
I think so. Closing everything in sight. |
Sorry, but I can't see how gh-79156 supersedes this issue. From what I understood that other issue is about TLS upgrade on a stream, not about TLS session resumption. Under the hood |
Okay, I admit it -- I don't understand anything about TLS, and I have no idea what "TLS session" means (nor do I have any desire to learn about all of this). I'll reopen the issue. Sorry! |
Let's not extend our tls APIs of |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: