tronikos/androidtvremote2

Failing to connect to Sony Bravia Android TV

alexanv1 opened this issue · 25 comments

I am seeing the following in the logs when trying to setup the integration via Home Assistant:

2023-05-06 06:18:51.085 DEBUG (MainThread) [androidtvremote2] Couldn't connect to 192.168.2.218:6466. [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1007)

The Android TV app on my phone connects just fine so it seems to tolerate SSL handshake failures better.

Is there any additional logging or troubleshooting steps I can take?
The Bravia is a couple of years old running version 9 of Android TV and no updates are available.

foxy82 commented

I get the same error with my Sony TV and a Philips TV as well. Works well on a Chromecast with Android TV though:

DEBUG:asyncio:<asyncio.sslproto.SSLProtocol object at 0x7fa440ca14c0> starts SSL handshake
DEBUG:asyncio:<asyncio.sslproto.SSLProtocol object at 0x7fa440ca14c0>: SSL handshake failed
Traceback (most recent call last):
  File "/usr/lib/python3.8/asyncio/sslproto.py", line 632, in _on_handshake_complete
    raise handshake_exc
  File "/usr/lib/python3.8/asyncio/sslproto.py", line 189, in feed_ssldata
    self._sslobj.do_handshake()
  File "/usr/lib/python3.8/ssl.py", line 944, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1131)
DEBUG:asyncio:<asyncio.sslproto.SSLProtocol object at 0x7fa440ca14c0>: SSL error in data received
Traceback (most recent call last):
  File "/usr/lib/python3.8/asyncio/sslproto.py", line 529, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "/usr/lib/python3.8/asyncio/sslproto.py", line 189, in feed_ssldata
    self._sslobj.do_handshake()
  File "/usr/lib/python3.8/ssl.py", line 944, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1131)
DEBUG:androidtvremote2:Couldn't connect to 192.168.1.47:6466. Error: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1131)

Without physical access to such a device I'm afraid it would be hard for me to debug this. I recommend running demo.py included in this library. My guess is you would need to make changes in certificate_generator.py. My guess is the device doesn't like the way the certificate is self signed.

But first you should confirm the date and time on the Android TV device is correct.

Thanks, I've confirmed that the data and time are correct. The library also works just fine with a different Android TV device (nVidia Shield) for me.

I will try to experiment with demo.py and certificate generation over the next few days.

Have you seen this work with any Android TV 9 devices? Wondering if that's the common denominator here..

I don't personally own an Android TV 9 device. You should also try experimenting with different versions of the library dependencies and different ways constructing the SSLContext in androidtv_remote.py

foxy82 commented

@alexanv1 depending on what you are trying to do I've made a library that allows a Pico W to act like a USB Keyboard/Mouse for Android TV that might be helpful if you can't get this to work:

https://github.com/foxy82/picow-usb-device

foxy82 commented

I've found a way to work around this by providing the server's certificate name and MAC manually...

In this PR: #7

I added two more params:

  --server-name SERVER_NAME
                        Retrieved from the server certificate
  --server-mac SERVER_MAC
                        Retrieved from the server certificate

I've added details of the command to run to get these details to the readme.

foxy82 commented

@alexanv1 - did you setup using the scan or entering an IP address in Home Assistant?

@foxy82, I setup using IP address.
Sounds like you have a way to fix this and just need to integrate into Home Assistant rather than using demo.py?

foxy82 commented

My main instance isn't on the latest HA version yet but I set up a test instance.

On my test instance all my devices are auto detected by zeroconf and setting up via that seems to work.

If I go in and ignore the list of detected devices and try to set via IP then I get the same error.

The change i made to demo.py can't be ported to Home Assistant but the concept could be. Home assistant could be made to request the host and Mac address when it asks for the IP then that should allow the code that doesn't work to be skipped.

Can you please confirm, using demo.py, my latest commit fixes the handshake failure? If it does I can release it and bump the version on Home Assistant.

foxy82 commented

It doesn't fix it. It skips it. If you have the MAC you don't need to call the method that breaks for some devices.

The same could be replicated in Home Assistants config flow so if a user tried to manually add a device they can also (optionally) enter the name and mac address.

I've confirmed that relying on zeroconf discovery in Home Assistant allows the integration to be setup successfully. I didn't have zeroconf enabled previously as I tend to prefer explicit\manual configuration vs discovery.

I can try demo.py with the fix tomorrow and see if setting up with just the IP works but it sounds like @foxy82 already confirmed that?

