Keystone SystemsKS Systems

TPM Enrollment

Version: 1.0 Date: 2025-11-03 Module: keystone.tpmEnrollment

Overview

This guide explains how to configure TPM-based automatic disk unlock for your Keystone installation. TPM (Trusted Platform Module) enrollment allows your system to unlock encrypted disks automatically during boot without manual password entry, while maintaining secure recovery credentials for emergency scenarios.


Quick Start

After a fresh Keystone installation, you'll see a security warning on first login:

��������������������������������������������������������������
   TPM ENROLLMENT NOT CONFIGURED
��������������������������������������������������������������

Choose one of these commands to get started:

# Recommended: Generate recovery key
$ sudo keystone-enroll-recovery

# Alternative: Set custom password
$ sudo keystone-enroll-password

Both commands will:

  1. Configure your chosen recovery credential
  2. Enroll TPM for automatic unlock
  3. Remove the default "keystone" password
  4. Enable automatic boot unlock

Prerequisites

Before enrolling TPM, verify these requirements:

1. Secure Boot Must Be Enabled

$ sudo bootctl status | grep "Secure Boot"
Secure Boot: enabled (user)

If output shows disabled or setup, complete Secure Boot enrollment first:

$ sudo sbctl status

2. TPM 2.0 Must Be Available

$ ls /dev/tpm*
/dev/tpm0  /dev/tpmrm0

For VMs: Enable TPM 2.0 emulation in your hypervisor For bare metal: Enable TPM in BIOS/UEFI settings

3. Credstore Volume Must Exist

$ ls /dev/zvol/rpool/credstore
/dev/zvol/rpool/credstore

This is automatically created during Keystone installation with keystone.disko.enable = true.


Enrollment Methods

Method 1: Recovery Key (Recommended)

Recovery keys provide maximum security with minimal memorization burden.

Run:

$ sudo keystone-enroll-recovery

You will receive a recovery key like:

fda7-w4n8-km9p-3jc2-vx5h-7qte-2nuw-8rbg

CRITICAL: Save this key immediately in:

  • Password manager (with offline backup)
  • Printed paper in physical safe
  • NOT on the encrypted disk
  • NOT only in digital form on same hardware

Advantages:

  • Maximum entropy (256-bit cryptographic key)
  • No memorization required (write it down)
  • Copy/paste friendly during recovery

Disadvantages:

  • Must be stored externally (requires secure storage infrastructure)
  • Easy to lose if not properly backed up

Method 2: Custom Password

Custom passwords are familiar and don't require external storage.

Run:

$ sudo keystone-enroll-password

Requirements:

  • Minimum 12 characters
  • Maximum 64 characters
  • Cannot be "keystone" (default password)
  • No complexity requirements (but longer is better)

Password Examples:

 GOOD: "coffee-morning-laptop-window" (28 characters)
 GOOD: "MyBlueServer2024Today" (20 characters)
 GOOD: "xK9mP2vL4nQ8wR7tY3nB" (20 characters)
 BAD:  "Password1!" (11 characters - too short)
 BAD:  "keystone" (prohibited - publicly known)

Advantages:

  • Familiar password workflow
  • No external storage needed
  • Can be committed to memory

Disadvantages:

  • Must be remembered (may not use for months/years)
  • Lower entropy than recovery key
  • Risk of forgetting during emergency recovery

What Happens During Enrollment

Both enrollment methods follow this workflow:

  1. Prerequisite Validation

    • Verify Secure Boot enabled in User Mode
    • Verify TPM 2.0 device available
    • Verify credstore volume exists
  2. Credential Setup

    • Recovery key: Generate cryptographically secure key
    • Custom password: Prompt and validate password (12-64 chars)
    • Add credential to LUKS keyslot
  3. TPM Enrollment

    • Configure TPM unlock using PCRs 1,7 (configurable)
    • Store sealed unlock key in LUKS header
    • PCR 1: Firmware configuration
    • PCR 7: Secure Boot certificates
  4. Security Cleanup

    • Verify TPM enrollment succeeded
    • Remove default "keystone" password
    • Create enrollment marker file
  5. Completion

    • Display confirmation message
    • Suppress future login banners

After enrollment, the system will unlock automatically during normal boots.


Recovery Scenarios

When Will I Need My Recovery Credential?

You will need your recovery key or custom password in these situations:

Hardware Changes

  • Motherboard replacement: New TPM chip has different keys
  • TPM hardware failure: Manufacturing defects (rare)
  • RAM/CPU changes: May affect firmware PCR measurements (depends on firmware)

Firmware Changes

  • Secure Boot key modifications: If you re-enroll Secure Boot keys, PCR 7 changes
  • Secure Boot disabled: System refuses automatic unlock (security feature working correctly)
  • BIOS/UEFI downgrade or reflash: Firmware state changes affect PCRs

