Vault Key Slots
Password-manager cryptography · key slots, passkeys, revocation

Vault Key Slots

Your vault is encrypted with one random vault key. Your master password, your unlock password and your passkey do not derive that key. Each one derives its own key that opens its own sealed copy of it, called a slot. Removing an unlock method deletes its slot. Rotating the vault key deals with anyone who already got the key out.

Why you can add and remove methods

Every method wraps the same vault key independently. The master password unlocks the vault key, and with it you can seal a new copy for a new method, or delete a copy, without touching the others.

Why a removed password stops working

The server holds the only copy of that slot and releases it only after login plus an authenticator code. Once the slot is deleted, the stolen password has nothing left to open.

When removal is not enough

If the thief also has a copy of the server data, or already logged in once, they hold the vault key itself. Then you must rotate: new vault key, re-encrypted vault, new copies for the methods you keep.

The key hierarchy

Three doors, one vault key

Master password recovery, kept offline Argon2id(password, salt₁) slow, memory-hard stretch HKDF → KEK₁ · HKDF → auth₁ two unrelated keys Unlock password everyday, + authenticator code Argon2id(password, salt₂) own salt, own result HKDF → KEK₂ · HKDF → auth₂ code is NOT key material Passkey secret stays in the device PRF(credential, prfSalt) WebAuthn extension, 32 bytes HKDF → KEK₃ · signature signs the login challenge SERVER GATE · releases a slot only after: auth₁ matches verifier auth₂ matches + valid code valid passkey signature Slot 1 KEK₁ opens slot private key Slot 2 KEK₂ opens slot private key Slot 3 KEK₃ opens slot private key decrypt its copy of VK Vault key VK (random) AES-GCM(VK, each item) Only salts, verifiers, public keys and ciphertext live on the server. Passwords, KEKs and VK never leave your device.
Each unlock method has its own key-encryption key (KEK) and its own slot. A slot holds a small key pair. Its private key is sealed with the method's KEK, and a copy of the vault key is sealed to its public key. That second layer is what makes clean rotation possible later on.
Derivations

What each method computes

Master / recovery password

The root. Can always add, remove and rotate.
  1. salt₁ = random 16 B (stored on server)Public. The server hands it to anyone before login so the client can derive.
  2. S₁ = Argon2id(master, salt₁, m=64 MiB, t=3)Makes each guess expensive. The demo uses PBKDF2 because browsers lack Argon2.
  3. KEK₁ = HKDF(S₁, "slot-kek")auth₁ = HKDF(S₁, "server-auth")Domain separation: knowing auth₁ reveals nothing about KEK₁.
  4. server stores verifier₁ = SHA-256(auth₁)Login proof. Real systems often use SRP or OPAQUE so the server never sees auth₁ either.
  5. slot1.encPriv = AES-GCM(KEK₁, slotPriv₁)Opening this gives the slot's private key, which opens the slot's copy of VK.

Unlock password + code

Convenient, and revocable on its own.
  1. salt₂ = random 16 B (new per slot)A fresh salt means the same password in two slots still gives unrelated keys.
  2. S₂ = Argon2id(unlockPw, salt₂)Same stretch as the master password. A short unlock password is the weak point under offline attack.
  3. KEK₂ = HKDF(S₂, "slot-kek")auth₂ = HKDF(S₂, "server-auth")Same split as before.
  4. totpSeed (20 B) stored server-side + in your appcode = HOTP(totpSeed, ⌊t/30⌋) mod 10⁶The server checks the code before releasing slot 2. The code never feeds the KEK.
  5. slot2.encPriv = AES-GCM(KEK₂, slotPriv₂)Created by a client that already holds VK, which means one unlocked with the master password.

Passkey (WebAuthn PRF)

No password at all. The secret stays in hardware.
  1. prfSalt = random 32 B (stored on server)Sent back to the authenticator on every login.
  2. s = SHA-256("WebAuthn PRF" ‖ 0x00 ‖ prfSalt)P = HMAC-SHA-256(credSecret, s)Computed inside the authenticator (CTAP2 hmac-secret). Same credential and salt always give the same P.
  3. KEK₃ = HKDF(P, "slot-kek")Deterministic, so it can wrap a key. Without the physical device and its PIN or biometric, no one can compute it.
  4. sig = ECDSA(credPriv, server challenge)The login proof. The server keeps the credential public key, never a secret.
  5. slot3.encPriv = AES-GCM(KEK₃, slotPriv₃)A passkey without PRF support can log you in, but it cannot unlock an encrypted vault.

Why the authenticator code can't be part of the key

A TOTP code changes every 30 seconds, so it cannot wrap anything long-lived. To check it, the server must also hold the seed, so any key derived from the seed would be available to the server or to anyone who breaches it. The code therefore guards the door, meaning who may download slot 2, and adds nothing to the lock. That distinction decides the threat table below: an attacker with a copy of the server data skips the door and attacks the lock offline with the stolen password.

Live lab · real WebCrypto, nothing leaves this page

Steal it, revoke it, rotate it

An example vault has been set up below: a master password, an unlock password with an authenticator, and a passkey on a simulated laptop. The attacker starts out knowing your unlock password. Every hex string is real key material or ciphertext produced in your browser (P-256, AES-256-GCM, HKDF, PBKDF2-SHA-256 at 120,000 iterations standing in for Argon2id). The passkey's authenticator is simulated, because this page cannot call real WebAuthn.

