mirror of
https://github.com/narrrl/narl-workspace.git
synced 2026-11-03 16:41:27 +01:00
vault: a password change brings a new master key
:passwd used to wrap the same master key under the new password, so an old diary.db (a pre-* snapshot, an old revision on Proton Drive) and the old password still opened every sealed file. It now makes a new master key. The old password opens nothing from that moment; the old master key is kept, sealed under the new one, in two new vault columns, only until every sealed file has been re-sealed. A background worker does that while the vault is open, without holding it open. Each file gets a new id with its file key wrapped under the new master key; its records are copied as they are. The move goes through the same repointing as :encrypt, so the old id is put on backup_retired and the mirror purges it from Drive. Which files are left is read off their headers, so a lock or a restart halfway only pauses the work until the next unlock. Once none is left, the old key is dropped. Readers take a Keyring that tries the current key, then the retired one. A second :passwd is refused until the first has finished. :vault shows the progress. Every sealed file is uploaded to Proton once more.
This commit is contained in:
1 parent
947cc2ba96
commit
3387db1c4d
13 files changed
+724
-143
No files matched your search
@@ -199,6 +199,11 @@ export interface VaultStatus {
|
||||
locks_in: number | null
|
||||
/** How many files are sealed: what an export needs the vault open for. */
|
||||
sealed: number
|
||||
/**
|
||||
* Present while files sealed before the last password change are still
|
||||
* being re-sealed under the new key; `total` is 0 until counted.
|
||||
*/
|
||||
rekey: { done: number; total: number } | null
|
||||
}
|
||||
|
||||
/** The Proton Drive mirror, as `/api/backup` reports it. */
|
||||
|
||||
+14
-3
@@ -889,7 +889,14 @@ export const commands: CommandSpec[] = [
|
||||
const vault = workspace.vault
|
||||
if (vault?.configured) {
|
||||
const left = vault.locks_in ? ` · locks in ${Math.ceil(vault.locks_in / 60)}m unless used` : ''
|
||||
return workspace.say(vault.unlocked ? `vault open${left} — :lock closes it` : 'vault locked — :unlock opens it')
|
||||
const rekey = vault.rekey
|
||||
? vault.unlocked
|
||||
? ` · re-sealing after the password change, ${vault.rekey.total ? `${vault.rekey.done}/${vault.rekey.total}` : 'counting'}`
|
||||
: ' · re-sealing after the password change waits for :unlock'
|
||||
: ''
|
||||
return workspace.say(
|
||||
vault.unlocked ? `vault open${left}${rekey} — :lock closes it` : `vault locked${rekey} — :unlock opens it`,
|
||||
)
|
||||
}
|
||||
const password = await workspace.askSecret('new vault password, 10+ characters — it cannot be recovered')
|
||||
if (password === null) return
|
||||
@@ -925,7 +932,7 @@ export const commands: CommandSpec[] = [
|
||||
{
|
||||
name: 'passwd',
|
||||
group: 'vault',
|
||||
help: "change the vault's password — no file is rewritten",
|
||||
help: "change the vault's password — every sealed file is then re-sealed under a new key, while the vault is open",
|
||||
run: () =>
|
||||
workspace.guard(async () => {
|
||||
const current = await workspace.askSecret('current vault password')
|
||||
@@ -936,7 +943,11 @@ export const commands: CommandSpec[] = [
|
||||
if (again === null) return
|
||||
if (again !== password) return workspace.say('the two passwords differ — nothing was changed', 'error')
|
||||
workspace.vault = await api.changeVaultPassword(current, password)
|
||||
workspace.say('vault password changed')
|
||||
workspace.say(
|
||||
workspace.vault.rekey
|
||||
? `vault password changed — re-sealing ${workspace.vault.sealed} file(s) under the new key; leave the vault open, :vault shows how far`
|
||||
: 'vault password changed',
|
||||
)
|
||||
}),
|
||||
},
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user