Cryptsetup & dm-crypt

By Paulus, 7 April, 2018

cryptsetup is a command line tool that interfaces with the dm_crypt kernel module that creates, access, and manages encrypted devices.

Warning

    • Debugging The --debug option does not leak the passphrase, however, using strace does.
    • Backups Always have a fresh backup before attempting anything, especially if you plan on reencrypting a device.
    • Cloning Do not distribute cloned encrypted devices because the clone will also include the LUKS header and master key.
    • Distribution Installers there is a chance that if you are installing a new distribution or version of a distribution where your data is in an encrypted container, the installer may not activate it resulting in loss of data. Ubuntu has been known to have a habit of killing encrypted containers.
    • Non-interactive Formatting There is no warning when formatting a device for encryption.
    • Passphrase is not the Master Key The LUKS passphrase is used in decrypting a master key that is randomly selected on header creation. Even if you create a new LUKS header with the same exact parameters with the same passphrase the master key will NOT be the same.

Setup

Before using the cryptsetup tool, the necessary kernel modules must either be available either as modules or into the kernel. For distributions such as Fedora and Ubuntu, the module are already there. For systems running Gentoo, you must do this yourself.

Kernel Configuration

General setup  --->
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
[*] Enable loadable module support
Device Drivers --->
    [*] Multiple devices driver support (RAID and LVM) --->
        <*> Device mapper support
        <*>   Crypt target support

In addition to the following options, enable any others you need for other tools.

[*] Cryptographic API --->
    <*> XTS support
    <*> SHA224 and SHA256 digest algorithm
    <*> AES cipher algorithms
    <*> AES cipher algorithms (x86_64)
    <*> User-space interface for hash algorithms
    <*> User-space interface for symmetric key cipher algorithms

To use TrueCrypt, tcplay/VeraCrypt, enable the following options.

Device Drivers --->
    [*] Block Devices ---> 
        <*> Loopback device support 
File systems ---> 
     <*> FUSE (Filesystem in Userspace) support 
[*] Cryptographic API ---> 
     <*> RIPEMD-160 digest algorithm 
     <*> SHA384 and SHA512 digest algorithms 
     <*> Whirlpool digest algorithms 
     <*> LRW support 
     <*> Serpent cipher algorithm 
     <*> Twofish cipher algorithm

Cryptsetup Installation

# echo "sys-fs/cryptsetup kernel nls python udev # urandom" >> /etc/portage/package.use
# emerge sys-fs/cryptsetup

Useflags for sys-fs/cryptsetup:

        • gcrypt Use dev-libs/libgcrypt crypto backend
        • nls add native language support
        • python add support for python.
        • reencrypt Build cryptsetup-reencrypt
        • udev Enable virtual/udev integration (device discovery, power and storage device support, etc)
        • urandom Use /dev/urandom instead of /dev/random
        • kernel Use kernel crypto backend (mainly for embedded systems)
        • libressl Use dev-libs/libressl (fork of openssl) crypto backend
        • nettle Use dev-libs/nettle crypto backend
        • openssl Use dev-libs/openssl crypto backend
        • pwquality Use dev-libs/libpwquality for password quality checking
        • static Link modules statically
        • static-libs Static versions of libraries

Python targets for sys-fs/cryptsetup:

        • python2_7
        • python3_4
        • python3_5
        • python3_6

When merging cryptsetup, only one crypto backends can be selected (gcrypt, kernel, nettle, libressl, or openssl). using gcrypt does not allow you to statically build the package.

# dnf -y install cryptsetup cryptsetup-reencrypt
# yum -y install cryptsetup cryptsetup-reencrypt
# aptitude -y install cryptsetup

The dm_crypt module should be autoloaded along with any other cipher modules. If not, create a file with the dm_crypt

dm_crypt

Selecting an Algorithm

dm-crypt is able to use different operating modes such as --type luks (Linux Unified Key Setup), --type plain for using dm-crypt plain mode when the master-key will not be on the device, --type loopaes for a loopaes legacy mode, and --type tcrypt for TrueCrypt compatibility.

If you're using Gentoo and enabled the kernel flag, you can view the list of available ciphers in /proc/crypto

To get an idea of the read and write speeds with various ciphers run cryptsetup benchmark

