Biometrics Backend
Status: Design Intent. The
AuthFactorId::Fingerprintvariant exists incore-types::authand theVaultAuthBackendtrait is defined incore-auth::backend, but no struct implements this factor today. This page documents what the backend will do when built, grounded in the trait interface and platform biometric APIs.
The biometrics backend enables vault unlock gated by fingerprint verification (and, in the
future, other biometric modalities such as face recognition). It maps to
AuthFactorId::Fingerprint (config string "fingerprint"). The critical design principle:
biometric data is never used as key material. Biometrics are authentication gates that release
a stored key, not secrets from which keys are derived.
Design Principle: Biometrics Are Not Secrets
Biometric features (fingerprint minutiae, facial geometry) are not secret – they can be observed, photographed, or lifted from surfaces. They are also not stable – they vary between readings. For these reasons, the biometrics backend never derives cryptographic key material from biometric data. Instead:
- At enrollment, the master key (or a KEK) is encrypted and stored on disk.
- The decryption key for that blob is held in a platform keystore that requires biometric verification to release.
- At unlock, the platform biometric subsystem verifies the user, and if successful, releases the decryption key to the backend.
The biometric template (the mathematical representation of the fingerprint or face) never leaves the platform biometric subsystem. Open Sesame never sees, stores, or transmits biometric data.
Platform Biometric APIs
Linux: fprintd
On Linux, fingerprint authentication is mediated by fprintd, a D-Bus service that manages
fingerprint readers and templates. The authentication flow:
- The backend calls
net.reactivated.Fprint.Device.VerifyStarton the fprintd D-Bus interface. fprintdcommunicates with the fingerprint sensor hardware vialibfprint, acquires a fingerprint image, and matches it against enrolled templates.- On match,
fprintdemits aVerifyStatussignal withverify-match. On failure,verify-no-matchorverify-retry-scan. - The backend calls
VerifyStopto end the session.
The backend uses the fprintd D-Bus API directly (not PAM) to avoid requiring a full PAM session context.
Future: macOS LocalAuthentication
On macOS (if platform support is added), LocalAuthentication.framework provides Touch ID
and Face ID gating of Keychain items. A Keychain item with
kSecAccessControlBiometryCurrentSet requires biometric verification before the Keychain
releases the stored secret. This maps directly to the “biometric gates release of a stored
key” model.
Future: Windows Hello
On Windows, Windows.Security.Credentials.KeyCredentialManager and the Windows Hello
biometric subsystem provide similar gating. The TPM-backed key is released only after
Windows Hello verification succeeds.
Mapping to VaultAuthBackend
factor_id()
Returns AuthFactorId::Fingerprint.
backend_id()
Returns "fingerprint".
name()
Returns "Fingerprint".
requires_interaction()
Returns AuthInteraction::HardwareTouch. The user must place their finger on the sensor.
is_enrolled(profile, config_dir)
Checks two conditions:
- An enrollment blob exists at
{config_dir}/profiles/{profile}/fingerprint.enrollment. - At least one fingerprint is enrolled in
fprintdfor the current system user (queried vianet.reactivated.Fprint.Device.ListEnrolledFingers).
Both must be true. If the system fingerprint enrollment is wiped (user re-enrolled fingers in system settings), the Open Sesame enrollment blob still exists on disk but the platform verification will match against different templates, making it effectively stale.
can_unlock(profile, config_dir)
- Verify enrollment exists via
is_enrolled(). - Check that
fprintdis running (D-Bus namenet.reactivated.Fprintis available). - Check that at least one fingerprint reader device is present.
D-Bus name lookup and device enumeration complete well within the 100ms trait budget.
enroll(profile, master_key, config_dir, salt, selected_key_index)
- Verify that
fprintdhas at least one enrolled fingerprint for the current user. If not, returnAuthError::BackendNotApplicable("no fingerprints enrolled in fprintd; enroll via system settings first"). - Generate a random 32-byte storage key.
- Wrap
master_keyunder the storage key using AES-256-GCM. - Store the storage key in a location protected by biometric gating:
- Primary strategy (Linux): Store the storage key in the user’s kernel keyring
(
keyctl) under a session-scoped key. The keyring entry is created with a timeout matching the user session. Biometric verification via fprintd acts as the authorization gate before the backend retrieves the keyring secret at unlock time. - Fallback strategy: Encrypt the storage key with a key derived from
saltand a device-specific identifier (machine-id). Store the encrypted storage key in the enrollment blob itself. The biometric check acts as the sole authorization gate.
- Primary strategy (Linux): Store the storage key in the user’s kernel keyring
(
- Write the enrollment blob to
{config_dir}/profiles/{profile}/fingerprint.enrollment.
selected_key_index is unused (there is one biometric subsystem per machine). It is ignored.
unlock(profile, config_dir, salt)
- Load the enrollment blob.
- Initiate fingerprint verification via fprintd D-Bus API (
VerifyStart). - Wait for the
VerifyStatussignal. The daemon overlay displays a “scan your fingerprint” prompt. - If verification fails (no match, timeout, or sensor error), return
AuthError::UnwrapFailed. - If verification succeeds, retrieve the storage key from the kernel keyring (primary strategy) or decrypt it from the blob (fallback strategy).
- Unwrap the master key using the storage key (AES-256-GCM decrypt).
- Return
UnlockOutcome:master_key: the unwrapped 32-byte key.ipc_strategy:IpcUnlockStrategy::DirectMasterKey.factor_id:AuthFactorId::Fingerprint.audit_metadata:{"method": "fprintd", "finger": "<which_finger>"}(if fprintd reports which finger matched).
revoke(profile, config_dir)
- Remove the storage key from the kernel keyring (if using primary strategy).
- Delete
{config_dir}/profiles/{profile}/fingerprint.enrollment.
Does not remove fingerprints from fprintd – those are system-level enrollments managed by the user outside of Open Sesame.
Enrollment Blob Format
Version: u8 (1)
Storage strategy: u8 (1 = kernel keyring, 2 = embedded encrypted key)
Wrapped master key: 12-byte nonce || ciphertext || 16-byte GCM tag
Embedded encrypted storage key (strategy 2 only): 12-byte nonce || ciphertext || 16-byte tag
Device binding hash: 32 bytes (SHA-256 of machine-id || profile name)
FactorContribution
AuthCombineMode::AnyorAuthCombineMode::Policy: The backend providesFactorContribution::CompleteMasterKey. It unwraps the full master key after biometric verification succeeds.AuthCombineMode::All: The backend providesFactorContribution::FactorPiece. A random 32-byte piece (not the master key) is stored behind the biometric gate and contributed to HKDF derivation upon successful verification.
The biometric itself does not contribute entropy – it is a gate. The piece is a random value generated at enrollment time and stored behind the biometric gate.
Liveness Detection
Fingerprint sensors vary in their resistance to spoofing:
| Sensor Type | Spoofing Resistance | Notes |
|---|---|---|
| Capacitive (most laptop sensors) | Moderate | Detects electrical properties of skin. Gummy fingerprints with conductive material can sometimes fool them. |
| Ultrasonic (e.g., Qualcomm 3D Sonic) | High | Measures sub-dermal features. More resistant to printed or molded replicas. |
| Optical (common in USB readers) | Low | Easiest to spoof with printed or molded fingerprints. |
Open Sesame delegates liveness detection entirely to the sensor hardware and fprintd. The
backend does not attempt its own liveness checks. Deployment guidance: use capacitive or
ultrasonic sensors for security-sensitive configurations, and combine biometric with a second
factor via AuthCombineMode::Policy.
Privacy Guarantees
- No template storage. Open Sesame never stores, transmits, or processes biometric
templates. Templates are managed exclusively by
fprintd(stored in/var/lib/fprint/). - No template access. The backend never requests raw biometric data or template bytes. It uses only the verify/match API, which returns a boolean result.
- No cross-profile linkability. The enrollment blob contains no biometric information. An attacker who obtains the blob cannot determine whose fingerprint unlocks the vault.
- User-controlled deletion. Revoking the backend deletes only the encrypted key blob. Biometric templates remain under user control in fprintd.
Integration Dependencies
| Dependency | Type | Purpose |
|---|---|---|
fprintd >= 1.94 | System service | Fingerprint verification via D-Bus |
libfprint >= 1.94 | System library | Sensor driver layer (used by fprintd) |
Rust crate: zbus | Cargo dependency | D-Bus client for fprintd communication |
Rust crate: keyutils | Cargo dependency | Linux kernel keyring access (primary storage strategy) |
| Compatible fingerprint reader | Hardware | Any reader supported by libfprint |
Threat Model Considerations
- Biometric spoofing. The backend is only as spoof-resistant as the sensor hardware. It
should not be the sole factor for high-value vaults. Combining biometric with password or
FIDO2 via
AuthCombineMode::Policyis recommended. - Stolen enrollment blob. The blob is useless without passing biometric verification (primary strategy) or without the device-specific derivation inputs (fallback strategy). The biometric gate is the critical protection.
- fprintd compromise. If an attacker can inject false D-Bus responses (by compromising fprintd or the user’s D-Bus session), they can bypass biometric verification. Running fprintd as a system service (not user session) and using D-Bus mediation via AppArmor or SELinux mitigates this.
- Irrevocable biometrics. If a fingerprint is compromised (lifted from a surface), the user cannot change their fingerprint. Mitigation: re-enroll with a different finger and revoke the old enrollment, or add a second factor requirement via policy.
- Fallback strategy weakness. The embedded-key fallback strategy protects the storage key only with device-specific derivation (machine-id + salt). An attacker with the enrollment blob and knowledge of the machine-id can bypass the biometric gate entirely. The primary strategy (kernel keyring) is strongly preferred.
See Also
- Factor Architecture –
VaultAuthBackendtrait definition and dispatch - FIDO2/WebAuthn – Roaming authenticator with on-device biometric UV
- Policy Engine – Combining biometric with other factors