TPAP Implementation - #1592
TPAP Implementation#1592ZeliardM wants to merge 73 commits into
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.21% 93.73% +0.52%
==========================================
Files 157 158 +1
Lines 9819 10637 +818
Branches 1005 1110 +105
==========================================
+ Hits 9153 9971 +818
Misses 472 472
Partials 194 194 ☔ View full report in Codecov by Harness. 🚀 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. |
@ZeliardM credentials recalled on reboots, and P110 plugs working perfectly. Thanks! |
RV30 Max validation update for PR #1592Tested against the current Device under test: Static checks
Relevant feature IDs confirmed in the PR code: Runtime results on RV30 Max
Reversible write details
Motion command detailsThe robot accepted and executed the motion commands, confirmed by status reads:
However, each action command returned this CLI error after the robot had already accepted the command: This looks like a CLI formatting issue for action responses, not a TPAP transport/device-control failure. The likely code path is ConclusionFor RV30 Max, the current PR works for the practical TPAP paths:
The only issue found is the CLI action-response formatting error above. I would not consider that a blocker for the TPAP implementation itself; it can be handled as a small CLI follow-up fix. |
|
Tested and seems to work, ty - AI comment below: L900-5(EU) with
|
| Model | Firmware | Transport | Result |
|---|---|---|---|
| L900-5(EU) | 1.4.3 Build 260402 | TPAP | OK |
| P110M(AU) ×2 | 1.4.3 Build 260526 | TPAP | OK |
| L535E(EU) | 1.4.2 Build 260203 | TPAP | OK |
| L430C(EU) | 1.0.9 Build 250718 | KLAP | OK (no regression) |
Also worth noting for anyone else with an L900 — with Third-Party Compatibility on, this strip advertises KLAP but its /app/handshake1 returns 500 Server Internal Error with an empty body, so it is completely unreachable on master. Turning the setting off puts it back on TPAP, where this branch handles it. That inverts the usual advice in these threads.
Happy to run further tests or capture full debug logs on request — I have the hardware and a reproducible setup.
|
Small FreeBSD/ PR against Verified on KP125M(US) with |
|
P304M(UK) confirmed working on Tested against PR head via: Result: authenticates and reads full state correctly — all 4 child sockets (relay state, voltage/current/power, energy monitoring, auto-off config, power protection threshold) all report as expected. No regressions observed. |
|
P100 (non-M), hw 2.0, fw 1.4.6 (Jun 2026) — TPAP handshake reaches Reporting a device family and a newer firmware than the successful reports so far (those were P125M / P110M on 1.4.2 Build 251204, and RV30). This is a plain P100, no Matter support. Environment
Discovery reports (from the stock integration's error): What I found on connection parameters
Failure So Note on -2203: earlier in this thread -2203 was associated with using a stored Possible firmware regression? Note this firmware (Build 260617, June 2026) is roughly six months newer than the builds in the working reports above (1.4.2 Build 251204). If TP-Link tightened the PAKE/NOC path in the meantime, Happy to run any additional diagnostics or patched builds against this P100 if that's useful — I have the device available for testing. |
|
I just came across this while troubleshooting some of my Tapo switches that auto-updated firmware and now won't connect to HA. How close are we to getting this merged? Do we still need help testing? Lmk what I can do to help this along. |
|
Got this branch authenticating against a real device. Tested The reason this one works when the RV30 doesn't is in discovery: {"tpap":{"tls":0,"dac":0,"noc":0,"pake":[2],"port":80}}No TLS, no attestation, plain HTTP on 80. The SPAKE2+ code here is already correct for devices that don't ask for DAC/NOC, which makes that a separate axis rather than a blocker. So the main suggestion: land TPAP for the non-attestation case now, and keep the NOC signature work on its own track. That covers at least the bulb and strip family, and those devices need nothing that isn't already in this branch. Three specific changes I'd make: 1. Move {"error_info":{"lockedMinute":0,"failedAttempts":1,"remainAttempts":14},"error_code":-2203}The device allows around 15 failures before locking. 2. Add 3. Treat HTTP 401 on the ds endpoint as a retryable live-session error in So one poll always drops. Minor, while I was in there: One debugging note that may save time on the devices still failing. One request tells you whether a username is right, independently of credentials, which is how I confirmed One limit on that: A related trap: this repo's I have the device on hand and a local harness against it, so send patches or trace requests my way. I can also generate a fixture for the test suite. |
|
Tested this locally against a real device today (L530B(EU) bulb, firmware 1.4.4). Test setup:
Also tested live in Home Assistant (Docker, linuxserver/homeassistant, Python 3.14): force-reinstalled python-kasa from this branch into the running container (pip install git+https://github.com/ZeliardM/python-kasa.git@feature/tpap — note a plain pip install won't actually replace the package since this branch reports the same version, 0.10.2, as what's already installed; needed --force-reinstall). Restarted HA, the tplink integration loaded with no errors, and the bulb entity worked normally through the dashboard. Thanks for raising the PR! If I can be of any further help, do let me know. I have a L900 lightstrip that is still using the old KLAP encryption but has a firmware update waiting for it. I haven't updated it yet, but can update to test if needed. |
|
I am running this live against two KP125M smart power switches and it seems to work. One of the switches, is on old firmware, which doesn’t require TPAP, and a second is on new firmware which does. Code so far seems to work fine on both of them. Feel free to ask if you need more details. |
|
I finally got a chance to test this. I re-installed python-kasa from this branch into my live HA. I have about 15 TP-link switches and smart plugs on various firmware versions that have slowly been updating on their own. Happy to report that they're all authenticating correctly after the update! Below are my device models and firmware versions confirmed working with this branch.
|
|
Following up with something about the Third-Party Compatibility workaround rather than the crypto on this branch, since it is being recommended here and in #1590. That toggle can cost the user their stored credentials. The L920 I tested against reverted from TPAP to KLAP, and discovery went from the profile in my earlier comment to: {"result":{"sub_method":"discover","tpap_preferred":false},"error_code":0}Firmware was byte identical to when TPAP worked ( Discovery handles the change correctly and rewrites the encryption type. I have opened #1749 against master with the details and a fix. It is reachable without TPAP on any aes or klap change so it stands alone there, but it covers this branch's hash format too, so a TPAP device that reverts to KLAP reconnects without a reauth. Two things from diagnosing it that are worth knowing here: The failure does not name TPAP. The warning comes from
|
sslaestransport and the tpap transport on python-kasa#1592 store a credentials_hash that is base64 json of the plaintext credentials, so the credentials can be read back out of it. klap and aes hashes are one way and cannot. When a device changes its encryption type the stored hash is the one the old transport wrote. The previous commit stops that being mistaken for a bad password, but the connection still has no credentials to offer and the caller has to prompt for them again. Home Assistant stores the hash as the only copy of the credentials for a device, so that prompt loses them. Read the credentials back out when the hash is one of the plaintext forms and let the transport derive its own. A tpap device that reverts to klap because Third-Party Compatibility was turned on then reconnects without a reauth. This only works in that direction.
|
Adding a KP125M(US) data point, since this thread has KP125M reported working but no firmware detail or fixture behind it. KP125M(US) hw 1.0, fw 1.4.1 Build 260721 — discovery: "tpap_preferred": true,
"tpap": { "tls": 0, "dac": 1, "noc": 1, "pake": [2], "port": 80 },
"mgt_encrypt_schm": { "is_support_https": false, "http_port": 80, "encrypt_type": "TPAP", "lv": 2 }Note I opened ZeliardM#8 against The plug is permanently on my LAN, so happy to run anything targeted against this firmware. |
|
Confirming this branch works on KP125M(US) — the port-80 / plaintext-HTTP Device / firmware
Result — all working against 4 physical plugs (2× dishwasher, 2× air purifier):
Environment
Migration note for existing entries (KLAP → TPAP after a firmware bump): Happy to capture packet traces or test specific commands on this hardware if useful. |
|
Following up on my KP125M(US) confirmation above with a security note, since I had the Captured 7 consecutive request/response transactions from a real KP125M(US) For an AEAD (AES-CCM or ChaCha20-Poly1305), nonce reuse under one key with two different This looks protocol-level (the device dictates the seq echo; Capture method (reproducible by anyone with a device): patch |
|
Hi, this may help with the part where I got the SPAKE2+ login and the The things that blocked me for a long time, in case it's the same for you:
Session key and nonce derivation, the full field list of |
…sa@feature/tpap) Upstream python-kasa doesn't support TPAP yet (the encryption scheme our P100 actually uses) — see python-kasa/python-kasa#1590 (open since 2025-10) and the fix PR python-kasa/python-kasa#1592 (not yet merged). Switch back to the plain PyPI package once that PR ships in a release. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Tested this branch against a KP125M (US, fw 1.4.1) that advertises So it advertises TPAP capability but prefers KLAP — and it connects and reads energy fine over plain KLAP (login_version 2, http port 80, https false). Could |
|
Disclaimer: AI-generated text below the line, generated by Claude Opus 5.5 (Claude Code), from the prompts:
The real value I provide here is the hardware to reproduce and test on. It's late night here, I haven't even bothered reading through the text below - perhaps I'll do more research tomorrow and edit this comment. RV50 Pro Omni(EU): in-band discover says
|
|
Camera data point, since none had been posted yet: with three small changes this branch logs in and runs a full
Handshake takes about 5 s, then detection settings, LED, pan/tilt and RSSI all read fine. |
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.