From a3790b9d4d84af96142c0e1c2ea9e207ffed6748 Mon Sep 17 00:00:00 2001 From: Arthur Gay Date: Fri, 25 Oct 2024 15:30:17 +0200 Subject: [PATCH] chromium: handle latest sqlite schema Starting from version 24, the sha256 of the domain is prepended to the encrypted value of the cookie. See: - https://issues.chromium.org/issues/40185252 - https://chromium-review.googlesource.com/c/chromium/src/+/5792044 - https://chromium.googlesource.com/chromium/src/net/+/master/extras/sqlite/sqlite_persistent_cookie_store.cc#193 --- browser_cookie3/__init__.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/browser_cookie3/__init__.py b/browser_cookie3/__init__.py index 33bf364..fc66df6 100644 --- a/browser_cookie3/__init__.py +++ b/browser_cookie3/__init__.py @@ -489,6 +489,7 @@ def load(self): with _DatabaseConnetion(self.cookie_file) as con: con.text_factory = _text_factory cur = con.cursor() + has_integrity_check_for_cookie_domain = self._has_integrity_check_for_cookie_domain(cur) try: # chrome <=55 cur.execute('SELECT host_key, path, secure, expires_utc, name, value, encrypted_value, is_httponly ' @@ -512,12 +513,34 @@ def load(self): expires = (expires_nt_time_epoch / 1000000) - \ self.UNIX_TO_NT_EPOCH_OFFSET - value = self._decrypt(value, enc_value) + value = self._decrypt(value, enc_value, has_integrity_check_for_cookie_domain) c = create_cookie(host, path, secure, expires, name, value, http_only) cj.set_cookie(c) return cj + @staticmethod + def _has_integrity_check_for_cookie_domain(con): + """Starting from version 24, the sha256 of the domain is prepended to the encrypted value + of the cookie. + + See: + - https://issues.chromium.org/issues/40185252 + - https://chromium-review.googlesource.com/c/chromium/src/+/5792044 + - https://chromium.googlesource.com/chromium/src/net/+/master/extras/sqlite/sqlite_persistent_cookie_store.cc#193 + """ + try: + value, = con.execute('SELECT value FROM meta WHERE key = "version";').fetchone() + except sqlite3.OperationalError: + return False + + try: + version = int(value) + except ValueError: + return False + + return version >= 24 + @staticmethod def _decrypt_windows_chromium(value, encrypted_value): @@ -531,7 +554,7 @@ def _decrypt_windows_chromium(value, encrypted_value): assert isinstance(data, bytes) return data.decode() - def _decrypt(self, value, encrypted_value): + def _decrypt(self, value, encrypted_value, has_integrity_check_for_cookie_domain=False): """Decrypt encoded cookies""" if sys.platform == 'win32': @@ -556,6 +579,8 @@ def _decrypt(self, value, encrypted_value): except ValueError: raise BrowserCookieError( 'Unable to get key for cookie decryption') + if has_integrity_check_for_cookie_domain: + data = data[32:] return data.decode() if value or (encrypted_value[:3] not in [b'v11', b'v10']): @@ -579,6 +604,8 @@ def _decrypt(self, value, encrypted_value): try: decrypted = unpad(cipher.decrypt( encrypted_value), AES.block_size) + if has_integrity_check_for_cookie_domain: + decrypted = decrypted[32:] return decrypted.decode('utf-8') except ValueError: pass