barrass.dev

Storing Files in Encrypted Disk Images

Posted Tuesday 09 April 2019 at 18:05

Estimated reading time: 11 mins

Everyone has data they want to protect from prying eyes, from a digital diary to more nefarious material. Some hide it in a folder labelled "PRIVATE DO NOT OPEN", followed by a maze of "SERIOUSLY STOP OPENING THESE", but of course there are better ways of protecting your data. Recently I was thinking about ways files could be encrypted. I have an interest in cryptography, so I wanted to see what I could do with this to successfully encrypt some files.

My first thought was of encrypted partitions. I could make one on my hard drive quite easily, but anyone with just a little bit of computer skill could easily find such a partition, and would know I had something to hide. The solution I came up with is to use image files.

For those unaware, the image files essentially act as a virtual hard disk. This can contain partitions which can be mounted, formatted, written to and read from, much like how a swap file can be used similarly to a swap partition.

This article is a guide on how to create an encrypted partition inside such an image file, and how to then use it for storing data.

Required Software

There are a few requirements to install, for which I will provide the apt commands needed to install them on Ubuntu. If you're using a different distro, you'll need to find these packages yourself.

We will also be using udisks to mount the image without root. All that's needed to install on Ubuntu is cryptsetup; everything else should be available as standard.

sudo apt install cryptsetup

Making the Image

In order to create the image, you need root access. This is required for the formatting stage of creating the image. I am not currently aware of any way to get around this, however root access is not required in order to mount your image.

For the purposes of this article, I'm going to be creating a 50MB image file called "encrypted.img", containing an encrypted ext4 partition.

The blank image file will be made using dd. To do this, we use the following command:

dd if=/dev/zero of=<filename> bs=1M count=<size in MB> status=progress

So to create my example, I would do dd if=/dev/zero of=encrypted.img bs=1M count=50 status=progress.

Before we can do anything more with it, we need to create a partition table and partition in the image. This can be accomplished with fdisk. Execute:

fdisk <filename>

(be aware, TAB autocompletion may not work for this -- fdisk is typically used for formatting real devices, not image files. Make sure to type the image file name in full and it will work just fine)

You should see something like the following:

Welcome to fdisk (util-linux 2.27.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognised partition table.
Created a new DOS disklabel with disk identifier 0x1a2a9193.

Command (m for help): 

If you enter the command p, you can see the properties of your image:

Command (m for help): p
Disk encrypted.img: 50 MiB, 52428800 bytes, 102400 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x1a2a9193

Type the following to create a new partition in the image ([RET] means to press the return/enter key): n [RET] p [RET] 1 [RET] [RET] [RET]. This creates a new Linux-format partition that fills the drive. This partition should be visible if you now run the p command.

Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-102399, default 2048): 
Last sector, +sectors or +size{K,M,G,T,P} (2048-102399, default 102399): 

Created a new partition 1 of type 'Linux' and of size 49 MiB.

Command (m for help): p
Disk encrypted.img: 50 MiB, 52428800 bytes, 102400 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x1a2a9193

Device         Boot Start    End Sectors Size Id Type
encrypted.img1       2048 102399  100352  49M 83 Linux

The actual format of the partition shouldn't matter too much -- after all, we're only going to overwrite it afterwards. However, I personally like to make sure that the partition is blank before proceeding. This can be done by executing t [RET] 0 [RET] to designate the partition as empty space. You can confirm that this has been done by executing p again. Alternatively, if you just want to create a mountable disk image, you can stop here.

Command (m for help): t
Selected partition 1
Partition type (type L to list all types): 0
Type 0 means free space to many systems. Having partitions of type 0 is probably unwise.

Changed type of partition 'Linux' to 'Empty'.

Command (m for help): p
Disk encrypted.img: 50 MiB, 52428800 bytes, 102400 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x1a2a9193

Device         Boot Start    End Sectors Size Id Type
encrypted.img1       2048 102399  100352  49M  0 Empty

Finally, write the changes by executing w.

This disk is now mountable, which will allow us to format it as an encrypted partition. We will mount it using udisksctl, by executing:

udisksctl loop-setup -f <filename>

Make note of which loopback device the image gets mounted on -- in my case, this was /dev/loop0.

With the image mounted as a loopback device, we can now use it as though it were a real drive with real partitions. This is how we will format the partition as an encrypted partition. We will use cryptsetup to provide LUKS encryption for our partition.

First of all, the blank partition on the drive must be formatted. This action requires root, so make sure to check and double check which partition you're formatting. Check twice, write once.

