Sicherheitsmodell
Diese Seite ist die Kurzfassung der verbindlichen Spezifikation. Sie nennt auch die Grenzen — ein Sicherheitsmodell, das nur Zusicherungen aufzählt, ist Werbung.
Schlüsselhierarchie
Vault Key (VK) 32 Byte, zufällig. Nur im RAM und im OS-Keystore.
├─ wrap_gerät = seal(VK, öffentlicher X25519-Schlüssel) liegt am Server
└─ wrap_recovery = XChaCha20-Poly1305(VK, Schlüssel aus dem Recovery-Code)
Record Key (RK) = HKDF(VK, epoch) — pro Epoch neu, nie wiederverwendet
Pro Gerät zwei Schlüsselpaare, beide im Secure Storage des Betriebssystems:
| Paar | Zweck |
|---|---|
| X25519 | Entschlüsselung des gewrappten Vault Key |
| Ed25519 | Authentifizierung gegenüber dem Server |
Datensätze
Jeder Record wird einzeln mit XChaCha20-Poly1305 verschlüsselt. Die zusätzlich authentifizierten Daten binden ihn an ID, Typ und Epoch:
aad = id ‖ 0x00 ‖ type ‖ 0x00 ‖ u64be(epoch)
Beim Öffnen wird sie aus den gespeicherten Metadaten neu gebildet und muss stimmen. Damit lässt sich ein Ciphertext nicht unter einer anderen ID, einem anderen Typ oder einer anderen Epoch unterschieben.
Nonces sind immer zufällig, nie ein Zähler. Bei 192 Bit ist die Kollisionswahrscheinlichkeit vernachlässigbar — und ein Zähler ohne dauerhaft persistierten Stand wäre nach einem Absturz genau der Fehler, der diese Konstruktion kaputt macht.
Anmeldung ohne Passwort
Der Server stellt eine einmalige Challenge aus, das Gerät signiert sie mit seinem Ed25519-Schlüssel:
msg = "tessera-auth-v1" ‖ 0x00 ‖ device_id ‖ 0x00 ‖ nonce
Der Kontextpräfix ist Pflicht. Ohne ihn wäre eine Signatur über dieselben Bytes aus einem anderen System — etwa einem SSH-Agenten mit demselben Schlüssel — hier gültig.
- Die Challenge wird beim ersten Prüfversuch gelöscht, unabhängig vom Ergebnis. Gültigkeit: 60 Sekunden.
- Eine Challenge wird auch für unbekannte Geräte ausgestellt. Sonst wäre der Endpunkt ein Orakel für die Existenz von Geräten.
- Access-Token 15 Minuten, Refresh-Token 30 Tage und in der Datenbank nur als Hash.
- Refresh-Tokens rotieren. Wird ein bereits benutztes noch einmal vorgelegt, gilt die gesamte Kette als kompromittiert und wird gelöscht.
- Der Algorithmus wird beim Prüfen nicht aus dem Header gelesen, sondern byteweise gegen den erwarteten verglichen.
alg=noneund alg-Confusion sind damit strukturell ausgeschlossen.
Autorisierung
- Die Konto-ID kommt ausschließlich aus dem verifizierten Token. Eine Angabe in Body, Query oder Header wird ignoriert.
- Jede Abfrage auf Records, Geräte und Wiederherstellung filtert nach Konto.
- Fremdzugriff antwortet 404, nie 403 — ein 403 verriete, dass es das Objekt gibt.
- Eine eigene Testsuite prüft das und schlägt fehl, sobald ein Endpunkt ohne Abdeckung dazukommt.
Was der Server im Klartext sieht
| Feld | Warum |
|---|---|
type | Ohne ihn kein typbezogener Delta-Sync |
id | Primärschlüssel |
epoch | Konsistenzprüfung der Rotation |
hlc | Konfliktauflösung |
seq | Delta-Sync |
| Gerätename | Anzeige in der Geräteliste |
| Zeitstempel | Aufräumlauf, letzter Kontakt |
Daraus lässt sich ablesen, wie viele Hosts, Snippets und Schlüssel es gibt und wann sie zuletzt geändert wurden. Nicht, welche. Wer auch das verbergen will, müsste den Typ mitverschlüsseln und immer den vollen Vault ziehen — eine bewusst nicht getroffene Entscheidung.
Ehrliche Grenzen
- Kein verlässliches Zeroize. Die verwendete Laufzeitumgebung bietet keine Garantie, dass Schlüsselmaterial im RAM überschrieben wird. Gegen einen Speicherabzug auf einem bereits kompromittierten Gerät ist nichts geschützt.
- Ein entzogenes Gerät behält die Vergangenheit. Es kennt den alten Vault Key und kann alte Ciphertexte weiterhin öffnen. Nur neue Daten bleiben ihm verschlossen.
- Metadaten sind nicht verborgen. Siehe die Tabelle oben.
- Der Wiederherstellungscode ist ein Generalschlüssel. Wer ihn hat, hat den Vault. Er gehört nicht in dieselbe Schublade wie das Notebook.
Sicherheit auf der SSH-Ebene
- Host-Keys: Ein geänderter Schlüssel bricht die Verbindung ab. Es gibt kein „trotzdem verbinden“ — der Eintrag lässt sich im Warnhinweis ersetzen, verbunden wird danach von Hand.
- Paste-Filter: Eingefügter Text verliert Steuerzeichen und ESC-Sequenzen; mehrzeiliges Einfügen ohne Bracketed Paste fragt mit Vorschau nach.
- Shell-Quoting: Snippet-Platzhalter und die Schlüsselzeile beim Ausrollen gehen durch dieselbe Quoting-Funktion. Beide haben Testsuiten, die genau das absichern.
- Kein rekursives Löschen im Dateibrowser, und beim Übertragen ganzer Ordner werden Verknüpfungen nicht verfolgt.
Sicherheit auf der Remote-Desktop-Ebene
Ein Windows-Rechner im eigenen Netz zeigt fast immer ein selbstsigniertes Zertifikat vor. Eine Kettenprüfung schlüge dort jedes Mal fehl — und ein Client, der daraufhin einfach alles annimmt, hat eine verschlüsselte Verbindung zu einem Unbekannten und merkt es nie.
tessera macht es deshalb genau wie bei SSH-Host-Schlüsseln, und zwar mit demselben Code und derselben Tabelle: Die Kette wird nicht geprüft, dafür wird der SHA-256-Fingerabdruck des vorgezeigten Zertifikats gemerkt. Beim ersten Mal steht er im Dialog zum Vergleichen; ändert er sich später, bricht die Verbindung ab. Es gibt kein „trotzdem verbinden“. Der Warnhinweis stellt beide Fingerabdrücke nebeneinander und lässt den gemerkten durch den neuen ersetzen — das ersetzt aber nur den Eintrag, nicht die Verbindung: Der nächste Versuch ist ein eigener Klick.
- Die Rückfrage steht vor der Anmeldung. Erst der TLS-Handschlag, dann das Zertifikat zur Bestätigung, dann CredSSP. Bis jemand zugestimmt hat, ist kein Byte an Zugangsdaten hinausgegangen.
- Nur NLA. Der Weg über den grafischen Anmeldebildschirm (TLS ohne Netzwerkebenen-Authentifizierung) ist abgeschaltet. Er öffnet die ganze Sitzung, bevor sich jemand ausgewiesen hat.
- Kerberos ist nicht eingerichtet, tessera spricht NTLM. Ein klarer Fehler ist ehrlicher als ein stiller Rückfall.
- Bekannte Zertifikate werden nicht abgeglichen. Wie die Host-Schlüssel sind sie eine Aussage darüber, was dieses Gerät in diesem Netz gesehen hat.
- Das RDP-Passwort liegt dort, wo alle Passwörter liegen: im gespeicherten Zugang, Ende-zu-Ende-verschlüsselt abgeglichen. Der Server sieht es nie.
Versionierung der Konstruktionen
Jeder Kontextstring trägt eine Version — tessera-seal-v1,
tessera-record-v1, tessera-auth-v1,
tessera-sas-v1 und so weiter. Eine Änderung an einer
Konstruktion bekommt eine neue Version, und der Client muss beide lesen
können, bis alle Geräte migriert sind. Ein stiller Wechsel bei
gleichbleibender Kennung wäre ein Datenverlust mit Ansage.