> On 8 Jul 2025, at 2:01 PM, Gary Lin via Grub-devel <grub-devel@gnu.org> wrote:
> 
> 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>
> ---

Reviewed-by: Sudhakar Kuppusamy <sudha...@linux.ibm.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

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

Reply via email to