1. Align Passkey.privateKey with item-scope crypto
Today Passkey.privateKey is encrypted via a Keystore key on the biometric channel, not via item-scope crypto bound to the parent vault. See feature/credentials/.../CreatePasskeyViewModel.kt (the EncryptPasskeyEncryptionKey flow). This means:
A passkey's secret material is not bound to its vault's vaultId via AAD.
When a Password is moved between vaults, the child Passkey rows survive intact via the password_id FK (PasskeyEntity.kt), but their crypto isn't aligned with the new vault.
Long-term we likely want Passkey.privateKey under item-scope crypto with vault-bound AAD, mirroring Password.password and Password.totpSecret. Blocked on: how should passkeys-without-a-parent-Password be handled? They can't exist today (FK enforces a parent), but the wider passkey roadmap may change that.
When unblocked, MoveItemsToVaultUseCase will need to also rewrap Passkey.privateKey for each child passkey of every moved Password.
1. Align Passkey.privateKey with item-scope crypto
Today
Passkey.privateKeyis encrypted via a Keystore key on the biometric channel, not via item-scope crypto bound to the parent vault. Seefeature/credentials/.../CreatePasskeyViewModel.kt(the EncryptPasskeyEncryptionKey flow). This means:A passkey's secret material is not bound to its vault's vaultId via AAD.
When a Password is moved between vaults, the child Passkey rows survive intact via the
password_idFK (PasskeyEntity.kt), but their crypto isn't aligned with the new vault.Long-term we likely want Passkey.privateKey under item-scope crypto with vault-bound AAD, mirroring Password.password and Password.totpSecret. Blocked on: how should passkeys-without-a-parent-Password be handled? They can't exist today (FK enforces a parent), but the wider passkey roadmap may change that.
When unblocked, MoveItemsToVaultUseCase will need to also rewrap Passkey.privateKey for each child passkey of every moved Password.