Whitepaper · Scheme v4 · June 2, 2026

Vardra security architecture

A technical description of how Vardra derives keys, encrypts data, synchronizes across devices, and bounds the damage of every realistic compromise. For the plain-language version, see the Security page.

01

Design goals

Vardra is engineered around a single non-negotiable property: the operator cannot read user data. Everything else follows from it.

  • Zero-knowledge. Keys are derived on-device. No key that opens a vault is ever transmitted or stored server-side.
  • Authenticated encryption everywhere. Confidentiality and integrity together. Tampering is always detected.
  • Domain separation. Every derived key is bound to a distinct purpose label so one sub-key never substitutes for another.
  • Offline-first. The local vault works without the network. Sync is an optimization, not a dependency.

02

Threat model

We assume a capable adversary and bound the damage of each compromise rather than assuming any single layer holds.

Network attacker (passive or active)

All transport runs over TLS. Vault contents are end-to-end encrypted before transport, so even a fully compromised TLS session or MITM sees only ciphertext. The login proof (auth_hash) is independent of the vault key, so capturing it never yields decryptable data.

Compromised account service / sync relay

The relay stores encrypted payloads, nonces, and routing metadata only. It never holds a key that can decrypt a vault. A full server breach exposes ciphertext — see the breach analysis below.

Database exfiltration of the auth store

The server stores Argon2id(auth_hash) with its own salt, never the raw value. Reversing it requires brute-forcing Argon2id over an unknown 256-bit input. And auth_hash cannot be turned into the vault key.

Offline attacker with a stolen vault file

Decryption requires deriving the key from the master password via Argon2id (memory-hard) plus, for v3/v4 vaults, the device-stored Secret Key (~165 bits). A weak password alone is insufficient without the Secret Key.

Malicious or curious Vardra operator

There is no server-side key escrow. No employee, court order, or internal tool can decrypt a user vault, because the keys simply do not exist on our infrastructure.

Lost device

The on-device password wrap lives in the OS keychain and never syncs. Revoking the device removes its sync access; the vault remains sealed without the master password.

Out of scope: a fully compromised endpoint (malware with your unlocked vault in memory) and a coerced user. No client-side encryption product can defend a device an attacker already controls while it is unlocked.

03

Key hierarchy

Two roots feed the hierarchy: your master password (the daily secret) and your Secret Key (a high-entropy device-stored value for bootstrap and recovery). A single random Data Encryption Key (DEK) protects all item data and is wrapped — never re-derived — so a password change rotates only a wrap.

master_password ─┐
                 ├─ argon2id(pw, hkdf(email,"vardra-auth-salt-v4")) = K_master
                 │       │
                 │       ├─ hkdf(K_master,"vardra-auth-hash-v4") = auth_hash  // to relay
                 │       └─ hkdf(K_master,"vardra-vault-key-v4") = K_vault    // on device
                 │                                                   │
Secret Key ──── argon2id(secret_key, salt) = K_secret               │
recovery 24w ── hkdf(bip39_entropy,"vardra-recovery-seed-v4")=K_rec │
                          │                  │                       │
                          ▼                  ▼                       ▼
                 wrapped_dek_secret  wrapped_dek_recovery   wrapped_dek_password
                          │                  │                       │
                          └──────────────────┴───────────────────────┘
                                             ▼
                                    DEK (random, 256-bit)
                                             │
                          hkdf(DEK, "item-encryption") = K_item  // XChaCha20-Poly1305
                          hkdf(DEK, "db-encryption")   = K_db
                          hkdf(DEK, "verification")    = K_verify

The DEK is generated once from the OS CSPRNG at vault creation and never rotated. Three independent wraps (password, Secret Key, recovery) each recover it, which is what lets any one recovery path work without the others.

04

Authentication without revealing the password

To log in, the client proves knowledge of the password without sending it. It derives K_master locally, then sends auth_hash = HKDF(K_master, "vardra-auth-hash-v4") — a value that is mathematically independent of K_vault by virtue of using a different HKDF label.

