Give each device an identity key that actually signs — ADR 0009 D4.
v1 minted a per-device Ed25519 keypair, stored it in IndexedDB wrapped under HKDF-SHA256(vaultPrivateKey, info="cipherbox-device-key-wrap-v1") → AES-GCM, derived deviceId = hex(SHA-256(publicKey)) — and then never signed anything with it. No caller reads its private half anywhere in the tree. It was inert identity material, which is why the approval exchange ended up bound to self-reported strings.
Work
- Mint, store and load a device identity key, and use it: sign the approval request over the ephemeral public key being offered, and sign the response.
- Register the device public key with the account so the API can verify those signatures.
- Decide what happens when the store is cleared — a device that loses its key is a new device, and that must be a defined path rather than a stuck one.
Design note
v1's wrapping key derived from the vault private key, which means the device key is unavailable exactly when it is most needed: before reconstruction, on a device that cannot yet reconstruct. That is why v1's approval request fell back to an ephemeral throwaway deviceId. Whatever v2 chooses, the device identity must be usable before the vault key exists, or it cannot sign the request it is meant to bind.
Acceptance
- A request and a response each carry a signature the API verifies against a registered device key.
- The device key is usable before vault reconstruction.
- A cleared store yields a defined new-device path, not a wedge.
Part of #1262
Give each device an identity key that actually signs — ADR 0009 D4.
v1 minted a per-device Ed25519 keypair, stored it in IndexedDB wrapped under
HKDF-SHA256(vaultPrivateKey, info="cipherbox-device-key-wrap-v1")→ AES-GCM, deriveddeviceId = hex(SHA-256(publicKey))— and then never signed anything with it. No caller reads its private half anywhere in the tree. It was inert identity material, which is why the approval exchange ended up bound to self-reported strings.Work
Design note
v1's wrapping key derived from the vault private key, which means the device key is unavailable exactly when it is most needed: before reconstruction, on a device that cannot yet reconstruct. That is why v1's approval request fell back to an ephemeral throwaway
deviceId. Whatever v2 chooses, the device identity must be usable before the vault key exists, or it cannot sign the request it is meant to bind.Acceptance
Part of #1262