PBKDF2-sha1       181540 iterations per second for 256-bit key
PBKDF2-sha256     218453 iterations per second for 256-bit key
PBKDF2-sha512     151528 iterations per second for 256-bit key
PBKDF2-ripemd160  156224 iterations per second for 256-bit key
PBKDF2-whirlpool  148607 iterations per second for 256-bit key
#  Algorithm | Key |  Encryption |  Decryption
     aes-cbc   128b   157.6 MiB/s   227.7 MiB/s
 serpent-cbc   128b    70.8 MiB/s   220.1 MiB/s
 twofish-cbc   128b   189.6 MiB/s   238.8 MiB/s
     aes-cbc   256b   161.0 MiB/s   173.3 MiB/s
 serpent-cbc   256b    88.7 MiB/s   221.8 MiB/s
 twofish-cbc   256b   195.6 MiB/s   240.9 MiB/s
     aes-xts   256b   217.8 MiB/s   221.8 MiB/s
 serpent-xts   256b   200.0 MiB/s   204.6 MiB/s
 twofish-xts   256b   219.7 MiB/s   219.1 MiB/s
     aes-xts   512b   169.3 MiB/s   171.7 MiB/s
 serpent-xts   512b   202.5 MiB/s   203.9 MiB/s
 twofish-xts   512b   219.6 MiB/s   218.3 MiB/s

When it comes to selecting the algorithm, aes-xts is enough for the average user.

Keys and Passphrases

A keyfile is a file whose data is used as the passphrase to unlock an encrypted volume. That means if such a file is lost or changed, decrypting the volume may no longer be possible. When no key file is specified during the setup of the encrypted device, then a passphrase will need to be entered manually. When selecting a passphrase, the length of it doesn't matter as much as entropy.

Characters/words Entropy
a-z 4.7
a-z0-9 5.2
a-zA-Z0-9 5.9
a-zA-Z0-9!@#$%^&:-+ 6.2
Average English word 0.6 - 1.3
Sentences Decrease entropy

Based on entropy, the number of guesses needed to find the correct one is 1/2 * 2^entropy. In short, choose a passphrase that has a entropy of at least 80 Bits.

Passphrase keyfile is a key file containing a simple passphrase. The benefit of this type of key file is that if the file is lost the data it contained is known and hopefully easily remembered by the owner of the encrypted volume. However the disadvantage is that this does not add any security over entering a passphrase during the initial system start.

# echo -n "mypassphrase" > /root/encryption.key
# chown root:root /root/encryption.key
# chmod 0400 /root/encryption.key

Building on the concept of passphrase keyfiles, the contents of the passphrase keyfile can be completely random. The benefit of this type of keyfile is that it is much more resistant to dictionary attacks than a simple passphrase. An additional strength of keyfiles can be utilized in this situation which is the length of data used. Since this is not a string meant to be memorized by a person for entry, it is trivial to create files containing thousands of random characters as the key. The disadvantage is that if this file is lost or changed, it will most likely not be possible to access the encrypted volume without a backup passphrase.

# dd bs=512 count=4 if=/dev/urandom of=/root/encryption.key
# chmod 600 /root/encryption.key

When creating a keyfile anywhere, will leave a trace even after you delete the file. Using the shred is ineffective due to journaling file systems and solid state storage. The best way to create a key without leaving a trace of it is to do it in RAM and move it immediately to a dedicated flash drive.

# mkdir /mnt/ram 
# mount -t ramfs ramfs /mnt/ram
# dd bs=512 count=4 if=/dev/urandom of=/mnt/ram/encryption.key
# mv /mnt/ram/encryption.key /run/media/root/usb-key

Using a binary file as a keyfile, such as images, text, video, etc. When identifying files as candidates for a keyfile, it is recommended to choose files that are relatively static such as photos, music, video clips. The benefit of these files is that they serve a dual function which can make them harder to identify as keyfiles. Instead of having a text file with a large amount of random text, the keyfile would look like a regular image file or music clip to the casual observer. The disadvantage is that if this file is lost or changed, it will most likely not be possible to access the encrypted volume without a backup passphrase. Additionally, there is a theoretical loss of randomness when compared to a randomly generated text file. This is due to the fact that images, videos and music have some intrinsic relationship between neighboring bits of data that does not exist for a text file. However this is controversial and has never been exploited publicly.

Key Storage

