From bdb7639ad53427fae38695d2dc5bf5bd794787c8 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 1 Dec 2017 04:54:32 +0800 Subject: [PATCH] Export keying material support (#725) * added method to export keying material from an ssl connection * updated tests to use bytestrings to avoid breaking python3 tests * added additional comments to test * simplify export_keying_material * add changelog * address review feedback --- CHANGELOG.rst | 2 ++ src/OpenSSL/SSL.py | 24 ++++++++++++++++++++++++ tests/test_ssl.py | 22 ++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3f5590fce..ae094cf75 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,6 +25,8 @@ Changes: - Fixed a potential use-after-free in the verify callback and resolved a memory leak when loading PKCS12 files with ``cacerts``. `#723 `_ +- Added ``Connection.export_keying_material`` for RFC 5705 compatible export of keying material. + `#725 `_ ---- diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py index ec338145e..b6642544f 100644 --- a/src/OpenSSL/SSL.py +++ b/src/OpenSSL/SSL.py @@ -2031,6 +2031,30 @@ def master_key(self): _lib.SSL_SESSION_get_master_key(session, outp, length) return _ffi.buffer(outp, length)[:] + def export_keying_material(self, label, olen, context=None): + """ + Obtain keying material for application use. + + :param label - a disambiguating label string as described in RFC 5705 + :param olen - the length of the exported key material in bytes + :param context - a per-association context value + :return the exported key material bytes or None + """ + outp = _no_zero_allocator("unsigned char[]", olen) + context_buf = _ffi.NULL + context_len = 0 + use_context = 0 + if context is not None: + context_buf = context + context_len = len(context) + use_context = 1 + success = _lib.SSL_export_keying_material(self._ssl, outp, olen, + label, len(label), + context_buf, context_len, + use_context) + _openssl_assert(success == 1) + return _ffi.buffer(outp, olen)[:] + def sock_shutdown(self, *args, **kwargs): """ See shutdown(2) diff --git a/tests/test_ssl.py b/tests/test_ssl.py index 76d8c4d9e..03dd93524 100644 --- a/tests/test_ssl.py +++ b/tests/test_ssl.py @@ -3379,6 +3379,28 @@ def test_memory_connect(self): assert server_conn.client_random() != server_conn.server_random() assert client_conn.client_random() != client_conn.server_random() + # Export key material for other uses. + cekm = client_conn.export_keying_material(b'LABEL', 32) + sekm = server_conn.export_keying_material(b'LABEL', 32) + assert cekm is not None + assert sekm is not None + assert cekm == sekm + assert len(sekm) == 32 + + # Export key material for other uses with additional context. + cekmc = client_conn.export_keying_material(b'LABEL', 32, b'CONTEXT') + sekmc = server_conn.export_keying_material(b'LABEL', 32, b'CONTEXT') + assert cekmc is not None + assert sekmc is not None + assert cekmc == sekmc + assert cekmc != cekm + assert sekmc != sekm + # Export with alternate label + cekmt = client_conn.export_keying_material(b'test', 32, b'CONTEXT') + sekmt = server_conn.export_keying_material(b'test', 32, b'CONTEXT') + assert cekmc != cekmt + assert sekmc != sekmt + # Here are the bytes we'll try to send. important_message = b'One if by land, two if by sea.'