LUKS is the acronym of Linux Unified Key Setup: it is the most used encryption implementation used on Linux systems and can be configured as an alternative to dm-crypt plain setup. Compared to the latter it provides some additional features like password hashing and salting and the ability to store multiple passwords in the so called LUKS header. In this tutorial I will assume the reader has a certain familiarity with LUKS; if you want to know more about this subject, you can check our basic guide about encrypting linux partitions with luks. The most common way to protect a LUKS device is to use a passphrase, however it is also possible to use a file as a key; in this tutorial we will see how to do this. Let’s go!
In this tutorial you will learn:
- How to create a file with random data to use as a LUKS device key
- How to add a key to a LUKS device
- How to automatically decrypt a LUKS device at boot using a file as a key
Software requirements and conventions used
|Category||Requirements, Conventions or Software Version Used|
|System||Any Linux distribution|
|Other||Root permissions to access encrypted block devices|
|Conventions||# – requires given linux-commands to be executed with root privileges either directly as a root user or by use of
$ – requires given linux-commands to be executed as a regular non-privileged user
Creating a LUKS container
For the sake of this tutorial we will create a LUKS container on a file filled with zeros we will generate using dd. To create the file we can run:
$ sudo dd if=/dev/zero of=/luks-container.img bs=1M count=300
In the example above we used the
/dev/zero file as the dd command input source (
/dev/zero is a “special” file: everytime we read from it, it returns 0s) and
/luks-container.img as destination and argument of the dd
of operand. We instructed dd to read and write 300 blocks of 1MiB of size using respectively the bs and count operands. To use the file as a LUKS container, we must prepare it using cryptsetup; we can run:
$ sudo cryptsetup luksFormat --type=luks1 --hash=sha512 --key-size=512 --cipher=aes-xts-plain64 /luks-container.img
The luksFormat sub-command of cryptsetup is used to initialize a LUKS container and set the initial passphrase. Once we run the command above, will be warned that the operation is destructive, since it will overwrite all the existing data. We will be prompted to confirm that we want to perform the operation; we write YES (capital letters) and press enter to confirm:
WARNING! ======== This will overwrite data on /luks-container.img irrevocably. Are you sure? (Type 'yes' in capital letters): YES
At this point we will be asked to provide and confirm a passphrase that will be used as the first of the eight possible device keys:
Enter passphrase for /luks-container.img: Verify passphrase:
Our LUKS container is now ready. We can use the luksDump subcommand of cryptsetup to dump header information:
$ sudo cryptsetup luksDump /luks-container.img LUKS header information for /luks-container.img Version: 1 Cipher name: aes Cipher mode: xts-plain64 Hash spec: sha512 Payload offset: 4096 MK bits: 512 MK digest: 91 da 2e 2e 7f ea ae a1 f7 81 55 cc b7 27 fd b1 ab f4 65 f1 MK salt: f1 03 65 e2 f1 d7 4e 77 99 48 e8 57 75 65 dd 73 a3 eb a4 24 be 36 9e 84 f7 84 c5 d3 94 2e d8 52 MK iterations: 79054 UUID: ea23c244-2dc5-402e-b23e-d9da3219ff8a Key Slot 0: ENABLED Iterations: 1108430 Salt: 69 99 95 88 6e 2f e8 b9 d8 9c 91 36 b6 a2 55 c1 35 27 c7 da 5d 9a 9e f9 8c ec 70 68 db 41 53 4b Key material offset: 8 AF stripes: 4000 Key Slot 1: DISABLED Key Slot 2: DISABLED Key Slot 3: DISABLED Key Slot 4: DISABLED Key Slot 5: DISABLED Key Slot 6: DISABLED Key Slot 7: DISABLED
In the output above we can see various information are reported: the Cipher name and Cipher mode used for the device, for example. What really interests us in this case, however, is the Key slots section. As you can see, in this case only the first keyslot is used: it stores the passphrase we provided when we formatted the device. In this case, there are a total of 8 slots; 7 are available to store additional keys. We will use one of them to store the file we will use to unlock the LUKS device.
Creating a random-data file to use as a key
Any existing file can be used as a LUKS device key, however it can be more secure to create a file specifically for the purpose, out of random data. To create the file, once again, we will resort to the venerable dd command, this time using
/dev/urandom as data source:
$ sudo dd if=/dev/urandom of=/container-key bs=512 count=8 8+0 records in 8+0 records out 4096 bytes (4.1 kB, 4.0 KiB) copied, 0.000631541 s, 6.5 MB/s
/dev/urandom file works similarly to
/dev/zero but it returns random data every time it is read. This time we read
8 blocks of
512 bytes, creating a file “filled” with
4096 bytes of random data.
Adding the key-file to the LUKS device
Once the file is created, we can add it to the LUKS header, and use it as a key. The cryptsetup sub-command which let us perform this task is luksAddKey.
The first argument it takes is the LUKS device the key should be used for; the second, optional, is the path of a key file to be used as key. If it is omitted the user is prompted to provide a passphrase. Among the options accepted by the command, there is
--key-slot: with it, we can specify what key slot should be used to store the key. In this case we will omit the option, so the first available slot will be used (in this case slot number 1).
To add the file as a LUKS key, we run:
$ sudo cryptsetup luksAddKey /luks-container.img /container-key
We will be asked to provide one already existing passphrase for the container; after we do it, the new key will be added. After the command above is successfully executed, if we run luksDump again, we can observe a new slot is now in use:
[...] Key Slot 0: ENABLED Iterations: 1108430 Salt: 69 99 95 88 6e 2f e8 b9 d8 9c 91 36 b6 a2 55 c1 35 27 c7 da 5d 9a 9e f9 8c ec 70 68 db 41 53 4b Key material offset: 8 AF stripes: 4000 Key Slot 1: ENABLED Iterations: 921420 Salt: 62 54 f1 61 c4 d3 8d 87 a6 45 3e f4 e8 66 b3 95 e0 5d 5d 78 18 6a e3 f0 ae 43 6d e2 24 14 bc 97 Key material offset: 512 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 [...]
Opening the LUKS container
To verify that the key is working, we can now attempt to open the LUKS container using it. For this purpose we use the luksOpen sub-command of cryptsetup: it takes two mandatory arguments:
- The LUKS device
- The name to use to map the device once it is opened.
How can we specify that we want to use a file to open the device? Easy! We use the
--key-file option and pass the path to the key file as its argument. In our
case, to open the device, the complete command to run is:
$ sudo cryptsetup luksOpen /luks-container.img luks-container-crypt --key-file=/container-key
If everything goes as expected, we should find an entry for the opened container under the
/dev/mapper directory, in this case:
By the way, we can now treat the container just as we would do with any block device: perhaps we can create a filesystem on it and mount it:
sudo mkfs.ext4 /dev/mapper/luks-container-crypt && sudo mount /dev/mapper/luks-container-crypt /media
Open a LUKS container automatically at boot
Once we learned how to use a file as a LUKS container key, we can make so that a LUKS device is automatically opened at boot, without user interaction. It goes by itself that this is a setup that poses security risks, so it should be used very carefully! At least in unsafe places, the file used to unlock the device should be accessible only by the root user, and should be itself stored on an encrypted filesystem, otherwise the encryption becomes useless (is the equivalent of using a big fat lock to protect a door but leaving the key where it can be reached by anyone).
To make so that a LUKS container is automatically unlocked at boot, we must specify the needed information inside the
/etc/crypttab file. This file is used to describe encrypted block devices that are setup during the system boot. The syntax to be used in the file is quite easy to understand; in each line we add, we need to specify, in order:
- The name to use for the mapping of the device (in the previous example we used
- The device hosting the LUKS container that should be opened
- The device password (optional)
- The options to use (optional)
In this case we would enter this line:
luks-container-crypt /luks-container.img /container-key luks
On the next boot, the device will be automatically unlocked!
In this tutorial we learned how we can use a file as a key to unlock a LUKS container. Although any file can be used for the purpose, we saw how to use dd to create a random-data file, and we saw how to add it to one of the 8 available LUKS header slots using the luksAddKey command. Finally, we saw how it’s possible to automatically unlock the LUKS container at boot by using a keyfile, providing the needed information inside the
/etc/crypttab file, and we saw why this can represent a possible security risk.