grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v19 22/33] key_protector: Add TPM2 Key Protector


From: Gary Lin
Subject: [PATCH v19 22/33] key_protector: Add TPM2 Key Protector
Date: Fri, 6 Sep 2024 17:11:14 +0800

From: Hernan Gatta <hegatta@linux.microsoft.com>

The TPM2 key protector is a module that enables the automatic retrieval
of a fully-encrypted disk's unlocking key from a TPM 2.0.

The theory of operation is such that the module accepts various
arguments, most of which are optional and therefore possess reasonable
defaults. One of these arguments is the keyfile/tpm2key parameter, which
is mandatory. There are two supported key formats:

1. Raw Sealed Key (--keyfile)
   When sealing a key with TPM2_Create, the public portion of the sealed
   key is stored in TPM2B_PUBLIC, and the private portion is in
   TPM2B_PRIVATE. The raw sealed key glues the fully marshalled
   TPM2B_PUBLIC and TPM2B_PRIVATE into one file.

2. TPM 2.0 Key (--tpm2key)
   The following is the ASN.1 definition of TPM 2.0 Key File:

   TPMPolicy ::= SEQUENCE {
     CommandCode   [0] EXPLICIT INTEGER
     CommandPolicy [1] EXPLICIT OCTET STRING
   }

   TPMAuthPolicy ::= SEQUENCE {
     Name    [0] EXPLICIT UTF8STRING OPTIONAL
     Policy  [1] EXPLICIT SEQUENCE OF TPMPolicy
   }

   TPMKey ::= SEQUENCE {
     type        OBJECT IDENTIFIER
     emptyAuth   [0] EXPLICIT BOOLEAN OPTIONAL
     policy      [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL
     secret      [2] EXPLICIT OCTET STRING OPTIONAL
     authPolicy  [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL
     description [4] EXPLICIT UTF8String OPTIONAL,
     rsaParent   [5] EXPLICIT BOOLEAN OPTIONAL,
     parent      INTEGER
     pubkey      OCTET STRING
     privkey     OCTET STRING
   }

  The TPM2 key protector only expects a "sealed" key in DER encoding,
  so 'type' is always 2.23.133.10.1.5, 'emptyAuth' is 'TRUE', and
  'secret' is empty. 'policy' and 'authPolicy' are the possible policy
  command sequences to construst the policy digest to unseal the key.
  Similar to the raw sealed key, the public portion (TPM2B_PUBLIC) of
  the sealed key is stored in 'pubkey', and the private portion
  (TPM2B_PRIVATE) is in 'privkey'.

  For more details: 
https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html

This sealed key file is created via the grub-protect tool. The tool
utilizes the TPM's sealing functionality to seal (i.e., encrypt) an
unlocking key using a Storage Root Key (SRK) to the values of various
Platform Configuration Registers (PCRs). These PCRs reflect the state
of the system as it boots. If the values are as expected, the system
may be considered trustworthy, at which point the TPM allows for a
caller to utilize the private component of the SRK to unseal (i.e.,
decrypt) the sealed key file. The caller, in this case, is this key
protector.

The TPM2 key protector registers two commands:

- tpm2_key_protector_init: Initializes the state of the TPM2 key
                           protector for later usage, clearing any
                           previous state, too, if any.

- tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init.

The way this is expected to be used requires the user to, either
interactively or, normally, via a boot script, initialize/configure
the key protector and then specify that it be used by the 'cryptomount'
command (modifications to this command are in a different patch).

For instance, to unseal the raw sealed key file:

tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub2/sealed-1.key
cryptomount -u <PART1_UUID> -P tpm2

tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub2/sealed-2.key --pcrs=7,11
cryptomount -u <PART2_UUID> -P tpm2

Or, to unseal the TPM 2.0 Key file:

tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-1.tpm
cryptomount -u <PART1_UUID> -P tpm2

tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-2.tpm --pcrs=7,11
cryptomount -u <PART2_UUID> -P tpm2

If a user does not initialize the key protector and attempts to use it
anyway, the protector returns an error.

Before unsealing the key, the TPM2 key protector follows the "TPMPolicy"
sequences to enforce the TPM policy commands to construct a valid policy
digest to unseal the key.

For the TPM 2.0 Key files, 'authPolicy' may contain multiple "TPMPolicy"
sequences, the TPM2 key protector iterates 'authPolicy' to find a valid
sequence to unseal key. If 'authPolicy' is empty or all sequences in
'authPolicy' fail, the protector tries the one from 'policy'. In case
'policy' is also empty, the protector creates a "TPMPolicy" sequence
based on the given PCR selection.

For the raw sealed key, the TPM2 key protector treats the key file as a
TPM 2.0 Key file without 'authPolicy' and 'policy', so the "TPMPolicy"
sequence is always based on the PCR selection from the command
parameters.

This commit only supports one policy command: TPM2_PolicyPCR. The
command set will be extended to support advanced features, such as
authorized policy, in the later commits.

Cc: Stefan Berger <stefanb@linux.ibm.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
Signed-off-by: Gary Lin <glin@suse.com>
---
 grub-core/Makefile.core.def                   |   11 +
 grub-core/commands/tpm2_key_protector/args.c  |  129 ++
 .../commands/tpm2_key_protector/module.c      | 1153 +++++++++++++++++
 grub-core/commands/tpm2_key_protector/tpm2.h  |   36 +
 .../commands/tpm2_key_protector/tpm2_args.h   |   49 +
 .../commands/tpm2_key_protector/tpm2key.asn   |   49 +
 .../commands/tpm2_key_protector/tpm2key.c     |  499 +++++++
 .../commands/tpm2_key_protector/tpm2key.h     |   87 ++
 .../tpm2_key_protector/tpm2key_asn1_tab.c     |   63 +
 9 files changed, 2076 insertions(+)
 create mode 100644 grub-core/commands/tpm2_key_protector/args.c
 create mode 100644 grub-core/commands/tpm2_key_protector/module.c
 create mode 100644 grub-core/commands/tpm2_key_protector/tpm2.h
 create mode 100644 grub-core/commands/tpm2_key_protector/tpm2_args.h
 create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key.asn
 create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key.c
 create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key.h
 create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 45b705a34..97ae4e49b 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2578,6 +2578,17 @@ module = {
   cppflags = '-I$(srcdir)/lib/tss2';
 };
 
+module = {
+  name = tpm2_key_protector;
+  common = commands/tpm2_key_protector/args.c;
+  common = commands/tpm2_key_protector/module.c;
+  common = commands/tpm2_key_protector/tpm2key.c;
+  common = commands/tpm2_key_protector/tpm2key_asn1_tab.c;
+  /* The plaform support of tpm2_key_protector depends on the tcg2 
implementation in tss2. */
+  enable = efi;
+  cppflags = '-I$(srcdir)/lib/tss2 -I$(srcdir)/lib/libtasn1-grub';
+};
+
 module = {
   name = tr;
   common = commands/tr.c;
diff --git a/grub-core/commands/tpm2_key_protector/args.c 
b/grub-core/commands/tpm2_key_protector/args.c
new file mode 100644
index 000000000..c58cbe307
--- /dev/null
+++ b/grub-core/commands/tpm2_key_protector/args.c
@@ -0,0 +1,129 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+
+#include "tpm2_args.h"
+
+grub_err_t
+grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs,
+                               grub_uint8_t *pcr_count)
+{
+  char *current_pcr = value;
+  char *next_pcr;
+  const char *pcr_end;
+  grub_uint64_t pcr;
+  grub_uint8_t i;
+
+  if (grub_strlen (value) == 0)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  *pcr_count = 0;
+  for (i = 0; i < TPM_MAX_PCRS; i++)
+    {
+      next_pcr = grub_strchr (current_pcr, ',');
+      if (next_pcr == current_pcr)
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "Empty entry in PCR list");
+      if (next_pcr != NULL)
+       *next_pcr = '\0';
+
+      grub_errno = GRUB_ERR_NONE;
+      pcr = grub_strtoul (current_pcr, &pcr_end, 10);
+      if (*current_pcr == '\0' || *pcr_end != '\0')
+       return grub_error (GRUB_ERR_BAD_NUMBER, "Entry '%s' in PCR list is not 
a number", current_pcr);
+
+      if (pcr > TPM_MAX_PCRS)
+       return grub_error (GRUB_ERR_OUT_OF_RANGE, "Entry %" PRIuGRUB_UINT64_T " 
in PCR list is too large to be a PCR number, PCR numbers range from 0 to %u", 
pcr, TPM_MAX_PCRS);
+
+      pcrs[i] = (grub_uint8_t) pcr;
+      ++(*pcr_count);
+
+      if (next_pcr == NULL)
+       break;
+
+      current_pcr = next_pcr + 1;
+      if (*current_pcr == '\0')
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "Trailing comma at the end of 
PCR list");
+    }
+
+  if (i == TPM_MAX_PCRS)
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "Too many PCRs in PCR list, the 
maximum number of PCRs is %u", TPM_MAX_PCRS);
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_tpm2_protector_parse_asymmetric (const char *value,
+                                     grub_srk_type_t *srk_type)
+{
+  if (grub_strcasecmp (value, "ECC") == 0 ||
+      grub_strcasecmp (value, "ECC_NIST_P256") == 0)
+    {
+      srk_type->type = TPM_ALG_ECC;
+      srk_type->detail.ecc_curve = TPM_ECC_NIST_P256;
+    }
+  else if (grub_strcasecmp (value, "RSA") == 0 ||
+          grub_strcasecmp (value, "RSA2048") == 0)
+    {
+      srk_type->type = TPM_ALG_RSA;
+      srk_type->detail.rsa_bits = 2048;
+    }
+  else
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "Value '%s' is not a valid 
asymmetric key type", value);
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID_t *bank)
+{
+  if (grub_strcasecmp (value, "SHA1") == 0)
+    *bank = TPM_ALG_SHA1;
+  else if (grub_strcasecmp (value, "SHA256") == 0)
+    *bank = TPM_ALG_SHA256;
+  else if (grub_strcasecmp (value, "SHA384") == 0)
+    *bank = TPM_ALG_SHA384;
+  else if (grub_strcasecmp (value, "SHA512") == 0)
+    *bank = TPM_ALG_SHA512;
+  else
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "Value '%s' is not a valid PCR 
bank", value);
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE_t *handle)
+{
+  grub_uint64_t num;
+  const char *str_end;
+
+  grub_errno = GRUB_ERR_NONE;
+  num = grub_strtoul (value, &str_end, 0);
+  if (*value == '\0' || *str_end != '\0')
+    return grub_error (GRUB_ERR_BAD_NUMBER, "TPM handle value '%s' is not a 
number", value);
+
+  if (num > GRUB_UINT_MAX)
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "Value %lu is too large to be a 
TPM handle, TPM handles are unsigned 32-bit integers", num);
+
+  *handle = (TPM_HANDLE_t) num;
+
+  return GRUB_ERR_NONE;
+}
diff --git a/grub-core/commands/tpm2_key_protector/module.c 
b/grub-core/commands/tpm2_key_protector/module.c
new file mode 100644
index 000000000..909c3cc6a
--- /dev/null
+++ b/grub-core/commands/tpm2_key_protector/module.c
@@ -0,0 +1,1153 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/extcmd.h>
+#include <grub/file.h>
+#include <grub/list.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/key_protector.h>
+
+#include <tss2_buffer.h>
+#include <tss2_types.h>
+#include <tss2_mu.h>
+
+#include "tpm2_args.h"
+#include "tpm2.h"
+#include "tpm2key.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+typedef enum grub_tpm2_protector_mode
+{
+  GRUB_TPM2_PROTECTOR_MODE_UNSET,
+  GRUB_TPM2_PROTECTOR_MODE_SRK,
+  GRUB_TPM2_PROTECTOR_MODE_NV
+} grub_tpm2_protector_mode_t;
+
+enum grub_tpm2_protector_options
+{
+  OPTION_MODE,
+  OPTION_PCRS,
+  OPTION_BANK,
+  OPTION_TPM2KEY,
+  OPTION_KEYFILE,
+  OPTION_SRK,
+  OPTION_ASYMMETRIC,
+  OPTION_NVINDEX
+};
+
+struct grub_tpm2_protector_context
+{
+  grub_tpm2_protector_mode_t mode;
+  grub_uint8_t pcrs[TPM_MAX_PCRS];
+  grub_uint8_t pcr_count;
+  grub_srk_type_t srk_type;
+  TPM_ALG_ID_t bank;
+  const char *tpm2key;
+  const char *keyfile;
+  TPM_HANDLE_t srk;
+  TPM_HANDLE_t nv;
+};
+
+static const struct grub_arg_option grub_tpm2_protector_init_cmd_options[] =
+  {
+    /* Options for all modes */
+    {
+      .longarg  = "mode",
+      .shortarg = 'm',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_STRING,
+      .doc      =
+       N_("Unseal key using SRK ('srk') (default) or retrieve it from an NV "
+          "Index ('nv')."),
+    },
+    {
+      .longarg  = "pcrs",
+      .shortarg = 'p',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_STRING,
+      .doc      =
+       N_("Comma-separated list of PCRs used to authorize key release "
+          "e.g., '7,11'. (default: 7)"),
+    },
+    {
+      .longarg  = "bank",
+      .shortarg = 'b',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_STRING,
+      .doc      =
+       N_("Bank of PCRs used to authorize key release: "
+          "SHA1, SHA256, SHA384 or SHA512. (default: SHA256)"),
+    },
+    /* SRK-mode options */
+    {
+      .longarg  = "tpm2key",
+      .shortarg = 'T',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_STRING,
+      .doc      =
+       N_("In SRK mode, path to the key file in the TPM 2.0 Key File format "
+          "to unseal using the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed.tpm)."),
+    },
+    {
+      .longarg  = "keyfile",
+      .shortarg = 'k',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_STRING,
+      .doc      =
+       N_("In SRK mode, path to the key file in the raw format to unseal "
+          "using the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed.key). "
+          "(Mainly for backward compatibility. Please use '--tpm2key'.)"),
+    },
+    {
+      .longarg  = "srk",
+      .shortarg = 's',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_STRING,
+      .doc      =
+       N_("In SRK mode, the SRK handle if the SRK is persistent."),
+    },
+    {
+      .longarg  = "asymmetric",
+      .shortarg = 'a',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_STRING,
+      .doc      =
+       N_("In SRK mode, the type of SRK: RSA (RSA2048) and ECC (ECC_NIST_P256)"
+          "(default: ECC)"),
+    },
+    /* NV Index-mode options */
+    {
+      .longarg  = "nvindex",
+      .shortarg = 'n',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_STRING,
+      .doc      =
+       N_("Required in NV Index mode, the NV handle to read which must "
+          "readily exist on the TPM and which contains the key."),
+    },
+    /* End of list */
+    {0, 0, 0, 0, 0, 0}
+  };
+
+static grub_extcmd_t grub_tpm2_protector_init_cmd;
+static grub_extcmd_t grub_tpm2_protector_clear_cmd;
+static struct grub_tpm2_protector_context grub_tpm2_protector_ctx = {0};
+
+static grub_err_t
+tpm2_protector_srk_read_file (const char *filepath, void **buffer, grub_size_t 
*buffer_size)
+{
+  grub_file_t file;
+  grub_off_t file_size;
+  void *read_buffer;
+  grub_off_t read_n;
+  grub_err_t err;
+
+  /*
+   * Using GRUB_FILE_TYPE_SIGNATURE ensures we do not hash the keyfile into 
PCR9
+   * otherwise we'll never be able to predict the value of PCR9 at unseal time
+   */
+  file = grub_file_open (filepath, GRUB_FILE_TYPE_SIGNATURE);
+  if (file == NULL)
+    {
+      /* Push errno from grub_file_open() into the error message stack */
+      grub_error_push();
+      err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "Could not open file: %s", 
filepath);
+      goto error;
+    }
+
+  file_size = grub_file_size (file);
+  if (file_size == 0)
+    {
+      err = grub_error (GRUB_ERR_OUT_OF_RANGE, "Could not read file size: %s", 
filepath);
+      goto error;
+    }
+
+  read_buffer = grub_malloc (file_size);
+  if (read_buffer == NULL)
+    {
+      err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "Could not allocate buffer for 
%s", filepath);
+      goto error;
+    }
+
+  read_n = grub_file_read (file, read_buffer, file_size);
+  if (read_n != file_size)
+    {
+      grub_free (read_buffer);
+      err = grub_error (GRUB_ERR_FILE_READ_ERROR, "Could not retrieve file 
contents: %s", filepath);
+      goto error;
+    }
+
+  *buffer = read_buffer;
+  *buffer_size = file_size;
+
+  err = GRUB_ERR_NONE;
+
+ error:
+  if (file != NULL)
+    grub_file_close (file);
+
+  return err;
+}
+
+static grub_err_t
+tpm2_protector_srk_unmarshal_keyfile (void *sealed_key,
+                                     grub_size_t sealed_key_size,
+                                     tpm2_sealed_key_t *sk)
+{
+  struct grub_tpm2_buffer buf;
+
+  grub_tpm2_buffer_init (&buf);
+  if (sealed_key_size > buf.cap)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Sealed key larger than %" 
PRIuGRUB_SIZE " bytes", buf.cap);
+
+  grub_memcpy (buf.data, sealed_key, sealed_key_size);
+  buf.size = sealed_key_size;
+
+  grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
+  grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private);
+
+  if (buf.error != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Malformed TPM wire key file");
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+tpm2_protector_srk_unmarshal_tpm2key (void *sealed_key,
+                                     grub_size_t sealed_key_size,
+                                     tpm2key_policy_t *policy_seq,
+                                     tpm2key_authpolicy_t *authpol_seq,
+                                     grub_uint8_t *rsaparent,
+                                     grub_uint32_t *parent,
+                                     tpm2_sealed_key_t *sk)
+{
+  asn1_node tpm2key = NULL;
+  grub_uint8_t rsaparent_tmp;
+  grub_uint32_t parent_tmp;
+  void *sealed_pub = NULL;
+  grub_size_t sealed_pub_size;
+  void *sealed_priv = NULL;
+  grub_size_t sealed_priv_size;
+  struct grub_tpm2_buffer buf;
+  grub_err_t err;
+
+  /*
+   * Start to parse the tpm2key file
+   * TPMKey ::= SEQUENCE {
+   *     type        OBJECT IDENTIFIER,
+   *     emptyAuth   [0] EXPLICIT BOOLEAN OPTIONAL,
+   *     policy      [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
+   *     secret      [2] EXPLICIT OCTET STRING OPTIONAL,
+   *     authPolicy  [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
+   *     description [4] EXPLICIT UTF8String OPTIONAL,
+   *     rsaParent   [5] EXPLICIT BOOLEAN OPTIONAL,
+   *     parent      INTEGER,
+   *     pubkey      OCTET STRING,
+   *     privkey     OCTET STRING
+   * }
+   */
+  err = grub_tpm2key_start_parsing (&tpm2key, sealed_key, sealed_key_size);
+  if (err != GRUB_ERR_NONE)
+    return err;
+
+  /*
+   * Retrieve the policy sequence from 'policy'
+   * policy_seq will be NULL when 'policy' is not available
+   */
+  err = grub_tpm2key_get_policy_seq (tpm2key, policy_seq);
+  if (err != GRUB_ERR_NONE)
+    goto error;
+
+  /*
+   * Retrieve the authpolicy sequence from 'authPolicy'
+   * authpol_seq will be NULL when 'authPolicy' is not available
+   */
+  err = grub_tpm2key_get_authpolicy_seq (tpm2key, authpol_seq);
+  if (err != GRUB_ERR_NONE)
+    goto error;
+
+  /* Retrieve rsaParent */
+  err = grub_tpm2key_get_rsaparent (tpm2key, &rsaparent_tmp);
+  if (err != GRUB_ERR_NONE)
+    goto error;
+
+  *rsaparent = rsaparent_tmp;
+
+  /* Retrieve the parent handle */
+  err = grub_tpm2key_get_parent (tpm2key, &parent_tmp);
+  if (err != GRUB_ERR_NONE)
+    goto error;
+
+  /*  The parent handle should be either PERMANENT or PERSISTENT. */
+  if (!TPM_HT_IS_PERMANENT (parent_tmp) && !TPM_HT_IS_PERSISTENT (parent_tmp))
+    {
+      err = GRUB_ERR_OUT_OF_RANGE;
+      goto error;
+    }
+
+  *parent = parent_tmp;
+
+  /* Retrieve the public part of the sealed key */
+  err = grub_tpm2key_get_pubkey (tpm2key, &sealed_pub, &sealed_pub_size);
+  if (err != GRUB_ERR_NONE)
+    goto error;
+
+  /* Retrieve the private part of the sealed key */
+  err = grub_tpm2key_get_privkey (tpm2key, &sealed_priv, &sealed_priv_size);
+  if (err != GRUB_ERR_NONE)
+    goto error;
+
+  /* Unmarshal the sealed key */
+  grub_tpm2_buffer_init (&buf);
+  if (sealed_pub_size + sealed_priv_size > buf.cap)
+    {
+      err = grub_error (GRUB_ERR_BAD_ARGUMENT, "Sealed key larger than %" 
PRIuGRUB_SIZE " bytes", buf.cap);
+      goto error;
+    }
+
+  grub_tpm2_buffer_pack (&buf, sealed_pub, sealed_pub_size);
+  grub_tpm2_buffer_pack (&buf, sealed_priv, sealed_priv_size);
+
+  buf.offset = 0;
+
+  grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
+  grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private);
+
+  if (buf.error != 0)
+    {
+      err = grub_error (GRUB_ERR_BAD_ARGUMENT, "Malformed TPM 2.0 key file");
+      goto error;
+    }
+
+  err = GRUB_ERR_NONE;
+
+ error:
+  /* End the parsing */
+  grub_tpm2key_end_parsing (tpm2key);
+  grub_free (sealed_pub);
+  grub_free (sealed_priv);
+
+  return err;
+}
+
+/* Check if the SRK exists in the specified handle */
+static grub_err_t
+tpm2_protector_srk_check (const TPM_HANDLE_t srk_handle)
+{
+  TPM_RC_t rc;
+  TPM2B_PUBLIC_t public;
+
+  /* Find SRK */
+  rc = grub_tpm2_readpublic (srk_handle, NULL, &public);
+  if (rc == TPM_RC_SUCCESS)
+    return GRUB_ERR_NONE;
+
+  return grub_error (GRUB_ERR_BAD_ARGUMENT, "Failed to retrieve SRK from 0x%x 
(TPM2_ReadPublic: 0x%x)", srk_handle, rc);
+}
+
+/* Get the SRK with the template */
+static grub_err_t
+tpm2_protector_srk_get (const grub_srk_type_t srk_type,
+                       const TPM_HANDLE_t parent,
+                       TPM_HANDLE_t *srk_handle)
+{
+  TPM_RC_t rc;
+  TPMT_PUBLIC_PARMS_t parms = {0};
+  TPMS_AUTH_COMMAND_t authCommand = {0};
+  TPM2B_SENSITIVE_CREATE_t inSensitive = {0};
+  TPM2B_PUBLIC_t inPublic = {0};
+  TPM2B_DATA_t outsideInfo = {0};
+  TPML_PCR_SELECTION_t creationPcr = {0};
+  TPM2B_PUBLIC_t outPublic = {0};
+  TPM2B_CREATION_DATA_t creationData = {0};
+  TPM2B_DIGEST_t creationHash = {0};
+  TPMT_TK_CREATION_t creationTicket = {0};
+  TPM2B_NAME_t srkName = {0};
+  TPM_HANDLE_t tmp_handle = 0;
+
+  inPublic.publicArea.type = srk_type.type;
+  inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
+  inPublic.publicArea.objectAttributes.restricted = 1;
+  inPublic.publicArea.objectAttributes.userWithAuth = 1;
+  inPublic.publicArea.objectAttributes.decrypt = 1;
+  inPublic.publicArea.objectAttributes.fixedTPM = 1;
+  inPublic.publicArea.objectAttributes.fixedParent = 1;
+  inPublic.publicArea.objectAttributes.sensitiveDataOrigin = 1;
+  inPublic.publicArea.objectAttributes.noDA = 1;
+
+  if (srk_type.type == TPM_ALG_RSA)
+    {
+      inPublic.publicArea.parameters.rsaDetail.symmetric.algorithm = 
TPM_ALG_AES;
+      inPublic.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128;
+      inPublic.publicArea.parameters.rsaDetail.symmetric.mode.aes = 
TPM_ALG_CFB;
+      inPublic.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
+      inPublic.publicArea.parameters.rsaDetail.keyBits = 
srk_type.detail.rsa_bits;
+      inPublic.publicArea.parameters.rsaDetail.exponent = 0;
+    }
+  else if (srk_type.type == TPM_ALG_ECC)
+    {
+      inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = 
TPM_ALG_AES;
+      inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128;
+      inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = 
TPM_ALG_CFB;
+      inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
+      inPublic.publicArea.parameters.eccDetail.curveID = 
srk_type.detail.ecc_curve;
+      inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+    }
+  else
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown SRK algorithm");
+
+  /* Test the parameters before SRK generation */
+  parms.type = srk_type.type;
+  grub_memcpy (&parms.parameters, &inPublic.publicArea.parameters,
+              sizeof (TPMU_PUBLIC_PARMS_t));
+
+  rc = grub_tpm2_testparms (&parms, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported SRK template 
(TPM2_TestParms: 0x%x)", rc);
+
+  /* Create SRK */
+  authCommand.sessionHandle = TPM_RS_PW;
+  rc = grub_tpm2_createprimary (parent, &authCommand, &inSensitive, &inPublic,
+                               &outsideInfo, &creationPcr, &tmp_handle, 
&outPublic,
+                               &creationData, &creationHash, &creationTicket,
+                               &srkName, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    return grub_error (GRUB_ERR_BAD_DEVICE, "Could not create SRK 
(TPM2_CreatePrimary: 0x%x)", rc);
+
+  *srk_handle = tmp_handle;
+
+  return GRUB_ERR_NONE;
+}
+
+/*
+ * Load the SRK from the persistent handle or create one with a given type of
+ * template, and then associate the sealed key with the SRK
+ * Return values:
+ * - GRUB_ERR_NONE: Everything is fine.
+ * - GRUB_ERR_BAD_ARGUMENT: The SRK doesn't match. Try another one.
+ * - Other: Something went wrong.
+ */
+static grub_err_t
+tpm2_protector_srk_load (const grub_srk_type_t srk_type,
+                        const tpm2_sealed_key_t *sealed_key,
+                        const TPM_HANDLE_t parent,
+                        TPM_HANDLE_t *sealed_handle,
+                        TPM_HANDLE_t *srk_handle)
+{
+  TPMS_AUTH_COMMAND_t authCmd = {0};
+  TPM2B_NAME_t name = {0};
+  TPM_RC_t rc;
+  grub_err_t err;
+
+  if (srk_handle == NULL)
+    return GRUB_ERR_BUG;
+
+  if (*srk_handle != 0)
+    {
+      err = tpm2_protector_srk_check (*srk_handle);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+  else
+    {
+      err = tpm2_protector_srk_get (srk_type, parent, srk_handle);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
+  /* Load the sealed key and associate it with the SRK */
+  authCmd.sessionHandle = TPM_RS_PW;
+  rc = grub_tpm2_load (*srk_handle, &authCmd, &sealed_key->private, 
&sealed_key->public,
+                      sealed_handle, &name, NULL);
+  /*
+   * If TPM2_Load returns (TPM_RC_INTEGRITY | TPM_RC_P | TPM_RC_1), then it
+   * implies the wrong SRK is used.
+   */
+  if (rc == (TPM_RC_INTEGRITY | TPM_RC_P | TPM_RC_1))
+    {
+      err = grub_error (GRUB_ERR_BAD_ARGUMENT, "SRK not matched");
+      goto error;
+    }
+  else if (rc != TPM_RC_SUCCESS)
+    {
+      err = grub_error (GRUB_ERR_BAD_DEVICE, "Failed to load sealed key 
(TPM2_Load: 0x%x)", rc);
+      goto error;
+    }
+
+  return GRUB_ERR_NONE;
+
+ error:
+  if (!TPM_HT_IS_PERSISTENT (*srk_handle))
+    grub_tpm2_flushcontext (*srk_handle);
+
+  return err;
+}
+
+static const char *
+srk_type_to_name (grub_srk_type_t srk_type)
+{
+  if (srk_type.type == TPM_ALG_ECC && srk_type.detail.ecc_curve == 
TPM_ECC_NIST_P256)
+    return "ECC_NIST_P256";
+  else if (srk_type.type == TPM_ALG_RSA && srk_type.detail.rsa_bits == 2048)
+    return "RSA2048";
+
+  return "Unknown";
+}
+
+static grub_err_t
+tpm2_protector_load_key (const struct grub_tpm2_protector_context *ctx,
+                        const tpm2_sealed_key_t *sealed_key,
+                        const TPM_HANDLE_t parent_handle,
+                        TPM_HANDLE_t *sealed_handle,
+                        TPM_HANDLE_t *srk_handle)
+{
+  grub_err_t err;
+  int i;
+  grub_srk_type_t fallback_srks[] = {
+    {
+      .type = TPM_ALG_ECC,
+      .detail.ecc_curve = TPM_ECC_NIST_P256,
+    },
+    {
+      .type = TPM_ALG_RSA,
+      .detail.rsa_bits = 2048,
+    },
+    {
+      .type = TPM_ALG_ERROR,
+    }
+  };
+
+  /* Try the given persistent SRK if exists */
+  if (*srk_handle != 0)
+    {
+      err = tpm2_protector_srk_load (ctx->srk_type, sealed_key,
+                                    parent_handle, sealed_handle,
+                                    srk_handle);
+      if (err != GRUB_ERR_BAD_ARGUMENT)
+       return err;
+
+      grub_print_error ();
+      grub_printf ("Trying the specified SRK algorithm: %s\n", 
srk_type_to_name (ctx->srk_type));
+      grub_errno = GRUB_ERR_NONE;
+      *srk_handle = 0;
+    }
+
+  /* Try the specified algorithm for the SRK template */
+  if (*srk_handle == 0)
+    {
+      err = tpm2_protector_srk_load (ctx->srk_type, sealed_key,
+                                    parent_handle, sealed_handle,
+                                    srk_handle);
+      if (err != GRUB_ERR_BAD_ARGUMENT)
+       return err;
+
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+      *srk_handle = 0;
+    }
+
+  /* Try all the fallback SRK templates */
+  for (i = 0; fallback_srks[i].type != TPM_ALG_ERROR; i++)
+    {
+      /* Skip the specified algorithm */
+      if (fallback_srks[i].type == ctx->srk_type.type &&
+         (fallback_srks[i].detail.rsa_bits == ctx->srk_type.detail.rsa_bits ||
+          fallback_srks[i].detail.ecc_curve == ctx->srk_type.detail.ecc_curve))
+       continue;
+
+      grub_printf ("Trying fallback %s template\n", srk_type_to_name 
(fallback_srks[i]));
+
+      *srk_handle = 0;
+
+      err = tpm2_protector_srk_load (fallback_srks[i], sealed_key,
+                                    parent_handle, sealed_handle,
+                                    srk_handle);
+      if (err != GRUB_ERR_BAD_ARGUMENT)
+       return err;
+
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+  }
+
+  return err;
+}
+
+static grub_err_t
+tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION_t session, struct 
grub_tpm2_buffer *cmd_buf)
+{
+  TPM2B_DIGEST_t pcr_digest;
+  TPML_PCR_SELECTION_t pcr_sel;
+  TPM_RC_t rc;
+
+  grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (cmd_buf, &pcr_digest);
+  grub_Tss2_MU_TPML_PCR_SELECTION_Unmarshal (cmd_buf, &pcr_sel);
+  if (cmd_buf->error != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Failed to unmarshal 
CommandPolicy for TPM2_PolicyPCR");
+
+  rc = grub_tpm2_policypcr (session, NULL, &pcr_digest, &pcr_sel, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    return grub_error (GRUB_ERR_BAD_DEVICE, "Failed to submit PCR policy 
(TPM2_PolicyPCR: 0x%x)", rc);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+tpm2_protector_enforce_policy (tpm2key_policy_t policy, TPMI_SH_AUTH_SESSION_t 
session)
+{
+  struct grub_tpm2_buffer buf;
+  grub_err_t err;
+
+  grub_tpm2_buffer_init (&buf);
+  if (policy->cmd_policy_len > buf.cap)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "CommandPolicy larger than TPM 
buffer");
+
+  grub_memcpy (buf.data, policy->cmd_policy, policy->cmd_policy_len);
+  buf.size = policy->cmd_policy_len;
+
+  switch (policy->cmd_code)
+    {
+    case TPM_CC_PolicyPCR:
+      err = tpm2_protector_policypcr (session, &buf);
+      break;
+    default:
+      return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown TPM Command: 0x%x", 
policy->cmd_code);
+    }
+
+  return err;
+}
+
+static grub_err_t
+tpm2_protector_enforce_policy_seq (tpm2key_policy_t policy_seq, 
TPMI_SH_AUTH_SESSION_t session)
+{
+  tpm2key_policy_t policy;
+  grub_err_t err;
+
+  FOR_LIST_ELEMENTS (policy, policy_seq)
+    {
+      err = tpm2_protector_enforce_policy (policy, session);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+tpm2_protector_simple_policy_seq (const struct grub_tpm2_protector_context 
*ctx,
+                                 tpm2key_policy_t *policy_seq)
+{
+  tpm2key_policy_t policy = NULL;
+  struct grub_tpm2_buffer buf;
+  TPML_PCR_SELECTION_t pcr_sel = {
+    .count = 1,
+    .pcrSelections = {
+      {
+       .hash = ctx->bank,
+       .sizeOfSelect = 3,
+       .pcrSelect = {0}
+      },
+    }
+  };
+  grub_uint8_t i;
+  grub_err_t err;
+
+  if (policy_seq == NULL)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_tpm2_buffer_init (&buf);
+
+  for (i = 0; i < ctx->pcr_count; i++)
+    TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], ctx->pcrs[i]);
+
+  grub_tpm2_buffer_pack_u16 (&buf, 0);
+  grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (&buf, &pcr_sel);
+
+  if (buf.error != 0)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  policy = grub_malloc (sizeof(struct tpm2key_policy));
+  if (policy == NULL)
+    {
+      err = GRUB_ERR_OUT_OF_MEMORY;
+      goto error;
+    }
+  policy->cmd_code = TPM_CC_PolicyPCR;
+  policy->cmd_policy = grub_malloc (buf.size);
+  if (policy->cmd_policy == NULL)
+    {
+      err = GRUB_ERR_OUT_OF_MEMORY;
+      goto error;
+    }
+  grub_memcpy (policy->cmd_policy, buf.data, buf.size);
+  policy->cmd_policy_len = buf.size;
+
+  grub_list_push (GRUB_AS_LIST_P (policy_seq), GRUB_AS_LIST (policy));
+
+  return GRUB_ERR_NONE;
+
+ error:
+  grub_free (policy);
+
+  return err;
+}
+
+static grub_err_t
+tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle,
+                      grub_uint8_t **key, grub_size_t *key_size)
+{
+  TPMS_AUTH_COMMAND_t authCmd = {0};
+  TPM2B_SENSITIVE_DATA_t data;
+  TPM2B_NONCE_t nonceCaller = {0};
+  TPMT_SYM_DEF_t symmetric = {0};
+  TPMI_SH_AUTH_SESSION_t session;
+  grub_uint8_t *key_out;
+  TPM_RC_t rc;
+  grub_err_t err;
+
+  /* Start Auth Session */
+  nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
+  symmetric.algorithm = TPM_ALG_NULL;
+  rc = grub_tpm2_startauthsession (TPM_RH_NULL, TPM_RH_NULL, NULL, 
&nonceCaller, NULL,
+                                  TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256,
+                                  &session, NULL, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    return grub_error (GRUB_ERR_BAD_DEVICE, "Failed to start auth session 
(TPM2_StartAuthSession: 0x%x)", rc);
+
+  /* Enforce the policy command sequence */
+  err = tpm2_protector_enforce_policy_seq (policy_seq, session);
+  if (err != GRUB_ERR_NONE)
+    goto error;
+
+  /* Unseal Sealed Key */
+  authCmd.sessionHandle = session;
+  rc = grub_tpm2_unseal (sealed_handle, &authCmd, &data, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      err = grub_error (GRUB_ERR_BAD_DEVICE, "Failed to unseal sealed key 
(TPM2_Unseal: 0x%x)", rc);
+      goto error;
+    }
+
+  /* Epilogue */
+  key_out = grub_malloc (data.size);
+  if (key_out == NULL)
+    {
+      err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "No memory left to allocate 
unlock key buffer");
+      goto error;
+    }
+
+  grub_memcpy (key_out, data.buffer, data.size);
+
+  *key = key_out;
+  *key_size = data.size;
+
+  err = GRUB_ERR_NONE;
+
+ error:
+  grub_tpm2_flushcontext (session);
+
+  return err;
+}
+
+static grub_err_t
+tpm2_protector_srk_recover (const struct grub_tpm2_protector_context *ctx,
+                           grub_uint8_t **key, grub_size_t *key_size)
+{
+  tpm2_sealed_key_t sealed_key = {0};
+  void *file_bytes = NULL;
+  grub_size_t file_size = 0;
+  grub_uint8_t rsaparent = 0;
+  TPM_HANDLE_t parent_handle = 0;
+  TPM_HANDLE_t srk_handle = 0;
+  TPM_HANDLE_t sealed_handle = 0;
+  tpm2key_policy_t policy_seq = NULL;
+  tpm2key_authpolicy_t authpol = NULL;
+  tpm2key_authpolicy_t authpol_seq = NULL;
+  grub_err_t err;
+
+  /*
+   * Retrieve sealed key, parent handle, policy sequence, and authpolicy
+   * sequence from the key file
+  */
+  if (ctx->tpm2key != NULL)
+    {
+      err = tpm2_protector_srk_read_file (ctx->tpm2key, &file_bytes,
+                                              &file_size);
+      if (err != GRUB_ERR_NONE)
+       return err;
+
+      err = tpm2_protector_srk_unmarshal_tpm2key (file_bytes,
+                                                 file_size,
+                                                 &policy_seq,
+                                                 &authpol_seq,
+                                                 &rsaparent,
+                                                 &parent_handle,
+                                                 &sealed_key);
+      if (err != GRUB_ERR_NONE)
+       goto exit1;
+
+      if (rsaparent == 1)
+       {
+         struct grub_tpm2_protector_context *ctx_w;
+
+         /* Overwrite the SRK type as noted in the key */
+         ctx_w = (struct grub_tpm2_protector_context *)ctx;
+         ctx_w->srk_type.type = TPM_ALG_RSA;
+         ctx_w->srk_type.detail.rsa_bits = 2048;
+       }
+    }
+  else
+    {
+      err = tpm2_protector_srk_read_file (ctx->keyfile, &file_bytes, 
&file_size);
+      if (err != GRUB_ERR_NONE)
+       return err;
+
+      parent_handle = TPM_RH_OWNER;
+      err = tpm2_protector_srk_unmarshal_keyfile (file_bytes, file_size, 
&sealed_key);
+      if (err != GRUB_ERR_NONE)
+       goto exit1;
+    }
+
+  /* Set the SRK handle if it is specified with '--srk' or inside the key file 
*/
+  if (ctx->srk != 0)
+    srk_handle = ctx->srk;
+  else if (TPM_HT_IS_PERSISTENT (parent_handle))
+    srk_handle = parent_handle;
+
+  /* Load the sealed key into TPM and associate it with the SRK */
+  err = tpm2_protector_load_key (ctx, &sealed_key, parent_handle, 
&sealed_handle, &srk_handle);
+  if (err != GRUB_ERR_NONE)
+    goto exit1;
+
+  /*
+   * Set err to an error code to trigger the standalone policy sequence
+   * if there is no authpolicy sequence
+   */
+  err = GRUB_ERR_READ_ERROR;
+
+  /* Iterate the authpolicy sequence to find one that unseals the key */
+  FOR_LIST_ELEMENTS (authpol, authpol_seq)
+    {
+      err = tpm2_protector_unseal (authpol->policy_seq, sealed_handle, key, 
key_size);
+      if (err == GRUB_ERR_NONE)
+        break;
+
+      /*
+       * Push the error message into the grub_error stack
+       * Note: The grub_error stack may overflow if there are too many policy
+       *       sequences. Anyway, we still can keep the error messages from
+       *       the first few policy sequences which are usually most likely to
+       *       unseal the key.
+       */
+      grub_error_push();
+    }
+
+  /* Give the standalone policy sequence a try */
+  if (err != GRUB_ERR_NONE)
+    {
+      /*
+       * Create a basic policy sequence based on the given PCR selection if the
+       * key file doesn't provide one
+       */
+      if (policy_seq == NULL)
+       {
+         err = tpm2_protector_simple_policy_seq (ctx, &policy_seq);
+         if (err != GRUB_ERR_NONE)
+           goto exit2;
+       }
+
+      err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size);
+    }
+
+  /* Pop error messages on success */
+  if (err == GRUB_ERR_NONE)
+    while (grub_error_pop ());
+
+ exit2:
+  grub_tpm2_flushcontext (sealed_handle);
+
+  if (!TPM_HT_IS_PERSISTENT (srk_handle))
+    grub_tpm2_flushcontext (srk_handle);
+
+ exit1:
+  grub_tpm2key_free_policy_seq (policy_seq);
+  grub_tpm2key_free_authpolicy_seq (authpol_seq);
+  grub_free (file_bytes);
+  return err;
+}
+
+static grub_err_t
+tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx 
__attribute__ ((unused)),
+                          grub_uint8_t **key __attribute__ ((unused)),
+                          grub_size_t *key_size __attribute__ ((unused)))
+{
+  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "NV Index mode is not 
implemented yet");
+}
+
+static grub_err_t
+tpm2_protector_recover (const struct grub_tpm2_protector_context *ctx,
+                       grub_uint8_t **key, grub_size_t *key_size)
+{
+  switch (ctx->mode)
+    {
+    case GRUB_TPM2_PROTECTOR_MODE_SRK:
+      return tpm2_protector_srk_recover (ctx, key, key_size);
+    case GRUB_TPM2_PROTECTOR_MODE_NV:
+      return tpm2_protector_nv_recover (ctx, key, key_size);
+    default:
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+}
+
+static grub_err_t
+tpm2_protector_recover_key (grub_uint8_t **key, grub_size_t *key_size)
+{
+  /* Expect a call to tpm2_protector_init before anybody tries to use us */
+  if (grub_tpm2_protector_ctx.mode == GRUB_TPM2_PROTECTOR_MODE_UNSET)
+    return grub_error (GRUB_ERR_INVALID_COMMAND, "Cannot use TPM2 key 
protector without initializing it, call tpm2_protector_init first");
+
+  if (key == NULL || key_size == NULL)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  return tpm2_protector_recover (&grub_tpm2_protector_ctx, key, key_size);
+}
+
+static grub_err_t
+tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
+{
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_UNSET)
+    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
+
+  /* Checks for SRK mode */
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK &&
+      (ctx->keyfile == NULL && ctx->tpm2key == NULL))
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "In SRK mode, a key file must be 
specified: --tpm2key/-T or --keyfile/-k");
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK &&
+      (ctx->keyfile != NULL && ctx->tpm2key != NULL))
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "In SRK mode, please specify a 
key file with only --tpm2key/-T or --keyfile/-k");
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->nv != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "In SRK mode, an NV Index cannot 
be specified");
+
+  /* Checks for NV mode */
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->nv == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "In NV Index mode, an NV Index 
must be specified: --nvindex or -n");
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV &&
+      (ctx->tpm2key != NULL || ctx->keyfile != NULL))
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "In NV Index mode, a keyfile 
cannot be specified");
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->srk != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "In NV Index mode, an SRK cannot 
be specified");
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV &&
+      ctx->srk_type.type != TPM_ALG_ERROR)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "In NV Index mode, an asymmetric 
key type cannot be specified");
+
+  /* Defaults assignment */
+  if (ctx->bank == TPM_ALG_ERROR)
+    ctx->bank = TPM_ALG_SHA256;
+
+  if (ctx->pcr_count == 0)
+    {
+      ctx->pcrs[0] = 7;
+      ctx->pcr_count = 1;
+    }
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK &&
+      ctx->srk_type.type == TPM_ALG_ERROR)
+    {
+      ctx->srk_type.type = TPM_ALG_ECC;
+      ctx->srk_type.detail.ecc_curve = TPM_ECC_NIST_P256;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+tpm2_protector_parse_file (const char *value, const char **file)
+{
+  if (grub_strlen (value) == 0)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  *file = grub_strdup (value);
+  if (*file == NULL)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "No memory to duplicate file 
path");
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+tpm2_protector_parse_mode (const char *value, grub_tpm2_protector_mode_t *mode)
+{
+  if (grub_strcmp (value, "srk") == 0)
+    *mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
+  else if (grub_strcmp (value, "nv") == 0)
+    *mode = GRUB_TPM2_PROTECTOR_MODE_NV;
+  else
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "Value '%s' is not a valid TPM2 
key protector mode", value);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc,
+                                char **args __attribute__ ((unused)))
+{
+  struct grub_arg_list *state = ctxt->state;
+  grub_err_t err;
+
+  if (argc)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "The TPM2 key protector does not 
accept any non-option arguments (i.e., like -o and/or --option only)");
+
+  grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
+  grub_memset (&grub_tpm2_protector_ctx, 0, sizeof (grub_tpm2_protector_ctx));
+
+  if (state[OPTION_MODE].set)  /* mode */
+    {
+      err = tpm2_protector_parse_mode (state[OPTION_MODE].arg, 
&grub_tpm2_protector_ctx.mode);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
+  if (state[OPTION_PCRS].set)  /* pcrs */
+    {
+      err = grub_tpm2_protector_parse_pcrs (state[OPTION_PCRS].arg,
+                                           grub_tpm2_protector_ctx.pcrs,
+                                           &grub_tpm2_protector_ctx.pcr_count);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
+  if (state[OPTION_BANK].set)  /* bank */
+    {
+      err = grub_tpm2_protector_parse_bank (state[OPTION_BANK].arg,
+                                           &grub_tpm2_protector_ctx.bank);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
+  if (state[OPTION_TPM2KEY].set)  /* tpm2key */
+    {
+      err = tpm2_protector_parse_file (state[OPTION_TPM2KEY].arg,
+                                      &grub_tpm2_protector_ctx.tpm2key);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
+  if (state[OPTION_KEYFILE].set)  /* keyfile */
+    {
+      err = tpm2_protector_parse_file (state[OPTION_KEYFILE].arg,
+                                      &grub_tpm2_protector_ctx.keyfile);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
+  if (state[OPTION_SRK].set)  /* srk */
+    {
+      err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_SRK].arg,
+                                                 &grub_tpm2_protector_ctx.srk);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
+  if (state[OPTION_ASYMMETRIC].set)  /* asymmetric */
+    {
+      err = grub_tpm2_protector_parse_asymmetric (state[OPTION_ASYMMETRIC].arg,
+                                                 
&grub_tpm2_protector_ctx.srk_type);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
+  if (state[OPTION_NVINDEX].set)  /* nvindex */
+    {
+      err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_NVINDEX].arg,
+                                                 &grub_tpm2_protector_ctx.nv);
+      if (err != GRUB_ERR_NONE)
+       return err;
+    }
+
+  err = tpm2_protector_check_args (&grub_tpm2_protector_ctx);
+
+  /* This command only initializes the protector, so nothing else to do. */
+
+  return err;
+}
+
+static grub_err_t
+tpm2_protector_clear_cmd_handler (grub_extcmd_context_t ctxt __attribute__ 
((unused)),
+                                 int argc, char **args __attribute__ 
((unused)))
+{
+  if (argc)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "tpm2_key_protector_clear 
accepts no arguments");
+
+  grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
+  grub_memset (&grub_tpm2_protector_ctx, 0, sizeof (grub_tpm2_protector_ctx));
+
+  return GRUB_ERR_NONE;
+}
+
+static struct grub_key_protector grub_tpm2_key_protector =
+  {
+    .name = "tpm2",
+    .recover_key = tpm2_protector_recover_key
+  };
+
+GRUB_MOD_INIT (tpm2_key_protector)
+{
+  grub_tpm2_protector_init_cmd =
+    grub_register_extcmd ("tpm2_key_protector_init",
+                         tpm2_protector_init_cmd_handler, 0,
+                         N_("[-m mode] "
+                            "[-p pcr_list] "
+                            "[-b pcr_bank] "
+                            "[-T tpm2_key_file_path] "
+                            "[-k sealed_key_file_path] "
+                            "[-s srk_handle] "
+                            "[-a asymmetric_key_type] "
+                            "[-n nv_index]"),
+                         N_("Initialize the TPM2 key protector."),
+                         grub_tpm2_protector_init_cmd_options);
+  grub_tpm2_protector_clear_cmd =
+    grub_register_extcmd ("tpm2_key_protector_clear",
+                         tpm2_protector_clear_cmd_handler, 0, NULL,
+                         N_("Clear the TPM2 key protector if previously 
initialized."),
+                         NULL);
+  grub_key_protector_register (&grub_tpm2_key_protector);
+}
+
+GRUB_MOD_FINI (tpm2_key_protector)
+{
+  grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
+
+  grub_key_protector_unregister (&grub_tpm2_key_protector);
+  grub_unregister_extcmd (grub_tpm2_protector_clear_cmd);
+  grub_unregister_extcmd (grub_tpm2_protector_init_cmd);
+}
diff --git a/grub-core/commands/tpm2_key_protector/tpm2.h 
b/grub-core/commands/tpm2_key_protector/tpm2.h
new file mode 100644
index 000000000..1c1d871b4
--- /dev/null
+++ b/grub-core/commands/tpm2_key_protector/tpm2.h
@@ -0,0 +1,36 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_TPM2_TPM2_HEADER
+#define GRUB_TPM2_TPM2_HEADER 1
+
+#include <tss2_types.h>
+#include <tss2_structs.h>
+#include <tpm2_cmd.h>
+
+/* Well-Known Windows SRK handle */
+#define TPM2_SRK_HANDLE 0x81000001
+
+struct tpm2_sealed_key {
+  TPM2B_PUBLIC_t  public;
+  TPM2B_PRIVATE_t private;
+};
+typedef struct tpm2_sealed_key tpm2_sealed_key_t;
+
+#endif /* ! GRUB_TPM2_TPM2_HEADER */
diff --git a/grub-core/commands/tpm2_key_protector/tpm2_args.h 
b/grub-core/commands/tpm2_key_protector/tpm2_args.h
new file mode 100644
index 000000000..7cf1ebc46
--- /dev/null
+++ b/grub-core/commands/tpm2_key_protector/tpm2_args.h
@@ -0,0 +1,49 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_TPM2_INTERNAL_ARGS_HEADER
+#define GRUB_TPM2_INTERNAL_ARGS_HEADER 1
+
+#include <grub/err.h>
+
+#include "tpm2.h"
+
+struct grub_srk_type
+{
+  TPMI_ALG_PUBLIC_t type;
+  union {
+    TPM_KEY_BITS_t rsa_bits;
+    TPM_ECC_CURVE_t ecc_curve;
+  } detail;
+};
+typedef struct grub_srk_type grub_srk_type_t;
+
+grub_err_t
+grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs, grub_uint8_t 
*pcr_count);
+
+grub_err_t
+grub_tpm2_protector_parse_asymmetric (const char *value, grub_srk_type_t 
*srk_type);
+
+grub_err_t
+grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID_t *bank);
+
+grub_err_t
+grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE_t *handle);
+
+#endif /* ! GRUB_TPM2_INTERNAL_ARGS_HEADER */
diff --git a/grub-core/commands/tpm2_key_protector/tpm2key.asn 
b/grub-core/commands/tpm2_key_protector/tpm2key.asn
new file mode 100644
index 000000000..e1fb51545
--- /dev/null
+++ b/grub-core/commands/tpm2_key_protector/tpm2key.asn
@@ -0,0 +1,49 @@
+--
+-- GRUB: GRand Unified Bootloader
+-- Copyright (C) 2024 Free Software Foundation, Inc.
+--
+-- GRUB is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- GRUB is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- This file describes TPM 2.0 Key File format for libtasn1.
+--  To generate tpm2key_asn1_tab.c: asn1Parser tpm2key.asn
+--
+TPM2KEY {}
+DEFINITIONS IMPLICIT TAGS ::=
+
+BEGIN
+
+TPMPolicy ::= SEQUENCE {
+    CommandCode   [0] EXPLICIT INTEGER,
+    CommandPolicy [1] EXPLICIT OCTET STRING
+}
+
+TPMAuthPolicy ::= SEQUENCE {
+    Name    [0] EXPLICIT UTF8String OPTIONAL,
+    Policy  [1] EXPLICIT SEQUENCE OF TPMPolicy
+}
+
+TPMKey ::= SEQUENCE {
+    type        OBJECT IDENTIFIER,
+    emptyAuth   [0] EXPLICIT BOOLEAN OPTIONAL,
+    policy      [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
+    secret      [2] EXPLICIT OCTET STRING OPTIONAL,
+    authPolicy  [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
+    description [4] EXPLICIT UTF8String OPTIONAL,
+    rsaParent   [5] EXPLICIT BOOLEAN OPTIONAL,
+    parent      INTEGER,
+    pubkey      OCTET STRING,
+    privkey     OCTET STRING
+}
+
+END
diff --git a/grub-core/commands/tpm2_key_protector/tpm2key.c 
b/grub-core/commands/tpm2_key_protector/tpm2key.c
new file mode 100644
index 000000000..7b188a93b
--- /dev/null
+++ b/grub-core/commands/tpm2_key_protector/tpm2key.c
@@ -0,0 +1,499 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2023 SUSE LLC
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/list.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+#include <tss2_buffer.h>
+
+#include "tpm2key.h"
+
+extern asn1_static_node tpm2key_asn1_tab[];
+const char *sealed_key_oid = "2.23.133.10.1.5";
+
+static int
+asn1_allocate_and_read (asn1_node node, const char *name, void **content, 
grub_size_t *content_size)
+{
+  grub_uint8_t *tmpstr = NULL;
+  int tmpstr_size = 0;
+  int ret;
+
+  if (content == NULL)
+    return ASN1_MEM_ERROR;
+
+  ret = asn1_read_value (node, name, NULL, &tmpstr_size);
+  if (ret != ASN1_MEM_ERROR)
+    return ret;
+
+  tmpstr = grub_malloc (tmpstr_size);
+  if (tmpstr == NULL)
+    return ASN1_MEM_ERROR;
+
+  ret = asn1_read_value (node, name, tmpstr, &tmpstr_size);
+  if (ret != ASN1_SUCCESS)
+    return ret;
+
+  *content = tmpstr;
+  *content_size = tmpstr_size;
+
+  return ASN1_SUCCESS;
+}
+
+static int
+asn1_read_uint32 (asn1_node node, const char *name, grub_uint32_t *out)
+{
+  grub_uint32_t tmp = 0;
+  grub_uint8_t *ptr;
+  void *data = NULL;
+  grub_size_t data_size;
+  int ret;
+
+  ret = asn1_allocate_and_read (node, name, &data, &data_size);
+  if (ret != ASN1_SUCCESS)
+    return ret;
+
+  /*
+   * ASN.1 INTEGER is encoded in the following format:
+   *
+   * TAG LENGTH OCTECTS
+   *
+   * The integer TAG is 02 and LENGTH is the number of followed OCTECTS in
+   * big endian. For example:
+   *
+   *    0x1: 02 01 01
+   * 0xabcd: 02 02 ab cd
+   *
+   * To decribe 0x1, it only takes 1 octect, so LENGTH is 0x01 and the
+   * octect is 0x01. On the other hand, 0xabcd requires 2 octects: 'ab" and
+   * 'cd', so LENGTH is 0x02.
+   *
+   * This function only expects a uint32 integer, so it rejects any integer
+   * containing more than 4 octects.
+   */
+  if (data_size > 4)
+    {
+      ret = ASN1_MEM_ERROR;
+      goto error;
+    }
+
+  /* Copy the octects into 'tmp' to make it a big-endian uint32 */
+  ptr = (grub_uint8_t *) &tmp + (4 - data_size);
+  grub_memcpy (ptr, data, data_size);
+
+  /* Convert the big-endian integer to host uint32 */
+  tmp = grub_be_to_cpu32 (tmp);
+
+  *out = tmp;
+ error:
+  if (data)
+    grub_free (data);
+  return ret;
+}
+
+grub_err_t
+grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t 
size)
+{
+  asn1_node tpm2key;
+  asn1_node tpm2key_asn1 = NULL;
+  void *type_oid = NULL;
+  grub_size_t type_oid_size = 0;
+  void *empty_auth = NULL;
+  grub_size_t empty_auth_size = 0;
+  int tmp_size = 0;
+  int ret;
+  grub_err_t err;
+
+  /*
+   * TPMKey ::= SEQUENCE {
+   *    type        OBJECT IDENTIFIER,
+   *    emptyAuth   [0] EXPLICIT BOOLEAN OPTIONAL,
+   *    policy      [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
+   *    secret      [2] EXPLICIT OCTET STRING OPTIONAL,
+   *    authPolicy  [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
+   *    description [4] EXPLICIT UTF8String OPTIONAL,
+   *    rsaParent   [5] EXPLICIT BOOLEAN OPTIONAL,
+   *    parent      INTEGER,
+   *    pubkey      OCTET STRING,
+   *    privkey     OCTET STRING
+   * }
+   */
+  ret = asn1_array2tree (tpm2key_asn1_tab, &tpm2key_asn1, NULL);
+  if (ret != ASN1_SUCCESS)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Failed to parse TPM2KEY ASN.1 
array");
+
+  ret = asn1_create_element (tpm2key_asn1, "TPM2KEY.TPMKey", &tpm2key);
+  if (ret != ASN1_SUCCESS)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Failed to create 
TPM2KEY.TPMKey");
+
+  ret = asn1_der_decoding (&tpm2key, data, size, NULL);
+  if (ret != ASN1_SUCCESS)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Failed to decode TPM2KEY DER");
+
+  /* Check if 'type' is Sealed Key or not */
+  ret = asn1_allocate_and_read (tpm2key, "type", &type_oid, &type_oid_size);
+  if (ret != ASN1_SUCCESS)
+    return grub_error (GRUB_ERR_BAD_FILE_TYPE, "Not a valid TPM2KEY file");
+
+  if (grub_memcmp (sealed_key_oid, type_oid, type_oid_size) != 0)
+    {
+      err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "Not a valid TPM2KEY file");
+      goto error;
+    }
+
+  /* 'emptyAuth' must be 'TRUE' since we don't support password authorization 
*/
+  ret = asn1_allocate_and_read (tpm2key, "emptyAuth", &empty_auth, 
&empty_auth_size);
+  if (ret != ASN1_SUCCESS || grub_strncmp ("TRUE", empty_auth, 
empty_auth_size) != 0)
+    {
+      err = grub_error (GRUB_ERR_BAD_ARGUMENT, "emptyAuth not TRUE");
+      goto error;
+    }
+
+  /* 'secret' should not be in a sealed key */
+  ret = asn1_read_value (tpm2key, "secret", NULL, &tmp_size);
+  if (ret != ASN1_ELEMENT_NOT_FOUND)
+    {
+      err = grub_error (GRUB_ERR_BAD_ARGUMENT, "\"secret\" not allowed for 
Sealed Key");
+      goto error;
+    }
+
+  *parsed_tpm2key = tpm2key;
+
+  err = GRUB_ERR_NONE;
+
+ error:
+  grub_free (type_oid);
+  grub_free (empty_auth);
+
+  return err;
+}
+
+void
+grub_tpm2key_end_parsing (asn1_node tpm2key)
+{
+  asn1_delete_structure (&tpm2key);
+  tpm2key = NULL;
+}
+
+grub_err_t
+grub_tpm2key_get_rsaparent (asn1_node tpm2key, grub_uint8_t *rsaparent)
+{
+  void *bool_str = NULL;
+  grub_size_t bool_str_size = 0;
+  int ret;
+
+  if (rsaparent == NULL)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "NULL pointer detected");
+
+  if (tpm2key == NULL)
+    return grub_error (GRUB_ERR_READ_ERROR, "Invalid parent node");
+
+  ret = asn1_allocate_and_read (tpm2key, "rsaParent", &bool_str, 
&bool_str_size);
+  if (ret == ASN1_SUCCESS)
+    {
+      if (grub_strncmp ("TRUE", bool_str, bool_str_size) == 0)
+       *rsaparent = 1;
+      else
+       *rsaparent = 0;
+    }
+  else if (ret == ASN1_ELEMENT_NOT_FOUND)
+    *rsaparent = 0;
+  else
+    return grub_error (GRUB_ERR_READ_ERROR, "Failed to retrieve rsaParent");
+
+  grub_free (bool_str);
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent)
+{
+  int ret;
+
+  if (parent == NULL)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "NULL pointer detected");
+
+  if (tpm2key == NULL)
+    return grub_error (GRUB_ERR_READ_ERROR, "Invalid parent node");
+
+  ret = asn1_read_uint32 (tpm2key, "parent", parent);
+  if (ret != ASN1_SUCCESS)
+    return grub_error (GRUB_ERR_READ_ERROR, "Failed to retrieve parent");
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+tpm2key_get_octstring (asn1_node tpm2key, const char *name, void **data, 
grub_size_t *size)
+{
+  int ret;
+
+  if (name == NULL || data == NULL || size == NULL)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid parameter(s)");
+
+  if (tpm2key == NULL)
+    return grub_error (GRUB_ERR_READ_ERROR, "Invalid %s node", name);
+
+  ret = asn1_allocate_and_read (tpm2key, name, data, size);
+  if (ret != ASN1_SUCCESS)
+    return grub_error (GRUB_ERR_READ_ERROR, "Failed to retrieve %s", name);
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size)
+{
+  return tpm2key_get_octstring (tpm2key, "pubkey", data, size);
+}
+
+grub_err_t
+grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size)
+{
+  return tpm2key_get_octstring (tpm2key, "privkey", data, size);
+}
+
+/*
+ * The maximum and minimum number of elements for 'policy' and 'authPolicy' 
sequences
+ *
+ * Although there is no limit for the number of sequences elements, we set the 
upper
+ * bound to 99 to make it easier to implement the code.
+ *
+ * Any 'policy' or 'authPolicy' contains more than 99 commands/policies would 
become
+ * extremely complex to manage so it is impractical to support such use case.
+ */
+#define TPM2KEY_ELEMENTS_MAX 99
+#define TPM2KEY_ELEMENTS_MIN 1
+
+/*
+ * The string to fetch 'Policy' from 'authPolicy':
+ *   authPolicy.?XX.Policy
+ */
+#define AUTHPOLICY_POL_MAX_STR "authPolicy.?XX.Policy"
+#define AUTHPOLICY_POL_MAX (sizeof (AUTHPOLICY_POL_MAX_STR))
+
+/*
+ * Expected strings for CommandCode and CommandPolicy:
+ *   policy.?XX.CommandCode
+ *   policy.?XX.CommandPolicy
+ *   authPolicy.?XX.Policy.?YY.CommandCode
+ *   authPolicy.?XX.Policy.?YY.CommandPolicy
+ */
+#define CMD_CODE_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandCode"
+#define CMD_POL_MAX_STR  AUTHPOLICY_POL_MAX_STR".?YY.CommandPolicy"
+#define CMD_CODE_MAX (sizeof (CMD_CODE_MAX_STR))
+#define CMD_POL_MAX  (sizeof (CMD_POL_MAX_STR))
+
+static int
+tpm2key_get_policy_seq (asn1_node tpm2key, const char *prefix,
+                       tpm2key_policy_t *policy_seq)
+{
+  tpm2key_policy_t tmp_seq = NULL;
+  tpm2key_policy_t policy = NULL;
+  int policy_n;
+  char cmd_code[CMD_CODE_MAX];
+  char cmd_pol[CMD_POL_MAX];
+  grub_size_t cmd_policy_len;
+  int i;
+  int ret;
+
+  ret = asn1_number_of_elements (tpm2key, prefix, &policy_n);
+  if (ret != ASN1_SUCCESS)
+    return ret;
+
+  /*
+   * Limit the number of policy commands to two digits (99)
+   * Although there is no upper bound for the number of policy commands,
+   * in practice, it takes one or two policy commands to unseal the key,
+   * so the 99 commands limit is more than enough.
+   */
+  if (policy_n > TPM2KEY_ELEMENTS_MAX || policy_n < TPM2KEY_ELEMENTS_MIN)
+    return ASN1_VALUE_NOT_VALID;
+
+  /*
+   * Iterate the policy commands backwards since grub_list_push() prepends
+   * the item into the list.
+   */
+  for (i = policy_n; i >= 1; i--) {
+    policy = grub_zalloc (sizeof (struct tpm2key_policy));
+    if (policy == NULL)
+      {
+       ret = ASN1_MEM_ALLOC_ERROR;
+       goto error;
+      }
+    grub_snprintf (cmd_code, CMD_CODE_MAX, "%s.?%d.CommandCode", prefix, i);
+    grub_snprintf (cmd_pol, CMD_POL_MAX, "%s.?%d.CommandPolicy", prefix, i);
+
+    /* CommandCode   [0] EXPLICIT INTEGER */
+    ret = asn1_read_uint32 (tpm2key, cmd_code, &policy->cmd_code);
+    if (ret != ASN1_SUCCESS)
+      return ret;
+
+    /* CommandPolicy [1] EXPLICIT OCTET STRING */
+    ret = tpm2key_get_octstring (tpm2key, cmd_pol, &policy->cmd_policy,
+                                &cmd_policy_len);
+    if (ret != ASN1_SUCCESS)
+      {
+       goto error;
+      }
+    else if (cmd_policy_len > GRUB_TPM2_BUFFER_CAPACITY)
+      {
+       /*
+        * CommandPolicy is the marshalled parameters for the TPM command so
+        * it should not be larger than the maximum TPM2 buffer.
+        */
+       ret = ASN1_VALUE_NOT_VALID;
+       goto error;
+      }
+    policy->cmd_policy_len = (grub_uint16_t)cmd_policy_len;
+
+    /* Prepend the policy command into the sequence */
+    grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (policy));
+  }
+
+  *policy_seq = tmp_seq;
+
+  return ASN1_SUCCESS;
+
+ error:
+  if (policy != NULL)
+    {
+      grub_free (policy->cmd_policy);
+      grub_free (policy);
+    }
+  grub_tpm2key_free_policy_seq (tmp_seq);
+
+  return ret;
+}
+
+grub_err_t
+grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq)
+{
+  int ret;
+
+  ret = tpm2key_get_policy_seq (tpm2key, "policy", policy_seq);
+  if (ret == ASN1_ELEMENT_NOT_FOUND)
+    {
+      /* "policy" is optional, so it may not be available */
+      *policy_seq = NULL;
+      return GRUB_ERR_NONE;
+    }
+  else if (ret != ASN1_SUCCESS)
+    return grub_error (GRUB_ERR_READ_ERROR, "Failed to retrieve policy");
+
+  return GRUB_ERR_NONE;
+}
+
+void
+grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq)
+{
+  tpm2key_policy_t policy;
+  tpm2key_policy_t next;
+
+  if (policy_seq == NULL)
+    return;
+
+  FOR_LIST_ELEMENTS_SAFE (policy, next, policy_seq)
+    {
+      grub_free (policy->cmd_policy);
+      grub_free (policy);
+    }
+}
+
+grub_err_t
+grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t 
*authpol_seq)
+{
+  tpm2key_authpolicy_t tmp_seq = NULL;
+  tpm2key_authpolicy_t authpol = NULL;
+  int authpol_n;
+  char authpol_pol[AUTHPOLICY_POL_MAX];
+  int i;
+  int ret;
+  grub_err_t err;
+
+  ret = asn1_number_of_elements (tpm2key, "authPolicy", &authpol_n);
+  if (ret == ASN1_ELEMENT_NOT_FOUND)
+    {
+      /* "authPolicy" is optional, so it may not be available */
+      *authpol_seq = NULL;
+      return GRUB_ERR_NONE;
+    }
+  else if (ret != ASN1_SUCCESS)
+    return grub_error (GRUB_ERR_READ_ERROR, "Failed to retrieve authPolicy");
+
+  /* Limit the number of authPolicy elements to two digits (99) */
+  if (authpol_n > TPM2KEY_ELEMENTS_MAX || authpol_n < TPM2KEY_ELEMENTS_MIN)
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid number of authPolicy 
elements");
+
+  /*
+   * Iterate the authPolicy elements backwards since grub_list_push() prepends
+   * the item into the list.
+   */
+  for (i = authpol_n; i >= 1; i--) {
+    authpol = grub_zalloc (sizeof (struct tpm2key_authpolicy));
+    if (authpol == NULL)
+      {
+       err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "Failed to allocate memory 
for authPolicy");
+       goto error;
+      }
+    grub_snprintf (authpol_pol, AUTHPOLICY_POL_MAX, "authPolicy.?%d.Policy", 
i);
+
+    ret = tpm2key_get_policy_seq (tpm2key, authpol_pol, &authpol->policy_seq);
+    if (ret != ASN1_SUCCESS)
+      {
+        err = grub_error (GRUB_ERR_READ_ERROR, "Failed to retrieve policy from 
authPolicy");
+        goto error;
+      }
+
+    /* Prepend the authPolicy element into the sequence */
+    grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (authpol));
+  }
+
+  *authpol_seq = tmp_seq;
+
+  return GRUB_ERR_NONE;
+
+ error:
+  if (authpol != NULL)
+    {
+      grub_tpm2key_free_policy_seq (authpol->policy_seq);
+      grub_free (authpol);
+    }
+
+  grub_tpm2key_free_authpolicy_seq (tmp_seq);
+
+  return err;
+}
+
+void
+grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq)
+{
+  tpm2key_authpolicy_t authpol;
+  tpm2key_authpolicy_t next;
+
+  if (authpol_seq == NULL)
+    return;
+
+  FOR_LIST_ELEMENTS_SAFE (authpol, next, authpol_seq)
+    {
+      grub_tpm2key_free_policy_seq (authpol->policy_seq);
+      grub_free (authpol);
+    }
+}
diff --git a/grub-core/commands/tpm2_key_protector/tpm2key.h 
b/grub-core/commands/tpm2_key_protector/tpm2key.h
new file mode 100644
index 000000000..cbd3f6119
--- /dev/null
+++ b/grub-core/commands/tpm2_key_protector/tpm2key.h
@@ -0,0 +1,87 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2023 SUSE LLC
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_TPM2_TPM2KEY_HEADER
+#define GRUB_TPM2_TPM2KEY_HEADER 1
+
+#include <grub/types.h>
+#include <libtasn1.h>
+
+/*
+ * TPMPolicy ::= SEQUENCE {
+ *   CommandCode   [0] EXPLICIT INTEGER,
+ *   CommandPolicy [1] EXPLICIT OCTET STRING
+ * }
+ */
+struct tpm2key_policy {
+  struct tpm2key_policy *next;
+  struct tpm2key_policy **prev;
+  grub_uint32_t cmd_code;
+  void *cmd_policy;
+  grub_uint16_t cmd_policy_len;
+};
+typedef struct tpm2key_policy *tpm2key_policy_t;
+
+/*
+ * TPMAuthPolicy ::= SEQUENCE {
+ *   Name    [0] EXPLICIT UTF8String OPTIONAL,
+ *   Policy  [1] EXPLICIT SEQUENCE OF TPMPolicy
+ * }
+ *
+ * Name is not a necessary part to unseal the key. Ignore it.
+ */
+struct tpm2key_authpolicy {
+  struct tpm2key_authpolicy *next;
+  struct tpm2key_authpolicy **prev;
+  /* char *name; */
+  tpm2key_policy_t policy_seq;
+};
+typedef struct tpm2key_authpolicy *tpm2key_authpolicy_t;
+
+grub_err_t
+grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t 
size);
+
+void
+grub_tpm2key_end_parsing (asn1_node tpm2key);
+
+grub_err_t
+grub_tpm2key_get_rsaparent (asn1_node tpm2key, grub_uint8_t *rsaparent);
+
+grub_err_t
+grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent);
+
+grub_err_t
+grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size);
+
+grub_err_t
+grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size);
+
+grub_err_t
+grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq);
+
+void
+grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq);
+
+grub_err_t
+grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t 
*authpol_seq);
+
+void
+grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq);
+
+#endif /* GRUB_TPM2_TPM2KEY_HEADER */
diff --git a/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c 
b/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c
new file mode 100644
index 000000000..bebe108a3
--- /dev/null
+++ b/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c
@@ -0,0 +1,63 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ *  This file is generated by 'asn1Parser tpm2key.asn' and the '#include'
+ *  headers are replaced with the ones in grub2.
+ *  - 'grub/mm.h' for the definition of 'NULL'
+ *  - 'libtasn1.h' for the definition of 'asn1_static_node'
+ */
+
+#include <grub/mm.h>
+#include <libtasn1.h>
+
+const asn1_static_node tpm2key_asn1_tab[] = {
+  { "TPM2KEY", 536875024, NULL },
+  { NULL, 1073741836, NULL },
+  { "TPMPolicy", 1610612741, NULL },
+  { "CommandCode", 1610620931, NULL },
+  { NULL, 2056, "0"},
+  { "CommandPolicy", 536879111, NULL },
+  { NULL, 2056, "1"},
+  { "TPMAuthPolicy", 1610612741, NULL },
+  { "Name", 1610637346, NULL },
+  { NULL, 2056, "0"},
+  { "Policy", 536879115, NULL },
+  { NULL, 1073743880, "1"},
+  { NULL, 2, "TPMPolicy"},
+  { "TPMKey", 536870917, NULL },
+  { "type", 1073741836, NULL },
+  { "emptyAuth", 1610637316, NULL },
+  { NULL, 2056, "0"},
+  { "policy", 1610637323, NULL },
+  { NULL, 1073743880, "1"},
+  { NULL, 2, "TPMPolicy"},
+  { "secret", 1610637319, NULL },
+  { NULL, 2056, "2"},
+  { "authPolicy", 1610637323, NULL },
+  { NULL, 1073743880, "3"},
+  { NULL, 2, "TPMAuthPolicy"},
+  { "description", 1610637346, NULL },
+  { NULL, 2056, "4"},
+  { "rsaParent", 1610637316, NULL },
+  { NULL, 2056, "5"},
+  { "parent", 1073741827, NULL },
+  { "pubkey", 1073741831, NULL },
+  { "privkey", 7, NULL },
+  { NULL, 0, NULL }
+};
-- 
2.35.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]