Key Lifecycle States
Key Metadata Structure
src/keystore/mod.rs:19-29
Field Semantics
key_id (Uuid)
key_id (Uuid)
Unique identifier for this specific key version.
- Generated via
Uuid::new_v4()on creation or rotation - Used as primary lookup key in storage
- Stored in
Envelope.key_idfield after encryption
lineage_id (Uuid)
lineage_id (Uuid)
Identifier shared across all versions in a rotation chain.
- Equal to the first key’s
key_idin the lineage - Enables querying all versions of a rotated key
- Immutable across rotations
version (i32)
version (i32)
Monotonically increasing version number within the lineage.
- Root key starts at
version=1 - Incremented by 1 on each rotation
- Enables auditing rotation history
active (bool)
active (bool)
Whether this key version can be used for new encryptions.
- Only the latest version in a lineage is active
- Inactive keys can still decrypt old envelopes
- Attempting to encrypt with inactive key returns
QimemError::KeyInactive
Key Material Structure
src/keystore/mod.rs:32-40
Security: The
material field uses zeroize::Zeroizing to ensure key bytes are securely erased from memory when dropped. See Security for details.Key Store Operations
TheKeyStore trait defines the key lifecycle contract:
src/keystore/mod.rs:43-50
Operation Details
- create_key()
- get_key()
- rotate_key()
Creates a new root key (the first key in a lineage).Process:Source:
- Generate new UUID for
key_id - Set
lineage_id = key_id(root property) - Set
version = 1 - Set
active = true - Generate 32 random bytes for key material via
OsRng - Store key and return metadata
src/keystore/memory.rs:23-45Rotation Design
From the README:Key rotation designSource:
- Rotation creates a new active key record and deactivates the old key.
- Old keys remain available for decrypt compatibility.
- Inactive keys are rejected for encryption.
README.md:32-35
Why Rotation Matters
Compliance
Many security standards (PCI-DSS, HIPAA, etc.) require periodic key rotation.
Blast Radius
Limits the amount of data encrypted with a single key, reducing impact of key compromise.
Forward Secrecy
Old data remains encrypted with old keys even after rotation.
Zero Downtime
Old envelopes decrypt seamlessly during and after rotation.
Storage Backend Comparison
- InMemoryKeyStore
- PostgresKeyStore
Stateless storage using Source:
RwLock<HashMap<Uuid, StoredKey>>.Structure:src/keystore/memory.rs:16-20Properties:- Keys lost on process restart
- No persistence between CLI invocations
- Suitable for testing and stateless API mode
- Lock contention on high concurrency
Key Material Generation
All backends use the same secure key generation:src/keystore/mod.rs:52-56
32 bytes (256 bits) is the required key size for AES-256-GCM. ChaCha20-Poly1305 also uses 256-bit keys.
Error Handling
| Error | When | Recovery |
|---|---|---|
KeyNotFound(Uuid) | get_key() or rotate_key() on unknown key | Ensure key was created |
KeyInactive(Uuid) | Attempting to encrypt with inactive key | Rotate key or use active version |
Config(String) | Lock poisoning (in-memory) or connection errors (Postgres) | Restart or fix database |
src/error.rs:20-37
Best Practices
Rotate Regularly
Establish a rotation schedule (e.g., every 90 days) based on compliance requirements.
Use Postgres in Production
The in-memory store loses keys on restart. Use
stateful mode for persistence.See Also
Envelope Format
How
key_id is embedded in envelopesSecurity
Key material protection with zeroize
API Reference
Full KeyStore trait documentation