First off, format the partition with a LUKS header with the following command:

sudo cryptsetup luksFormat /dev/loop0p1

Replace /dev/loop0 with your loopback device, but don't neglect the p1 suffix! This is what indicates which partition to format. When prompted, enter "YES" in uppercase and the secure passphrase you wish to use to decrypt your partition. With any luck, the command will exit without error and you will have a successfully encrypted partition!

This partition will, however, lack any kind of filesystem, so you won't yet be able to store any data on it. First, you have to open the partition:

sudo cryptsetup luksOpen /dev/loop0p1 encrypted-partition

Again, replace /dev/loop0 with your loopback device. encrypted-partition can be replaced by any identifier, just make sure to be consistent with whatever one you choose. Enter your passphrase when prompted. This will open your encrypted partition and map it to /dev/mapper/encrypted-partition, which will behave just like any other unencrypted partition. This can then be formatted. To format as ext4, use:

sudo mkfs.ext4 /dev/mapper/encrypted

If you're using a format that supports Linux permissions, you'll likely need to change the owner so that your user can write to the drive. First mount the drive (either with sudo mount or udisksctl mount -b):

udisksctl mount -b /dev/mapper/encrypted-partition

or...

sudo mount /dev/mapper/encrypted <mount point>

Now, cd to where the partition has been mounted and execute:

sudo chown $USER:$USER .     # Take ownership of the directory
chmod o- .                  # Give rw to user, nothing to everyone else

You will now be able to create new files and read files in the directory whilst disallowing reading and writing to all other users. The partition can now be unmounted and re-locked, and the loopback device unmounted.

udisksctl unmount -b /dev/mapper/encrypted-partition
sudo cryptsetup luksClose encrypted-partition
udisksctl loop-delete -b /dev/loop0
That's it! The partition has been successfully created, formatted, opened and closed.

Mounting and Unmounting

Of course, you're not going to want to have to go through all of that to manually mount, open, mount and then unmount, close, unmount every time you want to access your partition, and it would be much more convenient if the partition could be accessed without needing root access. Well, udisksctl actually includes commands to unlock (open) and lock (close) your encrypted partitions from user-space, as well as the facilities needed to make loopback devices and mount partitions. Therefore I've created two bash functions below for you to put in your ~/.bashrc or ~/.bash_aliases that will automate the procedure for you.

mount_encrypted_image() {
    if [ "$1" = "" ];
    then
	echo "First argument must be file!"
	return 0
    fi

    filename="$1"
    LOOP="$(udisksctl loop-setup -f $filename | awk 'NF>1{ print substr($NF, 1, length($NF)-1) }')"
    echo "${LOOP}p1"
    unlocked="$(udisksctl unlock -b ${LOOP}p1 | awk 'NF>1{ print substr($NF, 1, length($NF)-1) }')"
    udisksctl mount -b $unlocked
    echo $unlocked > "/tmp/${filename}.device"
}

umount_encrypted_image() {
    if [ "$1" = "" ];
    then
	echo "First argument must be file!"
	return 0
    fi

    filename="$1"
    loop="$(losetup | awk 'FNR>=2{ print $1,$6 }' | grep $filename | awk 'FNR==1{ print $1 }')"
    unlocked="$(cat /tmp/${filename}.device)"
    udisksctl unmount -b "$unlocked" || true
    rm "/tmp/${filename}.device"
    udisksctl lock -b "${loop}p1"
    udisksctl loop-delete -b "$loop"
}

These two functions will set up the loopback devices and mount your partition for you, and then take it down when you need, just from the filename of your image file. To mount your encrypted file, simply run mount_encrypted_image <filename> and enter your passphrase when prompted. The partition will be mounted and should become available through your favourite file manager. You can do everything you need and then close up the partition with umount_encrypted_image <filename>.

The partition should be safely unmounted and closed on system shutdown but if it's important, make sure to do it manually before you shutdown. I personally have had no problems shutting down with the partition still mounted, but your mileage may vary.


Hopefully this guide has been helpful to you. If everything has gone correctly, you should now have an encrypted disk image that you can use like a "locker" for any sensitive data you wish to keep encrypted. This is useful, but I can't make any guarantees about its effectiveness -- if you're using this to try and evade the law, I doubt the law will have much trouble catching you if it came to it. Instead, think of this more as one step in the chain, and put other cryptographic safeguards between your data and anyone who wishes to snoop on it. Thanks for reading!