Try this sequence

  1. Attacker: Log in online. Blocked: they have the password but no code.
  2. Tick can phish a live code and log in again. They get in and read the vault.
  3. Attacker: Register own passkey. The server refuses, because it requires the master password.
  4. Attacker: Copy the server database, as in a breach or a leaked backup.
  5. You: Remove the unlock-password slot on the server desk. Their online login now fails.
  6. You: Add a secret. Attacker: copy the database again, then Crack every copy. The new secret is readable with the vault key they kept.
  7. You: Rotate vault key. Attacker: copy and crack again. The new copy stays locked, while your master password and passkey still work.

Youclient · holds the secrets

Authenticator app
––––––for unlock password
Unlock
Locked.
Manage · needs master password

Server databasewhat a breach would leak

Vault key version–
The server never sees the vault key, only its version number.
Slots
Building example vault…
Encrypted items

Attackerstole your unlock password

Holds
Unlock password blue-otter-42
Vault keys learned 0
Online, through the server
Offline, on stolen copies
No copies yet.
Revocation

What removing a slot does, and what only rotation does

The attacker has…Remove the slotRemove + rotate the vault keyStill to do
Your unlock password onlylocked out They were already stopped at the code check. Removal makes the password worthless.locked out Not needed.Nothing. Pick a new unlock password.
Password + one phished code (logged in once)no new logins But that session gave them VK and the plaintext they viewed.future data safe VK they hold no longer matches the server.Change the actual passwords stored in the vault that they could have read.
Password + a copy of server data (breach, backup, device cache)no effect offline Their copy still holds the old slot. Code check doesn't apply offline. They get VK.old copy still open Later copies are safe. The copy they hold stays readable forever.Change stored secrets. Rotation cannot reach data the attacker already has.
VK itself (malware on an unlocked device)no effect Slots only guard VK, and they have it.future data safeClean the device first, then rotate, or the new key leaks too.
Your passkey device, lockedlocked out PRF output needs the device and its PIN or biometric. Remove the slot to be sure.locked out Not needed unless the device was unlocked and used.Revoke the passkey at the provider too.

Rule of thumb: remove when a credential is exposed, remove and rotate when the attacker may have had the vault key or a data copy, and change the stored secrets when they may have seen plaintext.

Rotation

Why each slot carries a key pair

The simplest design seals VK directly with each KEK: slot.encVK = AES(KEK, VK). Removal works fine. Rotation does not, because writing the new VK into the passkey slot needs KEK₃, and KEK₃ only exists while the passkey is plugged in and touched. A master-password-only rotation would have to drop every other method and make you re-enroll them.

The key-pair layer fixes this. Each slot publishes a public key. Rotation, done from a master-password session, encrypts the new VK to each remaining slot's public key. No other method's secret is needed. The removed slot simply isn't on the list, so it never receives the new key.

For this to be safe the client must trust the public keys it encrypts to. A malicious server could swap in its own. Real designs sign the slot public keys with a key only the account owner holds, and check that signature before re-wrapping.

# rotate(masterPw) — runs on your device
VK_old = unlock_with_master(masterPw)
VK_new = random(32)

# 1. re-encrypt the vault
for item in server.items:
    pt   = AES_GCM.open(VK_old, item, aad=f"item|v{v}")
    item = AES_GCM.seal(VK_new, pt,   aad=f"item|v{v+1}")

# 2. hand the new key to every slot you keep
for slot in server.slots:          # removed slot is gone
    verify_sig(owner_key, slot.pub)
    slot.encVK = ECIES.seal(slot.pub, VK_new,
                            aad=f"vk|v{v+1}")

server.vk_version = v + 1          # old VK now opens nothing new
Server rules

What has to be enforced for revocation to mean anything

In the wild

The same pattern in real systems

LUKS disk encryption

The textbook case of key slots
  1. One volume key, several keyslots (8 in LUKS1, 32 in LUKS2), each wrapping it under a passphrase or keyfile. luksKillSlot removes one. The cryptsetup docs warn that a saved header backup still contains the old slot, and that changing the actual volume key needs cryptsetup reencrypt. That is exactly the remove-versus-rotate split above.

Bitwarden

Master password + PRF passkeys
  1. The master password is stretched into a key that wraps the account's symmetric key. "Log in with passkey" can also decrypt the vault when the passkey supports the PRF extension. Account encryption-key rotation re-encrypts the vault under a new key.

1Password

Hardening the password slot
  1. Mixes a random 128-bit Secret Key, stored only on your devices, into the account password derivation (two-secret key derivation). A server breach alone then gives an attacker nothing to brute-force. Login uses SRP, so the server never receives a password-equivalent.
References
  1. W3C. Web Authentication Level 3, §10.1.4 Pseudo-random function extension (prf).
  2. FIDO Alliance. Client to Authenticator Protocol (CTAP) 2.1, hmac-secret extension.
  3. RFC 9106: Argon2 memory-hard function. RFC 5869: HKDF. RFC 6238: TOTP.
  4. cryptsetup project. cryptsetup FAQ, sections on keyslots and header backups.
  5. Bitwarden. Security whitepaper.
  6. 1Password. 1Password security design white paper.