]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tpm2: support RSA primary keys as fallback if TPM2 devices don't support ECC
authorLennart Poettering <lennart@poettering.net>
Mon, 13 Sep 2021 08:52:43 +0000 (10:52 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 13 Sep 2021 12:48:23 +0000 (14:48 +0200)
Previously, we hardcoded use of ECC as primary keys, since they are much
faster (i.e. saving multiple seconds) to do TPM2 operations with. Alas,
not all TPM2 chips appear to support ECC. Bummer.

Let's hence add a fallback logic: if we can't create an ECC primary key,
use an RSA key, and store that fact away.

AFIU the security guarantees should be roughly the same, it's just that
RSA primary keys is so much slower to work with than ECC.

The primary key algorithm is used is stored in the JSON header of LUKS
disks, in a new field. If the field is absent we assume to use ECC, to
provide full compatibility with old systemd versions.

The primary key algorithm is stored in a new field in the credentials
file format (in fact, a previously unused zero space is used), too.

Hopefully, this should ensure that TPM2 support will "just work" on more
systems.

Fixes: #20361
src/cryptenroll/cryptenroll-tpm2.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
src/cryptsetup/cryptsetup-tpm2.c
src/cryptsetup/cryptsetup-tpm2.h
src/cryptsetup/cryptsetup.c
src/partition/repart.c
src/shared/creds-util.c
src/shared/tpm2-util.c
src/shared/tpm2-util.h

index 697b4c2335b36d1b12deb6482bed21bfcec50d97..23deeed272d6f205fcb347dc4521256be7e9a043 100644 (file)
@@ -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");
 
index 0c9d566e0d6cc13ed1aff25e0132f1d19c22199c..5471cb35b6378bf5dc25b8b284e64d8fd90741b5 100644 (file)
@@ -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.");
index a5571f31f6dba262d3deab64ef69414572c05d17..4167275317ad2d010681d2f5ac2416e1134e47e9 100644 (file)
@@ -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);
 
index 1a20f2cc1fd73bd1480b18ef2efbc0328f6571d9..0c93ea82cc926eee1a1ee38b7499a4c98f77ad61 100644 (file)
@@ -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);
index 8da1880a35c4d8f75cf9eee93c9c2b144d76405f..4d95dacca5a744a35f14111cfaf2547bb28bf033 100644 (file)
@@ -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;
 }
index a82ecb459419d819a190d85adbf601a7361b777b..bd046204628cfafbb6683c66cd1f8462c4151703 100644 (file)
@@ -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,
index fdd57def86866bd17f15c2b0585bbe9d7356bec0..ceaf10e6477dc792e32e0db3d41621155fa9e103 100644 (file)
@@ -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,
index 926dbb2ae4a3c712b33bbe572cd660b756eb355f..bf6bac07c516d39e71050d0f303d38c8a9deb591 100644 (file)
@@ -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");
 
index 9980cd93a92b47b83abab0b4e559b80e93d2c92c..d1ca3778b7352458929d4619cab3b336e964f0db 100644 (file)
@@ -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),
index f29686e9948b1cf524aafce36cf2b77844658793..3cdb3ed7b0bc2bd7c2cccbd251296113b514fb81 100644 (file)
@@ -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);
+
+        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("Successfully created 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;
+}
index 8032afe7b000ea4d64e480f47d1a59bd12ecc68e..b8f12760d00018a5c54361969e03068fb237eaf5 100644 (file)
@@ -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;