Skip to main content

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.
PropertyKeychain approachPRF approach
Secret at restMaster password or key in OS keychainAES-GCM ciphertext (useless without PRF)
Extraction riskKeychain dump exposes secretNo secret to extract
Hardware bindingOS-level, software-extractableWebAuthn authenticator, hardware-bound
FallbackN/AMaster password always works

Architecture

Browser compatibility

BrowserMinimum versionPRF supportNotes
Chrome116+YesFull support on macOS, Windows, Android
Edge116+YesSame Chromium engine as Chrome
FirefoxNoPRF extension not yet implemented
SafariNoApple’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.
1

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.
2

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.
3

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.
4

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.
5

Wrap the IKM

The vault’s raw 32-byte IKM (from Argon2id) is encrypted with AES-256-GCM using a random 12-byte IV and the PRF-derived wrapping key. The resulting ciphertext, IV, and credential ID are stored in localStorage under passagent_biometric_blob.
Enrollment can happen transparently after any successful master password unlock. The user does not need to re-enter their password specifically for enrollment — the IKM is already in memory.

Unlock

1

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.
2

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.
3

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.
4

Unwrap IKM

AES-256-GCM decryption using the stored IV and ciphertext recovers the raw 32-byte IKM. This IKM then feeds into the standard HKDF key expansion to produce encKey, macKey, searchKey, and wrapKey.

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 string prf-mix-v3 to produce the final 256-bit augmented key
PRF-augmented mode means losing the hardware key locks you out of the vault permanently (unless you have a recovery mechanism). Only enable this if you have backup hardware keys or family vault recovery configured.

Storage format

The biometric blob stored in localStorage has the following structure:
FieldTypeDescription
credentialIdBase64url stringWebAuthn credential ID for the platform authenticator
wrappedIkm.ivBase64url string12-byte AES-GCM initialization vector
wrappedIkm.ciphertextBase64url stringAES-256-GCM encrypted IKM (32 bytes + 16-byte auth tag)
enrolledAtnumberUnix 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

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.
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.
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.
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.