]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptenroll: add support for calculated TPM2 enrollment
authorDan Streetman <ddstreet@ieee.org>
Fri, 21 Jul 2023 19:49:16 +0000 (15:49 -0400)
committerDan Streetman <ddstreet@ieee.org>
Tue, 7 Nov 2023 17:20:54 +0000 (12:20 -0500)
Instead of enrolling the local TPM to a luks volume, use the public key from a
TPM to enroll it into the luks volume. This is useful when enrolling a TPM that
is not currently accessible, for example if the TPM is located on a different
system.

man/systemd-cryptenroll.xml
src/cryptenroll/cryptenroll-tpm2.c
src/cryptenroll/cryptenroll-tpm2.h
src/cryptenroll/cryptenroll.c
src/shared/tpm2-util.c
src/shared/tpm2-util.h

index 3d0efc9bf7b7356b41a12da2779719a797374e8e..ad32bf68f2ad5aa5de5fa6d6da7cd22943c843ac 100644 (file)
         <xi:include href="version-info.xml" xpointer="v248"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--tpm2-device-key=</option><replaceable>PATH</replaceable></term>
+
+        <listitem><para>Enroll a TPM2 security chip using its public key. Expects a path referring to the
+        TPM2 public key in TPM2B_PUBLIC format. This cannot be used with <option>--tpm2-device=</option>, as
+        it performs the same operation, but without connecting to the TPM2 security chip; instead the
+        enrollment is calculated using the provided TPM2 key. This is useful in situations where the TPM2
+        security chip is not available at the time of enrollment.</para>
+
+        <para>The key, in most cases, should be the Storage Root Key (SRK) from the TPM2 security chip. If a
+        key from a different handle (not the SRK) is used, you must specify its handle index using
+        <option>--tpm2-seal-key-handle=</option>.</para>
+
+        <para>You may use tpm2-tss tools to get the SRK from the TPM2 security chip with <citerefentry
+        project='mankier'><refentrytitle>tpm2_readpublic</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+        for example:</para>
+
+        <programlisting>tpm2_readpublic -c 0x81000001 -o srk.pub</programlisting>
+
+        <xi:include href="version-info.xml" xpointer="v255"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--tpm2-seal-key-handle=</option><replaceable>HANDLE</replaceable></term>
 
index 1273822d3ae72f88bfdd0418bbb38485a62a5e9d..4b9f6b2d07fad78e58976792381c69630c5de2be 100644 (file)
@@ -134,6 +134,7 @@ int enroll_tpm2(struct crypt_device *cd,
                 size_t volume_key_size,
                 const char *device,
                 uint32_t seal_key_handle,
+                const char *device_key,
                 Tpm2PCRValue *hash_pcr_values,
                 size_t n_hash_pcr_values,
                 const char *pubkey_path,
@@ -208,25 +209,62 @@ int enroll_tpm2(struct crypt_device *cd,
                         return log_debug_errno(r, "Failed to read TPM PCR signature: %m");
         }
 
+        bool any_pcr_value_specified = tpm2_pcr_values_has_any_values(hash_pcr_values, n_hash_pcr_values);
+
         _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
         if (pcrlock_path) {
                 r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy);
                 if (r < 0)
                         return r;
 
+                any_pcr_value_specified = true;
                 flags |= TPM2_FLAGS_USE_PCRLOCK;
         }
 
         _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
