Re: RFC: expose ssl internals for use by ctypes/cffi
Barry <[email protected]> Thu, 1 Dec 2022 16:18:32 +0000
| Newsgroups | gmane.comp.python.devel |
|---|---|
| Message-ID | <[email protected]> |
> On 30 Nov 2022, at 16:59, [email protected] wrote: > > Hi all, > > for quite some time I've been working on a Python implementation of a protocol called NTS which requires access to an API in OpenSSL which is not provided by the Python ssl module. I added a patch for that which unfortunately for me the maintainer did not want to accept. Some comments were made of a possible future way to give more generic access to the openssl library via ctypes/cffi but I have been unable to find more information about that. I was home sick last week and decided to take a shot at it and have built something that I feel is a bit ugly but does seem to work. I'd like to some feedback on this approach. We use the pyOpenSSL to access APIs of openssl. No need to use ctypes. Barry > > My patches can be found on github, based on the Python 3.11 tag: > > https://github.com/python/cpython/compare/3.11...wingel:cpython:main > > Here's a short description of each patch on this branch: > > "bpo-37952: SSL: add support for export_keying_material" is my old patch which adds the method I need to the ssl library just for reference. > > The other commits add the necessary infrastructure with some example code. These commits are not ready for submission but hopefully they show what I have in mind. > > "Add CRYPTO_DLL_PATH and SSL_DLL_PATH to the _ssl module. " > > This commit adds two constants to the "_ssl" C module with the paths to libcrypto and libssl respectively. On Linux dladdr and on Windows GetModuleHandle/GetModuleFilename are used on a symbol in each library to find the path to the corresponding DLL. I've verified that this works Debian Bulleye and on Windows 10 with Visual Studio 2017. I don't own a Mac so I haven't been able to test this on macOS, but I believe dladdr is available on modern macOS so it might work out of the box. With the paths it's possible to use ctypes or cffi get a handle to these libraries. > > "Add API to get the address of the SSL structure" then adds an API to an SSLSocket which returns the address of the corresponding "SSL" C structure. This address can be used by ctypes/cffi. One would probably want to expose SSL_CTX, SSL_SESSION and BIO too but I started with just SSL since that's what my code needs right now. > > "Add a small test program" is a small test program that uses the infrastructure from the two above commits to call C functions in libssl/libcrypto using both ctypes and cffi. It's a bit ugly but hopefully it's not too hard to understand. > > "Example of how to extend the ssl library using ctypes" is an example of how a Python module that extends the SSL library using ctypes could look. First get a handle to libssl using ctypes, set up ctypes with the correct API for the export_keying_material function, wrap it in a more Pythonic function and then extend SSLSocket with the new function. A simplified version looks like this: > > import ssl, ctypes > ssl_lib = ctypes.CDLL(ssl._ssl.SSL_DLL_PATH) > ssl_lib.SSL_export_keying_material.argtypes = ( > ctypes.c_void_p, # SSL pointer > ctypes.c_void_p, ctypes.c_size_t, # out pointer, out length > ctypes.c_void_p, ctypes.c_size_t, # label buffer, label length > ctypes.c_void_p, ctypes.c_size_t, # context, context length > ctypes.c_int) # use context flag > ssl_lib.SSL_export_keying_material.restype = ctypes.c_int > > def SSL_export_keying_material(self, label, key_len, context = None): > c_key = ctypes.create_string_buffer(key_len) > c_label = ctypes.create_string_buffer(len(label)) > c_context = ctypes.create_string_buffer(context, len(context)) > if ssl_lib.SSL_export_keying_material( > self._sslobj.get_internal_addr(), > c_key, key_len, > c_label, len(label), > c_context, len(context), 1); > return bytes(c_key) > > ssl.SSLSocket.export_keying_material = SSL_export_keying_material > > There's a final commit "Expose more OPENSSL_ variables" which add some more constants to the ssl module which expose the cflags and build information from OpenSSL. This patch is not really necessary, but it might be a good idea to compare these constants with the corresponding constants retrieved using ctypes/cffi to make sure that exactly the same version of the openssl library is used. > > Does this seem like a good idea? As I said, I feel that it is a bit ugly, but it does mean that if someone wants to use some SSL_really_obscure_function in libcrypto or libssl they can do that without having to rebuild all of CPython themselves. Or if they want to integrate with some other C library that wants a raw pointer to a SSL socket. Hopefully this would reduce the burden on the ssl module maintainers a bit. > > Anyway, if you think this is a good approach I could clean up my patches, add support for SSL_CTX/SSL_SESSION/BIO, document all of this and make it into a proper pull request. > > /Christer > _______________________________________________ > Python-Dev mailing list -- [email protected] > To unsubscribe send an email to [email protected] > https://mail.python.org/mailman3/lists/python-dev.python.org/ > Message archived at https://mail.python.org/archives/list/[email protected]/message/JFUMGTPXEGV63DIY5BNZRRCNADPEQC2O/ > Code of Conduct: http://python.org/psf/codeofconduct/ > _______________________________________________ Python-Dev mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/[email protected]/message/ZEMKQD4JTJYLFBSSU4RNF4ZLFAVFX665/ Code of Conduct: http://python.org/psf/codeofconduct/