TPM based Luks encrypted partition auto unlock


Prerequisites

Introduction

When I installed Linux with an encrypted root partition on my Laptop, I found that I had to enter a password twice on boot. Once for unlocking my filesystem, and once to login to my account. I know from Bitlocker on Windows that they use the TPM chip to unlock the diskencryption, and demand a password or recovery key whenever the hardware or bootsector is changed. This method protects against a person ripping the hard disk/SSD from your system, and hunting for private data. This is done by using the fingerprint of the system in the TPM key to unlock the file incryption.

Disclaimer

Use the information in this document at your own risk. I disavow any potential liability for the contents of this document. Use of the concepts, examples, and/or other content of this document is entirely at your own risk.

All copyrights are owned by their owners, unless specifically noted otherwise. Use of a term in this document should not be regarded as affecting the validity of any trademark or service mark.

Naming of particular products or brands should not be seen as endorsements.

You are strongly recommended to take a backup of your system before major installation and backups at regular intervals.

PCR Registers

To create a fingerprint of the hardware the operating system is booted on, we can use the TPM PCR registers. These registers contain SHA-1 hashes of all kinds of hardware characteristics listed below.

You can find the PCRs on your system using

# cat /sys/class/misc/tpm0/device/pcrs 

Initializing the TPM

Config TPM in the Bios

Make sure the TPM is enabled, and also it is set to TPM 1.2, as TPM 2.0 is not supported by the current Mint kernel.

Take ownership

Before starting to use the TPM, you should own it for this you need the TPM command line tools.

# apt-get install tpm-tools trousers

With the tools installed, you should be able to take ownership of the TPM chip.

# tpm_takeownership
Enter owner password:
Confirm password:
Enter SRK password:
Confirm password:

You should enter the owner password and the Storage Root key password. If this step fails, please look at the more detailed documentation here : here

Preparing the encrypted partition

In Linux you should encrypt your partition when you install the distribution.

install screenshot

Tick the "Encrypt the new Linux Mint installation" box, to enable disk encryption during the installation.

Finding the encrypted partition name

After finishing the installation, and booting your brand new system, you should find the name of your encrypted partition.

# sudo dmsetup status
mint--vg-swap_1: 0 16531456 linear 
sda3_crypt: 0 498563072 crypt 
mint--vg-root: 0 482025472 linear 

In this case 'sda3_crypt' indicates sda3 is the encrypted partition. I'll use 'sda3' in the rest of the examples from now on.

Getting the PCR hash

Now we know what partition we have as our crypt partition, we should add the PCR hash as our password to the partition.

Taking all these registers together and creating an md5 hash out of them provides you with a password you can use to decrypt the drive.

# cat /sys/class/misc/tpm0/device/pcrs | md5sum | cut -f 1 -d ' '
88f6eddc5d0930d5d3938c7114be250b

Adding the PCR hash to the encrypted partition

Now we have the 'PCR' hash, we should add it to the encrypted partitions password list. The partition can have a maximal amount of 8 keys to the filesystem.

# cryptsetup luksAddKey /dev/sda3
Enter any passphrase:
Enter new passphrase for key slot:
Verify passphrase: 

The first passphrase is the passphrase you set during the Linux installation. The second passphrase should be the hash you got from the previous step, make sure not to copy any blank spaces, just the bare hash.

After this step the filesystem should have a passphrase that is reproducable by the initrd boot script, on this system only.

Patching the initrd image

In order for the boot process to use the md5 hash to unlock the encrypted drive, we need to change a script in the initrd image. Also some extra shell commands need to be added. To do this, we will first extract the current initrd image to /tmp/ do the changes, and then build it back into an image again.

Extracting the initrd

First get the current initrd.img content, and place it into a subdirectory of /tmp

# cd `mktemp -d` && gzip -dc /boot/initrd.img-`uname -r` | cpio -ivd

Adding the extra userland commands to initrd

In order for the patched script to work, 2 extra commands need to be added to the initrd userland.

# cp  /usr/bin/cut bin
# cp  /usr/bin/md5sum bin

Patching the boot script

I created a patch so the decryption script will try the PCR hash once, when that fials, the recovery password is asked.

This is the diff of scripts/local-top/cryptroot

262,266c262,275
<           if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \
<                $cryptkeyscript "$cryptkey" | $cryptcreate --key-file=- ; then
<               message "cryptsetup: cryptsetup failed, bad password or options?"
<               continue
<           fi
---
>       if [ $count -eq 1 ] && [ -e "/sys/class/misc/tpm0/device/pcrs" ]; then 
>         cryptmd5hash=`cat "/sys/class/misc/tpm0/device/pcrs" | md5sum | cut -f 1 -d ' '`
>         if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \
>           echo -n "$cryptmd5hash" |  $cryptcreate --key-file=- ; then
>           message "cryptsetup: cryptsetup failed, RPCs hash didn't match! $cryptmd5hash"
>           continue
>         fi
>       else 
>         if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \
>              $cryptkeyscript "$cryptkey" | $cryptcreate --key-file=- ; then
>           message "cryptsetup: cryptsetup failed, bad password or options?"
>           continue
>         fi
>       fi

link

Download and apply the patch :

# patch scripts/local-top/cryptroot <path to cryptroot.patch>

Building the new initrd

Now we have the modified initrd tree, we should build it back into an image.

# find . | cpio --create --format='newc' > /tmp/newinitrd
# gzip /tmp/newinitrd
# mv /tmp/newinitrd.gz /tmp/tpminitrd.img

Setting up the Grub 2 bootloader

