Skip to content

lenovo-x1-gen11: Add TPM-backed encryption for the persist partition#1232

Merged
brianmcgillion merged 2 commits intotiiuae:mainfrom
hros-tii:cryptenroll
Sep 19, 2025
Merged

lenovo-x1-gen11: Add TPM-backed encryption for the persist partition#1232
brianmcgillion merged 2 commits intotiiuae:mainfrom
hros-tii:cryptenroll

Conversation

@hros-tii
Copy link
Copy Markdown
Contributor

@hros-tii hros-tii commented Jun 4, 2025

Description of Changes

This PR builds upon #1074 which was merged in main as 3f25f73. It builds on top of the dm-verity protected rootfs.

The persist partition is encrypted using the TPM as a LUKS token. An initial step takes place where persist is enrolled to the TPM and a PIN is set. A recovery passphrase is also added to the LUKS header which can optionally be saved and used to later access the partition contents in case of decryption failure. The user is then prompted for the PIN during the boot sequence.

A new user profile is added (mvp-user-trial-hardening). For now selecting this profile only enables the feature described here. It is automatically enabled by the lenovo-x1-gen11-hardening-debug target along with dm-verity.

Limitations

  • Other partitions are not encrypted, namely rootfs and swap.

This could be implemented in a similar way as for persist, but some modifications could be made for user convenience, to avoid having to input a different PIN for each partition. The decryption step would also need to be moved from user space to initrd.

  • Plymouth is disabled, to display the PIN input prompt

Type of Change

  • New Feature
  • Bug Fix
  • Improvement / Refactor

Checklist

  • Clear summary in PR description
  • Detailed and meaningful commit message(s)
  • Commits are logically organized and squashed if appropriate
  • Contribution guidelines followed
  • Ghaf documentation updated with the commit - https://tiiuae.github.io/ghaf/
  • Author has run make-checks and it passes
  • All automatic GitHub Action checks pass - see actions
  • Author has added reviewers and removed PR draft status
  • Tested on Lenovo X1 x86_64 Gen11

Testing Instructions

Applicable Targets

  • Orin AGX aarch64
  • Orin NX aarch64
  • Lenovo X1 x86_64
  • Dell Latitude x86_64

Installation Method

  • Requires full re-installation
  • Can be updated with nixos-rebuild ... switch

Test Steps To Verify:

  1. Build an installer image for the lenovo-x1-gen11-hardening-debug-installer target.
  2. Flash to x86_64 laptop
  3. First boot should stop halfway to prompt for the PIN.
  4. Press Enter once (no input) to clear the passphrase prompt
  5. Enter the PIN that will be used to decrypt the partition.
  6. Press Enter a few times to complete the enrollment.
  7. System should boot normally. On host system run dmsetup status or mount to confirm the persist partition is mapped with dm-crypt.

Copy link
Copy Markdown
Collaborator

@vunnyso vunnyso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @hros-tii for nice feature integration.

Few findings when I have tested it.

  1. Persist size is reduced to 3G and its not using entire disk space compared to lenovo-x1-gen11-hardening
    image
  2. On every boot system is asking for PIN which can be prevented.
  3. PR need to be fix the run-checks and rebase your PR with latest.

@hros-tii
Copy link
Copy Markdown
Contributor Author

hros-tii commented Jun 6, 2025

Thanks @vunnyso for the review. Especially item 1) is a nice catch. I'll push some changes to address your comments.

On every boot system is asking for PIN which can be prevented.

This is on purpose to prevent the partition to be decrypted automatically, in the event of a stolen device for example. Do you mean that the PIN should be remembered for a small period of time after the first input?
I agree that this is a tradeoff with usability to ask the user to enter a password twice (although the two passwords can be identical), but it gives the best assurance security-wise.

@vunnyso
Copy link
Copy Markdown
Collaborator

vunnyso commented Jun 6, 2025

Thanks @vunnyso for the review. Especially item 1) is a nice catch. I'll push some changes to address your comments.

On every boot system is asking for PIN which can be prevented.

This is on purpose to prevent the partition to be decrypted automatically, in the event of a stolen device for example. Do you mean that the PIN should be remembered for a small period of time after the first input? I agree that this is a tradeoff with usability to ask the user to enter a password twice (although the two passwords can be identical), but it gives the best assurance security-wise.

