Cryptography
Every value here is chosen from current standards and documented so it can be reviewed. We do not invent cryptographic primitives.
| Symmetric cipher | AES-256-GCM (authenticated encryption) via Web Crypto |
|---|---|
| Password key derivation | PBKDF2-HMAC-SHA-256, 600,000 iterations (OWASP-recommended) |
| KDF salt | 16 random bytes, unique per file |
| File key | 256-bit random key, wrapped by the password key (and optional recovery key) |
| Recovery key | 160-bit random, Crockford base32 with a 2-character checksum |
| Streaming | STREAM construction; 1 MiB chunks; per-chunk nonce = 7 random bytes + 32-bit counter + final flag |
| Header authentication | SHA-256 of the exact header bytes, used as GCM additional data on every chunk |
| Randomness | crypto.getRandomValues (the browser CSPRNG) |
| Compression (optional) | deflate-raw before encryption |
Key derivation
Your password is normalized (Unicode NFKC) and stretched with PBKDF2-HMAC-SHA-256 using 600,000 iterations and a unique 16-byte random salt, producing a 256-bit key-encryption key. 600,000 iterations is the current recommendation in the OWASP Password Storage Cheat Sheet for PBKDF2-HMAC-SHA-256.
PBKDF2 is well supported natively in browsers but is not memory-hard, so a determined attacker with GPUs can still test weak passwords quickly. This is why we default the passphrase generator to six random words (about 77 bits of entropy) and show honest strength estimates. We are evaluating Argon2id (memory-hard) as a second KDF option; the container header records which KDF and parameters were used, so files can migrate.
Encryption and streaming
A fresh 256-bit file key is generated per file and used with AES-256-GCM. Large files are processed in 1 MiB chunks using the STREAM construction (the same shape used by age and Google Tink): each chunk gets a unique nonce built from a random 7-byte prefix, a 32-bit counter, and a final-chunk flag. Because the counter and final flag are part of the nonce, reordering, duplicating, dropping, or truncating chunks all cause authentication to fail.
The file header (cipher, KDF parameters, salts, wrapped keys, chunk size) is bound to the ciphertext: we compute SHA-256 over the exact header bytes and pass it as GCM additional authenticated data for every chunk. Any modification to the header makes decryption fail. Authentication failures are reported generically — we do not distinguish “wrong password” from “tampered file,” because that distinction can leak information and isn't reliably knowable.
Recovery keys
If you opt in, we generate a 160-bit recovery key, display it once, and use it to wrap a second copy of the file key. It is shown in Crockford base32 with a checksum to catch typos. We never store or transmit it. Either the password or the recovery key can open the file; losing both means the data is unrecoverable.
What this does not protect
Encryption protects data at rest. It cannot protect a device that is already compromised by malware, a weak or reused password, or content you paste into another site. See the threat model for the full picture, and the container format for byte-level details.
Last reviewed: 2026-07-14.