Vanilla JavaScript Projects

Project 29, Security and auth

Client-side Password Manager

Keep logins in a vault that only your master password can open. Everything is encrypted in this tab before it is saved, so the stored data is unreadable on its own.

Encryption
AES-GCM 256
Key from
PBKDF2, 600k rounds
Dependencies
None
Your browser
Checking

Unlock your vault

How it works

  1. Stretch the passwordYour master password and a random salt go through PBKDF2 600,000 times to make a 256-bit key.
  2. Encrypt the vaultThe whole list is turned into JSON and encrypted with AES-GCM and a fresh random IV on every save.
  3. UnlockOnly the salt, IV and ciphertext are stored. A wrong password fails the AES-GCM integrity check, so nothing is shown.
const salt = crypto.getRandomValues(new Uint8Array(16));
const base = await crypto.subtle.importKey("raw", new TextEncoder().encode(master), "PBKDF2", false, ["deriveKey"]);
const key = await crypto.subtle.deriveKey(
  { name: "PBKDF2", salt, iterations: 600_000, hash: "SHA-256" },
  base, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);

const iv = crypto.getRandomValues(new Uint8Array(12));   // new IV every save
const data = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key,
  new TextEncoder().encode(JSON.stringify(vault)));
// Store salt + iv + data. A wrong password makes decrypt() throw.