05 — Backups and the Rescue Kit
Status: Published
Last reviewed: August 2026
Scope
Everything that lets a vault survive the loss of the device it lives on: the backup file format, the hardened backup lock, peer recovery keys, and the rescue kit.
These are the parts of the system where key material deliberately leaves the keychain, so they are also where the threat model is largest. Documents 01–04 describe a vault protected by a device and a password; this document describes the copies of it that are not.
Summary
| Recovery mechanism | Requires | Bypasses the master password |
|---|---|---|
| Backup file | master password | no |
| Hardened backup | master password and the backup passphrase | no |
| Peer recovery key | a peer's identity private key | yes |
| Rescue kit | the 24 words | yes |
| Value | |
|---|---|
| Backup format | JSON — a VaultItem tree |
| Key material in a backup | carried verbatim from the vault, with its own salt, rounds and pwNorm |
| Backup file MAC | HMAC-SHA-256 under Kb — written, never verified (§7.2) |
| Hardened lock KDF | PBKDF2-HMAC-SHA-512, 262,144 iterations, 256-bit output |
| Hardened lock input | the W-OTS top public key recovered from the signature |
| Hardened lock salt | HMAC-SHA-256(Ky, backup passphrase) |
| Peer recovery | X25519 ECDH → expand → wrap the root key |
| Rescue kit | BIP-39, 24 words, is the 32-byte root key |
1. What a backup file is
One JSON document — a serialized VaultItem:
VaultItem
├── lock ← W-OTS signature, or null (§3)
└── box
├── info
│ ├── vaultId, deviceId, appVersion, name ← cleartext
│ ├── deviceData, settings, cdate, mdate ← cleartext
│ └── encryptedKey
│ ├── kdfAlgo, encAlgo, salt, rounds ← cleartext parameters
│ ├── pwNorm, keyId, version ← cleartext
│ ├── keyMaterial ← the 80-byte wrapped root key (doc 01 §2.7)
│ ├── challenge ← 48 bytes (doc 01 §2.8)
│ └── keyNonce ← encrypted block counters (doc 02 §5)
├── vault
│ ├── identity ← the three private keys, each encrypted
│ ├── previousRootKeys ← older root keys, encrypted (doc 03 §3.4)
│ ├── recoveryKeys ← root key wrapped per peer (§4)
│ └── items ← every vault item, each with its own MAC
└── mac ← §7.2
Nothing in a backup is encrypted by the backup. Every encrypted field is already encrypted in the vault and is copied across unchanged. A backup is a transcript, not a new envelope — which is why creating one costs no key derivation and why its security is exactly the vault's.
The cleartext metadata is worth reading twice. A backup file names the vault, the device, the app version, the creation and modification dates, and the device data. Anyone holding the file learns all of it without attacking anything.
Backups are written to app storage or, on Android, to external storage. An external backup is a file on removable media with no OS protection at all — the vault's own encryption is the entire defence.
2. The ordinary backup
2.1 Key material travels with its own parameters
The backup carries the vault's salt, rounds and pwNorm rather than
relying on whatever the app currently defaults to. That is what lets a backup
written years ago still open: the iteration count may have been raised since,
and the password encoding may have changed (doc 01 §2.1), and the file records
which ones applied to it.
2.2 Restoring
The password is checked against the backup's own key material, exactly as a login is checked against the vault's:
- Derive
Kx/Kyfrom the typed password using the backup's salt, rounds andpwNorm. - Verify the 32-byte tag over the backup's
keyMaterialin constant time. A wrong password stops here. - Decrypt to recover the root key, expand it under the backup's key id.
- Verify the backup's challenge.
Only then are items written into the keychain. The restored VaultKeyMaterial
carries the backup's pwNorm and challenge forward, so the vault continues
to behave as the backup described rather than as the current app would have
created it.
2.3 A restore adopts the backup's key generation
A backup taken before a re-key restores the older root key and the older key id. This is not a partial state — the vault genuinely goes back to that generation, including its previous-root-key archive. Doc 03 §9.5.
3. The hardened backup lock
An optional second passphrase, separate from the master password, that seals the backup's key material behind a post-quantum signature.
3.1 What it does
The 80-byte wrapper in an ordinary backup is IV ‖ tag ‖ ciphertext. Hardening
re-encrypts only the 32-byte ciphertext — the wrapped root key — under a
lock key, leaving the IV and tag in place:
locked = IV ‖ tag ‖ AES-256-CTR(K_lock, chainId, ciphertext)
and stores a W-OTS signature over the backup box in lock.
K_lock is derived from two things the file does not contain:
K_lock = PBKDF2-HMAC-SHA-512(
password = W-OTS top public key,
salt = HMAC-SHA-256(Ky, backup passphrase),
262144 iterations, 256 bits)
Ky is the master-password-derived MAC key, so both secrets are required:
the master password produces Ky, and the backup passphrase salts the
derivation. Neither alone gets you K_lock.
3.2 The signature is not checked and discarded
The W-OTS top public key is not stored. It is recovered by verifying the signature against a hash of the backup box — and that recovered value is then fed into the KDF as the password.
This is the neat part of the construction. There is no "if signature valid"
branch to bypass, because the signature's output is key material. Tamper with
any part of the box and signature verification recovers a different top public
key, which derives a different K_lock, which decrypts the root key ciphertext
to garbage — and the garbage then fails the ordinary tag check from doc 01 §3.
Integrity over the whole box comes for free from the fact that the key depends
on it.
3.3 What it buys
An attacker with the backup file and the master password still cannot open a hardened backup. That is its whole purpose: it decouples "someone got my backup and my password" from "someone got my vault". For a file that may sit on removable media or in cloud storage, that is a meaningful separation.
The cost is another secret to lose. A hardened backup with a forgotten passphrase is unreadable, and there is no recovery for it short of a rescue kit (§5), which bypasses the lock by supplying the root key directly.
4. Peer recovery keys
A vault can nominate peers who are able to recover it. This is under the Vault Recovery section in advanced settings in Pyramidion.
Each peer contributes an X25519 public key. The vault computes the ECDH shared
secret between its own identity exchange key and that public key, expands it
the same way any root key is expanded, and wraps the vault's root key under the
result. The wrapped copy is stored as a RecoveryKey in the vault — and
therefore in every backup.
Recovery reverses it: the peer recomputes the same shared secret from their private key and the vault's public key, unwraps, and hands back the root key.
Two consequences, stated plainly:
- A peer recovery key is a full bypass of the master password. Anyone holding the peer private key that a recovery key was made for can recover the vault's root key without knowing the password. This is the purpose and is there for recovering from a forgotten password.
- They travel in backups. A backup carries every recovery key with it, so the set of people who can open that file is the vault owner plus every nominated peer. This is useful for recovering backups if a master password is forgotten.
Recovery keys are re-derived on every re-key (doc 03 §3.5) so peers keep working across rotations without re-exchanging codes.
5. The rescue kit
5.1 What it is
Twenty-four BIP-39 words. They are not a passphrase, not a key-encrypting key, and not derived from anything — they are the vault's 32-byte root key, encoded as a mnemonic and back again with no transformation in between.
rescue kit words ⟷ 32 bytes of root key entropy
Anyone who reads those words has the vault. The app says so on the screen that displays them, and this document says so too, because it is the single most important property of the feature: a rescue kit is not a recovery credential, it is the key itself in a human-transcribable form. These rescue kit words still need to be expanded with the expandKeyType variable (key id) which must always be present with a vault or backup item.
5.2 Verifying one
There is no password to check a rescue kit against, so it is checked against the backup's challenge:
- Decode the words to 32 bytes.
- Expand them under the backup's key id (expandKeyType) — not the device's current one.
- Recompute the challenge and compare.
Using the backup's key id matters: a kit from a later key generation would
otherwise expand into keys that decrypt nothing, and the restore would overwrite
the vault with items nothing can open. Backups written before the challenge
field existed are excluded from rescue kit restores entirely, rather than
trusted — without a challenge there is no way to tell a correct root key from a
wrong one.
A rejected kit clears the derived keys, so a failed attempt cannot leave a half-expanded session for the next caller to inherit.
5.3 Rescue kits and hardened backups
A rescue kit supplies the root key directly, so it opens a hardened backup without the backup passphrase — the lock protects the wrapped key, and a rescue kit does not need the wrapper.
But restoring a hardened backup this way writes back key material that is still locked, which no password can open. The flow therefore has to be followed by setting a new master password, which re-wraps the recovered root key normally and recomputes the challenge under the vault's key id. Skipping that step leaves a vault that opens for nobody.
6. What a stolen backup gives an attacker
Immediately, with no work: the vault name, device id, app version, device data, settings, creation and modification dates, the number of items, and the KDF parameters.
With the master password: everything, unless the backup is hardened.
With the master password and the backup passphrase: everything.
With a nominated peer's private key: everything, no password needed (§4).
With the rescue kit words: everything, no password needed (§5).
With none of those: the offline password search of doc 01 §5, at 369,420 PBKDF2 iterations per guess against whatever entropy the master password has. Hardening adds a second such barrier.
The honest summary is that a backup has more doors than a device does. That is inherent to the purpose — a backup exists to be recoverable when the device and possibly the password are gone — but every door added for recoverability is a door in the threat model too.
7. Limitations and non-goals
7.1 A backup is only as fresh as when it was taken. Items added since are not in it, and a restore is a replacement, not a merge.
7.2 The backup file's own MAC is computed but never verified. Each backup
carries a box.mac — an HMAC-SHA-256 over the serialized VaultItem under the
vault's authentication key. No restore path checks it.
What still protects an ordinary backup: the key material's own 32-byte tag
(modifying the wrapper fails the password check), the challenge, and every
item's individual MAC (doc 02 §3). What is not covered is what doc 02 §8.3
already describes — nothing authenticates the set. An attacker who can write
the file can delete items, or alter the cleartext settings and metadata,
without any check failing.
Hardened backups do not have this gap: the W-OTS construction (§3.2) makes the
whole box integrity-protected as a side effect of key derivation. Verifying
box.mac on restore would close it for ordinary backups too, and would need a
convention for blanking the field before recomputation, since the MAC is taken
over a serialization that contains it.
7.3 Cleartext metadata. §1. A backup identifies its vault and device to anyone holding it.
7.4 External backups have no OS protection. A file on removable media carries only the vault's own encryption.
7.5 Recovery keys and rescue kits are password bypasses by design. Changing your master password does not invalidate either one. Both are bound to the root key, so only a re-key retires them — and a re-key does not reach copies already written to backups.
7.6 A rescue kit never expires. It is the root key. It remains valid until the vault is re-keyed, and any backup written before that re-key remains openable by it forever.
7.7 Nothing rate-limits a restore attempt. The attempt counter that guards PIN entry (doc 04 §1.3) has no equivalent here. A backup file is attacked offline at the KDF's cost and nothing else.
8. Reproducing a backup restore check
Given a backup file and the master password, verifying it without the app:
import base64, hashlib, hmac, json, unicodedata
from hashlib import pbkdf2_hmac
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
backup = json.load(open("backup.json"))
ek = backup["box"]["info"]["encryptedKey"]
# the backup's OWN parameters, never the app's current defaults
raw = "correct horse battery staple"
pw_norm = ek.get("pwNorm") or 0
pw = (unicodedata.normalize("NFKD", raw) if pw_norm == 1 else raw).strip()
dk = pbkdf2_hmac("sha512", pw.encode("utf-8"),
base64.b64decode(ek["salt"]), ek["rounds"], dklen=64)
Kx, Ky = dk[:32], dk[32:]
blob = base64.b64decode(ek["keyMaterial"])
iv, tag, ct = blob[:16], blob[16:48], blob[48:]
expected = hmac.new(Ky, hashlib.sha256(iv + ct).digest(), hashlib.sha256).digest()
assert hmac.compare_digest(expected, tag), "wrong password, or a hardened backup"
RK = Cipher(algorithms.AES(Kx), modes.CTR(iv)).decryptor().update(ct)
# expand under the BACKUP's key id (doc 03 §5), not the device's
salt2 = hashlib.sha256(ek["keyId"].encode("utf-8")).digest()
expanded = pbkdf2_hmac("sha512", RK, salt2, 2048, dklen=64)
Ka, Kb = expanded[:32], expanded[32:]
# Ka / Kb now decrypt every item in backup["box"]["vault"]["items"] (doc 02 §9)
If the tag check fails on a backup whose lock field is non-null, the failure
is expected: the ciphertext is sealed under the hardened lock (§3) and the
outer wrapper cannot be opened until the lock is removed.
For a rescue kit, skip the password entirely — decode the 24 words to 32 bytes
with any BIP-39 implementation, and that value is RK.
Change log
| Date | Change |
|---|---|
| 2026-08 | Initial version. Documents the backup file format, the W-OTS hardened lock and why its signature doubles as key material, peer recovery keys, the rescue kit's identity with the root key, and the unverified box.mac. |