]>
Commit | Line | Data |
---|---|---|
8710a681 LP |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
48765191 | 3 | #include "ask-password-api.h" |
8710a681 | 4 | #include "cryptenroll-fido2.h" |
48765191 | 5 | #include "cryptsetup-fido2.h" |
8710a681 LP |
6 | #include "hexdecoct.h" |
7 | #include "json.h" | |
8 | #include "libfido2-util.h" | |
9 | #include "memory-util.h" | |
10 | #include "random-util.h" | |
11 | ||
48765191 PC |
12 | int load_volume_key_fido2( |
13 | struct crypt_device *cd, | |
14 | const char *cd_node, | |
15 | const char *device, | |
16 | void *ret_vk, | |
17 | size_t *ret_vks) { | |
18 | ||
19 | _cleanup_(erase_and_freep) void *decrypted_key = NULL; | |
20 | _cleanup_(erase_and_freep) char *passphrase = NULL; | |
21 | size_t decrypted_key_size; | |
5e476b85 | 22 | ssize_t passphrase_size; |
48765191 PC |
23 | int r; |
24 | ||
25 | assert_se(cd); | |
26 | assert_se(cd_node); | |
27 | assert_se(ret_vk); | |
28 | assert_se(ret_vks); | |
29 | ||
30 | r = acquire_fido2_key_auto( | |
31 | cd, | |
32 | cd_node, | |
33 | cd_node, | |
34 | device, | |
35 | /* until= */ 0, | |
36 | /* headless= */ false, | |
37 | &decrypted_key, | |
38 | &decrypted_key_size, | |
39 | ASK_PASSWORD_PUSH_CACHE|ASK_PASSWORD_ACCEPT_CACHED); | |
40 | if (r == -EAGAIN) | |
41 | return log_error_errno(r, "FIDO2 token does not exist, or UV is blocked. Please try again."); | |
42 | if (r < 0) | |
43 | return r; | |
44 | ||
45 | /* Because cryptenroll requires a LUKS header, we can assume that this device is not | |
46 | * a PLAIN device. In this case, we need to base64 encode the secret to use as the passphrase */ | |
5e476b85 LP |
47 | passphrase_size = base64mem(decrypted_key, decrypted_key_size, &passphrase); |
48 | if (passphrase_size < 0) | |
48765191 PC |
49 | return log_oom(); |
50 | ||
51 | r = crypt_volume_key_get( | |
52 | cd, | |
53 | CRYPT_ANY_SLOT, | |
54 | ret_vk, | |
55 | ret_vks, | |
56 | passphrase, | |
5e476b85 | 57 | passphrase_size); |
48765191 PC |
58 | if (r < 0) |
59 | return log_error_errno(r, "Unlocking via FIDO2 device failed: %m"); | |
60 | ||
61 | return r; | |
62 | } | |
63 | ||
8710a681 LP |
64 | int enroll_fido2( |
65 | struct crypt_device *cd, | |
66 | const void *volume_key, | |
67 | size_t volume_key_size, | |
cde2f860 | 68 | const char *device, |
70e723c0 M |
69 | Fido2EnrollFlags lock_with, |
70 | int cred_alg) { | |
8710a681 LP |
71 | |
72 | _cleanup_(erase_and_freep) void *salt = NULL, *secret = NULL; | |
73 | _cleanup_(erase_and_freep) char *base64_encoded = NULL; | |
74 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; | |
75 | _cleanup_free_ char *keyslot_as_string = NULL; | |
76 | size_t cid_size, salt_size, secret_size; | |
77 | _cleanup_free_ void *cid = NULL; | |
5e476b85 | 78 | ssize_t base64_encoded_size; |
8710a681 LP |
79 | const char *node, *un; |
80 | int r, keyslot; | |
81 | ||
82 | assert_se(cd); | |
83 | assert_se(volume_key); | |
84 | assert_se(volume_key_size > 0); | |
85 | assert_se(device); | |
86 | ||
87 | assert_se(node = crypt_get_device_name(cd)); | |
88 | ||
89 | un = strempty(crypt_get_uuid(cd)); | |
90 | ||
91 | r = fido2_generate_hmac_hash( | |
92 | device, | |
93 | /* rp_id= */ "io.systemd.cryptsetup", | |
94 | /* rp_name= */ "Encrypted Volume", | |
95 | /* user_id= */ un, strlen(un), /* We pass the user ID and name as the same: the disk's UUID if we have it */ | |
96 | /* user_name= */ un, | |
97 | /* user_display_name= */ node, | |
98 | /* user_icon_name= */ NULL, | |
99 | /* askpw_icon_name= */ "drive-harddisk", | |
cde2f860 | 100 | lock_with, |
70e723c0 | 101 | cred_alg, |
8710a681 LP |
102 | &cid, &cid_size, |
103 | &salt, &salt_size, | |
104 | &secret, &secret_size, | |
0735ed95 LP |
105 | NULL, |
106 | &lock_with); | |
8710a681 LP |
107 | if (r < 0) |
108 | return r; | |
109 | ||
110 | /* Before we use the secret, we base64 encode it, for compat with homed, and to make it easier to type in manually */ | |
5e476b85 LP |
111 | base64_encoded_size = base64mem(secret, secret_size, &base64_encoded); |
112 | if (base64_encoded_size < 0) | |
113 | return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m"); | |
8710a681 LP |
114 | |
115 | r = cryptsetup_set_minimal_pbkdf(cd); | |
116 | if (r < 0) | |
117 | return log_error_errno(r, "Failed to set minimal PBKDF: %m"); | |
118 | ||
119 | keyslot = crypt_keyslot_add_by_volume_key( | |
120 | cd, | |
121 | CRYPT_ANY_SLOT, | |
122 | volume_key, | |
123 | volume_key_size, | |
124 | base64_encoded, | |
5e476b85 | 125 | base64_encoded_size); |
8710a681 | 126 | if (keyslot < 0) |
4b9aa29b | 127 | return log_error_errno(keyslot, "Failed to add new FIDO2 key to %s: %m", node); |
8710a681 LP |
128 | |
129 | if (asprintf(&keyslot_as_string, "%i", keyslot) < 0) | |
130 | return log_oom(); | |
131 | ||
132 | r = json_build(&v, | |
133 | JSON_BUILD_OBJECT( | |
0cdf6b14 | 134 | JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-fido2")), |
8710a681 LP |
135 | JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))), |
136 | JSON_BUILD_PAIR("fido2-credential", JSON_BUILD_BASE64(cid, cid_size)), | |
137 | JSON_BUILD_PAIR("fido2-salt", JSON_BUILD_BASE64(salt, salt_size)), | |
0cdf6b14 | 138 | JSON_BUILD_PAIR("fido2-rp", JSON_BUILD_CONST_STRING("io.systemd.cryptsetup")), |
06f08719 | 139 | JSON_BUILD_PAIR("fido2-clientPin-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_PIN))), |
896cc0da LB |
140 | JSON_BUILD_PAIR("fido2-up-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UP))), |
141 | JSON_BUILD_PAIR("fido2-uv-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UV))))); | |
8710a681 | 142 | if (r < 0) |
4b9aa29b | 143 | return log_error_errno(r, "Failed to prepare FIDO2 JSON token object: %m"); |
8710a681 LP |
144 | |
145 | r = cryptsetup_add_token_json(cd, v); | |
146 | if (r < 0) | |
147 | return log_error_errno(r, "Failed to add FIDO2 JSON token to LUKS2 header: %m"); | |
148 | ||
149 | log_info("New FIDO2 token enrolled as key slot %i.", keyslot); | |
150 | return keyslot; | |
151 | } |