]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
Merge pull request #20303 from andir/sysconfig-example
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-tokens / luks2-fido2.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <libcryptsetup.h>
4
5 #include "cryptsetup-token-util.h"
6 #include "hexdecoct.h"
7 #include "json.h"
8 #include "luks2-fido2.h"
9 #include "memory-util.h"
10 #include "strv.h"
11
12 int acquire_luks2_key(
13 struct crypt_device *cd,
14 const char *json,
15 const char *device,
16 const char *pin,
17 char **ret_keyslot_passphrase,
18 size_t *ret_keyslot_passphrase_size) {
19
20 int r;
21 Fido2EnrollFlags required;
22 size_t cid_size, salt_size, decrypted_key_size;
23 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
24 _cleanup_free_ void *cid = NULL, *salt = NULL;
25 _cleanup_free_ char *rp_id = NULL;
26 _cleanup_(erase_and_freep) void *decrypted_key = NULL;
27 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
28 _cleanup_strv_free_erase_ char **pins = NULL;
29
30 assert(ret_keyslot_passphrase);
31 assert(ret_keyslot_passphrase_size);
32
33 r = parse_luks2_fido2_data(cd, json, &rp_id, &salt, &salt_size, &cid, &cid_size, &required);
34 if (r < 0)
35 return r;
36
37 if (pin) {
38 pins = strv_new(pin);
39 if (!pins)
40 return crypt_log_oom(cd);
41 }
42
43 /* configured to use pin but none was provided */
44 if ((required & FIDO2ENROLL_PIN) && strv_isempty(pins))
45 return -ENOANO;
46
47 r = fido2_use_hmac_hash(
48 device,
49 rp_id ?: "io.systemd.cryptsetup",
50 salt, salt_size,
51 cid, cid_size,
52 pins,
53 required,
54 &decrypted_key,
55 &decrypted_key_size);
56 if (r == -ENOLCK) /* libcryptsetup returns -ENOANO also on wrong pin */
57 r = -ENOANO;
58 if (r < 0)
59 return r;
60
61 /* Before using this key as passphrase we base64 encode it, for compat with homed */
62 r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
63 if (r < 0)
64 return crypt_log_error_errno(cd, r, "Can not base64 encode key: %m");
65
66 *ret_keyslot_passphrase = TAKE_PTR(base64_encoded);
67 *ret_keyslot_passphrase_size = strlen(*ret_keyslot_passphrase);
68
69 return 0;
70 }
71
72 /* this function expects valid "systemd-fido2" in json */
73 int parse_luks2_fido2_data(
74 struct crypt_device *cd,
75 const char *json,
76 char **ret_rp_id,
77 void **ret_salt,
78 size_t *ret_salt_size,
79 void **ret_cid,
80 size_t *ret_cid_size,
81 Fido2EnrollFlags *ret_required) {
82
83 _cleanup_free_ void *cid = NULL, *salt = NULL;
84 size_t cid_size = 0, salt_size = 0;
85 _cleanup_free_ char *rp = NULL;
86 int r;
87 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
88 JsonVariant *w;
89 Fido2EnrollFlags required = 0;
90
91 assert(json);
92 assert(ret_rp_id);
93 assert(ret_salt);
94 assert(ret_salt_size);
95 assert(ret_cid);
96 assert(ret_cid_size);
97 assert(ret_required);
98
99 r = json_parse(json, 0, &v, NULL, NULL);
100 if (r < 0)
101 return crypt_log_error_errno(cd, r, "Failed to parse JSON token data: %m");
102
103 w = json_variant_by_key(v, "fido2-credential");
104 if (!w)
105 return -EINVAL;
106
107 r = unbase64mem(json_variant_string(w), SIZE_MAX, &cid, &cid_size);
108 if (r < 0)
109 return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-credentials' field: %m");
110
111 w = json_variant_by_key(v, "fido2-salt");
112 if (!w)
113 return -EINVAL;
114
115 r = unbase64mem(json_variant_string(w), SIZE_MAX, &salt, &salt_size);
116 if (r < 0)
117 return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-salt' field: %m");
118
119 w = json_variant_by_key(v, "fido2-rp");
120 if (w) {
121 /* The "rp" field is optional. */
122 rp = strdup(json_variant_string(w));
123 if (!rp) {
124 crypt_log_error(cd, "Not enough memory.");
125 return -ENOMEM;
126 }
127 }
128
129 w = json_variant_by_key(v, "fido2-clientPin-required");
130 if (w)
131 /* The "fido2-clientPin-required" field is optional. */
132 SET_FLAG(required, FIDO2ENROLL_PIN, json_variant_boolean(w));
133 else
134 required |= FIDO2ENROLL_PIN_IF_NEEDED; /* compat with 248, where the field was unset */
135
136 w = json_variant_by_key(v, "fido2-up-required");
137 if (w)
138 /* The "fido2-up-required" field is optional. */
139 SET_FLAG(required, FIDO2ENROLL_UP, json_variant_boolean(w));
140 else
141 required |= FIDO2ENROLL_UP_IF_NEEDED; /* compat with 248 */
142
143 w = json_variant_by_key(v, "fido2-uv-required");
144 if (w)
145 /* The "fido2-uv-required" field is optional. */
146 SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w));
147 else
148 required |= FIDO2ENROLL_UV_OMIT; /* compat with 248 */
149
150 *ret_rp_id = TAKE_PTR(rp);
151 *ret_cid = TAKE_PTR(cid);
152 *ret_cid_size = cid_size;
153 *ret_salt = TAKE_PTR(salt);
154 *ret_salt_size = salt_size;
155 *ret_required = required;
156
157 return 0;
158 }