tomikaa87 / gree-remote

Simple remote control utility for Gree Smart air conditioners
GNU General Public License v3.0
279 stars 64 forks source link

gree.py - "The length of the provided data is not a multiple of the block length" exception on initial search #52

Open marius-nicolae opened 2 years ago

marius-nicolae commented 2 years ago

Hi, First, thanks for your great initiative!

When using gree.py to discover my Gree devices (the search command) it first successfully decrypt my Cooper & Hunder's returned pack JSON field. Still, then it returns The length of the provided data is not a multiple of the block length exception while attempting to decrypt the pack field returned by the second Gree Soyal device (might be a newer model!!). I've tried to apply padding, using Python's PKCS7(128).padder(), it doesn't crash anymore but the decrypted data seems garbage and not a valid JSON.

Are the new Gree models make use of other generic keys? Can you, please provide some guidance, on how to investigate further?

Thanks!

tomikaa87 commented 2 years ago

@marius-nicolae please try the script with --verbose and paste its output here.

python3 gree.py --verbose -b <broadcast address> search

marius-nicolae commented 2 years ago

As I wrote in the other issue ( https://github.com/tomikaa87/gree-remote/issues/48#issuecomment-1231300348 ) the --verbose flag doesn't add much of a detail:

python3 ./gree.py search --broadcast 10.0.5.255 --verbose
Searching for devices using broadcast address: 10.0.5.255
Traceback (most recent call last):
  File "./gree.py", line 245, in <module>
    search_devices()
  File "./gree.py", line 109, in search_devices
    pack = json.loads(decrypt_generic(resp['pack']))
  File "./gree.py", line 73, in decrypt_generic
    return decrypt(pack_encoded, GENERIC_KEY)
  File "./gree.py", line 66, in decrypt
    pack_decrypted = decryptor.update(pack_decoded) + decryptor.finalize()
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/primitives/ciphers/base.py", line 159, in finalize
    data = self._ctx.finalize()
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/backends/openssl/ciphers.py", line 178, in finalize
    raise ValueError(
ValueError: The length of the provided data is not a multiple of the block length.
tomikaa87 commented 2 years ago

@marius-nicolae I've updated the script to print more details before trying to decrypt the response. Please run the verbose search command again and paste the whole output here. It should print the raw response packet now.

marius-nicolae commented 2 years ago

Indeed, I was running an older version. Here is the new output:

python3 ./gree.py search --broadcast 10.0.5.255 --verbose
Searching for devices using broadcast address: 10.0.5.255
search_devices: data=b'{"t":"pack","i":1,"uid":0,"cid":"502cc697d12f","tcid":"","tag":"UO+i92tMrddr39Az5C/S7w==","pack":"JtoKliwtrZWlpNCVOSARFZVjvdMQgUTwOVMwDOiNhOTTdG5N10OhtRdqKHNblRpBHGBvzVlwLvpUoMiLLqrWjKOY3wm/f0Jpc5AgXOKjc9kcuKHBT9Com8Xm5Ml/trwJCr+4qnpNA+LDG3HzzAut6DajQD0Unesye5fOVuITjYRj/S48EhXvMPf+uFR7BOq8wq3jRUvZweGu4Xuu19I3Y3C7wX1l3Q+V4pXFeTxudKE6Tqj62cJ3P6Mpx+gX8F0RFP+PoO155Efh8BcvfyRatHw="}\n', raw_json=b'{"t":"pack","i":1,"uid":0,"cid":"502cc697d12f","tcid":"","tag":"UO+i92tMrddr39Az5C/S7w==","pack":"JtoKliwtrZWlpNCVOSARFZVjvdMQgUTwOVMwDOiNhOTTdG5N10OhtRdqKHNblRpBHGBvzVlwLvpUoMiLLqrWjKOY3wm/f0Jpc5AgXOKjc9kcuKHBT9Com8Xm5Ml/trwJCr+4qnpNA+LDG3HzzAut6DajQD0Unesye5fOVuITjYRj/S48EhXvMPf+uFR7BOq8wq3jRUvZweGu4Xuu19I3Y3C7wX1l3Q+V4pXFeTxudKE6Tqj62cJ3P6Mpx+gX8F0RFP+PoO155Efh8BcvfyRatHw="}'
Traceback (most recent call last):
  File "./gree.py", line 245, in <module>
    search_devices()
  File "./gree.py", line 109, in search_devices
    pack = json.loads(decrypt_generic(resp['pack']))
  File "./gree.py", line 68, in decrypt_generic
    return decrypt(pack_encoded, GENERIC_KEY)
  File "./gree.py", line 62, in decrypt
    pack_decrypted = decryptor.update(pack_decoded) + decryptor.finalize()
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/primitives/ciphers/base.py", line 159, in finalize
    data = self._ctx.finalize()
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/backends/openssl/ciphers.py", line 178, in finalize
    raise ValueError(
ValueError: The length of the provided data is not a multiple of the block length.
bwibwi13 commented 2 years ago

Hello, I face the same issue with a newly installed model. Is there anything I can do to help progress on this problem? Thanks a lot for this great repo ;)

tomikaa87 commented 2 years ago

Unfortunately, I've disassembled the latest version of the Android app but didn't manage to find out if there is a new encryption key for these new units. There is a libGree.so library which I've tried to dissect with Ghidra, but it only finds a huge number of randomly named functions without any instructions in them, which doesn't seem to be right. Last time I've found the generic encryption key in the same library. If you have the time, you could try to disassemble the app.

tg44 commented 2 years ago

I tried doing this, my thought process was; I should find the old key somewhere for backward compatibility, if I found it, I will find the new key somewhere around. This was the first time I used disassemblers. I had no success with the latest version.

I found the already known key in an 1.7 version. As I understand this list they did some drastic changes in 1.8.0, going further they consolidated the imports (number of so-s in the lib dir) too, but the disassembled code looks kinda the same in 1.8 and 1.11 (lot of function with strange names).

The TOSOT+ seems as a rebranded GREE+ after a disassembly.

I also tried the invmateii app, but as I understand the code that interest us is probably in the java/kotlin land. (I found it mentioned in an other issue.) I'm almost sure that this app is a rebrand of the midea app. And after checking their site I'm not convinced that they do anything with Gree.

Do we have firmwares available for decompile too?

tomikaa87 commented 1 year ago

After a few months of pause, I took down the project from the shelf and tried to approach the problem from a different angle. Instead of trying to disassemble the Android app, I'm dissecting the iOS version. Since the GREE+ app can be installed on an M1 MacBook from the AppStore, I can access the internals of the app. I've started looking into the main executable with Ghidra and found a few different encryption and decryption methods.

One of them looks like this (decompiled Swift/Objective-C code, the function and variable names are added by me by analyzing the calls and contexts, so they can be inaccurate):

  dataObject = 0;
  __stubs::_objc_storeStrong(&dataObject,dataParam);
  local_88 = param_5;
  key = msgSend_class(local_68,dataToDecrypt);
  cancelPreviousPerformRequestsWithTarget(key,dataToDecrypt,local_68);
  __stubs::_NSLog(&::cf_=);
  key = dataObject;
  if (*(int *)(local_68 + 0x10) == 1) {
    length = getLength(dataObject,dataToDecrypt);
    length = length + -0x27;
    local_58 = 0x27;
    local_50 = 0x27;
    local_a0 = 0x27;
    local_60 = length;
    local_48 = length;
    subdataWithRange(key,dataToDecrypt,0x27,length);
    key = __stubs::_objc_retainAutoreleasedReturnValue();
    local_90 = key;
    stringWithFormat(&_OBJC_CLASS_$_NSString,dataToDecrypt,&cf_%s);
    uVar1 = __stubs::_objc_retainAutoreleasedReturnValue();
    AES128DecryptData:WithKey(&objc::class_t::SecurityUtil,dataToDecrypt,key);
    local_a8 = __stubs::_objc_retainAutoreleasedReturnValue();
    __stubs::_objc_release(uVar1);
    ::socket(local_68,dataToDecrypt);

I've tried to translate this to a python code and feed the data pack @marius-nicolae found:

def decrypt_aes128ecb(data, key):
    decryptor = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()).decryptor()
    decrypted = decryptor.update(data) + decryptor.finalize()
    return decrypted

tag = 'UO+i92tMrddr39Az5C/S7w=='
pack = 'JtoKliwtrZWlpNCVOSARFZVjvdMQgUTwOVMwDOiNhOTTdG5N10OhtRdqKHNblRpBHGBvzVlwLvpUoMiLLqrWjKOY3wm' \
       '/f0Jpc5AgXOKjc9kcuKHBT9Com8Xm5Ml/trwJCr+4qnpNA+LDG3HzzAut6DajQD0Unesye5fOVuITjYRj/S48EhXvMPf' \
       '+uFR7BOq8wq3jRUvZweGu4Xuu19I3Y3C7wX1l3Q+V4pXFeTxudKE6Tqj62cJ3P6Mpx+gX8F0RFP+PoO155Efh8BcvfyRatHw='

dpack = base64.b64decode(pack)
print('dpack', dpack.hex(','), len(dpack))
data = dpack[0x27:]
print('data', data.hex(','), len(data))
key = dpack[:16]
print('key', key.hex(','), len(key))
clear = decrypt_aes128ecb(data[:16], key)
print('clear', clear, len(clear))

Unfortunately I only got garbage as output, so something is not right. But the fact that these new packs can't be decrypted due to alignment issues proves that they attach some kind of extra data to the encrypted blocks and that extra info can be the decryption key for the scan response.

I've also found a few new master keys in the code:

a3K8Bx%2r8Y7GREE

// This was found in the context of "Gree Business" devices
WQNMLGBDGREECTMD

// These were obfuscated in the code and seem like used by Qualcomm devices
gvhL/#)^4s9t*255
{yxAHAY_Lm6pbC/<

In addition I've found out that devices which have tag in their reponses use a seemingly complicated method to decrypt the data. According to the code, these are some kind of Qualcomm-based devices (the string "$bMqualcomm-test_101108551" in one of the functions makes me think that).

I can share the Ghidra disassembly project if someone wants to experiment.

tomikaa87 commented 1 year ago

@tg44 The pack you've posted on the other issue seem to be decryptable with the generic key using the old method:

Any progress on this? I have the same issue, I can send the broadcast, get a response, but the
pack: 'LP24Ek0OaYogxs3iQLjL4BZGC1L9UK8LWYY9r9h4dgWMa9lM2RqI/KytvJ32IsGSZXrOr+MakVzzXHbghPeyijnWMzaLQaaw1aFXlE9k71L0cMm8bsr/y4FkxumpRg1t0xV8+/m47OTBNaX/8aUl1ZJhYuNQNgXxv5Sro8mBB9BzMQoS41XpnORSG7+GfavhnKYbt0iIDsdp8/ftXlA9Hi9SYH2dzE8EeLZzuqwrQT280gq9HxK8Loa8WXVjgZcP4Vf5MjKxa60Xt5J1oI+lsxUuXTHkgunLg76WWGy+euo='
has "invalid key len"
b'{"t":"dev","cid":"502cc65816e6","bc":"000000000000000000000000000000","brand":"gree","catalog":"gree","mac":"502cc65816e6","mid":"10001","model":"gree","name":"c65816e6","series":"gree","vender":"1","ver":"V1.2.1","lock":0}\x01'
tg44 commented 1 year ago

Nice! How did you do that?

Drjoachim commented 1 year ago

@tg44 ; the pack was decryptable with the old generic key

import base64
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

def create_cipher(key):
    return Cipher(algorithms.AES(key.encode('utf-8')), modes.ECB(), backend=default_backend())

if __name__ == '__main__':
    #your pack from the other issue
    encryptedPack = 'LP24Ek0OaYogxs3iQLjL4BZGC1L9UK8LWYY9r9h4dgWMa9lM2RqI/KytvJ32IsGSZXrOr+MakVzzXHbghPeyijnWMzaLQaaw1aFXlE9k71L0cMm8bsr/y4FkxumpRg1t0xV8+/m47OTBNaX/8aUl1ZJhYuNQNgXxv5Sro8mBB9BzMQoS41XpnORSG7+GfavhnKYbt0iIDsdp8/ftXlA9Hi9SYH2dzE8EeLZzuqwrQT280gq9HxK8Loa8WXVjgZcP4Vf5MjKxa60Xt5J1oI+lsxUuXTHkgunLg76WWGy+euo='

    #the old generic key
    key = "a3K8Bx%2r8Y7#xDh" 

    decryptor = create_cipher(key).decryptor()
    pack_decoded = base64.b64decode(encryptedPack)
    pack_decrypted = decryptor.update(pack_decoded) + decryptor.finalize()

    print( pack_decrypted)

Code above prints:

b'{"t":"dev","cid":"502cc65816e6","bc":"000000000000000000000000000000","brand":"gree","catalog":"gree","mac":"502cc65816e6","mid":"10001","model":"gree","name":"c65816e6","series":"gree","vender":"1","ver":"V1.2.1","lock":0}\x01'

Unfortunately this does not solve anything. I have the same issue with my Vaillant Airco Units with a "tag" in their response.

JensVanhooydonck commented 1 year ago

I did find the keys of my v2.0.0 Vaillant wifi-dongles by installing the gree+ app on my m1 macbook, then going to: \~/Library/Containers/GREE+/Data/Documents/room.sqlite There all my devices are listed in the rooms, and the keys are also there. Alternativly you could get them from the deviceTable from the GREE.sqlite file (~/Library/Containers/GREE+/Data/GREE.sqlite)

Skip the following. I used the self encrypted string instead of the response. My fault.



If i then use the following code:

import base64
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

def create_cipher(key):
    return Cipher(algorithms.AES(key.encode('utf-8')), modes.ECB(), backend=default_backend())

if __name__ == '__main__':
    #your pack from the other issue
    encryptedPack = 'hT5up561f1aMbmUJVyFJ7+7BPO7Frf4zKAjcmU8oPRV+pBRtTVSGg4KdOFC46a8LOkd5RiOOCiA/bIKU5qEAMua8wTpJRKluYGd5/ySY5dkg6825qK+ep8m3cHODYaW5EKFmT7MVos3+Ypnw1jxT3WjY9yDU6mZme+FiqgYZxs60BbfxCKJvsaaevtHfH2prJOkWVD888D/GNLhzgGjbrFKO3U07JM/LYb0phZpN5k/763gSxOCZUv7ygMm/ej53+5+Qc0uls0Eh2XsRbDtOBNIMOT6u9L8a+7+gAUpopNU='

    #the key from the database
    key = "s5h0vcJrYgN0D5S9" 

    decryptor = create_cipher(key).decryptor()
    pack_decoded = base64.b64decode(encryptedPack)
    pack_decrypted = decryptor.update(pack_decoded) + decryptor.finalize()

    print(pack_decrypted)

i get the following output:

b'{"cols":["Pow", "Mod", "SetTem", "WdSpd", "Air", "Blo", "Health", "SwhSlp", "Lig", "SwingLfRig", "SwUpDn", "Quiet", "Tur", "StHt", "TemUn", "HeatCoolType", "TemRec", "SvSt", "SlpMod"],"mac":"502cc66d136b","t":"status"}\x06\x06\x06\x06\x06\x06'

I'm trying to use the https://github.com/RobHofmann/HomeAssistant-GreeClimateComponent however the climate interfaces are added, but can't be updated (Update for climate.arico_bedroom fails)


EDIT: After looking around in most of the Cordova code, i have seen that most of the encrypted data is padded with pkcs7. Therefor when unpadding the data afterworths the data seems to be correct:

import base64
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from Cryptodome.Util.Padding import pad, unpad

def create_cipher(key):
    return Cipher(algorithms.AES(key.encode('utf-8')), modes.ECB(), backend=default_backend())

if __name__ == '__main__':
    #your pack from the other issue
    encryptedPack = 'hT5up561f1aMbmUJVyFJ7+7BPO7Frf4zKAjcmU8oPRV+pBRtTVSGg4KdOFC46a8LOkd5RiOOCiA/bIKU5qEAMua8wTpJRKluYGd5/ySY5dkg6825qK+ep8m3cHODYaW5EKFmT7MVos3+Ypnw1jxT3WjY9yDU6mZme+FiqgYZxs60BbfxCKJvsaaevtHfH2prJOkWVD888D/GNLhzgGjbrFKO3U07JM/LYb0phZpN5k/763gSxOCZUv7ygMm/ej53+5+Qc0uls0Eh2XsRbDtOBNIMOT6u9L8a+7+gAUpopNU='

    #the old generic key
    key = "s5h0vcJrYgN0D5S9" 

    decryptor = create_cipher(key).decryptor()
    pack_decoded = base64.b64decode(encryptedPack)
    pack_decrypted = decryptor.update(pack_decoded) + decryptor.finalize()

    print(unpad(pack_decrypted, 16))

Output:

b'{"cols":["Pow", "Mod", "SetTem", "WdSpd", "Air", "Blo", "Health", "SwhSlp", "Lig", "SwingLfRig", "SwUpDn", "Quiet", "Tur", "StHt", "TemUn", "HeatCoolType", "TemRec", "SvSt", "SlpMod"],"mac":"502cc66d136b","t":"status"}'
lovery commented 11 months ago

@tomikaa87 could you share the Ghidra disassembled project with me, I would like to try my luck.

eibenp commented 9 months ago

@lovery did you have any luck in disassembling the encryption protocol of packates with tag field?

eibenp commented 9 months ago

@tomikaa87 Szia! Készítettem egy Homebridge GREE plugint, de most összefutottam egy olyan felhasználóval, akinek GREE Wifi modul van a klímájában, és nem működik a dekódolás AES-128-ECB segítségével egyik kulccsal sem, ami ebben a beszélgetésben található. Jellemző. hogy az a modul is használja a tag mezőt. Ez a válasz jön a scan kérésre:

{"t":"pack","i":1,"uid":0,"cid":"502cc66cc89e","tcid":"06470c52c566","tag":"DW39Z7ZlxSgR/g1SddnjpQ==","pack":"JtoKliwtrZWlpNCVOSARFZVjvdMQgUTwNgc3BeOOhOTTdG5N10M5OI3w9aCGCJffjfuyCITofrMT4JbII6+A1+2Qyk7gfwk5dZR2EayhdZgEoOSGGofp1NG95slltfseFq+2oChWDqTDGSfh0Qvsoz/uHnpJj7cgLseHEa1Qy49usnE8T0XpY+Ox+QIjDK2y2vzlARuL1vKmpT7wkMRwPTuo1zE7mhrFvdLWdzI6Z6osCeD6tdJoLaE7k6FHvghQKe+boL4="}

Találtál már esetleg valami megoldást? A GREE+ app simán működik az eszközzel, az EWPE Smart App is működik. A Wifi modul típusa: GREE GRJ532-J14

Tudsz esetleg segíteni? Köszi, üdv

maxim-smirnov commented 8 months ago

Hi everyone! I've got some firmware files, tried to find new crypto algo in the firmware itself, but with no luck. Here are firmwares which I got. https://github.com/maxim-smirnov/gree-wifimodule-firmware

KiralyCraft commented 6 months ago

Well, I've been everywhere today, I hope it wasn't too invasive. I tried to link these issues such that we can keep track if them in a central place. Thanks to Maxim's suggestion, I managed to get my device's firmware. Fortunately, the version I have contains a LOT of information. Specifically, it is about the U-WB05RT13V1.21-4307516678339185323.bin file which you can find in this PR: https://github.com/maxim-smirnov/gree-wifimodule-firmware/pull/1

In this file, you can find the [I]: create_DeviceKey :%s string, which is situated in the data region of the binary, right adjacent to protocol-related stuff, such as InEvaTem, CompressorTem, CompressorFqy and other attributes you can query. My thought is that these strings are placed there, near eachother, because they belong to the same function (or they are at least close). In Maxim's issues I documented my findings, but I got stuck trying to figure out which where is the string referenced. I assume I got something wrong in Ghidra, but this is where my expertise fades. If anyone can help out with this particular task, please do.

The device itself seems to run FreeRTOS v10.0.1 and has been built with a AmebaZ2 RTL8710C (although the FCC pictures show a RTL8720CF). The language for Ghidra is Cortex 32-bit Little Endian, and you need to turn on ARM Aggressive Instruction Finder for better results.

UPDATE: It seems that right before the data region there's a function which seems to take a 32-bit argument (probably an int index) and return the corresponding string data from the "ds". Therefore, the string we need might be referenced somehow by ID. It crossed my mind, that the SDK they use is now open source, as well as the FreeRTOS. Could it be possible to build a sample firmware with debugging symbols, and check to see how it's structured, to figure out if we have similar functions?

KiralyCraft commented 6 months ago

Good news - Maxim managed to find a firmware which contains an AARCH64 version of the Gree communication thing. The best news is that it has debugging symbols! The binary is NOT stripped!

The old key can be found inside. It's at https://github.com/maxim-smirnov/gree-wifimodule-firmware/issues/2#issuecomment-2072925748

EDIT: Looks like there is an update pending, version 1.53 which solves these problems. With this, it seems that my units use the key a3K8Bx%2r8Y7GREE:

asyncio - DEBUG - Using selector: EpollSelector
__main__ - DEBUG - Scanning network for Gree devices
greeclimate.discovery - INFO - Scanning for Gree devices ...
greeclimate.discovery - DEBUG - Listening for devices on 192.168.0.255
greeclimate.network - DEBUG - Sending packet:
{"t": "scan"}
greeclimate.network - DEBUG - Received packet from 192.168.0.245:
{"t": "pack", "i": 1, "uid": 0, "cid": "", "tcid": "", "pack": {"t": "dev", "cid": "9424b8b6b21f", "bc": "00000000000000000000000000000000", "brand": "gree", "catalog": "gree", "mac": "9424b8b6b21f", "mid": "10001", "model": "gree", "name": "", "lock": 0, "series": "gree", "vender": "1", "ver": "V3.0.0", "mqttVer": "V3.2.M", "hid": "362001065279+U-WB05RT13V1.53.bin"}}
greeclimate.discovery - INFO - Found gree device Device: 9424b8b6b21f @ 192.168.0.245:7000 (mac: 9424b8b6b21f)
__main__ - INFO - Attempting to bind NOW!
greeclimate.device - INFO - Starting device binding to Device: 9424b8b6b21f @ 192.168.0.245:7000 (mac: 9424b8b6b21f)
greeclimate.device - INFO - Bound to device using key a3K8Bx%2r8Y7GREE
greeclimate.network - DEBUG - Sending packet:
{"cid": "app", "i": 0, "t": "pack", "uid": 0, "tcid": "9424b8b6b21f", "pack": {"mac": "9424b8b6b21f", "t": "status", "cols": ["hid"]}}
greeclimate.network - DEBUG - Received packet from 192.168.0.248:
{"t": "pack", "i": 1, "uid": 0, "cid": "", "tcid": "", "pack": {"t": "dev", "cid": "9424b8b6b1e9", "bc": "00000000000000000000000000000000", "brand": "gree", "catalog": "gree", "mac": "9424b8b6b1e9", "mid": "10001", "model": "gree", "name": "", "lock": 0, "series": "gree", "vender": "1", "ver": "V3.0.0", "mqttVer": "V3.2.M", "hid": "362001065279+U-WB05RT13V1.53.bin"}}
greeclimate.discovery - INFO - Found gree device Device: 9424b8b6b1e9 @ 192.168.0.248:7000 (mac: 9424b8b6b1e9)
__main__ - INFO - Attempting to bind NOW!
greeclimate.device - INFO - Starting device binding to Device: 9424b8b6b1e9 @ 192.168.0.248:7000 (mac: 9424b8b6b1e9)
greeclimate.device - INFO - Bound to device using key a3K8Bx%2r8Y7GREE
greeclimate.network - DEBUG - Sending packet:
{"cid": "app", "i": 0, "t": "pack", "uid": 0, "tcid": "9424b8b6b1e9", "pack": {"mac": "9424b8b6b1e9", "t": "status", "cols": ["hid"]}}
greeclimate.network - DEBUG - Received packet from 192.168.0.131:
{"t": "pack", "i": 1, "uid": 0, "cid": "", "tcid": "", "pack": {"t": "dev", "cid": "9424b8b6b00f", "bc": "00000000000000000000000000000000", "brand": "gree", "catalog": "gree", "mac": "9424b8b6b00f", "mid": "10001", "model": "gree", "name": "", "lock": 0, "series": "gree", "vender": "1", "ver": "V3.0.0", "mqttVer": "V3.2.M", "hid": "362001065279+U-WB05RT13V1.53.bin"}}
greeclimate.discovery - INFO - Found gree device Device: 9424b8b6b00f @ 192.168.0.131:7000 (mac: 9424b8b6b00f)
__main__ - INFO - Attempting to bind NOW!
greeclimate.device - INFO - Starting device binding to Device: 9424b8b6b00f @ 192.168.0.131:7000 (mac: 9424b8b6b00f)
greeclimate.device - INFO - Bound to device using key a3K8Bx%2r8Y7GREE
greeclimate.network - DEBUG - Sending packet:
{"cid": "app", "i": 0, "t": "pack", "uid": 0, "tcid": "9424b8b6b00f", "pack": {"mac": "9424b8b6b00f", "t": "status", "cols": ["hid"]}}
__main__ - INFO - Done discovering devices
JensVanhooydonck commented 5 months ago

I'm still having this issue.. I'm stuck on Firmware 1.08 (Vaillant) also tried to update firmware with an edited apk of gree+, still no updates.

http://test.grih.gree.com/wifiModule/Lastversion?firmwareCode=362001062107

tomaszduda23 commented 5 months ago

tag - this could be authentication tag for AES-GCM or AES-GCM-SIV.

tomaszduda23 commented 5 months ago
from Crypto.Cipher import AES
import base64

def mbedtls_gcm_auth_decrypt(key, iv, add, tag, encrypted_data):
    cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
    cipher.update(add)
    try:
        decrypted_data = cipher.decrypt_and_verify(encrypted_data, tag)
        return decrypted_data
    except ValueError:
        print("Incorrect decryption or tag verification failed")
        return None

def main():
    key = b'need a key'  # 128bit long key
    iv = b'\x54\x40\x78\x44\x49\x67\x5a\x51\x6c\x5e\x63\x13'
    add = b'qualcomm-test'
    tag = base64.b64decode('YOUR_BASE64_ENCODED_TAG')
    encrypted_data = base64.b64decode('YOUR_BASE64_ENCODED_ENCRYPTED_DATA')

    decrypted_data = mbedtls_gcm_auth_decrypt(key, iv, add, tag, encrypted_data)
    if decrypted_data is not None:
        print("Decrypted data:", decrypted_data.decode('utf-8'))
    else:
        print("Decryption failed")

if __name__ == "__main__":
    main()
tomaszduda23 commented 5 months ago

New algorithm was added her https://github.com/RobHofmann/HomeAssistant-GreeClimateComponent/pull/165

KiralyCraft commented 5 months ago

This is really cool, people confirmed it works on multiple versions. What made you believe the tag was an AES tag? How did you figure it was one of those variants specifically?

I'm curious about the actual research behind the discovery, it's been a long battle.