All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] Automatic TPM Disk Unlock
@ 2022-01-24 14:12 Hernan Gatta
  2022-01-24 14:12 ` [PATCH 1/5] protectors: Add key protectors framework Hernan Gatta
                   ` (6 more replies)
  0 siblings, 7 replies; 21+ messages in thread
From: Hernan Gatta @ 2022-01-24 14:12 UTC (permalink / raw)
  To: grub-devel

This patch series adds support for automatically unlocking fully-encrypted disks
using a TPM 2.0.

Currently, when GRUB encounters a fully-encrypted disk that it must access, its
corresponding cryptodisk module (LUKS 1, LUKS2, or GELI) interactively prompts
the user for a passphrase. An improvement to the boot process would be for GRUB
to automatically retrieve the unlocking key for fully-encrypted disks from a
protected location and to unlock these transparently. To this end, a TPM may be
used to protect the unlocking key to a known-good state of the platform. Once
the key is protected in this way, assuming that the platform remains
trustworthy, GRUB can then utilize the TPM to release the key during boot and
thus unlock fully-encrypted disks without user interaction. Such a model would
not only be more convenient for end-users but also for virtual machines in cloud
environments where no user is ever present.

Design
------

This patchset first adds a key protectors framework. This framework allows for
key protector modules to register when loaded. A key protector is defined as a
module that knows how to retrieve an unlocking key from a specific source. This
patchset adds a single such key protector module that understands how to
retrieve an unlocking key from a TPM 2.0 by unsealing a sealed key file via a
Storage Root Key (SRK).

Additionally, this patchset expands the cryptomount command to accept a key
protector parameter. This parameter carries the information necessary to select
and parameterize a key protector to be used to retrieve an unlocking key for the
disk in question. That is, given an invocation of cryptomount to mount a
specific disk (e.g., "cryptomount (hd0,gpt2)", "cryptomount -u UUID"), a key
protector can be used to automatically retrieve an unlocking key without an
interactive prompt.

Lastly, this patchset also includes a new tool, grub-protect, that allows the
user to seal a key file against a set of Platform Configuration Registers (PCRs)
using an SRK. This sealed key file is expected to be stored in an unencrypted
partition, such as the EFI System Partition (ESP), where GRUB can read it. The
sealed key is then unsealed by the TPM2 key protector automatically, provided
that the PCRs selected match on subsequent boots.

Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>

Hernan Gatta (5):
  protectors: Add key protectors framework
  tpm2: Add TPM Software Stack (TSS)
  protectors: Add TPM2 Key Protector
  cryptodisk: Support key protectors
  util/grub-protect: Add new tool

 .gitignore                             |    1 +
 Makefile.util.def                      |   17 +
 configure.ac                           |    1 +
 grub-core/Makefile.am                  |    1 +
 grub-core/Makefile.core.def            |   10 +
 grub-core/disk/cryptodisk.c            |   21 +-
 grub-core/kern/protectors.c            |   98 +++
 grub-core/tpm2/buffer.c                |  145 ++++
 grub-core/tpm2/module.c                |  742 ++++++++++++++++++
 grub-core/tpm2/mu.c                    |  807 +++++++++++++++++++
 grub-core/tpm2/tcg2.c                  |  143 ++++
 grub-core/tpm2/tpm2.c                  |  711 +++++++++++++++++
 include/grub/protector.h               |   55 ++
 include/grub/tpm2/buffer.h             |   65 ++
 include/grub/tpm2/internal/functions.h |  117 +++
 include/grub/tpm2/internal/structs.h   |  675 ++++++++++++++++
 include/grub/tpm2/internal/types.h     |  372 +++++++++
 include/grub/tpm2/mu.h                 |  292 +++++++
 include/grub/tpm2/tcg2.h               |   34 +
 include/grub/tpm2/tpm2.h               |   38 +
 util/grub-protect.c                    | 1344 ++++++++++++++++++++++++++++++++
 21 files changed, 5688 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/kern/protectors.c
 create mode 100644 grub-core/tpm2/buffer.c
 create mode 100644 grub-core/tpm2/module.c
 create mode 100644 grub-core/tpm2/mu.c
 create mode 100644 grub-core/tpm2/tcg2.c
 create mode 100644 grub-core/tpm2/tpm2.c
 create mode 100644 include/grub/protector.h
 create mode 100644 include/grub/tpm2/buffer.h
 create mode 100644 include/grub/tpm2/internal/functions.h
 create mode 100644 include/grub/tpm2/internal/structs.h
 create mode 100644 include/grub/tpm2/internal/types.h
 create mode 100644 include/grub/tpm2/mu.h
 create mode 100644 include/grub/tpm2/tcg2.h
 create mode 100644 include/grub/tpm2/tpm2.h
 create mode 100644 util/grub-protect.c

-- 
1.8.3.1



^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 1/5] protectors: Add key protectors framework
  2022-01-24 14:12 [PATCH 0/5] Automatic TPM Disk Unlock Hernan Gatta
@ 2022-01-24 14:12 ` Hernan Gatta
  2022-01-25  2:54   ` Glenn Washburn
  2022-01-24 14:12 ` [PATCH 2/5] tpm2: Add TPM Software Stack (TSS) Hernan Gatta
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 21+ messages in thread
From: Hernan Gatta @ 2022-01-24 14:12 UTC (permalink / raw)
  To: grub-devel

From: Hernan Gatta <hegatta@microsoft.com>

A key protector encapsulates functionality to retrieve an unlocking key for a
fully-encrypted disk from a specific source. A key protector module registers
itself with the key protectors framework when it is loaded and unregisters when
unloaded. Additionally, a key protector may accept parameters that describe how
it should operate.

The key protectors framework, besides offering registration and unregistration
functions, also offers a one-stop routine for finding and invoking a key
protector. This method accepts a formatted string with the name of a key
protector followed optionally by colon-separated, key protector-specific
parameters. If a key protector with the specified name exists and if an
unlocking key is successfully retrieved by the latter, the function returns to
the caller the retrieved key and its length.

Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
---
 grub-core/Makefile.am       |  1 +
 grub-core/Makefile.core.def |  1 +
 grub-core/kern/protectors.c | 98 +++++++++++++++++++++++++++++++++++++++++++++
 include/grub/protector.h    | 55 +++++++++++++++++++++++++
 4 files changed, 155 insertions(+)
 create mode 100644 grub-core/kern/protectors.c
 create mode 100644 include/grub/protector.h

diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index ee88e44..f78cd9d 100644
--- a/grub-core/Makefile.am
+++ b/grub-core/Makefile.am
@@ -90,6 +90,7 @@ endif
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/parser.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/partition.h
+KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/protector.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/stack_protector.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 8022e1c..e4ae78b 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -138,6 +138,7 @@ kernel = {
   common = kern/misc.c;
   common = kern/parser.c;
   common = kern/partition.c;
+  common = kern/protectors.c;
   common = kern/rescue_parser.c;
   common = kern/rescue_reader.c;
   common = kern/term.c;
diff --git a/grub-core/kern/protectors.c b/grub-core/kern/protectors.c
new file mode 100644
index 0000000..2df0c60
--- /dev/null
+++ b/grub-core/kern/protectors.c
@@ -0,0 +1,98 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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 <grub/protector.h>
+
+struct grub_key_protector *grub_key_protectors = NULL;
+
+grub_err_t
+grub_key_protector_register (struct grub_key_protector *protector)
+{
+  if (!protector || !protector->name || !grub_strlen(protector->name))
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (grub_key_protectors &&
+      grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
+                            protector->name))
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_list_push (GRUB_AS_LIST_P (&grub_key_protectors),
+                  GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_unregister (struct grub_key_protector *protector)
+{
+  if (!protector)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_list_remove (GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_recover_key (char *args, grub_uint8_t **key,
+                                grub_size_t *key_size)
+{
+  char *first_separator = NULL;
+  char *protector_args = NULL;
+  struct grub_key_protector *protector = NULL;
+
+  if (!grub_key_protectors)
+    return GRUB_ERR_OUT_OF_RANGE;
+
+  if (!args || !grub_strlen (args))
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  /* Find the position of the first parameter separator: the stuff before it is
+   * the name of the requested key protector and the stuff after it are the
+   * parameters for said key protector, if any. */
+  first_separator = grub_strchr (args, ':');
+  if (first_separator)
+    {
+      /* Reject a separator at the very beginning. */
+      if (first_separator == args)
+        return GRUB_ERR_BAD_ARGUMENT;
+
+      /* Having a lone colon after the name of the key protector with no
+       * further parameters thereafter does not make any sense. */
+      if (*(first_separator + 1) == '\0')
+        return GRUB_ERR_BAD_ARGUMENT;
+
+      /* Consume the colon, effectively splitting 'args' in two. */
+      first_separator[0] = '\0';
+
+      /* The protector-specific arguments are after the first colon. */
+      protector_args = first_separator + 1;
+    }
+
+  /* Try to find a key protector with the specified name. */
+  protector = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
+                                    args);
+  if (!protector)
+    return GRUB_ERR_OUT_OF_RANGE;
+
+  /* 'protector_args' may be NULL. */
+  return protector->recover_key (protector_args, key, key_size);
+}
diff --git a/include/grub/protector.h b/include/grub/protector.h
new file mode 100644
index 0000000..d445a33
--- /dev/null
+++ b/include/grub/protector.h
@@ -0,0 +1,55 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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_PROTECTOR_HEADER
+#define GRUB_PROTECTOR_HEADER 1
+
+#include <grub/err.h>
+#include <grub/types.h>
+
+enum grub_key_protector_flags
+  {
+    GRUB_KEY_PROTECTOR_FLAG_NONE = 0,
+    GRUB_KEY_PROTECTOR_FLAG_REPEATABLE = 1 << 0
+  };
+
+struct grub_key_protector
+{
+  struct grub_key_protector *next;
+  struct grub_key_protector **prev;
+
+  const char *name;
+  enum grub_key_protector_flags flags;
+
+  grub_err_t (*recover_key) (char *args, grub_uint8_t **key,
+                             grub_size_t *key_size);
+};
+
+extern struct grub_key_protector *EXPORT_VAR (grub_key_protectors);
+
+grub_err_t
+EXPORT_FUNC (grub_key_protector_register) (struct grub_key_protector *protector);
+
+grub_err_t
+EXPORT_FUNC (grub_key_protector_unregister) (struct grub_key_protector *protector);
+
+grub_err_t
+EXPORT_FUNC (grub_key_protector_recover_key) (char args[], grub_uint8_t **key,
+                                              grub_size_t *key_size);
+
+#endif /* ! GRUB_PROTECTOR_HEADER */
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 2/5] tpm2: Add TPM Software Stack (TSS)
  2022-01-24 14:12 [PATCH 0/5] Automatic TPM Disk Unlock Hernan Gatta
  2022-01-24 14:12 ` [PATCH 1/5] protectors: Add key protectors framework Hernan Gatta
@ 2022-01-24 14:12 ` Hernan Gatta
  2022-01-24 14:12 ` [PATCH 3/5] protectors: Add TPM2 Key Protector Hernan Gatta
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 21+ messages in thread
From: Hernan Gatta @ 2022-01-24 14:12 UTC (permalink / raw)
  To: grub-devel

From: Hernan Gatta <hegatta@microsoft.com>

A Trusted Platform Module (TPM) Software Stack (TSS) provides logic to compose,
submit, and parse TPM commands and responses.

A limited number of TPM commands may be accessed via the EFI TCG2 protocol. This
protocol exposes functionality that is primarily geared toward TPM usage within
the context of Secure Boot. For all other TPM commands, however, such as sealing
and unsealing, this protocol does not provide any help, with the exception of
passthrough command submission.

The SubmitCommand method allows a caller to send raw commands to the system's
TPM and to receive the corresponding response. These command/response pairs are
formatted using the TPM wire protocol. To construct commands in this way, and to
parse the TPM's response, it is necessary to, first, possess knowledge of the
various TPM structures, and, two, of the TPM wire protocol itself.

As such, this patch includes a set of header files that define the necessary TPM
structures and TSS functions, implementations of various TPM2_* functions
(inventoried below), and logic to write and read command and response buffers,
respectively, using the TPM wire protocol.

Functions: TPM2_Create, TPM2_CreatePrimary, TPM2_EvictControl,
TPM2_FlushContext, TPM2_Load, TPM2_PCR_Read, TPM2_PolicyGetDigest,
TPM2_PolicyPCR, TPM2_ReadPublic, TPM2_StartAuthSession, TPM2_Unseal.

Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
---
 grub-core/tpm2/buffer.c                | 145 ++++++
 grub-core/tpm2/mu.c                    | 807 +++++++++++++++++++++++++++++++++
 grub-core/tpm2/tcg2.c                  | 143 ++++++
 grub-core/tpm2/tpm2.c                  | 711 +++++++++++++++++++++++++++++
 include/grub/tpm2/buffer.h             |  65 +++
 include/grub/tpm2/internal/functions.h | 117 +++++
 include/grub/tpm2/internal/structs.h   | 675 +++++++++++++++++++++++++++
 include/grub/tpm2/internal/types.h     | 372 +++++++++++++++
 include/grub/tpm2/mu.h                 | 292 ++++++++++++
 include/grub/tpm2/tcg2.h               |  34 ++
 include/grub/tpm2/tpm2.h               |  38 ++
 11 files changed, 3399 insertions(+)
 create mode 100644 grub-core/tpm2/buffer.c
 create mode 100644 grub-core/tpm2/mu.c
 create mode 100644 grub-core/tpm2/tcg2.c
 create mode 100644 grub-core/tpm2/tpm2.c
 create mode 100644 include/grub/tpm2/buffer.h
 create mode 100644 include/grub/tpm2/internal/functions.h
 create mode 100644 include/grub/tpm2/internal/structs.h
 create mode 100644 include/grub/tpm2/internal/types.h
 create mode 100644 include/grub/tpm2/mu.h
 create mode 100644 include/grub/tpm2/tcg2.h
 create mode 100644 include/grub/tpm2/tpm2.h

diff --git a/grub-core/tpm2/buffer.c b/grub-core/tpm2/buffer.c
new file mode 100644
index 0000000..fee42d5
--- /dev/null
+++ b/grub-core/tpm2/buffer.c
@@ -0,0 +1,145 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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/misc.h>
+#include <grub/tpm2/buffer.h>
+
+void grub_tpm2_buffer_init (grub_tpm2_buffer_t buffer)
+{
+  grub_memset (buffer->data, 0xDD, sizeof (buffer->data));
+  buffer->size = 0;
+  buffer->offset = 0;
+  buffer->cap = sizeof (buffer->data);
+  buffer->error = 0;
+}
+
+void
+grub_tpm2_buffer_pack (grub_tpm2_buffer_t buffer, const void* data,
+                       grub_size_t size)
+{
+  grub_uint32_t r = buffer->cap - buffer->size;
+
+  if (buffer->error)
+    return;
+
+  if (size > r)
+    {
+      buffer->error = 1;
+      return;
+    }
+
+  grub_memcpy (&buffer->data[buffer->size], (void*) data, size);
+  buffer->size += size;
+}
+
+void
+grub_tpm2_buffer_pack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t value)
+{
+  grub_tpm2_buffer_pack (buffer, (const char*) &value, sizeof (value));
+}
+
+void
+grub_tpm2_buffer_pack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t value)
+{
+  grub_uint16_t tmp = grub_swap_bytes16 (value);
+  grub_tpm2_buffer_pack (buffer, (const char*) &tmp, sizeof (tmp));
+}
+
+void
+grub_tpm2_buffer_pack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t value)
+{
+  grub_uint32_t tmp = grub_swap_bytes32 (value);
+  grub_tpm2_buffer_pack (buffer, (const char*) &tmp, sizeof (tmp));
+}
+
+void
+grub_tpm2_buffer_unpack (grub_tpm2_buffer_t buffer, void* data,
+                         grub_size_t size)
+{
+  grub_uint32_t r = buffer->size - buffer->offset;
+
+  if (buffer->error)
+    return;
+
+  if (size > r)
+    {
+      buffer->error = 1;
+      return;
+    }
+
+  grub_memcpy (data, &buffer->data[buffer->offset], size);
+  buffer->offset += size;
+}
+
+void
+grub_tpm2_buffer_unpack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t* value)
+{
+  grub_uint32_t r = buffer->size - buffer->offset;
+
+  if (buffer->error)
+    return;
+
+  if (sizeof (*value) > r)
+    {
+      buffer->error = 1;
+      return;
+    }
+
+  grub_memcpy (value, &buffer->data[buffer->offset], sizeof (*value));
+  buffer->offset += sizeof (*value);
+}
+
+void
+grub_tpm2_buffer_unpack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t* value)
+{
+  grub_uint16_t tmp;
+  grub_uint32_t r = buffer->size - buffer->offset;
+
+  if (buffer->error)
+    return;
+
+  if (sizeof (tmp) > r)
+    {
+      buffer->error = 1;
+      return;
+    }
+
+  grub_memcpy (&tmp, &buffer->data[buffer->offset], sizeof (tmp));
+  buffer->offset += sizeof (tmp);
+  *value = grub_swap_bytes16 (tmp);
+}
+
+void
+grub_tpm2_buffer_unpack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t* value)
+{
+  grub_uint32_t tmp;
+  grub_uint32_t r = buffer->size - buffer->offset;
+
+  if (buffer->error)
+    return;
+
+  if (sizeof (tmp) > r)
+    {
+      buffer->error = 1;
+      return;
+    }
+
+  grub_memcpy (&tmp, &buffer->data[buffer->offset], sizeof (tmp));
+  buffer->offset += sizeof (tmp);
+  *value = grub_swap_bytes32 (tmp);
+}
diff --git a/grub-core/tpm2/mu.c b/grub-core/tpm2/mu.c
new file mode 100644
index 0000000..c5f5c7b
--- /dev/null
+++ b/grub-core/tpm2/mu.c
@@ -0,0 +1,807 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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/misc.h>
+#include <grub/tpm2/mu.h>
+
+void
+grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (grub_tpm2_buffer_t buffer,
+                                        const TPMS_AUTH_COMMAND* authCommand)
+{
+  grub_uint32_t start;
+  grub_uint32_t tmp;
+
+  grub_tpm2_buffer_pack_u32 (buffer, 0);
+  start = buffer->size;
+
+  grub_tpm2_buffer_pack_u32 (buffer, authCommand->sessionHandle);
+
+  grub_tpm2_buffer_pack_u16 (buffer, authCommand->nonce.size);
+  grub_tpm2_buffer_pack (buffer, authCommand->nonce.buffer,
+                         authCommand->nonce.size);
+
+  grub_tpm2_buffer_pack_u8 (buffer,
+                            *((const grub_uint8_t*) &authCommand->sessionAttributes));
+
+  grub_tpm2_buffer_pack_u16 (buffer, authCommand->hmac.size);
+  grub_tpm2_buffer_pack (buffer, authCommand->hmac.buffer,
+                         authCommand->hmac.size);
+
+  tmp = grub_swap_bytes32 (buffer->size - start);
+  grub_memcpy (&buffer->data[start - sizeof (grub_uint32_t)], &tmp,
+               sizeof (tmp));
+}
+
+void
+grub_tpm2_mu_TPM2B_Marshal (grub_tpm2_buffer_t buffer,
+                            grub_uint16_t size,
+                            const grub_uint8_t* b)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, size);
+
+  for (grub_uint16_t i = 0; i < size; i++)
+    grub_tpm2_buffer_pack_u8 (buffer, b[i]);
+}
+
+void
+grub_tpm2_mu_TPMU_SYM_KEY_BITS_Marshal (grub_tpm2_buffer_t buffer,
+                                        TPMI_ALG_SYM_OBJECT algorithm,
+                                        TPMU_SYM_KEY_BITS *p)
+{
+  switch (algorithm)
+    {
+    case TPM_ALG_AES:
+    case TPM_ALG_SM4:
+    case TPM_ALG_CAMELLIA:
+    case TPM_ALG_XOR:
+      grub_tpm2_buffer_pack_u16 (buffer, *((const grub_uint16_t*) p));
+      break;
+    case TPM_ALG_NULL:
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMU_SYM_MODE_Marshal (grub_tpm2_buffer_t buffer,
+                                    TPMI_ALG_SYM_OBJECT algorithm,
+                                    TPMU_SYM_MODE *p)
+{
+  switch (algorithm)
+    {
+    case TPM_ALG_AES:
+    case TPM_ALG_SM4:
+    case TPM_ALG_CAMELLIA:
+      grub_tpm2_buffer_pack_u16 (buffer, *((const grub_uint16_t*) p));
+      break;
+    case TPM_ALG_XOR:
+    case TPM_ALG_NULL:
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMT_SYM_DEF_Marshal (grub_tpm2_buffer_t buffer,
+                                   TPMT_SYM_DEF *p)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, p->algorithm);
+  grub_tpm2_mu_TPMU_SYM_KEY_BITS_Marshal (buffer, p->algorithm, &p->keyBits);
+  grub_tpm2_mu_TPMU_SYM_MODE_Marshal (buffer, p->algorithm, &p->mode);
+}
+
+void
+grub_tpm2_mu_TPMS_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buffer,
+                                         const TPMS_PCR_SELECTION* pcrSelection)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, pcrSelection->hash);
+  grub_tpm2_buffer_pack_u8 (buffer, pcrSelection->sizeOfSelect);
+
+  for (grub_uint32_t i = 0; i < pcrSelection->sizeOfSelect; i++)
+    grub_tpm2_buffer_pack_u8 (buffer, pcrSelection->pcrSelect[i]);
+}
+
+void
+grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buffer,
+                                         const TPML_PCR_SELECTION* pcrSelection)
+{
+  grub_tpm2_buffer_pack_u32 (buffer, pcrSelection->count);
+
+  for (grub_uint32_t i = 0; i < pcrSelection->count; i++)
+    grub_tpm2_mu_TPMS_PCR_SELECTION_Marshal (buffer,
+                                             &pcrSelection->pcrSelections[i]);
+}
+
+void
+grub_tpm2_mu_TPMA_OBJECT_Marshal (grub_tpm2_buffer_t buffer,
+                                  const TPMA_OBJECT *p)
+{
+  grub_tpm2_buffer_pack_u32 (buffer, *((const grub_uint32_t*) p));
+}
+
+void
+grub_tpm2_mu_TPMS_SCHEME_XOR_Marshal (grub_tpm2_buffer_t buffer,
+                                      TPMS_SCHEME_XOR *p)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, p->hashAlg);
+  grub_tpm2_buffer_pack_u16 (buffer, p->kdf);
+}
+
+void
+grub_tpm2_mu_TPMS_SCHEME_HMAC_Marshal (grub_tpm2_buffer_t buffer,
+                                       TPMS_SCHEME_HMAC *p)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, p->hashAlg);
+}
+
+void
+grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Marshal (grub_tpm2_buffer_t buffer,
+                                            TPMI_ALG_KEYEDHASH_SCHEME scheme,
+                                            TPMU_SCHEME_KEYEDHASH *p)
+{
+  switch (scheme)
+    {
+    case TPM_ALG_HMAC:
+      grub_tpm2_mu_TPMS_SCHEME_HMAC_Marshal (buffer, &p->hmac);
+      break;
+    case TPM_ALG_XOR:
+      grub_tpm2_mu_TPMS_SCHEME_XOR_Marshal (buffer, &p->exclusiveOr);
+      break;
+    case TPM_ALG_NULL:
+      break;
+    default:
+      buffer->error = 1;
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Marshal (grub_tpm2_buffer_t buffer,
+                                            TPMT_KEYEDHASH_SCHEME *p)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, p->scheme);
+  grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Marshal (buffer, p->scheme, &p->details);
+}
+
+void
+grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Marshal (grub_tpm2_buffer_t buffer,
+                                           TPMS_KEYEDHASH_PARMS *p)
+{
+  grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Marshal (buffer, &p->scheme);
+}
+
+void
+grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Marshal (grub_tpm2_buffer_t buffer,
+                                          TPMT_SYM_DEF_OBJECT *p)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, p->algorithm);
+  grub_tpm2_mu_TPMU_SYM_KEY_BITS_Marshal (buffer, p->algorithm, &p->keyBits);
+  grub_tpm2_mu_TPMU_SYM_MODE_Marshal (buffer, p->algorithm, &p->mode);
+}
+
+void
+grub_tpm2_mu_TPMU_ASYM_SCHEME_Marshal (grub_tpm2_buffer_t buffer,
+                                       TPMI_ALG_RSA_DECRYPT scheme,
+                                       TPMU_ASYM_SCHEME *p __attribute__ ((unused)))
+{
+  switch (scheme)
+    {
+    case TPM_ALG_NULL:
+      break;
+    default:
+      /* Unsupported */
+      buffer->error = 1;
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMT_RSA_SCHEME_Marshal (grub_tpm2_buffer_t buffer,
+                                      TPMT_RSA_SCHEME *p)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, p->scheme);
+  grub_tpm2_mu_TPMU_ASYM_SCHEME_Marshal (buffer, p->scheme, &p->details);
+}
+
+void
+grub_tpm2_mu_TPMS_RSA_PARMS_Marshal (grub_tpm2_buffer_t buffer,
+                                     TPMS_RSA_PARMS *p)
+{
+  grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Marshal (buffer, &p->symmetric);
+  grub_tpm2_mu_TPMT_RSA_SCHEME_Marshal (buffer, &p->scheme);
+  grub_tpm2_buffer_pack_u16 (buffer, p->keyBits);
+  grub_tpm2_buffer_pack_u32 (buffer, p->exponent);
+}
+
+void
+grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Marshal (grub_tpm2_buffer_t buffer,
+                                           TPMS_SYMCIPHER_PARMS *p)
+{
+  grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Marshal (buffer, &p->sym);
+}
+
+void
+grub_tpm2_mu_TPMT_ECC_SCHEME_Marshal (grub_tpm2_buffer_t buffer,
+                                      TPMT_ECC_SCHEME *p)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, p->scheme);
+  grub_tpm2_mu_TPMU_ASYM_SCHEME_Marshal (buffer, p->scheme, &p->details);
+}
+
+void
+grub_tpm2_mu_TPMU_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buffer,
+                                      TPMI_ALG_KDF scheme,
+                                      TPMU_KDF_SCHEME *p)
+{
+  switch (scheme)
+    {
+    case TPM_ALG_MGF1:
+      grub_tpm2_buffer_pack_u16 (buffer, p->mgf1.hashAlg);
+      break;
+    case TPM_ALG_KDF1_SP800_56A:
+      grub_tpm2_buffer_pack_u16 (buffer, p->kdf1_sp800_56a.hashAlg);
+      break;
+    case TPM_ALG_KDF2:
+      grub_tpm2_buffer_pack_u16 (buffer, p->kdf2.hashAlg);
+      break;
+    case TPM_ALG_KDF1_SP800_108:
+      grub_tpm2_buffer_pack_u16 (buffer, p->kdf1_sp800_108.hashAlg);
+      break;
+    case TPM_ALG_NULL:
+      break;
+    default:
+      buffer->error = 1;
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMT_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buffer,
+                                      TPMT_KDF_SCHEME *p)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, p->scheme);
+  grub_tpm2_mu_TPMU_KDF_SCHEME_Marshal (buffer, p->scheme, &p->details);
+}
+
+void
+grub_tpm2_mu_TPMS_ECC_PARMS_Marshal (grub_tpm2_buffer_t buffer,
+                                     TPMS_ECC_PARMS *p)
+{
+  grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Marshal (buffer, &p->symmetric);
+  grub_tpm2_mu_TPMT_ECC_SCHEME_Marshal (buffer, &p->scheme);
+  grub_tpm2_buffer_pack_u16 (buffer, p->curveID);
+  grub_tpm2_mu_TPMT_KDF_SCHEME_Marshal (buffer, &p->kdf);
+}
+
+void
+grub_tpm2_mu_TPMU_PUBLIC_PARMS_Marshal (grub_tpm2_buffer_t buffer,
+                                        grub_uint32_t type,
+                                        TPMU_PUBLIC_PARMS *p)
+{
+  switch (type)
+    {
+    case TPM_ALG_KEYEDHASH:
+      grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Marshal (buffer, &p->keyedHashDetail);
+      break;
+    case TPM_ALG_SYMCIPHER:
+      grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Marshal (buffer, &p->symDetail);
+      break;
+    case TPM_ALG_RSA:
+      grub_tpm2_mu_TPMS_RSA_PARMS_Marshal (buffer, &p->rsaDetail);
+      break;
+    case TPM_ALG_ECC:
+      grub_tpm2_mu_TPMS_ECC_PARMS_Marshal (buffer, &p->eccDetail);
+      break;
+    default:
+      buffer->error = 1;
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMS_ECC_POINT_Marshal (grub_tpm2_buffer_t buffer,
+                                     TPMS_ECC_POINT *p)
+{
+  grub_tpm2_mu_TPM2B_Marshal (buffer, p->x.size, p->x.buffer);
+  grub_tpm2_mu_TPM2B_Marshal (buffer, p->y.size, p->y.buffer);
+}
+
+void
+grub_tpm2_mu_TPMU_PUBLIC_ID_Marshal (grub_tpm2_buffer_t buffer,
+                                     TPMI_ALG_PUBLIC type,
+                                     TPMU_PUBLIC_ID *p)
+{
+  switch(type)
+    {
+    case TPM_ALG_KEYEDHASH:
+      grub_tpm2_mu_TPM2B_Marshal (buffer, p->keyedHash.size,
+                                  p->keyedHash.buffer);
+      break;
+    case TPM_ALG_SYMCIPHER:
+      grub_tpm2_mu_TPM2B_Marshal (buffer, p->sym.size, p->sym.buffer);
+      break;
+    case TPM_ALG_RSA:
+      grub_tpm2_mu_TPM2B_Marshal (buffer, p->rsa.size, p->rsa.buffer);
+      break;
+    case TPM_ALG_ECC:
+      grub_tpm2_mu_TPMS_ECC_POINT_Marshal (buffer, &p->ecc);
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMT_PUBLIC_Marshal (grub_tpm2_buffer_t buffer,
+                                  TPMT_PUBLIC *p)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, p->type);
+  grub_tpm2_buffer_pack_u16 (buffer, p->nameAlg);
+  grub_tpm2_mu_TPMA_OBJECT_Marshal (buffer, &p->objectAttributes);
+  grub_tpm2_mu_TPM2B_Marshal (buffer, p->authPolicy.size, p->authPolicy.buffer);
+  grub_tpm2_mu_TPMU_PUBLIC_PARMS_Marshal (buffer, p->type, &p->parameters);
+  grub_tpm2_mu_TPMU_PUBLIC_ID_Marshal (buffer, p->type, &p->unique);
+}
+
+void
+grub_tpm2_mu_TPM2B_PUBLIC_Marshal (grub_tpm2_buffer_t buffer,
+                                   TPM2B_PUBLIC *p)
+{
+  grub_uint32_t start;
+  grub_uint16_t size;
+
+  if (p)
+    {
+      grub_tpm2_buffer_pack_u16 (buffer, p->size);
+
+      start = buffer->size;
+      grub_tpm2_mu_TPMT_PUBLIC_Marshal (buffer, &p->publicArea);
+      size = grub_swap_bytes16 (buffer->size - start);
+      grub_memcpy (&buffer->data[start - sizeof (grub_uint16_t)], &size,
+                   sizeof (size));
+    }
+  else
+    grub_tpm2_buffer_pack_u16 (buffer, 0);
+}
+
+void
+grub_tpm2_mu_TPMS_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer,
+                                            TPMS_SENSITIVE_CREATE *p)
+{
+  grub_tpm2_mu_TPM2B_Marshal (buffer, p->userAuth.size, p->userAuth.buffer);
+  grub_tpm2_mu_TPM2B_Marshal (buffer, p->data.size, p->data.buffer);
+}
+
+void
+grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer,
+                                             TPM2B_SENSITIVE_CREATE *sensitiveCreate)
+{
+  grub_uint32_t start;
+  grub_uint16_t size;
+
+  if (sensitiveCreate)
+    {
+      grub_tpm2_buffer_pack_u16 (buffer, sensitiveCreate->size);
+      start = buffer->size;
+      grub_tpm2_mu_TPMS_SENSITIVE_CREATE_Marshal (buffer,
+                                                  &sensitiveCreate->sensitive);
+      size = grub_swap_bytes16 (buffer->size - start);
+
+      grub_memcpy (&buffer->data[start - sizeof (grub_uint16_t)], &size,
+                   sizeof (size));
+    }
+  else
+    grub_tpm2_buffer_pack_u16 (buffer, 0);
+}
+
+void
+grub_tpm2_mu_TPM2B_Unmarshal (grub_tpm2_buffer_t buffer,
+                              TPM2B* p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->size);
+
+  for (grub_uint16_t i = 0; i < p->size; i++)
+    grub_tpm2_buffer_unpack_u8 (buffer, &p->buffer[i]);
+}
+
+void
+grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (grub_tpm2_buffer_t buffer,
+                                           TPMS_AUTH_RESPONSE* p)
+{
+  grub_uint8_t tmp;
+  grub_uint32_t tmp32;
+
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->nonce.size);
+
+  if (p->nonce.size)
+    grub_tpm2_buffer_unpack (buffer, &p->nonce.buffer, p->nonce.size);
+
+  grub_tpm2_buffer_unpack_u8 (buffer, &tmp);
+  tmp32 = tmp;
+  grub_memcpy (&p->sessionAttributes, &tmp32, sizeof (grub_uint32_t));
+
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->hmac.size);
+
+  if (p->hmac.size)
+    grub_tpm2_buffer_unpack (buffer, &p->hmac.buffer, p->hmac.size);
+}
+
+void
+grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (grub_tpm2_buffer_t buffer,
+                                     TPM2B_DIGEST* digest)
+{
+  grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*)digest);
+}
+
+void
+grub_tpm2_mu_TPMA_OBJECT_Unmarshal (grub_tpm2_buffer_t buffer,
+                                    TPMA_OBJECT *p)
+{
+  grub_tpm2_buffer_unpack_u32 (buffer, (grub_uint32_t*)p);
+}
+
+void
+grub_tpm2_mu_TPMS_SCHEME_HMAC_Unmarshal (grub_tpm2_buffer_t buffer,
+                                         TPMS_SCHEME_HMAC *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->hashAlg);
+}
+
+void
+grub_tpm2_mu_TPMS_SCHEME_XOR_Unmarshal (grub_tpm2_buffer_t buffer,
+                                        TPMS_SCHEME_XOR *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->hashAlg);
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf);
+}
+
+void
+grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Unmarshal (grub_tpm2_buffer_t buffer,
+                                              TPMI_ALG_KEYEDHASH_SCHEME scheme,
+                                              TPMU_SCHEME_KEYEDHASH *p)
+{
+  switch (scheme)
+    {
+    case TPM_ALG_HMAC:
+      grub_tpm2_mu_TPMS_SCHEME_HMAC_Unmarshal (buffer, &p->hmac);
+      break;
+    case TPM_ALG_XOR:
+      grub_tpm2_mu_TPMS_SCHEME_XOR_Unmarshal (buffer, &p->exclusiveOr);
+      break;
+    case TPM_ALG_NULL:
+      break;
+    default:
+      buffer->error = 1;
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer,
+                                              TPMT_KEYEDHASH_SCHEME *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme);
+  grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Unmarshal (buffer, p->scheme, &p->details);
+}
+
+void
+grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Unmarshal (grub_tpm2_buffer_t buffer,
+                                             TPMS_KEYEDHASH_PARMS *p)
+{
+  grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Unmarshal (buffer, &p->scheme);
+}
+
+void
+grub_tpm2_mu_TPMU_SYM_KEY_BITS_Unmarshal (grub_tpm2_buffer_t buffer,
+                                          TPMI_ALG_SYM_OBJECT algorithm,
+                                          TPMU_SYM_KEY_BITS *p)
+{
+  switch (algorithm)
+    {
+    case TPM_ALG_AES:
+    case TPM_ALG_SM4:
+    case TPM_ALG_CAMELLIA:
+    case TPM_ALG_XOR:
+      grub_tpm2_buffer_unpack_u16 (buffer, (grub_uint16_t*) p);
+      break;
+    case TPM_ALG_NULL:
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMU_SYM_MODE_Unmarshal (grub_tpm2_buffer_t buffer,
+                                      TPMI_ALG_SYM_OBJECT algorithm,
+                                      TPMU_SYM_MODE *p)
+{
+  switch (algorithm)
+    {
+    case TPM_ALG_AES:
+    case TPM_ALG_SM4:
+    case TPM_ALG_CAMELLIA:
+      grub_tpm2_buffer_unpack_u16 (buffer, (grub_uint16_t*) p);
+      break;
+    case TPM_ALG_XOR:
+    case TPM_ALG_NULL:
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Unmarshal (grub_tpm2_buffer_t buffer,
+                                            TPMT_SYM_DEF_OBJECT *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->algorithm);
+  grub_tpm2_mu_TPMU_SYM_KEY_BITS_Unmarshal (buffer, p->algorithm, &p->keyBits);
+  grub_tpm2_mu_TPMU_SYM_MODE_Unmarshal (buffer, p->algorithm, &p->mode);
+}
+
+void
+grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Unmarshal (grub_tpm2_buffer_t buffer,
+                                             TPMS_SYMCIPHER_PARMS *p)
+{
+  grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Unmarshal (buffer, &p->sym);
+}
+
+void
+grub_tpm2_mu_TPMU_ASYM_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer,
+                                         TPMI_ALG_RSA_DECRYPT scheme,
+                                         TPMU_ASYM_SCHEME *p __attribute__((unused)))
+{
+  switch (scheme)
+    {
+    case TPM_ALG_NULL:
+      break;
+    default:
+      /* Unsupported */
+      buffer->error = 1;
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMT_RSA_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer,
+                                        TPMT_RSA_SCHEME *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme);
+  grub_tpm2_mu_TPMU_ASYM_SCHEME_Unmarshal (buffer, p->scheme, &p->details);
+}
+
+void
+grub_tpm2_mu_TPMS_RSA_PARMS_Unmarshal (grub_tpm2_buffer_t buffer,
+                                       TPMS_RSA_PARMS *p)
+{
+  grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Unmarshal (buffer, &p->symmetric);
+  grub_tpm2_mu_TPMT_RSA_SCHEME_Unmarshal (buffer, &p->scheme);
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->keyBits);
+  grub_tpm2_buffer_unpack_u32 (buffer, &p->exponent);
+}
+
+void
+grub_tpm2_mu_TPMT_ECC_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer,
+                                        TPMT_ECC_SCHEME *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme);
+  grub_tpm2_mu_TPMU_ASYM_SCHEME_Unmarshal (buffer, p->scheme, &p->details);
+}
+
+void
+grub_tpm2_mu_TPMU_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer,
+                                        TPMI_ALG_KDF scheme,
+                                        TPMU_KDF_SCHEME *p)
+{
+  switch (scheme)
+    {
+    case TPM_ALG_MGF1:
+      grub_tpm2_buffer_unpack_u16 (buffer, &p->mgf1.hashAlg);
+      break;
+    case TPM_ALG_KDF1_SP800_56A:
+      grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf1_sp800_56a.hashAlg);
+      break;
+    case TPM_ALG_KDF2:
+      grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf2.hashAlg);
+      break;
+    case TPM_ALG_KDF1_SP800_108:
+      grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf1_sp800_108.hashAlg);
+      break;
+    case TPM_ALG_NULL:
+      break;
+    default:
+      buffer->error = 1;
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMT_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer,
+                                        TPMT_KDF_SCHEME *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme);
+  grub_tpm2_mu_TPMU_KDF_SCHEME_Unmarshal (buffer, p->scheme, &p->details);
+}
+
+void
+grub_tpm2_mu_TPMS_ECC_PARMS_Unmarshal (grub_tpm2_buffer_t buffer,
+                                       TPMS_ECC_PARMS *p)
+{
+  grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Unmarshal (buffer, &p->symmetric);
+  grub_tpm2_mu_TPMT_ECC_SCHEME_Unmarshal (buffer, &p->scheme );
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->curveID);
+  grub_tpm2_mu_TPMT_KDF_SCHEME_Unmarshal (buffer, &p->kdf);
+}
+
+void
+grub_tpm2_mu_TPMU_PUBLIC_PARMS_Unmarshal (grub_tpm2_buffer_t buffer,
+                                          grub_uint32_t type,
+                                          TPMU_PUBLIC_PARMS *p)
+{
+  switch (type)
+    {
+    case TPM_ALG_KEYEDHASH:
+      grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Unmarshal (buffer, &p->keyedHashDetail);
+      break;
+    case TPM_ALG_SYMCIPHER:
+      grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Unmarshal (buffer, &p->symDetail);
+      break;
+    case TPM_ALG_RSA:
+      grub_tpm2_mu_TPMS_RSA_PARMS_Unmarshal (buffer, &p->rsaDetail);
+      break;
+    case TPM_ALG_ECC:
+      grub_tpm2_mu_TPMS_ECC_PARMS_Unmarshal (buffer, &p->eccDetail);
+      break;
+    default:
+      buffer->error = 1;
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMS_ECC_POINT_Unmarshal (grub_tpm2_buffer_t buffer,
+                                       TPMS_ECC_POINT *p)
+{
+  grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->x);
+  grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->y);
+}
+
+void
+grub_tpm2_mu_TPMU_PUBLIC_ID_Unmarshal (grub_tpm2_buffer_t buffer,
+                                       TPMI_ALG_PUBLIC type,
+                                       TPMU_PUBLIC_ID *p)
+{
+  switch(type)
+    {
+    case TPM_ALG_KEYEDHASH:
+      grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->keyedHash);
+      break;
+    case TPM_ALG_SYMCIPHER:
+      grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->sym);
+      break;
+    case TPM_ALG_RSA:
+      grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->rsa);
+      break;
+    case TPM_ALG_ECC:
+      grub_tpm2_mu_TPMS_ECC_POINT_Unmarshal (buffer, &p->ecc);
+      break;
+    }
+}
+
+void
+grub_tpm2_mu_TPMT_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer,
+                                    TPMT_PUBLIC *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->type);
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->nameAlg);
+  grub_tpm2_mu_TPMA_OBJECT_Unmarshal (buffer, &p->objectAttributes);
+  grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->authPolicy);
+  grub_tpm2_mu_TPMU_PUBLIC_PARMS_Unmarshal (buffer, p->type, &p->parameters);
+  grub_tpm2_mu_TPMU_PUBLIC_ID_Unmarshal (buffer, p->type, &p->unique);
+}
+
+void
+grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer,
+                                     TPM2B_PUBLIC *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->size);
+  grub_tpm2_mu_TPMT_PUBLIC_Unmarshal (buffer, &p->publicArea);
+}
+
+void
+grub_tpm2_mu_TPMS_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer,
+                                       TPMS_NV_PUBLIC *p)
+{
+  grub_tpm2_buffer_unpack_u32 (buffer, &p->nvIndex);
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->nameAlg);
+  grub_tpm2_buffer_unpack_u32 (buffer, &p->attributes);
+  grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (buffer, &p->authPolicy);
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->dataSize);
+}
+
+void
+grub_tpm2_mu_TPM2B_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer,
+                                        TPM2B_NV_PUBLIC *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->size);
+  grub_tpm2_mu_TPMS_NV_PUBLIC_Unmarshal (buffer, &p->nvPublic);
+}
+
+void
+grub_tpm2_mu_TPM2B_NAME_Unmarshal (grub_tpm2_buffer_t buffer,
+                                   TPM2B_NAME *n)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &n->size);
+  grub_tpm2_buffer_unpack (buffer, n->name, n->size);
+}
+
+void
+grub_tpm2_mu_TPMS_TAGGED_PROPERTY_Unmarshal (grub_tpm2_buffer_t buffer,
+                                             TPMS_TAGGED_PROPERTY* property)
+{
+  grub_tpm2_buffer_unpack_u32 (buffer, &property->property);
+  grub_tpm2_buffer_unpack_u32 (buffer, &property->value);
+}
+
+void
+grub_tpm2_mu_TPMS_CAPABILITY_DATA_tpmProperties_Unmarshal (grub_tpm2_buffer_t buffer,
+                                                           TPMS_CAPABILITY_DATA* capabilityData)
+{
+  grub_tpm2_buffer_unpack_u32 (buffer,
+                               &capabilityData->data.tpmProperties.count);
+
+  if (buffer->error)
+    return;
+
+  for (grub_uint32_t i = 0; i < capabilityData->data.tpmProperties.count; i++)
+    grub_tpm2_mu_TPMS_TAGGED_PROPERTY_Unmarshal (buffer,
+                                                 &capabilityData->data.tpmProperties.tpmProperty[i]);
+}
+
+void
+grub_tpm2_mu_TPMT_TK_CREATION_Unmarshal (grub_tpm2_buffer_t buffer,
+                                         TPMT_TK_CREATION *p)
+{
+  grub_tpm2_buffer_unpack_u16 (buffer, &p->tag);
+  grub_tpm2_buffer_unpack_u32 (buffer, &p->hierarchy);
+  grub_tpm2_mu_TPM2B_Unmarshal (buffer, (TPM2B*) &p->digest);
+}
+
+void
+grub_tpm2_mu_TPMS_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buf,
+                                           TPMS_PCR_SELECTION* pcrSelection)
+{
+  grub_tpm2_buffer_unpack_u16 (buf, &pcrSelection->hash);
+  grub_tpm2_buffer_unpack_u8 (buf, &pcrSelection->sizeOfSelect);
+
+  for (grub_uint32_t i = 0; i < pcrSelection->sizeOfSelect; i++)
+    grub_tpm2_buffer_unpack_u8 (buf, &pcrSelection->pcrSelect[i]);
+}
+
+void
+grub_tpm2_mu_TPML_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buf,
+                                           TPML_PCR_SELECTION* pcrSelection)
+{
+  grub_tpm2_buffer_unpack_u32 (buf, &pcrSelection->count);
+
+  for (grub_uint32_t i = 0; i < pcrSelection->count; i++)
+    grub_tpm2_mu_TPMS_PCR_SELECTION_Unmarshal (buf, &pcrSelection->pcrSelections[i]);
+}
+
+void
+grub_tpm2_mu_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buf,
+                                    TPML_DIGEST* digest)
+{
+  grub_tpm2_buffer_unpack_u32 (buf, &digest->count);
+
+  for (grub_uint32_t i = 0; i < digest->count; i++)
+    grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (buf, &digest->digests[i]);
+}
diff --git a/grub-core/tpm2/tcg2.c b/grub-core/tpm2/tcg2.c
new file mode 100644
index 0000000..4483721
--- /dev/null
+++ b/grub-core/tpm2/tcg2.c
@@ -0,0 +1,143 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/tpm.h>
+#include <grub/mm.h>
+#include <grub/tpm2/tcg2.h>
+
+static grub_err_t
+grub_tcg2_get_caps (grub_efi_tpm2_protocol_t *protocol, int *tpm2,
+                    grub_size_t *max_output_size)
+{
+  grub_efi_status_t status;
+
+  static int has_caps = 0;
+  static EFI_TCG2_BOOT_SERVICE_CAPABILITY caps =
+  {
+    .Size = (grub_uint8_t) sizeof (caps)
+  };
+
+  if (has_caps)
+    goto exit;
+
+  status = efi_call_2 (protocol->get_capability, protocol, &caps);
+  if (status != GRUB_EFI_SUCCESS || !caps.TPMPresentFlag)
+    return GRUB_ERR_FILE_NOT_FOUND;
+
+  has_caps = 1;
+
+exit:
+  if (tpm2)
+    *tpm2 = caps.TPMPresentFlag;
+  if (max_output_size)
+    *max_output_size = caps.MaxResponseSize;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tcg2_get_protocol (grub_efi_tpm2_protocol_t **protocol)
+{
+  static grub_efi_guid_t tpm2_guid = EFI_TPM2_GUID;
+  static grub_efi_tpm2_protocol_t *tpm2_protocol = NULL;
+
+  int tpm2;
+  grub_efi_handle_t *handles;
+  grub_efi_uintn_t num_handles;
+  grub_efi_handle_t tpm2_handle;
+  grub_err_t err = GRUB_ERR_FILE_NOT_FOUND;
+
+  if (tpm2_protocol)
+    {
+      *protocol = tpm2_protocol;
+      return GRUB_ERR_NONE;
+    }
+
+  handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm2_guid, NULL,
+                                    &num_handles);
+  if (!handles || !num_handles)
+    return err;
+
+  tpm2_handle = handles[0];
+
+  tpm2_protocol = grub_efi_open_protocol (tpm2_handle, &tpm2_guid,
+                                          GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+  if (!tpm2_protocol)
+    goto exit;
+
+  err = grub_tcg2_get_caps (tpm2_protocol, &tpm2, NULL);
+  if (err || !tpm2)
+    goto exit;
+
+  *protocol = tpm2_protocol;
+  err = GRUB_ERR_NONE;
+
+exit:
+  grub_free (handles);
+  return err;
+}
+
+grub_err_t
+grub_tcg2_get_max_output_size (grub_size_t *size)
+{
+  grub_err_t err;
+  grub_size_t max;
+  grub_efi_tpm2_protocol_t *protocol;
+
+  if (!size)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  err = grub_tcg2_get_protocol (&protocol);
+  if (err)
+    return err;
+
+  err = grub_tcg2_get_caps (protocol, NULL, &max);
+  if (err)
+    return err;
+
+  *size = max;
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_tcg2_submit_command (grub_size_t input_size,
+                          grub_uint8_t *input,
+                          grub_size_t output_size,
+                          grub_uint8_t *output)
+{
+  grub_err_t err;
+  grub_efi_status_t status;
+  grub_efi_tpm2_protocol_t *protocol;
+
+  if (!input_size || !input || !output_size || !output)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  err = grub_tcg2_get_protocol (&protocol);
+  if (err)
+    return err;
+
+  status = efi_call_5 (protocol->submit_command, protocol, input_size, input,
+                       output_size, output);
+  if (status != GRUB_EFI_SUCCESS)
+    return GRUB_ERR_INVALID_COMMAND;
+
+  return GRUB_ERR_NONE;
+}
diff --git a/grub-core/tpm2/tpm2.c b/grub-core/tpm2/tpm2.c
new file mode 100644
index 0000000..2407a84
--- /dev/null
+++ b/grub-core/tpm2/tpm2.c
@@ -0,0 +1,711 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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/misc.h>
+#include <grub/tpm2/buffer.h>
+#include <grub/tpm2/mu.h>
+#include <grub/tpm2/tcg2.h>
+#include <grub/tpm2/tpm2.h>
+#include <grub/types.h>
+
+static TPM_RC
+grub_tpm2_submit_command (TPMI_ST_COMMAND_TAG tag,
+                          TPM_CC commandCode,
+                          TPM_RC* responseCode,
+                          const struct grub_tpm2_buffer* in,
+                          struct grub_tpm2_buffer* out)
+{
+  grub_err_t err;
+  struct grub_tpm2_buffer buf;
+  TPMI_ST_COMMAND_TAG tag_out;
+  grub_uint32_t command_size;
+  grub_size_t max_output_size;
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&buf);
+  grub_tpm2_buffer_pack_u16 (&buf, tag);
+  grub_tpm2_buffer_pack_u32 (&buf, 0);
+  grub_tpm2_buffer_pack_u32 (&buf, commandCode);
+  grub_tpm2_buffer_pack (&buf, in->data, in->size);
+
+  if (buf.error)
+    return TPM_RC_FAILURE;
+
+  command_size = grub_swap_bytes32 (buf.size);
+  grub_memcpy (&buf.data[sizeof (grub_uint16_t)], &command_size,
+               sizeof (command_size));
+
+  /* Stay within output block limits */
+  err = grub_tcg2_get_max_output_size (&max_output_size);
+  if (err || max_output_size > out->cap)
+    max_output_size = out->cap - 1;
+
+  /* Submit */
+  err = grub_tcg2_submit_command (buf.size, buf.data, max_output_size,
+                                  out->data);
+  if (err)
+    return TPM_RC_FAILURE;
+
+  /* Unmarshal*/
+  out->size = sizeof (grub_uint16_t) + sizeof (grub_uint32_t) +
+              sizeof (grub_uint32_t);
+  grub_tpm2_buffer_unpack_u16 (out, &tag_out);
+  grub_tpm2_buffer_unpack_u32 (out, &command_size);
+  grub_tpm2_buffer_unpack_u32 (out, responseCode);
+  out->size = command_size;
+  if (out->error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_CreatePrimary (TPMI_RH_HIERARCHY primaryHandle,
+                    const TPMS_AUTH_COMMAND *authCommand,
+                    TPM2B_SENSITIVE_CREATE *inSensitive,
+                    TPM2B_PUBLIC *inPublic,
+                    TPM2B_DATA *outsideInfo,
+                    TPML_PCR_SELECTION *creationPCR,
+                    TPM_HANDLE *objectHandle,
+                    TPM2B_PUBLIC *outPublic,
+                    TPM2B_CREATION_DATA *creationData,
+                    TPM2B_DIGEST *creationHash,
+                    TPMT_TK_CREATION *creationTicket,
+                    TPM2B_NAME *name,
+                    TPMS_AUTH_RESPONSE *authResponse)
+{
+  TPM_RC rc;
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  TPM_HANDLE objectHandleTmp;
+  TPM2B_PUBLIC outPublicTmp;
+  TPM2B_CREATION_DATA creationDataTmp;
+  TPM2B_DIGEST creationHashTmp;
+  TPMT_TK_CREATION creationTicketTmp;
+  TPM2B_NAME nameTmp;
+  TPMS_AUTH_RESPONSE authResponseTmp;
+  TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS;
+  TPM_RC responseCode;
+  grub_uint32_t parameterSize;
+
+  if (!objectHandle)
+    objectHandle = &objectHandleTmp;
+  if (!outPublic)
+    outPublic = &outPublicTmp;
+  if (!creationData)
+    creationData = &creationDataTmp;
+  if (!creationHash)
+    creationHash = &creationHashTmp;
+  if (!creationTicket)
+    creationTicket = &creationTicketTmp;
+  if (!name)
+    name = &nameTmp;
+  if (!authResponse)
+    authResponse = &authResponseTmp;
+
+  grub_memset (outPublic, 0, sizeof (*outPublic));
+  grub_memset (creationData, 0, sizeof (*creationData));
+  grub_memset (creationHash, 0, sizeof (*creationHash));
+  grub_memset (creationTicket, 0, sizeof (*creationTicket));
+  grub_memset (name, 0, sizeof (*name));
+  grub_memset (authResponse, 0, sizeof (*authResponse));
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_buffer_pack_u32 (&in, primaryHandle);
+  if (authCommand)
+    grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand);
+  grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (&in, inSensitive);
+  grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&in, inPublic);
+  grub_tpm2_mu_TPM2B_Marshal (&in, outsideInfo->size, outsideInfo->buffer);
+  grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&in, creationPCR);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (tag, TPM_CC_CreatePrimary, &responseCode, &in,
+                                 &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  /* Unmarshal*/
+  grub_tpm2_buffer_unpack_u32 (&out, objectHandle);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_buffer_unpack_u32 (&out, &parameterSize);
+  grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&out, outPublic);
+  grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)creationData);
+  grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)creationHash);
+  grub_tpm2_mu_TPMT_TK_CREATION_Unmarshal (&out, creationTicket);
+  grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)name);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse);
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_StartAuthSession (TPMI_DH_OBJECT tpmKey,
+                       TPMI_DH_ENTITY bind,
+                       const TPMS_AUTH_COMMAND *authCommand,
+                       TPM2B_NONCE *nonceCaller,
+                       TPM2B_ENCRYPTED_SECRET *encryptedSalt,
+                       TPM_SE sessionType,
+                       TPMT_SYM_DEF *symmetric,
+                       TPMI_ALG_HASH authHash,
+                       TPMI_SH_AUTH_SESSION *sessionHandle,
+                       TPM2B_NONCE *nonceTpm,
+                       TPMS_AUTH_RESPONSE *authResponse)
+{
+  TPM_RC rc;
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  TPMI_SH_AUTH_SESSION sessionHandleTmp;
+  TPM2B_NONCE nonceTpmTmp;
+  TPMS_AUTH_RESPONSE authResponseTmp;
+  TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS;
+  TPM_RC responseCode;
+  grub_uint32_t param_size;
+
+  if (!sessionHandle)
+    sessionHandle = &sessionHandleTmp;
+  if (!nonceTpm)
+    nonceTpm = &nonceTpmTmp;
+  if (!authResponse)
+    authResponse = &authResponseTmp;
+
+  grub_memset (sessionHandle, 0, sizeof (*sessionHandle));
+  grub_memset (nonceTpm, 0, sizeof (*nonceTpm));
+  grub_memset (authResponse, 0, sizeof (*authResponse));
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_buffer_pack_u32 (&in, tpmKey);
+  grub_tpm2_buffer_pack_u32 (&in, bind);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand);
+  grub_tpm2_mu_TPM2B_Marshal (&in, nonceCaller->size, nonceCaller->buffer);
+  grub_tpm2_mu_TPM2B_Marshal (&in, encryptedSalt->size, encryptedSalt->secret);
+  grub_tpm2_buffer_pack_u8 (&in, sessionType);
+  grub_tpm2_mu_TPMT_SYM_DEF_Marshal (&in, symmetric);
+  grub_tpm2_buffer_pack_u16 (&in, authHash);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (tag, TPM_CC_StartAuthSession, &responseCode,
+                                 &in, &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  /* Unmarshal*/
+  grub_tpm2_buffer_unpack_u32 (&out, sessionHandle);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_buffer_unpack_u32 (&out, &param_size);
+  grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)nonceTpm);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse);
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_PolicyPCR (TPMI_SH_POLICY policySessions,
+                const TPMS_AUTH_COMMAND *authCommand,
+                TPM2B_DIGEST *pcrDigest,
+                TPML_PCR_SELECTION *pcrs,
+                TPMS_AUTH_RESPONSE *authResponse)
+{
+  TPM_RC rc;
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  TPMS_AUTH_RESPONSE authResponseTmp;
+  TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS;
+  TPM_RC responseCode;
+  grub_uint32_t param_size;
+
+  if (!authResponse)
+    authResponse = &authResponseTmp;
+
+  grub_memset (authResponse, 0, sizeof (*authResponse));
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_buffer_pack_u32 (&in, policySessions);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand);
+  grub_tpm2_mu_TPM2B_Marshal (&in, pcrDigest->size, pcrDigest->buffer);
+  grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&in, pcrs);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (tag, TPM_CC_PolicyPCR, &responseCode, &in,
+                                 &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  /* Unmarshal*/
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_buffer_unpack_u32 (&out, &param_size);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse);
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_ReadPublic (TPMI_DH_OBJECT objectHandle,
+                 const TPMS_AUTH_COMMAND* authCommand,
+                 TPM2B_PUBLIC *outPublic)
+{
+  TPM_RC rc;
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS;
+  TPM_RC responseCode;
+  grub_uint32_t parameterSize;
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_buffer_pack_u32 (&in, objectHandle);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (tag, TPM_CC_ReadPublic, &responseCode, &in,
+                                 &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  /* Unmarshal*/
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_buffer_unpack_u32 (&out, &parameterSize);
+  grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&out, outPublic);
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_Load (TPMI_DH_OBJECT parent_handle,
+           TPMS_AUTH_COMMAND const *authCommand,
+           TPM2B_PRIVATE *inPrivate,
+           TPM2B_PUBLIC *inPublic,
+           TPM_HANDLE *objectHandle,
+           TPM2B_NAME *name,
+           TPMS_AUTH_RESPONSE *authResponse)
+{
+  TPM_RC rc;
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  TPM_HANDLE objectHandleTmp;
+  TPM2B_NAME nonceTmp;
+  TPMS_AUTH_RESPONSE authResponseTmp;
+  TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS;
+  TPM_RC responseCode;
+  grub_uint32_t param_size;
+
+  if (!objectHandle)
+    objectHandle = &objectHandleTmp;
+  if (!name)
+    name = &nonceTmp;
+  if (!authResponse)
+    authResponse = &authResponseTmp;
+
+  grub_memset (objectHandle, 0, sizeof (*objectHandle));
+  grub_memset (name, 0, sizeof (*name));
+  grub_memset (authResponse, 0, sizeof (*authResponse));
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_buffer_pack_u32 (&in, parent_handle);
+  if (authCommand)
+    grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand);
+  grub_tpm2_mu_TPM2B_Marshal (&in, inPrivate->size, inPrivate->buffer);
+  grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&in, inPublic);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (tag, TPM_CC_Load, &responseCode, &in, &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  /* Unmarshal*/
+  grub_tpm2_buffer_unpack_u32 (&out, objectHandle);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_buffer_unpack_u32 (&out, &param_size);
+  grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)name);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse);
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_Unseal (TPMI_DH_OBJECT itemHandle,
+             const TPMS_AUTH_COMMAND *authCommand,
+             TPM2B_SENSITIVE_DATA *outData,
+             TPMS_AUTH_RESPONSE *authResponse)
+{
+  TPM_RC rc;
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  TPM2B_SENSITIVE_DATA outDataTmp;
+  TPMS_AUTH_RESPONSE authResponseTmp;
+  TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS;
+  TPM_RC responseCode;
+  grub_uint32_t param_size;
+
+  if (!outData)
+    outData = &outDataTmp;
+  if (!authResponse)
+    authResponse = &authResponseTmp;
+
+  grub_memset (outData, 0, sizeof (*outData));
+  grub_memset (authResponse, 0, sizeof (*authResponse));
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_buffer_pack_u32 (&in, itemHandle);
+  if (authCommand)
+    grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (tag, TPM_CC_Unseal, &responseCode, &in, &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  // Unmarhsal
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_buffer_unpack_u32 (&out, &param_size);
+  grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)outData);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse);
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_FlushContext (TPMI_DH_CONTEXT handle)
+{
+  TPM_RC rc;
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  TPM_RC responseCode;
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_buffer_pack_u32 (&in, handle);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (TPM_ST_NO_SESSIONS, TPM_CC_FlushContext,
+                                 &responseCode, &in, &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  /* Unmarshal*/
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_PCR_Read (const TPMS_AUTH_COMMAND *authCommand,
+               TPML_PCR_SELECTION  *pcrSelectionIn,
+               grub_uint32_t *pcrUpdateCounter,
+               TPML_PCR_SELECTION *pcrSelectionOut,
+               TPML_DIGEST *pcrValues,
+               TPMS_AUTH_RESPONSE *authResponse)
+{
+  TPM_RC rc;
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  grub_uint32_t pcrUpdateCounterTmp;
+  TPML_PCR_SELECTION pcrSelectionOutTmp;
+  TPML_DIGEST pcrValuesTmp;
+  TPMS_AUTH_RESPONSE authResponseTmp;
+  TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS;
+  TPM_RC responseCode;
+  grub_uint32_t parameterSize;
+
+  if (!pcrSelectionIn)
+    return TPM_RC_FAILURE;
+
+  if (!pcrUpdateCounter)
+    pcrUpdateCounter = &pcrUpdateCounterTmp;
+  if (!pcrSelectionOut)
+    pcrSelectionOut = &pcrSelectionOutTmp;
+  if (!pcrValues)
+    pcrValues = &pcrValuesTmp;
+  if (!authResponse)
+    authResponse = &authResponseTmp;
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&in, pcrSelectionIn);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (tag, TPM_CC_PCR_Read, &responseCode, &in,
+                                 &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  /* Unmarshal*/
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_buffer_unpack_u32 (&out, &parameterSize);
+  grub_tpm2_buffer_unpack_u32 (&out, pcrUpdateCounter);
+  grub_tpm2_mu_TPML_PCR_SELECTION_Unmarshal (&out, pcrSelectionOut);
+  grub_tpm2_mu_TPML_DIGEST_Unmarshal (&out, pcrValues);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse);
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_PolicyGetDigest (TPMI_SH_POLICY policySession,
+                      const TPMS_AUTH_COMMAND *authCommand,
+                      TPM2B_DIGEST *policyDigest,
+                      TPMS_AUTH_RESPONSE *authResponse)
+{
+  TPM_RC rc;
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  TPMS_AUTH_RESPONSE authResponseTmp;
+  TPM2B_DIGEST policyDigestTmp;
+  TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS;
+  TPM_RC responseCode;
+  grub_uint32_t parameterSize;
+
+  if (!authResponse)
+    authResponse = &authResponseTmp;
+  if (!policyDigest)
+    policyDigest = &policyDigestTmp;
+
+  grub_memset (authResponse, 0, sizeof (*authResponse));
+  grub_memset (policyDigest, 0, sizeof (*policyDigest));
+
+  /* Submit */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_buffer_pack_u32 (&in, policySession);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (tag, TPM_CC_PolicyGetDigest, &responseCode,
+                                 &in, &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  /* Unmarshal*/
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_buffer_unpack_u32 (&out, &parameterSize);
+  grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)policyDigest);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse);
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_Create (TPMI_DH_OBJECT parentHandle,
+             const TPMS_AUTH_COMMAND *authCommand,
+             TPM2B_SENSITIVE_CREATE *inSensitive,
+             TPM2B_PUBLIC *inPublic,
+             TPM2B_DATA *outsideInfo,
+             TPML_PCR_SELECTION *creationPCR,
+             TPM2B_PRIVATE *outPrivate,
+             TPM2B_PUBLIC *outPublic,
+             TPM2B_CREATION_DATA *creationData,
+             TPM2B_DIGEST *creationHash,
+             TPMT_TK_CREATION *creationTicket,
+             TPMS_AUTH_RESPONSE *authResponse)
+{
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  TPM2B_PUBLIC outPublicTmp;
+  TPM2B_PRIVATE outPrivateTmp;
+  TPM2B_CREATION_DATA creationDataTmp;
+  TPM2B_DIGEST creationHashTmp;
+  TPMT_TK_CREATION creationTicketTmp;
+  TPMS_AUTH_RESPONSE authResponseTmp;
+  TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS:TPM_ST_NO_SESSIONS;
+  TPM_RC responseCode;
+  TPM_RC rc;
+  grub_uint32_t parameterSize;
+
+  if (!outPrivate)
+    outPrivate = &outPrivateTmp;
+  if (!outPublic)
+    outPublic = &outPublicTmp;
+  if (!creationData)
+    creationData = &creationDataTmp;
+  if (!creationHash)
+    creationHash = &creationHashTmp;
+  if (!creationTicket)
+    creationTicket = &creationTicketTmp;
+  if (!authResponse)
+    authResponse = &authResponseTmp;
+
+  grub_memset (outPrivate, 0, sizeof (*outPrivate));
+  grub_memset (outPublic, 0, sizeof (*outPublic));
+  grub_memset (creationData, 0, sizeof (*creationData));
+  grub_memset (creationHash, 0, sizeof (*creationHash));
+  grub_memset (creationTicket, 0, sizeof (*creationTicket));
+  grub_memset (authResponse, 0, sizeof (*authResponse));
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_buffer_pack_u32 (&in, parentHandle);
+  if (authCommand)
+    grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand);
+  grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (&in, inSensitive);
+  grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&in, inPublic);
+  grub_tpm2_mu_TPM2B_Marshal (&in, outsideInfo->size, outsideInfo->buffer);
+  grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (&in, creationPCR);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (tag, TPM_CC_Create, &responseCode, &in,
+                                 &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  /* Unmarshal*/
+  if (tag == TPM_ST_SESSIONS)
+   grub_tpm2_buffer_unpack_u32 (&out, &parameterSize);
+  grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)outPrivate);
+  grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&out, outPublic);
+  grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)creationData);
+  grub_tpm2_mu_TPM2B_Unmarshal (&out, (TPM2B*)creationHash);
+  grub_tpm2_mu_TPMT_TK_CREATION_Unmarshal (&out, creationTicket);
+  if (tag == TPM_ST_SESSIONS)
+    grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal(&out, authResponse);
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
+
+TPM_RC
+TPM2_EvictControl (TPMI_RH_PROVISION auth,
+                   TPMI_DH_OBJECT objectHandle,
+                   TPMI_DH_PERSISTENT persistentHandle,
+                   const TPMS_AUTH_COMMAND *authCommand,
+                   TPMS_AUTH_RESPONSE *authResponse)
+{
+  struct grub_tpm2_buffer in;
+  struct grub_tpm2_buffer out;
+  TPMS_AUTH_RESPONSE authResponseTmp;
+  TPMI_ST_COMMAND_TAG tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS;
+  TPM_RC responseCode;
+  TPM_RC rc;
+  grub_uint32_t parameterSize;
+
+  if (!authResponse)
+    authResponse = &authResponseTmp;
+
+  grub_memset (authResponse, 0, sizeof (*authResponse));
+
+  /* Marshal */
+  grub_tpm2_buffer_init (&in);
+  grub_tpm2_buffer_pack_u32 (&in, auth);
+  grub_tpm2_buffer_pack_u32 (&in, objectHandle);
+  if (authCommand)
+    grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (&in, authCommand);
+  grub_tpm2_buffer_pack_u32 (&in, persistentHandle);
+  if (in.error)
+    return TPM_RC_FAILURE;
+
+  /* Submit */
+  grub_tpm2_buffer_init (&out);
+  rc = grub_tpm2_submit_command (tag, TPM_CC_EvictControl, &responseCode, &in,
+                                 &out);
+  if (rc != TPM_RC_SUCCESS)
+    return rc;
+  if (responseCode != TPM_RC_SUCCESS)
+    return responseCode;
+
+  /* Unmarshal*/
+  if (tag == TPM_ST_SESSIONS)
+    {
+      grub_tpm2_buffer_unpack_u32 (&out, &parameterSize);
+      grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal(&out, authResponse);
+    }
+  if (out.error)
+    return TPM_RC_FAILURE;
+
+  return TPM_RC_SUCCESS;
+}
diff --git a/include/grub/tpm2/buffer.h b/include/grub/tpm2/buffer.h
new file mode 100644
index 0000000..ad05393
--- /dev/null
+++ b/include/grub/tpm2/buffer.h
@@ -0,0 +1,65 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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_BUFFER_HEADER
+#define GRUB_TPM2_BUFFER_HEADER 1
+
+#include <grub/types.h>
+
+#define GRUB_TPM2_BUFFER_CAPACITY 4096
+
+struct grub_tpm2_buffer
+{
+  grub_uint8_t data[GRUB_TPM2_BUFFER_CAPACITY];
+  grub_size_t size;
+  grub_size_t offset;
+  grub_size_t cap;
+  int error;
+};
+typedef struct grub_tpm2_buffer *grub_tpm2_buffer_t;
+
+void
+grub_tpm2_buffer_init (grub_tpm2_buffer_t buffer);
+
+void
+grub_tpm2_buffer_pack (grub_tpm2_buffer_t buffer, const void* data,
+                       grub_size_t size);
+
+void
+grub_tpm2_buffer_pack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t value);
+
+void
+grub_tpm2_buffer_pack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t value);
+
+void
+grub_tpm2_buffer_pack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t value);
+
+void
+grub_tpm2_buffer_unpack (grub_tpm2_buffer_t buffer, void* data,
+                         grub_size_t size);
+
+void
+grub_tpm2_buffer_unpack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t* value);
+
+void
+grub_tpm2_buffer_unpack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t* value);
+
+void
+grub_tpm2_buffer_unpack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t* value);
+
+#endif /* ! GRUB_TPM2_BUFFER_HEADER */
diff --git a/include/grub/tpm2/internal/functions.h b/include/grub/tpm2/internal/functions.h
new file mode 100644
index 0000000..a1c71fa
--- /dev/null
+++ b/include/grub/tpm2/internal/functions.h
@@ -0,0 +1,117 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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_FUNCTIONS_HEADER
+#define GRUB_TPM2_INTERNAL_FUNCTIONS_HEADER 1
+
+#include <grub/tpm2/internal/structs.h>
+
+TPM_RC
+TPM2_CreatePrimary (TPMI_RH_HIERARCHY primaryHandle,
+                    const TPMS_AUTH_COMMAND *authCommand,
+                    TPM2B_SENSITIVE_CREATE *inSensitive,
+                    TPM2B_PUBLIC *inPublic,
+                    TPM2B_DATA *outsideInfo,
+                    TPML_PCR_SELECTION *creationPCR,
+                    TPM_HANDLE *objectHandle,
+                    TPM2B_PUBLIC *outPublic,
+                    TPM2B_CREATION_DATA *creationData,
+                    TPM2B_DIGEST *creationHash,
+                    TPMT_TK_CREATION *creationTicket,
+                    TPM2B_NAME *name,
+                    TPMS_AUTH_RESPONSE *authResponse);
+
+TPM_RC
+TPM2_StartAuthSession (TPMI_DH_OBJECT tpmKey,
+                       TPMI_DH_ENTITY bind,
+                       const TPMS_AUTH_COMMAND *authCommand,
+                       TPM2B_NONCE *nonceCaller,
+                       TPM2B_ENCRYPTED_SECRET *encryptedSalt,
+                       TPM_SE sessionType,
+                       TPMT_SYM_DEF *symmetric,
+                       TPMI_ALG_HASH authHash,
+                       TPMI_SH_AUTH_SESSION *sessionHandle,
+                       TPM2B_NONCE *nonceTpm,
+                       TPMS_AUTH_RESPONSE *authResponse);
+
+TPM_RC
+TPM2_PolicyPCR (TPMI_SH_POLICY policySession,
+                const TPMS_AUTH_COMMAND *authCommand,
+                TPM2B_DIGEST *pcrDigest,
+                TPML_PCR_SELECTION *pcrs,
+                TPMS_AUTH_RESPONSE *authResponse);
+
+TPM_RC
+TPM2_ReadPublic (TPMI_DH_OBJECT objectHandle,
+                 const TPMS_AUTH_COMMAND* authCommand,
+                 TPM2B_PUBLIC *outPublic);
+
+TPM_RC
+TPM2_Load (TPMI_DH_OBJECT parent_handle,
+           TPMS_AUTH_COMMAND const *authCommand,
+           TPM2B_PRIVATE *inPrivate,
+           TPM2B_PUBLIC *inPublic,
+           TPM_HANDLE *objectHandle,
+           TPM2B_NAME *name,
+           TPMS_AUTH_RESPONSE *authResponse);
+
+TPM_RC
+TPM2_Unseal (TPMI_DH_OBJECT item_handle,
+             const TPMS_AUTH_COMMAND *authCommand,
+             TPM2B_SENSITIVE_DATA *outData,
+             TPMS_AUTH_RESPONSE *authResponse);
+
+TPM_RC
+TPM2_FlushContext (TPMI_DH_CONTEXT handle);
+
+TPM_RC
+TPM2_PCR_Read (const TPMS_AUTH_COMMAND *authCommand,
+               TPML_PCR_SELECTION  *pcrSelectionIn,
+               grub_uint32_t *pcrUpdateCounter,
+               TPML_PCR_SELECTION *pcrSelectionOut,
+               TPML_DIGEST *pcrValues,
+               TPMS_AUTH_RESPONSE *authResponse);
+
+TPM_RC
+TPM2_PolicyGetDigest (TPMI_SH_POLICY policySession,
+                      const TPMS_AUTH_COMMAND *authCommand,
+                      TPM2B_DIGEST *policyDigest,
+                      TPMS_AUTH_RESPONSE *authResponse);
+
+TPM_RC
+TPM2_Create (TPMI_DH_OBJECT parentHandle,
+             const TPMS_AUTH_COMMAND *authCommand,
+             TPM2B_SENSITIVE_CREATE *inSensitive,
+             TPM2B_PUBLIC *inPublic,
+             TPM2B_DATA *outsideInfo,
+             TPML_PCR_SELECTION *creationPCR,
+             TPM2B_PRIVATE *outPrivate,
+             TPM2B_PUBLIC *outPublic,
+             TPM2B_CREATION_DATA *creationData,
+             TPM2B_DIGEST *creationHash,
+             TPMT_TK_CREATION *creationTicket,
+             TPMS_AUTH_RESPONSE *authResponse);
+
+TPM_RC
+TPM2_EvictControl (TPMI_RH_PROVISION auth,
+                   TPMI_DH_OBJECT objectHandle,
+                   TPMI_DH_PERSISTENT persistentHandle,
+                   const TPMS_AUTH_COMMAND *authCommand,
+                   TPMS_AUTH_RESPONSE *authResponse);
+
+#endif /* ! GRUB_TPM2_INTERNAL_FUNCTIONS_HEADER */
diff --git a/include/grub/tpm2/internal/structs.h b/include/grub/tpm2/internal/structs.h
new file mode 100644
index 0000000..75bf99e
--- /dev/null
+++ b/include/grub/tpm2/internal/structs.h
@@ -0,0 +1,675 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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_STRUCTS_HEADER
+#define GRUB_TPM2_INTERNAL_STRUCTS_HEADER 1
+
+#include <grub/tpm2/internal/types.h>
+
+/* TPMS_TAGGED_PROPERTY Structure */
+struct TPMS_TAGGED_PROPERTY
+{
+  TPM_PT property;
+  grub_uint32_t value;
+};
+typedef struct TPMS_TAGGED_PROPERTY TPMS_TAGGED_PROPERTY;
+
+/* TPML_TAGGED_TPM_PROPERTY Structure */
+struct TPML_TAGGED_TPM_PROPERTY
+{
+  grub_uint32_t count;
+  TPMS_TAGGED_PROPERTY tpmProperty[TPM_MAX_TPM_PROPERTIES];
+};
+typedef struct TPML_TAGGED_TPM_PROPERTY TPML_TAGGED_TPM_PROPERTY;
+
+/* TPMU_CAPABILITIES Structure */
+union TPMU_CAPABILITIES
+{
+  TPML_TAGGED_TPM_PROPERTY tpmProperties;
+};
+typedef union TPMU_CAPABILITIES TPMU_CAPABILITIES;
+
+/* TPMS_CAPABILITY_DATA Structure */
+struct TPMS_CAPABILITY_DATA
+{
+  TPM_CAP capability;
+  TPMU_CAPABILITIES data;
+};
+typedef struct TPMS_CAPABILITY_DATA TPMS_CAPABILITY_DATA;
+
+/* TPMS_PCR_SELECT Structure */
+struct TPMS_PCR_SELECT
+{
+  grub_uint8_t sizeOfSelect;
+  grub_uint8_t pcrSelect[TPM_PCR_SELECT_MAX];
+};
+typedef struct TPMS_PCR_SELECT TPMS_PCR_SELECT;
+
+/* TPMS_PCR_SELECTION Structure */
+struct TPMS_PCR_SELECTION
+{
+  TPMI_ALG_HASH hash;
+  grub_uint8_t sizeOfSelect;
+  grub_uint8_t pcrSelect[TPM_PCR_SELECT_MAX];
+};
+typedef struct TPMS_PCR_SELECTION TPMS_PCR_SELECTION;
+
+static inline void TPMS_PCR_SELECTION_SelectPCR(TPMS_PCR_SELECTION* self, grub_uint32_t n)
+{
+  self->pcrSelect[(n / 8)] |= (1 << (n % 8));
+}
+
+/* TPML_PCR_SELECTION Structure */
+struct TPML_PCR_SELECTION
+{
+  grub_uint32_t count;
+  TPMS_PCR_SELECTION pcrSelections[TPM_NUM_PCR_BANKS];
+};
+typedef struct TPML_PCR_SELECTION TPML_PCR_SELECTION;
+
+/* TPMU_HA Structure */
+union TPMU_HA
+{
+  grub_uint8_t sha1[TPM_SHA1_DIGEST_SIZE];
+  grub_uint8_t sha256[TPM_SHA256_DIGEST_SIZE];
+  grub_uint8_t sha384[TPM_SHA384_DIGEST_SIZE];
+  grub_uint8_t sha512[TPM_SHA512_DIGEST_SIZE];
+  grub_uint8_t sm3_256[TPM_SM3_256_DIGEST_SIZE];
+};
+typedef union TPMU_HA TPMU_HA;
+
+/* TPM2B Structure */
+struct TPM2B
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[1];
+};
+typedef struct TPM2B TPM2B;
+
+/* TPM2B_DIGEST Structure */
+struct TPM2B_DIGEST
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[sizeof(TPMU_HA)];
+};
+typedef struct TPM2B_DIGEST TPM2B_DIGEST;
+
+/* TPML_DIGEST Structure */
+struct TPML_DIGEST
+{
+  grub_uint32_t count;
+  TPM2B_DIGEST digests[8];
+};
+typedef struct TPML_DIGEST TPML_DIGEST;
+
+/* TPM2B_NONCE Type */
+typedef TPM2B_DIGEST TPM2B_NONCE;
+
+/* TPMA_SESSION Structure */
+struct TPMA_SESSION
+{
+  unsigned int continueSession:1;
+  unsigned int auditExclusive:1;
+  unsigned int auditReset:1;
+  unsigned int reserved1:2;
+  unsigned int decrypt:1;
+  unsigned int encrypt:1;
+  unsigned int audit:1;
+  unsigned int reserved:24;
+};
+typedef struct TPMA_SESSION TPMA_SESSION;
+
+/* TPM2B_AUTH Type */
+typedef TPM2B_DIGEST TPM2B_AUTH;
+
+/* TPMS_AUTH_COMMAND Structure */
+struct TPMS_AUTH_COMMAND
+{
+  TPMI_SH_AUTH_SESSION sessionHandle;
+  TPM2B_NONCE nonce;
+  TPMA_SESSION sessionAttributes;
+  TPM2B_AUTH hmac;
+};
+typedef struct TPMS_AUTH_COMMAND TPMS_AUTH_COMMAND;
+
+/* TPMS_AUTH_RESPONSE Structure */
+struct TPMS_AUTH_RESPONSE
+{
+  TPM2B_NONCE nonce;
+  TPMA_SESSION sessionAttributes;
+  TPM2B_AUTH hmac;
+};
+typedef struct TPMS_AUTH_RESPONSE TPMS_AUTH_RESPONSE;
+
+/* TPM2B_SENSITIVE_DATA Structure */
+struct TPM2B_SENSITIVE_DATA
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[TPM_MAX_SYM_DATA];
+};
+typedef struct TPM2B_SENSITIVE_DATA TPM2B_SENSITIVE_DATA;
+
+/* TPMS_SENSITIVE_CREATE Structure */
+struct TPMS_SENSITIVE_CREATE
+{
+  TPM2B_AUTH userAuth;
+  TPM2B_SENSITIVE_DATA data;
+};
+typedef struct TPMS_SENSITIVE_CREATE TPMS_SENSITIVE_CREATE;
+
+/* TPM2B_SENSITIVE_CREATE Structure */
+struct TPM2B_SENSITIVE_CREATE
+{
+  grub_uint16_t size;
+  TPMS_SENSITIVE_CREATE sensitive;
+};
+typedef struct TPM2B_SENSITIVE_CREATE TPM2B_SENSITIVE_CREATE;
+
+/* TPMA_OBJECT Structure */
+struct TPMA_OBJECT
+{
+  unsigned int reserved1:1;
+  unsigned int fixedTPM:1;
+  unsigned int stClear:1;
+  unsigned int reserved2:1;
+  unsigned int fixedParent:1;
+  unsigned int sensitiveDataOrigin:1;
+  unsigned int userWithAuth:1;
+  unsigned int adminWithPolicy:1;
+  unsigned int reserved3:2;
+  unsigned int noDA:1;
+  unsigned int encryptedDuplication:1;
+  unsigned int reserved4:4;
+  unsigned int restricted:1;
+  unsigned int decrypt:1;
+  unsigned int sign:1;
+  unsigned int reserved5:13;
+};
+typedef struct TPMA_OBJECT TPMA_OBJECT;
+
+/* TPMS_SCHEME_HASH Structure */
+struct TPMS_SCHEME_HASH
+{
+  TPMI_ALG_HASH hashAlg;
+};
+typedef struct TPMS_SCHEME_HASH TPMS_SCHEME_HASH;
+
+/* TPMS_SCHEME_HASH Types */
+typedef TPMS_SCHEME_HASH TPMS_KEY_SCHEME_ECDH;
+typedef TPMS_SCHEME_HASH TPMS_KEY_SCHEME_ECMQV;
+typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_RSASSA;
+typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_RSAPSS;
+typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_ECDSA;
+typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_ECDAA;
+typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_SM2;
+typedef TPMS_SCHEME_HASH TPMS_SIG_SCHEME_ECSCHNORR;
+typedef TPMS_SCHEME_HASH TPMS_ENC_SCHEME_RSAES;
+typedef TPMS_SCHEME_HASH TPMS_ENC_SCHEME_OAEP;
+typedef TPMS_SCHEME_HASH TPMS_SCHEME_KDF2;
+typedef TPMS_SCHEME_HASH TPMS_SCHEME_MGF1;
+typedef TPMS_SCHEME_HASH TPMS_SCHEME_KDF1_SP800_56A;
+typedef TPMS_SCHEME_HASH TPMS_SCHEME_KDF2;
+typedef TPMS_SCHEME_HASH TPMS_SCHEME_KDF1_SP800_108;
+
+/* TPMS_SCHEME_HMAC Type */
+typedef TPMS_SCHEME_HASH TPMS_SCHEME_HMAC;
+
+/* TPMS_SCHEME_XOR Structure */
+struct TPMS_SCHEME_XOR
+{
+  TPMI_ALG_HASH hashAlg;
+  TPMI_ALG_KDF kdf;
+};
+typedef struct TPMS_SCHEME_XOR TPMS_SCHEME_XOR;
+
+/* TPMU_SCHEME_KEYEDHASH Union */
+union TPMU_SCHEME_KEYEDHASH
+{
+  TPMS_SCHEME_HMAC hmac;
+  TPMS_SCHEME_XOR exclusiveOr;
+};
+typedef union TPMU_SCHEME_KEYEDHASH TPMU_SCHEME_KEYEDHASH;
+
+/* TPMT_KEYEDHASH_SCHEME Structure */
+struct TPMT_KEYEDHASH_SCHEME
+{
+  TPMI_ALG_KEYEDHASH_SCHEME scheme;
+  TPMU_SCHEME_KEYEDHASH details;
+};
+typedef struct TPMT_KEYEDHASH_SCHEME TPMT_KEYEDHASH_SCHEME;
+
+/* TPMS_KEYEDHASH_PARMS Structure */
+struct TPMS_KEYEDHASH_PARMS
+{
+  TPMT_KEYEDHASH_SCHEME scheme;
+};
+typedef struct TPMS_KEYEDHASH_PARMS TPMS_KEYEDHASH_PARMS;
+
+/* TPMU_SYM_KEY_BITS Union */
+union TPMU_SYM_KEY_BITS
+{
+  TPM_KEY_BITS aes;
+  TPM_KEY_BITS exclusiveOr;
+  TPM_KEY_BITS sm4;
+  TPM_KEY_BITS camellia;
+};
+typedef union TPMU_SYM_KEY_BITS TPMU_SYM_KEY_BITS;
+
+/* TPMU_SYM_MODE Union */
+union TPMU_SYM_MODE
+{
+  TPMI_ALG_SYM_MODE aes;
+  TPMI_ALG_SYM_MODE sm4;
+  TPMI_ALG_SYM_MODE camellia;
+  TPMI_ALG_SYM_MODE sym;
+};
+typedef union TPMU_SYM_MODE TPMU_SYM_MODE;
+
+/* TPMT_SYM_DEF_OBJECT Structure */
+struct TPMT_SYM_DEF_OBJECT
+{
+  TPMI_ALG_SYM_OBJECT algorithm;
+  TPMU_SYM_KEY_BITS keyBits;
+  TPMU_SYM_MODE mode;
+};
+typedef struct TPMT_SYM_DEF_OBJECT TPMT_SYM_DEF_OBJECT;
+
+/* TPMS_SYMCIPHER_PARMS Structure */
+struct TPMS_SYMCIPHER_PARMS
+{
+  TPMT_SYM_DEF_OBJECT sym;
+};
+typedef struct TPMS_SYMCIPHER_PARMS TPMS_SYMCIPHER_PARMS;
+
+/* TPMU_ASYM_SCHEME Union */
+union TPMU_ASYM_SCHEME
+{
+  TPMS_KEY_SCHEME_ECDH ecdh;
+  TPMS_KEY_SCHEME_ECMQV ecmqv;
+  TPMS_SIG_SCHEME_RSASSA rsassa;
+  TPMS_SIG_SCHEME_RSAPSS rsapss;
+  TPMS_SIG_SCHEME_ECDSA ecdsa;
+  TPMS_SIG_SCHEME_ECDAA ecdaa;
+  TPMS_SIG_SCHEME_SM2 sm2;
+  TPMS_SIG_SCHEME_ECSCHNORR ecschnorr;
+  TPMS_ENC_SCHEME_RSAES rsaes;
+  TPMS_ENC_SCHEME_OAEP oaep;
+  TPMS_SCHEME_HASH anySig;
+  unsigned char padding[4];
+};
+typedef union TPMU_ASYM_SCHEME TPMU_ASYM_SCHEME;
+
+/* TPMT_RSA_SCHEME Structure */
+struct TPMT_RSA_SCHEME
+{
+  TPMI_ALG_RSA_SCHEME scheme;
+  TPMU_ASYM_SCHEME details;
+};
+typedef struct TPMT_RSA_SCHEME TPMT_RSA_SCHEME;
+
+/* TPMS_RSA_PARMS Structure */
+struct TPMS_RSA_PARMS
+{
+  TPMT_SYM_DEF_OBJECT symmetric;
+  TPMT_RSA_SCHEME scheme;
+  TPM_KEY_BITS keyBits;
+  grub_uint32_t exponent;
+};
+typedef struct TPMS_RSA_PARMS TPMS_RSA_PARMS;
+
+/* TPMT_ECC_SCHEME Structure */
+struct TPMT_ECC_SCHEME
+{
+  TPMI_ALG_ECC_SCHEME scheme;
+  TPMU_ASYM_SCHEME details;
+};
+typedef struct TPMT_ECC_SCHEME TPMT_ECC_SCHEME;
+
+/* TPMU_KDF_SCHEME Union */
+union TPMU_KDF_SCHEME
+{
+  TPMS_SCHEME_MGF1 mgf1;
+  TPMS_SCHEME_KDF1_SP800_56A kdf1_sp800_56a;
+  TPMS_SCHEME_KDF2 kdf2;
+  TPMS_SCHEME_KDF1_SP800_108 kdf1_sp800_108;
+};
+typedef union TPMU_KDF_SCHEME TPMU_KDF_SCHEME;
+
+/* TPMT_KDF_SCHEME Structure */
+struct TPMT_KDF_SCHEME
+{
+  TPMI_ALG_KDF scheme;
+  TPMU_KDF_SCHEME details;
+};
+typedef struct TPMT_KDF_SCHEME TPMT_KDF_SCHEME;
+
+/* TPMS_ECC_PARMS Structure */
+struct TPMS_ECC_PARMS
+{
+  TPMT_SYM_DEF_OBJECT symmetric;
+  TPMT_ECC_SCHEME scheme;
+  TPMI_ECC_CURVE curveID;
+  TPMT_KDF_SCHEME kdf;
+};
+typedef struct TPMS_ECC_PARMS TPMS_ECC_PARMS;
+
+/* TPMT_ASYM_SCHEME Structure */
+struct TPMT_ASYM_SCHEME
+{
+  TPMI_ALG_ASYM_SCHEME scheme;
+  TPMU_ASYM_SCHEME details;
+};
+typedef struct TPMT_ASYM_SCHEME TPMT_ASYM_SCHEME;
+
+/* TPMS_ASYM_PARMS Structure */
+struct TPMS_ASYM_PARMS
+{
+  TPMT_SYM_DEF_OBJECT symmetric;
+  TPMT_ASYM_SCHEME scheme;
+};
+typedef struct TPMS_ASYM_PARMS TPMS_ASYM_PARMS;
+
+/* TPMU_PUBLIC_PARMS Union */
+union TPMU_PUBLIC_PARMS
+{
+  TPMS_KEYEDHASH_PARMS keyedHashDetail;
+  TPMS_SYMCIPHER_PARMS symDetail;
+  TPMS_RSA_PARMS rsaDetail;
+  TPMS_ECC_PARMS eccDetail;
+  TPMS_ASYM_PARMS asymDetail;
+};
+typedef union TPMU_PUBLIC_PARMS TPMU_PUBLIC_PARMS;
+
+/* TPM2B_PUBLIC_KEY_RSA Structure */
+struct TPM2B_PUBLIC_KEY_RSA
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[TPM_MAX_RSA_KEY_BYTES];
+};
+typedef struct TPM2B_PUBLIC_KEY_RSA TPM2B_PUBLIC_KEY_RSA;
+
+/* TPM2B_ECC_PARAMETER Structure */
+struct TPM2B_ECC_PARAMETER
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[TPM_MAX_ECC_KEY_BYTES];
+};
+typedef struct TPM2B_ECC_PARAMETER TPM2B_ECC_PARAMETER;
+
+/* TPMS_ECC_POINT Structure */
+struct TPMS_ECC_POINT
+{
+  TPM2B_ECC_PARAMETER x;
+  TPM2B_ECC_PARAMETER y;
+};
+typedef struct TPMS_ECC_POINT TPMS_ECC_POINT;
+
+/* TPMU_ENCRYPTED_SECRET Union */
+union TPMU_ENCRYPTED_SECRET
+{
+  grub_uint8_t ecc[sizeof(TPMS_ECC_POINT)];
+  grub_uint8_t rsa[TPM_MAX_RSA_KEY_BYTES];
+  grub_uint8_t symmetric[sizeof(TPM2B_DIGEST)];
+  grub_uint8_t keyedHash[sizeof(TPM2B_DIGEST)];
+};
+typedef union TPMU_ENCRYPTED_SECRET TPMU_ENCRYPTED_SECRET;
+
+/* TPM2B_ENCRYPTED_SECRET Structure */
+struct TPM2B_ENCRYPTED_SECRET
+{
+  grub_uint16_t size;
+  grub_uint8_t secret[sizeof(TPMU_ENCRYPTED_SECRET)];
+};
+typedef struct TPM2B_ENCRYPTED_SECRET TPM2B_ENCRYPTED_SECRET;
+
+/* TPMU_PUBLIC_ID Union */
+union TPMU_PUBLIC_ID
+{
+  TPM2B_DIGEST keyedHash;
+  TPM2B_DIGEST sym;
+  TPM2B_PUBLIC_KEY_RSA rsa;
+  TPMS_ECC_POINT ecc;
+};
+typedef union TPMU_PUBLIC_ID TPMU_PUBLIC_ID;
+
+/* TPMT_PUBLIC Structure */
+struct TPMT_PUBLIC 
+{
+  TPMI_ALG_PUBLIC type;
+  TPMI_ALG_HASH nameAlg;
+  TPMA_OBJECT objectAttributes;
+  TPM2B_DIGEST authPolicy;
+  TPMU_PUBLIC_PARMS parameters;
+  TPMU_PUBLIC_ID unique;
+};
+typedef struct TPMT_PUBLIC TPMT_PUBLIC;
+
+/* TPM2B_PUBLIC Structure */
+struct TPM2B_PUBLIC
+{
+  grub_uint16_t size;
+  TPMT_PUBLIC publicArea;
+};
+typedef struct TPM2B_PUBLIC TPM2B_PUBLIC;
+
+/* TPMT_HA Structure */
+struct TPMT_HA
+{
+  TPMI_ALG_HASH hashAlg;
+  TPMU_HA digest;
+};
+typedef struct TPMT_HA TPMT_HA;
+
+/* TPM2B_DATA Structure */
+struct TPM2B_DATA
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[sizeof(TPMT_HA)];
+};
+typedef struct TPM2B_DATA TPM2B_DATA;
+
+/* TPMA_LOCALITY Structure */
+struct TPMA_LOCALITY
+{
+  unsigned char TPM_LOC_ZERO:1;
+  unsigned char TPM_LOC_ONE:1;
+  unsigned char TPM_LOC_TWO:1;
+  unsigned char TPM_LOC_THREE:1;
+  unsigned char TPM_LOC_FOUR:1;
+  unsigned char Extended:3;
+};
+typedef struct TPMA_LOCALITY TPMA_LOCALITY;
+
+/* TPMU_NAME Union */
+union TPMU_NAME
+{
+  TPMT_HA digest;
+  TPM_HANDLE handle;
+};
+typedef union TPMU_NAME TPMU_NAME;
+
+/* TPM2B_NAME Structure */
+struct TPM2B_NAME
+{
+  grub_uint16_t size;
+  grub_uint8_t name[sizeof(TPMU_NAME)];
+};
+typedef struct TPM2B_NAME TPM2B_NAME;
+
+/* TPMS_CREATION_DATA Structure */
+struct TPMS_CREATION_DATA
+{
+  TPML_PCR_SELECTION pcrSelect;
+  TPM2B_DIGEST pcrDigest;
+  TPMA_LOCALITY locality;
+  TPM_ALG_ID parentNameAlg;
+  TPM2B_NAME parentName;
+  TPM2B_NAME parentQualifiedName;
+  TPM2B_DATA outsideInfo;
+};
+typedef struct TPMS_CREATION_DATA TPMS_CREATION_DATA;
+
+/* TPM2B_CREATION_DATA Structure */
+struct TPM2B_CREATION_DATA
+{
+  grub_uint16_t size;
+  TPMS_CREATION_DATA creationData;
+};
+typedef struct TPM2B_CREATION_DATA TPM2B_CREATION_DATA;
+
+/* TPMT_SYM_DEF Structure */
+struct TPMT_SYM_DEF
+{
+  TPMI_ALG_SYM algorithm;
+  TPMU_SYM_KEY_BITS keyBits;
+  TPMU_SYM_MODE mode;
+};
+typedef struct TPMT_SYM_DEF TPMT_SYM_DEF;
+
+/* TPM2B_MAX_BUFFER Structure */
+struct TPM2B_MAX_BUFFER
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[TPM_MAX_DIGEST_BUFFER];
+};
+typedef struct TPM2B_MAX_BUFFER TPM2B_MAX_BUFFER;
+
+/* TPMT_TK_HASHCHECK Structure */
+struct TPMT_TK_HASHCHECK
+{
+  TPM_ST tag;
+  TPMI_RH_HIERARCHY hierarchy;
+  TPM2B_DIGEST digest;
+};
+typedef struct TPMT_TK_HASHCHECK TPMT_TK_HASHCHECK;
+
+/* TPM2B_SYM_KEY Structure */
+struct TPM2B_SYM_KEY
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[TPM_MAX_SYM_KEY_BYTES];
+};
+typedef struct TPM2B_SYM_KEY TPM2B_SYM_KEY;
+
+/* TPM2B_PRIVATE_KEY_RSA Structure */
+struct TPM2B_PRIVATE_KEY_RSA
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[TPM_MAX_RSA_KEY_BYTES/2];
+};
+typedef struct TPM2B_PRIVATE_KEY_RSA TPM2B_PRIVATE_KEY_RSA;
+
+/* TPM2B_PRIVATE_VENDOR_SPECIFIC Structure */
+struct TPM2B_PRIVATE_VENDOR_SPECIFIC
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[TPM_PRIVATE_VENDOR_SPECIFIC_BYTES];
+};
+typedef struct TPM2B_PRIVATE_VENDOR_SPECIFIC TPM2B_PRIVATE_VENDOR_SPECIFIC;
+
+/* TPM2B_PRIVATE_VENDOR_SPECIFIC Union */
+union TPMU_SENSITIVE_COMPOSITE
+{
+  TPM2B_PRIVATE_KEY_RSA rsa;
+  TPM2B_ECC_PARAMETER ecc;
+  TPM2B_SENSITIVE_DATA bits;
+  TPM2B_SYM_KEY sym;
+  TPM2B_PRIVATE_VENDOR_SPECIFIC any;
+};
+typedef union TPMU_SENSITIVE_COMPOSITE TPMU_SENSITIVE_COMPOSITE;
+
+/* TPMT_SENSITIVE Structure */
+struct TPMT_SENSITIVE
+{
+  TPMI_ALG_PUBLIC sensitiveType;
+  TPM2B_AUTH authValue;
+  TPM2B_DIGEST seedValue;
+  TPMU_SENSITIVE_COMPOSITE sensitive;
+};
+typedef struct TPMT_SENSITIVE TPMT_SENSITIVE;
+
+/* TPM2B_SENSITIVE Structure */
+struct TPM2B_SENSITIVE
+{
+  grub_uint16_t size;
+  TPMT_SENSITIVE sensitiveArea;
+};
+typedef struct TPM2B_SENSITIVE TPM2B_SENSITIVE;
+
+/* _PRIVATE Structure */
+struct _PRIVATE
+{
+  TPM2B_DIGEST integrityOuter;
+  TPM2B_DIGEST integrityInner;
+  TPM2B_SENSITIVE sensitive;
+};
+typedef struct _PRIVATE _PRIVATE;
+
+/* TPM2B_PRIVATE Structure */
+struct TPM2B_PRIVATE
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[sizeof(_PRIVATE)];
+};
+typedef struct TPM2B_PRIVATE TPM2B_PRIVATE;
+
+/* TPML_DIGEST_VALUES Structure */
+struct TPML_DIGEST_VALUES
+{
+  grub_uint16_t count;
+  TPMT_HA digests[TPM_NUM_PCR_BANKS];
+};
+typedef struct TPML_DIGEST_VALUES TPML_DIGEST_VALUES;
+
+/* TPM2B_MAX_NV_BUFFER Structure */
+struct TPM2B_MAX_NV_BUFFER
+{
+  grub_uint16_t size;
+  grub_uint8_t buffer[TPM_MAX_NV_BUFFER_SIZE];
+};
+typedef struct TPM2B_MAX_NV_BUFFER TPM2B_MAX_NV_BUFFER;
+
+/* TPMS_NV_PUBLIC Structure */
+struct TPMS_NV_PUBLIC
+{
+    TPMI_RH_NV_INDEX nvIndex;
+    TPMI_ALG_HASH nameAlg;
+    TPMA_NV attributes;
+    TPM2B_DIGEST authPolicy;
+    grub_uint16_t dataSize;
+};
+typedef struct TPMS_NV_PUBLIC TPMS_NV_PUBLIC;
+
+/* TPM2B_NV_PUBLIC Structure */
+struct TPM2B_NV_PUBLIC
+{
+    grub_uint16_t size;
+    TPMS_NV_PUBLIC nvPublic;
+};
+typedef struct TPM2B_NV_PUBLIC TPM2B_NV_PUBLIC;
+
+/* TPMT_TK_CREATION Structure */
+struct TPMT_TK_CREATION
+{
+    TPM_ST tag;
+    TPMI_RH_HIERARCHY hierarchy;
+    TPM2B_DIGEST digest;
+};
+typedef struct TPMT_TK_CREATION TPMT_TK_CREATION;
+
+#endif /* ! GRUB_TPM2_INTERNAL_STRUCTS_HEADER */
diff --git a/include/grub/tpm2/internal/types.h b/include/grub/tpm2/internal/types.h
new file mode 100644
index 0000000..9714f75
--- /dev/null
+++ b/include/grub/tpm2/internal/types.h
@@ -0,0 +1,372 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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_TYPES_HEADER
+#define GRUB_TPM2_INTERNAL_TYPES_HEADER 1
+
+#include <grub/types.h>
+
+/* TPM2_RC Constants */
+typedef grub_uint32_t TPM_RC;
+
+#define TPM_RC_1                 ((TPM_RC) 0x100)
+#define TPM_RC_2                 ((TPM_RC) 0x200)
+#define TPM_RC_3                 ((TPM_RC) 0x300)
+#define TPM_RC_4                 ((TPM_RC) 0x400)
+#define TPM_RC_5                 ((TPM_RC) 0x500)
+#define TPM_RC_6                 ((TPM_RC) 0x600)
+#define TPM_RC_7                 ((TPM_RC) 0x700)
+#define TPM_RC_8                 ((TPM_RC) 0x800)
+#define TPM_RC_9                 ((TPM_RC) 0x900)
+#define TPM_RC_A                 ((TPM_RC) 0xA00)
+#define TPM_RC_ASYMMETRIC        ((TPM_RC) 0x081)
+#define TPM_RC_ATTRIBUTES        ((TPM_RC) 0x082)
+#define TPM_RC_AUTH_CONTEXT      ((TPM_RC) 0x145)
+#define TPM_RC_AUTH_FAIL         ((TPM_RC) 0x08E)
+#define TPM_RC_AUTH_MISSING      ((TPM_RC) 0x125)
+#define TPM_RC_AUTHSIZE          ((TPM_RC) 0x144)
+#define TPM_RC_AUTH_TYPE         ((TPM_RC) 0x124)
+#define TPM_RC_AUTH_UNAVAILABLE  ((TPM_RC) 0x12F)
+#define TPM_RC_B                 ((TPM_RC) 0xB00)
+#define TPM_RC_BAD_AUTH          ((TPM_RC) 0x0A2)
+#define TPM_RC_BAD_CONTEXT       ((TPM_RC) 0x150)
+#define TPM_RC_BAD_TAG           ((TPM_RC) 0x01E)
+#define TPM_RC_BINDING           ((TPM_RC) 0x0A5)
+#define TPM_RC_C                 ((TPM_RC) 0xC00)
+#define TPM_RC_CANCELED          ((TPM_RC) 0x909)
+#define TPM_RC_COMMAND_CODE      ((TPM_RC) 0x143)
+#define TPM_RC_COMMAND_SIZE      ((TPM_RC) 0x142)
+#define TPM_RC_CONTEXT_GAP       ((TPM_RC) 0x901)
+#define TPM_RC_CPHASH            ((TPM_RC) 0x151)
+#define TPM_RC_CURVE             ((TPM_RC) 0x0A6)
+#define TPM_RC_D                 ((TPM_RC) 0xD00)
+#define TPM_RC_DISABLED          ((TPM_RC) 0x120)
+#define TPM_RC_E                 ((TPM_RC) 0xE00)
+#define TPM_RC_ECC_POINT         ((TPM_RC) 0x0A7)
+#define TPM_RC_EXCLUSIVE         ((TPM_RC) 0x121)
+#define TPM_RC_EXPIRED           ((TPM_RC) 0x0A3)
+#define TPM_RC_F                 ((TPM_RC) 0xF00)
+#define TPM_RC_FAILURE           ((TPM_RC) 0x101)
+#define TPM_RC_H                 ((TPM_RC) 0x000)
+#define TPM_RC_HANDLE            ((TPM_RC) 0x08B)
+#define TPM_RC_HASH              ((TPM_RC) 0x083)
+#define TPM_RC_HIERARCHY         ((TPM_RC) 0x085)
+#define TPM_RC_HMAC              ((TPM_RC) 0x119)
+#define TPM_RC_INITIALIZE        ((TPM_RC) 0x100)
+#define TPM_RC_INSUFFICIENT      ((TPM_RC) 0x09A)
+#define TPM_RC_INTEGRITY         ((TPM_RC) 0x09F)
+#define TPM_RC_KDF               ((TPM_RC) 0x08C)
+#define TPM_RC_KEY               ((TPM_RC) 0x09C)
+#define TPM_RC_KEY_SIZE          ((TPM_RC) 0x087)
+#define TPM_RC_LOCALITY          ((TPM_RC) 0x907)
+#define TPM_RC_LOCKOUT           ((TPM_RC) 0x921)
+#define TPM_RC_MEMORY            ((TPM_RC) 0x904)
+#define TPM_RC_MGF               ((TPM_RC) 0x088)
+#define TPM_RC_MODE              ((TPM_RC) 0x089)
+#define TPM_RC_NEEDS_TEST        ((TPM_RC) 0x153)
+#define TPM_RC_N_MASK            ((TPM_RC) 0xF00)
+#define TPM_RC_NONCE             ((TPM_RC) 0x08F)
+#define TPM_RC_NO_RESULT         ((TPM_RC) 0x154)
+#define TPM_RC_NOT_USED          ((TPM_RC) 0x97F)
+#define TPM_RC_NV_AUTHORIZATION  ((TPM_RC) 0x149)
+#define TPM_RC_NV_DEFINED        ((TPM_RC) 0x14C)
+#define TPM_RC_NV_LOCKED         ((TPM_RC) 0x148)
+#define TPM_RC_NV_RANGE          ((TPM_RC) 0x146)
+#define TPM_RC_NV_RATE           ((TPM_RC) 0x920)
+#define TPM_RC_NV_SIZE           ((TPM_RC) 0x147)
+#define TPM_RC_NV_SPACE          ((TPM_RC) 0x14B)
+#define TPM_RC_NV_UNAVAILABLE    ((TPM_RC) 0x923)
+#define TPM_RC_NV_UNINITIALIZED  ((TPM_RC) 0x14A)
+#define TPM_RC_OBJECT_HANDLES    ((TPM_RC) 0x906)
+#define TPM_RC_OBJECT_MEMORY     ((TPM_RC) 0x902)
+#define TPM_RC_P                 ((TPM_RC) 0x040)
+#define TPM_RC_PARENT            ((TPM_RC) 0x152)
+#define TPM_RC_PCR               ((TPM_RC) 0x127)
+#define TPM_RC_PCR_CHANGED       ((TPM_RC) 0x128)
+#define TPM_RC_POLICY            ((TPM_RC) 0x126)
+#define TPM_RC_POLICY_CC         ((TPM_RC) 0x0A4)
+#define TPM_RC_POLICY_FAIL       ((TPM_RC) 0x09D)
+#define TPM_RC_PP                ((TPM_RC) 0x090)
+#define TPM_RC_PRIVATE           ((TPM_RC) 0x10B)
+#define TPM_RC_RANGE             ((TPM_RC) 0x08D)
+#define TPM_RC_REBOOT            ((TPM_RC) 0x130)
+#define TPM_RC_REFERENCE_H0      ((TPM_RC) 0x910)
+#define TPM_RC_REFERENCE_H1      ((TPM_RC) 0x911)
+#define TPM_RC_REFERENCE_H2      ((TPM_RC) 0x912)
+#define TPM_RC_REFERENCE_H3      ((TPM_RC) 0x913)
+#define TPM_RC_REFERENCE_H4      ((TPM_RC) 0x914)
+#define TPM_RC_REFERENCE_H5      ((TPM_RC) 0x915)
+#define TPM_RC_REFERENCE_H6      ((TPM_RC) 0x916)
+#define TPM_RC_REFERENCE_S0      ((TPM_RC) 0x918)
+#define TPM_RC_REFERENCE_S1      ((TPM_RC) 0x919)
+#define TPM_RC_REFERENCE_S2      ((TPM_RC) 0x91A)
+#define TPM_RC_REFERENCE_S3      ((TPM_RC) 0x91B)
+#define TPM_RC_REFERENCE_S4      ((TPM_RC) 0x91C)
+#define TPM_RC_REFERENCE_S5      ((TPM_RC) 0x91D)
+#define TPM_RC_REFERENCE_S6      ((TPM_RC) 0x91E)
+#define TPM_RC_RESERVED_BITS     ((TPM_RC) 0x0A1)
+#define TPM_RC_RETRY             ((TPM_RC) 0x922)
+#define TPM_RC_S                 ((TPM_RC) 0x800)
+#define TPM_RC_SCHEME            ((TPM_RC) 0x092)
+#define TPM_RC_SELECTOR          ((TPM_RC) 0x098)
+#define TPM_RC_SENSITIVE         ((TPM_RC) 0x155)
+#define TPM_RC_SEQUENCE          ((TPM_RC) 0x103)
+#define TPM_RC_SESSION_HANDLES   ((TPM_RC) 0x905)
+#define TPM_RC_SESSION_MEMORY    ((TPM_RC) 0x903)
+#define TPM_RC_SIGNATURE         ((TPM_RC) 0x09B)
+#define TPM_RC_SIZE              ((TPM_RC) 0x095)
+#define TPM_RC_SUCCESS           ((TPM_RC) 0x000)
+#define TPM_RC_SYMMETRIC         ((TPM_RC) 0x096)
+#define TPM_RC_TAG               ((TPM_RC) 0x097)
+#define TPM_RC_TESTING           ((TPM_RC) 0x90A)
+#define TPM_RC_TICKET            ((TPM_RC) 0x0A0)
+#define TPM_RC_TOO_MANY_CONTEXTS ((TPM_RC) 0x12E)
+#define TPM_RC_TYPE              ((TPM_RC) 0x08A)
+#define TPM_RC_UNBALANCED        ((TPM_RC) 0x131)
+#define TPM_RC_UPGRADE           ((TPM_RC) 0x12D)
+#define TPM_RC_VALUE             ((TPM_RC) 0x084)
+#define TPM_RC_YIELDED           ((TPM_RC) 0x908)
+
+/* TPMA_NV Constants */
+typedef grub_uint32_t TPMA_NV;
+
+#define TPMA_NV_PPWRITE        ((TPMA_NV) 0x00000001)
+#define TPMA_NV_OWNERWRITE     ((TPMA_NV) 0x00000002)
+#define TPMA_NV_AUTHWRITE      ((TPMA_NV) 0x00000004)
+#define TPMA_NV_POLICYWRITE    ((TPMA_NV) 0x00000008)
+#define TPMA_NV_TPM2_NT_MASK   ((TPMA_NV) 0x000000F0)
+#define TPMA_NV_TPM2_NT_SHIFT  (4)
+#define TPMA_NV_RESERVED1_MASK ((TPMA_NV) 0x00000300)
+#define TPMA_NV_POLICY_DELETE  ((TPMA_NV) 0x00000400)
+#define TPMA_NV_WRITELOCKED    ((TPMA_NV) 0x00000800)
+#define TPMA_NV_WRITEALL       ((TPMA_NV) 0x00001000)
+#define TPMA_NV_WRITEDEFINE    ((TPMA_NV) 0x00002000)
+#define TPMA_NV_WRITE_STCLEAR  ((TPMA_NV) 0x00004000)
+#define TPMA_NV_GLOBALLOCK     ((TPMA_NV) 0x00008000)
+#define TPMA_NV_PPREAD         ((TPMA_NV) 0x00010000)
+#define TPMA_NV_OWNERREAD      ((TPMA_NV) 0x00020000)
+#define TPMA_NV_AUTHREAD       ((TPMA_NV) 0x00040000)
+#define TPMA_NV_POLICYREAD     ((TPMA_NV) 0x00080000)
+#define TPMA_NV_RESERVED2_MASK ((TPMA_NV) 0x01F00000)
+#define TPMA_NV_NO_DA          ((TPMA_NV) 0x02000000)
+#define TPMA_NV_ORDERLY        ((TPMA_NV) 0x04000000)
+#define TPMA_NV_CLEAR_STCLEAR  ((TPMA_NV) 0x08000000)
+#define TPMA_NV_READLOCKED     ((TPMA_NV) 0x10000000)
+#define TPMA_NV_WRITTEN        ((TPMA_NV) 0x20000000)
+#define TPMA_NV_PLATFORMCREATE ((TPMA_NV) 0x40000000)
+#define TPMA_NV_READ_STCLEAR   ((TPMA_NV) 0x80000000)
+
+/* TPM_ALG_ID Constants */
+typedef grub_uint16_t TPM_ALG_ID;
+
+#define TPM_ALG_ERROR          ((TPM_ALG_ID) 0x0000)
+#define TPM_ALG_AES            ((TPM_ALG_ID) 0x0006)
+#define TPM_ALG_CAMELLIA       ((TPM_ALG_ID) 0x0026)
+#define TPM_ALG_CBC            ((TPM_ALG_ID) 0x0042)
+#define TPM_ALG_CFB            ((TPM_ALG_ID) 0x0043)
+#define TPM_ALG_ECB            ((TPM_ALG_ID) 0x0044)
+#define TPM_ALG_ECC            ((TPM_ALG_ID) 0x0023)
+#define TPM_ALG_HMAC           ((TPM_ALG_ID) 0x0005)
+#define TPM_ALG_KDF1_SP800_108 ((TPM_ALG_ID) 0x0022)
+#define TPM_ALG_KDF1_SP800_56A ((TPM_ALG_ID) 0x0020)
+#define TPM_ALG_KDF2           ((TPM_ALG_ID) 0x0021)
+#define TPM_ALG_KEYEDHASH      ((TPM_ALG_ID) 0x0008)
+#define TPM_ALG_MGF1           ((TPM_ALG_ID) 0x0007)
+#define TPM_ALG_NULL           ((TPM_ALG_ID) 0x0010)
+#define TPM_ALG_RSA            ((TPM_ALG_ID) 0x0001)
+#define TPM_ALG_SHA1           ((TPM_ALG_ID) 0x0004)
+#define TPM_ALG_SHA256         ((TPM_ALG_ID) 0x000B)
+#define TPM_ALG_SHA384         ((TPM_ALG_ID) 0x000C)
+#define TPM_ALG_SHA512         ((TPM_ALG_ID) 0x000D)
+#define TPM_ALG_SM3_256        ((TPM_ALG_ID) 0x0012)
+#define TPM_ALG_SM4            ((TPM_ALG_ID) 0x0013)
+#define TPM_ALG_SYMCIPHER      ((TPM_ALG_ID) 0x0025)
+#define TPM_ALG_XOR            ((TPM_ALG_ID) 0x000A)
+
+/* TPM_CAP Constants */
+typedef grub_uint32_t TPM_CAP;
+
+#define TPM_CAP_FIRST           ((TPM_CAP) 0x00000000)
+#define TPM_CAP_ALGS            ((TPM_CAP) 0x00000000)
+#define TPM_CAP_HANDLES         ((TPM_CAP) 0x00000001)
+#define TPM_CAP_COMMANDS        ((TPM_CAP) 0x00000002)
+#define TPM_CAP_PP_COMMANDS     ((TPM_CAP) 0x00000003)
+#define TPM_CAP_AUDIT_COMMANDS  ((TPM_CAP) 0x00000004)
+#define TPM_CAP_PCRS            ((TPM_CAP) 0x00000005)
+#define TPM_CAP_TPM_PROPERTIES  ((TPM_CAP) 0x00000006)
+#define TPM_CAP_PCR_PROPERTIES  ((TPM_CAP) 0x00000007)
+#define TPM_CAP_ECC_CURVES      ((TPM_CAP) 0x00000008)
+#define TPM_CAP_LAST            ((TPM_CAP) 0x00000008)
+#define TPM_CAP_VENDOR_PROPERTY ((TPM_CAP) 0x00000100)
+
+/* TPM_PT Constants */
+typedef grub_uint32_t TPM_PT;
+
+#define TPM_PT_NONE             ((TPM_PT) 0x00000000)
+#define PT_GROUP                ((TPM_PT) 0x00000100)
+#define PT_FIXED                ((TPM_PT) (PT_GROUP * 1))
+#define TPM_PT_FAMILY_INDICATOR ((TPM_PT) (PT_FIXED + 0))
+#define TPM_PT_LEVEL            ((TPM_PT) (PT_FIXED + 1))
+#define TPM_PT_REVISION         ((TPM_PT) (PT_FIXED + 2))
+#define TPM_PT_DAY_OF_YEAR      ((TPM_PT) (PT_FIXED + 3))
+#define TPM_PT_YEAR             ((TPM_PT) (PT_FIXED + 4))
+#define TPM_PT_PCR_COUNT        ((TPM_PT) (PT_FIXED + 18))
+
+/* TPM_SE Constants */
+typedef grub_uint8_t TPM_SE;
+
+#define TPM_SE_HMAC   ((TPM_SE) 0x00)
+#define TPM_SE_POLICY ((TPM_SE) 0x01)
+#define TPM_SE_TRIAL  ((TPM_SE) 0x03)
+
+/* TPMI_YES_NO Constants */
+typedef grub_uint8_t TPMI_YES_NO;
+
+#define TPM_NO  ((TPMI_YES_NO)0)
+#define TPM_YES ((TPMI_YES_NO)1)
+
+/* TPM_ST Constants */
+typedef grub_uint16_t TPM_ST;
+typedef TPM_ST TPMI_ST_COMMAND_TAG;
+
+#define TPM_ST_NO_SESSIONS ((TPMI_ST_COMMAND_TAG) 0x8001)
+#define TPM_ST_SESSIONS    ((TPMI_ST_COMMAND_TAG) 0x8002)
+
+/* TPM_HANDLE Types */
+typedef grub_uint32_t TPM_HANDLE;
+
+typedef TPM_HANDLE TPMI_RH_HIERARCHY;
+typedef TPM_HANDLE TPMI_RH_LOCKOUT;
+typedef TPM_HANDLE TPMI_SH_AUTH_SESSION;
+typedef TPM_HANDLE TPMI_DH_CONTEXT;
+typedef TPM_HANDLE TPMI_DH_OBJECT;
+typedef TPM_HANDLE TPMI_DH_ENTITY;
+typedef TPM_HANDLE TPMI_SH_POLICY;
+typedef TPM_HANDLE TPMI_DH_PCR;
+typedef TPM_HANDLE TPMI_RH_NV_AUTH;
+typedef TPM_HANDLE TPMI_RH_NV_INDEX;
+
+/* TPM_RH Constants */
+typedef TPM_HANDLE TPM_RH;
+
+#define TPM_RH_FIRST       ((TPM_RH) 0x40000000)
+#define TPM_RH_SRK         ((TPM_RH) 0x40000000)
+#define TPM_RH_OWNER       ((TPM_RH) 0x40000001)
+#define TPM_RH_REVOKE      ((TPM_RH) 0x40000002)
+#define TPM_RH_TRANSPORT   ((TPM_RH) 0x40000003)
+#define TPM_RH_OPERATOR    ((TPM_RH) 0x40000004)
+#define TPM_RH_ADMIN       ((TPM_RH) 0x40000005)
+#define TPM_RH_EK          ((TPM_RH) 0x40000006)
+#define TPM_RH_NULL        ((TPM_RH) 0x40000007)
+#define TPM_RH_UNASSIGNED  ((TPM_RH) 0x40000008)
+#define TPM_RS_PW          ((TPM_RH) 0x40000009)
+#define TPM_RH_LOCKOUT     ((TPM_RH) 0x4000000A)
+#define TPM_RH_ENDORSEMENT ((TPM_RH) 0x4000000B)
+#define TPM_RH_PLATFORM    ((TPM_RH) 0x4000000C)
+#define TPM_RH_PLATFORM_NV ((TPM_RH) 0x4000000D)
+#define TPM_RH_AUTH_00     ((TPM_RH) 0x40000010)
+#define TPM_RH_AUTH_FF     ((TPM_RH) 0x4000010F)
+#define TPM_RH_LAST        ((TPM_RH) 0x4000010F)
+
+/* TPM2_ECC_CURVE Constants */
+typedef grub_uint16_t TPM2_ECC_CURVE;
+
+#define TPM_ECC_NONE      ((TPM_ECC_CURVE) 0x0000)
+#define TPM_ECC_NIST_P192 ((TPM_ECC_CURVE) 0x0001)
+#define TPM_ECC_NIST_P224 ((TPM_ECC_CURVE) 0x0002)
+#define TPM_ECC_NIST_P256 ((TPM_ECC_CURVE) 0x0003)
+#define TPM_ECC_NIST_P384 ((TPM_ECC_CURVE) 0x0004)
+#define TPM_ECC_NIST_P521 ((TPM_ECC_CURVE) 0x0005)
+#define TPM_ECC_BN_P256   ((TPM_ECC_CURVE) 0x0010)
+#define TPM_ECC_BN_P638   ((TPM_ECC_CURVE) 0x0011)
+#define TPM_ECC_SM2_P256  ((TPM_ECC_CURVE) 0x0020)
+
+/* TPM_CC Constants */
+typedef grub_uint32_t TPM_CC;
+
+#define TPM_CC_EvictControl     ((TPM_CC) 0x00000120)
+#define TPM_CC_CreatePrimary    ((TPM_CC) 0x00000131)
+#define TPM_CC_Create           ((TPM_CC) 0x00000153)
+#define TPM_CC_FlushContext     ((TPM_CC) 0x00000165)
+#define TPM_CC_ReadPublic       ((TPM_CC) 0x00000173)
+#define TPM_CC_StartAuthSession ((TPM_CC) 0x00000176)
+#define TPM_CC_PolicyPCR        ((TPM_CC) 0x0000017f)
+#define TPM_CC_NV_Read          ((TPM_CC) 0x0000014e)
+#define TPM_CC_NV_ReadPublic    ((TPM_CC) 0x00000169)
+#define TPM_CC_GetCapability    ((TPM_CC) 0x0000017a)
+#define TPM_CC_PCR_Read         ((TPM_CC) 0x0000017e)
+#define TPM_CC_Load             ((TPM_CC) 0x00000157)
+#define TPM_CC_Unseal           ((TPM_CC) 0x0000015e)
+#define TPM_CC_PolicyGetDigest  ((TPM_CC) 0x00000189)
+
+/* Hash algorithm sizes */
+#define TPM_SHA1_DIGEST_SIZE    20
+#define TPM_SHA256_DIGEST_SIZE  32
+#define TPM_SM3_256_DIGEST_SIZE 32
+#define TPM_SHA384_DIGEST_SIZE  48
+#define TPM_SHA512_DIGEST_SIZE  64
+
+/* Encryption algorithm sizes */
+#define TPM_MAX_SYM_BLOCK_SIZE 16
+#define TPM_MAX_SYM_DATA       256
+#define TPM_MAX_ECC_KEY_BYTES  128
+#define TPM_MAX_SYM_KEY_BYTES  32
+#define TPM_MAX_RSA_KEY_BYTES  512
+
+/* Buffer Size Constants */
+#define TPM_MAX_PCRS                      32
+#define TPM_NUM_PCR_BANKS                 16
+#define TPM_PCR_SELECT_MAX                ((TPM_MAX_PCRS + 7) / 8)
+#define TPM_MAX_DIGEST_BUFFER             1024
+#define TPM_MAX_TPM_PROPERTIES            8
+#define TPM_MAX_NV_BUFFER_SIZE            2048
+#define TPM_PRIVATE_VENDOR_SPECIFIC_BYTES 1280
+
+/* TPM_GENERATED Constants */
+typedef grub_uint32_t TPM_GENERATED;
+
+#define TPM_GENERATED_VALUE ((TPM_GENERATED) 0xff544347)
+
+/* TPM_ALG_ID Types */
+typedef TPM_ALG_ID TPMI_ALG_PUBLIC;
+typedef TPM_ALG_ID TPMI_ALG_HASH;
+typedef TPM_ALG_ID TPMI_ALG_KEYEDHASH_SCHEME;
+typedef TPM_ALG_ID TPMI_ALG_KDF;
+typedef TPM_ALG_ID TPMI_ALG_SYM_OBJECT;
+typedef TPM_ALG_ID TPMI_ALG_SYM_MODE;
+typedef TPM_ALG_ID TPMI_ALG_RSA_DECRYPT;
+typedef TPM_ALG_ID TPMI_ALG_ECC_SCHEME;
+typedef TPM_ALG_ID TPMI_ALG_ASYM_SCHEME;
+typedef TPM_ALG_ID TPMI_ALG_RSA_SCHEME;
+typedef TPM_ALG_ID TPMI_ALG_SYM;
+
+/* TPM_KEY_BITS Type */
+typedef grub_uint16_t TPM_KEY_BITS;
+
+/* TPM_ECC_CURVE Types */
+typedef grub_uint16_t TPM_ECC_CURVE;
+
+typedef TPM_ECC_CURVE TPMI_ECC_CURVE;
+
+/* TPMI_RH_PROVISION Type */
+typedef TPM_HANDLE TPMI_RH_PROVISION;
+
+/* TPMI_RH_PROVISION Type */
+typedef TPM_HANDLE TPMI_DH_PERSISTENT;
+
+#endif /* ! GRUB_TPM2_INTERNAL_TYPES_HEADER */
diff --git a/include/grub/tpm2/mu.h b/include/grub/tpm2/mu.h
new file mode 100644
index 0000000..4f4058f
--- /dev/null
+++ b/include/grub/tpm2/mu.h
@@ -0,0 +1,292 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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_MU_HEADER
+#define GRUB_TPM2_MU_HEADER 1
+
+#include <grub/tpm2/buffer.h>
+#include <grub/tpm2/tpm2.h>
+
+void
+grub_tpm2_mu_TPMS_AUTH_COMMAND_Marshal (grub_tpm2_buffer_t buf,
+                                        const TPMS_AUTH_COMMAND* authCommand);
+
+void
+grub_tpm2_mu_TPM2B_Marshal (grub_tpm2_buffer_t buf,
+                            grub_uint16_t size,
+                            const grub_uint8_t* buffer);
+
+void
+grub_tpm2_mu_TPMU_SYM_KEY_BITS_Marshal (grub_tpm2_buffer_t buf,
+                                        TPMI_ALG_SYM_OBJECT algorithm,
+                                        TPMU_SYM_KEY_BITS *p);
+
+void
+grub_tpm2_mu_TPMU_SYM_MODE_Marshal (grub_tpm2_buffer_t buf,
+                                    TPMI_ALG_SYM_OBJECT algorithm,
+                                    TPMU_SYM_MODE *p);
+
+void
+grub_tpm2_mu_TPMT_SYM_DEF_Marshal (grub_tpm2_buffer_t buf,
+                                   TPMT_SYM_DEF *p);
+
+void
+grub_tpm2_mu_TPMS_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buf,
+                                         const TPMS_PCR_SELECTION* pcrSelection);
+
+void
+grub_tpm2_mu_TPML_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buf,
+                                         const TPML_PCR_SELECTION* pcrSelection);
+
+void
+grub_tpm2_mu_TPMA_OBJECT_Marshal (grub_tpm2_buffer_t buf,
+                                  const TPMA_OBJECT *p);
+
+void
+grub_tpm2_mu_TPMS_SCHEME_XOR_Marshal (grub_tpm2_buffer_t buf,
+                                      TPMS_SCHEME_XOR *p);
+
+void
+grub_tpm2_mu_TPMS_SCHEME_HMAC_Marshal (grub_tpm2_buffer_t buf,
+                                       TPMS_SCHEME_HMAC *p);
+
+void
+grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Marshal (grub_tpm2_buffer_t buf,
+                                            TPMI_ALG_KEYEDHASH_SCHEME scheme,
+                                            TPMU_SCHEME_KEYEDHASH *p);
+
+void
+grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Marshal (grub_tpm2_buffer_t buf,
+                                            TPMT_KEYEDHASH_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Marshal (grub_tpm2_buffer_t buf,
+                                           TPMS_KEYEDHASH_PARMS *p);
+
+void
+grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Marshal (grub_tpm2_buffer_t buf,
+                                          TPMT_SYM_DEF_OBJECT *p);
+
+void
+grub_tpm2_mu_TPMU_ASYM_SCHEME_Marshal (grub_tpm2_buffer_t buf,
+                                       TPMI_ALG_RSA_DECRYPT scheme,
+                                       TPMU_ASYM_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMT_RSA_SCHEME_Marshal (grub_tpm2_buffer_t buf,
+                                      TPMT_RSA_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMS_RSA_PARMS_Marshal (grub_tpm2_buffer_t buf,
+                                     TPMS_RSA_PARMS *p);
+
+void
+grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Marshal (grub_tpm2_buffer_t buf,
+                                           TPMS_SYMCIPHER_PARMS *p);
+
+void
+grub_tpm2_mu_TPMT_ECC_SCHEME_Marshal (grub_tpm2_buffer_t buf,
+                                      TPMT_ECC_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMU_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buf,
+                                      TPMI_ALG_KDF scheme,
+                                      TPMU_KDF_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMT_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buf,
+                                      TPMT_KDF_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMS_ECC_PARMS_Marshal (grub_tpm2_buffer_t buf,
+                                     TPMS_ECC_PARMS *p);
+
+void
+grub_tpm2_mu_TPMU_PUBLIC_PARMS_Marshal (grub_tpm2_buffer_t buf,
+                                        grub_uint32_t type,
+                                        TPMU_PUBLIC_PARMS *p);
+
+void
+grub_tpm2_mu_TPMS_ECC_POINT_Marshal (grub_tpm2_buffer_t buf,
+                                     TPMS_ECC_POINT *p);
+
+void
+grub_tpm2_mu_TPMU_PUBLIC_ID_Marshal (grub_tpm2_buffer_t buf,
+                                     TPMI_ALG_PUBLIC type,
+                                     TPMU_PUBLIC_ID *p);
+
+void
+grub_tpm2_mu_TPMT_PUBLIC_Marshal (grub_tpm2_buffer_t buf,
+                                  TPMT_PUBLIC *p);
+
+void
+grub_tpm2_mu_TPM2B_PUBLIC_Marshal (grub_tpm2_buffer_t buf,
+                                   TPM2B_PUBLIC *p);
+
+void
+grub_tpm2_mu_TPMS_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buf,
+                                            TPMS_SENSITIVE_CREATE *p);
+
+void
+grub_tpm2_mu_TPM2B_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buf,
+                                             TPM2B_SENSITIVE_CREATE *sensitiveCreate);
+
+void
+grub_tpm2_mu_TPM2B_Unmarshal (grub_tpm2_buffer_t buf,
+                              TPM2B* p);
+
+void
+grub_tpm2_mu_TPMS_AUTH_RESPONSE_Unmarshal (grub_tpm2_buffer_t buf,
+                                           TPMS_AUTH_RESPONSE* p);
+
+void
+grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (grub_tpm2_buffer_t buf,
+                                     TPM2B_DIGEST* digest);
+
+void
+grub_tpm2_mu_TPMA_OBJECT_Unmarshal (grub_tpm2_buffer_t buf,
+                                    TPMA_OBJECT *p);
+
+void
+grub_tpm2_mu_TPMS_SCHEME_HMAC_Unmarshal (grub_tpm2_buffer_t buf,
+                                         TPMS_SCHEME_HMAC *p);
+
+void
+grub_tpm2_mu_TPMS_SCHEME_XOR_Unmarshal (grub_tpm2_buffer_t buf,
+                                        TPMS_SCHEME_XOR *p);
+
+void
+grub_tpm2_mu_TPMU_SCHEME_KEYEDHASH_Unmarshal (grub_tpm2_buffer_t buf,
+                                              TPMI_ALG_KEYEDHASH_SCHEME scheme,
+                                              TPMU_SCHEME_KEYEDHASH *p);
+
+void
+grub_tpm2_mu_TPMT_KEYEDHASH_SCHEME_Unmarshal (grub_tpm2_buffer_t buf,
+                                              TPMT_KEYEDHASH_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMS_KEYEDHASH_PARMS_Unmarshal (grub_tpm2_buffer_t buf,
+                                             TPMS_KEYEDHASH_PARMS *p);
+
+void
+grub_tpm2_mu_TPMU_SYM_KEY_BITS_Unmarshal (grub_tpm2_buffer_t buf,
+                                          TPMI_ALG_SYM_OBJECT algorithm,
+                                          TPMU_SYM_KEY_BITS *p);
+
+void
+grub_tpm2_mu_TPMU_SYM_MODE_Unmarshal (grub_tpm2_buffer_t buf,
+                                      TPMI_ALG_SYM_OBJECT algorithm,
+                                      TPMU_SYM_MODE *p);
+
+void
+grub_tpm2_mu_TPMT_SYM_DEF_OBJECT_Unmarshal (grub_tpm2_buffer_t buf,
+                                            TPMT_SYM_DEF_OBJECT *p);
+
+void
+grub_tpm2_mu_TPMS_SYMCIPHER_PARMS_Unmarshal (grub_tpm2_buffer_t buf,
+                                             TPMS_SYMCIPHER_PARMS *p);
+
+void
+grub_tpm2_mu_TPMU_ASYM_SCHEME_Unmarshal (grub_tpm2_buffer_t buf,
+                                         TPMI_ALG_RSA_DECRYPT scheme,
+                                         TPMU_ASYM_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMT_RSA_SCHEME_Unmarshal (grub_tpm2_buffer_t buf,
+                                        TPMT_RSA_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMS_RSA_PARMS_Unmarshal (grub_tpm2_buffer_t buf,
+                                       TPMS_RSA_PARMS *p);
+
+void
+grub_tpm2_mu_TPMT_ECC_SCHEME_Unmarshal (grub_tpm2_buffer_t buf,
+                                        TPMT_ECC_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMU_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buf,
+                                        TPMI_ALG_KDF scheme,
+                                        TPMU_KDF_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMT_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buf,
+                                        TPMT_KDF_SCHEME *p);
+
+void
+grub_tpm2_mu_TPMS_ECC_PARMS_Unmarshal (grub_tpm2_buffer_t buf,
+                                       TPMS_ECC_PARMS *p);
+
+void
+grub_tpm2_mu_TPMU_PUBLIC_PARMS_Unmarshal (grub_tpm2_buffer_t buf,
+                                          grub_uint32_t type,
+                                          TPMU_PUBLIC_PARMS *p);
+
+void
+grub_tpm2_mu_TPMS_ECC_POINT_Unmarshal (grub_tpm2_buffer_t buf,
+                                       TPMS_ECC_POINT *p);
+
+void
+grub_tpm2_mu_TPMU_PUBLIC_ID_Unmarshal (grub_tpm2_buffer_t buf,
+                                       TPMI_ALG_PUBLIC type,
+                                       TPMU_PUBLIC_ID *p);
+
+void
+grub_tpm2_mu_TPMT_PUBLIC_Unmarshal (grub_tpm2_buffer_t buf,
+                                    TPMT_PUBLIC *p);
+
+void
+grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (grub_tpm2_buffer_t buf,
+                                     TPM2B_PUBLIC *p);
+
+void
+grub_tpm2_mu_TPMS_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buf,
+                                       TPMS_NV_PUBLIC *p);
+
+void
+grub_tpm2_mu_TPM2B_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buf,
+                                        TPM2B_NV_PUBLIC *p);
+
+void
+grub_tpm2_mu_TPM2B_NAME_Unmarshal (grub_tpm2_buffer_t buf,
+                                   TPM2B_NAME *n);
+
+void
+grub_tpm2_mu_TPMS_TAGGED_PROPERTY_Unmarshal (grub_tpm2_buffer_t buf,
+                                             TPMS_TAGGED_PROPERTY* property);
+
+void
+grub_tpm2_mu_TPMS_CAPABILITY_DATA_tpmProperties_Unmarshal (grub_tpm2_buffer_t buf,
+                                                           TPMS_CAPABILITY_DATA* capabilityData);
+
+void
+grub_tpm2_mu_TPMT_TK_CREATION_Unmarshal (grub_tpm2_buffer_t buf,
+                                         TPMT_TK_CREATION *p);
+
+void
+grub_tpm2_mu_TPMS_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buf,
+                                           TPMS_PCR_SELECTION* pcrSelection);
+
+void
+grub_tpm2_mu_TPML_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buf,
+                                           TPML_PCR_SELECTION* pcrSelection);
+
+void
+grub_tpm2_mu_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buf,
+                                    TPML_DIGEST* digest);
+
+#endif /* ! GRUB_TPM2_MU_HEADER */
diff --git a/include/grub/tpm2/tcg2.h b/include/grub/tpm2/tcg2.h
new file mode 100644
index 0000000..99f2fdd
--- /dev/null
+++ b/include/grub/tpm2/tcg2.h
@@ -0,0 +1,34 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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_TCG2_HEADER
+#define GRUB_TPM2_TCG2_HEADER 1
+
+#include <grub/err.h>
+#include <grub/types.h>
+
+grub_err_t
+grub_tcg2_get_max_output_size (grub_size_t *size);
+
+grub_err_t
+grub_tcg2_submit_command (grub_size_t input_size,
+                          grub_uint8_t *input,
+                          grub_size_t output_size,
+                          grub_uint8_t *output);
+
+#endif /* ! GRUB_TPM2_TCG2_HEADER */
diff --git a/include/grub/tpm2/tpm2.h b/include/grub/tpm2/tpm2.h
new file mode 100644
index 0000000..1569ea7
--- /dev/null
+++ b/include/grub/tpm2/tpm2.h
@@ -0,0 +1,38 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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 <grub/tpm2/internal/types.h>
+#include <grub/tpm2/internal/structs.h>
+#include <grub/tpm2/internal/functions.h>
+
+// Defined in: TCG TPM Specification, v1.59, Part 2, Section 10.6.1.
+#define TPM2_PCR_TO_SELECT(x)  ((x) / 8)
+#define TPM2_PCR_TO_BIT(x)     (1 << ((x) % 8))
+
+// Well-Known Windows SRK Handle
+#define TPM2_SRK_HANDLE 0x81000001
+
+typedef struct TPM2_SEALED_KEY {
+    TPM2B_PUBLIC  public;
+    TPM2B_PRIVATE private;
+} TPM2_SEALED_KEY;
+
+#endif /* ! GRUB_TPM2_TPM2_HEADER */
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 3/5] protectors: Add TPM2 Key Protector
  2022-01-24 14:12 [PATCH 0/5] Automatic TPM Disk Unlock Hernan Gatta
  2022-01-24 14:12 ` [PATCH 1/5] protectors: Add key protectors framework Hernan Gatta
  2022-01-24 14:12 ` [PATCH 2/5] tpm2: Add TPM Software Stack (TSS) Hernan Gatta
@ 2022-01-24 14:12 ` Hernan Gatta
  2022-01-25  3:55   ` Glenn Washburn
  2022-01-24 14:12 ` [PATCH 4/5] cryptodisk: Support key protectors Hernan Gatta
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 21+ messages in thread
From: Hernan Gatta @ 2022-01-24 14:12 UTC (permalink / raw)
  To: grub-devel

From: Hernan Gatta <hegatta@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 therefore possess reasonable defaults. One of these
arguments is the keyfile parameter, which is mandatory.

The value of this parameter must be a path to a sealed key file (e.g.,
(hd0,gpt1)/boot/grub2/sealed_key). 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.

Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
---
 grub-core/Makefile.core.def |   9 +
 grub-core/tpm2/module.c     | 742 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 751 insertions(+)
 create mode 100644 grub-core/tpm2/module.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index e4ae78b..8691440 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2498,6 +2498,15 @@ module = {
 };
 
 module = {
+  name = tpm2;
+  common = tpm2/buffer.c;
+  common = tpm2/module.c;
+  common = tpm2/mu.c;
+  common = tpm2/tpm2.c;
+  efi = tpm2/tcg2.c;
+};
+
+module = {
   name = tr;
   common = commands/tr.c;
 };
diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
new file mode 100644
index 0000000..cd97452
--- /dev/null
+++ b/grub-core/tpm2/module.c
@@ -0,0 +1,742 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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/file.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/protector.h>
+#include <grub/tpm2/buffer.h>
+#include <grub/tpm2/mu.h>
+#include <grub/tpm2/tpm2.h>
+#include <grub/types.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+typedef enum grub_tpm2_protector_args
+{
+  GRUB_TPM2_PROTECTOR_ARG_MODE       = 1 << 0,
+  GRUB_TPM2_PROTECTOR_ARG_PCRS       = 1 << 1,
+  GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC = 1 << 2,
+  GRUB_TPM2_PROTECTOR_ARG_BANK       = 1 << 3,
+  GRUB_TPM2_PROTECTOR_ARG_KEYFILE    = 1 << 4,
+  GRUB_TPM2_PROTECTOR_ARG_SRK        = 1 << 5,
+  GRUB_TPM2_PROTECTOR_ARG_NV         = 1 << 6
+} grub_tpm2_protector_args_t;
+
+typedef enum grub_tpm2_protector_mode
+{
+  GRUB_TPM2_PROTECTOR_MODE_SRK = 1,
+  GRUB_TPM2_PROTECTOR_MODE_NV  = 2
+} grub_tpm2_protector_mode_t;
+
+struct grub_tpm2_protector_context
+{
+  grub_tpm2_protector_args_t args;
+  grub_tpm2_protector_mode_t mode;
+  grub_uint8_t pcrs[TPM_MAX_PCRS];
+  grub_uint8_t pcr_count;
+  TPM_ALG_ID asymmetric;
+  TPM_ALG_ID bank;
+  const char *keyfile;
+  TPM_HANDLE srk;
+  TPM_HANDLE nv;
+};
+
+static grub_err_t
+grub_tpm2_protector_parse_mode (struct grub_tpm2_protector_context *ctx,
+                                const char *value)
+{
+  if (grub_strcmp (value, "srk") == 0)
+    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
+  else if (grub_strcmp (value, "nv") == 0)
+    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_NV;
+  else
+    return GRUB_ERR_OUT_OF_RANGE;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_pcrs (struct grub_tpm2_protector_context *ctx,
+                                char *value)
+{
+  grub_err_t err;
+  char *current_pcr = value;
+  char *next_pcr;
+  unsigned long pcr;
+  grub_uint8_t i;
+
+  if (grub_strlen (value) == 0)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  ctx->pcr_count = 0;
+  for (i = 0; i < TPM_MAX_PCRS; i++)
+    {
+      next_pcr = grub_strchr (current_pcr, ',');
+      if (next_pcr)
+        *next_pcr = '\0';
+      if (next_pcr == current_pcr)
+        return GRUB_ERR_BAD_ARGUMENT;
+
+      grub_errno = GRUB_ERR_NONE;
+      pcr = grub_strtoul (current_pcr, NULL, 10);
+      if (grub_errno != GRUB_ERR_NONE)
+        {
+          err = grub_errno;
+          grub_errno = GRUB_ERR_NONE;
+          return err;
+        }
+
+      if (pcr > GRUB_UCHAR_MAX)
+        return GRUB_ERR_OUT_OF_RANGE;
+
+      ctx->pcrs[i] = (grub_uint8_t)pcr;
+      ctx->pcr_count++;
+
+      if (!next_pcr)
+        break;
+
+      current_pcr = next_pcr + 1;
+      if (*current_pcr == '\0')
+        return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  if (i == TPM_MAX_PCRS)
+    return GRUB_ERR_OUT_OF_RANGE;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_asymmetric (struct grub_tpm2_protector_context *ctx,
+                                      const char *value)
+{
+  if (grub_strcasecmp (value, "ECC") == 0)
+    ctx->asymmetric = TPM_ALG_ECC;
+  else if (grub_strcasecmp (value, "RSA") == 0)
+    ctx->asymmetric = TPM_ALG_RSA;
+  else
+    return GRUB_ERR_OUT_OF_RANGE;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_bank (struct grub_tpm2_protector_context *ctx,
+                                const char *value)
+{
+  if (grub_strcasecmp (value, "SHA1") == 0)
+    ctx->bank = TPM_ALG_SHA1;
+  else if (grub_strcasecmp (value, "SHA256") == 0)
+    ctx->bank = TPM_ALG_SHA256;
+  else if (grub_strcasecmp (value, "SHA384") == 0)
+    ctx->bank = TPM_ALG_SHA384;
+  else
+    return GRUB_ERR_OUT_OF_RANGE;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_keyfile (struct grub_tpm2_protector_context *ctx,
+                                   const char *value)
+{
+  if (grub_strlen (value) == 0)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  ctx->keyfile = value;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle)
+{
+  grub_err_t err;
+  unsigned long num;
+
+  grub_errno = GRUB_ERR_NONE;
+  num = grub_strtoul (value, NULL, 0);
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      err = grub_errno;
+      grub_errno = GRUB_ERR_NONE;
+      return err;
+    }
+
+  if (num > GRUB_UINT_MAX)
+    return GRUB_ERR_OUT_OF_RANGE;
+
+  *handle = (TPM_HANDLE)num;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_srk (struct grub_tpm2_protector_context *ctx,
+                               const char *value)
+{
+  return grub_tpm2_protector_parse_tpm_handle (value, &ctx->srk);
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_nvindex (struct grub_tpm2_protector_context *ctx,
+                                   const char *value)
+{
+  return grub_tpm2_protector_parse_tpm_handle (value, &ctx->nv);
+}
+
+static grub_err_t
+grub_tpm2_protector_match_arg (struct grub_tpm2_protector_context *ctx,
+                               const char *key, char *value)
+{
+  grub_err_t err;
+
+  if (grub_strlen (key) == 0 || grub_strlen (value) == 0)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (grub_strcmp (key, "mode") == 0)
+  {
+    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_MODE)
+      return GRUB_ERR_BAD_ARGUMENT;
+
+    err = grub_tpm2_protector_parse_mode (ctx, value);
+    if (!err)
+      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_MODE;
+
+    return err;
+  }
+
+  if (grub_strcmp (key, "pcrs") == 0)
+  {
+    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_PCRS)
+      return GRUB_ERR_BAD_ARGUMENT;
+
+    err = grub_tpm2_protector_parse_pcrs (ctx, value);
+    if (!err)
+      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_PCRS;
+
+    return err;
+  }
+
+  if (grub_strcmp (key, "asymmetric") == 0)
+  {
+    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC)
+      return GRUB_ERR_BAD_ARGUMENT;
+
+    err = grub_tpm2_protector_parse_asymmetric (ctx, value);
+    if (!err)
+      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC;
+
+    return err;
+  }
+
+  if (grub_strcmp (key, "bank") == 0)
+  {
+    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_BANK)
+      return GRUB_ERR_BAD_ARGUMENT;
+
+    err = grub_tpm2_protector_parse_bank (ctx, value);
+    if (!err)
+      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_BANK;
+
+    return err;
+  }
+
+  if (grub_strcmp (key, "keyfile") == 0)
+  {
+    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_KEYFILE)
+      return GRUB_ERR_BAD_ARGUMENT;
+
+    err = grub_tpm2_protector_parse_keyfile (ctx, value);
+    if (!err)
+      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_KEYFILE;
+
+    return err;
+  }
+
+  if (grub_strcmp (key, "srk") == 0)
+  {
+    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_SRK)
+      return GRUB_ERR_BAD_ARGUMENT;
+
+    err = grub_tpm2_protector_parse_srk (ctx, value);
+    if (!err)
+      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_SRK;
+
+    return err;
+  }
+
+  if (grub_strcmp (key, "nvindex") == 0)
+  {
+    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_NV)
+      return GRUB_ERR_BAD_ARGUMENT;
+
+    err = grub_tpm2_protector_parse_nvindex (ctx, value);
+    if (!err)
+      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_NV;
+
+    return err;
+  }
+
+  return GRUB_ERR_OUT_OF_RANGE;
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_arg (struct grub_tpm2_protector_context *ctx,
+                               char *arg)
+{
+  char *key = arg;
+  char *value = NULL;
+
+  if (grub_strlen (arg) == 0)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  value = grub_strchr (arg, '=');
+  if (!value)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  *value = '\0';
+  value++;
+
+  if (*value == '\0')
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  return grub_tpm2_protector_match_arg (ctx, key, value);
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_args (struct grub_tpm2_protector_context *ctx,
+                                char *args)
+{
+  grub_err_t err;
+  char *cur_arg;
+  char *next_arg;
+
+  if (args == NULL)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (grub_strlen (args) == 0)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  cur_arg = args;
+  for (;;)
+    {
+      next_arg = grub_strchr (cur_arg, ':');
+      if (next_arg == cur_arg)
+        return GRUB_ERR_BAD_ARGUMENT;
+      if (next_arg)
+        *next_arg = '\0';
+
+      err = grub_tpm2_protector_parse_arg (ctx, cur_arg);
+      if (err)
+        return err;
+
+      if (!next_arg)
+        break;
+
+      cur_arg = next_arg + 1;
+      if (*cur_arg == '\0')
+        return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
+{
+  if (!(ctx->args & GRUB_TPM2_PROTECTOR_ARG_MODE))
+    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && !ctx->keyfile)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->nv)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->srk)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && !ctx->nv)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->asymmetric)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->bank)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (!(ctx->args & GRUB_TPM2_PROTECTOR_ARG_PCRS))
+    {
+      ctx->pcrs[0] = 7;
+      ctx->pcr_count = 1;
+    }
+
+  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK)
+    {
+      if (!ctx->srk)
+        ctx->srk = TPM2_SRK_HANDLE;
+
+      if (!ctx->asymmetric)
+        ctx->asymmetric = TPM_ALG_RSA;
+
+      if (!ctx->bank)
+        ctx->bank = TPM_ALG_SHA256;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_srk_read_keyfile (const char *filepath, void **buffer,
+                                      grub_size_t *buffer_size)
+{
+  grub_file_t sealed_key_file;
+  grub_off_t sealed_key_size;
+  void *sealed_key_buffer;
+  grub_off_t sealed_key_read;
+
+  sealed_key_file = grub_file_open (filepath, GRUB_FILE_TYPE_NONE);
+  if (!sealed_key_file)
+    {
+      /* grub_file_open sets grub_errno on error, and if we do no unset it,
+       * future calls to grub_file_open will fail (and so will anybody up the
+       * stack who checks the value, if any). */
+      grub_errno = GRUB_ERR_NONE;
+      return GRUB_ERR_FILE_NOT_FOUND;
+    }
+
+  sealed_key_size = grub_file_size (sealed_key_file);
+  if (!sealed_key_size)
+    {
+      grub_file_close (sealed_key_file);
+      return GRUB_ERR_OUT_OF_RANGE;
+    }
+
+  sealed_key_buffer = grub_malloc (sealed_key_size);
+  if (!sealed_key_buffer)
+    {
+      grub_file_close (sealed_key_file);
+      return GRUB_ERR_OUT_OF_MEMORY;
+    }
+
+  sealed_key_read = grub_file_read (sealed_key_file, sealed_key_buffer,
+                                    sealed_key_size);
+  if (sealed_key_read != sealed_key_size)
+    {
+      grub_free (sealed_key_buffer);
+      grub_file_close (sealed_key_file);
+      return GRUB_ERR_FILE_READ_ERROR;
+    }
+
+  grub_file_close (sealed_key_file);
+
+  *buffer = sealed_key_buffer;
+  *buffer_size = sealed_key_size;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_srk_unmarshal_keyfile (void *sealed_key,
+                                           grub_size_t sealed_key_size,
+                                           TPM2_SEALED_KEY *sk)
+{
+  struct grub_tpm2_buffer buf;
+
+  grub_tpm2_buffer_init (&buf);
+  if (sealed_key_size > buf.cap)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_memcpy (buf.data, sealed_key, sealed_key_size);
+  buf.size = sealed_key_size;
+
+  grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
+  grub_tpm2_mu_TPM2B_Unmarshal (&buf, (TPM2B *)&sk->private);
+
+  return buf.error ? GRUB_ERR_BAD_ARGUMENT : GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_srk_get (const struct grub_tpm2_protector_context *ctx,
+                             TPM_HANDLE *srk)
+{
+  TPM_RC rc;
+  TPM2B_PUBLIC public;
+  TPMS_AUTH_COMMAND authCommand = { 0 };
+  TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
+  TPM2B_PUBLIC inPublic = { 0 };
+  TPM2B_DATA outsideInfo = { 0 };
+  TPML_PCR_SELECTION creationPcr = { 0 };
+  TPM2B_PUBLIC outPublic = { 0 };
+  TPM2B_CREATION_DATA creationData = { 0 };
+  TPM2B_DIGEST creationHash = { 0 };
+  TPMT_TK_CREATION creationTicket = { 0 };
+  TPM2B_NAME srkName = { 0 };
+  TPM_HANDLE srkHandle;
+
+  /* Find SRK */
+  rc = TPM2_ReadPublic (ctx->srk, NULL, &public);
+  if (rc == TPM_RC_SUCCESS)
+    {
+      *srk = ctx->srk;
+      return GRUB_ERR_NONE;
+    }
+
+  /* The handle exists but its public area could not be read. */
+  if ((rc & ~TPM_RC_N_MASK) != TPM_RC_HANDLE)
+    return GRUB_ERR_BAD_DEVICE;
+
+  /* Create SRK */
+  authCommand.sessionHandle = TPM_RS_PW;
+  inPublic.publicArea.type = ctx->asymmetric;
+  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 (ctx->asymmetric == 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 = 2048;
+      inPublic.publicArea.parameters.rsaDetail.exponent = 0;
+    }
+  else if (ctx->asymmetric == 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 = TPM_ECC_NIST_P256;
+      inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+    }
+  else
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  rc = TPM2_CreatePrimary (TPM_RH_OWNER, &authCommand, &inSensitive, &inPublic,
+                           &outsideInfo, &creationPcr, &srkHandle, &outPublic,
+                           &creationData, &creationHash, &creationTicket,
+                           &srkName, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    return GRUB_ERR_BAD_DEVICE;
+
+  *srk = srkHandle;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_srk_recover (const struct grub_tpm2_protector_context *ctx,
+                                 grub_uint8_t **key, grub_size_t *key_size)
+{
+  TPM_RC rc;
+  TPM2_SEALED_KEY sealed_key;
+  void *sealed_key_bytes;
+  grub_size_t sealed_key_size;
+  TPM_HANDLE srk_handle;
+  TPM2B_NONCE nonceCaller = { 0 };
+  TPM2B_ENCRYPTED_SECRET salt = { 0 };
+  TPMT_SYM_DEF symmetric = { 0 };
+  TPM2B_NONCE nonceTPM = { 0 };
+  TPMI_SH_AUTH_SESSION session;
+  TPML_PCR_SELECTION pcrSel = {
+    .count = 1,
+    .pcrSelections = {
+      {
+        .hash = ctx->bank,
+        .sizeOfSelect = 3,
+        .pcrSelect = { 0 }
+      },
+    }
+  };
+  TPMS_AUTH_COMMAND authCmd = { 0 };
+  TPM_HANDLE sealed_key_handle;
+  TPM2B_NAME name;
+  TPMS_AUTH_RESPONSE authResponse;
+  TPM2B_SENSITIVE_DATA data;
+  grub_uint8_t *key_out;
+  grub_uint8_t i;
+  grub_err_t err;
+
+  /* Retrieve Sealed Key */
+  err = grub_tpm2_protector_srk_read_keyfile (ctx->keyfile, &sealed_key_bytes,
+                                              &sealed_key_size);
+  if (err)
+    return err;
+
+  err = grub_tpm2_protector_srk_unmarshal_keyfile (sealed_key_bytes,
+                                                   sealed_key_size,
+                                                   &sealed_key);
+  if (err)
+    goto exit1;
+
+  /* Get SRK */
+  err = grub_tpm2_protector_srk_get (ctx, &srk_handle);
+  if (err)
+    goto exit1;
+
+  err = GRUB_ERR_BAD_DEVICE;
+
+  /* Start Auth Session */
+  nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
+  symmetric.algorithm = TPM_ALG_NULL;
+
+  rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, 0, &nonceCaller, &salt,
+                              TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256,
+                              &session, &nonceTPM, 0);
+  if (rc)
+    goto exit2;
+
+  /* Policy PCR */
+  for (i = 0; i < ctx->pcr_count; i++)
+    pcrSel
+      .pcrSelections[0]
+      .pcrSelect[TPM2_PCR_TO_SELECT(ctx->pcrs[i])]
+        |= TPM2_PCR_TO_BIT(ctx->pcrs[i]);
+
+  rc = TPM2_PolicyPCR (session, NULL, NULL, &pcrSel, NULL);
+  if (rc)
+    goto exit3;
+
+  /* Load Sealed Key */
+  authCmd.sessionHandle = TPM_RS_PW;
+  rc = TPM2_Load (srk_handle, &authCmd, &sealed_key.private, &sealed_key.public,
+                  &sealed_key_handle, &name, &authResponse);
+  if (rc)
+    goto exit3;
+
+  /* Unseal Sealed Key */
+  authCmd.sessionHandle = session;
+  grub_memset (&authResponse, 0, sizeof (authResponse));
+
+  rc = TPM2_Unseal (sealed_key_handle, &authCmd, &data, &authResponse);
+  if (rc)
+    goto exit4;
+
+  /* Epilogue */
+  key_out = grub_malloc (data.size);
+  if (!key_out)
+    {
+      err = GRUB_ERR_OUT_OF_MEMORY;
+      goto exit4;
+    }
+
+  grub_memcpy (key_out, data.buffer, data.size);
+
+  *key = key_out;
+  *key_size = data.size;
+
+  err = GRUB_ERR_NONE;
+
+exit4:
+  TPM2_FlushContext (sealed_key_handle);
+
+exit3:
+  TPM2_FlushContext (session);
+
+exit2:
+  TPM2_FlushContext (srk_handle);
+
+exit1:
+  grub_free (sealed_key_bytes);
+  return err;
+}
+
+static grub_err_t
+grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx,
+                                grub_uint8_t **key, grub_size_t *key_size)
+{
+  (void)ctx;
+  (void)key;
+  (void)key_size;
+
+  return GRUB_ERR_NOT_IMPLEMENTED_YET;
+}
+
+static grub_err_t
+grub_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 grub_tpm2_protector_srk_recover (ctx, key, key_size);
+    case GRUB_TPM2_PROTECTOR_MODE_NV:
+      return grub_tpm2_protector_nv_recover (ctx, key, key_size);
+    default:
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+}
+
+static grub_err_t
+grub_tpm2_protector_recover_key (char *args, grub_uint8_t **key,
+                                 grub_size_t *key_size)
+{
+  grub_err_t err;
+  struct grub_tpm2_protector_context ctx = { 0 };
+
+  grub_dprintf ("tpm2", "Args: %s\n", args);
+
+  if (!args || !key)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  err = grub_tpm2_protector_parse_args (&ctx, args);
+  if (err) {
+    grub_dprintf("tpm2", "Failed to parse arguments\n");
+    return err;
+  }
+
+  err = grub_tpm2_protector_check_args (&ctx);
+  if (err) {
+    grub_dprintf("tpm2", "Invalid arguments\n");
+    return err;
+  }
+
+  err = grub_tpm2_protector_recover (&ctx, key, key_size);
+  if (err) {
+    grub_dprintf("tpm2", "Failed to recover key\n");
+    return err;
+  }
+
+  return GRUB_ERR_NONE;
+}
+
+struct grub_key_protector grub_tpm2_key_protector = {
+  .name = "tpm2",
+  .flags = GRUB_KEY_PROTECTOR_FLAG_NONE,
+  .recover_key = grub_tpm2_protector_recover_key
+};
+
+GRUB_MOD_INIT(tpm2)
+{
+  grub_key_protector_register (&grub_tpm2_key_protector);
+}
+
+GRUB_MOD_FINI(tpm2)
+{
+  grub_key_protector_unregister (&grub_tpm2_key_protector);
+}
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 4/5] cryptodisk: Support key protectors
  2022-01-24 14:12 [PATCH 0/5] Automatic TPM Disk Unlock Hernan Gatta
                   ` (2 preceding siblings ...)
  2022-01-24 14:12 ` [PATCH 3/5] protectors: Add TPM2 Key Protector Hernan Gatta
@ 2022-01-24 14:12 ` Hernan Gatta
  2022-01-25  5:42   ` Glenn Washburn
  2022-01-24 14:12 ` [PATCH 5/5] util/grub-protect: Add new tool Hernan Gatta
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 21+ messages in thread
From: Hernan Gatta @ 2022-01-24 14:12 UTC (permalink / raw)
  To: grub-devel

From: Hernan Gatta <hegatta@microsoft.com>

Add a new parameter to cryptomount to support the key protectors framework: -k.
This parameter is accepted whenever the cryptomount command is used to mount a
specific disk either via a disk specification (e.g., (hd0,gpt1)) or by UUID, but
not when disks are mounted in bulk (i.e., via -a or -b). The parameter is used
to automatically retrieve a key from the specified key protector.

Signed-off-by: <Hernan Gatta hegatta@linux.microsoft.com>
---
 Makefile.util.def           |  1 +
 grub-core/disk/cryptodisk.c | 21 ++++++++++++++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/Makefile.util.def b/Makefile.util.def
index f8b356c..39b53b3 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -35,6 +35,7 @@ library = {
   common = grub-core/kern/list.c;
   common = grub-core/kern/misc.c;
   common = grub-core/kern/partition.c;
+  common = grub-core/kern/protectors.c;
   common = grub-core/lib/crypto.c;
   common = grub-core/lib/json/json.c;
   common = grub-core/disk/luks.c;
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 4970973..176dd56 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -26,6 +26,7 @@
 #include <grub/file.h>
 #include <grub/procfs.h>
 #include <grub/partition.h>
+#include <grub/protector.h>
 
 #ifdef GRUB_UTIL
 #include <grub/emu/hostdisk.h>
@@ -42,6 +43,7 @@ static const struct grub_arg_option options[] =
     {"all", 'a', 0, N_("Mount all."), 0, 0},
     {"boot", 'b', 0, N_("Mount all volumes with `boot' flag set."), 0, 0},
     {"password", 'p', 0, N_("Password to open volumes."), 0, ARG_TYPE_STRING},
+    {"protector", 'k', 0, N_("Unlock disk using the specified key protector."), 0, ARG_TYPE_STRING},
     {0, 0, 0, 0, 0, 0}
   };
 
@@ -1160,6 +1162,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
 {
   struct grub_arg_list *state = ctxt->state;
   struct grub_cryptomount_args cargs = {0};
+  grub_err_t err;
 
   if (argc < 1 && !state[1].set && !state[2].set)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
@@ -1167,12 +1170,28 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
   if (grub_cryptodisk_list == NULL)
     return grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk modules loaded");
 
+  if (state[3].set && state[4].set) /* password and key protector */
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       "a password and a key protector cannot both be set");
+
+  if (state[4].set && argc < 1) /* key protector */
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       "key protectors require a device name or UUID");
+
   if (state[3].set) /* password */
     {
       cargs.key_data = (grub_uint8_t *) state[3].arg;
       cargs.key_len = grub_strlen (state[3].arg);
     }
 
+  if (state[4].set) /* key protector */
+    {
+      err = grub_key_protector_recover_key (state[4].arg, &cargs.key_data,
+                                                          &cargs.key_len);
+      if (err)
+        grub_printf_ (N_("Could not recover key from key protector.\n"));
+    }
+
   if (state[0].set) /* uuid */
     {
       int found_uuid;
@@ -1385,7 +1404,7 @@ GRUB_MOD_INIT (cryptodisk)
 {
   grub_disk_dev_register (&grub_cryptodisk_dev);
   cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0,
-			      N_("[-p password] <SOURCE|-u UUID|-a|-b>"),
+			      N_("[-p password] [-k protector[:args]] <SOURCE|-u UUID|-a|-b>"),
 			      N_("Mount a crypto device."), options);
   grub_procfs_register ("luks_script", &luks_script);
 }
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 5/5] util/grub-protect: Add new tool
  2022-01-24 14:12 [PATCH 0/5] Automatic TPM Disk Unlock Hernan Gatta
                   ` (3 preceding siblings ...)
  2022-01-24 14:12 ` [PATCH 4/5] cryptodisk: Support key protectors Hernan Gatta
@ 2022-01-24 14:12 ` Hernan Gatta
  2022-01-25  5:45   ` Glenn Washburn
  2022-01-24 23:21 ` [PATCH 0/5] Automatic TPM Disk Unlock Didier Spaier
  2022-01-25 20:30 ` Glenn Washburn
  6 siblings, 1 reply; 21+ messages in thread
From: Hernan Gatta @ 2022-01-24 14:12 UTC (permalink / raw)
  To: grub-devel

From: Hernan Gatta <hegatta@microsoft.com>

To utilize the key protectors framework, there must be a way to protect
full-disk encryption keys in the first place. The grub-protect tool includes
support for the TPM2 key protector but other protectors that require setup ahead
of time can be supported in the future.

For the TPM2 key protector, the intended flow is for a user to have a LUKS 1 or
LUKS 2-protected fully-encrypted disk. The user then creates a new key file, say
by reading /dev/urandom into a file, and creates a new LUKS key slot for this
key. Then, the user invokes the grub-protect tool to seal this key file to a set
of PCRs using the system's TPM 2.0. The resulting sealed key file is stored in
an unencrypted partition such as the EFI System Partition (ESP) so that GRUB may
read it. The user also ensures the cryptomount command is included in GRUB's
boot script and that it carries the requisite --protector (or -p) parameter.

Sample usage:

$ dd if=/dev/urandom of=key count=32
$ sudo cryptsetup luksAddKey /dev/sdb1 key --pbkdf=pbkdf2 --hash=sha512

$ sudo grub-protect --action=add
--protector=tpm2
--tpm2-keyfile=key
--tpm2-outfile=/boot/efi/boot/grub2/sealed_key

Then, in the boot script (wrapped for display):

cryptomount -u b20f95d0834842bc9197bd78b36732f8
-p tpm2:keyfile=(hd0,gpt1)/boot/grub2/sealed_key

where the UUID corresponds to /dev/sdb1.

Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
---
 .gitignore          |    1 +
 Makefile.util.def   |   16 +
 configure.ac        |    1 +
 util/grub-protect.c | 1344 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1362 insertions(+)
 create mode 100644 util/grub-protect.c

diff --git a/.gitignore b/.gitignore
index f6a1bd0..852327d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,6 +167,7 @@ widthspec.bin
 /grub-ofpathname.exe
 /grub-probe
 /grub-probe.exe
+/grub-protect
 /grub-reboot
 /grub-render-label
 /grub-render-label.exe
diff --git a/Makefile.util.def b/Makefile.util.def
index 39b53b3..05f9f09 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -52,6 +52,9 @@ library = {
   common = grub-core/partmap/msdos.c;
   common = grub-core/fs/proc.c;
   common = grub-core/fs/archelp.c;
+  common = grub-core/tpm2/buffer.c;
+  common = grub-core/tpm2/mu.c;
+  common = grub-core/tpm2/tpm2.c;
 };
 
 library = {
@@ -207,6 +210,19 @@ program = {
 };
 
 program = {
+  name = grub-protect;
+  common = grub-core/osdep/init.c;
+  common = util/grub-protect.c;
+  common = util/probe.c;
+
+  ldadd = libgrubmods.a;
+  ldadd = libgrubgcry.a;
+  ldadd = libgrubkern.a;
+  ldadd = grub-core/lib/gnulib/libgnu.a;
+  ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
+};
+
+program = {
   name = grub-mkrelpath;
   mansection = 1;
 
diff --git a/configure.ac b/configure.ac
index 4f649ed..3a4dc5a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,6 +71,7 @@ grub_TRANSFORM([grub-mkpasswd-pbkdf2])
 grub_TRANSFORM([grub-mkrelpath])
 grub_TRANSFORM([grub-mkrescue])
 grub_TRANSFORM([grub-probe])
+grub_TRANSFORM([grub-protect])
 grub_TRANSFORM([grub-reboot])
 grub_TRANSFORM([grub-script-check])
 grub_TRANSFORM([grub-set-default])
diff --git a/util/grub-protect.c b/util/grub-protect.c
new file mode 100644
index 0000000..4e4bbd7
--- /dev/null
+++ b/util/grub-protect.c
@@ -0,0 +1,1344 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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 <config.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grub/crypto.h>
+#include <grub/emu/getroot.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/misc.h>
+#include <grub/tpm2/buffer.h>
+#include <grub/tpm2/mu.h>
+#include <grub/tpm2/tcg2.h>
+#include <grub/tpm2/tpm2.h>
+#include <grub/util/misc.h>
+
+#include "progname.h"
+
+typedef enum grub_protect_arg
+{
+  GRUB_PROTECT_ARG_ACTION          = 1 << 0,
+  GRUB_PROTECT_ARG_PROTECTOR       = 1 << 1,
+  GRUB_PROTECT_ARG_TPM2_DEVICE     = 1 << 2,
+  GRUB_PROTECT_ARG_TPM2_PCRS       = 1 << 3,
+  GRUB_PROTECT_ARG_TPM2_ASYMMETRIC = 1 << 4,
+  GRUB_PROTECT_ARG_TPM2_BANK       = 1 << 5,
+  GRUB_PROTECT_ARG_TPM2_SRK        = 1 << 6,
+  GRUB_PROTECT_ARG_TPM2_KEYFILE    = 1 << 7,
+  GRUB_PROTECT_ARG_TPM2_OUTFILE    = 1 << 8,
+  GRUB_PROTECT_ARG_TPM2_PERSIST    = 1 << 9,
+  GRUB_PROTECT_ARG_TPM2_EVICT      = 1 << 10
+} grub_protect_arg_t;
+
+typedef enum grub_protect_protector
+{
+  GRUB_PROTECT_TYPE_ERROR,
+  GRUB_PROTECT_TYPE_TPM2
+} grub_protect_protector_t;
+
+typedef enum grub_protect_action
+{
+  GRUB_PROTECT_ACTION_ERROR,
+  GRUB_PROTECT_ACTION_ADD,
+  GRUB_PROTECT_ACTION_REMOVE
+} grub_protect_action_t;
+
+struct grub_protect_args
+{
+  grub_protect_arg_t args;
+  grub_protect_action_t action;
+  grub_protect_protector_t protector;
+
+  const char *tpm2_device;
+  grub_uint8_t tpm2_pcrs[TPM_MAX_PCRS];
+  grub_uint8_t tpm2_pcr_count;
+  TPM_ALG_ID tpm2_asymmetric;
+  TPM_ALG_ID tpm2_bank;
+  TPM_HANDLE tpm2_srk;
+  const char *tpm2_keyfile;
+  const char *tpm2_outfile;
+  int tpm2_persist;
+  int tpm2_evict;
+};
+
+static int tpm2_fd = -1;
+
+static grub_err_t
+grub_protect_read_file (const char *filepath, void **buffer,
+                        size_t *buffer_size)
+{
+  grub_err_t err;
+  FILE *f;
+  long len;
+  void *buf;
+
+  f = fopen (filepath, "rb");
+  if (!f)
+    return GRUB_ERR_FILE_NOT_FOUND;
+
+  if (fseek (f, 0, SEEK_END))
+    {
+       err = GRUB_ERR_FILE_READ_ERROR;
+       goto exit1;
+    }
+
+  len = ftell (f);
+  if (!len)
+    {
+       err = GRUB_ERR_FILE_READ_ERROR;
+       goto exit1;
+    }
+
+  rewind (f);
+
+  buf = grub_malloc (len);
+  if (!buf)
+    {
+       err = GRUB_ERR_OUT_OF_MEMORY;
+       goto exit1;
+    }
+
+  if (fread (buf, len, 1, f) != 1)
+    {
+       err = GRUB_ERR_FILE_READ_ERROR;
+       goto exit2;
+    }
+
+  *buffer = buf;
+  *buffer_size = len;
+
+  buf = NULL;
+  err = GRUB_ERR_NONE;
+
+exit2:
+  grub_free (buf);
+
+exit1:
+  fclose (f);
+
+  return err;
+}
+
+static grub_err_t
+grub_protect_write_file (const char *filepath, void *buffer, size_t buffer_size)
+{
+  grub_err_t err;
+  FILE *f;
+
+  f = fopen (filepath, "wb");
+  if (!f)
+    return GRUB_ERR_FILE_NOT_FOUND;
+
+  if (fwrite (buffer, buffer_size, 1, f) != 1)
+  {
+    err = GRUB_ERR_WRITE_ERROR;
+    goto exit1;
+  }
+
+  err = GRUB_ERR_NONE;
+
+exit1:
+  fclose (f);
+
+  return err;
+}
+
+static grub_err_t
+grub_protect_get_grub_drive_for_file (const char *filepath, char **drive)
+{
+  grub_err_t err = GRUB_ERR_IO;
+  char *disk;
+  char **devices;
+  char *grub_dev;
+  char *grub_path;
+  char *efi_drive;
+  char *partition;
+  char *grub_drive;
+  grub_device_t dev;
+  grub_size_t grub_drive_len;
+  int n;
+
+  grub_path = grub_canonicalize_file_name (filepath);
+  if (!grub_path)
+    goto exit1;
+
+  devices = grub_guess_root_devices (grub_path);
+  if (!devices || !devices[0])
+    goto exit2;
+
+  disk = devices[0];
+
+  grub_util_pull_device (disk);
+
+  grub_dev = grub_util_get_grub_dev (disk);
+  if (!grub_dev)
+    goto exit3;
+
+  dev = grub_device_open (grub_dev);
+  if (!dev)
+    goto exit4;
+
+  efi_drive = grub_util_guess_efi_drive (disk);
+  if (!efi_drive)
+    goto exit5;
+
+  partition = grub_partition_get_name (dev->disk->partition);
+  if (!partition)
+    goto exit6;
+
+  grub_drive_len = grub_strlen (efi_drive) + grub_strlen (partition) + 3;
+  grub_drive = grub_malloc (grub_drive_len + 1);
+  if (!grub_drive)
+    goto exit7;
+
+  n = grub_snprintf (grub_drive, grub_drive_len + 1, "(%s,%s)", efi_drive,
+                     partition);
+  if (n != grub_drive_len)
+    goto exit8;
+
+  *drive = grub_drive;
+  grub_drive = NULL;
+  err = GRUB_ERR_NONE;
+
+exit8:
+  grub_free (grub_drive);
+
+exit7:
+  grub_free (partition);
+
+exit6:
+  grub_free (efi_drive);
+
+exit5:
+  grub_device_close (dev);
+
+exit4:
+  grub_free (grub_dev);
+
+exit3:
+  grub_free (devices);
+
+exit2:
+  grub_free (grub_path);
+
+exit1:
+  return err;
+}
+
+grub_err_t
+grub_tcg2_get_max_output_size (grub_size_t *size)
+{
+  if (!size)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  *size = GRUB_TPM2_BUFFER_CAPACITY;
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_tcg2_submit_command (grub_size_t input_size, grub_uint8_t *input,
+                          grub_size_t output_size, grub_uint8_t *output)
+{
+  static const grub_size_t header_size = sizeof (grub_uint16_t) +
+                                         (2 * sizeof(grub_uint32_t));
+
+  if (write (tpm2_fd, input, input_size) != input_size)
+    return GRUB_ERR_BAD_DEVICE;
+
+  if (read (tpm2_fd, output, output_size) < header_size)
+    return GRUB_ERR_BAD_DEVICE;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_open_device (const char *dev_node)
+{
+  if (tpm2_fd != -1)
+    return GRUB_ERR_NONE;
+
+  tpm2_fd = open (dev_node, O_RDWR);
+  if (tpm2_fd == -1)
+    {
+      fprintf (stderr, _("Could not open TPM device (Error: %u).\n"), errno);
+      return GRUB_ERR_FILE_NOT_FOUND;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_close_device (void)
+{
+  int err;
+
+  if (tpm2_fd == -1)
+    return GRUB_ERR_NONE;
+
+  err = close (tpm2_fd);
+  if (err)
+  {
+    fprintf (stderr, _("Could not close TPM device (Error: %u).\n"), errno);
+    return GRUB_ERR_IO;
+  }
+
+  tpm2_fd = -1;
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_get_policy_digest (struct grub_protect_args *args,
+                                     TPM2B_DIGEST *digest)
+{
+  TPM_RC rc;
+  TPML_PCR_SELECTION pcr_sel = {
+    .count = 1,
+    .pcrSelections = {
+      {
+        .hash = args->tpm2_bank,
+        .sizeOfSelect = 3,
+        .pcrSelect = { 0 }
+      },
+    }
+  };
+  TPML_PCR_SELECTION pcr_sel_out = { 0 };
+  TPML_DIGEST pcr_values = { 0 };
+  grub_uint8_t *pcr_digest;
+  grub_size_t pcr_digest_len;
+  grub_uint8_t *pcr_concat;
+  grub_size_t pcr_concat_len;
+  grub_uint8_t *pcr_cursor;
+  const gcry_md_spec_t *hash_spec;
+  TPM2B_NONCE nonce = { 0 };
+  TPM2B_ENCRYPTED_SECRET salt = { 0 };
+  TPMT_SYM_DEF symmetric = { 0 };
+  TPMI_SH_AUTH_SESSION session = 0;
+  TPM2B_DIGEST pcr_digest_in = {
+    .size = TPM_SHA256_DIGEST_SIZE,
+    .buffer = { 0 }
+  };
+  TPM2B_DIGEST policy_digest = { 0 };
+  grub_uint8_t i;
+  grub_err_t err;
+
+  /* PCR Read */
+  for (i = 0; i < args->tpm2_pcr_count; i++)
+    pcr_sel
+      .pcrSelections[0]
+      .pcrSelect[TPM2_PCR_TO_SELECT(args->tpm2_pcrs[i])]
+        |= TPM2_PCR_TO_BIT(args->tpm2_pcrs[i]);
+
+  rc = TPM2_PCR_Read (NULL, &pcr_sel, NULL, &pcr_sel_out, &pcr_values, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      fprintf (stderr, _("Failed to read PCRs (TPM error: 0x%x).\n"), rc);
+      return GRUB_ERR_BAD_DEVICE;
+    }
+
+  if ((pcr_sel_out.count != pcr_sel.count) ||
+       (pcr_sel.pcrSelections[0].sizeOfSelect !=
+        pcr_sel_out.pcrSelections[0].sizeOfSelect))
+    {
+      fprintf (stderr, _("Could not read all the specified PCRs.\n"));
+      return GRUB_ERR_BAD_DEVICE;
+    }
+
+  /* Compute PCR Digest */
+  switch (args->tpm2_bank)
+    {
+    case TPM_ALG_SHA1:
+      pcr_digest_len = TPM_SHA1_DIGEST_SIZE;
+      hash_spec = GRUB_MD_SHA1;
+      break;
+    case TPM_ALG_SHA256:
+      pcr_digest_len = TPM_SHA256_DIGEST_SIZE;
+      hash_spec = GRUB_MD_SHA256;
+      break;
+    default:
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  pcr_digest = grub_malloc (pcr_digest_len);
+  if (!pcr_digest)
+    {
+      fprintf (stderr, _("Failed to allocate PCR digest buffer.\n"));
+      return GRUB_ERR_OUT_OF_MEMORY;
+    }
+
+  pcr_concat_len = pcr_digest_len * args->tpm2_pcr_count;
+  pcr_concat = grub_malloc (pcr_concat_len);
+  if (!pcr_concat)
+    {
+      err = GRUB_ERR_OUT_OF_MEMORY;
+      fprintf (stderr, _("Failed to allocate PCR concatenation buffer.\n"));
+      goto exit1;
+    }
+
+  pcr_cursor = pcr_concat;
+  for (i = 0; i < args->tpm2_pcr_count; i++)
+    {
+      if (pcr_values.digests[i].size != pcr_digest_len)
+        {
+          fprintf (stderr,
+                   _("Bad PCR value size: expected %lu bytes but got %u bytes.\n"),
+                   pcr_digest_len, pcr_values.digests[i].size);
+          goto exit2;
+        }
+
+      grub_memcpy (pcr_cursor, pcr_values.digests[i].buffer, pcr_digest_len);
+      pcr_cursor += pcr_digest_len;
+    }
+
+  grub_crypto_hash (hash_spec, pcr_digest, pcr_concat, pcr_concat_len);
+
+  /* Start Trial Session */
+  nonce.size = TPM_SHA256_DIGEST_SIZE;
+  symmetric.algorithm = TPM_ALG_NULL;
+
+  rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, 0, &nonce, &salt,
+                              TPM_SE_TRIAL, &symmetric, TPM_ALG_SHA256,
+                              &session, NULL, 0);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      fprintf (stderr,
+               _("Failed to start trial policy session (TPM error: 0x%x).\n"),
+               rc);
+      err = GRUB_ERR_BAD_DEVICE;
+      goto exit2;
+    }
+
+  /* PCR Policy */
+  memcpy (pcr_digest_in.buffer, pcr_digest, TPM_SHA256_DIGEST_SIZE);
+
+  rc = TPM2_PolicyPCR (session, NULL, &pcr_digest_in, &pcr_sel, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      fprintf (stderr, _("Failed to submit PCR policy (TPM error: 0x%x).\n"),
+               rc);
+      err = GRUB_ERR_BAD_DEVICE;
+      goto exit3;
+    }
+
+  /* Retrieve Policy Digest */
+  rc = TPM2_PolicyGetDigest (session, NULL, &policy_digest, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      fprintf (stderr, _("Failed to get policy digest (TPM error: 0x%x).\n"),
+               rc);
+      err = GRUB_ERR_BAD_DEVICE;
+      goto exit3;
+    }
+
+  /* Epilogue */
+  *digest = policy_digest;
+  err = GRUB_ERR_NONE;
+
+exit3:
+  TPM2_FlushContext (session);
+
+exit2:
+  grub_free (pcr_concat);
+
+exit1:
+  grub_free (pcr_digest);
+
+  return err;
+}
+
+static grub_err_t
+grub_protect_tpm2_get_srk (struct grub_protect_args *args, TPM_HANDLE *srk)
+{
+  TPM_RC rc;
+  TPM2B_PUBLIC public;
+  TPMS_AUTH_COMMAND authCommand = { 0 };
+  TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
+  TPM2B_PUBLIC inPublic = { 0 };
+  TPM2B_DATA outsideInfo = { 0 };
+  TPML_PCR_SELECTION creationPcr = { 0 };
+  TPM2B_PUBLIC outPublic = { 0 };
+  TPM2B_CREATION_DATA creationData = { 0 };
+  TPM2B_DIGEST creationHash = { 0 };
+  TPMT_TK_CREATION creationTicket = { 0 };
+  TPM2B_NAME srkName = { 0 };
+  TPM_HANDLE srkHandle;
+
+  /* Find SRK */
+  rc = TPM2_ReadPublic (args->tpm2_srk, NULL, &public);
+  if (rc == TPM_RC_SUCCESS)
+    {
+      if (args->tpm2_persist)
+        fprintf (stderr,
+                 _("Warning: --tpm2-persist was specified but the SRK already exists on the TPM. Continuing anyway...\n"));
+
+      *srk = TPM2_SRK_HANDLE;
+      return GRUB_ERR_NONE;
+    }
+
+  /* The handle exists but its public area could not be read. */
+  if ((rc & ~TPM_RC_N_MASK) != TPM_RC_HANDLE)
+    {
+      fprintf (stderr,
+               _("The SRK exists on the TPM but its public area cannot be read (TPM error: 0x%x).\n"),
+               rc);
+      return GRUB_ERR_BAD_DEVICE;
+    }
+
+  /* Create SRK */
+  authCommand.sessionHandle = TPM_RS_PW;
+  inPublic.publicArea.type = args->tpm2_asymmetric;
+  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;
+
+  switch (args->tpm2_asymmetric)
+    {
+    case 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 = 2048;
+      inPublic.publicArea.parameters.rsaDetail.exponent = 0;
+      break;
+
+    case 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 = TPM_ECC_NIST_P256;
+      inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+      break;
+
+    default:
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  rc = TPM2_CreatePrimary (TPM_RH_OWNER, &authCommand, &inSensitive, &inPublic,
+                           &outsideInfo, &creationPcr, &srkHandle, &outPublic,
+                           &creationData, &creationHash, &creationTicket,
+                           &srkName, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      fprintf (stderr, _("Failed to create SRK (TPM error: 0x%x).\n"), rc);
+      return GRUB_ERR_BAD_DEVICE;
+    }
+
+  /* Persist SRK */
+  if (args->tpm2_persist)
+    {
+      rc = TPM2_EvictControl (TPM_RH_OWNER, srkHandle, args->tpm2_srk,
+                              &authCommand, NULL);
+      if (rc == TPM_RC_SUCCESS)
+        {
+          TPM2_FlushContext (srkHandle);
+          srkHandle = args->tpm2_srk;
+        }
+      else
+        fprintf (stderr,
+                 _("Warning: Failed to persist SRK (TPM error: 0x%x\n). Continuing anyway...\n"),
+                 rc);
+    }
+
+  /* Epilogue */
+  *srk = srkHandle;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_seal (TPM2B_DIGEST *policyDigest, TPM_HANDLE srk,
+                        grub_uint8_t *clearText, grub_size_t clearTextLength,
+                        TPM2_SEALED_KEY *sealed_key)
+{
+  TPM_RC rc;
+  TPMS_AUTH_COMMAND authCommand = { 0 };
+  TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
+  TPM2B_PUBLIC inPublic  = { 0 };
+  TPM2B_DATA outsideInfo = { 0 };
+  TPML_PCR_SELECTION pcr_sel = { 0 };
+  TPM2B_PRIVATE outPrivate = { 0 };
+  TPM2B_PUBLIC outPublic = { 0 };
+
+  /* Seal Data */
+  authCommand.sessionHandle = TPM_RS_PW;
+
+  inSensitive.sensitive.data.size = clearTextLength;
+  memcpy(inSensitive.sensitive.data.buffer, clearText, clearTextLength);
+
+  inPublic.publicArea.type = TPM_ALG_KEYEDHASH;
+  inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
+  inPublic.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM_ALG_NULL;
+  inPublic.publicArea.authPolicy = *policyDigest;
+
+  rc = TPM2_Create (srk, &authCommand, &inSensitive, &inPublic, &outsideInfo,
+                    &pcr_sel, &outPrivate, &outPublic, NULL, NULL, NULL, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      fprintf (stderr, _("Failed to seal key (TPM error: 0x%x).\n"), rc);
+      return GRUB_ERR_BAD_DEVICE;
+    }
+
+  /* Epilogue */
+  sealed_key->public = outPublic;
+  sealed_key->private = outPrivate;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_export_sealed_key (const char *filepath,
+                                     TPM2_SEALED_KEY *sealed_key)
+{
+  grub_err_t err;
+  struct grub_tpm2_buffer buf;
+
+  grub_tpm2_buffer_init (&buf);
+  grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&buf, &sealed_key->public);
+  grub_tpm2_mu_TPM2B_Marshal (&buf, sealed_key->private.size,
+                              sealed_key->private.buffer);
+  if (buf.error)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  err = grub_protect_write_file (filepath, buf.data, buf.size);
+  if (err)
+    fprintf (stderr, _("Could not write sealed key file (Error: %u).\n"),
+             errno);
+
+  return err;
+}
+
+static grub_err_t
+grub_protect_tpm2_add (struct grub_protect_args *args)
+{
+  grub_err_t err;
+  grub_uint8_t *key;
+  grub_size_t key_size;
+  TPM_HANDLE srk;
+  TPM2B_DIGEST policy_digest;
+  TPM2_SEALED_KEY sealed_key;
+  char *grub_drive = NULL;
+
+  grub_protect_get_grub_drive_for_file (args->tpm2_outfile, &grub_drive);
+
+  err = grub_protect_tpm2_open_device (args->tpm2_device);
+  if (err)
+    return err;
+
+  err = grub_protect_read_file (args->tpm2_keyfile, (void **)&key, &key_size);
+  if (err)
+    goto exit1;
+
+  if (key_size > TPM_MAX_SYM_DATA)
+  {
+    fprintf (stderr,
+             _("Input key is too long, maximum allowed size is %u bytes.\n"),
+             TPM_MAX_SYM_DATA);
+    return GRUB_ERR_OUT_OF_RANGE;
+  }
+
+  err = grub_protect_tpm2_get_srk (args, &srk);
+  if (err)
+    goto exit2;
+
+  err = grub_protect_tpm2_get_policy_digest (args, &policy_digest);
+  if (err)
+    goto exit3;
+
+  err = grub_protect_tpm2_seal (&policy_digest, srk, key, key_size,
+                                &sealed_key);
+  if (err)
+    goto exit3;
+
+  err = grub_protect_tpm2_export_sealed_key (args->tpm2_outfile, &sealed_key);
+  if (err)
+    goto exit3;
+
+  if (grub_drive)
+    {
+      printf (_("GRUB drive for the sealed key file: %s\n"), grub_drive);
+      grub_free (grub_drive);
+    }
+  else
+    {
+      fprintf (stderr,
+               _("Warning: Could not determine GRUB drive for sealed key file.\n"));
+      err = GRUB_ERR_NONE;
+    }
+
+exit3:
+  TPM2_FlushContext (srk);
+
+exit2:
+  grub_free (key);
+
+exit1:
+  grub_protect_tpm2_close_device ();
+
+  return err;
+}
+
+static grub_err_t
+grub_protect_tpm2_remove (struct grub_protect_args *args)
+{
+  TPM_RC rc;
+  TPM2B_PUBLIC public;
+  TPMS_AUTH_COMMAND authCommand = { 0 };
+  grub_err_t err;
+
+  if (!args->tpm2_evict)
+    {
+      printf (_("--tpm2-evict not specified, nothing to do.\n"));
+      return GRUB_ERR_NONE;
+    }
+
+  err = grub_protect_tpm2_open_device (args->tpm2_device);
+  if (err)
+    return err;
+
+  /* Find SRK */
+  rc = TPM2_ReadPublic (args->tpm2_srk, NULL, &public);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      fprintf (stderr, _("SRK with handle 0x%x not found.\n"), args->tpm2_srk);
+      err = GRUB_ERR_BAD_ARGUMENT;
+      goto exit1;
+    }
+
+  /* Evict SRK */
+  authCommand.sessionHandle = TPM_RS_PW;
+
+  rc = TPM2_EvictControl (TPM_RH_OWNER, args->tpm2_srk, args->tpm2_srk,
+                          &authCommand, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      fprintf (stderr,
+               _("Failed to evict SRK with handle 0x%x (TPM Error: 0x%x).\n"),
+               args->tpm2_srk, rc);
+      err = GRUB_ERR_BAD_DEVICE;
+      goto exit2;
+    }
+
+  err = GRUB_ERR_NONE;
+
+exit2:
+  TPM2_FlushContext (args->tpm2_srk);
+
+exit1:
+  grub_protect_tpm2_close_device ();
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_tpm2_run (struct grub_protect_args *args)
+{
+  switch (args->action)
+    {
+    case GRUB_PROTECT_ACTION_ADD:
+      return grub_protect_tpm2_add (args);
+
+    case GRUB_PROTECT_ACTION_REMOVE:
+      return grub_protect_tpm2_remove (args);
+
+    default:
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+}
+
+static grub_err_t
+grub_protect_tpm2_args_verify (struct grub_protect_args *args)
+{
+  switch (args->action)
+    {
+    case GRUB_PROTECT_ACTION_ADD:
+      if (args->args & GRUB_PROTECT_ARG_TPM2_EVICT)
+        {
+          fprintf (stderr,
+                   _("--tpm2-evict is invalid when --action is 'add'.\n"));
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      if (!args->tpm2_keyfile)
+        {
+          fprintf (stderr, _("--tpm2-keyfile must be specified.\n"));
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      if (!args->tpm2_outfile)
+        {
+          fprintf (stderr, _("--tpm2-outfile must be specified.\n"));
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      if (!args->tpm2_device)
+        args->tpm2_device = "/dev/tpm0";
+
+      if (!args->tpm2_pcr_count)
+        {
+          args->tpm2_pcrs[0] = 7;
+          args->tpm2_pcr_count = 1;
+        }
+
+      if (!args->tpm2_srk)
+        args->tpm2_srk = TPM2_SRK_HANDLE;
+
+      if (!args->tpm2_asymmetric)
+        args->tpm2_asymmetric = TPM_ALG_RSA;
+
+      if (!args->tpm2_bank)
+        args->tpm2_bank = TPM_ALG_SHA256;
+
+      break;
+
+    case GRUB_PROTECT_ACTION_REMOVE:
+      if (args->args & GRUB_PROTECT_ARG_TPM2_ASYMMETRIC)
+        {
+          fprintf (stderr,
+                   _("--tpm2-asymmetric is invalid when --action is 'remove'.\n"));
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      if (args->args & GRUB_PROTECT_ARG_TPM2_BANK)
+        {
+          fprintf (stderr,
+                   _("--tpm2-bank is invalid when --action is 'remove'.\n"));
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      if (args->args & GRUB_PROTECT_ARG_TPM2_KEYFILE)
+        {
+          fprintf (stderr,
+                   _("--tpm2-keyfile is invalid when --action is 'remove'.\n"));
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      if (args->args & GRUB_PROTECT_ARG_TPM2_OUTFILE)
+        {
+          fprintf (stderr,
+                   _("--tpm2-outfile is invalid when --action is 'remove'.\n"));
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      if (args->args & GRUB_PROTECT_ARG_TPM2_PCRS)
+        {
+          fprintf (stderr,
+                   _("--tpm2-pcrs is invalid when --action is 'remove'.\n"));
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      if (args->args & GRUB_PROTECT_ARG_TPM2_PERSIST)
+        {
+          fprintf (stderr,
+                   _("--tpm2-persist is invalid when --action is 'remove'.\n"));
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+
+      if (!args->tpm2_device)
+        args->tpm2_device = "/dev/tpm0";
+
+      if (!args->tpm2_srk)
+        args->tpm2_srk = TPM2_SRK_HANDLE;
+
+      break;
+
+    default:
+      fprintf (stderr,
+               _("The TPM2 key protector only supports the following actions: add, remove.\n"));
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protector_args_kvp_parse (char *kvp, const char **key, char **value)
+{
+  char divider;
+  char *separator;
+  grub_size_t len;
+
+  if (!kvp)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  len = grub_strlen (kvp);
+  if (!len || len < 2)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (kvp[0] == '-' && kvp[1] != '-')
+    divider = ' ';
+  else if (kvp[0] == '-' && kvp[1] == '-')
+    divider = '=';
+  else
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  separator = grub_strchr (kvp, divider);
+  if (!separator)
+    goto exit;
+
+  *separator = '\0';
+  separator += 1;
+
+  if (separator - kvp >= len)
+  {
+    separator = NULL;
+    goto exit;
+  }
+
+exit:
+  *key = kvp;
+  *value = separator;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_args_parse (int argc, char *argv[],
+                         struct grub_protect_args *args)
+{
+  grub_err_t err;
+  char *current_arg;
+  const char *key;
+  char *value;
+  int i;
+
+  for (i = 1; i < argc; i++)
+    {
+      current_arg = argv[i];
+
+      err = grub_protector_args_kvp_parse (current_arg, &key, &value);
+      if (err)
+        return err;
+
+      if (grub_strcmp (key, "--action") == 0 ||
+          grub_strcmp (key, "-a") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_ACTION)
+            {
+              fprintf (stderr,
+                       _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (!value)
+            {
+              fprintf (stderr, _("Argument %s requires a value.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (grub_strcmp (value, "add") == 0)
+            args->action = GRUB_PROTECT_ACTION_ADD;
+          else if (grub_strcmp (value, "remove") == 0)
+            args->action = GRUB_PROTECT_ACTION_REMOVE;
+          else
+            {
+              fprintf (stderr, _("'%s' is not a valid action.\n"), value);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          args->args |= GRUB_PROTECT_ARG_ACTION;
+        }
+      else if (grub_strcmp (key, "--protector") == 0 ||
+               grub_strcmp (key, "-p") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_PROTECTOR)
+            {
+              fprintf (stderr, _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (!value)
+            {
+              fprintf (stderr, _("Argument %s requires a value.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (grub_strcmp (value, "tpm2") == 0)
+            args->protector = GRUB_PROTECT_TYPE_TPM2;
+          else
+            {
+              fprintf (stderr, _("'%s' is not a valid protector.\n"), value);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          args->args |= GRUB_PROTECT_ARG_PROTECTOR;
+        }
+      else if (grub_strcmp (key, "--tpm2-device") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_TPM2_DEVICE)
+            {
+              fprintf (stderr, _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (!value)
+            {
+              fprintf (stderr, _("Argument %s requires a value.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          args->tpm2_device = value;
+          args->args |= GRUB_PROTECT_ARG_TPM2_DEVICE;
+        }
+      else if (grub_strcmp (key, "--tpm2-pcrs") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_TPM2_PCRS)
+            {
+              fprintf (stderr, _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (!value)
+            {
+              fprintf (stderr, _("Argument %s requires a value.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          grub_size_t len = grub_strlen (value);
+          grub_size_t dist;
+
+          long pcr;
+          char *next_pcr;
+          char *current_pcr = value;
+          grub_uint8_t pcr_u8;
+          grub_uint8_t j;
+          grub_uint8_t k;
+
+          for (j = 0;; j++)
+            {
+              next_pcr = grub_strchr (current_pcr, ',');
+              if (next_pcr)
+                *next_pcr = '\0';
+
+              grub_errno = GRUB_ERR_NONE;
+              pcr = grub_strtoul (current_pcr, NULL, 0);
+              if (grub_errno != GRUB_ERR_NONE)
+                {
+                  fprintf (stderr, _("'%s' is not a valid PCR number.\n"),
+                           current_pcr);
+                  return grub_errno;
+                }
+
+              if (pcr > TPM_MAX_PCRS)
+                {
+                  fprintf (stderr,
+                           _("%lu larger than the maximum number of PCRs.\n"),
+                           pcr);
+                  return GRUB_ERR_OUT_OF_RANGE;
+                }
+
+              pcr_u8 = (grub_uint8_t)pcr;
+
+              for (k = 0; k < args->tpm2_pcr_count; k++)
+              {
+                if (args->tpm2_pcrs[k] == pcr_u8)
+                  {
+                    fprintf (stderr, _("PCR %u was already specified.\n"),
+                             pcr_u8);
+                    return GRUB_ERR_BAD_ARGUMENT;
+                  }
+              }
+
+              args->tpm2_pcrs[j] = pcr_u8;
+              args->tpm2_pcr_count++;
+
+              if (!next_pcr)
+                break;
+
+              current_pcr = next_pcr + 1;
+
+              dist = (current_pcr - value);
+              if (dist >= len)
+                break;
+            }
+
+          args->args |= GRUB_PROTECT_ARG_TPM2_PCRS;
+        }
+      else if (grub_strcmp (key, "--tpm2-srk") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_TPM2_SRK)
+            {
+              fprintf (stderr, _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (!value)
+            {
+              fprintf (stderr, _("Argument %s requires a value.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          long srk;
+
+          grub_errno = GRUB_ERR_NONE;
+          srk = grub_strtoul (value, NULL, 0);
+          if (grub_errno != GRUB_ERR_NONE)
+            {
+              fprintf (stderr, _("'%s' is not a valid SRK handle.\n"), value);
+              return grub_errno;
+            }
+
+          if (srk > GRUB_UINT_MAX)
+            {
+              fprintf (stderr,
+                       _("%lu is too large to be a valid SRK handle.\n"), srk);
+              return GRUB_ERR_OUT_OF_RANGE;
+            }
+
+          args->tpm2_srk = (TPM_HANDLE)srk;
+          args->args |= GRUB_PROTECT_ARG_TPM2_SRK;
+        }
+      else if (grub_strcmp (key, "--tpm2-asymmetric") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_TPM2_ASYMMETRIC)
+            {
+              fprintf (stderr, _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (!value)
+            {
+              fprintf (stderr, _("Argument %s requires a value.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (grub_strcasecmp (value, "ECC") == 0)
+            args->tpm2_asymmetric = TPM_ALG_ECC;
+          else if (grub_strcasecmp (value, "RSA") == 0)
+            args->tpm2_asymmetric = TPM_ALG_RSA;
+          else
+            {
+              fprintf (stderr, _("'%s' is not a valid asymmetric algorithm.\n"),
+                       value);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          args->args |= GRUB_PROTECT_ARG_TPM2_ASYMMETRIC;
+        }
+      else if (grub_strcmp (key, "--tpm2-bank") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_TPM2_BANK)
+            {
+              fprintf (stderr, _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (!value)
+            {
+              fprintf (stderr, _("Argument %s requires a value.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (grub_strcasecmp (value, "SHA1") == 0)
+            args->tpm2_bank = TPM_ALG_SHA1;
+          else if (grub_strcasecmp (value, "SHA256") == 0)
+            args->tpm2_bank = TPM_ALG_SHA256;
+          else
+            {
+              fprintf (stderr, _("'%s' is not a valid PCR bank.\n"), value);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          args->args |= GRUB_PROTECT_ARG_TPM2_BANK;
+        }
+      else if (grub_strcmp (key, "--tpm2-keyfile") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_TPM2_KEYFILE)
+            {
+              fprintf (stderr, _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (!value)
+            {
+              fprintf (stderr, _("Argument %s requires a value.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+	  args->tpm2_keyfile = value;
+          args->args |= GRUB_PROTECT_ARG_TPM2_KEYFILE;
+        }
+      else if (grub_strcmp (key, "--tpm2-outfile") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_TPM2_OUTFILE)
+            {
+              fprintf (stderr, _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (!value)
+            {
+              fprintf (stderr, _("Argument %s requires a value.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          args->tpm2_outfile = value;
+          args->args |= GRUB_PROTECT_ARG_TPM2_OUTFILE;
+        }
+      else if (grub_strcmp (key, "--tpm2-persist") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_TPM2_PERSIST)
+            {
+              fprintf (stderr, _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (value)
+            {
+              fprintf (stderr, _("Argument %s does not accept a value.\n"),
+                       key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          args->tpm2_persist = 1;
+          args->args |= GRUB_PROTECT_ARG_TPM2_PERSIST;
+        }
+      else if (grub_strcmp (key, "--tpm2-evict") == 0)
+        {
+          if (args->args & GRUB_PROTECT_ARG_TPM2_EVICT)
+            {
+              fprintf (stderr, _("Argument %s was already specified.\n"), key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          if (value)
+            {
+              fprintf (stderr, _("Argument %s does not accept a value.\n"),
+                       key);
+              return GRUB_ERR_BAD_ARGUMENT;
+            }
+
+          args->tpm2_evict = 1;
+          args->args |= GRUB_PROTECT_ARG_TPM2_EVICT;
+        }
+        else
+          {
+            fprintf (stderr, _("'%s' is not a valid argument.\n"), key);
+            return GRUB_ERR_BAD_ARGUMENT;
+          }
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_args_verify (struct grub_protect_args *args)
+{
+  if (args->action == GRUB_PROTECT_ACTION_ERROR)
+    {
+      fprintf (stderr, "--action is mandatory.\n");
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  /* At the moment, the only configurable key protector is the TPM2 one, so it
+   * is the only keu protector supported by this tool. */
+  if (args->protector != GRUB_PROTECT_TYPE_TPM2)
+    {
+      fprintf (stderr,
+               _("--protector is mandatory and only 'tpm2' is currently supported.\n"));
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  switch (args->protector)
+    {
+    case GRUB_PROTECT_TYPE_TPM2:
+      return grub_protect_tpm2_args_verify (args);
+    default:
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_protect_dispatch (struct grub_protect_args *args)
+{
+  switch (args->protector)
+    {
+    case GRUB_PROTECT_TYPE_TPM2:
+      return grub_protect_tpm2_run (args);
+    default:
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+}
+
+static void
+grub_protect_init (int *argc, char **argv[])
+{
+  grub_util_host_init (argc, argv);
+
+  grub_util_biosdisk_init (NULL);
+
+  grub_init_all ();
+  grub_gcry_init_all ();
+
+  grub_lvm_fini ();
+  grub_mdraid09_fini ();
+  grub_mdraid1x_fini ();
+  grub_diskfilter_fini ();
+  grub_diskfilter_init ();
+  grub_mdraid09_init ();
+  grub_mdraid1x_init ();
+  grub_lvm_init ();
+}
+
+static void
+grub_protect_fini (void)
+{
+  grub_gcry_fini_all ();
+  grub_fini_all ();
+  grub_util_biosdisk_fini ();
+}
+
+int
+main (int argc, char *argv[])
+{
+  grub_err_t err;
+  struct grub_protect_args args = { 0 };
+
+  grub_protect_init (&argc, &argv);
+
+  err = grub_protect_args_parse (argc, argv, &args);
+  if (err)
+    goto exit;
+
+  err = grub_protect_args_verify (&args);
+  if (err)
+    goto exit;
+
+  err = grub_protect_dispatch (&args);
+  if (err)
+    goto exit;
+
+exit:
+  grub_protect_fini ();
+
+  return err;
+}
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH 0/5] Automatic TPM Disk Unlock
  2022-01-24 14:12 [PATCH 0/5] Automatic TPM Disk Unlock Hernan Gatta
                   ` (4 preceding siblings ...)
  2022-01-24 14:12 ` [PATCH 5/5] util/grub-protect: Add new tool Hernan Gatta
@ 2022-01-24 23:21 ` Didier Spaier
  2022-01-25 20:30 ` Glenn Washburn
  6 siblings, 0 replies; 21+ messages in thread
From: Didier Spaier @ 2022-01-24 23:21 UTC (permalink / raw)
  To: grub-devel

Hi,

Le 24/01/2022 à 15:12, Hernan Gatta a écrit :
> This patch series adds support for automatically unlocking fully-encrypted disks
> using a TPM 2.0.
> 
> Currently, when GRUB encounters a fully-encrypted disk that it must access, its
> corresponding cryptodisk module (LUKS 1, LUKS2, or GELI) interactively prompts
> the user for a passphrase. An improvement to the boot process would be for GRUB
> to automatically retrieve the unlocking key for fully-encrypted disks from a
> protected location and to unlock these transparently. To this end, a TPM may be
> used to protect the unlocking key to a known-good state of the platform. Once
> the key is protected in this way, assuming that the platform remains
> trustworthy, GRUB can then utilize the TPM to release the key during boot and
> thus unlock fully-encrypted disks without user interaction. Such a model would
> not only be more convenient for end-users but also for virtual machines in cloud
> environments where no user is ever present.
> 
> Design
> ------
> 
> This patchset first adds a key protectors framework. This framework allows for
> key protector modules to register when loaded. A key protector is defined as a
> module that knows how to retrieve an unlocking key from a specific source. This
> patchset adds a single such key protector module that understands how to
> retrieve an unlocking key from a TPM 2.0 by unsealing a sealed key file via a
> Storage Root Key (SRK).
> 
> Additionally, this patchset expands the cryptomount command to accept a key
> protector parameter. This parameter carries the information necessary to select
> and parameterize a key protector to be used to retrieve an unlocking key for the
> disk in question. That is, given an invocation of cryptomount to mount a
> specific disk (e.g., "cryptomount (hd0,gpt2)", "cryptomount -u UUID"), a key
> protector can be used to automatically retrieve an unlocking key without an
> interactive prompt.
> 
> Lastly, this patchset also includes a new tool, grub-protect, that allows the
> user to seal a key file against a set of Platform Configuration Registers (PCRs)
> using an SRK. This sealed key file is expected to be stored in an unencrypted
> partition, such as the EFI System Partition (ESP), where GRUB can read it. The
> sealed key is then unsealed by the TPM2 key protector automatically, provided
> that the PCRs selected match on subsequent boots.

Sorry for a newbie question (I plan to allow installing Slint on a Secure Boot
enabled machine if/when I can but know almost nothing yet on this topic).

Currently we allow in the "auto" mode of installation to install Slint in a
fully encrypted drive (minus the ESP and the BIOS Boot partition), the user
typing then a passphrase only once when politely requested by GRUB before
displaying its menu (without using LVM as we store a LUKS key in the initramfs).

The main purpose is to forbid access to the system when the machine is powered
off, for instance in case of a laptop stolen during a travel.

Would the feature you describe possibly allow to circumvent this protection?

Thanks,
Didier
--
Didier Spaier
Slint maintainer


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 1/5] protectors: Add key protectors framework
  2022-01-24 14:12 ` [PATCH 1/5] protectors: Add key protectors framework Hernan Gatta
@ 2022-01-25  2:54   ` Glenn Washburn
  2022-01-27 14:05     ` Hernan Gatta
  0 siblings, 1 reply; 21+ messages in thread
From: Glenn Washburn @ 2022-01-25  2:54 UTC (permalink / raw)
  To: Hernan Gatta; +Cc: The development of GNU GRUB

On Mon, 24 Jan 2022 06:12:14 -0800
Hernan Gatta <hegatta@linux.microsoft.com> wrote:

> From: Hernan Gatta <hegatta@microsoft.com>
> 
> A key protector encapsulates functionality to retrieve an unlocking key for a
> fully-encrypted disk from a specific source. A key protector module registers
> itself with the key protectors framework when it is loaded and unregisters when
> unloaded. Additionally, a key protector may accept parameters that describe how
> it should operate.
> 
> The key protectors framework, besides offering registration and unregistration
> functions, also offers a one-stop routine for finding and invoking a key
> protector. This method accepts a formatted string with the name of a key
> protector followed optionally by colon-separated, key protector-specific
> parameters. If a key protector with the specified name exists and if an
> unlocking key is successfully retrieved by the latter, the function returns to
> the caller the retrieved key and its length.
> 
> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
> ---
>  grub-core/Makefile.am       |  1 +
>  grub-core/Makefile.core.def |  1 +
>  grub-core/kern/protectors.c | 98 +++++++++++++++++++++++++++++++++++++++++++++
>  include/grub/protector.h    | 55 +++++++++++++++++++++++++
>  4 files changed, 155 insertions(+)
>  create mode 100644 grub-core/kern/protectors.c
>  create mode 100644 include/grub/protector.h
> 
> diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
> index ee88e44..f78cd9d 100644
> --- a/grub-core/Makefile.am
> +++ b/grub-core/Makefile.am
> @@ -90,6 +90,7 @@ endif
>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm.h
>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/parser.h
>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/partition.h
> +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/protector.h
>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/stack_protector.h
>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h
>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 8022e1c..e4ae78b 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -138,6 +138,7 @@ kernel = {
>    common = kern/misc.c;
>    common = kern/parser.c;
>    common = kern/partition.c;
> +  common = kern/protectors.c;
>    common = kern/rescue_parser.c;
>    common = kern/rescue_reader.c;
>    common = kern/term.c;
> diff --git a/grub-core/kern/protectors.c b/grub-core/kern/protectors.c
> new file mode 100644
> index 0000000..2df0c60
> --- /dev/null
> +++ b/grub-core/kern/protectors.c
> @@ -0,0 +1,98 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2022 Microsoft Corporation
> + *
> + *  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 <grub/protector.h>
> +
> +struct grub_key_protector *grub_key_protectors = NULL;
> +
> +grub_err_t
> +grub_key_protector_register (struct grub_key_protector *protector)
> +{
> +  if (!protector || !protector->name || !grub_strlen(protector->name))
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (grub_key_protectors &&
> +      grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
> +                            protector->name))
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  grub_list_push (GRUB_AS_LIST_P (&grub_key_protectors),
> +                  GRUB_AS_LIST (protector));
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +grub_err_t
> +grub_key_protector_unregister (struct grub_key_protector *protector)
> +{
> +  if (!protector)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  grub_list_remove (GRUB_AS_LIST (protector));
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +grub_err_t
> +grub_key_protector_recover_key (char *args, grub_uint8_t **key,
> +                                grub_size_t *key_size)
> +{
> +  char *first_separator = NULL;
> +  char *protector_args = NULL;
> +  struct grub_key_protector *protector = NULL;
> +
> +  if (!grub_key_protectors)
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  if (!args || !grub_strlen (args))
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  /* Find the position of the first parameter separator: the stuff before it is
> +   * the name of the requested key protector and the stuff after it are the
> +   * parameters for said key protector, if any. */
> +  first_separator = grub_strchr (args, ':');
> +  if (first_separator)
> +    {
> +      /* Reject a separator at the very beginning. */
> +      if (first_separator == args)
> +        return GRUB_ERR_BAD_ARGUMENT;
> +
> +      /* Having a lone colon after the name of the key protector with no
> +       * further parameters thereafter does not make any sense. */
> +      if (*(first_separator + 1) == '\0')
> +        return GRUB_ERR_BAD_ARGUMENT;

This should be allowed, its an unnecessary restriction. For instance,
it prevents building this string without logic to determine if a colon
is needed or not. Instead of this if block, I suggest having...

> +
> +      /* Consume the colon, effectively splitting 'args' in two. */
> +      first_separator[0] = '\0';
> +

... here, an if with the opposite conditional.

if (*(first_separator + 1) != '\0')

> +      /* The protector-specific arguments are after the first colon. */
> +      protector_args = first_separator + 1;
> +    }
> +
> +  /* Try to find a key protector with the specified name. */
> +  protector = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
> +                                    args);
> +  if (!protector)
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  /* 'protector_args' may be NULL. */
> +  return protector->recover_key (protector_args, key, key_size);
> +}
> diff --git a/include/grub/protector.h b/include/grub/protector.h
> new file mode 100644
> index 0000000..d445a33
> --- /dev/null
> +++ b/include/grub/protector.h
> @@ -0,0 +1,55 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2022 Microsoft Corporation
> + *
> + *  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_PROTECTOR_HEADER
> +#define GRUB_PROTECTOR_HEADER 1
> +
> +#include <grub/err.h>
> +#include <grub/types.h>
> +
> +enum grub_key_protector_flags
> +  {
> +    GRUB_KEY_PROTECTOR_FLAG_NONE = 0,
> +    GRUB_KEY_PROTECTOR_FLAG_REPEATABLE = 1 << 0
> +  };
> +
> +struct grub_key_protector
> +{
> +  struct grub_key_protector *next;
> +  struct grub_key_protector **prev;
> +
> +  const char *name;
> +  enum grub_key_protector_flags flags;
> +
> +  grub_err_t (*recover_key) (char *args, grub_uint8_t **key,
> +                             grub_size_t *key_size);
> +};
> +
> +extern struct grub_key_protector *EXPORT_VAR (grub_key_protectors);
> +
> +grub_err_t
> +EXPORT_FUNC (grub_key_protector_register) (struct grub_key_protector *protector);
> +
> +grub_err_t
> +EXPORT_FUNC (grub_key_protector_unregister) (struct grub_key_protector *protector);
> +
> +grub_err_t
> +EXPORT_FUNC (grub_key_protector_recover_key) (char args[], grub_uint8_t **key,
> +                                              grub_size_t *key_size);
> +
> +#endif /* ! GRUB_PROTECTOR_HEADER */

Glenn


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 3/5] protectors: Add TPM2 Key Protector
  2022-01-24 14:12 ` [PATCH 3/5] protectors: Add TPM2 Key Protector Hernan Gatta
@ 2022-01-25  3:55   ` Glenn Washburn
  2022-01-27 14:11     ` Hernan Gatta
  0 siblings, 1 reply; 21+ messages in thread
From: Glenn Washburn @ 2022-01-25  3:55 UTC (permalink / raw)
  To: Hernan Gatta; +Cc: The development of GNU GRUB

On Mon, 24 Jan 2022 06:12:16 -0800
Hernan Gatta <hegatta@linux.microsoft.com> wrote:

> From: Hernan Gatta <hegatta@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 therefore possess reasonable defaults. One of these
> arguments is the keyfile parameter, which is mandatory.
> 
> The value of this parameter must be a path to a sealed key file (e.g.,
> (hd0,gpt1)/boot/grub2/sealed_key). 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.
> 

There are a lot of points in this code where an error can be returned.
In this case, all I'm going to get is a non-descript error code (almost
always GRUB_ERR_BAD_ARGUMENT). So as a user, how am I to know where the
error is actually coming from? For this reason, I like the use of
grub_error(error_code, error_format_string, fmtstr_args...), which will
set grub_errno and an error message, which can be retrieved up the stack
and presented to the user. I'm not sure that every return of a naked
error code should be replaced by grub_error(), depends on how granular
of an error message you want to see. I would error on the side of more
granular. For instance, its really nice that GCC will tell me which
format code type specifier and a argument its choking on, rather than a
catch all "I'm failing to compile your code because you have an
incorrect format code somewhere in your format string".

> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
> ---
>  grub-core/Makefile.core.def |   9 +
>  grub-core/tpm2/module.c     | 742 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 751 insertions(+)
>  create mode 100644 grub-core/tpm2/module.c
> 
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index e4ae78b..8691440 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -2498,6 +2498,15 @@ module = {
>  };
>  
>  module = {
> +  name = tpm2;
> +  common = tpm2/buffer.c;
> +  common = tpm2/module.c;
> +  common = tpm2/mu.c;
> +  common = tpm2/tpm2.c;
> +  efi = tpm2/tcg2.c;
> +};
> +
> +module = {
>    name = tr;
>    common = commands/tr.c;
>  };
> diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
> new file mode 100644
> index 0000000..cd97452
> --- /dev/null
> +++ b/grub-core/tpm2/module.c
> @@ -0,0 +1,742 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2022 Microsoft Corporation
> + *
> + *  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/file.h>
> +#include <grub/misc.h>
> +#include <grub/mm.h>
> +#include <grub/protector.h>
> +#include <grub/tpm2/buffer.h>
> +#include <grub/tpm2/mu.h>
> +#include <grub/tpm2/tpm2.h>
> +#include <grub/types.h>
> +
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +typedef enum grub_tpm2_protector_args
> +{
> +  GRUB_TPM2_PROTECTOR_ARG_MODE       = 1 << 0,
> +  GRUB_TPM2_PROTECTOR_ARG_PCRS       = 1 << 1,
> +  GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC = 1 << 2,
> +  GRUB_TPM2_PROTECTOR_ARG_BANK       = 1 << 3,
> +  GRUB_TPM2_PROTECTOR_ARG_KEYFILE    = 1 << 4,
> +  GRUB_TPM2_PROTECTOR_ARG_SRK        = 1 << 5,
> +  GRUB_TPM2_PROTECTOR_ARG_NV         = 1 << 6
> +} grub_tpm2_protector_args_t;
> +
> +typedef enum grub_tpm2_protector_mode
> +{
> +  GRUB_TPM2_PROTECTOR_MODE_SRK = 1,
> +  GRUB_TPM2_PROTECTOR_MODE_NV  = 2
> +} grub_tpm2_protector_mode_t;
> +
> +struct grub_tpm2_protector_context
> +{
> +  grub_tpm2_protector_args_t args;
> +  grub_tpm2_protector_mode_t mode;
> +  grub_uint8_t pcrs[TPM_MAX_PCRS];
> +  grub_uint8_t pcr_count;
> +  TPM_ALG_ID asymmetric;
> +  TPM_ALG_ID bank;
> +  const char *keyfile;
> +  TPM_HANDLE srk;
> +  TPM_HANDLE nv;
> +};
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_mode (struct grub_tpm2_protector_context *ctx,
> +                                const char *value)
> +{
> +  if (grub_strcmp (value, "srk") == 0)
> +    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
> +  else if (grub_strcmp (value, "nv") == 0)
> +    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_NV;
> +  else
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_pcrs (struct grub_tpm2_protector_context *ctx,
> +                                char *value)
> +{
> +  grub_err_t err;
> +  char *current_pcr = value;
> +  char *next_pcr;
> +  unsigned long pcr;
> +  grub_uint8_t i;
> +
> +  if (grub_strlen (value) == 0)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  ctx->pcr_count = 0;
> +  for (i = 0; i < TPM_MAX_PCRS; i++)
> +    {
> +      next_pcr = grub_strchr (current_pcr, ',');
> +      if (next_pcr)
> +        *next_pcr = '\0';
> +      if (next_pcr == current_pcr)
> +        return GRUB_ERR_BAD_ARGUMENT;
> +
> +      grub_errno = GRUB_ERR_NONE;
> +      pcr = grub_strtoul (current_pcr, NULL, 10);
> +      if (grub_errno != GRUB_ERR_NONE)
> +        {
> +          err = grub_errno;
> +          grub_errno = GRUB_ERR_NONE;
> +          return err;
> +        }
> +
> +      if (pcr > GRUB_UCHAR_MAX)
> +        return GRUB_ERR_OUT_OF_RANGE;
> +
> +      ctx->pcrs[i] = (grub_uint8_t)pcr;
> +      ctx->pcr_count++;
> +
> +      if (!next_pcr)
> +        break;
> +
> +      current_pcr = next_pcr + 1;
> +      if (*current_pcr == '\0')
> +        return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +
> +  if (i == TPM_MAX_PCRS)
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_asymmetric (struct grub_tpm2_protector_context *ctx,
> +                                      const char *value)
> +{
> +  if (grub_strcasecmp (value, "ECC") == 0)
> +    ctx->asymmetric = TPM_ALG_ECC;
> +  else if (grub_strcasecmp (value, "RSA") == 0)
> +    ctx->asymmetric = TPM_ALG_RSA;
> +  else
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_bank (struct grub_tpm2_protector_context *ctx,
> +                                const char *value)
> +{
> +  if (grub_strcasecmp (value, "SHA1") == 0)
> +    ctx->bank = TPM_ALG_SHA1;
> +  else if (grub_strcasecmp (value, "SHA256") == 0)
> +    ctx->bank = TPM_ALG_SHA256;
> +  else if (grub_strcasecmp (value, "SHA384") == 0)
> +    ctx->bank = TPM_ALG_SHA384;
> +  else
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_keyfile (struct grub_tpm2_protector_context *ctx,
> +                                   const char *value)
> +{
> +  if (grub_strlen (value) == 0)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  ctx->keyfile = value;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle)
> +{
> +  grub_err_t err;
> +  unsigned long num;
> +
> +  grub_errno = GRUB_ERR_NONE;
> +  num = grub_strtoul (value, NULL, 0);
> +  if (grub_errno != GRUB_ERR_NONE)
> +    {
> +      err = grub_errno;
> +      grub_errno = GRUB_ERR_NONE;
> +      return err;
> +    }
> +
> +  if (num > GRUB_UINT_MAX)
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  *handle = (TPM_HANDLE)num;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_srk (struct grub_tpm2_protector_context *ctx,
> +                               const char *value)
> +{
> +  return grub_tpm2_protector_parse_tpm_handle (value, &ctx->srk);
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_nvindex (struct grub_tpm2_protector_context *ctx,
> +                                   const char *value)
> +{
> +  return grub_tpm2_protector_parse_tpm_handle (value, &ctx->nv);
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_match_arg (struct grub_tpm2_protector_context *ctx,
> +                               const char *key, char *value)
> +{
> +  grub_err_t err;
> +
> +  if (grub_strlen (key) == 0 || grub_strlen (value) == 0)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (grub_strcmp (key, "mode") == 0)
> +  {
> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_MODE)
> +      return GRUB_ERR_BAD_ARGUMENT;
> +
> +    err = grub_tpm2_protector_parse_mode (ctx, value);
> +    if (!err)
> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_MODE;
> +
> +    return err;
> +  }
> +
> +  if (grub_strcmp (key, "pcrs") == 0)

Minor nit, why wouldn't this be better as an "else if"? Seems
unnecessary to check all the "if" statements if we've already found a
match.

> +  {
> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_PCRS)
> +      return GRUB_ERR_BAD_ARGUMENT;
> +
> +    err = grub_tpm2_protector_parse_pcrs (ctx, value);
> +    if (!err)
> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_PCRS;
> +
> +    return err;
> +  }
> +
> +  if (grub_strcmp (key, "asymmetric") == 0)

Ditto, for the rest of these ifs.

> +  {
> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC)
> +      return GRUB_ERR_BAD_ARGUMENT;
> +
> +    err = grub_tpm2_protector_parse_asymmetric (ctx, value);
> +    if (!err)
> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC;
> +
> +    return err;
> +  }
> +
> +  if (grub_strcmp (key, "bank") == 0)
> +  {
> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_BANK)
> +      return GRUB_ERR_BAD_ARGUMENT;
> +
> +    err = grub_tpm2_protector_parse_bank (ctx, value);
> +    if (!err)
> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_BANK;
> +
> +    return err;
> +  }
> +
> +  if (grub_strcmp (key, "keyfile") == 0)
> +  {
> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_KEYFILE)
> +      return GRUB_ERR_BAD_ARGUMENT;
> +
> +    err = grub_tpm2_protector_parse_keyfile (ctx, value);
> +    if (!err)
> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_KEYFILE;
> +
> +    return err;
> +  }
> +
> +  if (grub_strcmp (key, "srk") == 0)
> +  {
> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_SRK)
> +      return GRUB_ERR_BAD_ARGUMENT;
> +
> +    err = grub_tpm2_protector_parse_srk (ctx, value);
> +    if (!err)
> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_SRK;
> +
> +    return err;
> +  }
> +
> +  if (grub_strcmp (key, "nvindex") == 0)
> +  {
> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_NV)
> +      return GRUB_ERR_BAD_ARGUMENT;
> +
> +    err = grub_tpm2_protector_parse_nvindex (ctx, value);
> +    if (!err)
> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_NV;
> +
> +    return err;
> +  }
> +
> +  return GRUB_ERR_OUT_OF_RANGE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_arg (struct grub_tpm2_protector_context *ctx,
> +                               char *arg)
> +{
> +  char *key = arg;
> +  char *value = NULL;
> +
> +  if (grub_strlen (arg) == 0)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  value = grub_strchr (arg, '=');
> +  if (!value)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  *value = '\0';
> +  value++;
> +
> +  if (*value == '\0')
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  return grub_tpm2_protector_match_arg (ctx, key, value);
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_args (struct grub_tpm2_protector_context *ctx,
> +                                char *args)
> +{
> +  grub_err_t err;
> +  char *cur_arg;
> +  char *next_arg;
> +
> +  if (args == NULL)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (grub_strlen (args) == 0)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  cur_arg = args;
> +  for (;;)
> +    {
> +      next_arg = grub_strchr (cur_arg, ':');
> +      if (next_arg == cur_arg)
> +        return GRUB_ERR_BAD_ARGUMENT;
> +      if (next_arg)
> +        *next_arg = '\0';
> +
> +      err = grub_tpm2_protector_parse_arg (ctx, cur_arg);
> +      if (err)
> +        return err;
> +
> +      if (!next_arg)
> +        break;
> +
> +      cur_arg = next_arg + 1;
> +      if (*cur_arg == '\0')
> +        return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
> +{
> +  if (!(ctx->args & GRUB_TPM2_PROTECTOR_ARG_MODE))
> +    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
> +
> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && !ctx->keyfile)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->nv)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->srk)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && !ctx->nv)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->asymmetric)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->bank)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (!(ctx->args & GRUB_TPM2_PROTECTOR_ARG_PCRS))
> +    {
> +      ctx->pcrs[0] = 7;
> +      ctx->pcr_count = 1;
> +    }
> +
> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK)
> +    {
> +      if (!ctx->srk)
> +        ctx->srk = TPM2_SRK_HANDLE;
> +
> +      if (!ctx->asymmetric)
> +        ctx->asymmetric = TPM_ALG_RSA;
> +
> +      if (!ctx->bank)
> +        ctx->bank = TPM_ALG_SHA256;
> +    }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_srk_read_keyfile (const char *filepath, void **buffer,
> +                                      grub_size_t *buffer_size)
> +{
> +  grub_file_t sealed_key_file;
> +  grub_off_t sealed_key_size;
> +  void *sealed_key_buffer;
> +  grub_off_t sealed_key_read;
> +
> +  sealed_key_file = grub_file_open (filepath, GRUB_FILE_TYPE_NONE);
> +  if (!sealed_key_file)
> +    {
> +      /* grub_file_open sets grub_errno on error, and if we do no unset it,
> +       * future calls to grub_file_open will fail (and so will anybody up the
> +       * stack who checks the value, if any). */
> +      grub_errno = GRUB_ERR_NONE;
> +      return GRUB_ERR_FILE_NOT_FOUND;
> +    }
> +
> +  sealed_key_size = grub_file_size (sealed_key_file);
> +  if (!sealed_key_size)
> +    {
> +      grub_file_close (sealed_key_file);
> +      return GRUB_ERR_OUT_OF_RANGE;
> +    }
> +
> +  sealed_key_buffer = grub_malloc (sealed_key_size);
> +  if (!sealed_key_buffer)
> +    {
> +      grub_file_close (sealed_key_file);
> +      return GRUB_ERR_OUT_OF_MEMORY;
> +    }
> +
> +  sealed_key_read = grub_file_read (sealed_key_file, sealed_key_buffer,
> +                                    sealed_key_size);
> +  if (sealed_key_read != sealed_key_size)
> +    {
> +      grub_free (sealed_key_buffer);
> +      grub_file_close (sealed_key_file);
> +      return GRUB_ERR_FILE_READ_ERROR;
> +    }
> +
> +  grub_file_close (sealed_key_file);
> +
> +  *buffer = sealed_key_buffer;
> +  *buffer_size = sealed_key_size;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_srk_unmarshal_keyfile (void *sealed_key,
> +                                           grub_size_t sealed_key_size,
> +                                           TPM2_SEALED_KEY *sk)
> +{
> +  struct grub_tpm2_buffer buf;
> +
> +  grub_tpm2_buffer_init (&buf);
> +  if (sealed_key_size > buf.cap)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  grub_memcpy (buf.data, sealed_key, sealed_key_size);
> +  buf.size = sealed_key_size;
> +
> +  grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
> +  grub_tpm2_mu_TPM2B_Unmarshal (&buf, (TPM2B *)&sk->private);
> +
> +  return buf.error ? GRUB_ERR_BAD_ARGUMENT : GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_srk_get (const struct grub_tpm2_protector_context *ctx,
> +                             TPM_HANDLE *srk)
> +{
> +  TPM_RC rc;
> +  TPM2B_PUBLIC public;
> +  TPMS_AUTH_COMMAND authCommand = { 0 };
> +  TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
> +  TPM2B_PUBLIC inPublic = { 0 };
> +  TPM2B_DATA outsideInfo = { 0 };
> +  TPML_PCR_SELECTION creationPcr = { 0 };
> +  TPM2B_PUBLIC outPublic = { 0 };
> +  TPM2B_CREATION_DATA creationData = { 0 };
> +  TPM2B_DIGEST creationHash = { 0 };
> +  TPMT_TK_CREATION creationTicket = { 0 };
> +  TPM2B_NAME srkName = { 0 };
> +  TPM_HANDLE srkHandle;
> +
> +  /* Find SRK */
> +  rc = TPM2_ReadPublic (ctx->srk, NULL, &public);
> +  if (rc == TPM_RC_SUCCESS)
> +    {
> +      *srk = ctx->srk;
> +      return GRUB_ERR_NONE;
> +    }
> +
> +  /* The handle exists but its public area could not be read. */
> +  if ((rc & ~TPM_RC_N_MASK) != TPM_RC_HANDLE)
> +    return GRUB_ERR_BAD_DEVICE;
> +
> +  /* Create SRK */
> +  authCommand.sessionHandle = TPM_RS_PW;
> +  inPublic.publicArea.type = ctx->asymmetric;
> +  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 (ctx->asymmetric == 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 = 2048;
> +      inPublic.publicArea.parameters.rsaDetail.exponent = 0;
> +    }
> +  else if (ctx->asymmetric == 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 = TPM_ECC_NIST_P256;
> +      inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
> +    }
> +  else
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  rc = TPM2_CreatePrimary (TPM_RH_OWNER, &authCommand, &inSensitive, &inPublic,
> +                           &outsideInfo, &creationPcr, &srkHandle, &outPublic,
> +                           &creationData, &creationHash, &creationTicket,
> +                           &srkName, NULL);
> +  if (rc != TPM_RC_SUCCESS)
> +    return GRUB_ERR_BAD_DEVICE;
> +
> +  *srk = srkHandle;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_srk_recover (const struct grub_tpm2_protector_context *ctx,
> +                                 grub_uint8_t **key, grub_size_t *key_size)
> +{
> +  TPM_RC rc;
> +  TPM2_SEALED_KEY sealed_key;
> +  void *sealed_key_bytes;
> +  grub_size_t sealed_key_size;
> +  TPM_HANDLE srk_handle;
> +  TPM2B_NONCE nonceCaller = { 0 };
> +  TPM2B_ENCRYPTED_SECRET salt = { 0 };
> +  TPMT_SYM_DEF symmetric = { 0 };
> +  TPM2B_NONCE nonceTPM = { 0 };
> +  TPMI_SH_AUTH_SESSION session;
> +  TPML_PCR_SELECTION pcrSel = {
> +    .count = 1,
> +    .pcrSelections = {
> +      {
> +        .hash = ctx->bank,
> +        .sizeOfSelect = 3,
> +        .pcrSelect = { 0 }
> +      },
> +    }
> +  };
> +  TPMS_AUTH_COMMAND authCmd = { 0 };
> +  TPM_HANDLE sealed_key_handle;
> +  TPM2B_NAME name;
> +  TPMS_AUTH_RESPONSE authResponse;
> +  TPM2B_SENSITIVE_DATA data;
> +  grub_uint8_t *key_out;
> +  grub_uint8_t i;
> +  grub_err_t err;
> +
> +  /* Retrieve Sealed Key */
> +  err = grub_tpm2_protector_srk_read_keyfile (ctx->keyfile, &sealed_key_bytes,
> +                                              &sealed_key_size);
> +  if (err)
> +    return err;
> +
> +  err = grub_tpm2_protector_srk_unmarshal_keyfile (sealed_key_bytes,
> +                                                   sealed_key_size,
> +                                                   &sealed_key);
> +  if (err)
> +    goto exit1;
> +
> +  /* Get SRK */
> +  err = grub_tpm2_protector_srk_get (ctx, &srk_handle);
> +  if (err)
> +    goto exit1;
> +
> +  err = GRUB_ERR_BAD_DEVICE;
> +
> +  /* Start Auth Session */
> +  nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
> +  symmetric.algorithm = TPM_ALG_NULL;
> +
> +  rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, 0, &nonceCaller, &salt,
> +                              TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256,
> +                              &session, &nonceTPM, 0);
> +  if (rc)
> +    goto exit2;
> +
> +  /* Policy PCR */
> +  for (i = 0; i < ctx->pcr_count; i++)
> +    pcrSel
> +      .pcrSelections[0]
> +      .pcrSelect[TPM2_PCR_TO_SELECT(ctx->pcrs[i])]
> +        |= TPM2_PCR_TO_BIT(ctx->pcrs[i]);
> +
> +  rc = TPM2_PolicyPCR (session, NULL, NULL, &pcrSel, NULL);
> +  if (rc)
> +    goto exit3;
> +
> +  /* Load Sealed Key */
> +  authCmd.sessionHandle = TPM_RS_PW;
> +  rc = TPM2_Load (srk_handle, &authCmd, &sealed_key.private, &sealed_key.public,
> +                  &sealed_key_handle, &name, &authResponse);
> +  if (rc)
> +    goto exit3;
> +
> +  /* Unseal Sealed Key */
> +  authCmd.sessionHandle = session;
> +  grub_memset (&authResponse, 0, sizeof (authResponse));
> +
> +  rc = TPM2_Unseal (sealed_key_handle, &authCmd, &data, &authResponse);
> +  if (rc)
> +    goto exit4;
> +
> +  /* Epilogue */
> +  key_out = grub_malloc (data.size);
> +  if (!key_out)
> +    {
> +      err = GRUB_ERR_OUT_OF_MEMORY;
> +      goto exit4;
> +    }
> +
> +  grub_memcpy (key_out, data.buffer, data.size);
> +
> +  *key = key_out;
> +  *key_size = data.size;
> +
> +  err = GRUB_ERR_NONE;
> +
> +exit4:
> +  TPM2_FlushContext (sealed_key_handle);
> +
> +exit3:
> +  TPM2_FlushContext (session);
> +
> +exit2:
> +  TPM2_FlushContext (srk_handle);
> +
> +exit1:
> +  grub_free (sealed_key_bytes);
> +  return err;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx,
> +                                grub_uint8_t **key, grub_size_t *key_size)
> +{
> +  (void)ctx;
> +  (void)key;
> +  (void)key_size;
> +
> +  return GRUB_ERR_NOT_IMPLEMENTED_YET;
> +}
> +
> +static grub_err_t
> +grub_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 grub_tpm2_protector_srk_recover (ctx, key, key_size);
> +    case GRUB_TPM2_PROTECTOR_MODE_NV:
> +      return grub_tpm2_protector_nv_recover (ctx, key, key_size);
> +    default:
> +      return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_recover_key (char *args, grub_uint8_t **key,
> +                                 grub_size_t *key_size)
> +{
> +  grub_err_t err;
> +  struct grub_tpm2_protector_context ctx = { 0 };
> +
> +  grub_dprintf ("tpm2", "Args: %s\n", args);
> +
> +  if (!args || !key)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  err = grub_tpm2_protector_parse_args (&ctx, args);
> +  if (err) {
> +    grub_dprintf("tpm2", "Failed to parse arguments\n");
> +    return err;
> +  }
> +
> +  err = grub_tpm2_protector_check_args (&ctx);
> +  if (err) {
> +    grub_dprintf("tpm2", "Invalid arguments\n");

It's nice that this is here to at least let the user know that the
failure was because of invalid arguments as opposed to a failure in the
parsing. Even still, which argument was invalid? why was it invalid? As
a user, this might be frustrating to diagnose. This is why a message
specific to the error would be appreciated.

Glenn

> +    return err;
> +  }
> +
> +  err = grub_tpm2_protector_recover (&ctx, key, key_size);
> +  if (err) {
> +    grub_dprintf("tpm2", "Failed to recover key\n");
> +    return err;
> +  }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +struct grub_key_protector grub_tpm2_key_protector = {
> +  .name = "tpm2",
> +  .flags = GRUB_KEY_PROTECTOR_FLAG_NONE,
> +  .recover_key = grub_tpm2_protector_recover_key
> +};
> +
> +GRUB_MOD_INIT(tpm2)
> +{
> +  grub_key_protector_register (&grub_tpm2_key_protector);
> +}
> +
> +GRUB_MOD_FINI(tpm2)
> +{
> +  grub_key_protector_unregister (&grub_tpm2_key_protector);
> +}


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 4/5] cryptodisk: Support key protectors
  2022-01-24 14:12 ` [PATCH 4/5] cryptodisk: Support key protectors Hernan Gatta
@ 2022-01-25  5:42   ` Glenn Washburn
  2022-01-25 17:28     ` James Bottomley
  2022-01-27 14:26     ` Hernan Gatta
  0 siblings, 2 replies; 21+ messages in thread
From: Glenn Washburn @ 2022-01-25  5:42 UTC (permalink / raw)
  To: Hernan Gatta
  Cc: The development of GNU GRUB, James Bottomley, James Bottomley

On Mon, 24 Jan 2022 06:12:17 -0800
Hernan Gatta <hegatta@linux.microsoft.com> wrote:

> From: Hernan Gatta <hegatta@microsoft.com>
> 
> Add a new parameter to cryptomount to support the key protectors framework: -k.
> This parameter is accepted whenever the cryptomount command is used to mount a
> specific disk either via a disk specification (e.g., (hd0,gpt1)) or by UUID, but
> not when disks are mounted in bulk (i.e., via -a or -b). The parameter is used
> to automatically retrieve a key from the specified key protector.

Why shouldn't key protectors be used to try to unlock all devices that
a backend identified as being able to unlock? Specifically, can a key
stored via the TPM be used for multiple devices? With respect to a
future unencrypted keyfile protector, it is certainly the case that it
can be applied to multiple devices.

> 
> Signed-off-by: <Hernan Gatta hegatta@linux.microsoft.com>
> ---
>  Makefile.util.def           |  1 +
>  grub-core/disk/cryptodisk.c | 21 ++++++++++++++++++++-
>  2 files changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/Makefile.util.def b/Makefile.util.def
> index f8b356c..39b53b3 100644
> --- a/Makefile.util.def
> +++ b/Makefile.util.def
> @@ -35,6 +35,7 @@ library = {
>    common = grub-core/kern/list.c;
>    common = grub-core/kern/misc.c;
>    common = grub-core/kern/partition.c;
> +  common = grub-core/kern/protectors.c;
>    common = grub-core/lib/crypto.c;
>    common = grub-core/lib/json/json.c;
>    common = grub-core/disk/luks.c;
> diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
> index 4970973..176dd56 100644
> --- a/grub-core/disk/cryptodisk.c
> +++ b/grub-core/disk/cryptodisk.c
> @@ -26,6 +26,7 @@
>  #include <grub/file.h>
>  #include <grub/procfs.h>
>  #include <grub/partition.h>
> +#include <grub/protector.h>
>  
>  #ifdef GRUB_UTIL
>  #include <grub/emu/hostdisk.h>
> @@ -42,6 +43,7 @@ static const struct grub_arg_option options[] =
>      {"all", 'a', 0, N_("Mount all."), 0, 0},
>      {"boot", 'b', 0, N_("Mount all volumes with `boot' flag set."), 0, 0},
>      {"password", 'p', 0, N_("Password to open volumes."), 0, ARG_TYPE_STRING},
> +    {"protector", 'k', 0, N_("Unlock disk using the specified key protector."), 0, ARG_TYPE_STRING},

This conflicts with the keyfile and detached header patch series as is.
Ultimately, this is probably the right way to go and there will be an
protector for using unencrypted keyfiles.

Another option is to use -P for protectors and -k for keyfiles and have
two separate methods of using unencrypted keyfiles. I generally don't
like having two methods, the only benefit is that I believe the
--keyfile method is more intuitive. To use the keyfile protector the
user unfamiliar with these features will likely need to consult the
documentation.

>      {0, 0, 0, 0, 0, 0}
>    };
>  
> @@ -1160,6 +1162,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
>  {
>    struct grub_arg_list *state = ctxt->state;
>    struct grub_cryptomount_args cargs = {0};
> +  grub_err_t err;
>  
>    if (argc < 1 && !state[1].set && !state[2].set)
>      return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
> @@ -1167,12 +1170,28 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
>    if (grub_cryptodisk_list == NULL)
>      return grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk modules loaded");
>  
> +  if (state[3].set && state[4].set) /* password and key protector */
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT,
> +                       "a password and a key protector cannot both be set");
> +
> +  if (state[4].set && argc < 1) /* key protector */
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT,
> +                       "key protectors require a device name or UUID");

Referring to the my first comment, I think we can get rid of this if
block.

> +
>    if (state[3].set) /* password */
>      {
>        cargs.key_data = (grub_uint8_t *) state[3].arg;
>        cargs.key_len = grub_strlen (state[3].arg);
>      }
>  
> +  if (state[4].set) /* key protector */
> +    {
> +      err = grub_key_protector_recover_key (state[4].arg, &cargs.key_data,
> +                                                          &cargs.key_len);
> +      if (err)
> +        grub_printf_ (N_("Could not recover key from key protector.\n"));

Why is this not returning with an error here? Is it believed that
perhaps the device can be unlocked without recovering a key (even
though the user explicitly specified that a key should be used?)?

> +    }
> +
>    if (state[0].set) /* uuid */
>      {
>        int found_uuid;
> @@ -1385,7 +1404,7 @@ GRUB_MOD_INIT (cryptodisk)
>  {
>    grub_disk_dev_register (&grub_cryptodisk_dev);
>    cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0,
> -			      N_("[-p password] <SOURCE|-u UUID|-a|-b>"),
> +			      N_("[-p password] [-k protector[:args]] <SOURCE|-u UUID|-a|-b>"),

This looks eerily similiar to what I proposed to James in response to
his SEV patches[1]. As such, I am in favor of this syntax and it looks
to me like a framework that James can use for his SEV series.

Glenn

>  			      N_("Mount a crypto device."), options);
>    grub_procfs_register ("luks_script", &luks_script);
>  }

[1] https://lists.gnu.org/archive/html/grub-devel/2020-11/msg00105.html


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 5/5] util/grub-protect: Add new tool
  2022-01-24 14:12 ` [PATCH 5/5] util/grub-protect: Add new tool Hernan Gatta
@ 2022-01-25  5:45   ` Glenn Washburn
  2022-01-27 14:19     ` Hernan Gatta
  0 siblings, 1 reply; 21+ messages in thread
From: Glenn Washburn @ 2022-01-25  5:45 UTC (permalink / raw)
  To: Hernan Gatta; +Cc: The development of GNU GRUB

On Mon, 24 Jan 2022 06:12:18 -0800
Hernan Gatta <hegatta@linux.microsoft.com> wrote:

> From: Hernan Gatta <hegatta@microsoft.com>
> 
> To utilize the key protectors framework, there must be a way to protect
> full-disk encryption keys in the first place. The grub-protect tool includes
> support for the TPM2 key protector but other protectors that require setup ahead
> of time can be supported in the future.
> 
> For the TPM2 key protector, the intended flow is for a user to have a LUKS 1 or
> LUKS 2-protected fully-encrypted disk. The user then creates a new key file, say
> by reading /dev/urandom into a file, and creates a new LUKS key slot for this
> key. Then, the user invokes the grub-protect tool to seal this key file to a set
> of PCRs using the system's TPM 2.0. The resulting sealed key file is stored in
> an unencrypted partition such as the EFI System Partition (ESP) so that GRUB may
> read it. The user also ensures the cryptomount command is included in GRUB's
> boot script and that it carries the requisite --protector (or -p) parameter.

I think you mean '-k' instead of '-p' here.

> 
> Sample usage:
> 
> $ dd if=/dev/urandom of=key count=32
> $ sudo cryptsetup luksAddKey /dev/sdb1 key --pbkdf=pbkdf2 --hash=sha512
> 
> $ sudo grub-protect --action=add
> --protector=tpm2
> --tpm2-keyfile=key
> --tpm2-outfile=/boot/efi/boot/grub2/sealed_key

Why not have a syntax that is similar to whats given to cryptomount?
(eg. $ sudo grub-protect --action=add
--protector=tpm2:keyfile=/boot/efi/boot/grub2/sealed_key ) This would
allow you to re-use the parsing code from patch #3. Yes, its less
UNIXy, but I think could be more intuitive and cuts down on code size.
I'm not completely wedded to this idea either.

> 
> Then, in the boot script (wrapped for display):
> 
> cryptomount -u b20f95d0834842bc9197bd78b36732f8
> -p tpm2:keyfile=(hd0,gpt1)/boot/grub2/sealed_key

Also, -k here.

> 
> where the UUID corresponds to /dev/sdb1.
> 
> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
> ---
>  .gitignore          |    1 +
>  Makefile.util.def   |   16 +
>  configure.ac        |    1 +
>  util/grub-protect.c | 1344 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 1362 insertions(+)
>  create mode 100644 util/grub-protect.c
> 
> diff --git a/.gitignore b/.gitignore
> index f6a1bd0..852327d 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -167,6 +167,7 @@ widthspec.bin
>  /grub-ofpathname.exe
>  /grub-probe
>  /grub-probe.exe
> +/grub-protect
>  /grub-reboot
>  /grub-render-label
>  /grub-render-label.exe
> diff --git a/Makefile.util.def b/Makefile.util.def
> index 39b53b3..05f9f09 100644
> --- a/Makefile.util.def
> +++ b/Makefile.util.def
> @@ -52,6 +52,9 @@ library = {
>    common = grub-core/partmap/msdos.c;
>    common = grub-core/fs/proc.c;
>    common = grub-core/fs/archelp.c;
> +  common = grub-core/tpm2/buffer.c;
> +  common = grub-core/tpm2/mu.c;
> +  common = grub-core/tpm2/tpm2.c;
>  };
>  
>  library = {
> @@ -207,6 +210,19 @@ program = {
>  };
>  
>  program = {
> +  name = grub-protect;
> +  common = grub-core/osdep/init.c;
> +  common = util/grub-protect.c;
> +  common = util/probe.c;
> +
> +  ldadd = libgrubmods.a;
> +  ldadd = libgrubgcry.a;
> +  ldadd = libgrubkern.a;
> +  ldadd = grub-core/lib/gnulib/libgnu.a;
> +  ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
> +};
> +
> +program = {
>    name = grub-mkrelpath;
>    mansection = 1;
>  
> diff --git a/configure.ac b/configure.ac
> index 4f649ed..3a4dc5a 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -71,6 +71,7 @@ grub_TRANSFORM([grub-mkpasswd-pbkdf2])
>  grub_TRANSFORM([grub-mkrelpath])
>  grub_TRANSFORM([grub-mkrescue])
>  grub_TRANSFORM([grub-probe])
> +grub_TRANSFORM([grub-protect])
>  grub_TRANSFORM([grub-reboot])
>  grub_TRANSFORM([grub-script-check])
>  grub_TRANSFORM([grub-set-default])
> diff --git a/util/grub-protect.c b/util/grub-protect.c
> new file mode 100644
> index 0000000..4e4bbd7
> --- /dev/null
> +++ b/util/grub-protect.c
> @@ -0,0 +1,1344 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2022 Microsoft Corporation
> + *
> + *  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 <config.h>
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#include <grub/crypto.h>
> +#include <grub/emu/getroot.h>
> +#include <grub/emu/hostdisk.h>
> +#include <grub/emu/misc.h>
> +#include <grub/tpm2/buffer.h>
> +#include <grub/tpm2/mu.h>
> +#include <grub/tpm2/tcg2.h>
> +#include <grub/tpm2/tpm2.h>
> +#include <grub/util/misc.h>
> +
> +#include "progname.h"
> +
> +typedef enum grub_protect_arg
> +{
> +  GRUB_PROTECT_ARG_ACTION          = 1 << 0,
> +  GRUB_PROTECT_ARG_PROTECTOR       = 1 << 1,
> +  GRUB_PROTECT_ARG_TPM2_DEVICE     = 1 << 2,
> +  GRUB_PROTECT_ARG_TPM2_PCRS       = 1 << 3,
> +  GRUB_PROTECT_ARG_TPM2_ASYMMETRIC = 1 << 4,
> +  GRUB_PROTECT_ARG_TPM2_BANK       = 1 << 5,
> +  GRUB_PROTECT_ARG_TPM2_SRK        = 1 << 6,
> +  GRUB_PROTECT_ARG_TPM2_KEYFILE    = 1 << 7,
> +  GRUB_PROTECT_ARG_TPM2_OUTFILE    = 1 << 8,
> +  GRUB_PROTECT_ARG_TPM2_PERSIST    = 1 << 9,
> +  GRUB_PROTECT_ARG_TPM2_EVICT      = 1 << 10
> +} grub_protect_arg_t;
> +
> +typedef enum grub_protect_protector
> +{
> +  GRUB_PROTECT_TYPE_ERROR,
> +  GRUB_PROTECT_TYPE_TPM2
> +} grub_protect_protector_t;
> +
> +typedef enum grub_protect_action
> +{
> +  GRUB_PROTECT_ACTION_ERROR,
> +  GRUB_PROTECT_ACTION_ADD,
> +  GRUB_PROTECT_ACTION_REMOVE
> +} grub_protect_action_t;
> +
> +struct grub_protect_args
> +{
> +  grub_protect_arg_t args;
> +  grub_protect_action_t action;
> +  grub_protect_protector_t protector;
> +
> +  const char *tpm2_device;
> +  grub_uint8_t tpm2_pcrs[TPM_MAX_PCRS];
> +  grub_uint8_t tpm2_pcr_count;
> +  TPM_ALG_ID tpm2_asymmetric;
> +  TPM_ALG_ID tpm2_bank;
> +  TPM_HANDLE tpm2_srk;
> +  const char *tpm2_keyfile;
> +  const char *tpm2_outfile;
> +  int tpm2_persist;
> +  int tpm2_evict;
> +};
> +
> +static int tpm2_fd = -1;
> +
> +static grub_err_t
> +grub_protect_read_file (const char *filepath, void **buffer,
> +                        size_t *buffer_size)
> +{
> +  grub_err_t err;
> +  FILE *f;
> +  long len;
> +  void *buf;
> +
> +  f = fopen (filepath, "rb");
> +  if (!f)
> +    return GRUB_ERR_FILE_NOT_FOUND;
> +
> +  if (fseek (f, 0, SEEK_END))
> +    {
> +       err = GRUB_ERR_FILE_READ_ERROR;
> +       goto exit1;
> +    }
> +
> +  len = ftell (f);
> +  if (!len)
> +    {
> +       err = GRUB_ERR_FILE_READ_ERROR;
> +       goto exit1;
> +    }
> +
> +  rewind (f);
> +
> +  buf = grub_malloc (len);
> +  if (!buf)
> +    {
> +       err = GRUB_ERR_OUT_OF_MEMORY;
> +       goto exit1;
> +    }
> +
> +  if (fread (buf, len, 1, f) != 1)
> +    {
> +       err = GRUB_ERR_FILE_READ_ERROR;
> +       goto exit2;
> +    }
> +
> +  *buffer = buf;
> +  *buffer_size = len;
> +
> +  buf = NULL;
> +  err = GRUB_ERR_NONE;
> +
> +exit2:
> +  grub_free (buf);
> +
> +exit1:
> +  fclose (f);
> +
> +  return err;
> +}
> +
> +static grub_err_t
> +grub_protect_write_file (const char *filepath, void *buffer, size_t buffer_size)
> +{
> +  grub_err_t err;
> +  FILE *f;
> +
> +  f = fopen (filepath, "wb");
> +  if (!f)
> +    return GRUB_ERR_FILE_NOT_FOUND;
> +
> +  if (fwrite (buffer, buffer_size, 1, f) != 1)
> +  {
> +    err = GRUB_ERR_WRITE_ERROR;
> +    goto exit1;
> +  }
> +
> +  err = GRUB_ERR_NONE;
> +
> +exit1:
> +  fclose (f);
> +
> +  return err;
> +}
> +
> +static grub_err_t
> +grub_protect_get_grub_drive_for_file (const char *filepath, char **drive)
> +{
> +  grub_err_t err = GRUB_ERR_IO;
> +  char *disk;
> +  char **devices;
> +  char *grub_dev;
> +  char *grub_path;
> +  char *efi_drive;
> +  char *partition;
> +  char *grub_drive;
> +  grub_device_t dev;
> +  grub_size_t grub_drive_len;
> +  int n;
> +
> +  grub_path = grub_canonicalize_file_name (filepath);
> +  if (!grub_path)
> +    goto exit1;
> +
> +  devices = grub_guess_root_devices (grub_path);
> +  if (!devices || !devices[0])
> +    goto exit2;
> +
> +  disk = devices[0];
> +
> +  grub_util_pull_device (disk);
> +
> +  grub_dev = grub_util_get_grub_dev (disk);
> +  if (!grub_dev)
> +    goto exit3;
> +
> +  dev = grub_device_open (grub_dev);
> +  if (!dev)
> +    goto exit4;
> +
> +  efi_drive = grub_util_guess_efi_drive (disk);
> +  if (!efi_drive)
> +    goto exit5;
> +
> +  partition = grub_partition_get_name (dev->disk->partition);
> +  if (!partition)
> +    goto exit6;
> +
> +  grub_drive_len = grub_strlen (efi_drive) + grub_strlen (partition) + 3;
> +  grub_drive = grub_malloc (grub_drive_len + 1);
> +  if (!grub_drive)
> +    goto exit7;
> +
> +  n = grub_snprintf (grub_drive, grub_drive_len + 1, "(%s,%s)", efi_drive,
> +                     partition);
> +  if (n != grub_drive_len)
> +    goto exit8;
> +
> +  *drive = grub_drive;
> +  grub_drive = NULL;
> +  err = GRUB_ERR_NONE;
> +
> +exit8:
> +  grub_free (grub_drive);
> +
> +exit7:
> +  grub_free (partition);
> +
> +exit6:
> +  grub_free (efi_drive);
> +
> +exit5:
> +  grub_device_close (dev);
> +
> +exit4:
> +  grub_free (grub_dev);
> +
> +exit3:
> +  grub_free (devices);
> +
> +exit2:
> +  grub_free (grub_path);
> +
> +exit1:
> +  return err;
> +}
> +
> +grub_err_t
> +grub_tcg2_get_max_output_size (grub_size_t *size)
> +{
> +  if (!size)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  *size = GRUB_TPM2_BUFFER_CAPACITY;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +grub_err_t
> +grub_tcg2_submit_command (grub_size_t input_size, grub_uint8_t *input,
> +                          grub_size_t output_size, grub_uint8_t *output)
> +{
> +  static const grub_size_t header_size = sizeof (grub_uint16_t) +
> +                                         (2 * sizeof(grub_uint32_t));
> +
> +  if (write (tpm2_fd, input, input_size) != input_size)
> +    return GRUB_ERR_BAD_DEVICE;
> +
> +  if (read (tpm2_fd, output, output_size) < header_size)
> +    return GRUB_ERR_BAD_DEVICE;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_protect_tpm2_open_device (const char *dev_node)
> +{
> +  if (tpm2_fd != -1)
> +    return GRUB_ERR_NONE;
> +
> +  tpm2_fd = open (dev_node, O_RDWR);
> +  if (tpm2_fd == -1)
> +    {
> +      fprintf (stderr, _("Could not open TPM device (Error: %u).\n"), errno);
> +      return GRUB_ERR_FILE_NOT_FOUND;
> +    }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_protect_tpm2_close_device (void)
> +{
> +  int err;
> +
> +  if (tpm2_fd == -1)
> +    return GRUB_ERR_NONE;
> +
> +  err = close (tpm2_fd);
> +  if (err)
> +  {
> +    fprintf (stderr, _("Could not close TPM device (Error: %u).\n"), errno);
> +    return GRUB_ERR_IO;
> +  }
> +
> +  tpm2_fd = -1;
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_protect_tpm2_get_policy_digest (struct grub_protect_args *args,
> +                                     TPM2B_DIGEST *digest)
> +{
> +  TPM_RC rc;
> +  TPML_PCR_SELECTION pcr_sel = {
> +    .count = 1,
> +    .pcrSelections = {
> +      {
> +        .hash = args->tpm2_bank,
> +        .sizeOfSelect = 3,
> +        .pcrSelect = { 0 }
> +      },
> +    }
> +  };
> +  TPML_PCR_SELECTION pcr_sel_out = { 0 };
> +  TPML_DIGEST pcr_values = { 0 };
> +  grub_uint8_t *pcr_digest;
> +  grub_size_t pcr_digest_len;
> +  grub_uint8_t *pcr_concat;
> +  grub_size_t pcr_concat_len;
> +  grub_uint8_t *pcr_cursor;
> +  const gcry_md_spec_t *hash_spec;
> +  TPM2B_NONCE nonce = { 0 };
> +  TPM2B_ENCRYPTED_SECRET salt = { 0 };
> +  TPMT_SYM_DEF symmetric = { 0 };
> +  TPMI_SH_AUTH_SESSION session = 0;
> +  TPM2B_DIGEST pcr_digest_in = {
> +    .size = TPM_SHA256_DIGEST_SIZE,
> +    .buffer = { 0 }
> +  };
> +  TPM2B_DIGEST policy_digest = { 0 };
> +  grub_uint8_t i;
> +  grub_err_t err;
> +
> +  /* PCR Read */
> +  for (i = 0; i < args->tpm2_pcr_count; i++)
> +    pcr_sel
> +      .pcrSelections[0]
> +      .pcrSelect[TPM2_PCR_TO_SELECT(args->tpm2_pcrs[i])]
> +        |= TPM2_PCR_TO_BIT(args->tpm2_pcrs[i]);
> +
> +  rc = TPM2_PCR_Read (NULL, &pcr_sel, NULL, &pcr_sel_out, &pcr_values, NULL);
> +  if (rc != TPM_RC_SUCCESS)
> +    {
> +      fprintf (stderr, _("Failed to read PCRs (TPM error: 0x%x).\n"), rc);
> +      return GRUB_ERR_BAD_DEVICE;
> +    }
> +
> +  if ((pcr_sel_out.count != pcr_sel.count) ||
> +       (pcr_sel.pcrSelections[0].sizeOfSelect !=
> +        pcr_sel_out.pcrSelections[0].sizeOfSelect))
> +    {
> +      fprintf (stderr, _("Could not read all the specified PCRs.\n"));
> +      return GRUB_ERR_BAD_DEVICE;
> +    }
> +
> +  /* Compute PCR Digest */
> +  switch (args->tpm2_bank)
> +    {
> +    case TPM_ALG_SHA1:
> +      pcr_digest_len = TPM_SHA1_DIGEST_SIZE;
> +      hash_spec = GRUB_MD_SHA1;
> +      break;
> +    case TPM_ALG_SHA256:
> +      pcr_digest_len = TPM_SHA256_DIGEST_SIZE;
> +      hash_spec = GRUB_MD_SHA256;
> +      break;
> +    default:
> +      return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +
> +  pcr_digest = grub_malloc (pcr_digest_len);
> +  if (!pcr_digest)
> +    {
> +      fprintf (stderr, _("Failed to allocate PCR digest buffer.\n"));
> +      return GRUB_ERR_OUT_OF_MEMORY;
> +    }
> +
> +  pcr_concat_len = pcr_digest_len * args->tpm2_pcr_count;
> +  pcr_concat = grub_malloc (pcr_concat_len);
> +  if (!pcr_concat)
> +    {
> +      err = GRUB_ERR_OUT_OF_MEMORY;
> +      fprintf (stderr, _("Failed to allocate PCR concatenation buffer.\n"));
> +      goto exit1;
> +    }
> +
> +  pcr_cursor = pcr_concat;
> +  for (i = 0; i < args->tpm2_pcr_count; i++)
> +    {
> +      if (pcr_values.digests[i].size != pcr_digest_len)
> +        {
> +          fprintf (stderr,
> +                   _("Bad PCR value size: expected %lu bytes but got %u bytes.\n"),
> +                   pcr_digest_len, pcr_values.digests[i].size);
> +          goto exit2;
> +        }
> +
> +      grub_memcpy (pcr_cursor, pcr_values.digests[i].buffer, pcr_digest_len);
> +      pcr_cursor += pcr_digest_len;
> +    }
> +
> +  grub_crypto_hash (hash_spec, pcr_digest, pcr_concat, pcr_concat_len);
> +
> +  /* Start Trial Session */
> +  nonce.size = TPM_SHA256_DIGEST_SIZE;
> +  symmetric.algorithm = TPM_ALG_NULL;
> +
> +  rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, 0, &nonce, &salt,
> +                              TPM_SE_TRIAL, &symmetric, TPM_ALG_SHA256,
> +                              &session, NULL, 0);
> +  if (rc != TPM_RC_SUCCESS)
> +    {
> +      fprintf (stderr,
> +               _("Failed to start trial policy session (TPM error: 0x%x).\n"),
> +               rc);
> +      err = GRUB_ERR_BAD_DEVICE;
> +      goto exit2;
> +    }
> +
> +  /* PCR Policy */
> +  memcpy (pcr_digest_in.buffer, pcr_digest, TPM_SHA256_DIGEST_SIZE);
> +
> +  rc = TPM2_PolicyPCR (session, NULL, &pcr_digest_in, &pcr_sel, NULL);
> +  if (rc != TPM_RC_SUCCESS)
> +    {
> +      fprintf (stderr, _("Failed to submit PCR policy (TPM error: 0x%x).\n"),
> +               rc);
> +      err = GRUB_ERR_BAD_DEVICE;
> +      goto exit3;
> +    }
> +
> +  /* Retrieve Policy Digest */
> +  rc = TPM2_PolicyGetDigest (session, NULL, &policy_digest, NULL);
> +  if (rc != TPM_RC_SUCCESS)
> +    {
> +      fprintf (stderr, _("Failed to get policy digest (TPM error: 0x%x).\n"),
> +               rc);
> +      err = GRUB_ERR_BAD_DEVICE;
> +      goto exit3;
> +    }
> +
> +  /* Epilogue */
> +  *digest = policy_digest;
> +  err = GRUB_ERR_NONE;
> +
> +exit3:
> +  TPM2_FlushContext (session);
> +
> +exit2:
> +  grub_free (pcr_concat);
> +
> +exit1:
> +  grub_free (pcr_digest);
> +
> +  return err;
> +}
> +
> +static grub_err_t
> +grub_protect_tpm2_get_srk (struct grub_protect_args *args, TPM_HANDLE *srk)
> +{
> +  TPM_RC rc;
> +  TPM2B_PUBLIC public;
> +  TPMS_AUTH_COMMAND authCommand = { 0 };
> +  TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
> +  TPM2B_PUBLIC inPublic = { 0 };
> +  TPM2B_DATA outsideInfo = { 0 };
> +  TPML_PCR_SELECTION creationPcr = { 0 };
> +  TPM2B_PUBLIC outPublic = { 0 };
> +  TPM2B_CREATION_DATA creationData = { 0 };
> +  TPM2B_DIGEST creationHash = { 0 };
> +  TPMT_TK_CREATION creationTicket = { 0 };
> +  TPM2B_NAME srkName = { 0 };
> +  TPM_HANDLE srkHandle;
> +
> +  /* Find SRK */
> +  rc = TPM2_ReadPublic (args->tpm2_srk, NULL, &public);
> +  if (rc == TPM_RC_SUCCESS)
> +    {
> +      if (args->tpm2_persist)
> +        fprintf (stderr,
> +                 _("Warning: --tpm2-persist was specified but the SRK already exists on the TPM. Continuing anyway...\n"));
> +
> +      *srk = TPM2_SRK_HANDLE;
> +      return GRUB_ERR_NONE;
> +    }
> +
> +  /* The handle exists but its public area could not be read. */
> +  if ((rc & ~TPM_RC_N_MASK) != TPM_RC_HANDLE)
> +    {
> +      fprintf (stderr,
> +               _("The SRK exists on the TPM but its public area cannot be read (TPM error: 0x%x).\n"),
> +               rc);
> +      return GRUB_ERR_BAD_DEVICE;
> +    }
> +
> +  /* Create SRK */
> +  authCommand.sessionHandle = TPM_RS_PW;
> +  inPublic.publicArea.type = args->tpm2_asymmetric;
> +  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;
> +
> +  switch (args->tpm2_asymmetric)
> +    {
> +    case 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 = 2048;
> +      inPublic.publicArea.parameters.rsaDetail.exponent = 0;
> +      break;
> +
> +    case 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 = TPM_ECC_NIST_P256;
> +      inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
> +      break;
> +
> +    default:
> +      return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +
> +  rc = TPM2_CreatePrimary (TPM_RH_OWNER, &authCommand, &inSensitive, &inPublic,
> +                           &outsideInfo, &creationPcr, &srkHandle, &outPublic,
> +                           &creationData, &creationHash, &creationTicket,
> +                           &srkName, NULL);
> +  if (rc != TPM_RC_SUCCESS)
> +    {
> +      fprintf (stderr, _("Failed to create SRK (TPM error: 0x%x).\n"), rc);
> +      return GRUB_ERR_BAD_DEVICE;
> +    }
> +
> +  /* Persist SRK */
> +  if (args->tpm2_persist)
> +    {
> +      rc = TPM2_EvictControl (TPM_RH_OWNER, srkHandle, args->tpm2_srk,
> +                              &authCommand, NULL);
> +      if (rc == TPM_RC_SUCCESS)
> +        {
> +          TPM2_FlushContext (srkHandle);
> +          srkHandle = args->tpm2_srk;
> +        }
> +      else
> +        fprintf (stderr,
> +                 _("Warning: Failed to persist SRK (TPM error: 0x%x\n). Continuing anyway...\n"),
> +                 rc);
> +    }
> +
> +  /* Epilogue */
> +  *srk = srkHandle;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_protect_tpm2_seal (TPM2B_DIGEST *policyDigest, TPM_HANDLE srk,
> +                        grub_uint8_t *clearText, grub_size_t clearTextLength,
> +                        TPM2_SEALED_KEY *sealed_key)
> +{
> +  TPM_RC rc;
> +  TPMS_AUTH_COMMAND authCommand = { 0 };
> +  TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
> +  TPM2B_PUBLIC inPublic  = { 0 };
> +  TPM2B_DATA outsideInfo = { 0 };
> +  TPML_PCR_SELECTION pcr_sel = { 0 };
> +  TPM2B_PRIVATE outPrivate = { 0 };
> +  TPM2B_PUBLIC outPublic = { 0 };
> +
> +  /* Seal Data */
> +  authCommand.sessionHandle = TPM_RS_PW;
> +
> +  inSensitive.sensitive.data.size = clearTextLength;
> +  memcpy(inSensitive.sensitive.data.buffer, clearText, clearTextLength);
> +
> +  inPublic.publicArea.type = TPM_ALG_KEYEDHASH;
> +  inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
> +  inPublic.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM_ALG_NULL;
> +  inPublic.publicArea.authPolicy = *policyDigest;
> +
> +  rc = TPM2_Create (srk, &authCommand, &inSensitive, &inPublic, &outsideInfo,
> +                    &pcr_sel, &outPrivate, &outPublic, NULL, NULL, NULL, NULL);
> +  if (rc != TPM_RC_SUCCESS)
> +    {
> +      fprintf (stderr, _("Failed to seal key (TPM error: 0x%x).\n"), rc);
> +      return GRUB_ERR_BAD_DEVICE;
> +    }
> +
> +  /* Epilogue */
> +  sealed_key->public = outPublic;
> +  sealed_key->private = outPrivate;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_protect_tpm2_export_sealed_key (const char *filepath,
> +                                     TPM2_SEALED_KEY *sealed_key)
> +{
> +  grub_err_t err;
> +  struct grub_tpm2_buffer buf;
> +
> +  grub_tpm2_buffer_init (&buf);
> +  grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&buf, &sealed_key->public);
> +  grub_tpm2_mu_TPM2B_Marshal (&buf, sealed_key->private.size,
> +                              sealed_key->private.buffer);
> +  if (buf.error)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  err = grub_protect_write_file (filepath, buf.data, buf.size);
> +  if (err)
> +    fprintf (stderr, _("Could not write sealed key file (Error: %u).\n"),
> +             errno);
> +
> +  return err;
> +}
> +
> +static grub_err_t
> +grub_protect_tpm2_add (struct grub_protect_args *args)
> +{
> +  grub_err_t err;
> +  grub_uint8_t *key;
> +  grub_size_t key_size;
> +  TPM_HANDLE srk;
> +  TPM2B_DIGEST policy_digest;
> +  TPM2_SEALED_KEY sealed_key;
> +  char *grub_drive = NULL;
> +
> +  grub_protect_get_grub_drive_for_file (args->tpm2_outfile, &grub_drive);
> +
> +  err = grub_protect_tpm2_open_device (args->tpm2_device);
> +  if (err)
> +    return err;
> +
> +  err = grub_protect_read_file (args->tpm2_keyfile, (void **)&key, &key_size);
> +  if (err)
> +    goto exit1;
> +
> +  if (key_size > TPM_MAX_SYM_DATA)
> +  {
> +    fprintf (stderr,
> +             _("Input key is too long, maximum allowed size is %u bytes.\n"),
> +             TPM_MAX_SYM_DATA);
> +    return GRUB_ERR_OUT_OF_RANGE;
> +  }
> +
> +  err = grub_protect_tpm2_get_srk (args, &srk);
> +  if (err)
> +    goto exit2;
> +
> +  err = grub_protect_tpm2_get_policy_digest (args, &policy_digest);
> +  if (err)
> +    goto exit3;
> +
> +  err = grub_protect_tpm2_seal (&policy_digest, srk, key, key_size,
> +                                &sealed_key);
> +  if (err)
> +    goto exit3;
> +
> +  err = grub_protect_tpm2_export_sealed_key (args->tpm2_outfile, &sealed_key);
> +  if (err)
> +    goto exit3;
> +
> +  if (grub_drive)
> +    {
> +      printf (_("GRUB drive for the sealed key file: %s\n"), grub_drive);
> +      grub_free (grub_drive);
> +    }
> +  else
> +    {
> +      fprintf (stderr,
> +               _("Warning: Could not determine GRUB drive for sealed key file.\n"));
> +      err = GRUB_ERR_NONE;
> +    }
> +
> +exit3:
> +  TPM2_FlushContext (srk);
> +
> +exit2:
> +  grub_free (key);
> +
> +exit1:
> +  grub_protect_tpm2_close_device ();
> +
> +  return err;
> +}
> +
> +static grub_err_t
> +grub_protect_tpm2_remove (struct grub_protect_args *args)
> +{
> +  TPM_RC rc;
> +  TPM2B_PUBLIC public;
> +  TPMS_AUTH_COMMAND authCommand = { 0 };
> +  grub_err_t err;
> +
> +  if (!args->tpm2_evict)
> +    {
> +      printf (_("--tpm2-evict not specified, nothing to do.\n"));
> +      return GRUB_ERR_NONE;
> +    }
> +
> +  err = grub_protect_tpm2_open_device (args->tpm2_device);
> +  if (err)
> +    return err;
> +
> +  /* Find SRK */
> +  rc = TPM2_ReadPublic (args->tpm2_srk, NULL, &public);
> +  if (rc != TPM_RC_SUCCESS)
> +    {
> +      fprintf (stderr, _("SRK with handle 0x%x not found.\n"), args->tpm2_srk);
> +      err = GRUB_ERR_BAD_ARGUMENT;
> +      goto exit1;
> +    }
> +
> +  /* Evict SRK */
> +  authCommand.sessionHandle = TPM_RS_PW;
> +
> +  rc = TPM2_EvictControl (TPM_RH_OWNER, args->tpm2_srk, args->tpm2_srk,
> +                          &authCommand, NULL);
> +  if (rc != TPM_RC_SUCCESS)
> +    {
> +      fprintf (stderr,
> +               _("Failed to evict SRK with handle 0x%x (TPM Error: 0x%x).\n"),
> +               args->tpm2_srk, rc);
> +      err = GRUB_ERR_BAD_DEVICE;
> +      goto exit2;
> +    }
> +
> +  err = GRUB_ERR_NONE;
> +
> +exit2:
> +  TPM2_FlushContext (args->tpm2_srk);
> +
> +exit1:
> +  grub_protect_tpm2_close_device ();
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_protect_tpm2_run (struct grub_protect_args *args)
> +{
> +  switch (args->action)
> +    {
> +    case GRUB_PROTECT_ACTION_ADD:
> +      return grub_protect_tpm2_add (args);
> +
> +    case GRUB_PROTECT_ACTION_REMOVE:
> +      return grub_protect_tpm2_remove (args);
> +
> +    default:
> +      return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +}
> +
> +static grub_err_t
> +grub_protect_tpm2_args_verify (struct grub_protect_args *args)
> +{

I'm wondering if it wouldn't be a good idea to merge this function with
grub_tpm2_protector_check_args from patch #3. This is probably only
manageable if the protector argument looks like the one from grub as
well, so they can share error messages.

> +  switch (args->action)
> +    {
> +    case GRUB_PROTECT_ACTION_ADD:
> +      if (args->args & GRUB_PROTECT_ARG_TPM2_EVICT)
> +        {
> +          fprintf (stderr,
> +                   _("--tpm2-evict is invalid when --action is 'add'.\n"));
> +          return GRUB_ERR_BAD_ARGUMENT;
> +        }
> +
> +      if (!args->tpm2_keyfile)
> +        {
> +          fprintf (stderr, _("--tpm2-keyfile must be specified.\n"));
> +          return GRUB_ERR_BAD_ARGUMENT;
> +        }
> +
> +      if (!args->tpm2_outfile)
> +        {
> +          fprintf (stderr, _("--tpm2-outfile must be specified.\n"));
> +          return GRUB_ERR_BAD_ARGUMENT;
> +        }
> +
> +      if (!args->tpm2_device)
> +        args->tpm2_device = "/dev/tpm0";

Seems like this...

> +
> +      if (!args->tpm2_pcr_count)
> +        {
> +          args->tpm2_pcrs[0] = 7;
> +          args->tpm2_pcr_count = 1;
> +        }
> +
> +      if (!args->tpm2_srk)
> +        args->tpm2_srk = TPM2_SRK_HANDLE;

and maybe this, can be done outside the switch since they are common to
both cases.

> +
> +      if (!args->tpm2_asymmetric)
> +        args->tpm2_asymmetric = TPM_ALG_RSA;
> +
> +      if (!args->tpm2_bank)
> +        args->tpm2_bank = TPM_ALG_SHA256;
> +
> +      break;
> +
> +    case GRUB_PROTECT_ACTION_REMOVE:
> +      if (args->args & GRUB_PROTECT_ARG_TPM2_ASYMMETRIC)
> +        {
> +          fprintf (stderr,
> +                   _("--tpm2-asymmetric is invalid when --action is 'remove'.\n"));
> +          return GRUB_ERR_BAD_ARGUMENT;
> +        }
> +
> +      if (args->args & GRUB_PROTECT_ARG_TPM2_BANK)
> +        {
> +          fprintf (stderr,
> +                   _("--tpm2-bank is invalid when --action is 'remove'.\n"));
> +          return GRUB_ERR_BAD_ARGUMENT;
> +        }
> +
> +      if (args->args & GRUB_PROTECT_ARG_TPM2_KEYFILE)
> +        {
> +          fprintf (stderr,
> +                   _("--tpm2-keyfile is invalid when --action is 'remove'.\n"));
> +          return GRUB_ERR_BAD_ARGUMENT;
> +        }
> +
> +      if (args->args & GRUB_PROTECT_ARG_TPM2_OUTFILE)
> +        {
> +          fprintf (stderr,
> +                   _("--tpm2-outfile is invalid when --action is 'remove'.\n"));
> +          return GRUB_ERR_BAD_ARGUMENT;
> +        }
> +
> +      if (args->args & GRUB_PROTECT_ARG_TPM2_PCRS)
> +        {
> +          fprintf (stderr,
> +                   _("--tpm2-pcrs is invalid when --action is 'remove'.\n"));
> +          return GRUB_ERR_BAD_ARGUMENT;
> +        }
> +
> +      if (args->args & GRUB_PROTECT_ARG_TPM2_PERSIST)
> +        {
> +          fprintf (stderr,
> +                   _("--tpm2-persist is invalid when --action is 'remove'.\n"));
> +          return GRUB_ERR_BAD_ARGUMENT;
> +        }
> +
> +      if (!args->tpm2_device)
> +        args->tpm2_device = "/dev/tpm0";
> +
> +      if (!args->tpm2_srk)
> +        args->tpm2_srk = TPM2_SRK_HANDLE;
> +
> +      break;
> +
> +    default:
> +      fprintf (stderr,
> +               _("The TPM2 key protector only supports the following actions: add, remove.\n"));
> +      return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_protector_args_kvp_parse (char *kvp, const char **key, char **value)
> +{
> +  char divider;
> +  char *separator;
> +  grub_size_t len;
> +
> +  if (!kvp)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  len = grub_strlen (kvp);
> +  if (!len || len < 2)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (kvp[0] == '-' && kvp[1] != '-')
> +    divider = ' ';
> +  else if (kvp[0] == '-' && kvp[1] == '-')
> +    divider = '=';
> +  else
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  separator = grub_strchr (kvp, divider);
> +  if (!separator)
> +    goto exit;
> +
> +  *separator = '\0';
> +  separator += 1;
> +
> +  if (separator - kvp >= len)
> +  {
> +    separator = NULL;
> +    goto exit;
> +  }
> +
> +exit:
> +  *key = kvp;
> +  *value = separator;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_protect_args_parse (int argc, char *argv[],
> +                         struct grub_protect_args *args)
> +{
> +  grub_err_t err;
> +  char *current_arg;
> +  const char *key;
> +  char *value;
> +  int i;
> +
> +  for (i = 1; i < argc; i++)
> +    {
> +      current_arg = argv[i];
> +
> +      err = grub_protector_args_kvp_parse (current_arg, &key, &value);
> +      if (err)
> +        return err;
> +
> +      if (grub_strcmp (key, "--action") == 0 ||
> +          grub_strcmp (key, "-a") == 0)
> +        {
> +          if (args->args & GRUB_PROTECT_ARG_ACTION)
> +            {
> +              fprintf (stderr,
> +                       _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (!value)
> +            {
> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (grub_strcmp (value, "add") == 0)
> +            args->action = GRUB_PROTECT_ACTION_ADD;
> +          else if (grub_strcmp (value, "remove") == 0)
> +            args->action = GRUB_PROTECT_ACTION_REMOVE;
> +          else
> +            {
> +              fprintf (stderr, _("'%s' is not a valid action.\n"), value);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          args->args |= GRUB_PROTECT_ARG_ACTION;
> +        }
> +      else if (grub_strcmp (key, "--protector") == 0 ||
> +               grub_strcmp (key, "-p") == 0)

This might be confusing because its -p here but -k to cryptomount.

> +        {
> +          if (args->args & GRUB_PROTECT_ARG_PROTECTOR)
> +            {
> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (!value)
> +            {
> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (grub_strcmp (value, "tpm2") == 0)
> +            args->protector = GRUB_PROTECT_TYPE_TPM2;
> +          else
> +            {
> +              fprintf (stderr, _("'%s' is not a valid protector.\n"), value);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          args->args |= GRUB_PROTECT_ARG_PROTECTOR;
> +        }
> +      else if (grub_strcmp (key, "--tpm2-device") == 0)
> +        {
> +          if (args->args & GRUB_PROTECT_ARG_TPM2_DEVICE)
> +            {
> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (!value)
> +            {
> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          args->tpm2_device = value;
> +          args->args |= GRUB_PROTECT_ARG_TPM2_DEVICE;
> +        }
> +      else if (grub_strcmp (key, "--tpm2-pcrs") == 0)
> +        {
> +          if (args->args & GRUB_PROTECT_ARG_TPM2_PCRS)
> +            {
> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (!value)
> +            {
> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          grub_size_t len = grub_strlen (value);
> +          grub_size_t dist;
> +
> +          long pcr;
> +          char *next_pcr;
> +          char *current_pcr = value;
> +          grub_uint8_t pcr_u8;
> +          grub_uint8_t j;
> +          grub_uint8_t k;
> +
> +          for (j = 0;; j++)
> +            {
> +              next_pcr = grub_strchr (current_pcr, ',');
> +              if (next_pcr)
> +                *next_pcr = '\0';
> +
> +              grub_errno = GRUB_ERR_NONE;
> +              pcr = grub_strtoul (current_pcr, NULL, 0);
> +              if (grub_errno != GRUB_ERR_NONE)
> +                {
> +                  fprintf (stderr, _("'%s' is not a valid PCR number.\n"),
> +                           current_pcr);
> +                  return grub_errno;
> +                }
> +
> +              if (pcr > TPM_MAX_PCRS)
> +                {
> +                  fprintf (stderr,
> +                           _("%lu larger than the maximum number of PCRs.\n"),
> +                           pcr);
> +                  return GRUB_ERR_OUT_OF_RANGE;
> +                }
> +
> +              pcr_u8 = (grub_uint8_t)pcr;
> +
> +              for (k = 0; k < args->tpm2_pcr_count; k++)
> +              {
> +                if (args->tpm2_pcrs[k] == pcr_u8)
> +                  {
> +                    fprintf (stderr, _("PCR %u was already specified.\n"),
> +                             pcr_u8);
> +                    return GRUB_ERR_BAD_ARGUMENT;
> +                  }
> +              }
> +
> +              args->tpm2_pcrs[j] = pcr_u8;
> +              args->tpm2_pcr_count++;
> +
> +              if (!next_pcr)
> +                break;
> +
> +              current_pcr = next_pcr + 1;
> +
> +              dist = (current_pcr - value);
> +              if (dist >= len)
> +                break;
> +            }
> +
> +          args->args |= GRUB_PROTECT_ARG_TPM2_PCRS;
> +        }
> +      else if (grub_strcmp (key, "--tpm2-srk") == 0)
> +        {
> +          if (args->args & GRUB_PROTECT_ARG_TPM2_SRK)
> +            {
> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (!value)
> +            {
> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          long srk;
> +
> +          grub_errno = GRUB_ERR_NONE;
> +          srk = grub_strtoul (value, NULL, 0);
> +          if (grub_errno != GRUB_ERR_NONE)
> +            {
> +              fprintf (stderr, _("'%s' is not a valid SRK handle.\n"), value);
> +              return grub_errno;
> +            }
> +
> +          if (srk > GRUB_UINT_MAX)
> +            {
> +              fprintf (stderr,
> +                       _("%lu is too large to be a valid SRK handle.\n"), srk);
> +              return GRUB_ERR_OUT_OF_RANGE;
> +            }
> +
> +          args->tpm2_srk = (TPM_HANDLE)srk;
> +          args->args |= GRUB_PROTECT_ARG_TPM2_SRK;
> +        }
> +      else if (grub_strcmp (key, "--tpm2-asymmetric") == 0)
> +        {
> +          if (args->args & GRUB_PROTECT_ARG_TPM2_ASYMMETRIC)
> +            {
> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (!value)
> +            {
> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (grub_strcasecmp (value, "ECC") == 0)
> +            args->tpm2_asymmetric = TPM_ALG_ECC;
> +          else if (grub_strcasecmp (value, "RSA") == 0)
> +            args->tpm2_asymmetric = TPM_ALG_RSA;
> +          else
> +            {
> +              fprintf (stderr, _("'%s' is not a valid asymmetric algorithm.\n"),
> +                       value);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          args->args |= GRUB_PROTECT_ARG_TPM2_ASYMMETRIC;
> +        }
> +      else if (grub_strcmp (key, "--tpm2-bank") == 0)
> +        {
> +          if (args->args & GRUB_PROTECT_ARG_TPM2_BANK)
> +            {
> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (!value)
> +            {
> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (grub_strcasecmp (value, "SHA1") == 0)
> +            args->tpm2_bank = TPM_ALG_SHA1;
> +          else if (grub_strcasecmp (value, "SHA256") == 0)
> +            args->tpm2_bank = TPM_ALG_SHA256;
> +          else
> +            {
> +              fprintf (stderr, _("'%s' is not a valid PCR bank.\n"), value);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          args->args |= GRUB_PROTECT_ARG_TPM2_BANK;
> +        }
> +      else if (grub_strcmp (key, "--tpm2-keyfile") == 0)
> +        {
> +          if (args->args & GRUB_PROTECT_ARG_TPM2_KEYFILE)
> +            {
> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (!value)
> +            {
> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +	  args->tpm2_keyfile = value;
> +          args->args |= GRUB_PROTECT_ARG_TPM2_KEYFILE;
> +        }
> +      else if (grub_strcmp (key, "--tpm2-outfile") == 0)
> +        {
> +          if (args->args & GRUB_PROTECT_ARG_TPM2_OUTFILE)
> +            {
> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (!value)
> +            {
> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          args->tpm2_outfile = value;
> +          args->args |= GRUB_PROTECT_ARG_TPM2_OUTFILE;
> +        }
> +      else if (grub_strcmp (key, "--tpm2-persist") == 0)
> +        {
> +          if (args->args & GRUB_PROTECT_ARG_TPM2_PERSIST)
> +            {
> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (value)
> +            {
> +              fprintf (stderr, _("Argument %s does not accept a value.\n"),
> +                       key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          args->tpm2_persist = 1;
> +          args->args |= GRUB_PROTECT_ARG_TPM2_PERSIST;
> +        }
> +      else if (grub_strcmp (key, "--tpm2-evict") == 0)
> +        {
> +          if (args->args & GRUB_PROTECT_ARG_TPM2_EVICT)
> +            {
> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          if (value)
> +            {
> +              fprintf (stderr, _("Argument %s does not accept a value.\n"),
> +                       key);
> +              return GRUB_ERR_BAD_ARGUMENT;
> +            }
> +
> +          args->tpm2_evict = 1;
> +          args->args |= GRUB_PROTECT_ARG_TPM2_EVICT;
> +        }
> +        else
> +          {
> +            fprintf (stderr, _("'%s' is not a valid argument.\n"), key);
> +            return GRUB_ERR_BAD_ARGUMENT;
> +          }
> +    }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_protect_args_verify (struct grub_protect_args *args)
> +{
> +  if (args->action == GRUB_PROTECT_ACTION_ERROR)
> +    {
> +      fprintf (stderr, "--action is mandatory.\n");
> +      return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +
> +  /* At the moment, the only configurable key protector is the TPM2 one, so it
> +   * is the only keu protector supported by this tool. */
> +  if (args->protector != GRUB_PROTECT_TYPE_TPM2)
> +    {
> +      fprintf (stderr,
> +               _("--protector is mandatory and only 'tpm2' is currently supported.\n"));
> +      return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +
> +  switch (args->protector)
> +    {
> +    case GRUB_PROTECT_TYPE_TPM2:
> +      return grub_protect_tpm2_args_verify (args);
> +    default:
> +      return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_protect_dispatch (struct grub_protect_args *args)
> +{
> +  switch (args->protector)
> +    {
> +    case GRUB_PROTECT_TYPE_TPM2:
> +      return grub_protect_tpm2_run (args);
> +    default:
> +      return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +}
> +
> +static void
> +grub_protect_init (int *argc, char **argv[])
> +{
> +  grub_util_host_init (argc, argv);
> +
> +  grub_util_biosdisk_init (NULL);
> +
> +  grub_init_all ();
> +  grub_gcry_init_all ();
> +
> +  grub_lvm_fini ();
> +  grub_mdraid09_fini ();
> +  grub_mdraid1x_fini ();
> +  grub_diskfilter_fini ();
> +  grub_diskfilter_init ();
> +  grub_mdraid09_init ();
> +  grub_mdraid1x_init ();
> +  grub_lvm_init ();
> +}
> +
> +static void
> +grub_protect_fini (void)
> +{
> +  grub_gcry_fini_all ();
> +  grub_fini_all ();
> +  grub_util_biosdisk_fini ();
> +}
> +
> +int
> +main (int argc, char *argv[])
> +{
> +  grub_err_t err;
> +  struct grub_protect_args args = { 0 };
> +
> +  grub_protect_init (&argc, &argv);
> +
> +  err = grub_protect_args_parse (argc, argv, &args);
> +  if (err)
> +    goto exit;
> +
> +  err = grub_protect_args_verify (&args);
> +  if (err)
> +    goto exit;
> +
> +  err = grub_protect_dispatch (&args);
> +  if (err)
> +    goto exit;
> +
> +exit:
> +  grub_protect_fini ();
> +
> +  return err;
> +}

It would be nice to take arguments --help and -h and spit out something
useful.

Glenn



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 4/5] cryptodisk: Support key protectors
  2022-01-25  5:42   ` Glenn Washburn
@ 2022-01-25 17:28     ` James Bottomley
  2022-01-27 14:39       ` Hernan Gatta
  2022-01-27 14:26     ` Hernan Gatta
  1 sibling, 1 reply; 21+ messages in thread
From: James Bottomley @ 2022-01-25 17:28 UTC (permalink / raw)
  To: development, Hernan Gatta; +Cc: The development of GNU GRUB

On Mon, 2022-01-24 at 23:42 -0600, Glenn Washburn wrote:
> On Mon, 24 Jan 2022 06:12:17 -0800
> Hernan Gatta <hegatta@linux.microsoft.com> wrote:
[...]
> > +    }
> > +
> >    if (state[0].set) /* uuid */
> >      {
> >        int found_uuid;
> > @@ -1385,7 +1404,7 @@ GRUB_MOD_INIT (cryptodisk)
> >  {
> >    grub_disk_dev_register (&grub_cryptodisk_dev);
> >    cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount,
> > 0,
> > -			      N_("[-p password] <SOURCE|-u UUID|-a|-
> > b>"),
> > +			      N_("[-p password] [-k protector[:args]]
> > <SOURCE|-u UUID|-a|-b>"),
> 
> This looks eerily similiar to what I proposed to James in response to
> his SEV patches[1]. As such, I am in favor of this syntax and it
> looks to me like a framework that James can use for his SEV series.

Well, I could, but I've got to say for a shell like system, which grub
is, argument lists passed as a single argument always cause problems
with quoting and interpolation.  if the protector needs additional
arguments to initialize, then it should really be done as a separate
module to avoid the arguments within argument problem, so

protector_init [args]
crtptomount -k protector ...

means that [args] can use the standard arguments and doesn't have any
internal quoting issues.

James




^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 0/5] Automatic TPM Disk Unlock
  2022-01-24 14:12 [PATCH 0/5] Automatic TPM Disk Unlock Hernan Gatta
                   ` (5 preceding siblings ...)
  2022-01-24 23:21 ` [PATCH 0/5] Automatic TPM Disk Unlock Didier Spaier
@ 2022-01-25 20:30 ` Glenn Washburn
  2022-01-27 14:03   ` Hernan Gatta
  6 siblings, 1 reply; 21+ messages in thread
From: Glenn Washburn @ 2022-01-25 20:30 UTC (permalink / raw)
  To: Hernan Gatta; +Cc: The development of GNU GRUB

On Mon, 24 Jan 2022 06:12:13 -0800
Hernan Gatta <hegatta@linux.microsoft.com> wrote:

> This patch series adds support for automatically unlocking fully-encrypted disks
> using a TPM 2.0.
> 
> Currently, when GRUB encounters a fully-encrypted disk that it must access, its
> corresponding cryptodisk module (LUKS 1, LUKS2, or GELI) interactively prompts
> the user for a passphrase. An improvement to the boot process would be for GRUB
> to automatically retrieve the unlocking key for fully-encrypted disks from a
> protected location and to unlock these transparently. To this end, a TPM may be
> used to protect the unlocking key to a known-good state of the platform. Once
> the key is protected in this way, assuming that the platform remains
> trustworthy, GRUB can then utilize the TPM to release the key during boot and
> thus unlock fully-encrypted disks without user interaction. Such a model would
> not only be more convenient for end-users but also for virtual machines in cloud
> environments where no user is ever present.
> 
> Design
> ------
> 
> This patchset first adds a key protectors framework. This framework allows for
> key protector modules to register when loaded. A key protector is defined as a
> module that knows how to retrieve an unlocking key from a specific source. This
> patchset adds a single such key protector module that understands how to
> retrieve an unlocking key from a TPM 2.0 by unsealing a sealed key file via a
> Storage Root Key (SRK).
> 
> Additionally, this patchset expands the cryptomount command to accept a key
> protector parameter. This parameter carries the information necessary to select
> and parameterize a key protector to be used to retrieve an unlocking key for the
> disk in question. That is, given an invocation of cryptomount to mount a
> specific disk (e.g., "cryptomount (hd0,gpt2)", "cryptomount -u UUID"), a key
> protector can be used to automatically retrieve an unlocking key without an
> interactive prompt.
> 
> Lastly, this patchset also includes a new tool, grub-protect, that allows the
> user to seal a key file against a set of Platform Configuration Registers (PCRs)
> using an SRK. This sealed key file is expected to be stored in an unencrypted
> partition, such as the EFI System Partition (ESP), where GRUB can read it. The
> sealed key is then unsealed by the TPM2 key protector automatically, provided
> that the PCRs selected match on subsequent boots.

This series should include a documentation patch at a minimum. I would
also like to see a QEMU test added. But it should use my cryptomount
test series, which hasn't made it to master yet. So this shouldn't be a
hard requirement. It would be nice for someone more familiar with
Secure boot and TPM to figure out how to get it working in QEMU so we/I
can easily add it to the tests. It looks like this should work with
the required software[1].

Glenn

[1] https://en.opensuse.org/Software_TPM_Emulator_For_QEMU

> 
> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
> 
> Hernan Gatta (5):
>   protectors: Add key protectors framework
>   tpm2: Add TPM Software Stack (TSS)
>   protectors: Add TPM2 Key Protector
>   cryptodisk: Support key protectors
>   util/grub-protect: Add new tool
> 
>  .gitignore                             |    1 +
>  Makefile.util.def                      |   17 +
>  configure.ac                           |    1 +
>  grub-core/Makefile.am                  |    1 +
>  grub-core/Makefile.core.def            |   10 +
>  grub-core/disk/cryptodisk.c            |   21 +-
>  grub-core/kern/protectors.c            |   98 +++
>  grub-core/tpm2/buffer.c                |  145 ++++
>  grub-core/tpm2/module.c                |  742 ++++++++++++++++++
>  grub-core/tpm2/mu.c                    |  807 +++++++++++++++++++
>  grub-core/tpm2/tcg2.c                  |  143 ++++
>  grub-core/tpm2/tpm2.c                  |  711 +++++++++++++++++
>  include/grub/protector.h               |   55 ++
>  include/grub/tpm2/buffer.h             |   65 ++
>  include/grub/tpm2/internal/functions.h |  117 +++
>  include/grub/tpm2/internal/structs.h   |  675 ++++++++++++++++
>  include/grub/tpm2/internal/types.h     |  372 +++++++++
>  include/grub/tpm2/mu.h                 |  292 +++++++
>  include/grub/tpm2/tcg2.h               |   34 +
>  include/grub/tpm2/tpm2.h               |   38 +
>  util/grub-protect.c                    | 1344 ++++++++++++++++++++++++++++++++
>  21 files changed, 5688 insertions(+), 1 deletion(-)
>  create mode 100644 grub-core/kern/protectors.c
>  create mode 100644 grub-core/tpm2/buffer.c
>  create mode 100644 grub-core/tpm2/module.c
>  create mode 100644 grub-core/tpm2/mu.c
>  create mode 100644 grub-core/tpm2/tcg2.c
>  create mode 100644 grub-core/tpm2/tpm2.c
>  create mode 100644 include/grub/protector.h
>  create mode 100644 include/grub/tpm2/buffer.h
>  create mode 100644 include/grub/tpm2/internal/functions.h
>  create mode 100644 include/grub/tpm2/internal/structs.h
>  create mode 100644 include/grub/tpm2/internal/types.h
>  create mode 100644 include/grub/tpm2/mu.h
>  create mode 100644 include/grub/tpm2/tcg2.h
>  create mode 100644 include/grub/tpm2/tpm2.h
>  create mode 100644 util/grub-protect.c
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 0/5] Automatic TPM Disk Unlock
  2022-01-25 20:30 ` Glenn Washburn
@ 2022-01-27 14:03   ` Hernan Gatta
  2022-01-27 22:34     ` Glenn Washburn
  0 siblings, 1 reply; 21+ messages in thread
From: Hernan Gatta @ 2022-01-27 14:03 UTC (permalink / raw)
  To: Glenn Washburn; +Cc: Hernan Gatta, The development of GNU GRUB



On Tue, 25 Jan 2022, Glenn Washburn wrote:

> On Mon, 24 Jan 2022 06:12:13 -0800
> Hernan Gatta <hegatta@linux.microsoft.com> wrote:
>
>> This patch series adds support for automatically unlocking fully-encrypted disks
>> using a TPM 2.0.
>>
>> Currently, when GRUB encounters a fully-encrypted disk that it must access, its
>> corresponding cryptodisk module (LUKS 1, LUKS2, or GELI) interactively prompts
>> the user for a passphrase. An improvement to the boot process would be for GRUB
>> to automatically retrieve the unlocking key for fully-encrypted disks from a
>> protected location and to unlock these transparently. To this end, a TPM may be
>> used to protect the unlocking key to a known-good state of the platform. Once
>> the key is protected in this way, assuming that the platform remains
>> trustworthy, GRUB can then utilize the TPM to release the key during boot and
>> thus unlock fully-encrypted disks without user interaction. Such a model would
>> not only be more convenient for end-users but also for virtual machines in cloud
>> environments where no user is ever present.
>>
>> Design
>> ------
>>
>> This patchset first adds a key protectors framework. This framework allows for
>> key protector modules to register when loaded. A key protector is defined as a
>> module that knows how to retrieve an unlocking key from a specific source. This
>> patchset adds a single such key protector module that understands how to
>> retrieve an unlocking key from a TPM 2.0 by unsealing a sealed key file via a
>> Storage Root Key (SRK).
>>
>> Additionally, this patchset expands the cryptomount command to accept a key
>> protector parameter. This parameter carries the information necessary to select
>> and parameterize a key protector to be used to retrieve an unlocking key for the
>> disk in question. That is, given an invocation of cryptomount to mount a
>> specific disk (e.g., "cryptomount (hd0,gpt2)", "cryptomount -u UUID"), a key
>> protector can be used to automatically retrieve an unlocking key without an
>> interactive prompt.
>>
>> Lastly, this patchset also includes a new tool, grub-protect, that allows the
>> user to seal a key file against a set of Platform Configuration Registers (PCRs)
>> using an SRK. This sealed key file is expected to be stored in an unencrypted
>> partition, such as the EFI System Partition (ESP), where GRUB can read it. The
>> sealed key is then unsealed by the TPM2 key protector automatically, provided
>> that the PCRs selected match on subsequent boots.
>
> This series should include a documentation patch at a minimum. I would
> also like to see a QEMU test added. But it should use my cryptomount
> test series, which hasn't made it to master yet. So this shouldn't be a
> hard requirement. It would be nice for someone more familiar with
> Secure boot and TPM to figure out how to get it working in QEMU so we/I
> can easily add it to the tests. It looks like this should work with
> the required software[1].
>
> Glenn
>
> [1] https://en.opensuse.org/Software_TPM_Emulator_For_QEMU
>

Agreed. I wanted to get feedback on the feature as a whole first before 
writing documentation for it since, I figured, the way it functions may 
change as a result of your comments. Once we settle on how it should work 
and, more importantly, on how a user might use it (I'm thinking of James' 
comment on separating the cryptomount command from the configuration of a 
key protector), then I'll add documentation.

With respect to your comment about adding tests, it should indeed be 
possible to test this using QEMU and a software TPM such as swtpm. I'm 
happy to take a look at that.

Would it be alright for me to submit a patch series for tests 
separately from this series?

>>
>> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
>>
>> Hernan Gatta (5):
>>   protectors: Add key protectors framework
>>   tpm2: Add TPM Software Stack (TSS)
>>   protectors: Add TPM2 Key Protector
>>   cryptodisk: Support key protectors
>>   util/grub-protect: Add new tool
>>
>>  .gitignore                             |    1 +
>>  Makefile.util.def                      |   17 +
>>  configure.ac                           |    1 +
>>  grub-core/Makefile.am                  |    1 +
>>  grub-core/Makefile.core.def            |   10 +
>>  grub-core/disk/cryptodisk.c            |   21 +-
>>  grub-core/kern/protectors.c            |   98 +++
>>  grub-core/tpm2/buffer.c                |  145 ++++
>>  grub-core/tpm2/module.c                |  742 ++++++++++++++++++
>>  grub-core/tpm2/mu.c                    |  807 +++++++++++++++++++
>>  grub-core/tpm2/tcg2.c                  |  143 ++++
>>  grub-core/tpm2/tpm2.c                  |  711 +++++++++++++++++
>>  include/grub/protector.h               |   55 ++
>>  include/grub/tpm2/buffer.h             |   65 ++
>>  include/grub/tpm2/internal/functions.h |  117 +++
>>  include/grub/tpm2/internal/structs.h   |  675 ++++++++++++++++
>>  include/grub/tpm2/internal/types.h     |  372 +++++++++
>>  include/grub/tpm2/mu.h                 |  292 +++++++
>>  include/grub/tpm2/tcg2.h               |   34 +
>>  include/grub/tpm2/tpm2.h               |   38 +
>>  util/grub-protect.c                    | 1344 ++++++++++++++++++++++++++++++++
>>  21 files changed, 5688 insertions(+), 1 deletion(-)
>>  create mode 100644 grub-core/kern/protectors.c
>>  create mode 100644 grub-core/tpm2/buffer.c
>>  create mode 100644 grub-core/tpm2/module.c
>>  create mode 100644 grub-core/tpm2/mu.c
>>  create mode 100644 grub-core/tpm2/tcg2.c
>>  create mode 100644 grub-core/tpm2/tpm2.c
>>  create mode 100644 include/grub/protector.h
>>  create mode 100644 include/grub/tpm2/buffer.h
>>  create mode 100644 include/grub/tpm2/internal/functions.h
>>  create mode 100644 include/grub/tpm2/internal/structs.h
>>  create mode 100644 include/grub/tpm2/internal/types.h
>>  create mode 100644 include/grub/tpm2/mu.h
>>  create mode 100644 include/grub/tpm2/tcg2.h
>>  create mode 100644 include/grub/tpm2/tpm2.h
>>  create mode 100644 util/grub-protect.c
>>
>

Thank you,
Hernan


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 1/5] protectors: Add key protectors framework
  2022-01-25  2:54   ` Glenn Washburn
@ 2022-01-27 14:05     ` Hernan Gatta
  0 siblings, 0 replies; 21+ messages in thread
From: Hernan Gatta @ 2022-01-27 14:05 UTC (permalink / raw)
  To: Glenn Washburn; +Cc: Hernan Gatta, The development of GNU GRUB



On Mon, 24 Jan 2022, Glenn Washburn wrote:

> On Mon, 24 Jan 2022 06:12:14 -0800
> Hernan Gatta <hegatta@linux.microsoft.com> wrote:
>
>> From: Hernan Gatta <hegatta@microsoft.com>
>>
>> A key protector encapsulates functionality to retrieve an unlocking key for a
>> fully-encrypted disk from a specific source. A key protector module registers
>> itself with the key protectors framework when it is loaded and unregisters when
>> unloaded. Additionally, a key protector may accept parameters that describe how
>> it should operate.
>>
>> The key protectors framework, besides offering registration and unregistration
>> functions, also offers a one-stop routine for finding and invoking a key
>> protector. This method accepts a formatted string with the name of a key
>> protector followed optionally by colon-separated, key protector-specific
>> parameters. If a key protector with the specified name exists and if an
>> unlocking key is successfully retrieved by the latter, the function returns to
>> the caller the retrieved key and its length.
>>
>> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
>> ---
>>  grub-core/Makefile.am       |  1 +
>>  grub-core/Makefile.core.def |  1 +
>>  grub-core/kern/protectors.c | 98 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/grub/protector.h    | 55 +++++++++++++++++++++++++
>>  4 files changed, 155 insertions(+)
>>  create mode 100644 grub-core/kern/protectors.c
>>  create mode 100644 include/grub/protector.h
>>
>> diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
>> index ee88e44..f78cd9d 100644
>> --- a/grub-core/Makefile.am
>> +++ b/grub-core/Makefile.am
>> @@ -90,6 +90,7 @@ endif
>>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm.h
>>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/parser.h
>>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/partition.h
>> +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/protector.h
>>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/stack_protector.h
>>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h
>>  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h
>> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
>> index 8022e1c..e4ae78b 100644
>> --- a/grub-core/Makefile.core.def
>> +++ b/grub-core/Makefile.core.def
>> @@ -138,6 +138,7 @@ kernel = {
>>    common = kern/misc.c;
>>    common = kern/parser.c;
>>    common = kern/partition.c;
>> +  common = kern/protectors.c;
>>    common = kern/rescue_parser.c;
>>    common = kern/rescue_reader.c;
>>    common = kern/term.c;
>> diff --git a/grub-core/kern/protectors.c b/grub-core/kern/protectors.c
>> new file mode 100644
>> index 0000000..2df0c60
>> --- /dev/null
>> +++ b/grub-core/kern/protectors.c
>> @@ -0,0 +1,98 @@
>> +/*
>> + *  GRUB  --  GRand Unified Bootloader
>> + *  Copyright (C) 2022 Microsoft Corporation
>> + *
>> + *  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 <grub/protector.h>
>> +
>> +struct grub_key_protector *grub_key_protectors = NULL;
>> +
>> +grub_err_t
>> +grub_key_protector_register (struct grub_key_protector *protector)
>> +{
>> +  if (!protector || !protector->name || !grub_strlen(protector->name))
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  if (grub_key_protectors &&
>> +      grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
>> +                            protector->name))
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  grub_list_push (GRUB_AS_LIST_P (&grub_key_protectors),
>> +                  GRUB_AS_LIST (protector));
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +grub_err_t
>> +grub_key_protector_unregister (struct grub_key_protector *protector)
>> +{
>> +  if (!protector)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  grub_list_remove (GRUB_AS_LIST (protector));
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +grub_err_t
>> +grub_key_protector_recover_key (char *args, grub_uint8_t **key,
>> +                                grub_size_t *key_size)
>> +{
>> +  char *first_separator = NULL;
>> +  char *protector_args = NULL;
>> +  struct grub_key_protector *protector = NULL;
>> +
>> +  if (!grub_key_protectors)
>> +    return GRUB_ERR_OUT_OF_RANGE;
>> +
>> +  if (!args || !grub_strlen (args))
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  /* Find the position of the first parameter separator: the stuff before it is
>> +   * the name of the requested key protector and the stuff after it are the
>> +   * parameters for said key protector, if any. */
>> +  first_separator = grub_strchr (args, ':');
>> +  if (first_separator)
>> +    {
>> +      /* Reject a separator at the very beginning. */
>> +      if (first_separator == args)
>> +        return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +      /* Having a lone colon after the name of the key protector with no
>> +       * further parameters thereafter does not make any sense. */
>> +      if (*(first_separator + 1) == '\0')
>> +        return GRUB_ERR_BAD_ARGUMENT;
>
> This should be allowed, its an unnecessary restriction. For instance,
> it prevents building this string without logic to determine if a colon
> is needed or not. Instead of this if block, I suggest having...
>
>> +
>> +      /* Consume the colon, effectively splitting 'args' in two. */
>> +      first_separator[0] = '\0';
>> +
>
> ... here, an if with the opposite conditional.
>
> if (*(first_separator + 1) != '\0')
>

I'll modify this as suggested.

>> +      /* The protector-specific arguments are after the first colon. */
>> +      protector_args = first_separator + 1;
>> +    }
>> +
>> +  /* Try to find a key protector with the specified name. */
>> +  protector = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
>> +                                    args);
>> +  if (!protector)
>> +    return GRUB_ERR_OUT_OF_RANGE;
>> +
>> +  /* 'protector_args' may be NULL. */
>> +  return protector->recover_key (protector_args, key, key_size);
>> +}
>> diff --git a/include/grub/protector.h b/include/grub/protector.h
>> new file mode 100644
>> index 0000000..d445a33
>> --- /dev/null
>> +++ b/include/grub/protector.h
>> @@ -0,0 +1,55 @@
>> +/*
>> + *  GRUB  --  GRand Unified Bootloader
>> + *  Copyright (C) 2022 Microsoft Corporation
>> + *
>> + *  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_PROTECTOR_HEADER
>> +#define GRUB_PROTECTOR_HEADER 1
>> +
>> +#include <grub/err.h>
>> +#include <grub/types.h>
>> +
>> +enum grub_key_protector_flags
>> +  {
>> +    GRUB_KEY_PROTECTOR_FLAG_NONE = 0,
>> +    GRUB_KEY_PROTECTOR_FLAG_REPEATABLE = 1 << 0
>> +  };
>> +
>> +struct grub_key_protector
>> +{
>> +  struct grub_key_protector *next;
>> +  struct grub_key_protector **prev;
>> +
>> +  const char *name;
>> +  enum grub_key_protector_flags flags;
>> +
>> +  grub_err_t (*recover_key) (char *args, grub_uint8_t **key,
>> +                             grub_size_t *key_size);
>> +};
>> +
>> +extern struct grub_key_protector *EXPORT_VAR (grub_key_protectors);
>> +
>> +grub_err_t
>> +EXPORT_FUNC (grub_key_protector_register) (struct grub_key_protector *protector);
>> +
>> +grub_err_t
>> +EXPORT_FUNC (grub_key_protector_unregister) (struct grub_key_protector *protector);
>> +
>> +grub_err_t
>> +EXPORT_FUNC (grub_key_protector_recover_key) (char args[], grub_uint8_t **key,
>> +                                              grub_size_t *key_size);
>> +
>> +#endif /* ! GRUB_PROTECTOR_HEADER */
>
> Glenn
>

Thank you,
Hernan


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 3/5] protectors: Add TPM2 Key Protector
  2022-01-25  3:55   ` Glenn Washburn
@ 2022-01-27 14:11     ` Hernan Gatta
  2022-01-28  3:40       ` Glenn Washburn
  0 siblings, 1 reply; 21+ messages in thread
From: Hernan Gatta @ 2022-01-27 14:11 UTC (permalink / raw)
  To: Glenn Washburn; +Cc: Hernan Gatta, The development of GNU GRUB



On Mon, 24 Jan 2022, Glenn Washburn wrote:

> On Mon, 24 Jan 2022 06:12:16 -0800
> Hernan Gatta <hegatta@linux.microsoft.com> wrote:
>
>> From: Hernan Gatta <hegatta@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 therefore possess reasonable defaults. One of these
>> arguments is the keyfile parameter, which is mandatory.
>>
>> The value of this parameter must be a path to a sealed key file (e.g.,
>> (hd0,gpt1)/boot/grub2/sealed_key). 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.
>>
>
> There are a lot of points in this code where an error can be returned.
> In this case, all I'm going to get is a non-descript error code (almost
> always GRUB_ERR_BAD_ARGUMENT). So as a user, how am I to know where the
> error is actually coming from? For this reason, I like the use of
> grub_error(error_code, error_format_string, fmtstr_args...), which will
> set grub_errno and an error message, which can be retrieved up the stack
> and presented to the user. I'm not sure that every return of a naked
> error code should be replaced by grub_error(), depends on how granular
> of an error message you want to see. I would error on the side of more
> granular. For instance, its really nice that GCC will tell me which
> format code type specifier and a argument its choking on, rather than a
> catch all "I'm failing to compile your code because you have an
> incorrect format code somewhere in your format string".
>

Agreed. I'll add calls to grub_error to indicate why things fail and, when 
they do, capture the error up the stack (I was thinking in the command 
handler for cryptomount), and print it.

>> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
>> ---
>>  grub-core/Makefile.core.def |   9 +
>>  grub-core/tpm2/module.c     | 742 ++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 751 insertions(+)
>>  create mode 100644 grub-core/tpm2/module.c
>>
>> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
>> index e4ae78b..8691440 100644
>> --- a/grub-core/Makefile.core.def
>> +++ b/grub-core/Makefile.core.def
>> @@ -2498,6 +2498,15 @@ module = {
>>  };
>>
>>  module = {
>> +  name = tpm2;
>> +  common = tpm2/buffer.c;
>> +  common = tpm2/module.c;
>> +  common = tpm2/mu.c;
>> +  common = tpm2/tpm2.c;
>> +  efi = tpm2/tcg2.c;
>> +};
>> +
>> +module = {
>>    name = tr;
>>    common = commands/tr.c;
>>  };
>> diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
>> new file mode 100644
>> index 0000000..cd97452
>> --- /dev/null
>> +++ b/grub-core/tpm2/module.c
>> @@ -0,0 +1,742 @@
>> +/*
>> + *  GRUB  --  GRand Unified Bootloader
>> + *  Copyright (C) 2022 Microsoft Corporation
>> + *
>> + *  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/file.h>
>> +#include <grub/misc.h>
>> +#include <grub/mm.h>
>> +#include <grub/protector.h>
>> +#include <grub/tpm2/buffer.h>
>> +#include <grub/tpm2/mu.h>
>> +#include <grub/tpm2/tpm2.h>
>> +#include <grub/types.h>
>> +
>> +GRUB_MOD_LICENSE ("GPLv3+");
>> +
>> +typedef enum grub_tpm2_protector_args
>> +{
>> +  GRUB_TPM2_PROTECTOR_ARG_MODE       = 1 << 0,
>> +  GRUB_TPM2_PROTECTOR_ARG_PCRS       = 1 << 1,
>> +  GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC = 1 << 2,
>> +  GRUB_TPM2_PROTECTOR_ARG_BANK       = 1 << 3,
>> +  GRUB_TPM2_PROTECTOR_ARG_KEYFILE    = 1 << 4,
>> +  GRUB_TPM2_PROTECTOR_ARG_SRK        = 1 << 5,
>> +  GRUB_TPM2_PROTECTOR_ARG_NV         = 1 << 6
>> +} grub_tpm2_protector_args_t;
>> +
>> +typedef enum grub_tpm2_protector_mode
>> +{
>> +  GRUB_TPM2_PROTECTOR_MODE_SRK = 1,
>> +  GRUB_TPM2_PROTECTOR_MODE_NV  = 2
>> +} grub_tpm2_protector_mode_t;
>> +
>> +struct grub_tpm2_protector_context
>> +{
>> +  grub_tpm2_protector_args_t args;
>> +  grub_tpm2_protector_mode_t mode;
>> +  grub_uint8_t pcrs[TPM_MAX_PCRS];
>> +  grub_uint8_t pcr_count;
>> +  TPM_ALG_ID asymmetric;
>> +  TPM_ALG_ID bank;
>> +  const char *keyfile;
>> +  TPM_HANDLE srk;
>> +  TPM_HANDLE nv;
>> +};
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_parse_mode (struct grub_tpm2_protector_context *ctx,
>> +                                const char *value)
>> +{
>> +  if (grub_strcmp (value, "srk") == 0)
>> +    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
>> +  else if (grub_strcmp (value, "nv") == 0)
>> +    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_NV;
>> +  else
>> +    return GRUB_ERR_OUT_OF_RANGE;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_parse_pcrs (struct grub_tpm2_protector_context *ctx,
>> +                                char *value)
>> +{
>> +  grub_err_t err;
>> +  char *current_pcr = value;
>> +  char *next_pcr;
>> +  unsigned long pcr;
>> +  grub_uint8_t i;
>> +
>> +  if (grub_strlen (value) == 0)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  ctx->pcr_count = 0;
>> +  for (i = 0; i < TPM_MAX_PCRS; i++)
>> +    {
>> +      next_pcr = grub_strchr (current_pcr, ',');
>> +      if (next_pcr)
>> +        *next_pcr = '\0';
>> +      if (next_pcr == current_pcr)
>> +        return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +      grub_errno = GRUB_ERR_NONE;
>> +      pcr = grub_strtoul (current_pcr, NULL, 10);
>> +      if (grub_errno != GRUB_ERR_NONE)
>> +        {
>> +          err = grub_errno;
>> +          grub_errno = GRUB_ERR_NONE;
>> +          return err;
>> +        }
>> +
>> +      if (pcr > GRUB_UCHAR_MAX)
>> +        return GRUB_ERR_OUT_OF_RANGE;
>> +
>> +      ctx->pcrs[i] = (grub_uint8_t)pcr;
>> +      ctx->pcr_count++;
>> +
>> +      if (!next_pcr)
>> +        break;
>> +
>> +      current_pcr = next_pcr + 1;
>> +      if (*current_pcr == '\0')
>> +        return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +
>> +  if (i == TPM_MAX_PCRS)
>> +    return GRUB_ERR_OUT_OF_RANGE;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_parse_asymmetric (struct grub_tpm2_protector_context *ctx,
>> +                                      const char *value)
>> +{
>> +  if (grub_strcasecmp (value, "ECC") == 0)
>> +    ctx->asymmetric = TPM_ALG_ECC;
>> +  else if (grub_strcasecmp (value, "RSA") == 0)
>> +    ctx->asymmetric = TPM_ALG_RSA;
>> +  else
>> +    return GRUB_ERR_OUT_OF_RANGE;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_parse_bank (struct grub_tpm2_protector_context *ctx,
>> +                                const char *value)
>> +{
>> +  if (grub_strcasecmp (value, "SHA1") == 0)
>> +    ctx->bank = TPM_ALG_SHA1;
>> +  else if (grub_strcasecmp (value, "SHA256") == 0)
>> +    ctx->bank = TPM_ALG_SHA256;
>> +  else if (grub_strcasecmp (value, "SHA384") == 0)
>> +    ctx->bank = TPM_ALG_SHA384;
>> +  else
>> +    return GRUB_ERR_OUT_OF_RANGE;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_parse_keyfile (struct grub_tpm2_protector_context *ctx,
>> +                                   const char *value)
>> +{
>> +  if (grub_strlen (value) == 0)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  ctx->keyfile = value;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle)
>> +{
>> +  grub_err_t err;
>> +  unsigned long num;
>> +
>> +  grub_errno = GRUB_ERR_NONE;
>> +  num = grub_strtoul (value, NULL, 0);
>> +  if (grub_errno != GRUB_ERR_NONE)
>> +    {
>> +      err = grub_errno;
>> +      grub_errno = GRUB_ERR_NONE;
>> +      return err;
>> +    }
>> +
>> +  if (num > GRUB_UINT_MAX)
>> +    return GRUB_ERR_OUT_OF_RANGE;
>> +
>> +  *handle = (TPM_HANDLE)num;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_parse_srk (struct grub_tpm2_protector_context *ctx,
>> +                               const char *value)
>> +{
>> +  return grub_tpm2_protector_parse_tpm_handle (value, &ctx->srk);
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_parse_nvindex (struct grub_tpm2_protector_context *ctx,
>> +                                   const char *value)
>> +{
>> +  return grub_tpm2_protector_parse_tpm_handle (value, &ctx->nv);
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_match_arg (struct grub_tpm2_protector_context *ctx,
>> +                               const char *key, char *value)
>> +{
>> +  grub_err_t err;
>> +
>> +  if (grub_strlen (key) == 0 || grub_strlen (value) == 0)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  if (grub_strcmp (key, "mode") == 0)
>> +  {
>> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_MODE)
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +    err = grub_tpm2_protector_parse_mode (ctx, value);
>> +    if (!err)
>> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_MODE;
>> +
>> +    return err;
>> +  }
>> +
>> +  if (grub_strcmp (key, "pcrs") == 0)
>
> Minor nit, why wouldn't this be better as an "else if"? Seems
> unnecessary to check all the "if" statements if we've already found a
> match.
>

The reason why I wrote this in this way without an "else if" is because 
each "if" clause has an unconditional return. So, in the end, the entire 
sequence behaves in the manner you describe.

Would you prefer that I add the "else if"'s anyway for readability?

>> +  {
>> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_PCRS)
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +    err = grub_tpm2_protector_parse_pcrs (ctx, value);
>> +    if (!err)
>> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_PCRS;
>> +
>> +    return err;
>> +  }
>> +
>> +  if (grub_strcmp (key, "asymmetric") == 0)
>
> Ditto, for the rest of these ifs.
>

As above.

>> +  {
>> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC)
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +    err = grub_tpm2_protector_parse_asymmetric (ctx, value);
>> +    if (!err)
>> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC;
>> +
>> +    return err;
>> +  }
>> +
>> +  if (grub_strcmp (key, "bank") == 0)
>> +  {
>> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_BANK)
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +    err = grub_tpm2_protector_parse_bank (ctx, value);
>> +    if (!err)
>> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_BANK;
>> +
>> +    return err;
>> +  }
>> +
>> +  if (grub_strcmp (key, "keyfile") == 0)
>> +  {
>> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_KEYFILE)
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +    err = grub_tpm2_protector_parse_keyfile (ctx, value);
>> +    if (!err)
>> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_KEYFILE;
>> +
>> +    return err;
>> +  }
>> +
>> +  if (grub_strcmp (key, "srk") == 0)
>> +  {
>> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_SRK)
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +    err = grub_tpm2_protector_parse_srk (ctx, value);
>> +    if (!err)
>> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_SRK;
>> +
>> +    return err;
>> +  }
>> +
>> +  if (grub_strcmp (key, "nvindex") == 0)
>> +  {
>> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_NV)
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +    err = grub_tpm2_protector_parse_nvindex (ctx, value);
>> +    if (!err)
>> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_NV;
>> +
>> +    return err;
>> +  }
>> +
>> +  return GRUB_ERR_OUT_OF_RANGE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_parse_arg (struct grub_tpm2_protector_context *ctx,
>> +                               char *arg)
>> +{
>> +  char *key = arg;
>> +  char *value = NULL;
>> +
>> +  if (grub_strlen (arg) == 0)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  value = grub_strchr (arg, '=');
>> +  if (!value)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  *value = '\0';
>> +  value++;
>> +
>> +  if (*value == '\0')
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  return grub_tpm2_protector_match_arg (ctx, key, value);
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_parse_args (struct grub_tpm2_protector_context *ctx,
>> +                                char *args)
>> +{
>> +  grub_err_t err;
>> +  char *cur_arg;
>> +  char *next_arg;
>> +
>> +  if (args == NULL)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  if (grub_strlen (args) == 0)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  cur_arg = args;
>> +  for (;;)
>> +    {
>> +      next_arg = grub_strchr (cur_arg, ':');
>> +      if (next_arg == cur_arg)
>> +        return GRUB_ERR_BAD_ARGUMENT;
>> +      if (next_arg)
>> +        *next_arg = '\0';
>> +
>> +      err = grub_tpm2_protector_parse_arg (ctx, cur_arg);
>> +      if (err)
>> +        return err;
>> +
>> +      if (!next_arg)
>> +        break;
>> +
>> +      cur_arg = next_arg + 1;
>> +      if (*cur_arg == '\0')
>> +        return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
>> +{
>> +  if (!(ctx->args & GRUB_TPM2_PROTECTOR_ARG_MODE))
>> +    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
>> +
>> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && !ctx->keyfile)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->nv)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->srk)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && !ctx->nv)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->asymmetric)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->bank)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  if (!(ctx->args & GRUB_TPM2_PROTECTOR_ARG_PCRS))
>> +    {
>> +      ctx->pcrs[0] = 7;
>> +      ctx->pcr_count = 1;
>> +    }
>> +
>> +  if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK)
>> +    {
>> +      if (!ctx->srk)
>> +        ctx->srk = TPM2_SRK_HANDLE;
>> +
>> +      if (!ctx->asymmetric)
>> +        ctx->asymmetric = TPM_ALG_RSA;
>> +
>> +      if (!ctx->bank)
>> +        ctx->bank = TPM_ALG_SHA256;
>> +    }
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_srk_read_keyfile (const char *filepath, void **buffer,
>> +                                      grub_size_t *buffer_size)
>> +{
>> +  grub_file_t sealed_key_file;
>> +  grub_off_t sealed_key_size;
>> +  void *sealed_key_buffer;
>> +  grub_off_t sealed_key_read;
>> +
>> +  sealed_key_file = grub_file_open (filepath, GRUB_FILE_TYPE_NONE);
>> +  if (!sealed_key_file)
>> +    {
>> +      /* grub_file_open sets grub_errno on error, and if we do no unset it,
>> +       * future calls to grub_file_open will fail (and so will anybody up the
>> +       * stack who checks the value, if any). */
>> +      grub_errno = GRUB_ERR_NONE;
>> +      return GRUB_ERR_FILE_NOT_FOUND;
>> +    }
>> +
>> +  sealed_key_size = grub_file_size (sealed_key_file);
>> +  if (!sealed_key_size)
>> +    {
>> +      grub_file_close (sealed_key_file);
>> +      return GRUB_ERR_OUT_OF_RANGE;
>> +    }
>> +
>> +  sealed_key_buffer = grub_malloc (sealed_key_size);
>> +  if (!sealed_key_buffer)
>> +    {
>> +      grub_file_close (sealed_key_file);
>> +      return GRUB_ERR_OUT_OF_MEMORY;
>> +    }
>> +
>> +  sealed_key_read = grub_file_read (sealed_key_file, sealed_key_buffer,
>> +                                    sealed_key_size);
>> +  if (sealed_key_read != sealed_key_size)
>> +    {
>> +      grub_free (sealed_key_buffer);
>> +      grub_file_close (sealed_key_file);
>> +      return GRUB_ERR_FILE_READ_ERROR;
>> +    }
>> +
>> +  grub_file_close (sealed_key_file);
>> +
>> +  *buffer = sealed_key_buffer;
>> +  *buffer_size = sealed_key_size;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_srk_unmarshal_keyfile (void *sealed_key,
>> +                                           grub_size_t sealed_key_size,
>> +                                           TPM2_SEALED_KEY *sk)
>> +{
>> +  struct grub_tpm2_buffer buf;
>> +
>> +  grub_tpm2_buffer_init (&buf);
>> +  if (sealed_key_size > buf.cap)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  grub_memcpy (buf.data, sealed_key, sealed_key_size);
>> +  buf.size = sealed_key_size;
>> +
>> +  grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
>> +  grub_tpm2_mu_TPM2B_Unmarshal (&buf, (TPM2B *)&sk->private);
>> +
>> +  return buf.error ? GRUB_ERR_BAD_ARGUMENT : GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_srk_get (const struct grub_tpm2_protector_context *ctx,
>> +                             TPM_HANDLE *srk)
>> +{
>> +  TPM_RC rc;
>> +  TPM2B_PUBLIC public;
>> +  TPMS_AUTH_COMMAND authCommand = { 0 };
>> +  TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
>> +  TPM2B_PUBLIC inPublic = { 0 };
>> +  TPM2B_DATA outsideInfo = { 0 };
>> +  TPML_PCR_SELECTION creationPcr = { 0 };
>> +  TPM2B_PUBLIC outPublic = { 0 };
>> +  TPM2B_CREATION_DATA creationData = { 0 };
>> +  TPM2B_DIGEST creationHash = { 0 };
>> +  TPMT_TK_CREATION creationTicket = { 0 };
>> +  TPM2B_NAME srkName = { 0 };
>> +  TPM_HANDLE srkHandle;
>> +
>> +  /* Find SRK */
>> +  rc = TPM2_ReadPublic (ctx->srk, NULL, &public);
>> +  if (rc == TPM_RC_SUCCESS)
>> +    {
>> +      *srk = ctx->srk;
>> +      return GRUB_ERR_NONE;
>> +    }
>> +
>> +  /* The handle exists but its public area could not be read. */
>> +  if ((rc & ~TPM_RC_N_MASK) != TPM_RC_HANDLE)
>> +    return GRUB_ERR_BAD_DEVICE;
>> +
>> +  /* Create SRK */
>> +  authCommand.sessionHandle = TPM_RS_PW;
>> +  inPublic.publicArea.type = ctx->asymmetric;
>> +  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 (ctx->asymmetric == 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 = 2048;
>> +      inPublic.publicArea.parameters.rsaDetail.exponent = 0;
>> +    }
>> +  else if (ctx->asymmetric == 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 = TPM_ECC_NIST_P256;
>> +      inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
>> +    }
>> +  else
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  rc = TPM2_CreatePrimary (TPM_RH_OWNER, &authCommand, &inSensitive, &inPublic,
>> +                           &outsideInfo, &creationPcr, &srkHandle, &outPublic,
>> +                           &creationData, &creationHash, &creationTicket,
>> +                           &srkName, NULL);
>> +  if (rc != TPM_RC_SUCCESS)
>> +    return GRUB_ERR_BAD_DEVICE;
>> +
>> +  *srk = srkHandle;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_srk_recover (const struct grub_tpm2_protector_context *ctx,
>> +                                 grub_uint8_t **key, grub_size_t *key_size)
>> +{
>> +  TPM_RC rc;
>> +  TPM2_SEALED_KEY sealed_key;
>> +  void *sealed_key_bytes;
>> +  grub_size_t sealed_key_size;
>> +  TPM_HANDLE srk_handle;
>> +  TPM2B_NONCE nonceCaller = { 0 };
>> +  TPM2B_ENCRYPTED_SECRET salt = { 0 };
>> +  TPMT_SYM_DEF symmetric = { 0 };
>> +  TPM2B_NONCE nonceTPM = { 0 };
>> +  TPMI_SH_AUTH_SESSION session;
>> +  TPML_PCR_SELECTION pcrSel = {
>> +    .count = 1,
>> +    .pcrSelections = {
>> +      {
>> +        .hash = ctx->bank,
>> +        .sizeOfSelect = 3,
>> +        .pcrSelect = { 0 }
>> +      },
>> +    }
>> +  };
>> +  TPMS_AUTH_COMMAND authCmd = { 0 };
>> +  TPM_HANDLE sealed_key_handle;
>> +  TPM2B_NAME name;
>> +  TPMS_AUTH_RESPONSE authResponse;
>> +  TPM2B_SENSITIVE_DATA data;
>> +  grub_uint8_t *key_out;
>> +  grub_uint8_t i;
>> +  grub_err_t err;
>> +
>> +  /* Retrieve Sealed Key */
>> +  err = grub_tpm2_protector_srk_read_keyfile (ctx->keyfile, &sealed_key_bytes,
>> +                                              &sealed_key_size);
>> +  if (err)
>> +    return err;
>> +
>> +  err = grub_tpm2_protector_srk_unmarshal_keyfile (sealed_key_bytes,
>> +                                                   sealed_key_size,
>> +                                                   &sealed_key);
>> +  if (err)
>> +    goto exit1;
>> +
>> +  /* Get SRK */
>> +  err = grub_tpm2_protector_srk_get (ctx, &srk_handle);
>> +  if (err)
>> +    goto exit1;
>> +
>> +  err = GRUB_ERR_BAD_DEVICE;
>> +
>> +  /* Start Auth Session */
>> +  nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
>> +  symmetric.algorithm = TPM_ALG_NULL;
>> +
>> +  rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, 0, &nonceCaller, &salt,
>> +                              TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256,
>> +                              &session, &nonceTPM, 0);
>> +  if (rc)
>> +    goto exit2;
>> +
>> +  /* Policy PCR */
>> +  for (i = 0; i < ctx->pcr_count; i++)
>> +    pcrSel
>> +      .pcrSelections[0]
>> +      .pcrSelect[TPM2_PCR_TO_SELECT(ctx->pcrs[i])]
>> +        |= TPM2_PCR_TO_BIT(ctx->pcrs[i]);
>> +
>> +  rc = TPM2_PolicyPCR (session, NULL, NULL, &pcrSel, NULL);
>> +  if (rc)
>> +    goto exit3;
>> +
>> +  /* Load Sealed Key */
>> +  authCmd.sessionHandle = TPM_RS_PW;
>> +  rc = TPM2_Load (srk_handle, &authCmd, &sealed_key.private, &sealed_key.public,
>> +                  &sealed_key_handle, &name, &authResponse);
>> +  if (rc)
>> +    goto exit3;
>> +
>> +  /* Unseal Sealed Key */
>> +  authCmd.sessionHandle = session;
>> +  grub_memset (&authResponse, 0, sizeof (authResponse));
>> +
>> +  rc = TPM2_Unseal (sealed_key_handle, &authCmd, &data, &authResponse);
>> +  if (rc)
>> +    goto exit4;
>> +
>> +  /* Epilogue */
>> +  key_out = grub_malloc (data.size);
>> +  if (!key_out)
>> +    {
>> +      err = GRUB_ERR_OUT_OF_MEMORY;
>> +      goto exit4;
>> +    }
>> +
>> +  grub_memcpy (key_out, data.buffer, data.size);
>> +
>> +  *key = key_out;
>> +  *key_size = data.size;
>> +
>> +  err = GRUB_ERR_NONE;
>> +
>> +exit4:
>> +  TPM2_FlushContext (sealed_key_handle);
>> +
>> +exit3:
>> +  TPM2_FlushContext (session);
>> +
>> +exit2:
>> +  TPM2_FlushContext (srk_handle);
>> +
>> +exit1:
>> +  grub_free (sealed_key_bytes);
>> +  return err;
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx,
>> +                                grub_uint8_t **key, grub_size_t *key_size)
>> +{
>> +  (void)ctx;
>> +  (void)key;
>> +  (void)key_size;
>> +
>> +  return GRUB_ERR_NOT_IMPLEMENTED_YET;
>> +}
>> +
>> +static grub_err_t
>> +grub_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 grub_tpm2_protector_srk_recover (ctx, key, key_size);
>> +    case GRUB_TPM2_PROTECTOR_MODE_NV:
>> +      return grub_tpm2_protector_nv_recover (ctx, key, key_size);
>> +    default:
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +}
>> +
>> +static grub_err_t
>> +grub_tpm2_protector_recover_key (char *args, grub_uint8_t **key,
>> +                                 grub_size_t *key_size)
>> +{
>> +  grub_err_t err;
>> +  struct grub_tpm2_protector_context ctx = { 0 };
>> +
>> +  grub_dprintf ("tpm2", "Args: %s\n", args);
>> +
>> +  if (!args || !key)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  err = grub_tpm2_protector_parse_args (&ctx, args);
>> +  if (err) {
>> +    grub_dprintf("tpm2", "Failed to parse arguments\n");
>> +    return err;
>> +  }
>> +
>> +  err = grub_tpm2_protector_check_args (&ctx);
>> +  if (err) {
>> +    grub_dprintf("tpm2", "Invalid arguments\n");
>
> It's nice that this is here to at least let the user know that the
> failure was because of invalid arguments as opposed to a failure in the
> parsing. Even still, which argument was invalid? why was it invalid? As
> a user, this might be frustrating to diagnose. This is why a message
> specific to the error would be appreciated.
>

I'll remove these from here, add more granular error messages within the 
rest of the code, and have the message, if any, be printed upon error up 
the stack.

> Glenn
>
>> +    return err;
>> +  }
>> +
>> +  err = grub_tpm2_protector_recover (&ctx, key, key_size);
>> +  if (err) {
>> +    grub_dprintf("tpm2", "Failed to recover key\n");
>> +    return err;
>> +  }
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +struct grub_key_protector grub_tpm2_key_protector = {
>> +  .name = "tpm2",
>> +  .flags = GRUB_KEY_PROTECTOR_FLAG_NONE,
>> +  .recover_key = grub_tpm2_protector_recover_key
>> +};
>> +
>> +GRUB_MOD_INIT(tpm2)
>> +{
>> +  grub_key_protector_register (&grub_tpm2_key_protector);
>> +}
>> +
>> +GRUB_MOD_FINI(tpm2)
>> +{
>> +  grub_key_protector_unregister (&grub_tpm2_key_protector);
>> +}
>

Thank you,
Hernan


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 5/5] util/grub-protect: Add new tool
  2022-01-25  5:45   ` Glenn Washburn
@ 2022-01-27 14:19     ` Hernan Gatta
  0 siblings, 0 replies; 21+ messages in thread
From: Hernan Gatta @ 2022-01-27 14:19 UTC (permalink / raw)
  To: Glenn Washburn; +Cc: Hernan Gatta, The development of GNU GRUB



On Mon, 24 Jan 2022, Glenn Washburn wrote:

> On Mon, 24 Jan 2022 06:12:18 -0800
> Hernan Gatta <hegatta@linux.microsoft.com> wrote:
>
>> From: Hernan Gatta <hegatta@microsoft.com>
>>
>> To utilize the key protectors framework, there must be a way to protect
>> full-disk encryption keys in the first place. The grub-protect tool includes
>> support for the TPM2 key protector but other protectors that require setup ahead
>> of time can be supported in the future.
>>
>> For the TPM2 key protector, the intended flow is for a user to have a LUKS 1 or
>> LUKS 2-protected fully-encrypted disk. The user then creates a new key file, say
>> by reading /dev/urandom into a file, and creates a new LUKS key slot for this
>> key. Then, the user invokes the grub-protect tool to seal this key file to a set
>> of PCRs using the system's TPM 2.0. The resulting sealed key file is stored in
>> an unencrypted partition such as the EFI System Partition (ESP) so that GRUB may
>> read it. The user also ensures the cryptomount command is included in GRUB's
>> boot script and that it carries the requisite --protector (or -p) parameter.
>
> I think you mean '-k' instead of '-p' here.
>

True. I wrote this before I saw the changes to cryptomount that added the 
password parameter and failed to change the commit message.

>>
>> Sample usage:
>>
>> $ dd if=/dev/urandom of=key count=32
>> $ sudo cryptsetup luksAddKey /dev/sdb1 key --pbkdf=pbkdf2 --hash=sha512
>>
>> $ sudo grub-protect --action=add
>> --protector=tpm2
>> --tpm2-keyfile=key
>> --tpm2-outfile=/boot/efi/boot/grub2/sealed_key
>
> Why not have a syntax that is similar to whats given to cryptomount?
> (eg. $ sudo grub-protect --action=add
> --protector=tpm2:keyfile=/boot/efi/boot/grub2/sealed_key ) This would
> allow you to re-use the parsing code from patch #3. Yes, its less
> UNIXy, but I think could be more intuitive and cuts down on code size.
> I'm not completely wedded to this idea either.
>

I'm thinking that if we go with James' idea of decoupling the 
protector-specific parameters from the cryptomount command, then we should 
more easily be able to share argument parsing logic. The duplication is 
in fact something that I wasn't terribly happy with myself, so I'm happy 
to address it.

Thoughts?

>>
>> Then, in the boot script (wrapped for display):
>>
>> cryptomount -u b20f95d0834842bc9197bd78b36732f8
>> -p tpm2:keyfile=(hd0,gpt1)/boot/grub2/sealed_key
>
> Also, -k here.
>

Indeed.

>>
>> where the UUID corresponds to /dev/sdb1.
>>
>> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
>> ---
>>  .gitignore          |    1 +
>>  Makefile.util.def   |   16 +
>>  configure.ac        |    1 +
>>  util/grub-protect.c | 1344 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 1362 insertions(+)
>>  create mode 100644 util/grub-protect.c
>>
>> diff --git a/.gitignore b/.gitignore
>> index f6a1bd0..852327d 100644
>> --- a/.gitignore
>> +++ b/.gitignore
>> @@ -167,6 +167,7 @@ widthspec.bin
>>  /grub-ofpathname.exe
>>  /grub-probe
>>  /grub-probe.exe
>> +/grub-protect
>>  /grub-reboot
>>  /grub-render-label
>>  /grub-render-label.exe
>> diff --git a/Makefile.util.def b/Makefile.util.def
>> index 39b53b3..05f9f09 100644
>> --- a/Makefile.util.def
>> +++ b/Makefile.util.def
>> @@ -52,6 +52,9 @@ library = {
>>    common = grub-core/partmap/msdos.c;
>>    common = grub-core/fs/proc.c;
>>    common = grub-core/fs/archelp.c;
>> +  common = grub-core/tpm2/buffer.c;
>> +  common = grub-core/tpm2/mu.c;
>> +  common = grub-core/tpm2/tpm2.c;
>>  };
>>
>>  library = {
>> @@ -207,6 +210,19 @@ program = {
>>  };
>>
>>  program = {
>> +  name = grub-protect;
>> +  common = grub-core/osdep/init.c;
>> +  common = util/grub-protect.c;
>> +  common = util/probe.c;
>> +
>> +  ldadd = libgrubmods.a;
>> +  ldadd = libgrubgcry.a;
>> +  ldadd = libgrubkern.a;
>> +  ldadd = grub-core/lib/gnulib/libgnu.a;
>> +  ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
>> +};
>> +
>> +program = {
>>    name = grub-mkrelpath;
>>    mansection = 1;
>>
>> diff --git a/configure.ac b/configure.ac
>> index 4f649ed..3a4dc5a 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>> @@ -71,6 +71,7 @@ grub_TRANSFORM([grub-mkpasswd-pbkdf2])
>>  grub_TRANSFORM([grub-mkrelpath])
>>  grub_TRANSFORM([grub-mkrescue])
>>  grub_TRANSFORM([grub-probe])
>> +grub_TRANSFORM([grub-protect])
>>  grub_TRANSFORM([grub-reboot])
>>  grub_TRANSFORM([grub-script-check])
>>  grub_TRANSFORM([grub-set-default])
>> diff --git a/util/grub-protect.c b/util/grub-protect.c
>> new file mode 100644
>> index 0000000..4e4bbd7
>> --- /dev/null
>> +++ b/util/grub-protect.c
>> @@ -0,0 +1,1344 @@
>> +/*
>> + *  GRUB  --  GRand Unified Bootloader
>> + *  Copyright (C) 2022 Microsoft Corporation
>> + *
>> + *  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 <config.h>
>> +
>> +#include <errno.h>
>> +#include <fcntl.h>
>> +#include <stdio.h>
>> +#include <string.h>
>> +#include <unistd.h>
>> +
>> +#include <grub/crypto.h>
>> +#include <grub/emu/getroot.h>
>> +#include <grub/emu/hostdisk.h>
>> +#include <grub/emu/misc.h>
>> +#include <grub/tpm2/buffer.h>
>> +#include <grub/tpm2/mu.h>
>> +#include <grub/tpm2/tcg2.h>
>> +#include <grub/tpm2/tpm2.h>
>> +#include <grub/util/misc.h>
>> +
>> +#include "progname.h"
>> +
>> +typedef enum grub_protect_arg
>> +{
>> +  GRUB_PROTECT_ARG_ACTION          = 1 << 0,
>> +  GRUB_PROTECT_ARG_PROTECTOR       = 1 << 1,
>> +  GRUB_PROTECT_ARG_TPM2_DEVICE     = 1 << 2,
>> +  GRUB_PROTECT_ARG_TPM2_PCRS       = 1 << 3,
>> +  GRUB_PROTECT_ARG_TPM2_ASYMMETRIC = 1 << 4,
>> +  GRUB_PROTECT_ARG_TPM2_BANK       = 1 << 5,
>> +  GRUB_PROTECT_ARG_TPM2_SRK        = 1 << 6,
>> +  GRUB_PROTECT_ARG_TPM2_KEYFILE    = 1 << 7,
>> +  GRUB_PROTECT_ARG_TPM2_OUTFILE    = 1 << 8,
>> +  GRUB_PROTECT_ARG_TPM2_PERSIST    = 1 << 9,
>> +  GRUB_PROTECT_ARG_TPM2_EVICT      = 1 << 10
>> +} grub_protect_arg_t;
>> +
>> +typedef enum grub_protect_protector
>> +{
>> +  GRUB_PROTECT_TYPE_ERROR,
>> +  GRUB_PROTECT_TYPE_TPM2
>> +} grub_protect_protector_t;
>> +
>> +typedef enum grub_protect_action
>> +{
>> +  GRUB_PROTECT_ACTION_ERROR,
>> +  GRUB_PROTECT_ACTION_ADD,
>> +  GRUB_PROTECT_ACTION_REMOVE
>> +} grub_protect_action_t;
>> +
>> +struct grub_protect_args
>> +{
>> +  grub_protect_arg_t args;
>> +  grub_protect_action_t action;
>> +  grub_protect_protector_t protector;
>> +
>> +  const char *tpm2_device;
>> +  grub_uint8_t tpm2_pcrs[TPM_MAX_PCRS];
>> +  grub_uint8_t tpm2_pcr_count;
>> +  TPM_ALG_ID tpm2_asymmetric;
>> +  TPM_ALG_ID tpm2_bank;
>> +  TPM_HANDLE tpm2_srk;
>> +  const char *tpm2_keyfile;
>> +  const char *tpm2_outfile;
>> +  int tpm2_persist;
>> +  int tpm2_evict;
>> +};
>> +
>> +static int tpm2_fd = -1;
>> +
>> +static grub_err_t
>> +grub_protect_read_file (const char *filepath, void **buffer,
>> +                        size_t *buffer_size)
>> +{
>> +  grub_err_t err;
>> +  FILE *f;
>> +  long len;
>> +  void *buf;
>> +
>> +  f = fopen (filepath, "rb");
>> +  if (!f)
>> +    return GRUB_ERR_FILE_NOT_FOUND;
>> +
>> +  if (fseek (f, 0, SEEK_END))
>> +    {
>> +       err = GRUB_ERR_FILE_READ_ERROR;
>> +       goto exit1;
>> +    }
>> +
>> +  len = ftell (f);
>> +  if (!len)
>> +    {
>> +       err = GRUB_ERR_FILE_READ_ERROR;
>> +       goto exit1;
>> +    }
>> +
>> +  rewind (f);
>> +
>> +  buf = grub_malloc (len);
>> +  if (!buf)
>> +    {
>> +       err = GRUB_ERR_OUT_OF_MEMORY;
>> +       goto exit1;
>> +    }
>> +
>> +  if (fread (buf, len, 1, f) != 1)
>> +    {
>> +       err = GRUB_ERR_FILE_READ_ERROR;
>> +       goto exit2;
>> +    }
>> +
>> +  *buffer = buf;
>> +  *buffer_size = len;
>> +
>> +  buf = NULL;
>> +  err = GRUB_ERR_NONE;
>> +
>> +exit2:
>> +  grub_free (buf);
>> +
>> +exit1:
>> +  fclose (f);
>> +
>> +  return err;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_write_file (const char *filepath, void *buffer, size_t buffer_size)
>> +{
>> +  grub_err_t err;
>> +  FILE *f;
>> +
>> +  f = fopen (filepath, "wb");
>> +  if (!f)
>> +    return GRUB_ERR_FILE_NOT_FOUND;
>> +
>> +  if (fwrite (buffer, buffer_size, 1, f) != 1)
>> +  {
>> +    err = GRUB_ERR_WRITE_ERROR;
>> +    goto exit1;
>> +  }
>> +
>> +  err = GRUB_ERR_NONE;
>> +
>> +exit1:
>> +  fclose (f);
>> +
>> +  return err;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_get_grub_drive_for_file (const char *filepath, char **drive)
>> +{
>> +  grub_err_t err = GRUB_ERR_IO;
>> +  char *disk;
>> +  char **devices;
>> +  char *grub_dev;
>> +  char *grub_path;
>> +  char *efi_drive;
>> +  char *partition;
>> +  char *grub_drive;
>> +  grub_device_t dev;
>> +  grub_size_t grub_drive_len;
>> +  int n;
>> +
>> +  grub_path = grub_canonicalize_file_name (filepath);
>> +  if (!grub_path)
>> +    goto exit1;
>> +
>> +  devices = grub_guess_root_devices (grub_path);
>> +  if (!devices || !devices[0])
>> +    goto exit2;
>> +
>> +  disk = devices[0];
>> +
>> +  grub_util_pull_device (disk);
>> +
>> +  grub_dev = grub_util_get_grub_dev (disk);
>> +  if (!grub_dev)
>> +    goto exit3;
>> +
>> +  dev = grub_device_open (grub_dev);
>> +  if (!dev)
>> +    goto exit4;
>> +
>> +  efi_drive = grub_util_guess_efi_drive (disk);
>> +  if (!efi_drive)
>> +    goto exit5;
>> +
>> +  partition = grub_partition_get_name (dev->disk->partition);
>> +  if (!partition)
>> +    goto exit6;
>> +
>> +  grub_drive_len = grub_strlen (efi_drive) + grub_strlen (partition) + 3;
>> +  grub_drive = grub_malloc (grub_drive_len + 1);
>> +  if (!grub_drive)
>> +    goto exit7;
>> +
>> +  n = grub_snprintf (grub_drive, grub_drive_len + 1, "(%s,%s)", efi_drive,
>> +                     partition);
>> +  if (n != grub_drive_len)
>> +    goto exit8;
>> +
>> +  *drive = grub_drive;
>> +  grub_drive = NULL;
>> +  err = GRUB_ERR_NONE;
>> +
>> +exit8:
>> +  grub_free (grub_drive);
>> +
>> +exit7:
>> +  grub_free (partition);
>> +
>> +exit6:
>> +  grub_free (efi_drive);
>> +
>> +exit5:
>> +  grub_device_close (dev);
>> +
>> +exit4:
>> +  grub_free (grub_dev);
>> +
>> +exit3:
>> +  grub_free (devices);
>> +
>> +exit2:
>> +  grub_free (grub_path);
>> +
>> +exit1:
>> +  return err;
>> +}
>> +
>> +grub_err_t
>> +grub_tcg2_get_max_output_size (grub_size_t *size)
>> +{
>> +  if (!size)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  *size = GRUB_TPM2_BUFFER_CAPACITY;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +grub_err_t
>> +grub_tcg2_submit_command (grub_size_t input_size, grub_uint8_t *input,
>> +                          grub_size_t output_size, grub_uint8_t *output)
>> +{
>> +  static const grub_size_t header_size = sizeof (grub_uint16_t) +
>> +                                         (2 * sizeof(grub_uint32_t));
>> +
>> +  if (write (tpm2_fd, input, input_size) != input_size)
>> +    return GRUB_ERR_BAD_DEVICE;
>> +
>> +  if (read (tpm2_fd, output, output_size) < header_size)
>> +    return GRUB_ERR_BAD_DEVICE;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_tpm2_open_device (const char *dev_node)
>> +{
>> +  if (tpm2_fd != -1)
>> +    return GRUB_ERR_NONE;
>> +
>> +  tpm2_fd = open (dev_node, O_RDWR);
>> +  if (tpm2_fd == -1)
>> +    {
>> +      fprintf (stderr, _("Could not open TPM device (Error: %u).\n"), errno);
>> +      return GRUB_ERR_FILE_NOT_FOUND;
>> +    }
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_tpm2_close_device (void)
>> +{
>> +  int err;
>> +
>> +  if (tpm2_fd == -1)
>> +    return GRUB_ERR_NONE;
>> +
>> +  err = close (tpm2_fd);
>> +  if (err)
>> +  {
>> +    fprintf (stderr, _("Could not close TPM device (Error: %u).\n"), errno);
>> +    return GRUB_ERR_IO;
>> +  }
>> +
>> +  tpm2_fd = -1;
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_tpm2_get_policy_digest (struct grub_protect_args *args,
>> +                                     TPM2B_DIGEST *digest)
>> +{
>> +  TPM_RC rc;
>> +  TPML_PCR_SELECTION pcr_sel = {
>> +    .count = 1,
>> +    .pcrSelections = {
>> +      {
>> +        .hash = args->tpm2_bank,
>> +        .sizeOfSelect = 3,
>> +        .pcrSelect = { 0 }
>> +      },
>> +    }
>> +  };
>> +  TPML_PCR_SELECTION pcr_sel_out = { 0 };
>> +  TPML_DIGEST pcr_values = { 0 };
>> +  grub_uint8_t *pcr_digest;
>> +  grub_size_t pcr_digest_len;
>> +  grub_uint8_t *pcr_concat;
>> +  grub_size_t pcr_concat_len;
>> +  grub_uint8_t *pcr_cursor;
>> +  const gcry_md_spec_t *hash_spec;
>> +  TPM2B_NONCE nonce = { 0 };
>> +  TPM2B_ENCRYPTED_SECRET salt = { 0 };
>> +  TPMT_SYM_DEF symmetric = { 0 };
>> +  TPMI_SH_AUTH_SESSION session = 0;
>> +  TPM2B_DIGEST pcr_digest_in = {
>> +    .size = TPM_SHA256_DIGEST_SIZE,
>> +    .buffer = { 0 }
>> +  };
>> +  TPM2B_DIGEST policy_digest = { 0 };
>> +  grub_uint8_t i;
>> +  grub_err_t err;
>> +
>> +  /* PCR Read */
>> +  for (i = 0; i < args->tpm2_pcr_count; i++)
>> +    pcr_sel
>> +      .pcrSelections[0]
>> +      .pcrSelect[TPM2_PCR_TO_SELECT(args->tpm2_pcrs[i])]
>> +        |= TPM2_PCR_TO_BIT(args->tpm2_pcrs[i]);
>> +
>> +  rc = TPM2_PCR_Read (NULL, &pcr_sel, NULL, &pcr_sel_out, &pcr_values, NULL);
>> +  if (rc != TPM_RC_SUCCESS)
>> +    {
>> +      fprintf (stderr, _("Failed to read PCRs (TPM error: 0x%x).\n"), rc);
>> +      return GRUB_ERR_BAD_DEVICE;
>> +    }
>> +
>> +  if ((pcr_sel_out.count != pcr_sel.count) ||
>> +       (pcr_sel.pcrSelections[0].sizeOfSelect !=
>> +        pcr_sel_out.pcrSelections[0].sizeOfSelect))
>> +    {
>> +      fprintf (stderr, _("Could not read all the specified PCRs.\n"));
>> +      return GRUB_ERR_BAD_DEVICE;
>> +    }
>> +
>> +  /* Compute PCR Digest */
>> +  switch (args->tpm2_bank)
>> +    {
>> +    case TPM_ALG_SHA1:
>> +      pcr_digest_len = TPM_SHA1_DIGEST_SIZE;
>> +      hash_spec = GRUB_MD_SHA1;
>> +      break;
>> +    case TPM_ALG_SHA256:
>> +      pcr_digest_len = TPM_SHA256_DIGEST_SIZE;
>> +      hash_spec = GRUB_MD_SHA256;
>> +      break;
>> +    default:
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +
>> +  pcr_digest = grub_malloc (pcr_digest_len);
>> +  if (!pcr_digest)
>> +    {
>> +      fprintf (stderr, _("Failed to allocate PCR digest buffer.\n"));
>> +      return GRUB_ERR_OUT_OF_MEMORY;
>> +    }
>> +
>> +  pcr_concat_len = pcr_digest_len * args->tpm2_pcr_count;
>> +  pcr_concat = grub_malloc (pcr_concat_len);
>> +  if (!pcr_concat)
>> +    {
>> +      err = GRUB_ERR_OUT_OF_MEMORY;
>> +      fprintf (stderr, _("Failed to allocate PCR concatenation buffer.\n"));
>> +      goto exit1;
>> +    }
>> +
>> +  pcr_cursor = pcr_concat;
>> +  for (i = 0; i < args->tpm2_pcr_count; i++)
>> +    {
>> +      if (pcr_values.digests[i].size != pcr_digest_len)
>> +        {
>> +          fprintf (stderr,
>> +                   _("Bad PCR value size: expected %lu bytes but got %u bytes.\n"),
>> +                   pcr_digest_len, pcr_values.digests[i].size);
>> +          goto exit2;
>> +        }
>> +
>> +      grub_memcpy (pcr_cursor, pcr_values.digests[i].buffer, pcr_digest_len);
>> +      pcr_cursor += pcr_digest_len;
>> +    }
>> +
>> +  grub_crypto_hash (hash_spec, pcr_digest, pcr_concat, pcr_concat_len);
>> +
>> +  /* Start Trial Session */
>> +  nonce.size = TPM_SHA256_DIGEST_SIZE;
>> +  symmetric.algorithm = TPM_ALG_NULL;
>> +
>> +  rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, 0, &nonce, &salt,
>> +                              TPM_SE_TRIAL, &symmetric, TPM_ALG_SHA256,
>> +                              &session, NULL, 0);
>> +  if (rc != TPM_RC_SUCCESS)
>> +    {
>> +      fprintf (stderr,
>> +               _("Failed to start trial policy session (TPM error: 0x%x).\n"),
>> +               rc);
>> +      err = GRUB_ERR_BAD_DEVICE;
>> +      goto exit2;
>> +    }
>> +
>> +  /* PCR Policy */
>> +  memcpy (pcr_digest_in.buffer, pcr_digest, TPM_SHA256_DIGEST_SIZE);
>> +
>> +  rc = TPM2_PolicyPCR (session, NULL, &pcr_digest_in, &pcr_sel, NULL);
>> +  if (rc != TPM_RC_SUCCESS)
>> +    {
>> +      fprintf (stderr, _("Failed to submit PCR policy (TPM error: 0x%x).\n"),
>> +               rc);
>> +      err = GRUB_ERR_BAD_DEVICE;
>> +      goto exit3;
>> +    }
>> +
>> +  /* Retrieve Policy Digest */
>> +  rc = TPM2_PolicyGetDigest (session, NULL, &policy_digest, NULL);
>> +  if (rc != TPM_RC_SUCCESS)
>> +    {
>> +      fprintf (stderr, _("Failed to get policy digest (TPM error: 0x%x).\n"),
>> +               rc);
>> +      err = GRUB_ERR_BAD_DEVICE;
>> +      goto exit3;
>> +    }
>> +
>> +  /* Epilogue */
>> +  *digest = policy_digest;
>> +  err = GRUB_ERR_NONE;
>> +
>> +exit3:
>> +  TPM2_FlushContext (session);
>> +
>> +exit2:
>> +  grub_free (pcr_concat);
>> +
>> +exit1:
>> +  grub_free (pcr_digest);
>> +
>> +  return err;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_tpm2_get_srk (struct grub_protect_args *args, TPM_HANDLE *srk)
>> +{
>> +  TPM_RC rc;
>> +  TPM2B_PUBLIC public;
>> +  TPMS_AUTH_COMMAND authCommand = { 0 };
>> +  TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
>> +  TPM2B_PUBLIC inPublic = { 0 };
>> +  TPM2B_DATA outsideInfo = { 0 };
>> +  TPML_PCR_SELECTION creationPcr = { 0 };
>> +  TPM2B_PUBLIC outPublic = { 0 };
>> +  TPM2B_CREATION_DATA creationData = { 0 };
>> +  TPM2B_DIGEST creationHash = { 0 };
>> +  TPMT_TK_CREATION creationTicket = { 0 };
>> +  TPM2B_NAME srkName = { 0 };
>> +  TPM_HANDLE srkHandle;
>> +
>> +  /* Find SRK */
>> +  rc = TPM2_ReadPublic (args->tpm2_srk, NULL, &public);
>> +  if (rc == TPM_RC_SUCCESS)
>> +    {
>> +      if (args->tpm2_persist)
>> +        fprintf (stderr,
>> +                 _("Warning: --tpm2-persist was specified but the SRK already exists on the TPM. Continuing anyway...\n"));
>> +
>> +      *srk = TPM2_SRK_HANDLE;
>> +      return GRUB_ERR_NONE;
>> +    }
>> +
>> +  /* The handle exists but its public area could not be read. */
>> +  if ((rc & ~TPM_RC_N_MASK) != TPM_RC_HANDLE)
>> +    {
>> +      fprintf (stderr,
>> +               _("The SRK exists on the TPM but its public area cannot be read (TPM error: 0x%x).\n"),
>> +               rc);
>> +      return GRUB_ERR_BAD_DEVICE;
>> +    }
>> +
>> +  /* Create SRK */
>> +  authCommand.sessionHandle = TPM_RS_PW;
>> +  inPublic.publicArea.type = args->tpm2_asymmetric;
>> +  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;
>> +
>> +  switch (args->tpm2_asymmetric)
>> +    {
>> +    case 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 = 2048;
>> +      inPublic.publicArea.parameters.rsaDetail.exponent = 0;
>> +      break;
>> +
>> +    case 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 = TPM_ECC_NIST_P256;
>> +      inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
>> +      break;
>> +
>> +    default:
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +
>> +  rc = TPM2_CreatePrimary (TPM_RH_OWNER, &authCommand, &inSensitive, &inPublic,
>> +                           &outsideInfo, &creationPcr, &srkHandle, &outPublic,
>> +                           &creationData, &creationHash, &creationTicket,
>> +                           &srkName, NULL);
>> +  if (rc != TPM_RC_SUCCESS)
>> +    {
>> +      fprintf (stderr, _("Failed to create SRK (TPM error: 0x%x).\n"), rc);
>> +      return GRUB_ERR_BAD_DEVICE;
>> +    }
>> +
>> +  /* Persist SRK */
>> +  if (args->tpm2_persist)
>> +    {
>> +      rc = TPM2_EvictControl (TPM_RH_OWNER, srkHandle, args->tpm2_srk,
>> +                              &authCommand, NULL);
>> +      if (rc == TPM_RC_SUCCESS)
>> +        {
>> +          TPM2_FlushContext (srkHandle);
>> +          srkHandle = args->tpm2_srk;
>> +        }
>> +      else
>> +        fprintf (stderr,
>> +                 _("Warning: Failed to persist SRK (TPM error: 0x%x\n). Continuing anyway...\n"),
>> +                 rc);
>> +    }
>> +
>> +  /* Epilogue */
>> +  *srk = srkHandle;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_tpm2_seal (TPM2B_DIGEST *policyDigest, TPM_HANDLE srk,
>> +                        grub_uint8_t *clearText, grub_size_t clearTextLength,
>> +                        TPM2_SEALED_KEY *sealed_key)
>> +{
>> +  TPM_RC rc;
>> +  TPMS_AUTH_COMMAND authCommand = { 0 };
>> +  TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
>> +  TPM2B_PUBLIC inPublic  = { 0 };
>> +  TPM2B_DATA outsideInfo = { 0 };
>> +  TPML_PCR_SELECTION pcr_sel = { 0 };
>> +  TPM2B_PRIVATE outPrivate = { 0 };
>> +  TPM2B_PUBLIC outPublic = { 0 };
>> +
>> +  /* Seal Data */
>> +  authCommand.sessionHandle = TPM_RS_PW;
>> +
>> +  inSensitive.sensitive.data.size = clearTextLength;
>> +  memcpy(inSensitive.sensitive.data.buffer, clearText, clearTextLength);
>> +
>> +  inPublic.publicArea.type = TPM_ALG_KEYEDHASH;
>> +  inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
>> +  inPublic.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM_ALG_NULL;
>> +  inPublic.publicArea.authPolicy = *policyDigest;
>> +
>> +  rc = TPM2_Create (srk, &authCommand, &inSensitive, &inPublic, &outsideInfo,
>> +                    &pcr_sel, &outPrivate, &outPublic, NULL, NULL, NULL, NULL);
>> +  if (rc != TPM_RC_SUCCESS)
>> +    {
>> +      fprintf (stderr, _("Failed to seal key (TPM error: 0x%x).\n"), rc);
>> +      return GRUB_ERR_BAD_DEVICE;
>> +    }
>> +
>> +  /* Epilogue */
>> +  sealed_key->public = outPublic;
>> +  sealed_key->private = outPrivate;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_tpm2_export_sealed_key (const char *filepath,
>> +                                     TPM2_SEALED_KEY *sealed_key)
>> +{
>> +  grub_err_t err;
>> +  struct grub_tpm2_buffer buf;
>> +
>> +  grub_tpm2_buffer_init (&buf);
>> +  grub_tpm2_mu_TPM2B_PUBLIC_Marshal (&buf, &sealed_key->public);
>> +  grub_tpm2_mu_TPM2B_Marshal (&buf, sealed_key->private.size,
>> +                              sealed_key->private.buffer);
>> +  if (buf.error)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  err = grub_protect_write_file (filepath, buf.data, buf.size);
>> +  if (err)
>> +    fprintf (stderr, _("Could not write sealed key file (Error: %u).\n"),
>> +             errno);
>> +
>> +  return err;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_tpm2_add (struct grub_protect_args *args)
>> +{
>> +  grub_err_t err;
>> +  grub_uint8_t *key;
>> +  grub_size_t key_size;
>> +  TPM_HANDLE srk;
>> +  TPM2B_DIGEST policy_digest;
>> +  TPM2_SEALED_KEY sealed_key;
>> +  char *grub_drive = NULL;
>> +
>> +  grub_protect_get_grub_drive_for_file (args->tpm2_outfile, &grub_drive);
>> +
>> +  err = grub_protect_tpm2_open_device (args->tpm2_device);
>> +  if (err)
>> +    return err;
>> +
>> +  err = grub_protect_read_file (args->tpm2_keyfile, (void **)&key, &key_size);
>> +  if (err)
>> +    goto exit1;
>> +
>> +  if (key_size > TPM_MAX_SYM_DATA)
>> +  {
>> +    fprintf (stderr,
>> +             _("Input key is too long, maximum allowed size is %u bytes.\n"),
>> +             TPM_MAX_SYM_DATA);
>> +    return GRUB_ERR_OUT_OF_RANGE;
>> +  }
>> +
>> +  err = grub_protect_tpm2_get_srk (args, &srk);
>> +  if (err)
>> +    goto exit2;
>> +
>> +  err = grub_protect_tpm2_get_policy_digest (args, &policy_digest);
>> +  if (err)
>> +    goto exit3;
>> +
>> +  err = grub_protect_tpm2_seal (&policy_digest, srk, key, key_size,
>> +                                &sealed_key);
>> +  if (err)
>> +    goto exit3;
>> +
>> +  err = grub_protect_tpm2_export_sealed_key (args->tpm2_outfile, &sealed_key);
>> +  if (err)
>> +    goto exit3;
>> +
>> +  if (grub_drive)
>> +    {
>> +      printf (_("GRUB drive for the sealed key file: %s\n"), grub_drive);
>> +      grub_free (grub_drive);
>> +    }
>> +  else
>> +    {
>> +      fprintf (stderr,
>> +               _("Warning: Could not determine GRUB drive for sealed key file.\n"));
>> +      err = GRUB_ERR_NONE;
>> +    }
>> +
>> +exit3:
>> +  TPM2_FlushContext (srk);
>> +
>> +exit2:
>> +  grub_free (key);
>> +
>> +exit1:
>> +  grub_protect_tpm2_close_device ();
>> +
>> +  return err;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_tpm2_remove (struct grub_protect_args *args)
>> +{
>> +  TPM_RC rc;
>> +  TPM2B_PUBLIC public;
>> +  TPMS_AUTH_COMMAND authCommand = { 0 };
>> +  grub_err_t err;
>> +
>> +  if (!args->tpm2_evict)
>> +    {
>> +      printf (_("--tpm2-evict not specified, nothing to do.\n"));
>> +      return GRUB_ERR_NONE;
>> +    }
>> +
>> +  err = grub_protect_tpm2_open_device (args->tpm2_device);
>> +  if (err)
>> +    return err;
>> +
>> +  /* Find SRK */
>> +  rc = TPM2_ReadPublic (args->tpm2_srk, NULL, &public);
>> +  if (rc != TPM_RC_SUCCESS)
>> +    {
>> +      fprintf (stderr, _("SRK with handle 0x%x not found.\n"), args->tpm2_srk);
>> +      err = GRUB_ERR_BAD_ARGUMENT;
>> +      goto exit1;
>> +    }
>> +
>> +  /* Evict SRK */
>> +  authCommand.sessionHandle = TPM_RS_PW;
>> +
>> +  rc = TPM2_EvictControl (TPM_RH_OWNER, args->tpm2_srk, args->tpm2_srk,
>> +                          &authCommand, NULL);
>> +  if (rc != TPM_RC_SUCCESS)
>> +    {
>> +      fprintf (stderr,
>> +               _("Failed to evict SRK with handle 0x%x (TPM Error: 0x%x).\n"),
>> +               args->tpm2_srk, rc);
>> +      err = GRUB_ERR_BAD_DEVICE;
>> +      goto exit2;
>> +    }
>> +
>> +  err = GRUB_ERR_NONE;
>> +
>> +exit2:
>> +  TPM2_FlushContext (args->tpm2_srk);
>> +
>> +exit1:
>> +  grub_protect_tpm2_close_device ();
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_tpm2_run (struct grub_protect_args *args)
>> +{
>> +  switch (args->action)
>> +    {
>> +    case GRUB_PROTECT_ACTION_ADD:
>> +      return grub_protect_tpm2_add (args);
>> +
>> +    case GRUB_PROTECT_ACTION_REMOVE:
>> +      return grub_protect_tpm2_remove (args);
>> +
>> +    default:
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_tpm2_args_verify (struct grub_protect_args *args)
>> +{
>
> I'm wondering if it wouldn't be a good idea to merge this function with
> grub_tpm2_protector_check_args from patch #3. This is probably only
> manageable if the protector argument looks like the one from grub as
> well, so they can share error messages.
>

Same comment as above.

>> +  switch (args->action)
>> +    {
>> +    case GRUB_PROTECT_ACTION_ADD:
>> +      if (args->args & GRUB_PROTECT_ARG_TPM2_EVICT)
>> +        {
>> +          fprintf (stderr,
>> +                   _("--tpm2-evict is invalid when --action is 'add'.\n"));
>> +          return GRUB_ERR_BAD_ARGUMENT;
>> +        }
>> +
>> +      if (!args->tpm2_keyfile)
>> +        {
>> +          fprintf (stderr, _("--tpm2-keyfile must be specified.\n"));
>> +          return GRUB_ERR_BAD_ARGUMENT;
>> +        }
>> +
>> +      if (!args->tpm2_outfile)
>> +        {
>> +          fprintf (stderr, _("--tpm2-outfile must be specified.\n"));
>> +          return GRUB_ERR_BAD_ARGUMENT;
>> +        }
>> +
>> +      if (!args->tpm2_device)
>> +        args->tpm2_device = "/dev/tpm0";
>
> Seems like this...
>
>> +
>> +      if (!args->tpm2_pcr_count)
>> +        {
>> +          args->tpm2_pcrs[0] = 7;
>> +          args->tpm2_pcr_count = 1;
>> +        }
>> +
>> +      if (!args->tpm2_srk)
>> +        args->tpm2_srk = TPM2_SRK_HANDLE;
>
> and maybe this, can be done outside the switch since they are common to
> both cases.
>

The reason why I wrote this in this way is because carrying out these 
actions is only valid when args->action is valid. In other words, if I 
take these statements out of the switch, they'll be executed regardless of 
the value of args->action. Technically, args->action is always correct by 
the time we reach this function since the argument parser would have 
rejected invalid values, but I still wrote the switch like this to express 
intent (i.e., there are only two valid actions), and outside of that, the 
program will not do anything, including setting these defaults.

What do you think?

>> +
>> +      if (!args->tpm2_asymmetric)
>> +        args->tpm2_asymmetric = TPM_ALG_RSA;
>> +
>> +      if (!args->tpm2_bank)
>> +        args->tpm2_bank = TPM_ALG_SHA256;
>> +
>> +      break;
>> +
>> +    case GRUB_PROTECT_ACTION_REMOVE:
>> +      if (args->args & GRUB_PROTECT_ARG_TPM2_ASYMMETRIC)
>> +        {
>> +          fprintf (stderr,
>> +                   _("--tpm2-asymmetric is invalid when --action is 'remove'.\n"));
>> +          return GRUB_ERR_BAD_ARGUMENT;
>> +        }
>> +
>> +      if (args->args & GRUB_PROTECT_ARG_TPM2_BANK)
>> +        {
>> +          fprintf (stderr,
>> +                   _("--tpm2-bank is invalid when --action is 'remove'.\n"));
>> +          return GRUB_ERR_BAD_ARGUMENT;
>> +        }
>> +
>> +      if (args->args & GRUB_PROTECT_ARG_TPM2_KEYFILE)
>> +        {
>> +          fprintf (stderr,
>> +                   _("--tpm2-keyfile is invalid when --action is 'remove'.\n"));
>> +          return GRUB_ERR_BAD_ARGUMENT;
>> +        }
>> +
>> +      if (args->args & GRUB_PROTECT_ARG_TPM2_OUTFILE)
>> +        {
>> +          fprintf (stderr,
>> +                   _("--tpm2-outfile is invalid when --action is 'remove'.\n"));
>> +          return GRUB_ERR_BAD_ARGUMENT;
>> +        }
>> +
>> +      if (args->args & GRUB_PROTECT_ARG_TPM2_PCRS)
>> +        {
>> +          fprintf (stderr,
>> +                   _("--tpm2-pcrs is invalid when --action is 'remove'.\n"));
>> +          return GRUB_ERR_BAD_ARGUMENT;
>> +        }
>> +
>> +      if (args->args & GRUB_PROTECT_ARG_TPM2_PERSIST)
>> +        {
>> +          fprintf (stderr,
>> +                   _("--tpm2-persist is invalid when --action is 'remove'.\n"));
>> +          return GRUB_ERR_BAD_ARGUMENT;
>> +        }
>> +
>> +      if (!args->tpm2_device)
>> +        args->tpm2_device = "/dev/tpm0";
>> +
>> +      if (!args->tpm2_srk)
>> +        args->tpm2_srk = TPM2_SRK_HANDLE;
>> +
>> +      break;
>> +
>> +    default:
>> +      fprintf (stderr,
>> +               _("The TPM2 key protector only supports the following actions: add, remove.\n"));
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_protector_args_kvp_parse (char *kvp, const char **key, char **value)
>> +{
>> +  char divider;
>> +  char *separator;
>> +  grub_size_t len;
>> +
>> +  if (!kvp)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  len = grub_strlen (kvp);
>> +  if (!len || len < 2)
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  if (kvp[0] == '-' && kvp[1] != '-')
>> +    divider = ' ';
>> +  else if (kvp[0] == '-' && kvp[1] == '-')
>> +    divider = '=';
>> +  else
>> +    return GRUB_ERR_BAD_ARGUMENT;
>> +
>> +  separator = grub_strchr (kvp, divider);
>> +  if (!separator)
>> +    goto exit;
>> +
>> +  *separator = '\0';
>> +  separator += 1;
>> +
>> +  if (separator - kvp >= len)
>> +  {
>> +    separator = NULL;
>> +    goto exit;
>> +  }
>> +
>> +exit:
>> +  *key = kvp;
>> +  *value = separator;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_args_parse (int argc, char *argv[],
>> +                         struct grub_protect_args *args)
>> +{
>> +  grub_err_t err;
>> +  char *current_arg;
>> +  const char *key;
>> +  char *value;
>> +  int i;
>> +
>> +  for (i = 1; i < argc; i++)
>> +    {
>> +      current_arg = argv[i];
>> +
>> +      err = grub_protector_args_kvp_parse (current_arg, &key, &value);
>> +      if (err)
>> +        return err;
>> +
>> +      if (grub_strcmp (key, "--action") == 0 ||
>> +          grub_strcmp (key, "-a") == 0)
>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_ACTION)
>> +            {
>> +              fprintf (stderr,
>> +                       _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (!value)
>> +            {
>> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (grub_strcmp (value, "add") == 0)
>> +            args->action = GRUB_PROTECT_ACTION_ADD;
>> +          else if (grub_strcmp (value, "remove") == 0)
>> +            args->action = GRUB_PROTECT_ACTION_REMOVE;
>> +          else
>> +            {
>> +              fprintf (stderr, _("'%s' is not a valid action.\n"), value);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          args->args |= GRUB_PROTECT_ARG_ACTION;
>> +        }
>> +      else if (grub_strcmp (key, "--protector") == 0 ||
>> +               grub_strcmp (key, "-p") == 0)
>
> This might be confusing because its -p here but -k to cryptomount.
>

True. Another side-effect of me writing these patches before the password 
argument was added to cryptomount. Will fix.

>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_PROTECTOR)
>> +            {
>> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (!value)
>> +            {
>> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (grub_strcmp (value, "tpm2") == 0)
>> +            args->protector = GRUB_PROTECT_TYPE_TPM2;
>> +          else
>> +            {
>> +              fprintf (stderr, _("'%s' is not a valid protector.\n"), value);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          args->args |= GRUB_PROTECT_ARG_PROTECTOR;
>> +        }
>> +      else if (grub_strcmp (key, "--tpm2-device") == 0)
>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_TPM2_DEVICE)
>> +            {
>> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (!value)
>> +            {
>> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          args->tpm2_device = value;
>> +          args->args |= GRUB_PROTECT_ARG_TPM2_DEVICE;
>> +        }
>> +      else if (grub_strcmp (key, "--tpm2-pcrs") == 0)
>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_TPM2_PCRS)
>> +            {
>> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (!value)
>> +            {
>> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          grub_size_t len = grub_strlen (value);
>> +          grub_size_t dist;
>> +
>> +          long pcr;
>> +          char *next_pcr;
>> +          char *current_pcr = value;
>> +          grub_uint8_t pcr_u8;
>> +          grub_uint8_t j;
>> +          grub_uint8_t k;
>> +
>> +          for (j = 0;; j++)
>> +            {
>> +              next_pcr = grub_strchr (current_pcr, ',');
>> +              if (next_pcr)
>> +                *next_pcr = '\0';
>> +
>> +              grub_errno = GRUB_ERR_NONE;
>> +              pcr = grub_strtoul (current_pcr, NULL, 0);
>> +              if (grub_errno != GRUB_ERR_NONE)
>> +                {
>> +                  fprintf (stderr, _("'%s' is not a valid PCR number.\n"),
>> +                           current_pcr);
>> +                  return grub_errno;
>> +                }
>> +
>> +              if (pcr > TPM_MAX_PCRS)
>> +                {
>> +                  fprintf (stderr,
>> +                           _("%lu larger than the maximum number of PCRs.\n"),
>> +                           pcr);
>> +                  return GRUB_ERR_OUT_OF_RANGE;
>> +                }
>> +
>> +              pcr_u8 = (grub_uint8_t)pcr;
>> +
>> +              for (k = 0; k < args->tpm2_pcr_count; k++)
>> +              {
>> +                if (args->tpm2_pcrs[k] == pcr_u8)
>> +                  {
>> +                    fprintf (stderr, _("PCR %u was already specified.\n"),
>> +                             pcr_u8);
>> +                    return GRUB_ERR_BAD_ARGUMENT;
>> +                  }
>> +              }
>> +
>> +              args->tpm2_pcrs[j] = pcr_u8;
>> +              args->tpm2_pcr_count++;
>> +
>> +              if (!next_pcr)
>> +                break;
>> +
>> +              current_pcr = next_pcr + 1;
>> +
>> +              dist = (current_pcr - value);
>> +              if (dist >= len)
>> +                break;
>> +            }
>> +
>> +          args->args |= GRUB_PROTECT_ARG_TPM2_PCRS;
>> +        }
>> +      else if (grub_strcmp (key, "--tpm2-srk") == 0)
>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_TPM2_SRK)
>> +            {
>> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (!value)
>> +            {
>> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          long srk;
>> +
>> +          grub_errno = GRUB_ERR_NONE;
>> +          srk = grub_strtoul (value, NULL, 0);
>> +          if (grub_errno != GRUB_ERR_NONE)
>> +            {
>> +              fprintf (stderr, _("'%s' is not a valid SRK handle.\n"), value);
>> +              return grub_errno;
>> +            }
>> +
>> +          if (srk > GRUB_UINT_MAX)
>> +            {
>> +              fprintf (stderr,
>> +                       _("%lu is too large to be a valid SRK handle.\n"), srk);
>> +              return GRUB_ERR_OUT_OF_RANGE;
>> +            }
>> +
>> +          args->tpm2_srk = (TPM_HANDLE)srk;
>> +          args->args |= GRUB_PROTECT_ARG_TPM2_SRK;
>> +        }
>> +      else if (grub_strcmp (key, "--tpm2-asymmetric") == 0)
>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_TPM2_ASYMMETRIC)
>> +            {
>> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (!value)
>> +            {
>> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (grub_strcasecmp (value, "ECC") == 0)
>> +            args->tpm2_asymmetric = TPM_ALG_ECC;
>> +          else if (grub_strcasecmp (value, "RSA") == 0)
>> +            args->tpm2_asymmetric = TPM_ALG_RSA;
>> +          else
>> +            {
>> +              fprintf (stderr, _("'%s' is not a valid asymmetric algorithm.\n"),
>> +                       value);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          args->args |= GRUB_PROTECT_ARG_TPM2_ASYMMETRIC;
>> +        }
>> +      else if (grub_strcmp (key, "--tpm2-bank") == 0)
>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_TPM2_BANK)
>> +            {
>> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (!value)
>> +            {
>> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (grub_strcasecmp (value, "SHA1") == 0)
>> +            args->tpm2_bank = TPM_ALG_SHA1;
>> +          else if (grub_strcasecmp (value, "SHA256") == 0)
>> +            args->tpm2_bank = TPM_ALG_SHA256;
>> +          else
>> +            {
>> +              fprintf (stderr, _("'%s' is not a valid PCR bank.\n"), value);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          args->args |= GRUB_PROTECT_ARG_TPM2_BANK;
>> +        }
>> +      else if (grub_strcmp (key, "--tpm2-keyfile") == 0)
>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_TPM2_KEYFILE)
>> +            {
>> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (!value)
>> +            {
>> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +	  args->tpm2_keyfile = value;
>> +          args->args |= GRUB_PROTECT_ARG_TPM2_KEYFILE;
>> +        }
>> +      else if (grub_strcmp (key, "--tpm2-outfile") == 0)
>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_TPM2_OUTFILE)
>> +            {
>> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (!value)
>> +            {
>> +              fprintf (stderr, _("Argument %s requires a value.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          args->tpm2_outfile = value;
>> +          args->args |= GRUB_PROTECT_ARG_TPM2_OUTFILE;
>> +        }
>> +      else if (grub_strcmp (key, "--tpm2-persist") == 0)
>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_TPM2_PERSIST)
>> +            {
>> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (value)
>> +            {
>> +              fprintf (stderr, _("Argument %s does not accept a value.\n"),
>> +                       key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          args->tpm2_persist = 1;
>> +          args->args |= GRUB_PROTECT_ARG_TPM2_PERSIST;
>> +        }
>> +      else if (grub_strcmp (key, "--tpm2-evict") == 0)
>> +        {
>> +          if (args->args & GRUB_PROTECT_ARG_TPM2_EVICT)
>> +            {
>> +              fprintf (stderr, _("Argument %s was already specified.\n"), key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          if (value)
>> +            {
>> +              fprintf (stderr, _("Argument %s does not accept a value.\n"),
>> +                       key);
>> +              return GRUB_ERR_BAD_ARGUMENT;
>> +            }
>> +
>> +          args->tpm2_evict = 1;
>> +          args->args |= GRUB_PROTECT_ARG_TPM2_EVICT;
>> +        }
>> +        else
>> +          {
>> +            fprintf (stderr, _("'%s' is not a valid argument.\n"), key);
>> +            return GRUB_ERR_BAD_ARGUMENT;
>> +          }
>> +    }
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_args_verify (struct grub_protect_args *args)
>> +{
>> +  if (args->action == GRUB_PROTECT_ACTION_ERROR)
>> +    {
>> +      fprintf (stderr, "--action is mandatory.\n");
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +
>> +  /* At the moment, the only configurable key protector is the TPM2 one, so it
>> +   * is the only keu protector supported by this tool. */
>> +  if (args->protector != GRUB_PROTECT_TYPE_TPM2)
>> +    {
>> +      fprintf (stderr,
>> +               _("--protector is mandatory and only 'tpm2' is currently supported.\n"));
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +
>> +  switch (args->protector)
>> +    {
>> +    case GRUB_PROTECT_TYPE_TPM2:
>> +      return grub_protect_tpm2_args_verify (args);
>> +    default:
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_protect_dispatch (struct grub_protect_args *args)
>> +{
>> +  switch (args->protector)
>> +    {
>> +    case GRUB_PROTECT_TYPE_TPM2:
>> +      return grub_protect_tpm2_run (args);
>> +    default:
>> +      return GRUB_ERR_BAD_ARGUMENT;
>> +    }
>> +}
>> +
>> +static void
>> +grub_protect_init (int *argc, char **argv[])
>> +{
>> +  grub_util_host_init (argc, argv);
>> +
>> +  grub_util_biosdisk_init (NULL);
>> +
>> +  grub_init_all ();
>> +  grub_gcry_init_all ();
>> +
>> +  grub_lvm_fini ();
>> +  grub_mdraid09_fini ();
>> +  grub_mdraid1x_fini ();
>> +  grub_diskfilter_fini ();
>> +  grub_diskfilter_init ();
>> +  grub_mdraid09_init ();
>> +  grub_mdraid1x_init ();
>> +  grub_lvm_init ();
>> +}
>> +
>> +static void
>> +grub_protect_fini (void)
>> +{
>> +  grub_gcry_fini_all ();
>> +  grub_fini_all ();
>> +  grub_util_biosdisk_fini ();
>> +}
>> +
>> +int
>> +main (int argc, char *argv[])
>> +{
>> +  grub_err_t err;
>> +  struct grub_protect_args args = { 0 };
>> +
>> +  grub_protect_init (&argc, &argv);
>> +
>> +  err = grub_protect_args_parse (argc, argv, &args);
>> +  if (err)
>> +    goto exit;
>> +
>> +  err = grub_protect_args_verify (&args);
>> +  if (err)
>> +    goto exit;
>> +
>> +  err = grub_protect_dispatch (&args);
>> +  if (err)
>> +    goto exit;
>> +
>> +exit:
>> +  grub_protect_fini ();
>> +
>> +  return err;
>> +}
>
> It would be nice to take arguments --help and -h and spit out something
> useful.
>

I should have added this already. Will do.

> Glenn
>

Thank you,
Hernan


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 4/5] cryptodisk: Support key protectors
  2022-01-25  5:42   ` Glenn Washburn
  2022-01-25 17:28     ` James Bottomley
@ 2022-01-27 14:26     ` Hernan Gatta
  1 sibling, 0 replies; 21+ messages in thread
From: Hernan Gatta @ 2022-01-27 14:26 UTC (permalink / raw)
  To: Glenn Washburn
  Cc: Hernan Gatta, The development of GNU GRUB, James Bottomley,
	James Bottomley



On Mon, 24 Jan 2022, Glenn Washburn wrote:

> On Mon, 24 Jan 2022 06:12:17 -0800
> Hernan Gatta <hegatta@linux.microsoft.com> wrote:
>
>> From: Hernan Gatta <hegatta@microsoft.com>
>>
>> Add a new parameter to cryptomount to support the key protectors framework: -k.
>> This parameter is accepted whenever the cryptomount command is used to mount a
>> specific disk either via a disk specification (e.g., (hd0,gpt1)) or by UUID, but
>> not when disks are mounted in bulk (i.e., via -a or -b). The parameter is used
>> to automatically retrieve a key from the specified key protector.
>
> Why shouldn't key protectors be used to try to unlock all devices that
> a backend identified as being able to unlock? Specifically, can a key
> stored via the TPM be used for multiple devices? With respect to a
> future unencrypted keyfile protector, it is certainly the case that it
> can be applied to multiple devices.
>

I was wondering about this myself and was hoping to hear feedback on it. 
I'll remove the limitation as suggested.

>>
>> Signed-off-by: <Hernan Gatta hegatta@linux.microsoft.com>
>> ---
>>  Makefile.util.def           |  1 +
>>  grub-core/disk/cryptodisk.c | 21 ++++++++++++++++++++-
>>  2 files changed, 21 insertions(+), 1 deletion(-)
>>
>> diff --git a/Makefile.util.def b/Makefile.util.def
>> index f8b356c..39b53b3 100644
>> --- a/Makefile.util.def
>> +++ b/Makefile.util.def
>> @@ -35,6 +35,7 @@ library = {
>>    common = grub-core/kern/list.c;
>>    common = grub-core/kern/misc.c;
>>    common = grub-core/kern/partition.c;
>> +  common = grub-core/kern/protectors.c;
>>    common = grub-core/lib/crypto.c;
>>    common = grub-core/lib/json/json.c;
>>    common = grub-core/disk/luks.c;
>> diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
>> index 4970973..176dd56 100644
>> --- a/grub-core/disk/cryptodisk.c
>> +++ b/grub-core/disk/cryptodisk.c
>> @@ -26,6 +26,7 @@
>>  #include <grub/file.h>
>>  #include <grub/procfs.h>
>>  #include <grub/partition.h>
>> +#include <grub/protector.h>
>>
>>  #ifdef GRUB_UTIL
>>  #include <grub/emu/hostdisk.h>
>> @@ -42,6 +43,7 @@ static const struct grub_arg_option options[] =
>>      {"all", 'a', 0, N_("Mount all."), 0, 0},
>>      {"boot", 'b', 0, N_("Mount all volumes with `boot' flag set."), 0, 0},
>>      {"password", 'p', 0, N_("Password to open volumes."), 0, ARG_TYPE_STRING},
>> +    {"protector", 'k', 0, N_("Unlock disk using the specified key protector."), 0, ARG_TYPE_STRING},
>
> This conflicts with the keyfile and detached header patch series as is.
> Ultimately, this is probably the right way to go and there will be an
> protector for using unencrypted keyfiles.
>
> Another option is to use -P for protectors and -k for keyfiles and have
> two separate methods of using unencrypted keyfiles. I generally don't
> like having two methods, the only benefit is that I believe the
> --keyfile method is more intuitive. To use the keyfile protector the
> user unfamiliar with these features will likely need to consult the
> documentation.
>
>>      {0, 0, 0, 0, 0, 0}
>>    };
>>
>> @@ -1160,6 +1162,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
>>  {
>>    struct grub_arg_list *state = ctxt->state;
>>    struct grub_cryptomount_args cargs = {0};
>> +  grub_err_t err;
>>
>>    if (argc < 1 && !state[1].set && !state[2].set)
>>      return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
>> @@ -1167,12 +1170,28 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
>>    if (grub_cryptodisk_list == NULL)
>>      return grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk modules loaded");
>>
>> +  if (state[3].set && state[4].set) /* password and key protector */
>> +    return grub_error (GRUB_ERR_BAD_ARGUMENT,
>> +                       "a password and a key protector cannot both be set");
>> +
>> +  if (state[4].set && argc < 1) /* key protector */
>> +    return grub_error (GRUB_ERR_BAD_ARGUMENT,
>> +                       "key protectors require a device name or UUID");
>
> Referring to the my first comment, I think we can get rid of this if
> block.
>

Will do.

>> +
>>    if (state[3].set) /* password */
>>      {
>>        cargs.key_data = (grub_uint8_t *) state[3].arg;
>>        cargs.key_len = grub_strlen (state[3].arg);
>>      }
>>
>> +  if (state[4].set) /* key protector */
>> +    {
>> +      err = grub_key_protector_recover_key (state[4].arg, &cargs.key_data,
>> +                                                          &cargs.key_len);
>> +      if (err)
>> +        grub_printf_ (N_("Could not recover key from key protector.\n"));
>
> Why is this not returning with an error here? Is it believed that
> perhaps the device can be unlocked without recovering a key (even
> though the user explicitly specified that a key should be used?)?
>

Yes, the idea was to let the user type in a passphrase in case something 
goes wrong with the key protector.

Another idea would be to have a passphrase key protector and have it 
prompt for input. Then, a user would type instead:

cryptomount -k tpm2:... -k passphrase

In this way, the user can be explicit about what the expectations are. 
cryptomount would try the first protector and, if that fails, then try the 
second, then the third, and so on.

If a user were then to specify:

cryptomount -k tpm2:...

but no "-k passphrase", no fallback would be initiated.

Thoughts?

>> +    }
>> +
>>    if (state[0].set) /* uuid */
>>      {
>>        int found_uuid;
>> @@ -1385,7 +1404,7 @@ GRUB_MOD_INIT (cryptodisk)
>>  {
>>    grub_disk_dev_register (&grub_cryptodisk_dev);
>>    cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0,
>> -			      N_("[-p password] <SOURCE|-u UUID|-a|-b>"),
>> +			      N_("[-p password] [-k protector[:args]] <SOURCE|-u UUID|-a|-b>"),
>
> This looks eerily similiar to what I proposed to James in response to
> his SEV patches[1]. As such, I am in favor of this syntax and it looks
> to me like a framework that James can use for his SEV series.
>
> Glenn
>
>>  			      N_("Mount a crypto device."), options);
>>    grub_procfs_register ("luks_script", &luks_script);
>>  }
>
> [1] https://lists.gnu.org/archive/html/grub-devel/2020-11/msg00105.html
>

Thank you,
Hernan


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 4/5] cryptodisk: Support key protectors
  2022-01-25 17:28     ` James Bottomley
@ 2022-01-27 14:39       ` Hernan Gatta
  0 siblings, 0 replies; 21+ messages in thread
From: Hernan Gatta @ 2022-01-27 14:39 UTC (permalink / raw)
  To: James Bottomley; +Cc: development, Hernan Gatta, The development of GNU GRUB



On Tue, 25 Jan 2022, James Bottomley wrote:

> On Mon, 2022-01-24 at 23:42 -0600, Glenn Washburn wrote:
>> On Mon, 24 Jan 2022 06:12:17 -0800
>> Hernan Gatta <hegatta@linux.microsoft.com> wrote:
> [...]
>>> +    }
>>> +
>>>    if (state[0].set) /* uuid */
>>>      {
>>>        int found_uuid;
>>> @@ -1385,7 +1404,7 @@ GRUB_MOD_INIT (cryptodisk)
>>>  {
>>>    grub_disk_dev_register (&grub_cryptodisk_dev);
>>>    cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount,
>>> 0,
>>> -			      N_("[-p password] <SOURCE|-u UUID|-a|-
>>> b>"),
>>> +			      N_("[-p password] [-k protector[:args]]
>>> <SOURCE|-u UUID|-a|-b>"),
>>
>> This looks eerily similiar to what I proposed to James in response to
>> his SEV patches[1]. As such, I am in favor of this syntax and it
>> looks to me like a framework that James can use for his SEV series.
>
> Well, I could, but I've got to say for a shell like system, which grub
> is, argument lists passed as a single argument always cause problems
> with quoting and interpolation.  if the protector needs additional
> arguments to initialize, then it should really be done as a separate
> module to avoid the arguments within argument problem, so
>
> protector_init [args]
> crtptomount -k protector ...
>
> means that [args] can use the standard arguments and doesn't have any
> internal quoting issues.
>

I agree that having nested argument lists can cause trouble, particularly 
for any future key protectors where escaping may be required.

As Glenn mentioned in his E-mail regarding the question of whether we 
should assume fallback on a passphrase, would you be in favor of a system 
where we do, say:

tpm2_protector_init [args]
sev_protector_init [args]

cryptomount -k sev,tpm2,passphrase

And then have cryptomount go through the list, falling back if protectors 
error out? (I'm not suggesting that the TPM2 and SEV protectors would be 
used simultaneously, I'm only using them as examples). Readily existing 
uses of cryptomount without -k would assume a passphrase protector by 
default where the user would be prompted in the usual way.

Another option would be to remove the -k parameter altogether and assume 
the order of execution on the basis of the initialization order of the 
protectors::

A_protector_init [args]
B_protector_init [args]
cryptomount  // Would try A then B

protectors_clear

B_protector_init [args]
cryptomount  // Would try B only

I like the first option best since it requires no global state.

Thoughts? Do you think that having the -k parameter as it is now would 
prevent you from using this patch series as it is for your purposes?

> James
>
>

Thank you,
Hernan


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 0/5] Automatic TPM Disk Unlock
  2022-01-27 14:03   ` Hernan Gatta
@ 2022-01-27 22:34     ` Glenn Washburn
  0 siblings, 0 replies; 21+ messages in thread
From: Glenn Washburn @ 2022-01-27 22:34 UTC (permalink / raw)
  To: Hernan Gatta; +Cc: Hernan Gatta, The development of GNU GRUB

On Thu, 27 Jan 2022 06:03:21 -0800 (PST)
Hernan Gatta
<hegatta@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>
wrote:

> 
> 
> On Tue, 25 Jan 2022, Glenn Washburn wrote:
> 
> > On Mon, 24 Jan 2022 06:12:13 -0800
> > Hernan Gatta <hegatta@linux.microsoft.com> wrote:
> >
> >> This patch series adds support for automatically unlocking fully-encrypted disks
> >> using a TPM 2.0.
> >>
> >> Currently, when GRUB encounters a fully-encrypted disk that it must access, its
> >> corresponding cryptodisk module (LUKS 1, LUKS2, or GELI) interactively prompts
> >> the user for a passphrase. An improvement to the boot process would be for GRUB
> >> to automatically retrieve the unlocking key for fully-encrypted disks from a
> >> protected location and to unlock these transparently. To this end, a TPM may be
> >> used to protect the unlocking key to a known-good state of the platform. Once
> >> the key is protected in this way, assuming that the platform remains
> >> trustworthy, GRUB can then utilize the TPM to release the key during boot and
> >> thus unlock fully-encrypted disks without user interaction. Such a model would
> >> not only be more convenient for end-users but also for virtual machines in cloud
> >> environments where no user is ever present.
> >>
> >> Design
> >> ------
> >>
> >> This patchset first adds a key protectors framework. This framework allows for
> >> key protector modules to register when loaded. A key protector is defined as a
> >> module that knows how to retrieve an unlocking key from a specific source. This
> >> patchset adds a single such key protector module that understands how to
> >> retrieve an unlocking key from a TPM 2.0 by unsealing a sealed key file via a
> >> Storage Root Key (SRK).
> >>
> >> Additionally, this patchset expands the cryptomount command to accept a key
> >> protector parameter. This parameter carries the information necessary to select
> >> and parameterize a key protector to be used to retrieve an unlocking key for the
> >> disk in question. That is, given an invocation of cryptomount to mount a
> >> specific disk (e.g., "cryptomount (hd0,gpt2)", "cryptomount -u UUID"), a key
> >> protector can be used to automatically retrieve an unlocking key without an
> >> interactive prompt.
> >>
> >> Lastly, this patchset also includes a new tool, grub-protect, that allows the
> >> user to seal a key file against a set of Platform Configuration Registers (PCRs)
> >> using an SRK. This sealed key file is expected to be stored in an unencrypted
> >> partition, such as the EFI System Partition (ESP), where GRUB can read it. The
> >> sealed key is then unsealed by the TPM2 key protector automatically, provided
> >> that the PCRs selected match on subsequent boots.
> >
> > This series should include a documentation patch at a minimum. I would
> > also like to see a QEMU test added. But it should use my cryptomount
> > test series, which hasn't made it to master yet. So this shouldn't be a
> > hard requirement. It would be nice for someone more familiar with
> > Secure boot and TPM to figure out how to get it working in QEMU so we/I
> > can easily add it to the tests. It looks like this should work with
> > the required software[1].
> >
> > Glenn
> >
> > [1] https://en.opensuse.org/Software_TPM_Emulator_For_QEMU
> >
> 
> Agreed. I wanted to get feedback on the feature as a whole first before 
> writing documentation for it since, I figured, the way it functions may 
> change as a result of your comments. Once we settle on how it should work 
> and, more importantly, on how a user might use it (I'm thinking of James' 
> comment on separating the cryptomount command from the configuration of a 
> key protector), then I'll add documentation.

Works for me.

> With respect to your comment about adding tests, it should indeed be 
> possible to test this using QEMU and a software TPM such as swtpm. I'm 
> happy to take a look at that.
> 
> Would it be alright for me to submit a patch series for tests 
> separately from this series?

I prefer strongly suggesting patches for testing be included in the
patch series for new features they test. To my knowledge, this isn't
something that has been stressed much for GRUB in the past, but I would
like to change that. And having said that, I don't think it need be
applied so strenuously for new features for cryptomount because the
infrastructure for testing cryptomount isn't fully baked (IMO). I've
got some patches to help address that, but they aren't the priority for
reviewing at the moment (but hopefully will become more so after the
detached headers patch series goes through). So yes, I think a separate
patch series would be fine.

As for implementation, ideally it would be leveraging my
"cryptomont-test" patch series, for which I'll need to submit an updated
version. I should do that soon. I'm also open to other ideas on the
implementation.

Glenn

> >>
> >> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
> >>
> >> Hernan Gatta (5):
> >>   protectors: Add key protectors framework
> >>   tpm2: Add TPM Software Stack (TSS)
> >>   protectors: Add TPM2 Key Protector
> >>   cryptodisk: Support key protectors
> >>   util/grub-protect: Add new tool
> >>
> >>  .gitignore                             |    1 +
> >>  Makefile.util.def                      |   17 +
> >>  configure.ac                           |    1 +
> >>  grub-core/Makefile.am                  |    1 +
> >>  grub-core/Makefile.core.def            |   10 +
> >>  grub-core/disk/cryptodisk.c            |   21 +-
> >>  grub-core/kern/protectors.c            |   98 +++
> >>  grub-core/tpm2/buffer.c                |  145 ++++
> >>  grub-core/tpm2/module.c                |  742 ++++++++++++++++++
> >>  grub-core/tpm2/mu.c                    |  807 +++++++++++++++++++
> >>  grub-core/tpm2/tcg2.c                  |  143 ++++
> >>  grub-core/tpm2/tpm2.c                  |  711 +++++++++++++++++
> >>  include/grub/protector.h               |   55 ++
> >>  include/grub/tpm2/buffer.h             |   65 ++
> >>  include/grub/tpm2/internal/functions.h |  117 +++
> >>  include/grub/tpm2/internal/structs.h   |  675 ++++++++++++++++
> >>  include/grub/tpm2/internal/types.h     |  372 +++++++++
> >>  include/grub/tpm2/mu.h                 |  292 +++++++
> >>  include/grub/tpm2/tcg2.h               |   34 +
> >>  include/grub/tpm2/tpm2.h               |   38 +
> >>  util/grub-protect.c                    | 1344 ++++++++++++++++++++++++++++++++
> >>  21 files changed, 5688 insertions(+), 1 deletion(-)
> >>  create mode 100644 grub-core/kern/protectors.c
> >>  create mode 100644 grub-core/tpm2/buffer.c
> >>  create mode 100644 grub-core/tpm2/module.c
> >>  create mode 100644 grub-core/tpm2/mu.c
> >>  create mode 100644 grub-core/tpm2/tcg2.c
> >>  create mode 100644 grub-core/tpm2/tpm2.c
> >>  create mode 100644 include/grub/protector.h
> >>  create mode 100644 include/grub/tpm2/buffer.h
> >>  create mode 100644 include/grub/tpm2/internal/functions.h
> >>  create mode 100644 include/grub/tpm2/internal/structs.h
> >>  create mode 100644 include/grub/tpm2/internal/types.h
> >>  create mode 100644 include/grub/tpm2/mu.h
> >>  create mode 100644 include/grub/tpm2/tcg2.h
> >>  create mode 100644 include/grub/tpm2/tpm2.h
> >>  create mode 100644 util/grub-protect.c
> >>
> >
> 
> Thank you,
> Hernan


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 3/5] protectors: Add TPM2 Key Protector
  2022-01-27 14:11     ` Hernan Gatta
@ 2022-01-28  3:40       ` Glenn Washburn
  0 siblings, 0 replies; 21+ messages in thread
From: Glenn Washburn @ 2022-01-28  3:40 UTC (permalink / raw)
  To: Hernan Gatta; +Cc: Hernan Gatta, The development of GNU GRUB

On Thu, 27 Jan 2022 06:11:03 -0800 (PST)
Hernan Gatta
<hegatta@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>
wrote:

> 
> 
> On Mon, 24 Jan 2022, Glenn Washburn wrote:
> 
> > On Mon, 24 Jan 2022 06:12:16 -0800
> > Hernan Gatta <hegatta@linux.microsoft.com> wrote:
> >
> >> From: Hernan Gatta <hegatta@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 therefore possess reasonable defaults. One of these
> >> arguments is the keyfile parameter, which is mandatory.
> >>
> >> The value of this parameter must be a path to a sealed key file (e.g.,
> >> (hd0,gpt1)/boot/grub2/sealed_key). 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.
> >>
> >
> > There are a lot of points in this code where an error can be returned.
> > In this case, all I'm going to get is a non-descript error code (almost
> > always GRUB_ERR_BAD_ARGUMENT). So as a user, how am I to know where the
> > error is actually coming from? For this reason, I like the use of
> > grub_error(error_code, error_format_string, fmtstr_args...), which will
> > set grub_errno and an error message, which can be retrieved up the stack
> > and presented to the user. I'm not sure that every return of a naked
> > error code should be replaced by grub_error(), depends on how granular
> > of an error message you want to see. I would error on the side of more
> > granular. For instance, its really nice that GCC will tell me which
> > format code type specifier and a argument its choking on, rather than a
> > catch all "I'm failing to compile your code because you have an
> > incorrect format code somewhere in your format string".
> >
> 
> Agreed. I'll add calls to grub_error to indicate why things fail and, when 
> they do, capture the error up the stack (I was thinking in the command 
> handler for cryptomount), and print it.

IIRC, you shouldn't have to do anything, just return from cryptomount
with the error code and have grub_errno and grub_errmsg set. If you
swallow any errors, you should print the swallowed errors in
grub_dprintf() (maybe not always, judgement required).

> 
> >> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
> >> ---
> >>  grub-core/Makefile.core.def |   9 +
> >>  grub-core/tpm2/module.c     | 742 ++++++++++++++++++++++++++++++++++++++++++++
> >>  2 files changed, 751 insertions(+)
> >>  create mode 100644 grub-core/tpm2/module.c
> >>
> >> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> >> index e4ae78b..8691440 100644
> >> --- a/grub-core/Makefile.core.def
> >> +++ b/grub-core/Makefile.core.def
> >> @@ -2498,6 +2498,15 @@ module = {
> >>  };
> >>
> >>  module = {
> >> +  name = tpm2;
> >> +  common = tpm2/buffer.c;
> >> +  common = tpm2/module.c;
> >> +  common = tpm2/mu.c;
> >> +  common = tpm2/tpm2.c;
> >> +  efi = tpm2/tcg2.c;
> >> +};
> >> +
> >> +module = {
> >>    name = tr;
> >>    common = commands/tr.c;
> >>  };
> >> diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
> >> new file mode 100644
> >> index 0000000..cd97452
> >> --- /dev/null
> >> +++ b/grub-core/tpm2/module.c
> >> @@ -0,0 +1,742 @@
> >> +/*
> >> + *  GRUB  --  GRand Unified Bootloader
> >> + *  Copyright (C) 2022 Microsoft Corporation
> >> + *
> >> + *  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/file.h>
> >> +#include <grub/misc.h>
> >> +#include <grub/mm.h>
> >> +#include <grub/protector.h>
> >> +#include <grub/tpm2/buffer.h>
> >> +#include <grub/tpm2/mu.h>
> >> +#include <grub/tpm2/tpm2.h>
> >> +#include <grub/types.h>
> >> +
> >> +GRUB_MOD_LICENSE ("GPLv3+");
> >> +
> >> +typedef enum grub_tpm2_protector_args
> >> +{
> >> +  GRUB_TPM2_PROTECTOR_ARG_MODE       = 1 << 0,
> >> +  GRUB_TPM2_PROTECTOR_ARG_PCRS       = 1 << 1,
> >> +  GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC = 1 << 2,
> >> +  GRUB_TPM2_PROTECTOR_ARG_BANK       = 1 << 3,
> >> +  GRUB_TPM2_PROTECTOR_ARG_KEYFILE    = 1 << 4,
> >> +  GRUB_TPM2_PROTECTOR_ARG_SRK        = 1 << 5,
> >> +  GRUB_TPM2_PROTECTOR_ARG_NV         = 1 << 6
> >> +} grub_tpm2_protector_args_t;
> >> +
> >> +typedef enum grub_tpm2_protector_mode
> >> +{
> >> +  GRUB_TPM2_PROTECTOR_MODE_SRK = 1,
> >> +  GRUB_TPM2_PROTECTOR_MODE_NV  = 2
> >> +} grub_tpm2_protector_mode_t;
> >> +
> >> +struct grub_tpm2_protector_context
> >> +{
> >> +  grub_tpm2_protector_args_t args;
> >> +  grub_tpm2_protector_mode_t mode;
> >> +  grub_uint8_t pcrs[TPM_MAX_PCRS];
> >> +  grub_uint8_t pcr_count;
> >> +  TPM_ALG_ID asymmetric;
> >> +  TPM_ALG_ID bank;
> >> +  const char *keyfile;
> >> +  TPM_HANDLE srk;
> >> +  TPM_HANDLE nv;
> >> +};
> >> +
> >> +static grub_err_t
> >> +grub_tpm2_protector_parse_mode (struct grub_tpm2_protector_context *ctx,
> >> +                                const char *value)
> >> +{
> >> +  if (grub_strcmp (value, "srk") == 0)
> >> +    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
> >> +  else if (grub_strcmp (value, "nv") == 0)
> >> +    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_NV;
> >> +  else
> >> +    return GRUB_ERR_OUT_OF_RANGE;
> >> +
> >> +  return GRUB_ERR_NONE;
> >> +}
> >> +
> >> +static grub_err_t
> >> +grub_tpm2_protector_parse_pcrs (struct grub_tpm2_protector_context *ctx,
> >> +                                char *value)
> >> +{
> >> +  grub_err_t err;
> >> +  char *current_pcr = value;
> >> +  char *next_pcr;
> >> +  unsigned long pcr;
> >> +  grub_uint8_t i;
> >> +
> >> +  if (grub_strlen (value) == 0)
> >> +    return GRUB_ERR_BAD_ARGUMENT;
> >> +
> >> +  ctx->pcr_count = 0;
> >> +  for (i = 0; i < TPM_MAX_PCRS; i++)
> >> +    {
> >> +      next_pcr = grub_strchr (current_pcr, ',');
> >> +      if (next_pcr)
> >> +        *next_pcr = '\0';
> >> +      if (next_pcr == current_pcr)
> >> +        return GRUB_ERR_BAD_ARGUMENT;
> >> +
> >> +      grub_errno = GRUB_ERR_NONE;
> >> +      pcr = grub_strtoul (current_pcr, NULL, 10);
> >> +      if (grub_errno != GRUB_ERR_NONE)
> >> +        {
> >> +          err = grub_errno;
> >> +          grub_errno = GRUB_ERR_NONE;
> >> +          return err;
> >> +        }
> >> +
> >> +      if (pcr > GRUB_UCHAR_MAX)
> >> +        return GRUB_ERR_OUT_OF_RANGE;
> >> +
> >> +      ctx->pcrs[i] = (grub_uint8_t)pcr;
> >> +      ctx->pcr_count++;
> >> +
> >> +      if (!next_pcr)
> >> +        break;
> >> +
> >> +      current_pcr = next_pcr + 1;
> >> +      if (*current_pcr == '\0')
> >> +        return GRUB_ERR_BAD_ARGUMENT;
> >> +    }
> >> +
> >> +  if (i == TPM_MAX_PCRS)
> >> +    return GRUB_ERR_OUT_OF_RANGE;
> >> +
> >> +  return GRUB_ERR_NONE;
> >> +}
> >> +
> >> +static grub_err_t
> >> +grub_tpm2_protector_parse_asymmetric (struct grub_tpm2_protector_context *ctx,
> >> +                                      const char *value)
> >> +{
> >> +  if (grub_strcasecmp (value, "ECC") == 0)
> >> +    ctx->asymmetric = TPM_ALG_ECC;
> >> +  else if (grub_strcasecmp (value, "RSA") == 0)
> >> +    ctx->asymmetric = TPM_ALG_RSA;
> >> +  else
> >> +    return GRUB_ERR_OUT_OF_RANGE;
> >> +
> >> +  return GRUB_ERR_NONE;
> >> +}
> >> +
> >> +static grub_err_t
> >> +grub_tpm2_protector_parse_bank (struct grub_tpm2_protector_context *ctx,
> >> +                                const char *value)
> >> +{
> >> +  if (grub_strcasecmp (value, "SHA1") == 0)
> >> +    ctx->bank = TPM_ALG_SHA1;
> >> +  else if (grub_strcasecmp (value, "SHA256") == 0)
> >> +    ctx->bank = TPM_ALG_SHA256;
> >> +  else if (grub_strcasecmp (value, "SHA384") == 0)
> >> +    ctx->bank = TPM_ALG_SHA384;
> >> +  else
> >> +    return GRUB_ERR_OUT_OF_RANGE;
> >> +
> >> +  return GRUB_ERR_NONE;
> >> +}
> >> +
> >> +static grub_err_t
> >> +grub_tpm2_protector_parse_keyfile (struct grub_tpm2_protector_context *ctx,
> >> +                                   const char *value)
> >> +{
> >> +  if (grub_strlen (value) == 0)
> >> +    return GRUB_ERR_BAD_ARGUMENT;
> >> +
> >> +  ctx->keyfile = value;
> >> +
> >> +  return GRUB_ERR_NONE;
> >> +}
> >> +
> >> +static grub_err_t
> >> +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle)
> >> +{
> >> +  grub_err_t err;
> >> +  unsigned long num;
> >> +
> >> +  grub_errno = GRUB_ERR_NONE;
> >> +  num = grub_strtoul (value, NULL, 0);
> >> +  if (grub_errno != GRUB_ERR_NONE)
> >> +    {
> >> +      err = grub_errno;
> >> +      grub_errno = GRUB_ERR_NONE;
> >> +      return err;
> >> +    }
> >> +
> >> +  if (num > GRUB_UINT_MAX)
> >> +    return GRUB_ERR_OUT_OF_RANGE;
> >> +
> >> +  *handle = (TPM_HANDLE)num;
> >> +
> >> +  return GRUB_ERR_NONE;
> >> +}
> >> +
> >> +static grub_err_t
> >> +grub_tpm2_protector_parse_srk (struct grub_tpm2_protector_context *ctx,
> >> +                               const char *value)
> >> +{
> >> +  return grub_tpm2_protector_parse_tpm_handle (value, &ctx->srk);
> >> +}
> >> +
> >> +static grub_err_t
> >> +grub_tpm2_protector_parse_nvindex (struct grub_tpm2_protector_context *ctx,
> >> +                                   const char *value)
> >> +{
> >> +  return grub_tpm2_protector_parse_tpm_handle (value, &ctx->nv);
> >> +}
> >> +
> >> +static grub_err_t
> >> +grub_tpm2_protector_match_arg (struct grub_tpm2_protector_context *ctx,
> >> +                               const char *key, char *value)
> >> +{
> >> +  grub_err_t err;
> >> +
> >> +  if (grub_strlen (key) == 0 || grub_strlen (value) == 0)
> >> +    return GRUB_ERR_BAD_ARGUMENT;
> >> +
> >> +  if (grub_strcmp (key, "mode") == 0)
> >> +  {
> >> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_MODE)
> >> +      return GRUB_ERR_BAD_ARGUMENT;
> >> +
> >> +    err = grub_tpm2_protector_parse_mode (ctx, value);
> >> +    if (!err)
> >> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_MODE;
> >> +
> >> +    return err;
> >> +  }
> >> +
> >> +  if (grub_strcmp (key, "pcrs") == 0)
> >
> > Minor nit, why wouldn't this be better as an "else if"? Seems
> > unnecessary to check all the "if" statements if we've already found a
> > match.
> >
> 
> The reason why I wrote this in this way without an "else if" is because 
> each "if" clause has an unconditional return. So, in the end, the entire 
> sequence behaves in the manner you describe.
> 
> Would you prefer that I add the "else if"'s anyway for readability?

Somehow I missed the returns. I don't care as much now. Maybe Daniel
will have an opinion when he gets to this.

Glenn


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2022-01-28  3:43 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-24 14:12 [PATCH 0/5] Automatic TPM Disk Unlock Hernan Gatta
2022-01-24 14:12 ` [PATCH 1/5] protectors: Add key protectors framework Hernan Gatta
2022-01-25  2:54   ` Glenn Washburn
2022-01-27 14:05     ` Hernan Gatta
2022-01-24 14:12 ` [PATCH 2/5] tpm2: Add TPM Software Stack (TSS) Hernan Gatta
2022-01-24 14:12 ` [PATCH 3/5] protectors: Add TPM2 Key Protector Hernan Gatta
2022-01-25  3:55   ` Glenn Washburn
2022-01-27 14:11     ` Hernan Gatta
2022-01-28  3:40       ` Glenn Washburn
2022-01-24 14:12 ` [PATCH 4/5] cryptodisk: Support key protectors Hernan Gatta
2022-01-25  5:42   ` Glenn Washburn
2022-01-25 17:28     ` James Bottomley
2022-01-27 14:39       ` Hernan Gatta
2022-01-27 14:26     ` Hernan Gatta
2022-01-24 14:12 ` [PATCH 5/5] util/grub-protect: Add new tool Hernan Gatta
2022-01-25  5:45   ` Glenn Washburn
2022-01-27 14:19     ` Hernan Gatta
2022-01-24 23:21 ` [PATCH 0/5] Automatic TPM Disk Unlock Didier Spaier
2022-01-25 20:30 ` Glenn Washburn
2022-01-27 14:03   ` Hernan Gatta
2022-01-27 22:34     ` Glenn Washburn

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.