/** * Password hashing on Web Crypto only. * * Deliberately **not** bcrypt or argon2: both are native modules that cannot run on Cloudflare * Workers, which is the v1 production target. PBKDF2-SHA256 is reachable in both Node and Workers * through `crypto.subtle`, so one implementation covers every environment — but *not* identically, * which is the trap this file exists to document. * * Encoded form: `pbkdf2$$$`. The iteration count travels * with the hash so it can be raised later without invalidating existing passwords. */ /** * **workerd refuses more than 100,000 PBKDF2 iterations**, and the refusal is a thrown * `NotSupportedError`, not a clamp. This is a ceiling imposed by the runtime, not a security * preference — OWASP asks for more, and `crypto.subtle` on Workers is the only KDF available to a * CMS that ships zero native dependencies. * * The cost of getting this wrong is total and invisible until deployment: at 210,000 the first-run * setup screen 500s, so a Cloudflare deployment cannot create its first administrator, and every * sign-in attempt for an address that does not exist 500s too, because that path derives against * `DUMMY_HASH` to equalise timing. Nothing in Node reproduces it — Node has no cap, so every test, * every `npm run dev` session, and every local sign-in works perfectly. * * Raising this above 100,000 breaks production. If a future runtime lifts the cap, raise it there * first and confirm with `npm run preview`, which is the only local command that runs in workerd. */ export declare const MAX_WORKERD_ITERATIONS = 100000; /** * The iteration count new hashes use. * * One number for every environment on purpose: a hash written in dev has to verify in production * and the other way round, and a per-platform count would make a database that moves between them * hold passwords nobody can check. */ export declare const DEFAULT_ITERATIONS = 100000; export declare function hashPassword(password: string, iterations?: number): Promise; /** * Verify a password against an encoded hash. * * Returns `false` for malformed input rather than throwing, so a corrupt row cannot be * distinguished from a wrong password by an attacker watching for error responses. */ export declare function verifyPassword(password: string, encoded: string): Promise; /** True when a stored hash used a weaker iteration count and should be upgraded on next login. */ export declare function needsRehash(encoded: string, iterations?: number): boolean; /** * Compare two byte arrays without leaking their contents through timing. * * The length check short-circuits, which is fine: hash length is not secret. */ export declare function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean; export declare function toBase64(bytes: Uint8Array): string; export declare function fromBase64(value: string): Uint8Array;