trackslash
VAULT-46 P2

Encryption 7: add the unlock and lock service

0
Sub-issue of VAULT-22 P2 Add an optional password to the app lock

Description

Sub-issue 7 of the on-device encryption design (VAULT-26, docs/on-device-encryption.md, "Unlocking and locking").

Unlocking:

  1. Increment the persistent attempt counter before deriving the key.
  2. Derive the key off the main actor.
  3. Try every slot, with no early exit.
  4. If more than one slot opens, pick the most recently wrapped.
  5. Hold the result until a fixed deadline.
  6. Zero the keys.

Real, duress and wrong passwords must all do identical work and take the same time.

Locking: await any in-flight write, switch the store session to locked, and purge.

Tests: operation counts for each path, and the deadline, using an injected clock and the testing KDF.

Linked issues

0

GitHub

0

No branches or pull requests linked.

Comments

1
Bradley

Done in https://github.com/badbundle/vault-app/pull/641 (merged as e5eb9168).

API: VaultUnlockService has:

  • unlock(password:), which returns .unlocked, .wrongPassword or .mustWait(Duration), always at the deadline;
  • lock() async;
  • hasMemoryHeadroomToUnlock(), for AutoFill.

Unlocking:

  1. Count the attempt with AppLockPasswordAttemptCounter before deriving. Nothing is tried if the result is .delayed or the count fails.
  2. Derive off the main actor. The password is normalised to NFC through a single passwordKey(for: String).
  3. Try all 16 slots, with no early exit.
  4. If several slots open, the most recently wrapped wins.
  5. A wrong password opens a random decoy body, so every attempt opens exactly one body.
  6. Hold until the fixed deadline, zero the keys, and reset the counter when any vault opens, duress included.

The deadline:

  • It's raised only by work that's the same whatever the password: the derivation and the slot trials.
  • That work is timed in thread CPU time, so time spent suspended doesn't count.
  • Only a finished attempt that's still wanted raises it, it's capped at 5 s, and it's never lowered.

Locking: waits for any in-flight save, switches the session to locked and purges. An unlock only switches the session if it hasn't locked since the attempt began, so a lock always wins.

Memory check: on a device, 0 from os_proc_available_memory() means no headroom. The check reads only the header and the file size.

Tests:

  • Spies give real and decoy bodies different costs and prove that real, duress and wrong passwords return at the same instant, with the same work.
  • The tie-break, NFC, the counter, and each deadline rule.
  • Lock racing the switch, cancellation, and the memory check.

Review: a security review found one blocker (an uncapped deadline that counted suspended time) and four should-fixes. All were fixed, and a re-review confirmed them. A last fix stops the saved deadline from reflecting the largest vault ever opened.