-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Panasonic TV TX-65FZW804 not working correctly #9
Comments
The new TVs use encryption. You need to ask the TV to display a pin code, the TV then gives you back a challenge key, which (I believe) you then use to encrypt the pin code displayed on the TV and send it back. The first request uses "X_DisplayPinCode", like this:
You'll get a response like this:
I'm not sure what you do with the challenge key. I had a quick look at the Panasonic TV Remote 2 APK and it looks like they use AES-CBC, but haven't had time to see if they use that for the pin code challenge auth. I'd suggest looking more at the APK to see exactly how they do it. Anyway, the next request would like something like this using "X_RequestAuth" with "X_AuthInfo" containing the encrypted pin code using the challenge key:
That's about as far as I've looked, and I probably won't get time to look at it much more. Hopefully this is helpful though. |
Following up... After looking at the ELF binary that's responsible for the encryption, it looks like they use AES-CBC where the X_ChallengeKey is used for the IV (after being base64 decoded of course). The AES encryption key is derived from the IV like this:
As I mentioned before, the pin code must be encrypted (with the AES-CBC key algo above, and then base64-encoded) and sent in an X_RequestAuth SOAP action, inside the X_AuthInfo tag. The plaintext content of X_AuthInfo is in this format: <X_PinCode>1234</X_PinCode> So, the plaintext response to send the pin code would look like this:
And the encrypted version would be something like this:
|
OK I got it working, it was a little more complicated. Not only is the encryption key derived from the IV, but also there is a 16 byte header at the start of the payload (actually 12 random bytes and 4 bytes for the payload length in big endian). And there is also an HMAC signature to calculate, where the HMAC key is also derived from the IV. Anyway, working example code below. import binascii
import base64
import hmac, hashlib
from Crypto.Cipher import AES
# Example challenge (which is our IV)
iv = base64.b64decode("mUQdS7/RyJTMsiojPz9i1Q==")
# Get character codes from IV bytes
iv_vals = [ord(c) for c in iv]
# Initialise key character codes array
key_vals = [0] * 16
# Derive key from IV
i = 0
while i < 16:
key_vals[i] = ~iv_vals[i + 3] & 0xFF
key_vals[i + 1] = ~iv_vals[i + 2] & 0xFF
key_vals[i + 2] = ~iv_vals[i + 1] & 0xFF
key_vals[i + 3] = ~iv_vals[i] & 0xFF
i += 4
# Convert our key character codes to bytes
key = ''.join(chr(c) for c in key_vals)
# Initialise HMAC key mask (taken from libtvconnect.so)
hmac_key_mask_vals = [ord(c) for c in binascii.unhexlify("15C95AC2B08AA7EB4E228F811E34D04FA54BA7DCAC9879FA8ACDA3FC244F3854")]
# Initialise HMAC key character codes array
hmac_vals = [0] * 32
# Calculate HMAC key using HMAC key mask and IV
i = 0
while i < 32:
hmac_vals[i] = hmac_key_mask_vals[i] ^ iv_vals[(i + 2) & 0xF]
hmac_vals[i + 1] = hmac_key_mask_vals[i + 1] ^ iv_vals[(i + 3) & 0xF]
hmac_vals[i + 2] = hmac_key_mask_vals[i + 2] ^ iv_vals[i & 0xF]
hmac_vals[i + 3] = hmac_key_mask_vals[i + 3] ^ iv_vals[(i + 1) & 0xF]
i += 4
# Convert our HMAC key character codes to bytes
hmac_key = ''.join(chr(c) for c in hmac_vals)
# This is our plaintext SOAP argument for the pin code shown on the TV
authinfo = "<X_PinCode>4410</X_PinCode>"
# First 12 bytes are randomised, let's just set them to 0 because it doesn't matter
payload = "000000000000"
# The next 4 bytes contain the plaintext (SOAP arg) length in big endian
n = len(authinfo)
payload += chr(n >> 24)
payload += chr((n >> 16) & 0xFF)
payload += chr((n >> 8) & 0xFF)
payload += chr(n & 0xFF)
# Now we concatenate our payload, which is starting at byte 17 of the payload
payload += authinfo
# Let's encrypt it with AES-CBC! We need to make sure we pad it to a multiple of 16 bytes beforehand
aes = AES.new(key, AES.MODE_CBC, iv)
ciphertext = aes.encrypt(pad(payload))
# Calculate the HMAC-SHA-256 signature of our encrypted payload
sig = hmac.new(hmac_key, ciphertext, hashlib.sha256).digest()
# Concatenate the HMAC signature to the encrypted payload and base64 encode it, and we're done!
encrypted_payload = base64.b64encode(ciphertext + sig) I successfully authenticated with my TV using this, and got back the response below. I've redacted the auth result content for privacy reasons:
I will carry on researching this soon, hopefully this week. I believe the rest should be quite easy, but I'm not sure exactly how sending encrypted commands works yet. |
hey nick, thank you for your awesome work so far. |
All done on my fork :), all changes here: https://github.com/ngws/panasonic-viera/blob/master/panasonic_viera/__init__.py Tested with Python 2.7 and against a Panasonic TX-55FZ952B. I recommend testing with Python 3.x and ideally confirm that I didn't break anything for older TV models. |
since HA 0.91 I'm unable to turn the TV on or off. Today with HA 0.92.2 I also added the new parameter to my config and nothing happened except this exception:
|
@florianholzapfel & @ngws: Hi there. Any progress to release the fixes? Is there a way that I can help? |
@noodles101 I've created a pull request to merge my updated branch into the official repo here. |
Hi @noodles101 and @florianholzapfel - i’ve been following this github and this issue/thread with interest. Is it all working now as expected ? I’d love to see this capability within Node Red. |
Hello Guys, I'm might be not fully in line, but I'm sure you can help. I try to achieve the same, remote control a 2019 TV, but over HTTP SOAP commands. Thanks to all the above I'm able to get the Pin code appearing on the TV screen and the xml feedback with the Challenge Key. I'm starting in "coding", so any help will be highly appreciated.... |
The merge request #15 is still open and not merged. |
Hello |
I've adapted this code to Xojo and found the same issue. It was my padding function that wasn't adding a block of 16 nulls because the <X_ApplicationId> line has a factor of 16. I just added an extra block of 16 nulls for mods of 0. |
@ngws Thank you so much for your work on this. I've used your workings and code to create this in Xojo (language of choice). Works great! |
Is anybody able to convert it to a bash script? |
@noodles101 Are you still having this issue? If you are, it will very likely be fixed with version 0.109 of Home Assistant, since it features an updated :) |
@joogps thanks for the information. I will try to upgrade soon. |
Hi,
i’m trying to integrate my new tv Panasonic TX-65FZW804 on Home Assistant v0.90.1 on an Raspberry Pi 3 model B+.
The Home Assistant Repo ticket was closed because it must solved here in the library.
I also configured the MAC address like in the documentation.
So I can turn the TV on and use volume up or down.
I’m not able to turn it off or select a source.
When I try to turn it off I get the http error code 500.
Thanks for your help.
The text was updated successfully, but these errors were encountered: