TQ
dev.com

Blog about software development

Subscribe

LUKS with USB unlock

21 Jan 2022 - by 'Maurits van der Schee'

I feel that using full disk encryption of laptops is a must. Not to protect against attacks with physical access (to the unencrypted boot loader or unprotected BIOS), but to avoid leaking data when the laptop is either lost or stolen. Entering a long passphrase is not very convenient, especially when you are sharing the device with multiple people. This post will explain how to unlock your computer by inserting a USB drive containing a key file, while still allowing to unlock using a passphrase. At the end of the post we describe how to conveniently hide the USB drive in Windows and Linux.

USB keys for LUKS disk encryption

Instructions

I have tested the below steps on Ubuntu 22.04 and these are expected to be correct for any recent Debian based distribution.

1) Install the "uuid" tool

sudo apt install uuid

2) Create a random key name using the "uuid" tool:

uuid

will show a random UUID, mine was:

85125e5e-7bc4-11ec-afea-67650910c179

3) Create a 256 byte key file with random data (.lek = LUKS Encryption Key):

dd if=/dev/urandom bs=1 count=256 > 85125e5e-7bc4-11ec-afea-67650910c179.lek

4) Insert a USB drive. It gets mounted somewhere in "/media", let's find it:

lsblk

will show that the 16GB USB stick is mounted as "/media/maurits/B54D-B744":

NAME                   MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sdb                      8:0    1  14,7G  0 disk  
└─sdb1                   8:1    1  14,7G  0 part  /media/maurits/B54D-B744
...

5) Copy the key file to the USB drive:

cp 85125e5e-7bc4-11ec-afea-67650910c179.lek /media/maurits/B54D-B744

6) Find the encrypted volume:

sudo blkid --match-token TYPE=crypto_LUKS -o device

will show:

/dev/sda3

5) Add the key file to the LUKS volume:

sudo cryptsetup luksAddKey /dev/sda3 85125e5e-7bc4-11ec-afea-67650910c179.lek

This does not affect the existing hand-entered passphrase from the installer (in slot 0).

6) Delete the key file (it is loaded in LUKS and copied to the USB drive):

rm 85125e5e-7bc4-11ec-afea-67650910c179.lek

7) Edit /etc/crypttab. You should see a line like:

sda3_crypt UUID=b9570e0f-3bd3-40b0-801f-ee20ac460207 none luks,discard

Modify it to:

sda3_crypt UUID=b9570e0f-3bd3-40b0-801f-ee20ac460207 85125e5e-7bc4-11ec-afea-67650910c179 luks,discard,keyscript=/bin/luksunlockusb

8) Add a script that will search for the key on USB drives during boot:

cat << "END" > luksunlockusb
#!/bin/sh
set -e
if [ ! -e /mnt ]; then
    mkdir -p /mnt
    sleep 3
fi
for usbpartition in /dev/disk/by-id/usb-*-part1; do
    usbdevice=$(readlink -f $usbpartition)
    if mount -t vfat $usbdevice /mnt 2>/dev/null; then
        if [ -e /mnt/$CRYPTTAB_KEY.lek ]; then
            cat /mnt/$CRYPTTAB_KEY.lek
            umount $usbdevice
            exit
        fi
        umount $usbdevice
    fi
done
/lib/cryptsetup/askpass "Insert USB key and press ENTER: "
END

9) Make the script executable and move it to the right location:

chmod 755 luksunlockusb
sudo mv luksunlockusb /bin/luksunlockusb

10) Debian 11 only, add some modules to the /etc/initramfs-tools/modules file:

vfat
nls_cp437
nls_ascii
usb_storage

11) Include the "luksunlockusb" script (and modules) in the initial ram file system using:

sudo update-initramfs -u

12) Done. Reboot and enjoy!

Note that next to inserting the USB drive you can still enter the old passphrase during the boot even though the script has changed the prompt.

Creating a hidden partition

In order to hide the USB drive (prevent the partition from mounting in Linux and Windows) you can create a new GPT partition table on the USB drive and add only a bootable EFI Startup Partition (ESP) to hold your keys. This partition must be the first partition on the drive and have a minimum size of 16 megabytes. Technically it may contain any other content and may be followed by one or more partitions of any type. I prefer to leave them with only this single hidden partition as it prevents (non-technical) people from using the USB stick as a drive (I use key shaped USB drives). You can find some of my scripts on my GitHub:

https://github.com/mevdschee/bitlocker-luks-tools

Alternative: "passdev" keyscript

Some people say that one should use the (Debian) included "passdev" keyscript (see: /lib/cryptsetup/scripts/passdev). You can set it to wait for a USB drive with label 'USB_ENC_KEY' that contains the file '/85125e5e-7bc4-11ec-afea-67650910c179.lek' for a maximum of 10 seconds using:

sda3_crypt UUID=b9570e0f-3bd3-40b0-801f-ee20ac460207 /dev/disk/by-label/USB_ENC_KEY:/85125e5e-7bc4-11ec-afea-67650910c179.lek:10 luks,discard,keyscript=passdev

Note that Debian 11 does NOT support the timeout parameter and also that it doesn't properly unmount the drive, causing the start-job to hang for 1 minute and 30 seconds. I do recommend writing your own keyscript instead of using passdev.

Links


PS: Liked this article? Please share it on Facebook, Twitter or LinkedIn.