In order for us to be able to boot using the new image, we should add a new Grub entry. Also for debug purposes we should make the Grub boot interactive.

Setting the timeout for the Grub 2 bootloader

In '/etc/default/grub' we should set the following values (leave the others):

GRUB_DEFAULT=10
GRUB_HIDDEN_TIMEOUT=10
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_TIMEOUT=10

Adding the extra menu item

In '/boot/grub/grub.cfg' find the entry beginning with 'menuentry'

For example on my laptop :


menuentry 'Linux Mint 17.3 Cinnamon 64-bit' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-5edb2c36-20ff-415f-9e7f-cb75ea78e4c4' {
  recordfail
  load_video
  gfxmode $linux_gfx_mode
  insmod gzio
  insmod part_gpt
  insmod ext2
  set root='hd0,gpt2'
  if [ x$feature_platform_search_hint = xy ]; then
    search --no-floppy --fs-uuid --set=root --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hint-baremetal=ahci0,gpt2  0a426075-b408-4459-b96f-734a958ada04
  else
    search --no-floppy --fs-uuid --set=root 0a426075-b408-4459-b96f-734a958ada04
  fi
  linux /vmlinuz-3.19.0-32-generic root=/dev/mapper/mint--vg-root ro  quiet splash $vt_handoff
  initrd  /initrd.img-3.19.0-32-generic
}

Copy the config section from your grub.cfg, and paste it below the content already present in '/etc/grub.d/40_custom' Next change the line starting with 'initrd' to 'initrd /tpminitrd.img'.

For example on my machine '/etc/grub.d/40_custom' looks like


#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries.  Simply type the
# menu entries you want to add after this comment.  Be careful not to change
# the 'exec tail' line above.

menuentry 'Linux Mint 17.3 Cinnamon 64-bit TPM, with Linux 3.19.0-32-generic' --class ubuntu --class gnu-linux --class gnu --class os  {
    recordfail
    load_video
    gfxmode $linux_gfx_mode
    insmod gzio
    insmod part_gpt
    insmod ext2
    set root='hd0,gpt2'
    if [ x$feature_platform_search_hint = xy ]; then
      search --no-floppy --fs-uuid --set=root --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hint-baremetal=ahci0,gpt2  0a426075-b408-4459-b96f-734a958ada04
    else
      search --no-floppy --fs-uuid --set=root 0a426075-b408-4459-b96f-734a958ada04
    fi
    echo  'Loading Linux 3.19.0-32-generic ...'
    linux /vmlinuz-3.19.0-32-generic root=/dev/mapper/mint--vg-root ro  quiet splash $vt_handoff
    echo  'Loading initial ramdisk ...'
    initrd  /tpminitrd.img
}

Installing the new Grub config

Now we have fixed the initrd and Grub 2 config, we must update Grub 2, this will install the new boot menu items.

# update-grub2

Rebooting and testing

Next Reboot the linux system and hit 'ESC' after the bios screen has past. You should see the Grub menu and choose 'Linux Mint 17.3 Cinnamon 64-bit TPM, with Linux 3.19.0-32-generic'. The machine should boot straight into the login prompt, and no longer ask for the Luks disk password.

Managing the Keys (passphrases) in the Luks container

When ever the Grub config kernel or initrd change, a new hash is generated. Because we don't want old phrases lying around, we need to remove and add new phrases.

BE WARE REMOVING ALL PASSPHRASES WILL LOCK YOU OUT

Be carefull to remove slot '0' as this should contain your recovery key.

First see which Slots are occupied

# cryptsetup luksDump /dev/sda3
LUKS header information for /dev/sda3

Version:        1
Cipher name:    aes
Cipher mode:    cbc-essiv:sha256
Hash spec:      sha1
Payload offset: 4096
MK bits:        256
MK digest:      e5 88 07 f2 4b ce 79 21 85 34 f7 a6 e3 0b 6b b2 a7 b8 d5 a1
MK salt:        0c dd 95 3d 1e 30 1f 66 d4 5e 31 03 12 a0 61 29
                e5 ef 34 8e 13 5d 80 76 8b 4a 0a c3 55 02 22 d3
MK iterations:  5750
UUID:           e4971160-047b-49ce-8246-b63f1fb67db9

Key Slot 0: ENABLED
        Iterations:             23233
        Salt:                   ff bc fc 78 98 5d 35 50 97 76 37 b4 70 99 38 44
                                9f bd a1 b9 02 2d 4d 1d 18 b5 dc f6 4c a0 37 fc
        Key material offset:    8
        AF stripes:             4000
Key Slot 1: ENABLED
        Iterations:             23956
        Salt:                   3a a0 06 83 d3 e0 ba da b0 5c e2 56 cb ed 72 69
                                76 9a 8a b8 e1 eb e6 90 44 b3 71 7a 2f 96 80 39
        Key material offset:    264
        AF stripes:             4000
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED

We see that slot 0 and one have been enabled. Since we changed the system, and the old hash is no longer valid, we are going to delete slot one, and add the new key.

Delete slot one

# cryptsetup luksKillSlot /dev/sda3 1
Enter any remaining passphrase:

Add the new hash, using the hash from step one to enter as passphase in step 2 Also use your recovery password to set the new key to the Luks container.

# cat /sys/class/misc/tpm0/device/pcrs | md5sum | cut -f 1 -d ' '
88f6eddc5d0930d5d3938c7114be250 
# cryptsetup luksAddKey /dev/sda3
Enter any passphrase:
Enter new passphrase for key slot:
Verify passphrase:

TODO