Skip to content

[SEC-W-05] Ed25519 identity secret key stored unencrypted in localStorage #226

@intendednull

Description

@intendednull

Commit: 2f26d91 · Finding: SEC-W-05

Problem

save_identity_bytes at crates/client/src/storage.rs:96-101 writes the raw 32-byte Ed25519 secret key to localStorage["willow_identity"] base64-encoded in plaintext. Every same-origin script can read it with localStorage.getItem("willow_identity").

Combined with the absence of CSP (#175) and the normalized js_sys::eval pattern (#171), any single XSS — or a compromised npm-delivered CSS/font/build asset — exports the user's long-term identity for every server they're joined to.

Fix

Migrate identity storage to IndexedDB with crypto.subtle.importKey(..., extractable=false, ['sign']), storing only the non-extractable CryptoKey handle. For backward compatibility:

  1. On startup, if willow_identity localStorage key exists: read it, import as non-extractable, delete the legacy entry.
  2. Use the non-extractable handle for all subsequent signing.
  3. Optional (Phase 2): wrap at rest with a user passphrase (argon2 → AES-KW).

Obvious? Yes — storing a long-term signing key in localStorage is a canonical anti-pattern. Requires some refactor of the signing path; not a one-line fix.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions