decision: Argon2id runs at INTERACTIVE cost for the at-rest vault key #401

Closed
opened 2026-09-21 20:37:56 +02:00 by clawbot · 7 comments
Collaborator

Severity: hardening. Owner decision.

Where. src/shared/vault.js:86-93 and 113-120 (crypto_pwhash with
OPSLIMIT_INTERACTIVE / MEMLIMIT_INTERACTIVE); src/popup/views/addWallet.js:116
(the 12-character minimum password).

The question. The profile is stored unencrypted in extension storage except
for wallets[].encryptedSecret, so the key-derivation cost and the password
policy are the only protection of a stolen profile. vault.js deliberately uses
INTERACTIVE (2 passes, 64 MiB, ~150 ms) because the password is entered on every
signature and a heavier cost would slow every signature. libsodium recommends
MODERATE (3 passes, 256 MiB) or SENSITIVE for data at rest.

Options.

  • Keep INTERACTIVE and record the reasoning in README Key Decisions (the
    per-signature UX cost is the reason).
  • Move to MODERATE (~0.5–1 s per signature) and re-encrypt each secret on the
    next successful unlock.

Recommendation. Document the current choice in README Key Decisions. A
per-signature password prompt is a real UX constraint that argues for
INTERACTIVE, and the 12-character minimum is the lever that matters more for a
stolen profile — so raising the minimum (and rejecting weak passwords) may be a
better return than the KDF cost.

Definition of done.

  • The owner rules keep vs raise the parameters.
  • If kept: README Key Decisions states the parameters and why (the
    per-signature cost).
  • If raised: the new parameters are set, existing secrets are re-encrypted on
    the next unlock, and the README is updated.

Model: fable-5-1 (review); opus-4-8 (filing)

Severity: hardening. Owner decision. **Where.** `src/shared/vault.js:86-93` and `113-120` (`crypto_pwhash` with `OPSLIMIT_INTERACTIVE` / `MEMLIMIT_INTERACTIVE`); `src/popup/views/addWallet.js:116` (the 12-character minimum password). **The question.** The profile is stored unencrypted in extension storage except for `wallets[].encryptedSecret`, so the key-derivation cost and the password policy are the only protection of a stolen profile. `vault.js` deliberately uses INTERACTIVE (2 passes, 64 MiB, ~150 ms) because the password is entered on every signature and a heavier cost would slow every signature. libsodium recommends MODERATE (3 passes, 256 MiB) or SENSITIVE for data at rest. **Options.** - Keep INTERACTIVE and record the reasoning in README Key Decisions (the per-signature UX cost is the reason). - Move to MODERATE (~0.5–1 s per signature) and re-encrypt each secret on the next successful unlock. **Recommendation.** Document the current choice in README Key Decisions. A per-signature password prompt is a real UX constraint that argues for INTERACTIVE, and the 12-character minimum is the lever that matters more for a stolen profile — so raising the minimum (and rejecting weak passwords) may be a better return than the KDF cost. **Definition of done.** - [ ] The owner rules keep vs raise the parameters. - [ ] If kept: README Key Decisions states the parameters and why (the per-signature cost). - [ ] If raised: the new parameters are set, existing secrets are re-encrypted on the next unlock, and the README is updated. Model: fable-5-1 (review); opus-4-8 (filing)
clawbot added this to the 1.0.0 milestone 2026-09-21 20:37:56 +02:00
sneak was assigned by clawbot 2026-09-21 20:37:56 +02:00
Author
Collaborator

Ask: should the password hashing that protects the stored recovery phrase stay at libsodium's fast setting (64 MiB, about 0.15 s on every signature) or move to its stronger at-rest setting (256 MiB, roughly 0.5 to 1 s on every signature)? Recommendation: keep the fast setting and write the reason into README.md Key Decisions; against a stolen profile, the 12-character minimum password matters more.

Model: opus-5-5

Ask: should the password hashing that protects the stored recovery phrase stay at libsodium's fast setting (64 MiB, about 0.15 s on every signature) or move to its stronger at-rest setting (256 MiB, roughly 0.5 to 1 s on every signature)? Recommendation: keep the fast setting and write the reason into `README.md` Key Decisions; against a stolen profile, the 12-character minimum password matters more. Model: opus-5-5
Owner

Can we use the long one on first signature/pw entry then cache in ram only what we need to do a faster one on subsequent passphrase entries? The threat model against stolen secrets from disk and stolen secrets from ram is vastly different.

Can we use the long one on first signature/pw entry then cache in ram only what we need to do a faster one on subsequent passphrase entries? The threat model against stolen secrets from disk and stolen secrets from ram is vastly different.
sneak was unassigned by clawbot 2026-10-21 12:08:49 +02:00
clawbot self-assigned this 2026-10-21 12:08:49 +02:00
Author
Collaborator

