-
-
Notifications
You must be signed in to change notification settings - Fork 246
Description
IOT devices with KLAP encryption and login_version=2 use wrong transport (KlapTransport instead of KlapTransportV2)
Bug Description
HS300 (and possibly other IOT devices) with firmware that requires KLAP v2 authentication fail to connect with AuthenticationError: Device response did not match our challenge.
The root cause is that device_factory.py always uses KlapTransport for IOT devices with KLAP encryption, ignoring the login_version parameter from discovery. Devices with login_version=2 require KlapTransportV2 which uses a different hash algorithm.
Environment
- python-kasa version: 0.10.2
- Python version: 3.12
- OS: Linux (Ubuntu)
- Device: HS300 Power Strip (hw_ver 2.0)
Discovery Response
The device correctly reports login_version: 2 in discovery:
{
'device_model': 'HS300(US)',
'device_type': 'IOT.SMARTPLUGSWITCH',
'mgt_encrypt_schm': {
'encrypt_type': 'KLAP',
'http_port': 80,
'lv': 2, # <-- login_version = 2
},
...
}Root Cause
In device_factory.py line 231:
supported_device_protocols: dict[str, tuple[type[BaseProtocol], type[BaseTransport]]] = {
"IOT.XOR": (IotProtocol, XorTransport),
"IOT.KLAP": (IotProtocol, KlapTransport), # <-- Always uses KlapTransport (v1)
"SMART.AES": (SmartProtocol, AesTransport),
"SMART.KLAP": (SmartProtocol, KlapTransportV2), # <-- SMART devices use v2
...
}The protocol selection key is built from device_family + encryption_type only:
protocol_transport_key = protocol_name + "." + ctype.encryption_type.valueThe login_version is not considered, so all IOT KLAP devices get KlapTransport regardless of their actual requirements.
Hash Algorithm Difference
- KlapTransport (v1):
MD5(MD5(username) + MD5(password)) - KlapTransportV2 (v2):
SHA256(SHA1(username) + SHA1(password))
When the wrong transport is used, the handshake hash comparison fails:
Expected auth_hash (KlapTransportV2): 876584dd4d497651c678a8c3d6cb619c...
Actual _local_auth_hash (KlapTransport): 53a1c1366c1e8e04719da24462371caa
Steps to Reproduce
import asyncio
from kasa import Discover, Credentials
async def main():
creds = Credentials("user@example.com", "password")
devices = await Discover.discover(
target="10.0.0.255",
username=creds.username,
password=creds.password,
)
# HS300 with KLAP v2
if "10.0.0.110" in devices:
dev = devices["10.0.0.110"]
print(f"login_version: {dev.config.connection_type.login_version}")
print(f"Transport: {type(dev.protocol._transport).__name__}")
try:
await dev.update()
except Exception as e:
print(f"Error: {e}")
asyncio.run(main())Output:
login_version: 2
Transport: KlapTransport # <-- Should be KlapTransportV2!
Error: Device response did not match our challenge on ip 10.0.0.110...
Workaround
Manually create IotProtocol with KlapTransportV2:
from kasa.transports.klaptransport import KlapTransportV2
from kasa.protocols import IotProtocol
from kasa.iot import IotStrip
# Create protocol with correct transport
protocol = IotProtocol(transport=KlapTransportV2(config=device_config))
device = IotStrip(host=ip, protocol=protocol)
await device.update() # Works!Suggested Fix
Modify get_protocol() in device_factory.py to check login_version for IOT KLAP devices:
def get_protocol(config: DeviceConfig, *, strict: bool = False) -> BaseProtocol | None:
ctype = config.connection_type
# ... existing code ...
# For IOT devices with KLAP and login_version >= 2, use KlapTransportV2
if (ctype.device_family.value.startswith("IOT.") and
ctype.encryption_type == DeviceEncryptionType.Klap and
ctype.login_version is not None and
ctype.login_version >= 2):
return IotProtocol(transport=KlapTransportV2(config=config))
# ... rest of existing code ...Or add a new protocol transport key:
supported_device_protocols = {
"IOT.XOR": (IotProtocol, XorTransport),
"IOT.KLAP": (IotProtocol, KlapTransport),
"IOT.KLAP.V2": (IotProtocol, KlapTransportV2), # <-- New
...
}Additional Context
This issue appeared after a power outage caused the HS300 devices to reboot. The devices had been updated to newer firmware that uses KLAP v2 authentication. Before the reboot, existing sessions may have been cached, masking the issue.
The same credentials work correctly in the official Kasa app, confirming the credentials are valid.
Related: This may affect other IOT devices (not just HS300) that have been updated to firmware requiring KLAP v2.