Enroll your TPM2 device by creating a token inside LUKS2 header:
  systemd-cryptenroll /dev/sdXY --tpm2-device=auto

Then, in the boot script:
  tpm2_key_protector_init --device=hdX,gptY
  cryptomount hdX,gptY -P systemd-tpm2

The arguments --keyslot=i and/or --token=j can be used to filter which token to
use.

Supports trusted srk (tpm2_srk field) introduced in systemd v254.

Signed-off-by: Yann Diorcet <diorcet.y...@gmail.com>
---
 grub-core/Makefile.core.def                   |   3 +-
 .../commands/tpm2_key_protector/module.c      | 308 +++++++++++++++++-
 .../commands/tpm2_key_protector/tpm2_luks2.c  | 280 ++++++++++++++++
 .../commands/tpm2_key_protector/tpm2_luks2.h  |  39 +++
 4 files changed, 626 insertions(+), 4 deletions(-)
 create mode 100644 grub-core/commands/tpm2_key_protector/tpm2_luks2.c
 create mode 100644 grub-core/commands/tpm2_key_protector/tpm2_luks2.h

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 0dfe85ab6..219a96966 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2585,10 +2585,11 @@ module = {
   common = commands/tpm2_key_protector/tpm2key.c;
   common = commands/tpm2_key_protector/tpm2key_asn1_tab.c;
   common = commands/tpm2_key_protector/tpm2srk.c;
+  common = commands/tpm2_key_protector/tpm2_luks2.c;
   /* The plaform support of tpm2_key_protector depends on the tcg2 
implementation in tss2. */
   enable = efi;
   enable = emu;
-  cppflags = '-I$(srcdir)/lib/tss2 -I$(srcdir)/lib/libtasn1-grub';
+  cppflags = '$(CPPFLAGS_GNULIB) -I$(srcdir)/lib/tss2 
-I$(srcdir)/lib/libtasn1-grub';
 };
 
 module = {
diff --git a/grub-core/commands/tpm2_key_protector/module.c 
b/grub-core/commands/tpm2_key_protector/module.c
index 4f7b6e95d..60f45efa6 100644
--- a/grub-core/commands/tpm2_key_protector/module.c
+++ b/grub-core/commands/tpm2_key_protector/module.c
@@ -24,6 +24,8 @@
 #include <grub/misc.h>
 #include <grub/mm.h>
 #include <grub/key_protector.h>
+#include <grub/luks2.h>
+#include <base64.h>
 
 #include <tss2_buffer.h>
 #include <tss2_types.h>
@@ -32,6 +34,7 @@
 #include "tpm2_args.h"
 #include "tpm2.h"
 #include "tpm2key.h"
+#include "tpm2_luks2.h"
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -39,7 +42,8 @@ typedef enum tpm2_protector_mode
 {
   TPM2_PROTECTOR_MODE_UNSET,
   TPM2_PROTECTOR_MODE_SRK,
-  TPM2_PROTECTOR_MODE_NV
+  TPM2_PROTECTOR_MODE_NV,
+  TPM2_PROTECTOR_MODE_LUKS2_TOKEN,
 } tpm2_protector_mode_t;
 
 typedef enum tpm2_protector_options
@@ -51,7 +55,10 @@ typedef enum tpm2_protector_options
   OPTION_KEYFILE,
   OPTION_SRK,
   OPTION_ASYMMETRIC,
-  OPTION_NVINDEX
+  OPTION_NVINDEX,
+  OPTION_DEVICE,
+  OPTION_KEYSLOT,
+  OPTION_TOKEN,
 } tpm2_protector_options_t;
 
 typedef struct tpm2_protector_context
@@ -65,6 +72,9 @@ typedef struct tpm2_protector_context
   const char *keyfile;
   TPM_HANDLE_t srk;
   TPM_HANDLE_t nv;
+  grub_int8_t keyslot_idx;
+  grub_int8_t token_idx;
+  grub_luks2_token_t token;
 } tpm2_protector_context_t;
 
 static const struct grub_arg_option tpm2_protector_init_cmd_options[] =
@@ -152,6 +162,30 @@ static const struct grub_arg_option 
tpm2_protector_init_cmd_options[] =
        N_("Required in NV Index mode, the NV handle to read which must "
           "readily exist on the TPM and which contains the key."),
     },
