What it stores
For each account, the KeyStore maintains a set of keys:| Field | Description |
|---|---|
| keyId | Unique identifier for the key (bytes32 hash) |
| publicKey | The public key material (format depends on signature scheme) |
| authenticatorId | Identifier of the authenticator device (for WebAuthn keys) |
| signCount | Monotonically increasing counter for replay protection |
| active | Whether the key is currently authorized |
| lastUpdated | Timestamp of the last state change |
Key lifecycle
Registration - the first key is registered viainitialRegisterKey(), which verifies the account has no existing keys. Subsequent keys are added via registerKey(), which requires authorization (the account owner or an authorized contract).
Deactivation - a compromised key can be deactivated immediately. The key remains in storage (for audit purposes) but is no longer valid for signing.
Reactivation - a deactivated key can be reactivated if needed.
Replay protection
Each key tracks asignCount - a monotonically increasing counter. Every time the key is used to sign, the validator extracts the sign count from the authenticator data and compares it to the stored value. If the new count isn’t strictly greater, the signature is rejected.
This prevents replay attacks where a captured signature is resubmitted.
Authorization model
Two types of callers can manage keys:- The account itself - the smart account owner can always manage their own keys
- Authorized contracts - validator modules registered by the KeyStore owner can manage keys on behalf of accounts (e.g., the WebAuthnValidator migrates the initial key to the KeyStore after the first transaction)