Skip to content
PersonalPremiumFamilyTeamBusinessEnterprise

Encryption architecture

The four-layer key hierarchy — PDS, KEK, UMK, and per-vault DEKs — plus the wrap relationships, supported algorithms, and the ciphertext envelope format.

PulsVault protects vault data with a four-layer key hierarchy. Keys wrap keys: your credentials derive a key that unwraps your master key, which unwraps per-vault keys, which encrypt individual item fields. Every layer lives and dies on the client.

The key hierarchy#

Layer Key Generated Stored Wrapped with
1 PDS — Password-Derived Secret At every login: PBKDF2-SHA256(password, SHA256(email), 600k iterations) Client memory only — kept five minutes for the first unlock, then wiped —
1 KEK — Key Encryption Key (256-bit) At vault unlock, by combining the PDS with the Access Key (KDF ≥ 600k iterations) Never — memory only, wiped after use —
2 UMK — User Master Key (random 256-bit) Once, at signup, on the client users.encrypted_master_key KEK via AES-256-GCM
3 DEK — per-vault Data Encryption Key (random 256-bit) Once, at vault creation, on the client vaults.encrypted_dek UMK via AES-256-GCM
4 Ciphertext — encrypted item fields At every create/update Item columns (opaque strings) DEK via the vault's algorithm

Additional wraps of the UMK#

The UMK is the pivot of the hierarchy, so it carries independent wraps for different unlock paths — never the raw key:

  • Primary wrap — AES-256-GCM under the KEK (password + Access Key path).
  • Recovery wrap — under a KEK derived from the Recovery Key alone; used during account recovery to re-wrap the UMK for a new password without touching any vault data.
  • Device wrap — RSA-OAEP-2048 to a hardware-backed key (Secure Enclave / Android Keystore) on a trusted mobile; see Trusted-device unlock.

Per-vault DEKs and algorithms#

Each vault has its own independently random DEK, so compromising one vault's key exposes nothing about the others. The encryption algorithm is resolved by plan and organization policy:

Algorithm Notes Plans
AES_256_GCM Authenticated, hardware-accelerated; the default All plans
CHACHA20_POLY1305 Constant-time, fast without AES-NI Team+
XCHACHA20_POLY1305 192-bit nonce eliminates IV-collision risk Enterprise
NONE Explicit plaintext opt-out Enterprise, organization-admin approval only
Primary vault immutability

The primary vault is always AES-256-GCM, hardcoded at signup, regardless of organization encryption policy — and it is never shareable. Signup does not complete until the client has generated and stored the primary vault's wrapped DEK.

Ciphertext envelope format#

Every encrypted field is a self-describing JSON envelope, Base64-encoded and stored as an opaque string:

json
{ "v": 2, "alg": "AES-GCM", "iv": "<base64 12-byte IV>", "data": "<base64 ciphertext>" }

alg is AES-GCM, CHACHA20-POLY1305 or XCHACHA20-POLY1305. There is no separate tag field: the authentication tag is appended to the ciphertext and travels inside data.

Each ciphertext is bound to what it encrypts through AEAD associated data:

text
field:   pv-field-v2|<vault id>|<item id>|<field name>
history: pv-history-v2|<item id>|<field name>|<history id>

The ids are random, immutable identifiers your app generates when it first encrypts a vault, item, note, folder or attachment — not database keys, so a restored backup still decrypts. A server that swapped two ciphertexts — your password into the username field, or one item's secret into another — gets a decryption failure, never the wrong secret. Only version 2 is written or read.

Decryption dispatches on the alg field, making stored data forward-compatible across algorithms. Every AES-GCM encryption uses a unique 12-byte IV — IVs are never reused under the same key. Only sensitive fields (passwords, tokens, PINs, private keys, seed phrases, card numbers, …) are encrypted; an item's name, type, tags, remarks and non-secret fields are stored readable by the server (what is and isn't encrypted).

Key lifecycle properties#

  • Password change re-wraps, never re-encrypts. A new KEK re-wraps the UMK — one database field changes; all DEKs and ciphertext are untouched.
  • Memory-only UMK. The unwrapped UMK and the vault DEKs live in client memory only, with a user-chosen session length (5 minutes to never-expire). They are wiped on expiry, lock or logout — and a page reload starts locked, because nothing that can decrypt is ever written to browser storage. Locking one tab locks every open tab.
  • PDS alone is useless. It must be combined with the Access Key to derive the KEK.
  • RSA is never used for bulk data — only to wrap symmetric keys (sharing, device wraps).
Was this page helpful?