1

I had a setup where my removable device contained a single partition with encrypted data and nothing else on it to distinguish it from random data. On a physically separate device, I had a pair of scripts that opened/closed the device using a GPG-encrypted key:


open.sh

#!/bin/bash

# The crypt UUID is hard-coded. That way it will not try to decrypt the wrong
# device.
cryptuuid='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

# The UUID of the decrypted device is hard-coded. This is currently how the
# script knows what to mount after decrypting.
deviceuuid='yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy'

# The encrypted key bits are fed to cryptsetup with specific parameters to
# decrypt the crypt.
gpg2 --decrypt <<-  END | sudo cryptsetup --offset 0 --cipher aes-xts-plain64 --key-size 256 --hash sha256 --key-file - open --type plain "/dev/disk/by-partuuid/$cryptuuid" Secrets
    -----BEGIN PGP MESSAGE-----

    zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
    zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
    zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
    zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
    zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
    zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
    zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
    zzzzz
    -----END PGP MESSAGE-----
END

# A small delay is necessary to give time for the decrypted device to show up.
sleep 1

# Mount the decrypted device as if the user did it using the GUI.
gvfs-mount -d $(readlink -f $(blkid -U "$deviceuuid"))

close.sh

#!/bin/bash

# The UUID of the decrypted device is hard-coded. This is currently how the
# script knows what unmount.
deviceuuid='yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy'

# Find all processes using the decrypted device and issue a “friendly” kill
# signal.
sudo lsof -t $(findmnt -rn -S UUID="$deviceuuid" -o TARGET) | while read process; do kill -HUP $process; done

# Try to unmount the decrypted device and then close it with cryptsetup. findmnt
# identifies the path which the device is mounted. lsblk finds the decrypted
# device (minus “/dev/” from the UUID). dmsetup finds the decrypted device name
# given the block device name from before.
gvfs-mount -u $(findmnt -rn -S UUID="$deviceuuid" -o TARGET) && sudo cryptsetup close $(sudo dmsetup info --columns --noheadings --options Name --select BlkDevName=$(lsblk --raw --noheadings --output KNAME,UUID | awk '{if ($2=="'"$deviceuuid"'") { print $1}}'))

The scripts worked, but ideally, I would like to leverage as much of the built-in GUI support for encrypted partitions as possible, lessening the use of the terminal.

In my attempt to move closer to this goal, I took a look at the Logical Volume Manager (LVM) for its JBOD capability. The idea was to have a LUKS partition that would show up in the file manager, but to also keep the LUKS header away from the encrypted data, leaving the encrypted data indistinguishable from random data.

To accomplish this, I employed 2 volumes:

  1. A file header.img (exactly 2,139,648 bytes)
  2. A physical device /dev/sdx

Illustration of the Setup

header.img contains a GPT partition, which hosts a LVM PV just big enough to contain the entire LUKS header. /dev/sdx contains a GPT partition which hosts a LVM PV containing the encrypted data.

They are created using these steps:

  1. fallocate -l 2139648 header.img creates an image to hold the first PV.
  2. I mount the image and create a 4,112-sector GPT partition (GUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) within it for the PV.
  3. sudo pvcreate --labelsector 0 --metadatatype 2 --pvmetadatacopies 1 --metadatasize 32768b --dataalignment 512b /dev/disk/by-partuuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx creates the header’s physical volume.
  4. For the device (like /dev/sdx) where I want to store the encrypted data, I determine the partition alignment that I need and add one more sector at the beginning for the LVM header. If a SSD drive comes preformatted with a partition beginning at sector 65536, delete that partition and create one beginning at sector 65535. That way, the file system will still be optimally aligned.
  5. sudo pvcreate --labelsector 0 --pvmetadatacopies 0 --metadatasize 0b --dataalignment 512b '/dev/disk/by-partuuid/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' creates a physical volume with just the LVM header but no metadata.
  6. vgcreate --metadatacopies unmanaged --physicalextentsize 512b Secrets '/dev/disk/by-partuuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' '/dev/disk/by-partuuid/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' creates a volume group on top of both of those physical volumes.
  7. lvcreate --contiguous n --extents 100%FREE Secrets --name Crypt creates a single logical volume that occupies all of the free space in the volume group.
  8. Now a peculiar problem I ran into was that if I wrote any data into the logical volume, the largest physical volume would always be written to first. That’s bad, because I want the LUKS header to be written to the smaller of the 2 physical volumes. I fix it by exporting the volume group’s data to a text file where I can reorder the physical volumes: vgcfgbackup -f metadata.txt Secrets.
  9. Editing the resulting text file is straightforward. Under the logical_volumes section, there should be 2 segments named thusly: segment1 and segment2. You will need to edit the start_extent and extent_count values under each of them. segment1 must have start_extent = 0 and extent_count = 4040. And segment2 must have start_extent = 4040 and extent_count equal to the larger physical volume’s pe_count—which you can find under the physical_volumes section. You may need to swap pv0 and pv1 around, but double-check the volume definitions to make sure they’re sane.
  10. vgcfgrestore -f metadata.txt Secrets will apply your changes to the volume group.
  11. cryptsetup --verbose --cipher aes-xts-plain64 --key-size 512 --hash sha512 --align-payload 1 luksFormat /dev/Secrets/Crypt creates the LUKS encrypted volume.
  12. Open the new encrypted volume and do what needs to be done.
  13. To eject: unmount any file system on the encrypted volume, close it, and run vgchange -an Secrets. You can then detach and/or eject the physical volumes.

When /dev/sdx is inserted and header.img is mounted using the Disk Utility, an icon shows up on the Launcher which—when clicked—prompts for a password to unlock the volume; the terminal is not needed in any of the steps except on the random occasion that the logical volume doesn’t register. Then running vgscan --mknodes and udevadm trigger seems to fix the hiccup.

The problem is that when unmounting the volume, I have to also use the terminal to deactivate the LVM volume group (VG) before I remove the loop device and eject the encrypted storage. Physically removing the storage medium does not seem to make the volume group go away and loop devices are prevented from being dismounted.

I would be satisfied with just solving the device removal problem, but I think I’m over-complicating things just by creating and maintaining such a setup.

Does any Linux distribution support easy-to-use, multi-factor full disk encryption today?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.