In this blog post I will show how LUKS works perfect with FIDO2 devices. You can learn how to setup a LUKS device and then switch from password to a FIDO2 device. This ensures, that not only a password is required but also a hardware device (security factors “Knowledge: Something you know” and “Possession: Something you have”).

Test environment setup

First we will setup a small test environment. Let’s create a small new (virtual) disk.

 1$ dd if=/dev/zero of=test.img bs=1M count=20
 220+0 records in
 320+0 records out
 420971520 bytes (21 MB, 20 MiB) copied, 0.01 s, 1.2 GB/s
 5
 6$ cryptsetup luksFormat ./test.img
 7
 8WARNING!
 9========
10This will overwrite data on ./test.img irrevocably.
11
12Are you sure? (Type 'yes' in capital letters): YES
13Enter passphrase for ./test.img:
14Verify passphrase:
15
16$ cryptsetup luksDump ./test.img
17LUKS header information
18Version:       	2
19Epoch:         	3
20Metadata area: 	16384 [bytes]
21Keyslots area: 	16744448 [bytes]
22UUID:          	bedfd819-a0e9-43dd-a5c5-29e3c7679a65
23Label:         	(no label)
24Subsystem:     	(no subsystem)
25Flags:       	(no flags)
26
27Data segments:
28  0: crypt
29	offset: 16777216 [bytes]
30	length: (whole device)
31	cipher: aes-xts-plain64
32	sector: 4096 [bytes]
33
34Keyslots:
35  0: luks2
36	Key:        512 bits
37	Priority:   normal
38	Cipher:     aes-xts-plain64
39	Cipher key: 512 bits
40	PBKDF:      argon2id
41	Time cost:  7
42	Memory:     1048576
43	Threads:    4
44	Salt:       6d 1e 78 1c f5 c1 30 06 06 03 21 a2 ad 94 fd 31
45	            b1 b6 c3 8e 70 5a c4 14 36 cb 4b 6e 52 28 ff 84
46	AF stripes: 4000
47	AF hash:    sha256
48	Area offset:32768 [bytes]
49	Area length:258048 [bytes]
50	Digest ID:  0
51Tokens:
52Digests:
53  0: pbkdf2
54	Hash:       sha256
55	Iterations: 589750
56	Salt:       bd 77 84 53 6b cd 48 13 23 5d 09 fa 59 5d d3 5f
57	            72 ad 6e b7 93 57 a8 e8 ca 5d ab b2 0a 56 55 1d
58	Digest:     78 fc 99 e1 fe c5 63 99 1f c0 14 4f f9 fb ca 53
59	            c6 df 40 7b 4d 99 fa 01 a7 cb 5a 95 f6 b6 3e 84

Let’s now setup the FIDO device. For this we need a Yubikey (should also work with other devices which are FIDO compliant). The FIDO device should have set a pin to ensure the security factor possession.

 1$ ykman fido info
 2AAGUID:             00000000-0000-0000-0000-000000000000
 3PIN:                Not set
 4Minimum PIN length: 4
 5
 6$ ykman fido access change-pin
 7Enter your new PIN:
 8Repeat for confirmation:
 9FIDO PIN updated.
10
11$ ykman fido info
12AAGUID:             00000000-0000-0000-0000-000000000000
13PIN:                8 attempt(s) remaining
14Minimum PIN length: 4

Now we have a small disk and the Yubikey FIDO device is configured.

Change LUKS to FIDO

Let’s now add a FIDO secret to the LUKS device. This requires the FIDO password and after the password you have to touch the FIDO device twice.

1$ sudo systemd-cryptenroll --fido2-device=auto test.img
2🔐 Please enter current passphrase for disk ./test.img: ••••••••••••••••••••••••
3Initializing FIDO2 credential on security token.
4👆 (Hint: This might require confirmation of user presence on security token.)
5🔐 Please enter security token PIN: ••••••••••••••••••••••••
6Generating secret key on FIDO2 security token.
7👆 In order to allow secret key generation, please confirm presence on security token.
8New FIDO2 token enrolled as key slot 1.

Let’s have a look what is in the LUKS header now.

 1sudo systemd-cryptenroll ./test.img
 2SLOT TYPE
 3   0 password
 4   1 fido2
 5
 6$ cryptsetup luksDump ./test.img
 7LUKS header information
 8Version:       	2
 9Epoch:         	8
10Metadata area: 	16384 [bytes]
11Keyslots area: 	16744448 [bytes]
12UUID:          	00000000-0000-0000-0000-000000000000
13Label:         	(no label)
14Subsystem:     	(no subsystem)
15Flags:       	(no flags)
16
17Data segments:
18  0: crypt
19	offset: 16777216 [bytes]
20	length: (whole device)
21	cipher: aes-xts-plain64
22	sector: 4096 [bytes]
23
24Keyslots:
25  0: luks2
26	Key:        512 bits
27	Priority:   normal
28	Cipher:     aes-xts-plain64
29	Cipher key: 512 bits
30	PBKDF:      argon2id
31	Time cost:  7
32	Memory:     1048576
33	Threads:    4
34	Salt:       6d 1e 78 1c f5 c1 30 06 06 03 21 a2 ad 94 fd 31
35	            b1 b6 c3 8e 70 5a c4 14 36 cb 4b 6e 52 28 ff 84
36	AF stripes: 4000
37	AF hash:    sha256
38	Area offset:32768 [bytes]
39	Area length:258048 [bytes]
40	Digest ID:  0
41  1: luks2
42	Key:        512 bits
43	Priority:   normal
44	Cipher:     aes-xts-plain64
45	Cipher key: 512 bits
46	PBKDF:      pbkdf2
47	Hash:       sha512
48	Iterations: 1000
49	Salt:       1a bf 2e 7d 28 72 e6 18 f1 25 f2 aa 4c 89 86 1a
50	            4f 0c 95 d6 82 54 ee 34 40 8e 05 fe 1f fa 2e 3d
51	AF stripes: 4000
52	AF hash:    sha512
53	Area offset:290816 [bytes]
54	Area length:258048 [bytes]
55	Digest ID:  0
56Tokens:
57  0: systemd-fido2
58	fido2-credential:
59	            25 f2 ce 8a 37 bf 5c 05 5d 49 f6 63 23 e8 ce 69
60	            2f 06 b8 33 54 1a f9 92 b3 de 07 41 33 6b 12 c8
61	            8e c5 e6 cc dd 31 01 c4 9e b1 60 7c 25 25 71 27
62	            cf dd 82 b3 f1 50 e1 42 9c c4 a6 88 dd e0 95 5b
63	fido2-salt: eb f5 43 f0 22 7c da 78 a6 8e 6f 9e 34 4e bc ff
64	            49 f3 78 bc 46 8d ab a6 0e f4 fe a6 ff 98 e5 3a
65	fido2-rp:   io.systemd.cryptsetup
66	fido2-clientPin-required:
67	            true
68	fido2-up-required:
69	            true
70	fido2-uv-required:
71	            false
72	Keyslot:    1
73Digests:
74  0: pbkdf2
75	Hash:       sha256
76	Iterations: 589750
77	Salt:       bd 77 84 53 6b cd 48 13 23 5d 09 fa 59 5d d3 5f
78	            72 ad 6e b7 93 57 a8 e8 ca 5d ab b2 0a 56 55 1d
79	Digest:     78 fc 99 e1 fe c5 63 99 1f c0 14 4f f9 fb ca 53
80	            c6 df 40 7b 4d 99 fa 01 a7 cb 5a 95 f6 b6 3e 84

There are two keyslots used, keyslot 0 with the initial password and keyslot 1 with the systemd-fido2 token (referenced in the tokens section).

