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