Skip to main content
Version: 1.0

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)
FieldSizeDescription
Nonce12 bytesRandom IV for AES-256-GCM
CiphertextVariableEncrypted payload
Tag16 bytesGCM 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:

ContextAAD String
Account Key wrappingsyvault.account-key.v1
ECDH private key wrappingsyvault.ecdh-private-key.v1
Record DEK wrappingsyvault.record.{record_id}.dek.v1
Record payload encryptionsyvault.record.{record_id}.payload.v1
Metadata encryptionsyvault.metadata.v1
One-time share encryptionsyvault.share.one-time.v1
info

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> and syvault.enc.v1 ensure the Auth Hash and Encryption Key are independent.
  • Account Key layer: AAD syvault.account-key.v1 prevents the wrapped Account Key from being confused with any other wrapped blob.
  • Vault Key layer: HKDF info string syvault.vault.{vault_id}.v1 ensures each vault derives a unique key.
  • Record layer: AAD strings syvault.record.{id}.dek.v1 and syvault.record.{id}.payload.v1 bind 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.
warning

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.