Dev Notes

Software Development Resources by David Egan.

LUKS Encrypt Hard Drive with Cryptsetup on Ubuntu 20.04


Linux
David Egan

This article outlines how to LUKS encrypt a secondary drive on Ubuntu 20.04 Focal Fossa using cryptsetup on the command line.

Find the Unmounted Disk

Once you have physically connected the disk, find the unmounted disk in the system using lsblk:

lsblk

# Typical output:
NAME                    MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sda                       8:0    0 465.8G  0 disk
├─sda1                    8:1    0   512M  0 part
├─sda2                    8:2    0   488M  0 part  /boot
└─sda3                    8:3    0 464.8G  0 part
  └─sda3_crypt          252:0    0 464.8G  0 crypt
    ├─ubuntu--vg-root   252:1    0 448.8G  0 lvm   /
    └─ubuntu--vg-swap_1 252:2    0    16G  0 lvm
      └─cryptswap1      252:3    0    16G  0 crypt [SWAP]
sdb                       8:16   0 931.5G  0 disk
└─sdb_crypt             252:4    0 931.5G  0 crypt /media/secure-hdd-2
sdc                       8:32   0   1.8T  0 disk
├─sdc1                    8:33   0 994.9G  0 part  /media/datadrive
└─sdc2                    8:34   0 868.2G  0 part
  └─sdc2_crypt          252:5    0 868.2G  0 crypt /media/secure-hdd
sdd                       8:48   0 931.5G  0 disk
loop0                     7:0    0  83.8M  1 loop  /snap/core/3604
loop1                     7:1    0 178.2M  1 loop  /snap/atom/112
loop2                     7:2    0 174.2M  1 loop  /snap/atom/116
loop3                     7:3    0  28.1M  1 loop  /snap/telegram-latest/4
loop4                     7:4    0  83.8M  1 loop  /snap/core/3748
loop5                     7:5    0  81.3M  1 loop  /snap/core/3887
loop6                     7:6    0 178.2M  1 loop  /snap/atom/111

In this case you can see sdd is the target drive - the size type matches, and it is unmounted (as expected).

Double check disk info:

sudo hdparm -i /dev/sdd

# Output:

/dev/sdd:

 Model=Samsung SSD 860 EVO 1TB, FwRev=RVT01B6Q, SerialNo=S3Z9NB0JC01523A
 Config={ Fixed }
 RawCHS=16383/16/63, TrkSize=0, SectSize=0, ECCbytes=0
 BuffType=unknown, BuffSize=unknown, MaxMultSect=1, MultSect=1
 CurCHS=16383/16/63, CurSects=16514064, LBA=yes, LBAsects=1953525168
 IORDY=on/off, tPIO={min:120,w/IORDY:120}, tDMA={min:120,rec:120}
 PIO modes:  pio0 pio1 pio2 pio3 pio4
 DMA modes:  mdma0 mdma1 mdma2
 UDMA modes: udma0 udma1 udma2 udma3 udma4 udma5 *udma6
 AdvancedPM=no WriteCache=enabled
 Drive conforms to: unknown:  ATA/ATAPI-2,3,4,5,6,7

 * signifies the current active mode

If this checks out, you have the device reference.

Alternatively, list all drives in short format:

 sudo lshw -short -C disk
H/W path         Device     Class          Description
======================================================
/0/1/0.0.0       /dev/sda   disk           500GB Samsung SSD 850
/0/2/0.0.0       /dev/sdb   disk           1TB ST1000DM010-2EP1
/0/3/0.0.0       /dev/sdc   disk           2TB WDC WD20EZRZ-00Z
/0/4/0.0.0       /dev/sdd   disk           1TB Samsung SSD 860

Create a Partition

If the disk does not have an existing partition, create one.

sudo fdisk /dev/sdd

Follow instructions, hit all defaults to create one large partition…it will be /dev/sdd1.

If the disk is already partitioned, you can use an existing partition. Note that all data will be over-written.

LUKS Encryption

Next step is to LUKS encrypt the target partition - in this case, /dev/sdd1:

cryptsetup -y -v luksFormat /dev/sdd1
  • -v: verbose output
  • -y: Forces double entry from the user when interactively setting the passphrase - ask for it twice and complain if both inputs do not match

Create a Mapping

The encrypted partition is accessed by means of a mapping.

To create a mapping for the current session:

# Creates a mapping called backupSSD
sudo cryptsetup luksOpen /dev/sdd1 backupSSD

# Check:
ls -l /dev/mapper/backupSSD
lrwxrwxrwx 1 root root 7 Dec 17 15:48 /dev/mapper/backupSSD -> ../dm-6