+    {
+      .longarg  = "device",
+      .shortarg = 'd',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_STRING,
+      .doc      = N_("Use this LUKS partition looking for token to use"),
+    },
+    {
+      .longarg  = "keyslot",
+      .shortarg = 'K',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_INT,
+      .doc      = N_("Set the keyslot index to use in the LUKS partition"),
+    },
+    {
+      .longarg  = "token",
+      .shortarg = 't',
+      .flags    = 0,
+      .arg      = NULL,
+      .type     = ARG_TYPE_INT,
+      .doc      = N_("Set the token index to use in the LUKS partition"),
+    },
     /* End of list */
     {0, 0, 0, 0, 0, 0}
   };
@@ -1046,6 +1080,173 @@ tpm2_protector_nv_recover (const 
tpm2_protector_context_t *ctx,
   return err;
 }
 
+static inline int
+hex2val (char hex)
+{
+  if ('0' <= hex && hex <= '9')
+    return hex - '0';
+  if ('a' <= hex && hex <= 'f')
+    return hex - 'a' + 10;
+  if ('A' <= hex && hex <= 'F')
+    return hex - 'A' + 10;
+  return -1;
+}
+
+static grub_err_t
+unhex_hash (const char *p, grub_size_t l, void **ret, grub_size_t *ret_len) {
+  char *buf = NULL;
+  grub_size_t buf_size;
+  char *z;
+  int r = GRUB_ERR_NONE;
+
+  if (p == NULL || l == 0 || ret == NULL || ret_len == NULL)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  if (l == GRUB_SIZE_MAX)
+    l = grub_strlen(p);
+
+  /* Note that the calculation of memory size is an upper boundary,
+     as we ignore whitespace while decoding */
+  buf_size = (l + 1) / 2 + 1;
+  buf = z = grub_malloc(buf_size);
+  if (!buf)
+          return GRUB_ERR_OUT_OF_MEMORY;
+
+  while (p[0] != 0)
+  {
+    int high, low;
+
+    /* Remove spaces before first hex character */
+    while (p[0] != 0 && grub_isspace (p[0]))
+      p++;
+
+    /* End of the string */
+    if (p[0] == 0)
+      break;
+
+    /* First hex character */
+    high = hex2val (*p++);
+
+    /* Remove spaces after first hex character */
+    while (p[0] != 0 && grub_isspace (p[0]))
+      p++;
+    if (p[0] == 0 || high < 0)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid hex string");
+      goto on_failure;
+    }
+
+    /* Second hex character */
+    low = hex2val (*p++);
+    if (low < 0)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid hex string");
+      goto on_failure;
+    }
+
+    *z++ = (high << 4) | low;
+  }
+
+  *z = 0;
+
+  *ret_len = (grub_size_t) (z - buf);
+  *ret = buf;
+
+  return r;
+
+on_failure:
+  grub_free(buf);
+  *ret = NULL;
+  *ret_len = 0;
+
+  return r;
+}
+
+static grub_err_t
+tpm2_protector_luks2_token_recover (const tpm2_protector_context_t *ctx,
+                                grub_uint8_t **key, grub_size_t *key_size)
+{
+  const char *base64_blob;
+  char *blob;
+  idx_t blob_size;
+  void *decrypted_key;
+  grub_size_t decrypted_key_size;
+  char *base64_encode;
+  grub_size_t base64_encode_size;
+
+  const char *hex_hash;
+  char *policy_hash;
+  grub_size_t policy_hash_size;
+
+  const char *base64_srk;
+  char *srk = NULL;
+  idx_t srk_size = 0;
+
+  grub_err_t ret = GRUB_ERR_NONE;
+
+  if (key == NULL || key_size == NULL)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  base64_blob = ctx->token.u.systemd_tpm2.base64_blob;
+  if(base64_decode_alloc(base64_blob, grub_strlen (base64_blob), &blob, 
&blob_size) == 0)
+    {
+      return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Can't decode base64 
blob"));
+    }
+
+  hex_hash = ctx->token.u.systemd_tpm2.hex_policy_hash;
+  ret = unhex_hash(hex_hash, grub_strlen (hex_hash), (void*)&policy_hash, 
&policy_hash_size);
+  if(ret != GRUB_ERR_NONE)
+    {
+      ret = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Can't unhex policy hash"));
+      goto exit1;
+    }
+
+  base64_srk = ctx->token.u.systemd_tpm2.base64_srk;
+  if (base64_srk != NULL)
+    {
+      if(base64_decode_alloc(base64_srk, grub_strlen (base64_srk), &srk, 
&srk_size) == 0)
+        {
+          return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Can't decode base64 
srk"));
+        }
+    }
+
+  ret = tpm2_luks2_acquire_luks2_key(
+    ctx->token.u.systemd_tpm2.pcr_mask,
+    ctx->token.u.systemd_tpm2.pcr_bank,
+    ctx->token.u.systemd_tpm2.primary_alg,
+    blob,
+    blob_size,
+    policy_hash,
+    policy_hash_size,
+    srk,
+    srk_size,
+    &decrypted_key,
+    &decrypted_key_size);
+
+  if (ret != GRUB_ERR_NONE)
+    goto exit2;
+
+  /* Before using this key as passphrase we base64 encode it, for compat with 
homed */
+  base64_encode_size = base64_encode_alloc(decrypted_key, decrypted_key_size, 
&base64_encode);
+  if (base64_encode_size <= 0)
+  {
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Can't encode passphrase"));
+    goto exit3;
+  }
+
+  *key = (grub_uint8_t *) base64_encode;
+  *key_size = grub_strlen(base64_encode);
+
+exit3:
+  grub_free(decrypted_key);
+exit2:
+  grub_free(policy_hash);
+exit1:
+  grub_free(blob);
+
+  return ret;
+}
+
 static grub_err_t
 tpm2_protector_recover (const tpm2_protector_context_t *ctx,
                        grub_uint8_t **key, grub_size_t *key_size)
