From 1f3f1200423ab03aef582e7d3d2716b064f8290b Mon Sep 17 00:00:00 2001 From: alt-glitch Date: Sat, 11 Apr 2026 07:29:27 +0530 Subject: [PATCH] fix(matrix): persist E2EE crypto store and fix decrypted event dedup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address two bugs found by code review: 1. MemoryCryptoStore loses all E2EE keys on restart — now pickle the store to disk on disconnect and restore on connect, preserving Megolm sessions across restarts. 2. Encrypted events buffered for retry were silently dropped after decryption because _on_encrypted_event registered the event ID in the dedup set, then _on_room_message rejected it as a duplicate. Now clear the dedup entry before routing decrypted events. --- gateway/platforms/matrix.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/gateway/platforms/matrix.py b/gateway/platforms/matrix.py index 6c1041cf2..87a765882 100644 --- a/gateway/platforms/matrix.py +++ b/gateway/platforms/matrix.py @@ -296,6 +296,20 @@ class MatrixAdapter(BasePlatformAdapter): from mautrix.crypto.store import MemoryCryptoStore crypto_store = MemoryCryptoStore() + + # Restore persisted crypto state from a previous run. + pickle_path = _STORE_DIR / "crypto_store.pickle" + if pickle_path.exists(): + try: + import pickle + with open(pickle_path, "rb") as f: + saved = pickle.load(f) # noqa: S301 — trusted local file + if isinstance(saved, MemoryCryptoStore): + crypto_store = saved + logger.info("Matrix: restored E2EE crypto store from %s", pickle_path) + except Exception as exc: + logger.warning("Matrix: could not restore crypto store: %s", exc) + olm = OlmMachine(client, crypto_store, state_store) # Set trust policy: accept unverified devices so senders @@ -371,6 +385,20 @@ class MatrixAdapter(BasePlatformAdapter): except (asyncio.CancelledError, Exception): pass + # Persist E2EE crypto store before closing so the next restart + # can decrypt events using sessions from this run. + if self._client and self._encryption and getattr(self._client, "crypto", None): + try: + import pickle + crypto_store = self._client.crypto.crypto_store + _STORE_DIR.mkdir(parents=True, exist_ok=True) + pickle_path = _STORE_DIR / "crypto_store.pickle" + with open(pickle_path, "wb") as f: + pickle.dump(crypto_store, f) + logger.info("Matrix: persisted E2EE crypto store to %s", pickle_path) + except Exception as exc: + logger.debug("Matrix: could not persist crypto store on disconnect: %s", exc) + if self._client: try: await self._client.api.session.close() @@ -804,6 +832,11 @@ class MatrixAdapter(BasePlatformAdapter): ) # Route to the appropriate handler. + # Remove from dedup set so _on_room_message doesn't drop it + # (the encrypted event ID was already registered by _on_encrypted_event). + decrypted_id = str(getattr(decrypted, "event_id", getattr(event, "event_id", ""))) + if decrypted_id: + self._processed_events_set.discard(decrypted_id) try: await self._on_room_message(decrypted) except Exception as exc: