Conversation
There was a problem hiding this comment.
Pull Request Overview
Implements the new TPAP (TP-Link Adaptive Protocol) encryption type with SPAKE2+ HTTPS transport for TP-Link devices. This is an initial implementation to test handshake functionality with new firmware that uses TPAP encryption.
- Added complete TPAP transport implementation using SPAKE2+ P-256 handshake and AEAD data channel
- Updated device factory to support TPAP encryption type routing
- Added ecdsa dependency for elliptic curve operations
Reviewed Changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pyproject.toml | Added ecdsa dependency and mypy overrides for the new package |
| kasa/transports/tpaptransport.py | New TPAP transport implementation with SPAKE2+ handshake and secure channel |
| kasa/transports/init.py | Added TpapTransport to module exports |
| kasa/deviceconfig.py | Added Tpap enum value to DeviceEncryptionType |
| kasa/device_factory.py | Added SMART.TPAP.HTTPS protocol mapping and fixed typo |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1592 +/- ##
==========================================
+ Coverage 93.22% 93.75% +0.53%
==========================================
Files 157 158 +1
Lines 9815 10654 +839
Branches 1003 1112 +109
==========================================
+ Hits 9150 9989 +839
Misses 472 472
Partials 193 193 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@rytilahti Ok, I think this is a good first pass. I just need someone with a RV device to test it. The CodeQL Security flags, from my understanding, will always come up with md5 and sha1 hashing in the code, but it's required for the device communication, just like with the other transports. |
|
Sanitized discovery, logs, and TLS observations for the RV30 Max Plus(EU)-Firmware:1.3.0 Build 250909 Rel.135514 using TPAP. Personally identifying values are redacted. Hope this help.
uv run kasa --username '' --password '' --debug --host 192.168.68.63 discover config uv run kasa --username '' --password '' --debug --host 192.168.68.63 discover raw SSL: Wireshark: |
|
@danieyal Pull the latest commit and try giving it a shot again. |
|
unfortunately, still the same error. |
|
I think i have got the TLS working now but stuck on the authentication now.
The device is returning a JSON response with 'error_code': -2402 along with authentication failure details like failedAttempts and remainAttempts. The device is rejecting the authentication attempt at the pake_register stage returning error code -2402. My theory is the device is actively rejecting our credentials/authentication attempt before we even get to the SPAKE2+ cryptographic exchange. This suggests the device needs something we're not providing, most likely DAC support. |
|
I have been working on this today without much movement, still trying to figure out the authentication pieces. Looks like I will have to implement NOC but having issues getting the information and URLs for the certificate registration with the Tapo cloud. There is an API rate limit which causes problems as well. So, I'm still working on this, but nothing yet. |
|
@danieyal Are you able to use the Tapo app on your computer with Wireshark? I'm trying to reverse engineer the url for the certificates and the requests are not working. I am looking for something to do with: This is what points to where to apply for the certificates, but I can't get the communication to work correctly on my end. I'm trying to get the signature correct with the app to the cloud so I can pull the url, but I can't get that either. Until I have the URL to work with the serviceId: nbu.cvm-server-v2, then I can't get the library to handle the noc certificates. |
|
@danieyal try pulling again and let me know. I updated the ciphers like you found and I also corrected the last error you posted, it went to the stok field in the register parameters, it needed to be sessionId instead. Let me know what you get with this. |
|
@ZeliardM Thanks for the updates, and to clarify my side:
Re: NOC/
I also pulled the latest branch:
Frida Log for device commissioning, I reset my device to see if pake_register appear at all, but it seems like nope, it just communicates over cloud regardless, other Tapo devices (plug, bulb, hub, camera) that I own still communicates locally (so probably not local network issue), I can 'see' the local endpoint for other devices but only vacuum just straight to cloud endpoint. Sorry, if it takes too long, setting up Frida took longer than expected. |
|
@danieyal That's great, I do see something in your logs about nbu, that may help point me where I need to go. Yea, Frida is a pain. I've used it in the past but lost my devices to do so, so it's been rough lately. I appreciate your work so far. I am going through the encryption pieces again. Yea, the pake_register phase is essentially handshake1 and I still need to see what is going on. |
|
@danieyal Ok, I've spent most of the day going over things, and I think I'm at an impasse. I can't get the URLs to communicate with the cloud for the NOC certificates. I've gotten close, but what happens is that the APK uses a call, gets a cloud token, then pulls the URLs from the cloud. I can get the cloud token, but the I cannot get the signature matches for the calls out that have 'signature-required' set to true. I need to see if there is a way to reverse engineer these signatures so we can get them to match from my code based on how everything is supposed to be. I have some scripts and code that I've got for testing if you would be able to take it along with some of the Frida work you've done and possibly see if you can get the URL? I can send them via Discord if that works? |
|
@ZeliardM yeah sure, I will try but since I am working during the day, I might not have enough time to dedicate to it, but I'll try. Discord works for me. |
|
@danieyal All good, give it a shot and let me know and we will go from there. Thanks! |
|
@danieyal What I have been able to pull apart today is that the checkpassword call that is used in the frida logs you posted earlier, this has the same signature requirements that I am looking for. If you can get me some wireshark pulls of this actual communication so I can see the actual headers and packet information, then I might be able to reverse engineer this from there. If you want, we can keep going on Discord, my username is the same there. |
|
@danieyal nevermind, I finally got the matching signature and figured out how to get the right url for getting the certificates! |
|
Ok, now that I have all of this, my plan is as follows:
It's going to take quite some time for me to get through all of this, I want to put it together cleanly. I'm headed out of town with my family for the weekend so I may not work on it much right now, but will keep everyone updated as I keep working on it. |
|
I am still slowly working on this. I have the NOC capabilities set up, now I'm working on testing the code. Having issues with the CSR formatting so I'll keep plugging away at it and keep you in the loop. |
|
@krx252525 Not at this time, thanks for testing and reporting. I'm happy to help! |
|
Is this merged into the repo? So my RV30 Max Plus will be supported, then the tapo homeassistant integration can get updated? |
|
@bobsusedtires Not yet, I still have some things we are working out with this first. Have to break up some of the pieces to make it easier to keep updated and working better in the future. A little more modular, once all of that is done, it should be ready. The holidays threw everything off track but I hope to get back to it later this month. |
|
Hi @ZeliardM Very late to the party ... I have a P110 that the app refuses to provision. It was provisioned but I wanted to move it to another network and factory reset it (ie full 10s button hold) and since then it's a brick. I have another plug that's still fine. Even moved it to the new network (though, using the 5s button method). Anyway, recently came across your work and thought I'd found the solution. Alas, not. You mentioned above that you have a P110 working for the SPAKE2+ (though, I admit at this point, I don't know what that is) but my device persists in resisting. I'm hoping these might provide you with some new information: |
|
@aureliandevel Hey, thanks for the logs. I see where it's trying, but the information back from the device all out of whack and the connection is getting refused. Have you tried factory resetting it again? Setup on a different device? Android vs iOS? Setting it up in Android vs iOS? |
|
@ZeliardM, yeah, factory-reset, and soft-reset, numerous times. I don't have any iOS devices, but I've tried setting it up from a different Android device, and made sure I have the latest version (currently 3.17.109) It's interesting that you say the device information is all screwy. What are you expecting to see that isn't there? FYI, both devices are hardware version 1.0, so there's no Bluetooth. Firmware from the registered device is 1.4.0 build 251020 Rel. 161559 (as I assume every else's is too) ... obviously I can't see what FW version the unregistered device is but it was in daily use before I tried switching networks so I'm assuming it's the latest. |
|
@aureliandevel this information: DEBUG:kasa.discover:[DISCOVERY] 192.168.0.1 << {'error_code': 0, The tpap information is off, there's nothing reported really, so it doesn't know how to authenticate. The odb_src is still reported too, so it's like either the reset didn't work or something else is going on with the device, but factory_default is true so it should be ready to go. |
|
Hi @ZeliardM — thanks for the TPAP/SPAKE2+ reverse engineering work here, it was the starting point for a local Home Assistant integration I've built for the RV30 Max Plus: https://github.com/epg-pers/tapo-rv30-ha As a bonus it includes working selective room cleaning, which required a bit of its own reverse engineering — documented in the README if useful to anyone. Hope it's helpful! |
|
@epg-pers I'm glad I could help. I looked over that implementation and while it does work, there are certain aspects of it that I have yet to completely verify on everything and once the fixes are pushed here it should be more robust for future firmware where I cannot guarantee the code in your integration will continue to work if firmware changes. If you would like to pull together the code changes for the individual room cleaning, a PR here might be helpful in getting all of that in the official implementation as well. |
|
@ZeliardM Thanks, that makes sense, and I'd be happy to put together a PR for the room cleaning side. Before I do, would it be worth waiting until this transport PR is merged and settled? That way the room cleaning work can be built cleanly on top of the final upstream version. Happy to go ahead now if you'd prefer, just wanted to check. |
|
@epg-pers If you want to look over the existing code and modules and see how they have them setup, you can just build it off the latest main branch and then when this is finally merged in, we can update and test from there before we merge it in. |
|
I am making some major changes to the TPAP Implementation. I would like anyone's help testing it. I need to make sure that it functions and works with all of the device types that currently support TPAP, plugs, vacuums, and cameras. I am finalizing the changes now and should be able to get something out later today, but I will need people to test and get me debug plain discover logs from the CLI of kasa to be specific in the handling. If you have any questions, let me know. |
|
|
||
| @staticmethod | ||
| def _md5_hex(value: str) -> str: | ||
| return hashlib.md5(value.encode()).hexdigest() # noqa: S324 |
Check failure
Code scanning / CodeQL
Use of a broken or weak cryptographic hashing algorithm on sensitive data High
|
|
||
| @staticmethod | ||
| def _sha256_hex_upper(value: str) -> str: | ||
| return hashlib.sha256(value.encode()).hexdigest().upper() # noqa: S324 |
Check failure
Code scanning / CodeQL
Use of a broken or weak cryptographic hashing algorithm on sensitive data High
|
|
||
| @staticmethod | ||
| def _sha1_hex(value: str) -> str: | ||
| return hashlib.sha1(value.encode()).hexdigest() # noqa: S324 |
Check failure
Code scanning / CodeQL
Use of a broken or weak cryptographic hashing algorithm on sensitive data High
| ) | ||
| return passcode | ||
| return hashlib.sha256( | ||
| (username_hint + decoded_salt + passcode).encode() |
Check failure
Code scanning / CodeQL
Use of a broken or weak cryptographic hashing algorithm on sensitive data High
|
Ok, I have pushed all my changes. I still have some clean up, but we should be pretty close now. I made a lot of changes and the first thing to do is see if the device connections for tpap still work at all. There are two paths:
If you have any questions about this, let me know. And thanks for testing. |
Discovered with new firmware for devices, TP-Link is implementing a new Encryption Type, TPAP. This is an initial implementation to see if the coding works for the handshake. Testing of the code coverage still has to be worked on. The initial implementation includes the new transport, changes to the device_factory to allow devices to select the new transport, and a change to the project to include ecdsa as a new dependency along with cryptography for the new tpaptransport.py.