This guide sets out a method for hiding a LUKS keyfile between the MBR and first partition on a USB drive. When the USB drive is plugged in during boot, the keyfile is automatically read from the USB drive and used to decrypt the root partition.

All security measures must be balanced against usability. My main concern is that if my laptop or PC were stolen, someone might nefariously use the data stored on the hard drives to steal my identity. From a usability perspective, I want it to be possible for my family to access the data on my computers should something ever happen to me. A passphrase dies with me, unless I write it down somewhere on a piece of paper to be lost, but unless I try to escape from a fully armed and operational battle station through a garbage masher without a handry droid to help me get out, a USB drive will probably outlast me. In order for someone to gain unauthorised access, they would have to clone my USB drive and gain physical access to my computers - that’s a pretty targeted attack, and if someone really wants to break in that much, they’re probably not just looking to steal my identity. Remember: you don’t have to outrun the bear, you just have to outrun the other guy.

If you don’t know how to use cryptsetup, then go look at that first. If you mess up and somehow lose the ability to decrypt your drive, you probably deserve it.

There are plenty of guides out there already that describe how to set up an encrypted root partition for your Linux install, including the one on the Arch Linux wiki. This one picks up where those end, and assumes that you already have a functioning Linux system with an encrypted LUKS root partition. The instructions at the end of this guide that relate to setting kernel parameters are specific to Arch Linux (or any other distributions that might use Arch’s mkinitcpio script to generate the initramfs) and make use of the default encrypt mkinitcpio hook.

This will work with any size of USB drive, and with suitable modification will probably work with GPT instead of MBR.

First step, we need to write some random data to the USB drive to use for the key. The keyfile that we generate will be 256 bits (i.e. 32 bytes) - the same size as the default LUKS master key. I see people talking about 4096 byte keyfiles. What’s the point? LUKS only reads the first 256 bytes of your keyfile anyway (need a reference!), and even if it didn’t, the PBKDFv2 function that is used to derive the LUKS key from your keyfile is only 256 bits long, so it just becomes easier to try to crack that directly if your keyfile is longer.

If you’ve deviated from the default key size, e.g. because you’re worried that someone in the year 4,000,000,000 will be able to read your data once they’ve finally cracked the AES-256 encryption, just before the Sun enters its red giant phase and swallows the Earth, then you can make the relevant changes below.

To write random data to the drive, we use dd and /dev/urandom. Make sure you replace /dev/sdc with your USB drive (check using fdisk -l or lsblk). If you choose the wrong drive, you will destroy the partition table and potentially brick your Linux installation.

# dd if=/dev/urandom of=/dev/sdc bs=1 seek=512 count=32

The numer 512 comes from the size of the MBR in bytes and 32 is, of course, the size of the keyfile in bytes. Next, we create the MBR partition table. I have tested this with fdisk - other tools might have different behaviour, and might not leave the random bits that we just created immediately following the MBR intact.

# fdisk /dev/sdc

I won’t go through the inner workings of fdisk - look it up if you need to. Again make sure you replace /dev/sdc with your USB drive. After you have created a partition (or multiple partitions, if you’re that way inclined) on the USB drive, format it as FAT (or some other non-journalled file system) and mount the partition:

# mkfs.fat /dev/sdc1
# mount /dev/sdc1 /mnt/usb

Next, we create the key file from the random data stored after the MBR on the USB drive:

# dd if=/dev/sdc of=/mnt/usb/luks.key skip=512 count=32

Add the key to your LUKS partition (change /dev/sdb5 to your LUKS partition):

# cryptsetup luksAddKey /dev/sdb5 /run/media/michael/DATA/luks.key

And then securely delete the keyfile from the USB drive:

# shred --remove --zero /mnt/usb/luks.key

If you did as you were told and formatted the partition with a crappy old filesystem like FAT, then this should be sufficient to render the keyfile unrecoverable from the partition. Of course, the key itself is sitting unencrypted on the drive itself - the key point here is that the ability to decrypt the root drive is controlled via physical access to the USB drive, so it’s not really a problem if the key is recoverable by someone with physical access to the drive.

Finally, the kernel parameters passed on boot need to be suitably modified. I use the rEFInd bootloader, so I edited /boot/refind_linux.conf to add the following kernel paramter. If you’re using a different bootloader, it should be pretty obvious how to adapt.

cryptkey=/dev/sdc:512:32

The encrypt mkinitcpio hook provided in Arch Linux uses the same dd command to retrieve the key from the USB drive that we used to extract the key from the drive, so we pass it the same parameters skip=512 and count=32. (Note: need to find a way to get persistent device naming to work)

And that should be that.