diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 8c20051..1b3748a 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,4 @@ -# Checkpoint — Owner-only-from-first-byte file writes (2026-08-21) +# Checkpoint — Key material zeroization (2026-08-21) A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. @@ -6,13 +6,12 @@ verified green at the moment this file was written. ## Where things are - Project: `/home/avi/Projects/0_Nostr` -- Git repo: `master` @ `4461307` ("Create vault files owner-only from the first byte"). - Before it: `4bd7660` (legacy vault perms), `4d4dfde` (CSP + navigation guards), - `6e627a3` (upload pick tokens), `4bde395` (IPC allowlist), on top of contact-aware - feed (`a1445d1`) etc. +- Git repo: `master` @ `130d7e2` ("Zeroize transient secret key material in memory"). + Before it: `4461307` (owner-only writes), `4bd7660` (legacy vault perms), `4d4dfde` + (CSP + navigation guards), `6e627a3` (upload pick tokens), `4bde395` (IPC allowlist). - Working tree is **clean** apart from this checkpoint update, which is committed right after. -## What was completed: security hardening items #1–#5 +## What was completed: security hardening items #1–#6 Fixes from the 2026-08-21 security audit. @@ -80,8 +79,24 @@ page could read+publish arbitrary local files to nostr.build. Now: with a manual open-at-0600 + `io::copy`, making backups owner-only from their first byte. - New test: backup of a 0664 source comes out 0600 with identical content. +**#6 Key material zeroization (`130d7e2`, adds `zeroize = "1"`):** +Decrypted secrets and derived keys no longer linger in unscrubbed heap memory: +- `crypto.rs`: `decrypt_secret` returns `Zeroizing` (self-shredding on drop); the + UTF-8 error path wipes the raw bytes too; a failed Argon2 derivation wipes its key buffer. +- `profiles.rs`: `resolve_secret_key` / `resolve_active_secret_key` now return + `Zeroizing`, so every transient plaintext key flowing to publish/upload/signer is + wiped when its scope ends. Newly generated keys in `create_profile` are wrapped the same + way. `reveal_secret_key` still hands a display copy to the UI by design. +- `app.rs`: `lock()` and a new `Drop for App` zeroize the session vault key; wrong-password + derivations wipe their throwaway keys; password change wipes the previous vault key and + the old unlock key; `remove_password` moves decrypted values into storage via + `std::mem::take` without extra copies (plaintext-at-rest is that feature's purpose). +- Callers in `publish.rs`, `uploads.rs`, `signer.rs` needed no changes (deref coercion). + ## Commits +- `130d7e2` "Zeroize transient secret key material in memory" — Cargo.toml, crypto.rs, + profiles.rs, app.rs (+77/−23). - `4461307` "Create vault files owner-only from the first byte" — src/vault.rs (+48/−2). - `4bd7660` "Tighten permissions on leftover legacy vault files" — src/vault.rs (+79/−3). - `4d4dfde` "Enforce header-based CSP and block window open/navigation" — main.ts + index.html. @@ -122,12 +137,13 @@ Manual protocol checks worth doing once: ## Outstanding / next steps (if you continue) -Remaining audit items in priority order: +Remaining smaller hardening items from the audit: -1. **Zeroize** — wipe decrypted key material via the `zeroize` crate (`VaultKey`, - decrypted hex strings in `crypto.rs`/`profiles.rs`). -2. Smaller: link-preview SSRF guard (block loopback/private IPs), signer pending-cap, - NIP-46 secret echo check, backend request timeout. +1. Link-preview SSRF guard — block loopback/private IPs in `fetchLinkPreview` (`main.ts`). +2. Signer pending-cap — bound the approval queue (e.g. max 10) against relay spam. +3. NIP-46 secret echo check — verify the client echoes the handshake `secret`. +4. Backend request timeout — `backendRequest` in `main.ts` has no timeout; hung backend + leaks pending promises. -All five medium findings from the audit are closed (#1 IPC allowlist, #2 upload tokens, -#3 CSP + navigation, #4 legacy vault perms, #5 write-race windows). +All six primary findings are closed (#1 IPC allowlist, #2 upload tokens, #3 CSP + +navigation, #4 legacy vault perms, #5 write-race windows, #6 zeroization).