Yes. Design:

  • On disk (extension local storage, as today): each wallet's secret encrypted under a key derived from the password at libsodium's strongest setting, SENSITIVE (1 GiB, 4 passes). Nothing else secret is ever written.
  • First password entry for a wallet after the browser starts (signing, showing the phrase, anything): the slow derivation decrypts the secret, about 6 seconds measured here (MODERATE, 256 MiB, takes about 1.3 s). The wallet then encrypts the secret again under a second key, derived from the same password with a fresh salt at today's fast setting (64 MiB, 2 passes), and keeps only that copy in the browser's session storage. Creating or importing a wallet does both at once.
  • Every later entry decrypts the session copy in about 0.15 s, as today. The password is still asked for on every signature, and a wrong one is refused just as fast.
  • How long the memory copy lasts: until the browser closes or the extension is reloaded or updated, and it is removed when its wallet is deleted. Session storage is held in memory and never written to disk by Chrome or Firefox (the operating system can still swap any memory out, as it can the decrypted secret while a transaction is signed today).
  • Stolen profile: only the strong copy. Each password guess costs about 30 times the work it costs today and needs 1 GiB of memory, which also limits how many guesses a GPU can run at once.
  • Memory dump of a running browser after the first entry: the session copy, guessable at today's cost per guess, so no worse than a stolen profile is now. Someone who can read the browser's memory can also catch the password or the secret during the next signature, so the copy gives that attacker little more.

Recommendation: build this with SENSITIVE on disk. If either browser refuses the 1 GiB allocation in the popup, MODERATE is used instead; the browser test settles which. The 12-character minimum stays.

Model: opus-5-5

Yes. Design: - **On disk** (extension local storage, as today): each wallet's secret encrypted under a key derived from the password at libsodium's strongest setting, SENSITIVE (1 GiB, 4 passes). Nothing else secret is ever written. - **First password entry for a wallet after the browser starts** (signing, showing the phrase, anything): the slow derivation decrypts the secret, about 6 seconds measured here (MODERATE, 256 MiB, takes about 1.3 s). The wallet then encrypts the secret again under a second key, derived from the same password with a fresh salt at today's fast setting (64 MiB, 2 passes), and keeps only that copy in the browser's session storage. Creating or importing a wallet does both at once. - **Every later entry** decrypts the session copy in about 0.15 s, as today. The password is still asked for on every signature, and a wrong one is refused just as fast. - **How long the memory copy lasts:** until the browser closes or the extension is reloaded or updated, and it is removed when its wallet is deleted. Session storage is held in memory and never written to disk by Chrome or Firefox (the operating system can still swap any memory out, as it can the decrypted secret while a transaction is signed today). - **Stolen profile:** only the strong copy. Each password guess costs about 30 times the work it costs today and needs 1 GiB of memory, which also limits how many guesses a GPU can run at once. - **Memory dump of a running browser after the first entry:** the session copy, guessable at today's cost per guess, so no worse than a stolen profile is now. Someone who can read the browser's memory can also catch the password or the secret during the next signature, so the copy gives that attacker little more. Recommendation: build this with SENSITIVE on disk. If either browser refuses the 1 GiB allocation in the popup, MODERATE is used instead; the browser test settles which. The 12-character minimum stays. Model: opus-5-5
clawbot removed their assignment 2026-10-21 20:36:40 +02:00
sneak was assigned by clawbot 2026-10-21 20:36:40 +02:00
Owner

No, this is ridiculously inappropriate memory usage for a browser extension. Also this is all too complicated. The length of the passphrase is what protects the storage. A reasonable (100ms) kdf is ok but don’t use so much memory.

Also, the user should be able to set a weak passphrase if they REALLY want to, but “allow weak passphrases” should be disabled in prefs.

No, this is ridiculously inappropriate memory usage for a browser extension. Also this is all too complicated. The length of the passphrase is what protects the storage. A reasonable (100ms) kdf is ok but don’t use so much memory. Also, the user should be able to set a weak passphrase if they REALLY want to, but “allow weak passphrases” should be disabled in prefs.
Author
Collaborator

Recorded: the two-step design above is dropped as too heavy and too complicated. The passphrase's length is what protects the stored secret.

My reading, for you to confirm before anything is built:

  • Key derivation stays as it is today: one derivation of about a tenth of a second (libsodium's interactive setting, 64 MiB, about 0.15 s), with no second copy in memory.
  • The 12-character minimum stays the default.
  • A new setting, "allow weak passphrases", off by default, lets a user who turns it on set a shorter passphrase.

Question: is today's 64 MiB setting what you mean by a reasonable derivation, or should it use less memory? Recommendation: keep today's setting; it is the light one, and nothing about it changes.

Model: opus-5-5

Recorded: the two-step design above is dropped as too heavy and too complicated. The passphrase's length is what protects the stored secret. My reading, for you to confirm before anything is built: - Key derivation stays as it is today: one derivation of about a tenth of a second (libsodium's interactive setting, 64 MiB, about 0.15 s), with no second copy in memory. - The 12-character minimum stays the default. - A new setting, "allow weak passphrases", off by default, lets a user who turns it on set a shorter passphrase. Question: is today's 64 MiB setting what you mean by a reasonable derivation, or should it use less memory? Recommendation: keep today's setting; it is the light one, and nothing about it changes. Model: opus-5-5
Owner

confirmed and recommendation approved.

confirmed and recommendation approved.
Author
Collaborator

Recorded. The work is #674: the "Allow weak passphrases" setting, off by default; key derivation and the 12-character default stay as they are.

Model: opus-5-5

Recorded. The work is https://git.eeqj.de/sneak/AutistMask/issues/674: the "Allow weak passphrases" setting, off by default; key derivation and the 12-character default stay as they are. Model: opus-5-5
sneak was unassigned by clawbot 2026-10-25 03:07:30 +01:00
clawbot self-assigned this 2026-10-25 03:07:30 +01:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/AutistMask#401