The security model.
What VaultPony protects, how it protects it, and just as importantly what it does not protect. Security you cannot understand is not security.
What is being protected
Three things, in order of value. The contents of a vault, which is the whole point. The secrets in flight: your password, your PIM, the derived header key, the master key, and any decrypted data sitting in memory. And third, the usage metadata: filenames, vault paths, and the fact that a particular file is a vault at all.
That third one is in scope on purpose. Most apps leak it by accident, and for anyone who cares about hidden volumes it is exactly the thing that matters.
No network attack surface
The Android manifest declares no INTERNET permission, and the Rust core links no networking crates, which cargo-deny enforces as a dependency policy rather than a good intention. There is no update check, no online anything. Zero network is a testable property here, not a promise: unzip the APK and read the manifest yourself.
The accepted consequence is that the app will never tell you an update exists. Updates come from whichever channel you chose, GitHub or F-Droid.
Secrets in memory
- Every key container in the Rust core is zeroized on drop. Passwords and keys have single owners and as few copies as the design allows.
- Locking a vault wipes keys through every layer via one code path. Auto-lock on timeout, lock on screen-off, and locking by hand all run that same path, so there is no second route that forgets something.
- Secrets crossing the boundary between Rust and Kotlin use callback-scoped buffers that are wiped after use.
- No password, filename, vault path, or volume metadata is ever written to a log, an error string, or crash output. Error types carry structure; the interface decides what to say.
Hidden volumes and deniability
A hidden volume is only worth having if its absence and its presence look identical. Four properties make that true, and each is enforced by a test rather than by care:
- The password picks the volume. Unlocking probes the primary slot, the hidden slot, and both embedded backups, and whichever the password validates is what opens. There is no "open the hidden one" switch to fumble or to catch in a screenshot.
- The work is constant either way. A wrong password walks the entire position list before failing, and the error it returns is the same one an ordinary vault with no hidden volume gives. A vault with a hidden volume and a vault without one are indistinguishable, by timing and by error message.
- Nothing leaks into support output. The diagnostic output built for pasting into a support thread collapses the header source to primary or backup. The word "hidden" never appears in it.
- Probing leaves no trace. Checking the hidden slot writes nothing, logs nothing, and leaves no history in the interface.
Once open, a hidden volume browses exactly like any other, and the app will not label it as hidden anywhere the system can see: not in Recents, not in a notification, not in the mount list a screenshot could capture.
Hidden-volume write protection
Writing to an outer volume can destroy the hidden volume living in its free space. Desktop VeraCrypt warns about this, and VaultPony handles it the same way. Unlock with both passwords and the outer password mounts the outer volume read-write, while the hidden password is used only to read the hidden header and learn which region to defend. No hidden keys are kept.
From then on, any outer write that would reach that region is refused, and the volume latches read-only on the first attempt. This is verified in both directions: with protection on, the overrunning write is blocked and the hidden tree survives byte for byte; with it off, the same write corrupts the hidden volume. The failure surfaces as its own distinct error, so the app can tell you a write was stopped to save the hidden volume, and that message only ever reaches someone who already supplied the hidden password.
The container parser
A vault is attacker-controlled input. Someone can hand you a malicious file and hope the parser does something interesting with it. The core is memory-safe Rust, unsafe is treated as a code-review event rather than a convenience, and both header parsing and filesystem metadata parsing are fuzz targets running in CI. A hostile container should produce a clean error and nothing else.
On the device
- FLAG_SECURE is on by default for the unlock and browser screens, so screenshots are blocked and app contents are hidden in Recents.
- Auto-lock fires when the app is backgrounded or the screen turns off.
- Biometric unlock is opt-in and off by default, and the app never offers it for a hidden volume, because a stored secret is a discoverable one.
- No-trace mode stops the app remembering which vaults exist or where they are.
- Remembering a PIM is opt-in per vault, because a stored PIM is weak evidence that a vault has a non-default configuration.
- Vault contents never enter system search indexes, notification previews, or thumbnail caches.
Trust boundaries
The app reaches your files through the Storage Access Framework and hands the core a single duplicated file descriptor. The core never sees a storage API; that descriptor is the only capability it has. Unlocked volumes are exposed to other apps through a DocumentsProvider, user-initiated only, and that provider exposes volumes, never secrets.
Out of scope, said plainly
Every one of these is a real limit, and pretending otherwise would be worse than useless:
- A compromised operating system. Malware with your session, root access, or a hostile keyboard can capture your password or read decrypted content. No app defends against the platform it runs on.
- Screen capture the OS permits. FLAG_SECURE reduces accidents. It does not stop a platform that decides to record.
- Forensic memory capture of an unlocked device, and cold-boot-class attacks. Locking promptly is the only mitigation an app can offer; the rest is physics.
- The strength of your password. VaultPony implements the format faithfully. It cannot rescue a weak password.
- VeraCrypt's format-level cryptography itself. We implement it; we do not claim to improve it.
- Anything outside the app process observing that a vault exists. VaultPony never phones home, but a cloud sync client, a backup service, or device management software can still see the file sitting there.
Open to inspection
The app and the core are open source under Apache-2.0. None of the above has to be taken on faith. Read the crypto path, read the parser, read the manifest, and check the behavior for yourself. That is the whole idea: encryption you can inspect instead of taking a developer's word for it.
Reporting a vulnerability
If you find a security issue, email NorseHorse@norsehor.se with details and, ideally, a way to reproduce it, using the subject SECURITY. Please give a reasonable window to ship a fix before disclosing publicly. This is a one-person project, so a clear report gets a faster fix.
VeraCrypt is a registered trademark of IDRIX. VaultPony is an independent project, not affiliated with or endorsed by IDRIX. The container format is implemented clean-room from published format documentation; no VeraCrypt or TrueCrypt source is used or linked.