FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

IOT devices with KLAP encryption and login_version=2 use wrong transport (KlapTransport instead of KlapTransportV2) · Issue #1648 · python-kasa/python-kasa · GitHub

Repository navigation

IOT devices with KLAP encryption and login_version=2 use wrong transport (KlapTransport instead of KlapTransportV2) #1648

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.value

The 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.

Activity

  1. ZeliardM commented on Jan 23, 2026

    Collaborator

    @lasdolphin A known issue and fixed in PR #1580 waiting on final approval and merge.

  2. lasdolphin commented on Jan 23, 2026

    Author

    @ZeliardM thank you, I'll keep it opened if you don't mind, may be mentioned woraround will help someone.

  3. Allowencer commented on Feb 19, 2026

    My Kasa HS300 upgraded it's firmware yesterday and I'm running into this exact same issue:
    Raised error: Server response doesn't match our challenge on ip w.x.y.z

    Any update on when this will get merged in a new version of the module is available?

  4. WeAreBorgPony commented on Feb 21, 2026

    A workaround for Home Assistant users: pair your Tapo account via the app with your Kasa account. In settings, go to Third-Party Services and enable Third-party compatibility. This should immediately allow HA to reauthenticate with these devices. If a device reboots it will likely break auth again. Disable and re-enable Third-party compatibility again in the app to correct this.

  5. linked a pull request that will close this issueSupport new_klap IoT devices #1580on Feb 21, 2026
  6. thatdaveguy1 commented on Apr 18, 2026

    I reproduced this in a live Home Assistant environment with IOT.SMARTPLUGSWITCH KLAP lv=2 devices (HS103 firmware line), and opened #1692 with a focused fix for login_version-based transport selection plus a related class-detection fix for KLAP strips.

  7. sciamachy commented on Jul 8, 2026

    Confirming this on a second, different device family — an HS103 (hardware HS103V5) smart plug, i.e. IOT.SMARTPLUGSWITCH, not just the HS300 strip. So the bug is family-wide across IOT KLAP devices, not HS300-specific.

    Environment: python-kasa 0.10.2 (and current master), Python 3.14.

    Discovery reports:

    Device Type:   IOT.SMARTPLUGSWITCH
    Encrypt Type:  KLAP
    Login version: 2          (new_klap: 1, http_port 80, is_support_https False)
    

    Symptom: AuthenticationError: Device response did not match our challenge at handshake1. As this issue says, get_protocol() builds the transport key from family + encrypt_type + https only (login_version is never read), so IOT.KLAP is hard-mapped to the v1 KlapTransport and there is no path to KlapTransportV2.

    Confirmed by direct test:

    • I captured the device's handshake1 response and brute-forced the auth formula: the server hash matches sha256(local_seed + remote_seed + sha256(sha1(user)+sha1(pass))) — i.e. exactly KlapTransportV2's generate_auth_hash + handshake1_seed_auth_hash. The v1 formula does not match. So the device genuinely wants v2.
    • Manually pairing IotProtocol with KlapTransportV2 authenticates and controls the plug reliably.
    • The CLI overrides do not help, consistent with the root cause: --login-version 2 is ignored, and --device-family IOT.SMARTPLUGSWITCH --encrypt-type klap deterministically yields the IOT.KLAP → v1 mapping.

    Working interim workaround (for anyone hitting this before a fix ships), driving the public device API so it still generalises across device types:

    from kasa import DeviceConfig, Credentials
    from kasa.deviceconfig import DeviceConnectionParameters
    from kasa.protocols.iotprotocol import IotProtocol
    from kasa.transports.klaptransport import KlapTransportV2
    from kasa.device_factory import get_device_class_from_sys_info
    
    conn = DeviceConnectionParameters.from_values("IOT.SMARTPLUGSWITCH", "KLAP", login_version=2, https=False)
    cfg = DeviceConfig(host=HOST, credentials=Credentials(user, pw), connection_type=conn)
    proto = IotProtocol(transport=KlapTransportV2(config=cfg))
    resp = await proto.query({"system": {"get_sysinfo": {}}})
    dev = get_device_class_from_sys_info(resp)(HOST, protocol=proto)
    await dev.update()

    Happy to test PR #1692 against this HS103V5 if that helps confirm the fix on a plug as well as the HS300.

  8. ZeliardM commented on Jul 9, 2026

    Collaborator

    There is already an existing, extensively tested PR that handles all of the CLI and API changes, #1580, we are working to get this implemented and a new version released soon.

  9. malker97 commented on Jul 24, 2026

    Confirming this independently on an HS300 (US), hardware 2.0, firmware 1.1.2 Build 241220 Rel.171333. The device advertises IOT.SMARTPLUGSWITCH, KLAP, lv=2, and new_klap=1; python-kasa 0.10.2 chooses the v1 transport and fails authentication.

    I tested PR #1580 at 71a4147984aedc7cfa9ce4613204ecf28b15dac7 against the real device. Both discovery and a subsequent Device.connect() from serialized configuration selected KlapTransportV2, returned IotStrip, and exposed all six child outlets correctly. I posted the full sanitized validation details on #1580.

  10. Daisame commented on Jul 25, 2026

    There is a patch that another person that does work, but even then there are some bugs. With the help of Claude, made a secondary patch that will work. Ask Claude to impliment this for you and it can.

    Previous patch (bradcarnage : https://gist.github.com/bradcarnage/380363913bd1a991ad440c341e010064
    My Patch (with Claude Help): https://gist.github.com/Daisame/9fea710c518507dbb22a5de6841db4b5

    Problem was that if you updated HA or rebooted, there was a chance that the hash was never saved to the device and it would want to re-authenticate and fail. You could remove and re-add it, but wanted to try and mitigate that somewhat because it would not tell you if the hash was saved as there were a few ways the device would try to authenticate the first time. This warns you if the hash was not saved when you add it the first time, and says "remove and readd to try to correct it". Confirmed if you remove the hash, it would drop out of the registration and force you to readd it through the UI. Can't do itg by reconfigure, had to remove and readd it. Adding the hash back in immediately fixed it on HA reload. So as long as the hash saves, you 'should' be OK. Can't really test a negative very well, so don't know if it will properly warn you if it does not save the hash but it should. It's just a patch, so it does not otherwise interact or change base HA or TP-Link Home integration.

    [Disclaimer] NOT A PROGRAMMER. My local AI agents and Claude helped me with this. Happy to help where I can, but honestly, the files should be easily readable by even a low end AI model. I pointed Claude at this original patch author's readme and it immediately knew what to do.

  11. jeffprosser commented on Sep 10, 2026

    Confirming this on a third device family: a bulb — IOT.SMARTBULB, KL125(US)
    hw 4.0, fw 1.1.1 Build 260710 Rel.082646. The reports here so far cover the
    HS300 strip and the HS103 / KP115 plugs, so this is not limited to
    IOT.SMARTPLUGSWITCH.

    Discovery (UDP 20002):

    device_type      IOT.SMARTBULB
    device_model     KL125(US)
    mgt_encrypt_schm {"encrypt_type":"KLAP","new_klap":1,"lv":2,
                      "http_port":80,"is_support_https":false}
    

    Same hash result as #1740 — handshake1 returns HTTP 200 and only the credential
    comparison fails:

    derivation result
    v1 md5(md5(u) + md5(p)) no match
    v2 sha256(sha1(u) + sha1(p)) match

    One extra data point that may be worth having: this bulb does not support
    the SMART command set, so SMART.KLAP is not a usable workaround on these
    devices. Completing handshake1 + handshake2 with the v2 hash and querying both
    command sets on the same session:

    {"system":{"get_sysinfo":{}}}
      -> {"system":{"get_sysinfo":{"sw_ver":"1.1.1 Build 260710 Rel.082646",
         "hw_ver":"4.0","model":"KL125(US)","mic_type":"IOT.SMARTBULB", ...}}}
    
    {"method":"get_device_info","requestID":1,...}
      -> {}
    

    So the required pairing really is IotProtocol + KlapTransportV2, as #1580 and
    #1692 implement. Worth stating explicitly because repointing device_family at a
    SMART.* value looks like a config-only fix, but it authenticates and then fails
    every command.

    Happy to test either PR against a bulb if a third family is useful.

    Also flagging for anyone arriving from Home Assistant: HA 2026.9.1 pins
    python-kasa[speedups]==0.10.2, which predates both PRs, so there is no HA-side
    setting that avoids this.

  12. iozm commented on Sep 14, 2026

    Can this be fixed? experiencing issues with KL125s it wont let me re authenticate. "Unable to authenticate: Device response did not match our challenge on ip XXX.XXX.X.XX, check that your e-mail and password (both case-sensitive) are correct." integration works on every other kasa device I own except the 2 KL125 bulbs.

  13. androo829 commented on Sep 24, 2026

    Please fix. (KL125(US) hw 4.0): v1 rejected, v2 works with the same credentials.

  14. androo829 commented on Sep 24, 2026

    KP401(US) (sw 1.0.2 Build 230901) has the same problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL