March 27, 2020, 9:14 a.m.

Root LUKS Disk Encryption Raspberry Pi

IMPORTANT: Before you start following this tutorial, I suggest that you create backup of your Raspbian install (the entire microSD card of your Pi, if you are using a microSD card).

I will not be held responsible for the lost of your valuable files.

Situation: You want to encrypt your currently unencrypted Raspbian / partition including your /home partition. Except for the /boot partition. You have a free space that is twice of the total partition size of your /, this free space can be in your Debian or any other GNU+Linux Operating System or in your current Pi storage medium (microSD, USB HDD SATA, etc.).

Steps that we will do:

  1. Backup your Pi’s current root partition and boot partition (Out of the scope of this tutorial).
  2. Update the Raspbian install, install dependencies and reboot. The current version is Raspbian 10.3 Buster (debian 10.3 32bit / Linux 4.19.97-v7+).
  3. Create our encrypted partition with cryptsetup. If your kernel version is greater than 4.21, you may want to use a different cipher for cryptsetup as it should be much faster than AES.
  4. Configure the current system to enable booting with encrypted root partition. (Generate initramfs)
  5. Decrypt and mount our encrypted partition and rsync (copy) all of our current system files to it.
  6. Reboot the system. (The next boot should fail with dropping to initramfs, could not find root device!)
  7. Plug in Monitor and Keyboard to decrypt the encrypted root partition, Generate new initramfs then reboot.
  8. Enjoy

Current system configuration:

I am using a USB SATA HDD that has the unencrypted root partition. The microSD card has the boot partition because my USB SATA HDD cannot boot with Mass Storage Mode (Slow startup of spinning disk!).

By default here are the path of the devices:

/dev/mmcblk0p1 = /boot
/dev/sda1 = /

The microSD will always be detected as /dev/mmcblk0, for the first partition /dev/mmcblk0p1 (this is always the /boot partition), for the root partition it will be /dev/mmcblk0p2.
If you have more USB storage devices, it will be listed as /dev/sda2, /dev/sda3, ... /dev/sdX

My old /etc/fstab file:

proc            /proc           proc    defaults          0       0


# The line below is my unencrypted /boot partition (located on my microSD, detected as 63a3cc91-01, 63a3cc91 - is card UUID, 01 - is the first partition)
PARTUUID=63a3cc91-01   /boot             vfat    defaults          0       2

# The line below is my unencrypted root partition (this is located on my USB SATA HDD, 9f0bb82d is my HDD UUID, 03 - is the partition number of that HDD)
PARTUUID=9f0bb82d-03  /               ext4    defaults,noatime  0       1

# I was using a microSD for my / partition before, I added the lines below to prevent the wear of SD Card
tmpfs /var/tmp tmpfs nodev,nosuid,noatime 0 0
tmpfs /tmp tmpfs defaults,noatime,nosuid 0 0

You may want to update and upgrade your Raspbian:

$ sudo apt update
$ sudo apt upgrade

Install dependencies and reboot:

$ sudo apt-get install cryptsetup lvm2 busybox rsync initramfs-tools
$ sudo reboot

Take note of your current UUIDs:

$ sudo blkid

/dev/mmcblk0: PTUUID="63a3cc81" PTTYPE="dos"
/dev/mmcblk0p1: LABEL_FATBOOT="BOOT" LABEL="BOOT" TYPE="vfat" PARTUUID="63a3cc91-01"
/dev/sda:  PARTUUID="9f0bb82d"
/dev/sda3: LABEL="rfs" UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="ext4" PARTUUID="9f0bb82d-03"
...
/dev/mmcblk0p1 - is the /boot stored in the microSD.
/dev/sda3 - unencrypted current root partition
...

Create our encrypted LUKS partition:

Raspberry Pi does not have a hardware-accelerated AES support, AES encryption/decryption will be very slow! Starting in kernel 4.21, a new encryption cipher can be used with cryptsetup, this is the Google Adiantum. This is much faster than the AES and more secure.
Credits to Kendek: https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=252980&p=1543723&hilit=LUKS#p1543753

If you are using Linux Kernel >4.21 you can try to benchmark that encryption cipher and use that instead of the default AES:

$ sudo cryptsetup benchmark -c xchacha12,aes-adiantum

$ sudo cryptsetup benchmark -c xchacha20,aes-adiantum

Current default cipher benchmark for RPi 3B+:

$ sudo cryptsetup benchmark

# Tests are approximate using memory only (no storage IO).
PBKDF2-sha1       147936 iterations per second for 256-bit key
PBKDF2-sha256     242277 iterations per second for 256-bit key
PBKDF2-sha512     158299 iterations per second for 256-bit key
PBKDF2-ripemd160  115177 iterations per second for 256-bit key
PBKDF2-whirlpool   22021 iterations per second for 256-bit key
argon2i       4 iterations, 110020 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
argon2id      4 iterations, 140072 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
#     Algorithm |       Key |      Encryption |      Decryption
        aes-cbc        128b        25.2 MiB/s        49.2 MiB/s
        aes-cbc        256b        21.0 MiB/s        37.0 MiB/s
        aes-xts        256b        55.5 MiB/s        48.1 MiB/s
        aes-xts        512b        41.5 MiB/s        36.3 MiB/s

We now need to create a new partition to our target drive. You can use gparted with this. Be careful to not delete your current partition or system partitions!

I assume that we have a USB SATA HDD or microSD drive with free space, and uses the old Master Boot Record partition type (I have not tested this on a GPT boot record). The MBR partitioning table allows up to four primary partitions only. Use GPT if you want to create more partitions but you will need to reformat the entire disk to change the partition table.

Plug in your microSD or USB HDD to your computer, I assume that you use GNU+Linux or just get a bootable gparted CD or Virtual Machine.

Open gparted and select your target drive.
NOTE: /dev/sda is always the first drive in your system, in this case it could be your Operating System drive (DO NOT SELECT THIS DRIVE!, YOU CAN DELETE YOUR CURRENT OPERATING SYSTEM with this tool.), for example the target drive is detected as /dev/sda2.

Now, if you have a free space you can create a new partition. It must be the same size as your current Pi partition as we will clone the data from that partition to our encryped partition. If you don’t have a free space, you can shrink your current Pi partition but this may take a long long time and may risk data corruption if you accidentally disconnect the drive, so always create backups.

Create the partition, select the type to unformatted. Double check, then click Apply.

Eject the drive and plug it back in to our Raspberry Pi.

Boot up the Raspberry Pi. Proceed to the steps below.

SSH to your Pi or just plug in Monitor, Keyboard and Mouse. We will now format the partition that we created earlier with cryptsetup to encrypt it.

Display the device partitions, determine the new partition that we created earlier:

$ sudo blkid

/dev/mmcblk0: PTUUID="63a3cc81" PTTYPE="dos"
/dev/mmcblk0p1: LABEL_FATBOOT="BOOT" LABEL="BOOT" TYPE="vfat" PARTUUID="63a3cc91-01"
/dev/sda:  PARTUUID="9f0bb82d"
/dev/sda3: LABEL="rfs" UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="ext4" PARTUUID="9f0bb82d-03"
/dev/sda4: UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="unknown" PARTUUID="9f0bb82d-04"

/dev/sda4 is the partition that we created earlier. We will now format this partition to be encrypted.

/dev/mmcblk0p1 - is the /boot stored in the microSD.
/dev/sda3 - unencrypted current root partition
/dev/sda4 - our created partition

If the Pi’s Linux Kernel is 5.4 (Long Term Support) Release, then you should use the Google Adiantum cipher for a fast encryption/decryption. You can use this command to format the target partition (not tested!):

$ sudo cryptsetup luksFormat --type=luks2 --sector-size=4096 -c xchacha12,aes-adiantum-plain64 -s 256 -h sha512 --use-urandom /dev/sda4

Replace sda4 with your partition name. Credits to rpi forum member Kendek, for the command above.

