tpm2_key_protector: Support PCR capping

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>
This commit is contained in:
Gary Lin 2025-10-03 11:22:07 +08:00 committed by Daniel Kiper
parent ae7a399005
commit afddba0127
3 changed files with 73 additions and 5 deletions

View 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}.

View 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] "

View 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)