mirko / SonOTA

Flashing Itead Sonoff devices with custom firmware via original OTA mechanism
GNU General Public License v2.0
717 stars 105 forks source link

SSL Error on 11 ('xxx', 4098): [SSL] internal error (_ssl.c:1108) #181

Open mzealey opened 3 years ago

mzealey commented 3 years ago

The TLS 1.1 flag is not working for me and causes errors like

SSL Error on 11 ('xxx', 4098): [SSL] internal error (_ssl.c:1108)

Changing to v1_2 fixes this issue - perhaps modern ubuntu's ssl doesnt have tls 1.1 support or python 3.8 removed this support?

jemccullough commented 3 years ago

@mzealey having exact issue on same platform. Sorry for newb question, was trying to do a quick flash, but how would I change to v1_2? thanks in advance

msylw commented 3 years ago

@jemccullough Look for PROTOCOL_TLSv1_1 in sonota.py, change it to PROTOCOL_TLSv1_2.

00shoham commented 3 years ago

Hmm. I've got the same issue. Ubuntu 20.04, SSL internal error.

I changed sonota.py to PROTOCOL_TLSv1_2. Definitely got further, but now it hangs with this:

OSError: [Errno 0] Error Exception in callback None() handle: Traceback (most recent call last): File "/usr/lib/python3.8/asyncio/events.py", line 81, in _run self._context.run(self._callback, self._args) File "/usr/lib/python3/dist-packages/tornado/platform/asyncio.py", line 122, in _handle_events handler_func(fileobj, events) File "/usr/lib/python3/dist-packages/tornado/stack_context.py", line 300, in null_wrapper return fn(args, **kwargs) File "/usr/lib/python3/dist-packages/tornado/iostream.py", line 709, in _handle_events self._handle_read() File "/usr/lib/python3/dist-packages/tornado/iostream.py", line 1581, in _handle_read self._do_ssl_handshake() File "/usr/lib/python3/dist-packages/tornado/iostream.py", line 1501, in _do_ssl_handshake self.socket.do_handshake() File "/usr/lib/python3.8/ssl.py", line 1309, in do_handshake self._sslobj.do_handshake()

Looks like still an SSL related issue.

Help?

-- Idan

SmittyHalibut commented 6 months ago

While researching this myself before finding this ticket, I found this oddity from my Sonoff TXUS switch: image

    TLSv1.2 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 79
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 75
            Version: TLS 1.2 (0x0303)

Note the conflicting TLS versions provided in the Client Hello packet:

The default code in SonOTA hardcodes the TLS version to v1.1. This is why switching it to v1.2 works. Though, I'm not sure what's up with the outermost v1.0.

After changing the TLS version to v1.2, I get this error on the SonOTA console:

*** IMPORTANT! ***
** AFTER the first download is COMPLETE, with in a minute or so you should connect to the new SSID "FinalStage" to finish the process.
** ONLY disconnect when the new "FinalStage" SSID is visible as an available WiFi network.
This server should automatically be allocated the IP address: 192.168.4.2.
If you have successfully connected to "FinalStage" and this is not the IP Address you were allocated, please ensure no other device has connected, and reboot your Sonoff.
...SSL Error on 11 ('192.168.13.152', 17295): [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1007)
...............SSL Error on 11 ('192.168.13.152', 29447): [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1007)
......SSL Error on 11 ('192.168.13.152', 5280): [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1007)
......

Ciphers offered by my Sonoff TXUS when connecting to the server on my laptop, as decoded by wireshark:

image

            Cipher Suites Length: 18
            Cipher Suites (9 suites)
                Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA256 (0x003d)
                Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
                Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003c)
                Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
                Cipher Suite: TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 (0x00b7)
                Cipher Suite: TLS_RSA_PSK_WITH_AES_256_CBC_SHA (0x0095)
                Cipher Suite: TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 (0x00b6)
                Cipher Suite: TLS_RSA_PSK_WITH_AES_128_CBC_SHA (0x0094)
                Cipher Suite: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff)

Ciphers accepted by Tornado, by way of Python's SSL Context:

>>> pprint([foo['name'] for foo in ssl_ctx.get_ciphers()])
['TLS_AES_256_GCM_SHA384',
 'TLS_CHACHA20_POLY1305_SHA256',
 'TLS_AES_128_GCM_SHA256',
 'ECDHE-ECDSA-AES256-GCM-SHA384',
 'ECDHE-RSA-AES256-GCM-SHA384',
 'ECDHE-ECDSA-AES128-GCM-SHA256',
 'ECDHE-RSA-AES128-GCM-SHA256',
 'ECDHE-ECDSA-CHACHA20-POLY1305',
 'ECDHE-RSA-CHACHA20-POLY1305',
 'ECDHE-ECDSA-AES256-SHA384',
 'ECDHE-RSA-AES256-SHA384',
 'ECDHE-ECDSA-AES128-SHA256',
 'ECDHE-RSA-AES128-SHA256',
 'DHE-RSA-AES256-GCM-SHA384',
 'DHE-RSA-AES128-GCM-SHA256',
 'DHE-RSA-AES256-SHA256',
 'DHE-RSA-AES128-SHA256']
>>> 

Indeed, no common ciphers.

Documentation for ssl.SSLContext includes the following note:

Changed in version 3.10: The default cipher suites now include only secure AES and ChaCha20 ciphers with forward secrecy and security level 2. RSA and DH keys with less than 2048 bits and ECC keys with less than 224 bits are prohibited. [PROTOCOL_TLS](https://docs.python.org/3/library/ssl.html#ssl.PROTOCOL_TLS), [PROTOCOL_TLS_CLIENT](https://docs.python.org/3/library/ssl.html#ssl.PROTOCOL_TLS_CLIENT), and [PROTOCOL_TLS_SERVER](https://docs.python.org/3/library/ssl.html#ssl.PROTOCOL_TLS_SERVER) use TLS 1.2 as minimum TLS version.

You can add back one of the older ciphers if you build an SSL Context manually and pass it to the HTTPServer constructor instead of a dict of parameters:

Line 664:

    ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    ssl_ctx.load_cert_chain(certfile=resource_path("ssl/server.crt"), keyfile=resource_path("ssl/server.key"))
    ssl_ctx.set_ciphers('AES256-SHA256')  # aka TLS_RSA_WITH_AES_256_CBC_SHA256
                                          # https://ciphersuite.info/cs/TLS_RSA_WITH_AES_256_CBC_SHA256/

    app_ssl = tornado.httpserver.HTTPServer(app, ssl_options=ssl_ctx)
    #app_ssl = tornado.httpserver.HTTPServer(app, ssl_options={
    #    "certfile": resource_path("ssl/server.crt"),
    #    "keyfile": resource_path("ssl/server.key"),
    #    "ssl_version": ssl.PROTOCOL_TLSv1_2,
    #})

This gets SonOTA to at least respond to the Client Hello with a Server Hello, listing TLS_RSA_WITH_AES_256_CBC_SHA256 as the chosen cipher.

But then the Sonoff TXUS immediately closes the socket with a FIN ACK, with no indication of what it's unhappy about.

image

Unfortunately, the only other meaningful thing that's included in the Server Hello is the TLS certificate, which strongly suggests it's the TLS cert it's unhappy with. If its actually checking the server TLS cert, then there's very little we can do to spoof that, without having the matching private key.