Done in https://github.com/badbundle/vault-app/pull/648 (merged as 78cd21ba). This covers the slot mechanics. The per-vault backup settings were split into VAULT-70.
Making a duress database: EncryptedVaultStore.makeDuressVault(password:), plus a session version for the UI, creates an empty vault W in V.duressSlots[0].
W.duressSlots = V.duressSlots[1...] + [x], where x comes from the system random generator and is never V's or W's slot.- The file is replaced through the locked, verified write. V's slot isn't touched, and the steps are identical from the real vault or a duress vault.
VaultDuressSlotsis the single source of L (10).
Same passwords:
- V's own password is refused, checked against V's key box only.
- A password that opens another slot is accepted silently, and takes the same steps as a fresh one.
- At unlock, the most recently wrapped vault wins.
Wrap stamp: this closes a clock-rollback oracle found in review.
- Recency now comes from
VaultDeviceWrapStamper, a monotonic, device-local stamp:max(now, stamp + 1 ms, previous + 1 ms), held in the keychain on this device only. - It's used by every wrap.
- It moves on every successful unlock too, so it shows only when the device was last used.
Other fixes: new vaults start at a random generation, and the design doc's residual limits were corrected: new slot nonces, key-box contents, and the stamp.
Tests:
- Chains never target the real vault within 11 levels, checked through the store.
- Payload shapes are equal.
- Making a vault takes the same steps and time from either vault.
- Unlocks with real, duress and wrong passwords do equal work.
- A reused real password opens the newer vault.
- Rolling the clock back doesn't change which vault opens.
- Every failure step leaves the file unchanged.
- A stale writer gets
slotLost.