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/