Skip to content

E2EE-Status — Stand 2026-05-08

Dieses Dokument beschreibt den aktuellen Code-Stand und liefert eine aktualisierte Roadmap. Es ersetzt nicht den ursprünglichen E2EE-Plan, sondern konkretisiert ihn nach Audit des bestehenden Chat-Codes.

Was steht heute (Audit)

Es gibt keine Verschlüsselung in Prilog-Chat. Konkret:

  • Web-Client (prilog-web-client) hat einen self-rolled Matrix-Client:
    • src/gateways/matrix/matrix-gateway.ts (280 Zeilen) — direkte HTTP-Calls gegen Synapse
    • src/features/chat/chat-sync.ts (131 Zeilen) — eigener /sync-Long-Poll
    • src/features/chat/chat-store.ts (560 Zeilen) — eigene Room/Timeline-Verwaltung
    • IndexedDB-Persistenz selbst geschrieben
  • Keine Olm/Megolm-Codepfade. Suche nach encryption|m.room.encrypted|olm|megolm|crypto in prilog-web-client/src/features/chat und gateways/matrix liefert null Treffer.
  • matrix-js-sdk ist nicht in package.json (prilog-web-client).
  • Auf dem Server: Synapse läuft mit den Defaults — neue Räume werden automatisch unverschlüsselt angelegt (kein encryption_disabled_by_default-Override und auch kein encryption_enabled_by_default_for_room_type=all).

Das ist konsistent mit dem ursprünglichen Plan (project_e2ee_plan.md), der diesen Status als Ausgangspunkt nimmt. Die 4-6-Wochen-Schätzung ist realistisch, nicht konservativ — eher knapp.

Warum E2EE keine Nacht-Aktion ist

Wenn wir matrix-js-sdk einziehen, ersetzen wir im Wesentlichen alle drei Dateien oben. Das hat zwei harte Konsequenzen:

  1. Halbgare Crypto ist schlimmer als keine. Eine teilweise migrierte Olm-Setup, bei der Outgoing-Megolm-Sessions nicht korrekt rotiert werden oder Device-Keys nicht persistiert werden, suggeriert Sicherheit, die nicht da ist. Datenschützer können das angreifen.
  2. Sync-Loop und Crypto sind verzahnt. Olm-Pre-Keys müssen vor dem ersten verschlüsselten Send verfügbar sein. Megolm-Session-Keys werden per m.room.encrypted to-device verteilt. Das alles geht durch den Sync-Loop. Eigene Sync-Loop + matrix-js-sdk-Crypto ist möglich, aber die Verkabelung dauert genauso lang wie eine saubere Vollmigration.

3-Stufen-Plan (validiert)

Der ursprüngliche Plan hält. Hier mit konkreten Werkzeugen pro Stufe:

Stufe 0 — Foundation (kann jetzt ohne Risiko gemacht werden)

Das was wir vorbereiten können, ohne dass der Chat angefasst wird:

  • Schema-Erweiterungen im Backend, die später als Aufnahme-Slots dienen:
    • tenant_e2ee_settings — pro Tenant: e2ee_enabled, recovery_key_strategy ('school_admin' | 'individual'), default_room_encryption ('on_creation' | 'opt_in').
    • space_encryption_state — pro Space: encryption_pending, encryption_active, encrypted_since.
    • device_trust — pro UserDirectoryEntry × DeviceID: trust_level ('verified_by_admin' | 'self' | 'cross_signed' | 'unknown'), verified_at, verified_by_admin_id.
  • Recovery-Key-Slot im Tenant-Modell: encrypted_recovery_key_blob (zu späterem Zeitpunkt von Schul-Admin gesetzt, mit dem Tenant-Master-Recovery-Key verschlüsselt — ein zweiter Schlüssel im Hetzner Vault).
  • Audit-Tabelle e2ee_events — append-only Log für: Device-Verify, Recovery-Key-Setup, Key-Backup-Restore, Encryption-Toggle.
  • Doku der Bedrohungs-Modelle: was schützt E2EE vor wem?

Diese Stufe ist non-destructive — sie verändert das aktuelle Verhalten nicht, schafft nur die Aufnahme-Strukturen. Kann ich machen, ohne dass morgen irgendwas brennt. (Empfehlung: separate PR, sauber durchreviewen.)

Stufe 1 — Transport (1 Woche)

Ziel: TLS-Härte, ohne Olm/Megolm.

  • HSTS-Header in nginx (sollte da sein, prüfen)
  • TLS 1.3 only (sollte da sein, prüfen)
  • Synapse federation_verify_certificates: true (Default ja)
  • Backup von Synapse-Signing-Keys über die Backup-Pipeline (Phase 5) — wichtig damit Cross-Signing-Verlust nicht den Tenant zerstört
  • Audit der CSP-Header im Web-Client, damit kein In-Browser-XSS Chat-Tokens abziehen kann

