diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index 697b4c2335..23deeed272 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -65,7 +65,7 @@ int enroll_tpm2(struct crypt_device *cd, _cleanup_(erase_and_freep) char *base64_encoded = NULL; size_t secret_size, secret2_size, blob_size, hash_size; _cleanup_free_ void *blob = NULL, *hash = NULL; - uint16_t pcr_bank; + uint16_t pcr_bank, primary_alg; const char *node; int r, keyslot; @@ -76,7 +76,7 @@ int enroll_tpm2(struct crypt_device *cd, assert_se(node = crypt_get_device_name(cd)); - r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank); + r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg); if (r < 0) return r; @@ -93,7 +93,7 @@ int enroll_tpm2(struct crypt_device *cd, /* Quick verification that everything is in order, we are not in a hurry after all. */ log_debug("Unsealing for verification..."); - r = tpm2_unseal(device, pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &secret2, &secret2_size); + r = tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &secret2, &secret2_size); if (r < 0) return r; @@ -119,7 +119,7 @@ int enroll_tpm2(struct crypt_device *cd, if (keyslot < 0) return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node); - r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &v); + r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &v); if (r < 0) return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m"); diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c index 0c9d566e0d..5471cb35b6 100644 --- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c @@ -57,7 +57,7 @@ _public_ int cryptsetup_token_open( const char *json; size_t blob_size, policy_hash_size, decrypted_key_size; uint32_t pcr_mask; - uint16_t pcr_bank; + uint16_t pcr_bank, primary_alg; systemd_tpm2_plugin_params params = { .search_pcr_mask = UINT32_MAX }; @@ -78,7 +78,7 @@ _public_ int cryptsetup_token_open( if (usrptr) params = *(systemd_tpm2_plugin_params *)usrptr; - r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &base64_blob, &hex_policy_hash); + r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash); if (r < 0) return log_debug_open_error(cd, r); @@ -95,6 +95,7 @@ _public_ int cryptsetup_token_open( r = acquire_luks2_key( pcr_mask, pcr_bank, + primary_alg, params.device, blob, blob_size, @@ -135,7 +136,7 @@ _public_ void cryptsetup_token_dump( int r; uint32_t pcr_mask; - uint16_t pcr_bank; + uint16_t pcr_bank, primary_alg; size_t decoded_blob_size; _cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL, *pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL; @@ -143,7 +144,7 @@ _public_ void cryptsetup_token_dump( assert(json); - r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &base64_blob, &hex_policy_hash); + r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash); if (r < 0) return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m."); @@ -167,6 +168,7 @@ _public_ void cryptsetup_token_dump( crypt_log(cd, "\ttpm2-pcrs: %s\n", strna(pcrs_str)); crypt_log(cd, "\ttpm2-bank: %s\n", strna(tpm2_pcr_bank_to_string(pcr_bank))); + crypt_log(cd, "\ttpm2-primary-alg: %s\n", strna(tpm2_primary_alg_to_string(primary_alg))); crypt_log(cd, "\ttpm2-blob: %s\n", blob_str); crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str); } @@ -212,7 +214,8 @@ _public_ int cryptsetup_token_validate( } } - /* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded to SHA256 */ + /* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded + * to SHA256. */ w = json_variant_by_key(v, "tpm2-pcr-bank"); if (w) { /* The PCR bank field is optional */ @@ -228,6 +231,23 @@ _public_ int cryptsetup_token_validate( } } + /* The primary key algorithm field is optional, since it was also added in systemd 250 only. Before + * the algorithm was hardcoded to ECC. */ + w = json_variant_by_key(v, "tpm2-primary-alg"); + if (w) { + /* The primary key algorithm is optional */ + + if (!json_variant_is_string(w)) { + crypt_log_debug(cd, "TPM2 primary key algorithm is not a string."); + return 1; + } + + if (tpm2_primary_alg_from_string(json_variant_string(w)) < 0) { + crypt_log_debug(cd, "TPM2 primary key algorithm invalid or not supported: %s", json_variant_string(w)); + return 1; + } + } + w = json_variant_by_key(v, "tpm2-blob"); if (!w || !json_variant_is_string(w)) { crypt_log_debug(cd, "TPM2 token data lacks 'tpm2-blob' field."); diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c index a5571f31f6..4167275317 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c @@ -11,6 +11,7 @@ int acquire_luks2_key( uint32_t pcr_mask, uint16_t pcr_bank, + uint16_t primary_alg, const char *device, const void *key_data, size_t key_data_size, @@ -38,6 +39,7 @@ int acquire_luks2_key( return tpm2_unseal( device, pcr_mask, pcr_bank, + primary_alg, key_data, key_data_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size); @@ -49,19 +51,21 @@ int parse_luks2_tpm2_data( uint32_t search_pcr_mask, uint32_t *ret_pcr_mask, uint16_t *ret_pcr_bank, + uint16_t *ret_primary_alg, char **ret_base64_blob, char **ret_hex_policy_hash) { int r; JsonVariant *w, *e; uint32_t pcr_mask = 0; - uint16_t pcr_bank = UINT16_MAX; + uint16_t pcr_bank = UINT16_MAX, primary_alg = TPM2_ALG_ECC; _cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; assert(json); assert(ret_pcr_mask); assert(ret_pcr_bank); + assert(ret_primary_alg); assert(ret_base64_blob); assert(ret_hex_policy_hash); @@ -104,6 +108,20 @@ int parse_luks2_tpm2_data( pcr_bank = r; } + w = json_variant_by_key(v, "tpm2-primary-alg"); + if (w) { + /* The primary key algorithm is optional */ + + if (!json_variant_is_string(w)) + return -EINVAL; + + r = tpm2_primary_alg_from_string(json_variant_string(w)); + if (r < 0) + return r; + + primary_alg = r; + } + w = json_variant_by_key(v, "tpm2-blob"); if (!w || !json_variant_is_string(w)) return -EINVAL; @@ -122,6 +140,7 @@ int parse_luks2_tpm2_data( *ret_pcr_mask = pcr_mask; *ret_pcr_bank = pcr_bank; + *ret_primary_alg = primary_alg; *ret_base64_blob = TAKE_PTR(base64_blob); *ret_hex_policy_hash = TAKE_PTR(hex_policy_hash); diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h index 1a20f2cc1f..0c93ea82cc 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h +++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h @@ -7,6 +7,7 @@ struct crypt_device; int acquire_luks2_key( uint32_t pcr_mask, uint16_t pcr_bank, + uint16_t primary_alg, const char *device, const void *key_data, size_t key_data_size, @@ -20,5 +21,6 @@ int parse_luks2_tpm2_data( uint32_t search_pcr_mask, uint32_t *ret_pcr_mask, uint16_t *ret_pcr_bank, + uint16_t *ret_primary_alg, char **ret_base64_blob, char **ret_hex_policy_hash); diff --git a/src/cryptsetup/cryptsetup-tpm2.c b/src/cryptsetup/cryptsetup-tpm2.c index 8da1880a35..4d95dacca5 100644 --- a/src/cryptsetup/cryptsetup-tpm2.c +++ b/src/cryptsetup/cryptsetup-tpm2.c @@ -14,6 +14,7 @@ int acquire_tpm2_key( const char *device, uint32_t pcr_mask, uint16_t pcr_bank, + uint16_t primary_alg, const char *key_file, size_t key_file_size, uint64_t key_file_offset, @@ -63,7 +64,7 @@ int acquire_tpm2_key( blob = loaded_blob; } - return tpm2_unseal(device, pcr_mask, pcr_bank, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size); + return tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size); } int find_tpm2_auto_data( @@ -72,6 +73,7 @@ int find_tpm2_auto_data( int start_token, uint32_t *ret_pcr_mask, uint16_t *ret_pcr_bank, + uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, @@ -84,6 +86,7 @@ int find_tpm2_auto_data( int r, keyslot = -1, token = -1; uint32_t pcr_mask = 0; uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */ + uint16_t primary_alg = TPM2_ALG_ECC; /* ECC was the only supported algorithm in systemd < 250, use that as implied default, for compatibility */ assert(cd); @@ -122,8 +125,11 @@ int find_tpm2_auto_data( search_pcr_mask != pcr_mask) /* PCR mask doesn't match what is configured, ignore this entry */ continue; - /* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded to SHA256 */ assert(pcr_bank == UINT16_MAX); + assert(primary_alg == TPM2_ALG_ECC); + + /* The bank field is optional, since it was added in systemd 250 only. Before the bank was + * hardcoded to SHA256. */ w = json_variant_by_key(v, "tpm2-pcr-bank"); if (w) { /* The PCR bank field is optional */ @@ -139,6 +145,23 @@ int find_tpm2_auto_data( pcr_bank = r; } + /* The primary key algorithm field is optional, since it was also added in systemd 250 + * only. Before the algorithm was hardcoded to ECC. */ + w = json_variant_by_key(v, "tpm2-primary-alg"); + if (w) { + /* The primary key algorithm is optional */ + + if (!json_variant_is_string(w)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "TPM2 primary key algorithm is not a string."); + + r = tpm2_primary_alg_from_string(json_variant_string(w)); + if (r < 0) + return log_error_errno(r, "TPM2 primary key algorithm invalid or not supported: %s", json_variant_string(w)); + + primary_alg = r; + } + assert(!blob); w = json_variant_by_key(v, "tpm2-blob"); if (!w || !json_variant_is_string(w)) @@ -184,6 +207,7 @@ int find_tpm2_auto_data( *ret_keyslot = keyslot; *ret_token = token; *ret_pcr_bank = pcr_bank; + *ret_primary_alg = primary_alg; return 0; } diff --git a/src/cryptsetup/cryptsetup-tpm2.h b/src/cryptsetup/cryptsetup-tpm2.h index a82ecb4594..bd04620462 100644 --- a/src/cryptsetup/cryptsetup-tpm2.h +++ b/src/cryptsetup/cryptsetup-tpm2.h @@ -14,6 +14,7 @@ int acquire_tpm2_key( const char *device, uint32_t pcr_mask, uint16_t pcr_bank, + uint16_t primary_alg, const char *key_file, size_t key_file_size, uint64_t key_file_offset, @@ -30,6 +31,7 @@ int find_tpm2_auto_data( int start_token, uint32_t *ret_pcr_mask, uint16_t *ret_pcr_bank, + uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, @@ -44,6 +46,7 @@ static inline int acquire_tpm2_key( const char *device, uint32_t pcr_mask, uint16_t pcr_bank, + uint16_t primary_alg, const char *key_file, size_t key_file_size, uint64_t key_file_offset, @@ -64,6 +67,7 @@ static inline int find_tpm2_auto_data( int start_token, uint32_t *ret_pcr_mask, uint16_t *ret_pcr_bank, + uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index fdd57def86..ceaf10e647 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -1248,6 +1248,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( arg_tpm2_device, arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT : arg_tpm2_pcr_mask, UINT16_MAX, + 0, key_file, arg_keyfile_size, arg_keyfile_offset, key_data, key_data_size, NULL, 0, /* we don't know the policy hash */ @@ -1284,7 +1285,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( for (;;) { uint32_t pcr_mask; - uint16_t pcr_bank; + uint16_t pcr_bank, primary_alg; r = find_tpm2_auto_data( cd, @@ -1292,6 +1293,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( token, /* search for the token with this index, or any later index than this */ &pcr_mask, &pcr_bank, + &primary_alg, &blob, &blob_size, &policy_hash, &policy_hash_size, &keyslot, @@ -1314,6 +1316,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( arg_tpm2_device, pcr_mask, pcr_bank, + primary_alg, NULL, 0, 0, /* no key file */ blob, blob_size, policy_hash, policy_hash_size, diff --git a/src/partition/repart.c b/src/partition/repart.c index 926dbb2ae4..bf6bac07c5 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -2618,10 +2618,10 @@ static int partition_encrypt( _cleanup_(erase_and_freep) void *secret = NULL; _cleanup_free_ void *blob = NULL, *hash = NULL; size_t secret_size, blob_size, hash_size; - uint16_t pcr_bank; + uint16_t pcr_bank, primary_alg; int keyslot; - r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank); + r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg); if (r < 0) return log_error_errno(r, "Failed to seal to TPM2: %m"); @@ -2643,7 +2643,7 @@ static int partition_encrypt( if (keyslot < 0) return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node); - r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &v); + r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &v); if (r < 0) return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m"); diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 9980cd93a9..d1ca3778b7 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -371,10 +371,10 @@ struct _packed_ encrypted_credential_header { }; struct _packed_ tpm2_credential_header { - le64_t pcr_mask; /* Note that the spec for PC Clients only mandates 24 PCRs, and that's what systems - * generally have. But keep the door open for more. */ - le16_t pcr_bank; /* For now, either TPM2_ALG_SHA256 or TPM2_ALG_SHA1 */ - le16_t _zero; /* Filler to maintain 32bit alignment */ + le64_t pcr_mask; /* Note that the spec for PC Clients only mandates 24 PCRs, and that's what systems + * generally have. But keep the door open for more. */ + le16_t pcr_bank; /* For now, either TPM2_ALG_SHA256 or TPM2_ALG_SHA1 */ + le16_t primary_alg; /* Primary key algorithm (either TPM2_ALG_RSA or TPM2_ALG_ECC for now) */ le32_t blob_size; le32_t policy_hash_size; uint8_t policy_hash_and_blob[]; @@ -441,10 +441,10 @@ int encrypt_credential_and_warn( size_t host_key_size = 0, tpm2_key_size = 0, tpm2_blob_size = 0, tpm2_policy_hash_size = 0, output_size, p, ml; _cleanup_free_ void *tpm2_blob = NULL, *tpm2_policy_hash = NULL, *iv = NULL, *output = NULL; _cleanup_free_ struct metadata_credential_header *m = NULL; + uint16_t tpm2_pcr_bank = 0, tpm2_primary_alg = 0; struct encrypted_credential_header *h; int ksz, bsz, ivsz, tsz, added, r; uint8_t md[SHA256_DIGEST_LENGTH]; - uint16_t tpm2_pcr_bank = 0; const EVP_CIPHER *cc; #if HAVE_TPM2 bool try_tpm2 = false; @@ -512,7 +512,8 @@ int encrypt_credential_and_warn( &tpm2_blob_size, &tpm2_policy_hash, &tpm2_policy_hash_size, - &tpm2_pcr_bank); + &tpm2_pcr_bank, + &tpm2_primary_alg); if (r < 0) { if (!sd_id128_is_null(with_key)) return r; @@ -606,6 +607,7 @@ int encrypt_credential_and_warn( t = (struct tpm2_credential_header*) ((uint8_t*) output + p); t->pcr_mask = htole64(tpm2_pcr_mask); t->pcr_bank = htole16(tpm2_pcr_bank); + t->primary_alg = htole16(tpm2_primary_alg); t->blob_size = htole32(tpm2_blob_size); t->policy_hash_size = htole32(tpm2_policy_hash_size); memcpy(t->policy_hash_and_blob, tpm2_blob, tpm2_blob_size); @@ -747,10 +749,10 @@ int decrypt_credential_and_warn( if (le64toh(t->pcr_mask) >= (UINT64_C(1) << TPM2_PCRS_MAX)) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR mask out of range."); - if (!tpm2_pcr_bank_supported(le16toh(t->pcr_bank))) + if (!tpm2_pcr_bank_to_string(le16toh(t->pcr_bank))) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR bank invalid or not supported"); - if (le16toh(t->_zero) != 0) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 padding space not zero."); + if (!tpm2_primary_alg_to_string(le16toh(t->primary_alg))) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 primary key algorithm invalid or not supported."); if (le32toh(t->blob_size) > CREDENTIAL_FIELD_SIZE_MAX) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected TPM2 blob size."); if (le32toh(t->policy_hash_size) > CREDENTIAL_FIELD_SIZE_MAX) @@ -768,6 +770,7 @@ int decrypt_credential_and_warn( r = tpm2_unseal(tpm2_device, le64toh(t->pcr_mask), le16toh(t->pcr_bank), + le16toh(t->primary_alg), t->policy_hash_and_blob, le32toh(t->blob_size), t->policy_hash_and_blob + le32toh(t->blob_size), diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index f29686e994..3cdb3ed7b0 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -257,10 +257,12 @@ static int tpm2_credit_random(ESYS_CONTEXT *c) { static int tpm2_make_primary( ESYS_CONTEXT *c, - ESYS_TR *ret_primary) { + ESYS_TR *ret_primary, + TPMI_ALG_PUBLIC alg, + TPMI_ALG_PUBLIC *ret_alg) { static const TPM2B_SENSITIVE_CREATE primary_sensitive = {}; - static const TPM2B_PUBLIC primary_template = { + static const TPM2B_PUBLIC primary_template_ecc = { .size = sizeof(TPMT_PUBLIC), .publicArea = { .type = TPM2_ALG_ECC, @@ -280,35 +282,102 @@ static int tpm2_make_primary( }, }, }; + static const TPM2B_PUBLIC primary_template_rsa = { + .size = sizeof(TPMT_PUBLIC), + .publicArea = { + .type = TPM2_ALG_RSA, + .nameAlg = TPM2_ALG_SHA256, + .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH, + .parameters = { + .rsaDetail = { + .symmetric = { + .algorithm = TPM2_ALG_AES, + .keyBits.aes = 128, + .mode.aes = TPM2_ALG_CFB, + }, + .scheme.scheme = TPM2_ALG_NULL, + .keyBits = 2048, + }, + }, + }, + }; + static const TPML_PCR_SELECTION creation_pcr = {}; ESYS_TR primary = ESYS_TR_NONE; TSS2_RC rc; + usec_t ts; log_debug("Creating primary key on TPM."); - rc = sym_Esys_CreatePrimary( - c, - ESYS_TR_RH_OWNER, - ESYS_TR_PASSWORD, - ESYS_TR_NONE, - ESYS_TR_NONE, - &primary_sensitive, - &primary_template, - NULL, - &creation_pcr, - &primary, - NULL, - NULL, - NULL, - NULL); + /* So apparently not all TPM2 devices support ECC. ECC is generally preferably, because it's so much + * faster, noticeably so (~10s vs. ~240ms on my system). Hence, unless explicitly configured let's + * try to use ECC first, and if that does not work, let's fall back to RSA. */ - if (rc != TSS2_RC_SUCCESS) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Failed to generate primary key in TPM: %s", sym_Tss2_RC_Decode(rc)); + ts = now(CLOCK_MONOTONIC); - log_debug("Successfully created primary key on TPM."); + if (IN_SET(alg, 0, TPM2_ALG_ECC)) { + rc = sym_Esys_CreatePrimary( + c, + ESYS_TR_RH_OWNER, + ESYS_TR_PASSWORD, + ESYS_TR_NONE, + ESYS_TR_NONE, + &primary_sensitive, + &primary_template_ecc, + NULL, + &creation_pcr, + &primary, + NULL, + NULL, + NULL, + NULL); + + if (rc != TSS2_RC_SUCCESS) { + if (alg != 0) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to generate ECC primary key in TPM: %s", sym_Tss2_RC_Decode(rc)); + + log_debug("Failed to generate ECC primary key in TPM, trying RSA: %s", sym_Tss2_RC_Decode(rc)); + } else { + log_debug("Successfully created ECC primary key on TPM."); + alg = TPM2_ALG_ECC; + } + } + + if (IN_SET(alg, 0, TPM2_ALG_RSA)) { + rc = sym_Esys_CreatePrimary( + c, + ESYS_TR_RH_OWNER, + ESYS_TR_PASSWORD, + ESYS_TR_NONE, + ESYS_TR_NONE, + &primary_sensitive, + &primary_template_rsa, + NULL, + &creation_pcr, + &primary, + NULL, + NULL, + NULL, + NULL); + if (rc != TSS2_RC_SUCCESS) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to generate RSA primary key in TPM: %s", sym_Tss2_RC_Decode(rc)); + else if (alg == 0) { + log_notice("TPM2 chip apparently does not support ECC primary keys, falling back to RSA. " + "This likely means TPM2 operations will be relatively slow, please be patient."); + alg = TPM2_ALG_RSA; + } + + log_debug("Successfully created RSA primary key on TPM."); + } + + log_debug("Generating primary key on TPM2 took %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - ts, USEC_PER_MSEC)); *ret_primary = primary; + if (ret_alg) + *ret_alg = alg; + return 0; } @@ -525,7 +594,8 @@ int tpm2_seal( size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, - uint16_t *ret_pcr_bank) { + uint16_t *ret_pcr_bank, + uint16_t *ret_primary_alg) { _cleanup_(tpm2_context_destroy) struct tpm2_context c = {}; _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL; @@ -536,6 +606,7 @@ int tpm2_seal( _cleanup_free_ void *blob = NULL, *hash = NULL; TPM2B_SENSITIVE_CREATE hmac_sensitive; ESYS_TR primary = ESYS_TR_NONE; + TPMI_ALG_PUBLIC primary_alg; TPM2B_PUBLIC hmac_template; TPMI_ALG_HASH pcr_bank; size_t k, blob_size; @@ -555,13 +626,14 @@ int tpm2_seal( /* So here's what we do here: we connect to the TPM2 chip. It persistently contains a "seed" key that * is randomized when the TPM2 is first initialized or reset and remains stable across boots. We - * generate a "primary" key pair derived from that (RSA). Given the seed remains fixed this will - * result in the same key pair whenever we specify the exact same parameters for it. We then create a - * PCR-bound policy session, which calculates a hash on the current PCR values of the indexes we - * specify. We then generate a randomized key on the host (which is the key we actually enroll in the - * LUKS2 keyslots), which we upload into the TPM2, where it is encrypted with the "primary" key, - * taking the PCR policy session into account. We then download the encrypted key from the TPM2 - * ("sealing") and marshall it into binary form, which is ultimately placed in the LUKS2 JSON header. + * generate a "primary" key pair derived from that (ECC if possible, RSA as fallback). Given the seed + * remains fixed this will result in the same key pair whenever we specify the exact same parameters + * for it. We then create a PCR-bound policy session, which calculates a hash on the current PCR + * values of the indexes we specify. We then generate a randomized key on the host (which is the key + * we actually enroll in the LUKS2 keyslots), which we upload into the TPM2, where it is encrypted + * with the "primary" key, taking the PCR policy session into account. We then download the encrypted + * key from the TPM2 ("sealing") and marshall it into binary form, which is ultimately placed in the + * LUKS2 JSON header. * * The TPM2 "seed" key and "primary" keys never leave the TPM2 chip (and cannot be extracted at * all). The random key we enroll in LUKS2 we generate on the host using the Linux random device. It @@ -574,7 +646,7 @@ int tpm2_seal( if (r < 0) return r; - r = tpm2_make_primary(c.esys_context, &primary); + r = tpm2_make_primary(c.esys_context, &primary, 0, &primary_alg); if (r < 0) return r; @@ -701,6 +773,7 @@ int tpm2_seal( *ret_pcr_hash = TAKE_PTR(hash); *ret_pcr_hash_size = policy_digest->size; *ret_pcr_bank = pcr_bank; + *ret_primary_alg = primary_alg; r = 0; @@ -713,6 +786,7 @@ int tpm2_unseal( const char *device, uint32_t pcr_mask, uint16_t pcr_bank, + uint16_t primary_alg, const void *blob, size_t blob_size, const void *known_policy_hash, @@ -783,7 +857,7 @@ int tpm2_unseal( return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Current policy digest does not match stored policy digest, cancelling TPM2 authentication attempt."); - r = tpm2_make_primary(c.esys_context, &primary); + r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL); if (r < 0) return r; @@ -1012,6 +1086,7 @@ int tpm2_make_luks2_json( int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, + uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, @@ -1055,6 +1130,7 @@ int tpm2_make_luks2_json( JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_BASE64(blob, blob_size)), JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(a)), JSON_BUILD_PAIR_CONDITION(!!tpm2_pcr_bank_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_pcr_bank_to_string(pcr_bank))), + JSON_BUILD_PAIR_CONDITION(!!tpm2_primary_alg_to_string(primary_alg), "tpm2-primary-alg", JSON_BUILD_STRING(tpm2_primary_alg_to_string(primary_alg))), JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size)))); if (r < 0) return r; @@ -1065,24 +1141,9 @@ int tpm2_make_luks2_json( return keyslot; } -/* We want the helpers below to work also if TPM2 libs are not available, hence define these two defines if - * they are missing. */ -#ifndef TPM2_ALG_SHA256 -#define TPM2_ALG_SHA256 0xB -#endif - -#ifndef TPM2_ALG_SHA1 -#define TPM2_ALG_SHA1 0x4 -#endif - -int tpm2_pcr_bank_supported(uint16_t bank) { +const char *tpm2_pcr_bank_to_string(uint16_t bank) { /* For now, let's officially only support these two. We can extend this later on, should the need * arise. */ - return IN_SET(bank, TPM2_ALG_SHA256, TPM2_ALG_SHA1); -} - -const char *tpm2_pcr_bank_to_string(uint16_t bank) { - /* Similar here, only support the two for now, we can always extend this later. */ if (bank == TPM2_ALG_SHA256) return "sha256"; if (bank == TPM2_ALG_SHA1) @@ -1097,3 +1158,19 @@ int tpm2_pcr_bank_from_string(const char *bank) { return TPM2_ALG_SHA1; return -EINVAL; } + +const char *tpm2_primary_alg_to_string(uint16_t alg) { + if (alg == TPM2_ALG_ECC) + return "ecc"; + if (alg == TPM2_ALG_RSA) + return "rsa"; + return NULL; +} + +int tpm2_primary_alg_from_string(const char *alg) { + if (streq_ptr(alg, "ecc")) + return TPM2_ALG_ECC; + if (streq_ptr(alg, "rsa")) + return TPM2_ALG_RSA; + return -EINVAL; +} diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 8032afe7b0..b8f12760d0 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -34,8 +34,8 @@ extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], siz int dlopen_tpm2(void); -int tpm2_seal(const char *device, uint32_t pcr_mask, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank); -int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size); +int tpm2_seal(const char *device, uint32_t pcr_mask, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg); +int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size); #endif @@ -44,17 +44,37 @@ int tpm2_find_device_auto(int log_level, char **ret); int tpm2_parse_pcrs(const char *s, uint32_t *ret); -int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret); +int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret); #define TPM2_PCRS_MAX 24 /* Default to PCR 7 only */ #define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7) -int tpm2_pcr_bank_supported(uint16_t bank); +/* We want the helpers below to work also if TPM2 libs are not available, hence define these four defines if + * they are missing. */ +#ifndef TPM2_ALG_SHA256 +#define TPM2_ALG_SHA256 0xB +#endif + +#ifndef TPM2_ALG_SHA1 +#define TPM2_ALG_SHA1 0x4 +#endif + +#ifndef TPM2_ALG_ECC +#define TPM2_ALG_ECC 0x23 +#endif + +#ifndef TPM2_ALG_RSA +#define TPM2_ALG_RSA 0x1 +#endif + const char *tpm2_pcr_bank_to_string(uint16_t bank); int tpm2_pcr_bank_from_string(const char *bank); +const char *tpm2_primary_alg_to_string(uint16_t bank); +int tpm2_primary_alg_from_string(const char *alg); + typedef struct { uint32_t search_pcr_mask; const char *device;