Otherwise, we are just going to use the default non optimized cryptsetup LUKS cipher. ( I am too lazy to find an optimized alternative for AES. Read: https://security.stackexchange.com/questions/40208/recommended-options-for-luks-cryptsetup)

Start encrypting the partition (formatting), creating a new EXT4 filesystem then mounting it:

$ sudo cryptsetup luksFormat /dev/sda4

WARNING!
========
This will overwrite data on /dev/sda4 irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase for /dev/sda4:
Verify passphrase:

Decrypt the partition:

$ sudo cryptsetup luksOpen /dev/sda4 sda4
Enter passphrase for /dev/sda4:

Create a new EXT4 filesystem with a label of root:

$ sudo mkfs.ext4 -L root /dev/mapper/sda4
mke2fs 1.44.5 (15-Dec-2018)
Creating filesystem with 2615552 4k blocks and 654080 inodes
Filesystem UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Superblock backups stored on blocks:
    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

Mount the partition to /mnt:

$ sudo mount /dev/mapper/sda4 /mnt

Check our UUIDs again:

$ sudo blkid

/dev/mmcblk0: PTUUID="63a3cc81" PTTYPE="dos"
/dev/mmcblk0p1: LABEL_FATBOOT="BOOT" LABEL="BOOT" TYPE="vfat" PARTUUID="63a3cc91-01"
/dev/sda:  PARTUUID="9f0bb82d"
/dev/sda3: LABEL="rfs" UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="ext4" PARTUUID="9f0bb82d-03"
/dev/sda4: UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="crypto_LUKS" PARTUUID="9f0bb82d-04"

/dev/sda4 partition type crypto_LUKS, this means it is a LUKS encrypted partition.

/dev/mmcblk0p1 - is the /boot stored in the microSD.
/dev/sda3 - unencrypted current root partition
/dev/sda4 - our created LUKS encrypted partition

Configure the Raspbian to use the encrypted device as root partition:

Edit the file /boot/config.txt and append our to be created initramfs:

$ sudo nano /boot/config.txt

initramfs initramfs.gz followkernel

Edit the file /boot/cmdline.txt located on /boot and change the root location:

$ sudo nano /boot/cmdline.txt

root=/dev/mapper/crypt cryptdevice=/dev/sda4:crypt

Edit the file /etc/cryptsetup-initramfs/conf-hook and change to CRYPTSETUP=y:

$ sudo nano /etc/cryptsetup-initramfs/conf-hook

#
# Configuration file for the cryptroot initramfs hook.
#

#
# CRYPTSETUP: [ y | n ]
#
# Add cryptsetup and its dependencies to the initramfs image, regardless
# of _this_ machine configuration.  By default, they're only added when
# a device is detected that needs to be unlocked at initramfs stage
# (such as root or resume devices or ones with explicit 'initramfs' flag
# in /etc/crypttab).
# Note: Honoring this setting will be deprecated in the future.  Please
# uninstall the 'cryptsetup-initramfs' package if you don't want the
# cryptsetup initramfs integration.
#

CRYPTSETUP=y

Edit the file /etc/crypttab, add our encrypted luks partition:

crypt  /dev/sda4  none    luks

If you have more drives, you can change the /dev/sda4 to point to it’s UUID:

crypt  /dev/disk/by-uuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  none    luks

Edit the file /etc/fstab, change the root partition to point to our encrypted partition, comment out our old root partition so we can revert to this if we have issues:

proc            /proc           proc    defaults          0       0


# The line below is my unencrypted /boot partition (located on my microSD, detected as 63a3cc91-01, 63a3cc91 - is card UUID, 01 - is the first partition)
PARTUUID=63a3cc91-01   /boot             vfat    defaults          0       2

# The line below is my unencrypted root partition (this is located on my USB SATA HDD, 9f0bb82d is my HDD UUID, 03 - is the partition number of that HDD)
#PARTUUID=9f0bb82d-03  /               ext4    defaults,noatime  0       1

# This is now our encrypted root partition.
/dev/mapper/crypt     /               ext4    defaults,noatime  0       1

# I was using a microSD for my / partition before, I added the lines below to prevent the wear of SD Card
tmpfs /var/tmp tmpfs nodev,nosuid,noatime 0 0
tmpfs /tmp tmpfs defaults,noatime,nosuid 0 0

Generate initramfs (there will be warning, but ignore them for now):

$ sudo mkinitramfs -o /boot/initramfs.gz

Determine whether cryptsetup file is included in our initramfs. It must have that file, otherwise the we will fail to unlock our encrypted partition.

$ lsinitramfs /boot/initramfs.gz | grep cryptsetup


usr/lib/arm-linux-gnueabihf/libcryptsetup.so.12
usr/lib/arm-linux-gnueabihf/libcryptsetup.so.12.4.0
usr/lib/cryptsetup
usr/lib/cryptsetup/askpass
usr/lib/cryptsetup/functions
usr/sbin/cryptsetup

Clone our current system to our new encrypted partition:

Depending on your target medium and your number of files, this may take a while. Do not do anything until this is completed.

$ sudo rsync -avhPHAXx --progress --exclude={/dev/*,/proc/*,/sys/*,/tmp/*,/run/*,/mnt/*,/media/*,/lost+found} / /mnt/

sending incremental file list
./
.autorelabel
              0 100%    0.00kB/s    0:00:00 (xfr#1, ir-chk=1020/1022)
bin/
bin/bash
        925.12K 100%   42.55MB/s    0:00:00 (xfr#2, ir-chk=1165/1188)
bin/btrfs
        814.53K 100%   13.87MB/s    0:00:00 (xfr#3, ir-chk=1164/1188)
bin/btrfs-find-root
        440.88K 100%    5.39MB/s    0:00:00 (xfr#4, ir-chk=1163/1188)
bin/btrfs-image
        473.73K 100%    4.43MB/s    0:00:00 (xfr#5, ir-chk=1162/1188)
bin/btrfs-map-logical
        444.98K 100%    3.48MB/s    0:00:00 (xfr#6, ir-chk=1161/1188)
bin/btrfs-select-super
        440.88K 100%    2.96MB/s    0:00:00 (xfr#7, ir-chk=1160/1188)
bin/btrfsck -> btrfs
bin/btrfstune
        449.07K 100%    2.63MB/s    0:00:00 (xfr#8, ir-chk=1158/1188)
bin/busybox
        743.21K 100%    3.65MB/s    0:00:00 (xfr#9, ir-chk=1156/1188)
bin/bzcmp -> bzdiff
bin/bzdiff
          2.23K 100%   11.15kB/s    0:00:00 (xfr#10, ir-chk=1153/1188)
bin/bzegrep -> bzgrep
bin/bzexe

.
.
.
.

13.63K 100%  166.41kB/s    0:00:00 (xfr#222234, to-chk=0/270837)

sent 6.50G bytes  received 4.50M bytes  1.64M bytes/sec
total size is 7.00G  speedup is 1.08

First boot

We are now done, umount the encrypted partition and reboot.
Plug in your monitor, mouse and keyboard if you have not done so. The first boot will fail and we need to generate initramfs for it to work in the next reboot.

While in the busybox initramfs prompt, we now mount our encrypted partition and exit:

(initramfs) cryptsetup luksOpen /dev/sda4 crypt

WARNING: Locking directory /run/cryptsetup is missing?
Enter passphrase for /dev/sda4:

(initramfs) exit

Begin will now check root file system ... fsck from util-linux ...

The system should be able to boot completely.
After the bootup is completed, we will generate initramfs again to fix initramfs for not detecting the encrypted root partition:

$ sudo mkinitramfs -o /boot/initramfs.gz

Reboot the system and you should now be asked a passphrase when booting your encrypted root partition. You can now delete the old unencrypted partition. You need to generate initramfs after updating your Raspbian.

$ sudo sync
$ sudo reboot

Enjoy!

Video: https://youtube.com/watch?v=67WHIzLaly4

TODO: Dropbear SSH logging and USB Boot Key.

References:

https://robpol86.com/raspberry_pi_luks.html
https://github.com/NicoHood/NicoHood.github.io/wiki/Raspberry-Pi-Encrypt-Root-Partition-Tutorial
https://www.raspberrypi.org/forums/viewtopic.php?f=66&t=219867
https://paxswill.com/blog/2013/11/04/encrypted-raspberry-pi/