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 "tpm2-util.h"
13 static int search_policy_hash(
14 struct crypt_device
*cd
,
21 assert(hash
|| hash_size
== 0);
26 for (int token
= 0; token
< sym_crypt_token_max(CRYPT_LUKS2
); token
++) {
27 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
28 _cleanup_free_
void *thash
= NULL
;
29 size_t thash_size
= 0;
33 r
= cryptsetup_get_token_as_json(cd
, token
, "systemd-tpm2", &v
);
34 if (IN_SET(r
, -ENOENT
, -EINVAL
, -EMEDIUMTYPE
))
37 return log_error_errno(r
, "Failed to read JSON token data off disk: %m");
39 keyslot
= cryptsetup_get_keyslot_from_token(v
);
41 /* Handle parsing errors of the keyslots field gracefully, since it's not 'owned' by
42 * us, but by the LUKS2 spec */
43 log_warning_errno(keyslot
, "Failed to determine keyslot of JSON token %i, skipping: %m", token
);
47 w
= json_variant_by_key(v
, "tpm2-policy-hash");
48 if (!w
|| !json_variant_is_string(w
))
49 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
50 "TPM2 token data lacks 'tpm2-policy-hash' field.");
52 r
= unhexmem(json_variant_string(w
), SIZE_MAX
, &thash
, &thash_size
);
54 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
55 "Invalid base64 data in 'tpm2-policy-hash' field.");
57 if (memcmp_nn(hash
, hash_size
, thash
, thash_size
) == 0)
58 return keyslot
; /* Found entry with same hash. */
61 return -ENOENT
; /* Not found */
64 static int get_pin(char **ret_pin_str
, TPM2Flags
*ret_flags
) {
65 _cleanup_free_
char *pin_str
= NULL
;
72 r
= getenv_steal_erase("NEWPIN", &pin_str
);
74 return log_error_errno(r
, "Failed to acquire PIN from environment: %m");
76 flags
|= TPM2_FLAGS_USE_PIN
;
78 for (size_t i
= 5;; i
--) {
79 _cleanup_strv_free_erase_
char **pin
= NULL
, **pin2
= NULL
;
82 return log_error_errno(
83 SYNTHETIC_ERRNO(ENOKEY
), "Too many attempts, giving up.");
85 pin
= strv_free_erase(pin
);
86 r
= ask_password_auto(
87 "Please enter TPM2 PIN:",
91 "cryptenroll.tpm2-pin",
96 return log_error_errno(r
, "Failed to ask for user pin: %m");
97 assert(strv_length(pin
) == 1);
99 r
= ask_password_auto(
100 "Please enter TPM2 PIN (repeat):",
104 "cryptenroll.tpm2-pin",
109 return log_error_errno(r
, "Failed to ask for user pin: %m");
110 assert(strv_length(pin
) == 1);
112 if (strv_equal(pin
, pin2
)) {
113 pin_str
= strdup(*pin
);
116 flags
|= TPM2_FLAGS_USE_PIN
;
120 log_error("PINs didn't match, please try again!");
125 *ret_pin_str
= TAKE_PTR(pin_str
);
130 int enroll_tpm2(struct crypt_device
*cd
,
131 const void *volume_key
,
132 size_t volume_key_size
,
134 uint32_t hash_pcr_mask
,
135 const char *pubkey_path
,
136 uint32_t pubkey_pcr_mask
,
137 const char *signature_path
,
140 _cleanup_(erase_and_freep
) void *secret
= NULL
;
141 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *signature_json
= NULL
;
142 _cleanup_(erase_and_freep
) char *base64_encoded
= NULL
;
143 size_t secret_size
, blob_size
, hash_size
, pubkey_size
= 0;
144 _cleanup_free_
void *blob
= NULL
, *hash
= NULL
, *pubkey
= NULL
;
145 uint16_t pcr_bank
, primary_alg
;
147 _cleanup_(erase_and_freep
) char *pin_str
= NULL
;
148 ssize_t base64_encoded_size
;
154 assert(volume_key_size
> 0);
155 assert(TPM2_PCR_MASK_VALID(hash_pcr_mask
));
156 assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask
));
158 assert_se(node
= crypt_get_device_name(cd
));
161 r
= get_pin(&pin_str
, &flags
);
166 r
= tpm2_load_pcr_public_key(pubkey_path
, &pubkey
, &pubkey_size
);
168 if (pubkey_path
|| signature_path
|| r
!= -ENOENT
)
169 return log_error_errno(r
, "Failed read TPM PCR public key: %m");
171 log_debug_errno(r
, "Failed to read TPM2 PCR public key, proceeding without: %m");
174 /* Also try to load the signature JSON object, to verify that our enrollment will work. This is optional however. */
176 r
= tpm2_load_pcr_signature(signature_path
, &signature_json
);
178 if (signature_path
|| r
!= -ENOENT
)
179 return log_debug_errno(r
, "Failed to read TPM PCR signature: %m");
181 log_debug_errno(r
, "Failed to read TPM2 PCR signature, proceeding without: %m");
185 r
= tpm2_seal(device
,
190 &secret
, &secret_size
,
198 /* Let's see if we already have this specific PCR policy hash enrolled, if so, exit early. */
199 r
= search_policy_hash(cd
, hash
, hash_size
);
201 log_debug_errno(r
, "PCR policy hash not yet enrolled, enrolling now.");
205 log_info("This PCR set is already enrolled, executing no operation.");
206 return r
; /* return existing keyslot, so that wiping won't kill it */
209 /* Quick verification that everything is in order, we are not in a hurry after all.*/
210 if (!pubkey
|| signature_json
) {
211 _cleanup_(erase_and_freep
) void *secret2
= NULL
;
214 log_debug("Unsealing for verification...");
215 r
= tpm2_unseal(device
,
225 &secret2
, &secret2_size
);
229 if (memcmp_nn(secret
, secret_size
, secret2
, secret2_size
) != 0)
230 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "TPM2 seal/unseal verification failed.");
233 /* 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. */
234 base64_encoded_size
= base64mem(secret
, secret_size
, &base64_encoded
);
235 if (base64_encoded_size
< 0)
236 return log_error_errno(base64_encoded_size
, "Failed to base64 encode secret key: %m");
238 r
= cryptsetup_set_minimal_pbkdf(cd
);
240 return log_error_errno(r
, "Failed to set minimal PBKDF: %m");
242 keyslot
= crypt_keyslot_add_by_volume_key(
248 base64_encoded_size
);
250 return log_error_errno(keyslot
, "Failed to add new TPM2 key to %s: %m", node
);
252 r
= tpm2_make_luks2_json(
264 return log_error_errno(r
, "Failed to prepare TPM2 JSON token object: %m");
266 r
= cryptsetup_add_token_json(cd
, v
);
268 return log_error_errno(r
, "Failed to add TPM2 JSON token to LUKS2 header: %m");
270 log_info("New TPM2 token enrolled as key slot %i.", keyslot
);