March 3, 2026. I sat in my apartment in Kharkiv with a cup of cold tea. LockMargin had encryption. But how did I know it was actually secure?
I'm a solo developer. I don't have a security team. I don't have a QA department. I don't even have a colleague to review my code. Just me, a laptop, and a growing feeling that I was in over my head.
Then Maya sent her audit report.
The Problem: Why Encryption Is Harder Than It Looks
Let me be honest. I thought encryption was simple. Take a field. Run it through AES-256-GCM. Store it. Done.
Maya broke it in 20 minutes. Not the encryption itself — the assumptions around it.
"You're using PBKDF2," she wrote. "It's 2012. RFC 9106 says Argon2id for interactive use. And your key derivation parameters?" She attached a calculator showing it would take her gaming rig 3 hours to brute-force a 6-character password.
I stared at the screen. "I thought 100,000 iterations was secure."
"It was. In 2012."
That's when I realized: cryptography isn't a library you add. It's a discipline you practice.
The Solution: How We Built a Security Team Without Hiring One
Here's what we built.
1. Property-Based Testing (1,000 Iterations in CI, 10,000 Before Release)
We use proptest to generate thousands of random inputs and verify our encryption always behaves correctly.
Properties we test:
- Encrypt then decrypt returns original plaintext
- Different plaintexts produce different ciphertexts (randomized)
- Tampered ciphertext fails decryption
- Nonce uniqueness is maintained
Every commit runs 1,000 iterations. Before release, we run 10,000. The full suite takes 4 minutes on my laptop. It's caught 3 bugs in 6 months.
2. Fuzzing (48-Hour Runs Before Every Release)
AFL runs against our encryption APIs for 48 hours straight. We feed it random binary blobs, corrupted databases, malformed keys. It finds edge cases we never considered.
We run it in a dedicated VM with AddressSanitizer enabled. Crashes go directly into a bug report with full backtrace.
3. Static Analysis (Cargo Audit + Custom Linting)
Cargo audit catches vulnerable dependencies. We run it in CI on every PR.
We also have custom lints for:
- Hardcoded secrets (none allowed)
- Weak RNG usage (only
getrandomallowed) - Key material in logs (immediate fail)
Key derivation: Argon2id with 64 MB memory, 3 iterations, 4 parallelism (matches RFC 9106 recommendations). No exceptions.
We follow RFC 9106 exactly. These parameters make cracking expensive while keeping unlock times under 2 seconds on modern hardware.
Key storage: OS keychain only (macOS Keychain, Windows DPAPI, Linux libsecret). Never disk. Never environment variables.
Your key lives in the OS-provided secure storage. We use the keyring crate, which handles the platform differences automatically. No disk storage. No env vars. No config files.
Nonce generation: Cryptographically secure random number generator (OS-provided via getrandom on Linux, SecRandomCopyBytes on macOS, BCryptGenRandom on Windows), 96 bits, checked for uniqueness across a 1-million-entry test.
Rust's getrandom crate provides the CSPRNG. We never generate nonces from timestamps or counters. Each is 96 bits (as required by GCM) and we verify uniqueness across a 1-million-entry test before every release.
4. The "Evil Maid" Checklist
This is what I gave Vlad. Every release, we run through this list:
- Can an attacker extract keys from RAM on sleep mode?
- Are plaintext sensitive fields visible in memory dumps?
- Can clipboard contents be recovered after copy?
- Does the app cache decrypted data in swap?
- Are temporary files cleaned up?
- Can crash dumps contain sensitive data?
- Is the master password stored in process memory safely?
- Are decryption failures logged securely?
- Does file locking prevent concurrent access issues?
- Can backup files be recovered without the master password?
- Are thumbnails or previews cached encrypted?
- Does the print spooler contain plaintext?
- Can screenshot tools capture decrypted views?
- Is the database header readable without auth?
- Does SQLite leave traces in journal files?
- Are sensitive strings zeroed in memory after use?
- Can the Windows Event Log leak information?
- Is there a secure wipe for deleted records?
- Does app uninstall remove all key material?
- Are there side-channel timing attacks possible?
Each item gets checked manually. Each check gets documented. This is our equivalent of a security team.
5. Peer Review for Crypto PRs
I review all PRs. But crypto changes require a second pair of eyes. We use a rotating pool of 3 developers who have signed off on our security review process.
No crypto PR merges without a second approval. Ever.
The Lesson: Three Questions to Ask About Any Encryption Tool
Before you trust any tool with sensitive data, ask:
- How do you derive keys? If they say "PBKDF2" without dates, run. Use Argon2id for interactive use.
- Where do you store keys? If they say "config file" or hesitate, that's a no. OS keychain only.
- What testing do you run? If they can't name specific methods, be careful. Property-based testing and fuzzing are minimum standards.
FAQ
Frequently Asked Questions
What testing methods do we use?
We run 1,000 iterations of property-based tests in CI, 10,000 before every release. We run 48-hour fuzzing sessions before releases. We use Cargo Audit and custom linting for static analysis. We have a 20-point evil maid checklist. And we require peer review for every crypto-related PR.
Why Argon2id with 64 MB memory?
Argon2id with 64 MB memory, 3 iterations, and 4 parallelism matches RFC 9106 recommendations for interactive use. This makes brute-forcing computationally expensive while keeping the user experience reasonable.
Where are encryption keys stored?
Only in OS keychains: macOS Keychain, Windows DPAPI, Linux libsecret. Never on disk. Never in environment variables. Never in the database. The OS handles secure storage behind your system login.
How do you verify nonce uniqueness?
We use cryptographically secure RNG: getrandom on Linux, SecRandomCopyBytes on macOS, BCryptGenRandom on Windows. Each nonce is 96 bits and we verify uniqueness across a 1-million-entry test dataset.
How can I verify your claims?
All cryptography in LockMargin is open-source. You can verify it yourself. Our codebase uses the ring crate for crypto primitives, and our encryption implementation is visible in the repository. Independent security reviews are welcome.
Bug Bounty Terms
Reward: $500 per critical vulnerability (determined at our discretion)
Eligibility: Must be the first to report the vulnerability
Exclusions: Social engineering, physical attacks, denial-of-service
Disclosure: We ask for 90 days to fix before public disclosure
Vlad Shiyan
Founder & Developer, LockMargin
I rewrote our key derivation the next day. Argon2id with 64 MB memory, 3 iterations, 4 parallelism. I migrated all existing databases. I added a migration guide.
I also realized I couldn't do this alone. I needed a systematic approach to security testing.