Algorithm
aivault uses XChaCha20-Poly1305 for all secret encryption. This is an AEAD (Authenticated Encryption with Associated Data) cipher that provides both confidentiality and integrity.- XChaCha20: Stream cipher with a 256-bit key and 192-bit nonce (extended nonce variant, safe for random nonce generation)
- Poly1305: MAC for authentication, ensures ciphertext has not been tampered with
Key hierarchy
- Retrieve KEK from key provider
- Unwrap the DEK using the KEK
- Decrypt the secret value using the DEK
- Verify the AEAD tag and associated data
Key derivation (passphrase provider)
When using the passphrase key provider, the KEK is derived using Argon2 (memory-hard key derivation function):Authenticated associated data (AAD)
Each secret’s ciphertext is bound to its metadata via AAD (version 2+):| AAD field | Purpose |
|---|---|
| Secret ID | Prevents swapping ciphertext between secrets |
| Scope | Prevents moving a workspace secret to global |
| Pinned provider | Prevents re-pinning a secret to a different provider |
On-disk format
Secret records are stored as JSON files insecrets/<secret_id>.json:
ciphertext, wrapped_dek, and nonce fields are all base64-encoded binary. The value_version increments on each rotation.
Master key rotation
aivault rotate-master re-wraps every DEK with the new KEK:
- Derive new KEK from new passphrase/key
- For each secret: unwrap DEK with old KEK, re-wrap with new KEK
- Update vault metadata with new key provider config
- Verify: unwrap all DEKs with new KEK to confirm
Zeroization
Sensitive values (decrypted secrets, DEKs, KEKs) are zeroized in memory after use using thezeroize crate. This reduces the window during which secrets exist in plaintext memory.
Next: Audit log