Stufe 1 hat den geringsten Sicherheits-Lift, aber sie ist Pflicht-Hygiene und kann parallel zur Stufe 0 laufen. Wir vergeben uns nichts wenn wir das jetzt machen.

Stufe 2 — Server-Side (Synapse-Konfig, 3-5 Tage)

Ziel: Neue Räume werden serverseitig encrypted angelegt, Clients sind vorbereitet aber nicht zwingend.

  • Synapse encryption_enabled_by_default_for_room_type: invite (oder all)
  • Test-Tenant mit dummy-Crypto-Client (matrix-js-sdk reines Encrypt-Setup, kein Sync) — Räume werden verschlüsselt angelegt, sind aber für den Web-Client nicht lesbar (zeigt "Verschlüsselte Nachricht" statt Inhalt)

Stufe 2 ist rückwärts-kompatibel-brechend für alte Clients und macht nur Sinn wenn Stufe 3 zeitnah folgt. Sonst sind die Räume "eingefroren".

Stufe 3 — End-to-End (matrix-js-sdk, 3-4 Wochen)

Ziel: Web-Client kann verschlüsselte Räume vollständig.

Schrittfolge:

  1. Sync-Loop migrieren auf matrix-js-sdk MatrixClient. ~1 Woche inkl. IndexedDB-Migration und Re-Test aller Chat-Features.
  2. Olm-Crypto initialisieren mit cryptoStore (IndexedDB). ~3 Tage.
  3. Device-Trust-Flow:
    • Cross-Signing-Setup beim ersten Login (Master-Key + Self-Signing-Key).
    • Schul-Recovery-Key: Master-Key wird mit einem Recovery-Schlüssel verschlüsselt, der vom Tenant-Admin (Schul-Admin) gehalten wird. Bei Device-Verlust kann der Admin einen neuen Login authorisieren. Das ist die Prilog-spezifische Erweiterung, die Familien-Schulen brauchen (sonst bricht jeder Lehrer-Wechsel den Klassen-Chat).
    • Auto-Vertrauen: Tenant-Admin-Devices werden vom Backend gegengesigniert (Backend hält ein Cross-Signing-Privat-Key pro Tenant). Dadurch sind alle Lehrer-Devices automatisch verifiziert ohne manuelle Emoji-Vergleichs-Zeremonie.
  4. Key-Backup mit Server-Side-Backup-Module (m.megolm_backup.v1): automatisches Hochladen aller Megolm-Keys, Recovery beim Login.
  5. UI-Indikatoren: Schloss-Icon pro Raum, Device-Liste mit Trust-Level, Recovery-Key-Setup-Wizard.
  6. E2E-Tests: Lehrerin schickt Nachricht, neuer Lehrer-Account bekommt sie nach Login + Recovery-Key.

Phase 0 jetzt anfangen?

Empfehlung: ja, aber als separate PR mit explizitem Review. Was Phase 0 beinhaltet:

  • 4 Migrations (tenant_e2ee_settings, space_encryption_state, device_trust, e2ee_events)
  • Konfig-Service tenant-e2ee-config.service.ts (read-only Flags lesen, später schreibend)
  • Endpunkt GET /admin/v1/tenant-e2ee-status (zeigt: encryption_enabled false, aber Schema bereit)
  • Drift-Detection-Erweiterung: e2eeReadiness Spalte (was muss noch passieren bis Stufe 1 startbar)

Heute Nacht mache ich diese Migrations bewusst NICHT, weil:

  • Sie berühren Tenant-Schema und sollten reviewt sein, bevor sie auf Production landen.
  • Eine Migration die "vorbereitend" ist, ohne dass die nutzende Logik schon klar ist, kann später inkonsistent werden (Spalten-Namen ändern sich im Laufe der Implementation).

Ich habe kein Phase-0-Code-Eingriff gemacht. Diese Datei ist die Vorlage, gegen die wir gemeinsam bei der nächsten Session entscheiden können, was wir genau wann anfassen.

Was Lee zur Vorbereitung tun kann (ops, kein Code)

  • Recovery-Master-Key generieren (32 random bytes hex) und in einem Hetzner-Vault hinterlegen (NICHT im Repo, NICHT in der Backend-DB im Klartext). Der wird Stufe 3 brauchen.
  • CVE-Approval-Token erzeugen (passend zum CVE-Hotpath aus Phase 6), Hash in CVE_APPROVAL_TOKEN_HASH env eintragen, Klartext in 1Password.
  • Backup-Bucket bei Hetzner Object Storage anlegen — siehe Phase 5 Open-Items.

Damit ist die operative Vorbereitung an dir, der Code-Pfad an mir.