[Security] Per-Device-Tokens statt Shared Token (Revocation) #11

Closed
opened 2026-08-24 14:58:23 +00:00 by ARIA · 1 comment
Owner

Auth nutzt ein Shared Token (IRIS_TOKEN) als Security-Principal. Bei Rotation müssen alle Devices neu pairn; per-Device-Revocation steht nur als "stretch" in docs/09 §9.3.

Problem: Ein geleaktes Token = Zugriff auf alle Devices; ein kompromittiertes Device lässt sich nicht isolieren. Relevant, wenn die App mehreren Leuten zur Verfügung gestellt wird.

Vorschlag: Beim Pairing ein einzigartiges, revocables Token pro Device ausstellen (in devices.db). Shared Token bleibt als Setup/Bootstrap-Fallback.

Akzeptanz:

  • Per-Device-Token beim Pairing generiert + gespeichert
  • Einzelnes Device revocable ohne die anderen zu betreffen
  • Shared Token bleibt als Bootstrap-Path
  • docs/09 §9.3 von "stretch" auf "implemented"
Auth nutzt ein Shared Token (IRIS_TOKEN) als Security-Principal. Bei Rotation müssen alle Devices neu pairn; per-Device-Revocation steht nur als "stretch" in docs/09 §9.3. Problem: Ein geleaktes Token = Zugriff auf alle Devices; ein kompromittiertes Device lässt sich nicht isolieren. Relevant, wenn die App mehreren Leuten zur Verfügung gestellt wird. Vorschlag: Beim Pairing ein einzigartiges, revocables Token pro Device ausstellen (in devices.db). Shared Token bleibt als Setup/Bootstrap-Fallback. Akzeptanz: - Per-Device-Token beim Pairing generiert + gespeichert - Einzelnes Device revocable ohne die anderen zu betreffen - Shared Token bleibt als Bootstrap-Path - docs/09 §9.3 von "stretch" auf "implemented"
ARIA added the enhancement label 2026-08-24 15:00:00 +00:00
ARIA self-assigned this 2026-08-24 15:00:17 +00:00
Author
Owner

Implementiert (Working Tree, noch nicht committet):

Gateway

  • pairing.py: devices.token-Spalte (Migration) + revoked-Denylist; issue_token (idempotent, 64 hex), token_for, reissue_token, revoke/unrevoke/is_revoked/list_revoked. Token wird nie in Device-Dicts geleakt.
  • http_server.py: Auth akzeptiert Shared Token (Bootstrap/Legacy) ODER das Device-Token (beide constant-time); revoked device_ids werden vor dem Token-Vergleich mit 401 abgewiesen. Beim SSE-Open (Pairing) wird das Device-Token gemintet und in hello.ack.device_token zurückgegeben.
  • protocol.py: hello_ack(..., device_token).
  • Neues CLI gateway-plugin/tools/iris_devices.py: list / revoke <id> / unrevoke <id> / reissue <id>.

App

  • SecureStore.deviceToken (Android: EncryptedSharedPreferences; Desktop: zweiter Keyring-/Encrypted-File-Slot iris-device-token/device_token.enc).
  • HelloAckPayload.deviceToken; GatewayClient speichert es beim hello und präsentiert es fortan statt des Shared Tokens (Live-Provider in HttpGateway); savePairing/clear wischen es (Re-Pair auf anderes Gateway).

Akzeptanz

  • ✅ Per-Device-Token beim Pairing generiert + gespeichert (devices.db)
  • ✅ Einzelnes Device revocable ohne die anderen zu betreffen (Denylist schlägt auch den Shared Token)
  • ✅ Shared Token bleibt Bootstrap-Path (Legacy-Devices ohne Device-Token funktionieren weiter; Rotation bricht Pairings nicht mehr)
  • ✅ docs/09 §9.3 von "stretch" auf implemented (+ docs/04, frames.schema.json, M7-Tabelle Zeile 13)

Tests: 7 neue Python-Tests (Issuance, Acceptance, Revocation, Isolation, Unrevoke, 2 Registry-Unit) — 93/93 grün; 2 neue Kotlin-Wire-Tests — grün. Live-Verifiziert gegen laufendes Gateway: hello.ack trägt das Token (matcht devices.db), revoke → 401 auch mit Shared Token, unrevoke → 200.

Implementiert (Working Tree, noch nicht committet): **Gateway** - `pairing.py`: `devices.token`-Spalte (Migration) + `revoked`-Denylist; `issue_token` (idempotent, 64 hex), `token_for`, `reissue_token`, `revoke`/`unrevoke`/`is_revoked`/`list_revoked`. Token wird nie in Device-Dicts geleakt. - `http_server.py`: Auth akzeptiert Shared Token (Bootstrap/Legacy) ODER das Device-Token (beide constant-time); revoked device_ids werden vor dem Token-Vergleich mit 401 abgewiesen. Beim SSE-Open (Pairing) wird das Device-Token gemintet und in `hello.ack.device_token` zurückgegeben. - `protocol.py`: `hello_ack(..., device_token)`. - Neues CLI `gateway-plugin/tools/iris_devices.py`: `list` / `revoke <id>` / `unrevoke <id>` / `reissue <id>`. **App** - `SecureStore.deviceToken` (Android: EncryptedSharedPreferences; Desktop: zweiter Keyring-/Encrypted-File-Slot `iris-device-token`/`device_token.enc`). - `HelloAckPayload.deviceToken`; `GatewayClient` speichert es beim hello und präsentiert es fortan statt des Shared Tokens (Live-Provider in `HttpGateway`); `savePairing`/`clear` wischen es (Re-Pair auf anderes Gateway). **Akzeptanz** - ✅ Per-Device-Token beim Pairing generiert + gespeichert (devices.db) - ✅ Einzelnes Device revocable ohne die anderen zu betreffen (Denylist schlägt auch den Shared Token) - ✅ Shared Token bleibt Bootstrap-Path (Legacy-Devices ohne Device-Token funktionieren weiter; Rotation bricht Pairings nicht mehr) - ✅ docs/09 §9.3 von "stretch" auf implemented (+ docs/04, frames.schema.json, M7-Tabelle Zeile 13) **Tests**: 7 neue Python-Tests (Issuance, Acceptance, Revocation, Isolation, Unrevoke, 2 Registry-Unit) — 93/93 grün; 2 neue Kotlin-Wire-Tests — grün. Live-Verifiziert gegen laufendes Gateway: hello.ack trägt das Token (matcht devices.db), `revoke` → 401 auch mit Shared Token, `unrevoke` → 200.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ARIA/iris_x_hermes#11