# Shade V3.2 — At-Rest Storage Encryption **Status:** Implementert (0.4.0) — `@shade/storage-encrypted`, `@shade/keychain`, `shade migrate-storage`, `shade rotate-storage-key` **Effort:** L (4–8 uker) **Forrige:** V3.1 **Adresserer:** V2.1 §2 --- ## Mål Opt-in beskyttelse av sensitiv state — identity-nøkler, session-state, valgfri stream-resume-secret — med nøkler som **ikke** ligger i klartekst i databasen. Trusselmodellen sier i dag eksplisitt at en stjålet DB eksponerer private nøkler; dette løser det for deploys som velger å aktivere det. --- ## Scope ### Inn - Ny `EncryptedStorageProvider`-wrapper som dekorerer `SQLiteStorage` / `PostgresStorage`. - Per-rad AES-256-GCM på sensitive felter (`identity_*`, `session_*`, valgfritt `stream_state.streamSecret`). - KDF-pluggin (default `scrypt` fra `@noble/hashes`) for passphrase-basert master-nøkkel. - Tre nøkkelkilder ut av boksen: 1. **Passphrase + KDF** — utvikler oppgir secret ved oppstart. 2. **OS keychain** — macOS Keychain, Linux libsecret, Windows Credential Vault (Node-only). 3. **App-injected key** — appens egen kode forsyner 32-byte nøkkel (mest fleksibel). - Migrasjons-CLI: `shade migrate-storage --encrypt --key-source=...`. - Trusselmodell-oppdatering: "når enabled, hva er fortsatt udekket" — memory compromise, swap, runtime-tap. ### Ut - Browser/IndexedDB at-rest (egen pakke, vurderes etter V3.8). - HSM/Secure Enclave (separate driver senere). - "Always-on by default" — vi flyger opt-in for å ikke bryte eksisterende deploys. --- ## Design ### Krypteringsenhet - Per-rad AEAD: `nonce(12) || ciphertext || tag(16)`. - `nonce = HKDF(rowKey, "shade-row-nonce-v1" || tableName || pk)[..12]` — deterministisk per (tabell, pk) for å unngå nonce-reuse uten å lagre nonce separat. Endring av (tabell, pk) → re-encryption. - AAD binder `tableName || columnName || pk` så feltombytting blokkeres. ### Nøkkelhierarki ```text masterKey (fra kilde — passphrase / keychain / app-injected) │ ├─ HKDF("shade-storage-v1") → storageKey (32 bytes) │ │ │ └─ HKDF(storageKey, table || column) → fieldKey │ └─ HKDF("shade-storage-version-v1") → versjonsnøkkel (rotasjon) ``` ### Migrasjon 1. CLI leser ukryptert DB. 2. Skriver rad-for-rad-kryptering til ny `_v2`-tabell. 3. Atomisk rename + drop gammel. 4. Backup `.bak`-fil etterlatt i samme dir. ### Rotasjon - `shade rotate-storage-key --new-source=...` re-krypterer med ny masterKey. - Online ratchet (les med gammel, skriv med ny) for store DB. --- ## Leveranser ### Pakker - Ny modul: `@shade/storage-encrypted` (re-export over SQLite/PG). - Utvidelse i `@shade/cli`: `migrate-storage`, `rotate-storage-key`. - Hjelpe-pakke: `@shade/keychain` (Node-only, valgfri peer-dep) for OS-keychain. ### Tester - Unit: KDF-derivasjon, nonce-determinisme, AAD-binding. - Integration: full lifecycle på SQLite + PG; start/stopp; krasj under migrasjon. - Tamper: bit-flip i ciphertext / AAD / nonce → dekrypterings-feil. - Vector-fil: kryss-sjekk masterKey → fieldKey-derivasjon mot `test-vectors/storage-encryption.json`. ### Dokumentasjon - `docs/storage-encryption.md` — full guide. - `THREAT-MODEL.md` — ny kolonne "with at-rest enabled". - Migrasjonsnotat i `MIGRATION.md`. --- ## Akseptansekriterier - [ ] Eksisterende ukryptert deploy fortsetter uten endringer (opt-in). - [ ] `shade migrate-storage --encrypt` migrerer en levende SQLite uten datatap, verifisert med dump-diff. - [ ] Rotasjon kan gjøres uten downtime > 5 s for små DB. - [ ] Wrong passphrase / wrong key → klar feilmelding, ikke krasj. - [ ] Test-vectors deles med Android-implementasjonen (V3.5 forplikter at vector-filen kjøres der). --- ## Avhengigheter - V3.1 — `THREAT-MODEL.md` skal være lenket til testene først, så vi kan utvide tabellen. --- ## Risiko **Datatap.** En migrasjon som krasjer halvveis kan etterlate korrupt DB. Mitigeres ved: - Atomic-rename + `.bak`-fil. - Dry-run-modus (`--dry-run` validerer all dekryptering før skriving). - Refuser å starte hvis WAL har uncommitted writes. **Nøkkeltap = totaltap.** Hvis bruker mister passphrase = ingen tilgang. Dokumenter klart, og pek på V3.10 (Social Recovery) som langtidsløsning. --- ## Migrasjon 0.3.x deploys er ukrypterte → fortsatt ukrypterte. Aktivering er én CLI-kommando. Backwards-kompatibel.