]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
tpm2_key_protector: Support PCR capping
authorGary Lin <glin@suse.com>
Fri, 3 Oct 2025 03:22:07 +0000 (11:22 +0800)
committerDaniel Kiper <daniel.kiper@oracle.com>
Sat, 11 Oct 2025 13:43:58 +0000 (15:43 +0200)
To prevent a sealed key from being unsealed again, a common and
straightforward method is to "cap" the key by extending the associated
PCRs. When the PCRs associated with the sealed key are extended, TPM will
be unable to unseal the key, as the PCR values required for unsealing no
longer match, effectively rendering the key unusable until the next
system boot or a state where the PCRs are reset to their expected values.

To cap a specific set of PCRs, simply append the argument '-c pcr_list'
to the tpm2_key_protector command. Upon successfully unsealing the key,
the TPM2 key protector will then invoke tpm2_protector_cap_pcrs(). This
function extends the selected PCRs with an EV_SEPARATOR event,
effectively "capping" them. Consequently, the associated key cannot be
unsealed in any subsequent attempts until these PCRs are reset to their
original, pre-capped state, typically occurring upon the next system
boot.

Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
docs/grub.texi
grub-core/commands/tpm2_key_protector/module.c
grub-core/normal/main.c

index d697010cefdb437747ea07fb6f4fed2202e7dcd0..25e602a56756df2b3cdd94bac77ec36a273ab991 100644 (file)
@@ -8458,7 +8458,7 @@ either @var{expression1} or @var{expression2} is true
 @node tpm2_key_protector_init
 @subsection tpm2_key_protector_init
 
-@deffn Command tpm2_key_protector_init [@option{--mode} | @option{-m} mode] | [@option{--pcrs} | @option{-p} pcrlist] | [@option{--bank} | @option{-b} pcrbank] | [ [@option{--tpm2key} | @option{-T} tpm2key_file] | [@option{--keyfile} | @option{-k} keyfile] ] | [@option{--srk} | @option{-s} handle] | [@option{--asymmetric} | @option{-a} srk_type] | [@option{--nvindex} | @option{-n} nv_index]
+@deffn Command tpm2_key_protector_init [@option{--mode} | @option{-m} mode] | [@option{--pcrs} | @option{-p} pcrlist] | [@option{--bank} | @option{-b} pcrbank] | [@option{--cap-pcrs} | @option{-c} pcrlist] | [ [@option{--tpm2key} | @option{-T} tpm2key_file] | [@option{--keyfile} | @option{-k} keyfile] ] | [@option{--srk} | @option{-s} handle] | [@option{--asymmetric} | @option{-a} srk_type] | [@option{--nvindex} | @option{-n} nv_index]
 Initialize the TPM2 key protector to unseal the key for the @command{cryptomount}
 (@pxref{cryptomount}) command. There are two supported modes,
 SRK(@kbd{srk}) and NV index(@kbd{nv}), to be specified by the option
@@ -8473,6 +8473,24 @@ bank that the key is sealed with. The PCR list is a comma-separated list, e.g.,
 bank is chosen by selecting a hash algorithm. The current supported PCR banks
 are SHA1, SHA256, SHA384, and SHA512, and the default is SHA256.
 
+The @option{-c} option is introduced to enable the "capping" of a specified list of
+PCRs. This feature addresses scenarios where a user wants to ensure a sealed key
+cannot be unsealed again after its initial use. When the @option{-c} option is
+employed, and the key is successfully unsealed, the TPM2 key protector automatically
+extends the selected PCRs with an EV_SEPARATOR event. This action cryptographically
+alters the PCR values, thereby preventing the associated key from being unsealed in
+any subsequent attempts until those specific PCRs are reset to their original state,
+which typically occurs during a system reboot. In general, it is sufficient to
+extend one associated PCR to cap the key.
+
+It's noteworthy that a key sealed against PCR 8 naturally incorporates a "capping"
+behavior, even without explicitly using a @option{-c} option. This is because GRUB
+measures all commands into PCR 8, including those from configuration files. As a
+result, the value of PCR 8 changes with virtually every command execution during
+the boot process. Consequently, a key sealed against PCR 8 can only be unsealed
+once in a given boot session, as any subsequent GRUB command will alter PCR 8,
+invalidating the unsealing policy and effectively "capping" the key.
+
 Some options are only available for the specific mode. The SRK-specific
 options are @option{-T}, @option{-k}, @option{-a}, and @option{-s}. On the
 other hand, the NV index-specific option is @option{-n}.
index b84c2234f6aab7e4ee27802364f291fb917ce22c..1226b65e009a80fe9c2c50160f244f61043051b6 100644 (file)
@@ -28,6 +28,7 @@
 #include <tss2_buffer.h>
 #include <tss2_types.h>
 #include <tss2_mu.h>