Software Changes (with PCRs 1,7)

  • Normal NixOS updates:  No re-enrollment needed (signed by same Secure Boot keys)
  • Kernel updates:  No re-enrollment needed
  • Bootloader updates:  No re-enrollment needed
  • BIOS settings changes: May require re-enrollment if PCR 1 affected

Disaster Scenarios

  • Lost TPM unlock: TPM chip becomes permanently locked (dictionary attack)
  • PCR policy corruption: Extremely rare firmware bugs

Re-Enrolling TPM After Changes

If you intentionally changed firmware or Secure Boot configuration:

# 1. Boot with recovery key/custom password (system will prompt)

# 2. Verify Secure Boot is enabled
$ sudo bootctl status | grep "Secure Boot: enabled"

# 3. Remove old TPM keyslot (bound to old PCR values)
$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore

# 4. Enroll new TPM keyslot with updated PCR values
$ sudo keystone-enroll-tpm

# 5. Reboot to verify automatic unlock
$ sudo reboot

After reboot, the system should unlock automatically with the new PCR measurements.


Testing Your Recovery Credential

IMPORTANT: Test your recovery key/password BEFORE you need it:

Test Recovery Key

# 1. Temporarily disable TPM unlock
$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore

# 2. Reboot - system will prompt for password
$ sudo reboot

# Boot prompt will appear:
# Please enter passphrase for disk credstore: _

# 3. Enter your recovery key at the prompt

# 4. After successful login, re-enable TPM
$ sudo keystone-enroll-tpm

# 5. Reboot to verify automatic unlock works again
$ sudo reboot

Test Custom Password

Same procedure as recovery key testing above - enter your custom password at the boot prompt.


Advanced Configuration

Customizing PCR List

By default, TPM enrollment uses PCRs 1 and 7. You can customize this in your NixOS configuration:

# configuration.nix
{
  keystone.tpmEnrollment = {
    enable = true;

    # Custom PCR list - choose based on your security vs update trade-off
    tpmPCRs = [ 7 ];  # Secure Boot only (most update-resilient)
    # tpmPCRs = [ 1 7 ];  # Default: Firmware config + Secure Boot
    # tpmPCRs = [ 0 1 7 ];  # More restrictive: Firmware code + config + Secure Boot
  };
}

PCR Selection Guide:

PCR ListSecurityUpdate ResilienceWhen to Use
[7]GoodExcellentFrequent firmware updates, prioritize convenience
[1 7]BetterGoodDefault balanced approach (recommended)
[0 1 7]BestPoorMaximum security, rare firmware updates
[7 11]ExcellentPoorRequires signed PCR policies (future feature)

Trade-offs:

  • More PCRs = More security (harder to bypass) but more re-enrollment needed
  • Fewer PCRs = Less re-enrollment but easier to bypass with identical measurements

Custom Credstore Device

If you modified the disko configuration to use a different credstore path:

{
  keystone.tpmEnrollment = {
    enable = true;
    credstoreDevice = "/dev/mapper/my-custom-credstore";
  };
}

Troubleshooting

Problem: Banner Still Appears After Enrollment

Symptoms: Login banner shows "TPM ENROLLMENT NOT CONFIGURED" even after running enrollment

Diagnosis:

# Check if TPM keyslot exists
$ sudo cryptsetup luksDump /dev/zvol/rpool/credstore | grep systemd-tpm2

# Check marker file
$ cat /var/lib/keystone/tpm-enrollment-complete

Solutions:

  • If TPM keyslot exists but no marker file: Logout/login (self-healing will create marker)
  • If no TPM keyslot: Re-run enrollment command
  • If enrollment failed silently: Check error logs and re-run

Problem: System Prompts for Password on Every Boot

Symptoms: After enrollment, system still asks for password at boot

Diagnosis:

# Check boot logs for TPM unlock attempts
$ sudo journalctl -b | grep -i "credstore\|tpm"

# Expected: "Failed to activate with TPM2" (indicates PCR mismatch)

Common Causes:

  1. Secure Boot disabled: PCR 7 value changed

    • Fix: Re-enable Secure Boot in BIOS, reboot
  2. Firmware updated: PCR values changed

    • Fix: Re-enroll TPM (see "Re-Enrolling TPM" section)
  3. TPM enrollment failed silently: No TPM keyslot created

    • Fix: Re-run enrollment command, check for errors
  4. Wrong PCR configuration: PCRs configured don't match boot state

    • Fix: Adjust tpmPCRs in configuration, re-enroll

Problem: "No TPM2 Device Found" Error

For Virtual Machines:

libvirt/virt-manager:

# Check if TPM is enabled in VM XML
$ virsh dumpxml your-vm-name | grep -A 3 "<tpm"

# If missing, add TPM device:
$ virsh edit your-vm-name

