JurajNyiri / HomeAssistant-Tapo-Control

Control for Tapo cameras as a Home Assistant component
Apache License 2.0
968 stars 81 forks source link

Feature Request: Support new authorization on the newest firmwares #436

Closed mafr95 closed 8 months ago

mafr95 commented 8 months ago

Description

Yesterday I updated my Tapo C210 camera to version 1.3.9. After that, the integration could no longer connect to the camera. I decided to delete the camera from the integration and add it again. But every time I enter my cloud password, the error message "Invalid cloud password" appears. I have now checked the password 10 times. It is definitely correct. I suspect it has something to do with the camera update. Previously it worked without any problems.

I have two other cameras. (C200 and C220) I was able to add them with the same cloud password without any problems.

In the meantime, I removed the camera from the Tapo app, reset it and added it again. But without success.

Reproduction Steps

  1. Update C210 to version 1.3.9 Build 231019 Rel. 13805n(4555)
  2. Add camera in tapo integration
  3. Enter correct ip adress
  4. Enter correct username and passwort for camera account (created via tapo app)
  5. Enter cloud password ==> failed

Expected behavior

Add camera to integration

If applicable, add error logs.

No response

Device Firmware

1.3.9 Build 231019 Rel. 13805n(4555)

Using stream component

Yes

Does camera work via official integrations?

Yes

Camera has all attributes filled out in developer tools

Yes

HASS Environment

RPI 4

Search for similar issues

Yes

Additional information

No response

umaito commented 8 months ago

I have the same issue with the cloud password. And I updated my tapo C210 too

ramonrodriguezdiez commented 8 months ago

I have the same issue, C210 and updated to 1.3.9

kwermeister commented 8 months ago

I have the same issue, C210 and updated to 1.3.9. 2x C210 with 1.3.7. works fine.

imperadeiro commented 8 months ago

Same problem

JurajNyiri commented 8 months ago

Looks like they changed how the authorization works. Someone will need to inspect the communication and find a new proper username and password for videos playback / control on some cameras, then we can update the integration or find out what changed - what username/password is used for video playback (not rtsp) and what and how is used for control?

I do not have the update available for my cameras.

Please try it directly with pytapo library - 3rd party account and password used to work as well as admin and cloud password.

JurajNyiri commented 8 months ago

Follow https://github.com/JurajNyiri/pytapo#install and then try running this script:

from pytapo import Tapo

user = ""  # 3rd party account name
password = ""  # 3rd party account password
password_cloud = ""  # cloud password
host = ""  # ip of the camera, example: 192.168.1.52

tapo = Tapo(host, "admin", password_cloud)

request = tapo.getBasicInfo()
print(request)

tapo = Tapo(host, user, password)

request = tapo.getBasicInfo()
print(request)

Expected response would be the same output twice containing information about camera. Make sure to fill in the 4 variables.

mafr95 commented 8 months ago

I get this output:

