Overview
Biometric unlock lets you open your vault with Face ID, Touch ID, or Windows Hello instead of typing your master password every time. Unlike simple “remember me” shortcuts that store the password in the keychain, PassAgent uses the WebAuthn PRF (Pseudo-Random Function) extension to derive a cryptographic key directly from the biometric credential. The vault’s Initial Key Material (IKM) is wrapped with this PRF-derived key and stored locally. Without a successful biometric authentication, the stored blob is cryptographically useless.Why PRF, not a convenience shortcut
Most password managers that offer biometric unlock store your master password (or a derived key) in the OS keychain, protected by a biometric gate. This has a fundamental weakness: the secret exists in a recoverable form in the keychain. An attacker with OS-level access (malware, jailbreak, forensic tools) can extract it. PassAgent’s approach is different. The PRF extension produces a deterministic 32-byte output that only exists transiently in memory during a successful biometric authentication. It is never stored anywhere. The vault IKM is encrypted with a key derived from this PRF output. Without the authenticator hardware and the user’s biometric, there is no key to extract.| Property | Keychain approach | PRF approach |
|---|---|---|
| Secret at rest | Master password or key in OS keychain | AES-GCM ciphertext (useless without PRF) |
| Extraction risk | Keychain dump exposes secret | No secret to extract |
| Hardware binding | OS-level, software-extractable | WebAuthn authenticator, hardware-bound |
| Fallback | N/A | Master password always works |
Architecture
Browser compatibility
| Browser | Minimum version | PRF support | Notes |
|---|---|---|---|
| Chrome | 116+ | Yes | Full support on macOS, Windows, Android |
| Edge | 116+ | Yes | Same Chromium engine as Chrome |
| Firefox | — | No | PRF extension not yet implemented |
| Safari | — | No | Apple’s PRF implementation has known bugs; excluded |
PassAgent checks for PRF support at runtime. On unsupported browsers, the biometric option is hidden and the master password is the only unlock method. The
isPRFSupported() function explicitly excludes iOS devices due to unreliable PRF behavior.Enrollment
Enrollment happens after a successful master password unlock. The user opts in from Settings, and PassAgent registers a new platform credential with the PRF extension.Check availability
checkBiometricAvailability() verifies three conditions: WebAuthn API is present, a user-verifying platform authenticator is available (via isUserVerifyingPlatformAuthenticatorAvailable()), and the PRF extension is supported.Create platform credential
enrollBiometric(ikm, userId, userEmail) calls navigator.credentials.create() with authenticatorAttachment: "platform", userVerification: "required", and the PRF extension configured with the salt passagent-biometric-prf-v1. The credential uses ES256 or RS256.Extract PRF output
The PRF result is extracted from
credential.getClientExtensionResults().prf.results.first — a deterministic 32-byte ArrayBuffer derived from the authenticator’s internal secret and the provided salt.Derive wrapping key
The PRF output is fed into HKDF-SHA256 with info string
passagent-biometric-wrap-v1 to produce a non-extractable AES-256-GCM CryptoKey.Unlock
Retrieve stored blob
On vault open,
unlockWithBiometric() reads the passagent_biometric_blob from localStorage. If the blob is missing or corrupted, it falls back to master password entry.Authenticate with platform credential
navigator.credentials.get() is called with allowCredentials set to the stored credential ID, userVerification: "required", and the same PRF salt. The user authenticates with Face ID / Touch ID / Windows Hello.Extract PRF output and derive key
The PRF output from the assertion is fed through the same HKDF-SHA256 derivation with
passagent-biometric-wrap-v1 to reproduce the identical wrapping key.PRF-augmented vault key (hardware key mode)
For users who want even stronger protection,vault-prf.ts provides an advanced mode where the hardware key’s PRF output is mixed into the vault key derivation itself — not just used for wrapping.
In this mode:
- Master password alone is not enough — the hardware key must be physically present
- PRF output is deterministic — same authenticator + same eval input = same 32-byte output
- RP-bound — the PRF output is scoped to the PassAgent relying party ID
- The
mixPRFIntoKey()function concatenates the raw vault key with the PRF output and runs HKDF-SHA256 with info stringprf-mix-v3to produce the final 256-bit augmented key
Storage format
The biometric blob stored inlocalStorage has the following structure:
| Field | Type | Description |
|---|---|---|
credentialId | Base64url string | WebAuthn credential ID for the platform authenticator |
wrappedIkm.iv | Base64url string | 12-byte AES-GCM initialization vector |
wrappedIkm.ciphertext | Base64url string | AES-256-GCM encrypted IKM (32 bytes + 16-byte auth tag) |
enrolledAt | number | Unix timestamp of enrollment |
Unenrollment
unenrollBiometric() removes both passagent_biometric_blob and passagent_biometric_credential_id from localStorage. The WebAuthn credential remains on the platform authenticator but is harmless without the stored ciphertext — there is nothing left to decrypt.
Security properties
No plaintext secrets at rest
No plaintext secrets at rest
The IKM is stored only as AES-256-GCM ciphertext. The decryption key (PRF output) exists only transiently during authentication and is never written to disk, keychain, or any persistent store.
Hardware-bound key material
Hardware-bound key material
The PRF output is derived from the authenticator’s internal secret, which is non-extractable. Even with full filesystem access, an attacker cannot reproduce the PRF output without the physical authenticator and the user’s biometric.
Deterministic but unique
Deterministic but unique
The PRF function is deterministic (same inputs = same output) but unique per credential, per salt, and per relying party. Different PassAgent deployments on different domains produce different PRF outputs from the same authenticator.
Graceful fallback
Graceful fallback
Master password unlock is always available. If the biometric blob is corrupted, missing, or the authenticator is unavailable, the user falls through to password entry with no data loss.
The PRF salt
passagent-biometric-prf-v1 is a fixed, publicly known value. Changing it would invalidate all existing enrollments. The salt provides domain separation, not secrecy — security comes from the authenticator’s internal key and the biometric gate.