diff --git a/kasa/klaptransport.py b/kasa/klaptransport.py index 66052f590..898444c2e 100644 --- a/kasa/klaptransport.py +++ b/kasa/klaptransport.py @@ -46,11 +46,12 @@ import hashlib import logging import secrets +import struct import time from pprint import pformat as pf from typing import Any, Dict, Optional, Tuple, cast -from cryptography.hazmat.primitives import hashes, padding +from cryptography.hazmat.primitives import padding from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from .credentials import Credentials @@ -66,18 +67,15 @@ ONE_DAY_SECONDS = 86400 SESSION_EXPIRE_BUFFER_SECONDS = 60 * 20 +PACK_SIGNED_LONG = struct.Struct(">l").pack + def _sha256(payload: bytes) -> bytes: - digest = hashes.Hash(hashes.SHA256()) # noqa: S303 - digest.update(payload) - hash = digest.finalize() - return hash + return hashlib.sha256(payload).digest() # noqa: S324 def _sha1(payload: bytes) -> bytes: - digest = hashes.Hash(hashes.SHA1()) # noqa: S303 - digest.update(payload) - return digest.finalize() + return hashlib.sha1(payload).digest() # noqa: S324 class KlapTransport(BaseTransport): @@ -426,12 +424,15 @@ class KlapEncryptionSession: i.e. sequence number which the device expects to increment. """ + _cipher: Cipher + def __init__(self, local_seed, remote_seed, user_hash): self.local_seed = local_seed self.remote_seed = remote_seed self.user_hash = user_hash self._key = self._key_derive(local_seed, remote_seed, user_hash) (self._iv, self._seq) = self._iv_derive(local_seed, remote_seed, user_hash) + self._aes = algorithms.AES(self._key) self._sig = self._sig_derive(local_seed, remote_seed, user_hash) def _key_derive(self, local_seed, remote_seed, user_hash): @@ -451,31 +452,31 @@ def _sig_derive(self, local_seed, remote_seed, user_hash): payload = b"ldk" + local_seed + remote_seed + user_hash return hashlib.sha256(payload).digest()[:28] - def _iv_seq(self): - seq = self._seq.to_bytes(4, "big", signed=True) - iv = self._iv + seq - return iv + def _generate_cipher(self): + iv_seq = self._iv + PACK_SIGNED_LONG(self._seq) + cbc = modes.CBC(iv_seq) + self._cipher = Cipher(self._aes, cbc) def encrypt(self, msg): """Encrypt the data and increment the sequence number.""" - self._seq = self._seq + 1 + self._seq += 1 + self._generate_cipher() + if isinstance(msg, str): msg = msg.encode("utf-8") - cipher = Cipher(algorithms.AES(self._key), modes.CBC(self._iv_seq())) - encryptor = cipher.encryptor() + encryptor = self._cipher.encryptor() padder = padding.PKCS7(128).padder() padded_data = padder.update(msg) + padder.finalize() ciphertext = encryptor.update(padded_data) + encryptor.finalize() signature = hashlib.sha256( - self._sig + self._seq.to_bytes(4, "big", signed=True) + ciphertext + self._sig + PACK_SIGNED_LONG(self._seq) + ciphertext ).digest() return (signature + ciphertext, self._seq) def decrypt(self, msg): """Decrypt the data.""" - cipher = Cipher(algorithms.AES(self._key), modes.CBC(self._iv_seq())) - decryptor = cipher.decryptor() + decryptor = self._cipher.decryptor() dp = decryptor.update(msg[32:]) + decryptor.finalize() unpadder = padding.PKCS7(128).unpadder() plaintextbytes = unpadder.update(dp) + unpadder.finalize() diff --git a/kasa/protocol.py b/kasa/protocol.py index 60b3d7ca6..aa9e3cbea 100755 --- a/kasa/protocol.py +++ b/kasa/protocol.py @@ -11,6 +11,7 @@ """ import base64 import errno +import hashlib import logging import struct from abc import ABC, abstractmethod @@ -18,8 +19,6 @@ # When support for cpython older than 3.11 is dropped # async_timeout can be replaced with asyncio.timeout -from cryptography.hazmat.primitives import hashes - from .credentials import Credentials from .deviceconfig import DeviceConfig @@ -29,11 +28,8 @@ def md5(payload: bytes) -> bytes: - """Return an md5 hash of the payload.""" - digest = hashes.Hash(hashes.MD5()) # noqa: S303 - digest.update(payload) - hash = digest.finalize() - return hash + """Return the MD5 hash of the payload.""" + return hashlib.md5(payload).digest() # noqa: S324 class BaseTransport(ABC):