humbertogontijo / python-roborock

Python library & console tool for controlling Roborock vacuum
GNU General Public License v3.0
64 stars 30 forks source link

V2 Home Data api #155

Open Lash-L opened 11 months ago

Lash-L commented 11 months ago

Seemingly Roborock has rolled out a new api version for the /user/homes/{home_id} request. It is now /v2/user/homes/{home_id}

It uses the same hawk authentication method, butI have been getting invalid token error when I have been trying. I will continue to figure out why, but if you wanted to take a look as well @humbertogontijo, more eyes would be nice.

Basically we want this for 2 reasons:

1) In case they depreciate v1 of the api

2) Some of the newer non-robot vacuum devices are received from this api.

Let me know if you figure anything out

humbertogontijo commented 11 months ago

Hmm.. I wonder if the app is already using it

Lash-L commented 11 months ago

Hmm.. I wonder if the app is already using it

The app is using it and I can see it with mitm. But I can't figure out what the difference is in terms of the request headers for the one we do now and v2. I tried adding a few of the additional requests, but nothing would accept.

The response seems to be the same as well, except that it also gives the new devices as well

Lash-L commented 11 months ago

Next piece of the api that I have noticed that has changed:

/api/v1/login is now /api/v3/auth/email/login

And the request is

        params={
            "email": self._username,
            "password": password,
            "twoStep": 1,
            "version": 0
        },

Where password is some encrypted version of the password I have yet to figure out

I am trying to figure out if code login has changed as well, but it isn't ever asking me for a code for some reason

Lash-L commented 11 months ago

I got home data working for v2 now - I hvae not figured out v3 login though

Lash-L commented 11 months ago

If you're up for a challenge, I've been trying to RE the dyad vacuums, I was able to get the following set of bytes with the following local key: 'ELSYN0wTI4AUm7C4'

It's been so long since i've touched any of the decryption logic, I honestly forgot how a lot of it works, if you're up for it, i'd be interested to see if you would have more luck @humbertogontijo

I tried changing all of the version instances of 1.0 to A01, but that didn't make a difference