# Check mapping status
sudo cryptsetup -v status backupSSD
[sudo] password for david:
/dev/mapper/backupSSD is active and is in use.
  type:    LUKS1
  cipher:  aes-xts-plain64
  keysize: 256 bits
  device:  /dev/sdd1
  offset:  4096 sectors
  size:    1953519024 sectors
  mode:    read/write
Command successful.

This mapping is not persistent. If you want to open the disk/partition automatically on boot, you will need to amend /etc/crypttab to set up a mapped device name.

Crypttab

The file /etc/crypttab contains descriptive information about encrypted filesystems. Each filesystem is described on a separate line.

Fields on each line are separated by tabs or spaces. The order of records in crypttab is important because the init scripts sequentially iterate through crypttab doing their thing.

# /etc/crypttab
# Mapping name in field 1.
# UUID of partition in field 2
# Full path to keyfile in field 3
backupSSD UUID=4f942e15-ff00-4213-aab1-089448b17850 /root/.keyfiles/hdd-1.key luks,discard

The first field, target, describes the mapped device name. It must be a plain filename without any directory components. A mapped device which encrypts/decrypts data to/from the source device will be created at /dev/mapper/target by cryptsetup.

The second field, source device, describes either the block special device or file that contains the encrypted data. Instead of giving the source deviceexplicitly, the UUID is supported as well, using UUID=<luks_uuid>.

The third field, key file, describes the file to use as a key for decrypting the data of the source device. Note that the entire key file will be used as thepassphrase; the passphrase must not be followed by a newline character.

It can also be a device name (e.g. /dev/urandom), note however that LUKS requires a persistent key and therefore does not support random data keys.

If the key file is the string “none”, a passphrase will be read interactively from the console. In this case, the options precheck, check, checkargs and triesmay be useful.

The fourth field, options, describes the cryptsetup options associated with the encryption process. At minimum, the field should contain either the string luksrespectively tcrypt or the cipher, hash and size options.

Options are in the format: key=value [,key=value …]. The supported options are described below.Note that all four fields are mandatory and that a missing field will lead to unspecified behaviour.

–Man page: man crypttab

LUKS Header

Check it using the luksDump command. Data shown is anonymised (junk):

sudo cryptsetup luksDump /dev/sdd1

LUKS header information for /dev/sdd1

Version:       	1
Cipher name:   	aes
Cipher mode:   	xts-plain64
Hash spec:     	sha1
Payload offset:	4096
MK bits:       	256
MK digest:     	ad f8 5d b7 ce 4e 54 f6 17 79 a8 ce f5 07 12 ce 82 b1 94 28
MK salt:	cd 44 e4 6a db a0 b4 ba d1 9d c5 da 1b 97 d1 a2
		c8 a5 17 ac 7b 97 50 42 67 84 fe 74 a6 1f 5f d5               	
MK iterations: 	274632
UUID:          	2e72f987-ffa1-c0c0-d144-098f45d86100

Key Slot 0: ENABLED
	Iterations:         	978243
	Salt:               	1e b5 1f 35 7b 46 24 34 ec ab a2 7d 5a 74 ba 38
	                      	87 7e c9 57 b4 d0 2a ab b7 e1 13 32 92 65 b2 c2
	Key material offset:	8
	AF stripes:            	4000
Key Slot 1: ENABLED
	Iterations:         	4456344
	Salt:			39 68 d2 4e 04 51 a3 c2 c9 88 e8 71 a5 a9 0f d4 
	                      	17 3f 88 0c 39 2c f5 22 35 e5 19 48 3c 1b 47 49
	Key material offset:	264
	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

Amongst other interesting things, the output will show the size of the master key (MK bits). A 256 bit master key will equate to a LUKS header size of 1,052,672 Bytes See the cryptsetup FAQ

Format Partition

Do not omit this step - or the partition won’t mount.

sudo mkfs.ext4 /dev/mapper/backupSSD -L "Extra SSD 1TB"

…where -L denotes the partition label.

Mount Point

Create a mount point. Give the mount point appropriate ownership. E.g.:

sudo mkdir /media/secure-ssd
sudo chown $USER:$USER /media/secure-ssd

Mount

To mount, you need to reference the mapper not the partition:

sudo mount /dev/mapper/backupSSD /media/secure-ssd

Auto Mount

Mount at boot.

Edit /etc/fstab to reference the mapper to the decrypted volume

