VaultMemo

How AES-GCM and PBKDF2 protect your notes

6 min read, updated August 2026

A steel padlock on a chain securing a weathered green metal gate

An encrypted notebook is only as honest as its description of the cryptography. VaultMemo uses two well-studied primitives that every modern browser implements natively: PBKDF2 to stretch a human password into a 256-bit key, and AES in Galois/Counter Mode to encrypt and authenticate each note. Neither is exotic, and that is the point. This guide walks through what happens between typing your password and seeing a note on screen, and it is equally clear about the attacks that no cipher can stop.

From a password to a key

A password is a poor key. It is short, drawn from a tiny alphabet and chosen by a human. PBKDF2 (Password-Based Key Derivation Function 2, standardised in RFC 8018) fixes the shape of the problem: it feeds the password and a salt through HMAC-SHA-256 over and over, 600 000 times in VaultMemo, and outputs exactly the 256 bits AES needs. The iteration count follows the OWASP recommendation for PBKDF2-HMAC-SHA256, and it is what makes each password guess cost a measurable fraction of a second.

The salt is 16 random bytes generated when the vault is created and stored next to the ciphertext. It is not secret. Its job is to make your key unique even if someone else chose the same password, and to make precomputed tables useless: an attacker cannot hash a dictionary once and reuse the result against every vault. Derivation runs in the browser's native crypto code, off the main thread, so the page stays responsive while it happens.

Slowing guesses down is all a KDF can do. Six hundred thousand iterations turn a billion guesses into hours or days of GPU time instead of seconds, but a password from the top-thousand list still falls within the first minute. The derivation buys time; the password decides how much time is worth buying.

What AES-GCM actually does

AES-256 is a block cipher: it turns 16 bytes into 16 other bytes under a 256-bit key. Galois/Counter Mode is the recipe for using it on a whole note. Counter mode encrypts a running counter and XORs the output with the text, so any length works and blocks can be processed in parallel; the Galois part computes a 128-bit authentication tag over the ciphertext and appends it.

That tag is what makes GCM an authenticated cipher. On decryption the browser recomputes it, and if a single bit of the stored ciphertext, the IV or the tag has changed, decryption fails outright. VaultMemo treats that failure as a signal: a note that does not authenticate is reported and skipped, never shown as garbage or, worse, as subtly altered text.

Each note is a separate message. The title, body, tags and pin state are serialised together and encrypted as one unit, so the ciphertext in IndexedDB reveals nothing about the title and only an approximate length of the note.

Why the IV must never repeat

GCM has one hard rule: never encrypt two messages with the same key and the same IV (initialisation vector, also called a nonce). If you do, the counter stream repeats, XORing the two ciphertexts reveals the XOR of the plaintexts, and the authentication key can be recovered, which breaks integrity for everything encrypted under that key.

VaultMemo uses the standard 96-bit (12-byte) IV and draws a fresh one from crypto.getRandomValues for every encryption, including every autosave. NIST's guidance for random 96-bit IVs is to stay well below 2^32 messages per key, where the chance of a collision starts to matter. A notebook saved every few seconds for a lifetime does not come close, and a password change generates a new vault key in any case.

The IV is stored in the clear beside the ciphertext. Like the salt, it is not a secret; its only requirement is uniqueness.

The verifier and the wrapped key

A vault needs to tell you whether the password you typed is right without storing the password or anything derived from it in a reversible way. VaultMemo keeps two small sealed values. The verifier is a fixed phrase encrypted under the key derived from your password: if decryption authenticates, the password is correct. The wrapped key is the actual 256-bit vault key, itself encrypted under the password-derived key.

The indirection buys flexibility. Your notes are sealed under the random vault key, not directly under the password, so the app can hold that key in memory while you work and drop it on lock. When you change the master password, VaultMemo goes further than re-wrapping: it generates a brand-new vault key, re-encrypts every note under it, and wraps the new key with the new password, so a compromise of the old password gives nothing against the current vault.

What it protects against, and what it doesn't

The threat the design is built for is a device or a backup in someone else's hands while the vault is locked: a stolen laptop, a copied browser profile, a .vaultmemo file on a lost USB stick. In every one of those cases the attacker holds ciphertext, a salt and a wrapped key, and the only way in is to guess the password at PBKDF2 speed. With a passphrase of five or six random words that is not a practical attack.

It is not built for a compromised machine while the vault is open. A keylogger records the password as you type it; malware or a hostile browser extension with access to the page can read decrypted notes from memory just as your eyes read them from the screen. Locking helps by shrinking the window, but it does not make an infected computer safe. Neither does any other encryption tool, whatever its marketing says.

The last gap is the weakest link in every design: a short or reused password. If the same password appears in a breach dump, an attacker starts there. The derivation makes each guess slow; it cannot make a guess that is on the first page of the dictionary slow enough.

WebCrypto: the code you did not have to trust

All of the above is performed by crypto.subtle, the Web Cryptography API. The implementations belong to the browser (BoringSSL in Chrome, NSS in Firefox, CoreCrypto on Apple platforms) and are the same code the browser uses for TLS. VaultMemo's own JavaScript only assembles inputs and stores outputs; it never implements a cipher.

WebCrypto keys can be marked non-extractable, and the password-derived key is, so scripts on the page cannot export its bytes. The API is available only in secure contexts, which is why the site is served over HTTPS. And because the primitives are standard, an exported .vaultmemo file can be decrypted with any PBKDF2 and AES-GCM implementation, in any language, should this site ever disappear.

Related tool
VaultMemo, the encrypted notebook

Create a vault or open the sample one