The review found the store key readable from a copy of the disk. The
desktop app only added its OS layer with the lock on, the record from
before a lock stayed in LevelDB and opened the mail with no PIN, any
frame could unwrap the PIN record and guess at it, a lock left the key
in the renderer's memory, and a store waiting after a refused login went
to whoever signed in next under the same username@host.
- The desktop app wraps the store key with safeStorage, lock or no lock.
A record without the layer gets it at its next open. A refusal is said
out loud, and only a computer with no key store goes without, which
settings say. So do they for a browser, which has no such layer.
- A record that gained layers, and every record after a changed PIN, asks
for a new key: `sqlite3_backup` into a new file under a new key while
the store keeps working, one write to the record, then the old file
goes. A changed PIN makes a new lock key, since the old PIN's record
stays on disk too. The passkey and OS unlock go with the old key.
- `wrap` and `unwrap` come through `claim()`, and the key under the PIN
takes a secret from main, one counted trip per guess.
- A lock on the desktop ends the renderer rather than reload in it.
- A sign-in compares the waiting store's JMAP account and server first,
and wipes on a mismatch. The account id keeps its form: every store,
setting and server record of an account is keyed by it.
- The 30 days also run in an app that stays open.
Changes an existing assertion: turning the lock off now keeps the
safeStorage layer, by decision, so that test expects it to stay.
Forced OfflineEnabled values that make no sense read as forced off,
which wipes kept mail. They now stop new mail being kept and delete
nothing. An unreadable AppLockTimeoutMinutes now means the shortest
time, so the existing out-of-range test changes from no value to 1:
no value let the user's own choice win.
- recommended AutoUpdate, AppLockRequired, TelemetryAllowed and
CrashReportsAllowed are gone: nothing read them, or one acted as forced
- auth lists and certificate rules can't name a whole top-level domain
- ALL_WIRED is gone, as every key has a consumer
The lock is off by default, and an OS policy can require it and set the
timeout. It locks after a time without use counted across all windows and
tabs, on OS screen lock in the desktop app, and from Lock now.
Locking saves open composers, stops the outbox at its next message, closes
the store and stops its worker with every key, then reloads the page, so
nothing of the old page stays in reach. The new page holds the app back at a
Suspense boundary until someone unlocks. Pop-outs get no session while
locked, push notifications name nobody, and subjects leave plain storage.
One random lock key wraps each account's key as a layer. The PIN wraps the
lock key with PBKDF2 at 600,000 iterations, with a wait after wrong tries
that a reload doesn't reset. A passkey with PRF and the desktop's OS storage
wrap it too. Details and limits are in the decisions file.