-        r = tpm2_context_new(device, &tpm2_context);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create TPM2 context: %m");
+        TPM2B_PUBLIC device_key_public = {};
+        if (device_key) {
+                _cleanup_free_ char *device_key_buffer = NULL;
+                size_t device_key_buffer_size;
+                r = read_full_file(device_key, &device_key_buffer, &device_key_buffer_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read device key from file: %m");
 
-        bool pcr_value_specified = tpm2_pcr_values_has_any_values(hash_pcr_values, n_hash_pcr_values);
+                r = dlopen_tpm2();
+                if (r < 0)
+                        return log_debug_errno(r, "TPM2 support not installed: %m");
+
+                TSS2_RC rc;
+                size_t offset = 0;
+                rc = sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal(
+                                (uint8_t*) device_key_buffer,
+                                device_key_buffer_size,
+                                &offset,
+                                &device_key_public);
+                if (rc != TSS2_RC_SUCCESS)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Could not unmarshal public key from file.");
 
-        r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values);
-        if (r < 0)
-                return log_error_errno(r, "Could not read pcr values: %m");
+                assert(offset <= device_key_buffer_size);
+                if (offset != device_key_buffer_size)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Found %zu bytes of trailing garbage in public key file.",
+                                               device_key_buffer_size - offset);
+
+                if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Must provide all PCR values when using TPM2 device key.");
+        } else {
+                r = tpm2_context_new(device, &tpm2_context);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create TPM2 context: %m");
+
+                if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values)) {
+                        r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values);
+                        if (r < 0)
+                                return log_error_errno(r, "Could not read pcr values: %m");
+                }
+        }
 
         uint16_t hash_pcr_bank = 0;
         uint32_t hash_pcr_mask = 0;
@@ -263,15 +301,26 @@ int enroll_tpm2(struct crypt_device *cd,
         if (r < 0)
                 return r;
 
-        r = tpm2_seal(tpm2_context,
-                      seal_key_handle,
-                      &policy,
-                      pin_str,
-                      &secret, &secret_size,
-                      &blob, &blob_size,
-                      /* ret_primary_alg= */ NULL,
-                      &srk_buf,
-                      &srk_buf_size);
+        if (device_key)
+                r = tpm2_calculate_seal(
+                                seal_key_handle,
+                                &device_key_public,
+                                /* attributes= */ NULL,
+                                /* secret= */ NULL, /* secret_size= */ 0,
+                                &policy,
+                                pin_str,
+                                &secret, &secret_size,
+                                &blob, &blob_size,
+                                &srk_buf, &srk_buf_size);
+        else
+                r = tpm2_seal(tpm2_context,
+                              seal_key_handle,
+                              &policy,
+                              pin_str,
+                              &secret, &secret_size,
+                              &blob, &blob_size,
+                              /* ret_primary_alg= */ NULL,
+                              &srk_buf, &srk_buf_size);
         if (r < 0)
                 return log_error_errno(r, "Failed to seal to TPM2: %m");
 
@@ -286,8 +335,8 @@ int enroll_tpm2(struct crypt_device *cd,
                 return r; /* return existing keyslot, so that wiping won't kill it */
         }
 
-        /* Quick verification that everything is in order, we are not in a hurry after all. */
-        if ((!pubkey || signature_json) && !pcr_value_specified) {
+        /* If possible, verify the sealed data object. */
+        if ((!pubkey || signature_json) && !any_pcr_value_specified && !device_key) {
                 _cleanup_(erase_and_freep) void *secret2 = NULL;
                 size_t secret2_size;
 
index 6439a08df40a073d0835f5cd639aa179832ef66b..2fbcdd4b2f1650329ea35c6a4f4975ce380784ef 100644 (file)
@@ -8,9 +8,9 @@
 #include "tpm2-util.h"
 
 #if HAVE_TPM2
-int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path);
+int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, const char *device_key, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path);
 #else
-static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path) {
+static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, const char *device_key, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path) {
         return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                "TPM2 key enrollment not supported.");
 }
index 20d6bc268099b270f41c03d7e2a416a8f244b0a6..d2915ad7eec9680be82dfc35af9cbb7d67ad46d0 100644 (file)
@@ -37,6 +37,7 @@ static char *arg_pkcs11_token_uri = NULL;
 static char *arg_fido2_device = NULL;
 static char *arg_tpm2_device = NULL;
 static uint32_t arg_tpm2_seal_key_handle = 0;
