Skip to content

Commit b2bca41

Browse files
authored
Add SSL.Context.set_keylog_callback (#910)
* add SSL.Context.set_keylog_callback * don't fail on missing attribute * lint! * make it black
1 parent 0373718 commit b2bca41

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ Deprecations:
2424
Changes:
2525
^^^^^^^^
2626

27-
*none*
27+
- Added ``Context.set_keylog_callback`` to log key material.
28+
`#910 <https://github.com/pyca/pyopenssl/pull/910>`_
2829

2930

3031
19.1.0 (2019-11-18)

src/OpenSSL/SSL.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,11 @@ def explode(*args, **kwargs):
696696
)
697697

698698

699+
_requires_keylog = _make_requires(
700+
getattr(_lib, "Cryptography_HAS_KEYLOG", None), "Key logging not available"
701+
)
702+
703+
699704
class Session(object):
700705
"""
701706
A class representing an SSL session. A session defines certain connection
@@ -760,6 +765,7 @@ def __init__(self, method):
760765
self._verify_helper = None
761766
self._verify_callback = None
762767
self._info_callback = None
768+
self._keylog_callback = None
763769
self._tlsext_servername_callback = None
764770
self._app_data = None
765771
self._npn_advertise_helper = None
@@ -1338,6 +1344,31 @@ def wrapper(ssl, where, return_code):
13381344
)
13391345
_lib.SSL_CTX_set_info_callback(self._context, self._info_callback)
13401346

1347+
@_requires_keylog
1348+
def set_keylog_callback(self, callback):
1349+
"""
1350+
Set the TLS key logging callback to *callback*. This function will be
1351+
called whenever TLS key material is generated or received, in order
1352+
to allow applications to store this keying material for debugging
1353+
purposes.
1354+
1355+
:param callback: The Python callback to use. This should take two
1356+
arguments: a Connection object and a bytestring that contains
1357+
the key material in the format used by NSS for its SSLKEYLOGFILE
1358+
debugging output.
1359+
:return: None
1360+
"""
1361+
1362+
@wraps(callback)
1363+
def wrapper(ssl, line):
1364+
line = _ffi.string(line)
1365+
callback(Connection._reverse_mapping[ssl], line)
1366+
1367+
self._keylog_callback = _ffi.callback(
1368+
"void (*)(const SSL *, const char *)", wrapper
1369+
)
1370+
_lib.SSL_CTX_set_keylog_callback(self._context, self._keylog_callback)
1371+
13411372
def get_app_data(self):
13421373
"""
13431374
Get the application data (supplied via :meth:`set_app_data()`)

tests/test_ssl.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,37 @@ def info(conn, where, ret):
10011001
[] == notConnections
10021002
), "Some info callback arguments were not Connection instances."
10031003

1004+
@pytest.mark.skipif(
1005+
not getattr(_lib, "Cryptography_HAS_KEYLOG", None),
1006+
reason="SSL_CTX_set_keylog_callback unavailable",
1007+
)
1008+
def test_set_keylog_callback(self):
1009+
"""
1010+
`Context.set_keylog_callback` accepts a callable which will be
1011+
invoked when key material is generated or received.
1012+
"""
1013+
called = []
1014+
1015+
def keylog(conn, line):
1016+
called.append((conn, line))
1017+
1018+
server_context = Context(TLSv1_METHOD)
1019+
server_context.set_keylog_callback(keylog)
1020+
server_context.use_certificate(
1021+
load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
1022+
)
1023+
server_context.use_privatekey(
1024+
load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
1025+
)
1026+
1027+
client_context = Context(TLSv1_METHOD)
1028+
1029+
self._handshake_test(server_context, client_context)
1030+
1031+
assert called
1032+
assert all(isinstance(conn, Connection) for conn, line in called)
1033+
assert all(b"CLIENT_RANDOM" in line for conn, line in called)
1034+
10041035
def _load_verify_locations_test(self, *args):
10051036
"""
10061037
Create a client context which will verify the peer certificate and call

0 commit comments

Comments
 (0)