You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SSLSocket.shared_ciphers() does not document None is returned on session reuse
Summary
The fix of #96931 resulted in a change in SSLSocket.shared_ciphers().
If the session is reused, SSLSocket.shared_ciphers() returns None
Proposal: update documentation of SSLSocket.shared_ciphers() to note None is returned on session reuse.
Background & Motivation
As an example, here is a sample server.py and client.py scripts to show the behavior change in Python 3.11.2 and 3.11.3:
# client.pyimportsocketimportsslport=12345"""Use TLS 1.2 so session ticket is sent.https://docs.python.org/3/library/ssl.html#ssl-session describes:> Session tickets are no longer sent as part of the initial handshake and are handled differently. SSLSocket.session and SSLSession are not compatible with TLS 1.3."""context=ssl.SSLContext(protocol=ssl.PROTOCOL_TLSv1_2)
context.load_verify_locations(cafile="ca.pem")
conn: ssl.SSLSocket=context.wrap_socket(socket.socket(socket.AF_INET),
server_hostname="localhost")
conn.connect(("localhost", port))
conn.write(b"foo")
assertnotconn.session_reusedsession=conn.sessionconn.close()
# Connect again and reuse the session.conn=context.wrap_socket(socket.socket(socket.AF_INET),
server_hostname="localhost",
session=session)
conn.connect(("localhost", port))
conn.write(b"foo")
assertconn.session_reusedconn.close()
Here is the output of server.py on Python 3.11.2:
Python version: 3.11.2
server listening on port 12345
server got connection on address: ('127.0.0.1', 64310)
server shared ciphers: [('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256), ('TLS_CHACHA20_POLY1305_SHA256', 'TLSv1.3', 256), ('TLS_AES_128_GCM_SHA256', 'TLSv1.3', 128), ('ECDHE-ECDSA-AES256-GCM-SHA384', 'TLSv1.2', 256), ('ECDHE-RSA-AES256-GCM-SHA384', 'TLSv1.2', 256), ('ECDHE-ECDSA-AES128-GCM-SHA256', 'TLSv1.2', 128), ('ECDHE-RSA-AES128-GCM-SHA256', 'TLSv1.2', 128), ('ECDHE-ECDSA-CHACHA20-POLY1305', 'TLSv1.2', 256), ('ECDHE-RSA-CHACHA20-POLY1305', 'TLSv1.2', 256), ('ECDHE-ECDSA-AES256-SHA384', 'TLSv1.2', 256), ('ECDHE-RSA-AES256-SHA384', 'TLSv1.2', 256), ('ECDHE-ECDSA-AES128-SHA256', 'TLSv1.2', 128), ('ECDHE-RSA-AES128-SHA256', 'TLSv1.2', 128), ('DHE-RSA-AES256-GCM-SHA384', 'TLSv1.2', 256), ('DHE-RSA-AES128-GCM-SHA256', 'TLSv1.2', 128), ('DHE-RSA-AES256-SHA256', 'TLSv1.2', 256), ('DHE-RSA-AES128-SHA256', 'TLSv1.2', 128)]
server session reused? False
server got data b'foo'
server finished with client
server got connection on address: ('127.0.0.1', 64311)
server shared ciphers: [('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256), ('TLS_CHACHA20_POLY1305_SHA256', 'TLSv1.3', 256), ('TLS_AES_128_GCM_SHA256', 'TLSv1.3', 128), ('ECDHE-ECDSA-AES256-GCM-SHA384', 'TLSv1.2', 256), ('ECDHE-RSA-AES256-GCM-SHA384', 'TLSv1.2', 256), ('ECDHE-ECDSA-AES128-GCM-SHA256', 'TLSv1.2', 128), ('ECDHE-RSA-AES128-GCM-SHA256', 'TLSv1.2', 128), ('ECDHE-ECDSA-CHACHA20-POLY1305', 'TLSv1.2', 256), ('ECDHE-RSA-CHACHA20-POLY1305', 'TLSv1.2', 256), ('ECDHE-ECDSA-AES256-SHA384', 'TLSv1.2', 256), ('ECDHE-RSA-AES256-SHA384', 'TLSv1.2', 256), ('ECDHE-ECDSA-AES128-SHA256', 'TLSv1.2', 128), ('ECDHE-RSA-AES128-SHA256', 'TLSv1.2', 128), ('DHE-RSA-AES256-GCM-SHA384', 'TLSv1.2', 256), ('DHE-RSA-AES128-GCM-SHA256', 'TLSv1.2', 128), ('DHE-RSA-AES256-SHA256', 'TLSv1.2', 256), ('DHE-RSA-AES128-SHA256', 'TLSv1.2', 128)]
server session reused? True
server got data b'foo'
server finished with client
Here is the output of server.py on Python 3.11.3:
Python version: 3.11.3
server listening on port 12345
server got connection on address: ('127.0.0.1', 64316)
server shared ciphers: [('ECDHE-ECDSA-AES256-GCM-SHA384', 'TLSv1.2', 256), ('ECDHE-RSA-AES256-GCM-SHA384', 'TLSv1.2', 256), ('ECDHE-ECDSA-AES128-GCM-SHA256', 'TLSv1.2', 128), ('ECDHE-RSA-AES128-GCM-SHA256', 'TLSv1.2', 128), ('ECDHE-ECDSA-CHACHA20-POLY1305', 'TLSv1.2', 256), ('ECDHE-RSA-CHACHA20-POLY1305', 'TLSv1.2', 256), ('ECDHE-ECDSA-AES256-SHA384', 'TLSv1.2', 256), ('ECDHE-RSA-AES256-SHA384', 'TLSv1.2', 256), ('ECDHE-ECDSA-AES128-SHA256', 'TLSv1.2', 128), ('ECDHE-RSA-AES128-SHA256', 'TLSv1.2', 128), ('DHE-RSA-AES256-GCM-SHA384', 'TLSv1.2', 256), ('DHE-RSA-AES128-GCM-SHA256', 'TLSv1.2', 128), ('DHE-RSA-AES256-SHA256', 'TLSv1.2', 256), ('DHE-RSA-AES128-SHA256', 'TLSv1.2', 128)]
server session reused? False
server got data b'foo'
server finished with client
server got connection on address: ('127.0.0.1', 64317)
server shared ciphers: None
server session reused? True
server got data b'foo'
server finished with client
In 3.11.3, after the session is reused, the return of shared_ciphers() is None. The scripts and test certificates are located here.
Ah, the shared ciphers would only be computed when negotiation is performed (i.e., not resumption), yes. Hopefully you can update to a supported version of OpenSSL and pick up the needed funcitonality.
An alternative implementation could be to use SSL_CTX_set_client_hello_cb to obtain the list of ciphers sent in the ClientHello. Store the list of ciphers for retrieval in shared_ciphers(). #110902 implements this change but is (at present) left as draft. Storing the ciphers requires additional memory per socket and may not provide much value to users. Instead, #106345 proposes a documentation only change to inform users.
Known Impact
The None return resulted in a bug report in PyKMIP. The call to shared_ciphers was not checking for the None return value: OpenKMIP/PyKMIP#700
Documentation
SSLSocket.shared_ciphers()
does not documentNone
is returned on session reuseSummary
The fix of #96931 resulted in a change in
SSLSocket.shared_ciphers()
.If the session is reused,
SSLSocket.shared_ciphers()
returnsNone
Proposal: update documentation of
SSLSocket.shared_ciphers()
to noteNone
is returned on session reuse.Background & Motivation
As an example, here is a sample
server.py
andclient.py
scripts to show the behavior change in Python 3.11.2 and 3.11.3:Here is the output of
server.py
on Python 3.11.2:Here is the output of
server.py
on Python 3.11.3:In 3.11.3, after the session is reused, the return of
shared_ciphers()
isNone
. The scripts and test certificates are located here.Alternatives
openssl/openssl#4295 suggest alternative API to use in OpenSSL:
An alternative implementation could be to use
SSL_CTX_set_client_hello_cb
to obtain the list of ciphers sent in the ClientHello. Store the list of ciphers for retrieval inshared_ciphers()
. #110902 implements this change but is (at present) left as draft. Storing the ciphers requires additional memory per socket and may not provide much value to users. Instead, #106345 proposes a documentation only change to inform users.Known Impact
The
None
return resulted in a bug report in PyKMIP. The call toshared_ciphers
was not checking for theNone
return value: OpenKMIP/PyKMIP#700Linked PRs
SSLSocket.shared_ciphers()
#106345SSLSocket.shared_ciphers()
after session reuse #110902The text was updated successfully, but these errors were encountered: