DEV Community

Cover image for Encrypted Data and the Cloud
Ben Hilburn
Ben Hilburn

Posted on • Updated on • Originally published at bhilburn.org

Encrypted Data and the Cloud

Originally published here: Storing Encrypted Data in the Cloud.

I recently decided to put some of my most critically secure data in the cloud. I spent time over several weeks researching how best to do this, and got advice from some of my colleagues with the thickest tinfoil hats. The purpose of this post is to document my approach, mostly so that in the future I'll remember what I did. Perhaps it will also be useful to others who care about security but, like me, are not infosec experts.

Like many others, I had been using an encrypted USB drive which stored my most important secure data. For me, that's mostly my PGP revocation certificates and private master keys; I only keep subkeys on my laptop. If I had a bitcoin wallet, it would be on there, too, along with some other keys and credentials.

This always made me anxious, though. USB keys are really easy to lose, and if someone really wanted to, easy to steal. The drive is well-encrypted, so losing it wouldn't necessarily pose a great risk to the data (ignoring the wrench scenario), but it would be a royal PITA for me to recover from the data loss. And remembering to carry it around anytime I might need it was also somewhat obnoxious. Backing up data and making it easily available from anywhere are both things that cloud storage are really good at, so the solution seems pretty obvious.

Security and convenience are generally at two opposite ends of a sliding scale. Here, though, I honestly don't think I sacrificed much in the way of security for dramatically more convenience.

Background

My scheme really isn't anything special, and isn't all that different from an encrypted USB drive (i.e., an encrypted portable filesystem). The primary difference is that instead of keeping the filesystem on a USB drive, it's just a file that I store in the cloud. Or, put differently, it's an encrypted file that contains a filesystem. When I want to use it, I decrypt and mount it just like any other block device. I'm also using detached LUKS headers to provide something like 2FA.

This is a common approach, and really isn't novel in any way. As I said, this post is mostly for me to document my own research and process.

NB: This approach works well for me because the entire encrypted "file" is only tens-of-MB; relatively small. If you need to store a significant amount of data, however, this scheme could be painful as the entire file must be re-uploaded with every change. For simple scenarios like mine, though, it works well.

It's worth noting that using the same process I describe below you can easily containerize data into different encrypted files (e.g., your PGP master key in one, your Bitcoin wallet in another). As long as you aren't re-using passwords, this compartmentalization provides even more security.

LUKS

I'm using the Linux Unified Key System (LUKS) for encryption. It's easy to use on Linux systems, and well-trusted. On the downside, it is not supported on non-Linux systems, namely macOS. I don't have a Mac at the moment, though, so that's not currently a problem.

[Edit: When I originally published this on my personal blog, I didn't have a mac. Now I have a mac that I use daily at work, and so I have to do this in a Docker. The added inconvenience is admittedly a bit of a pain, but since Docker is already a normal part of my workflow, it's not terrible. That line above about not really caring that it doesn't work on macOS is kind of funny, now, though :-p]

There are a few things worth being aware of when you create a LUKS block device, which I'll lay out here. LUKS can be confusing to understand at first, especially if you don't have a background in cryptography. The ArchLinux wiki has a great ASCII visual that shows how LUKS works:

When you format something with LUKS, you provide the user passphrase in the above visual. This is not the password that is used to encrypt the data. A salt is added to the passphrase, and the result is input into a key derivation function. The result of this key derivation function is then used to protect the master key, which is what is used to actually encrypt the data. For the key derivation function, LUKS defaults to "Password Based Key Derivation Function 2" (PBKDF2), which is part of IETF RFC 2898. I found this Stack Overflow post on password hashing to be tremendously educational in understanding this structure.

LUKS is used through cryptsetup, which supports a number of other schemes, as well.

Passwords

The password you use makes a critical difference in the security of your data. The cryptsetup / LUKS FAQ has a good technical analysis of this. From that link, some back-of-the-envelope math on cost-to-break by password entropy:

Unfortunately, the way we have been taught to think about passwords is wrong. XKCD has a great comic on this topic:

I recommend reading the FAQ link on password entropy, and then playing with this Password Strength Test to get a feel for how strong different passwords are.

Password Hashing

The PKBDF2 algorithm used by LUKS will apply some hash function many times to thwart brute-force attacks.

If you run sudo cryptsetup --help, it will dump the defaults for your cryptsetup installation. On my system, this shows:

Default PBKDF2 iteration time for LUKS: 2000 (ms)

Default compiled-in device cipher parameters:
    loop-AES: aes, Key 256 bits
    plain: aes-cbc-essiv:sha256, Key: 256 bits, Password hashing: ripemd160
    LUKS1: aes-xts-plain64, Key: 256 bits, LUKS header hashing: sha256, RNG: /dev/urandom
Enter fullscreen mode Exit fullscreen mode

So, on my system, the LUKS default is 256-bit keys hashed with sha256 however many iterations it takes to consume two seconds of wall-clock time. You can change all of these settings with flags to cryptsetup, of course.

To see how different hash functions impact computation time, run sudo cryptsetup benchmark. The output of that command on my system shows:

PBKDF2-sha1       551301 iterations per second for 256-bit key
PBKDF2-sha256     732245 iterations per second for 256-bit key
PBKDF2-sha512     587108 iterations per second for 256-bit key
PBKDF2-ripemd160  324435 iterations per second for 256-bit key
PBKDF2-whirlpool  231575 iterations per second for 256-bit key
Enter fullscreen mode Exit fullscreen mode

The thing to keep in-mind, here, is that performance is different from system to system. If you choose a complex hash algorithm with many iterations that completes in 5 seconds on your current system, but then need to open your LUKS device on some other slower system, you may find it takes much longer. The reverse is also true, and upgrading your system will result in a faster computation time for the same algorithm.

Choosing a Cipher

This is the cipher with which your data is actually encrypted. With LUKS, the storage device never sees unencrypted data. Since the data is encrypted / decrypted on-the-fly, the cipher directly affects disk access speeds. It is entirely possible to outfit your computer with an ultra-fast SSD only to bottleneck its throughput with encryption.

I recommend running sudo cryptsetup benchmark, if you didn't do it earlier, to see the performance benchmarks for your system. On my laptop, I get:

#  Algorithm | Key |  Encryption |  Decryption
     aes-cbc   128b   585.8 MiB/s  2527.1 MiB/s
 serpent-cbc   128b    79.3 MiB/s   502.5 MiB/s
 twofish-cbc   128b   179.8 MiB/s   328.0 MiB/s
     aes-cbc   256b   442.3 MiB/s  1685.8 MiB/s
 serpent-cbc   256b    74.4 MiB/s   476.3 MiB/s
 twofish-cbc   256b   175.7 MiB/s   310.4 MiB/s
     aes-xts   256b  1771.8 MiB/s  2025.3 MiB/s
 serpent-xts   256b   471.8 MiB/s   457.7 MiB/s
 twofish-xts   256b   310.7 MiB/s   281.3 MiB/s
     aes-xts   512b  1561.7 MiB/s  1275.4 MiB/s
 serpent-xts   512b   468.7 MiB/s   455.3 MiB/s
 twofish-xts   512b   312.5 MiB/s   321.8 MiB/s
Enter fullscreen mode Exit fullscreen mode

You'll note that xts mode has significantly better performance, which is expected as it was designed for efficient disk encryption. In terms of key length, 128 bits or more is pretty damned good. Note that when using an xts mode, your key length is actually half of what's listed (so aes-xts with a 256b key actually only uses a 128b key).

Per the earlier dump from cryptsetup --help, the default on my system is aes-xts-plain64 with 256b keys. This looks excellent in terms of throughput and also provides great security, so if I was encrypting a portion of my SSD or a USB drive I would likely roll with that. Since I'm only encrypting a small amount of data contained in a single small file, though, I could go with basically any option and not see a real difference in performance.

Your (P)RNG

Random Number Generators (RNG) play a crucial role in cryptography algorithms. Since you don't have a way of creating truly random data on your computer, these are usually called Pseudo RNGs (PRNG). Linux systems generally provide /dev/random and /dev/urandom for this purpose, and you'll see one of them listed as your RNG in the cryptsetup --help defaults.

Right now, there doesn't seem to be a reason to really worry about which one you are using. If you're interested in the difference between /dev/random and /dev/urandom, this post is helpful.

Detached Headers

At the highest level, a LUKS-encrypted block has two pieces: the header, and the encrypted data. The header is unencrypted, and provides the information necessary to interact with the encrypted data. With cryptsetup, you have the option of storing these two pieces, the header and the data, separately rather than in one 'file'. This is called a detached header. There are two main reasons to use detached headers, in my opinion: backups, and additional security.

First, even if you don't use detached headers, you should back-up your LUKS headers. As the LUKS FAQ states, if you don't back-up your headers you will eventually lose access to your data. It's just a fact of hardware decay.

Secondly, by storing the header separately, even if an attacker got their hands on the encrypted file, it would be totally useless without the header. Per the FAQ, in fact, simply blowing away the LUKS header is a good way to effectively make your data permanently inaccessible. Detached headers essentially provide a method of 2FA. Since the point of this exercise is for me to feel comfortable putting my encrypted data on the Internet, I like the additional security this provides.

Using detached headers with LUKS is super easy: you simply use the --header flag.

Process

So, given that background, here was my process:

Creating the LUKS-Encrypted File

First, allocate the two files - one for the detached header, and one for the encrypted data:

$ fallocate -l 2MiB enc.h
$ fallocate -l 10M encData.bin
Enter fullscreen mode Exit fullscreen mode

Next, we'll create a LUKS block with those files. Here is where you would pass flags like --hash, --cipher, etc., if you wanted to use something other than your cryptsetup defaults.

$ sudo cryptsetup luksFormat -y --header enc.h encData.bin
Enter fullscreen mode Exit fullscreen mode

Now, I recommend inspecting your LUKS header to make sure it reflects what you asked for:

$ sudo cryptsetup luksDump enc.h
Enter fullscreen mode Exit fullscreen mode

Now, we need to open up the LUKS block and create a filesystem on it.

$ sudo cryptsetup luksOpen --header ./enc.h ./encData.bin encVolume
$ sudo mkfs.ext4 /dev/mapper/encVolume
Enter fullscreen mode Exit fullscreen mode

You're done. Now you can mount the device and use it as you would any other disk image in Linux.

Usage

Here's the quick summary of how you use your file day-to-day:

1) Map your LUKS device:

