From bd5ef68d82cf123924feeedec2519d4f3feb1fbf Mon Sep 17 00:00:00 2001 From: Austin519 Date: Thu, 17 Sep 2026 10:22:09 -0600 Subject: [PATCH] tpap: harden pre-auth DoS + use constant-time confirm-MAC compare Two defense-in-depth fixes to TpapEncryptionSession: - Cap PBKDF2-HMAC-SHA256 iterations from the unauthenticated pake_register response (MAX_PAKE_ITERATIONS = 100_000). The count was previously unbounded and hashed synchronously on the event loop, so a malicious/on-path device could stall the whole asyncio process (e.g. all of Home Assistant) before SPAKE2+ confirmation ever authenticates the peer. - Compare the device confirmation MAC with hmac.compare_digest instead of a short-circuiting '!=', removing a timing side-channel on MAC verification. No behavioral change for real devices (fixtures use iterations <= 100). --- kasa/transports/tpaptransport.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kasa/transports/tpaptransport.py b/kasa/transports/tpaptransport.py index 3a6471417..0087645ec 100644 --- a/kasa/transports/tpaptransport.py +++ b/kasa/transports/tpaptransport.py @@ -54,6 +54,10 @@ class TpapEncryptionSession: PAKE_CONTEXT_TAG = b"PAKE V1" TAG_LEN = 16 NONCE_LEN = 12 + # PBKDF2-HMAC-SHA256 iteration count from an unauthenticated pake_register + # response is otherwise unbounded and runs synchronously on the event loop; + # cap it well above any realistic device value to prevent a pre-auth DoS. + MAX_PAKE_ITERATIONS = 100_000 CIPHER_PARAMETERS = { "aes_128_ccm": ( b"tp-kdf-salt-aes128-key", @@ -795,7 +799,7 @@ def _build_share_params_from_register( "TPAP register response has invalid iterations" ) from exc - if iterations <= 0: + if iterations <= 0 or iterations > self.MAX_PAKE_ITERATIONS: raise KasaException("TPAP register response has invalid iterations") encryption = str(register_result.get("encryption") or "") @@ -931,7 +935,9 @@ def _establish_session_from_share_result( dev_confirm = str(share_result.get("dev_confirm") or "").lower() if not dev_confirm: raise KasaException("TPAP share response missing dev_confirm") - if dev_confirm != (self._expected_dev_confirm or "").lower(): + if not hmac.compare_digest( + dev_confirm, (self._expected_dev_confirm or "").lower() + ): raise KasaException("TPAP confirmation mismatch") if self._use_dac_certification():