How QRE Privacy Toolkit Protects Your Vault: Authentication System Deep Dive
Every password manager, encrypted vault, and private notes app makes the same implicit promise: even if someone steals your device, they cannot read your data. This post explains exactly how QRE Privacy Toolkit keeps that promise — not in vague marketing language, but in specific, auditable technical detail.
We will walk through every phase of the authentication lifecycle: first-time vault creation, daily unlock, session management, password recovery, and password changes. Along the way we will explain the specific cryptographic techniques involved, why each design decision was made, and what it means for your security in practical terms. No prior cryptography knowledge is assumed.
1. The Core Philosophy: Local-First, Zero-Knowledge
Before any code runs, QRE Privacy Toolkit makes a foundational architectural decision that shapes every security property that follows: all cryptographic operations happen entirely on your device. There is no server, no cloud sync, no authentication API, and no account to create. QRE has zero knowledge of your password, your master key, or the contents of your vault — because that information never leaves your machine.
This is a meaningful trade-off. It means there is no “forgot password” button that emails you a reset link, because there is nothing to reset on a remote server. Recovery is handled through a locally generated recovery code that you are responsible for storing safely. The benefit of this trade-off is an extremely small attack surface: there is no server to breach, no database of password hashes to steal, and no network traffic to intercept.
Zero-Knowledge Guarantee: QRE has no copy of your password, your master key, or your files. If you forget your password and lose your recovery code, your data cannot be recovered — by you or by anyone else. This is a feature, not a limitation.
2. The Key Hierarchy: How Your Password Protects Your Files
The most common misconception about encrypted storage is that files are encrypted directly with your password. If that were true, changing your password would require re-encrypting every single file you have ever stored — potentially terabytes of data. QRE uses a more sophisticated design called envelope encryption, which separates the concept of authentication from the concept of encryption.
The Master Key
When you create a new vault, QRE generates a single 256-bit Master Key using your operating system’s cryptographically secure random number generator (CSPRNG). This key is the actual cryptographic secret that encrypts and decrypts all of your files, passwords, notes, and bookmarks. It is completely random and has no relationship to your password whatsoever.
This Master Key lives in RAM only while you are logged in. The
moment you lock the vault or close the app, it is actively
overwritten with zeros in memory before being released — a
process called
zeroization, implemented via Rust’s
ZeroizeOnDrop trait. This means it cannot be recovered
from a memory dump, swap file, or hibernation image after logout.
The Keychain File
The Master Key itself is never written to disk in plaintext.
Instead, it is stored in a file called
keychain.json inside your OS application data directory
— but stored in encrypted form. The keychain file contains two
independent encrypted copies of the Master Key, called slots:
- Slot 1 — Password Slot: The Master Key encrypted with a key derived from your master password.
- Slot 2 — Recovery Slot: The same Master Key encrypted with a key derived from your recovery code.
Neither slot contains your password or your recovery code. Both
slots contain only the Master Key in AES-256-GCM encrypted form. An
attacker who obtains keychain.json gains nothing
without the password or the recovery code — the Master Key
remains mathematically inaccessible.
This design means a password change is a small, fast operation: QRE decrypts the Master Key using your current password, re-encrypts it with your new password, and writes the updated Slot 1 back to disk. The actual files you have encrypted are completely untouched, regardless of how many times you change your password.
3. First-Time Setup: Creating Your Vault
The setup flow is triggered the first time QRE launches and finds no
keychain.json in the app data directory. Here is
precisely what happens, step by step:
Step 1 — Password strength is validated on the frontend
Before any cryptographic operation begins, the TypeScript frontend evaluates your chosen password using a strength scoring algorithm that checks length, character variety, and common patterns. A score below 3 out of 4 is rejected immediately with a clear message. This ensures that Argon2id (introduced in Step 3) is working from a reasonably strong input.
Step 2 — A 256-bit Master Key is generated
Rust code calls OsRng.try_fill_bytes() — the
operating system’s CSPRNG — to fill a 32-byte array with
cryptographically random data. This is the Master Key. It is
immediately wrapped in a ZeroizeOnDrop struct so it
cannot outlive its intended scope in memory.
Step 3 — A Key Encryption Key (KEK) is derived from your password using Argon2id
Your password is intentionally weak compared to a random 256-bit key — it is short, human-memorable, and has limited entropy. Argon2id bridges this gap by being deliberately slow and memory-intensive, making brute-force attacks computationally expensive.
QRE’s parameters: 64 MB of RAM, 3 iterations, 4 parallel threads, producing a 256-bit output. A unique random salt is generated for this slot and stored in the keychain file. The salt ensures two vaults with the same password produce completely different KEKs.
Step 4 — The Master Key is encrypted with the KEK using AES-256-GCM
AES-256-GCM (Galois/Counter Mode) is an authenticated encryption algorithm. It provides both confidentiality (the data cannot be read without the key) and integrity (any tampering with the ciphertext is detectable). A fresh 96-bit random nonce is generated for this operation and stored alongside the ciphertext in Slot 1. The KEK itself is immediately zeroized after this step — it exists only for the duration of the encryption operation.
Step 5 — A 128-bit recovery code is generated
A random recovery code is generated using the format
QRE-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX, where each
group is a 32-bit cryptographically random integer formatted as
uppercase hexadecimal. Four groups × 32 bits = 128 bits of
entropy, meeting NIST SP 800-63B guidance for long-lived recovery
secrets. A second, independent KEK is derived from this recovery
code using Argon2id (with its own random salt), and the Master Key
is encrypted again to produce Slot 2.
Step 6 — The keychain file is written to disk atomically
The completed keychain structure — containing the vault ID,
KDF parameters, Slot 1, and Slot 2 — is serialized to JSON and
written using an atomic write pattern. The data is first written to
a temporary .tmp file. Once the write completes
successfully, the file is renamed over the final destination. On all
major operating systems, rename() within the same
filesystem is a single atomic operation. The keychain can never be
observed in a partial or empty state, even if the process is killed
mid-write.
Step 7 — The recovery code is displayed exactly once
The recovery code is shown to you on screen with a copy-to-clipboard button. The clipboard is automatically cleared after 30 seconds. Once you click “I have saved it”, the recovery code string is cleared from React state. It is never shown again. QRE has no record of it.
Step 8 — The Master Key is loaded into the session
The decrypted Master Key is stored in a
Mutex<Option<MasterKey>> inside the Tauri
application state. The vault is now unlocked and all features are
accessible. No further authentication is required until the session
is locked.
What is stored on disk after setup? The
keychain.json file contains: a vault UUID, the Argon2id
KDF parameters (memory, iterations, parallelism), two random salts,
two random nonces, and two encrypted blobs. It contains no plaintext
passwords, no recovery codes, and no Master Key. The file is safe to
back up to cloud storage — it is useless without your password
or recovery code.
4. Daily Use: Unlocking the Vault
After the initial setup, every subsequent launch finds
keychain.json on disk. The app presents the lock screen
and waits for your master password. Here is exactly what happens
when you unlock:
The Unlock Process
- You enter your master password into the password field and press Unlock.
-
The TypeScript frontend calls
invoke('login', { password })— Tauri’s IPC bridge — which passes the password string to the Rust backend over a secure local channel. -
Before touching the keychain, the backend checks an in-memory rate
limiter. If five or more consecutive failed attempts have
occurred, an exponential backoff penalty is enforced: 5 failures
→ 30 seconds, 6 → 60 seconds, 7 → 120 seconds, up
to a cap of 8 minutes at 9+ failures. This timer is stored in a
thread-safe
AtomicU32and resets to zero on a successful login. -
The
keychain.jsonfile is read from disk and deserialized. The stored Argon2id parameters (memory, iterations, parallelism, and the Slot 1 salt) are extracted. - Argon2id is run with your password and the stored salt, reproducing the original KEK. Because the salt is the same as when the vault was created, the output is deterministic — the same password will always produce the same KEK for this vault.
- The KEK is used to attempt AES-256-GCM decryption of the Slot 1 ciphertext. If the password was correct, decryption succeeds and the 32-byte Master Key is recovered. If the password was wrong, the AES-GCM authentication tag will not match — decryption fails and an “Incorrect Password” error is returned.
- The password string and the KEK are immediately cleared from memory. The Master Key is stored in the session state. The frontend transitions to the dashboard.
Why Wrong Passwords Fail Reliably
A wrong password does not produce an error during the Argon2id step. Argon2id will always produce a 256-bit output, regardless of whether the input password was correct. The error is detected only when AES-GCM attempts to verify the authentication tag of the encrypted blob. This means the unlock function has no mechanism to confirm whether a guess was “close” or not — any wrong password produces a uniformly random-looking KEK that will fail authentication, with no information leakage about the correct password.
Rate Limiting Note: The in-memory rate limiter resets when the application restarts. This is deliberate — a persistent lock-out counter could permanently deny access to a legitimate user. The primary brute-force protection comes from Argon2id itself: each guess costs real CPU time and memory on the attacker’s hardware, regardless of the rate limiter.
5. Session Management: Staying Secure While Logged In
Unlocking the vault is only the beginning of the security story. Once you are logged in, QRE must ensure the vault cannot be accessed by someone who walks up to your unlocked computer.
Automatic Session Lock
QRE monitors four user activity signals: mouse movement, keyboard input, mouse clicks, and touch events. A timer is reset to zero each time any of these events fires. If 14 minutes pass with no activity, QRE shows a countdown warning modal with a 60-second timer. If you interact with the app before the countdown ends, the session continues. If you do not, the vault locks automatically.
What Locking Actually Does
Locking is not just a UI state change. When the vault locks, the
Rust backend’s logout command sets the Master Key
in the session state to None. Because the Master Key is
stored inside a ZeroizeOnDrop wrapper, the 32 bytes are
actively overwritten with zeros when the Option is set
to None. The key is gone from memory. An attacker who
gains access to the process’s memory after logout cannot
reconstruct the Master Key from RAM.
The Session State Architecture
The Master Key is held inside a Tauri application state object with the following structure:
pub struct SessionState {
pub master_key: Arc<Mutex<Option<MasterKey>>>,
}
Each component serves a security purpose:
-
Arc(Atomically Reference Counted): Allows the state to be shared safely across multiple threads without copying. -
Mutex: Ensures only one thread can read or write the Master Key at a time, preventing data races. -
Option: The key isSome(key)when unlocked andNonewhen locked, making the locked/unlocked distinction impossible to bypass — there is no value to read when the vault is locked.
If the Mutex ever becomes poisoned (a thread panicked
while holding the lock), all commands return a “Session state
is corrupted, please re-login” error rather than silently
proceeding with potentially inconsistent data.
6. Encryption in Practice: What AES-256-GCM Actually Provides
Confidentiality
AES with a 256-bit key is widely considered computationally unbreakable. The best known attack against AES-256 requires 2254.4 operations — a number so astronomically large that no computer conceivable under current physics could perform it in any meaningful timeframe.
Authenticity and Integrity
The GCM (Galois/Counter Mode) component adds authenticated encryption. Along with the ciphertext, AES-GCM produces a 128-bit authentication tag. When decrypting, the tag is verified before the plaintext is released. If any byte of the ciphertext has been modified — whether by an attacker or by disk corruption — decryption fails entirely and no plaintext is returned. QRE’s encrypted files cannot be silently corrupted. Any tampering is detected.
Nonces and Why They Must Be Random
AES-GCM requires a 96-bit nonce (number used once). Using the same nonce twice with the same key is catastrophic — it allows an attacker to recover the keystream and decrypt messages encrypted with that nonce. QRE generates a fresh cryptographically random nonce for every single encryption operation, stored alongside the ciphertext. Fresh random nonces make nonce reuse statistically impossible.
7. Argon2id: Why Password Hashing Is Hard
The most common attack against password-based encryption is brute force: try every possible password until one works. Modern GPUs can check billions of SHA-256 hashes per second, making simple hashing completely inadequate for protecting user-chosen passwords.
Argon2id is a memory-hard key derivation function (KDF). Memory-hard means the algorithm intentionally requires a large amount of RAM to compute. GPUs have less RAM per compute unit than CPUs, making parallel brute-force attacks on Argon2id far less effective than they would be against simple hash functions.
QRE’s Parameters
Memory: 64 MB
Iterations: 3 passes
Parallelism: 4 threads
Output: 256 bits
These are the OWASP-recommended settings for desktop applications. At these parameters, a single password guess requires 64 MB of RAM and multiple CPU passes — a deliberate performance cost that is barely noticeable to a legitimate user performing a single unlock, but completely crippling to an attacker trying millions of guesses.
Because the KDF parameters are stored inside
keychain.json, they can be upgraded in future versions
of QRE. Existing vaults continue to use whatever parameters they
were created with. New vaults use the latest defaults.
The Role of Salts
Each slot in the keychain has its own independent, randomly
generated salt. The salt is not secret — it is stored in
plaintext in
keychain.json — but it ensures each Argon2id
computation is unique, even if two users have the same password.
Without salts, an attacker could pre-compute a table of common
passwords. With unique random salts, every brute-force attempt must
start fresh for each specific vault.
8. Password Recovery: The Recovery Code Flow
If you forget your master password, your only path to recovery is the recovery code shown to you during setup. This section explains how that flow works and why it does not compromise security.
What the Recovery Code Is
The recovery code is a randomly generated secret in the format
QRE-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX, where each X
is a hexadecimal character. Four groups of eight characters encode
128 bits of cryptographic entropy — enough that guessing it by
brute force is computationally infeasible even without Argon2id. The
recovery code is also processed through the same Argon2id pipeline
as your password, so it gets the same memory-hard brute-force
protection on top of its own high entropy.
The Recovery Flow
- You enter your recovery code and a new master password on the recovery screen.
- The rate limiter for recovery attempts is checked. After five failed attempts, the same exponential backoff that protects the login screen is applied.
- Argon2id derives a KEK from the recovery code using Slot 2’s stored salt.
- AES-GCM decrypts Slot 2 to recover the Master Key. If the recovery code is wrong, this fails and the error counter increments.
- With the Master Key now available, Argon2id derives a new KEK from your new password with a freshly generated salt.
- The Master Key is re-encrypted with this new KEK and written back to disk as Slot 1 — atomically.
- The session is unlocked with the recovered Master Key. The vault is accessible again.
The actual encrypted files are completely untouched throughout this
process. The Master Key is the same one that was created during
setup. Only the outer envelope — the encrypted wrapper in
keychain.json — changes. Recovery is therefore
fast, regardless of how many files are in the vault.
Recovery Code Security: The recovery code is shown exactly once. QRE never stores it anywhere — not on disk, not on a server, not in application memory after you acknowledge it. If you lose the recovery code and forget your password, the vault contents are permanently inaccessible. Store the recovery code in a physically secure location, separate from the device running QRE.
9. Changing Your Password
Current Password Verification
Before any change is made, you must provide your current password. The backend independently verifies this by running the full Argon2id + AES-GCM unlock process against the existing Slot 1 — the same code path used during normal login. If the current password is wrong, the change is rejected and no modification is made to the keychain file. This prevents a scenario where an attacker briefly accesses your unlocked session and changes the password to one they control.
Strength and Uniqueness Validation
The new password is evaluated by the same strength scoring used during initial setup (score of 3 or higher required). An additional check enforces that the new password must be different from the current one. Both checks happen on the frontend before the backend is contacted, providing immediate feedback.
The Key Operation
With the current password verified and the new password validated, a new random salt is generated, a new KEK is derived from the new password via Argon2id, and the Master Key (already in memory from the active session) is re-encrypted with the new KEK. The updated Slot 1 is written atomically to disk. The Master Key does not change. Slot 2 (the recovery code slot) is not modified. All encrypted files remain untouched.
10. Atomic Writes: Protecting Against Data Loss
Any time a security-critical file is modified on disk, there is a window of vulnerability: what happens if the process is killed, the operating system crashes, or the disk runs out of space mid-write?
A naive implementation opens the file for writing (which immediately truncates it to zero bytes) and then writes the new data. If anything goes wrong, the file is left empty or partially written — for a keychain file, this means permanent, unrecoverable data loss. QRE uses an atomic write pattern for every keychain mutation:
-
New keychain data is serialized and written to a temporary file
keychain.tmpin the same directory as the real keychain. -
Only once the write completes successfully is the
rename()operation performed. -
On all major operating systems,
rename()within the same filesystem is atomic. The file is either the old version or the new version — it is never empty or partial.
This pattern protects you during password changes, recovery flows, recovery code resets, and initial vault creation. In every case, if the operation is interrupted at any point before the rename completes, the original keychain is completely intact.
11. Backups: The Keychain File Is Safe to Copy
Because the keychain file is a self-contained encrypted blob, it can be backed up anywhere — a USB drive, cloud storage, email to yourself — without compromising security. Without your password or recovery code, the file is cryptographically useless to anyone who obtains it.
QRE tracks whether you have performed a backup using a sentinel file in the app data directory. The first time you encrypt a file after setting up the vault, a reminder is shown encouraging you to export the keychain. The reminder does not reappear after you complete a backup.
Backup Best Practice: Store your keychain backup and your recovery code in separate physical locations. A backup is only useful if you can still decrypt it — which requires your password or your recovery code. If both are in the same place, losing that location means losing everything.
12. The Complete Architecture at a Glance
Every layer of the authentication system and its security purpose:
- File encryption — AES-256-GCM: Confidentiality and integrity for all vault data.
- Key derivation — Argon2id: Transforms weak passwords into strong 256-bit keys; resists GPU brute-force.
-
Master Key storage —
ZeroizeOnDrop: Overwrites key bytes in RAM on logout; prevents memory forensics. -
KDF salts — CSPRNG (
OsRng): Unique per slot; prevents rainbow tables and cross-vault attacks. -
Encryption nonces — CSPRNG (
OsRng): Unique per operation; prevents nonce-reuse attacks. -
Keychain writes — Atomic
rename(): Prevents data loss if the process is killed mid-write. - Login protection — Exponential backoff: Raises the cost of online brute-force against the login screen.
- Recovery protection — Exponential backoff: Same protection applied to the recovery code path.
-
Session key —
Mutex<Option<T>>: Enforces locked/unlocked state; fails safely on mutex poison. - Password change guard — Current password check: Prevents session-hijack password escalation.
- Recovery code — 128-bit CSPRNG entropy: Meets NIST SP 800-63B guidance for long-lived recovery secrets.
13. Threat Model: What QRE Protects Against
Good security engineering requires stating explicitly what a system is designed to protect against — and what it is not.
Protected Against
-
Stolen device (locked screen): The Master Key is
in RAM only when unlocked. A locked or powered-off device exposes
only
keychain.json, which is computationally protected by Argon2id. -
Stolen
keychain.json: The file contains only ciphertext. Without the password or recovery code, it is useless. Argon2id makes offline brute-force expensive. - Brute-force against the unlock screen: Exponential backoff limits the practical guess rate in-app. Argon2id limits the rate even if the attacker extracts the keychain file.
- Session hijacking (password change): Changing the password requires proving knowledge of the current password, even from an active session.
-
Disk corruption during writes: Atomic
rename()guarantees the keychain is never left in a partial state. -
Memory forensics after logout:
ZeroizeOnDropoverwrites key bytes before releasing memory.
Not Protected Against
- Compromised OS or process: If an attacker has code execution in the same process or root access while the vault is unlocked, the Master Key in RAM is accessible. No application-layer security can defend against this.
- Keyloggers: A keylogger recording keystrokes at the OS level captures the master password as it is typed.
- Physical access to an unlocked session: The auto-lock timer (14 minutes) reduces this window but does not eliminate it. Use OS-level screen lock for tighter protection.
- Forgotten password and lost recovery code: This is unrecoverable by design. There is no server-side fallback.
Conclusion
QRE Privacy Toolkit’s authentication system is built on a small set of well-understood, widely audited cryptographic primitives: AES-256-GCM for encryption, Argon2id for key derivation, and OS-provided CSPRNGs for randomness. No novel cryptography is involved — the design deliberately avoids it.
The architectural decisions — envelope encryption, two independent keychain slots, atomic file writes, in-memory zeroization, and the zero-knowledge stance toward your password — all serve the same goal: your data should be accessible only to you, on your device, with your password. That guarantee should hold even if the keychain file is stolen, even if the app is crash-killed mid-write, and even if someone briefly sits down at your unlocked computer.
The full source code is available in the QRE Privacy Toolkit repository. The cryptographic code is fuzz-tested, and the authentication logic has been independently reviewed. We encourage technically inclined readers to read the code and hold us to the standards described here.