Ready to Start Your Career?

By: zhak
January 26, 2016
Protecting Your Data in Linux: A Deeper Look at Disk Encryption (Part 4/4)

By: zhak
January 26, 2016
In the previous three parts (one, two, and three) of the article we discussed hard drive encryption with LUKS. Today we will speak about plain mode encryption. This mode is considered advanced and a little bit more complicated. But you'll see that this is actually not true, and, what is more, has its benefits.Since plain mode encryption requires almost the same steps with slight differences only, here I'll describe just these differences.Before we dive into setup, some words about differences between LUKS and plain modes:
#!/bin/bashexport PATH=/bin:/sbin:/usr/bin:/usr/sbinexport HOME=/rootshell() {setsid bash -c ‘exec bash /dev/tty1 2>&1’}mount -t proc proc /procmount -t sysfs sysfs /sysmount -t devtmpfs devtmpfs /devecho 0 > /proc/sys/kernel/printkROOT_DEV=$(lsblk –no KNAME,MODEL | grep 'YOUR_SDD_MODEL_NAME')ROOT_DEV='/dev/'${ROOT_DEV%%'YOUR_SDD_MODEL_NAME'}if [ -n ${ROOT_DEV} ]; thencp –a /dev/tty /dev/tty.origcp -a /dev/console /dev/ttygpg –o /tmp/key –d ${HOME}/boot.key 2>/dev/nullcryptsetup --type=plain -c aes-xts-plain -s 512 -h sha512 -d /tmp/key open ${ROOT_DEV} ssdshred –uz /tmp/keykpartx –u /dev/dm-0[ -b /dev/dm-1 ] && mount /dev/dm-1 /mnt/root || shellcp –a /dev/tty.orig /dev/ttyrm /dev/tty.origexec switch_root /mnt/root /sbin/initelseshellfi
Okay, let's go through it in details.The topmost part till echo is the same. The next goesROOT_DEV=$(lsblk –no KNAME,MODEL | grep 'YOUR_SDD_MODEL_NAME')ROOT_DEV='/dev/'${ROOT_DEV%%'YOUR_SDD_MODEL_NAME'}You remember, in LUKS mode we used ROOT_DEV=$(blkid -U 12345678-0123-4567-0123456789ab) command to discover root partition device node by its UUID. In plain mode this won't work simply because there're no any UUIDs assigned to the device. We need something else to distinguish our hard disk with operating system on it from other attached devices (e.g. USB sticks). We will use device model for this purpose.# lsblk –no KNAME,MODELwill output a table of block devices. The table will contain two columns: internal kernel device name (KNAME) and device identifier (MODEL):sda YOUR_SSD_MODEL_NAMEsdb DataTraveler 3.0sdb1sdcThen we use grep to filter out any irrelevant devices and concatenate '/dev/' and internal kernel device name strings to get full device node path: /dev/sda.The next change is about gpg and cryptsetup operation.gpg –o /tmp/key –d ${HOME}/boot.key 2>/dev/nullcryptsetup --type=plain -c aes-xts-plain -s 512 -h sha512 -d /tmp/key open ${ROOT_DEV} ssdshred –uz /tmp/keyIt seems that in plain mode cryptsetup cannot read passphrase from an input stream. There was a bug submitted about broken "-d -" option in some older versions. I don't know if it's a regression or what, but this hasn't been working for me in amd64-nomultilib cryptsetup 1.6.5. That's why I first decrypt passphrase to a temporary file /tmp/key, and then feed this file to cryptsetup. When key file is no longer needed I shred it – we don't need plain keys in memory, right?The rest is the same: discover root partition on decrypted drive, mount it and switch root. Open shell if error occurs.So, in order to compile, you'll need to add lsblk, grep, and shred utils and /tmp dir to initramfs.NOTE: if you enter incorrect passphrase at boot time, cryptsetup will decrypt the drive with whatever invalid key provided. Of course, partition table won't be discovered correctly, and init script will fail and fallback to the shell. You'll probably need to reboot to try again. But if you're familiar with programming, you could write some small util to check if passphrase is correct. For example, I do it the following way:
- Devices encrypted in plain mode don't contain any headers or metadata. It's just raw encrypted sectors. Looking at such device, you can't say for sure if it's encrypted data on disk or just noise. This grants you so called plausible deniability – you can deny, that hard drive is encrypted: "I've just bought this used laptop with discount, because there's no OS installed on it, and I haven't had a chance to install OS, yet."
- Plain mode grants resilience to data corruption. One damaged encrypted sector is only one damaged encrypted sector. It won't affect the rest data.
- Plain mode is not very convenient in terms of usability – since there's no metadata stored anywhere, you should always remember encryption parameters like algorithm, key size, etc. and provide them each time you want to decrypt the device. However, this can hardly be named a disadvantage in our case, cause cryptsetup arguments will be hardcoded in initramfs.
- Plain mode doesn't offer any features LUKS does, like the ability to use multiple passphrases and change them when needed. Once you encrypted the device in plain mode with some passphrase, it cannot be changed. You forget the passphrase – you lose your data!
- In plain mode cryptsetup cannot check if provided passphrase is correct or not. It will decrypt either way. In case of bad passphrase you'll get junk, so be careful and don't accidentally write to disk if it was decrypted with wrong passphrase, or your good data at written sectors will be lost.
- With plain mode you should be aware of weak passphrases. It should be a good crypto key with high entropy. Again, this may not be an issue in our case, since we encrypt the passphrase with GnuPG and only need to remember its password to decrypt original crypto passphrase.
2. DM_CRYPT PLAIN
Step 4. Format Hard Disk
I will use the same AES-XTS (with 256 bits key) cipher, and first thing to do will be prepare hard disk for encryption in plain mode:# cryptsetup --type=plain -c aes-xts-plain -s 512 -h sha512 -d /mnt/usb-boot/key.file open /dev/sda sdaThat's pretty much it. Hard drive is ready. The same command will be used later to decrypt the disk.Now we should create partitions table and format root partition:# parted -s -- /dev/mapper/sda mktable gpt mkpart primary 2mib 100%# mkfs.ext2 /dev/mapper/sda1Step 5.1 Install Linux Distribution of Your Choice
This step goes without changes from described in part 1Step 5.2. Create Initramfs
The most significant changes compared to LUKS encryption are the ones in initramfs.Here's the sample init script to use with plain mode:#!/bin/bashexport PATH=/bin:/sbin:/usr/bin:/usr/sbinexport HOME=/rootshell() {setsid bash -c ‘exec bash /dev/tty1 2>&1’}mount -t proc proc /procmount -t sysfs sysfs /sysmount -t devtmpfs devtmpfs /devecho 0 > /proc/sys/kernel/printkROOT_DEV=$(lsblk –no KNAME,MODEL | grep 'YOUR_SDD_MODEL_NAME')ROOT_DEV='/dev/'${ROOT_DEV%%'YOUR_SDD_MODEL_NAME'}if [ -n ${ROOT_DEV} ]; thencp –a /dev/tty /dev/tty.origcp -a /dev/console /dev/ttygpg –o /tmp/key –d ${HOME}/boot.key 2>/dev/nullcryptsetup --type=plain -c aes-xts-plain -s 512 -h sha512 -d /tmp/key open ${ROOT_DEV} ssdshred –uz /tmp/keykpartx –u /dev/dm-0[ -b /dev/dm-1 ] && mount /dev/dm-1 /mnt/root || shellcp –a /dev/tty.orig /dev/ttyrm /dev/tty.origexec switch_root /mnt/root /sbin/initelseshellfi
Okay, let's go through it in details.The topmost part till echo is the same. The next goesROOT_DEV=$(lsblk –no KNAME,MODEL | grep 'YOUR_SDD_MODEL_NAME')ROOT_DEV='/dev/'${ROOT_DEV%%'YOUR_SDD_MODEL_NAME'}You remember, in LUKS mode we used ROOT_DEV=$(blkid -U 12345678-0123-4567-0123456789ab) command to discover root partition device node by its UUID. In plain mode this won't work simply because there're no any UUIDs assigned to the device. We need something else to distinguish our hard disk with operating system on it from other attached devices (e.g. USB sticks). We will use device model for this purpose.# lsblk –no KNAME,MODELwill output a table of block devices. The table will contain two columns: internal kernel device name (KNAME) and device identifier (MODEL):sda YOUR_SSD_MODEL_NAMEsdb DataTraveler 3.0sdb1sdcThen we use grep to filter out any irrelevant devices and concatenate '/dev/' and internal kernel device name strings to get full device node path: /dev/sda.The next change is about gpg and cryptsetup operation.gpg –o /tmp/key –d ${HOME}/boot.key 2>/dev/nullcryptsetup --type=plain -c aes-xts-plain -s 512 -h sha512 -d /tmp/key open ${ROOT_DEV} ssdshred –uz /tmp/keyIt seems that in plain mode cryptsetup cannot read passphrase from an input stream. There was a bug submitted about broken "-d -" option in some older versions. I don't know if it's a regression or what, but this hasn't been working for me in amd64-nomultilib cryptsetup 1.6.5. That's why I first decrypt passphrase to a temporary file /tmp/key, and then feed this file to cryptsetup. When key file is no longer needed I shred it – we don't need plain keys in memory, right?The rest is the same: discover root partition on decrypted drive, mount it and switch root. Open shell if error occurs.So, in order to compile, you'll need to add lsblk, grep, and shred utils and /tmp dir to initramfs.NOTE: if you enter incorrect passphrase at boot time, cryptsetup will decrypt the drive with whatever invalid key provided. Of course, partition table won't be discovered correctly, and init script will fail and fallback to the shell. You'll probably need to reboot to try again. But if you're familiar with programming, you could write some small util to check if passphrase is correct. For example, I do it the following way:
- I added CRC of the key file to its end.
- gpg decrypts key file
- check_tool read decrypted file and computes its CRC, then checks if it matches the one in the end of the key file.
- If they match, then cryptsetup is called.
- If CRC doesn't match, then appropriate action is taken.