b'A01\x00\x00\x0e\xa6\x00\x00\x0e\xa6ev5X\x00f\x00@$\xc4\x08\x08\x9b\x87D\xe0\xc1\xf9F\xf2\xc7\x9d\xc3?\xb3E\x08\x96\x06\x8d7\xb0\x1c\xc0\x9d`\x0c\xf0\x830\x1a\x90\xe0Y\xc9\xc6\xa9T}\xe8\x92\xfa`\xc3\xc8\xa9\xc2 \xc5N(\xe5\xec\x05]j\xe7\xf2\x0f1\xa4\xdb\xe1\x8b\xc5\xf5'
b'A01\x00\x00\x0e\xaf\x00\x00\x0e\xafev7x\x00f\x000\x90`OY\xcc\x7f\xb4\xc8\x1b\x9a\x9e\xc5Xy\x1e\xf11\x00\xbb \x1d\x9a\x81\xfcs\xa8+\x0e{!:\xa4`>.\x9f\x1aGq\xa4\xfe\xf8\xb8\xa9F\x7f*\xe2\xfbq#\xd8'
b'A01\x00\x00\x0e\xb1\x00\x00\x0e\xb1ev7y\x00f\x01\xc0\xf5/\xd4P`\x1e.\xdde\xe7\x16\xcc\x054{g\x8d\xde\xe1_\xdc:\xdc(-\xa2\x85\\\x90\x01\x17p\x0bK\x08#"uUbZ\x8f\x92\xd99\xf4\xf9\x8e\x86p\xdb\xa8~\xa1\x8cF\xff\'\x08h\x9f\xac3\x18\x03\xc2\xbd\xcca\xf5TI\x87\x89\x00\x05\x80\x92\x8b\xce?\x86\x02\xf3\x04\x10-\x89S\xea\x9f[\x8d\xb7\x82Lo/\x9a\x1aC\xd3@&\xf2D\xe3\x19\x12B\x92k\xc2|\xef\x97\xd9<\xdf"\xc7\xee\x01\x1a\x9f\xc8\x8f\x8b\xba\x8b~\xe34\xfe?\x1e\x00x\x13\xee0\xee|\xf4\xd9\xf1\x8c^\\\x11~\xb6\xaa\r\xc7\xf6\x9e\xfec\xd3\xe4H@{\xa6;-\xce\xff\xcf\xab\xfbk\xf8\xc6<{lGM\xd7d\xc3\x14E\tX,\xc3\xca3\xeeG\xa6\xc4\xdb#\x9f7^32\xaa\x8d$\x18 2\xa9\x88n\x96q\xe34&$\xff)\x19\x8d\xce\xd7"\x9d\x90\x08n\x16\x8ab\xd2\xc6:5\nP\x17\xb9\xe3\xbed\x8bM0\xef\xa5d\xb4\x18\x0c$\xc1\xe0\xa4Kc\x12px\xfc\xe2"\xd5`\xb3.\xb5\x05y1\xf5O\r\xf4?c\x885\xba\x9a\x08\xd7\xb3\x06E\xcdEIbk\xe1\n\xe6\xbf\xa7(d\xb8!C?&+\xabj}1\xf9Va\x8fN\x17\xd0`\xca\xb9\xf5\x8a\xcak4\x85\xb1\xdf\xac\xf8\x93\xbc\xee\x07\x84\xe7JT\xdf\rx\\(u\x8a\xac\xee\xe7l\xb1@\xd8\xbc\xad\xd5\x14b\xe7\x83Wj\x9ezY\x12P:xQ\x81\x00\x04\xdb\xcc\xa3\x84Q\xc5\xb6\xc54\xc0e\x95\x85\xccR\x95\xaa\xea%\xb5\x8f\x99\xb7\xf5[\xa6NBk\xd0A\x96Q\x00c8pqD{(\x0f\xd6\x97\xec\t\xde\x90"\xb0d\xbe\x1bU\xb9\x17\x19\xdd\x07s\xe2\x1d\x16PQF\xd3K<H\x10|\xc76\xdb\x11J/s\xa7\xe6\x7f'
b'A01\x00\x00\x0e\xb3\x00\x00\x0e\xb3ev7y\x00f\x00\xf0\xfe\x0c\xed\xe4\xff\x91\x83\xce\xadz\xac\'\x04s\x80\xda\xbe@\xd1\x13\xb02\xe4{\x18a\xbf=\xa7\x85+\xcc\x8a\n\xb2\xfa\xbb\x06\x16\x8ed\xfa\xadT.\xe8|\xae=?\x00m\xc6iC\xb2\x07\xc2\x0b2Z\x05\xa6\xdc\xd1BhF\x147\x9c\xd3\x04Bx\x1b\x84XR\x8dpy\xe1L|\xd5]\x98\xba\x87\xc0\x17\xd8\xa5\x11H\xae}0\xac)\x02\xe5\x8c{A3v/?\xfa6o\xcfL\x8db\xb6\xde\xa4\xe9n\xcf\x13\xbf\xe1\xdf\x01r\xc9\xd3\'\xff\x8ePOE\x19.Bx\xe6s+C\xf9\xefil0\xf4\x9e\xca\x82 \xf6\xb9c+9z\xcd\xc6X\x0c\x80[\xf6\xdd\xc4\x99\x91&w\xad\x11\x9d\xae\xe7\xca\x80"\x91\xa9\xbb\xd2\xdb\xbd\xa7`\xcc\xdca\x1cq,\x90W.\x8cq*\xbe\xa9\x96H\xd4\x0e.s\xf2\x8d\x8f \xf7\x83\xdek\xeb\x7fHq\xa5\xeczF\xb2O^\xf4\xb6\xedVQ\x14W9]\xb8S=J4\x92'
b'A01\x00\x00\x0e\xb0\x00\x00\x0e\xb0ev7x\x00f\x00\x90\xb4\xb1\x8b\xd8\x91\xf42\x919\xee\xe8\x83&\xcb`\xc9\x89Hi\xce2h\x83\xe6\x95\xa3\xab\xfd)}v\x97\x9dh\xdd\x1e\xcf)\xba#\xb5\xacl\x9cT@|!d\x94!$V\x99$ \x1e{\\o\xbbQ&\xcd\xf1J\x079pk\xbb\x08\xbfPC4H\x16\xa7\xc3\x8bg\xfd\xb5\x83\x92G\xf9\x8c\x99\xde\xfe\x0c_\xa3\xc1\x94\xff$r\xbf\xcbO\xd6\x84"[d\x92\xb4\xednx\x1c\xc1S\xe4\xb7\x1e\x0fj\xe45\xac\x98\x9bz\xf5"T\x8c\x03\xfb\xb7_\xb9\x97F\xbf\xc6\x81Uj6\xf4S\xacH'
b"A01\x00\x00\x0e\xb2\x00\x00\x0e\xb2ev7y\x00f\x00`\xfc\xa7\x9f\xe8n\xf5\x07\x8a\xf7\xc5qy\x10\xda\x99R\xb2 \xe1\x1b>\xc1\x87^\xd4\xa5W|\xa6\xe9a\xebh+\xc8\xab\xe5\xc9\x1f\x8f\xee\x04I'\xe5\xc6\x85\xc1\x89=9{\x8bA\rlO\x1a\x07\xa3\xf4F\x8d\x0b~\xc9\xde\x8c<)\xf5{\xf6Q\xb8\xa4wA\xf9\xbb3`\xa6\xa5\x97\xa47\xb9\x1eX~\x8a\x07U\x87\xe00\x10Tb"
b'A01\x00\x00\x0e\xb4\x00\x00\x0e\xb4ev7y\x00f\x00\xc09\x06\xaeN\n\r\\\x13y\xa3%\x1e\re\xd8vD\xa4\x1e)x\xd8dY8S\xb6y\xe4R\xae\xe9\xf1\xefe\xac\x01\xf06\xaf`\xeaE\x1a\xa4\xe55[\xc1\xf7\xe3\xd7X\x81\xcf\x0c\x9a\xa1q\x95*\xb9\xe7v/\x16A\xb7\x01\x1f2\xfe\xe2\xe0Xs\x97S\x9e\xf0\xc0\xf1\xf4\x05\xe4z\xde\x97\xec!\xb1\xe3>\xf4\x98\xcf-\xee3+\xa2Q\xcb\x8a\x934l\x01B\xa8\xb9\xfb\x9a&\x10e\xc0\x8c"\xb4\xcb\x12S-\xcd\xbeNP\xf3\x13v\x9b2\n\x84\x996\'\xd2`\x1d\xdf\x89\x96rbT\xac\xa7\xa4\xc3R\xe6T\x92\xccI\xcf\xf3HQ\xf1U\xdar=\x85E\xc7\xf2e+\xa6\x06\xb7\xa9\xd2\x08\xc68y4\x9e\'\xb2\x01\x07\x83\xca\xe2\xc6\x17\xc5Hr\xc0'
humbertogontijo commented 11 months ago

