Encryption Model
SyVault encrypts every piece of sensitive data using AES-256-GCM with per-record Data Encryption Keys, domain-separated Additional Authenticated Data (AAD), and a strict wire format. This page documents the cryptographic details.
AES-256-GCM
All symmetric encryption in SyVault uses AES-256-GCM (Galois/Counter Mode), which provides both confidentiality and integrity in a single operation. Key properties:
- 256-bit keys -- 2^256 key space, resistant to brute force even with quantum computers (Grover's algorithm reduces effective strength to 2^128, still infeasible).
- 12-byte random nonces -- generated from a cryptographically secure RNG for each encryption operation. The probability of a nonce collision is negligible given the per-record key structure (each DEK encrypts a small number of messages).
- 128-bit authentication tags -- any modification to the ciphertext, nonce, or AAD is detected with overwhelming probability.
Wire Format
Every encrypted blob in SyVault follows the same wire format:
nonce(12 bytes) || ciphertext(variable) || tag(16 bytes)
| Field | Size | Description |
|---|---|---|
| Nonce | 12 bytes | Random IV for AES-256-GCM |
| Ciphertext | Variable | Encrypted payload |
| Tag | 16 bytes | GCM authentication tag |
The nonce is prepended to the ciphertext so that decryption routines can extract it without additional metadata. The tag is appended by the GCM implementation (ring/aes-gcm crate convention).
Per-Record Data Encryption Keys
Each record (login, note, card, identity) gets its own random 256-bit DEK. The DEK is wrapped (encrypted) by the Vault Key before storage. This means:
- Compromise of one record's DEK does not expose other records. An attacker who somehow extracts a single DEK gains access to exactly one record.
- Key rotation is granular. A single record's DEK can be rotated without re-encrypting the entire vault.
- Sharing is key-based. To share a record, you share its DEK (re-wrapped for the recipient), not the vault key.
Additional Authenticated Data (AAD)
AAD is bound to every encryption operation but is not encrypted -- it is authenticated. SyVault uses AAD strings for domain separation, preventing cross-context attacks where ciphertext from one context is replayed in another.
The following AAD strings are used throughout the codebase:
| Context | AAD String |
|---|---|
| Account Key wrapping | syvault.account-key.v1 |
| ECDH private key wrapping | syvault.ecdh-private-key.v1 |
| Record DEK wrapping | syvault.record.{record_id}.dek.v1 |
| Record payload encryption | syvault.record.{record_id}.payload.v1 |
| Metadata encryption | syvault.metadata.v1 |
| One-time share encryption | syvault.share.one-time.v1 |
Because the record_id is embedded in the AAD, a wrapped DEK for record A cannot be used to decrypt record B even if both are wrapped by the same Vault Key. The GCM authentication tag verification will fail if the AAD does not match exactly.
Domain Separation Throughout
SyVault applies domain separation at every layer of the key hierarchy:
- KDF layer: HKDF info strings
syvault.auth.v1:<email>andsyvault.enc.v1ensure the Auth Hash and Encryption Key are independent. - Account Key layer: AAD
syvault.account-key.v1prevents the wrapped Account Key from being confused with any other wrapped blob. - Vault Key layer: HKDF info string
syvault.vault.{vault_id}.v1ensures each vault derives a unique key. - Record layer: AAD strings
syvault.record.{id}.dek.v1andsyvault.record.{id}.payload.v1bind each encryption to a specific record and purpose. - Sharing layer: HKDF info
syvault.share.v1.wrap:<sender_pk>:<recipient_pk>binds the shared secret derivation to both parties.
Domain separation is critical for security. Without it, an attacker who controls the server could swap ciphertext blobs between records, vaults, or users, potentially tricking the client into decrypting data in the wrong context. SyVault's AAD strings make any such swap detectable.