@@ -1056,6 +1257,8 @@ tpm2_protector_recover (const tpm2_protector_context_t 
*ctx,
       return tpm2_protector_srk_recover (ctx, key, key_size);
     case TPM2_PROTECTOR_MODE_NV:
       return tpm2_protector_nv_recover (ctx, key, key_size);
+    case TPM2_PROTECTOR_MODE_LUKS2_TOKEN:
+      return tpm2_protector_luks2_token_recover (ctx, key, key_size);
     default:
       return GRUB_ERR_BAD_ARGUMENT;
     }
@@ -1160,18 +1363,68 @@ tpm2_protector_parse_mode (const char *value, 
tpm2_protector_mode_t *mode)
   return GRUB_ERR_NONE;
 }
 
+static grub_err_t
+tpm2_protector_parse_device (const char *value, const char **file)
+{
+  if (grub_strlen (value) == 0)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  *file = value;
+
+  return GRUB_ERR_NONE;
+}
+
+static void
+luks2_iterate_fct(grub_luks2_keyslot_t *k,
+                  grub_luks2_digest_t *d __attribute__ ((unused)),
+                  grub_luks2_segment_t *s __attribute__ ((unused)),
+                  grub_luks2_token_t *t)
+{
+  if (tpm2_protector_ctx.keyslot_idx >= 0 && k->idx != 
(grub_uint64_t)tpm2_protector_ctx.keyslot_idx)
+    return;
+  if (tpm2_protector_ctx.token_idx >= 0 && t->idx != 
(grub_uint64_t)tpm2_protector_ctx.token_idx)
+    return;
+
+  if (tpm2_protector_ctx.token.type != LUKS2_TOKEN_TYPE_NONE)
+    {
+      grub_printf ("systemd-tpm2 already set: multiple keyslots matched. 
Ignoring the token %ld\n", t->idx);
+      return;
+    }
+
+  grub_memcpy (&tpm2_protector_ctx.token, t, sizeof(grub_luks2_token_t));
+  if (tpm2_protector_ctx.token.type == LUKS2_TOKEN_TYPE_SYSTEMD_TPM2)
+    {
+      tpm2_protector_ctx.token.u.systemd_tpm2.base64_blob =
+        grub_strdup (tpm2_protector_ctx.token.u.systemd_tpm2.base64_blob);
+      tpm2_protector_ctx.token.u.systemd_tpm2.hex_policy_hash =
+        grub_strdup (tpm2_protector_ctx.token.u.systemd_tpm2.hex_policy_hash);
+      if (tpm2_protector_ctx.token.u.systemd_tpm2.base64_srk != NULL)
+        tpm2_protector_ctx.token.u.systemd_tpm2.base64_srk =
+          grub_strdup (tpm2_protector_ctx.token.u.systemd_tpm2.base64_srk);
+    }
+}
+
 static grub_err_t
 tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc,
                                 char **args __attribute__ ((unused)))
 {
   struct grub_arg_list *state = ctxt->state;
+  const char *device = NULL;
   grub_err_t err;
 
   if (argc)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("the TPM2 key protector does 
not accept any non-option arguments (i.e., like -o and/or --option only)"));
 
   grub_free ((void *) tpm2_protector_ctx.keyfile);
+  if (tpm2_protector_ctx.token.type == LUKS2_TOKEN_TYPE_SYSTEMD_TPM2)
+    {
+      grub_free ((void *) tpm2_protector_ctx.token.u.systemd_tpm2.base64_blob);
+      grub_free ((void *) 
tpm2_protector_ctx.token.u.systemd_tpm2.hex_policy_hash);
+      grub_free ((void *) tpm2_protector_ctx.token.u.systemd_tpm2.base64_srk);
+    }
   grub_memset (&tpm2_protector_ctx, 0, sizeof (tpm2_protector_ctx));
+  tpm2_protector_ctx.keyslot_idx = -1;
+  tpm2_protector_ctx.token_idx = -1;
 
   if (state[OPTION_MODE].set)  /* mode */
     {
@@ -1237,9 +1490,44 @@ tpm2_protector_init_cmd_handler (grub_extcmd_context_t 
ctxt, int argc,
        return err;
     }
 
+  if (state[OPTION_DEVICE].set)  /* device */
+    {
+      err = tpm2_protector_parse_device (state[OPTION_DEVICE].arg, &device);
+      if (err != GRUB_ERR_NONE)
+        return err;
+    }
+
+  if (state[OPTION_KEYSLOT].set)
+    {
+      tpm2_protector_ctx.keyslot_idx = grub_strtol (state[OPTION_KEYSLOT].arg, 
0, 10);
+    }
+
+  if (state[OPTION_TOKEN].set)
+    {
+      tpm2_protector_ctx.token_idx = grub_strtol (state[OPTION_TOKEN].arg, 0, 
10);
+    }
+
   err = tpm2_protector_check_args (&tpm2_protector_ctx);
 
-  /* This command only initializes the protector, so nothing else to do. */
+  if (device != NULL)
+    {
+      /* Open disk. */
+      grub_disk_t disk = grub_disk_open (device);
+      if (! disk)
+        {
+          err =  grub_error (GRUB_ERR_BAD_ARGUMENT, 
N_("systemd_tpm2_key_protector_init: can't open device %s"), device);
+          return err;
+        }
+
+      luks2_iterate_keyslot(disk, LUKS2_ITERATE_FLAGS_WITH_TOKEN, 
luks2_iterate_fct);
+
+      if (tpm2_protector_ctx.token.type != LUKS2_TOKEN_TYPE_SYSTEMD_TPM2)
+        err =  grub_error (GRUB_ERR_BAD_ARGUMENT, 
N_("systemd_tpm2_key_protector_init: no systemd-tpm2 token found on %s"), 
device);
+      else
+        tpm2_protector_ctx.mode = TPM2_PROTECTOR_MODE_LUKS2_TOKEN;
+
+      grub_disk_close(disk);
+    }
 
   return err;
 }
@@ -1252,7 +1540,15 @@ tpm2_protector_clear_cmd_handler (grub_extcmd_context_t 
ctxt __attribute__ ((unu
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("tpm2_key_protector_clear 
accepts no arguments"));
 
   grub_free ((void *) tpm2_protector_ctx.keyfile);
+  if (tpm2_protector_ctx.token.type == LUKS2_TOKEN_TYPE_SYSTEMD_TPM2)
+    {
+      grub_free ((void *) tpm2_protector_ctx.token.u.systemd_tpm2.base64_blob);
+      grub_free ((void *) 
tpm2_protector_ctx.token.u.systemd_tpm2.hex_policy_hash);
+      grub_free ((void *) tpm2_protector_ctx.token.u.systemd_tpm2.base64_srk);
+    }
   grub_memset (&tpm2_protector_ctx, 0, sizeof (tpm2_protector_ctx));
+  tpm2_protector_ctx.keyslot_idx = -1;
+  tpm2_protector_ctx.token_idx = -1;
 
   return GRUB_ERR_NONE;
 }
@@ -1320,6 +1616,12 @@ GRUB_MOD_INIT (tpm2_key_protector)
 GRUB_MOD_FINI (tpm2_key_protector)
 {
   grub_free ((void *) tpm2_protector_ctx.keyfile);
+  if (tpm2_protector_ctx.token.type == LUKS2_TOKEN_TYPE_SYSTEMD_TPM2)
+    {
+      grub_free ((void *) tpm2_protector_ctx.token.u.systemd_tpm2.base64_blob);
+      grub_free ((void *) 
tpm2_protector_ctx.token.u.systemd_tpm2.hex_policy_hash);
+      grub_free ((void *) tpm2_protector_ctx.token.u.systemd_tpm2.base64_srk);
+    }
 
   grub_key_protector_unregister (&tpm2_key_protector);
   grub_unregister_extcmd (tpm2_protector_clear_cmd);
diff --git a/grub-core/commands/tpm2_key_protector/tpm2_luks2.c 
b/grub-core/commands/tpm2_key_protector/tpm2_luks2.c
new file mode 100644
index 000000000..de0a7f01f
--- /dev/null
+++ b/grub-core/commands/tpm2_key_protector/tpm2_luks2.c
@@ -0,0 +1,280 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tpm2_luks2.h"
+
+#include <grub/list.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/luks2.h>
+
+#include <tss2_buffer.h>
+#include <tss2_mu.h>
+#include <tss2_iesys.h>
+
+#include "tpm2.h"
+#include "tpm2srk.h"
+
+static grub_err_t
+tpm2_luks2_unmarshal_raw (const void *sealed_key,
+                             grub_size_t sealed_key_size,
+                             tpm2_sealed_key_t *sk)
+{
+  struct grub_tpm2_buffer buf;
+
+  grub_tpm2_buffer_init (&buf);
+  if (sealed_key_size > buf.cap)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("sealed key larger than %llu 
bytes"), (unsigned long long)buf.cap);
+
+  grub_memcpy (buf.data, sealed_key, sealed_key_size);
+  buf.size = sealed_key_size;
+
+  grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private);
+  grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
+
+  if (buf.error != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("malformed TPM wire key 
file"));
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+tpm2_luks2_unmarshal_srk (const void *srk,
+                             grub_size_t srk_size,
+                             TPM_IESYS_RESOURCE_t *rsrc)
+{
+  struct grub_tpm2_buffer buf;
+
+  grub_tpm2_buffer_init (&buf);
+  if (srk_size > buf.cap)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("srk larger than %llu 
bytes"), (unsigned long long)buf.cap);
+
+  grub_memcpy (buf.data, srk, srk_size);
+  buf.size = srk_size;
+
+  grub_Tss2_IESYS_RESOURCE_Unmarshal(&buf, rsrc);
+
+  if (buf.error != 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("malformed TPM wire key 
file"));
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+tpm2_luks2_make_pcr_session(TPMI_SH_AUTH_SESSION_t session, grub_uint32_t 
pcr_mask, grub_uint16_t pcr_bank, TPM2B_DIGEST_t *digest)
+{
+  grub_uint16_t i;
+
+  TPM_RC_t rc;
+  TPM2B_DIGEST_t pcr_digest = {0};
+  TPM2B_DIGEST_t policy_digest = {0};
+  TPML_PCR_SELECTION_t pcr_sel = {
+        .count = 1,
+        .pcrSelections = {
+                {
+                .hash = 0,
+                .sizeOfSelect = 3,
+                .pcrSelect = {0}
+                },
+        }
+  };
+
+  /* Specify the bank */
+  if (pcr_bank == LUKS2_TOKEN_SYSTEMD_TPM2_PCR_BANK_TYPE_SHA256)
+    {
+      pcr_sel.pcrSelections[0].hash = TPM_ALG_SHA256;
+    }
+  else if (LUKS2_TOKEN_SYSTEMD_TPM2_PCR_BANK_TYPE_SHA1)
+    {
+      pcr_sel.pcrSelections[0].hash = TPM_ALG_SHA1;
+    }
+  else
+    {
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  /*  Convert PCR mask to PCR selection */
+  for(i = 0; i < TPM_MAX_PCRS; ++i)
+    if ((pcr_mask & (1 << i)) != 0)
+      TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], i);
+
+  /* PCR Policy */
+  rc = grub_tpm2_policypcr (session, NULL, &pcr_digest, &pcr_sel, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+        return grub_error (GRUB_ERR_BAD_DEVICE, "Failed to submit PCR policy 
(TPM2_PolicyPCR: 0x%x).", rc);
+    }
+
+  /* Retrieve Policy Digest */
+  rc = grub_tpm2_policygetdigest (session, NULL, &policy_digest, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      return grub_error (GRUB_ERR_BAD_DEVICE, "failed to get policy digest 
(TPM2_PolicyGetDigest: 0x%x).", rc);
+    }
+
+  *digest = policy_digest;
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+tpm2_luks2_acquire_luks2_key(
+                grub_uint32_t pcr_mask,
+                grub_uint16_t pcr_bank,
+                grub_uint16_t primary_alg,
+                const void *key_data,
+                grub_size_t key_data_size,
+                const void *policy_hash,
+                grub_size_t policy_hash_size,
+                const void *srk_data,
+                grub_size_t srk_data_size,
+                void **ret_decrypted_key,
+                grub_size_t *ret_decrypted_key_size) {
+
+  tpm2_sealed_key_t sk;
+  grub_srk_type_t srk_type;
+
+  grub_err_t err;
+  grub_uint8_t *key_out;
+
+  TPM_HANDLE_t parent_handle = 0;
+  TPM_HANDLE_t sealed_handle = 0;
+  TPM_HANDLE_t srk_handle = 0;
+  TPMS_AUTH_COMMAND_t authCmd = {0};
+  TPM2B_NONCE_t nonceCaller = {0};
+  TPMT_SYM_DEF_t symmetric = {0};
+  TPM2B_DIGEST_t pcr_digest = {0};
+  TPM_RC_t rc;
+  TPMI_SH_AUTH_SESSION_t session;
+  TPM2B_SENSITIVE_DATA_t data;
+  TPM_IESYS_RESOURCE_t rsrc = {0};
+
+  if (ret_decrypted_key == NULL || ret_decrypted_key_size == NULL)
+    {
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  /* Decode blob: PRIVATE + PUBLIC */
+  err = tpm2_luks2_unmarshal_raw(key_data, key_data_size, &sk);
+  if (err != GRUB_ERR_NONE)
+    {
+      return grub_error (GRUB_ERR_BAD_ARGUMENT, "Can't unmarshal blob");
+    }
+
+  parent_handle = TPM_RH_OWNER;
+
+  if (srk_data != NULL)
+    {
+      err = tpm2_luks2_unmarshal_srk(srk_data, srk_data_size, &rsrc);
+      if (err != GRUB_ERR_NONE)
+        {
+          grub_print_error ();
+          return grub_error (GRUB_ERR_BAD_ARGUMENT, "Can't load srk from 
header");
+        }
+      srk_handle = rsrc.handle;
+    }
+  else if (TPM_HT_IS_PERSISTENT (parent_handle))
+    {
+      srk_handle = parent_handle;
+    }
+  else
+    {
+      /* Use specified algo */
+      if (primary_alg == LUKS2_TOKEN_SYSTEMD_TPM2_ALGO_TYPE_ECC)
+        {
+          srk_type.noDA = false;
+          srk_type.type = TPM_ALG_ECC;
+          srk_type.detail.ecc_curve = TPM_ECC_NIST_P256;
+        }
+      else if (LUKS2_TOKEN_SYSTEMD_TPM2_ALGO_TYPE_RSA)
+        {
+          srk_type.noDA = false;
+          srk_type.type = TPM_ALG_RSA;
+          srk_type.detail.rsa_bits = 2048;
+        }
+      else
+        {
+          return GRUB_ERR_BAD_ARGUMENT;
+        }
+    }
+
+  /* Load SRK and sealed and get handles */
+  err = tpm2_protector_srk_load(srk_type, &sk, parent_handle, &sealed_handle, 
&srk_handle);
+  if (err != GRUB_ERR_NONE)
+    {
+      grub_print_error ();
+      return grub_error (GRUB_ERR_BAD_ARGUMENT, "Can't load srk");
+    }
+
+  /* Start Auth Session */
+  nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
+  symmetric.algorithm = TPM_ALG_NULL;
+  rc = grub_tpm2_startauthsession (TPM_RH_NULL, TPM_RH_NULL, NULL, 
&nonceCaller, NULL,
+                                  TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256,
+                                  &session, NULL, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to start auth session 
(TPM2_StartAuthSession: 0x%x)", rc);
+      goto exit1;
+    }
+
+  /* Create PCR digest from the given setting */
+  err = tpm2_luks2_make_pcr_session(session, pcr_mask, pcr_bank, &pcr_digest);
+  if(err != GRUB_ERR_NONE)
+    goto exit2;
+
+  /* Compare the computed digest with the joined one */
+  if (policy_hash_size > 0 && ( policy_hash_size != pcr_digest.size || 
grub_memcmp(pcr_digest.buffer, policy_hash, policy_hash_size)))
+    {
+      err = grub_error (GRUB_ERR_BAD_DEVICE, "Current policy digest does not 
match stored policy digest, cancelling TPM2 authentication attempt.");
+      goto exit2;
+    }
+
+  /* Unseal Sealed Key */
+  authCmd.sessionHandle = session;
+  rc = grub_tpm2_unseal (sealed_handle, &authCmd, &data, NULL);
+  if (rc != TPM_RC_SUCCESS)
+    {
+      err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to unseal sealed key 
(TPM2_Unseal: 0x%x)", rc);
+      goto exit2;
+    }
+
+  /* Epilogue */
+  key_out = grub_malloc (data.size);
+  if (key_out == NULL)
+    {
+      err = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("no memory left to allocate 
unlock key buffer"));
+      goto exit2;
+    }
+
+  grub_memcpy (key_out, data.buffer, data.size);
+
+  *ret_decrypted_key = key_out;
+  *ret_decrypted_key_size = data.size;
+
+  err = GRUB_ERR_NONE;
+
+ exit2:
+  grub_tpm2_flushcontext (session);
+ exit1:
+  grub_tpm2_flushcontext (sealed_handle);
+
+  if (!TPM_HT_IS_PERSISTENT (srk_handle))
+    grub_tpm2_flushcontext (srk_handle);
+
+  return err;
+}
\ No newline at end of file
diff --git a/grub-core/commands/tpm2_key_protector/tpm2_luks2.h 
b/grub-core/commands/tpm2_key_protector/tpm2_luks2.h
new file mode 100644
index 000000000..face1a594
--- /dev/null
+++ b/grub-core/commands/tpm2_key_protector/tpm2_luks2.h
@@ -0,0 +1,39 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_SYSTEMD_TPM2_LUKS2_TPM2_HEADER
+#define GRUB_SYSTEMD_TPM2_LUKS2_TPM2_HEADER 1
+
+#include <grub/types.h>
+#include <grub/err.h>
+
+extern grub_err_t
+tpm2_luks2_acquire_luks2_key(
+                grub_uint32_t pcr_mask,
+                grub_uint16_t pcr_bank,
+                grub_uint16_t primary_alg,
+                const void *key_data,
+                grub_size_t key_data_size,
+                const void *policy_hash,
+                grub_size_t policy_hash_size,
+                const void *srk_data,
+                grub_size_t srk_data_size,
+                void **ret_decrypted_key,
+                grub_size_t *ret_decrypted_key_size);
+
+#endif /* GRUB_SYSTEMD_TPM2_LUKS2_TPM2_HEADER */
-- 
2.39.5


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

Reply via email to