TP-Link Tapo P100

Nice research again @GSzabados!

Unfortunately I do not own tapo plugs, so I am not able to work on this. I stick with the well supported tplink hs110 with api available.

1 Like

Getting API reverse engineered is the best option but Iā€™m using the following combination:

Pair Tapo with Google Assistant, Install assistant-relay (execute google home commands via api), hass.io making requests to assistant-relay.

1 Like

Iā€™ve watched what tapo app is sending and after a while Iā€™m able to generate valid TPIoTRequest which are sent to the p100. (p100 answers with error_code:0 so itā€™s valid)

Now I will try to decrypt the answer from the p100.

edit:
Iā€™m able to decrypt answer from the p100! So when sending login_device request, p100 sends {"error_code":-1501} backā€¦ I can communicate with p100 but my username and password are not correct.

Bingo! Username & Password needed to be base64 encoded + username needs to be a SHA-digest.
P100 answers: {"error_code":0,"result":{"token":"887E98FF98EB90AB1......9D0881A98"}}

6 Likes

Sounds very promising! Great work. :ok_hand:

Do you think this opens the door for a functional integration?

I think I should be able to send on/off requests

1 Like

After that point the P100 communicates by using the token the way how it is in this post:

Thanks for picking up this work and carrying further!

3 Likes

More progress!, Iā€™ve patched the android app with my pre-made key pair, so I can decrypt all information the app is sending/receiving to/from p100. (Itā€™s easier to reproduce pure HTTP requests than searching through decompiled code for requests)

App sends this request to change the state of the plug:

{"method":"set_device_info","params":{"device_on":false},"requestTimeMils":1602840338865,"terminalUUID":"88-54-DE-AD-52-E1"}

I can now craft it dynamically, so I can change the plug state (turn on/off) with my own code!

Edit: Now, itā€™s time to document it and create repository with an PoC.

7 Likes

Great work to all of you guys!
Really incredible what you achieved so far!

1 Like

Definitely easier, great work!

1 Like

Ah, nice technique.

So the plug uses the key pair from the app? Interesting!

Iā€™ll take a look at the Bluetooth packets tomorrow :+1:

1 Like

Yup, Iā€™ve written a post about the connection here: https://k4czp3r.xyz/posts/reverse-engineering-tp-link-tapo/

6 Likes

Great write up!

So, last night i paired a new p100 and i had packet capture enabled, but it failed to capture any packets.
However, while i was looking for the cached firmware, i found what looks like the AES key.

/mnt/sdcard/Android/data/com.tplink.iot/files/model_cache/deco_model/deco_model

its a txt file with two key:value pairs.

{
"DECO_TPSERIALIZEUTILS_AES_KEY":"skf6gYCmI25E1mcE9WvDWG5wxdExTTAtOeNrFN5aui1SMEHK6Udx9XEgsaDL1ifJEPcDce9gHbSB\nnqfAeSdcTvt+JNP7KBxTdGgU64irT2i8pt+uBxhODNmN\/PX5g2PorsnjsUs6op8us8v4xFxWsSp2\ng4jQLdEgbEkluTz8\/I+htZrgUvaV8EUWVy6oz0nSdkG5b8DJutna\/iqJOO75P2AaqSCf8fnLZet6\ntK1zrSa3+GBifnK+J98gjXn6I6kxkPPUWbRA0JTZhLS+JvLEAa4W34+ZggdFo6soeK7Ga\/gGUy1M\nls2xMSihnt9UR7MO6+P3cLKxu6COZzLbO7bPVQ==\n",

"DECO_TPSERIALIZEUTILS_AES_VECTOR":"IDoSrxMmme+YrIwVJ0tv2tI+ZcCYOPMgprpqwPdgsCu\/ncM4WY6BXNN272KaAHkrAlu+nbNHqa51\nxufCO2DsJz6eQjkuq9IUYw9mzeHsgU4\/lsHJmFd2thfdppaHyIGrFlYA5O\/mfC2gMBMSpXKxXq4G\nyhaxKHN7jynghLrHO6i9d3TvoFXPNbHc0+Koo7COIBMHNn6EbNMSlzrSWi7TEoX7NBwPf\/5FZcnL\ntV+d1IrShHeACrmF2D6ufJkXvaPuA3SIZ7lecdzlTn9ixrBWbJ4cZY3V\/2f7d2vTfm9QxDxkorbQ\n0h8DQtQ\/ub59rdcvjEmRfE9tHZwpLUNHmYOBgQ==\n"
}
3 Likes

@K4CZP3R, will you make a custom integration?

2 Likes

I should be able to make a pip package and a custom integration next week. Assuming nobody else wants too before then :slight_smile:

7 Likes

Sounds like @fishbigger will make one :wink:

2 Likes

Hey,

Iā€™m currently working on a pip package for the plug but Iā€™ve hit a problem. I am sending the turn off code to the plug using requests.post

def turnOn(cookie):
	URL = "http://192.168.0.157/app"
	Payload = {
		"method": "set_device_info",
		"params":{
			"device_on": "false"
		},
		"requestTimeMils":1603557345731,
		# "terminalUUID": "1c:3b:f3:a5:71:e5"
	}

	headers = {
		"Cookie": cookie
	}

	r = requests.post(URL, json=Payload, headers=headers)

The request is coming back with

{"error_code":0,"result":{"response":"SCJqlRa0KLL06A3YSoTiqWEMBAHgdD8O6hcwyIw2XUI="}}

But the plug is not turning off (or on for that matter)

@K4CZP3R do you know whatā€™s going on?

Thanks :slight_smile:

Your payload needs to be encrypted and inserted as param in payload with method: securePassthrough (Read ā€œfirst secure passthroughā€ in my write up)

Edit: Iā€™ve started to implement it in Python too. Iā€™m having problem with decryption methods (in Java itā€™s way easier because app is written in Java too). And there are so many formats of encoding etcā€¦

1 Like

Is this just a simple Base64 encode or does it involve another method?

Thanks for all the help :slight_smile:

Itā€™s a encryption using AES.

Ok, Thanks I will try that now :slight_smile:

edit: Whatā€™s the key I should be using? My public and private keys are too long. (Sorry if this is a stupid questionā€¦)

1 Like