One-Time Key is a key that changes at each boot. This key is usually created using /dev/urandom during dm-crypt setup. The key is not stored anywhere and no passphrase is needed. This type of key can only be used on file systems that can be formatted during reboot; swap or /tmp.

Removable storage allows you to access the key when needed but the data is vulnerable when decrypted and left unattended.

Using a hash value from a key phrase does not require the key to be stored in a file.

Encrypting the key using GPG and storing it on another device such as a flash drive where the passphrase used to encrypt the key can be changed by reencrypting it with a different one.

Using a Smart Card isn't directly implemented with dm_crypt, but can be implemented using a series of key-scripts

Encrypting the Device

Given that you want to use /dev/sdb to store your sensitive data on, you can simply run the following command:

# cryptsetup luksFormat /dev/sdb1

To see what defaults are used, simply run:

...snip...
Default compiled-in key and passphrase parameters:
        Maximum keyfile size: 8192kB, Maximum interactive passphrase length 512 (characters)
Default PBKDF2 iteration time for LUKS: 2000 (ms)

Default compiled-in device cipher parameters:
        loop-AES: aes, Key 256 bits
        plain: aes-cbc-essiv:sha256, Key: 256 bits, Password hashing: ripemd160
        LUKS1: aes-xts-plain64, Key: 256 bits, LUKS header hashing: sha256, RNG: /dev/random

On my system, the previous cryptsetup command is equivalent to the following.

# cryptsetup --cipher aes-xts-plain64 --key-size 256 --hash sha256 --iter-time 2000 --use-urandom --verify-passphrase luksFormat /dev/sdb1

When using xts ciphers, the key size is split in half. To obtain the desired key size, such as 256, 512 must be used. --iter-time is the number of milliseconds to spend with PBKDF2 passphrase processing.

# cryptsetup luksFormat --type luks2 /dev/sdb /root/encryption.key
# cryptsetup --type plain -v open /dev/sdb encd
# cryptsetup --type plain --cipher=serpent-xts-plain64 --hash=sha256 --key-size=256 --offset=10  open /dev/mapper/encd encd2
# parted -s -a optimal /dev/mapper/encd2 -- mklabel gpt mkpart primary encrypted 0 -1
# mkfs.ext4 /dev/mapper/encd21

Key Management

Up to 8 keys can be used with encrypted devices.

Key Slot 0: ENABLED
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
# cryptsetup luksAddKey /dev/sdb /root/encryption.key

cryptsetup will add the specified key to the next available slot. To use a different slot, use the -S option along with the desired slot. If no key is indicated, then a password prompt will be displayed.

cryptsetup luksAddKey /dev/sda -s 3

To change the key in a particular slot:

cryptsetup luksChangeKey /dev/sda3 -s 3

There are three subcommands that are used to remove keys:

        1. luksRemoveKey will remove the key in a slot by specifying the passphrase/key file.
        2. luksKillSlot will remove the key in a particular slot and replace it with a new one.
        3. luksErase will remove all keys without prompting for a passphrase or warning.

LUKS Header Management

# cryptsetup luksHeaderBackup /dev/sdb --header-backup-file /root/sdb-header.img
# mount -t ramfs ramfs /mnt/ram
# cryptsetup luksHeaderBackup /dev/sdb --header-backup-file /mnt/ram/sdb-header.img
# gpg2 --recipient  --encrypt /mnt/ram/sdb-header.img
# cp /mnt/ram/sdb-header.img.gpg /mnt/backup/
# umount /mnt/ram
# cryptsetup luksDump /dev/sdb | grep "Payload offset"
Payload offset:	4040
# fdisk -l /dev/sdb | grep "Sector size"
# mount -t ramfs ramfs /mnt/ram
# cp /mnt/backup/sdb-header.img.gpg /mnt/ram
# gpg2 --decrypt /mnt/ram/sdb-header.img.gpg
# dd if=/mnt/ram/sdb-header.img of=/dev/sdb bs=512 count=4040

Re-encrypting

cryptsetup-reencrypt can be used to convert an unencrypted FS to an encrypted one, remove encryption, or re-encrypt the device. Re-encryption is not possible for detached LUKS header or other encryption modes (e.g., plain-mode).