+static char *arg_tpm2_device_key = NULL;
 static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL;
 static size_t arg_tpm2_n_hash_pcr_values = 0;
 static bool arg_tpm2_pin = false;
@@ -63,6 +64,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_unlock_fido2_device, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device_key, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
@@ -137,6 +139,8 @@ static int help(void) {
                "                       Enroll a TPM2 device\n"
                "     --tpm2-seal-key-handle=HANDLE\n"
                "                       Specify handle of key to use for sealing\n"
+               "     --tpm2-device-key=PATH\n"
+               "                       Enroll a TPM2 device using its public key\n"
                "     --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
                "                       Specify TPM2 PCRs to seal against\n"
                "     --tpm2-public-key=PATH\n"
@@ -172,6 +176,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_FIDO2_DEVICE,
                 ARG_TPM2_DEVICE,
                 ARG_TPM2_SEAL_KEY_HANDLE,
+                ARG_TPM2_DEVICE_KEY,
                 ARG_TPM2_PCRS,
                 ARG_TPM2_PUBLIC_KEY,
                 ARG_TPM2_PUBLIC_KEY_PCRS,
@@ -200,6 +205,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV         },
                 { "tpm2-device",                  required_argument, NULL, ARG_TPM2_DEVICE           },
                 { "tpm2-seal-key-handle",         required_argument, NULL, ARG_TPM2_SEAL_KEY_HANDLE  },
+                { "tpm2-device-key",              required_argument, NULL, ARG_TPM2_DEVICE_KEY       },
                 { "tpm2-pcrs",                    required_argument, NULL, ARG_TPM2_PCRS             },
                 { "tpm2-public-key",              required_argument, NULL, ARG_TPM2_PUBLIC_KEY       },
                 { "tpm2-public-key-pcrs",         required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS  },
@@ -381,6 +387,19 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_TPM2_DEVICE_KEY:
+                        if (arg_enroll_type >= 0 || arg_tpm2_device_key)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Multiple operations specified at once, refusing.");
+
+
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_device_key);
+                        if (r < 0)
+                                return r;
+
+                        arg_enroll_type = ENROLL_TPM2;
+                        break;
+
                 case ARG_TPM2_PCRS:
                         auto_hash_pcr_values = false;
                         r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values);
@@ -715,7 +734,7 @@ static int run(int argc, char *argv[]) {
                 break;
 
         case ENROLL_TPM2:
-                slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_seal_key_handle, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin, arg_tpm2_pcrlock);
+                slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_seal_key_handle, arg_tpm2_device_key, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin, arg_tpm2_pcrlock);
                 break;
 
         case _ENROLL_TYPE_INVALID:
index 783a603e3d98b1603afe22d7306030fd3ef390c3..f3712c84962ab47f8701e60f9582601707f3938f 100644 (file)
@@ -88,7 +88,7 @@ static TSS2_RC (*sym_Tss2_MU_TPM2B_NAME_Marshal)(TPM2B_NAME const *src, uint8_t
 static TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
 static TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE  *dest) = NULL;
 static TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-static TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL;
+TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL;
 static TSS2_RC (*sym_Tss2_MU_TPM2B_SENSITIVE_Marshal)(TPM2B_SENSITIVE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
 static TSS2_RC (*sym_Tss2_MU_TPML_PCR_SELECTION_Marshal)(TPML_PCR_SELECTION const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
 static TSS2_RC (*sym_Tss2_MU_TPMS_NV_PUBLIC_Marshal)(TPMS_NV_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
index 1243b596a51c8b48692eb928abd2eb4d0a55deb0..e867976369efe32070ec385c71edc0d4ed43f129 100644 (file)
@@ -361,6 +361,8 @@ int tpm2_deserialize(Tpm2Context *c, const void *serialized, size_t serialized_s
                         0;                                              \
         })
 
+extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest);
+
 #else /* HAVE_TPM2 */
 typedef struct {} Tpm2Context;
 typedef struct {} Tpm2Handle;