Matthew Garrett ([personal profile] mjg59) wrote2023-04-17 03:44 pm
Entry tags:

PSA: upgrade your LUKS key derivation function

Here's an article from a French anarchist describing how his (encrypted) laptop was seized after he was arrested, and material from the encrypted partition has since been entered as evidence against him. His encryption password was supposedly greater than 20 characters and included a mixture of cases, numbers, and punctuation, so in the absence of any sort of opsec failures this implies that even relatively complex passwords can now be brute forced, and we should be transitioning to even more secure passphrases.

Or does it? Let's go into what LUKS is doing in the first place. The actual data is typically encrypted with AES, an extremely popular and well-tested encryption algorithm. AES has no known major weaknesses and is not considered to be practically brute-forceable - at least, assuming you have a random key. Unfortunately it's not really practical to ask a user to type in 128 bits of binary every time they want to unlock their drive, so another approach has to be taken.

This is handled using something called a "key derivation function", or KDF. A KDF is a function that takes some input (in this case the user's password) and generates a key. As an extremely simple example, think of MD5 - it takes an input and generates a 128-bit output, so we could simply MD5 the user's password and use the output as an AES key. While this could technically be considered a KDF, it would be an extremely bad one! MD5s can be calculated extremely quickly, so someone attempting to brute-force a disk encryption key could simply generate the MD5 of every plausible password (probably on a lot of machines in parallel, likely using GPUs) and test each of them to see whether it decrypts the drive.

(things are actually slightly more complicated than this - your password is used to generate a key that is then used to encrypt and decrypt the actual encryption key. This is necessary in order to allow you to change your password without having to re-encrypt the entire drive - instead you simply re-encrypt the encryption key with the new password-derived key. This also allows you to have multiple passwords or unlock mechanisms per drive)

Good KDFs reduce this risk by being what's technically referred to as "expensive". Rather than performing one simple calculation to turn a password into a key, they perform a lot of calculations. The number of calculations performed is generally configurable, in order to let you trade off between the amount of security (the number of calculations you'll force an attacker to perform when attempting to generate a key from a potential password) and performance (the amount of time you're willing to wait for your laptop to generate the key after you type in your password so it can actually boot). But, obviously, this tradeoff changes over time - defaults that made sense 10 years ago are not necessarily good defaults now. If you set up your encrypted partition some time ago, the number of calculations required may no longer be considered up to scratch.

And, well, some of these assumptions are kind of bad in the first place! Just making things computationally expensive doesn't help a lot if your adversary has the ability to test a large number of passwords in parallel. GPUs are extremely good at performing the sort of calculations that KDFs generally use, so an attacker can "just" get a whole pile of GPUs and throw them at the problem. KDFs that are computationally expensive don't do a great deal to protect against this. However, there's another axis of expense that can be considered - memory. If the KDF algorithm requires a significant amount of RAM, the degree to which it can be performed in parallel on a GPU is massively reduced. A Geforce 4090 may have 16,384 execution units, but if each password attempt requires 1GB of RAM and the card only has 24GB on board, the attacker is restricted to running 24 attempts in parallel.

So, in these days of attackers with access to a pile of GPUs, a purely computationally expensive KDF is just not a good choice. And, unfortunately, the subject of this story was almost certainly using one of those. Ubuntu 18.04 used the LUKS1 header format, and the only KDF supported in this format is PBKDF2. This is not a memory expensive KDF, and so is vulnerable to GPU-based attacks. But even so, systems using the LUKS2 header format used to default to argon2i, again not a memory expensive KDFwhich is memory strong, but not designed to be resistant to GPU attack (thanks to the comments pointing out my misunderstanding here). New versions default to argon2id, which is. You want to be using argon2id.

What makes this worse is that distributions generally don't update this in any way. If you installed your system and it gave you pbkdf2 as your KDF, you're probably still using pbkdf2 even if you've upgraded to a system that would use argon2id on a fresh install. Thankfully, this can all be fixed-up in place. But note that if anything goes wrong here you could lose access to all your encrypted data, so before doing anything make sure it's all backed up (and figure out how to keep said backup secure so you don't just have your data seized that way).

First, make sure you're running as up-to-date a version of your distribution as possible. Having tools that support the LUKS2 format doesn't mean that your distribution has all of that integrated, and old distribution versions may allow you to update your LUKS setup without actually supporting booting from it. Also, if you're using an encrypted /boot, stop now - very recent versions of grub2 support LUKS2, but they don't support argon2id, and this will render your system unbootable.

Next, figure out which device under /dev corresponds to your encrypted partition. Run

lsblk

and look for entries that have a type of "crypt". The device above that in the tree is the actual encrypted device. Record that name, and run

sudo cryptsetup luksHeaderBackup /dev/whatever --header-backup-file /tmp/luksheader

and copy that to a USB stick or something. If something goes wrong here you'll be able to boot a live image and run

sudo cryptsetup luksHeaderRestore /dev/whatever --header-backup-file luksheader

to restore it.

(Edit to add: Once everything is working, delete this backup! It contains the old weak key, and someone with it can potentially use that to brute force your disk encryption key using the old KDF even if you've updated the on-disk KDF.)

Next, run

sudo cryptsetup luksDump /dev/whatever

and look for the Version: line. If it's version 1, you need to update the header to LUKS2. Run

sudo cryptsetup convert /dev/whatever --type luks2

and follow the prompts. Make sure your system still boots, and if not go back and restore the backup of your header. Assuming everything is ok at this point, run

sudo cryptsetup luksDump /dev/whatever

again and look for the PBKDF: line in each keyslot (pay attention only to the keyslots, ignore any references to pbkdf2 that come after the Digests: line). If the PBKDF is either "pbkdf2" or "argon2i" you should convert to argon2id. Run the following:

sudo cryptsetup luksConvertKey /dev/whatever --pbkdf argon2id

and follow the prompts. If you have multiple passwords associated with your drive you'll have multiple keyslots, and you'll need to repeat this for each password.

Distributions! You should really be handling this sort of thing on upgrade. People who installed their systems with your encryption defaults several years ago are now much less secure than people who perform a fresh install today. Please please please do something about this.
ext_1793530: (Default)

Thanks for the heads up!

[identity profile] unixbhaskar.wordpress.com 2023-04-18 01:57 am (UTC)(link)
My external backup drive is LUKS protected ...time to look in.

systemd-cryptenroll

(Anonymous) 2023-04-18 04:00 am (UTC)(link)

I use systemd-cryptenroll for TPM–based automatic unlock. I’m sure doing so is a bad idea for many reasons, but regardless, I noticed the LUKS keyslot that systemd-cryptenroll creates is hardcoded to pbkdf2.

Indeed, looking at the source, it appears this decision was made because the author “found the Wikipedia entry relevant”:

  • https://github.com/systemd/systemd/blob/25d9c6cdaf82d3f627db92b69f3be3e2a68e06fa/src/cryptenroll/cryptenroll-tpm2.c#L182
  • https://github.com/systemd/systemd/blob/25d9c6cdaf82d3f627db92b69f3be3e2a68e06fa/src/shared/tpm2-util.c#L3068

Argon2i is memory hard

(Anonymous) 2023-04-18 04:00 am (UTC)(link)

systems using the LUKS2 header format used to default to argon2i, again not a memory expensive KDF.

Argon2 is parameterized by CPU cost, memory cost, and parallelization factor. You can read more at https://github.com/P-H-C/phc-winner-argon2, but Argon2 is memory hard, which includes Argon2i, Argon2d, and the hybrid Argon2id.

(Anonymous) 2023-04-18 04:38 am (UTC)(link)
His encryption password was supposedly greater than 20 characters and included a mixture of cases, numbers, and punctuation

So that's at least 64^20 = 2^120 variants. I call bullshit on brute-forcing. There was some kind of opsec failure.

PKBDF2

(Anonymous) 2023-04-18 06:52 am (UTC)(link)

Unfortunately, PBKDF2 is still the only KDF approved by NIST. While this doesn't affect home users, companies that are required to comply with US government regulations are stuck with PBKDF2 for now. Yet another case where NIST's regulations eventually yield questionable security...

(Anonymous) 2023-04-18 06:57 am (UTC)(link)
it is, but if you specify your password is 20 character mixture of cases, numbers, and punctuation, wouldn't you mean *random* characters?

Of course, people use imprecise language all the time, so it could be one that he made up himself, or a pwgen's default algorithm password, but not for example an xkcd correct horse battery staple password

Re: systemd-cryptenroll

(Anonymous) 2023-04-18 07:29 am (UTC)(link)
How so? If I rip the disk from the system, there's no TPM to begin with. And analogous to the situation in the article, I would be attacking the "passphrase" the TPM would hand out. So it's back to Attacker vs. PBKDF2. What am I missing here?

Re: systemd-cryptenroll

(Anonymous) 2023-04-18 07:55 am (UTC)(link)
Yes, I'm talking about the key the TPM returns. According to https://github.com/systemd/systemd/blob/25d9c6cdaf82d3f627db92b69f3be3e2a68e06fa/src/cryptenroll/cryptenroll-tpm2.c#L269, this gets base64-encoded and then used just like any passphrase.

And in line 265, is forces libcryptsetup to use a minimum version of PBKDF2.

It seems they assume the key returned by the TPM has a high enough entropy to make a proper KDF unnecessary. Is this a valid assumption?

-- sur5r

(Anonymous) 2023-04-18 08:09 am (UTC)(link)
Thank you for this writeup, it is obviously quite important and hitting the waves.

That said, as a PSA it is not very good. Consider adding a TL;DR or summary. Few people will actually reach the bottom as it's quite a bit of introduction.

"Delete this backup"

(Anonymous) 2023-04-18 08:36 am (UTC)(link)
Hi Matthew,
You mention to delete the luksHeader backup, but you don't mention it should be deleted using wipe(1).

HTH,
Colin

Re: "Delete this backup"

(Anonymous) 2023-04-18 09:35 am (UTC)(link)
what use do you find for wipe on (1) ramdisk, (2) non-spinning-rust, (3) encrypted spinning rust?

(Anonymous) 2023-04-18 10:23 am (UTC)(link)
> if you specify your password is 20 character mixture of cases, numbers, and punctuation, wouldn't you mean *random* characters?

Absolutely not. A password, that contains upper- and lowercase letters, numbers, and punctuation symbols, just contains these CLASSES of characters, meaning "at least one of each class".

Re: "Delete this backup"

(Anonymous) 2023-04-18 11:30 am (UTC)(link)
It has no interest on RAM disk once power has been cut - as far as I know. On an encrypted hard disk it is useful in case the encryption is broken. On "non-spinning-rust", it is very much useful. I suppose you are aware that deleting a file just means removing its directory entry (more or less), and this is why photorec, for example, can extract about 100% of deleted pictures on an USB stick or memory card ?

Matthew suggest to dump the luks header on "an USB stick or something", which makes absolute sense when you're working on your boot disk's encryption, so the safe thing is to wipe that file off the USB stick after.

HTH,
Colin

Changing KDF of digest

(Anonymous) 2023-04-18 11:32 am (UTC)(link)
Hi mjg,

thanks for this helpful post.

I have successfully changed the KDF to argon2id for my keyslots, however, the digest is still using pbkdf2:

Digests:
0: pbkdf2
Hash: sha256
Iterations: 149114
Salt: xxx
Digest: xxx

Is this a security issue? Is is possible to change this to argon2id as well?

(Anonymous) 2023-04-18 11:59 am (UTC)(link)
A password of 20 random ASCII characters has more than 130 bits of entropy. Such a password cannot be cracked, even if MD5 is used. His password either was not random at all, or someone stole it from him (keylogger, note etc.).

By the way SHA2 is not really slower than MD5 at all. It is even designed to be fast. The cracking of passwords is hard because of the exponential search space, not because of the algorithm speed. 128 bits are impossible to brute force, so even MD5 is safe in this regard. It is vulnerable to collision attacks, but that does not help with password cracking.

Because of this, KDF slowdown is snakeoil for many applications. The complexity of the password is more important than the slowdown of the KDF function. However, it makes a practical difference so your recommendations are still valid for disk and file encryption.

Initrd

(Anonymous) 2023-04-18 12:16 pm (UTC)(link)
Gentle reminder that initrds are not authenticated at the moment. It's also possible that some software on the machine was insecure (installed on the side or not updated; I doubt they used otherwise unknown issues since they're expensive).

It's pretty likely the group of people in this has been infiltrated at some point, or simply that the places have been for a few minutes at least Evil maid attacks are fairly quick to conduct when the target is simply the initrd.

Separate header

(Anonymous) 2023-04-18 12:34 pm (UTC)(link)
I always have the header separated from the disks especially when I'm travelling. You can put the headers on some cloud or web server, even maybe as an "addon" to an image etc. They are small, you can always download them from anywhere when you need 'em. At least this makes travelling way easier, no keys, no headers, no questions. Looks like an empty disk. You can always say: I bought the disk/laptop/whatsoever some days ago, haven't used that part of the disk yet...

But anyway, it's always better to see if there's a way to break the encryption. So, thank you for this real good article, I guess, I will start converting the next days...

Works for me

(Anonymous) 2023-04-18 12:59 pm (UTC)(link)
Thank you ! Following your step-by-step guide I successfully converted my disk to LUKS2/argon2id.

Re: Separate header

(Anonymous) 2023-04-18 01:15 pm (UTC)(link)
Hi there !
Interested in what you said.
How can I remove the header so the disk looks empty ?

Re: "Delete this backup"

(Anonymous) 2023-04-18 02:14 pm (UTC)(link)
On non-spinning-rust, even if it may be useful, it's probably not enough, as the data would only be erased when garbage collection kicks in. Of course, if the filesystem isn't mounted with `discard`, wiping instead of removing ensures that the blocks are deallocated instantly in the device's page table (rather than waiting for a scheduled trim). With non-spinning-rust, ensuring that the data is physically gone from the media has become harder : writing a firmware that is able to read deallocated pages is probably not that hard, especially for government agencies.

Re: Separate header

(Anonymous) 2023-04-18 02:45 pm (UTC)(link)
When unlocking the block device you use a file by adding the '--header' parameter to your call to 'cryptsetup'.

If you have your detached header, you can overwrite the original header of the encrypted block device with random data.

Here is a lengthy and technical description of how to do it:
https://wiki.archlinux.org/title/Dm-crypt/Specialties#Encrypted_/boot_and_a_detached_LUKS_header_on_USB

Page 1 of 4