1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
4 #include "ask-password-api.h"
5 #include "cryptenroll-tpm2.h"
10 #include "memory-util.h"
11 #include "random-util.h"
13 #include "tpm2-util.h"
15 static int search_policy_hash(
16 struct crypt_device
*cd
,
23 assert(hash
|| hash_size
== 0);
28 for (int token
= 0; token
< sym_crypt_token_max(CRYPT_LUKS2
); token
++) {
29 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
30 _cleanup_free_
void *thash
= NULL
;
31 size_t thash_size
= 0;
35 r
= cryptsetup_get_token_as_json(cd
, token
, "systemd-tpm2", &v
);
36 if (IN_SET(r
, -ENOENT
, -EINVAL
, -EMEDIUMTYPE
))
39 return log_error_errno(r
, "Failed to read JSON token data off disk: %m");
41 keyslot
= cryptsetup_get_keyslot_from_token(v
);
43 /* Handle parsing errors of the keyslots field gracefully, since it's not 'owned' by
44 * us, but by the LUKS2 spec */
45 log_warning_errno(keyslot
, "Failed to determine keyslot of JSON token %i, skipping: %m", token
);
49 w
= json_variant_by_key(v
, "tpm2-policy-hash");
50 if (!w
|| !json_variant_is_string(w
))
51 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
52 "TPM2 token data lacks 'tpm2-policy-hash' field.");
54 r
= unhexmem(json_variant_string(w
), &thash
, &thash_size
);
56 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
57 "Invalid base64 data in 'tpm2-policy-hash' field.");
59 if (memcmp_nn(hash
, hash_size
, thash
, thash_size
) == 0)
60 return keyslot
; /* Found entry with same hash. */
63 return -ENOENT
; /* Not found */
66 static int get_pin(char **ret_pin_str
, TPM2Flags
*ret_flags
) {
67 _cleanup_(erase_and_freep
) char *pin_str
= NULL
;
74 r
= getenv_steal_erase("NEWPIN", &pin_str
);
76 return log_error_errno(r
, "Failed to acquire PIN from environment: %m");
78 flags
|= TPM2_FLAGS_USE_PIN
;
80 for (size_t i
= 5;; i
--) {
81 _cleanup_strv_free_erase_
char **pin
= NULL
, **pin2
= NULL
;
84 return log_error_errno(
85 SYNTHETIC_ERRNO(ENOKEY
), "Too many attempts, giving up.");
87 pin
= strv_free_erase(pin
);
88 r
= ask_password_auto(
89 "Please enter TPM2 PIN:",
93 "cryptenroll.tpm2-pin",
98 return log_error_errno(r
, "Failed to ask for user pin: %m");
99 assert(strv_length(pin
) == 1);
101 r
= ask_password_auto(
102 "Please enter TPM2 PIN (repeat):",
106 "cryptenroll.tpm2-pin",
111 return log_error_errno(r
, "Failed to ask for user pin: %m");
112 assert(strv_length(pin
) == 1);
114 if (strv_equal(pin
, pin2
)) {
115 pin_str
= strdup(*pin
);
118 flags
|= TPM2_FLAGS_USE_PIN
;
122 log_error("PINs didn't match, please try again!");
127 *ret_pin_str
= TAKE_PTR(pin_str
);
132 int enroll_tpm2(struct crypt_device
*cd
,
133 const void *volume_key
,
134 size_t volume_key_size
,
136 uint32_t seal_key_handle
,
137 const char *device_key
,
138 Tpm2PCRValue
*hash_pcr_values
,
139 size_t n_hash_pcr_values
,
140 const char *pubkey_path
,
141 uint32_t pubkey_pcr_mask
,
142 const char *signature_path
,
144 const char *pcrlock_path
) {
146 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *signature_json
= NULL
;
147 _cleanup_(erase_and_freep
) char *base64_encoded
= NULL
;
148 _cleanup_(iovec_done
) struct iovec srk
= {}, blob
= {}, pubkey
= {};
149 _cleanup_(iovec_done_erase
) struct iovec secret
= {};
151 _cleanup_(erase_and_freep
) char *pin_str
= NULL
;
152 ssize_t base64_encoded_size
;
155 uint8_t binary_salt
[SHA256_DIGEST_SIZE
] = {};
157 * erase the salt, we'd rather attempt to not have this in a coredump
158 * as an attacker would have all the parameters but pin used to create
159 * the session key. This problem goes away when we move to a trusted
160 * primary key, aka the SRK.
162 CLEANUP_ERASE(binary_salt
);
166 assert(volume_key_size
> 0);
167 assert(tpm2_pcr_values_valid(hash_pcr_values
, n_hash_pcr_values
));
168 assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask
));
170 assert_se(node
= crypt_get_device_name(cd
));
173 r
= get_pin(&pin_str
, &flags
);
177 r
= crypto_random_bytes(binary_salt
, sizeof(binary_salt
));
179 return log_error_errno(r
, "Failed to acquire random salt: %m");
181 uint8_t salted_pin
[SHA256_DIGEST_SIZE
] = {};
182 CLEANUP_ERASE(salted_pin
);
183 r
= tpm2_util_pbkdf2_hmac_sha256(pin_str
, strlen(pin_str
), binary_salt
, sizeof(binary_salt
), salted_pin
);
185 return log_error_errno(r
, "Failed to perform PBKDF2: %m");
187 pin_str
= erase_and_free(pin_str
);
188 /* re-stringify pin_str */
189 base64_encoded_size
= base64mem(salted_pin
, sizeof(salted_pin
), &pin_str
);
190 if (base64_encoded_size
< 0)
191 return log_error_errno(base64_encoded_size
, "Failed to base64 encode salted pin: %m");
194 TPM2B_PUBLIC
public = {};
195 r
= tpm2_load_pcr_public_key(pubkey_path
, &pubkey
.iov_base
, &pubkey
.iov_len
);
197 if (pubkey_path
|| signature_path
|| r
!= -ENOENT
)
198 return log_error_errno(r
, "Failed to read TPM PCR public key: %m");
200 log_debug_errno(r
, "Failed to read TPM2 PCR public key, proceeding without: %m");
203 r
= tpm2_tpm2b_public_from_pem(pubkey
.iov_base
, pubkey
.iov_len
, &public);
205 return log_error_errno(r
, "Could not convert public key to TPM2B_PUBLIC: %m");
207 if (signature_path
) {
208 /* Also try to load the signature JSON object, to verify that our enrollment will work.
209 * This is optional however, skip it if it's not explicitly provided. */
211 r
= tpm2_load_pcr_signature(signature_path
, &signature_json
);
213 return log_debug_errno(r
, "Failed to read TPM PCR signature: %m");
217 bool any_pcr_value_specified
= tpm2_pcr_values_has_any_values(hash_pcr_values
, n_hash_pcr_values
);
219 _cleanup_(tpm2_pcrlock_policy_done
) Tpm2PCRLockPolicy pcrlock_policy
= {};
221 r
= tpm2_pcrlock_policy_load(pcrlock_path
, &pcrlock_policy
);
225 any_pcr_value_specified
= true;
226 flags
|= TPM2_FLAGS_USE_PCRLOCK
;
229 _cleanup_(tpm2_context_unrefp
) Tpm2Context
*tpm2_context
= NULL
;
230 TPM2B_PUBLIC device_key_public
= {};
232 r
= tpm2_load_public_key_file(device_key
, &device_key_public
);
236 if (!tpm2_pcr_values_has_all_values(hash_pcr_values
, n_hash_pcr_values
))
237 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
238 "Must provide all PCR values when using TPM2 device key.");
240 r
= tpm2_context_new(device
, &tpm2_context
);
242 return log_error_errno(r
, "Failed to create TPM2 context: %m");
244 if (!tpm2_pcr_values_has_all_values(hash_pcr_values
, n_hash_pcr_values
)) {
245 r
= tpm2_pcr_read_missing_values(tpm2_context
, hash_pcr_values
, n_hash_pcr_values
);
247 return log_error_errno(r
, "Could not read pcr values: %m");
251 uint16_t hash_pcr_bank
= 0;
252 uint32_t hash_pcr_mask
= 0;
253 if (n_hash_pcr_values
> 0) {
255 r
= tpm2_pcr_values_hash_count(hash_pcr_values
, n_hash_pcr_values
, &hash_count
);
257 return log_error_errno(r
, "Could not get hash count: %m");
260 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Multiple PCR banks selected.");
262 hash_pcr_bank
= hash_pcr_values
[0].hash
;
263 r
= tpm2_pcr_values_to_mask(hash_pcr_values
, n_hash_pcr_values
, hash_pcr_bank
, &hash_pcr_mask
);
265 return log_error_errno(r
, "Could not get hash mask: %m");
268 TPM2B_DIGEST policy
= TPM2B_DIGEST_MAKE(NULL
, TPM2_SHA256_DIGEST_SIZE
);
269 r
= tpm2_calculate_sealing_policy(
272 iovec_is_set(&pubkey
) ? &public : NULL
,
274 pcrlock_path
? &pcrlock_policy
: NULL
,
280 r
= tpm2_calculate_seal(
283 /* attributes= */ NULL
,
291 r
= tpm2_seal(tpm2_context
,
297 /* ret_primary_alg= */ NULL
,
300 return log_error_errno(r
, "Failed to seal to TPM2: %m");
302 /* Let's see if we already have this specific PCR policy hash enrolled, if so, exit early. */
303 r
= search_policy_hash(cd
, policy
.buffer
, policy
.size
);
305 log_debug_errno(r
, "PCR policy hash not yet enrolled, enrolling now.");
309 log_info("This PCR set is already enrolled, executing no operation.");
310 return r
; /* return existing keyslot, so that wiping won't kill it */
313 /* If possible, verify the sealed data object. */
314 if ((!iovec_is_set(&pubkey
) || signature_json
) && !any_pcr_value_specified
&& !device_key
) {
315 _cleanup_(iovec_done_erase
) struct iovec secret2
= {};
317 log_debug("Unsealing for verification...");
318 r
= tpm2_unseal(tpm2_context
,
325 pcrlock_path
? &pcrlock_policy
: NULL
,
326 /* primary_alg= */ 0,
328 &IOVEC_MAKE(policy
.buffer
, policy
.size
),
332 return log_error_errno(r
, "Failed to unseal secret using TPM2: %m");
334 if (iovec_memcmp(&secret
, &secret2
) != 0)
335 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "TPM2 seal/unseal verification failed.");
338 /* let's base64 encode the key to use, for compat with homed (and it's easier to every type it in by keyboard, if that might end up being necessary. */
339 base64_encoded_size
= base64mem(secret
.iov_base
, secret
.iov_len
, &base64_encoded
);
340 if (base64_encoded_size
< 0)
341 return log_error_errno(base64_encoded_size
, "Failed to base64 encode secret key: %m");
343 r
= cryptsetup_set_minimal_pbkdf(cd
);
345 return log_error_errno(r
, "Failed to set minimal PBKDF: %m");
347 keyslot
= crypt_keyslot_add_by_volume_key(
353 base64_encoded_size
);
355 return log_error_errno(keyslot
, "Failed to add new TPM2 key to %s: %m", node
);
357 r
= tpm2_make_luks2_json(
363 /* primary_alg= */ 0,
365 &IOVEC_MAKE(policy
.buffer
, policy
.size
),
366 use_pin
? &IOVEC_MAKE(binary_salt
, sizeof(binary_salt
)) : NULL
,
371 return log_error_errno(r
, "Failed to prepare TPM2 JSON token object: %m");
373 r
= cryptsetup_add_token_json(cd
, v
);
375 return log_error_errno(r
, "Failed to add TPM2 JSON token to LUKS2 header: %m");
377 log_info("New TPM2 token enrolled as key slot %i.", keyslot
);