# dm-crypt largest practical keyfile?

## Zazzman

Looking to update hard drive physical security, and my search engine skills are failing me. What is the largest possible key-size for use in cryptsetup? Is it different between LUKS and plain dm-crypt?

----------

## frostschutz

cryptsetup itself will tell you the limits.

```

# cryptsetup --help 

cryptsetup 2.0.0

Usage: cryptsetup [OPTION...] <action> <action-specific>

[...]

Default compiled-in key and passphrase parameters:

   Maximum keyfile size: 8192kB, Maximum interactive passphrase length 512 (characters)

Default PBKDF2 iteration time for LUKS: 2000 (ms)

Default PBKDF for LUKS2: argon2i

   Iteration time: 2000, Memory required: 1048576kB, Parallel threads: 4

```

So as a keyfile it supports up to 8MiB, or when typing it comes down to 512 bytes.

With LUKS PBKDF2 key derivation, one thing to take note of is that whichever algorithm you use, the supplied key will be reduced to the hash size.

So if you use an 8MiB key, it is possible to produce a significantly shorter key that also works.

Setting up a random LUKS container (100M) and key (4M):

```

# truncate -s 100M luks.container

# truncate -s 4M luks.key

# shred -v -n 1 luks.*

shred: luks.container: pass 1/1 (random)...

shred: luks.key: pass 1/1 (random)...

# cryptsetup luksFormat luks.container luks.key

WARNING!

========

This will overwrite data on luks.container irrevocably.

Are you sure? (Type uppercase yes): YES

```

Testing the key:

```

# cryptsetup luksOpen luks.container lukstest --key-file luks.key --test-passphrase && echo OK || echo FAIL

OK

```

Reducing the key:

```

# rhash --printf '%@{sha-256}' luks.key > luks.sha256

# ls -l luks.*

-rw-r--r-- 1 root root 104857600 Jan  8 01:36 luks.container

-rw-r--r-- 1 root root   4194304 Jan  8 01:35 luks.key

-rw-r--r-- 1 root root        32 Jan  8 01:41 luks.sha256

# cryptsetup luksOpen luks.container lukstest --key-file luks.sha256 --test-passphrase && echo OK || echo FAIL

OK

```

So while our key is 4MiB in size, the 32 byte key (raw binary sha256 hash of the 4MiB file) is also accepted due to an internal oddity of the PBKDF2 algorithm used by LUKS.

Using huge keyfiles is mostly pointless. Basically the only advantage of large keyfiles is to defeat data remanence - recovering 32 bytes is a lot more likely, than recovering megabytes of data. This is also why LUKS uses 128KiB as key material per keyslot. LUKS doesn't want data in say, a hard drive's reallocated sectors you can no longer reliably delete, to be useful to recover LUKS headers.

Using binary data keyfiles also has downsides. It's possible to use them the wrong way... when piping such keys instead of using the file directly, cryptsetup might stop reading at newline character. So the key you are actually using is a lot shorter (or even empty) than you believe it to be.

For this reason alone I prefer keys that are human-readable, without newlines, as it leaves no room for interpretation.

```

$ printf "%s" $(pwgen -s 32 1) > keyfile # no newline at end of file!

$ stat keyfile

  File: keyfile

  Size: 32           Blocks: 8          IO Block: 4096   regular file

$ cat keyfile

eMKhrRWQNJ7WW7cFKPrtBb0vD6wTuihA

```

That way you have a key that works as a keyfile, works when reading from stdin, and works when typing it on a keyboard. There is no difference between keyfile and passphrase and no room for different interperations.

As for increasing security, if you are concerned about brute-force, it is likely more important to check the iteration counts, and increase that if necessary with the --iter-time option.

By default cryptsetup will invest 1-2 seconds of CPU power into LUKS master key and each individual keyslot, however if you happened to create the LUKS container during a load spike, iteration counts might turn out order of magnitude lower than normal, so it's important to check or LUKS will not be as good against bruteforce attacks as expected.

----------

## Zazzman

 *frostschutz wrote:*   

> cryptsetup itself will tell you the limits.
> 
> [. . .]
> 
> ```
> ...

 

Well... that was simpler than I imagined. Only 8MiB? Larger than that seems only useful for using the key-offset flag, for obscurity even if the key is compromised... and/or having multiple keys in one file with different offsets.

 *frostschutz wrote:*   

> 
> 
> With LUKS PBKDF2 key derivation, one thing to take note of is that whichever algorithm you use, the supplied key will be reduced to the hash size.
> 
> [. . .]
> ...

 Does dm-crypt operate on a similar principle to PBKDF2? If so, then yeah, key files are almost entirely useless.

 *frostschutz wrote:*   

> 
> 
> Using huge keyfiles is mostly pointless. Basically the only advantage of large keyfiles is to defeat data remanence - recovering 32 bytes is a lot more likely, than recovering megabytes of data. This is also why LUKS uses 128KiB as key material per keyslot. LUKS doesn't want data in say, a hard drive's reallocated sectors you can no longer reliably delete, to be useful to recover LUKS headers.
> 
> Using binary data keyfiles also has downsides. It's possible to use them the wrong way... when piping such keys instead of using the file directly, cryptsetup might stop reading at newline character. So the key you are actually using is a lot shorter (or even empty) than you believe it to be.
> ...

  well, there's always 

[code]vim $keyfile

:%s///gc/code]

To remove line breaks. But thanks for bringing it up, I had no idea! I'd have been hosed after about ~60 characters in.

 *frostschutz wrote:*   

> 
> 
> As for increasing security, if you are concerned about brute-force, it is likely more important to check the iteration counts, and increase that if necessary with the --iter-time option.
> 
> By default cryptsetup will invest 1-2 seconds of CPU power into LUKS master key and each individual keyslot, however if you happened to create the LUKS container during a load spike, iteration counts might turn out order of magnitude lower than normal, so it's important to check or LUKS will not be as good against bruteforce attacks as expected.

 

Now, cryptsetup uses the ciphers compiled into the kernel? The --iter-time option *is* choosing where I am on a trade off between security and accessibility.

----------

## salahx

There's a difference between the passphrase key and the key ultimately passed to dm-crypt. When you create a LUKS container, a random key (or, for XTS mode, 2 random keys) are created. For each passphrase, another key is generated using PBDKF2 and THAT key is used to encrypt the random key. 

The random key is then passed to dm-crypt to do the actual encrpytion/decrpytion.

So in my case:

```

cryptsetup luksOpen luks.container lukstest --key-file luks.sha256

dmsetup table lukstest --showkeys

lukstest: 0 200704 crypt aes-xts-plain64 9251bd2e7be7a06e74527f84ea2271fe84b1f442b5b9fdea5014d453a7e86531 0 7:0 4096

```

The 9251... is the actual random key (of course, yours will be different),. Note: cryptsetup 2.0.0 stash the key in the kernel keyring, hiding it from view from --show-keys - which is probably for the best, because with this information i can now bypass LUKS entirely :

```

# MYLOOP=`losetup -f --show luks.container`

# dmsetup create lukstest --table '0 200704 crypt aes-xts-plain64 9251bd2e7be7a06e74527f84ea2271fe84b1f442b5b9fdea5014d453a7e86531 0 '"$MYLOOP"' 4096'

[do whatever to /dev/mapper/lukstest]

# dmsetup remove lukstest

# losetup -d $MYLOOP

```

If you're intested in controlling the device mapper directly, see https://wiki.gentoo.org/wiki/Device-mapper

----------

