Hide an encrypted message inside an ordinary image. Vault encrypts your text with a password and buries it in the pixels of a PNG — the output looks identical to the original. Anyone can see the picture; only someone with the password can recover what's inside.
It combines two layers:
- Encryption — AES‑256‑GCM, with the key derived from your password (PBKDF2‑HMAC‑SHA256, 600,000 iterations). Without the password the data is unreadable and tamper‑evident.
- Steganography — the encrypted bytes are written into the least‑significant bits of the image's color channels, so the picture looks unchanged to the eye.
The result: a secret that's both unreadable and invisible, carried in a file nobody would look at twice.
Hide a message in cat.png, producing cat_secret.png:
./VaultBuilder "meet at the pier, midnight" cat.png cat_secret.pngVault Builder
Password:
Confirm password:
Success!
New image now at: cat_secret.png
Send cat_secret.png to someone (it looks like an ordinary cat photo). They recover the message with the same password:
./VaultRunner cat_secret.pngVault Runner
Password:
Decrypted Data: meet at the pier, midnight
Wrong password? The decryption's authentication check fails and nothing leaks:
Caught Exception: Decryption failed: authentication tag mismatch
Vault needs a C++20 compiler, CMake ≥ 3.20, and OpenSSL. Dependencies are declared in vcpkg.json.
With a system OpenSSL (simplest on Linux):
sudo apt install libssl-devcmake -S . -B buildcmake --build buildWith vcpkg
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmakecmake --build buildThis produces two executables in build/: VaultBuilder and VaultRunner.
Hide a message — VaultBuilder:
./VaultBuilder <message> <input-image> <output-image>Reads <input-image>, prompts for a password (twice, to confirm), encrypts the message, and writes the result to <output-image>.
Reveal a message — VaultRunner:
./VaultRunner <image>Prompts for the password and prints the hidden message. If the password is wrong or the image carries no Vault payload, it fails with an authentication error rather than printing garbage.
Image format. Input can be any format stb_image reads (PNG, JPG, BMP, …), but output is always PNG — the format must be lossless, or the hidden bits would be destroyed by re‑compression. The alpha channel is preserved exactly; data is only ever written to the R/G/B channels, so transparent images stay transparent.
Capacity. Three bits per pixel (one per color channel), minus a 48‑byte header. Roughly: a 1000×1000 image holds up to ~366 KB.
Cryptography.
- Key derivation: PBKDF2‑HMAC‑SHA256, 600,000 iterations, 32‑byte key, random 16‑byte salt per message.
- Cipher: AES‑256‑GCM — authenticated encryption, so a wrong password or any tampering is detected and rejected.
- Keep the output lossless. If a messaging app or website re‑encodes the PNG to JPEG (or otherwise recompresses it), the hidden data is lost. Send the file as‑is — as a file/attachment, not an inline photo that gets recompressed.
- Not resistant to steganalysis. Sequential LSB embedding hides data from the eye, not from statistical detectors. Vault protects confidentiality (via encryption); it does not guarantee the presence of a message is undetectable.
- Password strength is on you. The encryption is only as strong as the password. PBKDF2 slows down guessing, but a weak password is still a weak password.
Vault is a personal project for learning and legitimate private communication. Use it lawfully and only on images and messages you have the right to use.