Traceback (most recent call last): File "/home/pi/testtapo.py", line 7, in tapo = Tapo(host, "admin", password_cloud) File "/home/pi/.local/lib/python3.9/site-packages/pytapo/init.py", line 53, in init self.basicInfo = self.getBasicInfo() File "/home/pi/.local/lib/python3.9/site-packages/pytapo/init.py", line 554, in getBasicInfo return self.executeFunction( File "/home/pi/.local/lib/python3.9/site-packages/pytapo/init.py", line 142, in executeFunction data = self.performRequest( File "/home/pi/.local/lib/python3.9/site-packages/pytapo/init.py", line 171, in performRequest self.ensureAuthenticated() File "/home/pi/.local/lib/python3.9/site-packages/pytapo/init.py", line 73, in ensureAuthenticated return self.refreshStok() File "/home/pi/.local/lib/python3.9/site-packages/pytapo/init.py", line 121, in refreshStok raise Exception("Invalid authentication data") Exception: Invalid authentication data

JurajNyiri commented 8 months ago

Could you try this:

from pytapo import Tapo

user = ""  # 3rd party account name
password = ""  # 3rd party account password
password_cloud = ""  # cloud password
host = ""  # ip of the camera, example: 192.168.1.52

tapo = Tapo(host, user, password)

request = tapo.getBasicInfo()
print(request)

tapo = Tapo(host, "admin", password_cloud)

request = tapo.getBasicInfo()
print(request)

Please make sure to post full output.

JurajNyiri commented 8 months ago

If you get access working via the 3rd party account, can you try if control works? Try setting for example privacy mode https://github.com/JurajNyiri/pytapo/blob/main/pytapo/__init__.py#L567 (tapo.setPrivacyMode(True)) .

fetsreg commented 8 months ago

Hi, I have the same issue with my C210 running on 1.3.9, even the authentication with the local camera account is failing:


Traceback (most recent call last):
  File "C:\Users\local\Repos\Tapo\tapo.py", line 8, in <module>
    tapo = Tapo(host, user, password)
  File "C:\Users\local\Repos\Tapo\.venv\lib\site-packages\pytapo\__init__.py", line 53, in __init__
    self.basicInfo = self.getBasicInfo()
  File "C:\Users\local\Repos\Tapo\.venv\lib\site-packages\pytapo\__init__.py", line 554, in getBasicInfo
    return self.executeFunction(
  File "C:\Users\local\Repos\Tapo\.venv\lib\site-packages\pytapo\__init__.py", line 142, in executeFunction
    data = self.performRequest(
  File "C:\Users\local\Repos\Tapo\.venv\lib\site-packages\pytapo\__init__.py", line 171, in performRequest
    self.ensureAuthenticated()
  File "C:\Users\local\Repos\Tapo\.venv\lib\site-packages\pytapo\__init__.py", line 73, in ensureAuthenticated
    return self.refreshStok()
  File "C:\Users\local\Repos\Tapo\.venv\lib\site-packages\pytapo\__init__.py", line 121, in refreshStok
    raise Exception("Invalid authentication data")
Exception: Invalid authentication data
JurajNyiri commented 8 months ago

Make sure to always restart the camera before executing and not use it anywhere else (including the official app) as there is limited number of control slots available.

fetsreg commented 8 months ago

It is the same behavior with local authentication not working, even after a fresh reboot

JurajNyiri commented 8 months ago

That is different to what was reported in OP. Let's wait for more users to try this eliminate user error or camera weirdness.

In any case, we will need someone to sniff the traffic going from phone to camera on firmware 1.3.9 document it here, and see what is done differently, only then I can start working on the fix.

Guide: https://httptoolkit.com/docs/guides/android/ including the part "Intercepting traffic from 3rd party Android apps with certificate pinning"

viprapp commented 8 months ago

I'm here to report the same behavior with my Tapo C210 after updating to 1.3.9. Along with others, I've encountered the 'Invalid cloud password' error through the Home Assistant integration and an 'Exception: Invalid authentication data' error when using the suggested Python login script. Notably, my Synology DiskStation (Surveillance Station) still manages to connect using third-party login credentials. From memory, the update notes mentioned enhanced security measures in the authentication process. This could mean that what we're seeing might be the start of broader issues as similar authentication updates roll out to other Tapo cameras.

cnecrea commented 8 months ago

Description

Yesterday I updated my Tapo C210 camera to version 1.3.9. After that, the integration could no longer connect to the camera. I decided to delete the camera from the integration and add it again. But every time I enter my cloud password, the error message "Invalid cloud password" appears. I have now checked the password 10 times. It is definitely correct. I suspect it has something to do with the camera update. Previously it worked without any problems.

I have two other cameras. (C200 and C220) I was able to add them with the same cloud password without any problems.

In the meantime, I removed the camera from the Tapo app, reset it and added it again. But without success.

Reproduction Steps

  1. Update C210 to version 1.3.9 Build 231019 Rel. 13805n(4555)
  2. Add camera in tapo integration
  3. Enter correct ip adress
  4. Enter correct username and passwort for camera account (created via tapo app)
  5. Enter cloud password ==> failed

Expected behavior

Add camera to integration

If applicable, add error logs.

No response

Device Firmware

1.3.9 Build 231019 Rel. 13805n(4555)

Using stream component

Yes

Does camera work via official integrations?

Yes

Camera has all attributes filled out in developer tools

Yes

HASS Environment

RPI 4

Search for similar issues

Yes

Additional information

No response

Same thing happens after firmware upgrade.

JurajNyiri commented 8 months ago

I have pushed a new code: https://github.com/JurajNyiri/pytapo/tree/debug_new_firmware

Download it manually, and next to README.md create file test.py with content:

from pytapo import Tapo

# Camera
user = ""  # 3rd party account name
password = ""  # 3rd party account password
password_cloud = ""  # cloud password
host = ""  # ip of the camera, example: 192.168.1.52

try:
    tapo = Tapo(host, "admin", password_cloud)
    request = tapo.getBasicInfo()
    print(request)
except Exception:
    pass

try:
    tapo = Tapo(host, user, password)

    request = tapo.getBasicInfo()
    print(request)
except Exception:
    pass

Post the whole output of the script.

viprapp commented 8 months ago
pyTapo - Version for debugging new firmware
True
admin
{'Host': 'REDACTED', 'Referer': 'REDACTED', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}
200
{"error_code": -40413, "result" : { "data": {"code": -40401, "encrypt_type": ["3"], "key": "REDACTED", "nonce": "REDACTED", "device_confirm": "REDACTED"}}}
pyTapo - Version for debugging new firmware
True
[USERNAME REDACTED]
{'Host': 'REDACTED', 'Referer': 'REDACTED', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}
200
{"error_code": -40401, "result" : { "data": {"code": -40411, "encrypt_type": ["1", "2"], "key": "REDACTED", "nonce": "REDACTED"}}}
GSzabados commented 8 months ago

@JurajNyiri, that is INVALID_NONCE(-40413). And that -40411 doesn't make any sense.

The "encrypt_type": ["3"] is indeed interesting.

JurajNyiri commented 8 months ago

@viprapp what is inside device_confirm? That field is new, as well as "encrypt_type": ["3"] Previously we had only 1 and 2.

Edit: try putting in incorrect cloud password as well. Does the response change?

viprapp commented 8 months ago

Each time the script is executed, the 'device_confirm' field generates an 80-character, uppercase hexadecimal string, while the first 'nonce' (associated with 'admin') is 16 characters in uppercase hexadecimal format. The second 'nonce' (related to 'username') produces an 8-character alphanumeric string. 'device_confirm' and both 'nonces' change with every run of the script.

Also, inputting an incorrect cloud password doesn't alter the script's output, with the exception of the 'nonces' and 'device_confirm' fields, which are regenerated.

JurajNyiri commented 8 months ago

Please try the same script with this https://github.com/JurajNyiri/pytapo/tree/debug_new_firmware_sha256

viprapp commented 8 months ago

Looks like some progress

pyTapo - Version for debugging new firmware 2
True
admin
{'Host': 'REDACTED', 'Referer': 'REDACTED', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}
200
{"error_code": 0, "result" : { "stok": "REDACTED", "user_group": "root", "start_seq": 0}}
pyTapo - Version for debugging new firmware 2
True
[USERNAME REDACTED]
{'Host': 'REDACTED', 'Referer': 'REDACTED', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}
200
{"error_code": -40401, "result" : { "data": {"code": -40411, "encrypt_type": ["1", "2"], "key": "REDACTED", "nonce": "REDACTED"}}}
JurajNyiri commented 8 months ago

Woah! That was unexpected.

Try now again with branch https://github.com/JurajNyiri/pytapo/tree/debug_new_firmware_sha256, pushed more debug changes, new script:

from pytapo import Tapo

# Camera
user = ""  # 3rd party account name
password_cloud = ""  # cloud password
host = ""  # ip of the camera, example: 192.168.1.52

tapo = Tapo(host, "admin", password_cloud)
request = tapo.getBasicInfo()
print(request)
GSzabados commented 8 months ago

@viprapp, could you also make sure that you have set up the non-admin local account in the Tapo app and you are using the right password for that?

JurajNyiri commented 8 months ago

@GSzabados I think this is the case of a camera refusing control without admin acc, there are cameras like that out, for example C200v3 C310 and probably C210 as well. That is why we default to control via admin acc in the integration.

GSzabados commented 8 months ago

Are those the Homekit compatible cameras?

viprapp commented 8 months ago

@viprapp, could you also make sure that you have set up the non-admin local account in the Tapo app and you are using the right password for that?

Yes, I've consistently used the same local non-admin username and password for all tests, which were set up in the Tapo app. This is further corroborated by the fact that my Synology Surveillance Station can still connect to the C210 camera using these third-party credentials without any issues. This seems to align with JurajNyiri's hypothesis regarding the admin account behavior post-update.

JurajNyiri commented 8 months ago

@viprapp please take a look at https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/issues/436#issuecomment-1792572589 when you have the time.

viprapp commented 8 months ago
pyTapo - Version for debugging new firmware 3
getBasicInfo
executeFunction
True
admin
{'Host': 'REDACTED', 'Referer': 'REDACTED', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'REDACTED', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}
{"error_code": 0, "result": {"stok": "REDACTED", "user_group": "root", "start_seq": 0}}
200
{"error_code": 0, "result": {"stok": "REDACTED", "user_group": "root", "start_seq": 0}}
{"error_code":0,"seq":0,"result":{"response":"REDACTED"}}
Traceback (most recent call last):
  File "../pytapo_debug/test.py", line 8, in <module>
    tapo = Tapo(host, "admin", password_cloud)
  File "../pytapo_debug/pytapo/__init__.py", line 56, in __init__
    self.basicInfo = self.getBasicInfo()
  File "../pytapo_debug/pytapo/__init__.py", line 565, in getBasicInfo
    return self.executeFunction(
  File "../pytapo_debug/pytapo/__init__.py", line 152, in executeFunction
    data = self.performRequest(
  File "../pytapo_debug/pytapo/__init__.py", line 211, in performRequest
    if not self.responseIsOK(res):
  File "../pytapo_debug/pytapo/__init__.py", line 134, in responseIsOK
    raise Exception(
Exception: Error communicating with Tapo Camera. Status code: 500
JurajNyiri commented 8 months ago

Please try again, same script, just pull the latest changes in https://github.com/JurajNyiri/pytapo/tree/debug_new_firmware_sha256

Edit: I also noticed {"error_code":0,"seq":0,"result":{"response":"REDACTED"}}.. Error code 0, what was in response?

viprapp commented 8 months ago
pyTapo - Version for debugging new firmware 4
getBasicInfo
executeFunction
True
admin
{'Host': 'REDACTED', 'Referer': 'REDACTED', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}
{"error_code": 0, "result": {"stok": "REDACTED", "user_group": "root", "start_seq": 0}}
200
{"error_code": 0, "result": {"stok": "REDACTED", "user_group": "root", "start_seq": 0}}
{"error_code":0,"seq":0,"result":{"response":"REDACTED"}}
{'method': 'multipleRequest', 'params': {'requests': [{'method': 'getDeviceInfo', 'params': {'device_info': {'name': ['basic_info']}}}]}, 'seq': 1}
Traceback (most recent call last):
  File "../pytapo_debug2/test.py", line 8, in <module>
    tapo = Tapo(host, "admin", password_cloud)
  File "../pytapo_debug2/pytapo/__init__.py", line 57, in __init__
    self.basicInfo = self.getBasicInfo()
  File "../pytapo_debug2/pytapo/__init__.py", line 570, in getBasicInfo
    return self.executeFunction(
  File "../pytapo_debug2/pytapo/__init__.py", line 153, in executeFunction
    data = self.performRequest(
  File "../pytapo_debug2/pytapo/__init__.py", line 216, in performRequest
    if not self.responseIsOK(res):
  File "../pytapo_debug2/pytapo/__init__.py", line 135, in responseIsOK
    raise Exception(
Exception: Error communicating with Tapo Camera. Status code: 500

Additionally, the response contains a 128-character alphanumeric string with '/' and '+' characters, which could indicate base64 encoding.

JurajNyiri commented 8 months ago

What is inside {"error_code":0,"seq":0,"result":{"response":"REDACTED"}}?

JurajNyiri commented 8 months ago

Another update: https://github.com/JurajNyiri/pytapo/tree/debug_new_firmware_sha256 with a clearer debugging. Please do not redact everything, only confidential information such as password, stok, mac, dev_id and oem_id. There is no reason to redact anything other than that. I do appreciate the help but you redacted important information 3 times now and it is making debugging a lot harder.

viprapp commented 8 months ago

What is inside {"error_code":0,"seq":0,"result":{"response":"REDACTED"}}?

Additionally, the response contains a 128-character alphanumeric string with '/' and '+' characters, which could indicate base64 encoding.

It is a string that looks like this (I kept the + and / dilimeters, the rest is randomized but same charset):

{"error_code":0,"seq":0,"result":{"response":"SaR5xPV6f/sQgudugVow4NxA7VUk9Cs81CT9Wi3h5lCVcy3Yudos5a5yGskQwJ17Pckgwze0vtu824ummnFDH+ZDtxPRYbFpDtZbwX2yE+ePmhvirznkGGZ4DQzwt5GP"}}

JurajNyiri commented 8 months ago

@viprapp please run again https://github.com/JurajNyiri/pytapo/tree/debug_new_firmware_sha256 Please do not redact more than password, stok, mac, dev_id and oem_id. It isn't necessary and its making debugging impossible. Feel free to share privately if it would make you feel better.

viprapp commented 8 months ago
pyTapo - Version for debugging new firmware 6
{'data': '{"method": "login", "params": {"hashed": true, "password": "REDACTED", "username": "admin"}}', 'headers': {'Host': 'REDACTED', 'Referer': 'REDACTED', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}, 'verify': False}
{"error_code": 0, "result" : { "stok": "REDACTED", "user_group": "root", "start_seq": 0}}

{'data': '{"method": "multipleRequest", "params": {"requests": [{"method": "getDeviceInfo", "params": {"device_info": {"name": ["basic_info"]}}}]}, "seq": 1}', 'headers': {'Host': 'REDACTED', 'Referer': 'REDACTED', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}, 'verify': False}
{"error_code":0,"seq":0,"result":{"response":"SaR5xPV6f/sQgudugVow4NxA7VUk9Cs81CT9Wi3h5lCVcy3Yudos5a5yGskQwJ17Pckgwze0vtu824ummnFDH+ZDtxPRYbFpDtZbwX2yE+ePmhvirznkGGZ4DQzwt5GP"}}

Traceback (most recent call last):
  File "../pytapo_debug3/test.py", line 8, in <module>
    tapo = Tapo(host, "admin", password_cloud)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "../pytapo_debug3/pytapo/__init__.py", line 58, in __init__
    self.basicInfo = self.getBasicInfo()
                     ^^^^^^^^^^^^^^^^^^^
  File "../pytapo_debug3/pytapo/__init__.py", line 573, in getBasicInfo
    return self.executeFunction(
           ^^^^^^^^^^^^^^^^^^^^^
  File "../pytapo_debug3/pytapo/__init__.py", line 158, in executeFunction
    data = self.performRequest(
           ^^^^^^^^^^^^^^^^^^^^
  File "../pytapo_debug3/pytapo/__init__.py", line 220, in performRequest
    if not self.responseIsOK(res):
           ^^^^^^^^^^^^^^^^^^^^^^
  File "../pytapo_debug3/pytapo/__init__.py", line 141, in responseIsOK
    raise Exception(
Exception: Error communicating with Tapo Camera. Status code: 500

I understand the importance of these details for debugging and appreciate your patience. As for {"response":"SaR5xPV6f/sQgudugVow4NxA7VUk9Cs81CT9Wi3h5lCVcy3Yudos5a5yGskQwJ17Pckgwze0vtu824ummnFDH+ZDtxPRYbFpDtZbwX2yE+ePmhvirznkGGZ4DQzwt5GP"}} if you require the actual non-randomised response, I'm willing to share it privately, once I'm clear about the type of data it represents and any potential implications for my home security setup.

timothybuchanan commented 8 months ago

I have two cameras like this; one is still working and the other not. I deleted the non-working and reinstalled, getting the invalid cloud password error. when I changed the passwor in the Tapo app, I noticed that it now requires seven characters but my previous password used only seven. Could this have anything to do with the problem?

kwermeister commented 8 months ago

I have two cameras like this; one is still working and the other not. I deleted the non-working and reinstalled, getting the invalid cloud password error. when I changed the passwor in the Tapo app, I noticed that it now requires seven characters but my previous password used only seven. Could this have anything to do with the problem?

I think this theory is unlikely. According to Tapo, there should be 8-32 characters and "At least two types of the following characters must be included: Letters, numbers and symbols." My current one has 20 characters with letters, numbers and symbols.

JurajNyiri commented 8 months ago

@viprapp it should contain whatever we requested before, in this case basic info of camera. Here is mine working example for comparison:

{'data': '{"method": "login", "params": {"hashed": true, "password": "REDACTED", "username": "admin"}}', 'headers': {'Host': '192.168.100.70', 'Referer': 'https://192.168.100.70', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}, 'verify': False}
{"error_code": 0, "result" : { "stok": "REDACTED", "user_group": "root"}}

{'data': '{"method": "multipleRequest", "params": {"requests": [{"method": "getDeviceInfo", "params": {"device_info": {"name": ["basic_info"]}}}]}, "seq": 1}', 'headers': {'Host': '192.168.100.70', 'Referer': 'https://192.168.100.70', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}, 'verify': False}
{ "result": { "responses": [ { "method": "getDeviceInfo", "result": { "device_info": { "basic_info": { "device_type": "SMART.IPCAMERA", "device_model": "C200", "device_name": "C200 1.0", "device_info": "C200 1.0 IPC", "hw_version": "1.0", "sw_version": "1.1.16 Build 211209 Rel.37726n(4555)", "device_alias": "Bedroom", "features": "3", "barcode": "", "mac": "REDACTED", "dev_id": "REDACTED", "oem_id": "REDACTED", "hw_desc": "00000000000000000000000000000000" } } }, "error_code": 0 } ] }, "error_code": 0 }

{'data': '{"method": "multipleRequest", "params": {"requests": [{"method": "getPresetConfig", "params": {"preset": {"name": ["preset"]}}}]}, "seq": 2}', 'headers': {'Host': '192.168.100.70', 'Referer': 'https://192.168.100.70', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}, 'verify': False}
{ "result": { "responses": [ { "method": "getPresetConfig", "result": { "preset": { "preset": { "id": [ "1", "2" ], "name": [ "Privacy", "Room" ], "read_only": [ "0", "0" ], "position_pan": [ "0.920372", "-0.066701" ], "position_tilt": [ "0.716080", "0.716080" ] } } }, "error_code": 0 } ] }, "error_code": 0 }

{'data': '{"method": "multipleRequest", "params": {"requests": [{"method": "getDeviceInfo", "params": {"device_info": {"name": ["basic_info"]}}}]}, "seq": 3}', 'headers': {'Host': '192.168.100.70', 'Referer': 'https://192.168.100.70', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}, 'verify': False}
{ "result": { "responses": [ { "method": "getDeviceInfo", "result": { "device_info": { "basic_info": { "device_type": "SMART.IPCAMERA", "device_model": "C200", "device_name": "C200 1.0", "device_info": "C200 1.0 IPC", "hw_version": "1.0", "sw_version": "1.1.16 Build 211209 Rel.37726n(4555)", "device_alias": "Bedroom", "features": "3", "barcode": "", "mac": "REDACTED", "dev_id": "REDACTED", "oem_id": "REDACTED", "hw_desc": "00000000000000000000000000000000" } } }, "error_code": 0 } ] }, "error_code": 0 }

{'device_info': {'basic_info': {'device_type': 'SMART.IPCAMERA', 'device_model': 'C200', 'device_name': 'C200 1.0', 'device_info': 'C200 1.0 IPC', 'hw_version': '1.0', 'sw_version': '1.1.16 Build 211209 Rel.37726n(4555)', 'device_alias': 'Bedroom', 'features': '3', 'barcode': '', 'mac': 'REDACTED', 'dev_id': 'REDACTED', 'oem_id': 'REDACTED', 'hw_desc': '00000000000000000000000000000000'}}}

If it is indeed base64 encoded string, the randomisation you introduced to obfuscate it corrupted it.

viprapp commented 8 months ago

@viprapp it should contain whatever we requested before, in this case basic info of camera. Here is mine working example for comparison:

{'data': '{"method": "login", "params": {"hashed": true, "password": "REDACTED", "username": "admin"}}', 'headers': {'Host': '192.168.100.70', 'Referer': 'https://192.168.100.70', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}, 'verify': False}
{"error_code": 0, "result" : { "stok": "REDACTED", "user_group": "root"}}

{'data': '{"method": "multipleRequest", "params": {"requests": [{"method": "getDeviceInfo", "params": {"device_info": {"name": ["basic_info"]}}}]}, "seq": 1}', 'headers': {'Host': '192.168.100.70', 'Referer': 'https://192.168.100.70', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}, 'verify': False}
{ "result": { "responses": [ { "method": "getDeviceInfo", "result": { "device_info": { "basic_info": { "device_type": "SMART.IPCAMERA", "device_model": "C200", "device_name": "C200 1.0", "device_info": "C200 1.0 IPC", "hw_version": "1.0", "sw_version": "1.1.16 Build 211209 Rel.37726n(4555)", "device_alias": "Bedroom", "features": "3", "barcode": "", "mac": "REDACTED", "dev_id": "REDACTED", "oem_id": "REDACTED", "hw_desc": "00000000000000000000000000000000" } } }, "error_code": 0 } ] }, "error_code": 0 }

{'data': '{"method": "multipleRequest", "params": {"requests": [{"method": "getPresetConfig", "params": {"preset": {"name": ["preset"]}}}]}, "seq": 2}', 'headers': {'Host': '192.168.100.70', 'Referer': 'https://192.168.100.70', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}, 'verify': False}
{ "result": { "responses": [ { "method": "getPresetConfig", "result": { "preset": { "preset": { "id": [ "1", "2" ], "name": [ "Privacy", "Room" ], "read_only": [ "0", "0" ], "position_pan": [ "0.920372", "-0.066701" ], "position_tilt": [ "0.716080", "0.716080" ] } } }, "error_code": 0 } ] }, "error_code": 0 }

{'data': '{"method": "multipleRequest", "params": {"requests": [{"method": "getDeviceInfo", "params": {"device_info": {"name": ["basic_info"]}}}]}, "seq": 3}', 'headers': {'Host': '192.168.100.70', 'Referer': 'https://192.168.100.70', 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Tapo CameraClient Android', 'Connection': 'close', 'requestByApp': 'true', 'Content-Type': 'application/json; charset=UTF-8'}, 'verify': False}
{ "result": { "responses": [ { "method": "getDeviceInfo", "result": { "device_info": { "basic_info": { "device_type": "SMART.IPCAMERA", "device_model": "C200", "device_name": "C200 1.0", "device_info": "C200 1.0 IPC", "hw_version": "1.0", "sw_version": "1.1.16 Build 211209 Rel.37726n(4555)", "device_alias": "Bedroom", "features": "3", "barcode": "", "mac": "REDACTED", "dev_id": "REDACTED", "oem_id": "REDACTED", "hw_desc": "00000000000000000000000000000000" } } }, "error_code": 0 } ] }, "error_code": 0 }

{'device_info': {'basic_info': {'device_type': 'SMART.IPCAMERA', 'device_model': 'C200', 'device_name': 'C200 1.0', 'device_info': 'C200 1.0 IPC', 'hw_version': '1.0', 'sw_version': '1.1.16 Build 211209 Rel.37726n(4555)', 'device_alias': 'Bedroom', 'features': '3', 'barcode': '', 'mac': 'REDACTED', 'dev_id': 'REDACTED', 'oem_id': 'REDACTED', 'hw_desc': '00000000000000000000000000000000'}}}

If it is indeed base64 encoded string, the randomisation you introduced to obfuscate it corrupted it.

@JurajNyiri, thank you for providing your example, it clarifies what kind of output is expected. I understand now that the base64 string likely contains the basic info of the camera which is indeed sensitive. I'm hesitant to share the full string as it likely contains details like MAC, dev_id, and oem_id.

Could you guide me on how to securely share the required info without exposing these sensitive elements?

JurajNyiri commented 8 months ago

Is it base64? You can decode it to confirm. Or you can send the string to me juraj.nyiri@gmail.com. Realistically there is absolutely nothing anyone can do with those data though even if you shared it here. -- that is why I redact password in the debugging code above, that's really the only thing that matters when shared.

viprapp commented 8 months ago

@JurajNyiri, I've sent an email with the terminal output where only the stok is redacted. I attempted to decode the string, but the standard base64 decoder wasn't successful.

JurajNyiri commented 8 months ago

At this point we will need someone who has the new firmware to inspect the traffic between the app and local cameras and document the requests so that we can learn from it. I did not receive the firmware yet.

I am going to drop this here: https://portswigger.net/burp/documentation/desktop/mobile/config-android-device (community free version works, no root required, install cert as user, sign in to app first before activating the proxy).

GSzabados commented 8 months ago

Or if someone could get a link to the latest firmware:

POST https://[IP_ADDRESS]/stok=[TOKEN]/ds

{
    "method": "get",
    "cloud_config": {
        "name": ["upgrade_info"]
    }
}

image

JurajNyiri commented 8 months ago

@GSzabados https://github.com/tapo-firmware/Directory The latest is not there yet though.

bonvga commented 8 months ago

Hello,

Same issue with C210 updated to 1.3.9.

@JurajNyiri, I send you by email the exchanges collected with the local proxy after a connection with app. I hope this helps ^^

neilhawsjones commented 8 months ago

For what it's worth, I have 2xC210. One is firmware 1.37 and connects & works perfectly, the other is 1.3.9 and has the "cloud password error".

JurajNyiri commented 8 months ago

Thank you @bonvga (also thanks for providing the camera for testing!) I now have everything I need to continue research and implement new authorization method.

JurajNyiri commented 8 months ago

So far I was able to find out how the new authorization works and was able to successfully replicate it - https://github.com/JurajNyiri/pytapo/blob/encryption_3/pytapo/__init__.py#L153 and I am now able to get stok just like before. The issue is with the requests now, they are all encrypted and decrypted with some algorithm and I do not know how.

This one will take a while. Summonning @depau @GSzabados I need your help!

JurajNyiri commented 8 months ago

This was fun.

Fixed in https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/releases/tag/5.4.0 .