feat(CF-2502): proper E2E encryption with cross-signing and device lifecycle

Replace insecure auto-trust-all-devices with cross-signed-only trust policy.
Extract cross-signing manager into reusable module with vault backup/recovery.
Add device cleanup script and automatic old device pruning on startup.

- device_trust.py: CrossSignedOnlyPolicy (only trust cross-signed devices)
- cross_signing.py: Extracted from bot.py, adds vault seed backup + recovery
- scripts/matrix_device_cleanup.py: Synapse Admin API bulk device cleanup CLI
- bot.py: Use new modules, add _cleanup_own_devices() on startup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian Gick
2026-03-23 19:05:48 +02:00
parent bfc717372c
commit 7fd3aae176
4 changed files with 609 additions and 114 deletions

34
device_trust.py Normal file
View File

@@ -0,0 +1,34 @@
"""Device trust policy: only trust cross-signed devices.
Replaces the insecure auto-trust-all pattern with selective verification
based on cross-signing signatures.
"""
import logging
logger = logging.getLogger(__name__)
class CrossSignedOnlyPolicy:
"""Trust only devices that carry a cross-signing signature.
A device's signatures dict typically contains its own ed25519:DEVICE_ID
self-signature. A cross-signed device additionally has a signature from
the user's self-signing key (ed25519:SELF_SIGNING_PUB). This policy
checks for that extra signature.
"""
def should_trust(self, user_id: str, device) -> bool:
"""Return True if device has a cross-signing signature beyond its own."""
sigs = getattr(device, "signatures", None)
if not sigs:
return False
user_sigs = sigs.get(user_id, {})
device_self_key = f"ed25519:{device.device_id}"
# Trust if any signature key is NOT the device's own key
for key_id in user_sigs:
if key_id != device_self_key:
return True
return False