If openssl s_client -connect <ANDROID TV IP>:6467 -prexit -state 2>/dev/null | grep "issuer=" works then we should be able to programmatically do the same. I rather not ask the user at setup to enter name and mac address. It complicates the config flow and I don't expect the average user would be able to run the openssl command to get the mac.

@alexanv1 yes it would be great if you could also try demo.py.

@tronikos, looks like it's still failing unfortunately:

C:\Temp\androidtvremote2\src>python demo.py --host 192.168.2.218

INFO:__main__:Generated new certificate
Traceback (most recent call last):
  File "C:\Temp\androidtvremote2\src\androidtvremote2\androidtv_remote.py", line 285, in async_get_name_and_mac
    _, writer = await asyncio.open_connection(
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\v_ale\AppData\Local\Programs\Python\Python311\Lib\asyncio\streams.py", line 48, in open_connection
    transport, _ = await loop.create_connection(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\v_ale\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 1112, in create_connection
    transport, protocol = await self._create_connection_transport(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\v_ale\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 1145, in _create_connection_transport
    await waiter
  File "C:\Users\v_ale\AppData\Local\Programs\Python\Python311\Lib\asyncio\sslproto.py", line 574, in _on_handshake_complete
    raise handshake_exc
  File "C:\Users\v_ale\AppData\Local\Programs\Python\Python311\Lib\asyncio\sslproto.py", line 556, in _do_handshake
    self._sslobj.do_handshake()
  File "C:\Users\v_ale\AppData\Local\Programs\Python\Python311\Lib\ssl.py", line 979, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1002)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Temp\androidtvremote2\src\demo.py", line 238, in <module>
    asyncio.run(_main(), debug=True)
  File "C:\Users\v_ale\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Users\v_ale\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\v_ale\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "C:\Temp\androidtvremote2\src\demo.py", line 198, in _main
    await _pair(remote)
  File "C:\Temp\androidtvremote2\src\demo.py", line 139, in _pair
    name, mac = await remote.async_get_name_and_mac()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Temp\androidtvremote2\src\androidtvremote2\androidtv_remote.py", line 292, in async_get_name_and_mac
    raise CannotConnect from exc
androidtvremote2.exceptions.CannotConnect```
foxy82 commented

When running openssl s_client -connect <IP>:6467 -prexit -state -debug 2> error_file.txt 1> out_file.txt targeting both the Android TV (works) and the Humax box (doesn't work with remote.async_get_name_and_mac())

I get differences in the errors_out

On the working Android TV:

SSL_connect:before SSL initialization
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS read server hello
Can't use SSL_get_servername
SSL_connect:TLSv1.3 read encrypted extensions
SSL_connect:SSLv3/TLS read server certificate request
depth=0 CN = atvremote/sabrina_prod_stable/sabrina/Chromecast/AA:BB:CC:DD:EE:FF
verify error:num=18:self-signed certificate
verify return:1
depth=0 CN = atvremote/sabrina_prod_stable/sabrina/Chromecast/AA:BB:CC:DD:EE:FF
verify return:1
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:TLSv1.3 read server certificate verify
SSL_connect:SSLv3/TLS read finished
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write client certificate
SSL_connect:SSLv3/TLS write finished
SSL3 alert read:fatal:unknown
805B6BC33D7F0000:error:0A00045C:SSL routines:ssl3_read_bytes:tlsv13 alert certificate required:../ssl/record/rec_layer_s3.c:1584:SSL alert number 116

On the Humax that doesn't work:

SSL_connect:before SSL initialization
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS write client hello
Can't use SSL_get_servername
SSL_connect:SSLv3/TLS read server hello
depth=0 CN = atvremote/fvp4kgtr/fvp4kgtr/FVP-4KGTR/AA:BB:CC:DD:EE:FF
verify error:num=18:self-signed certificate
verify return:1
depth=0 CN = atvremote/fvp4kgtr/fvp4kgtr/FVP-4KGTR/AA:BB:CC:DD:EE:FF
verify return:1
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:SSLv3/TLS read server key exchange
SSL_connect:SSLv3/TLS read server certificate request
SSL_connect:SSLv3/TLS read server done
SSL_connect:SSLv3/TLS write client certificate
SSL_connect:SSLv3/TLS write client key exchange
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write finished
SSL3 alert read:fatal:handshake failure
SSL_connect:error in error
80FBA447117F0000:error:0A000410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../ssl/record/rec_layer_s3.c:1584:SSL alert number 40

Then in the out file halfway down for the working I receive TLS v1.3:

No client certificate CA names sent
Requested Signature Algorithms: ECDSA+SHA256:RSA-PSS+SHA256:RSA+SHA256:ECDSA+SHA384:RSA-PSS+SHA384:RSA+SHA384:RSA-PSS+SHA512:RSA+SHA512:RSA+SHA1
Shared Requested Signature Algorithms: ECDSA+SHA256:RSA-PSS+SHA256:RSA+SHA256:ECDSA+SHA384:RSA-PSS+SHA384:RSA+SHA384:RSA-PSS+SHA512:RSA+SHA512
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 1060 bytes and written 403 bytes
Verification error: self-signed certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 18 (self-signed certificate)
---

But on the Non working I receiver TLS v1.2

No client certificate CA names sent
Client Certificate Types: RSA sign, ECDSA sign
Requested Signature Algorithms: ECDSA+SHA256:RSA-PSS+SHA256:RSA+SHA256:ECDSA+SHA384:RSA-PSS+SHA384:RSA+SHA384:RSA-PSS+SHA512:RSA+SHA512:RSA+SHA1
Shared Requested Signature Algorithms: ECDSA+SHA256:RSA-PSS+SHA256:RSA+SHA256:ECDSA+SHA384:RSA-PSS+SHA384:RSA+SHA384:RSA-PSS+SHA512:RSA+SHA512
Peer signing digest: SHA256
Peer signature type: RSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 983 bytes and written 398 bytes
Verification error: self-signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: REDACTED
    Session-ID-ctx:
    Master-Key: REDACTED
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1683882022
    Timeout   : 7200 (sec)
    Verify return code: 18 (self-signed certificate)
    Extended master secret: yes
---

I get similar at the end of the output:

Working:

---
SSL handshake has read 1084 bytes and written 403 bytes
Verification error: self-signed certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 18 (self-signed certificate)

Not working:

---
SSL handshake has read 983 bytes and written 398 bytes
Verification error: self-signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: REDACTED
    Session-ID-ctx:
    Master-Key: REDACTED
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1683882022
    Timeout   : 7200 (sec)
    Verify return code: 18 (self-signed certificate)
    Extended master secret: yes
---

I have the same problem, on a Xiaomi MI Box S. It runs android 9 as well and I compared the SSL handshake on port 6467 with a Chromecast with Google TV. It produces this error

SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../ssl/record/rec_layer_s3.c:1543:SSL alert number 40

I tried to run demo.py in home assistant container but python tries to load an old version of asyncio installed in site-packages instead of the built-in one and causes an error. Weird.

foxy82 commented

Do any of the answers in https://stackoverflow.com/questions/7689941/how-can-i-retrieve-the-tls-ssl-peer-certificate-of-a-remote-host-using-python work?

No none of them work. With the first Answer I get the same issue - there is a comment on this answer about changing:

cert = ssl.get_server_certificate((hostname, port))

to

cert = ssl.get_server_certificate((hostname, port), ssl_version=ssl.PROTOCOL_TLSv1)

When I do that the error changes to :

Traceback (most recent call last):
  File "/home/neil/git/androidtvremote2/src/tester.py", line 18, in <module>
    cert = ssl.get_server_certificate((hostname, port), ssl_version=ssl.PROTOCOL_TLSv1)
  File "/usr/lib/python3.10/ssl.py", line 1524, in get_server_certificate
    with context.wrap_socket(sock, server_hostname=host) as sslsock:
  File "/usr/lib/python3.10/ssl.py", line 513, in wrap_socket
    return self.sslsocket_class._create(
  File "/usr/lib/python3.10/ssl.py", line 1071, in _create
    self.do_handshake()
  File "/usr/lib/python3.10/ssl.py", line 1342, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL] internal error (_ssl.c:997)

Hi, I am just following this issue as I have the same problem with my Sony Android TV from 2015 with Andoid TV 7.

foxy82 commented

If you are using Home Assistant the workaround is to ensure the device is discovered by mDNS.

Hello, ok but I am not sure what exactly you mean.
TV is discovered by "Cast" integration without any problem so I guess mDNS is working.

However can you give me some hint how I can check it please?

Can you please try running the demo again after syncing to the latest changes? I found my old Nexus Player and I was able to fix it for that device that runs Android TV 8. If it works for you I can release a new version and bump the dependency in HA.

Hi @tronikos, thank you very much you are awesome the demo worked like a charm on my Android TV 7.
Below you can find my result.
image

foxy82 commented

Works on my Sony TV and Philips TV as well. Thanks @tronikos !!

Thanks for confirming this fixed it.