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.
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:
will show a random UUID, mine was:
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:
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
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):
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
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.
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:
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.