TP-Link Tapo P100

The secret and iv is returned after the private key sent to the plug at the really first step.

The returned message is encrypted with the public key and should be decrypted by your private key and then split to two byte arrays, secret and iv. (It is not just straight base64 encoded as many would think with RSA.)

3 Likes

Iā€™ve pushed my working Java code to github. You can look through it to see how it works.

And my partially working python code: GitHub - K4CZP3R/tapo-p100-python: Work in progress implementation of tapo protocol in python. (Iā€™m working on decryption now)

3 Likes

Looking through your code I have managed to create this code based on your method:

from base64 import b64decode, b64encode
from Crypto.Cipher import PKCS1_v1_5, AES
from Crypto.PublicKey import RSA
import hashlib
import pkcs7

def mime_encoder(to_encode: bytes):
    encoded_list = list(b64encode(to_encode).decode("UTF-8"))

    count = 0
    for i in range(76, len(encoded_list), 76):
        encoded_list.insert(i + count, '\r\n')
        count += 1
    return ''.join(encoded_list)

key="Xp0nEoxejAm9R6jM7/3l4mop5TsbKbd8DeEn7n930Mu0kMxulsiFFOGNS5TQzd9QOAzKnBNl24GwaI8uAewtojUSacNV8YZMaKnKhSg8zoii/Ob+yGGDNur/p4v5F6cIN8Blk8KDKkHdR8ZRcMVXJT4Ro8sX31fOmayQst9I7aI="
privatekey = "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCptAlYf3XVIu9+7I3q9quD4zvBPkBm2ec...1DIaKKiJslFU+HrFyq65S/00J+hw=\n-----END RSA PRIVATE KEY-----\n"

key = key.encode("ASCII")
decodedKey = b64decode(key)

cipher = PKCS1_v1_5.new(RSA.import_key(privatekey))

final =  cipher.decrypt(decodedKey, None)

aesKey = hashlib.sha256(final[:16]).digest()
IV = final[:-16]



data = pkcs7.PKCS7Encoder().encode("""
	{"method": "set_device_state",
	"params":{
	"device_on":false
	},
	"requestTimeMils":1603557345731,
	"terminalUUID":"1C-3B-F3-A5-71-E5"}""")
data = str(data)

cipher = AES.new(aesKey, AES.MODE_CBC, IV)
encrypted = cipher.encrypt(data.encode("UTF-8"))
encrypted = mime_encoder(encrypted).replace("\r\n","")

The problem is that when sending it through with the secure passthrough method Iā€™m still getting the

{"error_code":0,"result":{"response":"O+fvbk+c5nOYR/OTR0aqU4OhrF46B4dnb6IlZfro3kY="}}

but the plug is not turning off.

Thanks so much for all the help on this :slight_smile:

What is this response, if you decode it?

Do you use the TP_SESSIONID as cookie?

Cookie: TP_SESSIONID=4C22EA2D8FDDD4DA6EAAC3A916994898

Do you use the token as part of the URL as in this post?

Sorry! Iā€™ve written python implementation too fast and Iā€™ve made really stupid errors.
Iā€™ve updated my python code and it is working now (handshake, login and changing state)!

I would suggest using my repository for the pip package because it already works and only needs some cleanup / further implementation.

3 Likes

@K4CZP3R and @fishbigger thank you so much for all the hard work putted into this :slight_smile: I cloned @K4CZP3R repo, yesterday night and Iā€™m currently trying to get it to work with my plugs :slight_smile: Correct me if Iā€™m wrong, but only the main.py file needs to be customized? Iā€™ll try to interpret the code, but tbh my programming and python skills arenā€™t awesome!
Once again thanks for all your hard work!

1 Like

Yes, you need to change ip and tplink account email and password in main.py

1 Like

Is the MAC also mandatory? Iā€™m getting an erro while fetching the result:

Traceback (most recent call last):
  File "main.py", line 8, in <module>
    my_bulb.login_request("******@gmail.com", "***********")
  File "/mnt/c/Users/pedro/tapo-p100-python/p100.py", line 107, in login_request
    self.token = decrypted['result']['token']
KeyError: 'result'

Going to try to dig a bit deeper

1 Like

try to print decrypted to know the error_code, if itā€™s -1501 then your credentials are not valid.

1 Like

Thanks for the help :slight_smile:
Indeed itā€™s

{error_code: -1501}

Just changed my TAPO account password, and itā€™s the same.
Maybe Iā€™m doing something wrong :confounded:

1 Like

Iā€™ve updated my python repo, if it still does not work: try to use java implementation (for this one, Iā€™m 100% sure itā€™s handles encryption the same way as android app)

1 Like

I tried it out and it works! Great work!

@K4CZP3R I have made a fork where I added a method to get the device state. Unsurprisingly, the method to call is ā€œget_device_infoā€.

The method can return a lot of interesting stuff, like the firmware version, how long the plug is turned on, if itā€™s overheating, etc. For now I only added a method to check if the plug is turned on to the P100 class.

4 Likes

Unfortunatelly Iā€™m still getting the same error code.
Iā€™ve even switched one of my outlets to a new tplink account but I still get the same result.
Out of curiosity, which firmware versions do you guys have on your p100?

Thanks for all the help

1.3.3 Build 20200811 Rel. 52097

Thanks once again @K4CZP3R Iā€™m actually running the same version :slight_smile:
Going to try to dig a bit deeper to understand what Iā€™m doing wrong.

As I follow from the console log:

[p100.py:164 -        login_request() ] Device inner response: {'error_code': -1501}
Traceback (most recent call last):
  File "main.py", line 25, in <module>
    my_bulb.login_request(args.tplink_email, args.tplink_password)
  File "/home/kali/tapo-p100-python/p100.py", line 166, in login_request
    self.token = decrypted_inner_response['result']['token']
KeyError: 'result'

Its actually the device that is rejecting my login.

@pnmoliveira are you sure your credentials are correct? You need to use your TP-Link ID email and password.

1 Like

100% sure, even changed password and created a new TP-Link ID and associated one of my extra plugs to it just to make sure that it was not an account issue

May I ask how to use this?
I would like to implement this into my setup.

@dsoklic Another further step on troubleshooting tried to add the devices to my account via Android application instead of iOS application but unfortunately the result is exactly the same.

Will try to dig a bit further, but Iā€™m actually running out of ideas :stuck_out_tongue: