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
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 3f5590f..ae094cf 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -25,6 +25,8 @@
 
 - Fixed a potential use-after-free in the verify callback and resolved a memory leak when loading PKCS12 files with ``cacerts``.
   `#723 <https://github.com/pyca/pyopenssl/pull/723>`_
+- Added ``Connection.export_keying_material`` for RFC 5705 compatible export of keying material.
+  `#725 <https://github.com/pyca/pyopenssl/pull/725>`_
 
 ----
 
diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py
index ec33814..b664254 100644
--- a/src/OpenSSL/SSL.py
+++ b/src/OpenSSL/SSL.py
@@ -2031,6 +2031,30 @@
         _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 76d8c4d..03dd935 100644
--- a/tests/test_ssl.py
+++ b/tests/test_ssl.py
@@ -3379,6 +3379,28 @@
         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.'