# Samsung 860 1TB EVO SSD path installed 17-02-2018
# mapper              mount-point       type mount-options dump fs_passno
/dev/mapper/backupSSD /media/secure-ssd ext4 defaults 0 2

Mount-options refer to filesystem related parameters, formatted as a comma-separated list. This contains at least the type of mount (ro or rw), plus any additional options appropriate to the filesystem type (including performance-tuning options). Basic filesystem-independent options are:

  • defaults - use default options: rw, suid, dev, exec, auto, nouser, and async.
  • noauto - do not mount when “mount -a” is given (e.g., at boot time)
  • user - allow a user to mount
  • owner - allow device owner to mount
  • comment - or x- for use by fstab-maintaining programs
  • nofail - do not report errors for this device if it does not exist.

The sixth field (fs_passno) is used by fsck to determine the order in which filesystem checks are done at boot time.

The root filesystem should be specified with a fs_passno of 1. Other filesystems should have a fs_passno of 2. Filesystems within a drive will be checked sequentially, but filesystems on different drives will be checked at the same time to utilize parallelism available in the hardware. Defaults to zero (don’t fsck) if not present.

Decrypt using a key

I set up additonal drives to mount & decrypt automatically by means of a keyfile held in my encrypted boot drive. This means that I just decrypt the primary disk at start up, with a passphrase as usual. The keyfile is then used to decrypt additional disks automatically. Keyfiles are secure since the drive holding the keyfile is encrypted.

Cryptsetup allows you to specify up to 8 keyslots - passwords or keyfiles. When you add these, they are hashed and added to key-slots in the LUKS header at the start of the device. When you first run luksFormat, the initial password you supply is hashed and stored in key-slot 0 of the LUKS header. You can easily add an additional passphrase, and this can take the form of a keyfile. This means that the volume can be decrypted either with the initial passphrase or the keyfile.

To add a password to a LUKS partition, you need an unencrypted copy of the master key - so if the partition is not initialized, you will be prompted for the original passphrase. Because more than one password is possible under LUKS setups, the wording of the password prompt may seem confusing - it says: “Enter any passphrase”. This means enter any valid existing password for the partition.

Create a keyfile:

# Make an appropriate directory
sudo mkdir /root/.keyfiles

# Make a keyfile of randomly sourced bytes
sudo dd if=/dev/urandom of=/root/.keyfiles/hdd-1.key bs=1024 count=4

# Make this read-only by owner (in this case, root):
sudo chmod 0400 /root/.keyfiles/hdd-1.key