+#include <tcg2.h>
 
 #include "tpm2_args.h"
 #include "tpm2.h"
@@ -47,6 +48,7 @@ typedef enum tpm2_protector_options
   OPTION_MODE,
   OPTION_PCRS,
   OPTION_BANK,
+  OPTION_CAPPCRS,
   OPTION_TPM2KEY,
   OPTION_KEYFILE,
   OPTION_SRK,
@@ -61,6 +63,8 @@ typedef struct tpm2_protector_context
   grub_uint8_t pcr_count;
   grub_srk_type_t srk_type;
   TPM_ALG_ID_t bank;
+  grub_uint8_t cap_pcrs[TPM_MAX_PCRS];
+  grub_uint8_t cap_pcr_count;
   const char *tpm2key;
   const char *keyfile;
   TPM_HANDLE_t srk;
@@ -100,6 +104,16 @@ static const struct grub_arg_option tpm2_protector_init_cmd_options[] =
        N_("Bank of PCRs used to authorize key release: "
           "SHA1, SHA256, SHA384 or SHA512. (default: SHA256)"),
     },
+    {
+      .longarg  = "cap-pcrs",
+      .shortarg = 'c',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_STRING,
+      .doc      =
+       N_("Comma-separated list of PCRs to be capped after key release "
+          "e.g., '7,11'."),
+    },
     /* SRK-mode options */
     {
       .longarg  = "tpm2key",
@@ -1212,19 +1226,45 @@ tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx,
   return err;
 }
 
+static grub_err_t
+tpm2_protector_cap_pcrs (const tpm2_protector_context_t *ctx)
+{
+  grub_uint8_t i;
+  grub_err_t err;
+
+  for (i = 0; i < ctx->cap_pcr_count; i++)
+    {
+       err = grub_tcg2_cap_pcr (ctx->cap_pcrs[i]);
+       if (err != GRUB_ERR_NONE)
+         return err;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 tpm2_protector_recover (const tpm2_protector_context_t *ctx,
                        grub_uint8_t **key, grub_size_t *key_size)
 {
+  grub_err_t err;
+
   switch (ctx->mode)
     {
     case TPM2_PROTECTOR_MODE_SRK:
-      return tpm2_protector_srk_recover (ctx, key, key_size);
+      err = tpm2_protector_srk_recover (ctx, key, key_size);
+      break;
     case TPM2_PROTECTOR_MODE_NV:
-      return tpm2_protector_nv_recover (ctx, key, key_size);
+      err = tpm2_protector_nv_recover (ctx, key, key_size);
+      break;
     default:
-      return GRUB_ERR_BAD_ARGUMENT;
+      err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown Mode"));
     }
+
+  /* Cap the selected PCRs when the key is unsealed successfully */
+  if (ctx->cap_pcr_count > 0 && err == GRUB_ERR_NONE)
+    err = tpm2_protector_cap_pcrs (ctx);
+
+  return err;
 }
 
 static grub_err_t
@@ -1364,6 +1404,15 @@ tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc,
        return err;
     }
 
+  if (state[OPTION_CAPPCRS].set)  /* cap-pcrs */
+    {
+      err = grub_tpm2_protector_parse_pcrs (state[OPTION_CAPPCRS].arg,
+                                           tpm2_protector_ctx.cap_pcrs,
+                                           &tpm2_protector_ctx.cap_pcr_count);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
   if (state[OPTION_TPM2KEY].set)  /* tpm2key */
     {
       err = tpm2_protector_parse_file (state[OPTION_TPM2KEY].arg,
@@ -1465,6 +1514,7 @@ GRUB_MOD_INIT (tpm2_key_protector)
                          N_("[-m mode] "
                             "[-p pcr_list] "
                             "[-b pcr_bank] "
+                            "[-c pcr_list] "
                             "[-T tpm2_key_file_path] "
                             "[-k sealed_key_file_path] "
                             "[-s srk_handle] "
index a9e7a09d11fe49ef982e6a6475448dbd5917b691..01b79ac32f27b0347d668ac56f17cb16e04f5e9f 100644 (file)
@@ -518,7 +518,7 @@ static const char *features[] = {
   "feature_default_font_path", "feature_all_video_module",
   "feature_menuentry_id", "feature_menuentry_options", "feature_200_final",
   "feature_nativedisk_cmd", "feature_timeout_style",
-  "feature_search_cryptodisk_only"
+  "feature_search_cryptodisk_only", "feature_tpm2_cap_pcrs"
 };
 
 GRUB_MOD_INIT(normal)