Skip to content Skip to content

Shadow Vault

Deniable Encryption · Argon2id + ChaCha20-Poly1305

Hide a real message and a plausible decoy at passphrase-derived offsets inside one fixed-size container, then decrypt either slot by supplying its passphrase.

REAL MESSAGE

 

Bits of entropy estimate the number of guesses to break this passphrase: each bit doubles the work, so 80 bits is a million times harder than 60. Deniability holds only while both passphrases are too costly to guess.

0 bytes

DECOY MESSAGE

 

The decoy must be just as hard to guess — if an attacker can brute-force this passphrase, they can also test whether it hides the whole vault.

0 bytes

CONTAINER SIZE

The Deniability Problem

Standard encryption proves a message exists. Under coercion, possessing an encrypted file is itself incriminating. An adversary who compels you to surrender your passphrase gains the message — and knows there's nothing else to find.

Deniable encryption solves this: one container holds two independent encrypted messages at different offsets. You reveal the decoy passphrase. The adversary gets a plausible message. The real message stays unreadable and unprovable — the rest of the container is indistinguishable from random padding, so nothing in the bytes marks a second message as present. One honest caveat: Shadow Vault always writes exactly two slots and requires both passphrases, so an adversary who recognises the format knows a second slot exists. They still cannot read it, or prove it holds anything rather than padding, without the second passphrase. Against a format-aware adversary the deniability is about contents, not existence.

The Two-Passphrase Model

Each passphrase independently derives an encryption key, a nonce, and a slot offset via Argon2id. The real passphrase decrypts the real message. The decoy passphrase decrypts the decoy. A format-aware adversary knows the tool writes two slots, but without both passphrases cannot identify their contents or distinguish the unopened ciphertext from random padding.

Why Argon2id Is Load-Bearing

Argon2id is not incidental — it is the security foundation. Without memory-hard key derivation, the deniability model collapses: an attacker who can brute-force both passphrases simultaneously recovers both offsets and both messages.

Argon2id makes the first dictionary construction expensive, but this demo has no per-container salt: a table built for one parameter set can be reused across every Shadow Vault container at those settings. High-entropy, independently generated passphrases—not a claimed per-target work factor—are the defense.

Container Format

The container is one of four recognized fixed sizes and contains two encrypted slots at derived offsets in CSPRNG padding. It has no plaintext header or magic bytes. Each encrypted slot's plaintext includes a four-byte length field, hidden inside the authenticated ciphertext. Its bytes should look random, while the recognized size and known format still fingerprint it as a Shadow Vault container.

Passphrase → Argon2id(64B) → key[0..31] + nonce[32..43] + offset[44..63] → ChaCha20-Poly1305 AEAD

What This Cannot Protect Against

  • Implementation bugs in this demonstration code
  • Keyloggers or compromised devices capturing passphrases
  • Coercion with violence (rubber-hose cryptanalysis)
  • Metadata outside the container (filenames, timestamps, access logs)
  • Browser memory not being securely wiped (passphrases enter as JS strings)
  • Weak passphrases — deniability collapses if either passphrase can be brute-forced

Honest Limitations

Shadow Vault is a demonstration of deniable encryption concepts, not production-grade deniable storage. Browser-based cryptography is inherently weaker than native tools. Strong, unique passphrases are required for both messages — deniability depends on the attacker being unable to brute-force either one. For real-world deniable encryption, use VeraCrypt with hidden volumes.

Related Projects