TPM 2.0-Sealed Biometric "First Unlock" (Cold Boot Unlock) on Linux Desktop #22059
Replies: 2 comments 1 reply
|
✨ Thank you for your code contribution proposal! While the Bitwarden team reviews your submission, we encourage you to check out our contribution guidelines. Please ensure that your code contribution includes a detailed description of what you would like to contribute, along with any relevant screenshots and links to existing feature requests. This information helps us gather feedback from the community and Bitwarden team members before you start writing code. To keep discussions focused, posts that do not include a proposal for a code contribution will be removed.
Thank you for contributing to Bitwarden! |
|
@jon-warner one quetsion on this; do you happen to know if systemd-creds works from within flatpak / snap sandboxes? The most popular clients by far is flatpak, followed by snap. The unsandboxed installations are much less commonly used, so I'm wondering whether we can get to a solution which works on all platforms. |
Uh oh!
There was an error while loading. Please reload this page.
Select Topic Area
✅ Code Contribution Proposal
Code Contribution Proposal
Select Topic Area
✅ Code Contribution Proposal
Code Contribution Proposal
TPM 2.0-Sealed Biometric "First Unlock" (Cold Boot Unlock) on Linux Desktop
TL;DR: I want to implement native support for "First Unlock on App Startup" using Linux system authentication (Polkit/PAM). To ensure a robust security posture, the master vault key is cryptographically sealed inside the physical TPM 2.0 chip using
systemd-creds(in user space), and only the resulting sealed ciphertext is saved in the OS keyring.Background
On Windows (Windows Hello) and macOS (Touch ID), the Bitwarden Desktop client supports storing biometric-protected keys persistently. However, on Linux Desktop, biometric unlock has historically been ephemeral (in-session unlock only) and does not survive application restarts. On a cold restart, the user is forced to re-type their master password.
Furthermore, standard keyring storage (e.g., plain GNOME Keyring entries) offers a weak security model on Linux because the login keyring is often automatically unlocked at login. Storing the raw vault key in the keyring would effectively allow any user-space application to read it without user presence checks.
To solve this, I want to introduce a physical hardware-gated security model on Linux:
fprintd), which subsequently unseals the key from the TPM 2.0 hardware chip.Current State
Use Cases
Proposed Implementation
1. Hardware Sealing via
systemd-credsInstead of implementing raw TPM 2.0 interface code, we use standard Linux
systemd-credscommands running in user-space (using the--userflag) to encrypt/decrypt payload data. This avoids the need for elevated root permissions or Polkit prompts during keyring reads.Encryption (Enrollment):
The resulting ciphertext is stored in GNOME Keyring under the service
BitwardenBiometricsV2and the specific Bitwarden user GUID as the account.Decryption (Unlock):
The app feeds the ciphertext from GNOME Keyring to the decrypt command, which unseals it only after a successful authentication challenge via the system Polkit agent.
2. Runtime Gating & Fallbacks
To prevent UI settings from showing broken configurations on systems without a TPM 2.0 chip, the Rust native layer (
desktop_native) performs a one-time check during initialization by running a test encryption payload.is_tpm_available()fails, the setting is hidden or disabled.supportsBiometrics()on this status.3. Prompt Ordering Optimization
To prevent annoying "phantom" biometric prompts on startup, we check for the presence of the keyring entry before initiating the user challenge:
Ready to Contribute
I have verified this implementation locally on Ubuntu. It compiles cleanly and successfully unlocks the desktop vault on app startup using the local TPM 2.0 chip and system authentication.
I would appreciate your feedback on this architecture (specifically the reliance on
systemd-creds --userfor Linux user-space TPM operations) before submitting a formal Pull Request!All reactions