Now let’s get rid of the password slot.

 1$ sudo systemd-cryptenroll --unlock-fido2-device=auto --wipe-slot=0 ./test.img
 2Wiped slot 0.
 3
 4$ sudo systemd-cryptenroll ./test.img
 5SLOT TYPE
 6   1 fido2
 7
 8$ cryptsetup luksDump ./test.img
 9LUKS header information
10Version:       	2
11Epoch:         	9
12Metadata area: 	16384 [bytes]
13Keyslots area: 	16744448 [bytes]
14UUID:          	bedfd819-a0e9-43dd-a5c5-29e3c7679a65
15Label:         	(no label)
16Subsystem:     	(no subsystem)
17Flags:       	(no flags)
18
19Data segments:
20  0: crypt
21	offset: 16777216 [bytes]
22	length: (whole device)
23	cipher: aes-xts-plain64
24	sector: 4096 [bytes]
25
26Keyslots:
27  1: luks2
28	Key:        512 bits
29	Priority:   normal
30	Cipher:     aes-xts-plain64
31	Cipher key: 512 bits
32	PBKDF:      pbkdf2
33	Hash:       sha512
34	Iterations: 1000
35	Salt:       1a bf 2e 7d 28 72 e6 18 f1 25 f2 aa 4c 89 86 1a
36	            4f 0c 95 d6 82 54 ee 34 40 8e 05 fe 1f fa 2e 3d
37	AF stripes: 4000
38	AF hash:    sha512
39	Area offset:290816 [bytes]
40	Area length:258048 [bytes]
41	Digest ID:  0
42Tokens:
43  0: systemd-fido2
44	fido2-credential:
45	            25 f2 ce 8a 37 bf 5c 05 5d 49 f6 63 23 e8 ce 69
46	            2f 06 b8 33 54 1a f9 92 b3 de 07 41 33 6b 12 c8
47	            8e c5 e6 cc dd 31 01 c4 9e b1 60 7c 25 25 71 27
48	            cf dd 82 b3 f1 50 e1 42 9c c4 a6 88 dd e0 95 5b
49	fido2-salt: eb f5 43 f0 22 7c da 78 a6 8e 6f 9e 34 4e bc ff
50	            49 f3 78 bc 46 8d ab a6 0e f4 fe a6 ff 98 e5 3a
51	fido2-rp:   io.systemd.cryptsetup
52	fido2-clientPin-required:
53	            true
54	fido2-up-required:
55	            true
56	fido2-uv-required:
57	            false
58	Keyslot:    1
59Digests:
60  0: pbkdf2
61	Hash:       sha256
62	Iterations: 589750
63	Salt:       bd 77 84 53 6b cd 48 13 23 5d 09 fa 59 5d d3 5f
64	            72 ad 6e b7 93 57 a8 e8 ca 5d ab b2 0a 56 55 1d
65	Digest:     78 fc 99 e1 fe c5 63 99 1f c0 14 4f f9 fb ca 53
66	            c6 df 40 7b 4d 99 fa 01 a7 cb 5a 95 f6 b6 3e 84

That’s it. Now you can unlock the disk only with the FIDO device.

Unlocking LUKS devices

Let’s have a look how to unlock a LUKS device manually. During the unlock mechanism the FIDO pin is required and then you also have to touch the device.

1$ sudo systemd-cryptsetup attach luks-test test.img
2🔐 Please enter LUKS2 token PIN: ••••••••••••••••••••••••
3Asking FIDO2 token for authentication.
4👆 Please confirm presence on security token to unlock.
5
6$ ls -l /dev/mapper/luks-test
7lrwxrwxrwx 1 root root 7 Sep 17 20:32 /dev/mapper/luks-test -> ../dm-1
8
9$ sudo systemd-cryptsetup detach luks-test

That’s everything. With those commands you can encrypt your LUKS device with a FIDO device and ensure you have a MFA encrypted LUKS.