# Add this in <devices> section:
#   <tpm model='tpm-crb'>
#     <backend type='emulator' version='2.0'/>
#   </tpm>

QEMU directly:

# Add TPM emulation to QEMU command:
qemu-system-x86_64 \
  -tpmdev emulator,id=tpm0,chardev=chrtpm \
  -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
  -device tpm-crb,tpmdev=tpm0 \
  ...

bin/virtual-machine script: The Keystone bin/virtual-machine script automatically includes TPM 2.0 emulation.

For Bare Metal:

  1. Reboot into BIOS/UEFI firmware settings
  2. Look for "Security" or "Advanced" section
  3. Enable "TPM Device" or "TPM 2.0"
  4. Save and reboot
  5. Verify: ls /dev/tpm*

Problem: "Secure Boot Not Enabled" Error

Check current status:

$ sudo bootctl status
Secure Boot: disabled (setup)
Setup Mode: setup

If in Setup Mode:

  1. Secure Boot keys not yet enrolled
  2. Run Keystone Secure Boot enrollment (done during installation)
  3. Reboot to activate User Mode

If Secure Boot disabled in BIOS:

  1. Reboot into BIOS/UEFI settings
  2. Enable "Secure Boot"
  3. Save and reboot
  4. Verify: sudo bootctl status

Security Considerations

Why Remove Default Password?

The default "keystone" password is:

  • Publicly documented in Keystone documentation
  • Known to attackers who research the project
  • Zero security - anyone can unlock your disk

Enrollment scripts remove this password after adding your secure credential.

Why TPM + Recovery Credential?

TPM Automatic Unlock:

  • Provides convenience (no password entry during normal boots)
  • Binds unlock to specific boot state (Secure Boot + firmware config)
  • Prevents unauthorized boot configurations from accessing disk

Recovery Credential (recovery key or custom password):

  • Required for disaster recovery (TPM hardware failure)
  • Needed when PCR values change (firmware updates)
  • Ensures you never lose access to your data

Both together provide defense-in-depth:

  • Normal operation: Seamless automatic unlock
  • Hardware failure: Recovery credential works
  • Firmware changes: Re-enroll TPM with same recovery credential

PCR Selection Security

Default PCRs 1,7 Provide:

  • Protection against Secure Boot bypass
  • Protection against firmware tampering
  • Protection against unauthorized boot configurations

Default PCRs 1,7 Do NOT Protect Against:

  • Booting different OS signed with same Secure Boot keys
  • Rollback attacks to older signed kernels
  • Attacks that modify only non-measured boot components

For enhanced security, consider:

  • PCR 7+11 with signed policies (future feature)
  • Additional physical security measures
  • Regular firmware and software updates

Configuration Reference

Module Options

# configuration.nix
{
  keystone.tpmEnrollment = {
    # Enable the TPM enrollment module
    enable = true;

    # PCR list for TPM binding (default: [1 7])
    tpmPCRs = [ 1 7 ];

    # Credstore device path (default matches disko module)
    credstoreDevice = "/dev/zvol/rpool/credstore";
  };
}

Available Commands

After enabling the module, these commands become available:

CommandPurpose
keystone-enroll-recoveryGenerate recovery key + enroll TPM
keystone-enroll-passwordSet custom password + enroll TPM
keystone-enroll-tpmEnroll TPM only (advanced users)

File Locations

FilePurpose
/var/lib/keystone/tpm-enrollment-completeEnrollment status marker
/dev/zvol/rpool/credstoreLUKS-encrypted credstore volume
/etc/profile.d/tpm-enrollment-warning.shLogin banner script

Frequently Asked Questions

Q: Can I enroll both recovery key AND custom password?

Yes! LUKS supports up to 32 keyslots. You can run:

$ sudo keystone-enroll-recovery
# Save recovery key

$ sudo systemd-cryptenroll --password /dev/zvol/rpool/credstore
# Enter custom password

Both credentials will work for recovery scenarios.

Q: How do I change my custom password?

# Add new password (will prompt for current password)
$ sudo systemd-cryptenroll --password /dev/zvol/rpool/credstore

# Remove old password (optional - you can keep both)
$ sudo systemd-cryptenroll --wipe-slot=1 /dev/zvol/rpool/credstore

Q: How do I view current LUKS keyslots?

$ sudo systemd-cryptenroll /dev/zvol/rpool/credstore
SLOT TYPE
   1 recovery
   2 tpm2

# Or detailed view:
$ sudo cryptsetup luksDump /dev/zvol/rpool/credstore

Q: Can I disable TPM automatic unlock?

# Remove TPM keyslot
$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore

# System will now prompt for password on every boot

Q: What if I lose my recovery key?

Prevention: Save recovery key in multiple secure locations immediately

If lost before TPM failure: Generate new recovery key while system is accessible:

