Blog about software development


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


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":

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:


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
set -e
if [ ! -e /mnt ]; then
    mkdir -p /mnt
    sleep 3
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
        umount $usbdevice
/lib/cryptsetup/askpass "Insert USB key and press ENTER: "

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:


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:


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.


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