Re: [PATCH v18 12/25] key_protector: Add key protectors framework

2024-06-30 Thread Gary Lin via Grub-devel
On Fri, Jun 28, 2024 at 02:54:53PM +0300, Vladimir 'phcoder' Serbinenko wrote:
> > +  if (protector == NULL || protector->name == NULL || grub_strlen 
> > (protector->name) == 0)
> > +return GRUB_ERR_BAD_ARGUMENT;
> > +
> Here and in the other places you miss grub_error. Note that the
> message in such technical cases should be left untranslated (no N_
> mark).
> 
Ok. I'll add grub_error to provide the better error messages.

> > +  if (protector == NULL || grub_strlen (protector) == 0)
> 
> Rather than checking strlen just do protector[0] == '\0'

Ah, right. It's more efficient.

Gary Lin

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


[PATCH v18 22/25] cryptodisk: wipe out the cached keys from protectors

2024-06-28 Thread Gary Lin via Grub-devel
An attacker may insert a malicious disk with the same crypto UUID and
trick grub2 to mount the fake root. Even though the key from the key
protector fails to unlock the fake root, it's not wiped out cleanly so
the attacker could dump the memory to retrieve the secret key. To defend
such attack, wipe out the cached key when we don't need it.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/cryptodisk.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 1a994d935..de505a2ef 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1399,7 +1399,11 @@ grub_cryptodisk_clear_key_cache (struct 
grub_cryptomount_args *cargs)
 return;
 
   for (i = 0; cargs->protectors[i]; i++)
-grub_free (cargs->key_cache[i].key);
+{
+  if (cargs->key_cache[i].key)
+   grub_memset (cargs->key_cache[i].key, 0, cargs->key_cache[i].key_len);
+  grub_free (cargs->key_cache[i].key);
+}
 
   grub_free (cargs->key_cache);
 }
-- 
2.35.3


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


[PATCH v18 23/25] diskfilter: look up cryptodisk devices first

2024-06-28 Thread Gary Lin via Grub-devel
When using disk auto-unlocking with TPM 2.0, the typical grub.cfg may
look like this:

  tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
  cryptomount -u  -P tpm2
  search --fs-uuid --set=root 

Since the disk search order is based on the order of module loading, the
attacker could insert a malicious disk with the same FS-UUID root to
trick grub2 to boot into the malicious root and further dump memory to
steal the unsealed key.

Do defend against such an attack, we can specify the hint provided by
'grub-probe' to search the encrypted partition first:

search --fs-uuid --set=root --hint='cryptouuid/' 

However, for LVM on an encrypted partition, the search hint provided by
'grub-probe' is:

  --hint='lvmid//'

It doesn't guarantee to look up the logical volume from the encrypted
partition, so the attacker may have the chance to fool grub2 to boot
into the malicious disk.

To minimize the attack surface, this commit tweaks the disk device search
in diskfilter to look up cryptodisk devices first and then others, so
that the auto-unlocked disk will be found first, not the attacker's disk.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/diskfilter.c | 35 ++-
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c
index 21e239511..df1992305 100644
--- a/grub-core/disk/diskfilter.c
+++ b/grub-core/disk/diskfilter.c
@@ -226,15 +226,32 @@ scan_devices (const char *arname)
   int need_rescan;
 
   for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++)
-for (p = grub_disk_dev_list; p; p = p->next)
-  if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
- && p->disk_iterate)
-   {
- if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
-   return;
- if (arname && is_lv_readable (find_lv (arname), 1))
-   return;
-   }
+{
+  /* look up the crytodisk devices first */
+  for (p = grub_disk_dev_list; p; p = p->next)
+   if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID
+   && p->disk_iterate)
+ {
+   if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
+ return;
+   if (arname && is_lv_readable (find_lv (arname), 1))
+ return;
+   break;
+ }
+
+  /* check the devices other than crytodisk */
+  for (p = grub_disk_dev_list; p; p = p->next)
+   if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ continue;
+   else if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
+   && p->disk_iterate)
+ {
+   if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
+ return;
+   if (arname && is_lv_readable (find_lv (arname), 1))
+ return;
+ }
+}
 
   scan_depth = 0;
   need_rescan = 1;
-- 
2.35.3


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


[PATCH v18 20/25] tpm2_key_protector: Implement NV index

2024-06-28 Thread Gary Lin via Grub-devel
From: Patrick Colp 

Currently with the TPM2 protector, only SRK mode is supported and
NV index support is just a stub. Implement the NV index option.

Note: This only extends support on the unseal path. grub2_protect
has not been updated. tpm2-tools can be used to insert a key into
the NV index.

An example of inserting a key using tpm2-tools:

  # Get random key.
  tpm2_getrandom 32 > key.dat

  # Create primary object.
  tpm2_createprimary -C o -g sha256 -G ecc -c primary.ctx

  # Create policy object. `pcrs.dat` contains the PCR values to seal against.
  tpm2_startauthsession -S session.dat
  tpm2_policypcr -S session.dat -l sha256:7,11 -f pcrs.dat -L policy.dat
  tpm2_flushcontext session.dat

  # Seal key into TPM.
  cat key.dat | tpm2_create -C primary.ctx -u key.pub -r key.priv -L policy.dat 
-i-
  tpm2_load -C primary.ctx -u key.pub -r key.priv -n sealing.name -c sealing.ctx
  tpm2_evictcontrol -C o -c sealing.ctx 0x8100

Then to unseal the key in grub, add this to grub.cfg:

  tpm2_key_protector_init --mode=nv --nvindex=0x8100 --pcrs=7,11
  cryptomount -u  --protector tpm2

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 .../commands/tpm2_key_protector/module.c  | 27 ---
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/grub-core/commands/tpm2_key_protector/module.c 
b/grub-core/commands/tpm2_key_protector/module.c
index a98109c43..ae412e6f4 100644
--- a/grub-core/commands/tpm2_key_protector/module.c
+++ b/grub-core/commands/tpm2_key_protector/module.c
@@ -981,11 +981,30 @@ grub_tpm2_protector_srk_recover (const struct 
grub_tpm2_protector_context *ctx,
 }
 
 static grub_err_t
-grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx 
__attribute__ ((unused)),
-   grub_uint8_t **key __attribute__ ((unused)),
-   grub_size_t *key_size __attribute__ ((unused)))
+grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx,
+   grub_uint8_t **key, grub_size_t *key_size)
 {
-  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("NV Index mode is not 
implemented yet"));
+  TPM_HANDLE sealed_handle = ctx->nv;
+  tpm2key_policy_t policy_seq = NULL;
+  grub_err_t err;
+
+  /* Create a basic policy sequence based on the given PCR selection */
+  err = grub_tpm2_protector_simple_policy_seq (ctx, _seq);
+  if (err != GRUB_ERR_NONE)
+goto exit;
+
+  err = grub_tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size);
+
+  /* Pop error messages on success */
+  if (err == GRUB_ERR_NONE)
+while (grub_error_pop ());
+
+ exit:
+  TPM2_FlushContext (sealed_handle);
+
+  grub_tpm2key_free_policy_seq (policy_seq);
+
+  return err;
 }
 
 static grub_err_t
-- 
2.35.3


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


[PATCH v18 21/25] cryptodisk: Fallback to passphrase

2024-06-28 Thread Gary Lin via Grub-devel
From: Patrick Colp 

If a protector is specified, but it fails to unlock the disk, fall back
to asking for the passphrase. However, an error was set indicating that
the protector(s) failed. Later code (e.g., LUKS code) fails as
`grub_errno` is now set. Print the existing errors out first, before
proceeding with the passphrase.

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/cryptodisk.c | 17 -
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 6f7394942..1a994d935 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1167,6 +1167,10 @@ grub_cryptodisk_scan_device_real (const char *name,
  ret = cr->recover_key (source, dev, cargs);
  if (ret != GRUB_ERR_NONE)
{
+ /* Reset key data to trigger the passphrase prompt later */
+ cargs->key_data = NULL;
+ cargs->key_len = 0;
+
  part = grub_partition_get_name (source->partition);
  grub_dprintf ("cryptodisk",
"recovered a key from key protector %s but it "
@@ -1192,7 +1196,6 @@ grub_cryptodisk_scan_device_real (const char *name,
  source->name, source->partition != NULL ? "," : "",
  part != NULL ? part : N_("UNKNOWN"), dev->uuid);
   grub_free (part);
-  goto error;
 }
 
   if (cargs->key_len)
@@ -1207,6 +1210,18 @@ grub_cryptodisk_scan_device_real (const char *name,
   unsigned long tries = 3;
   const char *tries_env;
 
+  /*
+   * Print the error from key protectors and clear grub_errno.
+   * Since '--protector' doesn't not coexist with '--password' and
+   * '--key-file', only "cargs->key_len == 0" is expected if all
+   * key protectors fail.
+   */
+  if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
   askpass = 1;
   cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
   if (cargs->key_data == NULL)
-- 
2.35.3


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


[PATCH v18 25/25] tests: Add tpm2_key_protector_test

2024-06-28 Thread Gary Lin via Grub-devel
For the tpm2_key_protector module, the TCG2 command submission function
is the only difference between a QEMU instance and grub-emu. To test
TPM2 key unsealing with a QEMU instance, it requires an extra OS image
to invoke grub-protect to seal the LUKS key, rather than a simple
grub-shell rescue CD image. On the other hand, grub-emu can share the
emulated TPM2 device with the host, so that we can seal the LUKS key on
host and test key unsealing with grub-emu.

This test script firstly creates a simple LUKS image to be loaded as a
loopback device in grub-emu. Then an emulated TPM2 device is created by
"swtpm chardev" and PCR 0 and 1 are extended.

There are several test cases in the script to test various settings. Each
test case uses grub-protect or tpm2-tools to seal the LUKS password
with PCR 0 and PCR 1. Then grub-emu is launched to load the LUKS image,
try to mount the image with tpm2_key_protector_init and cryptomount, and
verify the result.

Based on the idea from Michael Chang.

Cc: Michael Chang 
Cc: Stefan Berger 
Cc: Glenn Washburn 
Signed-off-by: Gary Lin 
---
 Makefile.util.def|   6 +
 tests/tpm2_key_protector_test.in | 389 +++
 tests/util/grub-shell.in |   6 +-
 3 files changed, 400 insertions(+), 1 deletion(-)
 create mode 100644 tests/tpm2_key_protector_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index 074c0aff7..038253b37 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1290,6 +1290,12 @@ script = {
   common = tests/asn1_test.in;
 };
 
+script = {
+  testcase = native;
+  name = tpm2_key_protector_test;
+  common = tests/tpm2_key_protector_test.in;
+};
+
 program = {
   testcase = native;
   name = example_unit_test;
diff --git a/tests/tpm2_key_protector_test.in b/tests/tpm2_key_protector_test.in
new file mode 100644
index 0..a92e5f498
--- /dev/null
+++ b/tests/tpm2_key_protector_test.in
@@ -0,0 +1,389 @@
+#! @BUILD_SHEBANG@ -e
+
+# Test GRUBs ability to unseal a LUKS key with TPM 2.0
+# 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 .
+
+grubshell=@builddir@/grub-shell
+
+. "@builddir@/grub-core/modinfo.sh"
+
+if [ x${grub_modinfo_platform} != xemu ]; then
+  exit 77
+fi
+
+builddir="@builddir@"
+
+# Force build directory components
+PATH="${builddir}:${PATH}"
+export PATH
+
+if [ "x${EUID}" = "x" ] ; then
+  EUID=`id -u`
+fi
+
+if [ "${EUID}" != 0 ] ; then
+   echo "not root; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v cryptsetup >/dev/null 2>&1; then
+   echo "cryptsetup not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; then
+   echo "no tpm_vtpm_proxy support; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v swtpm >/dev/null 2>&1; then
+   echo "swtpm not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v tpm2_startup >/dev/null 2>&1; then
+   echo "tpm2-tools not installed; cannot test tpm2."
+   exit 99
+fi
+
+tpm2testdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XX"`" || 
exit 99
+
+disksize=20M
+
+luksfile=${tpm2testdir}/luks.disk
+lukskeyfile=${tpm2testdir}/password.txt
+
+# Choose a low iteration number to reduce the time to decrypt the disk
+csopt="--type luks2 --pbkdf pbkdf2 --iter-time 1000"
+
+tpm2statedir=${tpm2testdir}/tpm
+tpm2ctrl=${tpm2statedir}/ctrl
+tpm2log=${tpm2statedir}/logfile
+
+sealedkey=${tpm2testdir}/sealed.tpm
+
+timeout=20
+
+testoutput=${tpm2testdir}/testoutput
+
+vtext="TEST VERIFIED"
+
+ret=0
+
+# Create the password file
+echo -n "top secret" > "${lukskeyfile}"
+
+# Setup LUKS2 image
+truncate -s ${disksize} "${luksfile}" || exit 99
+cryptsetup luksFormat -q ${csopt} "${luksfile}" "${lukskeyfile}" || exit 99
+
+# Write vtext into the first block of the LUKS2 image
+luksdev=/dev/mapper/`basename "${tpm2testdir}"`
+cryptsetup open --key-file "${lukskeyfile}" "${luksfile}" `basename 
"${luksdev}"` || exit 99
+echo "${vtext}" > "${luksdev}"
+cryptsetup close "${luksdev}"
+
+# Shutdown the swtpm instance on exit
+cleanup() {
+RET=$?
+if [ -e "${tpm2ctrl}" ]; then
+   swtpm_ioctl -s --unix "${tpm2ctrl}"
+fi
+if [ "${RET}" -eq 0 ]; then
+   rm -rf "$tpm2testdir" || :
+fi
+}
+trap cleanup EXIT INT TERM KILL QUIT
+
+mkdir -p "${tpm2statedir}"
+
+# Create the swtpm chardev instance
+swtpm chardev --vtpm-proxy --tpmstate 

[PATCH v18 17/25] cryptodisk: Support key protectors

2024-06-28 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

Add a new parameter to cryptomount to support the key protectors framework: -P.
The parameter is used to automatically retrieve a key from specified key
protectors. The parameter may be repeated to specify any number of key
protectors. These are tried in order until one provides a usable key for any
given disk.

Signed-off-by: Hernan Gatta 
Signed-off-by: Michael Chang 
Signed-off-by: Gary Lin 
Reviewed-by: Glenn Washburn 
Reviewed-by: Stefan Berger 
---
 Makefile.util.def   |   1 +
 grub-core/disk/cryptodisk.c | 243 ++--
 include/grub/cryptodisk.h   |  16 +++
 3 files changed, 196 insertions(+), 64 deletions(-)

diff --git a/Makefile.util.def b/Makefile.util.def
index fe70cf9bd..fb82f59a0 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -40,6 +40,7 @@ library = {
   common = grub-core/disk/luks.c;
   common = grub-core/disk/luks2.c;
   common = grub-core/disk/geli.c;
+  common = grub-core/disk/key_protector.c;
   common = grub-core/disk/cryptodisk.c;
   common = grub-core/disk/AFSplitter.c;
   common = grub-core/lib/pbkdf2.c;
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 5e1eb2743..6f7394942 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #ifdef GRUB_UTIL
 #include 
@@ -45,7 +46,8 @@ enum
 OPTION_KEYFILE,
 OPTION_KEYFILE_OFFSET,
 OPTION_KEYFILE_SIZE,
-OPTION_HEADER
+OPTION_HEADER,
+OPTION_PROTECTOR
   };
 
 static const struct grub_arg_option options[] =
@@ -59,6 +61,8 @@ static const struct grub_arg_option options[] =
 {"keyfile-offset", 'O', 0, N_("Key file offset (bytes)"), 0, ARG_TYPE_INT},
 {"keyfile-size", 'S', 0, N_("Key file data size (bytes)"), 0, 
ARG_TYPE_INT},
 {"header", 'H', 0, N_("Read header from file"), 0, ARG_TYPE_STRING},
+{"protector", 'P', GRUB_ARG_OPTION_REPEATABLE,
+ N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING},
 {0, 0, 0, 0, 0, 0}
   };
 
@@ -1062,6 +1066,7 @@ grub_cryptodisk_scan_device_real (const char *name,
   grub_err_t ret = GRUB_ERR_NONE;
   grub_cryptodisk_t dev;
   grub_cryptodisk_dev_t cr;
+  int i;
   struct cryptodisk_read_hook_ctx read_hook_data = {0};
   int askpass = 0;
   char *part = NULL;
@@ -1114,79 +1119,151 @@ grub_cryptodisk_scan_device_real (const char *name,
   goto error_no_close;
 if (!dev)
   continue;
+break;
+  }
 
-if (cargs->key_len)
-  {
-   ret = cr->recover_key (source, dev, cargs);
-   if (ret != GRUB_ERR_NONE)
- goto error;
-  }
-else
-  {
-   /* Get the passphrase from the user, if no key data. */
-   unsigned long tries = 3;
-   const char *tries_env;
+  if (dev == NULL)
+{
+  grub_error (GRUB_ERR_BAD_MODULE,
+ "no cryptodisk module can handle this device");
+  goto error_no_close;
+}
 
-   askpass = 1;
-   cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
-   if (cargs->key_data == NULL)
- goto error_no_close;
+  if (cargs->protectors)
+{
+  for (i = 0; cargs->protectors[i]; i++)
+   {
+ if (cargs->key_cache[i].invalid)
+   continue;
 
-   tries_env = grub_env_get ("cryptodisk_passphrase_tries");
-   if (tries_env != NULL && tries_env[0] != '\0')
- {
-   unsigned long tries_env_val;
-   const char *p;
+ if (cargs->key_cache[i].key == NULL)
+   {
+ ret = grub_key_protector_recover_key (cargs->protectors[i],
+   >key_cache[i].key,
+   
>key_cache[i].key_len);
+ if (ret != GRUB_ERR_NONE)
+   {
+ if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
+ grub_dprintf ("cryptodisk",
+   "failed to recover a key from key protector "
+   "%s, will not try it again for any other "
+   "disks, if any, during this invocation of "
+   "cryptomount\n",
+   cargs->protectors[i]);
+
+ cargs->key_cache[i].invalid = 1;
+ continue;
+   }
+   }
 
-   tries_env_val = grub_strtoul (tries_env, , 0);
-   if (*p == '\0' && tries_env_val != ~0UL)
- tries = tries_env_val;
-   else
- grub_printf_ (N_("Invalid cryptodisk_passphrase_tries value `%s'. 
Defaulting to %lu.\n"),
-   tries_env,
-   tries);
- }
+ cargs->key_data = cargs->key_cache[i].key;
+ cargs->key_len = cargs->key_cache[i].key_len;
 
-   for (; tries > 0; tries--)
- {
- 

[PATCH v18 24/25] tpm2_key_protector: Add grub-emu support

2024-06-28 Thread Gary Lin via Grub-devel
As a preparation to test tpm2_key_protector with grub-emu, the new
option, --tpm-device, is introduced to specify the TPM device for
grub-emu so that grub-emu can share the emulated TPM device with
the host.

Since grub-emu can directly access the device node on host, it's easy to
implement the essential TCG2 command submission function with the
read/write functions and enable tpm2_key_protector module for grub-emu,
so that we can further test TPM2 key unsealing with grub-emu.

Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/Makefile.core.def   |  3 ++
 grub-core/kern/emu/main.c | 11 ++-
 grub-core/kern/emu/misc.c | 51 +
 grub-core/lib/tss2/tcg2_emu.c | 54 +++
 include/grub/emu/misc.h   |  5 
 5 files changed, 123 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/lib/tss2/tcg2_emu.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index d54c12574..df07c0f0e 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2574,7 +2574,9 @@ module = {
   common = lib/tss2/tpm2_cmd.c;
   common = lib/tss2/tss2.c;
   efi = lib/efi/tcg2.c;
+  emu = lib/tss2/tcg2_emu.c;
   enable = efi;
+  enable = emu;
   cppflags = '-I$(srcdir)/lib/tss2';
 };
 
@@ -2586,6 +2588,7 @@ module = {
   common = commands/tpm2_key_protector/tpm2key_asn1_tab.c;
   /* The plaform support of tpm2_key_protector depends on the tcg2 
implementation in tss2. */
   enable = efi;
+  enable = emu;
   cppflags = '-I$(srcdir)/lib/tss2 -I$(srcdir)/lib/libtasn1-grub';
 };
 
diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
index 855b11c3d..c10838613 100644
--- a/grub-core/kern/emu/main.c
+++ b/grub-core/kern/emu/main.c
@@ -55,7 +55,7 @@
 static jmp_buf main_env;
 
 /* Store the prefix specified by an argument.  */
-static char *root_dev = NULL, *dir = NULL;
+static char *root_dev = NULL, *dir = NULL, *tpm_dev = NULL;
 
 grub_addr_t grub_modbase = 0;
 
@@ -108,6 +108,7 @@ static struct argp_option options[] = {
   {"verbose", 'v', 0,  0, N_("print verbose messages."), 0},
   {"hold", 'H', N_("SECS"),  OPTION_ARG_OPTIONAL, N_("wait until a 
debugger will attach"), 0},
   {"kexec",   'X', 0,  0, N_("use kexec to boot Linux kernels via 
systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0},
+  {"tpm-device",  't', N_("DEV"), 0, N_("Set TPM device."), 0},
   { 0, 0, 0, 0, 0, 0 }
 };
 
@@ -168,6 +169,10 @@ argp_parser (int key, char *arg, struct argp_state *state)
 case 'X':
   grub_util_set_kexecute ();
   break;
+case 't':
+  free (tpm_dev);
+  tpm_dev = xstrdup (arg);
+  break;
 
 case ARGP_KEY_ARG:
   {
@@ -276,6 +281,9 @@ main (int argc, char *argv[])
 
   dir = xstrdup (dir);
 
+  if (tpm_dev)
+grub_util_tpm_open (tpm_dev);
+
   /* Start GRUB!  */
   if (setjmp (main_env) == 0)
 grub_main ();
@@ -283,6 +291,7 @@ main (int argc, char *argv[])
   grub_fini_all ();
   grub_hostfs_fini ();
   grub_host_fini ();
+  grub_util_tpm_close ();
 
   grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
 
diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
index 521220b49..1db24fde7 100644
--- a/grub-core/kern/emu/misc.c
+++ b/grub-core/kern/emu/misc.c
@@ -28,6 +28,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include 
 #include 
@@ -41,6 +43,8 @@
 int verbosity;
 int kexecute;
 
+static int grub_util_tpm_fd = -1;
+
 void
 grub_util_warn (const char *fmt, ...)
 {
@@ -230,3 +234,50 @@ grub_util_get_kexecute (void)
 {
   return kexecute;
 }
+
+grub_err_t
+grub_util_tpm_open (const char *tpm_dev)
+{
+  if (grub_util_tpm_fd != -1)
+return GRUB_ERR_NONE;
+
+  grub_util_tpm_fd = open (tpm_dev, O_RDWR);
+  if (grub_util_tpm_fd == -1)
+grub_util_error (_("cannot open TPM device '%s': %s"), tpm_dev, strerror 
(errno));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_util_tpm_close (void)
+{
+  int err;
+
+  if (grub_util_tpm_fd == -1)
+return GRUB_ERR_NONE;
+
+  err = close (grub_util_tpm_fd);
+  if (err != GRUB_ERR_NONE)
+grub_util_error (_("cannot close TPM device: %s"), strerror (errno));
+
+  grub_util_tpm_fd = -1;
+  return GRUB_ERR_NONE;
+}
+
+grub_size_t
+grub_util_tpm_read (void *output, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return read (grub_util_tpm_fd, output, size);
+}
+
+grub_size_t
+grub_util_tpm_write (const void *input, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return write (grub_util_tpm_fd, input, size);
+}
diff --git a/grub-core/lib/tss2/tcg2_emu.c b/grub-core/lib/tss2/tcg2_emu.c
new file mode 100644
index 0..e18a5806c
--- /dev/null
+++ b/grub-core/lib/tss2/tcg2_emu.c
@@ -0,0 +1,54 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *  Copyright (C) 2024 SUSE LLC
+ *
+ *  GRUB is free software: you can redistribute it and/or 

[PATCH v18 10/25] asn1_test: test module for libtasn1

2024-06-28 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

Import tests from libtasn1 that don't use functionality we don't
import. This test module is integrated into functional_test so that the
user can run the test in grub shell.

This doesn't test the full decoder but that will be exercised in
test suites for coming patch sets.

Add testcase target in accordance with
5e10be48e5 tests: Add check-native and check-nonnative make targets

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 Makefile.util.def |  6 
 autogen.sh| 10 +-
 grub-core/Makefile.core.def   | 15 
 grub-core/tests/asn1/asn1_test.c  | 49 +++
 grub-core/tests/asn1/asn1_test.h  | 44 
 grub-core/tests/lib/functional_test.c |  1 +
 tests/asn1_test.in| 11 ++
 7 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/tests/asn1/asn1_test.c
 create mode 100644 grub-core/tests/asn1/asn1_test.h
 create mode 100644 tests/asn1_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index 0f74a1680..fe70cf9bd 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1257,6 +1257,12 @@ script = {
   common = tests/luks2_test.in;
 };
 
+script = {
+  testcase = native;
+  name = asn1_test;
+  common = tests/asn1_test.in;
+};
+
 program = {
   testcase = native;
   name = example_unit_test;
diff --git a/autogen.sh b/autogen.sh
index 3cccd76c0..e464bac5e 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -60,12 +60,20 @@ mkdir -p grub-core/lib/libtasn1-grub/lib
 cp grub-core/lib/libtasn1/lib/*.[ch] grub-core/lib/libtasn1-grub/lib
 cp grub-core/lib/libtasn1/libtasn1.h grub-core/lib/libtasn1-grub/
 
+if [ -d grub-core/tests/asn1/tests ]; then
+  rm -rf grub-core/tests/asn1/tests
+fi
+
+mkdir grub-core/tests/asn1/tests
+cp grub-core/lib/libtasn1/tests/*.[ch] grub-core/tests/asn1/tests
+
 for patch in \
0001-libtasn1-disable-code-not-needed-in-grub.patch \
0002-libtasn1-use-bound-checked-_asn1_str_cat.patch \
0003-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch \
0004-libtasn1-Use-grub_divmod64-for-division.patch \
-   0005-libtasn1-fix-the-potential-buffer-overrun.patch ; do
+   0005-libtasn1-fix-the-potential-buffer-overrun.patch \
+   0006-asn1_test-changes-for-grub-compatibility.patch ; do
   patch -p1 -i grub-core/lib/libtasn1-patches/$patch
 done
 
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 01a05c9b3..fd6dfa515 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2621,3 +2621,18 @@ module = {
   /* -Wno-type-limits comes from configure.ac of libtasn1 */
   cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) 
-I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/lib/libtasn1-grub/lib 
-Wno-type-limits';
 };
+
+module = {
+  name = asn1_test;
+  common = tests/asn1/tests/CVE-2018-1000654.c;
+  common = tests/asn1/tests/object-id-decoding.c;
+  common = tests/asn1/tests/object-id-encoding.c;
+  common = tests/asn1/tests/octet-string.c;
+  common = tests/asn1/tests/reproducers.c;
+  common = tests/asn1/tests/Test_overflow.c;
+  common = tests/asn1/tests/Test_simple.c;
+  common = tests/asn1/tests/Test_strings.c;
+  common = tests/asn1/asn1_test.c;
+  cflags = '-Wno-uninitialized';
+  cppflags = '-I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/tests/asn1/';
+};
diff --git a/grub-core/tests/asn1/asn1_test.c b/grub-core/tests/asn1/asn1_test.c
new file mode 100644
index 0..2a2cd02d1
--- /dev/null
+++ b/grub-core/tests/asn1/asn1_test.c
@@ -0,0 +1,49 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020 IBM 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 .
+ */
+
+#include 
+#include 
+#include "asn1_test.h"
+
+/*
+ * libtasn1 tests - from which this is derived - are provided under GPL3+.
+ */
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static void
+asn1_test (void)
+{
+  grub_test_assert (test_CVE_2018_1000654 () == 0, "CVE-2018-1000654 test 
failed");
+
+  grub_test_assert (test_object_id_encoding () == 0, "ASN.1 object ID encoding 
test failed");
+
+  grub_test_assert (test_object_id_decoding () == 0, "ASN.1 object ID decoding 
test failed");
+
+  grub_test_assert (test_octet_string () == 0, "ASN.1 octet string test 
failed");
+
+  grub_test_assert (test_overflow () == 0, "ASN.1 overflow test 

[PATCH v18 06/25] libtasn1: Use grub_divmod64() for division

2024-06-28 Thread Gary Lin via Grub-devel
Replace a 64 bit division with a call to grub_divmod64, preventing
creation of __udivdi3 calls on 32 bit platforms.

Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 ...tasn1-Use-grub_divmod64-for-division.patch | 30 +++
 1 file changed, 30 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0004-libtasn1-Use-grub_divmod64-for-division.patch

diff --git 
a/grub-core/lib/libtasn1-patches/0004-libtasn1-Use-grub_divmod64-for-division.patch
 
b/grub-core/lib/libtasn1-patches/0004-libtasn1-Use-grub_divmod64-for-division.patch
new file mode 100644
index 0..a8f41cd74
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0004-libtasn1-Use-grub_divmod64-for-division.patch
@@ -0,0 +1,30 @@
+From 52ad4fb6a023f27fdcea490d8e4956ce3957b1f9 Mon Sep 17 00:00:00 2001
+From: Gary Lin 
+Date: Tue, 25 Jun 2024 16:32:50 +0800
+Subject: [PATCH 4/6] libtasn1: Use grub_divmod64() for division
+
+Replace a 64 bit division with a call to grub_divmod64, preventing
+creation of __udivdi3 calls on 32 bit platforms.
+
+Signed-off-by: Daniel Axtens 
+Signed-off-by: Gary Lin 
+---
+ grub-core/lib/libtasn1-grub/lib/parser_aux.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/lib/libtasn1-grub/lib/parser_aux.c 
b/grub-core/lib/libtasn1-grub/lib/parser_aux.c
+index c05bd2339..e4e4c0556 100644
+--- a/grub-core/lib/libtasn1-grub/lib/parser_aux.c
 b/grub-core/lib/libtasn1-grub/lib/parser_aux.c
+@@ -632,7 +632,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
+   count = 0;
+   do
+ {
+-  d = val / 10;
++  d = grub_divmod64(val, 10, NULL);
+   r = val - d * 10;
+   temp[start + count] = '0' + (char) r;
+   count++;
+-- 
+2.35.3
+
-- 
2.35.3


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


[PATCH v18 07/25] libtasn1: fix the potential buffer overrun

2024-06-28 Thread Gary Lin via Grub-devel
In _asn1_tag_der(), the first while loop for the long form may end up
with a 'k' value with 'ASN1_MAX_TAG_SIZE' and cause the buffer overrun
in the second while loop. This commit tweaks the conditional check to
avoid producing a too large 'k'.

This is a quick fix and may differ from the official upstream fix.

libtasn1 issue: https://gitlab.com/gnutls/libtasn1/-/issues/49

Signed-off-by: Gary Lin 
Reviewed-by: Daniel Kiper 
---
 ...sn1-fix-the-potential-buffer-overrun.patch | 35 +++
 1 file changed, 35 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0005-libtasn1-fix-the-potential-buffer-overrun.patch

diff --git 
a/grub-core/lib/libtasn1-patches/0005-libtasn1-fix-the-potential-buffer-overrun.patch
 
b/grub-core/lib/libtasn1-patches/0005-libtasn1-fix-the-potential-buffer-overrun.patch
new file mode 100644
index 0..8cca86fad
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0005-libtasn1-fix-the-potential-buffer-overrun.patch
@@ -0,0 +1,35 @@
+From 38cc5e33cf89ed5d3152923fbedd9869bf566bb5 Mon Sep 17 00:00:00 2001
+From: Gary Lin 
+Date: Mon, 8 Apr 2024 14:57:21 +0800
+Subject: [PATCH 5/6] libtasn1: fix the potential buffer overrun
+
+In _asn1_tag_der(), the first while loop for the long form may end up
+with a 'k' value with 'ASN1_MAX_TAG_SIZE' and cause the buffer overrun
+in the second while loop. This commit tweaks the conditional check to
+avoid producing a too large 'k'.
+
+This is a quick fix and may differ from the official upstream fix.
+
+libtasn1 issue: https://gitlab.com/gnutls/libtasn1/-/issues/49
+
+Signed-off-by: Gary Lin 
+---
+ grub-core/lib/libtasn1-grub/lib/coding.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/lib/libtasn1-grub/lib/coding.c 
b/grub-core/lib/libtasn1-grub/lib/coding.c
+index 5d03bca9d..0458829a5 100644
+--- a/grub-core/lib/libtasn1-grub/lib/coding.c
 b/grub-core/lib/libtasn1-grub/lib/coding.c
+@@ -143,7 +143,7 @@ _asn1_tag_der (unsigned char class, unsigned int tag_value,
+ temp[k++] = tag_value & 0x7F;
+ tag_value >>= 7;
+ 
+-if (k > ASN1_MAX_TAG_SIZE - 1)
++if (k >= ASN1_MAX_TAG_SIZE - 1)
+   break;  /* will not encode larger tags */
+   }
+   *ans_len = k + 1;
+-- 
+2.35.3
+
-- 
2.35.3


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


[PATCH v18 15/25] tss2: Add TPM2 Software Stack (TSS2) support

2024-06-28 Thread Gary Lin via Grub-devel
A Trusted Platform Module (TPM) Software Stack (TSS) provides logic to
compose and submit TPM commands and parse reponses.

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,
second, of the TPM wire protocol itself.

As such, this patch includes 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,
TPM2_LoadExternal, TPM2_Hash, TPM2_VerifySignature,
TPM2_PolicyAuthorize, TPM2_TestParms

Cc: Stefan Berger 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/Makefile.core.def   |   11 +
 grub-core/lib/efi/tcg2.c  |  144 +
 grub-core/lib/tss2/tcg2.h |   35 ++
 grub-core/lib/tss2/tpm2_cmd.c | 1054 +
 grub-core/lib/tss2/tpm2_cmd.h |  157 +
 grub-core/lib/tss2/tss2.c |   21 +
 6 files changed, 1422 insertions(+)
 create mode 100644 grub-core/lib/efi/tcg2.c
 create mode 100644 grub-core/lib/tss2/tcg2.h
 create mode 100644 grub-core/lib/tss2/tpm2_cmd.c
 create mode 100644 grub-core/lib/tss2/tpm2_cmd.h
 create mode 100644 grub-core/lib/tss2/tss2.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 5ba9c8548..b1865d3e1 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2567,6 +2567,17 @@ module = {
   enable = efi;
 };
 
+module = {
+  name = tss2;
+  common = lib/tss2/buffer.c;
+  common = lib/tss2/tss2_mu.c;
+  common = lib/tss2/tpm2_cmd.c;
+  common = lib/tss2/tss2.c;
+  efi = lib/efi/tcg2.c;
+  enable = efi;
+  cppflags = '-I$(srcdir)/lib/tss2';
+};
+
 module = {
   name = tr;
   common = commands/tr.c;
diff --git a/grub-core/lib/efi/tcg2.c b/grub-core/lib/efi/tcg2.c
new file mode 100644
index 0..b15546f62
--- /dev/null
+++ b/grub-core/lib/efi/tcg2.c
@@ -0,0 +1,144 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *  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 .
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+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 = protocol->get_capability (protocol, );
+  if (status != GRUB_EFI_SUCCESS || !caps.TPMPresentFlag)
+return GRUB_ERR_FILE_NOT_FOUND;
+
+  has_caps = 1;
+
+ exit:
+  if (tpm2 != NULL)
+*tpm2 = caps.TPMPresentFlag;
+  if (max_output_size != NULL)
+*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_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 != NULL)
+{
+  *protocol = tpm2_protocol;
+  return GRUB_ERR_NONE;
+}
+
+  handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, _guid, NULL,
+   _handles);
+  if (handles == NULL || num_handles == 0)
+return err;
+
+  tpm2_handle = handles[0];
+
+  tpm2_protocol = grub_efi_open_protocol (tpm2_handle, _guid,
+ 

[PATCH v18 08/25] asn1_test: changes for grub compatibility

2024-06-28 Thread Gary Lin via Grub-devel
Do a few things to make asn1 tests compile as part of grub:

- include asn1_test.h only

- rename the main functions to the test names

- remove 'verbose' and the unnecessary printf()

- print the error messages with grub_printf()

- return either 0 or 1 to reflect the results of the tests

- replace the system functions with grub functions, ex: memcmp() ->
  grub_memcmp()

Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 ..._test-changes-for-grub-compatibility.patch | 844 ++
 1 file changed, 844 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0006-asn1_test-changes-for-grub-compatibility.patch

diff --git 
a/grub-core/lib/libtasn1-patches/0006-asn1_test-changes-for-grub-compatibility.patch
 
b/grub-core/lib/libtasn1-patches/0006-asn1_test-changes-for-grub-compatibility.patch
new file mode 100644
index 0..63631f9ff
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0006-asn1_test-changes-for-grub-compatibility.patch
@@ -0,0 +1,844 @@
+From 650259866464a0f54df0c18fbcb9068d6536cfc3 Mon Sep 17 00:00:00 2001
+From: Gary Lin 
+Date: Tue, 18 Jun 2024 14:33:01 +0800
+Subject: [PATCH 6/6] asn1_test: changes for grub compatibility
+
+Do a few things to make asn1 tests compile as part of grub:
+
+- include asn1_test.h only
+
+- rename the main functions to the test names
+
+- remove 'verbose' and the unnecessary printf()
+
+- print the error messages with grub_printf()
+
+- return either 0 or 1 to reflect the results of the tests
+
+- replace the system functions with grub functions, ex: memcmp() ->
+  grub_memcmp()
+
+Signed-off-by: Daniel Axtens 
+Signed-off-by: Gary Lin 
+---
+ grub-core/tests/asn1/tests/CVE-2018-1000654.c | 32 +++-
+ grub-core/tests/asn1/tests/Test_overflow.c| 71 +-
+ grub-core/tests/asn1/tests/Test_simple.c  | 51 ++---
+ grub-core/tests/asn1/tests/Test_strings.c | 40 --
+ .../tests/asn1/tests/object-id-decoding.c | 28 +++
+ .../tests/asn1/tests/object-id-encoding.c | 42 ---
+ grub-core/tests/asn1/tests/octet-string.c | 75 ++-
+ grub-core/tests/asn1/tests/reproducers.c  | 32 +++-
+ 8 files changed, 126 insertions(+), 245 deletions(-)
+
+diff --git a/grub-core/tests/asn1/tests/CVE-2018-1000654.c 
b/grub-core/tests/asn1/tests/CVE-2018-1000654.c
+index 0c22b7012..6320af338 100644
+--- a/grub-core/tests/asn1/tests/CVE-2018-1000654.c
 b/grub-core/tests/asn1/tests/CVE-2018-1000654.c
+@@ -22,51 +22,41 @@
+ /* Description: reproducer for CVE-2018-1000654   */
+ //
+ 
+-#include 
+-#include 
+-#include 
++#include "asn1_test.h"
++
+ #include "CVE-2018-1000654-1_asn1_tab.h"
+ #include "CVE-2018-1000654-2_asn1_tab.h"
+ 
+ int
+-main (int argc, char *argv[])
++test_CVE_2018_1000654 (void)
+ {
+-  int result, verbose = 0;
++  int result;
+   asn1_node definitions = NULL;
+   char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+ 
+-  if (argc > 1)
+-verbose = 1;
+-
+-  printf ("Test 1\n");
+-
+   result =
+ asn1_array2tree (CVE_2018_1000654_1_asn1_tab, ,
+errorDescription);
+   if (result != ASN1_RECURSION)
+ {
+-  asn1_perror (result);
+-  printf ("ErrorDescription = %s\n\n", errorDescription);
+-  exit (1);
++  grub_printf ("Error: %s\nErrorDescription = %s\n\n",
++ asn1_strerror (result), errorDescription);
++  return 1;
+ }
+ 
+   asn1_delete_structure ();
+ 
+-  printf ("Test 2\n");
+-
+   result =
+ asn1_array2tree (CVE_2018_1000654_2_asn1_tab, ,
+errorDescription);
+   if (result != ASN1_RECURSION)
+ {
+-  asn1_perror (result);
+-  printf ("ErrorDescription = %s\n\n", errorDescription);
+-  exit (1);
++  grub_printf ("Error: %s\nErrorDescription = %s\n\n",
++ asn1_strerror (result), errorDescription);
++  return 1;
+ }
+ 
+   asn1_delete_structure ();
+ 
+-  if (verbose)
+-printf ("Success\n");
+-  exit (0);
++  return 0;
+ }
+diff --git a/grub-core/tests/asn1/tests/Test_overflow.c 
b/grub-core/tests/asn1/tests/Test_overflow.c
+index c61dea4bb..7367f1af1 100644
+--- a/grub-core/tests/asn1/tests/Test_overflow.c
 b/grub-core/tests/asn1/tests/Test_overflow.c
+@@ -20,22 +20,13 @@
+ 
+ /* Written by Simon Josefsson */
+ 
+-#include 
+-#include 
+-#include 
+-#include 
+-
+-#include "libtasn1.h"
++#include "asn1_test.h"
+ 
+ int
+-main (int argc, char **argv)
++test_overflow(void)
+ {
+   /* Test that values larger than long are rejected.  This has worked
+  fine with all versions of libtasn1. */
+-  int verbose = 0;
+-
+-  if (argc > 1)
+-verbose = 1;
+ 
+   {
+ unsigned char der[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
+@@ -44,23 +35,18 @@ main (int argc, char **argv)
+ 
+ l = asn1_get_length_der (der, sizeof der, );
+ 
+-if (l == -2L)
+-  {
+-  if (verbose)
+-puts ("OK: 

[PATCH v18 03/25] libtasn1: disable code not needed in grub

2024-06-28 Thread Gary Lin via Grub-devel
We don't expect to be able to write ASN.1, only read it,
so we can disable some code.

Do that with #if 0/#endif, rather than deletion. This means
that the difference between upstream and grub is smaller,
which should make updating libtasn1 easier in the future.

With these exclusions we also avoid the need for minmax.h,
which is convenient because it means we don't have to
import it from gnulib.

Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
Reviewed-by: Daniel Kiper 
---
 ...asn1-disable-code-not-needed-in-grub.patch | 319 ++
 1 file changed, 319 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch

diff --git 
a/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
 
b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
new file mode 100644
index 0..1c89227ab
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
@@ -0,0 +1,319 @@
+From 6b61d48782dc67bed72af70aa5db5b9cb1b4f1d0 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens 
+Date: Fri, 1 May 2020 17:12:23 +1000
+Subject: [PATCH 1/6] libtasn1: disable code not needed in grub
+
+We don't expect to be able to write ASN.1, only read it,
+so we can disable some code.
+
+Do that with #if 0/#endif, rather than deletion. This means
+that the difference between upstream and grub is smaller,
+which should make updating libtasn1 easier in the future.
+
+With these exclusions we also avoid the need for minmax.h,
+which is convenient because it means we don't have to
+import it from gnulib.
+
+Signed-off-by: Daniel Axtens 
+Signed-off-by: Gary Lin 
+---
+ grub-core/lib/libtasn1-grub/lib/coding.c| 12 ++--
+ grub-core/lib/libtasn1-grub/lib/decoding.c  |  2 ++
+ grub-core/lib/libtasn1-grub/lib/element.c   |  6 +++---
+ grub-core/lib/libtasn1-grub/lib/errors.c|  3 +++
+ grub-core/lib/libtasn1-grub/lib/structure.c | 10 ++
+ grub-core/lib/libtasn1-grub/libtasn1.h  | 15 +++
+ 6 files changed, 39 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/lib/libtasn1-grub/lib/coding.c 
b/grub-core/lib/libtasn1-grub/lib/coding.c
+index ea5bc370e..5d03bca9d 100644
+--- a/grub-core/lib/libtasn1-grub/lib/coding.c
 b/grub-core/lib/libtasn1-grub/lib/coding.c
+@@ -30,11 +30,11 @@
+ #include "parser_aux.h"
+ #include 
+ #include "element.h"
+-#include "minmax.h"
+ #include 
+ 
+ #define MAX_TAG_LEN 16
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_error_description_value_not_found */
+ /* Description: creates the ErrorDescription string   */
+@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node,
+   Estrcat (ErrorDescription, "' not found");
+ 
+ }
++#endif
+ 
+ /**
+  * asn1_length_der:
+@@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned 
char *str,
+   return ASN1_SUCCESS;
+ }
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_time_der  */
+ /* Description: creates the DER coding for a TIME */
+@@ -278,7 +280,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned 
char *der,
+ 
+   return ASN1_SUCCESS;
+ }
+-
++#endif
+ 
+ /*
+ void
+@@ -519,6 +521,7 @@ asn1_bit_der (const unsigned char *str, int bit_len,
+ }
+ 
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_complete_explicit_tag */
+ /* Description: add the length coding to the EXPLICIT */
+@@ -595,6 +598,7 @@ _asn1_complete_explicit_tag (asn1_node node, unsigned char 
*der,
+ 
+   return ASN1_SUCCESS;
+ }
++#endif
+ 
+ const tag_and_class_st _asn1_tags[] = {
+   [ASN1_ETYPE_GENERALSTRING] =
+@@ -647,6 +651,8 @@ const tag_and_class_st _asn1_tags[] = {
+ 
+ unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]);
+ 
++
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_insert_tag_der*/
+ /* Description: creates the DER coding of tags of one */
+@@ -1423,3 +1429,5 @@ error:
+   asn1_delete_structure ();
+   return err;
+ }
++
++#endif
+diff --git a/grub-core/lib/libtasn1-grub/lib/decoding.c 
b/grub-core/lib/libtasn1-grub/lib/decoding.c
+index b9245c486..bf9cb13ac 100644
+--- a/grub-core/lib/libtasn1-grub/lib/decoding.c
 b/grub-core/lib/libtasn1-grub/lib/decoding.c
+@@ -1620,6 +1620,7 @@ asn1_der_decoding (asn1_node * element, const void 
*ider, int ider_len,
+   return asn1_der_decoding2 (element, ider, _len, 0, errorDescription);
+ }
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**
+  * asn1_der_decoding_element:
+  * @structure: pointer to an ASN1 structure
+@@ -1650,6 +1651,7 @@ asn1_der_decoding_element (asn1_node * structure, const 
char *elementName,
+ {
+   return 

[PATCH v18 05/25] libtasn1: adjust the header paths in libtasn1.h

2024-06-28 Thread Gary Lin via Grub-devel
Use the grub headers instead of the standard POSIX headers.

Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 ...djust-the-header-paths-in-libtasn1.h.patch | 32 +++
 1 file changed, 32 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0003-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch

diff --git 
a/grub-core/lib/libtasn1-patches/0003-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch
 
b/grub-core/lib/libtasn1-patches/0003-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch
new file mode 100644
index 0..f832ab83b
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0003-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch
@@ -0,0 +1,32 @@
+From da7ac4d6dff8876fc18f006de3c72da3c93833f5 Mon Sep 17 00:00:00 2001
+From: Gary Lin 
+Date: Tue, 25 Jun 2024 16:30:40 +0800
+Subject: [PATCH 3/6] libtasn1: adjust the header paths in libtasn1.h
+
+Use the grub headers instead of the standard POSIX headers.
+
+Signed-off-by: Daniel Axtens 
+Signed-off-by: Gary Lin 
+---
+ grub-core/lib/libtasn1-grub/libtasn1.h | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/grub-core/lib/libtasn1-grub/libtasn1.h 
b/grub-core/lib/libtasn1-grub/libtasn1.h
+index 058ab27b0..7d64b6ab7 100644
+--- a/grub-core/lib/libtasn1-grub/libtasn1.h
 b/grub-core/lib/libtasn1-grub/libtasn1.h
+@@ -54,9 +54,8 @@
+ #  define __LIBTASN1_PURE__
+ # endif
+ 
+-# include 
+-# include 
+-# include/* for FILE* */
++# include 
++# include 
+ 
+ # ifdef __cplusplus
+ extern "C"
+-- 
+2.35.3
+
-- 
2.35.3


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


[PATCH v18 18/25] util/grub-protect: Add new tool

2024-06-28 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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 LUKS 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
has to ensure the cryptomount command is included in GRUB's boot script
and that it carries the requisite key protector (-P) parameter.

Sample usage:

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

To seal the key with TPM 2.0 Key File (recommended):

$ sudo grub-protect --action=add \
--protector=tpm2 \
--tpm2-pcrs=0,2,4,7,9 \
--tpm2key \
--tpm2-keyfile=luks-key \
--tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm

Or, to seal the key with the raw sealed key:

$ sudo grub-protect --action=add \
--protector=tpm2 \
--tpm2-pcrs=0,2,4,7,9 \
--tpm2-keyfile=luks-key \
--tpm2-outfile=/boot/efi/boot/grub2/sealed.key

Then, in the boot script, for TPM 2.0 Key File:

tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

Or, for the raw sealed key:

tpm2_key_protector_init --keyfile=(hd0,gpt1)/boot/grub2/sealed.key 
--pcrs=0,2,4,7,9
cryptomount -u  -P tpm2

The benefit of using TPM 2.0 Key File is that the PCR set is already
written in the key file, so there is no need to specify PCRs when
invoking tpm2_key_protector_init.

Cc: Stefan Berger 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 .gitignore|2 +
 Makefile.util.def |   26 +
 configure.ac  |   30 +
 docs/man/grub-protect.h2m |4 +
 util/grub-protect.c   | 1423 +
 5 files changed, 1485 insertions(+)
 create mode 100644 docs/man/grub-protect.h2m
 create mode 100644 util/grub-protect.c

diff --git a/.gitignore b/.gitignore
index 4c1f91db8..2105d87c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -169,6 +169,8 @@ widthspec.bin
 /grub-ofpathname.exe
 /grub-probe
 /grub-probe.exe
+/grub-protect
+/grub-protect.exe
 /grub-reboot
 /grub-render-label
 /grub-render-label.exe
diff --git a/Makefile.util.def b/Makefile.util.def
index fb82f59a0..074c0aff7 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -208,6 +208,32 @@ program = {
   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
 };
 
+program = {
+  name = grub-protect;
+  mansection = 1;
+
+  common = grub-core/kern/emu/argp_common.c;
+  common = grub-core/osdep/init.c;
+  common = grub-core/lib/tss2/buffer.c;
+  common = grub-core/lib/tss2/tss2_mu.c;
+  common = grub-core/lib/tss2/tpm2_cmd.c;
+  common = grub-core/commands/tpm2_key_protector/args.c;
+  common = grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c;
+  common = util/grub-protect.c;
+  common = util/probe.c;
+
+  cflags = '-I$(srcdir)/grub-core/lib/tss2 
-I$(srcdir)/grub-core/commands/tpm2_key_protector';
+
+  ldadd = libgrubmods.a;
+  ldadd = libgrubgcry.a;
+  ldadd = libgrubkern.a;
+  ldadd = grub-core/lib/gnulib/libgnu.a;
+  ldadd = '$(LIBTASN1)';
+  ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) 
$(LIBGEOM)';
+
+  condition = COND_GRUB_PROTECT;
+};
+
 program = {
   name = grub-mkrelpath;
   mansection = 1;
diff --git a/configure.ac b/configure.ac
index d4a14bf93..12681c19c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -76,6 +76,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])
@@ -2057,6 +2058,29 @@ fi
 AC_SUBST([LIBZFS])
 AC_SUBST([LIBNVPAIR])
 
+AC_ARG_ENABLE([grub-protect],
+ [AS_HELP_STRING([--enable-grub-protect],
+ [build and install the `grub-protect' utility 
(default=guessed)])])
+if test x"$enable_grub_protect" = xno ; then
+  grub_protect_excuse="explicitly disabled"
+fi
+
+LIBTASN1=
+if test x"$grub_protect_excuse" = x ; then
+  AC_CHECK_LIB([tasn1], [asn1_write_value], [LIBTASN1="-ltasn1"], 
[grub_protect_excuse="need libtasn1 library"])
+fi
+AC_SUBST([LIBTASN1])
+
+if test x"$enable_grub_protect" = xyes && test x"$grub_protect_excuse" != x ; 
then
+  

[PATCH v18 01/25] posix_wrap: tweaks in preparation for libtasn1

2024-06-28 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

 - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as
   SIZEOF_UNSIGNED_LONG.

 - Define WORD_BIT, the size in bits of an int. This is a defined
   in the Single Unix Specification and in gnulib's limits.h. gnulib
   assumes it's 32 bits on all our platforms, including 64 bit
   platforms, so we also use that value.

 - Provide strto[u]l[l] preprocessor macros that resolve to
   grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we
   also define HAVE_STRTOUL here.

 - Implement c-ctype.h and the functions defined in the header.

 - Implement strncat in string.h.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
Reviewed-by: Daniel Kiper 
---
 grub-core/lib/posix_wrap/c-ctype.h   | 114 +++
 grub-core/lib/posix_wrap/limits.h|   1 +
 grub-core/lib/posix_wrap/stdlib.h|   8 ++
 grub-core/lib/posix_wrap/string.h|  21 +
 grub-core/lib/posix_wrap/sys/types.h |   1 +
 5 files changed, 145 insertions(+)
 create mode 100644 grub-core/lib/posix_wrap/c-ctype.h

diff --git a/grub-core/lib/posix_wrap/c-ctype.h 
b/grub-core/lib/posix_wrap/c-ctype.h
new file mode 100644
index 0..5f8fc8ce3
--- /dev/null
+++ b/grub-core/lib/posix_wrap/c-ctype.h
@@ -0,0 +1,114 @@
+/*
+ *  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 .
+ */
+
+#ifndef GRUB_POSIX_C_CTYPE_H
+#define GRUB_POSIX_C_CTYPE_H   1
+
+#include 
+
+static inline bool
+c_isspace (int c)
+{
+  return !!grub_isspace (c);
+}
+
+static inline bool
+c_isdigit (int c)
+{
+  return !!grub_isdigit (c);
+}
+
+static inline bool
+c_islower (int c)
+{
+  return !!grub_islower (c);
+}
+
+static inline bool
+c_isascii (int c)
+{
+  return !(c & ~0x7f);
+}
+
+static inline bool
+c_isupper (int c)
+{
+  return !!grub_isupper (c);
+}
+
+static inline bool
+c_isxdigit (int c)
+{
+  return !!grub_isxdigit (c);
+}
+
+static inline bool
+c_isprint (int c)
+{
+  return !!grub_isprint (c);
+}
+
+static inline bool
+c_iscntrl (int c)
+{
+  return !grub_isprint (c);
+}
+
+static inline bool
+c_isgraph (int c)
+{
+  return grub_isprint (c) && !grub_isspace (c);
+}
+
+static inline bool
+c_isalnum (int c)
+{
+  return grub_isalpha (c) || grub_isdigit (c);
+}
+
+static inline bool
+c_ispunct (int c)
+{
+  return grub_isprint (c) && !grub_isspace (c) && !c_isalnum (c);
+}
+
+static inline bool
+c_isalpha (int c)
+{
+  return !!grub_isalpha (c);
+}
+
+static inline bool
+c_isblank (int c)
+{
+  return c == ' ' || c == '\t';
+}
+
+static inline int
+c_tolower (int c)
+{
+  return grub_tolower (c);
+}
+
+static inline int
+c_toupper (int c)
+{
+  return grub_toupper (c);
+}
+
+#endif
diff --git a/grub-core/lib/posix_wrap/limits.h 
b/grub-core/lib/posix_wrap/limits.h
index 26918c8a0..4be7b4080 100644
--- a/grub-core/lib/posix_wrap/limits.h
+++ b/grub-core/lib/posix_wrap/limits.h
@@ -41,5 +41,6 @@
 #define LONG_MAX GRUB_LONG_MAX
 
 #define CHAR_BIT 8
+#define WORD_BIT 32
 
 #endif
diff --git a/grub-core/lib/posix_wrap/stdlib.h 
b/grub-core/lib/posix_wrap/stdlib.h
index f5279756a..14e4efdd0 100644
--- a/grub-core/lib/posix_wrap/stdlib.h
+++ b/grub-core/lib/posix_wrap/stdlib.h
@@ -64,4 +64,12 @@ abort (void)
   grub_abort ();
 }
 
+#define strtol grub_strtol
+
+/* for libgcrypt */
+#define HAVE_STRTOUL
+#define strtoul grub_strtoul
+
+#define strtoull grub_strtoull
+
 #endif
diff --git a/grub-core/lib/posix_wrap/string.h 
b/grub-core/lib/posix_wrap/string.h
index 1adb450b5..d3e400d50 100644
--- a/grub-core/lib/posix_wrap/string.h
+++ b/grub-core/lib/posix_wrap/string.h
@@ -84,6 +84,27 @@ memchr (const void *s, int c, grub_size_t n)
   return grub_memchr (s, c, n);
 }
 
+static inline char *
+strncat (char *dest, const char *src, grub_size_t n)
+{
+  const char *end;
+  char *str = dest;
+  grub_size_t src_len;
+
+  dest += grub_strlen (dest);
+
+  end = grub_memchr (src, '\0', n);
+  if (end != NULL)
+src_len = (grub_size_t) (end - src);
+  else
+src_len = n;
+
+  dest[src_len] = '\0';
+  grub_memcpy (dest, src, src_len);
+
+  return str;
+}
+
 #define memcmp grub_memcmp
 #define memcpy grub_memcpy
 #define memmove grub_memmove
diff --git a/grub-core/lib/posix_wrap/sys/types.h 
b/grub-core/lib/posix_wrap/sys/types.h
index eeda543c4..2f3e86549 100644
--- a/grub-core/lib/posix_wrap/sys/types.h
+++ 

[PATCH v18 14/25] tss2: Add TPM2 types and Marshal/Unmarshal functions

2024-06-28 Thread Gary Lin via Grub-devel
This commit adds the necessary TPM2 types and structs as the preparation
for the TPM2 Software Stack (TSS2) support. The Marshal/Unmarshal
functions are also added to handle the data structure to be submitted to
TPM2 commands and to be received from the response.

Cc: Stefan Berger 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/lib/tss2/tss2_mu.c  | 1170 +
 grub-core/lib/tss2/tss2_mu.h  |  397 ++
 grub-core/lib/tss2/tss2_structs.h |  768 +++
 grub-core/lib/tss2/tss2_types.h   |  404 ++
 4 files changed, 2739 insertions(+)
 create mode 100644 grub-core/lib/tss2/tss2_mu.c
 create mode 100644 grub-core/lib/tss2/tss2_mu.h
 create mode 100644 grub-core/lib/tss2/tss2_structs.h
 create mode 100644 grub-core/lib/tss2/tss2_types.h

diff --git a/grub-core/lib/tss2/tss2_mu.c b/grub-core/lib/tss2/tss2_mu.c
new file mode 100644
index 0..af7a75266
--- /dev/null
+++ b/grub-core/lib/tss2/tss2_mu.c
@@ -0,0 +1,1170 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *  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 .
+ */
+
+#include 
+
+#include 
+
+void
+grub_Tss2_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*) 
>sessionAttributes));
+
+  grub_tpm2_buffer_pack_u16 (buffer, authCommand->hmac.size);
+  grub_tpm2_buffer_pack (buffer, authCommand->hmac.buffer,
+authCommand->hmac.size);
+
+  tmp = grub_cpu_to_be32 (buffer->size - start);
+  grub_memcpy (>data[start - sizeof (grub_uint32_t)], ,
+  sizeof (tmp));
+}
+
+void
+grub_Tss2_MU_TPM2B_Marshal (grub_tpm2_buffer_t buffer,
+   const 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_Tss2_MU_TPMU_SYM_KEY_BITS_Marshal (grub_tpm2_buffer_t buffer,
+   const TPMI_ALG_SYM_OBJECT algorithm,
+   const 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;
+default:
+  buffer->error = 1;
+  break;
+}
+}
+
+void
+grub_Tss2_MU_TPMU_SYM_MODE_Marshal (grub_tpm2_buffer_t buffer,
+   const TPMI_ALG_SYM_OBJECT algorithm,
+   const 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;
+default:
+  buffer->error = 1;
+  break;
+}
+}
+
+void
+grub_Tss2_MU_TPMT_SYM_DEF_Marshal (grub_tpm2_buffer_t buffer,
+  const TPMT_SYM_DEF *p)
+{
+  grub_tpm2_buffer_pack_u16 (buffer, p->algorithm);
+  grub_Tss2_MU_TPMU_SYM_KEY_BITS_Marshal (buffer, p->algorithm, >keyBits);
+  grub_Tss2_MU_TPMU_SYM_MODE_Marshal (buffer, p->algorithm, >mode);
+}
+
+void
+grub_Tss2_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_Tss2_MU_TPML_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buffer,
+

[PATCH v18 11/25] libtasn1: Add the documentation

2024-06-28 Thread Gary Lin via Grub-devel
Document libtasn1 in docs/grub-dev.texi and add the upgrade steps.
Also add the patches to make libtasn1 compatible with grub code.

Signed-off-by: Gary Lin 
Reviewed-by: Vladimir Serbinenko 
---
 docs/grub-dev.texi | 35 +++
 1 file changed, 35 insertions(+)

diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
index 1276c5930..0e136790b 100644
--- a/docs/grub-dev.texi
+++ b/docs/grub-dev.texi
@@ -506,6 +506,7 @@ to update it.
 * Gnulib::
 * jsmn::
 * minilzo::
+* libtasn1::
 @end menu
 
 @node Gnulib
@@ -596,6 +597,40 @@ cp minilzo-2.10/*.[hc] grub-core/lib/minilzo
 rm -r minilzo-2.10*
 @end example
 
+@node libtasn1
+@section libtasn1
+
+libtasn1 is a library providing Abstract Syntax Notation One (ASN.1, as
+specified by the X.680 ITU-T recommendation) parsing and structures management,
+and Distinguished Encoding Rules (DER, as per X.690) encoding and decoding
+functions.
+
+To upgrade to a new version of the libtasn1 library, download the release
+tarball and copy the files into the target directory:
+
+@example
+curl -L -O https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz
+tar xf libtasn1-4.19.0.tar.gz
+rm -rf grub-core/lib/libtasn1/
+mkdir -p grub-core/lib/libtasn1/lib/
+mkdir -p grub-core/lib/libtasn1/tests/
+cp libtasn1-4.19.0/@lbracechar{}README.md,COPYING@rbracechar{} 
grub-core/lib/libtasn1/
+cp 
libtasn1-4.19.0/lib/@lbracechar{}coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h@rbracechar{}
 grub-core/lib/libtasn1/lib/
+cp libtasn1-4.19.0/lib/includes/libtasn1.h grub-core/lib/libtasn1/
+cp 
libtasn1-4.19.0/tests/@lbracechar{}CVE-2018-1000654-1_asn1_tab.h,CVE-2018-1000654-2_asn1_tab.h,CVE-2018-1000654.c,object-id-decoding.c,object-id-encoding.c,octet-string.c,reproducers.c,Test_overflow.c,Test_simple.c,Test_strings.c@rbracechar{}
 grub-core/lib/libtasn1/tests
+rm -rf libtasn1-4.19.0*
+@end example
+
+After upgrading the library, it may be necessary to apply the patches in
+@file{grub-core/lib/libtasn1-patches/} to adjust the code to be compatible with
+GRUB. These patches were needed to use the current version of libtasn1. The
+existing patches may not apply cleanly, apply at all, or even be needed for a
+newer version of the library, and other patches may be needed due to changes in
+the newer version. If existing patches need to be refreshed to apply cleanly,
+please include updated patches as part of the a patch set sent to the list.
+If new patches are needed or existing patches are not needed, also please send
+additions or removals as part of any patch set upgrading libtasn1.
+
 @node Debugging
 @chapter Debugging
 
-- 
2.35.3


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


[PATCH v18 09/25] libtasn1: compile into asn1 module

2024-06-28 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

Create a wrapper file that specifies the module license.
Set up the makefile so it is built.

Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
Reviewed-by: Daniel Kiper 
---
 autogen.sh | 18 ++
 grub-core/Makefile.core.def| 15 +++
 grub-core/lib/libtasn1_wrap/wrap.c | 27 +++
 3 files changed, 60 insertions(+)
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c

diff --git a/autogen.sh b/autogen.sh
index 195daa541..3cccd76c0 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -51,6 +51,24 @@ for x in mpi-asm-defs.h mpih-add1.c mpih-sub1.c mpih-mul1.c 
mpih-mul2.c mpih-mul
 cp grub-core/lib/libgcrypt-grub/mpi/generic/"$x" 
grub-core/lib/libgcrypt-grub/mpi/"$x"
 done
 
+echo "Importing libtasn1..."
+if [ -d grub-core/lib/libtasn1-grub ]; then
+  rm -rf grub-core/lib/libtasn1-grub
+fi
+
+mkdir -p grub-core/lib/libtasn1-grub/lib
+cp grub-core/lib/libtasn1/lib/*.[ch] grub-core/lib/libtasn1-grub/lib
+cp grub-core/lib/libtasn1/libtasn1.h grub-core/lib/libtasn1-grub/
+
+for patch in \
+   0001-libtasn1-disable-code-not-needed-in-grub.patch \
+   0002-libtasn1-use-bound-checked-_asn1_str_cat.patch \
+   0003-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch \
+   0004-libtasn1-Use-grub_divmod64-for-division.patch \
+   0005-libtasn1-fix-the-potential-buffer-overrun.patch ; do
+  patch -p1 -i grub-core/lib/libtasn1-patches/$patch
+done
+
 echo "Generating Automake input..."
 
 # Automake doesn't like including files from a path outside the project.
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 705d73fab..01a05c9b3 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2606,3 +2606,18 @@ module = {
   enable = efi;
   depends = part_gpt;
 };
+
+module = {
+  name = asn1;
+  common = lib/libtasn1-grub/lib/decoding.c;
+  common = lib/libtasn1-grub/lib/coding.c;
+  common = lib/libtasn1-grub/lib/element.c;
+  common = lib/libtasn1-grub/lib/structure.c;
+  common = lib/libtasn1-grub/lib/parser_aux.c;
+  common = lib/libtasn1-grub/lib/gstr.c;
+  common = lib/libtasn1-grub/lib/errors.c;
+  common = lib/libtasn1_wrap/wrap.c;
+  cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+  /* -Wno-type-limits comes from configure.ac of libtasn1 */
+  cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) 
-I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/lib/libtasn1-grub/lib 
-Wno-type-limits';
+};
diff --git a/grub-core/lib/libtasn1_wrap/wrap.c 
b/grub-core/lib/libtasn1_wrap/wrap.c
new file mode 100644
index 0..156885d02
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/wrap.c
@@ -0,0 +1,27 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *  Copyright (C) 2020 IBM 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 .
+ */
+
+#include 
+
+/*
+ * libtasn1 is provided under LGPL2.1+, which is compatible
+ * with GPL3+. As GRUB as a whole is under GPL3+, this module
+ * is therefore under GPL3+ also.
+ */
+GRUB_MOD_LICENSE ("GPLv3+");
-- 
2.35.3


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


[PATCH v18 13/25] tss2: Add TPM2 buffer handling functions

2024-06-28 Thread Gary Lin via Grub-devel
As the prepartion to support TPM2 Software Stack (TSS2), this commit
implements the TPM2 buffer handling functions to pack data for the TPM2
commands and unpack the data from the response.

Cc: Stefan Berger 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/lib/tss2/buffer.c  | 149 +++
 grub-core/lib/tss2/tss2_buffer.h |  66 ++
 2 files changed, 215 insertions(+)
 create mode 100644 grub-core/lib/tss2/buffer.c
 create mode 100644 grub-core/lib/tss2/tss2_buffer.h

diff --git a/grub-core/lib/tss2/buffer.c b/grub-core/lib/tss2/buffer.c
new file mode 100644
index 0..00a57b464
--- /dev/null
+++ b/grub-core/lib/tss2/buffer.c
@@ -0,0 +1,149 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *  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 .
+ */
+
+#include 
+
+#include 
+
+void grub_tpm2_buffer_init (grub_tpm2_buffer_t buffer)
+{
+  grub_memset (buffer->data, 0, 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 (>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*) , sizeof (value));
+}
+
+void
+grub_tpm2_buffer_pack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t value)
+{
+  grub_uint16_t tmp = grub_cpu_to_be16 (value);
+
+  grub_tpm2_buffer_pack (buffer, (const char*) , sizeof (tmp));
+}
+
+void
+grub_tpm2_buffer_pack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t value)
+{
+  grub_uint32_t tmp = grub_cpu_to_be32 (value);
+
+  grub_tpm2_buffer_pack (buffer, (const char*) , 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, >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, >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 (, >data[buffer->offset], sizeof (tmp));
+  buffer->offset += sizeof (tmp);
+  *value = grub_be_to_cpu16 (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 (, >data[buffer->offset], sizeof (tmp));
+  buffer->offset += sizeof (tmp);
+  *value = grub_be_to_cpu32 (tmp);
+}
diff --git a/grub-core/lib/tss2/tss2_buffer.h b/grub-core/lib/tss2/tss2_buffer.h
new file mode 100644
index 0..92648a1cb
--- /dev/null
+++ b/grub-core/lib/tss2/tss2_buffer.h
@@ -0,0 +1,66 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *  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 

[PATCH v18 12/25] key_protector: Add key protectors framework

2024-06-28 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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 by name. If a key protector with the specified
name exists and if an unlocking key is successfully retrieved by it, the
function returns to the caller the retrieved key and its length.

Cc: Vladimir Serbinenko 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
Reviewed-by: Daniel Kiper 
---
 grub-core/Makefile.am  |  1 +
 grub-core/Makefile.core.def|  5 +++
 grub-core/disk/key_protector.c | 79 ++
 include/grub/key_protector.h   | 47 
 4 files changed, 132 insertions(+)
 create mode 100644 grub-core/disk/key_protector.c
 create mode 100644 include/grub/key_protector.h

diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index 1eda467e0..e50db8106 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/key_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 fd6dfa515..5ba9c8548 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1282,6 +1282,11 @@ module = {
   common = disk/raid6_recover.c;
 };
 
+module = {
+  name = key_protector;
+  common = disk/key_protector.c;
+};
+
 module = {
   name = scsi;
   common = disk/scsi.c;
diff --git a/grub-core/disk/key_protector.c b/grub-core/disk/key_protector.c
new file mode 100644
index 0..c2d8c7c44
--- /dev/null
+++ b/grub-core/disk/key_protector.c
@@ -0,0 +1,79 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 Free Software Foundation, Inc.
+ *  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 .
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+struct grub_key_protector *grub_key_protectors = NULL;
+
+grub_err_t
+grub_key_protector_register (struct grub_key_protector *protector)
+{
+  if (protector == NULL || protector->name == NULL || grub_strlen 
(protector->name) == 0)
+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 (_key_protectors),
+ GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_unregister (struct grub_key_protector *protector)
+{
+  if (protector == NULL)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_list_remove (GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_recover_key (const char *protector, grub_uint8_t **key,
+   grub_size_t *key_size)
+{
+  struct grub_key_protector *kp = NULL;
+
+  if (grub_key_protectors == NULL)
+return GRUB_ERR_OUT_OF_RANGE;
+
+  if (protector == NULL || grub_strlen (protector) == 0)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  kp = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
+protector);
+  if (kp == NULL)
+return grub_error (GRUB_ERR_OUT_OF_RANGE,
+  N_("A key protector with name '%s' could not be found. "
+ "Is the name spelled correctly and is the "
+ "corresponding module loaded?"), protector);
+
+  return kp->recover_key (key, key_size);
+}
diff --git a/include/grub/key_protector.h b/include/grub/key_protector.h
new file mode 100644
index 0..8e8de94f6
--- 

[PATCH v18 19/25] tpm2_key_protector: Support authorized policy

2024-06-28 Thread Gary Lin via Grub-devel
This commit handles the TPM2_PolicyAuthorize command from the key file
in TPM 2.0 Key File format.

TPM2_PolicyAuthorize is the essential command to support authorized
policy which allows the users to sign TPM policies with their own keys.
Per TPM 2.0 Key File(*1), CommandPolicy for TPM2_PolicyAuthorize
comprises 'TPM2B_PUBLIC pubkey', 'TPM2B_DIGEST policy_ref', and
'TPMT_SIGNATURE signature'. To verify the signature, the current policy
digest is hashed with the hash algorithm written in 'signature', and then
'signature' is verified with the hashed policy digest and 'pubkey'. Once
TPM accepts 'signature', TPM2_PolicyAuthorize is invoked to authorize the
signed policy.

To create the key file with authorized policy, here are the pcr-oracle(*2)
commands:

  # Generate the RSA key and create the authorized policy file
  $ pcr-oracle \
--rsa-generate-key \
--private-key policy-key.pem \
--auth authorized.policy \
create-authorized-policy 0,2,4,7,9

  # Seal the secret with the authorized policy
  $ pcr-oracle \
--key-format tpm2.0 \
--auth authorized.policy \
--input disk-secret.txt \
--output sealed.key \
seal-secret

  # Sign the predicted PCR policy
  $ pcr-oracle \
--key-format tpm2.0 \
--private-key policy-key.pem \
--from eventlog \
--stop-event "grub-file=grub.cfg" \
--after \
--input sealed.key \
--output sealed.tpm \
sign 0,2,4,7,9

Then specify the key file and the key protector to grub.cfg in the EFI
system partition:

tpm2_key_protector_init -a RSA --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

For any change in the boot components, just run the 'sign' command again
to update the signature in sealed.tpm, and TPM can unseal the key file
with the updated PCR policy.

(*1) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*2) https://github.com/okirch/pcr-oracle

Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 .../commands/tpm2_key_protector/module.c  | 72 +++
 1 file changed, 72 insertions(+)

diff --git a/grub-core/commands/tpm2_key_protector/module.c 
b/grub-core/commands/tpm2_key_protector/module.c
index 79440474b..a98109c43 100644
--- a/grub-core/commands/tpm2_key_protector/module.c
+++ b/grub-core/commands/tpm2_key_protector/module.c
@@ -618,6 +618,75 @@ grub_tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION 
session,
   return GRUB_ERR_NONE;
 }
 
+static grub_err_t
+grub_tpm2_protector_policyauthorize (TPMI_SH_AUTH_SESSION session,
+struct grub_tpm2_buffer *cmd_buf)
+{
+  TPM2B_PUBLIC pubkey;
+  TPM2B_DIGEST policy_ref;
+  TPMT_SIGNATURE signature;
+  TPM2B_DIGEST pcr_policy;
+  TPM2B_DIGEST pcr_policy_hash;
+  TPMI_ALG_HASH sig_hash;
+  TPMT_TK_VERIFIED verification_ticket;
+  TPM_HANDLE pubkey_handle = 0;
+  TPM2B_NAME pubname;
+  TPM_RC rc;
+  grub_err_t err;
+
+  grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (cmd_buf, );
+  grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (cmd_buf, _ref);
+  grub_Tss2_MU_TPMT_SIGNATURE_Unmarshal (cmd_buf, );
+  if (cmd_buf->error != 0)
+return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Failed to unmarshal the 
buffer for TPM2_PolicyAuthorize"));
+
+  /* Retrieve Policy Digest */
+  rc = TPM2_PolicyGetDigest (session, NULL, _policy, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE, N_("Failed to get policy digest 
(TPM2_PolicyGetDigest: 0x%x)."), rc);
+
+  /* Calculate the digest of the polcy for VerifySignature */
+  sig_hash = TPMT_SIGNATURE_get_hash_alg ();
+  if (sig_hash == TPM_ALG_NULL)
+return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Failed to get the hash 
algorithm of the signature"));
+
+  rc = TPM2_Hash (NULL, (TPM2B_MAX_BUFFER *)_policy, sig_hash,
+ TPM_RH_NULL, _policy_hash, NULL, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE, N_("Failed to create PCR policy 
hash (TPM2_Hash: 0x%x)"), rc);
+
+  /* Load the public key */
+  rc = TPM2_LoadExternal (NULL, NULL, , TPM_RH_OWNER,
+ _handle, , NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE, N_("Failed to load public key 
(TPM2_LoadExternal: 0x%x)"), rc);
+
+  /* Verify the signature against the public key and the policy digest */
+  rc = TPM2_VerifySignature (pubkey_handle, NULL, _policy_hash, ,
+_ticket, NULL);
+  if (rc != TPM_RC_SUCCESS)
+{
+  err = grub_error (GRUB_ERR_BAD_DEVICE, N_("Failed to verify signature 
(TPM2_VerifySignature: 0x%x)"), rc);
+  goto error;
+}
+
+  /* Authorize the signed policy with the public key and the verification 
ticket */
+  rc = TPM2_PolicyAuthorize (session, NULL, _policy, _ref, ,
+_ticket, NULL);
+  if (rc != TPM_RC_SUCCESS)
+{
+  err = grub_error (GRUB_ERR_BAD_DEVICE, N_("Failed to authorize PCR 
policy (TPM2_PolicyAuthorize: 0x%x)"), 

[PATCH v18 16/25] key_protector: Add TPM2 Key Protector

2024-06-28 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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

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

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

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

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

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

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

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

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

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

The TPM2 key protector registers two commands:

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

- tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init.

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

For instance, to unseal the raw sealed key file:

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

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

Or, to unseal the TPM 2.0 Key file:

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

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

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

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

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

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

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

Cc: Stefan Berger 
Cc: James Bottomley 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/Makefile.core.def   |   11 +
 grub-core/commands/tpm2_key_protector/args.c  |  130 ++
 .../commands/tpm2_key_protector/module.c  

[PATCH v18 04/25] libtasn1: use bound-checked _asn1_str_cat()

2024-06-28 Thread Gary Lin via Grub-devel
Remove _asn1_strcat() and replace strcat() with the bound-checked
_asn1_str_cat() except the one inside _asn1_str_cat(). That strcat
is replaced with strcpy.

Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 ...asn1-use-bound-checked-_asn1_str_cat.patch | 85 +++
 1 file changed, 85 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0002-libtasn1-use-bound-checked-_asn1_str_cat.patch

diff --git 
a/grub-core/lib/libtasn1-patches/0002-libtasn1-use-bound-checked-_asn1_str_cat.patch
 
b/grub-core/lib/libtasn1-patches/0002-libtasn1-use-bound-checked-_asn1_str_cat.patch
new file mode 100644
index 0..87bb13e06
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0002-libtasn1-use-bound-checked-_asn1_str_cat.patch
@@ -0,0 +1,85 @@
+From d87d8e78371e3afbdd876193295ab9e3f60c140b Mon Sep 17 00:00:00 2001
+From: Gary Lin 
+Date: Tue, 25 Jun 2024 16:27:57 +0800
+Subject: [PATCH 2/6] libtasn1: use bound-checked _asn1_str_cat()
+
+Remove _asn1_strcat() and replace strcat() with the bound-checked
+_asn1_str_cat() except the one inside _asn1_str_cat(). That strcat
+is replaced with strcpy.
+
+Signed-off-by: Daniel Axtens 
+Signed-off-by: Gary Lin 
+---
+ grub-core/lib/libtasn1-grub/lib/decoding.c | 8 
+ grub-core/lib/libtasn1-grub/lib/element.c  | 2 +-
+ grub-core/lib/libtasn1-grub/lib/gstr.c | 2 +-
+ grub-core/lib/libtasn1-grub/lib/int.h  | 1 -
+ 4 files changed, 6 insertions(+), 7 deletions(-)
+
+diff --git a/grub-core/lib/libtasn1-grub/lib/decoding.c 
b/grub-core/lib/libtasn1-grub/lib/decoding.c
+index bf9cb13ac..51859fe36 100644
+--- a/grub-core/lib/libtasn1-grub/lib/decoding.c
 b/grub-core/lib/libtasn1-grub/lib/decoding.c
+@@ -2016,8 +2016,8 @@ asn1_expand_octet_string (asn1_node_const definitions, 
asn1_node * element,
+ (p2->type & CONST_ASSIGN))
+   {
+ strcpy (name, definitions->name);
+-strcat (name, ".");
+-strcat (name, p2->name);
++_asn1_str_cat (name, sizeof (name), ".");
++_asn1_str_cat (name, sizeof (name), p2->name);
+ 
+ len = sizeof (value);
+ result = asn1_read_value (definitions, name, value, );
+@@ -2034,8 +2034,8 @@ asn1_expand_octet_string (asn1_node_const definitions, 
asn1_node * element,
+ if (p2)
+   {
+ strcpy (name, definitions->name);
+-strcat (name, ".");
+-strcat (name, p2->name);
++_asn1_str_cat (name, sizeof (name), ".");
++_asn1_str_cat (name, sizeof (name), p2->name);
+ 
+ result = asn1_create_element (definitions, name, );
+ if (result == ASN1_SUCCESS)
+diff --git a/grub-core/lib/libtasn1-grub/lib/element.c 
b/grub-core/lib/libtasn1-grub/lib/element.c
+index bc4c3c8d7..8694fecb9 100644
+--- a/grub-core/lib/libtasn1-grub/lib/element.c
 b/grub-core/lib/libtasn1-grub/lib/element.c
+@@ -688,7 +688,7 @@ asn1_write_value (asn1_node node_root, const char *name,
+ return ASN1_MEM_ERROR; \
+ } else { \
+ /* this strcat is checked */ \
+-if (ptr) _asn1_strcat (ptr, data); \
++if (ptr) _asn1_str_cat ((char *)ptr, ptr_size, (const char 
*)data); \
+ }
+ 
+ /**
+diff --git a/grub-core/lib/libtasn1-grub/lib/gstr.c 
b/grub-core/lib/libtasn1-grub/lib/gstr.c
+index eef419554..a9c16f5d3 100644
+--- a/grub-core/lib/libtasn1-grub/lib/gstr.c
 b/grub-core/lib/libtasn1-grub/lib/gstr.c
+@@ -36,7 +36,7 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char 
*src)
+ 
+   if (dest_tot_size - dest_size > str_size)
+ {
+-  strcat (dest, src);
++  strcpy (dest + dest_size, src);
+ }
+   else
+ {
+diff --git a/grub-core/lib/libtasn1-grub/lib/int.h 
b/grub-core/lib/libtasn1-grub/lib/int.h
+index d94d51c8c..cadd80df6 100644
+--- a/grub-core/lib/libtasn1-grub/lib/int.h
 b/grub-core/lib/libtasn1-grub/lib/int.h
+@@ -115,7 +115,6 @@ extern const tag_and_class_st _asn1_tags[];
+ # define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
+ # define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
+ # define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
+-# define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
+ 
+ # if SIZEOF_UNSIGNED_LONG_INT == 8
+ #  define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b)
+-- 
+2.35.3
+
-- 
2.35.3


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


Re: [PATCH v9 00/22] Automatic Disk Unlock with TPM2

2024-06-25 Thread Gary Lin via Grub-devel
On Mon, Jun 24, 2024 at 07:28:14PM +0200, Daniel Kiper wrote:
> On Thu, Mar 07, 2024 at 04:59:05PM +0800, Gary Lin via Grub-devel wrote:
> > On Thu, Feb 08, 2024 at 08:58:43PM +0100, Daniel Kiper wrote:
> > > Hey,
> > >
> > --8<--
> > >
> > > And I have attached the Coverity report. All issues reported there have
> > > to be fixed. If you cannot fix an issue you have to explain why you
> > > cannot do that and what is potential impact on the code stability,
> > > security, etc.
> > >
> > I have went through all the coverity issues. There are 6 issues in the
> > TPM2 stack and the utility:
> >
> > CID 435775, CID 435771, CID 435770, CID 435769, CID 435767, CID 435761
> >
> > Those will be fixed in the next version.
> >
> > The 9 issues are from libtasn1 and gnulib.
> >
> > There are two memory corruption issue: CID 435762 and CID 435766, and
> > I've filed the issues in libtasn1 upstream.
> >
> > - CID 435762
> >   https://gitlab.com/gnutls/libtasn1/-/issues/49
> >   This is a valid case. However, the only exploitable function,
> >   _asn1_insert_tag_der(), is disabled in grub2 patch, so the severity is
> >   low. I have a quick fix but upstream would like to fix it in another
> >   way.
> > - CID 435766
> >   https://gitlab.com/gnutls/libtasn1/-/issues/50
> >   IMO, this is false positive, but I need libtasn1 upstream to confirm
> >   that.
> >
> > Then, the remaining 7 Integer handling issues are involved with the macros
> > from gnulib, and I believe those are false positive.
> >
> > The following are my analyses:
> >
> > 
> > *** CID 435774:  Integer handling issues  (CONSTANT_EXPRESSION_RESULT)
> > /grub-core/lib/libtasn1/lib/decoding.c: 481 in asn1_get_object_id_der()
> > 475*/
> > 476   if (leading != 0 && der[len_len + k] == 0x80)
> > 477 return ASN1_DER_ERROR;
> > 478   leading = 0;
> > 479
> > 480   /* check for wrap around */
> > >>> CID 435774:  Integer handling issues  (CONSTANT_EXPRESSION_RESULT)
> > >>> "val < 1 ? 0 : val) - 1 < 0) ? ~1 ? 0 : val) + 1 << 62UL /* 
> > >>> sizeof (+val) * 8 - 2 */) - 1) * 2 + 1) : ((1 ? 0 : val) + 0)) >> 7)" 
> > >>> is always false regardless of the values of its operands. This occurs 
> > >>> as the second operand of "?:".
> > 481   if (INT_LEFT_SHIFT_OVERFLOW (val, 7))
> > 482 return ASN1_DER_ERROR;
> > 483
> > 484   val = val << 7;
> > 485   val |= der[len_len + k] & 0x7F;
> > 486
> >
> > Here are the related macros:
> >
> > #define EXPR_SIGNED(e) (_GL_INT_NEGATE_CONVERT (e, 1) < 0)
> >
> > #define _GL_INT_CONVERT(e, v) ((1 ? 0 : (e)) + (v))
> >
> > #define _GL_SIGNED_INT_MAXIMUM(e)   \
> >   (((_GL_INT_CONVERT (e, 1) << (TYPE_WIDTH (+ (e)) - 2)) - 1) * 2 + 1)
> >
> > #define _GL_INT_MINIMUM(e)  \
> >   (EXPR_SIGNED (e)  \
> >? ~ _GL_SIGNED_INT_MAXIMUM (e)   \
> >: _GL_INT_CONVERT (e, 0))
> >
> > #define INT_LEFT_SHIFT_OVERFLOW(a, b) \
> >   INT_LEFT_SHIFT_RANGE_OVERFLOW (a, b, \
> >  _GL_INT_MINIMUM (a), _GL_INT_MAXIMUM (a))
> >
> > #define INT_LEFT_SHIFT_RANGE_OVERFLOW(a, b, min, max)   \
> >   ((a) < 0  \
> >? (a) < (min) >> (b) \
> >: (max) >> (b) < (a))
> >
> > The statement in question is expanded "(a) < (min) >> (b)" of
> > INT_LEFT_SHIFT_RANGE_OVERFLOW. Since 'val' is 'uint64_t', '(a) < 0' is 
> > always
> > false, so the result of that statement doen't matter.
> 
> Something is missing and/or requires clarification/expansion here.
> AFAICT at least your description completely ignores "(max) >> (b) < (a)"
> expression result.
> 
Because Coverity only complained "(a) < (min) >> (b)".
"(max) >> (b) < (a)" does the right thing to check whether 'a' is
overflowed after the left shift.

> > 
> > **

Re: [PATCH v17 11/20] key_protector: Add TPM2 Key Protector

2024-06-20 Thread Gary Lin via Grub-devel
On Wed, Jun 19, 2024 at 06:34:13PM +0200, Daniel Kiper wrote:
> On Fri, Jun 14, 2024 at 02:45:44PM +0800, Gary Lin wrote:
> > From: Hernan Gatta 
> >
> > The TPM2 key protector is a module that enables the automatic retrieval
> > of a fully-encrypted disk's unlocking key from a TPM 2.0.
> >
> > The theory of operation is such that the module accepts various
> > arguments, most of which are optional and therefore possess reasonable
> > defaults. One of these arguments is the keyfile/tpm2key parameter, which
> > is mandatory. There are two supported key formats:
> >
> > 1. Raw Sealed Key (--keyfile)
> >When sealing a key with TPM2_Create, the public portion of the sealed
> >key is stored in TPM2B_PUBLIC, and the private portion is in
> >TPM2B_PRIVATE. The raw sealed key glues the fully marshalled
> >TPM2B_PUBLIC and TPM2B_PRIVATE into one file.
> >
> > 2. TPM 2.0 Key (--tpm2key)
> >The following is the ASN.1 definition of TPM 2.0 Key File:
> >
> >TPMPolicy ::= SEQUENCE {
> >  CommandCode   [0] EXPLICIT INTEGER
> >  CommandPolicy [1] EXPLICIT OCTET STRING
> >}
> >
> >TPMAuthPolicy ::= SEQUENCE {
> >  Name[0] EXPLICIT UTF8STRING OPTIONAL
> >  Policy  [1] EXPLICIT SEQUENCE OF TPMPolicy
> >}
> >
> >TPMKey ::= SEQUENCE {
> >  typeOBJECT IDENTIFIER
> >  emptyAuth   [0] EXPLICIT BOOLEAN OPTIONAL
> >  policy  [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL
> >  secret  [2] EXPLICIT OCTET STRING OPTIONAL
> >  authPolicy  [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL
> >  description [4] EXPLICIT UTF8String OPTIONAL,
> >  rsaParent   [5] EXPLICIT BOOLEAN OPTIONAL,
> >  parent  INTEGER
> >  pubkey  OCTET STRING
> >  privkey OCTET STRING
> >}
> >
> >   The TPM2 key protector only expects a "sealed" key in DER encoding,
> >   so 'type' is always 2.23.133.10.1.5, 'emptyAuth' is 'TRUE', and
> >   'secret' is empty. 'policy' and 'authPolicy' are the possible policy
> >   command sequences to construst the policy digest to unseal the key.
> >   Similar to the raw sealed key, the public portion (TPM2B_PUBLIC) of
> >   the sealed key is stored in 'pubkey', and the private portion
> >   (TPM2B_PRIVATE) is in 'privkey'.
> >
> >   For more details: 
> > https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
> >
> > This sealed key file is created via the grub-protect tool. The tool
> > utilizes the TPM's sealing functionality to seal (i.e., encrypt) an
> > unlocking key using a Storage Root Key (SRK) to the values of various
> > Platform Configuration Registers (PCRs). These PCRs reflect the state
> > of the system as it boots. If the values are as expected, the system
> > may be considered trustworthy, at which point the TPM allows for a
> > caller to utilize the private component of the SRK to unseal (i.e.,
> > decrypt) the sealed key file. The caller, in this case, is this key
> > protector.
> >
> > The TPM2 key protector registers two commands:
> >
> > - tpm2_key_protector_init: Initializes the state of the TPM2 key
> >protector for later usage, clearing any
> >previous state, too, if any.
> >
> > - tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init.
> >
> > The way this is expected to be used requires the user to, either
> > interactively or, normally, via a boot script, initialize/configure
> > the key protector and then specify that it be used by the 'cryptomount'
> > command (modifications to this command are in a different patch).
> >
> > For instance, to unseal the raw sealed key file:
> >
> > tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub2/sealed-1.key
> > cryptomount -u  -P tpm2
> >
> > tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub2/sealed-2.key 
> > --pcrs=7,11
> > cryptomount -u  -P tpm2
> >
> > Or, to unseal the TPM 2.0 Key file:
> >
> > tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-1.tpm
> > cryptomount -u  -P tpm2
> >
> > tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-2.tpm 
> > --pcrs=7,11
> > cryptomount -u  -P tpm2
> >
> > If a user does not initialize the key protector and attempts to use it
> > anyway, the protector returns an error.
> >
> > Before unsealing the key, the TPM2 key protector follows the "TPMPolicy"
> > sequences to enforce the TPM policy commands to construct a valid policy
> > digest to unseal the key.
> >
> > For the TPM 2.0 Key files, 'authPolicy' may contain multiple "TPMPolicy"
> > sequences, the TPM2 key protector iterates 'authPolicy' to find a valid
> > sequence to unseal key. If 'authPolicy' is empty or all sequences in
> > 'authPolicy' fail, the protector tries the one from 'policy'. In case
> > 'policy' is also empty, the protector creates a "TPMPolicy" sequence
> > based on the given PCR selection.
> >
> > For the raw sealed key, the TPM2 key protector treats the key file as a
> > TPM 2.0 Key file without 'authPolicy' 

Re: [PATCH v17 10/20] tpm2: Add TPM Software Stack (TSS)

2024-06-20 Thread Gary Lin via Grub-devel
On Wed, Jun 19, 2024 at 04:04:47PM +0200, Daniel Kiper wrote:
> On Wed, Jun 19, 2024 at 02:41:13PM +0800, Gary Lin wrote:
> > On Tue, Jun 18, 2024 at 03:30:03PM +0200, Daniel Kiper wrote:
> > > On Fri, Jun 14, 2024 at 02:45:43PM +0800, Gary Lin wrote:
> > > > From: Hernan Gatta 
> > > >
> > > > A Trusted Platform Module (TPM) Software Stack (TSS) provides logic to
> > > > compose and submit TPM commands and parse reponses.
> > > >
> > > > 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,
> > > > second, 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,
> > > > TPM2_LoadExternal, TPM2_Hash, TPM2_VerifySignature,
> > > > TPM2_PolicyAuthorize, TPM2_TestParms
> > > >
> > > > Signed-off-by: Hernan Gatta 
> > > > Signed-off-by: Gary Lin 
> > > > Reviewed-by: Stefan Berger 
> > > > ---
> > > >  grub-core/tpm2/buffer.c|  145 +++
> > > >  grub-core/tpm2/mu.c| 1168 
> > > >  grub-core/tpm2/tcg2.c  |  143 +++
> > > >  grub-core/tpm2/tpm2.c  | 1048 +
> > > >  include/grub/tpm2/buffer.h |   65 ++
> > > >  include/grub/tpm2/internal/functions.h |  156 
> > > >  include/grub/tpm2/internal/structs.h   |  768 
> > > >  include/grub/tpm2/internal/types.h |  403 
> > > >  include/grub/tpm2/mu.h |  396 
> > > >  include/grub/tpm2/tcg2.h   |   34 +
> > > >  include/grub/tpm2/tpm2.h   |   34 +
> > > >  11 files changed, 4360 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 0..cb9f29497
> > > > --- /dev/null
> > > > +++ b/grub-core/tpm2/buffer.c
> > >
> > > I think this together with other TPM2 driver files should go to the
> > > grub-core/commands/efi/tpm2 directory.
> > >
> > The TPM2 stack is not EFI only. The only EFI related code is in
> 
> Ah, right... Then I think we should have two GRUB modules. One TPM2
> generic and one strictly EFI which depends on generic one.
> 
> > grub-core/tpm2/tcg2.c which mainly implements how the TPM2 commands to
> > be submitted. I'd propose to move them to grub-core/commands/tpm2 and
> > rename tcg2.c to tcg2-efi.c.
> 
> One should land in the grub-core/commands/tss2 directory and another in
> the grub-core/commands/efi or grub-core/commands/efi/tmp2 if needed.
> 
> [...]
> 
Ok, I'll move most of files to grub-core/commands/tss2 and tcg2.c to
grub-core/commands/efi.

> > > > diff --git a/grub-core/tpm2/mu.c b/grub-core/tpm2/mu.c
> > > > new file mode 100644
> > > > index 0..10ed71c04
> > > > --- /dev/null
> > > > +++ b/grub-core/tpm2/mu.c
> > >
> > > I can imagine where it comes from but I think it should be efi.c instead
> > > of mu.c.
> > >
> > No, it's not from the MU firmware but stands for Marshal/Unmarshal.
> > The similar naming policy from tpm2-tss:
> >
> > https://github.com/tpm2-software/tpm2-tss/blob/master/include/tss2/tss2_mu.h
> 
> Then I would rename mu.c file to tss2_mu.c and replace "_tpm2_mu_"
> with "_Tss2_MU_" in function 

Re: [PATCH v17 10/20] tpm2: Add TPM Software Stack (TSS)

2024-06-19 Thread Gary Lin via Grub-devel
On Tue, Jun 18, 2024 at 05:41:13PM +0200, Daniel Kiper wrote:
> On Fri, Jun 14, 2024 at 02:45:43PM +0800, Gary Lin wrote:
> > From: Hernan Gatta 
> >
> > A Trusted Platform Module (TPM) Software Stack (TSS) provides logic to
> > compose and submit TPM commands and parse reponses.
> >
> > 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,
> > second, 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,
> > TPM2_LoadExternal, TPM2_Hash, TPM2_VerifySignature,
> > TPM2_PolicyAuthorize, TPM2_TestParms
> >
> > Signed-off-by: Hernan Gatta 
> > Signed-off-by: Gary Lin 
> > Reviewed-by: Stefan Berger 
> > ---
> >  grub-core/tpm2/buffer.c|  145 +++
> >  grub-core/tpm2/mu.c| 1168 
> >  grub-core/tpm2/tcg2.c  |  143 +++
> >  grub-core/tpm2/tpm2.c  | 1048 +
> >  include/grub/tpm2/buffer.h |   65 ++
> >  include/grub/tpm2/internal/functions.h |  156 
> >  include/grub/tpm2/internal/structs.h   |  768 
> >  include/grub/tpm2/internal/types.h |  403 
> >  include/grub/tpm2/mu.h |  396 
> >  include/grub/tpm2/tcg2.h   |   34 +
> >  include/grub/tpm2/tpm2.h   |   34 +
> >  11 files changed, 4360 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
> 
> And I think this patch can be broken up to smaller parts...
> 
Then I'll try to split the patch to 3 patches: buffer, mu/structs, and
TPM2 commands.

Gary Lin

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


Re: [PATCH v17 10/20] tpm2: Add TPM Software Stack (TSS)

2024-06-19 Thread Gary Lin via Grub-devel
On Tue, Jun 18, 2024 at 03:30:03PM +0200, Daniel Kiper wrote:
> On Fri, Jun 14, 2024 at 02:45:43PM +0800, Gary Lin wrote:
> > From: Hernan Gatta 
> >
> > A Trusted Platform Module (TPM) Software Stack (TSS) provides logic to
> > compose and submit TPM commands and parse reponses.
> >
> > 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,
> > second, 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,
> > TPM2_LoadExternal, TPM2_Hash, TPM2_VerifySignature,
> > TPM2_PolicyAuthorize, TPM2_TestParms
> >
> > Signed-off-by: Hernan Gatta 
> > Signed-off-by: Gary Lin 
> > Reviewed-by: Stefan Berger 
> > ---
> >  grub-core/tpm2/buffer.c|  145 +++
> >  grub-core/tpm2/mu.c| 1168 
> >  grub-core/tpm2/tcg2.c  |  143 +++
> >  grub-core/tpm2/tpm2.c  | 1048 +
> >  include/grub/tpm2/buffer.h |   65 ++
> >  include/grub/tpm2/internal/functions.h |  156 
> >  include/grub/tpm2/internal/structs.h   |  768 
> >  include/grub/tpm2/internal/types.h |  403 
> >  include/grub/tpm2/mu.h |  396 
> >  include/grub/tpm2/tcg2.h   |   34 +
> >  include/grub/tpm2/tpm2.h   |   34 +
> >  11 files changed, 4360 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 0..cb9f29497
> > --- /dev/null
> > +++ b/grub-core/tpm2/buffer.c
> 
> I think this together with other TPM2 driver files should go to the
> grub-core/commands/efi/tpm2 directory.
> 
The TPM2 stack is not EFI only. The only EFI related code is in
grub-core/tpm2/tcg2.c which mainly implements how the TPM2 commands to
be submitted. I'd propose to move them to grub-core/commands/tpm2 and
rename tcg2.c to tcg2-efi.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 .
> > + */
> > +
> > +#include 
> > +#include 
> > +
> > +void grub_tpm2_buffer_init (grub_tpm2_buffer_t buffer)
> > +{
> > +  grub_memset (buffer->data, 0xDD, sizeof (buffer->data));
> 
> If you init the buffer->data with 0xDD instead of 0 then it begs for
> a comment. And s/0xDD/0xdd/...
> 
It should be 0. I'll fix in v18.

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

Re: [PATCH v17 06/20] libtasn1: compile into asn1 module

2024-06-18 Thread Gary Lin via Grub-devel
On Mon, Jun 17, 2024 at 05:36:04PM +0200, Daniel Kiper wrote:
> On Fri, Jun 14, 2024 at 02:45:39PM +0800, Gary Lin wrote:
> > From: Daniel Axtens 
> >
> > Create a wrapper file that specifies the module license.
> > Set up the makefile so it is built.
> >
> > Signed-off-by: Daniel Axtens 
> > Signed-off-by: Gary Lin 
> > Reviewed-by: Vladimir Serbinenko 
> 
> Due to amount of changes to the patch I think this RB should be dropped now.
> 
Will drop this RB in v18.

> Though Reviewed-by: Daniel Kiper ...
> 
> Two nits below...
> 
> > ---
> >  autogen.sh | 16 
> >  grub-core/Makefile.core.def| 15 +++
> >  grub-core/lib/libtasn1_wrap/wrap.c | 26 ++
> >  3 files changed, 57 insertions(+)
> >  create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c
> >
> > diff --git a/autogen.sh b/autogen.sh
> > index 195daa541..43272722f 100755
> > --- a/autogen.sh
> > +++ b/autogen.sh
> > @@ -51,6 +51,22 @@ for x in mpi-asm-defs.h mpih-add1.c mpih-sub1.c 
> > mpih-mul1.c mpih-mul2.c mpih-mul
> >  cp grub-core/lib/libgcrypt-grub/mpi/generic/"$x" 
> > grub-core/lib/libgcrypt-grub/mpi/"$x"
> >  done
> >
> > +echo "Importing libtasn1..."
> > +if [ -d grub-core/lib/libtasn1-grub ]; then
> > +  rm -rf grub-core/lib/libtasn1-grub
> > +fi
> > +
> > +mkdir -p grub-core/lib/libtasn1-grub/lib
> > +cp grub-core/lib/libtasn1/lib/*.[ch] grub-core/lib/libtasn1-grub/lib
> > +cp grub-core/lib/libtasn1/libtasn1.h grub-core/lib/libtasn1-grub/
> > +
> > +for patch in \
> > +   0001-libtasn1-disable-code-not-needed-in-grub.patch \
> > +   0002-libtasn1-changes-for-grub-compatibility.patch \
> > +   0003-libtasn1-fix-the-potential-buffer-overrun.patch ; do
> > +  patch -p1 -i grub-core/lib/libtasn1-patches/$patch
> > +done
> > +
> >  echo "Generating Automake input..."
> >
> >  # Automake doesn't like including files from a path outside the project.
> > diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> > index 7fa9446bd..89944004b 100644
> > --- a/grub-core/Makefile.core.def
> > +++ b/grub-core/Makefile.core.def
> > @@ -2605,3 +2605,18 @@ module = {
> >enable = efi;
> >depends = part_gpt;
> >  };
> > +
> > +module = {
> > +  name = asn1;
> > +  common = lib/libtasn1-grub/lib/decoding.c;
> > +  common = lib/libtasn1-grub/lib/coding.c;
> > +  common = lib/libtasn1-grub/lib/element.c;
> > +  common = lib/libtasn1-grub/lib/structure.c;
> > +  common = lib/libtasn1-grub/lib/parser_aux.c;
> > +  common = lib/libtasn1-grub/lib/gstr.c;
> > +  common = lib/libtasn1-grub/lib/errors.c;
> > +  common = lib/libtasn1_wrap/wrap.c;
> > +  cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
> > +  // -Wno-type-limits comes from libtasn1's configure.ac
> 
> Please use "/* ... */" for comments.
> 
Will fix it in v18.

> > +  cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) 
> > -I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/lib/libtasn1-grub/lib 
> > -Wno-type-limits';
> > +};
> > diff --git a/grub-core/lib/libtasn1_wrap/wrap.c 
> > b/grub-core/lib/libtasn1_wrap/wrap.c
> > new file mode 100644
> > index 0..622ba942e
> > --- /dev/null
> > +++ b/grub-core/lib/libtasn1_wrap/wrap.c
> > @@ -0,0 +1,26 @@
> > +/*
> > + *  GRUB  --  GRand Unified Bootloader
> > + *  Copyright (C) 2020 IBM 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 .
> > + */
> > +
> > +#include 
> > +
> > +/*
> > + * libtasn1 is provided under LGPL2.1+, which is compatible
> > + * with GPL3+. As Grub as a whole is under GPL3+, this module
> 
> s/Grub/GRUB/
> 
ditto

Thanks,

Gary Lin

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


Re: [PATCH v17 08/20] libtasn1: Add the documentation

2024-06-18 Thread Gary Lin via Grub-devel
On Mon, Jun 17, 2024 at 06:00:44PM +0200, Daniel Kiper wrote:
> On Fri, Jun 14, 2024 at 02:45:41PM +0800, Gary Lin wrote:
> > Document libtasn1 in docs/grub-dev.texi and add the upgrade steps.
> > Also add the patches to make libtasn1 compatible with grub code.
> >
> > Signed-off-by: Gary Lin 
> > Reviewed-by: Vladimir Serbinenko 
> > ---
> >  docs/grub-dev.texi | 33 +
> >  1 file changed, 33 insertions(+)
> >
> > diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
> > index 1276c5930..62856f0f1 100644
> > --- a/docs/grub-dev.texi
> > +++ b/docs/grub-dev.texi
> > @@ -506,6 +506,7 @@ to update it.
> >  * Gnulib::
> >  * jsmn::
> >  * minilzo::
> > +* libtasn1::
> >  @end menu
> >
> >  @node Gnulib
> > @@ -596,6 +597,38 @@ cp minilzo-2.10/*.[hc] grub-core/lib/minilzo
> >  rm -r minilzo-2.10*
> >  @end example
> >
> > +@node libtasn1
> > +@section libtasn1
> > +
> > +libtasn1 is a library providing Abstract Syntax Notation One (ASN.1, as
> > +specified by the X.680 ITU-T recommendation) parsing and structures 
> > management,
> > +and Distinguished Encoding Rules (DER, as per X.690) encoding and decoding
> > +functions.
> > +
> > +To upgrade to a new version of the libtasn1 library, download the release
> > +tarball and copy the files into the target directory:
> > +
> > +@example
> > +curl -L -O https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz
> > +tar xf libtasn1-4.19.0.tar.gz
> > +rm -rf grub-core/lib/libtasn1/
> > +mkdir -p grub-core/lib/libtasn1/lib/
> > +cp libtasn1-4.19.0/@lbracechar{}README.md,COPYING@rbracechar{} 
> > grub-core/lib/libtasn1/
> > +cp 
> > libtasn1-4.19.0/lib/@lbracechar{}coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h@rbracechar{}
> >  grub-core/lib/libtasn1/lib/
> > +cp libtasn1-4.19.0/lib/includes/libtasn1.h grub-core/lib/libtasn1/
> > +rm -rf libtasn1-4.19.0*
> 
> Please add commands for copying libtasn1 test files here too.
> 
> > +@end example
> > +
> > +After upgrading the library, it may be necessary to apply the patches in
> > +@file{grub-core/lib/libtasn1-patches/} to adjust the code to be compatible 
> > with
> > +grub. These patches were needed to use the current version of libtasn1. The
> 
> s/grub/GRUB/
> 
> > +existing patches may not apply cleanly, apply at all, or even be needed 
> > for a
> > +newer version of the library, and other patches maybe needed due to 
> > changes in
> 
> s/maybe/may be/
> 
> > +the newer version. If existing patches need to be refreshed to apply 
> > cleanly,
> > +please include updated patches as part of the a patch set sent to the list.
> > +If new patches are needed or existing patches are not needed, also please 
> > send
> > +additions or removals as part of any patch set upgrading libtasn1.
> 
Thanks. Will update the commands and fix the typos in v18.

Gary Lin

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


Re: [PATCH v17 07/20] asn1_test: test module for libtasn1

2024-06-18 Thread Gary Lin via Grub-devel
On Mon, Jun 17, 2024 at 05:55:57PM +0200, Daniel Kiper wrote:
> On Fri, Jun 14, 2024 at 02:45:40PM +0800, Gary Lin wrote:
> > From: Daniel Axtens 
> >
> > Import tests from libtasn1 that don't use functionality we don't
> > import. This test module is integrated into functional_test so that the
> > user can run the test in grub shell.
> >
> > This doesn't test the full decoder but that will be exercised in
> > test suites for coming patch sets.
> >
> > Add testcase target in accordance with
> > 5e10be48e5 tests: Add check-native and check-nonnative make targets
> >
> > Cc: Vladimir Serbinenko 
> > Signed-off-by: Daniel Axtens 
> > Signed-off-by: Gary Lin 
> > ---
> >  Makefile.util.def |   6 +
> >  grub-core/Makefile.core.def   |  14 ++
> >  .../tests/asn1/CVE-2018-1000654-1_asn1_tab.h  |  32 +++
> >  .../tests/asn1/CVE-2018-1000654-2_asn1_tab.h  |  36 +++
> >  grub-core/tests/asn1/CVE-2018-1000654.c   |  58 +
> >  grub-core/tests/asn1/Test_overflow.c  | 134 
> >  grub-core/tests/asn1/Test_simple.c| 205 ++
> >  grub-core/tests/asn1/Test_strings.c   | 142 
> >  grub-core/tests/asn1/asn1_test.c  |  49 +
> >  grub-core/tests/asn1/asn1_test.h  |  44 
> >  grub-core/tests/asn1/object-id-decoding.c | 109 ++
> >  grub-core/tests/asn1/object-id-encoding.c | 114 ++
> >  grub-core/tests/asn1/octet-string.c   | 199 +
> >  grub-core/tests/asn1/reproducers.c|  80 +++
> >  grub-core/tests/lib/functional_test.c |   1 +
> >  tests/asn1_test.in|  11 +
> 
> If most of these files come from libtasn1 then you should add a list of
> commands which allows us recreate them from libtasn1 source. And probably
> introduction of original libtasn1 test files should happen in the patch #2.
> 
Ok, I'll import those files in patch #2 and add a follow-up patch for
the grub compatibility.

Gary Lin

> [...]
> 
> > diff --git a/grub-core/tests/asn1/Test_overflow.c 
> > b/grub-core/tests/asn1/Test_overflow.c
> > new file mode 100644
> > index 0..aff6b410a
> > --- /dev/null
> > +++ b/grub-core/tests/asn1/Test_overflow.c
> > @@ -0,0 +1,134 @@
> > +/*
> > + * Copyright (C) 2012-2014 Free Software Foundation, Inc.
> > + *
> > + * This file is part of LIBTASN1.
> > + *
> > + * This program 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.
> > + *
> > + * This program 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 this program.  If not, see .
> > + *
> > + */
> > +
> > +/* Written by Simon Josefsson */
> > +
> > +#include "asn1_test.h"
> > +
> > +int
> > +test_overflow(void)
> > +{
> > +  /* Test that values larger than long are rejected.  This has worked
> > + fine with all versions of libtasn1. */
> > +
> > +  {
> > +unsigned char der[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
> > +long l;
> > +int len;
> > +
> > +l = asn1_get_length_der (der, sizeof der, );
> > +
> > +if (l != -2L)
> > +  {
> > +   grub_printf ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, 
> > len);
> > +   return 1;
> > +  }
> > +  }
> > +
> > +  /* Test that values larger than int but smaller than long are
> > + rejected.  This limitation was introduced with libtasn1 2.12. */
> > +#if (GRUB_LONG_MAX > GRUB_INT_MAX)
> 
> This change suggests it is a mixture of libtasn1 source and GRUB specific
> patches. Please disaggregate them.
> 
> Daniel

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


Re: [PATCH v17 04/20] libtasn1: changes for grub compatibility

2024-06-18 Thread Gary Lin via Grub-devel
On Mon, Jun 17, 2024 at 05:12:12PM +0200, Daniel Kiper wrote:
> On Fri, Jun 14, 2024 at 02:45:37PM +0800, Gary Lin wrote:
> > Based on the patch from "Daniel Axtens "
> >
> > Do a few things to make libtasn1 compile as part of grub:
> >
> >  - remove _asn1_strcat and replace strcat with the bound-checked
> >_asn1_str_cat except the one inside _asn1_str_cat. That strcat is
> >replaced with strcpy.
> >
> >  - adjust header paths in libtasn1.h
> >
> >  - replace a 64 bit division with a call to grub_divmod64, preventing
> >creation of __udivdi3 calls on 32 bit platforms.
> 
> This patch should be split into three separate patches.
> 
> I think this and other libtasn1 fixes should have
> "Signed-off-by: Daniel Axtens " here.
> 
I'll add the SOB to the patches from Daniel Axtens.

Thanks,

Gary Lin

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


[PATCH v17 16/20] cryptodisk: Fallback to passphrase

2024-06-14 Thread Gary Lin via Grub-devel
From: Patrick Colp 

If a protector is specified, but it fails to unlock the disk, fall back
to asking for the passphrase. However, an error was set indicating that
the protector(s) failed. Later code (e.g., LUKS code) fails as
`grub_errno` is now set. Print the existing errors out first, before
proceeding with the passphrase.

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/cryptodisk.c | 17 -
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index cfa3e28ce..ffb41a5fe 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1167,6 +1167,10 @@ grub_cryptodisk_scan_device_real (const char *name,
  ret = cr->recover_key (source, dev, cargs);
  if (ret != GRUB_ERR_NONE)
{
+ /* Reset key data to trigger the passphrase prompt later */
+ cargs->key_data = NULL;
+ cargs->key_len = 0;
+
  part = grub_partition_get_name (source->partition);
  grub_dprintf ("cryptodisk",
"recovered a key from key protector %s but it "
@@ -1192,7 +1196,6 @@ grub_cryptodisk_scan_device_real (const char *name,
  source->name, source->partition != NULL ? "," : "",
  part != NULL ? part : N_("UNKNOWN"), dev->uuid);
   grub_free (part);
-  goto error;
 }
 
   if (cargs->key_len)
@@ -1207,6 +1210,18 @@ grub_cryptodisk_scan_device_real (const char *name,
   unsigned long tries = 3;
   const char *tries_env;
 
+  /*
+   * Print the error from key protectors and clear grub_errno.
+   * Since '--protector' doesn't not coexist with '--password' and
+   * '--key-file', only "cargs->key_len == 0" is expected if all
+   * key protectors fail.
+   */
+  if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
   askpass = 1;
   cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
   if (cargs->key_data == NULL)
-- 
2.35.3


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


[PATCH v17 20/20] tests: Add tpm2_test

2024-06-14 Thread Gary Lin via Grub-devel
For the tpm2 module, the TCG2 command submission function is the only
difference between the a QEMU instance and grub-emu. To test TPM key
unsealing with a QEMU instance, it requires an extra OS image to invoke
grub-protect to seal the LUKS key, rather than a simple grub-shell rescue
CD image. On the other hand, grub-emu can share the emulated TPM device
with the host, so that we can seal the LUKS key on host and test key
unsealing with grub-emu.

This test script firstly creates a simple LUKS image to be loaded as a
loopback device in grub-emu. Then an emulated TPM device is created by
"swtpm chardev" and PCR 0 and 1 are extended.

There are several test cases in the script to test various settings. Each
test case uses grub-protect or tpm2-tools to seal the LUKS password
against PCR 0 and PCR 1. Then grub-emu is launched to load the LUKS image,
try to mount the image with tpm2_key_protector_init and cryptomount, and
verify the result.

Based on the idea from Michael Chang.

Cc: Michael Chang 
Cc: Stefan Berger 
Cc: Glenn Washburn 
Signed-off-by: Gary Lin 
---
 Makefile.util.def|   6 +
 tests/tpm2_test.in   | 389 +++
 tests/util/grub-shell.in |   6 +-
 3 files changed, 400 insertions(+), 1 deletion(-)
 create mode 100644 tests/tpm2_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index c49098256..d586d11be 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1288,6 +1288,12 @@ script = {
   common = tests/asn1_test.in;
 };
 
+script = {
+  testcase = native;
+  name = tpm2_test;
+  common = tests/tpm2_test.in;
+};
+
 program = {
   testcase = native;
   name = example_unit_test;
diff --git a/tests/tpm2_test.in b/tests/tpm2_test.in
new file mode 100644
index 0..aecd1a5a3
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,389 @@
+#! @BUILD_SHEBANG@ -e
+
+# Test GRUBs ability to unseal a LUKS key with TPM 2.0
+# 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 .
+
+grubshell=@builddir@/grub-shell
+
+. "@builddir@/grub-core/modinfo.sh"
+
+if [ x${grub_modinfo_platform} != xemu ]; then
+  exit 77
+fi
+
+builddir="@builddir@"
+
+# Force build directory components
+PATH="${builddir}:${PATH}"
+export PATH
+
+if [ "x${EUID}" = "x" ] ; then
+  EUID=`id -u`
+fi
+
+if [ "${EUID}" != 0 ] ; then
+   echo "not root; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v cryptsetup >/dev/null 2>&1; then
+   echo "cryptsetup not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; then
+   echo "no tpm_vtpm_proxy support; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v swtpm >/dev/null 2>&1; then
+   echo "swtpm not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v tpm2_startup >/dev/null 2>&1; then
+   echo "tpm2-tools not installed; cannot test tpm2."
+   exit 99
+fi
+
+tpm2testdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XX"`" || 
exit 99
+
+disksize=20M
+
+luksfile=${tpm2testdir}/luks.disk
+lukskeyfile=${tpm2testdir}/password.txt
+
+# Choose a low iteration number to reduce the time to decrypt the disk
+csopt="--type luks2 --pbkdf pbkdf2 --iter-time 1000"
+
+tpm2statedir=${tpm2testdir}/tpm
+tpm2ctrl=${tpm2statedir}/ctrl
+tpm2log=${tpm2statedir}/logfile
+
+sealedkey=${tpm2testdir}/sealed.tpm
+
+timeout=20
+
+testoutput=${tpm2testdir}/testoutput
+
+vtext="TEST VERIFIED"
+
+ret=0
+
+# Create the password file
+echo -n "top secret" > "${lukskeyfile}"
+
+# Setup LUKS2 image
+truncate -s ${disksize} "${luksfile}" || exit 99
+cryptsetup luksFormat -q ${csopt} "${luksfile}" "${lukskeyfile}" || exit 99
+
+# Write vtext into the first block of the LUKS2 image
+luksdev=/dev/mapper/`basename "${tpm2testdir}"`
+cryptsetup open --key-file "${lukskeyfile}" "${luksfile}" `basename 
"${luksdev}"` || exit 99
+echo "${vtext}" > "${luksdev}"
+cryptsetup close "${luksdev}"
+
+# Shutdown the swtpm instance on exit
+cleanup() {
+RET=$?
+if [ -e "${tpm2ctrl}" ]; then
+   swtpm_ioctl -s --unix "${tpm2ctrl}"
+fi
+if [ "${RET}" -eq 0 ]; then
+   rm -rf "$tpm2testdir" || :
+fi
+}
+trap cleanup EXIT INT TERM KILL QUIT
+
+mkdir -p "${tpm2statedir}"
+
+# Create the swtpm chardev instance
+swtpm chardev --vtpm-proxy --tpmstate dir="${tpm2statedir}" \
+   --tpm2 --ctrl type=unixio,path="${tpm2ctrl}" \
+   --flags startup-clear 

[PATCH v17 15/20] tpm2: Implement NV index

2024-06-14 Thread Gary Lin via Grub-devel
From: Patrick Colp 

Currently with the TPM2 protector, only SRK mode is supported and
NV index support is just a stub. Implement the NV index option.

Note: This only extends support on the unseal path. grub2_protect
has not been updated. tpm2-tools can be used to insert a key into
the NV index.

An example of inserting a key using tpm2-tools:

  # Get random key.
  tpm2_getrandom 32 > key.dat

  # Create primary object.
  tpm2_createprimary -C o -g sha256 -G ecc -c primary.ctx

  # Create policy object. `pcrs.dat` contains the PCR values to seal against.
  tpm2_startauthsession -S session.dat
  tpm2_policypcr -S session.dat -l sha256:7,11 -f pcrs.dat -L policy.dat
  tpm2_flushcontext session.dat

  # Seal key into TPM.
  cat key.dat | tpm2_create -C primary.ctx -u key.pub -r key.priv -L policy.dat 
-i-
  tpm2_load -C primary.ctx -u key.pub -r key.priv -n sealing.name -c sealing.ctx
  tpm2_evictcontrol -C o -c sealing.ctx 0x8100

Then to unseal the key in grub, add this to grub.cfg:

  tpm2_key_protector_init --mode=nv --nvindex=0x8100 --pcrs=7,11
  cryptomount -u  --protector tpm2

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/tpm2/module.c | 25 -
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
index 115c74814..38e947985 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -1034,12 +1034,27 @@ 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;
+  TPM_HANDLE sealed_handle = ctx->nv;
+  tpm2key_policy_t policy_seq = NULL;
+  grub_err_t err;
+
+  /* Create a basic policy sequence based on the given PCR selection */
+  err = grub_tpm2_protector_simple_policy_seq (ctx, _seq);
+  if (err != GRUB_ERR_NONE)
+goto exit;
+
+  err = grub_tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size);
+
+  /* Pop error messages on success */
+  if (err == GRUB_ERR_NONE)
+while (grub_error_pop ());
+
+exit:
+  TPM2_FlushContext (sealed_handle);
 
-  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-N_("NV Index mode is not implemented yet"));
+  grub_tpm2key_free_policy_seq (policy_seq);
+
+  return err;
 }
 
 static grub_err_t
-- 
2.35.3


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


[PATCH v17 17/20] cryptodisk: wipe out the cached keys from protectors

2024-06-14 Thread Gary Lin via Grub-devel
An attacker may insert a malicious disk with the same crypto UUID and
trick grub2 to mount the fake root. Even though the key from the key
protector fails to unlock the fake root, it's not wiped out cleanly so
the attacker could dump the memory to retrieve the secret key. To defend
such attack, wipe out the cached key when we don't need it.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/cryptodisk.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index ffb41a5fe..5032d6b70 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1399,7 +1399,11 @@ grub_cryptodisk_clear_key_cache (struct 
grub_cryptomount_args *cargs)
 return;
 
   for (i = 0; cargs->protectors[i]; i++)
-grub_free (cargs->key_cache[i].key);
+{
+  if (cargs->key_cache[i].key)
+   grub_memset (cargs->key_cache[i].key, 0, cargs->key_cache[i].key_len);
+  grub_free (cargs->key_cache[i].key);
+}
 
   grub_free (cargs->key_cache);
 }
-- 
2.35.3


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


[PATCH v17 06/20] libtasn1: compile into asn1 module

2024-06-14 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

Create a wrapper file that specifies the module license.
Set up the makefile so it is built.

Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
Reviewed-by: Vladimir Serbinenko 
---
 autogen.sh | 16 
 grub-core/Makefile.core.def| 15 +++
 grub-core/lib/libtasn1_wrap/wrap.c | 26 ++
 3 files changed, 57 insertions(+)
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c

diff --git a/autogen.sh b/autogen.sh
index 195daa541..43272722f 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -51,6 +51,22 @@ for x in mpi-asm-defs.h mpih-add1.c mpih-sub1.c mpih-mul1.c 
mpih-mul2.c mpih-mul
 cp grub-core/lib/libgcrypt-grub/mpi/generic/"$x" 
grub-core/lib/libgcrypt-grub/mpi/"$x"
 done
 
+echo "Importing libtasn1..."
+if [ -d grub-core/lib/libtasn1-grub ]; then
+  rm -rf grub-core/lib/libtasn1-grub
+fi
+
+mkdir -p grub-core/lib/libtasn1-grub/lib
+cp grub-core/lib/libtasn1/lib/*.[ch] grub-core/lib/libtasn1-grub/lib
+cp grub-core/lib/libtasn1/libtasn1.h grub-core/lib/libtasn1-grub/
+
+for patch in \
+   0001-libtasn1-disable-code-not-needed-in-grub.patch \
+   0002-libtasn1-changes-for-grub-compatibility.patch \
+   0003-libtasn1-fix-the-potential-buffer-overrun.patch ; do
+  patch -p1 -i grub-core/lib/libtasn1-patches/$patch
+done
+
 echo "Generating Automake input..."
 
 # Automake doesn't like including files from a path outside the project.
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 7fa9446bd..89944004b 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2605,3 +2605,18 @@ module = {
   enable = efi;
   depends = part_gpt;
 };
+
+module = {
+  name = asn1;
+  common = lib/libtasn1-grub/lib/decoding.c;
+  common = lib/libtasn1-grub/lib/coding.c;
+  common = lib/libtasn1-grub/lib/element.c;
+  common = lib/libtasn1-grub/lib/structure.c;
+  common = lib/libtasn1-grub/lib/parser_aux.c;
+  common = lib/libtasn1-grub/lib/gstr.c;
+  common = lib/libtasn1-grub/lib/errors.c;
+  common = lib/libtasn1_wrap/wrap.c;
+  cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+  // -Wno-type-limits comes from libtasn1's configure.ac
+  cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) 
-I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/lib/libtasn1-grub/lib 
-Wno-type-limits';
+};
diff --git a/grub-core/lib/libtasn1_wrap/wrap.c 
b/grub-core/lib/libtasn1_wrap/wrap.c
new file mode 100644
index 0..622ba942e
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/wrap.c
@@ -0,0 +1,26 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020 IBM 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 .
+ */
+
+#include 
+
+/*
+ * libtasn1 is provided under LGPL2.1+, which is compatible
+ * with GPL3+. As Grub as a whole is under GPL3+, this module
+ * is therefore under GPL3+ also.
+ */
+GRUB_MOD_LICENSE ("GPLv3+");
-- 
2.35.3


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


[PATCH v17 00/20] Automatic Disk Unlock with TPM2

2024-06-14 Thread Gary Lin via Grub-devel
GIT repo for v17: https://github.com/lcp/grub2/tree/tpm2-unlock-v17

This patch series is based on "Automatic TPM Disk Unlock"(*1) posted by
Hernan Gatta to introduce the key protector framework and TPM2 stack
to GRUB2, and this could be a useful feature for the systems to
implement full disk encryption.

To support TPM 2.0 Key File format(*2), patch 1~4,6,7 are grabbed from
Daniel Axtens's "appended signature secure boot support" (*3) to import
libtasn1 into grub2. Besides, the libtasn1 version is upgraded to
4.19.0 instead of 4.16.0 in the original patch.

Patch 5 fixes a potential buffer overrun in libtasn1.
(https://gitlab.com/gnutls/libtasn1/-/issues/49)

Patch 8 adds the document for libtasn1 and the steps to upgrade the
library.

Patch 9~13 are Hernan Gatta's patches with the follow-up fixes and
improvements:
- Converting 8 spaces into 1 tab
- Merging the minor build fix from Michael Chang
  - Replacing "lu" with "PRIuGRUB_SIZE" for grub_dprintf
  - Adding "enable = efi" to the tpm2 module in grub-core/Makefile.core.def
- Rebasing "cryptodisk: Support key protectors" to the git master
- Removing the measurement on the sealed key
  - Based on the patch from Olaf Kirch 
- Adjusting the input parameters of TPM2_EvictControl to match the order
  in "TCG TPM2 Part3 Commands"
- Declaring the input arguments of TPM2 functions as const
- Resending TPM2 commands on TPM_RC_RETRY
- Adding checks for the parameters of TPM2 commands
- Packing the missing authorization command for TPM2_PCR_Read
- Tweaking the TPM2 command functions to allow some parameters to be
  NULL so that we don't have to declare empty variables
- Only enabling grub-protect for "efi" since the TPM2 stack currently
  relies on the EFI TCG2 protocol to send TPM2 commands
- Using grub_cpu_to_be*() in the TPM2 stack instead of grub_swap_bytes*()
  which may cause problems in big-indian machines
- Changing the short name of "--protector" of "cryptomount" from "-k" to
  "-P" to avoid the conflict with "--key-file"
- Supporting TPM 2.0 Key File Format besides the raw sealed key
- Adding the external libtasn1 dependency to grub-protect to write the
  TPM 2.0 Key files
- Extending the TPM2 TSS stack to support authorized policy

Patch 14 implements the authorized policy support.

Patch 15 implements the missing NV index mode. (Thanks to Patrick Colp)

Patch 16 improves the 'cryptomount' command to fall back to the
passphrase mode when the key protector fails to unlock the encrypted
partition. (Another patch from Patrick Colp)

Patch 17 and 18 fix the potential security issues spotted by Fabian Vogt.

Patch 19 and 20 implement the TPM key unsealing testcases.

To utilize the TPM2 key protector to unlock the encrypted partition
(sdb1), here are the sample steps:

1. Add an extra random key for LUKS (luks-key)
   $ dd if=/dev/urandom of=luks-key bs=1 count=32
   $ sudo cryptsetup luksAddKey /dev/sdb1 luks-key --pbkdf=pbkdf2

2. Seal the key
   $ sudo grub-protect --action=add \
   --protector=tpm2 \
   --tpm2key \
   --tpm2-keyfile=luks-key \
   --tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm

3. Unseal the key with the proper commands in grub.cfg:
   tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
   cryptomount -u  -P tpm2

(*1) https://lists.gnu.org/archive/html/grub-devel/2022-02/msg6.html
(*2) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*3) https://lists.gnu.org/archive/html/grub-devel/2021-06/msg00044.html

v17:
- Fixing the missing space in strncat()
- Updating the steps to import libtasn1
- Moving libtasn1.h to grub-core/lib/libtasn1/ and fixing the affected
  patches
  - libtasn1.h is included in tpm2key.h, so there is no need to include
the header again in module.c and tpm2key.c. 
- Applying the libtasn1 patches in a different way
  - Instead of applying the patches directly in the grub2 source code,
libtasn1 is copied to grub-core/lib/libtasn1-grub by autogen.sh and
then the script applies the libtasn1 patches to libtasn1-grub.
  - The patch files in patch 3, 4, and 5 are created by the following
repo against the libtasn1-4.19.0-base tag.
https://github.com/lcp/grub2/tree/import-libtasn1-4.19.0
- Correcting the description of the tpm2_test commit to replace
  swtpm_cuse with "swtpm chardev"

v16:
- https://lists.gnu.org/archive/html/grub-devel/2024-05/msg00093.html
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-v16
- Rebasing patch 6, 12, and 16 to fix the conflicts with the latest
  master branch
- Changes in cryptodisk:
  - Disallowing that both OPTION_KEYFILE and OPTION_PROTECTOR are set
since the key data for "--key-file" would be overwritten by the key
protectors
  - Resetting the cargs key data when the key from a key protector
doesn't work for the disk to ensure the passphrase prompt will be
triggered later
  - Adding the comment to address why grub_errno is only 

[PATCH v17 19/20] tpm2: Enable tpm2 module for grub-emu

2024-06-14 Thread Gary Lin via Grub-devel
As a preparation to test TPM 2.0 TSS stack with grub-emu, the new
option, --tpm-device, is introduced to specify the TPM device for
grub-emu so that grub-emu can share the emulated TPM device with
the host.

Since grub-emu can directly access the device node on host, it's easy to
implement the essential TCG2 command submission function with the
read/write functions and enable tpm2 module for grub-emu, so that we can
further test TPM key unsealing with grub-emu.

Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/Makefile.core.def |  2 ++
 grub-core/kern/emu/main.c   | 11 +++-
 grub-core/kern/emu/misc.c   | 51 
 grub-core/tpm2/tcg2-emu.c   | 52 +
 include/grub/emu/misc.h |  5 
 5 files changed, 120 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/tpm2/tcg2-emu.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 4adfbd175..fcd7addac 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2576,7 +2576,9 @@ module = {
   common = tpm2/tpm2key.c;
   common = tpm2/tpm2key_asn1_tab.c;
   efi = tpm2/tcg2.c;
+  emu = tpm2/tcg2-emu.c;
   enable = efi;
+  enable = emu;
   cppflags = '-I$(srcdir)/lib/libtasn1-grub';
 };
 
diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
index 855b11c3d..c10838613 100644
--- a/grub-core/kern/emu/main.c
+++ b/grub-core/kern/emu/main.c
@@ -55,7 +55,7 @@
 static jmp_buf main_env;
 
 /* Store the prefix specified by an argument.  */
-static char *root_dev = NULL, *dir = NULL;
+static char *root_dev = NULL, *dir = NULL, *tpm_dev = NULL;
 
 grub_addr_t grub_modbase = 0;
 
@@ -108,6 +108,7 @@ static struct argp_option options[] = {
   {"verbose", 'v', 0,  0, N_("print verbose messages."), 0},
   {"hold", 'H', N_("SECS"),  OPTION_ARG_OPTIONAL, N_("wait until a 
debugger will attach"), 0},
   {"kexec",   'X', 0,  0, N_("use kexec to boot Linux kernels via 
systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0},
+  {"tpm-device",  't', N_("DEV"), 0, N_("Set TPM device."), 0},
   { 0, 0, 0, 0, 0, 0 }
 };
 
@@ -168,6 +169,10 @@ argp_parser (int key, char *arg, struct argp_state *state)
 case 'X':
   grub_util_set_kexecute ();
   break;
+case 't':
+  free (tpm_dev);
+  tpm_dev = xstrdup (arg);
+  break;
 
 case ARGP_KEY_ARG:
   {
@@ -276,6 +281,9 @@ main (int argc, char *argv[])
 
   dir = xstrdup (dir);
 
+  if (tpm_dev)
+grub_util_tpm_open (tpm_dev);
+
   /* Start GRUB!  */
   if (setjmp (main_env) == 0)
 grub_main ();
@@ -283,6 +291,7 @@ main (int argc, char *argv[])
   grub_fini_all ();
   grub_hostfs_fini ();
   grub_host_fini ();
+  grub_util_tpm_close ();
 
   grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
 
diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
index 521220b49..1db24fde7 100644
--- a/grub-core/kern/emu/misc.c
+++ b/grub-core/kern/emu/misc.c
@@ -28,6 +28,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include 
 #include 
@@ -41,6 +43,8 @@
 int verbosity;
 int kexecute;
 
+static int grub_util_tpm_fd = -1;
+
 void
 grub_util_warn (const char *fmt, ...)
 {
@@ -230,3 +234,50 @@ grub_util_get_kexecute (void)
 {
   return kexecute;
 }
+
+grub_err_t
+grub_util_tpm_open (const char *tpm_dev)
+{
+  if (grub_util_tpm_fd != -1)
+return GRUB_ERR_NONE;
+
+  grub_util_tpm_fd = open (tpm_dev, O_RDWR);
+  if (grub_util_tpm_fd == -1)
+grub_util_error (_("cannot open TPM device '%s': %s"), tpm_dev, strerror 
(errno));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_util_tpm_close (void)
+{
+  int err;
+
+  if (grub_util_tpm_fd == -1)
+return GRUB_ERR_NONE;
+
+  err = close (grub_util_tpm_fd);
+  if (err != GRUB_ERR_NONE)
+grub_util_error (_("cannot close TPM device: %s"), strerror (errno));
+
+  grub_util_tpm_fd = -1;
+  return GRUB_ERR_NONE;
+}
+
+grub_size_t
+grub_util_tpm_read (void *output, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return read (grub_util_tpm_fd, output, size);
+}
+
+grub_size_t
+grub_util_tpm_write (const void *input, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return write (grub_util_tpm_fd, input, size);
+}
diff --git a/grub-core/tpm2/tcg2-emu.c b/grub-core/tpm2/tcg2-emu.c
new file mode 100644
index 0..0d7b8b16e
--- /dev/null
+++ b/grub-core/tpm2/tcg2-emu.c
@@ -0,0 +1,52 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 SUSE LLC
+ *
+ *  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 

[PATCH v17 11/20] key_protector: Add TPM2 Key Protector

2024-06-14 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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

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

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

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

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

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

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

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

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

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

The TPM2 key protector registers two commands:

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

- tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init.

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

For instance, to unseal the raw sealed key file:

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

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

Or, to unseal the TPM 2.0 Key file:

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

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

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

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

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

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

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

Cc: Stefan Berger 
Cc: James Bottomley 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/Makefile.core.def   |   14 +
 grub-core/tpm2/args.c |  140 
 grub-core/tpm2/module.c   | 1225 

[PATCH v17 12/20] cryptodisk: Support key protectors

2024-06-14 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

Add a new parameter to cryptomount to support the key protectors framework: -P.
The parameter is used to automatically retrieve a key from specified key
protectors. The parameter may be repeated to specify any number of key
protectors. These are tried in order until one provides a usable key for any
given disk.

Signed-off-by: Hernan Gatta 
Signed-off-by: Michael Chang 
Signed-off-by: Gary Lin 
Reviewed-by: Glenn Washburn 
Reviewed-by: Stefan Berger 
---
 Makefile.util.def   |   1 +
 grub-core/disk/cryptodisk.c | 243 ++--
 include/grub/cryptodisk.h   |  16 +++
 3 files changed, 196 insertions(+), 64 deletions(-)

diff --git a/Makefile.util.def b/Makefile.util.def
index fe70cf9bd..fb82f59a0 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -40,6 +40,7 @@ library = {
   common = grub-core/disk/luks.c;
   common = grub-core/disk/luks2.c;
   common = grub-core/disk/geli.c;
+  common = grub-core/disk/key_protector.c;
   common = grub-core/disk/cryptodisk.c;
   common = grub-core/disk/AFSplitter.c;
   common = grub-core/lib/pbkdf2.c;
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index f98acade6..cfa3e28ce 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #ifdef GRUB_UTIL
 #include 
@@ -45,7 +46,8 @@ enum
 OPTION_KEYFILE,
 OPTION_KEYFILE_OFFSET,
 OPTION_KEYFILE_SIZE,
-OPTION_HEADER
+OPTION_HEADER,
+OPTION_PROTECTOR
   };
 
 static const struct grub_arg_option options[] =
@@ -59,6 +61,8 @@ static const struct grub_arg_option options[] =
 {"keyfile-offset", 'O', 0, N_("Key file offset (bytes)"), 0, ARG_TYPE_INT},
 {"keyfile-size", 'S', 0, N_("Key file data size (bytes)"), 0, 
ARG_TYPE_INT},
 {"header", 'H', 0, N_("Read header from file"), 0, ARG_TYPE_STRING},
+{"protector", 'P', GRUB_ARG_OPTION_REPEATABLE,
+ N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING},
 {0, 0, 0, 0, 0, 0}
   };
 
@@ -1062,6 +1066,7 @@ grub_cryptodisk_scan_device_real (const char *name,
   grub_err_t ret = GRUB_ERR_NONE;
   grub_cryptodisk_t dev;
   grub_cryptodisk_dev_t cr;
+  int i;
   struct cryptodisk_read_hook_ctx read_hook_data = {0};
   int askpass = 0;
   char *part = NULL;
@@ -1114,79 +1119,151 @@ grub_cryptodisk_scan_device_real (const char *name,
   goto error_no_close;
 if (!dev)
   continue;
+break;
+  }
 
-if (cargs->key_len)
-  {
-   ret = cr->recover_key (source, dev, cargs);
-   if (ret != GRUB_ERR_NONE)
- goto error;
-  }
-else
-  {
-   /* Get the passphrase from the user, if no key data. */
-   unsigned long tries = 3;
-   const char *tries_env;
+  if (dev == NULL)
+{
+  grub_error (GRUB_ERR_BAD_MODULE,
+ "no cryptodisk module can handle this device");
+  goto error_no_close;
+}
 
-   askpass = 1;
-   cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
-   if (cargs->key_data == NULL)
- goto error_no_close;
+  if (cargs->protectors)
+{
+  for (i = 0; cargs->protectors[i]; i++)
+   {
+ if (cargs->key_cache[i].invalid)
+   continue;
 
-   tries_env = grub_env_get ("cryptodisk_passphrase_tries");
-   if (tries_env != NULL && tries_env[0] != '\0')
- {
-   unsigned long tries_env_val;
-   const char *p;
+ if (cargs->key_cache[i].key == NULL)
+   {
+ ret = grub_key_protector_recover_key (cargs->protectors[i],
+   >key_cache[i].key,
+   
>key_cache[i].key_len);
+ if (ret != GRUB_ERR_NONE)
+   {
+ if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
+ grub_dprintf ("cryptodisk",
+   "failed to recover a key from key protector "
+   "%s, will not try it again for any other "
+   "disks, if any, during this invocation of "
+   "cryptomount\n",
+   cargs->protectors[i]);
+
+ cargs->key_cache[i].invalid = 1;
+ continue;
+   }
+   }
 
-   tries_env_val = grub_strtoul (tries_env, , 0);
-   if (*p == '\0' && tries_env_val != ~0UL)
- tries = tries_env_val;
-   else
- grub_printf_ (N_("Invalid cryptodisk_passphrase_tries value `%s'. 
Defaulting to %lu.\n"),
-   tries_env,
-   tries);
- }
+ cargs->key_data = cargs->key_cache[i].key;
+ cargs->key_len = cargs->key_cache[i].key_len;
 
-   for (; tries > 0; tries--)
- {
- 

[PATCH v17 14/20] tpm2: Support authorized policy

2024-06-14 Thread Gary Lin via Grub-devel
This commit handles the TPM2_PolicyAuthorize command from the key file
in TPM 2.0 Key File format.

TPM2_PolicyAuthorize is the essential command to support authorized
policy which allows the users to sign TPM policies with their own keys.
Per TPM 2.0 Key File(*1), CommandPolicy for TPM2_PolicyAuthorize
comprises 'TPM2B_PUBLIC pubkey', 'TPM2B_DIGEST policy_ref', and
'TPMT_SIGNATURE signature'. To verify the signature, the current policy
digest is hashed with the hash algorithm written in 'signature', and then
'signature' is verified with the hashed policy digest and 'pubkey'. Once
TPM accepts 'signature', TPM2_PolicyAuthorize is invoked to authorize the
signed policy.

To create the key file with authorized policy, here are the pcr-oracle(*2)
commands:

  # Generate the RSA key and create the authorized policy file
  $ pcr-oracle \
--rsa-generate-key \
--private-key policy-key.pem \
--auth authorized.policy \
create-authorized-policy 0,2,4,7,9

  # Seal the secret with the authorized policy
  $ pcr-oracle \
--key-format tpm2.0 \
--auth authorized.policy \
--input disk-secret.txt \
--output sealed.key \
seal-secret

  # Sign the predicted PCR policy
  $ pcr-oracle \
--key-format tpm2.0 \
--private-key policy-key.pem \
--from eventlog \
--stop-event "grub-file=grub.cfg" \
--after \
--input sealed.key \
--output sealed.tpm \
sign 0,2,4,7,9

Then specify the key file and the key protector to grub.cfg in the EFI
system partition:

tpm2_key_protector_init -a RSA --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

For any change in the boot components, just run the 'sign' command again
to update the signature in sealed.tpm, and TPM can unseal the key file
with the updated PCR policy.

(*1) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*2) https://github.com/okirch/pcr-oracle

Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/tpm2/module.c | 84 +
 1 file changed, 84 insertions(+)

diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
index d85e5fb8c..115c74814 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -649,6 +649,87 @@ grub_tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION 
session,
   return GRUB_ERR_NONE;
 }
 
+static grub_err_t
+grub_tpm2_protector_policyauthorize (TPMI_SH_AUTH_SESSION session,
+struct grub_tpm2_buffer *cmd_buf)
+{
+  TPM2B_PUBLIC pubkey;
+  TPM2B_DIGEST policy_ref;
+  TPMT_SIGNATURE signature;
+  TPM2B_DIGEST pcr_policy;
+  TPM2B_DIGEST pcr_policy_hash;
+  TPMI_ALG_HASH sig_hash;
+  TPMT_TK_VERIFIED verification_ticket;
+  TPM_HANDLE pubkey_handle = 0;
+  TPM2B_NAME pubname;
+  TPM_RC rc;
+  grub_err_t err;
+
+  grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (cmd_buf, );
+  grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, _ref);
+  grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (cmd_buf, );
+  if (cmd_buf->error != 0)
+return grub_error (GRUB_ERR_BAD_ARGUMENT,
+  N_("Failed to unmarshal the buffer for 
TPM2_PolicyAuthorize"));
+
+  /* Retrieve Policy Digest */
+  rc = TPM2_PolicyGetDigest (session, NULL, _policy, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to get policy digest (TPM2_PolicyGetDigest: 
0x%x)."),
+  rc);
+
+  /* Calculate the digest of the polcy for VerifySignature */
+  sig_hash = TPMT_SIGNATURE_get_hash_alg ();
+  if (sig_hash == TPM_ALG_NULL)
+return grub_error (GRUB_ERR_BAD_ARGUMENT,
+  N_("Failed to get the hash algorithm of the signature"));
+
+  rc = TPM2_Hash (NULL, (TPM2B_MAX_BUFFER *)_policy, sig_hash,
+ TPM_RH_NULL, _policy_hash, NULL, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to create PCR policy hash (TPM2_Hash: 0x%x)"),
+  rc);
+
+  /* Load the public key */
+  rc = TPM2_LoadExternal (NULL, NULL, , TPM_RH_OWNER,
+ _handle, , NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to load public key (TPM2_LoadExternal: 
0x%x)"),
+  rc);
+
+  /* Verify the signature against the public key and the policy digest */
+  rc = TPM2_VerifySignature (pubkey_handle, NULL, _policy_hash, ,
+_ticket, NULL);
+  if (rc != TPM_RC_SUCCESS)
+{
+  err = grub_error (GRUB_ERR_BAD_DEVICE,
+   N_("Failed to verify signature (TPM2_VerifySignature: 
0x%x)"),
+   rc);
+  goto error;
+}
+
+  /* Authorize the signed policy with the public key and the verification 
ticket */
+  rc = TPM2_PolicyAuthorize (session, NULL, _policy, _ref, ,
+_ticket, NULL);
+  if (rc != 

[PATCH v17 18/20] diskfilter: look up cryptodisk devices first

2024-06-14 Thread Gary Lin via Grub-devel
When using disk auto-unlocking with TPM 2.0, the typical grub.cfg may
look like this:

  tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
  cryptomount -u  -P tpm2
  search --fs-uuid --set=root 

Since the disk search order is based on the order of module loading, the
attacker could insert a malicious disk with the same FS-UUID root to
trick grub2 to boot into the malicious root and further dump memory to
steal the unsealed key.

Do defend against such an attack, we can specify the hint provided by
'grub-probe' to search the encrypted partition first:

search --fs-uuid --set=root --hint='cryptouuid/' 

However, for LVM on an encrypted partition, the search hint provided by
'grub-probe' is:

  --hint='lvmid//'

It doesn't guarantee to look up the logical volume from the encrypted
partition, so the attacker may have the chance to fool grub2 to boot
into the malicious disk.

To minimize the attack surface, this commit tweaks the disk device search
in diskfilter to look up cryptodisk devices first and then others, so
that the auto-unlocked disk will be found first, not the attacker's disk.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/diskfilter.c | 35 ++-
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c
index 21e239511..df1992305 100644
--- a/grub-core/disk/diskfilter.c
+++ b/grub-core/disk/diskfilter.c
@@ -226,15 +226,32 @@ scan_devices (const char *arname)
   int need_rescan;
 
   for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++)
-for (p = grub_disk_dev_list; p; p = p->next)
-  if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
- && p->disk_iterate)
-   {
- if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
-   return;
- if (arname && is_lv_readable (find_lv (arname), 1))
-   return;
-   }
+{
+  /* look up the crytodisk devices first */
+  for (p = grub_disk_dev_list; p; p = p->next)
+   if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID
+   && p->disk_iterate)
+ {
+   if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
+ return;
+   if (arname && is_lv_readable (find_lv (arname), 1))
+ return;
+   break;
+ }
+
+  /* check the devices other than crytodisk */
+  for (p = grub_disk_dev_list; p; p = p->next)
+   if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ continue;
+   else if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
+   && p->disk_iterate)
+ {
+   if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
+ return;
+   if (arname && is_lv_readable (find_lv (arname), 1))
+ return;
+ }
+}
 
   scan_depth = 0;
   need_rescan = 1;
-- 
2.35.3


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


[PATCH v17 04/20] libtasn1: changes for grub compatibility

2024-06-14 Thread Gary Lin via Grub-devel
Based on the patch from "Daniel Axtens "

Do a few things to make libtasn1 compile as part of grub:

 - remove _asn1_strcat and replace strcat with the bound-checked
   _asn1_str_cat except the one inside _asn1_str_cat. That strcat is
   replaced with strcpy.

 - adjust header paths in libtasn1.h

 - replace a 64 bit division with a call to grub_divmod64, preventing
   creation of __udivdi3 calls on 32 bit platforms.

Signed-off-by: Gary Lin 
---
 ...tasn1-changes-for-grub-compatibility.patch | 125 ++
 1 file changed, 125 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0002-libtasn1-changes-for-grub-compatibility.patch

diff --git 
a/grub-core/lib/libtasn1-patches/0002-libtasn1-changes-for-grub-compatibility.patch
 
b/grub-core/lib/libtasn1-patches/0002-libtasn1-changes-for-grub-compatibility.patch
new file mode 100644
index 0..23c96b0ac
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0002-libtasn1-changes-for-grub-compatibility.patch
@@ -0,0 +1,125 @@
+From 1d62aa76a833855f58261fc69f40bd7763182274 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens 
+Date: Fri, 1 May 2020 20:44:29 +1000
+Subject: [PATCH 2/3] libtasn1: changes for grub compatibility
+
+Do a few things to make libtasn1 compile as part of grub:
+
+ - remove _asn1_strcat and replace strcat with the bound-checked
+   _asn1_str_cat except the one inside _asn1_str_cat. That strcat is
+   replaced with strcpy.
+
+ - adjust header paths in libtasn1.h
+
+ - replace a 64 bit division with a call to grub_divmod64, preventing
+   creation of __udivdi3 calls on 32 bit platforms.
+
+Cc: Vladimir Serbinenko 
+Signed-off-by: Daniel Axtens 
+Signed-off-by: Gary Lin 
+Reviewed-by: Stefan Berger 
+---
+ grub-core/lib/libtasn1-grub/lib/decoding.c   | 8 
+ grub-core/lib/libtasn1-grub/lib/element.c| 2 +-
+ grub-core/lib/libtasn1-grub/lib/gstr.c   | 2 +-
+ grub-core/lib/libtasn1-grub/lib/int.h| 1 -
+ grub-core/lib/libtasn1-grub/lib/parser_aux.c | 2 +-
+ grub-core/lib/libtasn1-grub/libtasn1.h   | 5 ++---
+ 6 files changed, 9 insertions(+), 11 deletions(-)
+
+diff --git a/grub-core/lib/libtasn1-grub/lib/decoding.c 
b/grub-core/lib/libtasn1-grub/lib/decoding.c
+index bf9cb13ac..51859fe36 100644
+--- a/grub-core/lib/libtasn1-grub/lib/decoding.c
 b/grub-core/lib/libtasn1-grub/lib/decoding.c
+@@ -2016,8 +2016,8 @@ asn1_expand_octet_string (asn1_node_const definitions, 
asn1_node * element,
+ (p2->type & CONST_ASSIGN))
+   {
+ strcpy (name, definitions->name);
+-strcat (name, ".");
+-strcat (name, p2->name);
++_asn1_str_cat (name, sizeof (name), ".");
++_asn1_str_cat (name, sizeof (name), p2->name);
+ 
+ len = sizeof (value);
+ result = asn1_read_value (definitions, name, value, );
+@@ -2034,8 +2034,8 @@ asn1_expand_octet_string (asn1_node_const definitions, 
asn1_node * element,
+ if (p2)
+   {
+ strcpy (name, definitions->name);
+-strcat (name, ".");
+-strcat (name, p2->name);
++_asn1_str_cat (name, sizeof (name), ".");
++_asn1_str_cat (name, sizeof (name), p2->name);
+ 
+ result = asn1_create_element (definitions, name, );
+ if (result == ASN1_SUCCESS)
+diff --git a/grub-core/lib/libtasn1-grub/lib/element.c 
b/grub-core/lib/libtasn1-grub/lib/element.c
+index bc4c3c8d7..8694fecb9 100644
+--- a/grub-core/lib/libtasn1-grub/lib/element.c
 b/grub-core/lib/libtasn1-grub/lib/element.c
+@@ -688,7 +688,7 @@ asn1_write_value (asn1_node node_root, const char *name,
+ return ASN1_MEM_ERROR; \
+ } else { \
+ /* this strcat is checked */ \
+-if (ptr) _asn1_strcat (ptr, data); \
++if (ptr) _asn1_str_cat ((char *)ptr, ptr_size, (const char 
*)data); \
+ }
+ 
+ /**
+diff --git a/grub-core/lib/libtasn1-grub/lib/gstr.c 
b/grub-core/lib/libtasn1-grub/lib/gstr.c
+index eef419554..a9c16f5d3 100644
+--- a/grub-core/lib/libtasn1-grub/lib/gstr.c
 b/grub-core/lib/libtasn1-grub/lib/gstr.c
+@@ -36,7 +36,7 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char 
*src)
+ 
+   if (dest_tot_size - dest_size > str_size)
+ {
+-  strcat (dest, src);
++  strcpy (dest + dest_size, src);
+ }
+   else
+ {
+diff --git a/grub-core/lib/libtasn1-grub/lib/int.h 
b/grub-core/lib/libtasn1-grub/lib/int.h
+index d94d51c8c..cadd80df6 100644
+--- a/grub-core/lib/libtasn1-grub/lib/int.h
 b/grub-core/lib/libtasn1-grub/lib/int.h
+@@ -115,7 +115,6 @@ extern const tag_and_class_st _asn1_tags[];
+ # define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
+ # define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
+ # define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
+-# define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
+ 
+ # if SIZEOF_UNSIGNED_LONG_INT == 8
+ #  define _asn1_strtou64(n,e,b) 

[PATCH v17 05/20] libtasn1: fix the potential buffer overrun

2024-06-14 Thread Gary Lin via Grub-devel
In _asn1_tag_der(), the first while loop for the long form may end up
with a 'k' value with 'ASN1_MAX_TAG_SIZE' and cause the buffer overrun
in the second while loop. This commit tweaks the conditional check to
avoid producing a too large 'k'.

This is a quick fix and may differ from the official upstream fix.

libtasn1 issue: https://gitlab.com/gnutls/libtasn1/-/issues/49

Signed-off-by: Gary Lin 
---
 ...sn1-fix-the-potential-buffer-overrun.patch | 35 +++
 1 file changed, 35 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0003-libtasn1-fix-the-potential-buffer-overrun.patch

diff --git 
a/grub-core/lib/libtasn1-patches/0003-libtasn1-fix-the-potential-buffer-overrun.patch
 
b/grub-core/lib/libtasn1-patches/0003-libtasn1-fix-the-potential-buffer-overrun.patch
new file mode 100644
index 0..e9789f67a
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0003-libtasn1-fix-the-potential-buffer-overrun.patch
@@ -0,0 +1,35 @@
+From 636c1cbfe1e244ba887c6e1a18fabbcb3bb8b1b8 Mon Sep 17 00:00:00 2001
+From: Gary Lin 
+Date: Mon, 8 Apr 2024 14:57:21 +0800
+Subject: [PATCH 3/3] libtasn1: fix the potential buffer overrun
+
+In _asn1_tag_der(), the first while loop for the long form may end up
+with a 'k' value with 'ASN1_MAX_TAG_SIZE' and cause the buffer overrun
+in the second while loop. This commit tweaks the conditional check to
+avoid producing a too large 'k'.
+
+This is a quick fix and may differ from the official upstream fix.
+
+libtasn1 issue: https://gitlab.com/gnutls/libtasn1/-/issues/49
+
+Signed-off-by: Gary Lin 
+---
+ grub-core/lib/libtasn1-grub/lib/coding.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/grub-core/lib/libtasn1-grub/lib/coding.c 
b/grub-core/lib/libtasn1-grub/lib/coding.c
+index 5d03bca9d..0458829a5 100644
+--- a/grub-core/lib/libtasn1-grub/lib/coding.c
 b/grub-core/lib/libtasn1-grub/lib/coding.c
+@@ -143,7 +143,7 @@ _asn1_tag_der (unsigned char class, unsigned int tag_value,
+ temp[k++] = tag_value & 0x7F;
+ tag_value >>= 7;
+ 
+-if (k > ASN1_MAX_TAG_SIZE - 1)
++if (k >= ASN1_MAX_TAG_SIZE - 1)
+   break;  /* will not encode larger tags */
+   }
+   *ans_len = k + 1;
+-- 
+2.35.3
+
-- 
2.35.3


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


[PATCH v17 09/20] key_protector: Add key protectors framework

2024-06-14 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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 by name. If a key protector with the specified
name exists and if an unlocking key is successfully retrieved by it, the
function returns to the caller the retrieved key and its length.

Cc: Vladimir Serbinenko 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/Makefile.am  |  1 +
 grub-core/Makefile.core.def|  5 +++
 grub-core/disk/key_protector.c | 78 ++
 include/grub/key_protector.h   | 46 
 4 files changed, 130 insertions(+)
 create mode 100644 grub-core/disk/key_protector.c
 create mode 100644 include/grub/key_protector.h

diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index 1eda467e0..e50db8106 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/key_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 fbe802740..457eb2e41 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1282,6 +1282,11 @@ module = {
   common = disk/raid6_recover.c;
 };
 
+module = {
+  name = key_protector;
+  common = disk/key_protector.c;
+};
+
 module = {
   name = scsi;
   common = disk/scsi.c;
diff --git a/grub-core/disk/key_protector.c b/grub-core/disk/key_protector.c
new file mode 100644
index 0..b84afe1c7
--- /dev/null
+++ b/grub-core/disk/key_protector.c
@@ -0,0 +1,78 @@
+/*
+ *  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 .
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+struct grub_key_protector *grub_key_protectors = NULL;
+
+grub_err_t
+grub_key_protector_register (struct grub_key_protector *protector)
+{
+  if (protector == NULL || protector->name == NULL || grub_strlen 
(protector->name) == 0)
+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 (_key_protectors),
+ GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_unregister (struct grub_key_protector *protector)
+{
+  if (protector == NULL)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_list_remove (GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_recover_key (const char *protector, grub_uint8_t **key,
+   grub_size_t *key_size)
+{
+  struct grub_key_protector *kp = NULL;
+
+  if (grub_key_protectors == NULL)
+return GRUB_ERR_OUT_OF_RANGE;
+
+  if (protector == NULL || grub_strlen (protector) == 0)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  kp = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
+protector);
+  if (kp == NULL)
+return grub_error (GRUB_ERR_OUT_OF_RANGE,
+  N_("A key protector with name '%s' could not be found. "
+ "Is the name spelled correctly and is the "
+ "corresponding module loaded?"), protector);
+
+  return kp->recover_key (key, key_size);
+}
diff --git a/include/grub/key_protector.h b/include/grub/key_protector.h
new file mode 100644
index 0..6e6a6fb24
--- /dev/null
+++ b/include/grub/key_protector.h
@@ -0,0 +1,46 @@
+/*
+ *  GRUB  --  

[PATCH v17 13/20] util/grub-protect: Add new tool

2024-06-14 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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 LUKS 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
has to ensure the cryptomount command is included in GRUB's boot script
and that it carries the requisite key protector (-P) parameter.

Sample usage:

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

To seal the key with TPM 2.0 Key File (recommended):

$ sudo grub-protect --action=add \
--protector=tpm2 \
--tpm2-pcrs=0,2,4,7,9 \
--tpm2key \
--tpm2-keyfile=luks-key \
--tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm

Or, to seal the key with the raw sealed key:

$ sudo grub-protect --action=add \
--protector=tpm2 \
--tpm2-pcrs=0,2,4,7,9 \
--tpm2-keyfile=luks-key \
--tpm2-outfile=/boot/efi/boot/grub2/sealed.key

Then, in the boot script, for TPM 2.0 Key File:

tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

Or, for the raw sealed key:

tpm2_key_protector_init --keyfile=(hd0,gpt1)/boot/grub2/sealed.key 
--pcrs=0,2,4,7,9
cryptomount -u  -P tpm2

The benefit of using TPM 2.0 Key File is that the PCR set is already
written in the key file, so there is no need to specify PCRs when
invoking tpm2_key_protector_init.

Cc: Stefan Berger 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 .gitignore|2 +
 Makefile.util.def |   24 +
 configure.ac  |   30 +
 docs/man/grub-protect.h2m |4 +
 util/grub-protect.c   | 1420 +
 5 files changed, 1480 insertions(+)
 create mode 100644 docs/man/grub-protect.h2m
 create mode 100644 util/grub-protect.c

diff --git a/.gitignore b/.gitignore
index 4c1f91db8..2105d87c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -169,6 +169,8 @@ widthspec.bin
 /grub-ofpathname.exe
 /grub-probe
 /grub-probe.exe
+/grub-protect
+/grub-protect.exe
 /grub-reboot
 /grub-render-label
 /grub-render-label.exe
diff --git a/Makefile.util.def b/Makefile.util.def
index fb82f59a0..c49098256 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -208,6 +208,30 @@ program = {
   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
 };
 
+program = {
+  name = grub-protect;
+  mansection = 1;
+
+  common = grub-core/kern/emu/argp_common.c;
+  common = grub-core/osdep/init.c;
+  common = grub-core/tpm2/args.c;
+  common = grub-core/tpm2/buffer.c;
+  common = grub-core/tpm2/mu.c;
+  common = grub-core/tpm2/tpm2.c;
+  common = grub-core/tpm2/tpm2key_asn1_tab.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 = '$(LIBTASN1)';
+  ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) 
$(LIBGEOM)';
+
+  condition = COND_GRUB_PROTECT;
+};
+
 program = {
   name = grub-mkrelpath;
   mansection = 1;
diff --git a/configure.ac b/configure.ac
index 99aae8700..949869bee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -76,6 +76,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])
@@ -2057,6 +2058,29 @@ fi
 AC_SUBST([LIBZFS])
 AC_SUBST([LIBNVPAIR])
 
+AC_ARG_ENABLE([grub-protect],
+ [AS_HELP_STRING([--enable-grub-protect],
+ [build and install the `grub-protect' utility 
(default=guessed)])])
+if test x"$enable_grub_protect" = xno ; then
+  grub_protect_excuse="explicitly disabled"
+fi
+
+LIBTASN1=
+if test x"$grub_protect_excuse" = x ; then
+  AC_CHECK_LIB([tasn1], [asn1_write_value], [LIBTASN1="-ltasn1"], 
[grub_protect_excuse="need libtasn1 library"])
+fi
+AC_SUBST([LIBTASN1])
+
+if test x"$enable_grub_protect" = xyes && test x"$grub_protect_excuse" != x ; 
then
+  AC_MSG_ERROR([grub-protect was explicitly requested but can't be compiled 
($grub_protect_excuse)])
+fi
+if test x"$grub_protect_excuse" = x ; then

[PATCH v17 01/20] posix_wrap: tweaks in preparation for libtasn1

2024-06-14 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

 - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as
   SIZEOF_UNSIGNED_LONG.

 - Define WORD_BIT, the size in bits of an int. This is a defined
   in the Single Unix Specification and in gnulib's limits.h. gnulib
   assumes it's 32 bits on all our platforms, including 64 bit
   platforms, so we also use that value.

 - Provide strto[u]l[l] preprocessor macros that resolve to
   grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we
   also define HAVE_STRTOUL here.

 - Implement c-ctype.h and the functions defined in the header.

 - Implement strncat in string.h.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
Reviewed-by: Daniel Kiper 
---
 grub-core/lib/posix_wrap/c-ctype.h   | 114 +++
 grub-core/lib/posix_wrap/limits.h|   1 +
 grub-core/lib/posix_wrap/stdlib.h|   8 ++
 grub-core/lib/posix_wrap/string.h|  21 +
 grub-core/lib/posix_wrap/sys/types.h |   1 +
 5 files changed, 145 insertions(+)
 create mode 100644 grub-core/lib/posix_wrap/c-ctype.h

diff --git a/grub-core/lib/posix_wrap/c-ctype.h 
b/grub-core/lib/posix_wrap/c-ctype.h
new file mode 100644
index 0..5f8fc8ce3
--- /dev/null
+++ b/grub-core/lib/posix_wrap/c-ctype.h
@@ -0,0 +1,114 @@
+/*
+ *  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 .
+ */
+
+#ifndef GRUB_POSIX_C_CTYPE_H
+#define GRUB_POSIX_C_CTYPE_H   1
+
+#include 
+
+static inline bool
+c_isspace (int c)
+{
+  return !!grub_isspace (c);
+}
+
+static inline bool
+c_isdigit (int c)
+{
+  return !!grub_isdigit (c);
+}
+
+static inline bool
+c_islower (int c)
+{
+  return !!grub_islower (c);
+}
+
+static inline bool
+c_isascii (int c)
+{
+  return !(c & ~0x7f);
+}
+
+static inline bool
+c_isupper (int c)
+{
+  return !!grub_isupper (c);
+}
+
+static inline bool
+c_isxdigit (int c)
+{
+  return !!grub_isxdigit (c);
+}
+
+static inline bool
+c_isprint (int c)
+{
+  return !!grub_isprint (c);
+}
+
+static inline bool
+c_iscntrl (int c)
+{
+  return !grub_isprint (c);
+}
+
+static inline bool
+c_isgraph (int c)
+{
+  return grub_isprint (c) && !grub_isspace (c);
+}
+
+static inline bool
+c_isalnum (int c)
+{
+  return grub_isalpha (c) || grub_isdigit (c);
+}
+
+static inline bool
+c_ispunct (int c)
+{
+  return grub_isprint (c) && !grub_isspace (c) && !c_isalnum (c);
+}
+
+static inline bool
+c_isalpha (int c)
+{
+  return !!grub_isalpha (c);
+}
+
+static inline bool
+c_isblank (int c)
+{
+  return c == ' ' || c == '\t';
+}
+
+static inline int
+c_tolower (int c)
+{
+  return grub_tolower (c);
+}
+
+static inline int
+c_toupper (int c)
+{
+  return grub_toupper (c);
+}
+
+#endif
diff --git a/grub-core/lib/posix_wrap/limits.h 
b/grub-core/lib/posix_wrap/limits.h
index 26918c8a0..4be7b4080 100644
--- a/grub-core/lib/posix_wrap/limits.h
+++ b/grub-core/lib/posix_wrap/limits.h
@@ -41,5 +41,6 @@
 #define LONG_MAX GRUB_LONG_MAX
 
 #define CHAR_BIT 8
+#define WORD_BIT 32
 
 #endif
diff --git a/grub-core/lib/posix_wrap/stdlib.h 
b/grub-core/lib/posix_wrap/stdlib.h
index f5279756a..14e4efdd0 100644
--- a/grub-core/lib/posix_wrap/stdlib.h
+++ b/grub-core/lib/posix_wrap/stdlib.h
@@ -64,4 +64,12 @@ abort (void)
   grub_abort ();
 }
 
+#define strtol grub_strtol
+
+/* for libgcrypt */
+#define HAVE_STRTOUL
+#define strtoul grub_strtoul
+
+#define strtoull grub_strtoull
+
 #endif
diff --git a/grub-core/lib/posix_wrap/string.h 
b/grub-core/lib/posix_wrap/string.h
index 1adb450b5..d3e400d50 100644
--- a/grub-core/lib/posix_wrap/string.h
+++ b/grub-core/lib/posix_wrap/string.h
@@ -84,6 +84,27 @@ memchr (const void *s, int c, grub_size_t n)
   return grub_memchr (s, c, n);
 }
 
+static inline char *
+strncat (char *dest, const char *src, grub_size_t n)
+{
+  const char *end;
+  char *str = dest;
+  grub_size_t src_len;
+
+  dest += grub_strlen (dest);
+
+  end = grub_memchr (src, '\0', n);
+  if (end != NULL)
+src_len = (grub_size_t) (end - src);
+  else
+src_len = n;
+
+  dest[src_len] = '\0';
+  grub_memcpy (dest, src, src_len);
+
+  return str;
+}
+
 #define memcmp grub_memcmp
 #define memcpy grub_memcpy
 #define memmove grub_memmove
diff --git a/grub-core/lib/posix_wrap/sys/types.h 
b/grub-core/lib/posix_wrap/sys/types.h
index eeda543c4..2f3e86549 100644
--- a/grub-core/lib/posix_wrap/sys/types.h
+++ 

[PATCH v17 08/20] libtasn1: Add the documentation

2024-06-14 Thread Gary Lin via Grub-devel
Document libtasn1 in docs/grub-dev.texi and add the upgrade steps.
Also add the patches to make libtasn1 compatible with grub code.

Signed-off-by: Gary Lin 
Reviewed-by: Vladimir Serbinenko 
---
 docs/grub-dev.texi | 33 +
 1 file changed, 33 insertions(+)

diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
index 1276c5930..62856f0f1 100644
--- a/docs/grub-dev.texi
+++ b/docs/grub-dev.texi
@@ -506,6 +506,7 @@ to update it.
 * Gnulib::
 * jsmn::
 * minilzo::
+* libtasn1::
 @end menu
 
 @node Gnulib
@@ -596,6 +597,38 @@ cp minilzo-2.10/*.[hc] grub-core/lib/minilzo
 rm -r minilzo-2.10*
 @end example
 
+@node libtasn1
+@section libtasn1
+
+libtasn1 is a library providing Abstract Syntax Notation One (ASN.1, as
+specified by the X.680 ITU-T recommendation) parsing and structures management,
+and Distinguished Encoding Rules (DER, as per X.690) encoding and decoding
+functions.
+
+To upgrade to a new version of the libtasn1 library, download the release
+tarball and copy the files into the target directory:
+
+@example
+curl -L -O https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz
+tar xf libtasn1-4.19.0.tar.gz
+rm -rf grub-core/lib/libtasn1/
+mkdir -p grub-core/lib/libtasn1/lib/
+cp libtasn1-4.19.0/@lbracechar{}README.md,COPYING@rbracechar{} 
grub-core/lib/libtasn1/
+cp 
libtasn1-4.19.0/lib/@lbracechar{}coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h@rbracechar{}
 grub-core/lib/libtasn1/lib/
+cp libtasn1-4.19.0/lib/includes/libtasn1.h grub-core/lib/libtasn1/
+rm -rf libtasn1-4.19.0*
+@end example
+
+After upgrading the library, it may be necessary to apply the patches in
+@file{grub-core/lib/libtasn1-patches/} to adjust the code to be compatible with
+grub. These patches were needed to use the current version of libtasn1. The
+existing patches may not apply cleanly, apply at all, or even be needed for a
+newer version of the library, and other patches maybe needed due to changes in
+the newer version. If existing patches need to be refreshed to apply cleanly,
+please include updated patches as part of the a patch set sent to the list.
+If new patches are needed or existing patches are not needed, also please send
+additions or removals as part of any patch set upgrading libtasn1.
+
 @node Debugging
 @chapter Debugging
 
-- 
2.35.3


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


[PATCH v17 03/20] libtasn1: disable code not needed in grub

2024-06-14 Thread Gary Lin via Grub-devel
Based on the patch from "Daniel Axtens "

We don't expect to be able to write ASN.1, only read it,
so we can disable some code.

Do that with #if 0/#endif, rather than deletion. This means
that the difference between upstream and grub is smaller,
which should make updating libtasn1 easier in the future.

With these exclusions we also avoid the need for minmax.h,
which is convenient because it means we don't have to
import it from gnulib.

Signed-off-by: Gary Lin 
---
 ...asn1-disable-code-not-needed-in-grub.patch | 320 ++
 1 file changed, 320 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch

diff --git 
a/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
 
b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
new file mode 100644
index 0..0282a08bc
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
@@ -0,0 +1,320 @@
+From f95a7b9c72fe91e44a90067c9008791a15e490c0 Mon Sep 17 00:00:00 2001
+From: Daniel Axtens 
+Date: Fri, 1 May 2020 17:12:23 +1000
+Subject: [PATCH 1/3] libtasn1: disable code not needed in grub
+
+We don't expect to be able to write ASN.1, only read it,
+so we can disable some code.
+
+Do that with #if 0/#endif, rather than deletion. This means
+that the difference between upstream and grub is smaller,
+which should make updating libtasn1 easier in the future.
+
+With these exclusions we also avoid the need for minmax.h,
+which is convenient because it means we don't have to
+import it from gnulib.
+
+Cc: Vladimir Serbinenko 
+Signed-off-by: Daniel Axtens 
+Signed-off-by: Gary Lin 
+---
+ grub-core/lib/libtasn1-grub/lib/coding.c| 12 ++--
+ grub-core/lib/libtasn1-grub/lib/decoding.c  |  2 ++
+ grub-core/lib/libtasn1-grub/lib/element.c   |  6 +++---
+ grub-core/lib/libtasn1-grub/lib/errors.c|  3 +++
+ grub-core/lib/libtasn1-grub/lib/structure.c | 10 ++
+ grub-core/lib/libtasn1-grub/libtasn1.h  | 15 +++
+ 6 files changed, 39 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/lib/libtasn1-grub/lib/coding.c 
b/grub-core/lib/libtasn1-grub/lib/coding.c
+index ea5bc370e..5d03bca9d 100644
+--- a/grub-core/lib/libtasn1-grub/lib/coding.c
 b/grub-core/lib/libtasn1-grub/lib/coding.c
+@@ -30,11 +30,11 @@
+ #include "parser_aux.h"
+ #include 
+ #include "element.h"
+-#include "minmax.h"
+ #include 
+ 
+ #define MAX_TAG_LEN 16
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_error_description_value_not_found */
+ /* Description: creates the ErrorDescription string   */
+@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node,
+   Estrcat (ErrorDescription, "' not found");
+ 
+ }
++#endif
+ 
+ /**
+  * asn1_length_der:
+@@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned 
char *str,
+   return ASN1_SUCCESS;
+ }
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_time_der  */
+ /* Description: creates the DER coding for a TIME */
+@@ -278,7 +280,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned 
char *der,
+ 
+   return ASN1_SUCCESS;
+ }
+-
++#endif
+ 
+ /*
+ void
+@@ -519,6 +521,7 @@ asn1_bit_der (const unsigned char *str, int bit_len,
+ }
+ 
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_complete_explicit_tag */
+ /* Description: add the length coding to the EXPLICIT */
+@@ -595,6 +598,7 @@ _asn1_complete_explicit_tag (asn1_node node, unsigned char 
*der,
+ 
+   return ASN1_SUCCESS;
+ }
++#endif
+ 
+ const tag_and_class_st _asn1_tags[] = {
+   [ASN1_ETYPE_GENERALSTRING] =
+@@ -647,6 +651,8 @@ const tag_and_class_st _asn1_tags[] = {
+ 
+ unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]);
+ 
++
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_insert_tag_der*/
+ /* Description: creates the DER coding of tags of one */
+@@ -1423,3 +1429,5 @@ error:
+   asn1_delete_structure ();
+   return err;
+ }
++
++#endif
+diff --git a/grub-core/lib/libtasn1-grub/lib/decoding.c 
b/grub-core/lib/libtasn1-grub/lib/decoding.c
+index b9245c486..bf9cb13ac 100644
+--- a/grub-core/lib/libtasn1-grub/lib/decoding.c
 b/grub-core/lib/libtasn1-grub/lib/decoding.c
+@@ -1620,6 +1620,7 @@ asn1_der_decoding (asn1_node * element, const void 
*ider, int ider_len,
+   return asn1_der_decoding2 (element, ider, _len, 0, errorDescription);
+ }
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**
+  * asn1_der_decoding_element:
+  * @structure: pointer to an ASN1 structure
+@@ -1650,6 +1651,7 @@ asn1_der_decoding_element (asn1_node * structure, const 
char *elementName,
+ {
+ 

Re: [PATCH v16 03/20] libtasn1: disable code not needed in grub

2024-06-13 Thread Gary Lin via Grub-devel
On Wed, Jun 12, 2024 at 06:10:12PM +0200, Daniel Kiper wrote:
> On Tue, Jun 11, 2024 at 03:10:56PM +0800, Gary Lin via Grub-devel wrote:
> > On Fri, Jun 07, 2024 at 04:14:54PM +0200, Daniel Kiper wrote:
> > > On Fri, Jun 07, 2024 at 11:07:31AM +0800, Gary Lin wrote:
> > > > On Wed, Jun 05, 2024 at 05:18:32PM +0200, Daniel Kiper wrote:
> > > > > On Wed, May 15, 2024 at 01:06:55PM +0800, Gary Lin wrote:
> > > > > > From: Daniel Axtens 
> > > > > >
> > > > > > We don't expect to be able to write ASN.1, only read it,
> > > > > > so we can disable some code.
> > > > > >
> > > > > > Do that with #if 0/#endif, rather than deletion. This means
> > > > > > that the difference between upstream and grub is smaller,
> > > > > > which should make updating libtasn1 easier in the future.
> > > > > >
> > > > > > With these exclusions we also avoid the need for minmax.h,
> > > > > > which is convenient because it means we don't have to
> > > > > > import it from gnulib.
> > > > >
> > > > > This and two following patches should be put in separate files in the
> > > > > grub-core/lib/libtasn1-patches directory. The gnulib is good example
> > > > > how it should be done.
> > > > >
> > > > Those patches are added to libtasn1-patches in another patch with the
> > > > title "libtasn1: Add the documentation" which mentions how libtasn1 is
> > > > updated.
> > >
> > > OK, then use them as gnulib does. Of course add the patches one by one
> > > to the GRUB source code.
> > >
> > Does it mean that I should use bootstrap to check out libtasn1 and then
> > apply the patches?
> 
> No, for time being it is enough to have static libtasn1 with the patches
> applied on top of that lib during bootstrap run.
> 
Got it. I'll update bootstrap or autogen.sh to apply the patches.

Gary Lin

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


Re: [PATCH v16 03/20] libtasn1: disable code not needed in grub

2024-06-11 Thread Gary Lin via Grub-devel
On Fri, Jun 07, 2024 at 04:14:54PM +0200, Daniel Kiper wrote:
> On Fri, Jun 07, 2024 at 11:07:31AM +0800, Gary Lin wrote:
> > On Wed, Jun 05, 2024 at 05:18:32PM +0200, Daniel Kiper wrote:
> > > On Wed, May 15, 2024 at 01:06:55PM +0800, Gary Lin wrote:
> > > > From: Daniel Axtens 
> > > >
> > > > We don't expect to be able to write ASN.1, only read it,
> > > > so we can disable some code.
> > > >
> > > > Do that with #if 0/#endif, rather than deletion. This means
> > > > that the difference between upstream and grub is smaller,
> > > > which should make updating libtasn1 easier in the future.
> > > >
> > > > With these exclusions we also avoid the need for minmax.h,
> > > > which is convenient because it means we don't have to
> > > > import it from gnulib.
> > >
> > > This and two following patches should be put in separate files in the
> > > grub-core/lib/libtasn1-patches directory. The gnulib is good example
> > > how it should be done.
> > >
> > Those patches are added to libtasn1-patches in another patch with the
> > title "libtasn1: Add the documentation" which mentions how libtasn1 is
> > updated.
> 
> OK, then use them as gnulib does. Of course add the patches one by one
> to the GRUB source code.
> 
Does it mean that I should use bootstrap to check out libtasn1 and then
apply the patches?

Gary Lin

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


Re: [PATCH v16 02/20] libtasn1: import libtasn1-4.19.0

2024-06-06 Thread Gary Lin via Grub-devel
On Wed, Jun 05, 2024 at 05:04:46PM +0200, Daniel Kiper wrote:
> On Wed, May 15, 2024 at 01:06:54PM +0800, Gary Lin wrote:
> > From: Daniel Axtens 
> >
> > Import a very trimmed-down set of libtasn1 files:
> 
> I hope you merge the latest one...
> 
Yes, I updated Daniel's patch to include the latest libtasn1, i.e.
4.19.0.

> > pushd /tmp
> 
> I would create tmp dir in the GRUB source code and extract libtasn1
> there. Then I would remove the tmp dir.
> 
Got it.

> > wget https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz
> > tar -xf libtasn1-4.19.0.tar.gz
> 
> The "-" is not needed. Please drop it.
> 
No problem.

> > popd
> > pushd grub-core/lib
> > rm -rf libtasn1
> > mkdir libtasn1
> > cp /tmp/libtasn1-4.19.0/{README.md,COPYING} libtasn1/
> > mkdir libtasn1/lib
> > cp 
> > /tmp/libtasn1-4.19.0/lib/{coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h}
> >  libtasn1/lib
> > cp /tmp/libtasn1-4.19.0/lib/includes/libtasn1.h ../../include/grub/
> > git add libtasn1/ ../../include/grub/libtasn1.h
> 
> I am not OK with adding libtasn1.h to the include/grub. Please do not do
> that. I think good example how it should be done is in miniLZO lib.
> 
Ok, I'll move libtasn1.h to the libtasn1 directory and update other
affected patches.

Gary Lin

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


Re: [PATCH v16 01/20] posix_wrap: tweaks in preparation for libtasn1

2024-06-06 Thread Gary Lin via Grub-devel
On Wed, Jun 05, 2024 at 04:45:07PM +0200, Daniel Kiper wrote:
> On Wed, May 15, 2024 at 01:06:53PM +0800, Gary Lin wrote:
> > From: Daniel Axtens 
> >
> >  - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as
> >SIZEOF_UNSIGNED_LONG.
> >
> >  - Define WORD_BIT, the size in bits of an int. This is a defined
> >in the Single Unix Specification and in gnulib's limits.h. gnulib
> >assumes it's 32 bits on all our platforms, including 64 bit
> >platforms, so we also use that value.
> >
> >  - Provide strto[u]l[l] preprocessor macros that resolve to
> >grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we
> >also define HAVE_STRTOUL here.
> >
> >  - Implement c-ctype.h and the functions defined in the header.
> >
> >  - Implement strncat in string.h.
> >
> > Cc: Vladimir Serbinenko 
> > Signed-off-by: Daniel Axtens 
> > Signed-off-by: Gary Lin 
> 
> Reviewed-by: Daniel Kiper 
> 
> A nit below...
> 
> > diff --git a/grub-core/lib/posix_wrap/string.h 
> > b/grub-core/lib/posix_wrap/string.h
> > index 1adb450b5..b0c5928d2 100644
> > --- a/grub-core/lib/posix_wrap/string.h
> > +++ b/grub-core/lib/posix_wrap/string.h
> > @@ -84,6 +84,27 @@ memchr (const void *s, int c, grub_size_t n)
> >return grub_memchr (s, c, n);
> >  }
> >
> > +static inline char *
> > +strncat(char *dest, const char *src, grub_size_t n)
> 
> Missing space before "(".
> 
Will fix in v17.

Thanks,

Gary Lin

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


Re: [PATCH v16 03/20] libtasn1: disable code not needed in grub

2024-06-06 Thread Gary Lin via Grub-devel
On Wed, Jun 05, 2024 at 05:18:32PM +0200, Daniel Kiper wrote:
> On Wed, May 15, 2024 at 01:06:55PM +0800, Gary Lin wrote:
> > From: Daniel Axtens 
> >
> > We don't expect to be able to write ASN.1, only read it,
> > so we can disable some code.
> >
> > Do that with #if 0/#endif, rather than deletion. This means
> > that the difference between upstream and grub is smaller,
> > which should make updating libtasn1 easier in the future.
> >
> > With these exclusions we also avoid the need for minmax.h,
> > which is convenient because it means we don't have to
> > import it from gnulib.
> 
> This and two following patches should be put in separate files in the
> grub-core/lib/libtasn1-patches directory. The gnulib is good example
> how it should be done.
> 
Those patches are added to libtasn1-patches in another patch with the
title "libtasn1: Add the documentation" which mentions how libtasn1 is
updated.

Gary Lin

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


Re: [PATCH v3 3/5] Adjust import script, definitions and API users for libgcrypt 1.10

2024-05-28 Thread Gary Lin via Grub-devel
On Fri, May 24, 2024 at 08:30:04PM +0300, Vladimir Serbinenko wrote:
> diff --git a/conf/Makefile.common b/conf/Makefile.common
> index b8f216f6c..1fd3fc9da 100644
> --- a/conf/Makefile.common
> +++ b/conf/Makefile.common
> @@ -81,8 +81,8 @@ CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib 
> -I$(top_srcdir)/grub-co
>  CFLAGS_POSIX = -fno-builtin
>  CPPFLAGS_POSIX = -I$(top_srcdir)/grub-core/lib/posix_wrap
>  
> -CFLAGS_GCRY = -Wno-error -Wno-missing-field-initializers 
> -Wno-redundant-decls -Wno-undef $(CFLAGS_POSIX)
> -CPPFLAGS_GCRY = -I$(top_srcdir)/grub-core/lib/libgcrypt_wrap 
> $(CPPFLAGS_POSIX) -D_GCRYPT_IN_LIBGCRYPT=1 -I$(top_srcdir)/include/grub/gcrypt
> +CFLAGS_GCRY = -Wno-error=sign-compare -Wno-error=shift-count-overflow 
> -Wno-missing-field-initializers -Wno-redundant-decls -Wno-undef 
> $(CFLAGS_POSIX)
> +CPPFLAGS_GCRY = -I$(top_srcdir)/grub-core/lib/libgcrypt_wrap 
> $(CPPFLAGS_POSIX) -D_GCRYPT_IN_LIBGCRYPT=1 -D_GCRYPT_CONFIG_H_INCLUDED=1 
> -DHAVE_STRTOUL=1 -I$(top_srcdir)/include/grub/gcrypt
>  
>  CPPFLAGS_EFIEMU = -I$(top_srcdir)/grub-core/efiemu/runtime
>  
The 'shift-count-overflow' warnings is caused by u32 MD_NBLOCKS_TYPE.
I'd propose to add "#define USE_SHA512 1" to cipher_wrap.h to change
MD_NBLOCKS_TYPE to u64 to fix the warnings.

Gary Lin

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


Re: [PATCH v3 5/5] keccak: Disable acceleration with SSE asm

2024-05-27 Thread Gary Lin via Grub-devel
On Fri, May 24, 2024 at 08:30:06PM +0300, Vladimir Serbinenko wrote:
> ---
>  .../lib/libgcrypt-patches/02_keccak_sse.diff  | 19 +++
>  1 file changed, 19 insertions(+)
>  create mode 100644 grub-core/lib/libgcrypt-patches/02_keccak_sse.diff
> 
> diff --git a/grub-core/lib/libgcrypt-patches/02_keccak_sse.diff 
> b/grub-core/lib/libgcrypt-patches/02_keccak_sse.diff
> new file mode 100644
> index 0..980ebb2b7
> --- /dev/null
> +++ b/grub-core/lib/libgcrypt-patches/02_keccak_sse.diff
> @@ -0,0 +1,19 @@
> +commit b0cf06271da5fe20360953a53a47c69da89669cd
> +Author: Vladimir Serbinenko 
> +Date:   Sun Apr 7 06:33:11 2024 +0300
> +
> +keccak: Disable acceleration with SSE asm
> +
> +diff --git a/grub-core/lib/libgcrypt/cipher/keccak.c 
> b/grub-core/lib/libgcrypt/cipher/keccak.c
Since this patch file is applied after importing libgcrypt to libgcrypt-grub,
the target file has to be libgcrypt-grub/cipher/keccak.c.

Gary Lin

> +index 11e64b3e7..8b570263b 100644
> +--- a/grub-core/lib/libgcrypt/cipher/keccak.c
>  b/grub-core/lib/libgcrypt/cipher/keccak.c
> +@@ -251,7 +251,7 @@ keccak_absorb_lane32bi(u32 *lane, u32 x0, u32 x1)
> + /* Construct generic 64-bit implementation. */
> + #ifdef USE_64BIT
> + 
> +-#if __GNUC__ >= 4 && defined(__x86_64__)
> ++#if __GNUC__ >= 4 && defined(__x86_64__) && 0
> + 
> + static inline void absorb_lanes64_8(u64 *dst, const byte *in)
> + {
> -- 
> 2.39.2
> 
> 
> ___
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel

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


Re: [PATCH 1/3] Import libgcrypt 1.10.3

2024-05-21 Thread Gary Lin via Grub-devel
On Tue, May 21, 2024 at 01:36:18PM +0300, Vladimir 'phcoder' Serbinenko wrote:
> I think at least AUTHORS and COPYING should be included.
I see the point to keep AUTHORS and COPYING but other library such
minilzo only copies the essential .c/.h files. 

> Rest is for the ease of update in the future.
Those unused files seem not very helpful for the future update. IMHO,
what we need is how libgcrypt is imported, so that developers can reproduce
the code and follow the steps to update in the future.

Thanks,

Gary Lin

> 
> Le mar. 21 mai 2024, 10:32, Gary Lin  a écrit :
> 
> > Hi Vladimir,
> >
> > Originally, there are only cipher, mpi, and src in the libgcrypt
> > directory, but the unnecessary stuff, e.g. AUTHORS, COPYING, build-aux,
> > tests, etc., was added and bloated the size of the patch. Could you
> > remove them and only keep the necessary files?
> >
> > Thanks,
> >
> > Gary Lin
> >

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


Re: [PATCH 1/3] Import libgcrypt 1.10.3

2024-05-21 Thread Gary Lin via Grub-devel
Hi Vladimir,

Originally, there are only cipher, mpi, and src in the libgcrypt
directory, but the unnecessary stuff, e.g. AUTHORS, COPYING, build-aux,
tests, etc., was added and bloated the size of the patch. Could you
remove them and only keep the necessary files?

Thanks,

Gary Lin

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


[PATCH v16 13/20] util/grub-protect: Add new tool

2024-05-14 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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 LUKS 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
has to ensure the cryptomount command is included in GRUB's boot script
and that it carries the requisite key protector (-P) parameter.

Sample usage:

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

To seal the key with TPM 2.0 Key File (recommended):

$ sudo grub-protect --action=add \
--protector=tpm2 \
--tpm2-pcrs=0,2,4,7,9 \
--tpm2key \
--tpm2-keyfile=luks-key \
--tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm

Or, to seal the key with the raw sealed key:

$ sudo grub-protect --action=add \
--protector=tpm2 \
--tpm2-pcrs=0,2,4,7,9 \
--tpm2-keyfile=luks-key \
--tpm2-outfile=/boot/efi/boot/grub2/sealed.key

Then, in the boot script, for TPM 2.0 Key File:

tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

Or, for the raw sealed key:

tpm2_key_protector_init --keyfile=(hd0,gpt1)/boot/grub2/sealed.key 
--pcrs=0,2,4,7,9
cryptomount -u  -P tpm2

The benefit of using TPM 2.0 Key File is that the PCR set is already
written in the key file, so there is no need to specify PCRs when
invoking tpm2_key_protector_init.

Cc: Stefan Berger 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 .gitignore|2 +
 Makefile.util.def |   24 +
 configure.ac  |   30 +
 docs/man/grub-protect.h2m |4 +
 util/grub-protect.c   | 1420 +
 5 files changed, 1480 insertions(+)
 create mode 100644 docs/man/grub-protect.h2m
 create mode 100644 util/grub-protect.c

diff --git a/.gitignore b/.gitignore
index 11fcecf5c..5391f7e6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -168,6 +168,8 @@ widthspec.bin
 /grub-ofpathname.exe
 /grub-probe
 /grub-probe.exe
+/grub-protect
+/grub-protect.exe
 /grub-reboot
 /grub-render-label
 /grub-render-label.exe
diff --git a/Makefile.util.def b/Makefile.util.def
index 19ad5a96f..40bfe713d 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -207,6 +207,30 @@ program = {
   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
 };
 
+program = {
+  name = grub-protect;
+  mansection = 1;
+
+  common = grub-core/kern/emu/argp_common.c;
+  common = grub-core/osdep/init.c;
+  common = grub-core/tpm2/args.c;
+  common = grub-core/tpm2/buffer.c;
+  common = grub-core/tpm2/mu.c;
+  common = grub-core/tpm2/tpm2.c;
+  common = grub-core/tpm2/tpm2key_asn1_tab.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 = '$(LIBTASN1)';
+  ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) 
$(LIBGEOM)';
+
+  condition = COND_GRUB_PROTECT;
+};
+
 program = {
   name = grub-mkrelpath;
   mansection = 1;
diff --git a/configure.ac b/configure.ac
index 84a202c6e..3a07ab570 100644
--- a/configure.ac
+++ b/configure.ac
@@ -76,6 +76,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])
@@ -2057,6 +2058,29 @@ fi
 AC_SUBST([LIBZFS])
 AC_SUBST([LIBNVPAIR])
 
+AC_ARG_ENABLE([grub-protect],
+ [AS_HELP_STRING([--enable-grub-protect],
+ [build and install the `grub-protect' utility 
(default=guessed)])])
+if test x"$enable_grub_protect" = xno ; then
+  grub_protect_excuse="explicitly disabled"
+fi
+
+LIBTASN1=
+if test x"$grub_protect_excuse" = x ; then
+  AC_CHECK_LIB([tasn1], [asn1_write_value], [LIBTASN1="-ltasn1"], 
[grub_protect_excuse="need libtasn1 library"])
+fi
+AC_SUBST([LIBTASN1])
+
+if test x"$enable_grub_protect" = xyes && test x"$grub_protect_excuse" != x ; 
then
+  AC_MSG_ERROR([grub-protect was explicitly requested but can't be compiled 
($grub_protect_excuse)])
+fi
+if test x"$grub_protect_excuse" = x ; then

[PATCH v16 00/20] Automatic Disk Unlock with TPM2

2024-05-14 Thread Gary Lin via Grub-devel
GIT repo for v16: https://github.com/lcp/grub2/tree/tpm2-unlock-v16

This patch series is based on "Automatic TPM Disk Unlock"(*1) posted by
Hernan Gatta to introduce the key protector framework and TPM2 stack
to GRUB2, and this could be a useful feature for the systems to
implement full disk encryption.

To support TPM 2.0 Key File format(*2), patch 1~5,7 are grabbed from
Daniel Axtens's "appended signature secure boot support" (*3) to import
libtasn1 into grub2. Besides, the libtasn1 version is upgraded to
4.19.0 instead of 4.16.0 in the original patch.

Patch 6 fixes a potential buffer overrun in libtasn1.
(https://gitlab.com/gnutls/libtasn1/-/issues/49)

Patch 8 adds the document for libtasn1 and the steps to upgrade the
library.

Patch 9~13 are Hernan Gatta's patches with the follow-up fixes and
improvements:
- Converting 8 spaces into 1 tab
- Merging the minor build fix from Michael Chang
  - Replacing "lu" with "PRIuGRUB_SIZE" for grub_dprintf
  - Adding "enable = efi" to the tpm2 module in grub-core/Makefile.core.def
- Rebasing "cryptodisk: Support key protectors" to the git master
- Removing the measurement on the sealed key
  - Based on the patch from Olaf Kirch 
- Adjusting the input parameters of TPM2_EvictControl to match the order
  in "TCG TPM2 Part3 Commands"
- Declaring the input arguments of TPM2 functions as const
- Resending TPM2 commands on TPM_RC_RETRY
- Adding checks for the parameters of TPM2 commands
- Packing the missing authorization command for TPM2_PCR_Read
- Tweaking the TPM2 command functions to allow some parameters to be
  NULL so that we don't have to declare empty variables
- Only enabling grub-protect for "efi" since the TPM2 stack currently
  relies on the EFI TCG2 protocol to send TPM2 commands
- Using grub_cpu_to_be*() in the TPM2 stack instead of grub_swap_bytes*()
  which may cause problems in big-indian machines
- Changing the short name of "--protector" of "cryptomount" from "-k" to
  "-P" to avoid the conflict with "--key-file"
- Supporting TPM 2.0 Key File Format besides the raw sealed key
- Adding the external libtasn1 dependency to grub-protect to write the
  TPM 2.0 Key files
- Extending the TPM2 TSS stack to support authorized policy

Patch 14 implements the authorized policy support.

Patch 15 implements the missing NV index mode. (Thanks to Patrick Colp)

Patch 16 improves the 'cryptomount' command to fall back to the
passphrase mode when the key protector fails to unlock the encrypted
partition. (Another patch from Patrick Colp)

Patch 17 and 18 fix the potential security issues spotted by Fabian Vogt.

Patch 19 and 20 implement the TPM key unsealing testcases.

To utilize the TPM2 key protector to unlock the encrypted partition
(sdb1), here are the sample steps:

1. Add an extra random key for LUKS (luks-key)
   $ dd if=/dev/urandom of=luks-key bs=1 count=32
   $ sudo cryptsetup luksAddKey /dev/sdb1 luks-key --pbkdf=pbkdf2

2. Seal the key
   $ sudo grub-protect --action=add \
   --protector=tpm2 \
   --tpm2key \
   --tpm2-keyfile=luks-key \
   --tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm

3. Unseal the key with the proper commands in grub.cfg:
   tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
   cryptomount -u  -P tpm2

(*1) https://lists.gnu.org/archive/html/grub-devel/2022-02/msg6.html
(*2) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*3) https://lists.gnu.org/archive/html/grub-devel/2021-06/msg00044.html

v16:
- Rebasing patch 6, 12, and 16 to fix the conflicts with the latest
  master branch
- Changes in cryptodisk:
  - Disallowing that both OPTION_KEYFILE and OPTION_PROTECTOR are set
since the key data for "--key-file" would be overwritten by the key
protectors
  - Resetting the cargs key data when the key from a key protector
doesn't work for the disk to ensure the passphrase prompt will be
triggered later
  - Adding the comment to address why grub_errno is only cleared for
cargs->key_len == 0

v15:
- https://lists.gnu.org/archive/html/grub-devel/2024-05/msg00059.html 
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-v15
- Changes in tpm2_test
  - Quoting the variables which contain file paths
  - Correcting the exit code for several commands
  - Writing the verification text directly into the LUKS device
  - Amending the waiting loop for swtpm
  - Replacing exit with return in tpm2_seal_unseal() and
tpm2_seal_unseal_nv()
  - Collecting the parameters for the SRK mode testcases in an array
and invoking tpm2_seal_unseal() with a for loop
  - Moving the tpm2-tools commands for the NV index mode to a separate
function  
  - Using tpm2_evictcontrol to remove the object from the NV index to
match the key sealing commands
  - Printing the test results
  - Printing error messages to stderr

v14:
- https://lists.gnu.org/archive/html/grub-devel/2024-05/msg00011.html
- GIT repo: 

[PATCH v16 12/20] cryptodisk: Support key protectors

2024-05-14 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

Add a new parameter to cryptomount to support the key protectors framework: -P.
The parameter is used to automatically retrieve a key from specified key
protectors. The parameter may be repeated to specify any number of key
protectors. These are tried in order until one provides a usable key for any
given disk.

Signed-off-by: Hernan Gatta 
Signed-off-by: Michael Chang 
Signed-off-by: Gary Lin 
Reviewed-by: Glenn Washburn 
Reviewed-by: Stefan Berger 
---
 Makefile.util.def   |   1 +
 grub-core/disk/cryptodisk.c | 243 ++--
 include/grub/cryptodisk.h   |  16 +++
 3 files changed, 196 insertions(+), 64 deletions(-)

diff --git a/Makefile.util.def b/Makefile.util.def
index b53afb1d3..19ad5a96f 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -40,6 +40,7 @@ library = {
   common = grub-core/disk/luks.c;
   common = grub-core/disk/luks2.c;
   common = grub-core/disk/geli.c;
+  common = grub-core/disk/key_protector.c;
   common = grub-core/disk/cryptodisk.c;
   common = grub-core/disk/AFSplitter.c;
   common = grub-core/lib/pbkdf2.c;
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index f98acade6..cfa3e28ce 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #ifdef GRUB_UTIL
 #include 
@@ -45,7 +46,8 @@ enum
 OPTION_KEYFILE,
 OPTION_KEYFILE_OFFSET,
 OPTION_KEYFILE_SIZE,
-OPTION_HEADER
+OPTION_HEADER,
+OPTION_PROTECTOR
   };
 
 static const struct grub_arg_option options[] =
@@ -59,6 +61,8 @@ static const struct grub_arg_option options[] =
 {"keyfile-offset", 'O', 0, N_("Key file offset (bytes)"), 0, ARG_TYPE_INT},
 {"keyfile-size", 'S', 0, N_("Key file data size (bytes)"), 0, 
ARG_TYPE_INT},
 {"header", 'H', 0, N_("Read header from file"), 0, ARG_TYPE_STRING},
+{"protector", 'P', GRUB_ARG_OPTION_REPEATABLE,
+ N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING},
 {0, 0, 0, 0, 0, 0}
   };
 
@@ -1062,6 +1066,7 @@ grub_cryptodisk_scan_device_real (const char *name,
   grub_err_t ret = GRUB_ERR_NONE;
   grub_cryptodisk_t dev;
   grub_cryptodisk_dev_t cr;
+  int i;
   struct cryptodisk_read_hook_ctx read_hook_data = {0};
   int askpass = 0;
   char *part = NULL;
@@ -1114,79 +1119,151 @@ grub_cryptodisk_scan_device_real (const char *name,
   goto error_no_close;
 if (!dev)
   continue;
+break;
+  }
 
-if (cargs->key_len)
-  {
-   ret = cr->recover_key (source, dev, cargs);
-   if (ret != GRUB_ERR_NONE)
- goto error;
-  }
-else
-  {
-   /* Get the passphrase from the user, if no key data. */
-   unsigned long tries = 3;
-   const char *tries_env;
+  if (dev == NULL)
+{
+  grub_error (GRUB_ERR_BAD_MODULE,
+ "no cryptodisk module can handle this device");
+  goto error_no_close;
+}
 
-   askpass = 1;
-   cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
-   if (cargs->key_data == NULL)
- goto error_no_close;
+  if (cargs->protectors)
+{
+  for (i = 0; cargs->protectors[i]; i++)
+   {
+ if (cargs->key_cache[i].invalid)
+   continue;
 
-   tries_env = grub_env_get ("cryptodisk_passphrase_tries");
-   if (tries_env != NULL && tries_env[0] != '\0')
- {
-   unsigned long tries_env_val;
-   const char *p;
+ if (cargs->key_cache[i].key == NULL)
+   {
+ ret = grub_key_protector_recover_key (cargs->protectors[i],
+   >key_cache[i].key,
+   
>key_cache[i].key_len);
+ if (ret != GRUB_ERR_NONE)
+   {
+ if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
+ grub_dprintf ("cryptodisk",
+   "failed to recover a key from key protector "
+   "%s, will not try it again for any other "
+   "disks, if any, during this invocation of "
+   "cryptomount\n",
+   cargs->protectors[i]);
+
+ cargs->key_cache[i].invalid = 1;
+ continue;
+   }
+   }
 
-   tries_env_val = grub_strtoul (tries_env, , 0);
-   if (*p == '\0' && tries_env_val != ~0UL)
- tries = tries_env_val;
-   else
- grub_printf_ (N_("Invalid cryptodisk_passphrase_tries value `%s'. 
Defaulting to %lu.\n"),
-   tries_env,
-   tries);
- }
+ cargs->key_data = cargs->key_cache[i].key;
+ cargs->key_len = cargs->key_cache[i].key_len;
 
-   for (; tries > 0; tries--)
- {
- 

[PATCH v16 20/20] tests: Add tpm2_test

2024-05-14 Thread Gary Lin via Grub-devel
For the tpm2 module, the TCG2 command submission function is the only
difference between the a QEMU instance and grub-emu. To test TPM key
unsealing with a QEMU instance, it requires an extra OS image to invoke
grub-protect to seal the LUKS key, rather than a simple grub-shell rescue
CD image. On the other hand, grub-emu can share the emulated TPM device
with the host, so that we can seal the LUKS key on host and test key
unsealing with grub-emu.

This test script firstly creates a simple LUKS image to be loaded as a
loopback device in grub-emu. Then an emulated TPM device is created by
swtpm_cuse and PCR 0 and 1 are extended.

There are several test cases in the script to test various settings. Each
test case uses grub-protect or tpm2-tools to seal the LUKS password
against PCR 0 and PCR 1. Then grub-emu is launched to load the LUKS image,
try to mount the image with tpm2_key_protector_init and cryptomount, and
verify the result.

Based on the idea from Michael Chang.

Cc: Michael Chang 
Cc: Stefan Berger 
Cc: Glenn Washburn 
Signed-off-by: Gary Lin 
---
 Makefile.util.def|   6 +
 tests/tpm2_test.in   | 389 +++
 tests/util/grub-shell.in |   6 +-
 3 files changed, 400 insertions(+), 1 deletion(-)
 create mode 100644 tests/tpm2_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index 40bfe713d..8d4c53a03 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1281,6 +1281,12 @@ script = {
   common = tests/asn1_test.in;
 };
 
+script = {
+  testcase = native;
+  name = tpm2_test;
+  common = tests/tpm2_test.in;
+};
+
 program = {
   testcase = native;
   name = example_unit_test;
diff --git a/tests/tpm2_test.in b/tests/tpm2_test.in
new file mode 100644
index 0..aecd1a5a3
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,389 @@
+#! @BUILD_SHEBANG@ -e
+
+# Test GRUBs ability to unseal a LUKS key with TPM 2.0
+# 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 .
+
+grubshell=@builddir@/grub-shell
+
+. "@builddir@/grub-core/modinfo.sh"
+
+if [ x${grub_modinfo_platform} != xemu ]; then
+  exit 77
+fi
+
+builddir="@builddir@"
+
+# Force build directory components
+PATH="${builddir}:${PATH}"
+export PATH
+
+if [ "x${EUID}" = "x" ] ; then
+  EUID=`id -u`
+fi
+
+if [ "${EUID}" != 0 ] ; then
+   echo "not root; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v cryptsetup >/dev/null 2>&1; then
+   echo "cryptsetup not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; then
+   echo "no tpm_vtpm_proxy support; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v swtpm >/dev/null 2>&1; then
+   echo "swtpm not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v tpm2_startup >/dev/null 2>&1; then
+   echo "tpm2-tools not installed; cannot test tpm2."
+   exit 99
+fi
+
+tpm2testdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XX"`" || 
exit 99
+
+disksize=20M
+
+luksfile=${tpm2testdir}/luks.disk
+lukskeyfile=${tpm2testdir}/password.txt
+
+# Choose a low iteration number to reduce the time to decrypt the disk
+csopt="--type luks2 --pbkdf pbkdf2 --iter-time 1000"
+
+tpm2statedir=${tpm2testdir}/tpm
+tpm2ctrl=${tpm2statedir}/ctrl
+tpm2log=${tpm2statedir}/logfile
+
+sealedkey=${tpm2testdir}/sealed.tpm
+
+timeout=20
+
+testoutput=${tpm2testdir}/testoutput
+
+vtext="TEST VERIFIED"
+
+ret=0
+
+# Create the password file
+echo -n "top secret" > "${lukskeyfile}"
+
+# Setup LUKS2 image
+truncate -s ${disksize} "${luksfile}" || exit 99
+cryptsetup luksFormat -q ${csopt} "${luksfile}" "${lukskeyfile}" || exit 99
+
+# Write vtext into the first block of the LUKS2 image
+luksdev=/dev/mapper/`basename "${tpm2testdir}"`
+cryptsetup open --key-file "${lukskeyfile}" "${luksfile}" `basename 
"${luksdev}"` || exit 99
+echo "${vtext}" > "${luksdev}"
+cryptsetup close "${luksdev}"
+
+# Shutdown the swtpm instance on exit
+cleanup() {
+RET=$?
+if [ -e "${tpm2ctrl}" ]; then
+   swtpm_ioctl -s --unix "${tpm2ctrl}"
+fi
+if [ "${RET}" -eq 0 ]; then
+   rm -rf "$tpm2testdir" || :
+fi
+}
+trap cleanup EXIT INT TERM KILL QUIT
+
+mkdir -p "${tpm2statedir}"
+
+# Create the swtpm chardev instance
+swtpm chardev --vtpm-proxy --tpmstate dir="${tpm2statedir}" \
+   --tpm2 --ctrl type=unixio,path="${tpm2ctrl}" \
+   --flags startup-clear --daemon > 

[PATCH v16 19/20] tpm2: Enable tpm2 module for grub-emu

2024-05-14 Thread Gary Lin via Grub-devel
As a preparation to test TPM 2.0 TSS stack with grub-emu, the new
option, --tpm-device, is introduced to specify the TPM device for
grub-emu so that grub-emu can share the emulated TPM device with
the host.

Since grub-emu can directly access the device node on host, it's easy to
implement the essential TCG2 command submission function with the
read/write functions and enable tpm2 module for grub-emu, so that we can
further test TPM key unsealing with grub-emu.

Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/Makefile.core.def |  2 ++
 grub-core/kern/emu/main.c   | 11 +++-
 grub-core/kern/emu/misc.c   | 51 
 grub-core/tpm2/tcg2-emu.c   | 52 +
 include/grub/emu/misc.h |  5 
 5 files changed, 120 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/tpm2/tcg2-emu.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 8a3eef13c..246e1dc9d 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2571,7 +2571,9 @@ module = {
   common = tpm2/tpm2key.c;
   common = tpm2/tpm2key_asn1_tab.c;
   efi = tpm2/tcg2.c;
+  emu = tpm2/tcg2-emu.c;
   enable = efi;
+  enable = emu;
 };
 
 module = {
diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
index 855b11c3d..c10838613 100644
--- a/grub-core/kern/emu/main.c
+++ b/grub-core/kern/emu/main.c
@@ -55,7 +55,7 @@
 static jmp_buf main_env;
 
 /* Store the prefix specified by an argument.  */
-static char *root_dev = NULL, *dir = NULL;
+static char *root_dev = NULL, *dir = NULL, *tpm_dev = NULL;
 
 grub_addr_t grub_modbase = 0;
 
@@ -108,6 +108,7 @@ static struct argp_option options[] = {
   {"verbose", 'v', 0,  0, N_("print verbose messages."), 0},
   {"hold", 'H', N_("SECS"),  OPTION_ARG_OPTIONAL, N_("wait until a 
debugger will attach"), 0},
   {"kexec",   'X', 0,  0, N_("use kexec to boot Linux kernels via 
systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0},
+  {"tpm-device",  't', N_("DEV"), 0, N_("Set TPM device."), 0},
   { 0, 0, 0, 0, 0, 0 }
 };
 
@@ -168,6 +169,10 @@ argp_parser (int key, char *arg, struct argp_state *state)
 case 'X':
   grub_util_set_kexecute ();
   break;
+case 't':
+  free (tpm_dev);
+  tpm_dev = xstrdup (arg);
+  break;
 
 case ARGP_KEY_ARG:
   {
@@ -276,6 +281,9 @@ main (int argc, char *argv[])
 
   dir = xstrdup (dir);
 
+  if (tpm_dev)
+grub_util_tpm_open (tpm_dev);
+
   /* Start GRUB!  */
   if (setjmp (main_env) == 0)
 grub_main ();
@@ -283,6 +291,7 @@ main (int argc, char *argv[])
   grub_fini_all ();
   grub_hostfs_fini ();
   grub_host_fini ();
+  grub_util_tpm_close ();
 
   grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
 
diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
index 521220b49..1db24fde7 100644
--- a/grub-core/kern/emu/misc.c
+++ b/grub-core/kern/emu/misc.c
@@ -28,6 +28,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include 
 #include 
@@ -41,6 +43,8 @@
 int verbosity;
 int kexecute;
 
+static int grub_util_tpm_fd = -1;
+
 void
 grub_util_warn (const char *fmt, ...)
 {
@@ -230,3 +234,50 @@ grub_util_get_kexecute (void)
 {
   return kexecute;
 }
+
+grub_err_t
+grub_util_tpm_open (const char *tpm_dev)
+{
+  if (grub_util_tpm_fd != -1)
+return GRUB_ERR_NONE;
+
+  grub_util_tpm_fd = open (tpm_dev, O_RDWR);
+  if (grub_util_tpm_fd == -1)
+grub_util_error (_("cannot open TPM device '%s': %s"), tpm_dev, strerror 
(errno));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_util_tpm_close (void)
+{
+  int err;
+
+  if (grub_util_tpm_fd == -1)
+return GRUB_ERR_NONE;
+
+  err = close (grub_util_tpm_fd);
+  if (err != GRUB_ERR_NONE)
+grub_util_error (_("cannot close TPM device: %s"), strerror (errno));
+
+  grub_util_tpm_fd = -1;
+  return GRUB_ERR_NONE;
+}
+
+grub_size_t
+grub_util_tpm_read (void *output, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return read (grub_util_tpm_fd, output, size);
+}
+
+grub_size_t
+grub_util_tpm_write (const void *input, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return write (grub_util_tpm_fd, input, size);
+}
diff --git a/grub-core/tpm2/tcg2-emu.c b/grub-core/tpm2/tcg2-emu.c
new file mode 100644
index 0..0d7b8b16e
--- /dev/null
+++ b/grub-core/tpm2/tcg2-emu.c
@@ -0,0 +1,52 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 SUSE LLC
+ *
+ *  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 

[PATCH v16 17/20] cryptodisk: wipe out the cached keys from protectors

2024-05-14 Thread Gary Lin via Grub-devel
An attacker may insert a malicious disk with the same crypto UUID and
trick grub2 to mount the fake root. Even though the key from the key
protector fails to unlock the fake root, it's not wiped out cleanly so
the attacker could dump the memory to retrieve the secret key. To defend
such attack, wipe out the cached key when we don't need it.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/cryptodisk.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index ffb41a5fe..5032d6b70 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1399,7 +1399,11 @@ grub_cryptodisk_clear_key_cache (struct 
grub_cryptomount_args *cargs)
 return;
 
   for (i = 0; cargs->protectors[i]; i++)
-grub_free (cargs->key_cache[i].key);
+{
+  if (cargs->key_cache[i].key)
+   grub_memset (cargs->key_cache[i].key, 0, cargs->key_cache[i].key_len);
+  grub_free (cargs->key_cache[i].key);
+}
 
   grub_free (cargs->key_cache);
 }
-- 
2.35.3


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


[PATCH v16 03/20] libtasn1: disable code not needed in grub

2024-05-14 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

We don't expect to be able to write ASN.1, only read it,
so we can disable some code.

Do that with #if 0/#endif, rather than deletion. This means
that the difference between upstream and grub is smaller,
which should make updating libtasn1 easier in the future.

With these exclusions we also avoid the need for minmax.h,
which is convenient because it means we don't have to
import it from gnulib.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 grub-core/lib/libtasn1/lib/coding.c| 12 ++--
 grub-core/lib/libtasn1/lib/decoding.c  |  2 ++
 grub-core/lib/libtasn1/lib/element.c   |  6 +++---
 grub-core/lib/libtasn1/lib/errors.c|  3 +++
 grub-core/lib/libtasn1/lib/structure.c | 10 ++
 include/grub/libtasn1.h| 15 +++
 6 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/grub-core/lib/libtasn1/lib/coding.c 
b/grub-core/lib/libtasn1/lib/coding.c
index ea5bc370e..5d03bca9d 100644
--- a/grub-core/lib/libtasn1/lib/coding.c
+++ b/grub-core/lib/libtasn1/lib/coding.c
@@ -30,11 +30,11 @@
 #include "parser_aux.h"
 #include 
 #include "element.h"
-#include "minmax.h"
 #include 
 
 #define MAX_TAG_LEN 16
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_error_description_value_not_found */
 /* Description: creates the ErrorDescription string   */
@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node,
   Estrcat (ErrorDescription, "' not found");
 
 }
+#endif
 
 /**
  * asn1_length_der:
@@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned 
char *str,
   return ASN1_SUCCESS;
 }
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_time_der  */
 /* Description: creates the DER coding for a TIME */
@@ -278,7 +280,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned 
char *der,
 
   return ASN1_SUCCESS;
 }
-
+#endif
 
 /*
 void
@@ -519,6 +521,7 @@ asn1_bit_der (const unsigned char *str, int bit_len,
 }
 
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_complete_explicit_tag */
 /* Description: add the length coding to the EXPLICIT */
@@ -595,6 +598,7 @@ _asn1_complete_explicit_tag (asn1_node node, unsigned char 
*der,
 
   return ASN1_SUCCESS;
 }
+#endif
 
 const tag_and_class_st _asn1_tags[] = {
   [ASN1_ETYPE_GENERALSTRING] =
@@ -647,6 +651,8 @@ const tag_and_class_st _asn1_tags[] = {
 
 unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]);
 
+
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_insert_tag_der*/
 /* Description: creates the DER coding of tags of one */
@@ -1423,3 +1429,5 @@ error:
   asn1_delete_structure ();
   return err;
 }
+
+#endif
diff --git a/grub-core/lib/libtasn1/lib/decoding.c 
b/grub-core/lib/libtasn1/lib/decoding.c
index b9245c486..bf9cb13ac 100644
--- a/grub-core/lib/libtasn1/lib/decoding.c
+++ b/grub-core/lib/libtasn1/lib/decoding.c
@@ -1620,6 +1620,7 @@ asn1_der_decoding (asn1_node * element, const void *ider, 
int ider_len,
   return asn1_der_decoding2 (element, ider, _len, 0, errorDescription);
 }
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**
  * asn1_der_decoding_element:
  * @structure: pointer to an ASN1 structure
@@ -1650,6 +1651,7 @@ asn1_der_decoding_element (asn1_node * structure, const 
char *elementName,
 {
   return asn1_der_decoding (structure, ider, len, errorDescription);
 }
+#endif
 
 /**
  * asn1_der_decoding_startEnd:
diff --git a/grub-core/lib/libtasn1/lib/element.c 
b/grub-core/lib/libtasn1/lib/element.c
index d4c558e10..bc4c3c8d7 100644
--- a/grub-core/lib/libtasn1/lib/element.c
+++ b/grub-core/lib/libtasn1/lib/element.c
@@ -118,7 +118,7 @@ _asn1_convert_integer (const unsigned char *value, unsigned 
char *value_out,
value_out[k2 - k] = val[k2];
 }
 
-#if 0
+#if 0 /* GRUB SKIPPED IMPORTING */
   printf ("_asn1_convert_integer: valueIn=%s, lenOut=%d", value, *len);
   for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++)
 printf (", vOut[%d]=%d", k, value_out[k]);
@@ -191,7 +191,7 @@ _asn1_append_sequence_set (asn1_node node, struct 
node_tail_cache_st *pcache)
   return ASN1_SUCCESS;
 }
 
-
+#if 0
 /**
  * asn1_write_value:
  * @node_root: pointer to a structure
@@ -646,7 +646,7 @@ asn1_write_value (asn1_node node_root, const char *name,
 
   return ASN1_SUCCESS;
 }
-
+#endif
 
 #define PUT_VALUE( ptr, ptr_size, data, data_size) \
*len = data_size; \
diff --git a/grub-core/lib/libtasn1/lib/errors.c 
b/grub-core/lib/libtasn1/lib/errors.c
index aef5dfe6f..2b2322152 100644
--- a/grub-core/lib/libtasn1/lib/errors.c
+++ b/grub-core/lib/libtasn1/lib/errors.c
@@ -57,6 +57,8 @@ static const libtasn1_error_entry error_algorithms[] = {
   {0, 0}
 };
 
+
+#if 

[PATCH v16 06/20] libtasn1: compile into asn1 module

2024-05-14 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

Create a wrapper file that specifies the module license.
Set up the makefile so it is built.

Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
Reviewed-by: Vladimir Serbinenko 
---
 grub-core/Makefile.core.def| 15 +++
 grub-core/lib/libtasn1_wrap/wrap.c | 26 ++
 2 files changed, 41 insertions(+)
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 8e1b1d9f3..109b19851 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2600,3 +2600,18 @@ module = {
   enable = efi;
   depends = part_gpt;
 };
+
+module = {
+  name = asn1;
+  common = lib/libtasn1/lib/decoding.c;
+  common = lib/libtasn1/lib/coding.c;
+  common = lib/libtasn1/lib/element.c;
+  common = lib/libtasn1/lib/structure.c;
+  common = lib/libtasn1/lib/parser_aux.c;
+  common = lib/libtasn1/lib/gstr.c;
+  common = lib/libtasn1/lib/errors.c;
+  common = lib/libtasn1_wrap/wrap.c;
+  cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+  // -Wno-type-limits comes from libtasn1's configure.ac
+  cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) 
-I$(srcdir)/lib/libtasn1/lib -Wno-type-limits';
+};
diff --git a/grub-core/lib/libtasn1_wrap/wrap.c 
b/grub-core/lib/libtasn1_wrap/wrap.c
new file mode 100644
index 0..622ba942e
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/wrap.c
@@ -0,0 +1,26 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020 IBM 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 .
+ */
+
+#include 
+
+/*
+ * libtasn1 is provided under LGPL2.1+, which is compatible
+ * with GPL3+. As Grub as a whole is under GPL3+, this module
+ * is therefore under GPL3+ also.
+ */
+GRUB_MOD_LICENSE ("GPLv3+");
-- 
2.35.3


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


[PATCH v16 15/20] tpm2: Implement NV index

2024-05-14 Thread Gary Lin via Grub-devel
From: Patrick Colp 

Currently with the TPM2 protector, only SRK mode is supported and
NV index support is just a stub. Implement the NV index option.

Note: This only extends support on the unseal path. grub2_protect
has not been updated. tpm2-tools can be used to insert a key into
the NV index.

An example of inserting a key using tpm2-tools:

  # Get random key.
  tpm2_getrandom 32 > key.dat

  # Create primary object.
  tpm2_createprimary -C o -g sha256 -G ecc -c primary.ctx

  # Create policy object. `pcrs.dat` contains the PCR values to seal against.
  tpm2_startauthsession -S session.dat
  tpm2_policypcr -S session.dat -l sha256:7,11 -f pcrs.dat -L policy.dat
  tpm2_flushcontext session.dat

  # Seal key into TPM.
  cat key.dat | tpm2_create -C primary.ctx -u key.pub -r key.priv -L policy.dat 
-i-
  tpm2_load -C primary.ctx -u key.pub -r key.priv -n sealing.name -c sealing.ctx
  tpm2_evictcontrol -C o -c sealing.ctx 0x8100

Then to unseal the key in grub, add this to grub.cfg:

  tpm2_key_protector_init --mode=nv --nvindex=0x8100 --pcrs=7,11
  cryptomount -u  --protector tpm2

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/tpm2/module.c | 25 -
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
index e83b02865..b754b38df 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -1035,12 +1035,27 @@ 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;
+  TPM_HANDLE sealed_handle = ctx->nv;
+  tpm2key_policy_t policy_seq = NULL;
+  grub_err_t err;
+
+  /* Create a basic policy sequence based on the given PCR selection */
+  err = grub_tpm2_protector_simple_policy_seq (ctx, _seq);
+  if (err != GRUB_ERR_NONE)
+goto exit;
+
+  err = grub_tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size);
+
+  /* Pop error messages on success */
+  if (err == GRUB_ERR_NONE)
+while (grub_error_pop ());
+
+exit:
+  TPM2_FlushContext (sealed_handle);
 
-  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-N_("NV Index mode is not implemented yet"));
+  grub_tpm2key_free_policy_seq (policy_seq);
+
+  return err;
 }
 
 static grub_err_t
-- 
2.35.3


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


[PATCH v16 16/20] cryptodisk: Fallback to passphrase

2024-05-14 Thread Gary Lin via Grub-devel
From: Patrick Colp 

If a protector is specified, but it fails to unlock the disk, fall back
to asking for the passphrase. However, an error was set indicating that
the protector(s) failed. Later code (e.g., LUKS code) fails as
`grub_errno` is now set. Print the existing errors out first, before
proceeding with the passphrase.

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/cryptodisk.c | 17 -
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index cfa3e28ce..ffb41a5fe 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1167,6 +1167,10 @@ grub_cryptodisk_scan_device_real (const char *name,
  ret = cr->recover_key (source, dev, cargs);
  if (ret != GRUB_ERR_NONE)
{
+ /* Reset key data to trigger the passphrase prompt later */
+ cargs->key_data = NULL;
+ cargs->key_len = 0;
+
  part = grub_partition_get_name (source->partition);
  grub_dprintf ("cryptodisk",
"recovered a key from key protector %s but it "
@@ -1192,7 +1196,6 @@ grub_cryptodisk_scan_device_real (const char *name,
  source->name, source->partition != NULL ? "," : "",
  part != NULL ? part : N_("UNKNOWN"), dev->uuid);
   grub_free (part);
-  goto error;
 }
 
   if (cargs->key_len)
@@ -1207,6 +1210,18 @@ grub_cryptodisk_scan_device_real (const char *name,
   unsigned long tries = 3;
   const char *tries_env;
 
+  /*
+   * Print the error from key protectors and clear grub_errno.
+   * Since '--protector' doesn't not coexist with '--password' and
+   * '--key-file', only "cargs->key_len == 0" is expected if all
+   * key protectors fail.
+   */
+  if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
   askpass = 1;
   cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
   if (cargs->key_data == NULL)
-- 
2.35.3


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


[PATCH v16 11/20] key_protector: Add TPM2 Key Protector

2024-05-14 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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

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

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

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

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

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

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

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

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

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

The TPM2 key protector registers two commands:

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

- tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init.

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

For instance, to unseal the raw sealed key file:

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

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

Or, to unseal the TPM 2.0 Key file:

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

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

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

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

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

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

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

Cc: Stefan Berger 
Cc: James Bottomley 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/Makefile.core.def   |   13 +
 grub-core/tpm2/args.c |  140 
 grub-core/tpm2/module.c   | 1226 

[PATCH v16 09/20] key_protector: Add key protectors framework

2024-05-14 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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 by name. If a key protector with the specified
name exists and if an unlocking key is successfully retrieved by it, the
function returns to the caller the retrieved key and its length.

Cc: Vladimir Serbinenko 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/Makefile.am  |  1 +
 grub-core/Makefile.core.def|  5 +++
 grub-core/disk/key_protector.c | 78 ++
 include/grub/key_protector.h   | 46 
 4 files changed, 130 insertions(+)
 create mode 100644 grub-core/disk/key_protector.c
 create mode 100644 include/grub/key_protector.h

diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index 1eda467e0..e50db8106 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/key_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 c21501869..d42e19af3 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1282,6 +1282,11 @@ module = {
   common = disk/raid6_recover.c;
 };
 
+module = {
+  name = key_protector;
+  common = disk/key_protector.c;
+};
+
 module = {
   name = scsi;
   common = disk/scsi.c;
diff --git a/grub-core/disk/key_protector.c b/grub-core/disk/key_protector.c
new file mode 100644
index 0..b84afe1c7
--- /dev/null
+++ b/grub-core/disk/key_protector.c
@@ -0,0 +1,78 @@
+/*
+ *  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 .
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+struct grub_key_protector *grub_key_protectors = NULL;
+
+grub_err_t
+grub_key_protector_register (struct grub_key_protector *protector)
+{
+  if (protector == NULL || protector->name == NULL || grub_strlen 
(protector->name) == 0)
+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 (_key_protectors),
+ GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_unregister (struct grub_key_protector *protector)
+{
+  if (protector == NULL)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_list_remove (GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_recover_key (const char *protector, grub_uint8_t **key,
+   grub_size_t *key_size)
+{
+  struct grub_key_protector *kp = NULL;
+
+  if (grub_key_protectors == NULL)
+return GRUB_ERR_OUT_OF_RANGE;
+
+  if (protector == NULL || grub_strlen (protector) == 0)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  kp = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
+protector);
+  if (kp == NULL)
+return grub_error (GRUB_ERR_OUT_OF_RANGE,
+  N_("A key protector with name '%s' could not be found. "
+ "Is the name spelled correctly and is the "
+ "corresponding module loaded?"), protector);
+
+  return kp->recover_key (key, key_size);
+}
diff --git a/include/grub/key_protector.h b/include/grub/key_protector.h
new file mode 100644
index 0..6e6a6fb24
--- /dev/null
+++ b/include/grub/key_protector.h
@@ -0,0 +1,46 @@
+/*
+ *  GRUB  --  

[PATCH v16 18/20] diskfilter: look up cryptodisk devices first

2024-05-14 Thread Gary Lin via Grub-devel
When using disk auto-unlocking with TPM 2.0, the typical grub.cfg may
look like this:

  tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
  cryptomount -u  -P tpm2
  search --fs-uuid --set=root 

Since the disk search order is based on the order of module loading, the
attacker could insert a malicious disk with the same FS-UUID root to
trick grub2 to boot into the malicious root and further dump memory to
steal the unsealed key.

Do defend against such an attack, we can specify the hint provided by
'grub-probe' to search the encrypted partition first:

search --fs-uuid --set=root --hint='cryptouuid/' 

However, for LVM on an encrypted partition, the search hint provided by
'grub-probe' is:

  --hint='lvmid//'

It doesn't guarantee to look up the logical volume from the encrypted
partition, so the attacker may have the chance to fool grub2 to boot
into the malicious disk.

To minimize the attack surface, this commit tweaks the disk device search
in diskfilter to look up cryptodisk devices first and then others, so
that the auto-unlocked disk will be found first, not the attacker's disk.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/diskfilter.c | 35 ++-
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c
index 21e239511..df1992305 100644
--- a/grub-core/disk/diskfilter.c
+++ b/grub-core/disk/diskfilter.c
@@ -226,15 +226,32 @@ scan_devices (const char *arname)
   int need_rescan;
 
   for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++)
-for (p = grub_disk_dev_list; p; p = p->next)
-  if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
- && p->disk_iterate)
-   {
- if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
-   return;
- if (arname && is_lv_readable (find_lv (arname), 1))
-   return;
-   }
+{
+  /* look up the crytodisk devices first */
+  for (p = grub_disk_dev_list; p; p = p->next)
+   if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID
+   && p->disk_iterate)
+ {
+   if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
+ return;
+   if (arname && is_lv_readable (find_lv (arname), 1))
+ return;
+   break;
+ }
+
+  /* check the devices other than crytodisk */
+  for (p = grub_disk_dev_list; p; p = p->next)
+   if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ continue;
+   else if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
+   && p->disk_iterate)
+ {
+   if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
+ return;
+   if (arname && is_lv_readable (find_lv (arname), 1))
+ return;
+ }
+}
 
   scan_depth = 0;
   need_rescan = 1;
-- 
2.35.3


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


[PATCH v16 14/20] tpm2: Support authorized policy

2024-05-14 Thread Gary Lin via Grub-devel
This commit handles the TPM2_PolicyAuthorize command from the key file
in TPM 2.0 Key File format.

TPM2_PolicyAuthorize is the essential command to support authorized
policy which allows the users to sign TPM policies with their own keys.
Per TPM 2.0 Key File(*1), CommandPolicy for TPM2_PolicyAuthorize
comprises 'TPM2B_PUBLIC pubkey', 'TPM2B_DIGEST policy_ref', and
'TPMT_SIGNATURE signature'. To verify the signature, the current policy
digest is hashed with the hash algorithm written in 'signature', and then
'signature' is verified with the hashed policy digest and 'pubkey'. Once
TPM accepts 'signature', TPM2_PolicyAuthorize is invoked to authorize the
signed policy.

To create the key file with authorized policy, here are the pcr-oracle(*2)
commands:

  # Generate the RSA key and create the authorized policy file
  $ pcr-oracle \
--rsa-generate-key \
--private-key policy-key.pem \
--auth authorized.policy \
create-authorized-policy 0,2,4,7,9

  # Seal the secret with the authorized policy
  $ pcr-oracle \
--key-format tpm2.0 \
--auth authorized.policy \
--input disk-secret.txt \
--output sealed.key \
seal-secret

  # Sign the predicted PCR policy
  $ pcr-oracle \
--key-format tpm2.0 \
--private-key policy-key.pem \
--from eventlog \
--stop-event "grub-file=grub.cfg" \
--after \
--input sealed.key \
--output sealed.tpm \
sign 0,2,4,7,9

Then specify the key file and the key protector to grub.cfg in the EFI
system partition:

tpm2_key_protector_init -a RSA --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

For any change in the boot components, just run the 'sign' command again
to update the signature in sealed.tpm, and TPM can unseal the key file
with the updated PCR policy.

(*1) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*2) https://github.com/okirch/pcr-oracle

Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/tpm2/module.c | 84 +
 1 file changed, 84 insertions(+)

diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
index 3db25ceca..e83b02865 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -650,6 +650,87 @@ grub_tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION 
session,
   return GRUB_ERR_NONE;
 }
 
+static grub_err_t
+grub_tpm2_protector_policyauthorize (TPMI_SH_AUTH_SESSION session,
+struct grub_tpm2_buffer *cmd_buf)
+{
+  TPM2B_PUBLIC pubkey;
+  TPM2B_DIGEST policy_ref;
+  TPMT_SIGNATURE signature;
+  TPM2B_DIGEST pcr_policy;
+  TPM2B_DIGEST pcr_policy_hash;
+  TPMI_ALG_HASH sig_hash;
+  TPMT_TK_VERIFIED verification_ticket;
+  TPM_HANDLE pubkey_handle = 0;
+  TPM2B_NAME pubname;
+  TPM_RC rc;
+  grub_err_t err;
+
+  grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (cmd_buf, );
+  grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, _ref);
+  grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (cmd_buf, );
+  if (cmd_buf->error != 0)
+return grub_error (GRUB_ERR_BAD_ARGUMENT,
+  N_("Failed to unmarshal the buffer for 
TPM2_PolicyAuthorize"));
+
+  /* Retrieve Policy Digest */
+  rc = TPM2_PolicyGetDigest (session, NULL, _policy, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to get policy digest (TPM2_PolicyGetDigest: 
0x%x)."),
+  rc);
+
+  /* Calculate the digest of the polcy for VerifySignature */
+  sig_hash = TPMT_SIGNATURE_get_hash_alg ();
+  if (sig_hash == TPM_ALG_NULL)
+return grub_error (GRUB_ERR_BAD_ARGUMENT,
+  N_("Failed to get the hash algorithm of the signature"));
+
+  rc = TPM2_Hash (NULL, (TPM2B_MAX_BUFFER *)_policy, sig_hash,
+ TPM_RH_NULL, _policy_hash, NULL, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to create PCR policy hash (TPM2_Hash: 0x%x)"),
+  rc);
+
+  /* Load the public key */
+  rc = TPM2_LoadExternal (NULL, NULL, , TPM_RH_OWNER,
+ _handle, , NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to load public key (TPM2_LoadExternal: 
0x%x)"),
+  rc);
+
+  /* Verify the signature against the public key and the policy digest */
+  rc = TPM2_VerifySignature (pubkey_handle, NULL, _policy_hash, ,
+_ticket, NULL);
+  if (rc != TPM_RC_SUCCESS)
+{
+  err = grub_error (GRUB_ERR_BAD_DEVICE,
+   N_("Failed to verify signature (TPM2_VerifySignature: 
0x%x)"),
+   rc);
+  goto error;
+}
+
+  /* Authorize the signed policy with the public key and the verification 
ticket */
+  rc = TPM2_PolicyAuthorize (session, NULL, _policy, _ref, ,
+_ticket, NULL);
+  if (rc != 

[PATCH v16 04/20] libtasn1: changes for grub compatibility

2024-05-14 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

Do a few things to make libtasn1 compile as part of grub:

 - remove _asn1_strcat and replace strcat with the bound-checked
   _asn1_str_cat except the one inside _asn1_str_cat. That strcat is
   replaced with strcpy.

 - adjust header paths in libtasn1.h

 - adjust header paths to "grub/libtasn1.h".

 - replace a 64 bit division with a call to grub_divmod64, preventing
   creation of __udivdi3 calls on 32 bit platforms.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/lib/libtasn1/lib/decoding.c   | 8 
 grub-core/lib/libtasn1/lib/element.c| 2 +-
 grub-core/lib/libtasn1/lib/gstr.c   | 2 +-
 grub-core/lib/libtasn1/lib/int.h| 3 +--
 grub-core/lib/libtasn1/lib/parser_aux.c | 2 +-
 include/grub/libtasn1.h | 5 ++---
 6 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/grub-core/lib/libtasn1/lib/decoding.c 
b/grub-core/lib/libtasn1/lib/decoding.c
index bf9cb13ac..51859fe36 100644
--- a/grub-core/lib/libtasn1/lib/decoding.c
+++ b/grub-core/lib/libtasn1/lib/decoding.c
@@ -2016,8 +2016,8 @@ asn1_expand_octet_string (asn1_node_const definitions, 
asn1_node * element,
  (p2->type & CONST_ASSIGN))
{
  strcpy (name, definitions->name);
- strcat (name, ".");
- strcat (name, p2->name);
+ _asn1_str_cat (name, sizeof (name), ".");
+ _asn1_str_cat (name, sizeof (name), p2->name);
 
  len = sizeof (value);
  result = asn1_read_value (definitions, name, value, );
@@ -2034,8 +2034,8 @@ asn1_expand_octet_string (asn1_node_const definitions, 
asn1_node * element,
  if (p2)
{
  strcpy (name, definitions->name);
- strcat (name, ".");
- strcat (name, p2->name);
+ _asn1_str_cat (name, sizeof (name), ".");
+ _asn1_str_cat (name, sizeof (name), p2->name);
 
  result = asn1_create_element (definitions, name, );
  if (result == ASN1_SUCCESS)
diff --git a/grub-core/lib/libtasn1/lib/element.c 
b/grub-core/lib/libtasn1/lib/element.c
index bc4c3c8d7..8694fecb9 100644
--- a/grub-core/lib/libtasn1/lib/element.c
+++ b/grub-core/lib/libtasn1/lib/element.c
@@ -688,7 +688,7 @@ asn1_write_value (asn1_node node_root, const char *name,
 return ASN1_MEM_ERROR; \
 } else { \
 /* this strcat is checked */ \
-if (ptr) _asn1_strcat (ptr, data); \
+if (ptr) _asn1_str_cat ((char *)ptr, ptr_size, (const char 
*)data); \
 }
 
 /**
diff --git a/grub-core/lib/libtasn1/lib/gstr.c 
b/grub-core/lib/libtasn1/lib/gstr.c
index eef419554..a9c16f5d3 100644
--- a/grub-core/lib/libtasn1/lib/gstr.c
+++ b/grub-core/lib/libtasn1/lib/gstr.c
@@ -36,7 +36,7 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char 
*src)
 
   if (dest_tot_size - dest_size > str_size)
 {
-  strcat (dest, src);
+  strcpy (dest + dest_size, src);
 }
   else
 {
diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h
index d94d51c8c..7409c7655 100644
--- a/grub-core/lib/libtasn1/lib/int.h
+++ b/grub-core/lib/libtasn1/lib/int.h
@@ -35,7 +35,7 @@
 #  include 
 # endif
 
-# include 
+# include "grub/libtasn1.h"
 
 # define ASN1_SMALL_VALUE_SIZE 16
 
@@ -115,7 +115,6 @@ extern const tag_and_class_st _asn1_tags[];
 # define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
 # define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
 # define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
-# define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
 
 # if SIZEOF_UNSIGNED_LONG_INT == 8
 #  define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b)
diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c 
b/grub-core/lib/libtasn1/lib/parser_aux.c
index c05bd2339..e4e4c0556 100644
--- a/grub-core/lib/libtasn1/lib/parser_aux.c
+++ b/grub-core/lib/libtasn1/lib/parser_aux.c
@@ -632,7 +632,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
   count = 0;
   do
 {
-  d = val / 10;
+  d = grub_divmod64(val, 10, NULL);
   r = val - d * 10;
   temp[start + count] = '0' + (char) r;
   count++;
diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
index 058ab27b0..7d64b6ab7 100644
--- a/include/grub/libtasn1.h
+++ b/include/grub/libtasn1.h
@@ -54,9 +54,8 @@
 #  define __LIBTASN1_PURE__
 # endif
 
-# include 
-# include 
-# include /* for FILE* */
+# include 
+# include 
 
 # ifdef __cplusplus
 extern "C"
-- 
2.35.3


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


[PATCH v16 01/20] posix_wrap: tweaks in preparation for libtasn1

2024-05-14 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

 - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as
   SIZEOF_UNSIGNED_LONG.

 - Define WORD_BIT, the size in bits of an int. This is a defined
   in the Single Unix Specification and in gnulib's limits.h. gnulib
   assumes it's 32 bits on all our platforms, including 64 bit
   platforms, so we also use that value.

 - Provide strto[u]l[l] preprocessor macros that resolve to
   grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we
   also define HAVE_STRTOUL here.

 - Implement c-ctype.h and the functions defined in the header.

 - Implement strncat in string.h.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 grub-core/lib/posix_wrap/c-ctype.h   | 114 +++
 grub-core/lib/posix_wrap/limits.h|   1 +
 grub-core/lib/posix_wrap/stdlib.h|   8 ++
 grub-core/lib/posix_wrap/string.h|  21 +
 grub-core/lib/posix_wrap/sys/types.h |   1 +
 5 files changed, 145 insertions(+)
 create mode 100644 grub-core/lib/posix_wrap/c-ctype.h

diff --git a/grub-core/lib/posix_wrap/c-ctype.h 
b/grub-core/lib/posix_wrap/c-ctype.h
new file mode 100644
index 0..5f8fc8ce3
--- /dev/null
+++ b/grub-core/lib/posix_wrap/c-ctype.h
@@ -0,0 +1,114 @@
+/*
+ *  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 .
+ */
+
+#ifndef GRUB_POSIX_C_CTYPE_H
+#define GRUB_POSIX_C_CTYPE_H   1
+
+#include 
+
+static inline bool
+c_isspace (int c)
+{
+  return !!grub_isspace (c);
+}
+
+static inline bool
+c_isdigit (int c)
+{
+  return !!grub_isdigit (c);
+}
+
+static inline bool
+c_islower (int c)
+{
+  return !!grub_islower (c);
+}
+
+static inline bool
+c_isascii (int c)
+{
+  return !(c & ~0x7f);
+}
+
+static inline bool
+c_isupper (int c)
+{
+  return !!grub_isupper (c);
+}
+
+static inline bool
+c_isxdigit (int c)
+{
+  return !!grub_isxdigit (c);
+}
+
+static inline bool
+c_isprint (int c)
+{
+  return !!grub_isprint (c);
+}
+
+static inline bool
+c_iscntrl (int c)
+{
+  return !grub_isprint (c);
+}
+
+static inline bool
+c_isgraph (int c)
+{
+  return grub_isprint (c) && !grub_isspace (c);
+}
+
+static inline bool
+c_isalnum (int c)
+{
+  return grub_isalpha (c) || grub_isdigit (c);
+}
+
+static inline bool
+c_ispunct (int c)
+{
+  return grub_isprint (c) && !grub_isspace (c) && !c_isalnum (c);
+}
+
+static inline bool
+c_isalpha (int c)
+{
+  return !!grub_isalpha (c);
+}
+
+static inline bool
+c_isblank (int c)
+{
+  return c == ' ' || c == '\t';
+}
+
+static inline int
+c_tolower (int c)
+{
+  return grub_tolower (c);
+}
+
+static inline int
+c_toupper (int c)
+{
+  return grub_toupper (c);
+}
+
+#endif
diff --git a/grub-core/lib/posix_wrap/limits.h 
b/grub-core/lib/posix_wrap/limits.h
index 26918c8a0..4be7b4080 100644
--- a/grub-core/lib/posix_wrap/limits.h
+++ b/grub-core/lib/posix_wrap/limits.h
@@ -41,5 +41,6 @@
 #define LONG_MAX GRUB_LONG_MAX
 
 #define CHAR_BIT 8
+#define WORD_BIT 32
 
 #endif
diff --git a/grub-core/lib/posix_wrap/stdlib.h 
b/grub-core/lib/posix_wrap/stdlib.h
index f5279756a..14e4efdd0 100644
--- a/grub-core/lib/posix_wrap/stdlib.h
+++ b/grub-core/lib/posix_wrap/stdlib.h
@@ -64,4 +64,12 @@ abort (void)
   grub_abort ();
 }
 
+#define strtol grub_strtol
+
+/* for libgcrypt */
+#define HAVE_STRTOUL
+#define strtoul grub_strtoul
+
+#define strtoull grub_strtoull
+
 #endif
diff --git a/grub-core/lib/posix_wrap/string.h 
b/grub-core/lib/posix_wrap/string.h
index 1adb450b5..b0c5928d2 100644
--- a/grub-core/lib/posix_wrap/string.h
+++ b/grub-core/lib/posix_wrap/string.h
@@ -84,6 +84,27 @@ memchr (const void *s, int c, grub_size_t n)
   return grub_memchr (s, c, n);
 }
 
+static inline char *
+strncat(char *dest, const char *src, grub_size_t n)
+{
+  const char *end;
+  char *str = dest;
+  grub_size_t src_len;
+
+  dest += grub_strlen (dest);
+
+  end = grub_memchr (src, '\0', n);
+  if (end != NULL)
+src_len = (grub_size_t) (end - src);
+  else
+src_len = n;
+
+  dest[src_len] = '\0';
+  grub_memcpy (dest, src, src_len);
+
+  return str;
+}
+
 #define memcmp grub_memcmp
 #define memcpy grub_memcpy
 #define memmove grub_memmove
diff --git a/grub-core/lib/posix_wrap/sys/types.h 
b/grub-core/lib/posix_wrap/sys/types.h
index eeda543c4..2f3e86549 100644
--- a/grub-core/lib/posix_wrap/sys/types.h
+++ b/grub-core/lib/posix_wrap/sys/types.h
@@ -50,6 

[PATCH v16 05/20] libtasn1: fix the potential buffer overrun

2024-05-14 Thread Gary Lin via Grub-devel
In _asn1_tag_der(), the first while loop for the long form may end up
with a 'k' value with 'ASN1_MAX_TAG_SIZE' and cause the buffer overrun
in the second while loop. This commit tweaks the conditional check to
avoid producing a too large 'k'.

This is a quick fix and may differ from the official upstream fix.

libtasn1 issue: https://gitlab.com/gnutls/libtasn1/-/issues/49

Signed-off-by: Gary Lin 
---
 grub-core/lib/libtasn1/lib/coding.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/grub-core/lib/libtasn1/lib/coding.c 
b/grub-core/lib/libtasn1/lib/coding.c
index 5d03bca9d..0458829a5 100644
--- a/grub-core/lib/libtasn1/lib/coding.c
+++ b/grub-core/lib/libtasn1/lib/coding.c
@@ -143,7 +143,7 @@ _asn1_tag_der (unsigned char class, unsigned int tag_value,
  temp[k++] = tag_value & 0x7F;
  tag_value >>= 7;
 
- if (k > ASN1_MAX_TAG_SIZE - 1)
+ if (k >= ASN1_MAX_TAG_SIZE - 1)
break;  /* will not encode larger tags */
}
   *ans_len = k + 1;
-- 
2.35.3


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


[PATCH v16 08/20] libtasn1: Add the documentation

2024-05-14 Thread Gary Lin via Grub-devel
Document libtasn1 in docs/grub-dev.texi and add the upgrade steps.
Also add the patches to make libtasn1 compatible with grub code.

Signed-off-by: Gary Lin 
Reviewed-by: Vladimir Serbinenko 
---
 docs/grub-dev.texi|  34 ++
 ...asn1-disable-code-not-needed-in-grub.patch | 320 ++
 ...tasn1-changes-for-grub-compatibility.patch | 135 
 ...sn1-fix-the-potential-buffer-overrun.patch |  35 ++
 4 files changed, 524 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
 create mode 100644 
grub-core/lib/libtasn1-patches/0002-libtasn1-changes-for-grub-compatibility.patch
 create mode 100644 
grub-core/lib/libtasn1-patches/0003-libtasn1-fix-the-potential-buffer-overrun.patch

diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
index 1276c5930..0cd419390 100644
--- a/docs/grub-dev.texi
+++ b/docs/grub-dev.texi
@@ -506,6 +506,7 @@ to update it.
 * Gnulib::
 * jsmn::
 * minilzo::
+* libtasn1::
 @end menu
 
 @node Gnulib
@@ -596,6 +597,39 @@ cp minilzo-2.10/*.[hc] grub-core/lib/minilzo
 rm -r minilzo-2.10*
 @end example
 
+@node libtasn1
+@section libtasn1
+
+libtasn1 is a library providing Abstract Syntax Notation One (ASN.1, as
+specified by the X.680 ITU-T recommendation) parsing and structures management,
+and Distinguished Encoding Rules (DER, as per X.690) encoding and decoding
+functions.
+
+To upgrade to a new version of the libtasn1 library, download the release
+tarball and copy the files into the target directory:
+
+@example
+curl -L -O https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz
+tar -zxf libtasn1-4.19.0.tar.gz
+rm -r grub-core/lib/libtasn1/
+mkdir libtasn1/lib
+mkdir -p grub-core/lib/libtasn1/lib/
+cp libtasn1-4.19.0/@lbracechar{}README.md,COPYING@rbracechar{} 
grub-core/lib/libtasn1/
+cp 
libtasn1-4.19.0/lib/@lbracechar{}coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h@rbracechar{}
 grub-core/lib/libtasn1/lib/
+cp libtasn1-4.19.0/lib/includes/libtasn1.h include/grub/
+rm -rf libtasn1-4.19.0
+@end example
+
+After upgrading the library, it may be necessary to apply the patches in
+@file{grub-core/lib/libtasn1-patches/} to adjust the code to be compatible with
+grub. These patches were needed to use the current version of libtasn1. The
+existing patches may not apply cleanly, apply at all, or even be needed for a
+newer version of the library, and other patches maybe needed due to changes in
+the newer version. If existing patches need to be refreshed to apply cleanly,
+please include updated patches as part of the a patch set sent to the list.
+If new patches are needed or existing patches are not needed, also please send
+additions or removals as part of any patch set upgrading libtasn1.
+
 @node Debugging
 @chapter Debugging
 
diff --git 
a/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
 
b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
new file mode 100644
index 0..e3264409f
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
@@ -0,0 +1,320 @@
+From 715f65934a120730316751536194ec5ed86aed9c Mon Sep 17 00:00:00 2001
+From: Daniel Axtens 
+Date: Fri, 1 May 2020 17:12:23 +1000
+Subject: [PATCH 1/3] libtasn1: disable code not needed in grub
+
+We don't expect to be able to write ASN.1, only read it,
+so we can disable some code.
+
+Do that with #if 0/#endif, rather than deletion. This means
+that the difference between upstream and grub is smaller,
+which should make updating libtasn1 easier in the future.
+
+With these exclusions we also avoid the need for minmax.h,
+which is convenient because it means we don't have to
+import it from gnulib.
+
+Cc: Vladimir Serbinenko 
+Signed-off-by: Daniel Axtens 
+Signed-off-by: Gary Lin 
+---
+ grub-core/lib/libtasn1/lib/coding.c| 12 ++--
+ grub-core/lib/libtasn1/lib/decoding.c  |  2 ++
+ grub-core/lib/libtasn1/lib/element.c   |  6 +++---
+ grub-core/lib/libtasn1/lib/errors.c|  3 +++
+ grub-core/lib/libtasn1/lib/structure.c | 10 ++
+ include/grub/libtasn1.h| 15 +++
+ 6 files changed, 39 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/lib/libtasn1/lib/coding.c 
b/grub-core/lib/libtasn1/lib/coding.c
+index ea5bc370e..5d03bca9d 100644
+--- a/grub-core/lib/libtasn1/lib/coding.c
 b/grub-core/lib/libtasn1/lib/coding.c
+@@ -30,11 +30,11 @@
+ #include "parser_aux.h"
+ #include 
+ #include "element.h"
+-#include "minmax.h"
+ #include 
+ 
+ #define MAX_TAG_LEN 16
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_error_description_value_not_found */
+ /* Description: creates the ErrorDescription string   */
+@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node,
+   Estrcat (ErrorDescription, "' not 

Re: [PATCH v15 12/20] cryptodisk: Support key protectors

2024-05-13 Thread Gary Lin via Grub-devel
On Fri, May 10, 2024 at 02:35:00PM +0800, Gary Lin wrote:
> From: Hernan Gatta 
> 
> Add a new parameter to cryptomount to support the key protectors framework: 
> -P.
> The parameter is used to automatically retrieve a key from specified key
> protectors. The parameter may be repeated to specify any number of key
> protectors. These are tried in order until one provides a usable key for any
> given disk.
> 
I'm aware of the conflict between this patch and with latest master
branch. Will rebase the whole patch set and send a new version.

Gary Lin

> Signed-off-by: Hernan Gatta 
> Signed-off-by: Michael Chang 
> Signed-off-by: Gary Lin 
> Reviewed-by: Glenn Washburn 
> Reviewed-by: Stefan Berger 

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


[PATCH v15 15/20] tpm2: Implement NV index

2024-05-10 Thread Gary Lin via Grub-devel
From: Patrick Colp 

Currently with the TPM2 protector, only SRK mode is supported and
NV index support is just a stub. Implement the NV index option.

Note: This only extends support on the unseal path. grub2_protect
has not been updated. tpm2-tools can be used to insert a key into
the NV index.

An example of inserting a key using tpm2-tools:

  # Get random key.
  tpm2_getrandom 32 > key.dat

  # Create primary object.
  tpm2_createprimary -C o -g sha256 -G ecc -c primary.ctx

  # Create policy object. `pcrs.dat` contains the PCR values to seal against.
  tpm2_startauthsession -S session.dat
  tpm2_policypcr -S session.dat -l sha256:7,11 -f pcrs.dat -L policy.dat
  tpm2_flushcontext session.dat

  # Seal key into TPM.
  cat key.dat | tpm2_create -C primary.ctx -u key.pub -r key.priv -L policy.dat 
-i-
  tpm2_load -C primary.ctx -u key.pub -r key.priv -n sealing.name -c sealing.ctx
  tpm2_evictcontrol -C o -c sealing.ctx 0x8100

Then to unseal the key in grub, add this to grub.cfg:

  tpm2_key_protector_init --mode=nv --nvindex=0x8100 --pcrs=7,11
  cryptomount -u  --protector tpm2

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/tpm2/module.c | 25 -
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
index e83b02865..b754b38df 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -1035,12 +1035,27 @@ 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;
+  TPM_HANDLE sealed_handle = ctx->nv;
+  tpm2key_policy_t policy_seq = NULL;
+  grub_err_t err;
+
+  /* Create a basic policy sequence based on the given PCR selection */
+  err = grub_tpm2_protector_simple_policy_seq (ctx, _seq);
+  if (err != GRUB_ERR_NONE)
+goto exit;
+
+  err = grub_tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size);
+
+  /* Pop error messages on success */
+  if (err == GRUB_ERR_NONE)
+while (grub_error_pop ());
+
+exit:
+  TPM2_FlushContext (sealed_handle);
 
-  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-N_("NV Index mode is not implemented yet"));
+  grub_tpm2key_free_policy_seq (policy_seq);
+
+  return err;
 }
 
 static grub_err_t
-- 
2.35.3


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


[PATCH v15 16/20] cryptodisk: Fallback to passphrase

2024-05-10 Thread Gary Lin via Grub-devel
From: Patrick Colp 

If a protector is specified, but it fails to unlock the disk, fall back
to asking for the passphrase. However, an error was set indicating that
the protector(s) failed. Later code (e.g., LUKS code) fails as
`grub_errno` is now set. Print the existing errors out first, before
proceeding with the passphrase.

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/cryptodisk.c | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index b7648ffb7..8f00f22a8 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1191,11 +1191,16 @@ grub_cryptodisk_scan_device_real (const char *name,
  source->name, source->partition != NULL ? "," : "",
  part != NULL ? part : N_("UNKNOWN"), dev->uuid);
   grub_free (part);
-  goto error;
 }
 
   if (!cargs->key_len)
 {
+  if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
   /* Get the passphrase from the user, if no key data. */
   askpass = 1;
   part = grub_partition_get_name (source->partition);
-- 
2.35.3


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


[PATCH v15 11/20] key_protector: Add TPM2 Key Protector

2024-05-10 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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

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

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

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

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

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

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

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

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

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

The TPM2 key protector registers two commands:

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

- tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init.

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

For instance, to unseal the raw sealed key file:

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

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

Or, to unseal the TPM 2.0 Key file:

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

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

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

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

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

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

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

Cc: Stefan Berger 
Cc: James Bottomley 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/Makefile.core.def   |   13 +
 grub-core/tpm2/args.c |  140 
 grub-core/tpm2/module.c   | 1226 

[PATCH v15 08/20] libtasn1: Add the documentation

2024-05-10 Thread Gary Lin via Grub-devel
Document libtasn1 in docs/grub-dev.texi and add the upgrade steps.
Also add the patches to make libtasn1 compatible with grub code.

Signed-off-by: Gary Lin 
Reviewed-by: Vladimir Serbinenko 
---
 docs/grub-dev.texi|  34 ++
 ...asn1-disable-code-not-needed-in-grub.patch | 320 ++
 ...tasn1-changes-for-grub-compatibility.patch | 135 
 ...sn1-fix-the-potential-buffer-overrun.patch |  35 ++
 4 files changed, 524 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
 create mode 100644 
grub-core/lib/libtasn1-patches/0002-libtasn1-changes-for-grub-compatibility.patch
 create mode 100644 
grub-core/lib/libtasn1-patches/0003-libtasn1-fix-the-potential-buffer-overrun.patch

diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
index 1276c5930..0cd419390 100644
--- a/docs/grub-dev.texi
+++ b/docs/grub-dev.texi
@@ -506,6 +506,7 @@ to update it.
 * Gnulib::
 * jsmn::
 * minilzo::
+* libtasn1::
 @end menu
 
 @node Gnulib
@@ -596,6 +597,39 @@ cp minilzo-2.10/*.[hc] grub-core/lib/minilzo
 rm -r minilzo-2.10*
 @end example
 
+@node libtasn1
+@section libtasn1
+
+libtasn1 is a library providing Abstract Syntax Notation One (ASN.1, as
+specified by the X.680 ITU-T recommendation) parsing and structures management,
+and Distinguished Encoding Rules (DER, as per X.690) encoding and decoding
+functions.
+
+To upgrade to a new version of the libtasn1 library, download the release
+tarball and copy the files into the target directory:
+
+@example
+curl -L -O https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz
+tar -zxf libtasn1-4.19.0.tar.gz
+rm -r grub-core/lib/libtasn1/
+mkdir libtasn1/lib
+mkdir -p grub-core/lib/libtasn1/lib/
+cp libtasn1-4.19.0/@lbracechar{}README.md,COPYING@rbracechar{} 
grub-core/lib/libtasn1/
+cp 
libtasn1-4.19.0/lib/@lbracechar{}coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h@rbracechar{}
 grub-core/lib/libtasn1/lib/
+cp libtasn1-4.19.0/lib/includes/libtasn1.h include/grub/
+rm -rf libtasn1-4.19.0
+@end example
+
+After upgrading the library, it may be necessary to apply the patches in
+@file{grub-core/lib/libtasn1-patches/} to adjust the code to be compatible with
+grub. These patches were needed to use the current version of libtasn1. The
+existing patches may not apply cleanly, apply at all, or even be needed for a
+newer version of the library, and other patches maybe needed due to changes in
+the newer version. If existing patches need to be refreshed to apply cleanly,
+please include updated patches as part of the a patch set sent to the list.
+If new patches are needed or existing patches are not needed, also please send
+additions or removals as part of any patch set upgrading libtasn1.
+
 @node Debugging
 @chapter Debugging
 
diff --git 
a/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
 
b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
new file mode 100644
index 0..e3264409f
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
@@ -0,0 +1,320 @@
+From 715f65934a120730316751536194ec5ed86aed9c Mon Sep 17 00:00:00 2001
+From: Daniel Axtens 
+Date: Fri, 1 May 2020 17:12:23 +1000
+Subject: [PATCH 1/3] libtasn1: disable code not needed in grub
+
+We don't expect to be able to write ASN.1, only read it,
+so we can disable some code.
+
+Do that with #if 0/#endif, rather than deletion. This means
+that the difference between upstream and grub is smaller,
+which should make updating libtasn1 easier in the future.
+
+With these exclusions we also avoid the need for minmax.h,
+which is convenient because it means we don't have to
+import it from gnulib.
+
+Cc: Vladimir Serbinenko 
+Signed-off-by: Daniel Axtens 
+Signed-off-by: Gary Lin 
+---
+ grub-core/lib/libtasn1/lib/coding.c| 12 ++--
+ grub-core/lib/libtasn1/lib/decoding.c  |  2 ++
+ grub-core/lib/libtasn1/lib/element.c   |  6 +++---
+ grub-core/lib/libtasn1/lib/errors.c|  3 +++
+ grub-core/lib/libtasn1/lib/structure.c | 10 ++
+ include/grub/libtasn1.h| 15 +++
+ 6 files changed, 39 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/lib/libtasn1/lib/coding.c 
b/grub-core/lib/libtasn1/lib/coding.c
+index ea5bc370e..5d03bca9d 100644
+--- a/grub-core/lib/libtasn1/lib/coding.c
 b/grub-core/lib/libtasn1/lib/coding.c
+@@ -30,11 +30,11 @@
+ #include "parser_aux.h"
+ #include 
+ #include "element.h"
+-#include "minmax.h"
+ #include 
+ 
+ #define MAX_TAG_LEN 16
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_error_description_value_not_found */
+ /* Description: creates the ErrorDescription string   */
+@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node,
+   Estrcat (ErrorDescription, "' not 

[PATCH v15 13/20] util/grub-protect: Add new tool

2024-05-10 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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 LUKS 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
has to ensure the cryptomount command is included in GRUB's boot script
and that it carries the requisite key protector (-P) parameter.

Sample usage:

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

To seal the key with TPM 2.0 Key File (recommended):

$ sudo grub-protect --action=add \
--protector=tpm2 \
--tpm2-pcrs=0,2,4,7,9 \
--tpm2key \
--tpm2-keyfile=luks-key \
--tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm

Or, to seal the key with the raw sealed key:

$ sudo grub-protect --action=add \
--protector=tpm2 \
--tpm2-pcrs=0,2,4,7,9 \
--tpm2-keyfile=luks-key \
--tpm2-outfile=/boot/efi/boot/grub2/sealed.key

Then, in the boot script, for TPM 2.0 Key File:

tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

Or, for the raw sealed key:

tpm2_key_protector_init --keyfile=(hd0,gpt1)/boot/grub2/sealed.key 
--pcrs=0,2,4,7,9
cryptomount -u  -P tpm2

The benefit of using TPM 2.0 Key File is that the PCR set is already
written in the key file, so there is no need to specify PCRs when
invoking tpm2_key_protector_init.

Cc: Stefan Berger 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 .gitignore|2 +
 Makefile.util.def |   24 +
 configure.ac  |   30 +
 docs/man/grub-protect.h2m |4 +
 util/grub-protect.c   | 1420 +
 5 files changed, 1480 insertions(+)
 create mode 100644 docs/man/grub-protect.h2m
 create mode 100644 util/grub-protect.c

diff --git a/.gitignore b/.gitignore
index 4d0dfb700..d7b7c22d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -169,6 +169,8 @@ widthspec.bin
 /grub-ofpathname.exe
 /grub-probe
 /grub-probe.exe
+/grub-protect
+/grub-protect.exe
 /grub-reboot
 /grub-render-label
 /grub-render-label.exe
diff --git a/Makefile.util.def b/Makefile.util.def
index 19ad5a96f..40bfe713d 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -207,6 +207,30 @@ program = {
   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
 };
 
+program = {
+  name = grub-protect;
+  mansection = 1;
+
+  common = grub-core/kern/emu/argp_common.c;
+  common = grub-core/osdep/init.c;
+  common = grub-core/tpm2/args.c;
+  common = grub-core/tpm2/buffer.c;
+  common = grub-core/tpm2/mu.c;
+  common = grub-core/tpm2/tpm2.c;
+  common = grub-core/tpm2/tpm2key_asn1_tab.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 = '$(LIBTASN1)';
+  ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) 
$(LIBGEOM)';
+
+  condition = COND_GRUB_PROTECT;
+};
+
 program = {
   name = grub-mkrelpath;
   mansection = 1;
diff --git a/configure.ac b/configure.ac
index 84a202c6e..3a07ab570 100644
--- a/configure.ac
+++ b/configure.ac
@@ -76,6 +76,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])
@@ -2057,6 +2058,29 @@ fi
 AC_SUBST([LIBZFS])
 AC_SUBST([LIBNVPAIR])
 
+AC_ARG_ENABLE([grub-protect],
+ [AS_HELP_STRING([--enable-grub-protect],
+ [build and install the `grub-protect' utility 
(default=guessed)])])
+if test x"$enable_grub_protect" = xno ; then
+  grub_protect_excuse="explicitly disabled"
+fi
+
+LIBTASN1=
+if test x"$grub_protect_excuse" = x ; then
+  AC_CHECK_LIB([tasn1], [asn1_write_value], [LIBTASN1="-ltasn1"], 
[grub_protect_excuse="need libtasn1 library"])
+fi
+AC_SUBST([LIBTASN1])
+
+if test x"$enable_grub_protect" = xyes && test x"$grub_protect_excuse" != x ; 
then
+  AC_MSG_ERROR([grub-protect was explicitly requested but can't be compiled 
($grub_protect_excuse)])
+fi
+if test x"$grub_protect_excuse" = x ; then

[PATCH v15 06/20] libtasn1: compile into asn1 module

2024-05-10 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

Create a wrapper file that specifies the module license.
Set up the makefile so it is built.

Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
Reviewed-by: Vladimir Serbinenko 
---
 grub-core/Makefile.core.def| 15 +++
 grub-core/lib/libtasn1_wrap/wrap.c | 26 ++
 2 files changed, 41 insertions(+)
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 007ff628e..93274bbaf 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2599,3 +2599,18 @@ module = {
   efi = commands/bli.c;
   enable = efi;
 };
+
+module = {
+  name = asn1;
+  common = lib/libtasn1/lib/decoding.c;
+  common = lib/libtasn1/lib/coding.c;
+  common = lib/libtasn1/lib/element.c;
+  common = lib/libtasn1/lib/structure.c;
+  common = lib/libtasn1/lib/parser_aux.c;
+  common = lib/libtasn1/lib/gstr.c;
+  common = lib/libtasn1/lib/errors.c;
+  common = lib/libtasn1_wrap/wrap.c;
+  cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+  // -Wno-type-limits comes from libtasn1's configure.ac
+  cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) 
-I$(srcdir)/lib/libtasn1/lib -Wno-type-limits';
+};
diff --git a/grub-core/lib/libtasn1_wrap/wrap.c 
b/grub-core/lib/libtasn1_wrap/wrap.c
new file mode 100644
index 0..622ba942e
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/wrap.c
@@ -0,0 +1,26 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020 IBM 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 .
+ */
+
+#include 
+
+/*
+ * libtasn1 is provided under LGPL2.1+, which is compatible
+ * with GPL3+. As Grub as a whole is under GPL3+, this module
+ * is therefore under GPL3+ also.
+ */
+GRUB_MOD_LICENSE ("GPLv3+");
-- 
2.35.3


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


[PATCH v15 17/20] cryptodisk: wipe out the cached keys from protectors

2024-05-10 Thread Gary Lin via Grub-devel
An attacker may insert a malicious disk with the same crypto UUID and
trick grub2 to mount the fake root. Even though the key from the key
protector fails to unlock the fake root, it's not wiped out cleanly so
the attacker could dump the memory to retrieve the secret key. To defend
such attack, wipe out the cached key when we don't need it.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/cryptodisk.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 8f00f22a8..3b19f2d2d 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1349,7 +1349,11 @@ grub_cryptodisk_clear_key_cache (struct 
grub_cryptomount_args *cargs)
 return;
 
   for (i = 0; cargs->protectors[i]; i++)
-grub_free (cargs->key_cache[i].key);
+{
+  if (cargs->key_cache[i].key)
+   grub_memset (cargs->key_cache[i].key, 0, cargs->key_cache[i].key_len);
+  grub_free (cargs->key_cache[i].key);
+}
 
   grub_free (cargs->key_cache);
 }
-- 
2.35.3


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


[PATCH v15 18/20] diskfilter: look up cryptodisk devices first

2024-05-10 Thread Gary Lin via Grub-devel
When using disk auto-unlocking with TPM 2.0, the typical grub.cfg may
look like this:

  tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
  cryptomount -u  -P tpm2
  search --fs-uuid --set=root 

Since the disk search order is based on the order of module loading, the
attacker could insert a malicious disk with the same FS-UUID root to
trick grub2 to boot into the malicious root and further dump memory to
steal the unsealed key.

Do defend against such an attack, we can specify the hint provided by
'grub-probe' to search the encrypted partition first:

search --fs-uuid --set=root --hint='cryptouuid/' 

However, for LVM on an encrypted partition, the search hint provided by
'grub-probe' is:

  --hint='lvmid//'

It doesn't guarantee to look up the logical volume from the encrypted
partition, so the attacker may have the chance to fool grub2 to boot
into the malicious disk.

To minimize the attack surface, this commit tweaks the disk device search
in diskfilter to look up cryptodisk devices first and then others, so
that the auto-unlocked disk will be found first, not the attacker's disk.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/disk/diskfilter.c | 35 ++-
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c
index 21e239511..df1992305 100644
--- a/grub-core/disk/diskfilter.c
+++ b/grub-core/disk/diskfilter.c
@@ -226,15 +226,32 @@ scan_devices (const char *arname)
   int need_rescan;
 
   for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++)
-for (p = grub_disk_dev_list; p; p = p->next)
-  if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
- && p->disk_iterate)
-   {
- if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
-   return;
- if (arname && is_lv_readable (find_lv (arname), 1))
-   return;
-   }
+{
+  /* look up the crytodisk devices first */
+  for (p = grub_disk_dev_list; p; p = p->next)
+   if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID
+   && p->disk_iterate)
+ {
+   if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
+ return;
+   if (arname && is_lv_readable (find_lv (arname), 1))
+ return;
+   break;
+ }
+
+  /* check the devices other than crytodisk */
+  for (p = grub_disk_dev_list; p; p = p->next)
+   if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ continue;
+   else if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
+   && p->disk_iterate)
+ {
+   if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
+ return;
+   if (arname && is_lv_readable (find_lv (arname), 1))
+ return;
+ }
+}
 
   scan_depth = 0;
   need_rescan = 1;
-- 
2.35.3


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


[PATCH v15 20/20] tests: Add tpm2_test

2024-05-10 Thread Gary Lin via Grub-devel
For the tpm2 module, the TCG2 command submission function is the only
difference between the a QEMU instance and grub-emu. To test TPM key
unsealing with a QEMU instance, it requires an extra OS image to invoke
grub-protect to seal the LUKS key, rather than a simple grub-shell rescue
CD image. On the other hand, grub-emu can share the emulated TPM device
with the host, so that we can seal the LUKS key on host and test key
unsealing with grub-emu.

This test script firstly creates a simple LUKS image to be loaded as a
loopback device in grub-emu. Then an emulated TPM device is created by
swtpm_cuse and PCR 0 and 1 are extended.

There are several test cases in the script to test various settings. Each
test case uses grub-protect or tpm2-tools to seal the LUKS password
against PCR 0 and PCR 1. Then grub-emu is launched to load the LUKS image,
try to mount the image with tpm2_key_protector_init and cryptomount, and
verify the result.

Based on the idea from Michael Chang.

Cc: Michael Chang 
Cc: Stefan Berger 
Cc: Glenn Washburn 
Signed-off-by: Gary Lin 
---
 Makefile.util.def|   6 +
 tests/tpm2_test.in   | 389 +++
 tests/util/grub-shell.in |   6 +-
 3 files changed, 400 insertions(+), 1 deletion(-)
 create mode 100644 tests/tpm2_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index 40bfe713d..8d4c53a03 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1281,6 +1281,12 @@ script = {
   common = tests/asn1_test.in;
 };
 
+script = {
+  testcase = native;
+  name = tpm2_test;
+  common = tests/tpm2_test.in;
+};
+
 program = {
   testcase = native;
   name = example_unit_test;
diff --git a/tests/tpm2_test.in b/tests/tpm2_test.in
new file mode 100644
index 0..aecd1a5a3
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,389 @@
+#! @BUILD_SHEBANG@ -e
+
+# Test GRUBs ability to unseal a LUKS key with TPM 2.0
+# 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 .
+
+grubshell=@builddir@/grub-shell
+
+. "@builddir@/grub-core/modinfo.sh"
+
+if [ x${grub_modinfo_platform} != xemu ]; then
+  exit 77
+fi
+
+builddir="@builddir@"
+
+# Force build directory components
+PATH="${builddir}:${PATH}"
+export PATH
+
+if [ "x${EUID}" = "x" ] ; then
+  EUID=`id -u`
+fi
+
+if [ "${EUID}" != 0 ] ; then
+   echo "not root; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v cryptsetup >/dev/null 2>&1; then
+   echo "cryptsetup not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; then
+   echo "no tpm_vtpm_proxy support; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v swtpm >/dev/null 2>&1; then
+   echo "swtpm not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v tpm2_startup >/dev/null 2>&1; then
+   echo "tpm2-tools not installed; cannot test tpm2."
+   exit 99
+fi
+
+tpm2testdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XX"`" || 
exit 99
+
+disksize=20M
+
+luksfile=${tpm2testdir}/luks.disk
+lukskeyfile=${tpm2testdir}/password.txt
+
+# Choose a low iteration number to reduce the time to decrypt the disk
+csopt="--type luks2 --pbkdf pbkdf2 --iter-time 1000"
+
+tpm2statedir=${tpm2testdir}/tpm
+tpm2ctrl=${tpm2statedir}/ctrl
+tpm2log=${tpm2statedir}/logfile
+
+sealedkey=${tpm2testdir}/sealed.tpm
+
+timeout=20
+
+testoutput=${tpm2testdir}/testoutput
+
+vtext="TEST VERIFIED"
+
+ret=0
+
+# Create the password file
+echo -n "top secret" > "${lukskeyfile}"
+
+# Setup LUKS2 image
+truncate -s ${disksize} "${luksfile}" || exit 99
+cryptsetup luksFormat -q ${csopt} "${luksfile}" "${lukskeyfile}" || exit 99
+
+# Write vtext into the first block of the LUKS2 image
+luksdev=/dev/mapper/`basename "${tpm2testdir}"`
+cryptsetup open --key-file "${lukskeyfile}" "${luksfile}" `basename 
"${luksdev}"` || exit 99
+echo "${vtext}" > "${luksdev}"
+cryptsetup close "${luksdev}"
+
+# Shutdown the swtpm instance on exit
+cleanup() {
+RET=$?
+if [ -e "${tpm2ctrl}" ]; then
+   swtpm_ioctl -s --unix "${tpm2ctrl}"
+fi
+if [ "${RET}" -eq 0 ]; then
+   rm -rf "$tpm2testdir" || :
+fi
+}
+trap cleanup EXIT INT TERM KILL QUIT
+
+mkdir -p "${tpm2statedir}"
+
+# Create the swtpm chardev instance
+swtpm chardev --vtpm-proxy --tpmstate dir="${tpm2statedir}" \
+   --tpm2 --ctrl type=unixio,path="${tpm2ctrl}" \
+   --flags startup-clear --daemon > 

[PATCH v15 19/20] tpm2: Enable tpm2 module for grub-emu

2024-05-10 Thread Gary Lin via Grub-devel
As a preparation to test TPM 2.0 TSS stack with grub-emu, the new
option, --tpm-device, is introduced to specify the TPM device for
grub-emu so that grub-emu can share the emulated TPM device with
the host.

Since grub-emu can directly access the device node on host, it's easy to
implement the essential TCG2 command submission function with the
read/write functions and enable tpm2 module for grub-emu, so that we can
further test TPM key unsealing with grub-emu.

Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/Makefile.core.def |  2 ++
 grub-core/kern/emu/main.c   | 11 +++-
 grub-core/kern/emu/misc.c   | 51 
 grub-core/tpm2/tcg2-emu.c   | 52 +
 include/grub/emu/misc.h |  5 
 5 files changed, 120 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/tpm2/tcg2-emu.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 85aaadf68..b2456a07e 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2571,7 +2571,9 @@ module = {
   common = tpm2/tpm2key.c;
   common = tpm2/tpm2key_asn1_tab.c;
   efi = tpm2/tcg2.c;
+  emu = tpm2/tcg2-emu.c;
   enable = efi;
+  enable = emu;
 };
 
 module = {
diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
index 855b11c3d..c10838613 100644
--- a/grub-core/kern/emu/main.c
+++ b/grub-core/kern/emu/main.c
@@ -55,7 +55,7 @@
 static jmp_buf main_env;
 
 /* Store the prefix specified by an argument.  */
-static char *root_dev = NULL, *dir = NULL;
+static char *root_dev = NULL, *dir = NULL, *tpm_dev = NULL;
 
 grub_addr_t grub_modbase = 0;
 
@@ -108,6 +108,7 @@ static struct argp_option options[] = {
   {"verbose", 'v', 0,  0, N_("print verbose messages."), 0},
   {"hold", 'H', N_("SECS"),  OPTION_ARG_OPTIONAL, N_("wait until a 
debugger will attach"), 0},
   {"kexec",   'X', 0,  0, N_("use kexec to boot Linux kernels via 
systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0},
+  {"tpm-device",  't', N_("DEV"), 0, N_("Set TPM device."), 0},
   { 0, 0, 0, 0, 0, 0 }
 };
 
@@ -168,6 +169,10 @@ argp_parser (int key, char *arg, struct argp_state *state)
 case 'X':
   grub_util_set_kexecute ();
   break;
+case 't':
+  free (tpm_dev);
+  tpm_dev = xstrdup (arg);
+  break;
 
 case ARGP_KEY_ARG:
   {
@@ -276,6 +281,9 @@ main (int argc, char *argv[])
 
   dir = xstrdup (dir);
 
+  if (tpm_dev)
+grub_util_tpm_open (tpm_dev);
+
   /* Start GRUB!  */
   if (setjmp (main_env) == 0)
 grub_main ();
@@ -283,6 +291,7 @@ main (int argc, char *argv[])
   grub_fini_all ();
   grub_hostfs_fini ();
   grub_host_fini ();
+  grub_util_tpm_close ();
 
   grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
 
diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
index 521220b49..1db24fde7 100644
--- a/grub-core/kern/emu/misc.c
+++ b/grub-core/kern/emu/misc.c
@@ -28,6 +28,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include 
 #include 
@@ -41,6 +43,8 @@
 int verbosity;
 int kexecute;
 
+static int grub_util_tpm_fd = -1;
+
 void
 grub_util_warn (const char *fmt, ...)
 {
@@ -230,3 +234,50 @@ grub_util_get_kexecute (void)
 {
   return kexecute;
 }
+
+grub_err_t
+grub_util_tpm_open (const char *tpm_dev)
+{
+  if (grub_util_tpm_fd != -1)
+return GRUB_ERR_NONE;
+
+  grub_util_tpm_fd = open (tpm_dev, O_RDWR);
+  if (grub_util_tpm_fd == -1)
+grub_util_error (_("cannot open TPM device '%s': %s"), tpm_dev, strerror 
(errno));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_util_tpm_close (void)
+{
+  int err;
+
+  if (grub_util_tpm_fd == -1)
+return GRUB_ERR_NONE;
+
+  err = close (grub_util_tpm_fd);
+  if (err != GRUB_ERR_NONE)
+grub_util_error (_("cannot close TPM device: %s"), strerror (errno));
+
+  grub_util_tpm_fd = -1;
+  return GRUB_ERR_NONE;
+}
+
+grub_size_t
+grub_util_tpm_read (void *output, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return read (grub_util_tpm_fd, output, size);
+}
+
+grub_size_t
+grub_util_tpm_write (const void *input, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return write (grub_util_tpm_fd, input, size);
+}
diff --git a/grub-core/tpm2/tcg2-emu.c b/grub-core/tpm2/tcg2-emu.c
new file mode 100644
index 0..0d7b8b16e
--- /dev/null
+++ b/grub-core/tpm2/tcg2-emu.c
@@ -0,0 +1,52 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 SUSE LLC
+ *
+ *  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 

[PATCH v15 12/20] cryptodisk: Support key protectors

2024-05-10 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

Add a new parameter to cryptomount to support the key protectors framework: -P.
The parameter is used to automatically retrieve a key from specified key
protectors. The parameter may be repeated to specify any number of key
protectors. These are tried in order until one provides a usable key for any
given disk.

Signed-off-by: Hernan Gatta 
Signed-off-by: Michael Chang 
Signed-off-by: Gary Lin 
Reviewed-by: Glenn Washburn 
Reviewed-by: Stefan Berger 
---
 Makefile.util.def   |   1 +
 grub-core/disk/cryptodisk.c | 172 +---
 include/grub/cryptodisk.h   |  16 
 3 files changed, 158 insertions(+), 31 deletions(-)

diff --git a/Makefile.util.def b/Makefile.util.def
index b53afb1d3..19ad5a96f 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -40,6 +40,7 @@ library = {
   common = grub-core/disk/luks.c;
   common = grub-core/disk/luks2.c;
   common = grub-core/disk/geli.c;
+  common = grub-core/disk/key_protector.c;
   common = grub-core/disk/cryptodisk.c;
   common = grub-core/disk/AFSplitter.c;
   common = grub-core/lib/pbkdf2.c;
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 2246af51b..b7648ffb7 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -26,6 +26,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #ifdef GRUB_UTIL
 #include 
@@ -44,7 +45,8 @@ enum
 OPTION_KEYFILE,
 OPTION_KEYFILE_OFFSET,
 OPTION_KEYFILE_SIZE,
-OPTION_HEADER
+OPTION_HEADER,
+OPTION_PROTECTOR
   };
 
 static const struct grub_arg_option options[] =
@@ -58,6 +60,8 @@ static const struct grub_arg_option options[] =
 {"keyfile-offset", 'O', 0, N_("Key file offset (bytes)"), 0, ARG_TYPE_INT},
 {"keyfile-size", 'S', 0, N_("Key file data size (bytes)"), 0, 
ARG_TYPE_INT},
 {"header", 'H', 0, N_("Read header from file"), 0, ARG_TYPE_STRING},
+{"protector", 'P', GRUB_ARG_OPTION_REPEATABLE,
+ N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING},
 {0, 0, 0, 0, 0, 0}
   };
 
@@ -1061,6 +1065,7 @@ grub_cryptodisk_scan_device_real (const char *name,
   grub_err_t ret = GRUB_ERR_NONE;
   grub_cryptodisk_t dev;
   grub_cryptodisk_dev_t cr;
+  int i;
   struct cryptodisk_read_hook_ctx read_hook_data = {0};
   int askpass = 0;
   char *part = NULL;
@@ -1113,41 +1118,112 @@ grub_cryptodisk_scan_device_real (const char *name,
   goto error_no_close;
 if (!dev)
   continue;
+break;
+  }
 
-if (!cargs->key_len)
-  {
-   /* Get the passphrase from the user, if no key data. */
-   askpass = 1;
-   part = grub_partition_get_name (source->partition);
-   grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name,
-source->partition != NULL ? "," : "",
-part != NULL ? part : N_("UNKNOWN"),
-dev->uuid);
-   grub_free (part);
-
-   cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
-   if (cargs->key_data == NULL)
- goto error_no_close;
-
-   if (!grub_password_get ((char *) cargs->key_data, 
GRUB_CRYPTODISK_MAX_PASSPHRASE))
- {
-   grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied");
-   goto error;
- }
-   cargs->key_len = grub_strlen ((char *) cargs->key_data);
-  }
+  if (dev == NULL)
+{
+  grub_error (GRUB_ERR_BAD_MODULE,
+ "no cryptodisk module can handle this device");
+  goto error_no_close;
+}
 
-ret = cr->recover_key (source, dev, cargs);
-if (ret != GRUB_ERR_NONE)
-  goto error;
+  if (cargs->protectors)
+{
+  for (i = 0; cargs->protectors[i]; i++)
+   {
+ if (cargs->key_cache[i].invalid)
+   continue;
+
+ if (cargs->key_cache[i].key == NULL)
+   {
+ ret = grub_key_protector_recover_key (cargs->protectors[i],
+   >key_cache[i].key,
+   
>key_cache[i].key_len);
+ if (ret != GRUB_ERR_NONE)
+   {
+ if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
+ grub_dprintf ("cryptodisk",
+   "failed to recover a key from key protector "
+   "%s, will not try it again for any other "
+   "disks, if any, during this invocation of "
+   "cryptomount\n",
+   cargs->protectors[i]);
+
+ cargs->key_cache[i].invalid = 1;
+ continue;
+   }
+   }
+
+ cargs->key_data = cargs->key_cache[i].key;
+ cargs->key_len = cargs->key_cache[i].key_len;
+
+ ret = cr->recover_key (source, dev, cargs);
+ if (ret != GRUB_ERR_NONE)
+ 

[PATCH v15 03/20] libtasn1: disable code not needed in grub

2024-05-10 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

We don't expect to be able to write ASN.1, only read it,
so we can disable some code.

Do that with #if 0/#endif, rather than deletion. This means
that the difference between upstream and grub is smaller,
which should make updating libtasn1 easier in the future.

With these exclusions we also avoid the need for minmax.h,
which is convenient because it means we don't have to
import it from gnulib.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 grub-core/lib/libtasn1/lib/coding.c| 12 ++--
 grub-core/lib/libtasn1/lib/decoding.c  |  2 ++
 grub-core/lib/libtasn1/lib/element.c   |  6 +++---
 grub-core/lib/libtasn1/lib/errors.c|  3 +++
 grub-core/lib/libtasn1/lib/structure.c | 10 ++
 include/grub/libtasn1.h| 15 +++
 6 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/grub-core/lib/libtasn1/lib/coding.c 
b/grub-core/lib/libtasn1/lib/coding.c
index ea5bc370e..5d03bca9d 100644
--- a/grub-core/lib/libtasn1/lib/coding.c
+++ b/grub-core/lib/libtasn1/lib/coding.c
@@ -30,11 +30,11 @@
 #include "parser_aux.h"
 #include 
 #include "element.h"
-#include "minmax.h"
 #include 
 
 #define MAX_TAG_LEN 16
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_error_description_value_not_found */
 /* Description: creates the ErrorDescription string   */
@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node,
   Estrcat (ErrorDescription, "' not found");
 
 }
+#endif
 
 /**
  * asn1_length_der:
@@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned 
char *str,
   return ASN1_SUCCESS;
 }
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_time_der  */
 /* Description: creates the DER coding for a TIME */
@@ -278,7 +280,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned 
char *der,
 
   return ASN1_SUCCESS;
 }
-
+#endif
 
 /*
 void
@@ -519,6 +521,7 @@ asn1_bit_der (const unsigned char *str, int bit_len,
 }
 
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_complete_explicit_tag */
 /* Description: add the length coding to the EXPLICIT */
@@ -595,6 +598,7 @@ _asn1_complete_explicit_tag (asn1_node node, unsigned char 
*der,
 
   return ASN1_SUCCESS;
 }
+#endif
 
 const tag_and_class_st _asn1_tags[] = {
   [ASN1_ETYPE_GENERALSTRING] =
@@ -647,6 +651,8 @@ const tag_and_class_st _asn1_tags[] = {
 
 unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]);
 
+
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_insert_tag_der*/
 /* Description: creates the DER coding of tags of one */
@@ -1423,3 +1429,5 @@ error:
   asn1_delete_structure ();
   return err;
 }
+
+#endif
diff --git a/grub-core/lib/libtasn1/lib/decoding.c 
b/grub-core/lib/libtasn1/lib/decoding.c
index b9245c486..bf9cb13ac 100644
--- a/grub-core/lib/libtasn1/lib/decoding.c
+++ b/grub-core/lib/libtasn1/lib/decoding.c
@@ -1620,6 +1620,7 @@ asn1_der_decoding (asn1_node * element, const void *ider, 
int ider_len,
   return asn1_der_decoding2 (element, ider, _len, 0, errorDescription);
 }
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**
  * asn1_der_decoding_element:
  * @structure: pointer to an ASN1 structure
@@ -1650,6 +1651,7 @@ asn1_der_decoding_element (asn1_node * structure, const 
char *elementName,
 {
   return asn1_der_decoding (structure, ider, len, errorDescription);
 }
+#endif
 
 /**
  * asn1_der_decoding_startEnd:
diff --git a/grub-core/lib/libtasn1/lib/element.c 
b/grub-core/lib/libtasn1/lib/element.c
index d4c558e10..bc4c3c8d7 100644
--- a/grub-core/lib/libtasn1/lib/element.c
+++ b/grub-core/lib/libtasn1/lib/element.c
@@ -118,7 +118,7 @@ _asn1_convert_integer (const unsigned char *value, unsigned 
char *value_out,
value_out[k2 - k] = val[k2];
 }
 
-#if 0
+#if 0 /* GRUB SKIPPED IMPORTING */
   printf ("_asn1_convert_integer: valueIn=%s, lenOut=%d", value, *len);
   for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++)
 printf (", vOut[%d]=%d", k, value_out[k]);
@@ -191,7 +191,7 @@ _asn1_append_sequence_set (asn1_node node, struct 
node_tail_cache_st *pcache)
   return ASN1_SUCCESS;
 }
 
-
+#if 0
 /**
  * asn1_write_value:
  * @node_root: pointer to a structure
@@ -646,7 +646,7 @@ asn1_write_value (asn1_node node_root, const char *name,
 
   return ASN1_SUCCESS;
 }
-
+#endif
 
 #define PUT_VALUE( ptr, ptr_size, data, data_size) \
*len = data_size; \
diff --git a/grub-core/lib/libtasn1/lib/errors.c 
b/grub-core/lib/libtasn1/lib/errors.c
index aef5dfe6f..2b2322152 100644
--- a/grub-core/lib/libtasn1/lib/errors.c
+++ b/grub-core/lib/libtasn1/lib/errors.c
@@ -57,6 +57,8 @@ static const libtasn1_error_entry error_algorithms[] = {
   {0, 0}
 };
 
+
+#if 

[PATCH v15 09/20] key_protector: Add key protectors framework

2024-05-10 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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 by name. If a key protector with the specified
name exists and if an unlocking key is successfully retrieved by it, the
function returns to the caller the retrieved key and its length.

Cc: Vladimir Serbinenko 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/Makefile.am  |  1 +
 grub-core/Makefile.core.def|  5 +++
 grub-core/disk/key_protector.c | 78 ++
 include/grub/key_protector.h   | 46 
 4 files changed, 130 insertions(+)
 create mode 100644 grub-core/disk/key_protector.c
 create mode 100644 include/grub/key_protector.h

diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index f18550c1c..9d3d5f519 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/key_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 8cecb9183..fe58ec740 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1282,6 +1282,11 @@ module = {
   common = disk/raid6_recover.c;
 };
 
+module = {
+  name = key_protector;
+  common = disk/key_protector.c;
+};
+
 module = {
   name = scsi;
   common = disk/scsi.c;
diff --git a/grub-core/disk/key_protector.c b/grub-core/disk/key_protector.c
new file mode 100644
index 0..b84afe1c7
--- /dev/null
+++ b/grub-core/disk/key_protector.c
@@ -0,0 +1,78 @@
+/*
+ *  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 .
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+struct grub_key_protector *grub_key_protectors = NULL;
+
+grub_err_t
+grub_key_protector_register (struct grub_key_protector *protector)
+{
+  if (protector == NULL || protector->name == NULL || grub_strlen 
(protector->name) == 0)
+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 (_key_protectors),
+ GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_unregister (struct grub_key_protector *protector)
+{
+  if (protector == NULL)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_list_remove (GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_recover_key (const char *protector, grub_uint8_t **key,
+   grub_size_t *key_size)
+{
+  struct grub_key_protector *kp = NULL;
+
+  if (grub_key_protectors == NULL)
+return GRUB_ERR_OUT_OF_RANGE;
+
+  if (protector == NULL || grub_strlen (protector) == 0)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  kp = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
+protector);
+  if (kp == NULL)
+return grub_error (GRUB_ERR_OUT_OF_RANGE,
+  N_("A key protector with name '%s' could not be found. "
+ "Is the name spelled correctly and is the "
+ "corresponding module loaded?"), protector);
+
+  return kp->recover_key (key, key_size);
+}
diff --git a/include/grub/key_protector.h b/include/grub/key_protector.h
new file mode 100644
index 0..6e6a6fb24
--- /dev/null
+++ b/include/grub/key_protector.h
@@ -0,0 +1,46 @@
+/*
+ *  GRUB  --  

[PATCH v15 00/20] Automatic Disk Unlock with TPM2

2024-05-10 Thread Gary Lin via Grub-devel
GIT repo for v15: https://github.com/lcp/grub2/tree/tpm2-unlock-v15

This patch series is based on "Automatic TPM Disk Unlock"(*1) posted by
Hernan Gatta to introduce the key protector framework and TPM2 stack
to GRUB2, and this could be a useful feature for the systems to
implement full disk encryption.

To support TPM 2.0 Key File format(*2), patch 1~5,7 are grabbed from
Daniel Axtens's "appended signature secure boot support" (*3) to import
libtasn1 into grub2. Besides, the libtasn1 version is upgraded to
4.19.0 instead of 4.16.0 in the original patch.

Patch 6 fixes a potential buffer overrun in libtasn1.
(https://gitlab.com/gnutls/libtasn1/-/issues/49)

Patch 8 adds the document for libtasn1 and the steps to upgrade the
library.

Patch 9~13 are Hernan Gatta's patches with the follow-up fixes and
improvements:
- Converting 8 spaces into 1 tab
- Merging the minor build fix from Michael Chang
  - Replacing "lu" with "PRIuGRUB_SIZE" for grub_dprintf
  - Adding "enable = efi" to the tpm2 module in grub-core/Makefile.core.def
- Rebasing "cryptodisk: Support key protectors" to the git master
- Removing the measurement on the sealed key
  - Based on the patch from Olaf Kirch 
- Adjusting the input parameters of TPM2_EvictControl to match the order
  in "TCG TPM2 Part3 Commands"
- Declaring the input arguments of TPM2 functions as const
- Resending TPM2 commands on TPM_RC_RETRY
- Adding checks for the parameters of TPM2 commands
- Packing the missing authorization command for TPM2_PCR_Read
- Tweaking the TPM2 command functions to allow some parameters to be
  NULL so that we don't have to declare empty variables
- Only enabling grub-protect for "efi" since the TPM2 stack currently
  relies on the EFI TCG2 protocol to send TPM2 commands
- Using grub_cpu_to_be*() in the TPM2 stack instead of grub_swap_bytes*()
  which may cause problems in big-indian machines
- Changing the short name of "--protector" of "cryptomount" from "-k" to
  "-P" to avoid the conflict with "--key-file"
- Supporting TPM 2.0 Key File Format besides the raw sealed key
- Adding the external libtasn1 dependency to grub-protect to write the
  TPM 2.0 Key files
- Extending the TPM2 TSS stack to support authorized policy

Patch 14 implements the authorized policy support.

Patch 15 implements the missing NV index mode. (Thanks to Patrick Colp)

Patch 16 improves the 'cryptomount' command to fall back to the
passphrase mode when the key protector fails to unlock the encrypted
partition. (Another patch from Patrick Colp)

Patch 17 and 18 fix the potential security issues spotted by Fabian Vogt.

Patch 19 and 20 implement the TPM key unsealing testcases.

To utilize the TPM2 key protector to unlock the encrypted partition
(sdb1), here are the sample steps:

1. Add an extra random key for LUKS (luks-key)
   $ dd if=/dev/urandom of=luks-key bs=1 count=32
   $ sudo cryptsetup luksAddKey /dev/sdb1 luks-key --pbkdf=pbkdf2

2. Seal the key
   $ sudo grub-protect --action=add \
   --protector=tpm2 \
   --tpm2key \
   --tpm2-keyfile=luks-key \
   --tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm

3. Unseal the key with the proper commands in grub.cfg:
   tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
   cryptomount -u  -P tpm2

(*1) https://lists.gnu.org/archive/html/grub-devel/2022-02/msg6.html
(*2) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*3) https://lists.gnu.org/archive/html/grub-devel/2021-06/msg00044.html

v15:
- Changes in tpm2_test
  - Quoting the variables which contain file paths
  - Correcting the exit code for several commands
  - Writing the verification text directly into the LUKS device
  - Amending the waiting loop for swtpm
  - Replacing exit with return in tpm2_seal_unseal() and
tpm2_seal_unseal_nv()
  - Collecting the parameters for the SRK mode testcases in an array
and invoking tpm2_seal_unseal() with a for loop
  - Moving the tpm2-tools commands for the NV index mode to a separate
function  
  - Using tpm2_evictcontrol to remove the object from the NV index to
match the key sealing commands
  - Printing the test results
  - Printing error messages to stderr

v14:
- https://lists.gnu.org/archive/html/grub-devel/2024-05/msg00011.html
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-v14
- Addressing the libtasn1 patches more in the document
- Various improvements in tpm2_test
  - Verifying the test inside the LUKS device
  - Improving the return status checks and the waiting loop for swtpm
  - Fixing the portability issues
  - Making all variables braced
- Renaming grub-emu-opts to --emu-opts (grub-shell)

v13:
- https://lists.gnu.org/archive/html/grub-devel/2024-04/msg00155.html
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-v13
- Fixing typos and a few multi-line comments
- Improving the conditional checks for the arguments of
  tpm2_key_protector_init 
- Updating to the 

[PATCH v15 04/20] libtasn1: changes for grub compatibility

2024-05-10 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

Do a few things to make libtasn1 compile as part of grub:

 - remove _asn1_strcat and replace strcat with the bound-checked
   _asn1_str_cat except the one inside _asn1_str_cat. That strcat is
   replaced with strcpy.

 - adjust header paths in libtasn1.h

 - adjust header paths to "grub/libtasn1.h".

 - replace a 64 bit division with a call to grub_divmod64, preventing
   creation of __udivdi3 calls on 32 bit platforms.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/lib/libtasn1/lib/decoding.c   | 8 
 grub-core/lib/libtasn1/lib/element.c| 2 +-
 grub-core/lib/libtasn1/lib/gstr.c   | 2 +-
 grub-core/lib/libtasn1/lib/int.h| 3 +--
 grub-core/lib/libtasn1/lib/parser_aux.c | 2 +-
 include/grub/libtasn1.h | 5 ++---
 6 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/grub-core/lib/libtasn1/lib/decoding.c 
b/grub-core/lib/libtasn1/lib/decoding.c
index bf9cb13ac..51859fe36 100644
--- a/grub-core/lib/libtasn1/lib/decoding.c
+++ b/grub-core/lib/libtasn1/lib/decoding.c
@@ -2016,8 +2016,8 @@ asn1_expand_octet_string (asn1_node_const definitions, 
asn1_node * element,
  (p2->type & CONST_ASSIGN))
{
  strcpy (name, definitions->name);
- strcat (name, ".");
- strcat (name, p2->name);
+ _asn1_str_cat (name, sizeof (name), ".");
+ _asn1_str_cat (name, sizeof (name), p2->name);
 
  len = sizeof (value);
  result = asn1_read_value (definitions, name, value, );
@@ -2034,8 +2034,8 @@ asn1_expand_octet_string (asn1_node_const definitions, 
asn1_node * element,
  if (p2)
{
  strcpy (name, definitions->name);
- strcat (name, ".");
- strcat (name, p2->name);
+ _asn1_str_cat (name, sizeof (name), ".");
+ _asn1_str_cat (name, sizeof (name), p2->name);
 
  result = asn1_create_element (definitions, name, );
  if (result == ASN1_SUCCESS)
diff --git a/grub-core/lib/libtasn1/lib/element.c 
b/grub-core/lib/libtasn1/lib/element.c
index bc4c3c8d7..8694fecb9 100644
--- a/grub-core/lib/libtasn1/lib/element.c
+++ b/grub-core/lib/libtasn1/lib/element.c
@@ -688,7 +688,7 @@ asn1_write_value (asn1_node node_root, const char *name,
 return ASN1_MEM_ERROR; \
 } else { \
 /* this strcat is checked */ \
-if (ptr) _asn1_strcat (ptr, data); \
+if (ptr) _asn1_str_cat ((char *)ptr, ptr_size, (const char 
*)data); \
 }
 
 /**
diff --git a/grub-core/lib/libtasn1/lib/gstr.c 
b/grub-core/lib/libtasn1/lib/gstr.c
index eef419554..a9c16f5d3 100644
--- a/grub-core/lib/libtasn1/lib/gstr.c
+++ b/grub-core/lib/libtasn1/lib/gstr.c
@@ -36,7 +36,7 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char 
*src)
 
   if (dest_tot_size - dest_size > str_size)
 {
-  strcat (dest, src);
+  strcpy (dest + dest_size, src);
 }
   else
 {
diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h
index d94d51c8c..7409c7655 100644
--- a/grub-core/lib/libtasn1/lib/int.h
+++ b/grub-core/lib/libtasn1/lib/int.h
@@ -35,7 +35,7 @@
 #  include 
 # endif
 
-# include 
+# include "grub/libtasn1.h"
 
 # define ASN1_SMALL_VALUE_SIZE 16
 
@@ -115,7 +115,6 @@ extern const tag_and_class_st _asn1_tags[];
 # define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
 # define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
 # define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
-# define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
 
 # if SIZEOF_UNSIGNED_LONG_INT == 8
 #  define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b)
diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c 
b/grub-core/lib/libtasn1/lib/parser_aux.c
index c05bd2339..e4e4c0556 100644
--- a/grub-core/lib/libtasn1/lib/parser_aux.c
+++ b/grub-core/lib/libtasn1/lib/parser_aux.c
@@ -632,7 +632,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
   count = 0;
   do
 {
-  d = val / 10;
+  d = grub_divmod64(val, 10, NULL);
   r = val - d * 10;
   temp[start + count] = '0' + (char) r;
   count++;
diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
index 058ab27b0..7d64b6ab7 100644
--- a/include/grub/libtasn1.h
+++ b/include/grub/libtasn1.h
@@ -54,9 +54,8 @@
 #  define __LIBTASN1_PURE__
 # endif
 
-# include 
-# include 
-# include /* for FILE* */
+# include 
+# include 
 
 # ifdef __cplusplus
 extern "C"
-- 
2.35.3


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


[PATCH v15 14/20] tpm2: Support authorized policy

2024-05-10 Thread Gary Lin via Grub-devel
This commit handles the TPM2_PolicyAuthorize command from the key file
in TPM 2.0 Key File format.

TPM2_PolicyAuthorize is the essential command to support authorized
policy which allows the users to sign TPM policies with their own keys.
Per TPM 2.0 Key File(*1), CommandPolicy for TPM2_PolicyAuthorize
comprises 'TPM2B_PUBLIC pubkey', 'TPM2B_DIGEST policy_ref', and
'TPMT_SIGNATURE signature'. To verify the signature, the current policy
digest is hashed with the hash algorithm written in 'signature', and then
'signature' is verified with the hashed policy digest and 'pubkey'. Once
TPM accepts 'signature', TPM2_PolicyAuthorize is invoked to authorize the
signed policy.

To create the key file with authorized policy, here are the pcr-oracle(*2)
commands:

  # Generate the RSA key and create the authorized policy file
  $ pcr-oracle \
--rsa-generate-key \
--private-key policy-key.pem \
--auth authorized.policy \
create-authorized-policy 0,2,4,7,9

  # Seal the secret with the authorized policy
  $ pcr-oracle \
--key-format tpm2.0 \
--auth authorized.policy \
--input disk-secret.txt \
--output sealed.key \
seal-secret

  # Sign the predicted PCR policy
  $ pcr-oracle \
--key-format tpm2.0 \
--private-key policy-key.pem \
--from eventlog \
--stop-event "grub-file=grub.cfg" \
--after \
--input sealed.key \
--output sealed.tpm \
sign 0,2,4,7,9

Then specify the key file and the key protector to grub.cfg in the EFI
system partition:

tpm2_key_protector_init -a RSA --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

For any change in the boot components, just run the 'sign' command again
to update the signature in sealed.tpm, and TPM can unseal the key file
with the updated PCR policy.

(*1) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*2) https://github.com/okirch/pcr-oracle

Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 grub-core/tpm2/module.c | 84 +
 1 file changed, 84 insertions(+)

diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
index 3db25ceca..e83b02865 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -650,6 +650,87 @@ grub_tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION 
session,
   return GRUB_ERR_NONE;
 }
 
+static grub_err_t
+grub_tpm2_protector_policyauthorize (TPMI_SH_AUTH_SESSION session,
+struct grub_tpm2_buffer *cmd_buf)
+{
+  TPM2B_PUBLIC pubkey;
+  TPM2B_DIGEST policy_ref;
+  TPMT_SIGNATURE signature;
+  TPM2B_DIGEST pcr_policy;
+  TPM2B_DIGEST pcr_policy_hash;
+  TPMI_ALG_HASH sig_hash;
+  TPMT_TK_VERIFIED verification_ticket;
+  TPM_HANDLE pubkey_handle = 0;
+  TPM2B_NAME pubname;
+  TPM_RC rc;
+  grub_err_t err;
+
+  grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (cmd_buf, );
+  grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, _ref);
+  grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (cmd_buf, );
+  if (cmd_buf->error != 0)
+return grub_error (GRUB_ERR_BAD_ARGUMENT,
+  N_("Failed to unmarshal the buffer for 
TPM2_PolicyAuthorize"));
+
+  /* Retrieve Policy Digest */
+  rc = TPM2_PolicyGetDigest (session, NULL, _policy, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to get policy digest (TPM2_PolicyGetDigest: 
0x%x)."),
+  rc);
+
+  /* Calculate the digest of the polcy for VerifySignature */
+  sig_hash = TPMT_SIGNATURE_get_hash_alg ();
+  if (sig_hash == TPM_ALG_NULL)
+return grub_error (GRUB_ERR_BAD_ARGUMENT,
+  N_("Failed to get the hash algorithm of the signature"));
+
+  rc = TPM2_Hash (NULL, (TPM2B_MAX_BUFFER *)_policy, sig_hash,
+ TPM_RH_NULL, _policy_hash, NULL, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to create PCR policy hash (TPM2_Hash: 0x%x)"),
+  rc);
+
+  /* Load the public key */
+  rc = TPM2_LoadExternal (NULL, NULL, , TPM_RH_OWNER,
+ _handle, , NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to load public key (TPM2_LoadExternal: 
0x%x)"),
+  rc);
+
+  /* Verify the signature against the public key and the policy digest */
+  rc = TPM2_VerifySignature (pubkey_handle, NULL, _policy_hash, ,
+_ticket, NULL);
+  if (rc != TPM_RC_SUCCESS)
+{
+  err = grub_error (GRUB_ERR_BAD_DEVICE,
+   N_("Failed to verify signature (TPM2_VerifySignature: 
0x%x)"),
+   rc);
+  goto error;
+}
+
+  /* Authorize the signed policy with the public key and the verification 
ticket */
+  rc = TPM2_PolicyAuthorize (session, NULL, _policy, _ref, ,
+_ticket, NULL);
+  if (rc != 

[PATCH v15 01/20] posix_wrap: tweaks in preparation for libtasn1

2024-05-10 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

 - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as
   SIZEOF_UNSIGNED_LONG.

 - Define WORD_BIT, the size in bits of an int. This is a defined
   in the Single Unix Specification and in gnulib's limits.h. gnulib
   assumes it's 32 bits on all our platforms, including 64 bit
   platforms, so we also use that value.

 - Provide strto[u]l[l] preprocessor macros that resolve to
   grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we
   also define HAVE_STRTOUL here.

 - Implement c-ctype.h and the functions defined in the header.

 - Implement strncat in string.h.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 grub-core/lib/posix_wrap/c-ctype.h   | 114 +++
 grub-core/lib/posix_wrap/limits.h|   1 +
 grub-core/lib/posix_wrap/stdlib.h|   8 ++
 grub-core/lib/posix_wrap/string.h|  21 +
 grub-core/lib/posix_wrap/sys/types.h |   1 +
 5 files changed, 145 insertions(+)
 create mode 100644 grub-core/lib/posix_wrap/c-ctype.h

diff --git a/grub-core/lib/posix_wrap/c-ctype.h 
b/grub-core/lib/posix_wrap/c-ctype.h
new file mode 100644
index 0..5f8fc8ce3
--- /dev/null
+++ b/grub-core/lib/posix_wrap/c-ctype.h
@@ -0,0 +1,114 @@
+/*
+ *  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 .
+ */
+
+#ifndef GRUB_POSIX_C_CTYPE_H
+#define GRUB_POSIX_C_CTYPE_H   1
+
+#include 
+
+static inline bool
+c_isspace (int c)
+{
+  return !!grub_isspace (c);
+}
+
+static inline bool
+c_isdigit (int c)
+{
+  return !!grub_isdigit (c);
+}
+
+static inline bool
+c_islower (int c)
+{
+  return !!grub_islower (c);
+}
+
+static inline bool
+c_isascii (int c)
+{
+  return !(c & ~0x7f);
+}
+
+static inline bool
+c_isupper (int c)
+{
+  return !!grub_isupper (c);
+}
+
+static inline bool
+c_isxdigit (int c)
+{
+  return !!grub_isxdigit (c);
+}
+
+static inline bool
+c_isprint (int c)
+{
+  return !!grub_isprint (c);
+}
+
+static inline bool
+c_iscntrl (int c)
+{
+  return !grub_isprint (c);
+}
+
+static inline bool
+c_isgraph (int c)
+{
+  return grub_isprint (c) && !grub_isspace (c);
+}
+
+static inline bool
+c_isalnum (int c)
+{
+  return grub_isalpha (c) || grub_isdigit (c);
+}
+
+static inline bool
+c_ispunct (int c)
+{
+  return grub_isprint (c) && !grub_isspace (c) && !c_isalnum (c);
+}
+
+static inline bool
+c_isalpha (int c)
+{
+  return !!grub_isalpha (c);
+}
+
+static inline bool
+c_isblank (int c)
+{
+  return c == ' ' || c == '\t';
+}
+
+static inline int
+c_tolower (int c)
+{
+  return grub_tolower (c);
+}
+
+static inline int
+c_toupper (int c)
+{
+  return grub_toupper (c);
+}
+
+#endif
diff --git a/grub-core/lib/posix_wrap/limits.h 
b/grub-core/lib/posix_wrap/limits.h
index 26918c8a0..4be7b4080 100644
--- a/grub-core/lib/posix_wrap/limits.h
+++ b/grub-core/lib/posix_wrap/limits.h
@@ -41,5 +41,6 @@
 #define LONG_MAX GRUB_LONG_MAX
 
 #define CHAR_BIT 8
+#define WORD_BIT 32
 
 #endif
diff --git a/grub-core/lib/posix_wrap/stdlib.h 
b/grub-core/lib/posix_wrap/stdlib.h
index f5279756a..14e4efdd0 100644
--- a/grub-core/lib/posix_wrap/stdlib.h
+++ b/grub-core/lib/posix_wrap/stdlib.h
@@ -64,4 +64,12 @@ abort (void)
   grub_abort ();
 }
 
+#define strtol grub_strtol
+
+/* for libgcrypt */
+#define HAVE_STRTOUL
+#define strtoul grub_strtoul
+
+#define strtoull grub_strtoull
+
 #endif
diff --git a/grub-core/lib/posix_wrap/string.h 
b/grub-core/lib/posix_wrap/string.h
index 1adb450b5..b0c5928d2 100644
--- a/grub-core/lib/posix_wrap/string.h
+++ b/grub-core/lib/posix_wrap/string.h
@@ -84,6 +84,27 @@ memchr (const void *s, int c, grub_size_t n)
   return grub_memchr (s, c, n);
 }
 
+static inline char *
+strncat(char *dest, const char *src, grub_size_t n)
+{
+  const char *end;
+  char *str = dest;
+  grub_size_t src_len;
+
+  dest += grub_strlen (dest);
+
+  end = grub_memchr (src, '\0', n);
+  if (end != NULL)
+src_len = (grub_size_t) (end - src);
+  else
+src_len = n;
+
+  dest[src_len] = '\0';
+  grub_memcpy (dest, src, src_len);
+
+  return str;
+}
+
 #define memcmp grub_memcmp
 #define memcpy grub_memcpy
 #define memmove grub_memmove
diff --git a/grub-core/lib/posix_wrap/sys/types.h 
b/grub-core/lib/posix_wrap/sys/types.h
index eeda543c4..2f3e86549 100644
--- a/grub-core/lib/posix_wrap/sys/types.h
+++ b/grub-core/lib/posix_wrap/sys/types.h
@@ -50,6 

[PATCH v15 05/20] libtasn1: fix the potential buffer overrun

2024-05-10 Thread Gary Lin via Grub-devel
In _asn1_tag_der(), the first while loop for the long form may end up
with a 'k' value with 'ASN1_MAX_TAG_SIZE' and cause the buffer overrun
in the second while loop. This commit tweaks the conditional check to
avoid producing a too large 'k'.

This is a quick fix and may differ from the official upstream fix.

libtasn1 issue: https://gitlab.com/gnutls/libtasn1/-/issues/49

Signed-off-by: Gary Lin 
---
 grub-core/lib/libtasn1/lib/coding.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/grub-core/lib/libtasn1/lib/coding.c 
b/grub-core/lib/libtasn1/lib/coding.c
index 5d03bca9d..0458829a5 100644
--- a/grub-core/lib/libtasn1/lib/coding.c
+++ b/grub-core/lib/libtasn1/lib/coding.c
@@ -143,7 +143,7 @@ _asn1_tag_der (unsigned char class, unsigned int tag_value,
  temp[k++] = tag_value & 0x7F;
  tag_value >>= 7;
 
- if (k > ASN1_MAX_TAG_SIZE - 1)
+ if (k >= ASN1_MAX_TAG_SIZE - 1)
break;  /* will not encode larger tags */
}
   *ans_len = k + 1;
-- 
2.35.3


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


Re: [PATCH v14 20/20] tests: Add tpm2_test

2024-05-08 Thread Gary Lin via Grub-devel
On Wed, May 08, 2024 at 03:25:29PM -0500, Glenn Washburn wrote:
> On Tue, 7 May 2024 16:19:19 +0800
> Gary Lin  wrote:
> 
> > On Mon, May 06, 2024 at 02:09:12PM -0500, Glenn Washburn wrote:
> > > On Fri,  3 May 2024 14:48:56 +0800
> > > Gary Lin  wrote:
> > > 
> > > > For the tpm2 module, the TCG2 command submission function is the only
> > > > difference between the a QEMU instance and grub-emu. To test TPM key
> > > > unsealing with a QEMU instance, it requires an extra OS image to invoke
> > > > grub-protect to seal the LUKS key, rather than a simple grub-shell 
> > > > rescue
> > > > CD image. On the other hand, grub-emu can share the emulated TPM device
> > > > with the host, so that we can seal the LUKS key on host and test key
> > > > unsealing with grub-emu.
> > > > 
> > > > This test script firstly creates a simple LUKS image to be loaded as a
> > > > loopback device in grub-emu. Then an emulated TPM device is created by
> > > > swtpm_cuse and PCR 0 and 1 are extended.
> > > > 
> > > > There are several test cases in the script to test various settings. 
> > > > Each
> > > > test case uses grub-protect or tpm2-tools to seal the LUKS password
> > > > against PCR 0 and PCR 1. Then grub-emu is launched to load the LUKS 
> > > > image,
> > > > try to mount the image with tpm2_key_protector_init and cryptomount, and
> > > > verify the result.
> > > > 
> > > > Based on the idea from Michael Chang.
> > > > 
> > > > Cc: Michael Chang 
> > > > Cc: Stefan Berger 
> > > > Cc: Glenn Washburn 
> > > > Signed-off-by: Gary Lin 
> > > > ---
> > > >  Makefile.util.def|   6 +
> > > >  tests/tpm2_test.in   | 306 +++
> > > >  tests/util/grub-shell.in |   6 +-
> > > >  3 files changed, 317 insertions(+), 1 deletion(-)
> > > >  create mode 100644 tests/tpm2_test.in
> > > > 
> > > > diff --git a/Makefile.util.def b/Makefile.util.def
> > > > index 40bfe713d..8d4c53a03 100644
> > > > --- a/Makefile.util.def
> > > > +++ b/Makefile.util.def
> > > > @@ -1281,6 +1281,12 @@ script = {
> > > >common = tests/asn1_test.in;
> > > >  };
> > > >  
> > > > +script = {
> > > > +  testcase = native;
> > > > +  name = tpm2_test;
> > > > +  common = tests/tpm2_test.in;
> > > > +};
> > > > +
> > > >  program = {
> > > >testcase = native;
> > > >name = example_unit_test;
> > > > diff --git a/tests/tpm2_test.in b/tests/tpm2_test.in
> > > > new file mode 100644
> > > > index 0..75c042274
> > > > --- /dev/null
> > > > +++ b/tests/tpm2_test.in
> > > > @@ -0,0 +1,306 @@
> > > > +#! @BUILD_SHEBANG@ -e
> > > > +
> > > > +# Test GRUBs ability to unseal a LUKS key with TPM 2.0
> > > > +# 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 .
> > > > +
> > > > +grubshell=@builddir@/grub-shell
> > > > +
> > > > +. "@builddir@/grub-core/modinfo.sh"
> > > > +
> > > > +if [ x${grub_modinfo_platform} != xemu ]; then
> > > > +  exit 77
> > > > +fi
> > > > +
> > > > +builddir="@builddir@"
> > > > +
> > > > +# Force build directory components
> > > > +PATH="${builddir}:${PATH}"
> > > > +export PATH
> > > > +
> > > > +if [ "x${EUID}" = "x" ] ; then
> > > 
> > > Braces in quoted variables at the end of the quoted string don't need
> > > braces. But no need to change it back.
> > > 
> > > > +  EUID=`id -u`
> > > > +fi
> > > > +
> > > > +if [ "${EUID}" != 0 ] ; then
> > > > +   echo "not root; cannot test tpm2."
> > > > +   exit 99
> > > > +fi
> > > > +
> > > > +if ! command -v cryptsetup >/dev/null 2>&1; then
> > > > +   echo "cryptsetup not installed; cannot test tpm2."
> > > > +   exit 99
> > > > +fi
> > > > +
> > > > +if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe 
> > > > tpm_vtpm_proxy; then
> > > > +   echo "no tpm_vtpm_proxy support; cannot test tpm2."
> > > > +   exit 99
> > > > +fi
> > > > +
> > > > +if ! command -v swtpm >/dev/null 2>&1; then
> > > > +   echo "swtpm not installed; cannot test tpm2."
> > > > +   exit 99
> > > > +fi
> > > > +
> > > > +if ! command -v tpm2_startup >/dev/null 2>&1; then
> > > > +   echo "tpm2-tools not installed; cannot test tpm2."
> > > > +   exit 99
> > > > +fi
> > > > +
> > > > +tpm2testdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename 
> > > > "$0").XX"`" || exit 99
> > > > +
> > > > +disksize=20M
> > > > +
> > > > 

Re: [PATCH v14 20/20] tests: Add tpm2_test

2024-05-07 Thread Gary Lin via Grub-devel
On Mon, May 06, 2024 at 02:09:12PM -0500, Glenn Washburn wrote:
> On Fri,  3 May 2024 14:48:56 +0800
> Gary Lin  wrote:
> 
> > For the tpm2 module, the TCG2 command submission function is the only
> > difference between the a QEMU instance and grub-emu. To test TPM key
> > unsealing with a QEMU instance, it requires an extra OS image to invoke
> > grub-protect to seal the LUKS key, rather than a simple grub-shell rescue
> > CD image. On the other hand, grub-emu can share the emulated TPM device
> > with the host, so that we can seal the LUKS key on host and test key
> > unsealing with grub-emu.
> > 
> > This test script firstly creates a simple LUKS image to be loaded as a
> > loopback device in grub-emu. Then an emulated TPM device is created by
> > swtpm_cuse and PCR 0 and 1 are extended.
> > 
> > There are several test cases in the script to test various settings. Each
> > test case uses grub-protect or tpm2-tools to seal the LUKS password
> > against PCR 0 and PCR 1. Then grub-emu is launched to load the LUKS image,
> > try to mount the image with tpm2_key_protector_init and cryptomount, and
> > verify the result.
> > 
> > Based on the idea from Michael Chang.
> > 
> > Cc: Michael Chang 
> > Cc: Stefan Berger 
> > Cc: Glenn Washburn 
> > Signed-off-by: Gary Lin 
> > ---
> >  Makefile.util.def|   6 +
> >  tests/tpm2_test.in   | 306 +++
> >  tests/util/grub-shell.in |   6 +-
> >  3 files changed, 317 insertions(+), 1 deletion(-)
> >  create mode 100644 tests/tpm2_test.in
> > 
> > diff --git a/Makefile.util.def b/Makefile.util.def
> > index 40bfe713d..8d4c53a03 100644
> > --- a/Makefile.util.def
> > +++ b/Makefile.util.def
> > @@ -1281,6 +1281,12 @@ script = {
> >common = tests/asn1_test.in;
> >  };
> >  
> > +script = {
> > +  testcase = native;
> > +  name = tpm2_test;
> > +  common = tests/tpm2_test.in;
> > +};
> > +
> >  program = {
> >testcase = native;
> >name = example_unit_test;
> > diff --git a/tests/tpm2_test.in b/tests/tpm2_test.in
> > new file mode 100644
> > index 0..75c042274
> > --- /dev/null
> > +++ b/tests/tpm2_test.in
> > @@ -0,0 +1,306 @@
> > +#! @BUILD_SHEBANG@ -e
> > +
> > +# Test GRUBs ability to unseal a LUKS key with TPM 2.0
> > +# 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 .
> > +
> > +grubshell=@builddir@/grub-shell
> > +
> > +. "@builddir@/grub-core/modinfo.sh"
> > +
> > +if [ x${grub_modinfo_platform} != xemu ]; then
> > +  exit 77
> > +fi
> > +
> > +builddir="@builddir@"
> > +
> > +# Force build directory components
> > +PATH="${builddir}:${PATH}"
> > +export PATH
> > +
> > +if [ "x${EUID}" = "x" ] ; then
> 
> Braces in quoted variables at the end of the quoted string don't need
> braces. But no need to change it back.
> 
> > +  EUID=`id -u`
> > +fi
> > +
> > +if [ "${EUID}" != 0 ] ; then
> > +   echo "not root; cannot test tpm2."
> > +   exit 99
> > +fi
> > +
> > +if ! command -v cryptsetup >/dev/null 2>&1; then
> > +   echo "cryptsetup not installed; cannot test tpm2."
> > +   exit 99
> > +fi
> > +
> > +if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; 
> > then
> > +   echo "no tpm_vtpm_proxy support; cannot test tpm2."
> > +   exit 99
> > +fi
> > +
> > +if ! command -v swtpm >/dev/null 2>&1; then
> > +   echo "swtpm not installed; cannot test tpm2."
> > +   exit 99
> > +fi
> > +
> > +if ! command -v tpm2_startup >/dev/null 2>&1; then
> > +   echo "tpm2-tools not installed; cannot test tpm2."
> > +   exit 99
> > +fi
> > +
> > +tpm2testdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XX"`" || 
> > exit 99
> > +
> > +disksize=20M
> > +
> > +luksfile=${tpm2testdir}/luks.disk
> 
> Its a good idea to have variables that can be user provided and thus
> have spaces to be quoted. tpm2testdir and any variable contructed from
> it fall in that category. Note that putting a variable in braces
> unquoted, does not make implicit quoting.
> 
Ok, will quote those variables.

> > +lukskeyfile=${tpm2testdir}/password.txt
> > +
> > +# Choose a low iteration number to reduce the time to decrypt the disk
> > +csopt="--type luks2 --pbkdf pbkdf2 --iter-time 1000"
> > +
> > +tpm2statedir=${tpm2testdir}/tpm
> > +tpm2ctrl=${tpm2statedir}/ctrl
> > +tpm2log=${tpm2statedir}/logfile
> > +
> > +sealedkey=${tpm2testdir}/sealed.tpm
> > +

  1   2   3   4   >