The server does not trust this value blindly or store it raw. It applies its own server-side Argon2id with a server-held salt and stores only that digest. Verification recomputes the digest and compares in constant time. The result: even with the full auth database, an attacker faces an Argon2id brute-force over an unknown 256-bit input and still has nothing that decrypts a vault.

The Argon2id salt for K_master is derived from your normalized email via HKDF, not stored — so every device of the same account reaches the same master key from email + password with no extra round-trip.

05

Item encryption

Each secret field is sealed with XChaCha20-Poly1305: a 256-bit key, a 192-bit (24-byte) random nonce per encryption, and a Poly1305 authentication tag. The extended nonce removes any practical risk of random-nonce collision, so there is no fragile nonce counter to maintain or corrupt.

Field keys come from the DEK by HKDF-SHA256 with a per-purpose label (item encryption, database encryption, verification), giving each domain an isolated key. Each ciphertext is bound by length-prefixed Additional Authenticated Data to its (item_id, field_label, scheme_version). A tampered ciphertext fails the tag; a ciphertext relocated to a different field also fails — preventing both tampering and cut-and-paste substitution attacks.

06

End-to-end sync

Sync moves sealed payloads, not plaintext. The client encrypts each change locally, then pushes the resulting ciphertext and nonce to the relay; other devices pull and decrypt them with the same DEK-derived keys. The relay's role is storage and routing — it pattern-matches account identifiers and device metadata, never content.

Because the encryption boundary is the client, the relay can scale, replicate, cache, and back up encrypted blobs freely without ever being able to read them. Turning sync off does not change the vault's security — it only stops the encrypted copies from leaving your device.

07

Recovery and key escrow

Recovery is client-held key escrow, not server-held. The DEK is wrapped under three independent keys; any one recovers the vault, and each wrap's AAD is bound to its purpose, the vault UUID, and the scheme version so a wrap cannot be replayed across slots, vaults, or schemes.

  • wrapped_dek_password — under K_vault, OS keychain only, never synced. Daily unlock.
  • wrapped_dek_secret — under the Secret Key, in the synced sidecar. New-device bootstrap and password reset.
  • wrapped_dek_recovery — under the BIP-39 recovery seed, in the synced sidecar. Last resort.

The recovery phrase is a 24-word BIP-39 mnemonic encoding 256 bits of entropy with a built-in checksum that catches transcription errors. In v3 vaults the recovery seed also wraps the Secret Key itself, so the mnemonic alone can reconstruct everything. If every recovery path is lost, the vault is unrecoverable — there is no operator backdoor.

08

Breach analysis: what a relay compromise exposes

The honest answer to "what if your servers are breached?" — broken down by what the attacker actually obtains.

Exposed

Encrypted vault payloads (opaque ciphertext + nonces), account email and identifiers, subscription/billing state, device labels, and sync routing metadata. The server-side Argon2id digest of auth_hash.

Not exposed — and not derivable

Master passwords, K_master, K_vault, the DEK, item keys, Secret Keys, recovery phrases, and any plaintext item. None of these exist on the relay in any form.

To turn a relay breach into readable secrets, an attacker would need to additionally defeat Argon2id over each user's unknown master key and, for v3/v4 vaults, supply the device-stored Secret Key. The breach yields ciphertext; it does not yield the keys. That is the entire point of the architecture.

09

Cryptographic primitives

Purpose Primitive
Password stretchingArgon2id v1.3 · 64 MiB / 3 / 4 (login)
Local-vault stretchingArgon2id v1.3 · 128 MiB / 3 / 4 (default)
Key derivation / splitHKDF-SHA256, per-purpose labels
Item & wrap encryptionXChaCha20-Poly1305 · 256-bit key · 192-bit nonce
Verification MACHMAC-SHA256
Recovery encodingBIP-39 · 24 words · 256-bit entropy
Secret KeyCrockford base32 · 34 chars · ~165-bit
RandomnessOS CSPRNG (OsRng)
Memory handlingZeroize on drop · constant-time compare

All primitives are implemented in the Rust vardra-core crate and shared across every client, so each platform enforces the identical cryptographic contract.