I changed every 1.0 to A01 in protocol.py file. All info except payload looks correct. Which means the key might be different from

Utils.md5(
    Utils.encode_timestamp(ctx.timestamp) + Utils.ensure_bytes(ctx.search("local_key")) + SALT
)

Could be a different salt

Lash-L commented 11 months ago

How did you determine salt when you original reverse engineered it?

Lash-L commented 11 months ago

For what it is worth, api seems to point towards rpc beeing a different code:

                {
                    "code": "rpc_req",
                    "desc": null,
                    "id": "10101",
                    "mode": "wo",
                    "name": "rpc req",
                    "property": null,
                    "type": "STRING"
                },
                {
                    "code": "rpc_resp",
                    "desc": null,
                    "id": "10102",
                    "mode": "ro",
                    "name": "rpc resp",
                    "property": null,
                    "type": "STRING"
                }
humbertogontijo commented 11 months ago

I got salt from decompiled android app

Lash-L commented 11 months ago

I got salt from decompiled android app

oh it was just in plain text? Interesting, i'll go searching through and see if i can find another

Lash-L commented 11 months ago

Looks like it is here: https://github.com/Lash-L/roborock_decompiled/blob/74802b6d018c2becdf8b7efe281937307ef10adf/sources/roborock/sawmill/common/msg/Msg%24LogRequest.java#L128 Mind taking a look and telling me what you think?

humbertogontijo commented 11 months ago

Previously that was some files that hold these constants like salt and broadcast token. But I couldn't find them in your decompiled version

Lash-L commented 11 months ago

I used http://www.javadecompilers.com/apk Did you use something different?

humbertogontijo commented 11 months ago

I don't think so. It was probably just an older version of the app. I did it twice. On January this year and at the time we managed to get local integration working. Both times those variables were available as plain text

Lash-L commented 11 months ago

I see broadcast token here: https://github.com/Lash-L/roborock_decompiled/blob/b9a0741fae7f8e74152d3456e83155a2c2ccfbd2/resources/AndroidManifest.xml#L91

Here is it in a version from January:

https://github.com/Lash-L/roborock_decompiled/blob/c07b03f52d00f69cf52d22827288f2436a5d24bb/resources/AndroidManifest.xml#L129

I don't see anything about the salt though

humbertogontijo commented 11 months ago

I remembered it. I got it from here https://gist.github.com/rovo89/dff47ed19fca0dfdda77503e66c2b7c7#file-test-js-L28

LukeSerne commented 9 months ago

I remembered it. I got it from here https://gist.github.com/rovo89/dff47ed19fca0dfdda77503e66c2b7c7#file-test-js-L28

Based on the comment in this file, I looked into librrcodec.so. I identified several relevant functions, but unfortunately, the code for the more complex functions is a bit hard to follow because control flow obfuscation was used. There are 2 authentication functions, and their behaviour seems very similar.

Before I spend more time looking into how both authentication methods work, I wanted to check if there is already some work done on this? Maybe someone already figured out how the new authentication works, or made significant progress. It'd be a shame to repeat work that has already been done. Is there a community where these things are discussed?

Lash-L commented 9 months ago

I remembered it. I got it from here gist.github.com/rovo89/dff47ed19fca0dfdda77503e66c2b7c7#file-test-js-L28

Based on the comment in this file, I looked into librrcodec.so. I identified several relevant functions, but unfortunately, the code for the more complex functions is a bit hard to follow because control flow obfuscation was used. There are 2 authentication functions, and their behaviour seems very similar.

Before I spend more time looking into how both authentication methods work, I wanted to check if there is already some work done on this? Maybe someone already figured out how the new authentication works, or made significant progress. It'd be a shame to repeat work that has already been done. Is there a community where these things are discussed?

Yes - we have fully Reverse engineered it, I have not taken the time to implement it into this library though. You can add me on discord @conway220