$ sudo systemd-cryptenroll --recovery-key /dev/zvol/rpool/credstore
# Save new key immediately

If lost after TPM failure: DATA IS UNRECOVERABLE - no way to unlock disk

Q: Can I use the same recovery key on multiple systems?

No - each system generates a unique recovery key during enrollment. This is a security feature - if one system's key is compromised, other systems remain secure.

Q: How do I check which PCRs are currently enrolled?

$ sudo cryptsetup luksDump /dev/zvol/rpool/credstore | grep -A 4 systemd-tpm2
  0: systemd-tpm2
    tpm2-hash-pcrs: 1+7
    tpm2-pcr-bank: sha256
    tpm2-pin: false

Q: Can I add a PIN to TPM unlock?

Currently not supported by the enrollment scripts, but can be done manually:

$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore
$ sudo systemd-cryptenroll \
    --tpm2-device=auto \
    --tpm2-pcrs=1,7 \
    --tpm2-with-pin=yes \
    /dev/zvol/rpool/credstore

This will prompt for a PIN during boot before TPM unlock.


Best Practices

1. Test Recovery Immediately

Don't wait for an emergency - test your recovery credential right after enrollment:

# Disable TPM temporarily
$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore
$ sudo reboot

# Use recovery credential at boot prompt
# Re-enroll TPM after successful login
$ sudo keystone-enroll-tpm

2. Store Recovery Key Offline

DO:

  • Print recovery key on paper, store in physical safe
  • Save in password manager with offline backup
  • Keep copy in safety deposit box (for critical systems)

DON'T:

  • Store only on the encrypted disk (defeats the purpose)
  • Store only on same physical machine
  • Store in unencrypted cloud storage
  • Email recovery key to yourself

3. Document Your PCR Configuration

If you customize tpmPCRs, document the choice:

{
  keystone.tpmEnrollment = {
    enable = true;
    # Using PCR 7 only for maximum update resilience
    # Acceptable for home server with physical security
    tpmPCRs = [ 7 ];
  };
}

4. Regular Testing Schedule

Test recovery quarterly:

  • Q1: Test recovery key/password works
  • Q2: Verify automatic unlock still working
  • Q3: Test PCR re-enrollment procedure
  • Q4: Review and rotate credentials if needed

Migration and Compatibility

Upgrading from Manual TPM Enrollment

If you manually enrolled TPM before this module existed:

The module will self-heal:

  1. Login banner script detects existing TPM keyslot
  2. Automatically creates marker file
  3. Banner stops appearing on subsequent logins

No action required - existing enrollment continues working.

Disabling the Module

To disable TPM enrollment features:

{
  keystone.tpmEnrollment.enable = false;
}

This will:

  • Remove enrollment commands from PATH
  • Remove login banner
  • NOT affect existing TPM enrollment (LUKS configuration persists)

To fully remove TPM unlock:

$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore

Technical Details

LUKS Keyslot Layout

After enrollment, typical keyslot configuration:

SlotTypeCredential
0(removed)Default "keystone" password (removed)
1password/recoveryYour recovery key or custom password
2tpm2TPM-sealed automatic unlock

TPM Token Structure

The TPM token stored in LUKS header contains:

  • PCR Policy: Which PCRs must match (e.g., 1,7)
  • PCR Bank: Hash algorithm (sha256)
  • Sealed Blob: Encrypted unlock key (only unseals when PCRs match)
  • Public Key: TPM's public endorsement key

Boot Process with TPM

1. UEFI firmware loads bootloader (lanzaboote)
2. TPM measures boot state into PCRs
3. systemd initrd starts
4. systemd-cryptsetup attempts unlock:
   a. Try TPM unlock (if PCRs match policy)
   b. If TPM fails, prompt for password/recovery key
5. Credstore unlocks, ZFS key loaded
6. System continues booting

Support and Troubleshooting

Debug Commands

# Check TPM device
$ ls -la /dev/tpm*

# Check TPM capabilities
$ sudo tpm2_getcap properties-variable

# Check PCR values
$ sudo tpm2_pcrread

# Check Secure Boot status
$ sudo bootctl status

# Check LUKS keyslots
$ sudo cryptsetup luksDump /dev/zvol/rpool/credstore

# Check enrollment marker
$ cat /var/lib/keystone/tpm-enrollment-complete

# Check boot logs
$ sudo journalctl -b | grep -i "credstore\|tpm"

Get Help

If you encounter issues:

  1. Check Prerequisites: Verify Secure Boot and TPM available
  2. Review Error Messages: Enrollment scripts provide detailed guidance
  3. Consult Boot Logs: journalctl -b | grep tpm
  4. Test Recovery: Ensure recovery credential works before relying on TPM

References


Version: 1.0 Last Updated: 2025-11-03 Maintainer: Keystone Project