Default LUKS header requires 4096 512-byte sectors. In order to encrypt an unencrypted device:

        • Unmount the device.
        • Check the file system using fsck.
        • Resize the file system using the appropriate tool to the smallest possible size.
        • Reduce the device by 4096 sectors so the LUKS header can be added then encrypt it using the default options.
        • Open the newly created encrypted disk
        • Resize the newly created encrypted device to its maximum size.
        • Mount the mapped device and continue using it.
# umount /mnt/data
# fsck -f /dev/sdb1
# resize2fs -M /dev/sdb1
# cryptsetup-reencrypt /dev/sdb1 --new  --reduce-device-size 4096S
# cryptsetup open /dev/sdb1 recrypt
# resize2fs /dev/mapper/recrypt
# mount /dev/mapper/recrypt /mnt

To reencrypt a device using the same options and only changing the passphrase:

# cryptsetup-reencrypt /dev/sdb1

The ability to change the luks header may be limited by its size.

Cipher mode:   	cbc-essiv:sha256
Payload offset:	2048
MK bits:       	128

It's possible to upgrade the encryption of a device but only so in two steps.

        1. Re-encrypting with the same encryption options, but using the --reduce-device-size option to make further space for the larger LUKS header.
        2. Re-encypt the whole device again with the desired cipher.

Since a backup should always be made in any case, create a new encrypted device and restoring to it will be faster.

Unlocking at Boot

For a secondary device, add an entry to the /etc/crypttab. In this file, each line represents one entry, all text after `#` is ignored. Each field on the line is separated by a space where the first two fields are required.

        1. Name to map the device to.
        2. Device path or UUID=
        3. Encryption password
        4. Encryption options
          • discard - allows discard requests to be passed to the device.
          • cipher= - specifies the cipher for the encrypted device.
          • hash= - specifies the hash for the the encrypted device.
          • header= - Used for LUKS devices that have a detached head. This option specifies where that header is located.
          • offset= - Start offset in the backend device, using 512-byte sectors. Only works for plain devices.
          • skip= - Number of 512-byte sectors of the encrypted data to skip from the beginning. This indicates where the data on the device starts.
          • keyfile-offset= - Number of bytes to skip at the start of the keyfile.
          • keyfile-size= - Maximum number of bytes to read from the keyfile. This is ignored in plain mode.
          • key-slot= - Which slot the to compare the key file or passphrase.
          • luks - Force LUKS mode. When used, cipher,hash,size are ignored since they are provided by the LUKS header.
          • _netdev - Marks the device as requiring network connectivity.
          • noauto - Device will not be added to the cryptsetup.target meaning it will not be unlocked on boot.
          • nofail - Device is not a hard dependency of the cryptsetup.target. It is still pulled in and started but they system will not wait for the device to show up and be unlocked.
          • offset= - Start offset in the backend device, in 512-byte sectors. This is only relevant for plain devices.
          • plain - Force plain encryption mode.
          • read-only, readonly - Sets the encrypted device in read-only mode.
          • skip= - Number of 512-byte sectors of encrypted data to skip at the beginning.
          • size= - Specifies the key size in bits.
          • swap - Device will be used as a swap device, and will be formatted accordingly after setting up the encrypted block device, with mkswap. Implies plain
          • tcrypt - Enables TrueCrypt mode. When used, cipher, hash, keyfile-offset, size are ignored since they are provided by the TrueCrypt header.
          • tcrypt-hidden - Use the hidden TrueCrypt volume. Implies tcrypt.
          • tcrypt-keyfile= - Specifies the absolute path to a keyfile to use for a TrueCrypt volume. Implies tcrypt. This option can be specified more than once.
          • tcrypt-system - TrueCrypt system encryption mode.
          • tcrypt-veracrypt - Check for a VeraCrypt colume. VeraCrypt is a fork of TrueCrypt that is mostly compatible but uses different, stronger key derivation algorithms that cannot be detected without this flag. This option could substantially slow down unlocking due to the aforementioned.
          • timeout= - Specifies the timeout for querying for a password. Value is in seconds by default, appending s, ms, us, min, h, d. A value of 0 waits indefinitely, the default wait time.
          • tmp - Device will be prepared for using it as /tmp, being formatted using mke2fs. Implies plain
          • tries= - Number of times a user is asked for the password. Default is 3, 0 means there is no limit.
          • verify - Requires the user to enter the passphrase twice.
          • x-systemd.device-timeout= - How long systemd should wait for a device to show up before giving up on the entry.

Further Reading