Set up a keyfile for the LUKS partition (in this case, /dev/sdd1:

sudo cryptsetup luksAddKey /dev/sdd1 /root/.keyfiles/hdd-1.key

To automatically mount at boot, the mapping in /etc/crypttab should reference the keyfile:

# Modify line to /etc/crypttab if the keyfile has not been added
backupSSD UUID=4f942e15-ff00-4213-aab1-089448b17850 /root/.keyfiles/hdd-1.key luks,discard

Unmount & Secure

umount /dev/mapper/backupSSD
cryptsetup luksClose backupSSD

Decrypt & remount:

cryptsetup luksOpen /dev/sdd1 backupSSD
# mount /dev/mapper/backupSSD /media/mountpoint

Good image of LUKS header

Check Passphrase

sudo cryptsetup luksOpen --test-passphrase /dev/sdX && echo correct

# Prompts for password, echoes "correct" if successful

# Alternative - specify slot
cryptsetup luksOpen --test-passphrase --key-slot 0 /dev/sdX && echo correct

Backup Header Files

sudo cryptsetup luksHeaderBackup --header-backup-file ~/backup-dir/LUKS-headers/my-hostname/sda3 /dev/sda3
sudo cryptsetup luksHeaderBackup --header-backup-file ~/backup-dir/LUKS-headers/my-hostname/sdb /dev/sdb
sudo cryptsetup luksHeaderBackup --header-backup-file ~/backup-dir/LUKS-headers/my-hostname/sdc2 /dev/sdc2
sudo cryptsetup luksHeaderBackup --header-backup-file ~/backup-dir/LUKS-headers/my-hostname/sdd1 /dev/sdd1

Restore Header Files

# General form:
# cryptsetup luksHeaderRestore --header-backup-file <file> <device>

sudo cryptsetup luksHeaderRestore --header-backup-file ~/backup-dir/LUKS-headers/my-hostname/sda3 /dev/sda3
sudo cryptsetup luksHeaderRestore --header-backup-file ~/backup-dir/LUKS-headers/my-hostname/sdb /dev/sdb
sudo cryptsetup luksHeaderRestore --header-backup-file ~/backup-dir/LUKS-headers/my-hostname/sdc2 /dev/sdc2
sudo cryptsetup luksHeaderRestore --header-backup-file ~/backup-dir/LUKS-headers/my-hostname/sdd1 /dev/sdd1

Breaking LUKS Encryption

This article describes a method for breaking LUKS encryption that involves dumping the contents of memory while running cryptsetup luksOpen, and then finding the hashed password. Once an adversary has a copy of the hashed password, they can run it through a password cracker. The method relies on the fact that the hashed password is loaded into memory by cyrptsetup as part of the authentication process. I haven’t checked this vulnerability, but it certainly sounds plausible.

To prevent such an attack, use a complex password. My personal preference is for a memorised > 12 character mneumonic passsphrase drawn from a possible keyspace of 94 (upper & lowercase, number, special character), with additional padding characters to increase overall length up to 22. If you encrypt disks with LUKS and then select “password123” as your passphrase, you may as well not bother.

Appendix

lsblk is a straightforward and concise way to get all available drives & partitions. LUKS partitions/drives have the FSTYPE crypto_LUKS:

david@ubuntu:$ sudo lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,LABEL,UUID

NAME                 FSTYPE        SIZE MOUNTPOINT              LABEL         UUID
sda                              465.8G
├─sda1               vfat          512M /boot/efi                             xxx
├─sda2               ext2          488M /boot                                 xxx
└─sda3               crypto_LUKS 464.8G                                       xxx
  └─sda3_crypt       LVM2_member 464.8G                                       xxx
    ├─ubuntu--vg-root
    │                ext4        448.8G /                                     xxx
    └─ubuntu--vg-swap_1
                     swap           16G                                       xxx
      └─cryptswap1   swap           16G [SWAP]                                xxx
sdb                  crypto_LUKS 931.5G                                       xxx
└─sdb_crypt          ext4        931.5G /media/secure-hdd-2     Barracuda 1TB xxx
sdc                                1.8T
├─sdc1               ext4        994.9G /media/datadrive        Public HD     xxx
└─sdc2               crypto_LUKS 868.2G                                       xxx
  └─sdc2_crypt       ext4        868.2G /media/secure-hdd       Secure HD     xxx
sdd                              931.5G
└─sdd1               crypto_LUKS 931.5G                                       xxx
  └─backupSSD        ext4        931.5G /media/secure-ssd       Extra SSD 1TB xxx
loop0                squashfs     83.8M /snap/core/3604
loop1                squashfs    178.2M /snap/atom/111
loop2                squashfs     83.8M /snap/core/3748
loop3                squashfs    174.2M /snap/atom/116
loop4                squashfs    178.2M /snap/atom/112
loop5                squashfs     81.3M /snap/core/3887
loop6                squashfs     28.1M /snap/telegram-latest/4

Available columns (for –output):

  • NAME: device name
  • KNAME: internal kernel device name
  • MAJ::MIN major:minor device number
  • FSTYPE: filesystem type
  • MOUNTPOINT: where the device is mounted
  • LABEL: filesystem LABEL
  • UUID: filesystem UUID
  • PARTTYPE: partition type UUID
  • PARTLABEL: partition LABEL
  • PARTUUID: partition UUID
  • PARTFLAGS: partition flags
  • RA: read-ahead of the device
  • RO: read-only device
  • RM: removable device
  • HOTPLUG: removable or hotplug device (usb, pcmcia, …)
  • MODEL: device identifier
  • SERIAL: disk serial number
  • SIZE: size of the device
  • STATE: state of the device
  • OWNER: user name
  • GROUP: group name
  • MODE: device node permissions
  • ALIGNMENT: alignment offset
  • MIN:-IO minimum I/O size
  • OPT:-IO optimal I/O size
  • PHY:-SEC physical sector size
  • LOG:-SEC logical sector size
  • ROTA: rotational device
  • SCHED: I/O scheduler name
  • RQ:-SIZE request queue size
  • TYPE: device type
  • DISC:-ALN discard alignment offset
  • DISC:-GRAN discard granularity
  • DISC:-MAX discard max bytes
  • DISC:-ZERO discard zeroes data
  • WSAME: write same max bytes
  • WWN: unique storage identifier
  • RAND: adds randomness
  • PKNAME: internal parent kernel device name
  • HCTL: Host:Channel:Target:Lun for SCSI
  • TRAN: device transport type
  • SUBSYSTEMS: de-duplicated chain of subsystems
  • REV: device revision
  • VENDOR: device vendor

References

Resources


comments powered by Disqus