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 a 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 <g...@suse.com>
---
 docs/grub.texi                                | 20 ++++++-
 .../commands/tpm2_key_protector/module.c      | 56 ++++++++++++++++++-
 grub-core/normal/main.c                       |  2 +-
 3 files changed, 73 insertions(+), 5 deletions(-)

diff --git a/docs/grub.texi b/docs/grub.texi
index 34b3484dc..bd35e9d46 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -8084,7 +8084,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
@@ -8099,6 +8099,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 a 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}.
diff --git a/grub-core/commands/tpm2_key_protector/module.c 
b/grub-core/commands/tpm2_key_protector/module.c
index b84c2234f..1226b65e0 100644
--- a/grub-core/commands/tpm2_key_protector/module.c
+++ b/grub-core/commands/tpm2_key_protector/module.c
@@ -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] "
diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
index 96abfda2f..f823851c4 100644
--- a/grub-core/normal/main.c
+++ b/grub-core/normal/main.c
@@ -512,7 +512,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)
-- 
2.43.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to