pub struct KeyStore { /* private fields */ }Expand description
Thread-safe store that loads, encrypts, and caches Ed25519 signing keys.
Encryption key is derived from a passphrase using Argon2id with a random 128-bit salt. A separate 32-byte integrity key (derived from the same passphrase + salt with a different Argon2 context) is used to compute an HMAC-SHA256 over the serialised key map, protecting against file tampering. Legacy stores (pre-0.1.0) that used bare SHA-256 are automatically migrated on first write.
Implementations§
Source§impl KeyStore
impl KeyStore
Sourcepub fn open_and_migrate(
path: impl AsRef<Path>,
passphrase: &str,
) -> Result<Self>
pub fn open_and_migrate( path: impl AsRef<Path>, passphrase: &str, ) -> Result<Self>
Open the key store, run migrate_if_needed if the store uses the legacy
SHA-256 KDF, and — if migration actually ran — re-open the file so
self.cipher reflects the new Argon2id key.
This is the recommended entry point for production code. For stores
already at V2/V3 (Argon2id) it is identical to a single open call.
Sourcepub fn migrate_if_needed(&self, passphrase: &str) -> Result<bool>
pub fn migrate_if_needed(&self, passphrase: &str) -> Result<bool>
If this store was opened from a legacy format, re-encrypt all keys with the Argon2id-derived key and persist. Call this once after opening and verifying the passphrase works (e.g. by loading a key).
Returns true if migration ran, false if the store was already at
V2/V3. Use open_and_migrate in production to avoid the post-migration
cipher inconsistency (this object’s self.cipher is not updated here).
Source§impl KeyStore
impl KeyStore
Sourcepub fn archive_key(&self, key_id: &str) -> Result<()>
pub fn archive_key(&self, key_id: &str) -> Result<()>
Archive the current key under a timestamped key so it can still be used to verify older signatures after rotation.
Sourcepub fn rotate_key(&self, key_id: &str) -> Result<KeyEntry>
pub fn rotate_key(&self, key_id: &str) -> Result<KeyEntry>
Atomically rotate the key: archive the current key (kept valid so older signatures still verify) and install a fresh current key, in a single persisted write. Use for routine/hygiene rotation. Returns the new key.
Unlike calling archive_key then
generate_key, there is no intermediate on-disk
state where the archive exists but the new key does not (identity I4).
Sourcepub fn revoke_and_rotate(&self, key_id: &str) -> Result<KeyEntry>
pub fn revoke_and_rotate(&self, key_id: &str) -> Result<KeyEntry>
Atomically revoke the current key and install a fresh one. The old key
is archived but marked revoked, so dpp_vc::did_builder drops it from
the published DID document and signatures it produced no longer verify.
Use this on key compromise (vs. hygiene rotation, where the old key
stays valid — see rotate_key).
Source§impl KeyStore
impl KeyStore
Sourcepub fn open(path: impl AsRef<Path>, passphrase: &str) -> Result<Self>
pub fn open(path: impl AsRef<Path>, passphrase: &str) -> Result<Self>
Open a store, refusing one that predates any of the current security properties.
§Why this refuses rather than accommodates
Every legacy shape UpgradeNeeded names was previously accepted here
silently: a store with kdf absent opened under the unsalted SHA-256
KDF, and a store with no hmac opened with no integrity check at
all — which left the plaintext revoked flag unauthenticated, and that
flag is what dpp-vc’s did:web builder reads to drop a compromised key
from the published DID document.
Accepting a weaker shape on read means an attacker who can write the file
can choose the weaker shape. The tolerance was for stores written by
older versions of this crate, and refusing them here does not strand one:
Self::open_and_migrate upgrades and opens them. What changes is that
the weak path now requires a caller who asked for it by name.
§Errors
Names the specific property the store predates, and points at
open_and_migrate.
pub fn generate_key(&self, key_id: &str) -> Result<KeyEntry>
pub fn load_key(&self, key_id: &str) -> Result<KeyEntry>
pub fn has_key(&self, key_id: &str) -> bool
Sourcepub fn public_key(&self, key_id: &str) -> Option<PublicKeyInfo>
pub fn public_key(&self, key_id: &str) -> Option<PublicKeyInfo>
The public key and revocation state of the current key under key_id,
without decrypting the private key. Returns None if no such key exists.
pub rather than pub(crate) because the did:web document builder
lives in dpp-vc and needs exactly this: public key material and
revocation state, never the private key.
Sourcepub fn archived_public_keys(&self, key_id: &str) -> Vec<PublicKeyInfo>
pub fn archived_public_keys(&self, key_id: &str) -> Vec<PublicKeyInfo>
Public keys of all archived records for key_id, in the same ascending
timestamp order as Self::load_archived_keys, without decrypting any
private key material.
Sourcepub fn load_archived_keys(&self, key_id: &str) -> Vec<KeyEntry>
pub fn load_archived_keys(&self, key_id: &str) -> Vec<KeyEntry>
Return all archived keys for the given identifier in ascending timestamp order.