Renew the handshake session 20 minutes before we think it will expire (#697)

* Renew the KLAP handshake session 20 minutes before we think it will expire

Currently we assumed the clocks were perfectly aligned and the handshake
session lasted 20 hours.  We now add a 20 minute buffer

* use timeout cookie when available
This commit is contained in:
J. Nick Koston 2024-01-23 23:11:27 -10:00 committed by GitHub
parent 24c645746e
commit bab40d43e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 31 additions and 11 deletions

View File

@ -36,6 +36,10 @@ from .protocol import DEFAULT_CREDENTIALS, BaseTransport, get_default_credential
_LOGGER = logging.getLogger(__name__)
ONE_DAY_SECONDS = 86400
SESSION_EXPIRE_BUFFER_SECONDS = 60 * 20
def _sha1(payload: bytes) -> str:
sha1_algo = hashlib.sha1() # noqa: S324
sha1_algo.update(payload)
@ -59,6 +63,7 @@ class AesTransport(BaseTransport):
DEFAULT_PORT: int = 80
SESSION_COOKIE_NAME = "TP_SESSIONID"
TIMEOUT_COOKIE_NAME = "TIMEOUT"
COMMON_HEADERS = {
"Content-Type": "application/json",
"requestByApp": "true",
@ -254,7 +259,9 @@ class AesTransport(BaseTransport):
**self.COMMON_HEADERS,
self.CONTENT_LENGTH: str(self.KEY_PAIR_CONTENT_LENGTH),
}
status_code, resp_dict = await self._http_client.post(
http_client = self._http_client
status_code, resp_dict = await http_client.post(
url,
json=self._generate_key_pair_payload(),
headers=headers,
@ -277,17 +284,21 @@ class AesTransport(BaseTransport):
handshake_key = resp_dict["result"]["key"]
if (
cookie := self._http_client.get_cookie( # type: ignore
cookie := http_client.get_cookie( # type: ignore
self.SESSION_COOKIE_NAME
)
) or (
cookie := self._http_client.get_cookie( # type: ignore
"SESSIONID"
)
cookie := http_client.get_cookie("SESSIONID") # type: ignore
):
self._session_cookie = {self.SESSION_COOKIE_NAME: cookie}
self._session_expire_at = time.time() + 86400
timeout = int(
http_client.get_cookie(self.TIMEOUT_COOKIE_NAME) or ONE_DAY_SECONDS
)
# There is a 24 hour timeout on the session cookie
# but the clock on the device is not always accurate
# so we set the expiry to 24 hours from now minus a buffer
self._session_expire_at = time.time() + timeout - SESSION_EXPIRE_BUFFER_SECONDS
if TYPE_CHECKING:
assert self._key_pair is not None
self._encryption_session = AesEncyptionSession.create_from_keypair(

View File

@ -63,6 +63,10 @@ from .protocol import DEFAULT_CREDENTIALS, BaseTransport, get_default_credential
_LOGGER = logging.getLogger(__name__)
ONE_DAY_SECONDS = 86400
SESSION_EXPIRE_BUFFER_SECONDS = 60 * 20
def _sha256(payload: bytes) -> bytes:
digest = hashes.Hash(hashes.SHA256()) # noqa: S303
digest.update(payload)
@ -86,6 +90,7 @@ class KlapTransport(BaseTransport):
DEFAULT_PORT: int = 80
DISCOVERY_QUERY = {"system": {"get_sysinfo": None}}
SESSION_COOKIE_NAME = "TP_SESSIONID"
TIMEOUT_COOKIE_NAME = "TIMEOUT"
def __init__(
self,
@ -271,14 +276,18 @@ class KlapTransport(BaseTransport):
self._session_cookie = None
local_seed, remote_seed, auth_hash = await self.perform_handshake1()
if cookie := self._http_client.get_cookie( # type: ignore
self.SESSION_COOKIE_NAME
):
http_client = self._http_client
if cookie := http_client.get_cookie(self.SESSION_COOKIE_NAME): # type: ignore
self._session_cookie = {self.SESSION_COOKIE_NAME: cookie}
# The device returns a TIMEOUT cookie on handshake1 which
# it doesn't like to get back so we store the one we want
self._session_expire_at = time.time() + 86400
timeout = int(
http_client.get_cookie(self.TIMEOUT_COOKIE_NAME) or ONE_DAY_SECONDS
)
# There is a 24 hour timeout on the session cookie
# but the clock on the device is not always accurate
# so we set the expiry to 24 hours from now minus a buffer
self._session_expire_at = time.time() + timeout - SESSION_EXPIRE_BUFFER_SECONDS
self._encryption_session = await self.perform_handshake2(
local_seed, remote_seed, auth_hash
)