drbild / sslpsk

Adds TLS-PSK support to the Python ssl package
Apache License 2.0
25 stars 32 forks source link

Provide a SSLContext() API #19

Open paravoid opened 4 years ago

paravoid commented 4 years ago

The Python ssl module documentation says:

Since Python 3.2 and 2.7.9, it is recommended to use the SSLContext.wrap_socket() of an SSLContext instance to wrap sockets as SSLSocket objects. The helper functions create_default_context() returns a new context with secure default settings. The old wrap_socket() function is deprecated since it is both inefficient and has no support for server name indication (SNI) and hostname matching.

It'd be nice if sslpsk followed suit and provided a similar API. It seems that 3.7+ provides some core API improvements that would make this possible. I played around and this seemed to work:

def _ssl_setup_psk_callbacks(sslobj):
    psk = sslobj.context.psk
    hint = sslobj.context.hint
    if psk:
        if sslobj.server_side:
            cb = psk if callable(psk) else lambda _identity: psk
            _ssl_set_psk_server_callback(sslobj, cb, hint)
        else:
            cb = psk if callable(psk) else lambda _hint: psk if isinstance(psk, tuple) else (psk, b"")
            _ssl_set_psk_client_callback(sslobj, cb)

class SSLPSKContext(ssl.SSLContext):
    @property
    def psk(self):
        return getattr(self, "_psk", None)

    @psk.setter
    def psk(self, psk):
        self._psk = psk

    @property
    def hint(self):
        return getattr(self, "_hint", None)

    @hint.setter
    def hint(self, hint):
        self._hint = hint

class SSLPSKObject(ssl.SSLObject):
    def do_handshake(self, *args, **kwargs):
        _ssl_setup_psk_callbacks(self)
        super().do_handshake(*args, **kwargs)

class SSLPSKSocket(ssl.SSLSocket):
    def do_handshake(self, *args, **kwargs):
        _ssl_setup_psk_callbacks(self)
        super().do_handshake(*args, **kwargs)

SSLPSKContext.sslobject_class = SSLPSKObject
SSLPSKContext.sslsocket_class = SSLPSKSocket

(It seems like SSLPSKSocket alone is useful to replace the existing functionality. I think SSLPSKObject is useful only under certain frameworks. I could not test that, so perhaps it'd be smarter to even avoid including that for now.)

With that, one can use SSLPSKContext where they'd use SSLContext before, and SSLPSKContext.psk = … to set the PSK (and .hint = … for the hint).

A backwards-compatible sslpsk.wrap_socket() can still be offered with:

def wrap_socket(sock, psk, hint=None,
                server_side=False,
                ssl_version=ssl.PROTOCOL_TLS,
                do_handshake_on_connect=True,
                suppress_ragged_eofs=True,
                ciphers=None):

    context = SSLPSKContext(ssl_version)
    if ciphers:
        context.set_ciphers(ciphers)
    context.psk = psk
    context.hint = hint

    return context.wrap_socket(
        sock=sock, server_side=server_side,
        do_handshake_on_connect=do_handshake_on_connect,
        suppress_ragged_eofs=suppress_ragged_eofs
    )
paravoid commented 4 years ago

FWIW, in theory an an SSLContext + SSLObject API would allow this to work with asyncio. Sadly, I haven't managed to make that work with the above code, however. The OpenSSL library calls are never made and I haven't figured out why yet...

gennadykr commented 4 years ago

This makes it possible to add PSK support to paho-mqtt client. A problem only appears with PSK ciphers, and context.set_ciphers('PSK') didn't help till I patched sslpsk a bit to work with python3.8. Thanks a lot!

maovidal commented 2 years ago

I'm trying to make work the proposed by @paravoid on the paho-mqtt client. So far I haven't been able to achieve it. Here is what I have done so far: https://github.com/maovidal/paho_sslpsk2_demo/blob/main/paho_sslpsk2_demo.py

Any help would be much appreciated.

ddurham2 commented 2 years ago

Wouldn't it be great to see this whole thing as an effort against https://github.com/python/cpython/issues/63284

It states:

Please feel free to re-open it with a patch ...

doronz88 commented 11 months ago

@drbild I just submitted a tested PR with this fix and this works great. I would really appreciate it if you could review it