1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include <libcryptsetup.h>
6 #include "cryptsetup-token.h"
7 #include "cryptsetup-token-util.h"
10 #include "luks2-tpm2.h"
11 #include "memory-util.h"
13 #include "tpm2-util.h"
16 #define TOKEN_NAME "systemd-tpm2"
17 #define TOKEN_VERSION_MAJOR "1"
18 #define TOKEN_VERSION_MINOR "0"
20 /* for libcryptsetup debug purpose */
21 _public_
const char *cryptsetup_token_version(void) {
23 return TOKEN_VERSION_MAJOR
"." TOKEN_VERSION_MINOR
" systemd-v" STRINGIFY(PROJECT_VERSION
) " (" GIT_VERSION
")";
26 static int log_debug_open_error(struct crypt_device
*cd
, int r
) {
28 return crypt_log_debug_errno(cd
, r
, "TPM2 device not found.");
30 return crypt_log_debug_errno(cd
, r
, "No matching TPM2 token data found.");
32 return crypt_log_debug_errno(cd
, r
, TOKEN_NAME
" open failed: %m.");
35 _public_
int cryptsetup_token_open_pin(
36 struct crypt_device
*cd
, /* is always LUKS2 context */
37 int token
/* is always >= 0 */,
40 char **ret_password
, /* freed by cryptsetup_token_buffer_free */
41 size_t *ret_password_len
,
42 void *usrptr
/* plugin defined parameter passed to crypt_activate_by_token*() API */) {
44 _cleanup_(erase_and_freep
) char *base64_encoded
= NULL
, *pin_string
= NULL
;
45 _cleanup_(iovec_done
) struct iovec blob
= {}, pubkey
= {}, policy_hash
= {}, salt
= {}, srk
= {};
46 _cleanup_(iovec_done_erase
) struct iovec decrypted_key
= {};
47 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
48 uint32_t hash_pcr_mask
, pubkey_pcr_mask
;
49 systemd_tpm2_plugin_params params
= {
50 .search_pcr_mask
= UINT32_MAX
52 uint16_t pcr_bank
, primary_alg
;
53 ssize_t base64_encoded_size
;
59 assert(!pin
|| pin_size
> 0);
61 assert(ret_password_len
);
63 /* This must not fail at this moment (internal error) */
64 r
= crypt_token_json_get(cd
, token
, &json
);
68 r
= crypt_normalize_pin(pin
, pin_size
, &pin_string
);
70 return crypt_log_debug_errno(cd
, r
, "Cannot normalize PIN: %m");
73 params
= *(systemd_tpm2_plugin_params
*)usrptr
;
75 r
= json_parse(json
, 0, &v
, NULL
, NULL
);
77 return crypt_log_debug_errno(cd
, r
, "Failed to parse token JSON data: %m");
79 r
= tpm2_parse_luks2_json(
81 /* ret_keyslot= */ NULL
,
93 return log_debug_open_error(cd
, r
);
95 if (params
.search_pcr_mask
!= UINT32_MAX
&& hash_pcr_mask
!= params
.search_pcr_mask
)
96 return crypt_log_debug_errno(cd
, ENXIO
, "PCR mask doesn't match expectation (%" PRIu32
" vs. %" PRIu32
")", hash_pcr_mask
, params
.search_pcr_mask
);
98 r
= acquire_luks2_key(
104 params
.signature_path
,
115 return log_debug_open_error(cd
, r
);
117 /* Before using this key as passphrase we base64 encode it, for compat with homed */
118 base64_encoded_size
= base64mem(decrypted_key
.iov_base
, decrypted_key
.iov_len
, &base64_encoded
);
119 if (base64_encoded_size
< 0)
120 return log_debug_open_error(cd
, base64_encoded_size
);
122 /* free'd automatically by libcryptsetup */
123 *ret_password
= TAKE_PTR(base64_encoded
);
124 *ret_password_len
= base64_encoded_size
;
130 * This function is called from within following libcryptsetup calls
131 * provided conditions further below are met:
133 * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-tpm2'):
135 * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
136 * (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
137 * and token is assigned to at least single keyslot).
139 * - if plugin defines validate function (see cryptsetup_token_validate below) it must have
140 * passed the check (aka return 0)
142 _public_
int cryptsetup_token_open(
143 struct crypt_device
*cd
, /* is always LUKS2 context */
144 int token
/* is always >= 0 */,
145 char **ret_password
, /* freed by cryptsetup_token_buffer_free */
146 size_t *ret_password_len
,
147 void *usrptr
/* plugin defined parameter passed to crypt_activate_by_token*() API */) {
149 return cryptsetup_token_open_pin(cd
, token
, NULL
, 0, ret_password
, ret_password_len
, usrptr
);
153 * libcryptsetup callback for memory deallocation of 'password' parameter passed in
154 * any crypt_token_open_* plugin function
156 _public_
void cryptsetup_token_buffer_free(void *buffer
, size_t buffer_len
) {
157 erase_and_free(buffer
);
161 * prints systemd-tpm2 token content in crypt_dump().
162 * 'type' and 'keyslots' fields are printed by libcryptsetup
164 _public_
void cryptsetup_token_dump(
165 struct crypt_device
*cd
/* is always LUKS2 context */,
166 const char *json
/* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
168 _cleanup_free_
char *hash_pcrs_str
= NULL
, *pubkey_pcrs_str
= NULL
, *blob_str
= NULL
, *policy_hash_str
= NULL
, *pubkey_str
= NULL
;
169 _cleanup_(iovec_done
) struct iovec blob
= {}, pubkey
= {}, policy_hash
= {}, salt
= {}, srk
= {};
170 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
171 uint32_t hash_pcr_mask
, pubkey_pcr_mask
;
172 uint16_t pcr_bank
, primary_alg
;
178 r
= json_parse(json
, 0, &v
, NULL
, NULL
);
180 return (void) crypt_log_debug_errno(cd
, r
, "Failed to parse " TOKEN_NAME
" JSON object: %m");
182 r
= tpm2_parse_luks2_json(
196 return (void) crypt_log_debug_errno(cd
, r
, "Failed to parse " TOKEN_NAME
" JSON fields: %m");
198 hash_pcrs_str
= tpm2_pcr_mask_to_string(hash_pcr_mask
);
200 return (void) crypt_log_debug_errno(cd
, ENOMEM
, "Cannot format PCR hash mask: %m");
202 pubkey_pcrs_str
= tpm2_pcr_mask_to_string(pubkey_pcr_mask
);
203 if (!pubkey_pcrs_str
)
204 return (void) crypt_log_debug_errno(cd
, ENOMEM
, "Cannot format PCR hash mask: %m");
206 r
= crypt_dump_buffer_to_hex_string(blob
.iov_base
, blob
.iov_len
, &blob_str
);
208 return (void) crypt_log_debug_errno(cd
, r
, "Cannot dump " TOKEN_NAME
" content: %m");
210 r
= crypt_dump_buffer_to_hex_string(pubkey
.iov_base
, pubkey
.iov_len
, &pubkey_str
);
212 return (void) crypt_log_debug_errno(cd
, r
, "Cannot dump " TOKEN_NAME
" content: %m");
214 r
= crypt_dump_buffer_to_hex_string(policy_hash
.iov_base
, policy_hash
.iov_len
, &policy_hash_str
);
216 return (void) crypt_log_debug_errno(cd
, r
, "Cannot dump " TOKEN_NAME
" content: %m");
218 crypt_log(cd
, "\ttpm2-hash-pcrs: %s\n", strna(hash_pcrs_str
));
219 crypt_log(cd
, "\ttpm2-pcr-bank: %s\n", strna(tpm2_hash_alg_to_string(pcr_bank
)));
220 crypt_log(cd
, "\ttpm2-pubkey:" CRYPT_DUMP_LINE_SEP
"%s\n", pubkey_str
);
221 crypt_log(cd
, "\ttpm2-pubkey-pcrs: %s\n", strna(pubkey_pcrs_str
));
222 crypt_log(cd
, "\ttpm2-primary-alg: %s\n", strna(tpm2_asym_alg_to_string(primary_alg
)));
223 crypt_log(cd
, "\ttpm2-blob: %s\n", blob_str
);
224 crypt_log(cd
, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP
"%s\n", policy_hash_str
);
225 crypt_log(cd
, "\ttpm2-pin: %s\n", true_false(flags
& TPM2_FLAGS_USE_PIN
));
226 crypt_log(cd
, "\ttpm2-pcrlock: %s\n", true_false(flags
& TPM2_FLAGS_USE_PCRLOCK
));
227 crypt_log(cd
, "\ttpm2-salt: %s\n", true_false(iovec_is_set(&salt
)));
228 crypt_log(cd
, "\ttpm2-srk: %s\n", true_false(iovec_is_set(&srk
)));
233 * If plugin is available in library path, it's called in before following libcryptsetup calls:
235 * crypt_token_json_set, crypt_dump, any crypt_activate_by_token_* flavour
237 _public_
int cryptsetup_token_validate(
238 struct crypt_device
*cd
, /* is always LUKS2 context */
239 const char *json
/* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-tpm2' */) {
243 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
247 r
= json_parse(json
, 0, &v
, NULL
, NULL
);
249 return crypt_log_debug_errno(cd
, r
, "Could not parse " TOKEN_NAME
" json object: %m");
251 w
= json_variant_by_key(v
, "tpm2-pcrs");
252 if (!w
|| !json_variant_is_array(w
)) {
253 crypt_log_debug(cd
, "TPM2 token data lacks 'tpm2-pcrs' field.");
257 JSON_VARIANT_ARRAY_FOREACH(e
, w
) {
260 if (!json_variant_is_number(e
)) {
261 crypt_log_debug(cd
, "TPM2 PCR is not a number.");
265 u
= json_variant_unsigned(e
);
266 if (!TPM2_PCR_INDEX_VALID(u
)) {
267 crypt_log_debug(cd
, "TPM2 PCR number out of range.");
272 /* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded
274 w
= json_variant_by_key(v
, "tpm2-pcr-bank");
276 /* The PCR bank field is optional */
278 if (!json_variant_is_string(w
)) {
279 crypt_log_debug(cd
, "TPM2 PCR bank is not a string.");
283 if (tpm2_hash_alg_from_string(json_variant_string(w
)) < 0) {
284 crypt_log_debug(cd
, "TPM2 PCR bank invalid or not supported: %s.", json_variant_string(w
));
289 /* The primary key algorithm field is optional, since it was also added in systemd 250 only. Before
290 * the algorithm was hardcoded to ECC. */
291 w
= json_variant_by_key(v
, "tpm2-primary-alg");
293 /* The primary key algorithm is optional */
295 if (!json_variant_is_string(w
)) {
296 crypt_log_debug(cd
, "TPM2 primary key algorithm is not a string.");
300 if (tpm2_asym_alg_from_string(json_variant_string(w
)) < 0) {
301 crypt_log_debug(cd
, "TPM2 primary key algorithm invalid or not supported: %s", json_variant_string(w
));
306 w
= json_variant_by_key(v
, "tpm2-blob");
307 if (!w
|| !json_variant_is_string(w
)) {
308 crypt_log_debug(cd
, "TPM2 token data lacks 'tpm2-blob' field.");
312 r
= unbase64mem(json_variant_string(w
), SIZE_MAX
, NULL
, NULL
);
314 return crypt_log_debug_errno(cd
, r
, "Invalid base64 data in 'tpm2-blob' field: %m");
316 w
= json_variant_by_key(v
, "tpm2-policy-hash");
317 if (!w
|| !json_variant_is_string(w
)) {
318 crypt_log_debug(cd
, "TPM2 token data lacks 'tpm2-policy-hash' field.");
322 r
= unhexmem(json_variant_string(w
), SIZE_MAX
, NULL
, NULL
);
324 return crypt_log_debug_errno(cd
, r
, "Invalid base64 data in 'tpm2-policy-hash' field: %m");
326 w
= json_variant_by_key(v
, "tpm2-pin");
328 if (!json_variant_is_boolean(w
)) {
329 crypt_log_debug(cd
, "TPM2 PIN policy is not a boolean.");