Thanks @hros-tii. I mean entering a PIN at every boot is not user-friendly. It would be beneficial if we could make PIN as persistent by trying different TPM configuration registers or another security mechanism. For an additional layer of security, we can enroll mandatory Yubikey presence if needed.

Copy link
Copy Markdown
Member

@humaidq-tii humaidq-tii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than the comments, it looks good. Good that we switch to systemd-boot now, would be also useful when implementing systemd-sysupdate.

It is odd that Plymouth doesn't display the PIN, as it should. Maybe it is a problem with the theme or Plymouth isn't configured properly.

@hros2
Copy link
Copy Markdown

hros2 commented Jun 16, 2025

Hi all,
Thank you for all the comments, I tried to include your feedback in the recently pushed commits. I just want to warn that I haven't had time to thoroughly test all the changes - in particular the Yubikey/FIDO related functionality - and plan to do so during the week. Still, comments on the new code are welcome.

On configuring the TPM without a PIN: an attacker having access to a stolen device that unlocks automatically can use other physical channels to access the disk contents (memory dumps for example). Binding to different PCRs cannot prevent these side-channel attacks as they don't modify any code running on the device. If this setup has to be used, I believe the user should be warned that their data could still be recovered in case their laptop is stolen. Even a weak PIN with a few digits can help mitigate this when coupled with rate limiting from the TPM to prevent brute-force attacks.
With that said I have added the option to encrypt the disk using a Yubikey instead of the TPM, and in that case no input is required from the user.

@hros-tii
Copy link
Copy Markdown
Contributor Author

The new code is ready for review. A couple things were added:

  • build time option to enroll a Yubikey instead of the hardware TPM
  • encrypted swap partition
  • persistence partition is resized on first boot

The setup steps are the same as in the PR description, but in order to verify the new features some more things can be checked. On ghaf-host:

  1. Verify that swap is enabled: free -h
  2. Check that persist and swap are encrypted (dmsetup status or mount).
  3. persist should be the size of the whole disk
  4. The partitions should decrypt successfully on reboot with either TPM or Yubikey.

@hros-tii
Copy link
Copy Markdown
Contributor Author

There is an open upstream bug when using FIDO2 devices to unlock the disk, specifically the models without a PIN input: systemd-cryptsetup + FIDO2 + --fido2-with-client-pin=false: fails to prompt for unlock at boot.

There's a race between the kernel detecting the plugged in USB devices and when the cryptsetup service tries to decrypt the partition. In some cases the latter runs first and finds the security token missing, falling back to the passphrase prompt. I could replicate it on the Lenovo laptop, here is how it looks in the logs:

Jun 25 14:53:19 ghaf-host systemd-cryptsetup[277]: Set cipher aes, mode xts-plain64, key size 512 bits for device /dev/disk/by-partuuid/20936304-3d57-49c2-8762-bbba07edbe75.
Jun 25 14:53:19 ghaf-host systemd-cryptsetup[277]: Security token not present for unlocking volume persist, please plug it in.
[...]
Jun 25 14:53:20 ghaf-host kernel: usb 3-7: New USB device found, idVendor=1050, idProduct=0402, bcdDevice= 5.71
Jun 25 14:53:20 ghaf-host kernel: usb 3-7: New USB device strings: Mfr=1, Product=2, SerialNumber=0
Jun 25 14:53:20 ghaf-host kernel: usb 3-7: Product: YubiKey FIDO
Jun 25 14:53:20 ghaf-host kernel: usb 3-7: Manufacturer: Yubico
Jun 25 14:53:20 ghaf-host kernel: hid-generic 0003:1050:0402.0001: hiddev96,hidraw0: USB HID v1.10 Device [Yubico YubiKey FIDO] on usb-0000:00:14.0-7/input0

Signed-off-by: Hugo Ros <Hugo.Ros@tii.ae>
- enroll persist and swap to chosen backend (TPM or Yubikey)
- decrypt both partitions in initrd
- add early service to expand swap and persist
- btrfs postboot: resize encrypted partitions

Signed-off-by: Hugo Ros <Hugo.Ros@tii.ae>
@vunnyso
Copy link
Copy Markdown
Collaborator

vunnyso commented Sep 19, 2025

Tested on Lenovo-X1 Gen11, filesystem snapshot remains intact. Basic testing looks fine.

Branch File system snapshot
mainline image
This PR image

@brianmcgillion brianmcgillion merged commit 26732c2 into tiiuae:main Sep 19, 2025
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants