KalmPass

Security

Every password manager says it takes security seriously. This page gives you the specifics instead: the exact algorithms, the exact parameters, and a complete account of what we hold. Judge KalmPass on what it does rather than on what it claims.

The short version

Your vault is encrypted in your browser before anything is sent. The key comes from your master password, which never leaves your device. We store ciphertext, and we store it encrypted a second time under a key kept outside the database. There is no mechanism by which KalmPass can read your items, and no support process that can restore them for you.

What protects your data

1. Turning a password into a key

Your master password is stretched with PBKDF2-HMAC-SHA256 at 1,000,000 iterations, salted with your email address. OWASP's current floor for PBKDF2-SHA256 is 600,000; we sit comfortably above it. Salting with the address means two people who choose the same password still derive different keys, and rules out precomputed tables.

The result is split by HKDF-SHA256 into two independent branches: an encryption key that stays in the browser tab, and an auth key that is sent to the server to prove who you are. Because HKDF outputs for different labels are independent, holding the auth key reveals nothing about the encryption key. A fully compromised server still cannot decrypt a single item.

2. Encrypting the vault

Items are encrypted with AES-256-GCM under a random vault key, with a fresh 96-bit IV drawn from the CSPRNG for every single encryption. Item names, usernames, URLs and notes are all inside the encrypted blob, so the database cannot be mined for which sites you hold credentials for. Plaintext is padded to a 256-byte boundary first, so the stored length does not disclose how long a note or a password is.

The vault key itself is random and stable. Your master password wraps it rather than encrypting items directly, which is why changing your password takes a moment instead of re-encrypting your whole vault, and why your Recovery Key keeps working afterwards.

3. A second layer on our side

Everything the client sends is encrypted again by our server under a key derived from a Cloudflare Worker secret that is never stored in the database. The ciphertext is bound to its row with AES-GCM additional authenticated data, so a blob cannot be lifted out of one record and replayed into another.

This layer is not what keeps us out. The client layer already does that. It is there so that a leaked database backup, a stolen storage credential, or a subpoena served on the storage layer alone yields nothing readable at all.

4. Even the email address

Account addresses are stored as a keyed HMAC blind index for lookup, plus an encrypted copy. The database holds no readable identifier, so it cannot be mined for a customer list or cross-referenced against a breach corpus.

Signing in

Password verifier The server stores HMAC(pepper, PBKDF2(auth_key, salt, 100k)). The pepper lives in a Worker secret, not the database, so a stolen database cannot be cracked offline at all.
Sessions 256-bit random tokens in an HttpOnly, Secure, SameSite=Strict cookie. Only the HMAC of the token is stored, so the session table cannot be used to mint a login. Sessions expire after 30 minutes idle and 12 hours absolute, whichever comes first.
Two-factor Optional TOTP (RFC 6238) on the login itself, verified in constant time across the accepted clock-drift window. Ten single-use backup codes are issued, stored only as peppered hashes.
Rate limiting Failures are counted by email and by IP separately, with exponential lockout, so a flood from one address cannot lock a different account out. Counters are keyed by blind index.
Enumeration Login spends identical work whether or not the address exists, and always returns the same error. Account reset requests always answer the same way.
CSRF SameSite=Strict on the session cookie, plus a strict Origin check on every state-changing request.
The page itself A Content-Security-Policy with no inline script and no external origins at all, plus HSTS, X-Frame-Options: DENY, and no-store on everything. The app loads nothing from a CDN, because a CDN is a place someone else can change your crypto.

Recovery, honestly

Zero-knowledge encryption and "email me a password reset" cannot both be true. If a server can restore your vault from an email link, then that server can read your vault, and so can anyone who breaks into it.

KalmPass resolves this the way an emergency kit does. At signup you are issued a Recovery Key: 125 bits of randomness, printed in Crockford base32 so it cannot be misread. Your vault key is wrapped under it as well as under your master password. Forget the password, use the key, set a new one. Nothing is lost, and we still never held anything that could open your vault.

Using the Recovery Key issues a fresh one, because the old one has now been typed into a browser. You can also replace it at any time from Settings.

If you lose both

There is nothing anyone can do. We can clear the account so the address can be used again, but the data is unrecoverable by us, by you, by anyone. This is the price of the guarantee, and we would rather state it plainly than bury it.

What we can see

Item contents No. Encrypted in your browser under a key we do not have.
Item names, URLs, usernames No. They are inside the same encrypted blob.
How many items you have Yes. Row counts are visible. Sizes are padded, so lengths are not.
Your email address Only by decrypting it with the Worker secret, which we do to send you mail. It is not readable from the database alone.
When you sign in, and from what kind of device Yes. It is in your own activity log, encrypted at rest.
Your master password or Recovery Key Never. Neither is transmitted, in any form, ever.

Outbound requests

In normal use KalmPass makes no request to anywhere but its own origin. The single exception is the breach check, which you have to press a button to run: your browser SHA-1s a password locally and sends only the first five hex characters to Have I Been Pwned, which returns every hash sharing that prefix. The match is found on your device. We proxy the request so HIBP does not see your IP either.

What this does not protect against

Being straight about the limits is part of the job:

Reporting a problem

If you find a vulnerability, please email security@kalmpass.net before disclosing it publicly. We will confirm receipt, keep you updated, and credit you if you would like that. We will not threaten anyone who reports a problem in good faith.

Last reviewed 15 September 2026.