$ sudo cryptsetup luksOpen --header ./enc.h ./encData.bin encVolume
Enter fullscreen mode Exit fullscreen mode

2) Mount the volume:

$ sudo mount /dev/mapper/encVolume <mount point>
Enter fullscreen mode Exit fullscreen mode

3) Do stuff.

4) Unmount the volume:

$ sudo umount <mount point>
Enter fullscreen mode Exit fullscreen mode

5) Unmap the LUKS device:

$ sudo cryptsetup luksClose encVolume
Enter fullscreen mode Exit fullscreen mode

Close

As I said at the top of this post, there isn't anything novel in my scheme or process. The purpose of this article is really for me to document resources I found helpful while researching how best to do this, and to provide a reference for myself in the future. I do hope it's useful to others, though, and of course, if you have comments, suggestions, or questions, please share! =)

Discussion (6)

Collapse
rkfg profile image
rkfg

Using block devices to encrypt to the cloud might be not the best idea since when you change something on it the whole file will be rehashed and reuploaded (fully or partly, depending on the cloud client). Try gocryptfs, it works on both Linux and Mac (in beta) and it encrypts each file individually, including the name. It's also more secure than EncFS which has known vulnerabilities and it's more functional. For example, it has a reverse mode where you mount a plain-text directory and get an encrypted view of it for encrypted backups. It's quite performant and doesn't require anything except FUSE. I use it for quite a while with Syncthing, seems to be robust.

Collapse
bhilburn profile image
Ben Hilburn Author • Edited

Great point, @rkfg ! Rahul raised the same point on Twitter.

I added the clarifying note that in my scenario, the entire block device is only tens-of-MB, so re-uploading the entire file isn't a problem for me. You're both totally right, though, that using this scheme to store a significant amount of data would be painful.

Thanks for sharing your insight and recommendation!

Collapse
endorama profile image
Edoardo Tenani

It's also more secure than EncFS which has known vulnerabilities

@rkfg It's not clear to me ( but I could be missing something, please correct me if so! ) what is the relation between LUKS ( which I suspect means dm-crypt with LUKS ) with EncFS: from this ArchWiki page it seems are 2 distinct tools.

For anybody searching more informations, the gocryptfs comparison chart and the linked ArchWiki page are a good starting point.

Collapse
rkfg profile image
rkfg

LUKS encrypts the whole block device while EncFS works on individual files. They both protect some of your files but using different methods. If you plan to sync these files via network the file by file approach is preferred.

Collapse
pbouillon profile image
Pierre Bouillon

Very interesting and helpful, I'll consider leaving my old USB too

Collapse
bdwakefield profile image
Benjamin D Wakefield

A non-OS specific solution would be awesome. I am primarily on Windows so I've been using a BitLocker USB key for a while.