Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,35 @@
| Vault item plaintext | ❌ never | Only the client (with master password) decrypts |
| Item count | ✅ | Acceptable leak |
| Item type (login/note/...) | ✅ | Acceptable leak |
| TOTP shared secret (opt-in)| ✅ when 2FA enabled| RFC 6238 verifier requires the secret — see below |
| TOTP recovery codes | ❌ Argon2id-hashed | Plaintext shown to user once, never persisted |

## Threats considered

### Optional TOTP (2FA) — what it changes

Enabling 2FA adds a per-user TOTP secret (RFC 4226 / 6238) to the
`users` row. **This is the one server-stored secret that vault data is
not derived from.** Crucially:

- **Vault contents stay zero-knowledge.** A breach that leaks both the
auth_key hash and the TOTP secret still does **not** enable vault
decryption — the symmetric key is encrypted with the master key, and
the master key is never on the server.
- **Login auth widens by one secret.** An attacker who exfiltrates the
TOTP secret can compute valid OTPs for that user and bypass the 2FA
challenge — but they still need the master password to get past the
first factor and to actually decrypt anything.
- **Recovery codes are stored as Argon2id hashes**, identical posture to
the auth_key hash. A leak of the recovery-codes column does not
enable login.
- **Disable always requires a fresh code** (current OTP or recovery
code). A stolen access token alone can't downgrade the auth posture.

If you need a true zero-knowledge second factor, leave TOTP off and use
the upcoming WebAuthn / passkey path (tracked) which uses public-key
crypto — the server only stores the public key, never a verifier secret.

### Database breach
Attacker pulls a full dump. They have: emails, Argon2 hashes of auth keys,
KDF parameters, encrypted blobs.
Expand Down
41 changes: 41 additions & 0 deletions docs/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,47 @@ The file shape is intentionally stable (`{ format, version, vault,
items: [...] }`) so a future Restore flow can consume it without a
schema bump.

### 5g. Two-factor authentication (TOTP)

Open the **Settings** link in the sidebar's user card → click **Set up 2FA**.

The setup flow has three steps:

1. **Scan the QR** with your authenticator (Google Authenticator,
1Password, Authy, Microsoft Authenticator, …). If your phone can't
reach the screen, expand "Can't scan? Type this manually" and copy
the base32 secret directly into the app.
2. **Confirm the first code.** Type the 6-digit code your authenticator
shows. Passman verifies against the stored secret and only then
flips the `totp_enabled` flag — if you abandon the flow before this
step your account stays at single-factor.
3. **Save your recovery codes.** Ten single-use codes are shown
*exactly once*. Each lets you log in if you lose your phone. Copy
them, download the `.txt`, or write them down — Passman keeps only
Argon2id hashes server-side, so this list cannot be retrieved later.

Once enabled, login becomes two-step: email + master password, then a
6-digit code. Recovery codes work in place of the 6-digit code (and
are consumed on first use — `9 remaining` becomes `8`).

> **Trade-off, plainly stated.** TOTP is **not** zero-knowledge —
> RFC 6238 requires the verifier to know the shared secret, so the
> server now holds your OTP secret alongside the existing auth_key
> hash. Vault contents stay zero-knowledge: a server breach that leaks
> both still can't decrypt your credentials. The benefit is that
> credential-stuffing or phishing attacks against the password alone
> can't get past login.
>
> If you need the strongest server-side guarantee, leave 2FA off —
> the original posture (auth_key + master-password-derived key) is
> already strong against offline attacks. WebAuthn / passkeys are the
> tracked alternative for users who want a second factor without
> server-side secrets.

To turn 2FA off: Settings → **Disable 2FA** → enter a current code or
a recovery code. A stolen access token alone can't downgrade — the
disable endpoint always requires fresh proof.

---

## 6. Chrome extension
Expand Down
Loading
Loading